forked from CITKParus/P8-Panels
377 lines
16 KiB
JavaScript
377 lines
16 KiB
JavaScript
/*
|
||
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||
Компонент: Карточка события
|
||
*/
|
||
|
||
//---------------------
|
||
//Подключение библиотек
|
||
//---------------------
|
||
|
||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||
import { Draggable } from "react-beautiful-dnd"; //Работа с drag&drop
|
||
import { Card, CardHeader, Typography, IconButton, Icon, Box, Menu, MenuItem, CardContent, Avatar, Stack } from "@mui/material"; //Интерфейсные компоненты
|
||
import { TaskDialog } from "../task_dialog"; //Форма события
|
||
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
||
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
||
import { MessagingСtx } from "../../../context/messaging"; //Контекст сообщений
|
||
import { TASK_COLORS, getTaskExpiredColor, getTaskBgColorByRule, makeCardActionsArray } from "../layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
||
import { useDictionary } from "../hooks/dict_hooks"; //Состояние открытия разделов
|
||
import { useTasksFunctions } from "../hooks/tasks_hooks"; //Состояние вспомогательных функций событий
|
||
|
||
//---------
|
||
//Константы
|
||
//---------
|
||
|
||
//Стили
|
||
const STYLES = {
|
||
MENU_ITEM_DELIMITER: { borderBottom: "1px solid lightgrey" },
|
||
CARD: (indicatorClr, bgClr) => {
|
||
const i = indicatorClr ? { borderLeft: `solid ${indicatorClr}` } : null;
|
||
const bc = bgClr ? { backgroundColor: bgClr } : null;
|
||
return { ...i, ...bc };
|
||
},
|
||
CARD_HEADER_TITLE: {
|
||
padding: "4px",
|
||
width: "292px",
|
||
display: "-webkit-box",
|
||
hyphens: "auto",
|
||
WebkitBoxOrient: "vertical",
|
||
WebkitLineClamp: 2,
|
||
overflow: "hidden"
|
||
},
|
||
CARD_HEADER: { padding: 0, cursor: "pointer" },
|
||
CARD_CONTENT: { padding: "4px !important" },
|
||
CARD_CONTENT_BOX: { display: "flex", alignItems: "center" },
|
||
STACK_SENDER: { alignItems: "center", marginLeft: "auto" },
|
||
TYPOGRAPHY_SECONDARY: {
|
||
color: "text.secondary",
|
||
fontSize: 14
|
||
},
|
||
ICON_COLOR: linked => {
|
||
return { color: theme => (linked ? TASK_COLORS.LINKED : theme.palette.grey[500]) };
|
||
}
|
||
};
|
||
|
||
//------------------------------------
|
||
//Вспомогательные функции и компоненты
|
||
//------------------------------------
|
||
|
||
//Действия карточки события
|
||
const CardActions = ({
|
||
taskRn,
|
||
menuItems,
|
||
cardActions,
|
||
onMethodsMenuButtonClick,
|
||
onMethodsMenuClose,
|
||
onTasksReload,
|
||
pointSettings,
|
||
onOpenNoteDialog
|
||
}) => {
|
||
//При нажатии на действие меню
|
||
const handleActionClick = action => {
|
||
//Выполняем действие
|
||
action.func({
|
||
nEvent: taskRn,
|
||
onReload: action.tasksReload ? () => onTasksReload(action.needAccountsReload) : null,
|
||
onNoteOpen: pointSettings.ADDNOTE_ONSEND ? onOpenNoteDialog : null
|
||
});
|
||
//Закрываем меню действий
|
||
onMethodsMenuClose();
|
||
};
|
||
|
||
return (
|
||
<Box sx={STYLES.BOX_ROW}>
|
||
<IconButton id={`${taskRn}_menu_button`} aria-haspopup="true" onClick={onMethodsMenuButtonClick}>
|
||
<Icon>more_vert</Icon>
|
||
</IconButton>
|
||
<Menu id={`${taskRn}_menu`} anchorEl={cardActions.anchorMenuMethods} open={cardActions.openMethods} onClose={onMethodsMenuClose}>
|
||
{menuItems.map(action => {
|
||
if (action.visible)
|
||
return (
|
||
<MenuItem
|
||
sx={action.delimiter ? STYLES.MENU_ITEM_DELIMITER : {}}
|
||
key={`${taskRn}_${action.method}`}
|
||
onClick={() => handleActionClick(action)}
|
||
>
|
||
<Icon>{action.icon}</Icon>
|
||
<Typography pl={1}>{action.name}</Typography>
|
||
</MenuItem>
|
||
);
|
||
})}
|
||
</Menu>
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
//Контроль свойств - Действия карточки события
|
||
CardActions.propTypes = {
|
||
taskRn: PropTypes.number.isRequired,
|
||
menuItems: PropTypes.array.isRequired,
|
||
cardActions: PropTypes.object.isRequired,
|
||
onMethodsMenuButtonClick: PropTypes.func.isRequired,
|
||
onMethodsMenuClose: PropTypes.func.isRequired,
|
||
onTasksReload: PropTypes.func,
|
||
pointSettings: PropTypes.object,
|
||
onOpenNoteDialog: PropTypes.func
|
||
};
|
||
|
||
//-----------
|
||
//Тело модуля
|
||
//-----------
|
||
|
||
//Карточка события
|
||
const TaskCard = ({ task, index, onTasksReload, colorRule, pointSettings, onOpenNoteDialog }) => {
|
||
//Состояние диалога события
|
||
const [taskDialogOpen, setTaskDialogOpen] = useState(false);
|
||
|
||
//Состояние действий события
|
||
const [cardActions, setCardActions] = useState({ anchorMenuMethods: null, openMethods: false });
|
||
|
||
//Состояние списка действий меню
|
||
const [menuItems, setMenuItems] = useState([]);
|
||
|
||
//Вспомогательные функции открытия раздела
|
||
const { handleClientEventsOpen, handleClientEventsNotesOpen, handleFileLinksOpen } = useDictionary();
|
||
|
||
//Состояние вспомогательных функций событий
|
||
const { handleTaskStateChange, handleTaskSend } = useTasksFunctions();
|
||
|
||
//Подключение к контексту взаимодействия с сервером
|
||
const { executeStored } = useContext(BackEndСtx);
|
||
|
||
//Подключение к контексту сообщений
|
||
const { showMsgWarn } = useContext(MessagingСtx);
|
||
|
||
//Подключение к контексту приложения
|
||
const { pOnlineShowDocument } = useContext(ApplicationСtx);
|
||
|
||
//По нажатию на открытие меню действий
|
||
const handleMethodsMenuButtonClick = useCallback(event => {
|
||
setCardActions(pv => ({ ...pv, anchorMenuMethods: event.currentTarget, openMethods: true }));
|
||
}, []);
|
||
|
||
//При закрытии меню
|
||
const handleMethodsMenuClose = useCallback(() => {
|
||
setCardActions(pv => ({ ...pv, anchorMenuMethods: null, openMethods: false }));
|
||
}, []);
|
||
|
||
//При удалении контрагента
|
||
const handleTaskDelete = useCallback(
|
||
async ({ nEvent, onReload }) => {
|
||
await executeStored({
|
||
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DELETE",
|
||
args: { NCLNEVENTS: nEvent }
|
||
});
|
||
//Если требуется перезагрузить данные
|
||
onReload ? onReload() : null;
|
||
},
|
||
[executeStored]
|
||
);
|
||
|
||
//При возврате в предыдущую точку события
|
||
const handleTaskReturn = useCallback(
|
||
async ({ nEvent, onReload }) => {
|
||
await executeStored({
|
||
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_RETURN",
|
||
args: { NCLNEVENTS: nEvent }
|
||
});
|
||
//Если требуется перезагрузить данные
|
||
onReload ? onReload() : null;
|
||
},
|
||
[executeStored]
|
||
);
|
||
|
||
//По нажатию действия "Направить"
|
||
const handleSendAction = useCallback(
|
||
async ({ nEvent, onReload, onNoteOpen }) => {
|
||
//Выполняем направление события
|
||
handleTaskSend({ nEvent, onReload, onNoteOpen });
|
||
},
|
||
[handleTaskSend]
|
||
);
|
||
|
||
//По нажатия действия "Редактировать"
|
||
const handleTaskEditAction = useCallback(() => {
|
||
setTaskDialogOpen(true);
|
||
}, []);
|
||
|
||
//По нажатия действия "Редактировать в разделе"
|
||
const handleTaskEditClientAction = useCallback(
|
||
async ({ nEvent }) => {
|
||
const data = await executeStored({
|
||
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SELECT",
|
||
args: {
|
||
NCLNEVENTS: nEvent
|
||
}
|
||
});
|
||
if (data.NIDENT) {
|
||
//Открываем раздел "События" с фильтром по записи
|
||
handleClientEventsOpen({ nIdent: data.NIDENT });
|
||
}
|
||
},
|
||
[executeStored, handleClientEventsOpen]
|
||
);
|
||
|
||
//По нажатию действия "Удалить"
|
||
const handleTaskDeleteAction = useCallback(
|
||
({ nEvent, onReload }) => {
|
||
showMsgWarn("Удалить событие?", () => handleTaskDelete({ nEvent, onReload }));
|
||
},
|
||
[handleTaskDelete, showMsgWarn]
|
||
);
|
||
|
||
//По нажатию действия "Выполнить возврат"
|
||
const handleTaskReturnAction = useCallback(
|
||
({ nEvent, onReload }) => {
|
||
showMsgWarn("Выполнить возврат события в предыдущую точку?", () => handleTaskReturn({ nEvent, onReload }));
|
||
},
|
||
[handleTaskReturn, showMsgWarn]
|
||
);
|
||
|
||
//По нажатию действия "Примечания"
|
||
const handleEventNotesOpenAction = useCallback(
|
||
({ nEvent }) => {
|
||
handleClientEventsNotesOpen({ nPrn: nEvent });
|
||
},
|
||
[handleClientEventsNotesOpen]
|
||
);
|
||
|
||
//По нажатию действия "Присоединенные документы"
|
||
const handleTaskFileLinksOpenAction = useCallback(
|
||
({ nEvent }) => {
|
||
handleFileLinksOpen({ nPrn: nEvent, sUnitCode: "ClientEvents" });
|
||
},
|
||
[handleFileLinksOpen]
|
||
);
|
||
|
||
//По нажатию действия "Перейти"
|
||
const handleStateChangeAction = useCallback(
|
||
async ({ nEvent, onReload, onNoteOpen }) => {
|
||
//Выполняем изменения статуса события
|
||
handleTaskStateChange({ nEvent, onReload, onNoteOpen });
|
||
},
|
||
[handleTaskStateChange]
|
||
);
|
||
|
||
//При изменении ссылок в меню действий (для того, чтобы ссылка на объект менялась при реальной необходимости)
|
||
useEffect(() => {
|
||
//Устанавливаем список меню
|
||
setMenuItems(
|
||
makeCardActionsArray(
|
||
handleTaskEditAction,
|
||
handleTaskEditClientAction,
|
||
handleTaskDeleteAction,
|
||
handleStateChangeAction,
|
||
handleTaskReturnAction,
|
||
handleSendAction,
|
||
handleEventNotesOpenAction,
|
||
handleTaskFileLinksOpenAction
|
||
)
|
||
);
|
||
}, [
|
||
handleEventNotesOpenAction,
|
||
handleTaskFileLinksOpenAction,
|
||
handleSendAction,
|
||
handleStateChangeAction,
|
||
handleTaskDeleteAction,
|
||
handleTaskEditAction,
|
||
handleTaskEditClientAction,
|
||
handleTaskReturnAction
|
||
]);
|
||
|
||
//Генерация содержимого
|
||
return (
|
||
<Box>
|
||
{taskDialogOpen ? (
|
||
<TaskDialog
|
||
taskRn={task.nRn}
|
||
taskType={task.sType}
|
||
editable={pointSettings.BAN_UPDATE ? false : true}
|
||
onTasksReload={onTasksReload}
|
||
onClose={() => {
|
||
setTaskDialogOpen(false);
|
||
}}
|
||
/>
|
||
) : null}
|
||
<Draggable draggableId={task.id.toString()} key={task.id} index={index}>
|
||
{provided => (
|
||
<Card
|
||
ref={provided.innerRef}
|
||
{...provided.draggableProps}
|
||
{...provided.dragHandleProps}
|
||
sx={STYLES.CARD(getTaskExpiredColor(task), colorRule.SCOLOR ? getTaskBgColorByRule(task, colorRule) : null)}
|
||
>
|
||
<CardHeader
|
||
title={
|
||
<Typography
|
||
className="task-info"
|
||
sx={STYLES.CARD_HEADER_TITLE}
|
||
lang="ru"
|
||
onClick={() => {
|
||
menuItems.find(action =>
|
||
action.method === "EDIT" ? action.func(task.nRn, action.tasksReload ? onTasksReload : null) : null
|
||
);
|
||
}}
|
||
>
|
||
{task.sDescription}
|
||
</Typography>
|
||
}
|
||
sx={STYLES.CARD_HEADER}
|
||
action={
|
||
<CardActions
|
||
taskRn={task.nRn}
|
||
menuItems={menuItems}
|
||
cardActions={cardActions}
|
||
onMethodsMenuButtonClick={handleMethodsMenuButtonClick}
|
||
onMethodsMenuClose={handleMethodsMenuClose}
|
||
onTasksReload={onTasksReload}
|
||
pointSettings={pointSettings}
|
||
onOpenNoteDialog={onOpenNoteDialog}
|
||
/>
|
||
}
|
||
/>
|
||
<CardContent sx={STYLES.CARD_CONTENT}>
|
||
<Box sx={STYLES.CARD_CONTENT_BOX}>
|
||
<IconButton
|
||
title={task.nLinkedRn ? "Событие получено по статусной модели" : null}
|
||
onClick={
|
||
task.nLinkedRn ? () => pOnlineShowDocument({ unitCode: task.sLinkedUnit, document: task.nLinkedRn }) : null
|
||
}
|
||
sx={STYLES.ICON_COLOR(task.nLinkedRn)}
|
||
disabled={!task.nLinkedRn}
|
||
>
|
||
<Icon>assignment</Icon>
|
||
</IconButton>
|
||
<Typography sx={STYLES.TYPOGRAPHY_SECONDARY}>{task.name}</Typography>
|
||
{task.sSender ? (
|
||
<Stack direction="row" spacing={0.5} sx={STYLES.STACK_SENDER}>
|
||
<Typography sx={STYLES.TYPOGRAPHY_SECONDARY}>{task.sSender}</Typography>
|
||
<Avatar src={task.avatar ? `data:image/png;base64,${task.avatar}` : null} />
|
||
</Stack>
|
||
) : null}
|
||
</Box>
|
||
</CardContent>
|
||
</Card>
|
||
)}
|
||
</Draggable>
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
//Контроль свойств - Карточка события
|
||
TaskCard.propTypes = {
|
||
task: PropTypes.object.isRequired,
|
||
index: PropTypes.number.isRequired,
|
||
onTasksReload: PropTypes.func,
|
||
colorRule: PropTypes.object,
|
||
pointSettings: PropTypes.object,
|
||
onOpenNoteDialog: PropTypes.func
|
||
};
|
||
|
||
//----------------
|
||
//Интерфейс модуля
|
||
//----------------
|
||
|
||
export { TaskCard };
|