@@ -22,21 +22,14 @@ import {
2222} from 'requests' ;
2323import LinkButton from '../../components/LinkButton' ;
2424import NumberInput from '../../components/NumberInput' ;
25- import TeamDropdown from '../../components/TeamDropdown' ;
2625import MultiButton from '../../components/MultiButton' ;
2726import Checkbox from '../../components/Checkbox' ;
2827import TextInput from '../../components/TextInput' ;
2928import HoldButton from '../../components/HoldButton' ;
29+ import SignIn from '../../components/SignIn' ;
3030import { useStatus } from '../../lib/useStatus' ;
3131import { useQueue } from '../../lib/useQueue' ;
3232import { usePreventUnload } from '../../lib/usePreventUnload' ;
33- import { useLocalStorage } from '../../lib/useLocalStorage' ;
34- import {
35- getOrCreateTabletId ,
36- TABLET_SLOT_STORAGE_KEY ,
37- formatTabletSlotLabel ,
38- isAssignableRobotPosition ,
39- } from '../../lib/tabletId' ;
4033import scheduleFile from '../../assets/matchSchedule.json' ;
4134import { formatMatchTime , gameConfig } from '../../lib/gameConfig' ;
4235
@@ -95,6 +88,16 @@ const matchTimelineSegments: MatchTimelineSegment[] = gameConfig.segments.map(
9588) ;
9689
9790const schedule = scheduleFile as MatchSchedule ;
91+ const scheduleMatchNumbers = Object . keys ( schedule )
92+ . map ( value => Number . parseInt ( value , 10 ) )
93+ . filter ( Number . isFinite )
94+ . sort ( ( a , b ) => a - b ) ;
95+ const firstScheduledMatchNumber = scheduleMatchNumbers [ 0 ] ;
96+
97+ function nextScheduledMatchNumber ( matchNumber : number | undefined ) {
98+ if ( matchNumber == undefined ) return firstScheduledMatchNumber ;
99+ return scheduleMatchNumbers . find ( value => value > matchNumber ) ?? matchNumber + 1 ;
100+ }
98101
99102const autoStartingOptions : Array < { label : string ; value : AutoStartingPosition | null } > = [
100103 { label : 'Left' , value : 'left' } ,
@@ -408,15 +411,13 @@ function buildActionTimelineFromTicks(ticks: ActionTick[]): MatchData['actionTim
408411function MatchApp ( ) {
409412 usePreventUnload ( ) ;
410413 const [ sendQueue , sendAll , queue , sending ] = useQueue ( ) ;
411- const [ tabletId ] = useState ( getOrCreateTabletId ) ;
412- const [ tabletSlot ] = useLocalStorage < RobotPosition | null > (
413- null ,
414- TABLET_SLOT_STORAGE_KEY
415- ) ;
416414 const [ teamNumber , setTeamNumber ] = useState < number > ( ) ;
417- const [ matchNumber , setMatchNumber ] = useState < number > ( ) ;
415+ const [ matchNumber , setMatchNumber ] = useState < number | undefined > (
416+ firstScheduledMatchNumber
417+ ) ;
418418 const [ scouterName , setScouterName ] = useState ( '' ) ;
419419 const [ robotPosition , setRobotPosition ] = useState < RobotPosition > ( ) ;
420+ const [ signedIn , setSignedIn ] = useState ( false ) ;
420421 const [ showCheck , setShowCheck ] = useState ( false ) ;
421422
422423 const [ robotAbsent , setRobotAbsent ] = useState ( false ) ;
@@ -546,12 +547,8 @@ function MatchApp() {
546547 [ actionTicks ]
547548 ) ;
548549
549- const assignedRobotPosition = isAssignableRobotPosition ( tabletSlot )
550- ? tabletSlot
551- : undefined ;
552- const hasTabletAssignment = assignedRobotPosition != undefined ;
553550 const canTrackActions =
554- hasTabletAssignment && isRunning && ! robotAbsent && remainingSec > 0 ;
551+ isRunning && ! robotAbsent && remainingSec > 0 ;
555552 const sectionClass = 'rounded-2xl border border-white/10 bg-[#1d2433]/95 p-4 shadow-lg shadow-black/25' ;
556553 const inputClass =
557554 'mt-1 w-full rounded-lg border border-white/15 bg-[#0f1522] px-3 py-2 text-sm text-white outline-none focus:border-[#48c55c]/60 focus:ring-2 focus:ring-[#48c55c]/30' ;
@@ -567,17 +564,7 @@ function MatchApp() {
567564 ) ;
568565 } ;
569566
570- useStatus ( tabletId , robotPosition , matchNumber , scouterName ) ;
571-
572- useEffect ( ( ) => {
573- if ( ! assignedRobotPosition ) {
574- setRobotPosition ( undefined ) ;
575- setScouterName ( '' ) ;
576- return ;
577- }
578- setRobotPosition ( assignedRobotPosition ) ;
579- setScouterName ( formatTabletSlotLabel ( assignedRobotPosition ) ) ;
580- } , [ assignedRobotPosition ] ) ;
567+ useStatus ( '' , robotPosition , matchNumber , scouterName ) ;
581568
582569 useEffect ( ( ) => {
583570 if ( ! schedule || ! robotPosition || matchNumber == undefined ) {
@@ -977,11 +964,25 @@ function MatchApp() {
977964 setScrubbingTimeline ( false ) ;
978965 } ;
979966
980- const handleStartNewMatch = ( ) => {
981- if ( ! hasTabletAssignment ) {
982- alert ( 'This tablet is unassigned. Open Admin and set a tablet slot first.' ) ;
967+ const handleSignIn = ( ) => {
968+ const trimmedName = scouterName . trim ( ) ;
969+ if ( ! trimmedName ) {
970+ alert ( 'Enter your name before signing in.' ) ;
983971 return ;
984972 }
973+ if ( ! robotPosition ) {
974+ alert ( 'Pick your scout position before signing in.' ) ;
975+ return ;
976+ }
977+
978+ setScouterName ( trimmedName ) ;
979+ setSignedIn ( true ) ;
980+ if ( matchNumber == undefined ) {
981+ setMatchNumber ( firstScheduledMatchNumber ) ;
982+ }
983+ } ;
984+
985+ const handleStartNewMatch = ( ) => {
985986 resetScoutingFields ( ) ;
986987 setIsRunning ( true ) ;
987988 } ;
@@ -992,10 +993,6 @@ function MatchApp() {
992993 } ;
993994
994995 const handlePauseResume = ( ) => {
995- if ( ! hasTabletAssignment ) {
996- alert ( 'This tablet is unassigned. Open Admin and set a tablet slot first.' ) ;
997- return ;
998- }
999996 if ( isRunning ) {
1000997 endActionHold ( 'shoot' ) ;
1001998 endActionHold ( 'pass' ) ;
@@ -1006,8 +1003,14 @@ function MatchApp() {
10061003 } ;
10071004
10081005 const handleSubmit = ( ) => {
1009- if ( robotPosition == undefined || matchNumber == undefined || teamNumber == undefined ) {
1010- alert ( 'Check tablet slot assignment, match number, and team number' ) ;
1006+ if (
1007+ ! signedIn ||
1008+ ! scouterName . trim ( ) ||
1009+ robotPosition == undefined ||
1010+ matchNumber == undefined ||
1011+ teamNumber == undefined
1012+ ) {
1013+ alert ( 'Check sign-in, match number, and scheduled team number.' ) ;
10111014 return ;
10121015 }
10131016 if ( activeHoldsRef . current . shoot || activeHoldsRef . current . pass ) {
@@ -1048,10 +1051,38 @@ function MatchApp() {
10481051 sendQueue ( '/data/match' , data ) ;
10491052 setShowCheck ( true ) ;
10501053 setTimeout ( ( ) => setShowCheck ( false ) , 1800 ) ;
1051- setMatchNumber ( prev => ( prev == undefined ? prev : prev + 1 ) ) ;
1054+ setMatchNumber ( prev => nextScheduledMatchNumber ( prev ) ) ;
10521055 handleResetMatch ( ) ;
10531056 } ;
10541057
1058+ if ( ! signedIn ) {
1059+ return (
1060+ < div className = 'min-h-screen bg-gradient-to-b from-[#151a25] via-[#111722] to-[#0b111a] pb-8 text-sm text-white' >
1061+ < main className = 'mx-auto flex min-h-screen w-full max-w-5xl flex-col items-center justify-center gap-4 px-4 pb-10 pt-5 md:px-6' >
1062+ < section className = { `${ sectionClass } w-full max-w-2xl text-center` } >
1063+ < h1 className = 'text-xl font-semibold text-[#48c55c]' > Match Scouting Sign-In</ h1 >
1064+ < p className = 'mt-2 text-xs text-gray-300' >
1065+ Choose your scout position once, then this app auto-fills team
1066+ numbers from the hardcoded schedule using match number + position.
1067+ </ p >
1068+ </ section >
1069+
1070+ < SignIn
1071+ scouterName = { scouterName }
1072+ onChangeScouterName = { setScouterName }
1073+ robotPosition = { robotPosition }
1074+ onChangeRobotPosition = { setRobotPosition }
1075+ onSubmit = { handleSignIn }
1076+ />
1077+
1078+ < LinkButton link = '/' className = 'snap-none' >
1079+ < MaterialSymbol icon = 'home' size = { 38 } fill grade = { 200 } color = 'green' />
1080+ </ LinkButton >
1081+ </ main >
1082+ </ div >
1083+ ) ;
1084+ }
1085+
10551086 return (
10561087 < div className = 'min-h-screen bg-gradient-to-b from-[#151a25] via-[#111722] to-[#0b111a] pb-8 text-sm text-white' >
10571088 { showCheck && (
@@ -1070,40 +1101,24 @@ function MatchApp() {
10701101 < div >
10711102 < h1 className = 'text-xl font-semibold text-[#48c55c]' > Match Scouting</ h1 >
10721103 < p className = 'mt-1 text-xs text-gray-400' >
1073- { scouterName || 'Unassigned Tablet ' } { ' ' }
1104+ { scouterName || 'Scout Not Signed In ' } { ' ' }
10741105 { robotPosition ? `(${ robotPosition } )` : '' }
10751106 </ p >
1076- < p className = 'text-[11px] text-gray-500' >
1077- Tablet ID: { tabletId }
1078- { hasTabletAssignment ? ' (assigned)' : ' (no assignment)' }
1079- </ p >
10801107 </ div >
10811108
10821109 < div className = 'flex items-center gap-2' >
1110+ < button
1111+ type = 'button'
1112+ onClick = { ( ) => setSignedIn ( false ) }
1113+ className = 'rounded-lg bg-[#3a4254] px-3 py-2 text-xs font-semibold text-white' >
1114+ Switch Scout
1115+ </ button >
10831116 < LinkButton link = '/' className = 'snap-none' >
10841117 < MaterialSymbol icon = 'home' size = { 38 } fill grade = { 200 } color = 'green' />
10851118 </ LinkButton >
1086- < LinkButton link = '/admin' className = 'snap-none' >
1087- < MaterialSymbol
1088- icon = 'admin_panel_settings'
1089- size = { 36 }
1090- fill
1091- grade = { 200 }
1092- color = 'orange'
1093- />
1094- </ LinkButton >
10951119 </ div >
10961120 </ header >
10971121
1098- { ! hasTabletAssignment && (
1099- < section className = 'rounded-2xl border border-yellow-400/50 bg-yellow-900/30 p-4' >
1100- < p className = 'text-sm text-yellow-100' >
1101- This tablet has no slot assignment. Open Admin on this tablet and set
1102- `This Tablet Slot` before scouting matches.
1103- </ p >
1104- </ section >
1105- ) }
1106-
11071122 < section className = { sectionClass } >
11081123 < h2 className = 'text-base font-semibold text-[#48c55c]' > Overview</ h2 >
11091124 < div className = 'mt-3 grid gap-3 md:grid-cols-2' >
@@ -1120,8 +1135,8 @@ function MatchApp() {
11201135 </ div >
11211136 < div >
11221137 < p className = 'text-xs uppercase tracking-wide text-gray-300' > Team Number</ p >
1123- < div className = 'mt-1 rounded-lg border border-white/10 bg-[#0f1522] px-2 py-2' >
1124- < TeamDropdown value = { teamNumber } onChange = { setTeamNumber } />
1138+ < div className = 'mt-1 rounded-lg border border-white/10 bg-[#0f1522] px-3 py-2 text-sm font-semibold text-white ' >
1139+ { teamNumber ?? 'No scheduled team for selected match/position' }
11251140 </ div >
11261141 </ div >
11271142 </ div >
@@ -1149,7 +1164,8 @@ function MatchApp() {
11491164 < span className = 'ml-1.5' > Robot Absent</ span >
11501165 </ Checkbox >
11511166 < p className = 'text-gray-400' >
1152- Team auto-fills from schedule using match number + tablet slot.
1167+ Team auto-fills from hardcoded schedule using match number + scout
1168+ position.
11531169 </ p >
11541170 </ div >
11551171 </ section >
0 commit comments