diff --git a/web/src/components/common/DeletionMarkChip.jsx b/web/src/components/common/DeletionMarkChip.jsx new file mode 100644 index 0000000..289640e --- /dev/null +++ b/web/src/components/common/DeletionMarkChip.jsx @@ -0,0 +1,11 @@ +import { Tooltip } from '@mui/material'; +import { formatDeletionHint } from '../../utils/formatDeletionHint'; +import { Chip } from '../styles/StyledChip'; + +const chipSx = { background: '#fde8e8', color: '#b42318', whiteSpace: 'nowrap', width: 'max-content', cursor: 'default' }; + +export const DeletionMarkChip = ({ task, usersById, sx = {} }) => ( + + На удаление + +); diff --git a/web/src/components/common/TaskCardComponent.jsx b/web/src/components/common/TaskCardComponent.jsx index 07cde07..128f9c1 100644 --- a/web/src/components/common/TaskCardComponent.jsx +++ b/web/src/components/common/TaskCardComponent.jsx @@ -9,6 +9,7 @@ import { TasksApi } from '../../api/tasks'; import { FORM_TYPE_TRANSLATE } from '../../constants/constants'; import { ExportButton } from '../common/Buttons/ButtonsActions'; import { Chip } from '../styles/StyledChip'; +import { DeletionMarkChip } from './DeletionMarkChip'; import { LastOpenedBadge } from './LastOpenedBadge'; import { IconWithContent } from './IconWithContent'; import Modal from './Modal/Modal'; @@ -62,6 +63,7 @@ export const TaskCardComponent = ({ isAdmin = false, onChanged, currentUserId = null, + usersById = {}, }) => { const navigate = useNavigate(); const location = useLocation(); @@ -213,7 +215,7 @@ export const TaskCardComponent = ({ ID: {id} - {taskForm.to_delete && На удаление} + {taskForm.to_delete && } {isLastOpened && Последняя открытая} diff --git a/web/src/pages/TaskPage/TaskPage.jsx b/web/src/pages/TaskPage/TaskPage.jsx index 91d37e3..fd77b74 100644 --- a/web/src/pages/TaskPage/TaskPage.jsx +++ b/web/src/pages/TaskPage/TaskPage.jsx @@ -4,9 +4,11 @@ import { useNavigate, useParams } from 'react-router-dom'; import { toast } from 'react-toastify'; import { ProjectsApi } from '../../api/projects'; // Добавьте импорт API для проектов import { TasksApi } from '../../api/tasks'; +import { UsersApi } from '../../api/users'; import { SettingModal as SettingStageModal } from '../../components/Stages/SettingModal'; import { DangerOutlinedButton, PrimaryButton, PrimaryOutlinedButton } from '../../components/common/Buttons/Buttons'; import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions'; +import { DeletionMarkChip } from '../../components/common/DeletionMarkChip'; import { IconWithContent } from '../../components/common/IconWithContent'; import { HeaderPortal } from '../../components/common/HeaderPortal'; import SearchComponent from '../../components/common/SearchComponent'; @@ -49,6 +51,15 @@ export default function TaskPage() { const [addYearModalOpen, setAddYearModalOpen] = useState(false); const [isAddingYear, setIsAddingYear] = useState(false); const [dataReloadKey, setDataReloadKey] = useState(0); + const [usersById, setUsersById] = useState({}); + const isMarkedForDeletion = Boolean(task?.to_delete); + + useEffect(() => { + if (!isMarkedForDeletion) return; + UsersApi.getAll() + .then((data) => setUsersById(Object.fromEntries((data.result || []).map((item) => [item.id, item])))) + .catch(() => toast.error('Ошибка загрузки пользователей')); + }, [isMarkedForDeletion]); useEffect(() => { const getData = async () => { @@ -162,6 +173,7 @@ export default function TaskPage() { Таблиц: {tables.length} + {isMarkedForDeletion && } diff --git a/web/src/pages/TasksPage/TasksTable.jsx b/web/src/pages/TasksPage/TasksTable.jsx index 52ea827..fe97cc7 100644 --- a/web/src/pages/TasksPage/TasksTable.jsx +++ b/web/src/pages/TasksPage/TasksTable.jsx @@ -5,6 +5,7 @@ import { Link as RouterLink, useLocation, useNavigate } from 'react-router-dom'; import { toast } from 'react-toastify'; import { TasksApi } from '../../api/tasks'; import { ExportButton, ExportExitButton, ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions'; +import { DeletionMarkChip } from '../../components/common/DeletionMarkChip'; import FormBadge from '../../components/common/FormBadge'; import { LastOpenedBadge } from '../../components/common/LastOpenedBadge'; import Modal from '../../components/common/Modal/Modal'; @@ -84,6 +85,7 @@ export default function TasksTable({ sx={{ color: 'inherit', fontWeight: 500, fontSize: '1rem' }}> {cell.getValue()} + {row.original.to_delete && } {String(row.original.id) === lastOpenedId && ( Последняя открытая @@ -106,7 +108,7 @@ export default function TasksTable({ {creator?.is_active === false && ( - Отправлен на удаление + Пользователь удалён )} diff --git a/web/src/utils/formatDeletionHint.test.js b/web/src/utils/formatDeletionHint.test.js index ad33e98..e77ab62 100644 --- a/web/src/utils/formatDeletionHint.test.js +++ b/web/src/utils/formatDeletionHint.test.js @@ -6,10 +6,7 @@ const at = new Date(2026, 10, 5, 9, 7).toISOString(); const users = { 7: { id: 7, full_name: ' Иванов Иван ', username: 'ivanov' } }; test('дата и ФИО', () => { - assert.equal( - formatDeletionHint({ deleted_at: at, deleted_by: 7 }, users), - 'Отправлена на удаление 05.11.2026 09:07 — Иванов Иван', - ); + assert.equal(formatDeletionHint({ deleted_at: at, deleted_by: 7 }, users), 'Отправлена на удаление 05.11.2026 09:07 — Иванов Иван'); }); test('без deleted_by', () => { @@ -31,5 +28,8 @@ test('deleted_by нет в справочнике', () => { }); test('нет full_name — username; невалидная дата игнорируется', () => { - assert.equal(formatDeletionHint({ deleted_at: 'мусор', deleted_by: 8 }, { 8: { username: 'petrov' } }), 'Отправлена на удаление — petrov'); + assert.equal( + formatDeletionHint({ deleted_at: 'мусор', deleted_by: 8 }, { 8: { username: 'petrov' } }), + 'Отправлена на удаление — petrov', + ); });