import { useState, useEffect } from 'react'; import { Video, VideoOff, Clock, CheckCircle, XCircle, AlertTriangle, RefreshCw } from 'lucide-react'; import { matchingAPI } from '../../services/api'; import Avatar from '../common/Avatar'; import { SUGGESTION_STATUS, SUGGESTION_TYPE } from '../../constants'; /** * RecordingTab - Main component for managing recording partnerships * Shows suggestions for who will record user's heats and who user needs to record */ const RecordingTab = ({ slug, event, myHeats }) => { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [suggestions, setSuggestions] = useState(null); const [recorderOptOut, setRecorderOptOut] = useState(false); const [updatingOptOut, setUpdatingOptOut] = useState(false); const [runningMatching, setRunningMatching] = useState(false); // Load suggestions on mount useEffect(() => { loadSuggestions(); }, [slug]); const loadSuggestions = async () => { try { setLoading(true); setError(null); const data = await matchingAPI.getSuggestions(slug); setSuggestions(data); } catch (err) { console.error('Failed to load suggestions:', err); setError('Failed to load suggestions'); } finally { setLoading(false); } }; const handleOptOutChange = async (newValue) => { try { setUpdatingOptOut(true); await matchingAPI.setRecorderOptOut(slug, newValue); setRecorderOptOut(newValue); } catch (err) { console.error('Failed to update opt-out:', err); } finally { setUpdatingOptOut(false); } }; const handleUpdateStatus = async (suggestionId, status) => { try { await matchingAPI.updateSuggestionStatus(slug, suggestionId, status); // Reload suggestions await loadSuggestions(); } catch (err) { console.error('Failed to update suggestion status:', err); } }; const handleRunMatching = async () => { try { setRunningMatching(true); await matchingAPI.runMatching(slug); await loadSuggestions(); } catch (err) { console.error('Failed to run matching:', err); } finally { setRunningMatching(false); } }; // Calculate countdown to matching const getCountdown = () => { if (!event?.registrationDeadline) return null; const deadline = new Date(event.registrationDeadline); const now = new Date(); const diff = deadline.getTime() - now.getTime(); if (diff <= 0) return null; const hours = Math.floor(diff / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); return { hours, minutes }; }; if (loading) { return (

Loading...

); } if (error) { return (
{error}
); } const countdown = getCountdown(); const toBeRecorded = suggestions?.toBeRecorded || []; const toRecord = suggestions?.toRecord || []; const matchingRunAt = suggestions?.matchingRunAt; return (
{/* Header with info */}

System automatically pairs people for mutual recording of performances.

{/* Countdown or Status */} {countdown ? (

Matching will run in: {countdown.hours}h {countdown.minutes}min

After registration closes, the system will automatically pair recording partners.

) : matchingRunAt ? (

Matching completed

Last run: {new Date(matchingRunAt).toLocaleString('en-US')}

) : (

Matching has not been run yet

)} {/* My heats to be recorded */}

{toBeRecorded.length === 0 ? (
{myHeats.length === 0 ? "You haven't declared any heats" : 'No suggestions - run matching' }
) : (
{toBeRecorded.map((suggestion) => ( handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.ACCEPTED)} onReject={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.REJECTED)} /> ))}
)}
{/* Heats I need to record */}

Recording others ({toRecord.length})

{toRecord.length === 0 ? (
You have no assigned heats to record
) : (
{toRecord.map((suggestion) => ( handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.ACCEPTED)} onReject={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.REJECTED)} /> ))}
)}
{/* Opt-out toggle */}

If checked, you'll be deprioritized when assigning recording partners.

); }; /** * SuggestionCard - Single suggestion item */ const SuggestionCard = ({ suggestion, type, onAccept, onReject }) => { const heat = suggestion.heat; const recorder = suggestion.recorder; const dancer = heat?.user; const status = suggestion.status; // Format heat info const heatInfo = heat ? `${heat.division?.abbreviation || '?'} ${heat.competitionType?.abbreviation || '?'} H${heat.heatNumber}` : 'Unknown heat'; const person = type === SUGGESTION_TYPE.TO_BE_RECORDED ? recorder : dancer; const personLabel = type === SUGGESTION_TYPE.TO_BE_RECORDED ? 'Recording you:' : 'You record:'; // Status badge const getStatusBadge = () => { switch (status) { case SUGGESTION_STATUS.ACCEPTED: return ( Accepted ); case SUGGESTION_STATUS.REJECTED: return ( Rejected ); case SUGGESTION_STATUS.NOT_FOUND: return ( Not found ); default: return null; } }; // No recorder found if (status === SUGGESTION_STATUS.NOT_FOUND) { return (

{heatInfo}

No available partner found

{getStatusBadge()}
); } return (
{person ? ( ) : (
)}

{heatInfo}

{personLabel} @{person?.username || '?'} {person?.city && ( ({person.city}) )}

{status === SUGGESTION_STATUS.PENDING ? ( <> ) : ( getStatusBadge() )}
); }; export default RecordingTab;