/* Парус 8 - Панели мониторинга - Редактор настройки регламентированного отчёта Пользовательские хуки */ //--------------------- //Подключение библиотек //--------------------- import { useState, useContext, useEffect, useCallback, useLayoutEffect } from "react"; //Классы React import { ApplicationСtx } from "../../context/application"; //Контекст приложения import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { NavigationCtx } from "../../context/navigation"; //Контекст навигации import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений import { STATUSES } from "./IUD/iud_form_dialog"; //Статусы диалогов import { TEXTS } from "../../../app.text"; //Тексты для ошибок //----------- //Тело модуля //----------- //Хук для отработки изменений ширины и высоты рабочей области окна const useWindowResize = () => { //Состояние размера рабочей области const [size, setSize] = useState([0, 0]); //При изменении размера useLayoutEffect(() => { const updateSize = () => { setSize([document.documentElement.clientWidth, document.documentElement.clientHeight]); }; window.addEventListener("resize", updateSize); updateSize(); return () => window.removeEventListener("resize", updateSize); }, []); //Вернём размеры return size; }; //Хук для настройки регламентированного отчета const useConf = (currentTab, handleSectionChange, order) => { //Собственное состояние - таблица данных const dataGrid = { rn: 0, code: "", name: "", dataLoaded: false, columnsDef: [], groups: [], rows: [], fixedHeader: false, fixedColumns: 0, reload: false }; //Собственное состояние const [rrpConf, setRrpConf] = useState({ docLoaded: false, sections: [], orderChanged: false, reload: true }); //Состояние массива данных разделов const [dataGrids] = useState([]); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); //Подключение к контексту навигации const { getNavigationSearch } = useContext(NavigationCtx); //При необходимости обновить const handleReload = useCallback(async () => { setRrpConf(pv => ({ ...pv, reload: true })); }, []); //Загрузка данных разделов регламентированного отчёта const loadData = useCallback( async () => { if (rrpConf.reload) { //Переменная номера раздела с фокусом let tabFocus = currentTab ? currentTab : 0; const data = await executeStored({ stored: "PKG_P8PANELS_RRPCONFED.RRPCONF_GET_SECTIONS", args: { NRN_RRPCONF: Number(getNavigationSearch().NRN), NROW_ORDER: Number(order.rowOrder), NCOL_ORDER: Number(order.columnOrder) }, respArg: "COUT" }); //Флаг первой загрузки данных let firstLoad = dataGrids.length == 0 ? true : false; //Копирование массива уже загруженных разделов let cloneDGs = dataGrids.slice(); //Массив из нескольких разделов и из одного const sections = data.SECTIONS ? (data.SECTIONS.length ? data.SECTIONS : [data.SECTIONS]) : []; //Заполнение очередного раздела по шаблону sections .sort((a, b) => a.SCODE - b.SCODE) .map(s => { let dg = {}; Object.assign(dg, dataGrid, { ...s.XDATA.XDATA_GRID, rn: s.NRN, code: s.SCODE, name: s.SNAME, delete_allow: s.NDELETE_ALLOW, dataLoaded: true, columnsDef: [...(s.XDATA.XDATA_GRID.columnsDef || [])], groups: [...(s.XDATA.XDATA_GRID.groups || [])], rows: [...(s.XDATA.XDATA_GRID.rows || [])], reload: false }); //Если раздел имеет составы показателей if (s.MARK_CNS.MARK_CN) { //Обходим строки раздела dg.rows.map(row => { //Цикл по ключам строки for (let key in row) { //Если это ключ для группы составов показателей if (key.match(/MARK_CNS_.*/)) { //Считываем рег. номер показателя let markRn = key.substring(9); //Переносим из раздела row[key] = Array.isArray(s.MARK_CNS.MARK_CN) ? [...s.MARK_CNS.MARK_CN].filter(el => el.NPRN === row[`NMARK_RN_${markRn}`]) : s.MARK_CNS.MARK_CN.NPRN === row[`NMARK_RN_${markRn}`] ? [s.MARK_CNS.MARK_CN] : null; } } }); } //Ищем загружен ли уже раздел с таким же ид. const dgItem = dataGrids.find(x => x.rn === dg.rn); //Его индекс, если нет соответствия, то -1 let index = dataGrids.indexOf(dgItem); //Если было соответствие if (dgItem) { //Если в нём не найдено изменений if (JSON.stringify(dgItem, null, 4) === JSON.stringify(dg, null, 4)) { //То из копированного массива его удаляем cloneDGs.splice(cloneDGs.indexOf(cloneDGs.find(x => x.rn === dgItem.rn)), 1); } else { //Иначе обновляем раздел в массиве dataGrids[index] = dg; //Удаляем из копированного массива cloneDGs.splice(cloneDGs.indexOf(cloneDGs.find(x => x.rn === dg.rn)), 1); //Устанавливаем фокус на обновлённый раздел, если был добавлен tabFocus = rrpConf.orderChanged ? 0 : index; } } else { //Если раздел новый, то добавляем его в массив данных dataGrids.push(dg); //И устанавливаем на него фокус, если флаг первой загрузки = false tabFocus = !firstLoad ? dataGrids.length - 1 : 0; } }); //Обходим разделы, что остались в копированном массиве (на удаление) cloneDGs.map(s => { let curIndex = dataGrids.indexOf(dataGrids.find(x => x.rn === s.rn)); //Устаревший раздел удаляем из массива данных dataGrids.splice(curIndex, 1); //Фокус на предшествующий раздел if (curIndex > 0) tabFocus = curIndex - 1; //Иначе фокус на следующий, если был удалён первый раздел else tabFocus = curIndex; }); setRrpConf(pv => ({ ...pv, docLoaded: true, orderChanged: false, reload: false, sections: dataGrids })); handleSectionChange(tabFocus); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [rrpConf.reload, rrpConf.docLoaded, dataGrid.reload, dataGrid.docLoaded, executeStored] ); //При изменении сортировок useEffect(() => { setRrpConf(pv => ({ ...pv, orderChanged: true, reload: true })); }, [order]); //При необходимости обновить данные таблицы useEffect(() => { loadData(); }, [rrpConf.reload, dataGrid.reload, loadData]); return [rrpConf, handleReload]; }; //Хук для вкладки const useTab = () => { //Состояние раздела const [tabValue, setTabValue] = useState(""); //Переключение раздела const handleSectionChange = useCallback(newValue => { setTabValue(newValue); }, []); return [tabValue, handleSectionChange]; }; //Хук для функций открытия записей const useRecOpen = handleReload => { //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); //Подключение к контексту приложения const { pOnlineShowDictionary } = useContext(ApplicationСtx); //Подключение к контексту сообщений const { showMsgErr } = useContext(MessagingСtx); //Отображение показателя раздела const handleMarkOpen = useCallback( async nRrpConfSctnMrk => { const data = await executeStored({ stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_GET_CODES", args: { NRN: nRrpConfSctnMrk }, tagValueProcessor: () => undefined }); if (data) { pOnlineShowDictionary({ unitCode: "RRPConfig", showMethod: "main_mrk_settings", inputParameters: [ { name: "in_CODE", value: data.SRRPCONF }, { name: "in_SCTN_CODE", value: data.SRRPCONFSCTN }, { name: "in_MRK_CODE", value: data.SRRPCONFSCTNMRK } ], callBack: res => { res.success ? handleReload() : null; } }); } else showMsgErr(TEXTS.NO_DATA_FOUND); }, [executeStored, handleReload, pOnlineShowDictionary, showMsgErr] ); //Отображение показателя раздела const handleMarkCnOpen = useCallback( async (nRrpConfSctnMrk, nRrpConfSctnMrkCn) => { pOnlineShowDictionary({ unitCode: "RRPConfigSectionMark", showMethod: "link_cn", inputParameters: [ { name: "in_RN", value: nRrpConfSctnMrk }, { name: "in_RRPCONFSCTNMRKCN", value: nRrpConfSctnMrkCn } ], callBack: res => { res.success ? handleReload() : null; } }); }, [handleReload, pOnlineShowDictionary] ); //Отображение показателя раздела const handleMarkCnInsert = useCallback( async nRrpConfSctnMrk => { pOnlineShowDictionary({ unitCode: "RRPConfigSectionMarkConstitution", showMethod: "link_add", inputParameters: [{ name: "in_PRN", value: nRrpConfSctnMrk }], callBack: res => { res.success ? handleReload() : null; } }); }, [handleReload, pOnlineShowDictionary] ); return [handleMarkOpen, handleMarkCnOpen, handleMarkCnInsert]; }; //Хук для форм диалогового окна const useFormDialog = () => { //Состояние открытия диалогового окна const [formOpen, setForm] = useState(false); //Состояние диалогового окна const [formData, setFormData] = useState({ reload: false, rn: "", prn: "", sctnName: "", sctnCode: "", status: "", code: "", name: "", colCode: "", colRn: null, rowCode: "", rowRn: null }); //Подключение к контексту навигации const { getNavigationSearch } = useContext(NavigationCtx); //Открытие диалогового окна const openForm = () => { setForm(true); }; //Очистка диалогового окна const clearFormData = () => { setFormData({ reload: false, rn: "", prn: "", sctnName: "", sctnCode: "", status: "", code: "", name: "", colCode: "", colRn: null, rowCode: "", rowRn: null }); }; //Отработка нажатия на кнопку добавления секции const handleSectionAdd = () => { setFormData({ status: STATUSES.CREATE, prn: Number(getNavigationSearch().NRN) }); openForm(); }; //Отработка нажатия на кнопку исправления секции const handleSectionEdit = (rn, code, name) => { setFormData({ rn: rn, code: code, name: name, status: STATUSES.EDIT }); openForm(); }; //Отработка нажатия на кнопку удаления секции const handleSectionDelete = (rn, code, name) => { setFormData({ rn: rn, code: code, name: name, status: STATUSES.DELETE }); openForm(); }; //Отработка нажатия на кнопку добавления показателя раздела const handleMarkAdd = (prn, rowRn = null, rowCode = "", colRn = null, colCode = "") => { setFormData({ reload: rowRn && colRn ? true : false, prn: prn, rowRn: rowRn, rowCode: rowCode, colRn: colRn, colCode: colCode, status: STATUSES.RRPCONFSCTNMRK_CREATE }); openForm(); }; //Отработка нажатия на кнопку исправления показателя раздела const handleMarkEdit = rn => { setFormData({ reload: true, rn: rn, status: STATUSES.RRPCONFSCTNMRK_EDIT }); openForm(); }; //Отработка нажатия на кнопку удаления показателя раздела const handleMarkDelete = rn => { setFormData({ rn: rn, status: STATUSES.RRPCONFSCTNMRK_DELETE }); openForm(); }; //При закрытии диалога const handleDialogClose = () => { setForm(false); clearFormData(); }; return [ formOpen, formData, handleSectionAdd, handleSectionEdit, handleSectionDelete, handleMarkAdd, handleMarkEdit, handleMarkDelete, handleDialogClose ]; }; //Формирование разделов const a11yProps = index => { return { id: `simple-tab-${index}`, "aria-controls": `simple-tabpanel-${index}` }; }; //---------------- //Интерфейс модуля //---------------- export { useWindowResize, useConf, useTab, useRecOpen, useFormDialog, a11yProps };