This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { confirmModal } from '@tnsu/ui-kit';
|
||||
import { RequestTypeId } from '../models/types';
|
||||
import { REQUEST_STATUS, approvalServiceFor } from './approval';
|
||||
import { resolveDataList } from './dataList';
|
||||
import { emitRequestsChanged } from './requestEvents';
|
||||
|
||||
/**
|
||||
* Удаление СВОЕЙ заявки в статусе «Черновик» — из формы черновика и из таблицы
|
||||
* «Мои заявки». Отправленную заявку (любой другой статус) удалить нельзя.
|
||||
*/
|
||||
|
||||
export interface IDraftRef {
|
||||
/** Ключ формы (он же RequestTypeId портала) — по нему выводятся имена списков. */
|
||||
formKey: RequestTypeId;
|
||||
itemId: number;
|
||||
/** Партиция, где лежит заявка; не задана — текущая (см. resolveDataList). */
|
||||
listId?: string;
|
||||
/** Уникальный код для текста подтверждения; '' — покажем Id. */
|
||||
code?: string;
|
||||
}
|
||||
|
||||
export const NOT_DRAFT_MESSAGE = 'Заявка уже не черновик — удалить можно только черновик. Обновите страницу.';
|
||||
|
||||
/** Можно ли предлагать удаление заявки с таким статусом. */
|
||||
export function isDeletableStatus(status: string | undefined): boolean {
|
||||
return status === REQUEST_STATUS.draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить черновик: элемент списка данных — в корзину сайта (вместе с
|
||||
* вложениями; восстановит администратор, если удалили по ошибке), строки
|
||||
* маршрута — насовсем (у черновика это лишь заготовка, её перепишет следующая
|
||||
* отправка — см. writeRoute).
|
||||
*
|
||||
* Статус перепроверяется по серверу: заявку могли отправить из другой вкладки,
|
||||
* пока эта показывала её черновиком.
|
||||
*
|
||||
* Порядок — сначала заявка, потом маршрут: если второй шаг сорвётся, останутся
|
||||
* лишь «осиротевшие» строки маршрута черновика, которые нигде не показываются;
|
||||
* наоборот же остался бы живой черновик без маршрута.
|
||||
*/
|
||||
export async function deleteDraftRequest(ref: IDraftRef): Promise<void> {
|
||||
const list = resolveDataList(ref.formKey, ref.listId);
|
||||
const item = (await list.items.getById(ref.itemId).select('RequestStatus')()) as { RequestStatus?: string };
|
||||
if (!isDeletableStatus(item.RequestStatus)) {
|
||||
throw new Error(NOT_DRAFT_MESSAGE);
|
||||
}
|
||||
|
||||
await list.items.getById(ref.itemId).recycle();
|
||||
|
||||
try {
|
||||
await approvalServiceFor(ref.formKey, ref.listId).deleteForRequest(ref.itemId);
|
||||
} catch (e) {
|
||||
console.error(`[Удаление черновика] строки маршрута заявки ${ref.itemId} не удалены:`, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Спросить подтверждение и удалить черновик. true — удалён (таблицы уже
|
||||
* оповещены), false — пользователь передумал. Ошибку удаления пробрасывает —
|
||||
* показать её должен вызывающий, у него для этого своё место на экране.
|
||||
*/
|
||||
export async function confirmAndDeleteDraft(ref: IDraftRef): Promise<boolean> {
|
||||
const ok = await confirmModal({
|
||||
title: 'Удалить черновик',
|
||||
message: `Удалить черновик №${ref.code || ref.itemId}? Это действие нельзя отменить.`,
|
||||
confirmText: 'Удалить',
|
||||
cancelText: 'Отмена',
|
||||
danger: true
|
||||
});
|
||||
if (!ok) return false;
|
||||
|
||||
await deleteDraftRequest(ref);
|
||||
emitRequestsChanged();
|
||||
return true;
|
||||
}
|
||||
@@ -53,6 +53,12 @@ export interface ISubmitActionsProps {
|
||||
onSubmit: () => void;
|
||||
/** Подпись основной кнопки. По умолчанию — «Отправить на согласование». */
|
||||
submitLabel?: string;
|
||||
/**
|
||||
* Удалить черновик (см. getDraftDeleteHandler). Не задан — кнопки нет: заявка ещё
|
||||
* не сохранена или уже не черновик. Заморозка подачи на удаление не влияет —
|
||||
* это уборка своего, а не подача.
|
||||
*/
|
||||
onDelete?: () => void;
|
||||
}
|
||||
|
||||
// Единый ряд кнопок форм подачи: Отмена / Сохранить черновик / Отправить.
|
||||
@@ -69,6 +75,7 @@ export const SubmitActions: React.FC<ISubmitActionsProps> = ({
|
||||
onSaveDraft,
|
||||
onSubmit,
|
||||
submitLabel = 'Отправить на согласование',
|
||||
onDelete,
|
||||
}) => {
|
||||
const { saving, kind } = submit;
|
||||
const unfrozenForm = formId != null && UNFROZEN_FORMS.has(formId);
|
||||
@@ -97,6 +104,12 @@ export const SubmitActions: React.FC<ISubmitActionsProps> = ({
|
||||
<>
|
||||
{frozen && !readOnly && <Alert variant="warn">Подача заявок временно недоступна.</Alert>}
|
||||
<FormActions>
|
||||
{onDelete && (
|
||||
<Button variant="danger" onClick={onDelete} disabled={saving}>
|
||||
<Icon name="trash" size="sm" />
|
||||
Удалить черновик
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="secondary" onClick={onCancel} disabled={saving}>
|
||||
Отмена
|
||||
</Button>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { RequestTypeId } from '../../../models/types';
|
||||
import { confirmAndDeleteDraft, isDeletableStatus } from '../../draftDeletion';
|
||||
import { friendlyErrorMessage } from '../../diagnostics';
|
||||
import { markFormClean } from '../../unsavedChanges';
|
||||
import { IRequestSubmit } from '../useRequestSubmit';
|
||||
|
||||
export interface IDraftDeletionParams {
|
||||
formKey: RequestTypeId;
|
||||
/** Id сохранённой заявки; undefined — новая, ещё не сохранённая (удалять нечего). */
|
||||
requestId: number | undefined;
|
||||
requestStatus: string | undefined;
|
||||
listId?: string;
|
||||
code?: string;
|
||||
/** Ошибка удаления показывается там же, где ошибки сохранения формы. */
|
||||
submit: IRequestSubmit;
|
||||
/** После удаления — уйти с формы (обычно onCancel формы: к плиткам / закрыть панель). */
|
||||
onDeleted: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик кнопки «Удалить черновик» для SubmitActions (проп onDelete).
|
||||
* undefined — кнопку не показывать: заявка не сохранена или уже не черновик.
|
||||
*/
|
||||
export function getDraftDeleteHandler(params: IDraftDeletionParams): (() => void) | undefined {
|
||||
const { formKey, requestId, requestStatus, listId, code, submit, onDeleted } = params;
|
||||
if (!requestId || !isDeletableStatus(requestStatus)) return undefined;
|
||||
|
||||
return () => {
|
||||
confirmAndDeleteDraft({ formKey, itemId: requestId, listId, code })
|
||||
.then((deleted) => {
|
||||
if (!deleted) return;
|
||||
// Черновика больше нет — предупреждать о «несохранённых правках» при
|
||||
// уходе с формы уже не о чем.
|
||||
markFormClean();
|
||||
onDeleted();
|
||||
})
|
||||
.catch((e) => submit.setError(`Не удалось удалить черновик: ${friendlyErrorMessage(e)}`));
|
||||
};
|
||||
}
|
||||
@@ -46,6 +46,7 @@ export { OrgLetterhead } from './OrgLetterhead/OrgLetterhead';
|
||||
export { LinkField } from './LinkField/LinkField';
|
||||
export { ErrorOverlay } from './ErrorOverlay/ErrorOverlay';
|
||||
export { SubmitActions } from './SubmitActions/SubmitActions';
|
||||
export { getDraftDeleteHandler } from './SubmitActions/getDraftDeleteHandler';
|
||||
export { InitiatorLine } from './InitiatorLine/InitiatorLine';
|
||||
export type { IInitiatorLineProps } from './InitiatorLine/InitiatorLine';
|
||||
export { useRequestSubmit } from './useRequestSubmit';
|
||||
|
||||
Reference in New Issue
Block a user