Centralize project loading and status-state handling across dashboard surfaces. - add a reusable DataBoundary component for loading, empty, error, and loaded states - extract shared project status config helpers for labels, colors, icons, and initializing detection - update project overview, cards, badges, and selector UI to use the shared helpers - add regression coverage for data-boundary behavior, project overview empty/loading transitions, and status config fallbacks Files changed: packages/dashboard/app/components/DataBoundary.tsx | 64 ++++++++++++++++++++++ packages/dashboard/app/components/ProjectCard.tsx | 48 ++-------------- .../app/components/ProjectHealthBadge.tsx | 45 +-------------- .../dashboard/app/components/ProjectOverview.tsx | 4 +- .../dashboard/app/components/ProjectSelector.tsx | 27 +-------- .../app/components/__tests__/DataBoundary.test.tsx | 62 +++++++++++++++++++++ .../components/__tests__/ProjectOverview.test.tsx | 25 +++++++++ .../__tests__/ProjectThemeTokens.test.tsx | 37 ++++--------- .../utils/__tests__/projectStatusConfig.test.ts | 53 ++++++++++++++++++ .../dashboard/app/utils/projectStatusConfig.ts | 50 +++++++++++++++++ packages/dashboard/vitest.config.ts | 2 + 11 files changed, 282 insertions(+), 135 deletions(-) Fusion-Task-Id: FN-5894 Fusion-Task-Lineage: 3626a4af-b62b-456d-82d7-cf47bbf55c21
65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
import { AlertCircle } from "lucide-react";
|
|
import type { ReactNode } from "react";
|
|
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 }) {
|
|
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">Unable to load data</p>
|
|
<p className="agent-empty-state__description text-secondary">
|
|
{getErrorMessage(error) || "Something went wrong while loading this view."}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function DataBoundary({
|
|
isEmpty,
|
|
isLoading = false,
|
|
hasFetched = false,
|
|
error,
|
|
loadingFallback,
|
|
emptyFallback,
|
|
errorFallback,
|
|
children,
|
|
}: DataBoundaryProps) {
|
|
if (error) {
|
|
return <>{errorFallback ?? <DefaultErrorFallback error={error} />}</>;
|
|
}
|
|
|
|
const shouldShowLoading = isLoading || (!hasFetched && !error);
|
|
if (shouldShowLoading) {
|
|
return <>{loadingFallback ?? <ProjectGridSkeleton />}</>;
|
|
}
|
|
|
|
if (hasFetched && isEmpty) {
|
|
return (
|
|
<>
|
|
{emptyFallback ?? (
|
|
<AgentEmptyState
|
|
title="No data available"
|
|
description="There is nothing to show yet."
|
|
ctaLabel=""
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|