Migration (multi-agent sweep over 216 files, 60 batches):
- Every user-visible dashboard + TUI string moved to t() with the exact
English inline default (en rendering byte-identical)
- Catalogs merged from per-batch fragments: en/zh-CN/zh-TW/fr/es now
carry ~5,930 keys each across common/app/errors/cli namespaces;
CLI bundles regenerated (6 locales incl. ko)
Integration fixes:
- 18 type errors: reserved {{count}} interpolations renamed, malformed
plural call, hand-rolled t-param types replaced with TFunction<"app">
- 23 lint errors: superseded label constants/helpers removed
- ExecutorStatusBar hook-order violation (keyboard-open early return
moved below hooks)
- TUI tests wrapped in I18nextProvider (uninitialized fallback renders
literal {{placeholders}}); dashboard vitest.setup boots a minimal en
i18next instance for the same reason
Known WIP (next commits): ~457 residual strings across 50 batches,
Korean drafts for swept keys, and a dashboard test-suite pass that is
still being stabilized (~283 failures under investigation — fake-timer
waitFor interaction, likely stale node_modules vs merged lockfile).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
68 lines
1.9 KiB
TypeScript
68 lines
1.9 KiB
TypeScript
import { AlertCircle } from "lucide-react";
|
|
import type { ReactNode } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { getErrorMessage } from "@fusion/core";
|
|
import { AgentEmptyState } from "./AgentEmptyState";
|
|
import { ProjectGridSkeleton } from "./ProjectGridSkeleton";
|
|
|
|
export interface DataBoundaryProps {
|
|
isEmpty: boolean;
|
|
isLoading?: boolean;
|
|
hasFetched?: boolean;
|
|
error?: unknown;
|
|
loadingFallback?: ReactNode;
|
|
emptyFallback?: ReactNode;
|
|
errorFallback?: ReactNode;
|
|
children: ReactNode;
|
|
}
|
|
|
|
function DefaultErrorFallback({ error }: { error: unknown }) {
|
|
const { t } = useTranslation("app");
|
|
return (
|
|
<div className="agent-empty" data-testid="data-boundary-error">
|
|
<AlertCircle className="agent-empty-state__icon" size={48} opacity={0.3} />
|
|
<p className="agent-empty-state__title">{t("common.unableToLoadData", "Unable to load data")}</p>
|
|
<p className="agent-empty-state__description text-secondary">
|
|
{getErrorMessage(error) || t("common.somethingWentWrong", "Something went wrong while loading this view.")}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function DataBoundary({
|
|
isEmpty,
|
|
isLoading = false,
|
|
hasFetched = false,
|
|
error,
|
|
loadingFallback,
|
|
emptyFallback,
|
|
errorFallback,
|
|
children,
|
|
}: DataBoundaryProps) {
|
|
const { t } = useTranslation("app");
|
|
if (error) {
|
|
return <>{errorFallback ?? <DefaultErrorFallback error={error} />}</>;
|
|
}
|
|
|
|
const shouldShowLoading = isLoading || (!hasFetched && !error);
|
|
if (shouldShowLoading) {
|
|
return <>{loadingFallback ?? <ProjectGridSkeleton />}</>;
|
|
}
|
|
|
|
if (hasFetched && isEmpty) {
|
|
return (
|
|
<>
|
|
{emptyFallback ?? (
|
|
<AgentEmptyState
|
|
title={t("common.noDataAvailable", "No data available")}
|
|
description={t("common.nothingToShow", "There is nothing to show yet.")}
|
|
ctaLabel=""
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|