/* Парус 8 - Панели мониторинга - Редактор панелей Общие компоненты редакторов свойств */ //--------------------- //Подключение библиотек //--------------------- import React, { useState, useContext, useEffect } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Box, Stack, IconButton, Icon, Typography, Divider, Chip, Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField, InputAdornment, MenuItem, Menu, Card, CardContent, CardActions, CardActionArea } from "@mui/material"; //Интерфейсные элементы import client from "../../../core/client"; //Клиент БД import { ApplicationСtx } from "../../../context/application"; //Контекст приложения import { BUTTONS } from "../../../../app.text"; //Общие текстовые ресурсы import { useUserProcDesc } from "./components_hooks"; //Общие хуки компонентов import "../panels_editor.css"; //Стили редактора //--------- //Константы //--------- //Стили const STYLES = { CHIP: (fullWidth = false, multiLine = false) => ({ ...(multiLine ? { height: "auto" } : {}), "& .MuiChip-label": { ...(multiLine ? { display: "block", whiteSpace: "normal" } : {}), ...(fullWidth ? { width: "100%" } : {}) } }) }; //Типы даных аргументов const ARGUMENT_DATA_TYPE = { STR: client.SERV_DATA_TYPE_STR, NUMB: client.SERV_DATA_TYPE_NUMB, DATE: client.SERV_DATA_TYPE_DATE }; //Типы источников данных const DATA_SOURCE_TYPE = { USER_PROC: "USER_PROC", QUERY: "QUERY" }; //Типы источников данных (наименования) const DATA_SOURCE_TYPE_NAME = { [DATA_SOURCE_TYPE.USER_PROC]: "Пользовательская процедура", [DATA_SOURCE_TYPE.QUERY]: "Запрос" }; //Структура аргумента источника данных const DATA_SOURCE_ARGUMENT_SHAPE = PropTypes.shape({ name: PropTypes.string.isRequired, caption: PropTypes.string.isRequired, dataType: PropTypes.oneOf(Object.values(ARGUMENT_DATA_TYPE)), req: PropTypes.bool.isRequired, value: PropTypes.any, valueSource: PropTypes.string }); //Начальное состояние аргумента источника данных const DATA_SOURCE_ARGUMENT_INITIAL = { name: "", caption: "", dataType: "", req: false, value: "", valueSource: "" }; //Структура источника данных const DATA_SOURCE_SHAPE = PropTypes.shape({ type: PropTypes.oneOf([...Object.values(DATA_SOURCE_TYPE), ""]), userProc: PropTypes.string, stored: PropTypes.string, respArg: PropTypes.string, arguments: PropTypes.arrayOf(DATA_SOURCE_ARGUMENT_SHAPE) }); //Начальное состояние истоника данных const DATA_SOURCE_INITIAL = { type: "", userProc: "", stored: "", respArg: "", arguments: [] }; //----------- //Тело модуля //----------- //Контейнер редактора const EditorBox = ({ title, children, onSave }) => { //При нажатии на "Сохранить" const handleSaveClick = (closeEditor = false) => onSave && onSave(closeEditor); //Формирование представления return ( {title} {children} handleSaveClick(false)} title={BUTTONS.APPLY}> done handleSaveClick(true)} title={BUTTONS.SAVE}> done_all ); }; //Контроль свойств компонента - контейнер редактора EditorBox.propTypes = { title: PropTypes.string.isRequired, children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]), onSave: PropTypes.func }; //Заголовок раздела редактора const EditorSubHeader = ({ title }) => { //Формирование представления return ( ); }; //Контроль свойств компонента - заголовок раздела редактора EditorSubHeader.propTypes = { title: PropTypes.string.isRequired }; //Диалог настройки const ConfigDialog = ({ title, children, onOk, onCancel }) => { //Формирование представления return ( {title} {children} ); }; //Контроль свойств компонента - диалог настройки ConfigDialog.propTypes = { title: PropTypes.string.isRequired, children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]), onOk: PropTypes.func, onCancel: PropTypes.func }; //Диалог настройки источника данных const ConfigDataSourceDialog = ({ dataSource = null, valueProviders = {}, onOk = null, onCancel = null } = {}) => { //Собственное состояние - параметры элемента формы const [state, setState] = useState({ ...DATA_SOURCE_INITIAL, ...dataSource }); //Собственное состояние - флаги обновление данных const [refresh, setRefresh] = useState({ userProcDesc: 0 }); //Собственное состояние - элемент привязки меню выбора источника const [valueProvidersMenuAnchorEl, setValueProvidersMenuAnchorEl] = useState(null); //Описание выбранной пользовательской процедуры const [userProcDesc] = useUserProcDesc({ code: state.userProc, refresh: refresh.userProcDesc }); //Подключение к контексту приложения const { pOnlineShowDictionary } = useContext(ApplicationСtx); //Установка значения/привязки аргумента const setArgumentValueSource = (index, value, valueSource) => setState(pv => ({ ...pv, arguments: pv.arguments.map((argument, i) => ({ ...argument, ...(i == index ? { value, valueSource } : {}) })) })); //Открытие/сокрытие меню выбора источника const toggleValueProvidersMenu = target => setValueProvidersMenuAnchorEl(target instanceof Element ? target : null); //При нажатии на очистку наименования пользовательской процедуры const handleUserProcClearClick = () => setState({ ...DATA_SOURCE_INITIAL }); //При нажатии на выбор пользовательской процедуры в качестве источника данных const handleUserProcSelectClick = () => { pOnlineShowDictionary({ unitCode: "UserProcedures", showMethod: "main", inputParameters: [{ name: "in_CODE", value: state.userProc }], callBack: res => { if (res.success) { setState(pv => ({ ...pv, type: DATA_SOURCE_TYPE.USER_PROC, userProc: res.outParameters.out_CODE })); setRefresh(pv => ({ ...pv, userProcDesc: pv.userProcDesc + 1 })); } } }); }; //При закрытии дилога с сохранением const handleOk = () => onOk && onOk({ ...state }); //При закртии диалога отменой const handleCancel = () => onCancel && onCancel(); //При очистке значения/связывания аргумента const handleArgumentClearClick = index => setArgumentValueSource(index, "", ""); //При отображении меню связывания аргумента с поставщиком данных const handleArgumentLinkMenuClick = e => setValueProvidersMenuAnchorEl(e.currentTarget); //При выборе элемента меню связывания аргумента с поставщиком данных const handleArgumentLinkClick = valueSource => { setArgumentValueSource(valueProvidersMenuAnchorEl.id, "", valueSource); toggleValueProvidersMenu(); }; //При вводе значения аргумента const handleArgumentChange = (index, value) => setArgumentValueSource(index, value, ""); //При изменении описания пользовательской процедуры useEffect(() => { if (userProcDesc) setState(pv => ({ ...pv, stored: userProcDesc?.stored?.name, respArg: userProcDesc?.stored?.respArg, arguments: (userProcDesc?.arguments || []).map(argument => ({ ...DATA_SOURCE_ARGUMENT_INITIAL, ...argument })) })); }, [userProcDesc]); //Список значений const values = Object.keys(valueProviders).reduce((res, key) => [...res, ...Object.keys(valueProviders[key])], []); //Наличие значений const isValues = values && values.length > 0 ? true : false; //Меню привязки к поставщикам значений const valueProvidersMenu = isValues && ( {values.map((value, i) => ( handleArgumentLinkClick(value)}> {value} ))} ); //Формирование представления return ( {valueProvidersMenu} clear list ) }} /> {Array.isArray(state?.arguments) && state.arguments.map((argument, i) => ( handleArgumentChange(i, e.target.value)} InputLabelProps={{ shrink: true }} InputProps={{ endAdornment: ( handleArgumentClearClick(i)}> clear {isValues && ( settings_ethernet )} ) }} /> ))} ); }; //Контроль свойств компонента - Диалог настройки источника данных ConfigDataSourceDialog.propTypes = { dataSource: DATA_SOURCE_SHAPE, valueProviders: PropTypes.object, onOk: PropTypes.func, onCancel: PropTypes.func }; //Источник данных const DataSource = ({ dataSource = null, valueProviders = {}, onChange = null } = {}) => { //Собственное состояние - отображение диалога настройки const [configDlg, setConfigDlg] = useState(false); //Уведомление родителя о смене настроек источника данных const notifyChange = settings => onChange && onChange(settings); //При нажатии на настройку источника данных const handleSetup = () => setConfigDlg(true); //При нажатии на настройку источника данных const handleSetupOk = dataSource => { setConfigDlg(false); notifyChange(dataSource); }; //При нажатии на настройку источника данных const handleSetupCancel = () => setConfigDlg(false); //При удалении настроек источника данных const handleDelete = () => notifyChange({ ...DATA_SOURCE_INITIAL }); //Расчет флага "настроенности" const configured = dataSource?.type ? true : false; //Список аргументов const args = configured && dataSource.arguments.map((argument, i) => ( )); //Формирование представления return ( <> {configDlg && ( )} {configured && ( {dataSource.type === DATA_SOURCE_TYPE.USER_PROC ? dataSource.userProc : "Источник без наименования"} {DATA_SOURCE_TYPE_NAME[dataSource.type] || "Неизвестный тип источника"} {args} delete )} {!configured && ( )} ); }; //Контроль свойств компонента - Источник данных DataSource.propTypes = { dataSource: DATA_SOURCE_SHAPE, valueProviders: PropTypes.object, onChange: PropTypes.func }; //---------------- //Интерфейс модуля //---------------- export { STYLES, ARGUMENT_DATA_TYPE, DATA_SOURCE_TYPE, DATA_SOURCE_SHAPE, DATA_SOURCE_INITIAL, EditorBox, EditorSubHeader, ConfigDialog, DataSource };