import { useState, useEffect, useCallback } from 'react'; interface UserInfo { discordId: string; username: string; avatar: string | null; globalName: string | null; } interface SoundOption { name: string; fileName: string; folder: string; relativePath: string; } interface UserSettingsProps { user: UserInfo; onClose: () => void; onLogout: () => void; } export default function UserSettings({ user, onClose, onLogout }: UserSettingsProps) { const [entranceSound, setEntranceSound] = useState(null); const [exitSound, setExitSound] = useState(null); const [availableSounds, setAvailableSounds] = useState([]); const [search, setSearch] = useState(''); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState<'entrance' | 'exit' | null>(null); const [message, setMessage] = useState<{ text: string; type: 'success' | 'error' } | null>(null); const [activeSection, setActiveSection] = useState<'entrance' | 'exit'>('entrance'); // Fetch current sounds + available sounds useEffect(() => { Promise.all([ fetch('/api/soundboard/user/sounds', { credentials: 'include' }).then(r => r.json()), fetch('/api/soundboard/user/available-sounds').then(r => r.json()), ]) .then(([userSounds, sounds]) => { setEntranceSound(userSounds.entrance ?? null); setExitSound(userSounds.exit ?? null); setAvailableSounds(sounds); setLoading(false); }) .catch(() => { setMessage({ text: 'Fehler beim Laden der Einstellungen', type: 'error' }); setLoading(false); }); }, []); // Close on Escape useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', handler); return () => window.removeEventListener('keydown', handler); }, [onClose]); const showMessage = useCallback((text: string, type: 'success' | 'error') => { setMessage({ text, type }); setTimeout(() => setMessage(null), 3000); }, []); async function setSound(type: 'entrance' | 'exit', fileName: string) { setSaving(type); try { const resp = await fetch(`/api/soundboard/user/${type}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fileName }), credentials: 'include', }); if (resp.ok) { const data = await resp.json(); if (type === 'entrance') setEntranceSound(data.entrance); else setExitSound(data.exit); showMessage(`${type === 'entrance' ? 'Entrance' : 'Exit'}-Sound gesetzt!`, 'success'); } else { const err = await resp.json().catch(() => ({ error: 'Unbekannter Fehler' })); showMessage(err.error || 'Fehler', 'error'); } } catch { showMessage('Verbindungsfehler', 'error'); } setSaving(null); } async function removeSound(type: 'entrance' | 'exit') { setSaving(type); try { const resp = await fetch(`/api/soundboard/user/${type}`, { method: 'DELETE', credentials: 'include', }); if (resp.ok) { if (type === 'entrance') setEntranceSound(null); else setExitSound(null); showMessage(`${type === 'entrance' ? 'Entrance' : 'Exit'}-Sound entfernt`, 'success'); } } catch { showMessage('Verbindungsfehler', 'error'); } setSaving(null); } // Group sounds by folder const folders = new Map(); const q = search.toLowerCase(); for (const s of availableSounds) { if (q && !s.name.toLowerCase().includes(q) && !s.fileName.toLowerCase().includes(q)) continue; const key = s.folder || 'Allgemein'; if (!folders.has(key)) folders.set(key, []); folders.get(key)!.push(s); } // Sort folders alphabetically, "Allgemein" first const sortedFolders = [...folders.entries()].sort(([a], [b]) => { if (a === 'Allgemein') return -1; if (b === 'Allgemein') return 1; return a.localeCompare(b); }); const currentSound = activeSection === 'entrance' ? entranceSound : exitSound; return (
e.stopPropagation()}> {/* Header */}
{user.avatar ? ( ) : (
{user.username[0]?.toUpperCase()}
)}
{user.globalName || user.username} @{user.username}
{/* Message toast */} {message && (
{message.type === 'success' ? '\u2705' : '\u274C'} {message.text}
)} {loading ? (
Lade Einstellungen...
) : (
{/* Section tabs */}
{/* Current sound display */}
Aktuell: {' '} {currentSound ? ( {'\uD83C\uDFB5'} {currentSound} ) : ( Kein Sound gesetzt )}
{/* Search */}
setSearch(e.target.value)} /> {search && ( )}
{/* Sound list */}
{sortedFolders.length === 0 ? (
{search ? 'Keine Treffer' : 'Keine Sounds verfügbar'}
) : ( sortedFolders.map(([folder, sounds]) => (
{'\uD83D\uDCC1'} {folder}
{sounds.map(s => { const isSelected = currentSound === s.relativePath || currentSound === s.fileName; return ( ); })}
)) )}
)}
); }