diff --git a/app/panels/prj_jobs/lab_plan_fot_dtl.js b/app/panels/prj_jobs/lab_plan_fot_dtl.js
new file mode 100644
index 0000000..37027eb
--- /dev/null
+++ b/app/panels/prj_jobs/lab_plan_fot_dtl.js
@@ -0,0 +1,122 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Работы проектов
+ Компонент панели: Детализация плановой трудоёмкости по ФОТ
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useContext, useState, useCallback, useEffect } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Dialog, DialogContent, DialogActions, Button, DialogTitle } from "@mui/material"; //Интерфейсные элементы
+import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
+import { ApplicationСtx } from "../../context/application"; //Контекст приложения
+import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции
+import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы
+import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
+import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
+
+//-----------
+//Тело модуля
+//-----------
+
+//Детализация плановой трудоёмкости по ФОТ
+const LabPlanFOTDtl = ({ periodId, title, onHide }) => {
+ //Состояние таблицы детализации плановой трудоёмкости по ФОТ ресурса
+ const [planFOTDtl, setPlanFOTDtl] = useState({
+ dataLoaded: false,
+ columnsDef: [],
+ orders: [],
+ rows: [],
+ reload: true,
+ pageNumber: 1,
+ morePages: true
+ });
+
+ //Подключение к контексту приложения
+ const { configSystemPageSize } = useContext(ApplicationСtx);
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
+
+ //Загрузка детализации плановой трудоёмкости по ФОТ для ресурса
+ const loadPlanFOTDtl = useCallback(async () => {
+ if (planFOTDtl.reload) {
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_PROJECTS.JB_PERIODS_LIST_PLAN_FOT",
+ args: {
+ NJB_PERIODS: periodId,
+ CORDERS: { VALUE: object2Base64XML(planFOTDtl.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
+ NPAGE_NUMBER: planFOTDtl.pageNumber,
+ NPAGE_SIZE: configSystemPageSize,
+ NINCLUDE_DEF: planFOTDtl.dataLoaded ? 0 : 1
+ },
+ respArg: "COUT"
+ });
+ setPlanFOTDtl(pv => ({
+ ...pv,
+ columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
+ rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
+ dataLoaded: true,
+ reload: false,
+ morePages: (data.XROWS || []).length >= configSystemPageSize
+ }));
+ }
+ }, [
+ periodId,
+ planFOTDtl.reload,
+ planFOTDtl.orders,
+ planFOTDtl.dataLoaded,
+ planFOTDtl.pageNumber,
+ executeStored,
+ configSystemPageSize,
+ SERV_DATA_TYPE_CLOB
+ ]);
+
+ //При изменении состояния сортировки в детализации плана ФОТ по строке ресурса
+ const handlePlanFOTDtlDGOrderChanged = ({ orders }) => setPlanFOTDtl(pv => ({ ...pv, orders, pageNumber: 1, reload: true }));
+
+ //При изменении количества отображаемых страниц в в детализации плана ФОТ по строке ресурса
+ const handlePlanFOTDtlDGPagesCountChanged = () => setPlanFOTDtl(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
+
+ //При необходимости обновить данные
+ useEffect(() => {
+ loadPlanFOTDtl();
+ }, [planFOTDtl.reload, loadPlanFOTDtl]);
+
+ //Генерация содержимого
+ return planFOTDtl.dataLoaded ? (
+
+ ) : null;
+};
+
+//Контроль свойств - Детализация плановой трудоёмкости по ФОТ
+LabPlanFOTDtl.propTypes = {
+ periodId: PropTypes.number.isRequired,
+ title: PropTypes.string.isRequired,
+ onHide: PropTypes.func.isRequired
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { LabPlanFOTDtl };
diff --git a/app/panels/prj_jobs/lab_plan_jobs_dtl.js b/app/panels/prj_jobs/lab_plan_jobs_dtl.js
new file mode 100644
index 0000000..5f829c3
--- /dev/null
+++ b/app/panels/prj_jobs/lab_plan_jobs_dtl.js
@@ -0,0 +1,202 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Работы проектов
+ Компонент панели: Детализация плановой трудоёмкости по графику
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useContext, useState, useCallback, useEffect } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Dialog, DialogContent, DialogActions, Button, DialogTitle, Stack, Icon, Link } from "@mui/material"; //Интерфейсные элементы
+import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
+import { ApplicationСtx } from "../../context/application"; //Контекст приложения
+import { object2Base64XML, formatDateRF } from "../../core/utils"; //Вспомогательные функции
+import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы
+import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
+import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+//Формирование значения для колонки "Состояние" этапа
+const formatJobStatusValue = value => {
+ const [text, icon] =
+ value == 0
+ ? ["Не начата", "not_started"]
+ : value == 1
+ ? ["Выполняется", "loop"]
+ : value == 2
+ ? ["Выполнена", "task_alt"]
+ : value == 3
+ ? ["Остановлена", "do_not_disturb_on"]
+ : ["Отменена", "cancel"];
+ return (
+
+ {icon}
+
+ );
+};
+
+//Форматирование значений колонок в таблице детализации трудоёмкости по графику
+const planJobsDtlValueFormatter = ({ value, columnDef }) => {
+ switch (columnDef.name) {
+ case "NJOB_STATE":
+ return formatJobStatusValue(value);
+ case "DJOB_BEG":
+ case "DJOB_END":
+ return formatDateRF(value);
+ }
+ return value;
+};
+
+//Генерация представления ячейки заголовка в таблице детализации трудоёмкости по графику
+const planJobsDtlHeadCellRender = ({ columnDef }) => {
+ switch (columnDef.name) {
+ case "NJOB_STATE":
+ return {
+ stackProps: { justifyContent: "center" },
+ cellProps: { align: "center" }
+ };
+ }
+};
+
+//Генерация представления ячейки c данными в таблице детализации трудоёмкости по графику
+const planJobsDtlDataCellRender = ({ row, columnDef, onProjectClick }) => {
+ switch (columnDef.name) {
+ case "SPRJ":
+ return {
+ data: row[columnDef.name] ? (
+ (onProjectClick ? onProjectClick({ sender: row }) : null)}
+ >
+ {row[columnDef.name]}
+
+ ) : (
+ row[columnDef.name]
+ )
+ };
+ case "NSTATE":
+ return {
+ cellProps: { align: "center" },
+ data: formatJobStatusValue(row[columnDef.name])
+ };
+ }
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Детализация плановой трудоёмкости по графику
+const LabPlanJobsDtl = ({ periodId, title, onHide, onProjectClick }) => {
+ //Состояние таблицы детализации плановой трудоёмкости по графику
+ const [planJobsDtl, setPlanJobsDtl] = useState({
+ dataLoaded: false,
+ columnsDef: [],
+ orders: [],
+ rows: [],
+ reload: true,
+ pageNumber: 1,
+ morePages: true
+ });
+
+ //Подключение к контексту приложения
+ const { configSystemPageSize } = useContext(ApplicationСtx);
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
+
+ //Загрузка детализации плановой трудоёмкости по ФОТ для ресурса
+ const loadPlanFOTDtl = useCallback(async () => {
+ if (planJobsDtl.reload) {
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_PROJECTS.JB_PERIODS_LIST_PLAN_JOBS",
+ args: {
+ NJB_PERIODS: periodId,
+ CORDERS: { VALUE: object2Base64XML(planJobsDtl.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
+ NPAGE_NUMBER: planJobsDtl.pageNumber,
+ NPAGE_SIZE: configSystemPageSize,
+ NINCLUDE_DEF: planJobsDtl.dataLoaded ? 0 : 1
+ },
+ respArg: "COUT"
+ });
+ setPlanJobsDtl(pv => ({
+ ...pv,
+ columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
+ rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
+ dataLoaded: true,
+ reload: false,
+ morePages: (data.XROWS || []).length >= configSystemPageSize
+ }));
+ }
+ }, [
+ periodId,
+ planJobsDtl.reload,
+ planJobsDtl.orders,
+ planJobsDtl.dataLoaded,
+ planJobsDtl.pageNumber,
+ executeStored,
+ configSystemPageSize,
+ SERV_DATA_TYPE_CLOB
+ ]);
+
+ //При изменении состояния сортировки в детализации плана ФОТ по строке ресурса
+ const handlePlanJobsDtlDGOrderChanged = ({ orders }) => setPlanJobsDtl(pv => ({ ...pv, orders, pageNumber: 1, reload: true }));
+
+ //При изменении количества отображаемых страниц в в детализации плана ФОТ по строке ресурса
+ const handlePlanJobsDtlDGPagesCountChanged = () => setPlanJobsDtl(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
+
+ //При нажатии на проект в таблице детализацц
+ const handleProjectClick = ({ sender }) => (onProjectClick ? onProjectClick({ sender }) : null);
+
+ //При необходимости обновить данные
+ useEffect(() => {
+ loadPlanFOTDtl();
+ }, [planJobsDtl.reload, loadPlanFOTDtl]);
+
+ //Генерация содержимого
+ return planJobsDtl.dataLoaded ? (
+
+ ) : null;
+};
+
+//Контроль свойств - Детализация плановой трудоёмкости по графику
+LabPlanJobsDtl.propTypes = {
+ periodId: PropTypes.number.isRequired,
+ title: PropTypes.string.isRequired,
+ onHide: PropTypes.func.isRequired,
+ onProjectClick: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { LabPlanJobsDtl };
diff --git a/app/panels/prj_jobs/prj_jobs.js b/app/panels/prj_jobs/prj_jobs.js
index 9f40ec6..6b05f07 100644
--- a/app/panels/prj_jobs/prj_jobs.js
+++ b/app/panels/prj_jobs/prj_jobs.js
@@ -13,9 +13,10 @@ import { Drawer, Fab, Box, Grid, List, ListItemButton, ListItemText, ListItemIco
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
+import { formatDateJSONDateOnly } from "../../core/utils"; //Вспомогательные функции
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { P8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
-import { formatDateJSONDateOnly } from "../../core/utils"; //Вспомогательные функции
+import { ResMon } from "./res_mon"; //Монитор ресурсов
//---------
//Константы
@@ -29,6 +30,7 @@ const GANTT_WIDTH = "98vw";
//Стили
const STYLES = {
+ PROJECTS_LIST_SAVE_BUTTON: { backgroundColor: "orange" },
PROJECTS_LIST_ITEM_NOJOBS: { backgroundColor: "#ff000045" },
PROJECTS_LIST_ITEM_PRIMARY: { wordWrap: "break-word" },
PROJECTS_LIST_ITEM_SECONDARY: { wordWrap: "break-word", fontSize: "0.5rem", textTransform: "uppercase" },
@@ -38,7 +40,9 @@ const STYLES = {
PROJECTS_BUTTON: { position: "absolute" },
PROJECTS_DRAWER: { width: "250px", flexShrink: 0, [`& .MuiDrawer-paper`]: { width: "250px", boxSizing: "border-box" } },
GANTT_CONTAINER: { height: GANTT_HEIGHT, width: GANTT_WIDTH },
- GANTT_TITLE: { paddingLeft: "100px", paddingRight: "100px" }
+ GANTT_TITLE: { paddingLeft: "100px", paddingRight: "120px" },
+ PERIODS_BUTTON: { position: "absolute", right: "20px" },
+ PERIODS_DRAWER: { width: "1000px", flexShrink: 0, [`& .MuiDrawer-paper`]: { width: "1000px", boxSizing: "border-box" } }
};
//------------------------------------
@@ -153,8 +157,15 @@ ProjectsList.propTypes = {
const PrjJobs = () => {
//Собственное состояние
let [state, setState] = useState({
+ needSave: false,
showProjectsList: false,
+ showPeriodsList: false,
init: false,
+ dateBegin: null,
+ dateFact: null,
+ durationMeas: null,
+ labMeas: null,
+ resourceStatus: null,
ident: null,
projects: [],
projectsLoaded: false,
@@ -175,18 +186,21 @@ const PrjJobs = () => {
const { executeStored } = useContext(BackEndСtx);
//Загрузка списка проектов
- const loadProjects = useCallback(async () => {
- if (!state.projectsLoaded) {
- const data = await executeStored({
- stored: "PKG_P8PANELS_PROJECTS.JB_PRJCTS_LIST",
- args: {
- NIDENT: state.ident
- },
- respArg: "COUT"
- });
- setState(pv => ({ ...pv, projectsLoaded: true, projects: [...(data?.XPROJECTS || [])] }));
- }
- }, [executeStored, state.ident, state.projectsLoaded]);
+ const loadProjects = useCallback(
+ async (force = false) => {
+ if (!state.projectsLoaded || force) {
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_PROJECTS.JB_PRJCTS_LIST",
+ args: {
+ NIDENT: state.ident
+ },
+ respArg: "COUT"
+ });
+ setState(pv => ({ ...pv, projectsLoaded: true, projects: [...(data?.XPROJECTS || [])] }));
+ }
+ },
+ [executeStored, state.ident, state.projectsLoaded]
+ );
//Загрузка списка работ проекта
const loadProjectJobs = useCallback(
@@ -212,25 +226,55 @@ const PrjJobs = () => {
[executeStored, state.ident, state.selectedProject]
);
+ //Изменение работы в графике
+ const modifyJob = useCallback(
+ async (job, dateFrom, dateTo) => {
+ try {
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_PROJECTS.JB_JOBS_MODIFY_PERIOD",
+ args: {
+ NJB_JOBS: job,
+ DDATE_FROM: new Date(dateFrom),
+ DDATE_TO: new Date(dateTo),
+ DBEGIN: new Date(state.dateBegin)
+ }
+ });
+ setState(pv => ({ ...pv, resourceStatus: data.NRESOURCE_STATUS, needSave: true }));
+ loadProjects(true);
+ } finally {
+ loadProjectJobs(true);
+ }
+ },
+ [executeStored, loadProjectJobs, loadProjects, state.dateBegin]
+ );
+
//Инициализация данных балансировки
const initJobs = useCallback(async () => {
if (!state.init) {
const data = await executeStored({
stored: "PKG_P8PANELS_PROJECTS.JB_INIT",
args: {
- DBEGIN: null,
- DFACT: null,
- NDURATION_MEAS: 0,
- SLAB_MEAS: null,
- NINCLUDE_DEF: null,
+ DBEGIN: state.dateBegin ? new Date(state.dateBegin) : null,
+ DFACT: state.dateFact ? new Date(state.dateFact) : null,
+ NDURATION_MEAS: state.durationMeas,
+ SLAB_MEAS: state.labMeas,
NIDENT: state.ident
}
});
- setState(pv => ({ ...pv, init: true, ident: data.NIDENT }));
+ setState(pv => ({
+ ...pv,
+ init: true,
+ dateBegin: data.DBEGIN,
+ dateFact: data.DFACT,
+ durationMeas: data.NDURATION_MEAS,
+ labMeas: data.SLAB_MEAS,
+ resourceStatus: data.NRESOURCE_STATUS,
+ ident: data.NIDENT
+ }));
}
- }, [state.init, state.ident, executeStored]);
+ }, [state.init, state.dateBegin, state.dateFact, state.durationMeas, state.labMeas, state.ident, executeStored]);
- //При смене идентификатора процесса
+ //Грузим список проектов при смене идентификатора процесса
useEffect(() => {
if (state.ident) loadProjects();
}, [state.ident, loadProjects]);
@@ -246,28 +290,35 @@ const PrjJobs = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ //Выбор проекта
+ const selectPoject = (project, projectDocRn) => {
+ setState(pv => ({
+ ...pv,
+ selectedProject: project,
+ selectedProjectDocRn: projectDocRn,
+ selectedProjectJobsLoaded: false,
+ selectedProjectTasks: [],
+ selectedProjectGanttDef: {},
+ showProjectsList: false
+ }));
+ };
+
+ //Сброс выбора проекта
+ const unselectProject = () =>
+ setState(pv => ({
+ ...pv,
+ selectedProjectJobsLoaded: false,
+ selectedProject: null,
+ selectedProjectDocRn: null,
+ selectedProjectTasks: [],
+ selectedProjectGanttDef: {},
+ showProjectsList: false
+ }));
+
//Обработка нажатия на элемент в списке проектов
const handleProjectClick = project => {
- if (state.selectedProject != project.NRN) {
- setState(pv => ({
- ...pv,
- selectedProject: project.NRN,
- selectedProjectDocRn: project.NPROJECT,
- selectedProjectJobsLoaded: false,
- selectedProjectTasks: [],
- selectedProjectGanttDef: {},
- showProjectsList: false
- }));
- } else
- setState(pv => ({
- ...pv,
- selectedProjectJobsLoaded: false,
- selectedProject: null,
- selectedProjectDocRn: null,
- selectedProjectTasks: [],
- selectedProjectGanttDef: {},
- showProjectsList: false
- }));
+ if (state.selectedProject != project.NRN) selectPoject(project.NRN, project.NPROJECT);
+ else unselectProject();
};
//Отработка нажатия на заголовок плана-графика
@@ -280,10 +331,7 @@ const PrjJobs = () => {
console.log(task);
console.log(start);
console.log(end);
- if (isMain) {
- console.log("ЭТО - ГЛАВНОЕ. ПОЙДЁМ НА СЕРВЕР...");
- loadProjectJobs(true);
- }
+ if (isMain) modifyJob(task.rn, start, end);
};
//Обработка изменения прогресса задачи в диаграмме Гантта
@@ -305,11 +353,23 @@ const PrjJobs = () => {
}
};
+ //Обработка нажатия на проект в таблице детализации трудоёмкости по плану-графику монитора ресурсов
+ const handlePlanJobsDtlProjectClick = ({ sender }) => {
+ setState(pv => ({ ...pv, showPeriodsList: false }));
+ if (state.selectedProject != sender.NJB_PRJCTS) selectPoject(sender.NJB_PRJCTS, sender.NPROJECT);
+ };
+
//Генерация содержимого
return (
setState(pv => ({ ...pv, showProjectsList: !pv.showProjectsList }))}>
Проекты
+ {state.needSave ? (
+ <>
+
+ save
+ >
+ ) : null}
{
sx={STYLES.PROJECTS_DRAWER}
>
{state.projectsLoaded ? (
-
+ <>
+ {state.needSave ? (
+
+
+
+ save
+
+
+
+
+ ) : null}
+
+ >
) : null}
+ setState(pv => ({ ...pv, showPeriodsList: !pv.showPeriodsList }))}>
+ Ресурсы
+ {[0, 1].includes(state.resourceStatus) ? (
+ <>
+
+ {state.resourceStatus === 0 ? "done" : "error"}
+ >
+ ) : null}
+
+ setState(pv => ({ ...pv, showPeriodsList: false }))}
+ sx={STYLES.PERIODS_DRAWER}
+ >
+ {state.ident ? : null}
+
{state.init == true ? (
diff --git a/app/panels/prj_jobs/res_mon.js b/app/panels/prj_jobs/res_mon.js
new file mode 100644
index 0000000..cf9d095
--- /dev/null
+++ b/app/panels/prj_jobs/res_mon.js
@@ -0,0 +1,203 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Работы проектов
+ Компонент панели: Монитор ресурсов
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useContext, useState, useCallback, useEffect } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Icon, Stack, Link } from "@mui/material"; //Интерфейсные элементы
+import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
+import { ApplicationСtx } from "../../context/application"; //Контекст приложения
+import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции
+import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
+import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
+import { LabPlanFOTDtl } from "./lab_plan_fot_dtl"; //Детализация плановой трудоёмкости по ФОТ
+import { LabPlanJobsDtl } from "./lab_plan_jobs_dtl"; //Детализация плановой трудоёмкости по графику
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+//Генерация представления ячейки c данными в таблице периодов балансировки
+const periodsDataCellRender = ({ row, columnDef, onLabPlanFOTClick, onLabPlanJobsClick }) => {
+ switch (columnDef.name) {
+ case "NLAB_PLAN_FOT":
+ return {
+ data: row[columnDef.name] ? (
+ (onLabPlanFOTClick ? onLabPlanFOTClick({ sender: row }) : null)}
+ >
+ {row[columnDef.name]}
+
+ ) : (
+ row[columnDef.name]
+ )
+ };
+ case "NLAB_DIFF_RPT_FOT":
+ return { data: {row[columnDef.name]}
};
+ case "NLAB_PLAN_JOBS":
+ return {
+ data: row[columnDef.name] ? (
+ (onLabPlanJobsClick ? onLabPlanJobsClick({ sender: row }) : null)}
+ >
+ {row[columnDef.name]}
+
+ ) : (
+ row[columnDef.name]
+ )
+ };
+ case "NLAB_DIFF_JOBS_FOT":
+ return {
+ data: (
+
+ {row[columnDef.name]}
+ {row[columnDef.name] <= 0 ? "done" : "error"}
+
+ )
+ };
+ }
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Монитор ресурсов
+const ResMon = ({ ident, onPlanJobsDtlProjectClick }) => {
+ //Собственное состояние
+ const [state, setState] = useState({ displayPlanFOTDtl: null, titlePlanFOTDtl: null, displayPlanJobsDtl: null, titlePlanJobsDtl: null });
+
+ //Состояние таблицы периодов монитора ресурсов
+ const [peridos, setPeriods] = useState({
+ dataLoaded: false,
+ columnsDef: [],
+ orders: [],
+ rows: [],
+ reload: true,
+ pageNumber: 1,
+ morePages: true
+ });
+
+ //Подключение к контексту приложения
+ const { configSystemPageSize } = useContext(ApplicationСtx);
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
+
+ //Загрузка данных монитора балансировки периодов с сервера
+ const loadPeriods = useCallback(async () => {
+ if (peridos.reload) {
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_PROJECTS.JB_PERIODS_LIST",
+ args: {
+ NIDENT: ident,
+ CORDERS: { VALUE: object2Base64XML(peridos.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
+ NPAGE_NUMBER: peridos.pageNumber,
+ NPAGE_SIZE: configSystemPageSize,
+ NINCLUDE_DEF: peridos.dataLoaded ? 0 : 1
+ },
+ attributeValueProcessor: (name, val) => (name == "SPERIOD" ? undefined : val),
+ respArg: "COUT"
+ });
+ setPeriods(pv => ({
+ ...pv,
+ columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
+ rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
+ dataLoaded: true,
+ reload: false,
+ morePages: (data.XROWS || []).length >= configSystemPageSize
+ }));
+ }
+ }, [ident, peridos.reload, peridos.orders, peridos.dataLoaded, peridos.pageNumber, executeStored, configSystemPageSize, SERV_DATA_TYPE_CLOB]);
+
+ //При сокрытии детализации
+ const handleHideDtl = () =>
+ setState(pv => ({ ...pv, displayPlanFOTDtl: null, titlePlanFOTDtl: null, displayPlanJobsDtl: null, titlePlanJobsDtl: null }));
+
+ //При нажатии на плановую трудоёмкость по ФОТ
+ const handleLabPlanFOTClick = ({ sender }) =>
+ setState(pv => ({
+ ...pv,
+ displayPlanFOTDtl: sender.NRN,
+ titlePlanFOTDtl: `${sender.SPERIOD} - ${sender.SINS_DEPARTMENT} - ${sender.SFCMANPOWER} - ${sender.NLAB_PLAN_FOT}`
+ }));
+
+ //При нажатии на проект в списке детализации плановой трудоёмкости по графику
+ const handleLabPlanJobsClick = ({ sender }) =>
+ setState(pv => ({
+ ...pv,
+ displayPlanJobsDtl: sender.NRN,
+ titlePlanJobsDtl: `${sender.SPERIOD} - ${sender.SINS_DEPARTMENT} - ${sender.SFCMANPOWER} - ${sender.NLAB_PLAN_JOBS}`
+ }));
+
+ //При изменении состояния сортировки в таблице периодов балансировки
+ const handlePeriodsOrderChanged = ({ orders }) => setPeriods(pv => ({ ...pv, orders, pageNumber: 1, reload: true }));
+
+ //При изменении количества отображаемых страниц в таблице периодов балансировки
+ const handlePeriodsPagesCountChanged = () => setPeriods(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
+
+ //При нажатии на проект в таблице детализации трудоёмкости по плану-графику
+ const handlePlanJobsDtlProjectClick = ({ sender }) => (onPlanJobsDtlProjectClick ? onPlanJobsDtlProjectClick({ sender }) : null);
+
+ //При необходимости обновить данные
+ useEffect(() => {
+ loadPeriods();
+ }, [peridos.reload, loadPeriods]);
+
+ //Генерация содержимого
+ return (
+ <>
+ {peridos.dataLoaded ? (
+
+ periodsDataCellRender({ ...prms, onLabPlanFOTClick: handleLabPlanFOTClick, onLabPlanJobsClick: handleLabPlanJobsClick })
+ }
+ />
+ ) : null}
+ {state.displayPlanFOTDtl ? (
+
+ ) : null}
+ {state.displayPlanJobsDtl ? (
+
+ ) : null}
+ >
+ );
+};
+
+//Контроль свойств - Монитор ресурсов
+ResMon.propTypes = {
+ ident: PropTypes.number.isRequired,
+ onPlanJobsDtlProjectClick: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { ResMon };