import * as React from 'react'; import styles from './RoutesScreen.module.scss'; import { RouteFormModal } from './RouteFormModal'; import { RouteCopyModal } from './RouteCopyModal'; import { ApprovalRouteApi } from '../../services/api'; import { PortalDictionaryClient } from '../../services/portalDictionary'; import { TemplateRoute } from '../../models'; import { texts } from '../../texts'; import { shortGuid } from '../../utils/guid'; 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 { ActionsCell, Alert, Badge, Breadcrumbs, Button, ConfirmDialog, DataTable, Field, IconButton, PageHeader, PrimaryCell, Select } from '../ui'; export interface IRoutesScreenProps { api: ApprovalRouteApi; dictionary?: PortalDictionaryClient; /** Фильтр из ссылки (aarfilter) — перекрывает сохранённый, если задан. */ initialFilterRequestTypeId?: string; onOpenSteps: (route: TemplateRoute) => void; } // Фильтр по типу заявки переживает перезагрузку: администратор обычно работает // с одним типом заявок подряд, и сбрасывать выбор на каждый заход неудобно. const ROUTES_FILTER_STORAGE_KEY = 'approvalAdmin.routesFilter'; /** Окно создания маршрута переживает F5 — поля подставляются из черновика (см. RouteFormModal). */ const CREATE_OPEN_STORAGE_KEY = 'approvalAdmin.draftOpen.route'; export const RoutesScreen: React.FC = ({ api, dictionary, initialFilterRequestTypeId, onOpenSteps }) => { const requestTypes = useRequestTypes(api); const portalDictionaries = usePortalDictionaries(dictionary); const [filterRequestTypeId, setFilterRequestTypeId] = useLocalStorageState(ROUTES_FILTER_STORAGE_KEY, ''); // Открыли по ссылке с фильтром — она сильнее сохранённого значения. Только на // монтировании: дальше источник истины — сам экран (он же пишет фильтр в URL // ниже), иначе собственный replaceRoute тут же вернулся бы сюда как «внешний». React.useEffect(() => { if (initialFilterRequestTypeId) setFilterRequestTypeId(initialFilterRequestTypeId); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Держим URL синхронным с фильтром — ссылку на отфильтрованный список можно // скопировать. replaceRoute, а не navigate: смена фильтра не должна плодить // записи в истории браузера. React.useEffect(() => { replaceRoute({ tab: 'routes', routesFilter: filterRequestTypeId }); }, [filterRequestTypeId]); const routes = useTemplateRoutes(api, filterRequestTypeId ? Number(filterRequestTypeId) : undefined); const [editing, setEditing] = React.useState(); // 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(); const deleteTarget = useDeleteTarget(item => api.templateRoutes.remove(item.id), routes.reload); const requestTypeName = (id: number): string => requestTypes.items.find(rt => rt.id === id)?.name ?? `#${id}`; // Пока справочник не загрузился (или маршрут ссылается на удалённую запись), // показываем усечённый Guid — чтобы строка таблицы не выглядела пустой. const companyName = (id: string): string => portalDictionaries.companies.find(c => c.id === id)?.name ?? shortGuid(id); const departmentName = (id: string): string => portalDictionaries.departments.find(d => d.id === id)?.name ?? shortGuid(id); const openCreate = (): void => { setEditing(undefined); setShowForm(false); createOpen.open(); }; const openEdit = (item: TemplateRoute): void => { setEditing(item); createOpen.close(); setShowForm(true); }; return (
{texts.routes.add}} />