/* Парус 8 - Панели мониторинга - УДП - Доски задач Пользовательские хуки: Хуки основных данных */ //--------------------- //Подключение библиотек //--------------------- import { useState, useContext, useEffect, useCallback } from "react"; //Классы React import { ApplicationСtx } from "../../../context/application"; //Контекст приложения import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером import { object2Base64XML } from "../../../core/utils"; //Вспомогательные функции import { arrayFormer, randomColor } from "../layouts"; //Формировщик массива и формирование случайного цвета //----------- //Тело модуля //----------- //Хук получения событий const useTasks = ({ filters, orders, extraData, getDocLinks }) => { //Состояние событий const [tasks, setTasks] = useState({ groupsLoaded: false, tasksLoaded: false, rows: [], statuses: [], reload: true }); //Подключение к контексту взаимодействия с сервером const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); //Подключение к контексту приложения const { pOnlineShowDictionary } = useContext(ApplicationСtx); //Надо обновить данные const needUpdateTasks = () => setTasks(pv => ({ ...pv, groupsLoaded: false, tasksLoaded: false, reload: true })); //Инициализация параметров события const initTask = (id, gp, task) => { //Добавление дополнительных свойств let newDocProps = {}; Object.keys(task) .filter(k => k.includes("DP_")) .map(dp => (newDocProps = { ...newDocProps, [dp]: task[dp] })); return { id: id, name: task.SPREF_NUMB, category: gp, nrn: task.NRN, scrn: "", sprefix: task.SEVPREF, snumber: task.SEVNUMB, stype: task.SEVTYPE_CODE, sstatus: task.SEVSTAT_NAME, sdescription: task.SEVDESCR, sclnt_clnclients: "", sclnt_clnperson: "", dchange_date: task.DCHANGE_DATE, dstart_date: task.DREG_DATE, dexpire_date: task.DEXPIRE_DATE, dplan_date: task.DPLAN_DATE, sinit_clnperson: task.SINIT_PERSON, sinit_user: "", sinit_reason: "", //SEND_CLIENT sto_company: "", //SEND_DIVISION sto_department: task.SSEND_DIVISION, //SEND_POST sto_clnpost: "", //SEND_PERFORM sto_clnpsdep: "", //SEND_PERSON sto_clnperson: task.SSEND_PERSON, //SEND_STAFFGRP sto_fcstaffgrp: "", //SEND_USER_AUTHID sto_user: "", //SEND_USER_GROUP sto_usergrp: task.SSEND_USRGRP, sSender: task.SSENDER, scurrent_user: "", slinked_unit: task.SLINKED_UNIT, nlinked_rn: task.NLINKED_RN, docProps: newDocProps }; }; //Изменение статуса события (переносом) const handleStateChange = useCallback( async (nEvent, sNextStat, note) => { try { //Выполняем инициализацию параметров const firstStep = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: { NSTEP: 1, NEVENT: nEvent, SNEXT_STAT: sNextStat } }); if (firstStep) { //Если требуется выбрать получателя if (firstStep.NSELECT_EXEC === 1) { //Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя pOnlineShowDictionary({ unitCode: "EventRoutesPointExecuters", showMethod: "executers", inputParameters: [ { name: "in_IDENT", value: firstStep.NIDENT }, { name: "in_EVENT", value: nEvent }, { name: "in_EVENT_TYPE", value: firstStep.SEVENT_TYPE }, { name: "in_EVENT_STAT", value: firstStep.SEVENT_STAT }, { name: "in_INIT_PERSON", value: firstStep.SINIT_PERSON }, { name: "in_INIT_AUTHNAME", value: firstStep.SINIT_AUTHNAME }, { name: "in_CLIENT_CLIENT", value: firstStep.SCLIENT_CLIENT }, { name: "in_CLIENT_PERSON", value: firstStep.SCLIENT_PERSON } ], callBack: async send => { //Общие аргументы const mainArgs = { NIDENT: firstStep.NIDENT, NSTEP: 3, NEVENT: nEvent, SEVENT_STAT: firstStep.SEVENT_STAT, SSEND_CLIENT: send.outParameters.out_CLIENT_CODE, SSEND_DIVISION: send.outParameters.out_DIVISION_CODE, SSEND_POST: send.outParameters.out_POST_CODE, SSEND_PERFORM: send.outParameters.out_POST_IN_DIV_CODE, SSEND_PERSON: send.outParameters.out_PERSON_CODE, SSEND_STAFFGRP: send.outParameters.out_STAFFGRP_CODE, SSEND_USER_GROUP: send.outParameters.out_USER_GROUP_CODE, SSEND_USER_NAME: send.outParameters.out_USER_NAME, NSEND_PREDEFINED_EXEC: send.outParameters.out_PREDEFINED_EXEC, NSEND_PREDEFINED_PROC: send.outParameters.out_PREDEFINED_PROC }; //Выполняем переход к выбранной точке с исполнителем await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: note ? { ...mainArgs, SNOTE_HEADER: note.header, SNOTE: note.text } : mainArgs }); //Необходимо обновить данные setTasks(pv => ({ ...pv, reload: true })); } }); } else { //Общие аргументы const mainArgs = { NIDENT: firstStep.NIDENT, NSTEP: 3, NEVENT: nEvent, SEVENT_STAT: firstStep.SEVENT_STAT }; //Выполняем переход к выбранной точке с предопределенным исполнителем await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: note ? { ...mainArgs, ...{ SNOTE_HEADER: note.header, SNOTE: note.text } } : mainArgs }); //Необходимо обновить данные setTasks(pv => ({ ...pv, reload: true })); } } } catch (e) { //Необходимо обновить данные setTasks(pv => ({ ...pv, reload: true })); } }, [executeStored, pOnlineShowDictionary] ); //Надо обновить события const handleReload = useCallback(() => { setTasks(pv => ({ ...pv, reload: true })); }, []); //Взаимодействие с событием (через перенос) const onDragEnd = useCallback( (result, eventPoints, openNoteDialog) => { //Определяем нужные параметры const { source, destination } = result; //Если путь не указан if (!destination) { return; } //Если происходит изменение статуса if (destination.droppableId !== source.droppableId) { //Считываем строку, у которой изменяется статус let row = tasks.rows.find(f => f.id === parseInt(result.draggableId)); //Формируем события с учетом изменения let rows = tasks.rows.map(task => task.id === parseInt(result.draggableId) ? { ...task, category: parseInt(result.destination.droppableId) } : task ); //Мнемокод точки назначения const destCode = tasks.statuses.find(s => s.id == destination.droppableId).code; //Получение настройки точки назначения const pointSettings = eventPoints.find(ep => ep.point === destCode); //Если необходимо примечание при переходе if (pointSettings.addNoteOnChst) { //Изменяем статус события с добавлением примечания openNoteDialog(n => { setTasks(pv => ({ ...pv, rows: [...rows] })); handleStateChange(row.nrn, destCode, n); }); } //Изменяем статус события else { //Переинициализируем строки с учетом изменений (для визуального отображения) setTasks(pv => ({ ...pv, rows: [...rows] })); handleStateChange(row.nrn, destCode); } } }, [handleStateChange, tasks.rows, tasks.statuses] ); //Перезагружать при изменении фильтра или сортировки useEffect(() => { filters || orders ? handleReload() : null; }, [filters, orders, handleReload]); useEffect(() => { //Считывание данных с учетом фильтрации let getTasks = async () => { const ds = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DATASET", args: { CFILTERS: { VALUE: object2Base64XML(filters.fArray, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, CORDERS: { VALUE: object2Base64XML(orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, NINCLUDE_DEF: tasks.tasksLoaded ? 0 : 1 }, respArg: "COUT" }); //Инициализируем статусы и события let newGroups = []; let newRows = []; //Если статусы есть if (ds.XDATA_GRID.groups) { //Формируем структуру статусов arrayFormer(ds.XDATA_GRID.groups).map((group, i) => { newGroups.push({ id: i, code: group.name, name: group.caption, color: randomColor(i + 1) }); }); //Если есть события if (ds.XDATA_GRID.rows) { //Формируем структуру событий arrayFormer(ds.XDATA_GRID.rows).map((task, i) => { newRows.push(initTask(i, newGroups.find(x => x.name === task.groupName).id, task)); }); } } //Возвращаем информацию return { statuses: [...newGroups], rows: [...newRows] }; }; //Считывание данных let getData = async () => { //Считываем информацию о задачах let eventTasks = await getTasks(); //Добавление описания точки маршрута eventTasks.statuses.map(s => (s["pointDescr"] = extraData.evPoints.find(ep => ep.point === s.code).pointDescr)); //Загружаем данные setTasks(pv => ({ ...pv, groupsLoaded: true, tasksLoaded: true, statuses: eventTasks.statuses, rows: eventTasks.rows, reload: false })); }; //Если необходимо загрузить данные и указан тип событий и загружены все необходимые вспомогательные данные if ( tasks.reload && filters.loaded && filters.values.type && extraData.dataLoaded && filters.values.type === extraData.typeLoaded && extraData.evPoints.length ) { //Загружаем данные getData(); } }, [ tasks.reload, filters.values.type, filters.fArray, orders, executeStored, SERV_DATA_TYPE_CLOB, tasks.tasksLoaded, extraData, getDocLinks, filters.loaded ]); return [tasks, handleReload, onDragEnd, needUpdateTasks]; }; //Хук дополнительных данных const useExtraData = filtersType => { //Состояние дополнительных данных const [extraData, setExtraData] = useState({ dataLoaded: false, typeLoaded: "", evRoutes: [], evPoints: [], noteTypes: [], docLinks: [], accounts: [] }); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); //Надо обновить данные const needUpdateExtraData = () => setExtraData(pv => ({ ...pv, dataLoaded: false })); //Получение учётных документов const getDocLinks = useCallback( async (type = filtersType) => { const data = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DOCLINKS", args: { SEVNTYPE_CODE: type }, respArg: "COUT" }); //Инициализируем учётные документы let newDocLinks = []; //Если найдены учётные документы if (data.XDOCLINKS) { arrayFormer(data.XDOCLINKS).map(d => { newDocLinks.push({ id: d.NRN, descr: d.SDESCR }); }); } //Возвращаем сформированные учётные документы return newDocLinks; }, [executeStored, filtersType] ); useEffect(() => { //Получение вспомогательных данных const getExtraData = async () => { const data = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INFO_BY_CODE", args: { SEVNTYPE_CODE: filtersType }, respArg: "COUT" }); //Инициализируем маршруты событий let newRoutes = data.XEVROUTES ? arrayFormer(data.XEVROUTES).reduce((prev, cur) => { prev.push({ src: cur.SSOURCE, dest: cur.SDESTINATION }); return prev; }, []) : []; //Инициализируем точки событий let newPoints = data.XEVPOINTS ? arrayFormer(data.XEVPOINTS).reduce((prev, cur) => { prev.push({ point: cur.SEVPOINT, pointDescr: cur.SEVPOINT_DESCR, addNoteOnChst: cur.ADDNOTE_ONCHST, addNoteOnSend: cur.ADDNOTE_ONSEND, banUpdate: cur.BAN_UPDATE }); return prev; }, []) : []; //Инициализируем типы заголовков примечаний let newNoteTypes = data.XNOTETYPES ? arrayFormer(data.XNOTETYPES).reduce((prev, cur) => { prev.push(cur.SNAME); return prev; }, []) : []; //Инициализируем пользователей let newAccounts = data.XACCOUNTS ? arrayFormer(data.XACCOUNTS).reduce((prev, cur) => { prev.push({ agnAbbr: cur.SAGNABBR, image: cur.BIMAGE }); return prev; }, []) : []; //Загружаем учётные документы let docLinks = await getDocLinks(filtersType); //Возвращаем результат return { dataLoaded: true, typeLoaded: filtersType, evRoutes: [...newRoutes], evPoints: [...newPoints], noteTypes: [...newNoteTypes], docLinks: [...docLinks], accounts: [...newAccounts] }; }; //Считывание данных const updateExtraData = async () => { let newExtraData = await getExtraData(); setExtraData(newExtraData); }; //Если указан тип событий if (filtersType) { //Загружаем дополнительные данные if (!extraData.typeLoaded || filtersType !== extraData.typeLoaded) { //setExtraData(pv => ({ ...pv, dataLoaded: false })); updateExtraData(); } } }, [executeStored, extraData.typeLoaded, filtersType, getDocLinks]); return [extraData, getDocLinks, needUpdateExtraData]; }; //Хук для получения пользовательских настроек разметки const useColorRules = () => { //Собственное состояние const [clrRules, setClrRules] = useState({ loaded: false, rules: [] }); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); useEffect(() => { let getClrRules = async () => { //Получаем массив пользовательских настроек const data = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DP_RULES_GET", respArg: "COUT" }); //Инициализируем let newClrRules = []; if (data) { //Формируем структуру настройки arrayFormer(data.XRULES).map((cr, i) => { let fromV; let toV; if (cr.STYPE === "number") { fromV = cr.NFROM; toV = cr.NTO; } else if (cr.STYPE === "string") { fromV = cr.SFROM; toV = cr.STO; } else { fromV = cr.DFROM; toV = cr.DTO; } newClrRules.push({ id: i, fieldCode: cr.SFIELD, propName: cr.SDP_NAME, color: cr.SCOLOR, vType: cr.STYPE, from: fromV, to: toV }); }); setClrRules({ loaded: true, rules: [...newClrRules] }); } }; if (!clrRules.loaded) getClrRules(); }, [clrRules.loaded, executeStored]); return [clrRules]; }; //Хук дополнительных настроек const useSettings = statuses => { //Собственное состояние const [settings, setSettings] = useState({ statusesSort: { sorted: false, attr: localStorage.getItem("settingsSortAttr") ? localStorage.getItem("settingsSortAttr") : "name", dest: localStorage.getItem("settingsSortDest") ? localStorage.getItem("settingsSortDest") : "asc", statuses: [] }, colorRule: localStorage.getItem("settingsColorRule") ? JSON.parse(localStorage.getItem("settingsColorRule")) : {} }); //Изменение состояния после сортировки const afterSort = statuses => setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, sorted: true, statuses: statuses } })); //При закрытии диалога дополнительных настроек по кнопке ОК const handleSettingsChange = s => { setSettings({ ...s, statusesSort: { ...s.statusesSort, sorted: false } }); }; //При получении новых настроек сортировки useEffect(() => { //Подгрузкка новых статусов if (statuses.length > 0 && statuses.toString() !== settings.statusesSort.statuses.toString() && settings.statusesSort.sorted) setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, sorted: false } })); //Сортировка if (statuses.length > 0 && !settings.statusesSort.sorted) { const attr = settings.statusesSort.attr; const d = settings.statusesSort.dest; let s = statuses; s.sort((a, b) => (d === "asc" ? a[attr].localeCompare(b[attr]) : b[attr].localeCompare(a[attr]))); afterSort(s); } }, [settings.statusesSort.attr, settings.statusesSort.dest, settings.statusesSort.sorted, settings.statusesSort.statuses, statuses]); //Сохранение при закрытии панели useEffect(() => { window.addEventListener("beforeunload", function () { localStorage.setItem("settingsSortAttr", settings.statusesSort.attr); localStorage.setItem("settingsSortDest", settings.statusesSort.dest); localStorage.setItem("settingsColorRule", JSON.stringify(settings.colorRule)); }); }, [settings.colorRule, settings.statusesSort.attr, settings.statusesSort.dest]); return [settings, handleSettingsChange]; }; //---------------- //Интерфейс модуля //---------------- export { useExtraData, useTasks, useSettings, useColorRules };