refactor(admin): move event details page to admin section
- Moved EventDetailsPage from /events/:slug/details to /admin/events/:slug/details - Added admin authentication check with redirect to login/home - Updated all navigation links across the app: - EventsPage: "View details (admin)" button - EventChatPage: "View QR Code (admin)" link - EventCard: handleViewDetails navigation - Fixed relative imports after moving to admin folder This page contains admin-only features (QR codes, participants list, matching config, scheduler config, matching runs) and should only be accessible to administrators.
This commit is contained in:
173
frontend/src/pages/admin/EventDetailsPage.jsx
Normal file
173
frontend/src/pages/admin/EventDetailsPage.jsx
Normal file
@@ -0,0 +1,173 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, Link, useNavigate } from 'react-router-dom';
|
||||
import { Calendar, MapPin } from 'lucide-react';
|
||||
import Layout from '../../components/layout/Layout';
|
||||
import { eventsAPI } from '../../services/api';
|
||||
import QRCodeSection from '../../components/events/QRCodeSection';
|
||||
import ParticipantsSection from '../../components/events/ParticipantsSection';
|
||||
import MatchingConfigSection from '../../components/events/MatchingConfigSection';
|
||||
import ScheduleConfigSection from '../../components/events/ScheduleConfigSection';
|
||||
import MatchingRunsSection from '../../components/events/MatchingRunsSection';
|
||||
|
||||
export default function EventDetailsPage() {
|
||||
const { slug } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [user, setUser] = useState(null);
|
||||
const [eventDetails, setEventDetails] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
// Check if user is admin
|
||||
useEffect(() => {
|
||||
const userData = localStorage.getItem('user');
|
||||
if (userData) {
|
||||
const parsedUser = JSON.parse(userData);
|
||||
setUser(parsedUser);
|
||||
|
||||
if (!parsedUser.isAdmin) {
|
||||
navigate('/');
|
||||
}
|
||||
} else {
|
||||
navigate('/login');
|
||||
}
|
||||
}, [navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (user?.isAdmin) {
|
||||
fetchEventDetails();
|
||||
}
|
||||
}, [slug, user]);
|
||||
|
||||
const fetchEventDetails = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await eventsAPI.getDetails(slug);
|
||||
setEventDetails(response.data);
|
||||
} catch (err) {
|
||||
console.error('Error loading event details:', err);
|
||||
setError(err.message || 'Failed to load event details');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Layout>
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600 mx-auto"></div>
|
||||
<p className="mt-4 text-gray-600">Loading event details...</p>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Layout>
|
||||
<div className="max-w-4xl mx-auto p-6">
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
|
||||
<p className="text-red-800">{error}</p>
|
||||
<Link to="/events" className="text-red-600 hover:underline mt-2 inline-block">
|
||||
Back to Events
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
if (!eventDetails) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { event, checkin, participants, stats } = eventDetails;
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="max-w-6xl mx-auto p-6">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<Link to="/events" className="text-primary-600 hover:underline mb-2 inline-block">
|
||||
← Back to Events
|
||||
</Link>
|
||||
<h1 className="text-3xl font-bold text-gray-900">{event.name}</h1>
|
||||
<div className="flex items-center gap-4 mt-2 text-gray-600">
|
||||
<div className="flex items-center gap-1">
|
||||
<MapPin size={16} />
|
||||
<span>{event.location}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar size={16} />
|
||||
<span>{formatDate(event.startDate)} - {formatDate(event.endDate)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* QR Code & Participants Grid */}
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<QRCodeSection checkin={checkin} formatDate={formatDate} />
|
||||
<ParticipantsSection participants={participants} totalCount={stats.totalParticipants} />
|
||||
</div>
|
||||
|
||||
{/* Auto-Matching Configuration */}
|
||||
<div className="mt-6">
|
||||
<MatchingConfigSection slug={slug} event={event} onRefresh={fetchEventDetails} />
|
||||
</div>
|
||||
|
||||
{/* Matching Runs (admin tools) */}
|
||||
<div className="mt-6">
|
||||
<MatchingRunsSection slug={slug} />
|
||||
</div>
|
||||
|
||||
{/* Schedule Configuration */}
|
||||
<div className="mt-6">
|
||||
<ScheduleConfigSection slug={slug} event={event} onRefresh={fetchEventDetails} />
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="mt-6 flex gap-4">
|
||||
<Link
|
||||
to={`/events/${slug}/chat`}
|
||||
className="flex-1 bg-primary-600 text-white px-6 py-3 rounded-lg hover:bg-primary-700 transition-colors text-center font-medium"
|
||||
>
|
||||
Go to Event Chat
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => window.print()}
|
||||
className="px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors font-medium"
|
||||
>
|
||||
Print QR Code
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Print Styles */}
|
||||
<style>{`
|
||||
@media print {
|
||||
body * {
|
||||
visibility: hidden;
|
||||
}
|
||||
.print-area, .print-area * {
|
||||
visibility: visible;
|
||||
}
|
||||
.print-area {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user