347 lines
16 KiB
JavaScript
347 lines
16 KiB
JavaScript
/*
|
||
Парус 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 };
|