forked from CITKParus/P8-Panels
532 lines
24 KiB
JavaScript
532 lines
24 KiB
JavaScript
/*
|
||
Парус 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 };
|