import * as React from 'react'; // Маршрутизация портала по параметрам URL. // // Обычный режим (нет параметров) — подача заявок (features/submit). // Режим согласующего — переход по ссылке из потока Power Automate: // …/СтраницаПортала.aspx?rpmode=approve&rptype=<форма>&rpid= // где rptype = ключ формы в реестре (forms/registry), rpid = RowID заявки в списке данных. // // Префикс rp (request-portal): имена id/mode/type зарезервированы SharePoint // (напр. ?id= он трактует как навигацию к элементу → «Элемент не существует»), // поэтому используем собственные, неконфликтные имена параметров. // // Навигация внутри портала (напр. клик по заявке в «Мои заявки») идёт через // navigate() — это history.pushState без перезагрузки страницы. useRoute() // подписан и на popstate (кнопка «назад»), и на её собственное событие, // поэтому реагирует на оба способа смены URL. export type PortalMode = 'submit' | 'approve' | 'create'; export interface IRoute { mode: PortalMode; /** Ключ формы (тип заявки) — для 'approve' и 'create'. */ formKey?: string; /** Id заявки в списке данных — для 'approve'. */ itemId?: number; /** Id исходной заявки для предзаполнения — для 'create' (напр. орг. места на основе прием). */ sourceId?: number; } const ROUTE_CHANGE_EVENT = 'rp-route-change'; /** Разобрать строку запроса (по умолчанию — из адреса страницы) в маршрут. */ export function parseRoute(search: string = window.location.search): IRoute { const params = new URLSearchParams(search); const mode = params.get('rpmode'); const formKey = params.get('rptype') ?? undefined; const idRaw = params.get('rpid'); const itemId = idRaw ? Number(idRaw) : undefined; const sourceRaw = params.get('rpsource'); const sourceId = sourceRaw ? Number(sourceRaw) : undefined; // Режим согласующего — при полном наборе параметров. if (mode === 'approve' && formKey && itemId && !isNaN(itemId)) { return { mode: 'approve', formKey, itemId }; } // Создание производной заявки с предзаполнением из исходной. if (formKey && sourceId && !isNaN(sourceId)) { return { mode: 'create', formKey, sourceId }; } return { mode: 'submit' }; } /** * Перейти на другой маршрут без перезагрузки страницы (SPA): обновляет * query-параметры адресной строки через history.pushState и оповещает * useRoute() своим событием (popstate на pushState сам не срабатывает). */ export function navigate(route: IRoute): void { const params = new URLSearchParams(window.location.search); params.delete('rpmode'); params.delete('rptype'); params.delete('rpid'); params.delete('rpsource'); if (route.mode === 'approve' && route.formKey && route.itemId) { params.set('rpmode', 'approve'); params.set('rptype', route.formKey); params.set('rpid', String(route.itemId)); } else if (route.mode === 'create' && route.formKey && route.sourceId) { params.set('rptype', route.formKey); params.set('rpsource', String(route.sourceId)); } const query = params.toString(); const url = `${window.location.pathname}${query ? `?${query}` : ''}${window.location.hash}`; window.history.pushState({}, '', url); window.dispatchEvent(new Event(ROUTE_CHANGE_EVENT)); } /** Ссылка на маршрут (для href — открытие в новой вкладке, копирование и т.п.). */ export function routeUrl(route: IRoute): string { const params = new URLSearchParams(window.location.search); params.delete('rpmode'); params.delete('rptype'); params.delete('rpid'); params.delete('rpsource'); if (route.mode === 'approve' && route.formKey && route.itemId) { params.set('rpmode', 'approve'); params.set('rptype', route.formKey); params.set('rpid', String(route.itemId)); } else if (route.mode === 'create' && route.formKey && route.sourceId) { params.set('rptype', route.formKey); params.set('rpsource', String(route.sourceId)); } const query = params.toString(); return `${window.location.pathname}${query ? `?${query}` : ''}${window.location.hash}`; } /** Текущий маршрут, реактивно обновляется по navigate() и кнопке «назад». */ export function useRoute(): IRoute { const [route, setRoute] = React.useState(() => parseRoute()); React.useEffect(() => { const onChange = (): void => setRoute(parseRoute()); window.addEventListener('popstate', onChange); window.addEventListener(ROUTE_CHANGE_EVENT, onChange); return () => { window.removeEventListener('popstate', onChange); window.removeEventListener(ROUTE_CHANGE_EVENT, onChange); }; }, []); return route; }