From b128637c645f9c868dc5b41d9338d4233e5ee725 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 25 Sep 2026 16:42:27 +0500 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D1=8B=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D1=82=D0=B8=D0=BF=D1=83=20=D0=B7=D0=B0=D1=8F=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B8=20=D1=81=D0=BE=D1=82=D1=80=D1=83=D0=B4?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D1=83=20=D0=B2=20=C2=AB=D0=94=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D1=83=D0=BF=20=D0=BA=20=D0=B7=D0=B0=D1=8F=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=D0=BC=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Персистятся тем же приёмом, что и routesFilter у маршрутов: живут в адресе (aaacctype/aaaccemail) и в localStorage, переживают перезагрузку и возврат на вкладку. Co-Authored-By: Claude Sonnet 5 --- .../approvalAdmin/components/app/AdminApp.tsx | 9 ++- .../RequestTypeAccessScreen.module.scss | 12 +++ .../screens/RequestTypeAccessScreen.tsx | 79 ++++++++++++++++++- .../src/webparts/approvalAdmin/routing.ts | 16 ++++ frontend/src/webparts/approvalAdmin/texts.ts | 5 ++ 5 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 frontend/src/webparts/approvalAdmin/components/screens/RequestTypeAccessScreen.module.scss 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={} /> +
+
+ +