/*
Парус 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 (
);
};
//Контроль свойств компонента - диалог настройки
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 && (
);
//Формирование представления
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 };