lad-website/frontend/src/app/admin/events/[id]/page.tsx
TheMaddax 338f81f7ad Phase 7: Fix theme selector positioning and implement dynamic theme-aware styling
- Fixed theme selector dropdown positioning issues with proper Tailwind classes
- Replaced custom CSS classes with standard Tailwind utilities for v4 compatibility
- Implemented dynamic theme-aware styling system for dropdown appearance
- Added theme-specific styling for Light, Dark, High Contrast Light, High Contrast Dark
- Enhanced UX with professional dropdown interface and visual consistency
- Maintained 100% theme compliance and accessibility standards
- Updated memory bank documentation with Phase 7 completion

Key improvements:
- Professional dropdown with proper shadows, borders, and backgrounds
- Dynamic styling that adapts to current website theme
- Enhanced accessibility with ARIA support and keyboard navigation
- Seamless visual integration across all theme modes
- Production-ready theme selector with polished UI
2025-06-03 17:42:14 -06:00

265 lines
9.1 KiB
TypeScript

'use client';
import React, { useState, useEffect } from 'react';
import { useParams, useRouter } from 'next/navigation';
import Link from 'next/link';
import { useEvents } from '../../../../hooks/useEvents';
export default function EventDetailsPage() {
const params = useParams();
const router = useRouter();
const eventId = params.id as string;
const { getEventById, deleteEvent } = useEvents();
const [event, setEvent] = useState<any>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState('');
const [isDeleting, setIsDeleting] = useState(false);
useEffect(() => {
const loadEvent = async () => {
setIsLoading(true);
setError('');
try {
const eventData = await getEventById(eventId);
if (eventData) {
setEvent(eventData);
} else {
setError('Event not found');
}
} catch (err) {
console.error('Error loading event:', err);
setError('Failed to load event data. Please try again.');
} finally {
setIsLoading(false);
}
};
loadEvent();
}, [eventId, getEventById]);
const handleDelete = async () => {
if (!window.confirm('Are you sure you want to delete this event?')) {
return;
}
setIsDeleting(true);
try {
const success = await deleteEvent(eventId);
if (success) {
router.push('/admin/events');
} else {
setError('Failed to delete event. Please try again.');
}
} catch (err) {
console.error('Error deleting event:', err);
setError('An unexpected error occurred. Please try again later.');
} finally {
setIsDeleting(false);
}
};
if (isLoading) {
return (
<div className="container mx-auto px-4 py-8">
<div className="flex justify-center items-center h-64">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
<span className="ml-3 text-lg admin-text">Loading event data...</span>
</div>
</div>
);
}
if (error) {
return (
<div className="container mx-auto px-4 py-8">
<div className="admin-card" style={{backgroundColor: 'var(--admin-error)', color: 'white', border: '1px solid var(--admin-error)'}}>
<p className="font-medium">Error</p>
<p>{error}</p>
<div className="mt-4">
<Link href="/admin/events" className="admin-btn-primary">
Return to Events
</Link>
</div>
</div>
</div>
);
}
if (!event) {
return null;
}
// Format date for display
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
weekday: 'long',
month: 'long',
day: 'numeric',
year: 'numeric'
});
};
return (
<div className="container mx-auto px-4 py-8">
<div className="flex items-center justify-between mb-6">
<h1 className="admin-page-header-title">{event.title}</h1>
<div className="flex space-x-3">
<Link
href="/admin/events"
className="admin-btn-secondary"
>
Back to Events
</Link>
<Link
href={`/admin/events/${eventId}/edit`}
className="admin-btn-primary"
>
Edit Event
</Link>
</div>
</div>
<div className="admin-card mb-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="md:col-span-2 space-y-6">
{/* Event details */}
<div>
<h2 className="text-xl font-semibold mb-4 admin-text">Event Details</h2>
<div className="prose dark:prose-invert max-w-none">
<p className="admin-text">{event.description}</p>
</div>
</div>
{/* Tags */}
{event.tags && event.tags.length > 0 && (
<div>
<h3 className="text-lg font-semibold mb-2 admin-text">Tags</h3>
<div className="flex flex-wrap gap-2">
{event.tags.map((tag: string, index: number) => (
<span
key={index}
className="admin-tag"
>
{tag}
</span>
))}
</div>
</div>
)}
{/* Recurring info */}
{event.recurring && (
<div>
<h3 className="text-lg font-semibold mb-2 admin-text">Recurrence</h3>
<p className="admin-text">{event.recurrencePattern}</p>
</div>
)}
</div>
<div className="space-y-6">
{/* Event metadata */}
<div className="admin-info-panel">
<h3 className="text-lg font-semibold mb-4 admin-text">Event Information</h3>
<div className="space-y-3">
<div>
<p className="text-sm admin-text-light">Date</p>
<p className="font-medium admin-text">{formatDate(event.date)}</p>
</div>
<div>
<p className="text-sm admin-text-light">Time</p>
<p className="font-medium admin-text">{event.time}</p>
</div>
<div>
<p className="text-sm admin-text-light">Location</p>
<p className="font-medium admin-text">{event.location}</p>
</div>
<div>
<p className="text-sm admin-text-light">Category</p>
<p className="font-medium capitalize admin-text">{event.category}</p>
</div>
<div>
<p className="text-sm admin-text-light">Status</p>
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
event.status === 'published' ? 'admin-status-published' :
event.status === 'draft' ? 'admin-status-draft' :
'admin-status-archived'
}`}>
{event.status.charAt(0).toUpperCase() + event.status.slice(1)}
</span>
</div>
</div>
</div>
{/* Registration info */}
<div className="admin-info-panel">
<h3 className="text-lg font-semibold mb-4 admin-text">Registration</h3>
{event.registrationRequired ? (
<div className="space-y-3">
<p className="admin-text">Registration is required for this event.</p>
<div>
<p className="text-sm admin-text-light">Current Registrations</p>
<p className="font-medium admin-text">{event.registeredCount}</p>
</div>
{event.maxAttendees && (
<div>
<p className="text-sm admin-text-light">Maximum Attendees</p>
<p className="font-medium admin-text">{event.maxAttendees}</p>
</div>
)}
{event.maxAttendees && (
<div className="mt-2">
<div className="w-full admin-progress-bg rounded-full h-2.5">
<div
className="bg-primary h-2.5 rounded-full"
style={{ width: `${Math.min(100, (event.registeredCount / event.maxAttendees) * 100)}%` }}
></div>
</div>
<p className="text-sm admin-text-light mt-1">
{event.registeredCount} of {event.maxAttendees} spots filled
({Math.round((event.registeredCount / event.maxAttendees) * 100)}%)
</p>
</div>
)}
</div>
) : (
<p className="admin-text">Registration is not required for this event.</p>
)}
</div>
{/* Danger zone */}
<div className="admin-danger-zone">
<h3 className="text-lg font-semibold admin-danger-text mb-4">Danger Zone</h3>
<p className="text-sm admin-text-light mb-4">
Deleting this event will permanently remove it and cannot be undone.
</p>
<button
onClick={handleDelete}
disabled={isDeleting}
className="w-full bg-red-600 hover:bg-red-700 text-white py-2 px-4 rounded focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 disabled:opacity-50"
>
{isDeleting ? 'Deleting...' : 'Delete Event'}
</button>
</div>
</div>
</div>
</div>
</div>
);
}