теперь можно пройти по заявку в мои заявки

This commit is contained in:
gorbenko.pavel
2026-08-04 12:09:47 +05:00
parent 29a91a5654
commit 79e44e01be
10 changed files with 199 additions and 53 deletions
@@ -1,3 +1,5 @@
import * as React from 'react';
// Маршрутизация портала по параметрам URL.
//
// Обычный режим (нет параметров) — подача заявок (features/submit).
@@ -8,6 +10,11 @@
// Префикс rp (request-portal): имена id/mode/type зарезервированы SharePoint
// (напр. ?id= он трактует как навигацию к элементу → «Элемент не существует»),
// поэтому используем собственные, неконфликтные имена параметров.
//
// Навигация внутри портала (напр. клик по заявке в «Мои заявки») идёт через
// navigate() — это history.pushState без перезагрузки страницы. useRoute()
// подписан и на popstate (кнопка «назад»), и на её собственное событие,
// поэтому реагирует на оба способа смены URL.
export type PortalMode = 'submit' | 'approve' | 'create';
@@ -21,6 +28,8 @@ export interface IRoute {
sourceId?: number;
}
const ROUTE_CHANGE_EVENT = 'rp-route-change';
/** Разобрать строку запроса (по умолчанию — из адреса страницы) в маршрут. */
export function parseRoute(search: string = window.location.search): IRoute {
const params = new URLSearchParams(search);
@@ -41,3 +50,68 @@ export function parseRoute(search: string = window.location.search): IRoute {
}
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<IRoute>(() => 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;
}