fix(i18n): localize admin diagnostics
This commit is contained in:
@@ -21,6 +21,7 @@ const mockedApi = api as jest.Mocked<typeof api>;
|
||||
|
||||
describe('AdminSystemPage', () => {
|
||||
beforeEach(() => {
|
||||
window.localStorage.clear();
|
||||
mockedApi.get.mockImplementation((url: string) => {
|
||||
if (url === '/admin/system') {
|
||||
return Promise.resolve({
|
||||
@@ -199,4 +200,14 @@ describe('AdminSystemPage', () => {
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('localizes benchmark diagnostics without changing runtime values', async () => {
|
||||
window.localStorage.setItem('uiLanguage', 'nb');
|
||||
render(<I18nProvider><AdminSystemPage /></I18nProvider>);
|
||||
|
||||
expect(await screen.findByText('Gjennomgang av CV-referansetest')).toBeTruthy();
|
||||
expect(screen.getByText(/Mistenkelig kontaktsted: san francisco, hobbies/i)).toBeTruthy();
|
||||
expect(screen.getByText('Production')).toBeTruthy();
|
||||
expect(screen.getByText(/Modell · qwen2.5:7b/i)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1367,6 +1367,46 @@ export const translations = {
|
||||
kanbanFollowUpNow: "Follow up now",
|
||||
kanbanReplyDueIn: "Reply due in {days}d",
|
||||
adminSystemTitle: "System status",
|
||||
adminSystemLoadFailed: "Failed to load system status.",
|
||||
adminSystemTestEmailFailed: "Failed to send test email.",
|
||||
adminSystemEmailSaveFailed: "Failed to save email settings.",
|
||||
adminSystemProbeFailed: "Failed to run AI service probe.",
|
||||
adminSystemVersionCommit: "Version {version} · Commit {commit}",
|
||||
adminSystemNoTarget: "No target",
|
||||
adminSystemUnknownDevice: "unknown device",
|
||||
adminSystemProbeDevice: "{latency} ms probe · {device}",
|
||||
adminSystemHealthDevice: "{latency} ms health · {device}",
|
||||
adminSystemOllamaVersion: "Ollama version",
|
||||
adminSystemLoadedModels: "Loaded models",
|
||||
adminSystemOcrEnabled: "OCR {languages}",
|
||||
adminSystemModelName: "Model · {model}",
|
||||
adminSystemLoadedModel: "Loaded · {model}",
|
||||
adminBenchmarkTitle: "CV benchmark review",
|
||||
adminBenchmarkRoot: "Benchmark root",
|
||||
adminBenchmarkLastUpdate: "Last benchmark update",
|
||||
adminBenchmarkCorpusRoot: "Corpus root",
|
||||
adminBenchmarkFiles: "Files",
|
||||
adminBenchmarkCorpusInputs: "Corpus inputs",
|
||||
adminBenchmarkCoverage: "Coverage",
|
||||
adminBenchmarkCoverageHelp: "Structured field coverage",
|
||||
adminBenchmarkConfidence: "Confidence",
|
||||
adminBenchmarkConfidenceHelp: "Field metadata confidence",
|
||||
adminBenchmarkConsistency: "Consistency",
|
||||
adminBenchmarkConsistencyHelp: "Normalization consistency",
|
||||
adminBenchmarkMissingApproved: "Missing approved",
|
||||
adminBenchmarkNeedsReview: "Needs fixture review",
|
||||
adminBenchmarkTopFindings: "Top parser findings",
|
||||
adminBenchmarkNoAnomalies: "No standout benchmark anomalies in the current run.",
|
||||
adminBenchmarkWeakest: "Weakest files in current run",
|
||||
adminBenchmarkCoverageValue: "Coverage {value}",
|
||||
adminBenchmarkConfidenceValue: "Confidence {value}",
|
||||
adminBenchmarkConsistencyValue: "Consistency {value}",
|
||||
adminBenchmarkLatestSummary: "Latest markdown summary",
|
||||
adminBenchmarkRunScript: "Run scripts/run-cv-benchmark.sh to generate the latest corpus report and fixture candidates.",
|
||||
adminBenchmarkSuspiciousLocation: "Suspicious contact location: {value}",
|
||||
adminBenchmarkEducationOvercaptured: "Education qualification looks over-captured.",
|
||||
adminBenchmarkWorkTitleOvercaptured: "Work title looks over-captured.",
|
||||
adminBenchmarkQualificationOther: "Qualification level fell back to Other.",
|
||||
adminSystemSubtitle: "Production diagnostics for runtime, database, auth, email, AI service health, and OCR readiness.",
|
||||
adminSystemRunProbe: "Run probe now",
|
||||
adminSystemRunningProbe: "Running probe...",
|
||||
@@ -3456,6 +3496,46 @@ export const translations = {
|
||||
kanbanFollowUpNow: "Følg opp nå",
|
||||
kanbanReplyDueIn: "Svar forfaller om {days}d",
|
||||
adminSystemTitle: "Systemstatus",
|
||||
adminSystemLoadFailed: "Kunne ikke laste systemstatus.",
|
||||
adminSystemTestEmailFailed: "Kunne ikke sende test-e-post.",
|
||||
adminSystemEmailSaveFailed: "Kunne ikke lagre e-postinnstillingene.",
|
||||
adminSystemProbeFailed: "Kunne ikke kjøre kontrollen av AI-tjenesten.",
|
||||
adminSystemVersionCommit: "Versjon {version} · Commit {commit}",
|
||||
adminSystemNoTarget: "Ingen målressurs",
|
||||
adminSystemUnknownDevice: "ukjent enhet",
|
||||
adminSystemProbeDevice: "{latency} ms kontroll · {device}",
|
||||
adminSystemHealthDevice: "{latency} ms helse · {device}",
|
||||
adminSystemOllamaVersion: "Ollama-versjon",
|
||||
adminSystemLoadedModels: "Innlastede modeller",
|
||||
adminSystemOcrEnabled: "OCR {languages}",
|
||||
adminSystemModelName: "Modell · {model}",
|
||||
adminSystemLoadedModel: "Innlastet · {model}",
|
||||
adminBenchmarkTitle: "Gjennomgang av CV-referansetest",
|
||||
adminBenchmarkRoot: "Rotmappe for referansetest",
|
||||
adminBenchmarkLastUpdate: "Siste oppdatering av referansetest",
|
||||
adminBenchmarkCorpusRoot: "Rotmappe for korpus",
|
||||
adminBenchmarkFiles: "Filer",
|
||||
adminBenchmarkCorpusInputs: "Inndata i korpus",
|
||||
adminBenchmarkCoverage: "Dekning",
|
||||
adminBenchmarkCoverageHelp: "Dekning av strukturerte felt",
|
||||
adminBenchmarkConfidence: "Sikkerhet",
|
||||
adminBenchmarkConfidenceHelp: "Sikkerhet i feltmetadata",
|
||||
adminBenchmarkConsistency: "Konsistens",
|
||||
adminBenchmarkConsistencyHelp: "Konsistens i normalisering",
|
||||
adminBenchmarkMissingApproved: "Mangler godkjenning",
|
||||
adminBenchmarkNeedsReview: "Trenger gjennomgang av testdata",
|
||||
adminBenchmarkTopFindings: "Viktigste parserfunn",
|
||||
adminBenchmarkNoAnomalies: "Ingen fremtredende avvik i den gjeldende kjøringen.",
|
||||
adminBenchmarkWeakest: "Svakeste filer i gjeldende kjøring",
|
||||
adminBenchmarkCoverageValue: "Dekning {value}",
|
||||
adminBenchmarkConfidenceValue: "Sikkerhet {value}",
|
||||
adminBenchmarkConsistencyValue: "Konsistens {value}",
|
||||
adminBenchmarkLatestSummary: "Siste markdown-sammendrag",
|
||||
adminBenchmarkRunScript: "Kjør scripts/run-cv-benchmark.sh for å generere den nyeste korpusrapporten og kandidater til testdata.",
|
||||
adminBenchmarkSuspiciousLocation: "Mistenkelig kontaktsted: {value}",
|
||||
adminBenchmarkEducationOvercaptured: "Utdanningskvalifikasjonen ser ut til å inneholde for mye tekst.",
|
||||
adminBenchmarkWorkTitleOvercaptured: "Jobbtittelen ser ut til å inneholde for mye tekst.",
|
||||
adminBenchmarkQualificationOther: "Kvalifikasjonsnivået falt tilbake til Annet.",
|
||||
adminSystemSubtitle: "Produksjonsdiagnostikk for kjøretid, database, autentisering, e-post, AI-tjenestehelse og OCR-beredskap.",
|
||||
adminSystemRunProbe: "Kjør probe nå",
|
||||
adminSystemRunningProbe: "Kjører probe...",
|
||||
|
||||
@@ -166,8 +166,8 @@ function displayMetadata(value?: string | null) {
|
||||
return value && value.trim().length > 0 ? value : "-";
|
||||
}
|
||||
|
||||
function formatDate(value?: string | null) {
|
||||
return value ? new Date(value).toLocaleString() : "-";
|
||||
function formatDate(value?: string | null, locale?: string) {
|
||||
return value ? new Date(value).toLocaleString(locale) : "-";
|
||||
}
|
||||
|
||||
function formatPercent(value?: number | null) {
|
||||
@@ -206,7 +206,7 @@ function DetailRow({ label, value }: { label: string; value: React.ReactNode })
|
||||
}
|
||||
|
||||
export default function AdminSystemPage() {
|
||||
const { t } = useI18n();
|
||||
const { language, t } = useI18n();
|
||||
const [tab, setTab] = useState(0);
|
||||
const [status, setStatus] = useState<SystemStatus | null>(null);
|
||||
const [emailSettings, setEmailSettings] = useState<EditableEmailSettings | null>(null);
|
||||
@@ -237,7 +237,7 @@ export default function AdminSystemPage() {
|
||||
setSmtpPassword("");
|
||||
setClearPassword(false);
|
||||
} catch (e: any) {
|
||||
setError(getApiErrorMessage(e, "Failed to load system status."));
|
||||
setError(getApiErrorMessage(e, t("adminSystemLoadFailed")));
|
||||
setStatus(null);
|
||||
setEmailSettings(null);
|
||||
setBenchmarkStatus(null);
|
||||
@@ -277,20 +277,20 @@ export default function AdminSystemPage() {
|
||||
return benchmarkIndex.Entries.flatMap((entry) => {
|
||||
const findings: Array<{ file: string; issue: string }> = [];
|
||||
if (entry.ContactLocation && /(culture|education|arial|hobbies|cooperate|ag, ni|bold)/i.test(entry.ContactLocation)) {
|
||||
findings.push({ file: entry.FileName, issue: `Suspicious contact location: ${entry.ContactLocation}` });
|
||||
findings.push({ file: entry.FileName, issue: t("adminBenchmarkSuspiciousLocation", { value: entry.ContactLocation }) });
|
||||
}
|
||||
if (entry.FirstEducation && entry.FirstEducation.length > 120) {
|
||||
findings.push({ file: entry.FileName, issue: "Education qualification looks over-captured." });
|
||||
findings.push({ file: entry.FileName, issue: t("adminBenchmarkEducationOvercaptured") });
|
||||
}
|
||||
if ((entry.FirstJob ?? "").length > 120) {
|
||||
findings.push({ file: entry.FileName, issue: "Work title looks over-captured." });
|
||||
findings.push({ file: entry.FileName, issue: t("adminBenchmarkWorkTitleOvercaptured") });
|
||||
}
|
||||
if ((entry.QualificationLevels ?? []).includes("Other")) {
|
||||
findings.push({ file: entry.FileName, issue: "Qualification level fell back to Other." });
|
||||
findings.push({ file: entry.FileName, issue: t("adminBenchmarkQualificationOther") });
|
||||
}
|
||||
return findings;
|
||||
}).slice(0, 10);
|
||||
}, [benchmarkIndex]);
|
||||
}, [benchmarkIndex, language]);
|
||||
|
||||
const sendTestEmail = async () => {
|
||||
setSendingTestEmail(true);
|
||||
@@ -302,7 +302,7 @@ export default function AdminSystemPage() {
|
||||
});
|
||||
setError(null);
|
||||
} catch (e: any) {
|
||||
setError(getApiErrorMessage(e, "Failed to send test email."));
|
||||
setError(getApiErrorMessage(e, t("adminSystemTestEmailFailed")));
|
||||
} finally {
|
||||
setSendingTestEmail(false);
|
||||
}
|
||||
@@ -329,7 +329,7 @@ export default function AdminSystemPage() {
|
||||
setClearPassword(false);
|
||||
await load();
|
||||
} catch (e: any) {
|
||||
setError(getApiErrorMessage(e, "Failed to save email settings."));
|
||||
setError(getApiErrorMessage(e, t("adminSystemEmailSaveFailed")));
|
||||
} finally {
|
||||
setSavingSettings(false);
|
||||
}
|
||||
@@ -352,7 +352,7 @@ export default function AdminSystemPage() {
|
||||
await api.post("/admin/system/ai/probe");
|
||||
await load();
|
||||
} catch (e: any) {
|
||||
setError(getApiErrorMessage(e, "Failed to run AI service probe."));
|
||||
setError(getApiErrorMessage(e, t("adminSystemProbeFailed")));
|
||||
} finally {
|
||||
setRunningProbe(false);
|
||||
}
|
||||
@@ -382,12 +382,12 @@ export default function AdminSystemPage() {
|
||||
<SummaryCard
|
||||
title={t("adminSystemEnvironment")}
|
||||
value={status?.environment ?? "-"}
|
||||
subtitle={`Version ${displayMetadata(status?.version)} · Commit ${displayMetadata(status?.commitSha)}`}
|
||||
subtitle={t("adminSystemVersionCommit", { version: displayMetadata(status?.version), commit: displayMetadata(status?.commitSha) })}
|
||||
/>
|
||||
<SummaryCard
|
||||
title={t("adminSystemDatabase")}
|
||||
value={status ? (status.database.canConnect ? t("adminSystemConnected") : t("adminSystemOffline")) : "-"}
|
||||
subtitle={status ? `${status.database.provider} · ${status.database.target || "No target"}` : "-"}
|
||||
subtitle={status ? `${status.database.provider} · ${status.database.target || t("adminSystemNoTarget")}` : "-"}
|
||||
tone={dbTone}
|
||||
/>
|
||||
<SummaryCard
|
||||
@@ -400,9 +400,9 @@ export default function AdminSystemPage() {
|
||||
title={t("adminSystemSummarizer")}
|
||||
value={status?.ai.healthy ? t("adminSystemHealthy") : t("adminSystemOffline")}
|
||||
subtitle={status?.ai.probeLatencyMs != null
|
||||
? `${status.ai.probeLatencyMs} ms probe · ${status.ai.device || "unknown device"}`
|
||||
? t("adminSystemProbeDevice", { latency: status.ai.probeLatencyMs, device: status.ai.device || t("adminSystemUnknownDevice") })
|
||||
: status?.ai.healthLatencyMs != null
|
||||
? `${status.ai.healthLatencyMs} ms health · ${status.ai.device || "unknown device"}`
|
||||
? t("adminSystemHealthDevice", { latency: status.ai.healthLatencyMs, device: status.ai.device || t("adminSystemUnknownDevice") })
|
||||
: t("adminSystemNoLatencyData")}
|
||||
tone={aiTone}
|
||||
/>
|
||||
@@ -468,13 +468,13 @@ export default function AdminSystemPage() {
|
||||
<DetailRow label={t("adminSystemOllamaReachable")} value={status?.ai.ollamaReachable ? t("yes") : t("noWord")} />
|
||||
<DetailRow label={t("adminSystemOllamaModel")} value={status?.ai.ollamaModel || "-"} />
|
||||
<DetailRow label={t("adminSystemOllamaModelAvailable")} value={status?.ai.ollamaModelAvailable ? t("yes") : t("noWord")} />
|
||||
<DetailRow label="Ollama version" value={status?.ai.ollamaVersion || "-"} />
|
||||
<DetailRow label="Loaded models" value={status?.ai.ollamaLoadedCount ?? 0} />
|
||||
<DetailRow label={t("adminSystemOllamaVersion")} value={status?.ai.ollamaVersion || "-"} />
|
||||
<DetailRow label={t("adminSystemLoadedModels")} value={status?.ai.ollamaLoadedCount ?? 0} />
|
||||
<DetailRow label={t("adminSystemHealthLatency")} value={status?.ai.healthLatencyMs != null ? `${status.ai.healthLatencyMs} ms` : "-"} />
|
||||
<DetailRow label={t("adminSystemProbeLatency")} value={status?.ai.probeLatencyMs != null ? `${status.ai.probeLatencyMs} ms` : "-"} />
|
||||
<DetailRow label={t("adminSystemLastProbe")} value={formatDate(status?.ai.lastProbeAt)} />
|
||||
<DetailRow label={t("adminSystemLastSuccessfulProbe")} value={formatDate(status?.ai.lastProbeSuccessAt)} />
|
||||
<DetailRow label={t("adminSystemLastSummarizationSuccess")} value={formatDate(status?.ai.lastSuccessAt)} />
|
||||
<DetailRow label={t("adminSystemLastProbe")} value={formatDate(status?.ai.lastProbeAt, language === "nb" ? "nb-NO" : "en")} />
|
||||
<DetailRow label={t("adminSystemLastSuccessfulProbe")} value={formatDate(status?.ai.lastProbeSuccessAt, language === "nb" ? "nb-NO" : "en")} />
|
||||
<DetailRow label={t("adminSystemLastSummarizationSuccess")} value={formatDate(status?.ai.lastSuccessAt, language === "nb" ? "nb-NO" : "en")} />
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
@@ -497,57 +497,57 @@ export default function AdminSystemPage() {
|
||||
<Chip label={status?.auth.googleConfigured ? t("adminSystemGoogleReady") : t("adminSystemGoogleOff")} variant="outlined" size="small" />
|
||||
<Chip label={status?.auth.gmailConfigured ? t("adminSystemGmailReady") : t("adminSystemGmailIncomplete")} variant="outlined" size="small" />
|
||||
<Chip label={status?.ai.gpuAvailable ? t("adminSystemGpuVisible") : t("adminSystemCpuMode")} color={status?.ai.gpuAvailable ? "success" : "default"} size="small" />
|
||||
<Chip label={status?.ai.ocrAvailable ? `OCR ${status.ai.ocrLanguages || "enabled"}` : t("adminSystemOcrUnavailable")} variant="outlined" size="small" />
|
||||
<Chip label={status?.ai.ocrAvailable ? t("adminSystemOcrEnabled", { languages: status.ai.ocrLanguages || t("adminSystemEnabled") }) : t("adminSystemOcrUnavailable")} variant="outlined" size="small" />
|
||||
{(status?.ai.ollamaInstalledModels ?? []).slice(0, 4).map((model) => (
|
||||
<Chip key={model} label={`Model · ${model}`} variant="outlined" size="small" />
|
||||
<Chip key={model} label={t("adminSystemModelName", { model })} variant="outlined" size="small" />
|
||||
))}
|
||||
{(status?.ai.ollamaLoadedModels ?? []).slice(0, 3).map((model) => (
|
||||
<Chip key={`loaded-${model}`} label={`Loaded · ${model}`} color="primary" variant="outlined" size="small" />
|
||||
<Chip key={`loaded-${model}`} label={t("adminSystemLoadedModel", { model })} color="primary" variant="outlined" size="small" />
|
||||
))}
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<Paper sx={{ p: 2, borderRadius: 3 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 900, mb: 1 }}>CV benchmark review</Typography>
|
||||
<Typography variant="h6" sx={{ fontWeight: 900, mb: 1 }}>{t("adminBenchmarkTitle")}</Typography>
|
||||
<Stack spacing={0.75}>
|
||||
<DetailRow label="Benchmark root" value={benchmarkStatus?.rootPath || "-"} />
|
||||
<DetailRow label="Last benchmark update" value={formatDate(benchmarkStatus?.lastUpdatedAtUtc)} />
|
||||
<DetailRow label="Corpus root" value={benchmarkIndex?.CorpusRoot || "-"} />
|
||||
<DetailRow label={t("adminBenchmarkRoot")} value={benchmarkStatus?.rootPath || "-"} />
|
||||
<DetailRow label={t("adminBenchmarkLastUpdate")} value={formatDate(benchmarkStatus?.lastUpdatedAtUtc, language === "nb" ? "nb-NO" : "en")} />
|
||||
<DetailRow label={t("adminBenchmarkCorpusRoot")} value={benchmarkIndex?.CorpusRoot || "-"} />
|
||||
</Stack>
|
||||
|
||||
{benchmarkIndex ? (
|
||||
<>
|
||||
<Box sx={{ mt: 2, display: "grid", gridTemplateColumns: { xs: "1fr 1fr", md: "repeat(5, 1fr)" }, gap: 1.25 }}>
|
||||
<SummaryCard title="Files" value={String(benchmarkIndex.TotalFiles)} subtitle="Corpus inputs" />
|
||||
<SummaryCard title="Coverage" value={formatPercent(benchmarkIndex.AverageCoverage)} subtitle="Structured field coverage" tone={benchmarkTone(benchmarkIndex.AverageCoverage)} />
|
||||
<SummaryCard title="Confidence" value={formatPercent(benchmarkIndex.AverageConfidence)} subtitle="Field metadata confidence" tone={benchmarkTone(benchmarkIndex.AverageConfidence)} />
|
||||
<SummaryCard title="Consistency" value={formatPercent(benchmarkIndex.AverageConsistency)} subtitle="Normalization consistency" tone={benchmarkTone(benchmarkIndex.AverageConsistency)} />
|
||||
<SummaryCard title="Missing approved" value={String(benchmarkIndex.MissingApprovedFixtures)} subtitle="Needs fixture review" tone={benchmarkIndex.MissingApprovedFixtures > 0 ? "warning" : "success"} />
|
||||
<SummaryCard title={t("adminBenchmarkFiles")} value={String(benchmarkIndex.TotalFiles)} subtitle={t("adminBenchmarkCorpusInputs")} />
|
||||
<SummaryCard title={t("adminBenchmarkCoverage")} value={formatPercent(benchmarkIndex.AverageCoverage)} subtitle={t("adminBenchmarkCoverageHelp")} tone={benchmarkTone(benchmarkIndex.AverageCoverage)} />
|
||||
<SummaryCard title={t("adminBenchmarkConfidence")} value={formatPercent(benchmarkIndex.AverageConfidence)} subtitle={t("adminBenchmarkConfidenceHelp")} tone={benchmarkTone(benchmarkIndex.AverageConfidence)} />
|
||||
<SummaryCard title={t("adminBenchmarkConsistency")} value={formatPercent(benchmarkIndex.AverageConsistency)} subtitle={t("adminBenchmarkConsistencyHelp")} tone={benchmarkTone(benchmarkIndex.AverageConsistency)} />
|
||||
<SummaryCard title={t("adminBenchmarkMissingApproved")} value={String(benchmarkIndex.MissingApprovedFixtures)} subtitle={t("adminBenchmarkNeedsReview")} tone={benchmarkIndex.MissingApprovedFixtures > 0 ? "warning" : "success"} />
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mt: 2.5, display: "grid", gridTemplateColumns: { xs: "1fr", xl: "1.1fr 0.9fr" }, gap: 2 }}>
|
||||
<Paper variant="outlined" sx={{ p: 1.5, borderRadius: 3 }}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 900, mb: 1 }}>Top parser findings</Typography>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 900, mb: 1 }}>{t("adminBenchmarkTopFindings")}</Typography>
|
||||
<Stack spacing={1}>
|
||||
{benchmarkFindings.length > 0 ? benchmarkFindings.map((finding) => (
|
||||
<Box key={`${finding.file}:${finding.issue}`} sx={{ p: 1.25, borderRadius: 2, backgroundColor: "background.default", border: "none", boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)" }}>
|
||||
<Typography variant="caption" sx={{ color: "text.secondary" }}>{finding.file}</Typography>
|
||||
<Typography variant="body2">{finding.issue}</Typography>
|
||||
</Box>
|
||||
)) : <Typography variant="body2" sx={{ color: "text.secondary" }}>No standout benchmark anomalies in the current run.</Typography>}
|
||||
)) : <Typography variant="body2" sx={{ color: "text.secondary" }}>{t("adminBenchmarkNoAnomalies")}</Typography>}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
<Paper variant="outlined" sx={{ p: 1.5, borderRadius: 3 }}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 900, mb: 1 }}>Weakest files in current run</Typography>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 900, mb: 1 }}>{t("adminBenchmarkWeakest")}</Typography>
|
||||
<Stack spacing={1}>
|
||||
{weakestEntries.map((entry) => (
|
||||
<Box key={entry.Slug} sx={{ p: 1.25, borderRadius: 2, backgroundColor: "background.default", border: "none", boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)" }}>
|
||||
<Typography variant="body2" sx={{ fontWeight: 800 }}>{entry.FileName}</Typography>
|
||||
<Box sx={{ display: "flex", gap: 0.75, flexWrap: "wrap", mt: 0.75 }}>
|
||||
<Chip size="small" label={`Coverage ${formatPercent(entry.CoverageScore)}`} color={benchmarkTone(entry.CoverageScore)} />
|
||||
<Chip size="small" label={`Confidence ${formatPercent(entry.ConfidenceScore)}`} color={benchmarkTone(entry.ConfidenceScore)} />
|
||||
<Chip size="small" label={`Consistency ${formatPercent(entry.ConsistencyScore)}`} color={benchmarkTone(entry.ConsistencyScore)} />
|
||||
<Chip size="small" label={t("adminBenchmarkCoverageValue", { value: formatPercent(entry.CoverageScore) })} color={benchmarkTone(entry.CoverageScore)} />
|
||||
<Chip size="small" label={t("adminBenchmarkConfidenceValue", { value: formatPercent(entry.ConfidenceScore) })} color={benchmarkTone(entry.ConfidenceScore)} />
|
||||
<Chip size="small" label={t("adminBenchmarkConsistencyValue", { value: formatPercent(entry.ConsistencyScore) })} color={benchmarkTone(entry.ConsistencyScore)} />
|
||||
</Box>
|
||||
<Typography variant="caption" sx={{ display: "block", color: "text.secondary", mt: 0.75 }}>{entry.DiffSummary || "-"}</Typography>
|
||||
</Box>
|
||||
@@ -557,7 +557,7 @@ export default function AdminSystemPage() {
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mt: 2, p: 1.5, borderRadius: 2, backgroundColor: "background.default", border: "none", boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)", maxHeight: 280, overflow: "auto" }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 800, mb: 1 }}>Latest markdown summary</Typography>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 800, mb: 1 }}>{t("adminBenchmarkLatestSummary")}</Typography>
|
||||
<Typography variant="body2" sx={{ whiteSpace: "pre-wrap", fontFamily: "ui-monospace, SFMono-Regular, monospace" }}>
|
||||
{benchmarkStatus?.reportMarkdown || "-"}
|
||||
</Typography>
|
||||
@@ -566,7 +566,7 @@ export default function AdminSystemPage() {
|
||||
) : (
|
||||
<Box sx={{ mt: 1.5, p: 1.5, borderRadius: 2, backgroundColor: "background.default", border: "none", boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)" }}>
|
||||
<Typography variant="body2" sx={{ whiteSpace: "pre-wrap", fontFamily: "ui-monospace, SFMono-Regular, monospace" }}>
|
||||
{benchmarkStatus?.reportMarkdown || "Run scripts/run-cv-benchmark.sh to generate the latest corpus report and fixture candidates."}
|
||||
{benchmarkStatus?.reportMarkdown || t("adminBenchmarkRunScript")}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user