diff --git a/src/webparts/stroyBot/app/project/ProjectContext.tsx b/src/webparts/stroyBot/app/project/ProjectContext.tsx index 59aefb6..a33cfcf 100644 --- a/src/webparts/stroyBot/app/project/ProjectContext.tsx +++ b/src/webparts/stroyBot/app/project/ProjectContext.tsx @@ -4,6 +4,7 @@ import { useRouter } from '../routing/RouterContext'; import { IAccessibleProject } from './projectTypes'; import { fetchAccessibleProjects } from './projectRepository'; import { readStoredProject, storeProject } from './projectSession'; +import { resolveCurrentProject } from './projectRules'; export type ProjectsLoadState = 'loading' | 'ready' | 'error'; @@ -51,13 +52,12 @@ export const ProjectProvider: React.FC<{ children: React.ReactNode }> = ({ child if (isCancelled) return; setProjects(loaded); setLoadState('ready'); - // В сессии могли остаться устаревшие права — обновляем по свежим данным (как в main). + // В сессии могли остаться устаревшие статус и права — сохраняем свежие. setStoredProject((stored) => { const fresh = stored ? findProject(loaded, stored.id) : undefined; - if (!stored || !fresh) return stored; - const updated = { ...stored, canEdit: fresh.canEdit }; - storeProject(updated); - return updated; + if (!fresh) return stored; + storeProject(fresh); + return fresh; }); }) .catch((error: unknown) => { @@ -71,13 +71,7 @@ export const ProjectProvider: React.FC<{ children: React.ReactNode }> = ({ child }, [services, reloadKey]); const routeProjectId = route.projectId ? Number(route.projectId) : undefined; - let currentProject: IAccessibleProject | undefined; - if (routeProjectId === undefined) { - currentProject = storedProject; - } else { - currentProject = - findProject(projects, routeProjectId) ?? (storedProject?.id === routeProjectId ? storedProject : undefined); - } + const currentProject = resolveCurrentProject(projects, routeProjectId, storedProject); const currentProjectId = route.projectId ?? (storedProject ? String(storedProject.id) : undefined); const selectProject = React.useCallback( diff --git a/src/webparts/stroyBot/app/project/projectRules.test.ts b/src/webparts/stroyBot/app/project/projectRules.test.ts index 9ee0436..628870b 100644 --- a/src/webparts/stroyBot/app/project/projectRules.test.ts +++ b/src/webparts/stroyBot/app/project/projectRules.test.ts @@ -5,6 +5,7 @@ import { isSectionAvailable, mergeAccessRows, pickRecentProjectIds, + resolveCurrentProject, sortProjects } from './projectRules'; import { IAccessibleProject } from './projectTypes'; @@ -71,6 +72,25 @@ describe('getStatusKind', () => { }); }); +describe('resolveCurrentProject', () => { + const stored = project({ id: 51, status: 'Текущий', canEdit: true }); + const fresh = project({ id: 51, status: 'Архив', canEdit: false }); + + it('свежие статус и права из списка главнее сохранённых в сессии (баг T-HUB-07/09)', () => { + expect(resolveCurrentProject([fresh], undefined, stored)).toBe(fresh); + expect(resolveCurrentProject([fresh], 51, stored)).toBe(fresh); + }); + + it('пока список не загрузился — сохранённый выбор', () => { + expect(resolveCurrentProject([], undefined, stored)).toBe(stored); + }); + + it('проект из адреса, которого нет ни в списке, ни в сессии, — не выбран', () => { + expect(resolveCurrentProject([fresh], 7, stored)).toBeUndefined(); + expect(resolveCurrentProject([], undefined, undefined)).toBeUndefined(); + }); +}); + describe('isSectionAvailable', () => { it('архивный проект — закрыты все разделы, кроме главной', () => { const archived = project({ status: 'архивный' }); diff --git a/src/webparts/stroyBot/app/project/projectRules.ts b/src/webparts/stroyBot/app/project/projectRules.ts index eb1316b..d78455a 100644 --- a/src/webparts/stroyBot/app/project/projectRules.ts +++ b/src/webparts/stroyBot/app/project/projectRules.ts @@ -85,6 +85,23 @@ export function isSectionAvailable(page: PageId, project: IAccessibleProject): b return true; } +/** + * Текущий проект: id из адреса, а без него — сохранённый в сессии выбор. + * Данные (статус, права) берутся из свежего списка с сервера; сохранённая копия — только пока список + * не загрузился или если проекта в нём нет. Иначе после смены статуса / прав F5 показывал бы старые. + */ +export function resolveCurrentProject( + projects: IAccessibleProject[], + routeProjectId: number | undefined, + storedProject: IAccessibleProject | undefined +): IAccessibleProject | undefined { + const targetId = routeProjectId ?? storedProject?.id; + if (targetId === undefined) return undefined; + const fresh = projects.filter((project) => project.id === targetId)[0]; + if (fresh) return fresh; + return storedProject?.id === targetId ? storedProject : undefined; +} + /** Выгрузка за период недоступна только для архивного проекта. */ export function isExportAvailable(project: IAccessibleProject): boolean { return getStatusKind(project.status) !== 'archive';