/* Парус 8 - Панели мониторинга - УДП - Доски задач Компонент панели: Диалог формы события */ //--------------------- //Подключение библиотек //--------------------- import React, { useState, useContext, useEffect } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Box, Typography, TextField, Dialog, DialogContent, DialogActions, Button, Tabs, Tab, InputAdornment, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты import { useClientEvent, useDocsProps } from "../hooks"; //Вспомогательные хуки import { APP_STYLES } from "../../../../app.styles"; //Типовые стили import { ApplicationСtx } from "../../../context/application"; //Контекст приложения import dayjs from "dayjs"; //Работа с датами import customParseFormat from "dayjs/plugin/customParseFormat"; //Настройка пользовательского формата даты //--------- //Константы //--------- //Перечисление "Значение по умолчанию" const DP_DEFAULT_VALUE = Object.freeze({ 0: "defaultStr", 1: "defaultNum", 2: "defaultDate", 3: "defaultNum" }); //Перечисление "Префикс формата данных" const DP_TYPE_PREFIX = Object.freeze({ 0: "S", 1: "N", 2: "D", 3: "N" }); //Перечисление "Входящее значение дополнительного словаря" const DP_IN_VALUE = Object.freeze({ 0: "pos_str_value", 1: "pos_num_value", 2: "pos_date_value", 3: "pos_num_value" }); //Перечисление "Исходящее значение дополнительного словаря" const DP_RETURN_VALUE = Object.freeze({ 0: "str_value", 1: "num_value", 2: "date_value", 3: "num_value" }); //Стили const STYLES = { CONTAINER: { margin: "5px 0px", textAlign: "center" }, DIALOG_CONTENT: { paddingBottom: "0px", maxHeight: "740px", minHeight: "740px", ...APP_STYLES.SCROLL }, DIALOG_ACTIONS: { justifyContent: "end", paddingRight: "24px", paddingLeft: "24px" }, BOX_WITH_LEGEND: { border: "1px solid #939393" }, BOX_SINGLE_COLUMN: { display: "flex", flexDirection: "column", gap: "10px" }, BOX_FEW_COLUMNS: { display: "flex", flexWrap: "wrap", justifyContent: "space-between" }, BOX_LEFT_ALIGN: { display: "flex", justifyContent: "flex-start" }, LEGEND: { textAlign: "left" }, TEXT_FIELD: (widthVal, greyDisabled = false) => ({ margin: "4px", ...(widthVal ? { width: widthVal } : {}), ...(greyDisabled ? { "& .MuiInputBase-input.Mui-disabled": { WebkitTextFillColor: "rgba(0, 0, 0, 0.87)" }, "& .MuiInputLabel-root.Mui-disabled": { WebkitTextFillColor: "rgba(0, 0, 0, 0.6)" } } : {}) }) }; //------------------------------------ //Вспомогательные функции и компоненты //------------------------------------ //Подключение настройки пользовательского формата даты dayjs.extend(customParseFormat); //Свойства вкладки function a11yProps(index) { return { id: `simple-tab-${index}`, "aria-controls": `simple-tabpanel-${index}` }; } //Формирование кнопки для открытия раздела const getInputProps = (onClick, disabled = false, icon = "list") => { return { endAdornment: ( {icon} ) }; }; //Вкладка информации function CustomTabPanel(props) { const { children, value, index, ...other } = props; return ( ); } //Контроль свойств - Вкладка информации CustomTabPanel.propTypes = { children: PropTypes.node, index: PropTypes.number.isRequired, value: PropTypes.number.isRequired }; //Вкладка основной информации const MainEventInfoTab = ({ task, editable, handleFieldEdit, handleClientClientsOpen, handleClientPersonOpen, handleCrnOpen, handleEventNextNumbGet }) => { return ( Событие Клиент handleClientPersonOpen(0), !task.stype)} > ); }; //Контроль свойств - Вкладка основной информации MainEventInfoTab.propTypes = { task: PropTypes.object.isRequired, editable: PropTypes.bool.isRequired, handleFieldEdit: PropTypes.func.isRequired, handleClientClientsOpen: PropTypes.func.isRequired, handleClientPersonOpen: PropTypes.func.isRequired, handleCrnOpen: PropTypes.func.isRequired, handleEventNextNumbGet: PropTypes.func.isRequired }; //Вкладка информации об исполнителе const ExecutorEventInfoTab = ({ task, handleFieldEdit, handleClientPersonOpen }) => { return ( Планирование Инициатор handleClientPersonOpen(1), task.isUpdate)} > Направить ); }; //Контроль свойств - Вкладка информации об исполнителе ExecutorEventInfoTab.propTypes = { task: PropTypes.object.isRequired, handleFieldEdit: PropTypes.func.isRequired, handleClientPersonOpen: PropTypes.func.isRequired }; //Вкладка информации со свойствами const PropsEventInfoTab = ({ task, docProps, handlePropEdit }) => { //Подключение к контексту приложения const { pOnlineShowDictionary } = useContext(ApplicationСtx); //Формат дополнительного свойства типа число (длина, точность) const DPNumFormat = (l, p) => new RegExp("^(\\d{1," + (l - p) + "}" + (p > 0 ? "((\\.|,)\\d{1," + p + "})?" : "") + ")?$"); //Формат дополнительного свойства типа строка (длина) const DPStrFormat = l => new RegExp("^.{0," + l + "}$"); //Проверка валидности числа const isValidDPNum = (length, prec, value) => { return DPNumFormat(length, prec).test(value); }; //Проверка валидности строки const isValidDPStr = (length, value) => { return DPStrFormat(length).test(value); }; //Признак ошибки валидации const validationError = (value = "", format, numW, numPrec, strW) => { if (format === 0) return isValidDPStr(strW, value); else if (format === 1) { return isValidDPNum(numW, numPrec, value); } else return true; }; //Конвертация времени в привычный формат const timeFromSqlFormat = ts => { if (ts.indexOf(".") !== -1) { let s = 24 * 60 * 60 * ts; const h = Math.trunc(s / (60 * 60)); s = s % (60 * 60); const m = Math.trunc(s / 60); s = Math.round(s % 60); const formattedTime = ("0" + h).slice(-2) + ":" + ("0" + m).slice(-2) + ":" + ("0" + s).slice(-2); return formattedTime; } return ts; }; //Выбор из словаря или дополнительного словаря const handleDict = async (dp, curValue = null) => { dp.entryType === 1 ? pOnlineShowDictionary({ unitCode: dp.unitcode, showMethod: dp.showMethodCode, inputParameters: dp.paramRn ? [{ name: dp.paramIn, value: curValue }] : null, callBack: res => { res.success ? handlePropEdit({ target: { id: `${DP_TYPE_PREFIX[dp.format]}DP_${dp.rn}`, value: res.outParameters[dp.paramOut] } }) : null; } }) : pOnlineShowDictionary({ unitCode: "ExtraDictionaries", showMethod: "values", inputParameters: [ { name: "pos_rn", value: dp.extraDictRn }, { name: DP_IN_VALUE[dp.format], value: curValue } ], callBack: res => { res.success ? handlePropEdit({ target: { id: `${DP_TYPE_PREFIX[dp.format]}DP_${dp.rn}`, value: res.outParameters[DP_RETURN_VALUE[dp.format]] } }) : null; } }); }; //Инициализация дополнительного свойства const initProp = prop => { //Значение свойства const value = task.docProps[`${DP_TYPE_PREFIX[prop.format]}DP_${prop.rn}`]; if ( (task.nrn || task.docProps[`${DP_TYPE_PREFIX[prop.format]}DP_${prop.rn}`]) && task.docProps[`${DP_TYPE_PREFIX[prop.format]}DP_${prop.rn}`] !== undefined ) { //Строка или число if (prop.format < 2) return prop.numPrecision ? String(value).replace(".", ",") : value; //Дата else if (prop.format === 2) { //Дата без времени if (prop.dataSubtype === 0) return dayjs(value).format("YYYY-MM-DD"); //Дата + время без секунд else if (prop.dataSubtype === 1) return dayjs(value).format("YYYY-MM-DD HH:mm"); //Дата + время с секундами else return dayjs(value).format("YYYY-MM-DD HH:mm:ss"); } //Время else { return timeFromSqlFormat(value); } } else if (task.nrn) { return ""; } else return prop[DP_DEFAULT_VALUE[prop.format]]; }; //Генерация содержимого return ( {docProps.props.map(dp => { return dp.showInGrid ? ( 0 ? getInputProps(() => handleDict(dp, task.docProps[`${DP_TYPE_PREFIX[dp.format]}DP_${dp.rn}`])) : null } InputLabelProps={ dp.format < 2 ? {} : { shrink: true } } required={dp.require} disabled={dp.readonly} /> ) : null; })} ); }; //Контроль свойств - Вкладка информации со свойствами PropsEventInfoTab.propTypes = { task: PropTypes.object.isRequired, docProps: PropTypes.object.isRequired, handlePropEdit: PropTypes.func.isRequired }; //----------- //Тело модуля //----------- //Форма события const TaskForm = ({ task, taskType, setTask, editable, handleClientClientsOpen, handleClientPersonOpen, handleCrnOpen, handleEventNextNumbGet, handleDPReady }) => { //Состояние вкладки const [value, setValue] = useState(0); //Состояние допустимых дополнительных свойств const [docProps] = useDocsProps(taskType); //При изменении вкладки const handleChange = (event, newValue) => { setValue(newValue); }; //При изменении поля const handleFieldEdit = e => { setTask(pv => ({ ...pv, [e.target.id]: e.target.value, //Связанные значения, если меняется одно, то необходимо обнулить другое ...(e.target.id === "sclnt_clnperson" ? { sclnt_clnclients: "" } : {}), ...(e.target.id === "sclnt_clnclients" ? { sclnt_clnperson: "" } : {}) })); }; //При изменении свойства const handlePropEdit = e => { setTask(pv => ({ ...pv, docProps: { ...pv.docProps, [e.target.id]: e.target.value } })); }; //При заполнении всех обязательных свойств useEffect(() => { let i = 0; docProps.props.filter(dp => dp.require === true).map(prop => (!task.docProps[`${DP_TYPE_PREFIX[prop.format]}DP_${prop.rn}`] ? i++ : null)); docProps.loaded && i === 0 ? handleDPReady(true) : handleDPReady(false); }, [docProps, handleDPReady, task.docProps]); //Генерация содержимого return ( {task.nrn ? "Исправление события" : "Добавление события"} {docProps.props.length > 0 ? : null} {docProps.props.length > 0 ? ( ) : null} ); }; //Контроль свойств - Форма события TaskForm.propTypes = { task: PropTypes.object.isRequired, taskType: PropTypes.string.isRequired, setTask: PropTypes.func.isRequired, editable: PropTypes.bool.isRequired, handleClientClientsOpen: PropTypes.func.isRequired, handleClientPersonOpen: PropTypes.func.isRequired, handleCrnOpen: PropTypes.func.isRequired, handleEventNextNumbGet: PropTypes.func.isRequired, handleDPReady: PropTypes.func.isRequired }; //Диалог с формой события const TaskFormDialog = ({ taskRn, taskType, taskStatus, editable, handleReload, onClose }) => { //Собственное состояние const [task, setTask, insertEvent, updateEvent, handleClientClientsOpen, handleClientPersonOpen, handleCrnOpen, handleEventNextNumbGet] = useClientEvent(taskRn, taskType, taskStatus); //Состояние заполненности всех обязательных свойств const [dpReady, setDPReady] = useState(false); //Изменение состояния заполненности всех обязательных свойств const handleDPReady = v => setDPReady(v); //Генерация содержимого return ( {onClose ? ( {taskRn ? ( ) : ( )} ) : null} ); }; //Контроль свойств - Диалог с формой события TaskFormDialog.propTypes = { taskRn: PropTypes.number, taskType: PropTypes.string.isRequired, taskStatus: PropTypes.string, editable: PropTypes.bool, handleReload: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired }; //---------------- //Интерфейс модуля //---------------- export { TaskFormDialog };