diff --git a/frontend/src/webparts/approvalAdmin/components/app/AdminApp.tsx b/frontend/src/webparts/approvalAdmin/components/app/AdminApp.tsx index 1fb17ab..efaa5b9 100644 --- a/frontend/src/webparts/approvalAdmin/components/app/AdminApp.tsx +++ b/frontend/src/webparts/approvalAdmin/components/app/AdminApp.tsx @@ -149,7 +149,14 @@ export const AdminApp: React.FC = ({ api, dictionary, sharePoint
{route.tab === 'dashboard' && } {route.tab === 'requestTypes' && } - {route.tab === 'requestTypeAccess' && } + {route.tab === 'requestTypeAccess' && ( + + )} {route.tab === 'routes' && !route.routeId && ( = ({ api, dictionary }) => { +// Фильтры переживают перезагрузку — тот же приём, что и ROUTES_FILTER_STORAGE_KEY в RoutesScreen. +const ACCESS_FILTER_TYPE_STORAGE_KEY = 'approvalAdmin.accessFilterRequestTypeId'; +const ACCESS_FILTER_EMAIL_STORAGE_KEY = 'approvalAdmin.accessFilterEmail'; + +export const RequestTypeAccessScreen: React.FC = ({ + api, + dictionary, + initialFilterRequestTypeId, + initialFilterEmail +}) => { const requestTypes = useRequestTypes(api); const access = useRequestTypeAccess(api); const employees = usePortalEmployees(dictionary); @@ -41,10 +57,37 @@ export const RequestTypeAccessScreen: React.FC = const [email, setEmail] = React.useState(''); const submit = useAsyncSubmit(); + const [filterRequestTypeId, setFilterRequestTypeId] = useLocalStorageState(ACCESS_FILTER_TYPE_STORAGE_KEY, ''); + const [filterEmail, setFilterEmail] = useLocalStorageState(ACCESS_FILTER_EMAIL_STORAGE_KEY, ''); + + // Ссылка с фильтром сильнее сохранённого значения — только на монтировании + // (см. тот же приём в RoutesScreen). + React.useEffect(() => { + if (initialFilterRequestTypeId) setFilterRequestTypeId(initialFilterRequestTypeId); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useEffect(() => { + if (initialFilterEmail) setFilterEmail(initialFilterEmail); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // Держим адрес синхронным с фильтрами — отфильтрованную таблицу можно + // передать ссылкой. replaceRoute, а не navigate: смена фильтра не должна + // плодить записи в истории браузера. + React.useEffect(() => { + replaceRoute({ tab: 'requestTypeAccess', accessFilterRequestTypeId: filterRequestTypeId, accessFilterEmail: filterEmail }); + }, [filterRequestTypeId, filterEmail]); + const deleteTarget = useDeleteTarget(item => api.requestTypeAccess.remove(item.id), access.reload); const requestTypeName = (id: number): string => requestTypes.items.find(rt => rt.id === id)?.name ?? `#${id}`; + const filteredItems = access.items.filter(item => { + if (filterRequestTypeId && String(item.requestTypeId) !== filterRequestTypeId) return false; + if (filterEmail && item.email.toLowerCase().indexOf(filterEmail.trim().toLowerCase()) === -1) return false; + return true; + }); + // Список SharePoint нужно вручную открыть на чтение этому email — иначе доступ // здесь настраивает только видимость в UI approval-route, но не в самом // SharePoint (поиск по порталу отфильтрует чужие заявки правами списка). @@ -87,6 +130,28 @@ export const RequestTypeAccessScreen: React.FC = actions={} /> +
+
+ +