feat: weather conditions check from stroy-bot-main
Ports the weather check added to stroy-bot-main on 2026-10-16: daily station weather, report positions with weather per shift and the AI assessment of working conditions. Opens as ?page=weather&start=&end= instead of the #weathercheck hash, and hub dates survive the way back. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import { buildSearch, IRoute, parseRoute } from './routes';
|
||||
|
||||
interface INavigateOptions {
|
||||
/** Заменить текущую запись истории (replaceState), а не добавить новую. */
|
||||
replace?: boolean;
|
||||
}
|
||||
|
||||
interface IRouterValue {
|
||||
route: IRoute;
|
||||
/** Перейти в раздел: меняет адрес (history.pushState) и перерисовывает приложение. */
|
||||
navigate: (route: IRoute) => void;
|
||||
navigate: (route: IRoute, options?: INavigateOptions) => void;
|
||||
/** Полный адрес раздела — для настоящих ссылок (Ctrl+клик, «открыть в новой вкладке»). */
|
||||
buildHref: (route: IRoute) => string;
|
||||
}
|
||||
@@ -29,8 +34,12 @@ export const RouterProvider: React.FC<{ children: React.ReactNode }> = ({ childr
|
||||
return () => window.removeEventListener('popstate', onPopState);
|
||||
}, []);
|
||||
|
||||
const navigate = React.useCallback((next: IRoute): void => {
|
||||
window.history.pushState(undefined, '', buildHref(next));
|
||||
const navigate = React.useCallback((next: IRoute, options: INavigateOptions = {}): void => {
|
||||
if (options.replace) {
|
||||
window.history.replaceState(undefined, '', buildHref(next));
|
||||
} else {
|
||||
window.history.pushState(undefined, '', buildHref(next));
|
||||
}
|
||||
setRoute(next);
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
@@ -17,4 +17,20 @@ describe('parseRoute', () => {
|
||||
it('адрес только с projectId=7', () => {
|
||||
expect(parseRoute('?projectId=7')).toEqual({ page: 'hub', projectId: '7' });
|
||||
});
|
||||
it('сверка погоды с периодом', () => {
|
||||
expect(parseRoute('?page=weather&projectId=7&start=2026-10-01&end=2026-10-07')).toEqual({
|
||||
page: 'weather',
|
||||
projectId: '7',
|
||||
start: '2026-10-01',
|
||||
end: '2026-10-07'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildSearch', () => {
|
||||
it('параметры прошлого маршрута не переносятся, посторонние параметры страницы остаются', () => {
|
||||
expect(buildSearch({ page: 'weather', projectId: '7', start: '2026-10-01' }, '?debug=true&reportId=r1')).toEqual(
|
||||
'?debug=true&page=weather&projectId=7&start=2026-10-01'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,17 @@
|
||||
// SharePoint (?page=objects&projectId=5), поэтому ссылку на любой раздел можно
|
||||
// скопировать, открыть в новой вкладке, а «назад» в браузере работает как раньше.
|
||||
|
||||
export const PAGE_IDS = ['hub', 'objects', 'works', 'reports', 'report', 'import', 'resources', 'binder'] as const;
|
||||
export const PAGE_IDS = [
|
||||
'hub',
|
||||
'objects',
|
||||
'works',
|
||||
'reports',
|
||||
'report',
|
||||
'import',
|
||||
'resources',
|
||||
'binder',
|
||||
'weather'
|
||||
] as const;
|
||||
|
||||
export type PageId = (typeof PAGE_IDS)[number];
|
||||
|
||||
@@ -16,6 +26,10 @@ export interface IRoute {
|
||||
reportId?: string;
|
||||
/** Форма отчёта: создать на основе существующего (GUID Dataverse). */
|
||||
copyFromId?: string;
|
||||
/** Главная и сверка погоды: начало периода (YYYY-MM-DD). */
|
||||
start?: string;
|
||||
/** Главная и сверка погоды: конец периода (YYYY-MM-DD). */
|
||||
end?: string;
|
||||
}
|
||||
|
||||
export const ROUTE_PARAMS = {
|
||||
@@ -23,10 +37,19 @@ export const ROUTE_PARAMS = {
|
||||
projectId: 'projectId',
|
||||
objectId: 'objectId',
|
||||
reportId: 'reportId',
|
||||
copyFromId: 'copyFromId'
|
||||
copyFromId: 'copyFromId',
|
||||
start: 'start',
|
||||
end: 'end'
|
||||
} as const;
|
||||
|
||||
const ROUTE_VALUE_KEYS: Array<Exclude<keyof IRoute, 'page'>> = ['projectId', 'objectId', 'reportId', 'copyFromId'];
|
||||
const ROUTE_VALUE_KEYS: Array<Exclude<keyof IRoute, 'page'>> = [
|
||||
'projectId',
|
||||
'objectId',
|
||||
'reportId',
|
||||
'copyFromId',
|
||||
'start',
|
||||
'end'
|
||||
];
|
||||
|
||||
function isPageId(value: string | undefined): value is PageId {
|
||||
return value !== undefined && (PAGE_IDS as readonly string[]).indexOf(value) !== -1;
|
||||
|
||||
Reference in New Issue
Block a user