diff --git a/app/panels/query_editor/components/argument/argument.js b/app/panels/query_editor/components/argument/argument.js new file mode 100644 index 0000000..eec7b71 --- /dev/null +++ b/app/panels/query_editor/components/argument/argument.js @@ -0,0 +1,89 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Компоненты: Аргумент запроса +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Stack, ListItem, IconButton, Icon, ListItemButton, ListItemText, Typography, ListItemIcon, Chip } from "@mui/material"; //Компоненты UI +import { STYLES as COMMON_STYLES } from "../../../../components/editors/p8p_editors_common"; //Общие ресурсы редаторов +import { DATA_TYPE, DATA_TYPE_ICON } from "../../common"; //Общие ресурсы и константы редактора запросов + +//--------- +//Константы +//--------- + +//Варианты представления +const ARGUMENT_VARIANT = { + LIST_ITEM: "LIST_ITEM", + CHIP: "CHIP" +}; + +//Структура аргумента +const ARGUMENT_SHAPE = PropTypes.shape({ + name: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + dataType: PropTypes.oneOf(Object.values(DATA_TYPE)), + mandatory: PropTypes.oneOf([0, 1]).isRequired +}); + +//Иконки +const ICONS = { ...DATA_TYPE_ICON, DEFAULT: "category" }; + +//----------- +//Тело модуля +//----------- + +//Аргумент запроса +const Argument = ({ arg, variant, onClick = null, onDelete = null }) => { + //Заголовок аргумента + const title = `${arg.mandatory == 1 ? "*" : ""}${arg.title}`; + + //Иконка аргумента + const icon = ICONS[arg.dataType] || ICONS.DEFAULT; + + //Формирование представления + return variant == ARGUMENT_VARIANT.LIST_ITEM ? ( + + onClick && onClick(arg)} dense> + + {icon} + + + {arg.name} + + } + /> + + onDelete && onDelete(e, arg)} title={"Удалить"}> + delete + + + + + ) : variant == ARGUMENT_VARIANT.CHIP ? ( + {icon}} label={title} variant={"outlined"} sx={COMMON_STYLES.CHIP(true)} /> + ) : null; +}; + +//Контроль свойств компонента - Аргумент запроса +Argument.propTypes = { + arg: ARGUMENT_SHAPE, + variant: PropTypes.oneOf(Object.values(ARGUMENT_VARIANT)).isRequired, + onClick: PropTypes.func, + onDelete: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { Argument, ARGUMENT_VARIANT, ARGUMENT_SHAPE }; diff --git a/app/panels/query_editor/components/inspector/inspector.js b/app/panels/query_editor/components/inspector/inspector.js new file mode 100644 index 0000000..7ecb070 --- /dev/null +++ b/app/panels/query_editor/components/inspector/inspector.js @@ -0,0 +1,62 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Инспектор свойств +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { P8PEditorBox } from "../../../../components/editors/p8p_editor_box"; //Контейнер параметров редактора +import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора +import { ENTITY_DATA_SHAPE } from "../entity/entity"; //Описание сущности +import { RELATION_DATA_SHAPE } from "../relation/relation"; //Описание связи +import { InspectorQueryArguments } from "../inspector_query_args/inspector_query_args"; //Управление аргументами запроса +import { InspectorQueryEntities } from "../inspector_query_ents/inspector_query_ents"; //Управление сущностями запроса +import { InspectorQueryRelations } from "../inspector_query_rls/inspector_query_rls"; //Управление связями запроса +import { ARGUMENT_SHAPE } from "../argument/argument"; //Аргументы запроса + +//----------- +//Тело модуля +//----------- + +//Инспектор свойств +const Inspector = ({ query, entity, relation, args = [], cond = null, onOptionsChanged = null }) => { + //При изменении настроек запроса + const handleOptionsChanged = () => onOptionsChanged && onOptionsChanged(); + + //Генерация содержимого + return ( + + + + + + + {relation && ( + <> + + + + )} + + ); +}; + +//Контроль свойств компонента - Инспектор свойств +Inspector.propTypes = { + query: PropTypes.number.isRequired, + entity: ENTITY_DATA_SHAPE, + relation: RELATION_DATA_SHAPE, + args: PropTypes.arrayOf(ARGUMENT_SHAPE), + cond: PropTypes.string, + onOptionsChanged: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { Inspector }; diff --git a/app/panels/query_editor/components/inspector_query_args/arg_iu_dialog.js b/app/panels/query_editor/components/inspector_query_args/arg_iu_dialog.js new file mode 100644 index 0000000..de8e147 --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_args/arg_iu_dialog.js @@ -0,0 +1,76 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Компонент: Диалог добавления/исправления аргумента запроса +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог +import { TITLES } from "../../../../../app.text"; //Общие текстовые ресурсы приложения +import { DATA_TYPE } from "../../common"; //Общие константы редактора + +//----------- +//Тело модуля +//----------- + +//Диалог добавления/исправления аргумента запроса +const ArgIUDialog = ({ name = "", title = "", dataType = DATA_TYPE.NUMB, mandatory = 0, insert = true, onOk, onCancel }) => { + //Нажатие на кнопку "Ok" + const handleOk = values => onOk && onOk({ ...values }); + + //Нажатие на кнопку "Отмена" + const handleCancel = () => onCancel && onCancel(); + + //Генерация содержимого + return ( + + ); +}; + +//Контроль свойств - Диалог добавления/исправления аргумента запроса +ArgIUDialog.propTypes = { + name: PropTypes.string, + title: PropTypes.string, + dataType: PropTypes.number, + mandatory: PropTypes.number, + insert: PropTypes.bool, + onOk: PropTypes.func, + onCancel: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { ArgIUDialog }; diff --git a/app/panels/query_editor/components/inspector_query_args/args_list.js b/app/panels/query_editor/components/inspector_query_args/args_list.js new file mode 100644 index 0000000..d5b8251 --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_args/args_list.js @@ -0,0 +1,63 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Компонент: Список аргументов +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { List } from "@mui/material"; //Интерфейсные компоненты MUI +import { APP_STYLES } from "../../../../../app.styles"; //Общие стили приложения + +import { Argument, ARGUMENT_VARIANT, ARGUMENT_SHAPE } from "../argument/argument"; //Аргумент запроса + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + LIST: { height: "500px", width: "360px", bgcolor: "background.paper", overflowY: "auto", ...APP_STYLES.SCROLL } +}; + +//----------- +//Тело модуля +//----------- + +//Список аргументов +const ArgsList = ({ args = [], onSelect = null, onDelete = null } = {}) => { + //При нажатии на элемент списка + const handleItemClick = arg => onSelect && onSelect(arg); + + //При нажатии на удалении элемента списка + const handleItemDeleteClick = (e, arg) => { + e.stopPropagation(); + onDelete && onDelete(arg); + }; + + //Формирование представления + return ( + + {args && + args.map((arg, i) => ( + + ))} + + ); +}; + +//Контроль свойств компонента - Список аргументов +ArgsList.propTypes = { + args: PropTypes.arrayOf(ARGUMENT_SHAPE), + onSelect: PropTypes.func, + onDelete: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { ArgsList }; diff --git a/app/panels/query_editor/components/inspector_query_args/hooks.js b/app/panels/query_editor/components/inspector_query_args/hooks.js new file mode 100644 index 0000000..554d55e --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_args/hooks.js @@ -0,0 +1,66 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Пользовательские хуки настройки аргументов запроса +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { useContext, useCallback } from "react"; //Классы React +import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//----------- +//Тело модуля +//----------- + +//Работа с аргументами запроса +const useQueryArgs = query => { + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Добавление аргумента запроса + const addArg = useCallback( + async (name, title, dataType, mandatory) => { + await executeStored({ + stored: "PKG_P8PANELS_QE.QUERY_OPT_ARG_ADD", + args: { NRN: query, SNAME: name, STITLE: title, NDATA_TYPE: dataType, NMANDATORY: mandatory }, + loader: false + }); + }, + [query, executeStored] + ); + + //Исправление аргумента запроса + const editArg = useCallback( + async (name, title, dataType, mandatory) => { + await executeStored({ + stored: "PKG_P8PANELS_QE.QUERY_OPT_ARG_EDIT", + args: { NRN: query, SNAME: name, STITLE: title, NDATA_TYPE: dataType, NMANDATORY: mandatory }, + loader: false + }); + }, + [query, executeStored] + ); + + //Удаление аргумента запроса + const removeArg = useCallback( + async name => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_OPT_ARG_REMOVE", args: { NRN: query, SNAME: name }, loader: false }); + }, + [query, executeStored] + ); + + //Возвращаем интерфейс хука + return { addArg, editArg, removeArg }; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { useQueryArgs }; diff --git a/app/panels/query_editor/components/inspector_query_args/inspector_query_args.js b/app/panels/query_editor/components/inspector_query_args/inspector_query_args.js new file mode 100644 index 0000000..4a75480 --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_args/inspector_query_args.js @@ -0,0 +1,107 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Компонент инспектора - Аргументы запроса +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Stack, Icon, Button, Card, CardContent, CardActionArea } from "@mui/material"; //Интерфейсные элементы +import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы +import { useQueryArgs } from "./hooks"; //Хуки для работы с аргументами запроса на сервере +import { QueryArgsDialog } from "./query_args_dialog"; //Диалог настройки состава атрибутов +import { Argument, ARGUMENT_SHAPE, ARGUMENT_VARIANT } from "../argument/argument"; //Аргумент запроса + +//----------- +//Тело модуля +//----------- + +//Компонент инспектора - Аргументы запроса +const InspectorQueryArguments = ({ query, args = [], onOptionsChanged = null }) => { + //Собственное состояние - отображение диалога настройки состава аргументов + const [openQueryArgsDialog, setOpenQueryArgsDialog] = useState(false); + + //Хук для взаимодействия с сервером + const { addArg, editArg, removeArg } = useQueryArgs(query); + + //Уведомление родителя об изменении свойств + const notifyOptionsChanged = () => onOptionsChanged && onOptionsChanged(); + + //При нажатии на настройку аргументов + const handleSetup = () => setOpenQueryArgsDialog(true); + + //При добавлении аргумента + const handleArgAdd = async (arg, cb) => { + await addArg(arg.name, arg.title, arg.dataType, arg.mandatory); + cb && cb(); + notifyOptionsChanged(); + }; + + //При изменении аргумента + const handleArgEdit = async (arg, cb) => { + await editArg(arg.name, arg.title, arg.dataType, arg.mandatory); + cb && cb(); + notifyOptionsChanged(); + }; + + //При удалении аргумента + const handleArgRemove = async arg => { + await removeArg(arg.name); + notifyOptionsChanged(); + }; + + //Закрытие диалога настройки состава аргументов по "Закрыть" + const handleQueryArgsDialogClose = () => setOpenQueryArgsDialog(false); + + //Расчет флага "настроенности" + const configured = args && args.length > 0 ? true : false; + + //Формирование представления + return ( + <> + {openQueryArgsDialog && ( + + )} + {configured && ( + + + + + {args.map((arg, i) => ( + + ))} + + + + + )} + {!configured && ( + + )} + + ); +}; + +//Контроль свойств компонента - Компонент инспектора - Аргументы запроса +InspectorQueryArguments.propTypes = { + query: PropTypes.number.isRequired, + args: PropTypes.arrayOf(ARGUMENT_SHAPE), + onOptionsChanged: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { InspectorQueryArguments }; diff --git a/app/panels/query_editor/components/inspector_query_args/query_args_dialog.js b/app/panels/query_editor/components/inspector_query_args/query_args_dialog.js new file mode 100644 index 0000000..86ba714 --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_args/query_args_dialog.js @@ -0,0 +1,80 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Компонент: Диалог настройки аргументов запроса +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Button, Icon } from "@mui/material"; //Интерфейсные компоненты MUI +import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений +import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения +import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог +import { ARGUMENT_SHAPE } from "../argument/argument"; //Аргумент запроса +import { ArgsList } from "./args_list"; //Список аргументов запроса +import { ArgIUDialog } from "./arg_iu_dialog"; //Диалог добавления/исправления аргумента + +//----------- +//Тело модуля +//----------- + +//Диалог настройки аргументов запроса +const QueryArgsDialog = ({ args, onArgAdd, onArgEdit, onArgRemove, onClose }) => { + //Собственное состояние - изменяемый аргумент + const [modArg, setModArg] = useState(null); + + //Подключение к контексту сообщений + const { showMsgWarn } = useContext(MessagingСtx); + + //Нажатие на кнопку "Закрыть" + const handleClose = () => onClose && onClose(); + + //При выборе аргумента в списке + const handleArgSelect = arg => setModArg({ ...arg }); + + //При добавлении аргумента + const handleArgAdd = () => setModArg(true); + + //Удаление аргумента + const handleArgRemove = arg => showMsgWarn("Удалить аргумент?", () => onArgRemove && onArgRemove(arg)); + + //При закрытии диалога добавления/исправления по "ОК" + const handleIUDialogOk = async values => { + if (modArg === true) onArgAdd && onArgAdd(values, handleIUDialogCancel); + else onArgEdit && onArgEdit(values, handleIUDialogCancel); + }; + + //При закрытии диалога добавления/исправления по "Отмена" + const handleIUDialogCancel = () => setModArg(null); + + //Генерация содержимого + return ( + + {modArg && ( + + )} + + + + ); +}; + +//Контроль свойств - Диалог настройки аргументов запроса +QueryArgsDialog.propTypes = { + args: PropTypes.arrayOf(ARGUMENT_SHAPE), + onArgAdd: PropTypes.func, + onArgEdit: PropTypes.func, + onArgRemove: PropTypes.func, + onClose: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { QueryArgsDialog }; diff --git a/app/panels/query_editor/components/entity_attrs_dialog/attrs_list.js b/app/panels/query_editor/components/inspector_query_ents/attrs_list.js similarity index 100% rename from app/panels/query_editor/components/entity_attrs_dialog/attrs_list.js rename to app/panels/query_editor/components/inspector_query_ents/attrs_list.js diff --git a/app/panels/query_editor/components/entity_add_dialog/entity_add_dialog.js b/app/panels/query_editor/components/inspector_query_ents/entity_add_dialog.js similarity index 100% rename from app/panels/query_editor/components/entity_add_dialog/entity_add_dialog.js rename to app/panels/query_editor/components/inspector_query_ents/entity_add_dialog.js diff --git a/app/panels/query_editor/components/entity_attrs_dialog/entity_attrs_dialog.js b/app/panels/query_editor/components/inspector_query_ents/entity_attrs_dialog.js similarity index 100% rename from app/panels/query_editor/components/entity_attrs_dialog/entity_attrs_dialog.js rename to app/panels/query_editor/components/inspector_query_ents/entity_attrs_dialog.js diff --git a/app/panels/query_editor/components/entity_attrs_dialog/hooks.js b/app/panels/query_editor/components/inspector_query_ents/hooks.js similarity index 67% rename from app/panels/query_editor/components/entity_attrs_dialog/hooks.js rename to app/panels/query_editor/components/inspector_query_ents/hooks.js index 5532430..3a8f03d 100644 --- a/app/panels/query_editor/components/entity_attrs_dialog/hooks.js +++ b/app/panels/query_editor/components/inspector_query_ents/hooks.js @@ -1,24 +1,53 @@ /* Парус 8 - Панели мониторинга - Редактор запросов - Пользовательские хуки диалога настройки атрибутов сущности + Пользовательские хуки для работы с сущностями */ //--------------------- //Подключение библиотек //--------------------- -import { useState, useContext, useEffect, useCallback } from "react"; //Классы React +import { useContext, useCallback, useEffect, useState } from "react"; //Классы React import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером import { object2Base64XML } from "../../../../core/utils"; //Вспомогательные функции -//------------------------------------ -//Вспомогательные функции и компоненты -//------------------------------------ - //----------- //Тело модуля //----------- +//Работа с сущностями запроса +const useQueryEntities = query => { + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Добавление сущности в запрос + const addEnt = useCallback( + async (name, type) => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_ADD", args: { NRN: query, SNAME: name, STYPE: type }, loader: false }); + }, + [query, executeStored] + ); + + //Удаление сущности из запроса + const removeEnt = useCallback( + async ent => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_REMOVE", args: { NRN: query, SID: ent }, loader: false }); + }, + [query, executeStored] + ); + + //Сохранение координат сущности на диаграммем + const setEntPosition = useCallback( + async (ent, x, y) => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_POSITION_SET", args: { NRN: query, SID: ent, NX: x, NY: y }, loader: false }); + }, + [query, executeStored] + ); + + //Возвращаем интерфейс хука + return { addEnt, removeEnt, setEntPosition }; +}; + //Работа с атрибутами сущности const useEntityAttrs = (query, entity) => { //Собственное состояние - флаг загрузки @@ -89,4 +118,4 @@ const useEntityAttrs = (query, entity) => { //Интерфейс модуля //---------------- -export { useEntityAttrs }; +export { useQueryEntities, useEntityAttrs }; diff --git a/app/panels/query_editor/components/inspector_query_ents/inspector_query_ents.js b/app/panels/query_editor/components/inspector_query_ents/inspector_query_ents.js new file mode 100644 index 0000000..118e014 --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_ents/inspector_query_ents.js @@ -0,0 +1,112 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Компонент инспектора - Сущности запроса +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useContext, useState } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Icon, Button } from "@mui/material"; //Интерфейсные элементы +import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений приложения +import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора +import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы +import { ENTITY_DATA_SHAPE } from "../entity/entity"; //Описание сущности +import { EntityAddDialog } from "./entity_add_dialog"; //Диалог добавления сущности +import { EntityAttrsDialog } from "./entity_attrs_dialog"; //Диалог настройки атрибутов сущности +import { useQueryEntities } from "./hooks"; //Хуки для работы с сущностями на сервере + +//----------- +//Тело модуля +//----------- + +//Компонент инспектора - Сущности запроса +const InspectorQueryEntities = ({ query, entity, onOptionsChanged }) => { + //Отображение диалога добавления сущности + const [openEntityAddDialog, setOpenEntityAddDialog] = useState(false); + + //Отображение диалога настройки атрибутов сущности + const [openEntityAttrsDialog, setOpenEntityAttrsDialog] = useState(false); + + //Работа с сущностями на сервере + const { addEnt, removeEnt } = useQueryEntities(query); + + //Подключение к контексту сообщений + const { showMsgWarn } = useContext(MessagingСtx); + + //Уведомление родителя об изменении свойств + const notifyOptionsChanged = () => onOptionsChanged && onOptionsChanged(); + + //При нажатии на кнопку добавлении сущности в запрос + const handleEntityAddClick = () => setOpenEntityAddDialog(true); + + //При нажатии на кнопку настройки атрибутов сущности + const handleEntityAttrsClick = () => setOpenEntityAttrsDialog(true); + + //При нажатии на кнопку даления сущности из запроса + const handleEntityRemoveClick = () => + showMsgWarn(`Удалить сущность "${entity.title}"?`, async () => { + if (entity?.id) { + await removeEnt(entity.id); + notifyOptionsChanged(); + } + }); + + //Закрытие диалога добавления сущности по "Отмена" + const handleEntityAddDialogCancel = () => setOpenEntityAddDialog(false); + + //Закрытие диалога добавления сущности по "ОК" + const handleEntityAddDialogOk = async values => { + await addEnt(values.name, "VIEW"); + setOpenEntityAddDialog(false); + notifyOptionsChanged(); + }; + + //Закрытие диалога настройки атрибутов сущности по "Отмена" + const handleEntityAttrsDialogCancel = () => setOpenEntityAttrsDialog(false); + + //Закрытие диалога настройки атрибутов сущности по "ОК" + const handleEntityAttrsDialogOk = () => { + notifyOptionsChanged(); + setOpenEntityAttrsDialog(); + }; + + //Формирование представления + return ( + <> + {openEntityAddDialog && } + {openEntityAttrsDialog && ( + + )} + + {entity && ( + <> + + + + + )} + + ); +}; + +//Контроль свойств компонента - Компонент инспектора - Сущности запроса +InspectorQueryEntities.propTypes = { + query: PropTypes.number.isRequired, + entity: ENTITY_DATA_SHAPE, + onOptionsChanged: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { InspectorQueryEntities }; diff --git a/app/panels/query_editor/components/inspector_query_rls/hooks.js b/app/panels/query_editor/components/inspector_query_rls/hooks.js new file mode 100644 index 0000000..53af628 --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_rls/hooks.js @@ -0,0 +1,46 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Пользовательские хуки для работы со связями +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { useContext, useCallback } from "react"; //Классы React +import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером + +//----------- +//Тело модуля +//----------- + +//Работа со связами запроса +const useQueryRelations = query => { + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Добавление отношения сущностей в запрос + const addRl = useCallback( + async (source, target) => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_RL_ADD", args: { NRN: query, SSOURCE: source, STARGET: target }, loader: false }); + }, + [query, executeStored] + ); + + //Удаление отношения сущностей из запроса + const removeRl = useCallback( + async rl => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_RL_REMOVE", args: { NRN: query, SID: rl }, loader: false }); + }, + [query, executeStored] + ); + + //Возвращаем интерфейс хука + return { addRl, removeRl }; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { useQueryRelations }; diff --git a/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js b/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js new file mode 100644 index 0000000..7295c3c --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js @@ -0,0 +1,61 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Компонент инспектора - Связи запроса +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Icon, Button } from "@mui/material"; //Интерфейсные элементы +import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений приложения +import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы +import { RELATION_DATA_SHAPE } from "../relation/relation"; //Описание связи +import { useQueryRelations } from "./hooks"; //Хуки для работы со связями + +//----------- +//Тело модуля +//----------- + +//Компонент инспектора - Связи запроса +const InspectorQueryRelations = ({ query, relation, onOptionsChanged }) => { + //Подключение к контексту сообщений + const { showMsgWarn } = useContext(MessagingСtx); + + //Работа со связями на сервере + const { removeRl } = useQueryRelations(query); + + //Уведомление родителя об изменении свойств + const notifyOptionsChanged = () => onOptionsChanged && onOptionsChanged(); + + //При нажатии на кнопку даления связи из запроса + const handleRelationRemoveClick = () => + showMsgWarn(`Удалить связь "${relation.source}" - "${relation.target}"?`, async () => { + if (relation?.id) { + await removeRl(relation.id); + notifyOptionsChanged(); + } + }); + + //Формирование представления + return ( + + ); +}; + +//Контроль свойств компонента - Компонент инспектора - Связи запроса +InspectorQueryRelations.propTypes = { + query: PropTypes.number.isRequired, + relation: RELATION_DATA_SHAPE.isRequired, + onOptionsChanged: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { InspectorQueryRelations }; diff --git a/app/panels/query_editor/components/queries_manager/hooks.js b/app/panels/query_editor/components/queries_manager/hooks.js new file mode 100644 index 0000000..6d6bb51 --- /dev/null +++ b/app/panels/query_editor/components/queries_manager/hooks.js @@ -0,0 +1,116 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Пользовательские хуки для работы с запросами +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { useState, useContext, useEffect, useCallback } from "react"; //Классы React +import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером + +//----------- +//Тело модуля +//----------- + +//Работа с запросами +const useQueries = () => { + //Собственное состояние - флаг инициализированности + const [isInit, setInit] = useState(false); + + //Собственное состояние - флаг загрузки + const [isLoading, setLoading] = useState(false); + + //Собственное состояние - флаг необходимости обновления + const [refresh, setRefresh] = useState(true); + + //Собственное состояние - данные + const [data, setData] = useState(null); + + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Обновление данных + const doRefresh = () => setRefresh(true); + + //Добавление запроса + const insertQuery = useCallback( + async (code, name) => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_INSERT", args: { SCODE: code, SNAME: name }, loader: false }); + setRefresh(true); + }, + [executeStored] + ); + + //Изменение запроса + const updateQuery = useCallback( + async (query, code, name) => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_UPDATE", args: { NRN: query, SCODE: code, SNAME: name }, loader: false }); + setRefresh(true); + }, + [executeStored] + ); + + //Удаление запроса + const deleteQuery = useCallback( + async query => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_DELETE", args: { NRN: query }, loader: false }); + setRefresh(true); + }, + [executeStored] + ); + + //Установка флага готовности запроса + const setQueryReady = useCallback( + async (query, ready) => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_READY_SET", args: { NRN: query, NREADY: ready }, loader: false }); + setRefresh(true); + }, + [executeStored] + ); + + //Установка флага публичности запроса + const setQueryPbl = useCallback( + async (query, pbl) => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_PBL_SET", args: { NRN: query, NPBL: pbl }, loader: false }); + setRefresh(true); + }, + [executeStored] + ); + + //При необходимости получить/обновить данные + useEffect(() => { + //Загрузка данных с сервера + const loadData = async () => { + try { + setLoading(true); + const data = await executeStored({ + stored: "PKG_P8PANELS_QE.QUERY_LIST", + respArg: "COUT", + isArray: name => ["XQUERY"].includes(name), + attributeValueProcessor: (name, val) => (["code", "name"].includes(name) ? undefined : val), + loader: true + }); + setData(data?.XQUERIES?.XQUERY || []); + setInit(true); + } finally { + setRefresh(false); + setLoading(false); + } + }; + //Если надо обновить + if (refresh) + //Получим данные + loadData(); + }, [refresh, executeStored]); + + //Возвращаем интерфейс хука + return [data, insertQuery, updateQuery, deleteQuery, setQueryReady, setQueryPbl, doRefresh, isLoading, isInit]; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { useQueries }; diff --git a/app/panels/query_editor/components/queries_manager/queries_manager.js b/app/panels/query_editor/components/queries_manager/queries_manager.js index 3491408..2dbffea 100644 --- a/app/panels/query_editor/components/queries_manager/queries_manager.js +++ b/app/panels/query_editor/components/queries_manager/queries_manager.js @@ -13,7 +13,7 @@ import { Button, Icon } from "@mui/material"; //Интерфейсные ком import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения import { P8PConfigDialog } from "../../../../components/editors/p8p_config_dialog"; //Типовой диалог настройки -import { useQuery } from "../../hooks"; //Пользовательские хуки +import { useQueries } from "./hooks"; //Хуки для работы с запросами import { QueriesList } from "./queries_list"; //Список запросов import { QueryIUDialog } from "./query_iu_dialog"; //Диалог добавления/исправления запроса @@ -27,7 +27,7 @@ const QueriesManager = ({ current = null, onQuerySelect = null, onCancel = null const [modQuery, setModQuery] = useState(null); //Работа со списком запросов - const [queries, insertQuery, updateQuery, deleteQuery, setQueryReady, setQueryPbl] = useQuery(); + const [queries, insertQuery, updateQuery, deleteQuery, setQueryReady, setQueryPbl] = useQueries(); //Подключение к контексту сообщений const { showMsgWarn } = useContext(MessagingСtx); diff --git a/app/panels/query_editor/components/query_options/query_options.js b/app/panels/query_editor/components/query_options/query_options.js deleted file mode 100644 index 0fbb2f0..0000000 --- a/app/panels/query_editor/components/query_options/query_options.js +++ /dev/null @@ -1,122 +0,0 @@ -/* - Парус 8 - Панели мониторинга - Редактор запросов - Свойства запроса -*/ - -//--------------------- -//Подключение библиотек -//--------------------- - -import React, { useState, useContext } from "react"; //Классы React -import PropTypes from "prop-types"; //Контроль свойств компонента -import { Button, Icon } from "@mui/material"; //Интерфейсные компоненты MUI -import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений приложения -import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения -import { EntityAddDialog } from "../entity_add_dialog/entity_add_dialog"; //Диалог добавления сущности -import { EntityAttrsDialog } from "../entity_attrs_dialog/entity_attrs_dialog"; //Диалог настройки атрибутов сущности -import { P8PEditorBox } from "../../../../components/editors/p8p_editor_box"; //Контейнер параметров редактора -import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора -import { ENTITY_DATA_SHAPE } from "../entity/entity"; //Описание сущности -import { RELATION_DATA_SHAPE } from "../relation/relation"; //Описание связи - -//----------- -//Тело модуля -//----------- - -//Свойства запроса -const QueryOptions = ({ query, entity, relation, onEntityAdd, onEntityRemove, onRelationRemove, onQueryOptionsChanged }) => { - //Отображение диалога добавления сущности - const [openEntityAddDialog, setOpenEntityAddDialog] = useState(false); - - //Отображение диалога настройки атрибутов сущности - const [openEntityAttrsDialog, setOpenEntityAttrsDialog] = useState(false); - - //Подключение к контексту сообщений - const { showMsgWarn } = useContext(MessagingСtx); - - //При нажатии на кнопку добавлении сущности в запрос - const handleEntityAddClick = () => setOpenEntityAddDialog(true); - - //При нажатии на кнопку настройки атрибутов сущности - const handleEntityAttrsClick = () => setOpenEntityAttrsDialog(true); - - //При нажатии на кнопку даления сущности из запроса - const handleEntityRemoveClick = () => - showMsgWarn(`Удалить сущность "${entity.title}"?`, () => entity?.id && onEntityRemove && onEntityRemove(entity.id)); - - //При нажатии на кнопку даления связи из запроса - const handleRelationRemoveClick = () => - showMsgWarn( - `Удалить связь "${relation.source}" - "${relation.target}"?`, - () => relation?.id && onRelationRemove && onRelationRemove(relation.id) - ); - - //Закрытие диалога добавления сущности по "Отмена" - const handleEntityAddDialogCancel = () => setOpenEntityAddDialog(false); - - //Закрытие диалога добавления сущности по "ОК" - const handleEntityAddDialogOk = values => onEntityAdd && onEntityAdd(values.name, res => res && setOpenEntityAddDialog(false)); - - //Закрытие диалога настройки атрибутов сущности по "Отмена" - const handleEntityAttrsDialogCancel = () => setOpenEntityAttrsDialog(false); - - //Закрытие диалога настройки атрибутов сущности по "ОК" - const handleEntityAttrsDialogOk = () => { - onQueryOptionsChanged && onQueryOptionsChanged(); - setOpenEntityAttrsDialog(); - }; - - //Генерация содержимого - return ( - <> - {openEntityAddDialog && } - {openEntityAttrsDialog && ( - - )} - - - - - - {entity && ( - <> - - - - - )} - {relation && ( - <> - - - - )} - - - ); -}; - -//Контроль свойств компонента - Свойства запроса -QueryOptions.propTypes = { - query: PropTypes.number.isRequired, - entity: ENTITY_DATA_SHAPE, - relation: RELATION_DATA_SHAPE, - onEntityAdd: PropTypes.func, - onEntityRemove: PropTypes.func, - onRelationRemove: PropTypes.func, - onQueryOptionsChanged: PropTypes.func -}; - -//---------------- -//Интерфейс модуля -//---------------- - -export { QueryOptions }; diff --git a/app/panels/query_editor/hooks.js b/app/panels/query_editor/hooks.js index eb6a9f9..594722c 100644 --- a/app/panels/query_editor/hooks.js +++ b/app/panels/query_editor/hooks.js @@ -1,13 +1,13 @@ /* Парус 8 - Панели мониторинга - Редактор запросов - Пользовательские хуки + Пользовательские хуки для работы с метаданными запроса */ //--------------------- //Подключение библиотек //--------------------- -import { useState, useContext, useEffect, useCallback } from "react"; //Классы React +import { useState, useContext, useEffect } from "react"; //Классы React import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { NODE_TYPE } from "./common"; //Общие ресурсы и константы редактора @@ -88,8 +88,8 @@ const serverQueryData2QueryDiagram = (entities, relations) => { //Тело модуля //----------- -//Работа с запросами -const useQuery = () => { +//Работа с метаданными запроса +const useQuery = query => { //Собственное состояние - флаг инициализированности const [isInit, setInit] = useState(false); @@ -99,8 +99,11 @@ const useQuery = () => { //Собственное состояние - флаг необходимости обновления const [refresh, setRefresh] = useState(true); - //Собственное состояние - данные - const [data, setData] = useState(null); + //Собственное состояние - данные диаграммы + const [queryDiagram, setQueryDiagram] = useState(null); + + //Собственное состояние - данные настроек + const [queryOption, setQueryOption] = useState(null); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); @@ -108,51 +111,6 @@ const useQuery = () => { //Обновление данных const doRefresh = () => setRefresh(true); - //Добавление запроса - const insertQuery = useCallback( - async (code, name) => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_INSERT", args: { SCODE: code, SNAME: name }, loader: false }); - setRefresh(true); - }, - [executeStored] - ); - - //Изменение запроса - const updateQuery = useCallback( - async (query, code, name) => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_UPDATE", args: { NRN: query, SCODE: code, SNAME: name }, loader: false }); - setRefresh(true); - }, - [executeStored] - ); - - //Удаление запроса - const deleteQuery = useCallback( - async query => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_DELETE", args: { NRN: query }, loader: false }); - setRefresh(true); - }, - [executeStored] - ); - - //Установка флага готовности запроса - const setQueryReady = useCallback( - async (query, ready) => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_READY_SET", args: { NRN: query, NREADY: ready }, loader: false }); - setRefresh(true); - }, - [executeStored] - ); - - //Установка флага публичности запроса - const setQueryPbl = useCallback( - async (query, pbl) => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_PBL_SET", args: { NRN: query, NPBL: pbl }, loader: false }); - setRefresh(true); - }, - [executeStored] - ); - //При необходимости получить/обновить данные useEffect(() => { //Загрузка данных с сервера @@ -160,107 +118,14 @@ const useQuery = () => { try { setLoading(true); const data = await executeStored({ - stored: "PKG_P8PANELS_QE.QUERY_LIST", - respArg: "COUT", - isArray: name => ["XQUERY"].includes(name), - attributeValueProcessor: (name, val) => (["code", "name"].includes(name) ? undefined : val), - loader: true - }); - setData(data?.XQUERIES?.XQUERY || []); - setInit(true); - } finally { - setRefresh(false); - setLoading(false); - } - }; - //Если надо обновить - if (refresh) - //Получим данные - loadData(); - }, [refresh, executeStored]); - - //Возвращаем интерфейс хука - return [data, insertQuery, updateQuery, deleteQuery, setQueryReady, setQueryPbl, doRefresh, isLoading, isInit]; -}; - -//Работа с содержимым запроса -const useQueryDesc = query => { - //Собственное состояние - флаг инициализированности - const [isInit, setInit] = useState(false); - - //Собственное состояние - флаг загрузки - const [isLoading, setLoading] = useState(false); - - //Собственное состояние - флаг необходимости обновления - const [refresh, setRefresh] = useState(true); - - //Собственное состояние - данные - const [data, setData] = useState(null); - - //Подключение к контексту взаимодействия с сервером - const { executeStored } = useContext(BackEndСtx); - - //Обновление данных - const doRefresh = () => setRefresh(true); - - //Добавление сущности в запрос - const addEnt = useCallback( - async (name, type) => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_ADD", args: { NRN: query, SNAME: name, STYPE: type }, loader: false }); - setRefresh(true); - }, - [query, executeStored] - ); - - //Удаление сущности из запроса - const removeEnt = useCallback( - async ent => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_REMOVE", args: { NRN: query, SID: ent }, loader: false }); - setRefresh(true); - }, - [query, executeStored] - ); - - //Сохранение координат сущности на диаграммем - const setEntPosition = useCallback( - async (ent, x, y) => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_POSITION_SET", args: { NRN: query, SID: ent, NX: x, NY: y }, loader: false }); - }, - [query, executeStored] - ); - - //Добавление отношения сущностей в запрос - const addRl = useCallback( - async (source, target) => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_RL_ADD", args: { NRN: query, SSOURCE: source, STARGET: target }, loader: false }); - setRefresh(true); - }, - [query, executeStored] - ); - - //Удаление отношения сущностей из запроса - const removeRl = useCallback( - async rl => { - await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_RL_REMOVE", args: { NRN: query, SID: rl }, loader: false }); - setRefresh(true); - }, - [query, executeStored] - ); - - //При необходимости получить/обновить данные - useEffect(() => { - //Загрузка данных с сервера - const loadData = async () => { - try { - setLoading(true); - const data = await executeStored({ - stored: "PKG_P8PANELS_QE.QUERY_DESC", + stored: "PKG_P8PANELS_QE.QUERY", args: { NRN: query }, respArg: "COUT", - isArray: name => ["XENT", "XATTR", "XRL"].includes(name), + isArray: name => ["XENT", "XATTR", "XRL", "XARG"].includes(name), loader: true }); - setData(serverQueryData2QueryDiagram(data?.XENTS?.XENT || [], data?.XRLS?.XRL || [])); + setQueryDiagram(serverQueryData2QueryDiagram(data?.XENTS?.XENT || [], data?.XRLS?.XRL || [])); + setQueryOption({ args: data?.XOPT?.XARGS?.XARG || [], cond: data?.XOPT?.XCOND || null }); setInit(true); } finally { setRefresh(false); @@ -273,18 +138,21 @@ const useQueryDesc = query => { //Если есть для чего получать данные loadData(); //Нет идентификатора запроса - нет данных - else setData(null); + else { + setQueryDiagram(null); + setQueryOption(null); + } }, [refresh, query, executeStored]); //При изменении входных свойств - поднимаем флаг обновления useEffect(() => setRefresh(true), [query]); //Возвращаем интерфейс хука - return [data, addEnt, removeEnt, setEntPosition, addRl, removeRl, doRefresh, isLoading, isInit]; + return [queryDiagram, queryOption, doRefresh, isLoading, isInit]; }; //---------------- //Интерфейс модуля //---------------- -export { useQuery, useQueryDesc }; +export { useQuery }; diff --git a/app/panels/query_editor/query_editor.js b/app/panels/query_editor/query_editor.js index e67d03d..a1b0e3e 100644 --- a/app/panels/query_editor/query_editor.js +++ b/app/panels/query_editor/query_editor.js @@ -13,9 +13,11 @@ import { ApplicationСtx } from "../../context/application"; //Контекст import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Компоненты рабочего стола import { P8PEditorToolBar } from "../../components/editors/p8p_editor_toolbar"; //Панель инструментов редактора import { QueryDiagram } from "./components/query_diagram/query_diagram"; //Диаграмма запроса -import { QueryOptions } from "./components/query_options/query_options"; //Свойства запроса +import { Inspector } from "./components/inspector/inspector"; //Инспектор свойств import { QueriesManager } from "./components/queries_manager/queries_manager"; //Менеджер запросов -import { useQueryDesc } from "./hooks"; //Пользовательские хуки +import { useQuery } from "./hooks"; //Хуки для работы с метаданными запроса на сервере +import { useQueryRelations } from "./components/inspector_query_rls/hooks"; //Хуки для работы со связями запроса на сервере +import { useQueryEntities } from "./components/inspector_query_ents/hooks"; //Хуки для работы с сущностями запроса на сервере //--------- //Константы @@ -49,8 +51,14 @@ const QueryEditor = () => { //Отображения менеджера запросов const [openQueriesManager, setOpenQueriesManager] = useState(true); - //Получение данных запроса - const [queryDiagram, addEnt, removeEnt, setEntPosition, addRl, removeRl, doRefresh] = useQueryDesc(query); + //Получение метаданных с описанием запроса + const [queryDiagram, queryOption, doRefresh] = useQuery(query); + + //Работа с сущностями на сервере + const { removeEnt, setEntPosition } = useQueryEntities(query); + + //Работа со связями на сервере + const { addRl, removeRl } = useQueryRelations(query); //Подключение к контексту приложения const { setAppBarTitle } = useContext(ApplicationСtx); @@ -78,16 +86,11 @@ const QueryEditor = () => { //Обработка изменения положения сущности на диаграмме const handleEntityPositionChange = (ent, position) => setEntPosition(ent, position.x, position.y); - //Обработка добавления сущности в запрос - const handleEntityAdd = async (entName, cb) => { - await addEnt(entName, "VIEW"); - cb(true); - }; - //Обработка удаления сущности из запроса const handleEntityRemove = async ent => { await removeEnt(ent); if (entity && entity?.id === ent) cleanupEnRlSelection(); + doRefresh(); }; //Обработка выделения сущности @@ -100,15 +103,17 @@ const QueryEditor = () => { const handleRelationClick = rl => selectRelation(rl); //Обработка добавления отношения cущностей - const handleRelationAdd = (source, target) => { + const handleRelationAdd = async (source, target) => { cleanupEnRlSelection(); - addRl(source, target); + await addRl(source, target); + doRefresh(); }; //Обработка удаления отношения cущностей const handleRelationRemove = async rl => { await removeRl(rl); if (relation && relation?.id === rl) cleanupEnRlSelection(); + doRefresh(); }; //При нажатии на панели (пустом месте) диаграммы запроса @@ -173,15 +178,7 @@ const QueryEditor = () => { {toolBar} {query && ( - + )}