This commit is contained in:
gpt-engineer-app[bot]
2025-12-19 01:54:13 +00:00
parent 278f709201
commit ff877266b0
13 changed files with 2540 additions and 231 deletions

130
src/pages/Events.tsx Normal file
View File

@@ -0,0 +1,130 @@
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { supabase } from '@/integrations/supabase/client';
import { AppLayout } from '@/components/AppLayout';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
import { formatDateTime } from '@/lib/format';
import { Calendar, Video, Users, BookOpen } from 'lucide-react';
interface Event {
id: string;
type: string;
product_id: string | null;
title: string;
starts_at: string;
ends_at: string;
status: string;
product?: {
slug: string;
title: string;
} | null;
}
export default function Events() {
const [events, setEvents] = useState<Event[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchEvents();
}, []);
const fetchEvents = async () => {
const { data, error } = await supabase
.from('events')
.select(`
*,
product:products (slug, title)
`)
.eq('status', 'confirmed')
.gte('ends_at', new Date().toISOString())
.order('starts_at', { ascending: true });
if (!error && data) {
setEvents(data as unknown as Event[]);
}
setLoading(false);
};
const getTypeIcon = (type: string) => {
switch (type) {
case 'bootcamp': return BookOpen;
case 'webinar': return Video;
case 'consulting': return Users;
default: return Calendar;
}
};
const getTypeLabel = (type: string) => {
switch (type) {
case 'bootcamp': return 'Bootcamp';
case 'webinar': return 'Webinar';
case 'consulting': return 'Konsultasi';
default: return type;
}
};
return (
<AppLayout>
<div className="container mx-auto px-4 py-8">
<div className="flex items-center gap-3 mb-8">
<Calendar className="w-8 h-8" />
<div>
<h1 className="text-4xl font-bold">Kalender Acara</h1>
<p className="text-muted-foreground">Jadwal webinar, bootcamp, dan sesi konsultasi</p>
</div>
</div>
{loading ? (
<div className="space-y-4">
{[...Array(3)].map((_, i) => (
<Skeleton key={i} className="h-32 w-full" />
))}
</div>
) : events.length === 0 ? (
<Card className="border-2 border-border">
<CardContent className="py-12 text-center">
<Calendar className="w-12 h-12 mx-auto mb-4 text-muted-foreground" />
<p className="text-muted-foreground">Belum ada acara terjadwal</p>
</CardContent>
</Card>
) : (
<div className="space-y-4">
{events.map((event) => {
const Icon = getTypeIcon(event.type);
return (
<Card key={event.id} className="border-2 border-border">
<CardHeader className="pb-2">
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<div className="p-2 bg-muted rounded-none">
<Icon className="w-5 h-5" />
</div>
<div>
<CardTitle className="text-lg">{event.title}</CardTitle>
<CardDescription>{formatDateTime(event.starts_at)}</CardDescription>
</div>
</div>
<Badge className="bg-secondary">{getTypeLabel(event.type)}</Badge>
</div>
</CardHeader>
<CardContent>
{event.product && (
<Link to={`/products/${event.product.slug}`}>
<Button variant="outline" size="sm" className="border-2">
Lihat Produk
</Button>
</Link>
)}
</CardContent>
</Card>
);
})}
</div>
)}
</div>
</AppLayout>
);
}