arm-core/web/src/widgets/StatusChangeModal/ChangeStatusModal.tsx

85 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Form, Input, Modal, Select, message } from 'antd';
import { useState } from 'react';
import { ApiError } from '../../api/client';
import { TableTag } from '../../components/TableTag/TableTag';
import { useBeforeUnload } from '../../hooks/useBeforeUnload';
import { updateStatus } from '../../services/api/consolidations';
import type { VersionStatusUpdate } from '../../types/api';
import type { ConsStatus } from '../../types/consolidations/common_types';
import type { VersionCard } from '../../types/consolidations/version_members';
import { CONS_STATUS_LABEL, MAX_REASON_LENGTH, canSubmitStatusChange, manualTransitions } from '../../utils/consolidations';
import { statusColorMapping } from '../ConsoleTable/consts';
import CustomModalCloseIcon from '../../components/CustomModalCloseIcon';
type StatusModalProps = {
open: boolean;
/** Карточка версии со страницы: из неё текущий статус и допустимые переходы. */
card: VersionCard;
onClose: () => void;
/** 200: карточка из ответа `POST …/status` — экран берёт её вместо повторного GET. */
onSuccess: (card: VersionCard) => void;
};
type FormValues = { to_status?: ConsStatus; comment?: string };
/** «Изменить статус» [33-01]: варианты — ручные переходы (`MANUAL_TRANSITIONS`), по умолчанию ничего не выбрано. */
export default function ChangeStatusModal({ open, card, onClose, onSuccess }: StatusModalProps) {
const [form] = Form.useForm<FormValues>();
const [submitting, setSubmitting] = useState(false);
// до монтирования формы useWatch отдаёт undefined — правила это учитывают
const nextStatus = Form.useWatch('to_status', form);
const comment = Form.useWatch('comment', form);
const currentLabel = CONS_STATUS_LABEL[card.status];
const options = manualTransitions(card.status).map((s) => ({ value: s, label: CONS_STATUS_LABEL[s] }));
const canSubmit = canSubmitStatusChange(nextStatus, comment);
useBeforeUnload(open && (!!nextStatus || !!comment?.trim()));
const handleOk = async () => {
if (!nextStatus || !canSubmit || submitting) return;
const body: VersionStatusUpdate = { to_status: nextStatus, comment: (comment ?? '').trim() };
setSubmitting(true);
try {
const newCard = await updateStatus(card.id, body);
onSuccess(newCard);
message.success(`Статус изменён: ${CONS_STATUS_LABEL[newCard.status]}`);
onClose();
} catch (e) {
// 409 VERSION_NOT_EDITABLE / LOCK_REQUIRED / LOCKED_BY_OTHER / TRANSITION_NOT_ALLOWED, 422 COMMENT_REQUIRED —
// текст сервера; модалка открыта, статус на экране прежний
message.error(e instanceof ApiError ? e.message : 'Не удалось изменить статус');
} finally {
setSubmitting(false);
}
};
return (
<Modal
open={open}
title='Изменить статус'
okText='Изменить статус'
cancelText='Отмена'
okButtonProps={{ disabled: !canSubmit }}
confirmLoading={submitting}
onOk={handleOk}
onCancel={() => !submitting && onClose()}
destroyOnHidden
centered
closeIcon={<CustomModalCloseIcon/>}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16 }}>
<span style={{ color: '#667085' }}>Текущий этап</span>
<TableTag text={currentLabel} {...statusColorMapping[currentLabel]} />
</div>
<Form form={form} layout='vertical' requiredMark={false} preserve={false}>
<Form.Item name='to_status' label='Новый этап'>
<Select placeholder='Выберите этап' options={options} />
</Form.Item>
<Form.Item name='comment' label='Комментарий'>
<Input.TextArea rows={3} maxLength={MAX_REASON_LENGTH} placeholder='Укажите комментарий к изменению статуса' />
</Form.Item>
</Form>
</Modal>
);
}