Персист модалок: черновики, восстановление после F5, предупреждение при выходе (#30)
Модалки по жёлтым кнопкам теряли введённое при случайном клике мимо окна. **Формы создания** (типы заявок, доступ к заявкам, маршруты, шаги маршрута) - Черновик полей хранится в localStorage (useFormDraft) и переживает закрытие окна, «Отмену» и F5. - Окно, открытое в момент F5, открывается снова (usePersistedOpen); закрытие сбрасывает только признак, черновик остаётся. - Черновик стирается после успешного сохранения или кнопкой «Очистить» в подвале. **Формы редактирования** - Не персистятся — открываются с данными сервера. - При выходе с несохранёнными правками (крестик, «Отмена», клик мимо) — диалог «Закрыть без сохранения?» (isDirty в FormModal). **Устойчивость к старым данным в localStorage** - coerceStored приводит сохранённое значение к форме текущего дефолта по полям: новое поле — дефолт, удалённое отбрасывается, значение другого типа и null заменяются дефолтом. Покрыто тестами. **Прочее** - Modal и ConfirmDialog рендерятся порталом в body поверх шапки SharePoint — на живой странице верх высокого окна уезжал под шапку. - Убраны Sass-предупреждения mixed-decls в Card.module.scss. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/30
This commit was merged in pull request #30.
This commit is contained in:
@@ -11,6 +11,7 @@ import { useRequestTypes } from '../../hooks/useRequestTypes';
|
||||
import { useTemplateRoutes } from '../../hooks/useTemplateRoutes';
|
||||
import { usePortalDictionaries } from '../../hooks/usePortalDictionaries';
|
||||
import { useDeleteTarget } from '../../hooks/useDeleteTarget';
|
||||
import { usePersistedOpen } from '../../hooks/usePersistedOpen';
|
||||
import { useLocalStorageState } from '../../hooks/useLocalStorageState';
|
||||
import { replaceRoute } from '../../routing';
|
||||
import {
|
||||
@@ -39,6 +40,8 @@ export interface IRoutesScreenProps {
|
||||
// Фильтр по типу заявки переживает перезагрузку: администратор обычно работает
|
||||
// с одним типом заявок подряд, и сбрасывать выбор на каждый заход неудобно.
|
||||
const ROUTES_FILTER_STORAGE_KEY = 'approvalAdmin.routesFilter';
|
||||
/** Окно создания маршрута переживает F5 — поля подставляются из черновика (см. RouteFormModal). */
|
||||
const CREATE_OPEN_STORAGE_KEY = 'approvalAdmin.draftOpen.route';
|
||||
|
||||
export const RoutesScreen: React.FC<IRoutesScreenProps> = ({
|
||||
api,
|
||||
@@ -69,7 +72,13 @@ export const RoutesScreen: React.FC<IRoutesScreenProps> = ({
|
||||
const routes = useTemplateRoutes(api, filterRequestTypeId ? Number(filterRequestTypeId) : undefined);
|
||||
|
||||
const [editing, setEditing] = React.useState<TemplateRoute | undefined>();
|
||||
// showForm — только редактирование; окно создания восстанавливается после F5 (usePersistedOpen).
|
||||
const [showForm, setShowForm] = React.useState(false);
|
||||
const createOpen = usePersistedOpen(CREATE_OPEN_STORAGE_KEY);
|
||||
const closeForm = (): void => {
|
||||
setShowForm(false);
|
||||
createOpen.close();
|
||||
};
|
||||
const [copying, setCopying] = React.useState<TemplateRoute | undefined>();
|
||||
|
||||
const deleteTarget = useDeleteTarget<TemplateRoute>(item => api.templateRoutes.remove(item.id), routes.reload);
|
||||
@@ -83,11 +92,13 @@ export const RoutesScreen: React.FC<IRoutesScreenProps> = ({
|
||||
|
||||
const openCreate = (): void => {
|
||||
setEditing(undefined);
|
||||
setShowForm(true);
|
||||
setShowForm(false);
|
||||
createOpen.open();
|
||||
};
|
||||
|
||||
const openEdit = (item: TemplateRoute): void => {
|
||||
setEditing(item);
|
||||
createOpen.close();
|
||||
setShowForm(true);
|
||||
};
|
||||
|
||||
@@ -159,7 +170,7 @@ export const RoutesScreen: React.FC<IRoutesScreenProps> = ({
|
||||
emptyMessage={routes.isLoading ? texts.common.loading : texts.routes.emptyList}
|
||||
/>
|
||||
|
||||
{showForm && (
|
||||
{(showForm || createOpen.isOpen) && (
|
||||
<RouteFormModal
|
||||
api={api}
|
||||
editing={editing}
|
||||
@@ -169,7 +180,7 @@ export const RoutesScreen: React.FC<IRoutesScreenProps> = ({
|
||||
departments={portalDictionaries.departments}
|
||||
dictionaryLoading={portalDictionaries.isLoading}
|
||||
onSaved={routes.reload}
|
||||
onClose={() => setShowForm(false)}
|
||||
onClose={closeForm}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user