Move business logic out of App Router into module packages, add boundary validation scripts, and keep all routes as thin re-exports without changing URLs or API behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
113 lines
4.4 KiB
TypeScript
113 lines
4.4 KiB
TypeScript
"use client";
|
||
|
||
import { useQuery } from "@tanstack/react-query";
|
||
import Link from "next/link";
|
||
import { useTenantId } from "@/hooks/useTenantId";
|
||
import { sportsCenterApi } from "@/modules/sports-center/services/sports-center-api";
|
||
import { useSportsCenterCapabilities } from "@/modules/sports-center/hooks/useSportsCenterCapabilities";
|
||
import { SportsCenterPageLoader, SportsCenterPageError } from "@/modules/sports-center/pages/shared";
|
||
import { PageHeader, Card, CardContent, Button, Badge } from "@/components/ds";
|
||
|
||
function MobileShell({ title, children }: { title: string; children: React.ReactNode }) {
|
||
return (
|
||
<div className="mx-auto min-h-screen max-w-md bg-[var(--canvas)] pb-20">
|
||
<header className="sticky top-0 z-10 border-b border-[var(--border)] bg-[var(--surface)] px-4 py-4">
|
||
<p className="text-lg font-semibold text-secondary">{title}</p>
|
||
</header>
|
||
<div className="p-4">{children}</div>
|
||
<nav className="fixed bottom-0 left-0 right-0 flex justify-around border-t border-[var(--border)] bg-[var(--surface)] py-2">
|
||
<Link href="/sports-center/athlete" className="text-xs text-[var(--sports-accent)]">خانه</Link>
|
||
<Link href="/sports-center/athlete/workout" className="text-xs text-[var(--muted)]">تمرین</Link>
|
||
<Link href="/sports-center/athlete/progress" className="text-xs text-[var(--muted)]">پیشرفت</Link>
|
||
<Link href="/sports-center/athlete/profile" className="text-xs text-[var(--muted)]">پروفایل</Link>
|
||
</nav>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function AthleteHomePage() {
|
||
const { tenantId } = useTenantId();
|
||
const caps = useSportsCenterCapabilities();
|
||
const q = useQuery({
|
||
queryKey: ["sports-center", tenantId, "athlete-home"],
|
||
queryFn: () => sportsCenterApi.trainingPrograms.list(tenantId!, { page: 1, page_size: 5 }),
|
||
enabled: !!tenantId && caps.data?.features?.training_management === true,
|
||
});
|
||
|
||
if (!tenantId) return <SportsCenterPageLoader />;
|
||
|
||
return (
|
||
<MobileShell title="تمرین امروز">
|
||
{q.isLoading ? (
|
||
<SportsCenterPageLoader />
|
||
) : q.error ? (
|
||
<SportsCenterPageError error={q.error} onRetry={() => q.refetch()} />
|
||
) : (
|
||
<div className="space-y-4">
|
||
<Card>
|
||
<CardContent className="p-4">
|
||
<p className="text-sm font-medium">برنامههای فعال</p>
|
||
<p className="mt-1 text-2xl font-bold text-[var(--sports-accent)]">{q.data?.length ?? 0}</p>
|
||
</CardContent>
|
||
</Card>
|
||
{q.data?.map((p) => (
|
||
<Card key={String(p.id)}>
|
||
<CardContent className="flex items-center justify-between p-4">
|
||
<span className="font-medium">{String(p.name ?? p.code)}</span>
|
||
<Badge tone="primary">برنامه</Badge>
|
||
</CardContent>
|
||
</Card>
|
||
))}
|
||
<Link href="/sports-center/athlete/workout">
|
||
<Button className="w-full">شروع تمرین</Button>
|
||
</Link>
|
||
</div>
|
||
)}
|
||
</MobileShell>
|
||
);
|
||
}
|
||
|
||
export function AthleteWorkoutPage() {
|
||
return (
|
||
<MobileShell title="جزئیات تمرین">
|
||
<Card>
|
||
<CardContent className="space-y-3 p-4 text-sm text-[var(--muted)]">
|
||
<p>برنامه تمرین از API workout-plans و exercises بارگذاری میشود.</p>
|
||
<Link href="/sports-center/training/exercises">
|
||
<Button variant="outline" className="w-full">کتابخانه تمرین</Button>
|
||
</Link>
|
||
</CardContent>
|
||
</Card>
|
||
</MobileShell>
|
||
);
|
||
}
|
||
|
||
export function AthleteProgressPage() {
|
||
return (
|
||
<MobileShell title="پیشرفت">
|
||
<Card>
|
||
<CardContent className="space-y-3 p-4">
|
||
<p className="text-sm text-[var(--muted)]">ثبت پیشرفت از API progress-records</p>
|
||
<Link href="/sports-center/training/progress">
|
||
<Button className="w-full">ثبت پیشرفت جدید</Button>
|
||
</Link>
|
||
</CardContent>
|
||
</Card>
|
||
</MobileShell>
|
||
);
|
||
}
|
||
|
||
export function AthleteProfilePage() {
|
||
return (
|
||
<MobileShell title="پروفایل">
|
||
<Card>
|
||
<CardContent className="p-4 text-sm text-[var(--muted)]">
|
||
پروفایل ورزشکار — لینک به Member API
|
||
</CardContent>
|
||
</Card>
|
||
</MobileShell>
|
||
);
|
||
}
|
||
|
||
export default AthleteHomePage;
|