Files
fusion/packages/dashboard/app/components/DbCorruptionBanner.tsx
gsxdsm a2e96b132a feat(i18n): round-2 sweep — 488 residual strings migrated, markup fidelity restored (#1352)
- 51 fix agents covered every dirty batch from the round-1 verifiers:
  helper-function labels (roles, statuses, relative time), constant
  label maps (SETTINGS_SECTIONS, PROVIDER_INFO, EVENT_TYPE_LABELS),
  TUI help overlay + tab labels, toasts, placeholders, aria-labels
- Inline markup flattened by round 1 restored with <Trans>
  (DbCorruptionBanner storage-docs link, UpdateAvailableBanner code chip)
- Catalogs merged: +527 en keys across 5 locales; CLI bundles + app
  locale tree regenerated (6 locales)
- All 23 sweep-caused test regressions fixed: delta vs the clean-main
  baseline is now zero (remaining 4 local failures reproduce identically
  on origin/main; CI-green upstream)
- typecheck/lint clean; TUI 82/82, core locale 9/9

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 20:52:44 -07:00

82 lines
3.2 KiB
TypeScript

import { AlertTriangle, RefreshCw } from "lucide-react";
import { Trans, useTranslation } from "react-i18next";
import "./DbCorruptionBanner.css";
interface DbCorruptionBannerProps {
errors: string[];
lastCheckedAt: string | null;
onRefresh: () => void | Promise<void>;
refreshing: boolean;
refreshError: string | null;
}
export function DbCorruptionBanner({
errors,
lastCheckedAt,
onRefresh,
refreshing,
refreshError,
}: DbCorruptionBannerProps) {
const { t } = useTranslation("app");
if (errors.length === 0) {
return null;
}
const visibleErrors = errors.slice(0, 5);
const checkedAtLabel = lastCheckedAt ? new Date(lastCheckedAt).toLocaleString() : null;
return (
<section className="db-corruption-banner" role="alert" aria-live="assertive">
<div className="db-corruption-banner__header">
<div className="db-corruption-banner__headline-wrap">
<span className="status-dot status-dot--error" aria-hidden="true" />
<AlertTriangle aria-hidden="true" />
<div className="db-corruption-banner__headline-copy">
<h2 className="db-corruption-banner__headline">{t("dbBanner.title", "Database corruption detected")}</h2>
{checkedAtLabel ? (
<p className="db-corruption-banner__meta">{t("dbBanner.lastChecked", "Last checked: {{checkedAtLabel}}", { checkedAtLabel })}</p>
) : null}
</div>
</div>
<button
type="button"
className="btn btn-sm db-corruption-banner__refresh"
onClick={() => {
void onRefresh();
}}
disabled={refreshing}
>
<RefreshCw className={refreshing ? "db-corruption-banner__refresh-icon db-corruption-banner__refresh-icon--spinning" : "db-corruption-banner__refresh-icon"} aria-hidden="true" />
{refreshing ? t("dbBanner.refreshing", "Refreshing…") : t("dbBanner.refreshHealth", "Refresh health")}
</button>
</div>
<p className="db-corruption-banner__body">
{t("dbBanner.body", "Fusion's background SQLite integrity check reported corruption. Review the failing objects below before continuing critical operations.")}
</p>
<ul className="db-corruption-banner__list">
{visibleErrors.map((error, index) => (
<li key={`${index}:${error}`} className="db-corruption-banner__item">
<code className="db-corruption-banner__error-code">{error}</code>
</li>
))}
</ul>
<p className="db-corruption-banner__footer">
<strong className="db-corruption-banner__footer-label">{t("dbBanner.whatToDo", "What to do:")}</strong>{" "}
<Trans
i18nKey="app:dbBanner.instructions"
defaults="Back up the project, try <cmd>fn db --vacuum</cmd> if the database still opens cleanly, and restore from a known-good backup if corruption persists. See <docsLink>docs/storage.md</docsLink> for the storage layout and recovery guidance."
components={{
cmd: <code />,
docsLink: <a href="docs/storage.md" />,
}}
/>
</p>
{refreshError ? <p className="db-corruption-banner__error">{refreshError}</p> : null}
</section>
);
}