import { PageId } from '../routing/routes'; import { IAccessibleProject, IAccessRow, ProjectStatusKind } from './projectTypes'; // Правила главной страницы (stroy-bot-main), вынесенные из DOM-кода в чистые функции. /** Сколько проектов показывать в выпадающем списке и сколько «недавних» поднимать наверх. */ export const PROJECT_LIST_LIMIT = 7; /** Несколько записей «Доступы» на один проект сливаются: canEdit — OR по edit0. */ export function mergeAccessRows(rows: IAccessRow[]): IAccessibleProject[] { const byId = new Map(); rows.forEach((row) => { if (!row.project) { return; } const existing = byId.get(row.project.Id); if (existing) { existing.canEdit = existing.canEdit || !!row.edit0; return; } byId.set(row.project.Id, { id: row.project.Id, title: row.project.Title ?? '', canEdit: !!row.edit0 }); }); const projects: IAccessibleProject[] = []; byId.forEach((project) => projects.push(project)); return projects; } /** Алфавитный порядок, затем недавно активные (в порядке recentIds) — наверх. */ export function sortProjects(projects: IAccessibleProject[], recentIds: number[]): IAccessibleProject[] { const alphabetical = [...projects].sort((a, b) => a.title.localeCompare(b.title)); if (recentIds.length === 0) { return alphabetical; } return alphabetical.sort((a, b) => { const aIndex = recentIds.indexOf(a.id); const bIndex = recentIds.indexOf(b.id); if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex; if (aIndex !== -1) return -1; if (bIndex !== -1) return 1; return 0; }); } /** Первые N уникальных projectId из отчётов, уже отсортированных по дате (desc). */ export function pickRecentProjectIds(projectIds: Array): number[] { const result: number[] = []; for (const id of projectIds) { if (id !== undefined && result.indexOf(id) === -1) { result.push(id); if (result.length === PROJECT_LIST_LIMIT) break; } } return result; } /** Поиск в выпадающем списке: подстрока без учёта регистра, не больше 7 строк. */ export function filterProjects(projects: IAccessibleProject[], search: string): IAccessibleProject[] { const query = search.toLowerCase(); return projects.filter((project) => project.title.toLowerCase().indexOf(query) !== -1).slice(0, PROJECT_LIST_LIMIT); } export function getStatusKind(status: string | undefined): ProjectStatusKind { const value = status?.trim().toLowerCase() ?? ''; if (value === 'текущий') return 'current'; if (value === 'завершен' || value === 'завершён') return 'done'; if (value === 'архивный') return 'archive'; return 'unknown'; } /** * Доступен ли раздел для проекта. Правила карточек stroy-bot-main: * - архивный проект — закрыто всё (кроме архива, он не раздел приложения); * - завершён — нельзя «Отправить отчёт»; * - нет права edit0 — нельзя «Отправить отчёт». * Импорт отчёта из Excel создаёт отчёты — те же правила, что у «Отправить отчёт». * Привязка к справочнику — только с правом редактирования (решение при объединении). * Импорт работ из Excel меняет работы проекта — тоже только с правом редактирования. */ export function isSectionAvailable(page: PageId, project: IAccessibleProject): boolean { if (page === 'hub') return true; const kind = getStatusKind(project.status); if (kind === 'archive') return false; if (page === 'report' || page === 'reportImport') return kind !== 'done' && project.canEdit; if (page === 'binder' || page === 'worksImport') return project.canEdit; 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'; }