Files
monorepo/baseball/src/components/DashboardClient.tsx
T
2026-05-06 15:08:32 +00:00

125 lines
4.5 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { AppShell } from "@/components/AppShell";
import { EmptyState } from "@/components/EmptyState";
import { SectionCard } from "@/components/SectionCard";
import { StatusBadge } from "@/components/StatusBadge";
import { fetchJson } from "@/lib/client";
import type { DashboardResponse } from "@/lib/types";
export function DashboardClient() {
const [data, setData] = useState<DashboardResponse | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [message, setMessage] = useState<string | null>(null);
async function loadDashboard() {
setLoading(true);
setError(null);
try {
const response = await fetchJson<DashboardResponse>("/api/dashboard");
setData(response);
} catch (loadError) {
setError(loadError instanceof Error ? loadError.message : "Failed to load dashboard.");
} finally {
setLoading(false);
}
}
async function seedSampleData() {
try {
const response = await fetchJson<{ seeded: boolean; reason?: string }>("/api/seed", { method: "POST" });
setMessage(response.seeded ? "Sample team loaded." : response.reason ?? "Seed skipped.");
await loadDashboard();
} catch (seedError) {
setError(seedError instanceof Error ? seedError.message : "Failed to seed data.");
}
}
useEffect(() => {
void loadDashboard();
}, []);
return (
<AppShell
title="Game Day Snapshot"
subtitle="Check roster health, pitcher rest, and lineup readiness before first pitch."
actions={
<button className="button button-secondary" onClick={() => void seedSampleData()}>
Load Sample Team
</button>
}
>
{message ? <div className="banner banner-success">{message}</div> : null}
{error ? <div className="banner banner-error">{error}</div> : null}
{loading ? (
<SectionCard title="Loading" description="Pulling current roster and pitching data." />
) : data && data.rosterCount > 0 ? (
<>
<section className="stats-grid">
<article className="stat-card">
<span>Rostered Players</span>
<strong>{data.rosterCount}</strong>
</article>
<article className="stat-card">
<span>Active Today</span>
<strong>{data.activePlayers}</strong>
</article>
<article className="stat-card stat-card-success">
<span>Pitchers Available</span>
<strong>{data.availablePitchers}</strong>
</article>
<article className="stat-card stat-card-warning">
<span>Need Rest</span>
<strong>{data.needsRest}</strong>
</article>
</section>
<SectionCard
title="Pitcher Availability"
description="Availability is based on the latest appearance and your configured rest rules."
>
<div className="list-stack">
{data.recentPitching.map((entry) => (
<article key={entry.playerId} className="list-row">
<div>
<h3>{entry.playerName}</h3>
<p>
{entry.lastPitchedDate
? `${entry.pitchCount} pitches on ${entry.lastPitchedDate}`
: "No pitching history recorded yet."}
</p>
</div>
<div className="list-meta">
<StatusBadge status={entry.status} />
<small>
{entry.daysSinceLastAppearance === null
? "No history"
: `${entry.daysSinceLastAppearance} day(s) since last outing`}
</small>
</div>
</article>
))}
</div>
</SectionCard>
</>
) : (
<SectionCard title="No Team Yet" description="Start with sample data or add players on the roster screen.">
<EmptyState
title="Roster is empty"
description="Load the sample team to explore the app, then adjust players and pitching records as needed."
action={
<button className="button" onClick={() => void seedSampleData()}>
Load Sample Team
</button>
}
/>
</SectionCard>
)}
</AppShell>
);
}