/* Парус 8 - Панели мониторинга - Редакторы панелей Пользовательские хуки компонента "Источник данных" */ //--------------------- //Подключение библиотек //--------------------- import { useState, useContext, useEffect, useRef } from "react"; //Классы React import client from "../../core/client"; //Клиент взаимодействия с сервером приложений import { ERRORS } from "../../../app.text"; //Общие текстовые ресурсы import { formatErrorMessage } from "../../core/utils"; //Общие вспомогательные функции import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { P8P_DATA_SOURCE_TYPE, P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE } from "./p8p_data_source_common"; //Общие ресурсы источника данных import { getConditionsValues } from "./p8p_component_condition/util"; //Вспомогательные ресурсы условий import { getHandlersByActions } from "./p8p_component_action/util"; //Вспомогательные ресурсы действий import { ApplicationСtx } from "../../context/application"; //Контекст приложения //----------- //Тело модуля //----------- //Описание пользовательской процедуры const useUserProcDesc = ({ code, refresh }) => { //Собственное состояние - флаг загрузки const [isLoading, setLoading] = useState(false); //Собственное состояние - данные const [data, setData] = useState(null); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); //При необходимости обновить данные компонента useEffect(() => { //Загрузка данных с сервера const loadData = async () => { try { setLoading(true); const data = await executeStored({ stored: "PKG_P8PANELS_PE.USERPROCS_DESC", args: { SCODE: code }, respArg: "COUT", isArray: name => name === "arguments", loader: false }); setData(data?.XUSERPROC || null); } finally { setLoading(false); } }; //Если надо обновить и есть для чего получать данные if (refresh > 0) if (code) loadData(); else setData(null); }, [refresh, code, executeStored]); //Возвращаем интерфейс хука return [data, isLoading]; }; //Получение данных из источника const useDataSource = ({ dataSource, values, componentRespArg = "" }) => { //Контроллер для прерывания запросов const abortController = useRef(null); //Собственное состояние - параметры исполнения const [state, setState] = useState({ stored: null, storedArgs: [], respArg: null, reqSet: false }); //Собственное состояние - флаг загрузки const [isLoading, setLoading] = useState(false); //Собственное состояние - данные const [data, setData] = useState({ componentData: {}, init: false }); //Собственное состояние - ошибка получения данных const [error, setError] = useState(null); //Собственное состояние - наличие настроек const [haveConfing, setHaveConfig] = useState(false); //Собственное состояние - наличие данных const [haveData, setHaveData] = useState(false); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); //При необходимости обновление информации о наличии данных useEffect(() => { setHaveData(data.init === true && !error ? true : false); }, [data.init, error]); //При необходимости обновить данные useEffect(() => { //Загрузка данных с сервера const loadData = async () => { try { setLoading(true); abortController.current?.abort?.(); abortController.current = new AbortController(); const data = await executeStored({ stored: state.stored, args: { ...(state.storedArgs ? state.storedArgs : {}) }, respArg: state.respArg, loader: false, signal: abortController.current.signal, showErrorMessage: false }); setError(null); setData({ componentData: { ...data[componentRespArg] }, init: true }); } catch (e) { if (e.message !== client.ERR_ABORTED) { setError(formatErrorMessage(e.message).text); setData({ componentData: {}, init: false }); } } finally { setLoading(false); } }; if (state.reqSet) { if (state.stored) loadData(); } else setData({ componentData: {}, init: false }); return () => abortController.current?.abort?.(); }, [state.stored, state.storedArgs, state.respArg, state.reqSet, executeStored, componentRespArg]); //При изменении свойств useEffect(() => { //Устанавливаем признак наличия настроек setHaveConfig(dataSource?.stored ? true : false); //Устанавливаем параметры исполнения setState(pv => { if (dataSource?.type == P8P_DATA_SOURCE_TYPE.USER_PROC) { const { stored, respArg } = dataSource; let reqSet = true; const storedArgs = {}; dataSource.arguments.forEach(argument => { let v = argument.valueSource ? values[argument.valueSource] : argument.value; storedArgs[argument.name] = argument.dataType == P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE.NUMB ? isNaN(parseFloat(v)) ? null : parseFloat(v) : argument.dataType == P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE.DATE ? new Date(v) : String(v === undefined ? "" : v); if (argument.req === true && [undefined, null, ""].includes(storedArgs[argument.name])) reqSet = false; }); if (pv.stored != stored || pv.respArg != respArg || JSON.stringify(pv.storedArgs) != JSON.stringify(storedArgs)) { if (!reqSet) { setError(ERRORS.DATA_SOURCE_NO_REQ_ARGS); setData({ componentData: {}, init: false }); } return { stored, respArg, storedArgs, reqSet }; } else return pv; } else return pv; }); }, [dataSource, values]); //Возвращаем интерфейс хука return [data.componentData, error, haveConfing, haveData, isLoading]; }; //Изменение данных компонента с учетом условий const useConditions = ({ componentData, conditions }) => { //Собственное состояние - текущие условия компонента const [currentConditions, setCurrentConditions] = useState([]); //Собственное состояние - данные const [data, setData] = useState(); //При обновлении условий компонента useEffect(() => { //Если условия изменились if (JSON.stringify(currentConditions) != JSON.stringify(conditions)) { //Устанавливаем новые условия setCurrentConditions(conditions); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [conditions]); //При обновлении данных или условий компонента useEffect(() => { //Если есть текущие условия if (currentConditions.length !== 0) { //Устанавливаем данные с учетом условий setData({ ...componentData, ...getConditionsValues(componentData, currentConditions) }); } else { //Оставляем данные компонента setData({ ...componentData }); } }, [currentConditions, componentData]); //Возвращаем интерфейс хука return [data]; }; //Получение обработчиков компонента const useComponentHandlers = ({ actions = [], onValuesChange = null, getCustomTypeValue = null }) => { //Контроллер для текущего состояния действий const currentActions = useRef([]); //Собственное состояние - обработчики компонента const [handlers, setHandlers] = useState({}); //Подключение к контексту приложения const { configUrlBase, pOnlineShowTab, pOnlineShowUnit } = useContext(ApplicationСtx); //При необходимости обновления информации об обработчиках useEffect(() => { //Если изменились действия или параметры if (JSON.stringify(currentActions.current) != JSON.stringify(actions)) { //Считываем обработчики компонента setHandlers(getHandlersByActions(actions, pOnlineShowUnit, configUrlBase, pOnlineShowTab, onValuesChange, getCustomTypeValue)); //Устанавливаем контроллер текущих действий currentActions.current = actions; } // eslint-disable-next-line react-hooks/exhaustive-deps }, [actions]); //Возвращаем интерфейс хука return [handlers]; }; //---------------- //Интерфейс модуля //---------------- export { useUserProcDesc, useDataSource, useConditions, useComponentHandlers };