Initial commit for Start9 packaging
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { Plus, Activity, Upload } from "lucide-react";
|
||||
import { getCurrentUser } from "@/lib/auth";
|
||||
import { getWorkouts } from "@/lib/db/workouts";
|
||||
import WorkoutCard from "@/components/workouts/WorkoutCard";
|
||||
|
||||
interface PageProps {
|
||||
searchParams: { q?: string; dateFrom?: string; dateTo?: string };
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: "Workout History",
|
||||
description: "View your workout history",
|
||||
};
|
||||
|
||||
export default async function WorkoutsPage({ searchParams }: PageProps) {
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
redirect("/auth/login");
|
||||
}
|
||||
|
||||
// Parse search params
|
||||
const query = searchParams.q || "";
|
||||
const dateFrom = searchParams.dateFrom
|
||||
? new Date(searchParams.dateFrom)
|
||||
: undefined;
|
||||
const dateTo = searchParams.dateTo
|
||||
? new Date(searchParams.dateTo)
|
||||
: undefined;
|
||||
|
||||
// Fetch workouts
|
||||
const workouts = await getWorkouts(user.id, {
|
||||
query,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
limit: 50,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#0A0A0A]">
|
||||
{/* Header */}
|
||||
<div className="border-b border-zinc-800 sticky top-0 z-40">
|
||||
<div className="max-w-2xl mx-auto px-4 py-4 sm:py-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-white">
|
||||
Workout History
|
||||
</h1>
|
||||
<Link
|
||||
href="/main/import"
|
||||
className="p-2 hover:bg-zinc-900 rounded-lg text-zinc-400 hover:text-white transition"
|
||||
title="Import workouts from CSV"
|
||||
>
|
||||
<Upload className="w-5 h-5" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-2xl mx-auto px-4 py-6">
|
||||
{/* Search and filters */}
|
||||
<form className="mb-6 space-y-4">
|
||||
{/* Search bar */}
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="q"
|
||||
placeholder="Search workouts..."
|
||||
defaultValue={query}
|
||||
className="w-full px-4 py-3 border border-zinc-700 bg-zinc-800 rounded-lg focus:outline-none focus:ring-2 focus:ring-white text-white placeholder-zinc-500 text-base"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Date range */}
|
||||
<div className="grid grid-cols-2 gap-3 sm:gap-4">
|
||||
<div>
|
||||
<label className="block text-sm text-zinc-400 mb-1">From</label>
|
||||
<input
|
||||
type="date"
|
||||
name="dateFrom"
|
||||
defaultValue={searchParams.dateFrom || ""}
|
||||
className="w-full px-4 py-2 border border-zinc-700 bg-zinc-800 rounded-lg focus:outline-none focus:ring-2 focus:ring-white text-white text-base"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm text-zinc-400 mb-1">To</label>
|
||||
<input
|
||||
type="date"
|
||||
name="dateTo"
|
||||
defaultValue={searchParams.dateTo || ""}
|
||||
className="w-full px-4 py-2 border border-zinc-700 bg-zinc-800 rounded-lg focus:outline-none focus:ring-2 focus:ring-white text-white text-base"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit buttons */}
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="submit"
|
||||
className="flex-1 px-4 py-2 bg-white text-black rounded-lg font-medium hover:bg-gray-100 touch-target"
|
||||
>
|
||||
Filter
|
||||
</button>
|
||||
<Link
|
||||
href="/main/workouts"
|
||||
className="flex-1 px-4 py-2 bg-zinc-800 text-white rounded-lg font-medium hover:bg-zinc-700 text-center touch-target"
|
||||
>
|
||||
Clear
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Workout list */}
|
||||
{workouts.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="flex justify-center mb-4">
|
||||
<Activity className="w-12 h-12 text-zinc-600" />
|
||||
</div>
|
||||
<h2 className="text-lg font-semibold text-white mb-2">
|
||||
No workouts yet
|
||||
</h2>
|
||||
<p className="text-zinc-400 mb-6">
|
||||
{query || dateFrom || dateTo
|
||||
? "No workouts match your filters. Try adjusting your search."
|
||||
: "Start tracking your fitness journey by logging your first workout."}
|
||||
</p>
|
||||
<Link
|
||||
href="/main/workouts/new"
|
||||
className="inline-flex items-center gap-2 px-6 py-3 bg-white text-black rounded-lg font-semibold hover:bg-gray-100 touch-target"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
Log Your First Workout
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3 pb-20 sm:pb-6">
|
||||
{workouts.map((workout) => (
|
||||
<WorkoutCard key={workout.id} workout={workout} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Floating action button for mobile, regular button for desktop */}
|
||||
{workouts.length > 0 && (
|
||||
<>
|
||||
{/* Mobile FAB */}
|
||||
<div className="fixed bottom-6 right-6 sm:hidden">
|
||||
<Link
|
||||
href="/main/workouts/new"
|
||||
className="flex items-center justify-center w-14 h-14 bg-white text-black rounded-full shadow-lg hover:bg-gray-100 active:bg-gray-200 touch-target"
|
||||
aria-label="Log new workout"
|
||||
>
|
||||
<Plus className="w-6 h-6" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Desktop button */}
|
||||
<div className="hidden sm:block fixed bottom-6 right-6">
|
||||
<Link
|
||||
href="/main/workouts/new"
|
||||
className="flex items-center gap-2 px-6 py-3 bg-white text-black rounded-lg font-semibold hover:bg-gray-100 shadow-lg"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
Log Workout
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user