/* Парус 8 - Панели мониторинга - ПУП - Работы проектов Панель мониторинга: Корневая панель работ проектов */ //--------------------- //Подключение библиотек //--------------------- import React, { useContext, useState, useCallback, useEffect } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Drawer, Fab, Box, Grid, List, ListItemButton, ListItemText, ListItemIcon, Icon, Typography } from "@mui/material"; //Интерфейсные элементы 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 { ResMon } from "./res_mon"; //Монитор ресурсов import { taskAttributeRenderer } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов //--------- //Константы //--------- //Высота диаграммы Ганта const GANTT_HEIGHT = "75vh"; //Ширина диаграммы Ганта 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" }, PROJECTS_LIST_ITEM_SECONDARY_NOJOBS: { color: "red" }, PROJECTS_LIST_ITEM_SECONDARY_NOEDIT: { color: "gray" }, PROJECTS_LIST_ITEM_SECONDARY_CHANGED: { color: "green" }, 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: "120px" }, PERIODS_BUTTON: { position: "absolute", right: "20px" }, PERIODS_DRAWER: { width: "1200px", flexShrink: 0, [`& .MuiDrawer-paper`]: { width: "1200px", boxSizing: "border-box" } } }; //------------------------------------ //Вспомогательные функции и компоненты //------------------------------------ //Список проектов const ProjectsList = ({ projects = [], selectedProject, onClick } = {}) => { //Подключение к контексту сообщений const { InlineMsgErr } = useContext(MessagingСtx); //Генерация содержимого return projects.length > 0 ? ( {projects.map(p => ( (onClick ? onClick(p) : null)} > {p.NEDITABLE == 1 ? "edit" : "edit_off"} {p.SNAME}} secondary={ {p.NJOBS == 1 ? p.NEDITABLE == 1 ? p.NCHANGED == 1 ? "Изменён" : "Не изменён" : "Редактирование недоступно" : "Работы не определены"} } /> ))} ) : ( ); }; //Контроль свойств - Список проектов ProjectsList.propTypes = { projects: PropTypes.array, selectedProject: PropTypes.number, onClick: PropTypes.func }; //----------- //Тело модуля //----------- //Корневая панель работ проектов 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, selectedProjectJobsLoaded: false, selectedProject: null, selectedProjectDocRn: null, selectedProjectGanttDef: {}, selectedProjectTasks: [] }); //Подключение к контексту приложения const { pOnlineShowDocument } = useContext(ApplicationСtx); //Подключение к контексту сообщений const { InlineMsgInfo } = useContext(MessagingСtx); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); //Загрузка списка проектов 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", isArray: name => name === "XPROJECTS" }); setState(pv => ({ ...pv, projectsLoaded: true, projects: [...(data?.XPROJECTS || [])] })); } }, [executeStored, state.ident, state.projectsLoaded] ); //Загрузка списка работ проекта const loadProjectJobs = useCallback( async (tasksOnly = false) => { const data = await executeStored({ stored: "PKG_P8PANELS_PROJECTS.JB_JOBS_LIST", args: { NIDENT: state.ident, NPRN: state.selectedProject, NINCLUDE_DEF: tasksOnly === false ? 1 : 0 }, attributeValueProcessor: (name, val) => name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val, respArg: "COUT" }); setState(pv => ({ ...pv, selectedProjectJobsLoaded: true, selectedProjectGanttDef: tasksOnly === true ? { ...pv.selectedProjectGanttDef } : data.XGANTT_DEF ? { ...data.XGANTT_DEF } : {}, selectedProjectTasks: [...data.XGANTT_TASKS] })); }, [executeStored, state.ident, state.selectedProject] ); //Изменение работы в графике const modifyJob = useCallback( async (job, dateFrom, dateTo, dateBegin, dateFact, durationMeas) => { let data = null; try { data = await executeStored({ stored: "PKG_P8PANELS_PROJECTS.JB_JOBS_MODIFY_PERIOD", args: { NJB_JOBS: job, DDATE_FROM: dateFrom, DDATE_TO: dateTo, DBEGIN: dateBegin, DFACT: dateFact, NDURATION_MEAS: durationMeas } }); if (data?.NRESOURCE_STATUS != -1) { setState(pv => ({ ...pv, resourceStatus: data.NRESOURCE_STATUS, needSave: true })); loadProjects(true); } } finally { loadProjectJobs(true); } }, [executeStored, loadProjectJobs, loadProjects] ); //Сохранение буфера балансировки в проекты const saveProjects = useCallback(async () => { const data = await executeStored({ stored: "PKG_P8PANELS_PROJECTS.JB_SAVE", args: { NIDENT: state.ident }, respArg: "COUT" }); setState(pv => ({ ...pv, needSave: false, projects: [...(data?.XPROJECTS || [])] })); }, [executeStored, state.ident]); //Инициализация данных балансировки const initJobs = useCallback(async () => { if (!state.init) { const data = await executeStored({ stored: "PKG_P8PANELS_PROJECTS.JB_INIT", args: { 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, dateBegin: data.DBEGIN, dateFact: data.DFACT, durationMeas: data.NDURATION_MEAS, labMeas: data.SLAB_MEAS, resourceStatus: data.NRESOURCE_STATUS, ident: data.NIDENT })); } }, [state.init, state.dateBegin, state.dateFact, state.durationMeas, state.labMeas, state.ident, executeStored]); //Грузим список проектов при смене идентификатора процесса useEffect(() => { if (state.ident) loadProjects(); }, [state.ident, loadProjects]); //При смене выбранного проекта useEffect(() => { if (state.selectedProject) loadProjectJobs(false); }, [state.selectedProject, loadProjectJobs]); //При подключении компонента к странице useEffect(() => { initJobs(); // 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) selectPoject(project.NRN, project.NPROJECT); else unselectProject(); }; //Отработка нажатия на заголовок плана-графика const handleTitleClick = () => state.selectedProjectDocRn ? pOnlineShowDocument({ unitCode: "Projects", document: state.selectedProjectDocRn }) : null; //Обработка измненения сроков задачи в диаграмме Гантта const handleTaskDatesChange = ({ task, start, end, isMain }) => { if (isMain) modifyJob(task.rn, new Date(start), new Date(end), new Date(state.dateBegin), new Date(state.dateFact), state.durationMeas); }; //Обработка нажатия на сохранение данных в проект const handleSaveToProjectsClick = () => saveProjects(); //Обработка нажатия на проект в таблице детализации трудоёмкости по плану-графику монитора ресурсов 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} setState(pv => ({ ...pv, showProjectsList: false }))} 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 ? ( {state.selectedProjectJobsLoaded ? ( ) : !state.selectedProject ? ( ) : null} ) : null} ); }; //---------------- //Интерфейс модуля //---------------- export { PrjJobs };