diff --git a/app/panels/prj_jobs/prj_jobs.js b/app/panels/prj_jobs/prj_jobs.js index 49d703e..9f40ec6 100644 --- a/app/panels/prj_jobs/prj_jobs.js +++ b/app/panels/prj_jobs/prj_jobs.js @@ -7,13 +7,143 @@ //Подключение библиотек //--------------------- -import React, { useContext, useState } from "react"; //Классы React -import Button from "@mui/material/Button"; //Кнопка -import Typography from "@mui/material/Typography"; //Текст -import { NavigationCtx } from "../../context/navigation"; //Контекст навигации +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, Stack } from "@mui/material"; //Интерфейсные элементы import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений import { ApplicationСtx } from "../../context/application"; //Контекст приложения +import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { P8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта +import { formatDateJSONDateOnly } from "../../core/utils"; //Вспомогательные функции + +//--------- +//Константы +//--------- + +//Высота диаграммы Ганта +const GANTT_HEIGHT = "650px"; + +//Ширина диаграммы Ганта +const GANTT_WIDTH = "98vw"; + +//Стили +const STYLES = { + 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: "100px" } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Формирование значения для колонки "Состояние" этапа +const formatStageStatusValue = value => { + const [text, icon] = + value == 0 + ? ["Зарегистрирован", "app_registration"] + : value == 1 + ? ["Открыт", "lock_open"] + : value == 2 + ? ["Закрыт", "lock_outline"] + : value == 3 + ? ["Согласован", "thumb_up_alt"] + : value == 4 + ? ["Исполнение прекращено", "block"] + : ["Остановлен", "do_not_disturb_on"]; + return ( + + {icon} + {text} + + ); +}; + +//Формирование значения для колонки "Состояние" работы +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} + {text} + + ); +}; + +//Список проектов +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 +}; //----------- //Тело модуля @@ -22,157 +152,199 @@ import { ApplicationСtx } from "../../context/application"; //Контекст //Корневая панель работ проектов const PrjJobs = () => { //Собственное состояние - let [result, setResult] = useState(""); + let [state, setState] = useState({ + showProjectsList: false, + init: false, + ident: null, + projects: [], + projectsLoaded: false, + selectedProjectJobsLoaded: false, + selectedProject: null, + selectedProjectDocRn: null, + selectedProjectGanttDef: {}, + selectedProjectTasks: [] + }); - //Подключение к контексту навигации - const { navigateBack, navigateRoot, isNavigationState, getNavigationState, navigatePanelByName } = useContext(NavigationCtx); + //Подключение к контексту приложения + const { pOnlineShowDocument } = useContext(ApplicationСtx); + + //Подключение к контексту сообщений + const { InlineMsgInfo } = useContext(MessagingСtx); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); - //Подключение к контексту сообщений - const { MSG_TYPE, showMsgErr, showMsgWarn, showMsgInfo, InlineMsg, InlineMsgErr, InlineMsgInfo, InlineMsgWarn } = useContext(MessagingСtx); - - //Подключение к контексту приложения - const { pOnlineShowTab, pOnlineShowDocument, pOnlineShowDictionary, pOnlineUserProcedure, pOnlineUserReport } = useContext(ApplicationСtx); - - //Выполнение запроса к серверу - const makeReq = async throwError => { - try { + //Загрузка списка проектов + const loadProjects = useCallback(async () => { + if (!state.projectsLoaded) { const data = await executeStored({ - throwError, - showErrorMessage: false, - stored: "UDO_P_P8PANELS_TEST", - args: { NRN: 123, SCODE: "123", DDATE: new Date() }, - respArg: "COUT", - spreadOutArguments: false + stored: "PKG_P8PANELS_PROJECTS.JB_PRJCTS_LIST", + args: { + NIDENT: state.ident + }, + respArg: "COUT" }); - setResult(JSON.stringify(data)); - } catch (e) { - setResult(""); - showMsgErr(e.message); + 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 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, + NIDENT: state.ident + } + }); + setState(pv => ({ ...pv, init: true, ident: data.NIDENT })); + } + }, [state.init, 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 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 + })); + }; + + //Отработка нажатия на заголовок плана-графика + const handleTitleClick = () => + state.selectedProjectDocRn ? pOnlineShowDocument({ unitCode: "Projects", document: state.selectedProjectDocRn }) : null; + + //Обработка измненения сроков задачи в диаграмме Гантта + const handleTaskDatesChange = ({ task, start, end, isMain }) => { + console.log("ПОМЕНЯЛИ ДАТЫ"); + console.log(task); + console.log(start); + console.log(end); + if (isMain) { + console.log("ЭТО - ГЛАВНОЕ. ПОЙДЁМ НА СЕРВЕР..."); + loadProjectJobs(true); } }; - //Отображение закладки - const openTab = () => { - const id = pOnlineShowTab({ id: "123", url: "Modules/p8-panels/#/prj_fin", caption: "Экономика проектов", onClose: handleTabClose }); - if (id) console.log(`Открыта закладка ${id}`); - else console.log("Закладка не открыта"); + //Обработка изменения прогресса задачи в диаграмме Гантта + const handleTaskProgressChange = ({ task, progress }) => { + console.log("ПОМЕНЯЛИ % ГОТОВНОСТИ"); + console.log(task); + console.log(progress); }; - //При сокрытии закладки - const handleTabClose = id => console.log(`Закрыта закладка ${id}`); + //Генерация кастомных представлений атрибутов задачи в редакторе + const taskAttributeRenderer = ({ task, attribute }) => { + switch (attribute.name) { + case "type": + return task.type === 1 ? "Этап проекта" : "Работа проекта"; + case "state": + return task.type === 1 ? formatStageStatusValue(task[attribute.name]) : formatJobStatusValue(task[attribute.name]); + default: + return null; + } + }; //Генерация содержимого return ( -
- console.log("INLINE MESSAGE ON OK")} - /> - console.log("INLINE INFO ON OK")} /> - console.log("INLINE WARN ON OK")} /> - console.log("INLINE ERR ON OK")} /> -

Это панель работ!

-
-

Параметры: {isNavigationState() ? JSON.stringify(getNavigationState()) : "НЕ ПЕРЕДАНЫ"}

-
- -
- -
- -
- -
- -
- -
- -
- -
- -
- RESULT: {result} -
-
-
- - - - - -
-
-
+ {state.projectsLoaded ? ( + + ) : null} + + {state.init == true ? ( + + + {state.selectedProjectJobsLoaded ? ( + + + + ) : !state.selectedProject ? ( + + ) : null} + + + ) : null} + ); };