Files
yourwillyourwish/app/account/webinars/page.tsx
2026-02-06 21:44:04 -06:00

202 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
interface Webinar {
id: string;
title: string;
description: string;
speaker: string;
startAt: string;
duration: number;
bannerUrl?: string;
category: string;
capacity: number;
priceCents: number;
}
interface Registration {
id: string;
userId: string;
webinarId: string;
registeredAt: string;
webinar: Webinar;
}
function formatDate(dateString: string) {
const date = new Date(dateString);
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
});
}
export default function AccountWebinarsPage() {
const router = useRouter();
const [registrations, setRegistrations] = useState<Registration[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchRegistrations() {
try {
const res = await fetch("/api/account/webinars");
if (!res.ok) {
if (res.status === 401) {
router.push("/");
return;
}
throw new Error("Failed to fetch registrations");
}
const data = await res.json();
setRegistrations(data.registrations || []);
} catch (err) {
console.error("Error fetching registrations:", err);
setError("Failed to load your webinars");
} finally {
setLoading(false);
}
}
fetchRegistrations();
}, [router]);
if (loading) {
return (
<main className="max-w-7xl mx-auto px-6 py-16">
<div className="mb-8">
<h1 className="text-3xl font-bold mb-2 text-slate-900 dark:text-white">
📚 My Webinars
</h1>
<p className="text-gray-600 dark:text-gray-400">
View your registered webinars
</p>
</div>
<div className="card">
<p className="text-center text-gray-600 dark:text-gray-400 py-12">
Loading your webinars...
</p>
</div>
</main>
);
}
return (
<main className="max-w-7xl mx-auto px-6 py-16">
<div className="mb-8">
<h1 className="text-3xl font-bold mb-2 text-slate-900 dark:text-white">
📚 My Webinars
</h1>
<p className="text-gray-600 dark:text-gray-400">
View your registered webinars
</p>
</div>
{error && (
<div className="bg-red-500/10 text-red-600 dark:text-red-400 p-4 rounded-lg mb-6">
{error}
</div>
)}
{registrations.length === 0 ? (
<div className="card">
<div className="text-center py-12">
<p className="text-gray-600 dark:text-gray-400 mb-4">
You haven't registered for any webinars yet.
</p>
<a href="/webinars" className="btn-primary btn-sm">
🎓 Browse Webinars
</a>
</div>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{registrations.map((registration) => {
const webinar = registration.webinar;
const startDate = new Date(webinar.startAt);
const now = new Date();
const isPast = startDate < now;
return (
<div
key={registration.id}
className="card group hover:shadow-lg dark:hover:shadow-lg/50 transition-all duration-300 overflow-hidden"
>
{webinar.bannerUrl && (
<div className="relative h-40 overflow-hidden bg-gradient-to-br from-primary/20 to-secondary/20">
<img
src={webinar.bannerUrl}
alt={webinar.title}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
{isPast && (
<div className="absolute inset-0 bg-black/40 flex items-center justify-center">
<span className="badge badge-secondary">Completed</span>
</div>
)}
</div>
)}
<div className="p-5">
<div className="flex items-start justify-between mb-2">
<h3 className="text-lg font-semibold text-slate-900 dark:text-white flex-1">
{webinar.title}
</h3>
<span className="badge badge-primary text-xs ml-2">
{webinar.category}
</span>
</div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4 line-clamp-2">
{webinar.description}
</p>
<div className="space-y-2 mb-4 text-sm">
<div className="flex items-center gap-2 text-gray-700 dark:text-gray-300">
<span>🎤</span>
<span className="font-medium">{webinar.speaker}</span>
</div>
<div className="flex items-center gap-2 text-gray-700 dark:text-gray-300">
<span>📅</span>
<span>{formatDate(webinar.startAt)}</span>
</div>
<div className="flex items-center gap-2 text-gray-700 dark:text-gray-300">
<span></span>
<span>{webinar.duration} minutes</span>
</div>
<div className="flex items-center gap-2 text-gray-600 dark:text-gray-400 text-xs">
<span></span>
<span>
Registered {formatDate(registration.registeredAt).split(",")[0]}
</span>
</div>
</div>
<div className="flex gap-2">
<a
href={`/webinars/${webinar.id}`}
className="flex-1 btn-primary btn-sm"
>
📖 View Details
</a>
{!isPast && (
<button className="flex-1 btn-secondary btn-sm">
🔗 Join Now
</button>
)}
</div>
</div>
</div>
);
})}
</div>
)}
</main>
);
}