diff --git a/app/panels/prj_graph/index.js b/app/panels/prj_graph/index.js
new file mode 100644
index 0000000..ca5d2c3
--- /dev/null
+++ b/app/panels/prj_graph/index.js
@@ -0,0 +1,16 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Графики проектов
+ Панель мониторинга: Точка входа
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { PrjGraph } from "./prj_graph"; //Корневая панель графиков проекта
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export const RootClass = PrjGraph;
diff --git a/app/panels/prj_graph/layouts.js b/app/panels/prj_graph/layouts.js
new file mode 100644
index 0000000..f2605bf
--- /dev/null
+++ b/app/panels/prj_graph/layouts.js
@@ -0,0 +1,96 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Графики проектов
+ Дополнительная разметка и вёрстка клиентских элементов
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import { Icon, Stack, Link } from "@mui/material"; //Интерфейсные компоненты
+import { formatDateRF } from "../../core/utils"; //Вспомогательные процедуры и функции
+
+//---------
+//Константы
+//---------
+
+//Шаблон имени ячейки месяца
+const MONTH_COLUMN_REG_EXP = /[0-9]{4}_[0-9]{1,2}/;
+
+//-----------
+//Тело модуля
+//-----------
+
+//Формирование значения для плашки этапа
+const formatStageItemValue = (state, text) => {
+ const [stateText, icon] =
+ state == 0
+ ? ["Зарегистрирован", "app_registration"]
+ : state == 1
+ ? ["Открыт", "lock_open"]
+ : state == 2
+ ? ["Закрыт", "lock_outline"]
+ : state == 3
+ ? ["Согласован", "thumb_up_alt"]
+ : state == 4
+ ? ["Исполнение прекращено", "block"]
+ : ["Остановлен", "do_not_disturb_on"];
+ return (
+
+ {icon}
+ {text}
+
+ );
+};
+
+//Генерация представления ячейки заголовка группы
+export const groupCellRender = ({ group, pOnlineShowDocument }) => ({
+ cellStyle: { padding: "2px" },
+ data: (
+ pOnlineShowDocument({ unitCode: "Projects", document: group.name })}>
+ {group.caption}
+
+ )
+});
+
+//Генерация представления ячейки c данными
+export const dataCellRender = ({ row, columnDef, pOnlineShowDocument }) => {
+ if (MONTH_COLUMN_REG_EXP.test(columnDef.name)) {
+ const dF = new Date(row.DFROM);
+ const dT = new Date(row.DTO);
+ const [year, month] = columnDef.name.split("_");
+ const mF = new Date(year, month - 1, 1);
+ const mT = new Date(year, month, 0);
+ let cellStyle = {};
+ let cellProps = {};
+ let data = null;
+ if ((dF <= mF && dT >= mT) || (dF >= mF && dF <= mT) || (dT >= mF && dT <= mT)) {
+ if (year == dF.getFullYear() && month == dF.getMonth() + 1) data = formatStageItemValue(row.NSTATE, row.SRESP);
+ cellStyle = { backgroundColor: row.NSTATE == 0 ? "lightyellow" : row.NSTATE == 1 ? "lightgreen" : "lightblue", cursor: "pointer" };
+ cellProps = {
+ title: `${formatDateRF(dF)} - ${formatDateRF(dT)}`,
+ onClick: () => pOnlineShowDocument({ unitCode: "ProjectsStages", document: row.NRN })
+ };
+ }
+ return {
+ cellStyle: { padding: "2px", maxWidth: "30px", overflow: "visible", fontSize: "smaller", whiteSpace: "nowrap", ...cellStyle },
+ cellProps,
+ data
+ };
+ }
+ switch (columnDef.name) {
+ case "SJOB":
+ return {
+ cellProps: { title: row[columnDef.name] },
+ cellStyle: {
+ padding: "2px",
+ maxWidth: "300px",
+ textOverflow: "ellipsis",
+ overflow: "hidden",
+ whiteSpace: "pre",
+ fontSize: "smaller"
+ }
+ };
+ }
+};
diff --git a/app/panels/prj_graph/prj_graph.js b/app/panels/prj_graph/prj_graph.js
new file mode 100644
index 0000000..c4ba8ca
--- /dev/null
+++ b/app/panels/prj_graph/prj_graph.js
@@ -0,0 +1,93 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Графики проектов
+ Панель мониторинга: Корневая панель графиков проекта
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
+import { Grid, Box } from "@mui/material"; //Интерфейсные элементы
+import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
+import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
+import { ApplicationСtx } from "../../context/application"; //Контекст приложения
+import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
+import { dataCellRender, groupCellRender } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
+
+//-----------
+//Тело модуля
+//-----------
+
+//Графики проектов
+const PrjGraph = () => {
+ //Собственное состояние - таблица данных
+ const [dataGrid, setdataGrid] = useState({
+ dataLoaded: false,
+ columnsDef: [],
+ groups: [],
+ rows: [],
+ reload: true
+ });
+
+ //Подключение к контексту приложения
+ const { pOnlineShowDocument } = useContext(ApplicationСtx);
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //Загрузка данных таблицы с сервера
+ const loadData = useCallback(async () => {
+ if (dataGrid.reload) {
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_PROJECTS.GRAPH",
+ args: {},
+ respArg: "COUT",
+ attributeValueProcessor: (name, val) => (["caption", "name", "parent"].includes(name) ? undefined : val)
+ });
+ setdataGrid(pv => ({
+ ...pv,
+ columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
+ rows: [...(data.XROWS || [])],
+ groups: [...(data.XGROUPS || [])],
+ dataLoaded: true,
+ reload: false
+ }));
+ }
+ }, [dataGrid.reload, executeStored]);
+
+ //При необходимости обновить данные таблицы
+ useEffect(() => {
+ loadData();
+ }, [dataGrid.reload, loadData]);
+
+ //Генерация содержимого
+ return (
+
+
+
+
+ {dataGrid.dataLoaded ? (
+ dataCellRender({ ...prms, pOnlineShowDocument })}
+ groupCellRender={prms => groupCellRender({ ...prms, pOnlineShowDocument })}
+ />
+ ) : null}
+
+
+
+
+ );
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { PrjGraph };
diff --git a/img/prj_graph.jpg b/img/prj_graph.jpg
new file mode 100644
index 0000000..355b6c8
Binary files /dev/null and b/img/prj_graph.jpg differ