From 6ce3111cdd1c2e7552a469018689e86f5533d1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Gierwia=C5=82o?= Date: Sun, 30 Nov 2025 15:13:50 +0100 Subject: [PATCH] feat(dashboard): improve RecordingSummaryCard styling and fix tab navigation - Increase font size from xs to sm for better readability - Reduce avatar size from xs to 24px for better proportions - Add proper layout with heat names in separate line - Add truncate for long usernames to prevent overflow - Style status badges with colored backgrounds and icons (pending/accepted) - Fix EventChatPage to read and handle ?tab=records URL parameter - Map 'records' query param to 'recording' tab for proper navigation --- .../dashboard/RecordingSummaryCard.jsx | 163 ++++++++++++++++++ frontend/src/pages/EventChatPage.jsx | 21 ++- 2 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/dashboard/RecordingSummaryCard.jsx diff --git a/frontend/src/components/dashboard/RecordingSummaryCard.jsx b/frontend/src/components/dashboard/RecordingSummaryCard.jsx new file mode 100644 index 0000000..8a3ff14 --- /dev/null +++ b/frontend/src/components/dashboard/RecordingSummaryCard.jsx @@ -0,0 +1,163 @@ +import { useNavigate } from 'react-router-dom'; +import { Video, Clock, CheckCircle, XCircle, ChevronRight } from 'lucide-react'; +import Avatar from '../common/Avatar'; + +/** + * Recording summary card for dashboard - shows recording assignments for an event + */ +const RecordingSummaryCard = ({ event }) => { + const navigate = useNavigate(); + + const { toBeRecorded, toRecord } = event.recordingSuggestions || { toBeRecorded: [], toRecord: [] }; + + // Count by status + const toBeRecordedPending = toBeRecorded.filter(s => s.status === 'pending').length; + const toBeRecordedAccepted = toBeRecorded.filter(s => s.status === 'accepted').length; + const toRecordPending = toRecord.filter(s => s.status === 'pending').length; + const toRecordAccepted = toRecord.filter(s => s.status === 'accepted').length; + + // If no recording suggestions, don't render + if (toBeRecorded.length === 0 && toRecord.length === 0) { + return null; + } + + const getStatusIcon = (status) => { + switch (status) { + case 'pending': + return ; + case 'accepted': + return ; + case 'rejected': + return ; + default: + return null; + } + }; + + const formatHeat = (heat) => { + return `${heat.division} ${heat.competitionType} H${heat.heatNumber}`; + }; + + return ( +
+
+
+

{event.name}

+

{event.location}

+
+
+ +
+ {/* Heats to be recorded */} + {toBeRecorded.length > 0 && ( +
+

+

+
+ {toBeRecorded.slice(0, 2).map((suggestion) => ( +
+
+
{formatHeat(suggestion.heat)}
+ {suggestion.recorder ? ( +
+ + @{suggestion.recorder.username} +
+ ) : ( + No recorder + )} +
+
+ {getStatusIcon(suggestion.status)} +
+
+ ))} + {toBeRecorded.length > 2 && ( +

+ +{toBeRecorded.length - 2} more +

+ )} +
+
+ {toBeRecordedPending > 0 && ( + + + {toBeRecordedPending} pending + + )} + {toBeRecordedAccepted > 0 && ( + + + {toBeRecordedAccepted} accepted + + )} +
+
+ )} + + {/* Recording others */} + {toRecord.length > 0 && ( +
+

+

+
+ {toRecord.slice(0, 2).map((suggestion) => ( +
+
+
{formatHeat(suggestion.heat)}
+
+ + @{suggestion.dancer.username} +
+
+
+ {getStatusIcon(suggestion.status)} +
+
+ ))} + {toRecord.length > 2 && ( +

+ +{toRecord.length - 2} more +

+ )} +
+
+ {toRecordPending > 0 && ( + + + {toRecordPending} pending + + )} + {toRecordAccepted > 0 && ( + + + {toRecordAccepted} accepted + + )} +
+
+ )} +
+ + +
+ ); +}; + +export default RecordingSummaryCard; diff --git a/frontend/src/pages/EventChatPage.jsx b/frontend/src/pages/EventChatPage.jsx index 29d0fdf..1bd4744 100644 --- a/frontend/src/pages/EventChatPage.jsx +++ b/frontend/src/pages/EventChatPage.jsx @@ -1,5 +1,5 @@ import { useState, useRef, useEffect } from 'react'; -import { useParams, useNavigate, Link } from 'react-router-dom'; +import { useParams, useNavigate, useSearchParams, Link } from 'react-router-dom'; import Layout from '../components/layout/Layout'; import { useAuth } from '../contexts/AuthContext'; import { Send, UserPlus, Loader2, LogOut, AlertTriangle, QrCode, Edit2, Filter, X, MessageSquare, Users, Video } from 'lucide-react'; @@ -20,6 +20,7 @@ const EventChatPage = () => { const { slug } = useParams(); const { user } = useAuth(); const navigate = useNavigate(); + const [searchParams] = useSearchParams(); const [event, setEvent] = useState(null); const [isParticipant, setIsParticipant] = useState(false); const [loading, setLoading] = useState(true); @@ -56,6 +57,24 @@ const EventChatPage = () => { // Tab state: 'chat' | 'participants' | 'recording' const [activeTab, setActiveTab] = useState('chat'); + // Read tab from URL query parameter + useEffect(() => { + const tabParam = searchParams.get('tab'); + if (tabParam) { + // Map 'records' to 'recording' for backwards compatibility + const tabMapping = { + 'chat': 'chat', + 'participants': 'participants', + 'records': 'recording', + 'recording': 'recording' + }; + const mappedTab = tabMapping[tabParam]; + if (mappedTab) { + setActiveTab(mappedTab); + } + } + }, [searchParams]); + const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); };