import { useMemo } from 'react'; import { useApp } from '../../app/AppProvider'; import type { DatasetSheet, ImportedDataset } from '../../domain/dataset'; import type { ImportProfile } from '../../domain/import-profile'; import type { Review } from '../../domain/review'; import { measureNavigationStep } from '../../app/navigation-diagnostics'; import { SECURITY_DOMAIN_FACET_ID, securityDomainCatalog } from '../dashboard/security-domains'; import { type DynamicFacetFilters } from '../search-filter/dynamic-facets'; import { selectWorkFacets } from '../search-filter/work-facets'; import type { ContextClusterIndex } from '../context-layer/context-cluster-facet'; import { useContextClusterIndex } from '../context-layer/use-context-cluster-index'; import { businessImpactControls, BUSINESS_IMPACT_FACET_ID } from '../search-filter/business-impact-controls'; import { useEnginePreferences } from '../../app/engine-preferences'; interface SecurityDomainOptions { sheet: DatasetSheet | undefined; dataset: ImportedDataset | null; activeProfile: ImportProfile; reviews: Record; statusFilter: string; facetFilters: DynamicFacetFilters; pathFindingIds?: readonly string[]; search: string; actionOnly: boolean; contextClusters?: ContextClusterIndex; } export interface SidebarSecurityDomain { id: string; label: string; count: number; } export interface SidebarSecurityDomainSummary { facetId: typeof SECURITY_DOMAIN_FACET_ID; domains: SidebarSecurityDomain[]; } /** Uses the exact shared Client Info facet, ordering and self-excluding counts. */ export function summarizeSecurityDomains(options: SecurityDomainOptions): SidebarSecurityDomainSummary | undefined { const { sheet, dataset, activeProfile, reviews, statusFilter, facetFilters, pathFindingIds, search, actionOnly, contextClusters } = options; if (!sheet) return undefined; const facets = selectWorkFacets(sheet, { dataset, activeProfile, reviews, statusFilter, facetFilters, pathFindingIds, search, actionOnly, contextClusters }); const domainFacet = facets.find(facet => facet.id === SECURITY_DOMAIN_FACET_ID); if (!domainFacet) return undefined; const counts = new Map(domainFacet.values.map(value => [value.value, value.count])); const domains: SidebarSecurityDomain[] = securityDomainCatalog.map(domain => ({ id: domain.id, label: domain.label, count: counts.get(domain.id) ?? 0 })); return { facetId: SECURITY_DOMAIN_FACET_ID, domains }; } export function toggleSecurityDomainSelection(selected: string[], domainId: string): string[] { return selected.includes(domainId) ? [] : [domainId]; } export function SidebarSecurityDomains({ sheet }: { sheet: DatasetSheet | undefined }) { const { dataset, activeProfile, reviews, statusFilter, facetFilters, pathFindingIds, search, actionOnly, setFacetFilter, setStatusFilter } = useApp(); const contextClusters = useContextClusterIndex(); const engines = useEnginePreferences(); const impactCounts = useMemo(() => { if (!engines.report || !engines.context || !contextClusters) return undefined; const facet = selectWorkFacets(sheet, { dataset, activeProfile, reviews, statusFilter: 'deviation', facetFilters, pathFindingIds, search, actionOnly, contextClusters }).find(item => item.id === BUSINESS_IMPACT_FACET_ID); if (!facet) return undefined; const counts = new Map(facet.values.map(value => [value.value, value.count])); return businessImpactControls.map(theme => ({ ...theme, count: counts.get(theme.id) ?? 0 })); }, [engines.report, engines.context, contextClusters, sheet, dataset, activeProfile, reviews, facetFilters, pathFindingIds, search, actionOnly]); const summary = useMemo(() => measureNavigationStep('security-domain-summary', () => summarizeSecurityDomains({ sheet, dataset, activeProfile, reviews, statusFilter, facetFilters, pathFindingIds, search, actionOnly, contextClusters })), [actionOnly, activeProfile, contextClusters, dataset, facetFilters, pathFindingIds, reviews, search, sheet, statusFilter]); if (!summary) return null; const selected = facetFilters[summary.facetId]?.values ?? []; return (
{summary.domains.map(domain => { const active = selected.includes(domain.id); return ; })} {impactCounts &&
{impactCounts.map(theme => { const active = facetFilters[BUSINESS_IMPACT_FACET_ID]?.values?.includes(theme.id) ?? false; return ; })}
}
); }