- Create DashboardLoader component with pulsing Fusion logo and progress bar animation - Add CSS keyframe animations for smooth loading states - Gate initial App render with loader until dashboard data is ready - Add comprehensive tests for DashboardLoader component and App loading behavior - Update existing App tests to account for loader wrapper
80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
import { Loader2 } from "lucide-react";
|
|
|
|
export type DashboardLoaderStage = "projects" | "project" | "tasks" | "ready";
|
|
|
|
interface DashboardLoaderProps {
|
|
stage: DashboardLoaderStage;
|
|
}
|
|
|
|
interface LoaderStep {
|
|
id: Exclude<DashboardLoaderStage, "ready">;
|
|
label: string;
|
|
}
|
|
|
|
const LOADER_STEPS: LoaderStep[] = [
|
|
{ id: "projects", label: "Loading projects" },
|
|
{ id: "project", label: "Selecting project" },
|
|
{ id: "tasks", label: "Fetching tasks" },
|
|
];
|
|
|
|
function getStepState(stepId: LoaderStep["id"], stage: DashboardLoaderStage): "done" | "active" | "pending" {
|
|
if (stage === "ready") {
|
|
return "done";
|
|
}
|
|
|
|
const currentStageIndex = LOADER_STEPS.findIndex((step) => step.id === stage);
|
|
const stepIndex = LOADER_STEPS.findIndex((step) => step.id === stepId);
|
|
|
|
if (stepIndex < currentStageIndex) {
|
|
return "done";
|
|
}
|
|
|
|
if (stepIndex === currentStageIndex) {
|
|
return "active";
|
|
}
|
|
|
|
return "pending";
|
|
}
|
|
|
|
export function DashboardLoader({ stage }: DashboardLoaderProps) {
|
|
return (
|
|
<div
|
|
className="dashboard-loader"
|
|
role="status"
|
|
aria-live="polite"
|
|
aria-label="Loading Fusion dashboard"
|
|
data-stage={stage}
|
|
>
|
|
<div className="dashboard-loader__content">
|
|
<h1 className="dashboard-loader__logo">Fusion</h1>
|
|
<p className="dashboard-loader__message">Initializing dashboard...</p>
|
|
|
|
<ol className="dashboard-loader__steps" aria-label="Dashboard loading progress">
|
|
{LOADER_STEPS.map((step) => {
|
|
const stepState = getStepState(step.id, stage);
|
|
|
|
return (
|
|
<li
|
|
key={step.id}
|
|
className={`dashboard-loader__step dashboard-loader__step--${stepState}`}
|
|
data-testid={`dashboard-loader-step-${step.id}`}
|
|
>
|
|
<span className="dashboard-loader__step-icon" aria-hidden="true">
|
|
{stepState === "done" ? (
|
|
"✓"
|
|
) : stepState === "active" ? (
|
|
<Loader2 className="dashboard-loader__spinner animate-spin" size={14} />
|
|
) : (
|
|
"•"
|
|
)}
|
|
</span>
|
|
<span className="dashboard-loader__step-label">{step.label}</span>
|
|
</li>
|
|
);
|
|
})}
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|