feat: удаление своих заявок в статусе Черновик
Tests / frontend-test (push) Successful in 1m15s

This commit is contained in:
Тимур Городецкий
2026-09-24 16:52:53 +05:00
parent 852b16c327
commit 9b83c54491
18 changed files with 355 additions and 3 deletions
@@ -13,8 +13,11 @@ import {
searchOwnRequests,
} from './myRequests.service';
import { IMyRequest } from './models/IMyRequest';
import { DataTable, IDataTableColumn } from '@tnsu/ui-kit';
import { DataTable, IDataTableColumn, IconButton } from '@tnsu/ui-kit';
import { REQUEST_STATUS, REQUEST_STATUS_CHOICES, IRequestType, StatusBadge } from '../../shared/approval';
import { confirmAndDeleteDraft, isDeletableStatus } from '../../shared/draftDeletion';
import { friendlyErrorMessage } from '../../shared/diagnostics';
import { getFormComponent } from '../../forms/registry';
import { onRequestsChanged } from '../../shared/requestEvents';
import { perfScope } from '../../shared/perfTrace';
import { useRoutedQuery } from '../../shared/usePersistedQuery';
@@ -197,12 +200,23 @@ function formatDate(iso: string): string {
// (доступ с клавиатуры: Enter/Space).
//
// Набор колонок зависит от раздела и от фильтра — см. columnsFor.
/**
* Можно ли удалить строку прямо из таблицы: своя заявка в статусе «Черновик»
* и форма этого типа есть в портале (чужие типы из approval-route портал не
* открывает и не удаляет).
*/
function canDeleteRow(r: IMyRequest): boolean {
return r.kind === 'own' && isDeletableStatus(r.status) && !!getFormComponent(r.formKey);
}
const Table: React.FC<{
rows: IMyRequest[];
view: ListView;
statusFilter: string;
onOpen: (request: IMyRequest) => void;
}> = ({ rows, view, statusFilter, onOpen }) => {
/** Удалить черновик; задан только там, где в таблице бывают свои черновики. */
onDelete?: (request: IMyRequest) => void;
}> = ({ rows, view, statusFilter, onOpen, onDelete }) => {
const cols = columnsFor(view, statusFilter);
// Колонки для DataTable строим динамически: базовые всегда, автор/статус/
@@ -241,6 +255,27 @@ const Table: React.FC<{
render: (r) => r.decisionComment || <span className={styles.commentEmpty}>Без комментария</span>
});
}
if (onDelete && rows.some(canDeleteRow)) {
columns.push({
key: 'actions',
title: '',
width: '56px',
render: (r) =>
canDeleteRow(r) ? (
<IconButton
icon="trash"
title="Удалить черновик"
danger
size="sm"
onClick={(e) => {
// Клик по всей строке открывает заявку — кнопке это не нужно.
e.stopPropagation();
onDelete(r);
}}
/>
) : null
});
}
return (
<div className={styles.tableWrap}>
@@ -300,6 +335,18 @@ export const RequestsTableView: React.FC<IRequestsTableViewProps> = ({
const [rows, setRows] = React.useState<IMyRequest[] | undefined>(undefined);
const [totalRows, setTotalRows] = React.useState<number>(0);
const [error, setError] = React.useState<string>('');
const [deleteError, setDeleteError] = React.useState<string>('');
// Список перечитается сам: confirmAndDeleteDraft шлёт rp-requests-changed.
const handleDelete = (request: IMyRequest): void => {
setDeleteError('');
confirmAndDeleteDraft({
formKey: request.formKey,
itemId: request.id,
listId: request.listId,
code: request.code
}).catch((e) => setDeleteError(`Не удалось удалить черновик: ${friendlyErrorMessage(e)}`));
};
// input — то, что в поле ввода (меняется сразу); в маршрут значение уходит
// после паузы в наборе. Данные грузятся по МАРШРУТНОМУ query — он и так уже
@@ -545,6 +592,7 @@ export const RequestsTableView: React.FC<IRequestsTableViewProps> = ({
</div>
{error && <div className={styles.error}>Не удалось загрузить заявки: {error}</div>}
{deleteError && <div className={styles.error}>{deleteError}</div>}
{!error && !loaded && <div className={styles.empty}>Загрузка…</div>}
{!error && loaded && total === 0 && (
<div className={styles.empty}>
@@ -553,7 +601,13 @@ export const RequestsTableView: React.FC<IRequestsTableViewProps> = ({
)}
{!error && rows && total > 0 && (
<>
<Table rows={visible} view={view} statusFilter={statusFilter} onOpen={onOpen} />
<Table
rows={visible}
view={view}
statusFilter={statusFilter}
onOpen={onOpen}
onDelete={view === 'own:created' ? handleDelete : undefined}
/>
<Pager page={page} totalPages={totalPages} onChange={onPageChange} />
</>
)}