Yaltopia-FIFA/components/manager/manager-dashboard-client.tsx
Kirubel-Kibru-Yaltopia 89440985f1
Some checks failed
Deploy to Cloudflare Workers / deploy (push) Has been cancelled
x
2026-05-24 21:46:10 +03:00

251 lines
8.3 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { api } from "@/lib/api/client";
import { PageHeader } from "@/components/dashboard/page-header";
import { StatCard } from "@/components/dashboard/stat-card";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Calendar,
Target,
TrendingUp,
Trophy,
ArrowUpRight,
} from "lucide-react";
import { FormDonutChart, GoalsTrendChart } from "@/components/manager/TeamCharts";
export function ManagerDashboardClient() {
const [activeTab, setActiveTab] = useState("overview");
const [data, setData] = useState<{
nextFixture: Record<string, unknown> | null;
recentResults: Record<string, unknown>[];
stats: {
wins: number;
draws: number;
losses: number;
goalsFor: number;
played: number;
} | null;
} | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
api.manager.dashboard().then(setData).finally(() => setLoading(false));
}, []);
const stats = data?.stats;
const results = (data?.recentResults ?? []) as {
result: string;
goals_for: number;
goals_against: number;
opponent_name: string;
scheduled_at: string;
}[];
const winRate =
stats && stats.played > 0
? Math.round((stats.wins / stats.played) * 100)
: 0;
return (
<div className="space-y-6">
<PageHeader
title="Dashboard"
description="Result analysis and your next fixture"
tabs={[
{ id: "overview", label: "Overview" },
{ id: "reports", label: "Reports" },
{ id: "activity", label: "Activity" },
]}
activeTab={activeTab}
onTabChange={setActiveTab}
actions={
<Button variant="outline" size="sm" asChild>
<Link href="/manager/leagues">
View leagues
<ArrowUpRight className="h-3.5 w-3.5" />
</Link>
</Button>
}
/>
{loading ? (
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
{[1, 2, 3, 4].map((i) => (
<Card key={i} className="h-32 animate-pulse bg-muted/30" />
))}
</div>
) : activeTab === "overview" ? (
<>
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
<StatCard
title="Matches played"
value={stats?.played ?? 0}
subtitle="Last 8 results"
icon={TrendingUp}
trend={stats && stats.wins > stats.losses ? "up" : "neutral"}
trendLabel={`${stats?.wins ?? 0}W ${stats?.draws ?? 0}D ${stats?.losses ?? 0}L`}
/>
<StatCard
title="Goals scored"
value={stats?.goalsFor ?? 0}
subtitle="Recent window"
icon={Target}
trend="up"
trendLabel="attacking"
/>
<StatCard
title="Win rate"
value={`${winRate}%`}
subtitle="From recent form"
icon={Trophy}
trend={winRate >= 50 ? "up" : "down"}
trendLabel="form"
/>
<StatCard
title="Next fixture"
value={data?.nextFixture ? "Ready" : "—"}
subtitle={data?.nextFixture ? "See below" : "None scheduled"}
icon={Calendar}
/>
</div>
<div className="grid gap-4 lg:grid-cols-7">
<Card className="lg:col-span-4">
<CardHeader>
<CardTitle>Result analysis</CardTitle>
<CardDescription>Form and goals trend</CardDescription>
</CardHeader>
<CardContent>
{results.length > 0 ? (
<div className="grid gap-6 md:grid-cols-2">
<FormDonutChart
data={[
{ name: "W", value: stats?.wins ?? 0 },
{ name: "D", value: stats?.draws ?? 0 },
{ name: "L", value: stats?.losses ?? 0 },
].filter((d) => d.value > 0)}
/>
<GoalsTrendChart
data={results.map((r, i) => ({
matchday: i + 1,
gf: r.goals_for,
ga: r.goals_against,
}))}
/>
</div>
) : (
<p className="text-sm text-muted-foreground">
Complete matches to unlock charts.
</p>
)}
</CardContent>
</Card>
<Card className="lg:col-span-3">
<CardHeader>
<CardTitle>Next fixture check</CardTitle>
<CardDescription>Upcoming match</CardDescription>
</CardHeader>
<CardContent>
{data?.nextFixture ? (
<NextFixtureCard fixture={data.nextFixture} />
) : (
<p className="text-sm text-muted-foreground">
No upcoming fixtures. Check{" "}
<Link
href="/manager/leagues"
className="text-foreground underline-offset-4 hover:underline"
>
leagues
</Link>
.
</p>
)}
</CardContent>
</Card>
</div>
<Card>
<CardHeader>
<CardTitle>Recent results</CardTitle>
<CardDescription>Latest match outcomes</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
{results.map((r, i) => (
<div
key={i}
className="flex items-center justify-between rounded-lg border border-border bg-muted/30 px-4 py-3 text-sm"
>
<span className="font-medium">vs {r.opponent_name}</span>
<div className="flex items-center gap-2">
<span className="tabular-nums text-muted-foreground">
{r.goals_for}-{r.goals_against}
</span>
<Badge
variant={
r.result === "W"
? "success"
: r.result === "L"
? "destructive"
: "warning"
}
>
{r.result}
</Badge>
</div>
</div>
))}
{results.length === 0 && (
<p className="text-sm text-muted-foreground">No results yet</p>
)}
</CardContent>
</Card>
</>
) : (
<Card>
<CardContent className="py-12 text-center text-sm text-muted-foreground">
{activeTab === "reports"
? "Detailed reports coming soon."
: "Activity feed coming soon."}
</CardContent>
</Card>
)}
</div>
);
}
function NextFixtureCard({ fixture }: { fixture: Record<string, unknown> }) {
const home = fixture.home as { name: string } | null;
const away = fixture.away as { name: string } | null;
const when =
(fixture.scheduled_at as string) ||
(fixture.proposed_scheduled_at as string);
return (
<div className="space-y-3">
<p className="text-lg font-semibold">
{home?.name} <span className="text-muted-foreground">vs</span>{" "}
{away?.name}
</p>
{when && (
<p className="text-sm text-muted-foreground">
{new Date(when).toLocaleString()}
</p>
)}
<Badge variant="outline" className="capitalize">
{(fixture.status as string)?.replace(/_/g, " ")}
</Badge>
</div>
);
}