import type { GpoBackupStatusRecord, ProjectAssessmentHistoryResponse } from '../../api/assessor-api-contract'; import type { AssessmentProject, ImportFileRecord } from '../../domain/canonical-model'; import { groupProjectImportFiles } from './datahub-import-groups'; import { summarizeClientProcessingReadiness } from './datahub-processing-readiness'; import type { DatahubTimeslotResultMetrics } from './datahub-scan-analysis'; import { DonutMetricCard } from '../../components/DonutMetricCard/DonutMetricCard'; import type { TimeslotOpenReadiness } from './datahub-open-readiness'; import { PanelLoadingState } from '../../components/PanelLoadingState/PanelLoadingState'; export interface DatahubTimeslotSelection { projectId: string; slots: Array<{ id: string; runIds: string[]; assessmentIds: string[]; effectiveAt: string }>; } interface DatahubHistoryOverviewProps { projects: AssessmentProject[]; projectData: Record; projectHistory: Record; projectGpoData: Record; projectOpenReadiness?: Record>; selection: DatahubTimeslotSelection | null; timeslotMetrics: DatahubTimeslotResultMetrics | null; loading: boolean; comparisonBusy: boolean; comparisonError: string | null; onCreate(): void; onShow(project: AssessmentProject): void; onOpen(project: AssessmentProject, scanRunIds: string[], assessmentIds?: string[]): void; onSelectProject(projectId: string): void; } function formatDate(value: string): string { const date = new Date(value); return Number.isNaN(date.getTime()) ? '—' : date.toLocaleDateString('de-DE'); } function resultPercentage(value: number, total: number): number { return total > 0 ? Math.min(100, Math.max(0, (value / total) * 100)) : 0; } export function DatahubHistoryOverview({ projects, projectData, projectHistory, projectGpoData, projectOpenReadiness = {}, selection, timeslotMetrics, loading, comparisonBusy, comparisonError, onCreate, onShow, onOpen, onSelectProject }: DatahubHistoryOverviewProps) { const resultMetrics = timeslotMetrics ?? { all: null, deviation: null, passed: null, notApplicable: null, notRun: null, error: null }; const pendingLabel = loading || comparisonBusy ? 'Loading…' : comparisonError ? 'Unavailable' : selection ? 'No results' : 'Select client'; return <>
{comparisonError && {comparisonError}}
{loading ? : (
{projects.map(project => { const history = projectHistory[project.id]; const groups = groupProjectImportFiles( projectData[project.id] ?? [], history?.assessments, history?.scanRuns, projectGpoData[project.id] ?? [] ); const latest = groups[0]; const latestOpenable = groups.find(group => projectOpenReadiness[project.id]?.[group.id]?.canOpen); const activeProcessingRunIds = groups.flatMap(group => group.scanRunIds); const processingSummary = summarizeClientProcessingReadiness( projectData[project.id] ?? [], activeProcessingRunIds, projectGpoData[project.id] ?? [] ); const selected = selection?.projectId === project.id; return
{processingSummary.state !== 'no-data' && } {(selected || processingSummary.state === 'update' || processingSummary.state === 'review' || processingSummary.state === 'error') && }
; })}
)}
; }