Initial commit
This commit is contained in:
201
app/account/webinars/page.tsx
Normal file
201
app/account/webinars/page.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user