347 lines
16 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 - Панели мониторинга - УДП - Доски задач
Панель мониторинга: Корневая панель доски задач
*/
//---------------------
//Подключение библиотек
//---------------------
import React, { useState, useContext, useCallback, useEffect, useRef } from "react"; //Классы React
import { DragDropContext, Droppable } from "react-beautiful-dnd";
import { Stack, Card, CardHeader, CardContent, Box, Button, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
//import { Draggable } from "react-draggable";
import { TaskCard } from "./components/task_card";
import { TaskFormDialog } from "./components/task_form";
import { object2Base64XML } from "../../core/utils";
import { Filter } from "./filter.js";
import { FilterDialog } from "./components/filter_dialog"; //Компонент диалогового окна фильтра отбора
import { TaskCardSettings } from "./components/task_card_settings.js"; //
//---------
//Константы
//---------
const STYLES = {
CONTAINER: { width: "100%", padding: 1 },
DEF_SIZE: { minWidth: "200px", minHeight: "100px" },
SETTINGS_BUTTON: {
position: "absolute",
right: 8,
top: 8,
color: theme => theme.palette.grey[500]
}
};
const COLORS = [
"mediumSlateBlue",
"lightSalmon",
"fireBrick",
"orange",
"gold",
"limeGreen",
"yellowGreen",
"mediumAquaMarine",
"paleTurquoise",
"steelBlue",
"skyBlue",
"tan"
];
//-----------
//Тело модуля
//-----------
//Корневая панель доски задач
const ClntTaskBoard = () => {
const [insertTask, setInsertTask] = useState(false);
const [cardSettings, setCardSettings] = useState({ isOpen: false, settings: {} });
const [config, setConfig] = useState({
groupsLoaded: false,
tasksLoaded: false,
filters: {
isOpen: false,
isSetByUser: false,
values: { type: "", sendPerson: "", sendDivision: "", sendUsrGrp: "" },
fArray: [
{ name: "SEVTYPE_CODE", from: "", to: "" },
{ name: "SSEND_PERSON", from: "", to: "" },
{ name: "SSEND_DIVISION", from: "", to: "" },
{ name: "SSEND_USRGRP", from: "", to: "" }
]
},
reload: true
});
const [statuses, setStatuses] = useState([]);
const [tasks, setTasks] = useState([]);
let usedColors = useRef([]);
//Подключение к контексту взаимодействия с сервером
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
// const appendGroup = newStatus => {};
// const appendTask = task => {
// //setTasks([...tasks, task]);
// };
const randomColor = () => {
let color = COLORS[Math.floor(Math.random() * COLORS.length)];
while (usedColors.current.find(c => c === color) !== undefined) {
color = COLORS[Math.floor(Math.random() * COLORS.length)];
}
usedColors.current = [...usedColors.current, color];
return color;
};
const initTask = (id, gp, task) => {
return {
id: id,
name: `${task.SEVPREF}-${task.NEVNUMB}`,
category: gp,
nrn: task.NRN,
scrn: "",
sprefix: task.SEVPREF,
snumber: task.NEVNUMB,
stype: task.SEVTYPE_CODE,
sstatus: task.SEVSTAT_NAME,
sdescription: task.SEVDESCR,
sclnt_clnclients: "",
sclnt_clnperson: "",
dstart_date: task.DREG_DATE,
sinit_clnperson: task.SINIT_PERSON,
sinit_user: "",
sinit_reason: "",
sto_company: "",
sto_department: "",
sto_clnpost: "",
sto_clnpsdep: "",
sto_clnperson: "",
sto_fcstaffgrp: "",
sto_user: task.SSEND_PERSON,
sto_usergrp: task.SSEND_USRGRP,
scurrent_user: ""
};
};
const getTasks = useCallback(async () => {
if (config.reload) {
const data = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DATASET",
args: {
CFILTERS: { VALUE: object2Base64XML(config.filters.fArray, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
NINCLUDE_DEF: config.tasksLoaded ? 0 : 1
},
respArg: "COUT"
});
console.log(object2Base64XML(config.filters.fArray, { arrayNodeName: "filters" }));
let newSt = [];
if (data.XGROUPS != null) {
const x = statuses.length;
data.XGROUPS.map((group, i) => {
//setStatuses([...statuses, { id: x + i, name: group.name }]);
newSt.push({ id: x + i, name: group.name, color: randomColor() });
//console.log(`${x + i} ${group.name}`);
});
setStatuses([...newSt].sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1)));
setConfig(pv => ({ ...pv, groupsLoaded: true }));
}
if (data.XROWS != null) {
let newT = [];
const x = tasks.length;
data.XROWS.map((task, i) => {
//setStatuses([...statuses, { id: x + i, name: group.name }]);
//console.log(statuses.find(x => x.name === task.groupName).id ? newSt.find(x => x.name === task.groupName) : null);
newT.push(initTask(x + i, newSt.find(x => x.name === task.groupName).id, task));
});
setTasks([...newT]);
setConfig(pv => ({ ...pv, tasksLoaded: true, reload: false }));
}
}
}, [SERV_DATA_TYPE_CLOB, config.filters, config.reload, config.tasksLoaded, executeStored, statuses, tasks]);
useEffect(() => {
getTasks();
}, [config.reload, getTasks]);
// useEffect(() => {
// config.groupsLoaded ? console.log(statuses) : null;
// config.tasksLoaded ? console.log(tasks) : null;
// }, [config.groupsLoaded, statuses]);
const onDragEnd = result => {
const { source, destination } = result;
if (!destination) {
return;
}
if (destination.droppableId !== source.droppableId) {
setTasks(tasks =>
tasks.map(task =>
task.id === parseInt(result.draggableId)
? {
...task,
category: parseInt(result.destination.droppableId)
}
: task
)
);
console.log(statuses.find(s => s.id == destination.droppableId).name); // Тут вызов смены статуса
} else {
setTasks(tasks);
}
};
//При открытии диалога фильтра
const handleFilterClick = () => setFilterOpen(true);
//При изменении фильтра в диалоге
const handleFilterOk = filter => {
setFilterValues(filter);
setFilterOpen(false);
};
//При закрытии диалога фильтра
const handleFilterCancel = () => setFilterOpen(false);
//Установить значение фильтра
const setFilterValues = values => {
let filterArr = config.filters.fArray.slice();
values.type ? (filterArr.find(f => f.name === "SEVTYPE_CODE").from = values.type) : null;
values.sendPerson ? (filterArr.find(f => f.name === "SSEND_PERSON").from = values.sendPerson) : null;
values.sendDivision ? (filterArr.find(f => f.name === "SSEND_DIVISION").from = values.sendDivision) : null;
values.sendUsrGrp ? (filterArr.find(f => f.name === "SSEND_USRGRP").from = values.sendUsrGrp) : null;
setConfig(pv => ({ ...pv, filters: { ...pv.filters, isSetByUser: true, values: { ...values }, fArray: [...filterArr] }, reload: true }));
};
//Показать/скрыть фильтр
const setFilterOpen = isOpen => setConfig(pv => ({ ...pv, filters: { ...pv.filters, isOpen } }));
const handleCardSettingsClick = curSettings => {
setCardSettings({ isOpen: true, settings: { ...curSettings } });
};
const handleCardSettingsCancel = () => setCardSettings(pv => ({ ...pv, isOpen: false }));
const handleCardSettingsOk = settings => {
let cloneS = statuses.slice();
cloneS[statuses.findIndex(x => x.id === settings.id)] = { ...settings };
setStatuses(cloneS);
setCardSettings({ isOpen: false, settings: {} });
};
return (
<Box sx={STYLES.CONTAINER}>
{config.filters.isOpen ? <FilterDialog initial={config.filters.values} onOk={handleFilterOk} onCancel={handleFilterCancel} /> : null}
<Filter filter={config.filters.values} onClick={handleFilterClick} />
<DragDropContext onDragEnd={onDragEnd}>
<div>
<Droppable droppableId="Statuses" type="droppableTask">
{provided => (
<div ref={provided.innerRef}>
<Stack direction="row" spacing={2}>
{statuses.map((status, index) => (
<div key={index}>
<Droppable droppableId={status.id.toString()}>
{provided => (
<div ref={provided.innerRef}>
<Card
className="category-card"
sx={{ ...STYLES.DEF_SIZE, backgroundColor: status.color, padding: 1 }}
>
<CardHeader
action={
<IconButton aria-label="settings" onClick={() => handleCardSettingsClick(status)}>
<Icon>more_vert</Icon>
</IconButton>
}
title={status.name}
subheader={<Button onClick={() => setInsertTask(true)}>+ Добавить</Button>}
sx={{ padding: 0 }}
/>
<CardContent sx={{ padding: 0 }}>
<Stack spacing={1}>
{tasks
.filter(item => item.category === status.id)
.map((item, index) => (
<TaskCard task={item} index={index} key={item.id} />
// <Draggable draggableId={item.id.toString()} key={item.id} index={index}>
// {provided => (
// <Card
// ref={provided.innerRef}
// {...provided.draggableProps}
// {...provided.dragHandleProps}
// >
// <CardHeader
// action={
// <IconButton
// aria-label="settings"
// onClick={() => {
// console.log("Опции задачи");
// }}
// >
// <Icon>more_vert</Icon>
// </IconButton>
// }
// title={
// <Typography
// className="task-info"
// sx={{ padding: "2px" }}
// >
// {item.id} {item.name}
// </Typography>
// }
// sx={{ padding: 0 }}
// />
// </Card>
// )}
// </Draggable>
))}
{provided.placeholder}
</Stack>
</CardContent>
</Card>
</div>
)}
</Droppable>
</div>
))}
</Stack>
{provided.placeholder}
</div>
)}
</Droppable>
</div>
</DragDropContext>
{insertTask ? (
<TaskFormDialog
onClose={() => {
setInsertTask(false);
}}
/>
) : null}
{cardSettings.isOpen ? (
<TaskCardSettings
initial={cardSettings.settings}
availableClrs={COLORS.filter(c => !usedColors.current.includes(c))}
onOk={handleCardSettingsOk}
onCancel={handleCardSettingsCancel}
/>
) : null}
</Box>
);
};
//----------------
//Интерфейс модуля
//----------------
export { ClntTaskBoard };