feat: add StroyBot app shell with URL routing
One web part replaces the separate stroy-bot-* web parts. The section and its parameters live in the page query string (?page=objects&projectId=5), so links can be copied, opened in a new tab and the browser Back button works. - shared config: site/Dataverse/API addresses, list and table names - HTTP layer behind IHttpClient so repositories can be unit-tested - ui-kit AppLayout + Sidebar, placeholder for sections not ported yet - migration checklist with every feature of the old web parts - design tokens mirrored from ui-kit instead of 8 copies Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
// Маршруты приложения. Раздел и его параметры живут в query-строке страницы
|
||||
// SharePoint (?page=objects&projectId=5), поэтому ссылку на любой раздел можно
|
||||
// скопировать, открыть в новой вкладке, а «назад» в браузере работает как раньше.
|
||||
|
||||
export const PAGE_IDS = ['hub', 'objects', 'works', 'reports', 'report', 'import', 'resources', 'binder'] as const;
|
||||
|
||||
export type PageId = (typeof PAGE_IDS)[number];
|
||||
|
||||
/** Параметры маршрута. Все id — строки как в URL; разбор в числа делают страницы. */
|
||||
export interface IRoute {
|
||||
page: PageId;
|
||||
projectId?: string;
|
||||
/** Работы: id элемента списка «Объекты». */
|
||||
objectId?: string;
|
||||
/** Форма отчёта: открыть существующий отчёт (GUID Dataverse). */
|
||||
reportId?: string;
|
||||
/** Форма отчёта: создать на основе существующего (GUID Dataverse). */
|
||||
copyFromId?: string;
|
||||
}
|
||||
|
||||
export const ROUTE_PARAMS = {
|
||||
page: 'page',
|
||||
projectId: 'projectId',
|
||||
objectId: 'objectId',
|
||||
reportId: 'reportId',
|
||||
copyFromId: 'copyFromId'
|
||||
} as const;
|
||||
|
||||
const ROUTE_VALUE_KEYS: Array<Exclude<keyof IRoute, 'page'>> = ['projectId', 'objectId', 'reportId', 'copyFromId'];
|
||||
|
||||
function isPageId(value: string | undefined): value is PageId {
|
||||
return value !== undefined && (PAGE_IDS as readonly string[]).indexOf(value) !== -1;
|
||||
}
|
||||
|
||||
export function parseRoute(search: string): IRoute {
|
||||
const params = new URLSearchParams(search);
|
||||
const page = params.get(ROUTE_PARAMS.page) ?? undefined;
|
||||
const route: IRoute = { page: isPageId(page) ? page : 'hub' };
|
||||
|
||||
ROUTE_VALUE_KEYS.forEach((key) => {
|
||||
const value = params.get(ROUTE_PARAMS[key]);
|
||||
if (value) {
|
||||
route[key] = value;
|
||||
}
|
||||
});
|
||||
return route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Собирает query-строку для маршрута. Посторонние параметры страницы SharePoint
|
||||
* (например, режим редактирования) сохраняются, параметры прошлого маршрута — нет.
|
||||
*/
|
||||
export function buildSearch(route: IRoute, currentSearch: string): string {
|
||||
const params = new URLSearchParams(currentSearch);
|
||||
params.delete(ROUTE_PARAMS.page);
|
||||
ROUTE_VALUE_KEYS.forEach((key) => params.delete(ROUTE_PARAMS[key]));
|
||||
|
||||
if (route.page !== 'hub') {
|
||||
params.set(ROUTE_PARAMS.page, route.page);
|
||||
}
|
||||
ROUTE_VALUE_KEYS.forEach((key) => {
|
||||
const value = route[key];
|
||||
if (value) {
|
||||
params.set(ROUTE_PARAMS[key], value);
|
||||
}
|
||||
});
|
||||
|
||||
const query = params.toString();
|
||||
return query ? `?${query}` : '';
|
||||
}
|
||||
Reference in New Issue
Block a user