'use client'; import { useEffect, useState } from 'react'; import { useParams, useRouter } from 'next/navigation'; import WaveformEditor from '@/components/WaveformEditor'; interface Song { id: number; title: string; artist: string; filename: string; } interface SpecialSong { id: number; songId: number; startTime: number; order: number | null; song: Song; } interface Special { id: number; name: string; subtitle?: string; maxAttempts: number; unlockSteps: string; songs: SpecialSong[]; } export default function SpecialEditorPage() { const params = useParams(); const router = useRouter(); const specialId = params.id as string; const [special, setSpecial] = useState(null); const [selectedSongId, setSelectedSongId] = useState(null); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [pendingStartTime, setPendingStartTime] = useState(null); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); useEffect(() => { fetchSpecial(); }, [specialId]); const fetchSpecial = async () => { try { const res = await fetch(`/api/specials/${specialId}`); if (res.ok) { const data = await res.json(); setSpecial(data); if (data.songs.length > 0) { setSelectedSongId(data.songs[0].songId); // Initialize pendingStartTime with the current startTime of the first song setPendingStartTime(data.songs[0].startTime); } } } catch (error) { console.error('Error fetching special:', error); } finally { setLoading(false); } }; const handleStartTimeChange = (newStartTime: number) => { setPendingStartTime(newStartTime); setHasUnsavedChanges(true); }; const handleSave = async () => { if (!special || !selectedSongId || pendingStartTime === null) return; setSaving(true); try { const res = await fetch(`/api/specials/${specialId}/songs`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ songId: selectedSongId, startTime: pendingStartTime }) }); if (res.ok) { // Update local state setSpecial(prev => { if (!prev) return prev; return { ...prev, songs: prev.songs.map(ss => ss.songId === selectedSongId ? { ...ss, startTime: pendingStartTime } : ss ) }; }); setHasUnsavedChanges(false); setPendingStartTime(null); // Reset pending state after saving } } catch (error) { console.error('Error updating start time:', error); } finally { setSaving(false); } }; if (loading) { return (

Loading...

); } if (!special) { return (

Special not found

); } const selectedSpecialSong = special.songs.find(ss => ss.songId === selectedSongId); const unlockSteps = JSON.parse(special.unlockSteps); const totalDuration = unlockSteps[unlockSteps.length - 1]; return (

Edit Special: {special.name}

{special.subtitle && (

{special.subtitle}

)}

Max Attempts: {special.maxAttempts} | Puzzle Duration: {totalDuration}s

{special.songs.length === 0 ? (

No songs assigned to this special yet.

Go back to the admin dashboard to add songs to this special.

) : (

Select Song to Curate

{special.songs.map(ss => (
setSelectedSongId(ss.songId)} style={{ padding: '1rem', background: selectedSongId === ss.songId ? '#4f46e5' : '#f3f4f6', color: selectedSongId === ss.songId ? 'white' : 'black', borderRadius: '0.5rem', cursor: 'pointer', border: selectedSongId === ss.songId ? '2px solid #4f46e5' : '2px solid transparent' }} >
{ss.song.title}
{ss.song.artist}
Start: {ss.startTime}s
))}
{selectedSpecialSong && (

Curate: {selectedSpecialSong.song.title}

Click on the waveform to select where the puzzle should start. The highlighted region shows what players will hear.

)}
)}
); }