200 lines
7.9 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 { formatDateJSONDateOnly, formatDateRF } from "../../core/utils"; //Вспомогательные функции
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
import { P8PGantt } 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 [gantt, setGantt] = useState({
init: false,
dataLoaded: false,
ident: null,
useCustomTaskDialog: false
});
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Загрузка данных диаграммы с сервера
const loadData = useCallback(async () => {
const data = await executeStored({
stored: "PKG_P8PANELS_SAMPLES.GANTT",
args: { NIDENT: gantt.ident },
attributeValueProcessor: (name, val) =>
name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
respArg: "COUT"
});
setGantt(pv => ({ ...pv, dataLoaded: true, ...data.XGANTT }));
}, [gantt.ident, executeStored]);
//Инициализация данных диаграммы
const initData = useCallback(async () => {
if (!gantt.init) {
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: gantt.ident } });
setGantt(pv => ({ ...pv, init: true, ident: data.NIDENT }));
}
}, [gantt.init, gantt.ident, executeStored]);
//Изменение данных диаграммы
const modifyData = useCallback(
async ({ rn, start, end }) => {
try {
await executeStored({
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
args: { NIDENT: gantt.ident, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
});
} finally {
loadData();
}
},
[gantt.ident, executeStored, loadData]
);
//Обработка измненения сроков задачи в диаграмме Гантта
const handleTaskDatesChange = ({ task, start, end, isMain }) => {
if (isMain) modifyData({ rn: task.rn, start, end });
};
//При необходимости обновить данные таблицы
useEffect(() => {
if (gantt.ident) loadData();
}, [gantt.ident, loadData]);
//При подключении компонента к странице
useEffect(() => {
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={() => setGantt(pv => ({ ...pv, useCustomTaskDialog: !pv.useCustomTaskDialog }))} />}
label="Отображать пользовательский диалог задачи"
/>
<Grid container direction="column" alignItems="center">
<Grid item xs={12}>
{gantt.dataLoaded ? (
<P8PGantt
{...P8P_GANTT_CONFIG_PROPS}
{...gantt}
containerStyle={STYLES.GANTT_CONTAINER}
onTaskDatesChange={handleTaskDatesChange}
taskAttributeRenderer={taskAttributeRenderer}
taskDialogRenderer={gantt.useCustomTaskDialog ? taskDialogRenderer : null}
/>
) : null}
</Grid>
</Grid>
</div>
);
};
//Контроль свойств - Пример: Диаграмма Ганта "P8Gantt"
Gantt.propTypes = {
title: PropTypes.string.isRequired
};
//----------------
//Интерфейс модуля
//----------------
export { Gantt };