1455 lines
65 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Парус 8 - Панели мониторинга - Редактор настройки регламентированного отчёта
Пользовательские хуки
*/
//---------------------
//Подключение библиотек
//---------------------
import { useState, useContext, useEffect, useCallback } from "react"; //Классы React
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
import { object2Base64XML, deepCopyObject } from "../../core/utils"; //Вспомогательные функции
import { EVENT_STATES } from "./components/filter_dialog"; //Перечисление состояний события
//---------
//Константы
//---------
//Цвета статусов
export const COLORS = [
"mediumSlateBlue",
"lightSalmon",
"fireBrick",
"orange",
"gold",
"limeGreen",
"yellowGreen",
"mediumAquaMarine",
"paleTurquoise",
"steelBlue",
"skyBlue",
"tan"
];
//---------------------------------------------
//Вспомогательные функции форматирования данных
//---------------------------------------------
//Формирование случайного цвета
const randomColor = index => {
const hue = index * 137.508;
return hslToRgba(hue, 50, 70);
};
//Цвет из hsl формата в rgba формат
const hslToRgba = (h, s, l) => {
s /= 100;
l /= 100;
const k = n => (n + h / 30) % 12;
const a = s * Math.min(l, 1 - l);
const f = n => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
return `rgba(${Math.floor(255 * f(0))},${Math.floor(255 * f(8))},${Math.floor(255 * f(4))},0.3)`;
};
//Формирование массива из 0, 1 и 1< элементов
export const arrayFormer = arr => {
return arr ? (arr.length ? arr : [arr]) : [];
};
//-----------
//Тело модуля
//-----------
//Хук основных данных
const useTasks = () => {
//Состояние открытия формы события
const [taskFormOpen, setTaskFormOpen] = useState(false);
//Состояние изменения настройки статуса
const [cardSettings, setCardSettings] = useState({ isOpen: false, settings: {} });
//Состояние событий
const [tasks, setTasks] = useState({
groupsLoaded: false,
tasksLoaded: false,
orders: [],
filters: {
isOpen: true,
isSetByUser: false,
needSave: false,
values: {
evState: EVENT_STATES[1],
type: "",
catalog: "",
crn: "",
wSubcatalogs: false,
sendPerson: "",
sendDivision: "",
sendUsrGrp: "",
docLink: ""
},
fArray: [
{ name: "NCLOSED", from: 0, to: 1 },
{ name: "SEVTYPE_CODE", from: "", to: "" },
{ name: "NCRN", from: "", to: "" },
{ name: "SSEND_PERSON", from: "", to: "" },
{ name: "SSEND_DIVISION", from: "", to: "" },
{ name: "SSEND_USRGRP", from: "", to: "" },
{ name: "NLINKED_RN", from: "", to: "" }
]
},
extraData: { typeLoaded: "", evRoutes: [], evPoints: [], noteTypes: [], docLinks: [], accounts: [] },
rows: [],
statuses: [],
openCardForm: false,
reload: true
});
//Подключение к контексту взаимодействия с сервером
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
//Подключение к контексту приложения
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
//Инициализация параметров события
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 handleFilterClick = () => setFilterOpen(true);
//При изменении фильтра в диалоге
const handleFilterOk = filter => {
setFilterValues(filter);
setFilterOpen(false);
};
//При закрытии диалога фильтра
const handleFilterCancel = () => setFilterOpen(false);
//Установить значение фильтра
const setFilterValues = (values, ns = true) => {
//Считываем массив фильтров
let filterArr = tasks.filters.fArray.slice();
//Состояние
if (values.evState) {
if (values.evState === EVENT_STATES[0]) {
filterArr.find(f => f.name === "NCLOSED").from = 0;
filterArr.find(f => f.name === "NCLOSED").to = 1;
} else if (values.evState === EVENT_STATES[1]) {
filterArr.find(f => f.name === "NCLOSED").from = 0;
filterArr.find(f => f.name === "NCLOSED").to = 0;
} else if (values.evState === EVENT_STATES[2]) {
filterArr.find(f => f.name === "NCLOSED").from = 1;
filterArr.find(f => f.name === "NCLOSED").to = 1;
}
}
//Тип
filterArr.find(f => f.name === "SEVTYPE_CODE").from = values.type ? values.type : null;
//Каталог
filterArr.find(f => f.name === "NCRN").from = values.crn ? values.crn : null;
//Исполнитель
filterArr.find(f => f.name === "SSEND_PERSON").from = values.sendPerson ? values.sendPerson : null;
//Подразделение
filterArr.find(f => f.name === "SSEND_DIVISION").from = values.sendDivision ? values.sendDivision : null;
//Группа пользователей
filterArr.find(f => f.name === "SSEND_USRGRP").from = values.sendUsrGrp ? values.sendUsrGrp : null;
//Учётный документ
filterArr.find(f => f.name === "NLINKED_RN").from = values.docLink ? values.docLink : null;
//Устанавливаем фильтры
setTasks(pv => ({
...pv,
filters: { ...pv.filters, isSetByUser: true, needSave: ns, values: { ...values }, fArray: [...filterArr] },
reload: true
}));
};
//Загрузка значений фильтра из локального хранилища браузера
const loadLocalFilter = useCallback(async () => {
let vs = { ...tasks.filters.values };
if (localStorage.getItem("type")) {
Object.keys(vs).map(function (k) {
if (k === "wSubcatalogs") vs[k] = localStorage.getItem(k) === "true";
else k !== "docLink" ? (vs[k] = localStorage.getItem(k)) : null;
});
setFilterValues(vs, false);
setFilterOpen(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//При закрытии панели
useEffect(() => {
tasks.filters.needSave
? window.addEventListener("beforeunload", function () {
Object.keys(tasks.filters.values).map(function (k) {
k !== "docLink" ? localStorage.setItem(k, tasks.filters.values[k] ? tasks.filters.values[k] : "") : null;
});
})
: null;
}, [tasks.filters.needSave, tasks.filters.values]);
//При отсутствии пользовательских настроек фильтра
useEffect(() => {
if (!tasks.filters.isSetByUser) setFilterOpen(true);
}, [tasks.filters.isSetByUser]);
//При подключении к странице
useEffect(() => {
localStorage.length > 0 ? loadLocalFilter() : null;
}, [loadLocalFilter]);
//Показать/скрыть фильтр
const setFilterOpen = isOpen => {
setTasks(pv => ({ ...pv, filters: { ...pv.filters, isOpen } }));
};
//Открытие настройки статуса
const handleCardSettingsClick = curSettings => {
setCardSettings({ isOpen: true, settings: { ...curSettings } });
};
//Закрытие настройки статуса
const handleCardSettingsCancel = () => setCardSettings(pv => ({ ...pv, isOpen: false }));
//Применение настройки статуса
const handleCardSettingsOk = settings => {
//Считываем статусы
let cloneS = tasks.statuses.slice();
//Изменяем статус у выбранного
cloneS[tasks.statuses.findIndex(x => x.id === settings.id)] = { ...settings };
setTasks(pv => ({ ...pv, statuses: cloneS }));
setCardSettings({ isOpen: false, settings: {} });
};
//При изменении сортировки
const handleOrderChanged = useCallback(
columnName => {
let newOrders = deepCopyObject(tasks.orders);
const colOrder = newOrders.find(o => o.name == columnName);
const newDirection = colOrder?.direction == "ASC" ? "DESC" : colOrder?.direction == "DESC" ? null : "ASC";
if (newDirection == null && colOrder) newOrders.splice(newOrders.indexOf(colOrder), 1);
if (newDirection != null && !colOrder) newOrders.push({ name: columnName, direction: newDirection });
if (newDirection != null && colOrder) colOrder.direction = newDirection;
setTasks(pv => ({ ...pv, orders: newOrders, reload: true }));
},
[tasks.orders]
);
//Изменение статуса события (переносом)
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]
);
//Получение учётных документов
const getDocLinks = useCallback(
async (type = tasks.filters.values.type) => {
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, tasks.filters.values.type]
);
useEffect(() => {
//Считывание данных с учетом фильтрации
let getTasks = async () => {
const ds = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DATASET",
args: {
CFILTERS: { VALUE: object2Base64XML(tasks.filters.fArray, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
CORDERS: { VALUE: object2Base64XML(tasks.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 getEventData = async () => {
const data = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INFO_BY_CODE",
args: {
SEVNTYPE_CODE: tasks.filters.values.type
},
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(tasks.filters.values.type);
//Возвращаем результат
return {
typeLoaded: tasks.filters.values.type,
evRoutes: [...newRoutes],
evPoints: [...newPoints],
noteTypes: [...newNoteTypes],
docLinks: [...docLinks],
accounts: [...newAccounts]
};
};
//Считывание данных
let getData = async () => {
//Инициализируем информацию о типе событии
let eventData = { ...tasks.extraData };
//Если необходимо обновить информацию о типе события
if (!tasks.extraData.typeLoaded || tasks.filters.values.type !== tasks.extraData.typeLoaded) {
//Загружаем информацию о типе события
eventData = await getEventData();
}
//Считываем информацию о задачах
let eventTasks = await getTasks();
//Добавление описания точки маршрута
eventTasks.statuses.map(s => (s["pointDescr"] = eventData.evPoints.find(ep => ep.point === s.code).pointDescr));
//Загружаем данные
setTasks(pv => ({
...pv,
groupsLoaded: true,
tasksLoaded: true,
statuses: eventTasks.statuses,
rows: eventTasks.rows,
extraData: eventData,
reload: false
}));
};
//Если необходимо загрузить данные и указан тип событий
if (tasks.reload && tasks.filters.values.type) {
//Загружаем данные
getData();
}
}, [
tasks.reload,
tasks.filters.values.type,
tasks.orders,
executeStored,
SERV_DATA_TYPE_CLOB,
tasks.filters.fArray,
tasks.tasksLoaded,
tasks.extraData,
getDocLinks
]);
return [
tasks,
taskFormOpen,
setTaskFormOpen,
cardSettings,
handleFilterOk,
handleFilterCancel,
handleFilterClick,
handleCardSettingsClick,
handleCardSettingsOk,
handleCardSettingsCancel,
handleReload,
onDragEnd,
handleOrderChanged,
getDocLinks
];
};
//Хук для получения пользовательских настроек разметки
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 useDocsProps = taskType => {
//Собственное состояние
const [docProps, setDocsProps] = useState({ loaded: false, props: [] });
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
useEffect(() => {
let getDocsProps = async () => {
//Получаем массив пользовательских настроек
const data = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_PROPS_GET",
args: { SEVNTYPE_CODE: taskType },
respArg: "COUT"
});
//Инициализируем
let newDocProps = [];
if (data) {
//Формируем структуру настройки
arrayFormer(data.XPROPS).map((dp, i) => {
newDocProps.push({
id: i,
rn: dp.NRN,
name: dp.SNAME,
readonly: dp.READONLY,
checkValue: dp.CHECK_VALUE,
checkUnique: dp.CHECK_UNIQUE,
require: dp.REQUIRE,
duplicateValue: dp.DUPLICATE_VALUE,
accessMode: dp.NACCESS_MODE,
showInGrid: dp.SHOW_IN_GRID,
defaultStr: dp.SDEFAULT_STR,
defaultNum: dp.NDEFAULT_NUM,
defaultDate: dp.DDEFAULT_DATE,
entryType: dp.NENTRY_TYPE,
format: dp.NFORMAT,
dataSubtype: dp.NDATA_SUBTYPE,
numWidth: dp.NNUM_WIDTH,
numPrecision: dp.NNUM_PRECISION,
strWidth: dp.NSTR_WIDTH,
unitcode: dp.SUNITCODE,
paramRn: dp.NPARAM_RN,
paramIn: dp.SPARAM_IN_CODE,
paramOut: dp.SPARAM_OUT_CODE,
showMethodRn: dp.NSHOW_METHOD_RN,
showMethodCode: dp.SMETHOD_CODE,
extraDictRn: dp.NEXTRA_DICT_RN,
initRn: dp.NINIT_RN
});
});
setDocsProps({ loaded: true, props: [...newDocProps] });
}
};
if (!docProps.loaded) getDocsProps();
}, [docProps.loaded, executeStored, taskType]);
return [docProps];
};
//Хук для события
const useClientEvent = (taskRn, taskType = "", taskStatus = "") => {
//Собственное состояние
const [task, setTask] = useState({
init: true,
nrn: taskRn,
scrn: "",
sprefix: "",
snumber: "",
stype: taskType,
sstatus: taskStatus,
sdescription: "",
sclnt_clnclients: "",
sclnt_clnperson: "",
dstart_date: "",
sinit_clnperson: "",
sinit_user: "",
sinit_reason: "",
sto_company: "",
sto_department: "",
sto_clnpost: "",
sto_clnpsdep: "",
sto_clnperson: "",
sto_fcstaffgrp: "",
sto_user: "",
sto_usergrp: "",
scurrent_user: "",
isUpdate: false,
insertDisabled: true,
updateDisabled: true,
docProps: {}
});
//Подключение к контексту взаимодействия с сервером
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
//Подключение к контексту приложения
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
const initEventType = useCallback(async () => {
//Считываем параметры исходя из типа события
const data = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVNTYPES_INIT",
args: {
SEVENT_TYPE: task.stype,
SCURRENT_PREF: task.sprefix
},
tagValueProcessor: () => undefined
});
if (data) {
setTask(pv => ({
...pv,
sprefix: data.SPREF,
snumber: data.SNUMB
}));
}
}, [task.sprefix, task.stype, executeStored]);
//Отображение раздела "Клиенты"
const handleClientClientsOpen = useCallback(async () => {
pOnlineShowDictionary({
unitCode: "ClientClients",
showMethod: "main",
inputParameters: [{ name: "in_CLIENT_CODE", value: task.sclnt_clnclients }],
callBack: res => {
res.success
? setTask(pv => ({
...pv,
sclnt_clnclients: res.outParameters.out_CLIENT_CODE,
sclnt_clnperson: ""
}))
: null;
}
});
}, [pOnlineShowDictionary, task.sclnt_clnclients]);
//Отображение раздела "Сотрудники"
const handleClientPersonOpen = useCallback(
//Тип открытия (0 - для клиента, 1 - для инициатора)
async (nType = 0) => {
pOnlineShowDictionary({
unitCode: "ClientPersons",
showMethod: "main",
inputParameters: [{ name: "in_CODE", value: nType === 0 ? task.sclnt_clnperson : task.sinit_clnperson }],
callBack: res => {
if (res.success) {
if (nType === 0) {
setTask(pv => ({
...pv,
sclnt_clnperson: res.outParameters.out_CODE,
sclnt_clnclients: ""
}));
} else {
setTask(pv => ({
...pv,
sinit_clnperson: res.outParameters.out_CODE
}));
}
}
}
});
},
[pOnlineShowDictionary, task.sclnt_clnperson, task.sinit_clnperson]
);
//Отображение раздела "Каталоги" для событий
const handleCrnOpen = useCallback(async () => {
pOnlineShowDictionary({
unitCode: "CatalogTree",
showMethod: "main",
inputParameters: [
{ name: "in_DOCNAME", value: "ClientEvents" },
{ name: "in_NAME", value: task.scrn }
],
callBack: res => {
res.success
? setTask(pv => ({
...pv,
scrn: res.outParameters.out_NAME
}))
: null;
}
});
}, [pOnlineShowDictionary, task.scrn]);
//Считывание следующего номера события
const getEventNextNumb = useCallback(async () => {
const data = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_NEXTNUMB_GET",
args: {
SPREFIX: task.sprefix
}
});
if (data) {
setTask(pv => ({
...pv,
snumber: data.SEVENT_NUMB
}));
}
}, [executeStored, task.sprefix]);
//Добавление события
const insertEvent = useCallback(
async callBack => {
await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INSERT",
args: {
SCRN: task.scrn,
SPREF: task.sprefix,
SNUMB: task.snumber,
STYPE: task.stype,
SSTATUS: task.sstatus,
SPLAN_DATE: task.dplan_date, // ? dayjs(task.dplan_date).format("DD.MM.YYYY HH:mm") : null,
SINIT_PERSON: task.sinit_clnperson,
SCLIENT_CLIENT: task.sclnt_clnclients,
SCLIENT_PERSON: task.sclnt_clnperson,
SDESCRIPTION: task.sdescription,
SREASON: task.sinit_reason,
CPROPS: {
VALUE: object2Base64XML(
[
Object.fromEntries(
Object.entries(task.docProps)
// eslint-disable-next-line no-unused-vars
.filter(([_, v]) => v != (null || ""))
)
],
{
arrayNodeName: "props"
}
),
SDATA_TYPE: SERV_DATA_TYPE_CLOB
}
}
});
callBack();
},
[
executeStored,
task.scrn,
task.sprefix,
task.snumber,
task.stype,
task.sstatus,
task.dplan_date,
task.sinit_clnperson,
task.sclnt_clnclients,
task.sclnt_clnperson,
task.sdescription,
task.sinit_reason,
task.docProps,
SERV_DATA_TYPE_CLOB
]
);
//Исправление события
const updateEvent = useCallback(
async callBack => {
await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_UPDATE",
args: {
NCLNEVENTS: task.nrn,
SCLIENT_CLIENT: task.sclnt_clnclients,
SCLIENT_PERSON: task.sclnt_clnperson,
SDESCRIPTION: task.sdescription,
CPROPS: {
// eslint-disable-next-line no-unused-vars
VALUE: object2Base64XML([Object.fromEntries(Object.entries(task.docProps).filter(([_, v]) => v != (null || "")))], {
arrayNodeName: "props"
}),
SDATA_TYPE: SERV_DATA_TYPE_CLOB
}
}
});
callBack();
},
[SERV_DATA_TYPE_CLOB, executeStored, task.docProps, task.nrn, task.sclnt_clnclients, task.sclnt_clnperson, task.sdescription]
);
useEffect(() => {
if (task.init) {
if (taskRn) {
//Считывание параметров события
const readEvent = async () => {
const data = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_GET",
args: {
NCLNEVENTS: task.nrn
},
respArg: "COUT"
});
let newDocProps = {};
Object.keys(data.XEVENT)
.filter(k => k.includes("DP_"))
.map(dp => (newDocProps = { ...newDocProps, [dp]: data.XEVENT[dp] }));
setTask(pv => ({
...pv,
scrn: data.XEVENT.SCRN,
sprefix: data.XEVENT.SPREF,
snumber: data.XEVENT.SNUMB,
stype: data.XEVENT.STYPE,
sstatus: data.XEVENT.SSTATUS,
sdescription: data.XEVENT.SDESCRIPTION,
sclnt_clnclients: data.XEVENT.SCLIENT_CLIENT,
sclnt_clnperson: data.XEVENT.SCLIENT_PERSON,
dplan_date: data.XEVENT.SPLAN_DATE,
sinit_clnperson: data.XEVENT.SINIT_PERSON,
sinit_user: data.XEVENT.SINIT_AUTHID,
sinit_reason: data.XEVENT.SREASON,
sto_company: data.XEVENT.SSEND_CLIENT,
sto_department: data.XEVENT.SSEND_DIVISION,
sto_clnpost: data.XEVENT.SSEND_POST,
sto_clnpsdep: data.XEVENT.SSEND_PERFORM,
sto_clnperson: data.XEVENT.SSEND_PERSON,
sto_fcstaffgrp: data.XEVENT.SSEND_STAFFGRP,
sto_user: data.XEVENT.SSEND_USER_NAME,
sto_usergrp: data.XEVENT.SSEND_USER_GROUP,
scurrent_user: data.XEVENT.SINIT_AUTHID,
isUpdate: true,
init: false,
docProps: newDocProps
}));
};
//Инициализация параметров события
readEvent();
} else {
//Считывание изначальных параметров события
const initEvent = async () => {
const data = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INIT",
args: {}
});
if (data) {
setTask(pv => ({
...pv,
sprefix: data.SPREF,
snumber: data.SNUMB,
scurrent_user: data.SINIT_AUTHNAME,
sinit_clnperson: data.SINIT_PERSON,
sinit_user: !data.SINIT_PERSON ? data.SINIT_AUTHNAME : "",
init: false
}));
}
};
//Инициализация изначальных параметров события
initEvent();
initEventType();
}
}
if (!task.init) {
setTask(pv => ({ ...pv, sinit_user: !task.sinit_clnperson ? task.scurrent_user : "" }));
}
}, [executeStored, task.init, task.nrn, task.stype, task.scurrent_user, task.sinit_clnperson, taskRn, initEventType]);
//Проверка доступности действия
useEffect(() => {
setTask(pv => ({
...pv,
insertDisabled:
!task.scrn ||
!task.sprefix ||
!task.snumber ||
!task.stype ||
!task.sstatus ||
!task.sdescription ||
(!task.sinit_clnperson && !task.sinit_user),
updateDisabled: !task.sdescription
}));
}, [task.scrn, task.sdescription, task.sinit_clnperson, task.sinit_user, task.snumber, task.sprefix, task.sstatus, task.stype]);
return [task, setTask, insertEvent, updateEvent, handleClientClientsOpen, handleClientPersonOpen, handleCrnOpen, getEventNextNumb];
};
//Хук дополнительныч настроек
const useSettings = statuses => {
//Собственное состояние
const [settings, setSettings] = useState({
open: false,
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 settingsOpen = () => setSettings(pv => ({ ...pv, open: true }));
//При закрытии диалога дополнительных настроек
const settingsClose = () => setSettings(pv => ({ ...pv, open: false }));
//Изменение состояния после сортировки
const afterSort = statuses => setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, sorted: true, statuses: statuses } }));
//При закрытии диалога дополнительных настроек по кнопке ОК
const handleSettingsOk = s => {
setSettings({ ...s, open: false, 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, settingsOpen, settingsClose, handleSettingsOk];
};
//Хук карточки события
const useTaskCard = () => {
//Собственное состояние
const [taskCard, setTaskCard] = useState({ openEdit: false });
//Состояние действий
const [cardActions, setCardActions] = useState({ anchorMenuMethods: null, openMethods: false });
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Подключение к контексту сообщений
const { showMsgWarn } = useContext(MessagingСtx);
//Подключение к контексту приложения
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
//Удаление контрагента
const deleteTask = useCallback(
async (nEvent, handleReload) => {
await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DELETE",
args: { NCLNEVENTS: nEvent }
});
//Если требуется перезагрузить данные
if (handleReload) {
handleReload();
}
},
[executeStored]
);
//Возврат в предыдущую точку события
const returnTask = useCallback(
async (nEvent, handleReload) => {
await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_RETURN",
args: { NCLNEVENTS: nEvent }
});
//Если требуется перезагрузить данные
if (handleReload) {
handleReload();
}
},
[executeStored]
);
//По нажатию на открытие меню действий
const handleMethodsMenuButtonClick = event => {
setCardActions(pv => ({ ...pv, anchorMenuMethods: event.currentTarget, openMethods: true }));
};
//При закрытии меню
const handleMethodsMenuClose = () => {
setCardActions(pv => ({ ...pv, anchorMenuMethods: null, openMethods: false }));
};
//По нажатия действия "Редактировать"
const handleTaskEdit = () => {
setTaskCard(pv => ({ ...pv, openEdit: true }));
};
//По нажатия действия "Редактировать в разделе"
const handleTaskEditClient = useCallback(
async nEvent => {
const data = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.SELECT_CLNEVENT",
args: {
NRN: nEvent
}
});
if (data.NIDENT) {
pOnlineShowDictionary({
unitCode: "ClientEvents",
inputParameters: [{ name: "in_Ident", value: data.NIDENT }]
});
}
},
[executeStored, pOnlineShowDictionary]
);
//По нажатию действия "Удалить"
const handleTaskDelete = (nEvent, handleReload) => {
showMsgWarn("Удалить событие?", () => deleteTask(nEvent, handleReload));
};
//По нажатию действия "Выполнить возврат"
const handleTaskReturn = (nEvent, handleReload) => {
showMsgWarn("Выполнить возврат события в предыдущую точку?", () => returnTask(nEvent, handleReload));
};
//По нажатию действия "Примечания"
const handleEventNotesOpen = useCallback(
async nEvent => {
pOnlineShowDictionary({
unitCode: "ClientEventsNotes",
showMethod: "main",
inputParameters: [{ name: "in_PRN", value: nEvent }]
});
},
[pOnlineShowDictionary]
);
//По нажатию действия "Присоединенные документы"
const handleFileLinksOpen = useCallback(
async nEvent => {
pOnlineShowDictionary({
unitCode: "FileLinks",
showMethod: "main_link",
inputParameters: [
{ name: "in_PRN", value: nEvent },
{ name: "in_UNITCODE", value: "ClientEvents" }
]
});
},
[pOnlineShowDictionary]
);
//По нажатию действия "Перейти"
const handleStateChange = useCallback(
async (nEvent, handleReload, evPoints, handleNote) => {
//Выполняем инициализацию параметров
const firstStep = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
args: {
NSTEP: 1,
NEVENT: nEvent
}
});
if (firstStep) {
//Открываем раздел "Маршруты событий (точки перехода)" для выбора следующей точки
pOnlineShowDictionary({
unitCode: "EventRoutesPointsPasses",
showMethod: "main_passes",
inputParameters: [
{ name: "in_ENVTYPE_CODE", value: firstStep.SEVENT_TYPE },
{ name: "in_ENVSTAT_CODE", value: firstStep.SEVENT_STAT },
{ name: "in_POINT", value: firstStep.NPOINT }
],
callBack: async point => {
//Выполняем проверку необходимости выбора исполнителя
const secondStep = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
args: {
NIDENT: firstStep.NIDENT,
NSTEP: 2,
NPASS: point.outParameters.out_RN
}
});
const pointSettings = evPoints.find(ep => ep.point === point.outParameters.out_NEXT_POINT);
if (secondStep) {
//Если требуется выбрать получателя
if (secondStep.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: point.outParameters.out_NEXT_POINT,
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
};
//Выполняем переход к выбранной точке с исполнителем
pointSettings.addNoteOnChst
? handleNote(async n => {
//Если требуется примечание
await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
args: {
...mainArgs,
...{ SNOTE_HEADER: n.header, SNOTE: n.text }
}
});
//Если требуется перезагрузить данные
if (handleReload) {
handleReload();
}
})
: await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
args: mainArgs
});
//Если требуется перезагрузить данные
if (handleReload && !pointSettings.addNoteOnChst) {
handleReload();
}
}
});
} else {
//Общие аргументы
const mainArgs = {
NIDENT: firstStep.NIDENT,
NSTEP: 3,
NEVENT: nEvent,
SEVENT_STAT: point.outParameters.out_NEXT_POINT
};
//Выполняем переход к выбранной точке с предопределенным исполнителем
pointSettings.addNoteOnChst
? handleNote(async n => {
//Если требуется примечание
await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
args: {
...mainArgs,
...{ SNOTE_HEADER: n.header, SNOTE: n.text }
}
});
//Если требуется перезагрузить данные
if (handleReload) {
handleReload();
}
})
: await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
args: mainArgs
});
//Если требуется перезагрузить данные
if (handleReload && !pointSettings.addNoteOnChst) {
handleReload();
}
}
}
}
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[executeStored, pOnlineShowDictionary]
);
//Изменение статуса события
const handleSend = useCallback(
async (nEvent, handleReload, note = null) => {
//Выполняем инициализацию параметров
const firstStep = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND",
args: {
NSTEP: 1,
NEVENT: nEvent
}
});
if (firstStep) {
//Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя
pOnlineShowDictionary({
unitCode: "EventRoutesPointExecuters",
showMethod: "executers",
inputParameters: [
{ name: "in_IDENT", value: firstStep.NIDENT },
{ name: "in_EVENT", value: nEvent },
{ name: "in_PERSON_CODE", value: firstStep.SSEND_PERSON },
{ name: "in_USER_NAME", value: firstStep.SSEND_USER_NAME },
{ 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 => {
//Выполняем проверку необходимости выбора исполнителя
await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND",
args: {
NIDENT: firstStep.NIDENT,
NSTEP: 2,
NEVENT: nEvent,
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,
SNOTE_HEADER: note.text ? note.header : null,
SNOTE: note.text ? note.text : null
}
});
//Если требуется перезагрузить данные
if (handleReload) {
handleReload();
}
}
});
}
},
[executeStored, pOnlineShowDictionary]
);
//Формируем меню показателей
const menuItems = [
{ method: "EDIT", name: "Исправить", icon: "edit", visible: false, delimiter: false, needReload: false, func: handleTaskEdit },
{
method: "EDIT_CLIENT",
name: "Исправить в разделе",
icon: "edit_note",
visible: true,
delimiter: false,
needReload: false,
func: handleTaskEditClient
},
{ method: "DELETE", name: "Удалить", icon: "delete", visible: true, delimiter: true, needReload: true, func: handleTaskDelete },
{
method: "TASK_STATE_CHANGE",
name: "Перейти",
icon: "turn_right",
visible: true,
delimiter: false,
needReload: true,
func: handleStateChange
},
{
method: "TASK_RETURN",
name: "Выполнить возврат",
icon: "turn_left",
visible: true,
delimiter: false,
needReload: true,
func: handleTaskReturn
},
{ method: "TASK_SEND", name: "Направить", icon: "send", visible: true, delimiter: true, needReload: true, func: handleSend },
{ method: "NOTES", name: "Примечания", icon: "event_note", visible: true, delimiter: true, needReload: false, func: handleEventNotesOpen },
{
method: "FILE_LINKS",
name: "Присоединенные документы",
icon: "attach_file",
visible: true,
delimiter: false,
needReload: false,
func: handleFileLinksOpen
}
];
return [taskCard, setTaskCard, cardActions, handleMethodsMenuButtonClick, handleMethodsMenuClose, menuItems];
};
//Хук для сортировок
const useOrders = () => {
//Состояние меню сортировки
const [menuOrders, setMenuOrders] = useState({ anchorMenuOrders: null, openOrders: false });
//По нажатию на открытие меню сортировки
const handleOrdersMenuButtonClick = event => {
setMenuOrders(pv => ({ ...pv, anchorMenuOrders: event.currentTarget, openOrders: true }));
};
//При закрытии меню
const handleOrdersMenuClose = () => {
setMenuOrders(pv => ({ ...pv, anchorMenuOrders: null, openOrders: false }));
};
return [menuOrders, handleOrdersMenuButtonClick, handleOrdersMenuClose];
};
export { useTasks, useSettings, useColorRules, useDocsProps, useClientEvent, useTaskCard, useOrders };