From ea27e3cc7625ae5c25ed7a1c37ec43e169822ea6 Mon Sep 17 00:00:00 2001 From: Preston Date: Tue, 22 Sep 2026 10:01:25 -0400 Subject: [PATCH] Move most analysis features out of match planning --- app/page.tsx | 421 +++++++++++++++++++++++++++++---------------------- 1 file changed, 241 insertions(+), 180 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index f887a23..f0586a4 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -330,6 +330,9 @@ function Counter({ export default function Home() { const { data: session, isPending } = authClient.useSession(); const [activeView, setActiveView] = useState('Home'); + const [teamsSection, setTeamsSection] = useState< + 'directory' | 'analysis' | 'picks' | 'review' + >('directory'); const navigationDepthRef = useRef(0); const selectedMatchKeyRef = useRef(''); const [autoFuel, setAutoFuel] = useState(0); @@ -665,7 +668,7 @@ export default function Home() { }, [online, queuedCount, session]); useEffect(() => { - if (activeView !== 'Plan' || !canUseStrategy || !selectedTeam || !online) + if (activeView !== 'Teams' || !canUseStrategy || !selectedTeam || !online) return; fetch(`/api/analysis?team=${selectedTeam}`) .then(async (response) => { @@ -680,7 +683,13 @@ export default function Home() { }, [activeView, online, selectedTeam]); useEffect(() => { - if (activeView !== 'Plan' || !canUseStrategy || !online || !session) return; + if ( + !['Plan', 'Teams'].includes(activeView) || + !canUseStrategy || + !online || + !session + ) + return; fetch('/api/strategy') .then(async (response) => { const result = (await response.json()) as { @@ -703,6 +712,11 @@ export default function Home() { replaceView('Home'); }, [activeView, canUseStrategy, eventPack, isAdmin]); + useEffect(() => { + if (!canUseStrategy && teamsSection !== 'directory') + setTeamsSection('directory'); + }, [canUseStrategy, teamsSection]); + useEffect(() => { if (activeView !== 'Admin' || !isAdmin || !online) return; setEventsLoading(true); @@ -1774,9 +1788,15 @@ export default function Home() { Refresh live data {canUseStrategy && ( - )} {isAdmin && ( @@ -3115,7 +3135,28 @@ export default function Home() { )} - {activeView === 'Teams' && ( + {activeView === 'Teams' && canUseStrategy && ( +
+ {( + [ + ['directory', 'Directory'], + ['analysis', 'Analysis'], + ['picks', 'Pick list'], + ['review', 'Data review'], + ] as const + ).map(([section, label]) => ( + + ))} +
+ )} + {activeView === 'Teams' && teamsSection === 'directory' && (
@@ -3129,7 +3170,7 @@ export default function Home() {
)} - {activeView === 'Plan' && canUseStrategy && ( -
- - {eventPack && ( - + +
+ )} + {activeView === 'Teams' && + canUseStrategy && + teamsSection === 'picks' && ( +
+ {eventPack && ( + + )} +
+ )} + {activeView === 'Teams' && + canUseStrategy && + teamsSection === 'analysis' && ( +
+ - )} - - - - Event ranking workspace -
- {strategyTeams.length} teams - {eventPack && canReopenEntries(eventPack.role) && ( - - )} -
-
- -
- Team - Samples - Median pts - Fuel/cycle - Coverage - {[...strategyTeams] - .sort((a, b) => b.medianPoints - a.medianPoints) - .map((team) => ( - - ))} -
-
-
- - - - {selectedTeam - ? `Team ${selectedTeam} snapshot` - : 'Select a team above'} - - - {analysis?.samples ?? 0} samples - - - -

{analysis?.medianPoints ?? 0}

-

- median observed points -

-
- - {analysis?.medianActiveFuel ?? 0} median - active FUEL - + + Export CSV + + )} +
+ + +
+ Team + Samples + Median pts + Fuel/cycle + Coverage + {[...strategyTeams] + .sort((a, b) => b.medianPoints - a.medianPoints) + .map((team) => ( + + ))} +
+
+
+ + + + {selectedTeam + ? `Team ${selectedTeam} snapshot` + : 'Select a team above'} + + + {analysis?.samples ?? 0} samples + + + +

{analysis?.medianPoints ?? 0}

+

+ median observed points +

+
+ + {analysis?.medianActiveFuel ?? 0} median + active FUEL + + + + {analysis?.medianFuelPerCycle.toFixed(1) ?? '0.0'} + {' '} + FUEL / cycle + + + + {analysis?.pointStdDev.toFixed(1) ?? '0.0'} + {' '} + point deviation + + + + {analysis?.averageDefense.toFixed(1) ?? '0.0'} + {' '} + defense rating + +
+
+
+ + + Reliability and coverage + + - {analysis?.medianFuelPerCycle.toFixed(1) ?? '0.0'} + {Math.round((analysis?.towerSuccessRate ?? 0) * 100)}% {' '} - FUEL / cycle + tower success - {analysis?.pointStdDev.toFixed(1) ?? '0.0'}{' '} - point deviation + + {Math.round((analysis?.disabledRate ?? 0) * 100)}% + {' '} + disabled rate - {analysis?.averageDefense.toFixed(1) ?? '0.0'} + {Math.round((analysis?.coverage ?? 0) * 100)}% {' '} - defense rating + data coverage -
- - - - - Reliability and coverage - - - - - {Math.round((analysis?.towerSuccessRate ?? 0) * 100)}% - {' '} - tower success - - - - {Math.round((analysis?.disabledRate ?? 0) * 100)}% - {' '} - disabled rate - - - - {Math.round((analysis?.coverage ?? 0) * 100)}% - {' '} - data coverage - - - {analysis?.scheduledMatches ?? 0} scheduled - matches - - - - - - - {selectedTeam ? `Team ${selectedTeam} trends` : 'Team trends'} - - Match by match - - - - - - - - Submitted entries - - - {analysis?.entries.map((entry) => ( -
- - {entry.matchKey.split('_').at(-1)?.toUpperCase()} - - - {entry.scoutName} - {entry.reopened ? ' · reopened' : ''} - - {eventPack && - canReopenEntries(eventPack.role) && - !entry.reopened ? ( - - ) : ( - {entry.reopened ? 'Editable' : 'Locked'} - )} -
- ))} - {analysis?.entries.length === 0 && ( -

- No synchronized entries for this team yet. -

- )} -
-
- - )} + + {analysis?.scheduledMatches ?? 0} scheduled + matches + + + + + + + {selectedTeam + ? `Team ${selectedTeam} trends` + : 'Team trends'} + + Match by match + + + + + + + + Submitted entries + + + {analysis?.entries.map((entry) => ( +
+ + {entry.matchKey.split('_').at(-1)?.toUpperCase()} + + + {entry.scoutName} + {entry.reopened ? ' · reopened' : ''} + + {eventPack && + canReopenEntries(eventPack.role) && + !entry.reopened ? ( + + ) : ( + {entry.reopened ? 'Editable' : 'Locked'} + )} +
+ ))} + {analysis?.entries.length === 0 && ( +

+ No synchronized entries for this team yet. +

+ )} +
+
+ + )} {activeView === 'Admin' && isAdmin && adminSection === 'settings' && (