import type { Requirement, RequirementStatus } from './requirement'; export type RequirementScopeSelection = | { type: 'all' } | { type: 'product'; productId: string } | { type: 'project'; projectId: string }; export interface RequirementScopeCount { total: number; byStatus: Partial>; } export interface RequirementProjectScopeNode { id: string; name: string; count: RequirementScopeCount; } export interface RequirementProductScopeNode { id: string; name: string; count: RequirementScopeCount; projects: RequirementProjectScopeNode[]; } export function filterRequirementsByScope( requirements: Requirement[], scope: RequirementScopeSelection, ): Requirement[] { if (scope.type === 'product') { return requirements.filter((requirement) => requirement.productId === scope.productId); } if (scope.type === 'project') { return requirements.filter((requirement) => requirement.projectId === scope.projectId); } return requirements; } export function buildRequirementScopeTree( overview: { id: string; name: string; projects: { id: string; name: string }[] }[], requirements: Requirement[], ): RequirementProductScopeNode[] { return overview.map((product) => { const productRequirements = requirements.filter((requirement) => requirement.productId === product.id); return { id: product.id, name: product.name, count: countRequirements(productRequirements), projects: product.projects.map((project) => { const projectRequirements = requirements.filter((requirement) => requirement.projectId === project.id); return { id: project.id, name: project.name, count: countRequirements(projectRequirements), }; }), }; }); } export function filterRequirementScopeTreeByKeyword( tree: RequirementProductScopeNode[], keyword: string, ): RequirementProductScopeNode[] { const query = normalizeScopeKeyword(keyword); if (!query) return tree; const filtered: RequirementProductScopeNode[] = []; for (const product of tree) { if (normalizeScopeKeyword(product.name).includes(query)) { filtered.push(product); continue; } const projects = product.projects.filter((project) => normalizeScopeKeyword(project.name).includes(query)); if (projects.length > 0) { filtered.push({ ...product, projects }); } } return filtered; } function countRequirements(requirements: Requirement[]): RequirementScopeCount { const byStatus: Partial> = {}; for (const requirement of requirements) { byStatus[requirement.status] = (byStatus[requirement.status] ?? 0) + 1; } return { total: requirements.length, byStatus, }; } function normalizeScopeKeyword(value: string): string { return value.trim().toLowerCase(); }