195 lines
7.3 KiB
TypeScript
195 lines
7.3 KiB
TypeScript
import { redirect } from "next/navigation";
|
|
import Link from "next/link";
|
|
import { getCurrentUser } from "@/lib/auth";
|
|
import {
|
|
getWeeklyWorkoutCount,
|
|
getMonthlyWorkoutCount,
|
|
getYearlyWorkoutCount,
|
|
getWeeklyVolume,
|
|
} from "@/lib/db/stats";
|
|
import { getRecentWorkouts } from "@/lib/db/workouts";
|
|
import { ActivitySquare, Calendar, CalendarDays, History, Plus } from "lucide-react";
|
|
|
|
export default async function DashboardPage() {
|
|
const user = await getCurrentUser();
|
|
|
|
if (!user) {
|
|
redirect("/auth/login");
|
|
}
|
|
|
|
const [weeklyCount, monthlyCount, yearlyCount, _weeklyVolume, recentWorkouts] =
|
|
await Promise.all([
|
|
getWeeklyWorkoutCount(user.id),
|
|
getMonthlyWorkoutCount(user.id),
|
|
getYearlyWorkoutCount(user.id),
|
|
getWeeklyVolume(user.id),
|
|
getRecentWorkouts(user.id, 5),
|
|
]);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-[#0A0A0A]">
|
|
{/* Header with greeting */}
|
|
<div className="px-4 py-6 sm:px-6 lg:px-8">
|
|
<div className="max-w-7xl mx-auto">
|
|
<h1 className="text-4xl font-bold text-white">
|
|
Welcome back, {user.name || "Trainer"}!
|
|
</h1>
|
|
<p className="text-zinc-400 mt-2">
|
|
Keep pushing your limits and achieving your goals.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main content */}
|
|
<div className="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8">
|
|
{/* Stats Cards */}
|
|
<div className="grid grid-cols-3 gap-3 sm:gap-4 mb-8">
|
|
{/* This Week */}
|
|
<div className="bg-zinc-900 border border-zinc-800 rounded-lg p-4 sm:p-6">
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<p className="text-zinc-400 text-xs sm:text-sm font-medium">
|
|
This Week
|
|
</p>
|
|
<p className="text-3xl sm:text-4xl font-bold text-white mt-1 sm:mt-2">
|
|
{weeklyCount}
|
|
</p>
|
|
<p className="text-zinc-500 text-[10px] sm:text-xs mt-1 sm:mt-2">
|
|
workouts
|
|
</p>
|
|
</div>
|
|
<div className="bg-zinc-800 p-2 sm:p-3 rounded-lg hidden sm:block">
|
|
<ActivitySquare className="text-white w-5 h-5 sm:w-6 sm:h-6" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* This Month */}
|
|
<div className="bg-zinc-900 border border-zinc-800 rounded-lg p-4 sm:p-6">
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<p className="text-zinc-400 text-xs sm:text-sm font-medium">
|
|
This Month
|
|
</p>
|
|
<p className="text-3xl sm:text-4xl font-bold text-white mt-1 sm:mt-2">
|
|
{monthlyCount}
|
|
</p>
|
|
<p className="text-zinc-500 text-[10px] sm:text-xs mt-1 sm:mt-2">
|
|
workouts
|
|
</p>
|
|
</div>
|
|
<div className="bg-zinc-800 p-2 sm:p-3 rounded-lg hidden sm:block">
|
|
<Calendar className="text-white w-5 h-5 sm:w-6 sm:h-6" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* This Year */}
|
|
<div className="bg-zinc-900 border border-zinc-800 rounded-lg p-4 sm:p-6">
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<p className="text-zinc-400 text-xs sm:text-sm font-medium">
|
|
This Year
|
|
</p>
|
|
<p className="text-3xl sm:text-4xl font-bold text-white mt-1 sm:mt-2">
|
|
{yearlyCount}
|
|
</p>
|
|
<p className="text-zinc-500 text-[10px] sm:text-xs mt-1 sm:mt-2">
|
|
workouts
|
|
</p>
|
|
</div>
|
|
<div className="bg-zinc-800 p-2 sm:p-3 rounded-lg hidden sm:block">
|
|
<CalendarDays className="text-white w-5 h-5 sm:w-6 sm:h-6" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Quick Actions */}
|
|
<div className="flex flex-col sm:flex-row gap-4 mb-8">
|
|
<Link
|
|
href="/main/workouts/new"
|
|
className="flex-1 bg-white hover:bg-gray-100 text-black font-medium py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition"
|
|
>
|
|
<Plus className="w-5 h-5" />
|
|
Log Workout
|
|
</Link>
|
|
<Link
|
|
href="/main/workouts"
|
|
className="flex-1 bg-zinc-800 hover:bg-zinc-700 text-white font-medium py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition"
|
|
>
|
|
<History className="w-5 h-5" />
|
|
View History
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Recent Workouts */}
|
|
<div className="bg-zinc-900 border border-zinc-800 rounded-lg">
|
|
<div className="px-6 py-4 border-b border-zinc-800">
|
|
<h2 className="text-lg font-bold text-white">
|
|
Recent Workouts
|
|
</h2>
|
|
</div>
|
|
|
|
{recentWorkouts.length === 0 ? (
|
|
<div className="px-6 py-12 text-center">
|
|
<ActivitySquare className="w-12 h-12 text-zinc-600 mx-auto mb-4" />
|
|
<h3 className="text-lg font-semibold text-white mb-2">
|
|
No workouts yet
|
|
</h3>
|
|
<p className="text-zinc-400 mb-6">
|
|
Start your fitness journey by logging your first workout!
|
|
</p>
|
|
<Link
|
|
href="/main/workouts/new"
|
|
className="inline-block bg-white hover:bg-gray-100 text-black font-medium py-2 px-4 rounded-lg transition"
|
|
>
|
|
Log First Workout
|
|
</Link>
|
|
</div>
|
|
) : (
|
|
<div className="divide-y divide-zinc-800">
|
|
{recentWorkouts.map((workout) => (
|
|
<Link
|
|
key={workout.id}
|
|
href={`/main/workouts/${workout.id}`}
|
|
className="px-6 py-4 hover:bg-zinc-800 transition block"
|
|
>
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<h3 className="font-semibold text-white">
|
|
{workout.name || "Unnamed Workout"}
|
|
</h3>
|
|
<p className="text-sm text-zinc-400 mt-1">
|
|
{new Date(workout.date).toLocaleDateString("en-US", {
|
|
weekday: "short",
|
|
month: "short",
|
|
day: "numeric",
|
|
year: "numeric",
|
|
})}
|
|
</p>
|
|
<p className="text-sm text-zinc-500 mt-1">
|
|
{(workout as any).setLogs.length} sets
|
|
{workout.durationMinutes &&
|
|
` · ${workout.durationMinutes} min`}
|
|
</p>
|
|
</div>
|
|
<div className="text-right">
|
|
<div className="text-2xl font-bold text-white">
|
|
{(workout as any).setLogs.length}
|
|
</div>
|
|
<p className="text-xs text-zinc-400 mt-1">
|
|
{(workout as any).setLogs.length === 1 ? "set" : "sets"}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|