From 6c0d021a65432cd4ce9d85bd404f60956258f0d0 Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Wed, 25 Oct 2023 18:57:43 +0300 Subject: [PATCH] =?UTF-8?q?WEB=20APP:=20=D0=9F=D0=B0=D0=BD=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=20"=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=D0=BE=D0=B2"=20-=20=D0=BC=D0=BE=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D1=80=D0=B5=D1=81?= =?UTF-8?q?=D1=83=D1=80=D1=81=D0=BE=D0=B2,=20=D1=81=D0=BE=D1=85=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=B7=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B9=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=20(=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=D0=BE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/panels/prj_jobs/lab_plan_fot_dtl.js | 122 ++++++++++++++ app/panels/prj_jobs/lab_plan_jobs_dtl.js | 202 ++++++++++++++++++++++ app/panels/prj_jobs/prj_jobs.js | 183 ++++++++++++++------ app/panels/prj_jobs/res_mon.js | 203 +++++++++++++++++++++++ 4 files changed, 663 insertions(+), 47 deletions(-) create mode 100644 app/panels/prj_jobs/lab_plan_fot_dtl.js create mode 100644 app/panels/prj_jobs/lab_plan_jobs_dtl.js create mode 100644 app/panels/prj_jobs/res_mon.js 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 ? ( + + {title} + + + + + + + + ) : 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 ? ( + + {title} + + planJobsDtlDataCellRender({ ...prms, onProjectClick: handleProjectClick })} + onOrderChanged={handlePlanJobsDtlDGOrderChanged} + onPagesCountChanged={handlePlanJobsDtlDGPagesCountChanged} + /> + + + + + + ) : 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 };