263 lines
11 KiB
JavaScript
263 lines
11 KiB
JavaScript
/*
|
||
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||
Компонент панели: Карточка события
|
||
*/
|
||
|
||
//---------------------
|
||
//Подключение библиотек
|
||
//---------------------
|
||
|
||
import React, { useContext } from "react"; //Классы React
|
||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||
import { Draggable } from "react-beautiful-dnd";
|
||
import { Card, CardHeader, Typography, IconButton, Icon, Box, Menu, MenuItem, CardContent, Avatar, Stack } from "@mui/material"; //Интерфейсные компоненты
|
||
import { useTaskCard } from "../hooks"; //Вспомогательные хуки
|
||
import { TaskFormDialog } from "./task_form"; //Форма события
|
||
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
||
|
||
//---------
|
||
//Константы
|
||
//---------
|
||
|
||
//Перечисление "Цвет индикации"
|
||
const EVENT_INDICATORS = Object.freeze({ EXPIRED: "#ff0000", EXPIRES_SOON: "#ffdf00", LINKED: "#1e90ff" });
|
||
|
||
//Стили
|
||
const STYLES = {
|
||
CONTAINER: { margin: "5px 0px", textAlign: "center" },
|
||
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_DESC: { padding: 0, cursor: "pointer" },
|
||
CARD_CONTENT: { padding: "4px !important" },
|
||
CARD_CONTENT_BOX: { display: "flex", alignItems: "center" },
|
||
ACCOUNT_STACK: { alignItems: "center", marginLeft: "auto" },
|
||
SECONDARY_TEXT: {
|
||
color: "text.secondary",
|
||
fontSize: 14
|
||
},
|
||
ICON_COLOR: linked => {
|
||
return { color: theme => (linked ? EVENT_INDICATORS.LINKED : theme.palette.grey[500]) };
|
||
}
|
||
};
|
||
|
||
//------------------------------------
|
||
//Вспомогательные функции и компоненты
|
||
//------------------------------------
|
||
|
||
//Действия карточки события
|
||
const DataCellCardActions = ({
|
||
taskRn,
|
||
menuItems,
|
||
cardActions,
|
||
handleMethodsMenuButtonClick,
|
||
handleMethodsMenuClose,
|
||
handleReload,
|
||
eventPoints,
|
||
pointSettings,
|
||
openNoteDialog
|
||
}) => {
|
||
return (
|
||
<Box sx={STYLES.BOX_ROW}>
|
||
<IconButton id={`${taskRn}_menu_button`} aria-haspopup="true" onClick={handleMethodsMenuButtonClick}>
|
||
<Icon>more_vert</Icon>
|
||
</IconButton>
|
||
<Menu id={`${taskRn}_menu`} anchorEl={cardActions.anchorMenuMethods} open={cardActions.openMethods} onClose={handleMethodsMenuClose}>
|
||
{menuItems.map(action => {
|
||
if (action.visible)
|
||
return (
|
||
<MenuItem
|
||
sx={action.delimiter ? STYLES.MENU_ITEM_DELIMITER : {}}
|
||
key={`${taskRn}_${action.method}`}
|
||
onClick={() => {
|
||
if (openNoteDialog && action.method === "TASK_STATE_CHANGE") {
|
||
action.func(taskRn, action.needReload ? handleReload : null, eventPoints, openNoteDialog);
|
||
} else if (openNoteDialog && action.method === "TASK_SEND" && pointSettings.addNoteOnSend) {
|
||
openNoteDialog(n => action.func(taskRn, action.needReload ? handleReload : null, n));
|
||
} else {
|
||
//Выполняем действие
|
||
action.func(taskRn, action.needReload ? handleReload : null);
|
||
}
|
||
//Закрываем меню
|
||
handleMethodsMenuClose();
|
||
}}
|
||
>
|
||
<Icon>{action.icon}</Icon>
|
||
<Typography pl={1}>{action.name}</Typography>
|
||
</MenuItem>
|
||
);
|
||
})}
|
||
</Menu>
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
//Контроль свойств - Действия карточки события
|
||
DataCellCardActions.propTypes = {
|
||
taskRn: PropTypes.number.isRequired,
|
||
menuItems: PropTypes.array.isRequired,
|
||
cardActions: PropTypes.object.isRequired,
|
||
handleMethodsMenuButtonClick: PropTypes.func.isRequired,
|
||
handleMethodsMenuClose: PropTypes.func.isRequired,
|
||
handleReload: PropTypes.func,
|
||
eventPoints: PropTypes.array,
|
||
pointSettings: PropTypes.object,
|
||
openNoteDialog: PropTypes.func
|
||
};
|
||
|
||
//-----------
|
||
//Тело модуля
|
||
//-----------
|
||
|
||
//Карточка события
|
||
const TaskCard = ({ task, avatar, index, handleReload, eventPoints, colorRule, pointSettings, openNoteDialog }) => {
|
||
//Собственное состояние
|
||
const [taskCard, setTaskCard, cardActions, handleMethodsMenuButtonClick, handleMethodsMenuClose, menuItems] = useTaskCard();
|
||
|
||
//Подключение к контексту приложения
|
||
const { pOnlineShowDocument } = useContext(ApplicationСtx);
|
||
|
||
//Конвертация формата HEX в формат RGB
|
||
const hexToRGB = hex => {
|
||
let r = parseInt(hex.slice(1, 3), 16);
|
||
let g = parseInt(hex.slice(3, 5), 16);
|
||
let b = parseInt(hex.slice(5, 7), 16);
|
||
let a = 0.5;
|
||
r = Math.round((a * (r / 255) + a * (255 / 255)) * 255);
|
||
g = Math.round((a * (g / 255) + a * (255 / 255)) * 255);
|
||
b = Math.round((a * (b / 255) + a * (255 / 255)) * 255);
|
||
return "rgb(" + r + ", " + g + ", " + b + ")";
|
||
};
|
||
|
||
//Проверка выполнения условия заливки события
|
||
const bgColorRule = () => {
|
||
let ruleCode;
|
||
let bgColor = null;
|
||
if (colorRule.vType === "string") ruleCode = `S${colorRule.fieldCode}`;
|
||
else if (colorRule.vType === "number") ruleCode = `N${colorRule.fieldCode}`;
|
||
else if (colorRule.vType === "date") ruleCode = `D${colorRule.fieldCode}`;
|
||
ruleCode ? (task.docProps[ruleCode] == colorRule.from ? (bgColor = hexToRGB(colorRule.color)) : null) : null;
|
||
return bgColor;
|
||
};
|
||
|
||
//Индикация истечения срока отработки события
|
||
const indicatorColorRule = task => {
|
||
let sysDate = new Date();
|
||
let expireDate = task.dexpire_date ? new Date(task.dexpire_date) : null;
|
||
let daysDiff = null;
|
||
if (expireDate) {
|
||
daysDiff = ((expireDate.getTime() - sysDate.getTime()) / (1000 * 60 * 60 * 24)).toFixed(2);
|
||
if (daysDiff < 0) return EVENT_INDICATORS.EXPIRED;
|
||
else if (daysDiff < 4) return EVENT_INDICATORS.EXPIRES_SOON;
|
||
}
|
||
return null;
|
||
};
|
||
|
||
//Генерация содержимого
|
||
return (
|
||
<Box>
|
||
<Draggable draggableId={task.id.toString()} key={task.id} index={index}>
|
||
{provided => (
|
||
<Card
|
||
ref={provided.innerRef}
|
||
{...provided.draggableProps}
|
||
{...provided.dragHandleProps}
|
||
sx={STYLES.CARD(indicatorColorRule(task), colorRule.color ? bgColorRule() : null)}
|
||
>
|
||
<CardHeader
|
||
title={
|
||
<Typography
|
||
className="task-info"
|
||
sx={STYLES.CARD_HEADER_TITLE}
|
||
lang="ru"
|
||
onClick={() => {
|
||
menuItems.find(a => (a.method === "EDIT" ? a.func(task.nrn, a.needReload ? handleReload : null) : null));
|
||
}}
|
||
>
|
||
{task.sdescription}
|
||
</Typography>
|
||
}
|
||
sx={STYLES.CARD_HEADER_DESC}
|
||
action={
|
||
<DataCellCardActions
|
||
taskRn={task.nrn}
|
||
menuItems={menuItems}
|
||
cardActions={cardActions}
|
||
handleMethodsMenuButtonClick={handleMethodsMenuButtonClick}
|
||
handleMethodsMenuClose={handleMethodsMenuClose}
|
||
handleReload={handleReload}
|
||
eventPoints={eventPoints}
|
||
pointSettings={pointSettings}
|
||
openNoteDialog={openNoteDialog}
|
||
/>
|
||
}
|
||
/>
|
||
<CardContent sx={STYLES.CARD_CONTENT}>
|
||
<Box sx={STYLES.CARD_CONTENT_BOX}>
|
||
<IconButton
|
||
title={task.nlinked_rn ? "Событие получено по статусной модели" : null}
|
||
onClick={
|
||
task.nlinked_rn ? () => pOnlineShowDocument({ unitCode: task.slinked_unit, document: task.nlinked_rn }) : null
|
||
}
|
||
sx={STYLES.ICON_COLOR(task.nlinked_rn)}
|
||
disabled={!task.nlinked_rn}
|
||
>
|
||
<Icon>assignment</Icon>
|
||
</IconButton>
|
||
<Typography sx={STYLES.SECONDARY_TEXT}>{task.name}</Typography>
|
||
{task.sSender ? (
|
||
<Stack direction="row" spacing={0.5} sx={STYLES.ACCOUNT_STACK}>
|
||
<Typography sx={STYLES.SECONDARY_TEXT}>{task.sSender}</Typography>
|
||
<Avatar src={avatar ? `data:image/png;base64,${avatar}` : null} />
|
||
</Stack>
|
||
) : null}
|
||
</Box>
|
||
</CardContent>
|
||
</Card>
|
||
)}
|
||
</Draggable>
|
||
{taskCard.openEdit ? (
|
||
<TaskFormDialog
|
||
taskRn={task.nrn}
|
||
taskType={task.stype}
|
||
editable={pointSettings.banUpdate ? false : true}
|
||
handleReload={handleReload}
|
||
onClose={() => {
|
||
setTaskCard(pv => ({ ...pv, openEdit: false }));
|
||
}}
|
||
/>
|
||
) : null}
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
//Контроль свойств - Карточка события
|
||
TaskCard.propTypes = {
|
||
task: PropTypes.object.isRequired,
|
||
avatar: PropTypes.string,
|
||
index: PropTypes.number.isRequired,
|
||
handleReload: PropTypes.func,
|
||
eventPoints: PropTypes.array,
|
||
colorRule: PropTypes.object,
|
||
pointSettings: PropTypes.object,
|
||
openNoteDialog: PropTypes.func
|
||
};
|
||
|
||
//----------------
|
||
//Интерфейс модуля
|
||
//----------------
|
||
|
||
export { TaskCard };
|