import type { GpoBackupStatusRecord } from '../../api/assessor-api-contract'; import type { GpoEvaluationUpdate } from '../../domain/policy-analysis-overview'; import { GpoFolderImport } from './GpoFolderImport'; import { missingGpoSourceMessage, unavailableGpoStorageMessage } from './gpo-source-recovery'; const labels: Record = { queued: 'Queued', running: 'Processing', succeeded: 'Ready', partial: 'Limited analysis', 'dead-letter': 'Failed', 'recovery-required': 'Recovery pending', 'upload-required': 'Upload required', 'storage-unavailable': 'Storage unavailable' }; /** Names every retained input that changed, so the cause stays visible before re-evaluating. */ export function gpoReevaluationCause(updates: readonly GpoEvaluationUpdate[]): string { const reasons = new Set(updates.flatMap(update => update.reasons ?? ['engine-version'])); const causes = [ ...(reasons.has('engine-version') ? [`Engine ${[...new Set(updates.map(update => `${update.previousVersion} → ${update.currentVersion}`))].join(', ')}`] : []), ...(reasons.has('baseline-analysis') ? ['Updated Microsoft baseline analysis'] : []) ]; return causes.join(' · '); } export function GpoStatusRow({ projectId, record, imported, busy, onEvaluate, onReprocess, onRetry, onDelete }: { projectId: string; record: GpoBackupStatusRecord; imported: string; busy: boolean; onEvaluate(): void; onReprocess(): void; onRetry(): void; onDelete(): void; }) { const pending = ['queued', 'running'].includes(record.jobStatus ?? record.state); const sourceBlocked = record.state === 'upload-required' || record.state === 'storage-unavailable'; const failed = ['dead-letter', 'recovery-required'].includes(record.state) || sourceBlocked; // Reprocessing rebuilds what parsing retained and therefore precedes re-evaluation. const analysis = record.analysisUpdate; const outdated = Boolean(record.evaluationUpdates?.length) || Boolean(analysis); const cause = analysis ? `Analyzer ${analysis.previousAnalyzerVersion} → ${analysis.currentAnalyzerVersion}` : gpoReevaluationCause(record.evaluationUpdates ?? []); return
G GPO Backup {record.fileName} · {record.gpoCount.toLocaleString('en-US')} GPO{record.gpoCount === 1 ? '' : 's'} {Boolean(record.notLinkedGpoCount) && · {record.notLinkedGpoCount!.toLocaleString('en-US')} not linked} {sourceBlocked && {record.state === 'upload-required' ? missingGpoSourceMessage : unavailableGpoStorageMessage}} {sourceBlocked && outdated && !analysis && Re-evaluation required · {cause}. Retained inputs can still be re-evaluated.} {outdated && !sourceBlocked && {analysis ? 'Reprocessing required' : 'Re-evaluation required'} · {cause}. Saved results are historical. {analysis ? 'Reprocess the stored source' : 'Re-evaluate retained inputs'}; no file upload is required for this update.} {imported} {outdated && !pending && !failed ? analysis ? 'Reprocessing required' : 'Re-evaluation required' : labels[record.state]} {record.state === 'upload-required' && } {analysis ? !sourceBlocked && : outdated && } {record.state === 'dead-letter' && }
; }