220 lines
8.2 KiB
JavaScript
220 lines
8.2 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import { useParams, useNavigate } from 'react-router-dom';
|
|
import api from '../utils/api';
|
|
import { Card } from '../components/ui/card';
|
|
import { Button } from '../components/ui/button';
|
|
import { Badge } from '../components/ui/badge';
|
|
import { toast } from 'sonner';
|
|
import Navbar from '../components/Navbar';
|
|
import MemberFooter from '../components/MemberFooter';
|
|
import AddToCalendarButton from '../components/AddToCalendarButton';
|
|
import { Calendar, MapPin, Users, ArrowLeft, Check, X, HelpCircle } from 'lucide-react';
|
|
|
|
const EventDetails = () => {
|
|
const { id } = useParams();
|
|
const navigate = useNavigate();
|
|
const [event, setEvent] = useState(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [rsvpLoading, setRsvpLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
fetchEvent();
|
|
}, [id]);
|
|
|
|
const fetchEvent = async () => {
|
|
try {
|
|
const response = await api.get(`/events/${id}`);
|
|
setEvent(response.data);
|
|
} catch (error) {
|
|
toast.error('Failed to load event');
|
|
navigate('/events');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleRSVP = async (status) => {
|
|
setRsvpLoading(true);
|
|
try {
|
|
await api.post(`/events/${id}/rsvp`, { rsvp_status: status });
|
|
toast.success(`RSVP updated to: ${status}`);
|
|
fetchEvent();
|
|
} catch (error) {
|
|
toast.error('Failed to update RSVP');
|
|
} finally {
|
|
setRsvpLoading(false);
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="min-h-screen bg-white">
|
|
<Navbar />
|
|
<div className="flex items-center justify-center min-h-[60vh]">
|
|
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!event) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white">
|
|
<Navbar />
|
|
|
|
<div className="max-w-4xl mx-auto px-6 py-12">
|
|
<button
|
|
onClick={() => navigate('/events')}
|
|
className="inline-flex items-center text-[#664fa3] hover:text-[#ff9e77] transition-colors mb-8"
|
|
data-testid="back-to-events-button"
|
|
>
|
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
|
Back to Events
|
|
</button>
|
|
|
|
<Card className="p-8 md:p-12 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg">
|
|
<div className="mb-8">
|
|
<div className="flex items-center gap-4 mb-6">
|
|
<div className="bg-[#DDD8EB]/20 p-4 rounded-xl">
|
|
<Calendar className="h-10 w-10 text-[#664fa3]" />
|
|
</div>
|
|
{event.user_rsvp_status && (
|
|
<Badge
|
|
className={`px-4 py-2 rounded-full text-sm ${
|
|
event.user_rsvp_status === 'yes'
|
|
? 'bg-[#81B29A] text-white'
|
|
: event.user_rsvp_status === 'no'
|
|
? 'bg-gray-400 text-white'
|
|
: 'bg-orange-100 text-orange-700'
|
|
}`}
|
|
>
|
|
{event.user_rsvp_status === 'yes' && 'Going'}
|
|
{event.user_rsvp_status === 'no' && 'Not Going'}
|
|
{event.user_rsvp_status === 'maybe' && 'Maybe'}
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
|
|
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
|
|
{event.title}
|
|
</h1>
|
|
|
|
<div className="space-y-4 text-lg">
|
|
<div className="flex items-center gap-3 text-[#664fa3]">
|
|
<Calendar className="h-5 w-5" />
|
|
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
|
{new Date(event.start_at).toLocaleDateString('en-US', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-3 text-[#664fa3]">
|
|
<Calendar className="h-5 w-5" />
|
|
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
|
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} -{' '}
|
|
{new Date(event.end_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-3 text-[#664fa3]">
|
|
<MapPin className="h-5 w-5" />
|
|
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span>
|
|
</div>
|
|
<div className="flex items-center gap-3 text-[#664fa3]">
|
|
<Users className="h-5 w-5" />
|
|
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
|
{event.rsvp_count || 0} {event.rsvp_count === 1 ? 'person' : 'people'} attending
|
|
{event.capacity && ` (Capacity: ${event.capacity})`}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{event.description && (
|
|
<div className="mb-8 pb-8 border-b border-[#ddd8eb]">
|
|
<h2 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
|
|
About This Event
|
|
</h2>
|
|
<p className="text-[#664fa3] leading-relaxed whitespace-pre-line" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
|
{event.description}
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
<div>
|
|
<h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
|
|
RSVP to This Event
|
|
</h2>
|
|
<div className="flex gap-4 flex-wrap">
|
|
<Button
|
|
onClick={() => handleRSVP('yes')}
|
|
disabled={rsvpLoading}
|
|
className={`rounded-full px-8 py-6 flex items-center gap-2 ${
|
|
event.user_rsvp_status === 'yes'
|
|
? 'bg-[#81B29A] text-white'
|
|
: 'bg-[#DDD8EB] text-[#422268] hover:bg-white'
|
|
}`}
|
|
data-testid="rsvp-yes-button"
|
|
>
|
|
<Check className="h-5 w-5" />
|
|
I'm Going
|
|
</Button>
|
|
<Button
|
|
onClick={() => handleRSVP('maybe')}
|
|
disabled={rsvpLoading}
|
|
variant="outline"
|
|
className={`rounded-full px-8 py-6 flex items-center gap-2 border-2 ${
|
|
event.user_rsvp_status === 'maybe'
|
|
? 'border-orange-400 bg-orange-100 text-orange-700'
|
|
: 'border-[#664fa3] text-[#664fa3] hover:bg-[#f1eef9]'
|
|
}`}
|
|
data-testid="rsvp-maybe-button"
|
|
>
|
|
<HelpCircle className="h-5 w-5" />
|
|
Maybe
|
|
</Button>
|
|
<Button
|
|
onClick={() => handleRSVP('no')}
|
|
disabled={rsvpLoading}
|
|
variant="outline"
|
|
className={`rounded-full px-8 py-6 flex items-center gap-2 border-2 ${
|
|
event.user_rsvp_status === 'no'
|
|
? 'border-gray-400 bg-gray-100 text-gray-700'
|
|
: 'border-gray-400 text-gray-600 hover:bg-gray-50'
|
|
}`}
|
|
data-testid="rsvp-no-button"
|
|
>
|
|
<X className="h-5 w-5" />
|
|
Can't Attend
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Add to Calendar Section */}
|
|
<div className="mt-8 pt-8 border-t border-[#ddd8eb]">
|
|
<h2 className="text-xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
|
|
Add to Your Calendar
|
|
</h2>
|
|
<p className="text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
|
Never miss this event! Add it to your calendar app for reminders.
|
|
</p>
|
|
<AddToCalendarButton
|
|
event={event}
|
|
showSubscribe={false}
|
|
variant="outline"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
<MemberFooter />
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default EventDetails;
|