Обходной лист (#12)
Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/12
This commit was merged in pull request #12.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
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';
|
||||
@@ -10,6 +11,8 @@ import { useRequestTypes } from '../../hooks/useRequestTypes';
|
||||
import { useTemplateRoutes } from '../../hooks/useTemplateRoutes';
|
||||
import { usePortalDictionaries } from '../../hooks/usePortalDictionaries';
|
||||
import { useDeleteTarget } from '../../hooks/useDeleteTarget';
|
||||
import { useLocalStorageState } from '../../hooks/useLocalStorageState';
|
||||
import { replaceRoute } from '../../routing';
|
||||
import {
|
||||
ActionsCell,
|
||||
Alert,
|
||||
@@ -28,18 +31,46 @@ import {
|
||||
export interface IRoutesScreenProps {
|
||||
api: ApprovalRouteApi;
|
||||
dictionary?: PortalDictionaryClient;
|
||||
/** Фильтр из ссылки (aarfilter) — перекрывает сохранённый, если задан. */
|
||||
initialFilterRequestTypeId?: string;
|
||||
onOpenSteps: (route: TemplateRoute) => void;
|
||||
}
|
||||
|
||||
export const RoutesScreen: React.FC<IRoutesScreenProps> = ({ api, dictionary, onOpenSteps }) => {
|
||||
// Фильтр по типу заявки переживает перезагрузку: администратор обычно работает
|
||||
// с одним типом заявок подряд, и сбрасывать выбор на каждый заход неудобно.
|
||||
const ROUTES_FILTER_STORAGE_KEY = 'approvalAdmin.routesFilter';
|
||||
|
||||
export const RoutesScreen: React.FC<IRoutesScreenProps> = ({
|
||||
api,
|
||||
dictionary,
|
||||
initialFilterRequestTypeId,
|
||||
onOpenSteps
|
||||
}) => {
|
||||
const requestTypes = useRequestTypes(api);
|
||||
const portalDictionaries = usePortalDictionaries(dictionary);
|
||||
|
||||
const [filterRequestTypeId, setFilterRequestTypeId] = React.useState('');
|
||||
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<TemplateRoute | undefined>();
|
||||
const [showForm, setShowForm] = React.useState(false);
|
||||
const [copying, setCopying] = React.useState<TemplateRoute | undefined>();
|
||||
|
||||
const deleteTarget = useDeleteTarget<TemplateRoute>(item => api.templateRoutes.remove(item.id), routes.reload);
|
||||
|
||||
@@ -87,6 +118,7 @@ export const RoutesScreen: React.FC<IRoutesScreenProps> = ({ api, dictionary, on
|
||||
|
||||
<DataTable
|
||||
columns={[
|
||||
{ key: 'id', header: texts.routes.columnId, width: '90px', render: r => `#${r.id}` },
|
||||
{
|
||||
key: 'requestType',
|
||||
header: texts.routes.columnRequestType,
|
||||
@@ -115,6 +147,7 @@ export const RoutesScreen: React.FC<IRoutesScreenProps> = ({ api, dictionary, on
|
||||
render: r => (
|
||||
<ActionsCell>
|
||||
<Button size="sm" variant="secondary" icon="clipboard" onClick={() => onOpenSteps(r)}>{texts.routes.openSteps}</Button>
|
||||
<IconButton icon="copy" label={texts.routes.copy} onClick={() => setCopying(r)} />
|
||||
<IconButton icon="edit" label={texts.common.edit} onClick={() => openEdit(r)} />
|
||||
<IconButton icon="trash" label={texts.common.remove} danger onClick={() => deleteTarget.ask(r)} />
|
||||
</ActionsCell>
|
||||
@@ -140,6 +173,19 @@ export const RoutesScreen: React.FC<IRoutesScreenProps> = ({ api, dictionary, on
|
||||
/>
|
||||
)}
|
||||
|
||||
{copying && (
|
||||
<RouteCopyModal
|
||||
api={api}
|
||||
source={copying}
|
||||
requestTypeName={requestTypeName(copying.requestTypeId)}
|
||||
companies={portalDictionaries.companies}
|
||||
departments={portalDictionaries.departments}
|
||||
dictionaryLoading={portalDictionaries.isLoading}
|
||||
onCopied={routes.reload}
|
||||
onClose={() => setCopying(undefined)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{deleteTarget.target && (
|
||||
<ConfirmDialog
|
||||
title={texts.routes.deleteTitle}
|
||||
|
||||
Reference in New Issue
Block a user