185 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Парус 8 - Панели мониторинга - Примеры для разработчиков
Пример: Диаграмма Ганта "P8PGantt"
*/
//---------------------
//Подключение библиотек
//---------------------
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Typography, Grid, Stack, Icon, FormControlLabel, Checkbox, Card, CardHeader, CardActions, Avatar, CardContent, Button } from "@mui/material"; //Интерфейсные элементы
import { formatDateRF, hasValue } from "../../core/utils"; //Вспомогательные функции
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
import { P8PGantt, useP8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
//---------
//Константы
//---------
//Отступ контейнера страницы от заголовка
const CONTAINER_PADDING_TOP = "20px";
//Высота заголовка страницы
const TITLE_HEIGHT = "47px";
//Высота элементов управления
const CONTROL_HEIGHT = "42px";
//Стили
const STYLES = {
CONTAINER: { textAlign: "center", paddingTop: CONTAINER_PADDING_TOP },
TITLE: { paddingBottom: "15px", height: TITLE_HEIGHT },
CONTROL: { height: CONTROL_HEIGHT },
GANTT_CONTAINER: {
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${TITLE_HEIGHT} - ${CONTROL_HEIGHT} - ${CONTAINER_PADDING_TOP})`,
width: "100vw",
paddingTop: "5px"
}
};
//---------------------------------------------
//Вспомогательные функции форматирования данных
//---------------------------------------------
//Формирование значения для колонки "Тип задачи"
const formatTaskTypeValue = value => {
const [text, icon] = value == 0 ? ["Этап проекта", "check"] : ["Работа проекта", "work_outline"];
return (
<Stack direction="row" gap={0.5}>
<Icon title={text}>{icon}</Icon>
{text}
</Stack>
);
};
//Генерация кастомных представлений атрибутов задачи в редакторе
const taskAttributeRenderer = ({ task, attribute }) => {
switch (attribute.name) {
case "type":
return formatTaskTypeValue(task.type);
default:
return null;
}
};
//Генерация кастомного диалога задачи
const taskDialogRenderer = ({ task, close }) => {
return (
<Card>
<CardHeader
avatar={<Avatar sx={{ bgcolor: task.bgColor }}>{task.type == 0 ? "Эт" : "Ра"}</Avatar>}
title={task.name}
subheader={`с ${formatDateRF(task.start)} по ${formatDateRF(task.end)}`}
/>
<CardContent>
<Typography variant="body2" color="text.secondary">
Это пользовательский диалог с данными о задаче. Вы можете формировать такие указав свой функциональный компонент в качестве
свойства &quot;taskDialogRenderer&quot; компонента &quot;P8PGantt&quot;.
</Typography>
</CardContent>
<CardActions disableSpacing>
<Button size="small" onClick={close}>
Закрыть
</Button>
</CardActions>
</Card>
);
};
//-----------
//Тело модуля
//-----------
//Пример: Диаграмма Ганта "P8Gantt"
const Gantt = ({ title }) => {
//Собственное состояние - идентификатор данных
const [dataIdent, setDataIdent] = useState(null);
//Собственное состояние - использование собственного диалога задачи
const [isCustomTaskDialog, setIsCustomTaskDialog] = useState(false);
//Собственное состояние - диаграмма Ганта
const { gantt, isDataLoaded, doReload } = useP8PGantt({
stored: "PKG_P8PANELS_SAMPLES.GANTT",
storedArgs: { NIDENT: dataIdent },
allowDataLoad: () => hasValue(dataIdent)
});
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Изменение данных диаграммы
const modifyData = useCallback(
async ({ rn, start, end }) => {
try {
await executeStored({
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
args: { NIDENT: dataIdent, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
});
} finally {
doReload();
}
},
[dataIdent, executeStored, doReload]
);
//Обработка измненения сроков задачи в диаграмме Гантта
const handleTaskDatesChange = ({ task, start, end, isMain }) => {
if (isMain) modifyData({ rn: task.rn, start, end });
};
//При подключении компонента к странице
useEffect(() => {
//Инициализация данных диаграммы Ганта
const initData = async () => {
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: null } });
setDataIdent(data.NIDENT);
};
initData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//Генерация содержимого
return (
<div style={STYLES.CONTAINER}>
<Typography sx={STYLES.TITLE} variant={"h6"}>
{title}
</Typography>
<FormControlLabel
sx={STYLES.CONTROL}
control={<Checkbox onChange={() => setIsCustomTaskDialog(!isCustomTaskDialog)} />}
label="Отображать пользовательский диалог задачи"
/>
<Grid container direction="column" alignItems="center">
<Grid item xs={12}>
{isDataLoaded ? (
<P8PGantt
{...P8P_GANTT_CONFIG_PROPS}
{...gantt}
containerStyle={STYLES.GANTT_CONTAINER}
onTaskDatesChange={handleTaskDatesChange}
taskAttributeRenderer={taskAttributeRenderer}
taskDialogRenderer={isCustomTaskDialog ? taskDialogRenderer : null}
/>
) : null}
</Grid>
</Grid>
</div>
);
};
//Контроль свойств - Пример: Диаграмма Ганта "P8Gantt"
Gantt.propTypes = {
title: PropTypes.string.isRequired
};
//----------------
//Интерфейс модуля
//----------------
export { Gantt };