'use client'; import ScanFixPanel from './ScanFixPanel'; import { useTranslation } from '../../../i18n/useTranslation'; import type { WorkspaceScanResult } from '../hooks/useI18nScanWorkflow'; import { useModalAnimation } from '../hooks/useModalAnimation'; type LanguageEntry = { code: string; name: string; }; type NamespaceScanResult = { ns: string; total: number; translated: number; missing: number; }; type Props = { isOpen: boolean; onClose: () => void; lastScanTime: Date | null; workspaceScan: WorkspaceScanResult | null; totalKeys: number; namespacesCount: number; allLanguages: LanguageEntry[]; activeLang: string; scanResults: NamespaceScanResult[]; scanError: string | null; isScanning: boolean; isAutoFixing: boolean; fixableFiles: string[]; selectedFiles: string[]; forceConvertToClient: boolean; onToggleFile: (file: string) => void; onSelectAll: () => void; onClear: () => void; onToggleForceConvertToClient: () => void; onRunFixSelected: () => void; }; export default function ScanResultsModal({ isOpen, onClose, lastScanTime, workspaceScan, totalKeys, namespacesCount, allLanguages, activeLang, scanResults, scanError, isScanning, isAutoFixing, fixableFiles, selectedFiles, forceConvertToClient, onToggleFile, onSelectAll, onClear, onToggleForceConvertToClient, onRunFixSelected, }: Props) { const { t } = useTranslation(); const { isRendered, isVisible } = useModalAnimation(isOpen); if (!isRendered) return null; const totalTranslated = scanResults.reduce((sum, row) => sum + row.translated, 0); const coveragePercent = totalKeys === 0 ? 100 : Math.round((totalTranslated / totalKeys) * 100); return (

{t('autofix.kb2217bdf')}

{workspaceScan ? `${workspaceScan.scannedFiles} files across ${workspaceScan.scannedDirectories} directories scanned` : `${totalKeys} keys across ${namespacesCount} namespaces`} {lastScanTime && ( · Scanned {lastScanTime.toLocaleTimeString()} )}

Overall coverage ({allLanguages.find((l) => l.code === activeLang)?.name ?? activeLang}) {activeLang === 'en' ? `${totalKeys} / ${totalKeys}` : `${totalTranslated} / ${totalKeys}` }
{workspaceScan && (

{t('autofix.k91052e3f')}

{workspaceScan.translationCallCount}

{t('autofix.k90a6e795')}

{workspaceScan.uniqueKeyCount}

{t('autofix.k8cf40180')}

{workspaceScan.missingKeys.length}

{t('autofix.k1bf4ffa4')}

{workspaceScan.untranslatedLiterals.length}

{t('autofix.k9b173204')}

{workspaceScan.changedFileCount ?? 0}

{t('autofix.k60874ea3')}

{workspaceScan.createdKeyCount ?? 0}

)}
{scanError && (
{scanError}
)} {isScanning && (
{t('autofix.k0d9c63c5')}
)} {isAutoFixing && (
{t('autofix.ka802064d')}
)}
{workspaceScan && (workspaceScan.changedFileCount ?? 0) > 0 && (
Auto-fix updated {(workspaceScan.changedFileCount ?? 0)} files and created {(workspaceScan.createdKeyCount ?? 0)} translation keys.
)} {workspaceScan && Array.isArray(workspaceScan.changedFiles) && workspaceScan.changedFiles.length > 0 && (

{t('autofix.k5ad4d864')}

{workspaceScan.changedFiles.map((entry) => (

{entry.file}

{entry.replacements} replacements {entry.addedImport ? ' · import added' : ''} {entry.addedHook ? ' · hook added' : ''}

))}
)} {workspaceScan && Array.isArray(workspaceScan.skippedFiles) && workspaceScan.skippedFiles.length > 0 && (

{t('autofix.k56a52520')}

{workspaceScan.skippedFiles.map((entry) => (

{entry.file}

{entry.reason}

))}
)} {workspaceScan && Array.isArray(workspaceScan.autoFixDebug) && workspaceScan.autoFixDebug.length > 0 && (

{t('autofix.kc8034db6')}

{t('autofix.k9bd0812b')}

{workspaceScan.autoFixDebug.slice(0, 200).map((entry) => (

{entry.file}

{entry.status}

before: {entry.beforeLiteralCount} · text: {entry.textReplacements} · attr: {entry.attrReplacements} · after: {entry.afterLiteralCount}

{entry.reason &&

{entry.reason}

}
))}
)}

{t('autofix.kbe30c353')}

{scanResults.map(({ ns, total, translated, missing }) => { const pct = total === 0 ? 100 : Math.round((translated / total) * 100); return ( ); })}
Namespace Keys Translated Missing Coverage
{ns} {total} {translated} 0 ? 'text-red-500' : 'text-gray-400'}`}> {missing}
= 50 ? 'bg-yellow-400' : 'bg-red-400' }`} style={{ width: `${pct}%` }} />
{pct}%
{workspaceScan && workspaceScan.missingKeys.length > 0 && (

{t('autofix.kae63e46a')}

{workspaceScan.missingKeys.map((entry) => (

{entry.key}

{entry.files.slice(0, 3).join(', ')} {entry.files.length > 3 ? ` (+${entry.files.length - 3} more)` : ''}

))}
)} {workspaceScan && workspaceScan.untranslatedLiterals.length > 0 && (

{t('autofix.k14eb468b')}

These literals appear directly in JSX. Replace them with t('...') to make the page translatable.

{workspaceScan.untranslatedLiterals.slice(0, 80).map((entry) => (

{entry.text}

{entry.files.slice(0, 3).join(', ')} {entry.files.length > 3 ? ` (+${entry.files.length - 3} more)` : ''}

))}
)}

Scan now checks workspace files (pages, components, hooks, utils) and compares used keys against en.ts.

); }