Предупреждение при выходе из формы редактирования с несохранёнными правками
FormModal получил isDirty: если в форме редактирования существующей записи что-то изменили, крестик, «Отмена» и клик мимо окна сначала показывают диалог «Закрыть без сохранения?». Подключено к редактированию типа заявки, маршрута, шага маршрута и согласующего в заявке. Формы создания не затронуты — их черновик и так сохраняется. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+6
-1
@@ -74,6 +74,11 @@ export const ManageRequestsApproverModal: React.FC<IManageRequestsApproverModalP
|
||||
const excludedSet = new Set(excludedEmails.map(e => e.toLowerCase()));
|
||||
const selectableEmployees = employees.filter(e => !excludedSet.has(e.email.toLowerCase()));
|
||||
|
||||
const isDirty =
|
||||
email !== initialEmail ||
|
||||
slaHours !== slaHoursToInput(initialSlaHours) ||
|
||||
askOverdueReason !== boolToTriState(initialAskOverdueReason);
|
||||
|
||||
const handleSubmit = (): void => {
|
||||
const trimmedEmail = email.trim();
|
||||
if (!trimmedEmail) {
|
||||
@@ -95,7 +100,7 @@ export const ManageRequestsApproverModal: React.FC<IManageRequestsApproverModalP
|
||||
};
|
||||
|
||||
return (
|
||||
<FormModal title={title} error={submit.error} isSaving={submit.isBusy} onSubmit={handleSubmit} onClose={onClose}>
|
||||
<FormModal title={title} error={submit.error} isSaving={submit.isBusy} onSubmit={handleSubmit} onClose={onClose} isDirty={isDirty}>
|
||||
<FormGrid>
|
||||
<Field label={texts.manageRequests.approverLabel} required full>
|
||||
<SearchableSelect
|
||||
|
||||
@@ -61,6 +61,18 @@ const EMPTY_FORM: IFormState = {
|
||||
/** Черновик формы создания — переживает закрытие модалки, см. useFormDraft. */
|
||||
const CREATE_DRAFT_STORAGE_KEY = 'approvalAdmin.draft.requestType';
|
||||
|
||||
function toFormState(item: RequestType): IFormState {
|
||||
return {
|
||||
name: item.name,
|
||||
pageUrl: item.pageUrl,
|
||||
section: item.section,
|
||||
siteUrl: item.siteUrl ?? '',
|
||||
listId: item.listId ?? '',
|
||||
slaHours: slaHoursToInput(item.slaHours),
|
||||
askOverdueReason: boolToTriState(item.askOverdueReason)
|
||||
};
|
||||
}
|
||||
|
||||
// siteUrl/listId — поля RequestType, зеркалящие nullable JSON-контракт бэкенда
|
||||
// (см. models/requestTypes.ts) — здесь сразу приводим null к undefined,
|
||||
// внутренний код (хуки/кэш SharePointListsClient) с null не работает нигде.
|
||||
@@ -86,6 +98,7 @@ export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sh
|
||||
const draft = useFormDraft<IFormState>(CREATE_DRAFT_STORAGE_KEY, EMPTY_FORM);
|
||||
const [editForm, setEditForm] = React.useState<IFormState>(EMPTY_FORM);
|
||||
const form = editing ? editForm : draft.value;
|
||||
const isEditDirty = Boolean(editing) && JSON.stringify(editForm) !== JSON.stringify(toFormState(editing as RequestType));
|
||||
const setForm = editing ? setEditForm : draft.setValue;
|
||||
const submit = useAsyncSubmit();
|
||||
const sharePointLists = useSharePointLists(sharePoint, form.siteUrl);
|
||||
@@ -104,15 +117,7 @@ export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sh
|
||||
|
||||
const openEdit = (item: RequestType): void => {
|
||||
setEditing(item);
|
||||
setEditForm({
|
||||
name: item.name,
|
||||
pageUrl: item.pageUrl,
|
||||
section: item.section,
|
||||
siteUrl: item.siteUrl ?? '',
|
||||
listId: item.listId ?? '',
|
||||
slaHours: slaHoursToInput(item.slaHours),
|
||||
askOverdueReason: boolToTriState(item.askOverdueReason)
|
||||
});
|
||||
setEditForm(toFormState(item));
|
||||
setShowForm(true);
|
||||
};
|
||||
|
||||
@@ -208,6 +213,7 @@ export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sh
|
||||
onClose={() => setShowForm(false)}
|
||||
onReset={editing ? undefined : draft.reset}
|
||||
canReset={draft.isDirty}
|
||||
isDirty={isEditDirty}
|
||||
>
|
||||
<FormGrid>
|
||||
<Field label={texts.requestTypes.nameLabel} required full>
|
||||
|
||||
@@ -116,6 +116,7 @@ export const RouteFormModal: React.FC<IRouteFormModalProps> = ({
|
||||
onClose={onClose}
|
||||
onReset={editing ? undefined : draft.reset}
|
||||
canReset={draft.isDirty}
|
||||
isDirty={Boolean(editing) && draft.isDirty}
|
||||
>
|
||||
<FormGrid>
|
||||
<Field label={texts.routes.requestTypeLabel} required full>
|
||||
|
||||
@@ -423,6 +423,7 @@ export const RouteStepFormModal: React.FC<IRouteStepFormModalProps> = ({
|
||||
onClose={onClose}
|
||||
onReset={editing ? undefined : draft.reset}
|
||||
canReset={draft.isDirty}
|
||||
isDirty={Boolean(editing) && draft.isDirty}
|
||||
>
|
||||
<FormGrid>
|
||||
<Field label={texts.routeSteps.orderNoLabel} required>
|
||||
|
||||
Reference in New Issue
Block a user