import { useState, useMemo, useEffect, useCallback } from 'react'; import { DebateSetup } from './components/DebateSetup.jsx'; import { SpeakingOrder } from './components/SpeakingOrder.jsx'; import { Scoring } from './components/Scoring.jsx'; import { Scoreboard } from './components/Scoreboard.jsx'; import { useCollection, useBroadcast, usePresence } from '@deplixo/sdk'; function App() { const [activeTab, setActiveTab] = useState('setup'); const [judgeInput, setJudgeInput] = useState(''); const [scoreSyncTick, setScoreSyncTick] = useState(0); const { items: judgeSessions, add: addJudgeSession, update: updateJudgeSession } = useCollection('judge-sessions', { personal: true }); const handleScoreUpdate = useCallback(() => { setScoreSyncTick((prev) => prev + 1); }, []); const { send: broadcastScoreUpdate } = useBroadcast('score-update', handleScoreUpdate); const syncScoreboards = useCallback((payload = {}) => { broadcastScoreUpdate({ type: 'score-changed', updatedAt: Date.now(), ...payload, }); setScoreSyncTick((prev) => prev + 1); }, [broadcastScoreUpdate]); useEffect(() => { if (judgeSessions?.length) { const latest = judgeSessions[judgeSessions.length - 1]; if (latest?.judgeId) { setJudgeInput(latest.judgeId); } } }, [judgeSessions]); const judgeIdentity = useMemo(() => { const value = judgeInput.trim(); return value || 'Anonymous Judge'; }, [judgeInput]); const { users: audienceUsers, update: updateAudiencePresence } = usePresence({ role: 'audience', name: judgeIdentity, activeTab, }); useEffect(() => { updateAudiencePresence({ role: 'audience', name: judgeIdentity, activeTab, updatedAt: Date.now(), }); }, [activeTab, judgeIdentity, updateAudiencePresence]); const audienceCount = audienceUsers?.length || 0; const saveJudgeIdentity = () => { const trimmed = judgeInput.trim(); if (!trimmed) return; const existing = judgeSessions?.[0]; if (existing?.id) { updateJudgeSession(existing.id, { judgeId: trimmed, updatedAt: Date.now() }); } else { addJudgeSession({ judgeId: trimmed, createdAt: Date.now(), updatedAt: Date.now() }); } }; const tabs = [ { id: 'setup', label: '⚙️ Setup', icon: '⚙️' }, { id: 'speaking', label: '🎤 Speaking', icon: '🎤' }, { id: 'scoring', label: '📝 Scoring', icon: '📝' }, { id: 'scoreboard', label: '🏆 Scoreboard', icon: '🏆' }, ]; return (
Structured Debate Tool
Enter a judge name or ID to persist it for this session and attribute score changes correctly.
Connected viewers are tracked in real time as they join or leave the session.