From d0be8e4fc68247eb4d48ce0fa90f5e7e43104b43 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Thu, 22 Jan 2026 22:13:00 +0200 Subject: [PATCH 01/23] Yoni - basic shift tab --- .../frontend/src/scouter/pages/ScoutMatch.tsx | 34 +++++++++++-------- .../src/scouter/pages/tabs/ShiftTab.tsx | 31 +++++++++++++++++ 2 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx diff --git a/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx b/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx index 0605b8a..61a09fd 100644 --- a/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx +++ b/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx @@ -9,10 +9,16 @@ import { useState, } from "react"; import { defaultScoutForm, type ScoutingForm } from "@repo/scouting_types"; +import { ShiftTab } from "./tabs/ShiftTab"; + +export interface TabProps { + setForm: Dispatch>; + alliance: "red" | "blue"; +} interface Tab { name: string; - Component: FC<{ setForm: Dispatch> }>; + Component: FC; } const TABS: Tab[] = [ { @@ -24,25 +30,21 @@ const TABS: Tab[] = [ name: "Trans", Component: () =>
Transition Content
, }, - { - name: "Tele", - Component: () =>
Teleop Content
, - }, { name: "Shift1", - Component: () =>
Shift1 Content
, + Component: (props) => , }, { name: "Shift2", - Component: () =>
Shift2 Content
, + Component: (props) => , }, { name: "Shift3", - Component: () =>
Shift3 Content
, + Component: (props) => , }, { name: "Shift4", - Component: () =>
Shift4 Content
, + Component: (props) => , }, { name: "Endgame", @@ -149,16 +151,20 @@ export const ScoutMatch: FC = () => { className="max-h-screen bg-black p-4 md:p-6 flex items-center justify-center force-landscape" > -
+ rounded-2xl shadow-[0_0_30px_rgba(34,197,94,0.3)] overflow-hidden h-[90vh] relative" + >
-
- + animate-in fade-in slide-in-from-right-4 duration-300" + > +
diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx new file mode 100644 index 0000000..66189f6 --- /dev/null +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -0,0 +1,31 @@ +// בס"ד + +import { useState, type FC } from "react"; +import type { TabProps } from "../ScoutMatch"; +import { ScoreMap } from "../../components/ScoreMap"; +import type { Point } from "@repo/scouting_types"; + +type Alliance = "red" | "blue"; + +interface ShiftTabProps extends TabProps { + tabIndex: number; +} + +export const ShiftTab: FC = ({ + setForm, + tabIndex, + alliance, +}) => { + const [mapPosition, setMapPosition] = useState(); + const [mapZone, setMapZone] = useState(alliance); + return ( + <> + + + ); +}; From 63a7c5fbb27ec1c6de434ca934bf71d77c6176d2 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Thu, 22 Jan 2026 22:22:07 +0200 Subject: [PATCH 02/23] Yoni - fixed image scaling issue --- .../src/scouter/components/ScoreMap.tsx | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx index ca055bc..39de16d 100644 --- a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx +++ b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx @@ -58,20 +58,17 @@ export const ScoreMap: FC = ({ }; return ( -
{ - setHolding(true); - }} - onTouchEnd={() => { - setHolding(false); - }} - > +
{ + setHolding(true); + }} + onTouchEnd={() => { + setHolding(false); + }} + className="h-full block select-none" alt="Game Map" draggable={false} /> From 1d25cfe6fdee455b6f75decc16a995e3b869abc7 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Thu, 22 Jan 2026 22:26:16 +0200 Subject: [PATCH 03/23] Yoni - better styling --- apps/scouting/frontend/src/scouter/components/ScoreMap.tsx | 2 +- apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx index 39de16d..2ab213d 100644 --- a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx +++ b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx @@ -58,7 +58,7 @@ export const ScoreMap: FC = ({ }; return ( -
+
= ({ const [mapPosition, setMapPosition] = useState(); const [mapZone, setMapZone] = useState(alliance); return ( - <> +
- + +
); }; From f87ef2c4ba8252b2ff9ffeb2a0ba77a77fab1478 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Thu, 22 Jan 2026 22:31:06 +0200 Subject: [PATCH 04/23] Yoni - tweaked stopwatch --- .../frontend/src/components/stopwatch.tsx | 37 +++++-------------- .../src/scouter/pages/tabs/ShiftTab.tsx | 3 +- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/apps/scouting/frontend/src/components/stopwatch.tsx b/apps/scouting/frontend/src/components/stopwatch.tsx index 7032e48..50a2a45 100644 --- a/apps/scouting/frontend/src/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/components/stopwatch.tsx @@ -11,7 +11,8 @@ const MILLLISECONDS_IN_A_SECOND = 1000; const SECOND_IN_A_MINUTE = 60; const INITIAL_TIME_MILLISECONDS = 0; const CYCLE_TIME_MILLISECONDS = 10; -const DECIMAL_PLACES = 2 +const DECIMAL_PLACES = 2; +const DECIMAL_PLACES_MILLISECONDS = 3; const Stopwatch: React.FC = () => { const [isRunning, setIsRunning] = useState(false); @@ -30,13 +31,6 @@ const Stopwatch: React.FC = () => { setIsRunning(false); } - function calculateMinutes() { - return Math.floor( - (elapsedTime / (MILLLISECONDS_IN_A_SECOND * SECOND_IN_A_MINUTE)) % - SECOND_IN_A_MINUTE, - ); - } - function calculateSeconds() { return Math.floor( (elapsedTime / MILLLISECONDS_IN_A_SECOND) % SECOND_IN_A_MINUTE, @@ -44,9 +38,7 @@ const Stopwatch: React.FC = () => { } function calculateMilliSeconds() { - return Math.floor( - (elapsedTime % MILLLISECONDS_IN_A_SECOND) / SECOND_IN_A_MINUTE, - ); + return Math.floor(elapsedTime % MILLLISECONDS_IN_A_SECOND); } const getCurrentRelativeTime = () => { @@ -98,18 +90,20 @@ const Stopwatch: React.FC = () => { }, [cycleTimesInMilliseconds]); function formatTime() { - const minutes = String(calculateMinutes()).padStart(DECIMAL_PLACES, "0"); const seconds = String(calculateSeconds()).padStart(DECIMAL_PLACES, "0"); - const milliseconds = String(calculateMilliSeconds()).padStart(DECIMAL_PLACES, "0"); - return `${minutes}:${seconds}:${milliseconds}`; + const milliseconds = String(calculateMilliSeconds()).padStart( + DECIMAL_PLACES_MILLISECONDS, + "0", + ); + return `${seconds}:${milliseconds}`; } return (
{ > {formatTime()}
- -
); }; diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx index 2a932cd..7817e01 100644 --- a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -4,6 +4,7 @@ import { useState, type FC } from "react"; import type { TabProps } from "../ScoutMatch"; import { ScoreMap } from "../../components/ScoreMap"; import type { Point } from "@repo/scouting_types"; +import Stopwatch from "../../../components/stopwatch"; type Alliance = "red" | "blue"; @@ -26,7 +27,7 @@ export const ShiftTab: FC = ({ alliance={alliance} mapZone={mapZone} /> - +
); }; From 1667b72d17e891638404e5af92e3d32bef1417f4 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Thu, 22 Jan 2026 22:46:45 +0200 Subject: [PATCH 05/23] Yoni - changed props of stopwatch --- .../frontend/src/components/stopwatch.tsx | 42 +++++++++---------- .../src/scouter/pages/tabs/ShiftTab.tsx | 10 ++++- packages/scouting_types/rebuilt/Interval.ts | 1 + packages/scouting_types/rebuilt/ShootEvent.ts | 2 + packages/scouting_types/rebuilt/index.ts | 1 + 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/apps/scouting/frontend/src/components/stopwatch.tsx b/apps/scouting/frontend/src/components/stopwatch.tsx index 50a2a45..3b96b36 100644 --- a/apps/scouting/frontend/src/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/components/stopwatch.tsx @@ -1,11 +1,12 @@ // בס"ד import type React from "react"; -import { useEffect, useRef, useState } from "react"; - -interface CycleStopwatchCounter { - startCycleTime: number; - endCycleTimer: number; -} +import { + useEffect, + useRef, + useState, + type Dispatch, +} from "react"; +import type { Interval } from "@repo/scouting_types"; const MILLLISECONDS_IN_A_SECOND = 1000; const SECOND_IN_A_MINUTE = 60; @@ -14,15 +15,19 @@ const CYCLE_TIME_MILLISECONDS = 10; const DECIMAL_PLACES = 2; const DECIMAL_PLACES_MILLISECONDS = 3; -const Stopwatch: React.FC = () => { +interface StopwatchProps { + setCycleTimesInSeconds: Dispatch<(prev: Interval[]) => Interval[]>; + originTime: number; +} + +const Stopwatch: React.FC = ({ + setCycleTimesInSeconds, + originTime, +}) => { const [isRunning, setIsRunning] = useState(false); const [elapsedTime, setElapsedTime] = useState(INITIAL_TIME_MILLISECONDS); - const [cycleTimesInMilliseconds, setCycleTimesInMilliseconds] = useState< - CycleStopwatchCounter[] - >([]); const startTimeRef = useRef(INITIAL_TIME_MILLISECONDS); - const originRef = useRef(null); const startCurrentCycleTime = useRef(INITIAL_TIME_MILLISECONDS); @@ -42,8 +47,7 @@ const Stopwatch: React.FC = () => { } const getCurrentRelativeTime = () => { - originRef.current ??= Date.now(); - return Date.now() - originRef.current; + return Date.now() - originTime; }; useEffect(() => { @@ -75,20 +79,16 @@ const Stopwatch: React.FC = () => { return; } - const cycleStopwatchCounter: CycleStopwatchCounter = { - startCycleTime: startCurrentCycleTime.current, - endCycleTimer: getCurrentRelativeTime(), + const cycleStopwatchCounter: Interval = { + start: startCurrentCycleTime.current * MILLLISECONDS_IN_A_SECOND, + end: getCurrentRelativeTime() * MILLLISECONDS_IN_A_SECOND, }; - setCycleTimesInMilliseconds((prev) => [...prev, cycleStopwatchCounter]); + setCycleTimesInSeconds((prev) => [...prev, cycleStopwatchCounter]); setIsRunning(false); reset(); } - useEffect(() => { - console.log(cycleTimesInMilliseconds); - }, [cycleTimesInMilliseconds]); - function formatTime() { const seconds = String(calculateSeconds()).padStart(DECIMAL_PLACES, "0"); const milliseconds = String(calculateMilliSeconds()).padStart( diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx index 7817e01..4f0ade8 100644 --- a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -3,7 +3,7 @@ import { useState, type FC } from "react"; import type { TabProps } from "../ScoutMatch"; import { ScoreMap } from "../../components/ScoreMap"; -import type { Point } from "@repo/scouting_types"; +import type { Point, ScoutingForm, ShootEvent } from "@repo/scouting_types"; import Stopwatch from "../../../components/stopwatch"; type Alliance = "red" | "blue"; @@ -19,6 +19,7 @@ export const ShiftTab: FC = ({ }) => { const [mapPosition, setMapPosition] = useState(); const [mapZone, setMapZone] = useState(alliance); + return (
= ({ alliance={alliance} mapZone={mapZone} /> - + { + console.log("not implemented"); + }} + originTime={0} + />
); }; diff --git a/packages/scouting_types/rebuilt/Interval.ts b/packages/scouting_types/rebuilt/Interval.ts index 89de702..c29d63a 100644 --- a/packages/scouting_types/rebuilt/Interval.ts +++ b/packages/scouting_types/rebuilt/Interval.ts @@ -6,3 +6,4 @@ export const maxInterval: t.TypeOf = { start: 0, end: 255 }; +export type Interval = t.TypeOf diff --git a/packages/scouting_types/rebuilt/ShootEvent.ts b/packages/scouting_types/rebuilt/ShootEvent.ts index 06f4f11..1ff7d0e 100644 --- a/packages/scouting_types/rebuilt/ShootEvent.ts +++ b/packages/scouting_types/rebuilt/ShootEvent.ts @@ -9,3 +9,5 @@ export const shootEventCodec = t.type({ interval: intervalCodec, startPosition: point, }); + +export type ShootEvent = t.TypeOf; diff --git a/packages/scouting_types/rebuilt/index.ts b/packages/scouting_types/rebuilt/index.ts index 848b208..182a9e3 100644 --- a/packages/scouting_types/rebuilt/index.ts +++ b/packages/scouting_types/rebuilt/index.ts @@ -2,3 +2,4 @@ export * from "./GameData"; export * from "./ScoutingForm"; export * from "./ShootEvent"; +export * from "./Interval"; From 3db316fa0021e00c4085758086afb4be83d5558c Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Thu, 22 Jan 2026 22:52:55 +0200 Subject: [PATCH 06/23] Yoni - added disable to stopwatch --- apps/scouting/frontend/src/components/stopwatch.tsx | 13 +++++-------- .../frontend/src/scouter/pages/tabs/ShiftTab.tsx | 7 ++++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/scouting/frontend/src/components/stopwatch.tsx b/apps/scouting/frontend/src/components/stopwatch.tsx index 3b96b36..decc6a4 100644 --- a/apps/scouting/frontend/src/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/components/stopwatch.tsx @@ -1,11 +1,6 @@ // בס"ד import type React from "react"; -import { - useEffect, - useRef, - useState, - type Dispatch, -} from "react"; +import { useEffect, useRef, useState, type Dispatch } from "react"; import type { Interval } from "@repo/scouting_types"; const MILLLISECONDS_IN_A_SECOND = 1000; @@ -18,11 +13,13 @@ const DECIMAL_PLACES_MILLISECONDS = 3; interface StopwatchProps { setCycleTimesInSeconds: Dispatch<(prev: Interval[]) => Interval[]>; originTime: number; + disabled: boolean; } const Stopwatch: React.FC = ({ setCycleTimesInSeconds, originTime, + disabled, }) => { const [isRunning, setIsRunning] = useState(false); const [elapsedTime, setElapsedTime] = useState(INITIAL_TIME_MILLISECONDS); @@ -64,7 +61,7 @@ const Stopwatch: React.FC = ({ }, [isRunning]); function start() { - if (isRunning) { + if (isRunning || disabled) { return; } const relativeTime = getCurrentRelativeTime(); @@ -104,7 +101,7 @@ const Stopwatch: React.FC = ({ className={` select-none cursor-pointer rounded-2xl px-4 py-2 text-2xl font-mono font-semibold shadow-lg transition-all duration-150 - ${isRunning ? "bg-emerald-500 text-white scale-95" : "bg-slate-800 text-emerald-400 hover:bg-slate-700"} + ${disabled ? "bg-slate-800 text-slate-900" : isRunning ? "bg-emerald-500 text-white scale-95" : "bg-slate-800 text-emerald-400 hover:bg-slate-700"} `} onMouseDown={start} onMouseUp={stop} diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx index 4f0ade8..fc24296 100644 --- a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -3,7 +3,7 @@ import { useState, type FC } from "react"; import type { TabProps } from "../ScoutMatch"; import { ScoreMap } from "../../components/ScoreMap"; -import type { Point, ScoutingForm, ShootEvent } from "@repo/scouting_types"; +import type { Point } from "@repo/scouting_types"; import Stopwatch from "../../../components/stopwatch"; type Alliance = "red" | "blue"; @@ -29,10 +29,11 @@ export const ShiftTab: FC = ({ mapZone={mapZone} /> { - console.log("not implemented"); + setCycleTimesInSeconds={(handler) => { + console.log(""); }} originTime={0} + disabled={mapPosition === undefined} />
); From f83659214569874a06b5121d3de74c20a2f006a3 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Fri, 23 Jan 2026 13:21:35 +0200 Subject: [PATCH 07/23] Yoni - got correct times on form --- .../frontend/src/components/stopwatch.tsx | 11 ++++++----- .../frontend/src/scouter/pages/ScoutMatch.tsx | 19 ++++++++++++++++--- .../src/scouter/pages/tabs/ShiftTab.tsx | 14 +++++++++++--- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/apps/scouting/frontend/src/components/stopwatch.tsx b/apps/scouting/frontend/src/components/stopwatch.tsx index decc6a4..fde0e7f 100644 --- a/apps/scouting/frontend/src/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/components/stopwatch.tsx @@ -11,13 +11,13 @@ const DECIMAL_PLACES = 2; const DECIMAL_PLACES_MILLISECONDS = 3; interface StopwatchProps { - setCycleTimesInSeconds: Dispatch<(prev: Interval[]) => Interval[]>; + addCycleTimeSeconds: Dispatch; originTime: number; disabled: boolean; } const Stopwatch: React.FC = ({ - setCycleTimesInSeconds, + addCycleTimeSeconds, originTime, disabled, }) => { @@ -77,10 +77,11 @@ const Stopwatch: React.FC = ({ } const cycleStopwatchCounter: Interval = { - start: startCurrentCycleTime.current * MILLLISECONDS_IN_A_SECOND, - end: getCurrentRelativeTime() * MILLLISECONDS_IN_A_SECOND, + start: startCurrentCycleTime.current, + end: getCurrentRelativeTime(), }; - setCycleTimesInSeconds((prev) => [...prev, cycleStopwatchCounter]); + + addCycleTimeSeconds(cycleStopwatchCounter); setIsRunning(false); reset(); diff --git a/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx b/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx index 61a09fd..62c0637 100644 --- a/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx +++ b/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx @@ -10,10 +10,12 @@ import { } from "react"; import { defaultScoutForm, type ScoutingForm } from "@repo/scouting_types"; import { ShiftTab } from "./tabs/ShiftTab"; +import { useLocalStorage } from "@repo/local_storage_hook"; export interface TabProps { setForm: Dispatch>; alliance: "red" | "blue"; + originTime: number; } interface Tab { @@ -135,12 +137,19 @@ const SideBar: FC = ({ setActiveTab, activeTabIndex }) => { ); }; -const createNewScoutingForm = () => structuredClone(defaultScoutForm); +const createNewScoutingForm = (): ScoutingForm => + JSON.parse(JSON.stringify(defaultScoutForm)); export const ScoutMatch: FC = () => { - const [scoutingForm, setScoutingForm] = useState(createNewScoutingForm()); + const [scoutingForm, setScoutingForm] = useLocalStorage( + "form", + createNewScoutingForm(), + ); const [activeTabIndex, setActiveTab] = useState(STARTING_TAB_INDEX); + const originTime = useMemo(() => Date.now(), []); + console.log(originTime); + const CurrentTab = useMemo( () => TABS[activeTabIndex].Component, [activeTabIndex], @@ -164,7 +173,11 @@ export const ScoutMatch: FC = () => { bg-black/40 rounded-xl p-6 border border-green-500/20 shadow-inner animate-in fade-in slide-in-from-right-4 duration-300" > - +
diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx index fc24296..5eb68b5 100644 --- a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -16,6 +16,7 @@ export const ShiftTab: FC = ({ setForm, tabIndex, alliance, + originTime, }) => { const [mapPosition, setMapPosition] = useState(); const [mapZone, setMapZone] = useState(alliance); @@ -29,10 +30,17 @@ export const ShiftTab: FC = ({ mapZone={mapZone} /> { - console.log(""); + addCycleTimeSeconds={(cycle) => { + setForm((prevForm) => { + const prevEvents = prevForm.tele.shifts[tabIndex].shootEvents; + prevEvents.push({ + interval: cycle, + startPosition: mapPosition ?? { x: 0, y: 0 }, + }); + return prevForm; + }); }} - originTime={0} + originTime={originTime} disabled={mapPosition === undefined} /> From d3f89cebcd8db9b6b52d54495da6c0a0c434e120 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Fri, 23 Jan 2026 13:22:35 +0200 Subject: [PATCH 08/23] Yoni - truncated position --- apps/scouting/frontend/src/scouter/components/ScoreMap.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx index 2ab213d..56a6e84 100644 --- a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx +++ b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx @@ -36,7 +36,7 @@ const getRobotPosition = (touch: Touch, bound: DOMRect) => { bound.bottom - dotRadius - bound.top, Math.max(y, dotRadius), ); - return { x: boundedX, y: boundedY }; + return { x: Math.round(boundedX), y: Math.round(boundedY) }; }; export const ScoreMap: FC = ({ From 939c0331f6baa1cb8b5da8b6cc500192e2a2dc77 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Fri, 23 Jan 2026 14:09:59 +0200 Subject: [PATCH 09/23] Yoni - truly standardized the allmap position --- .../src/scouter/components/ScoreMap.tsx | 65 ++++++++++++++++--- .../frontend/src/scouter/pages/ScoutMatch.tsx | 2 +- .../src/scouter/pages/tabs/ShiftTab.tsx | 4 +- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx index 56a6e84..66241a2 100644 --- a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx +++ b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx @@ -9,14 +9,44 @@ import { type Touch, } from "react"; import type { Point } from "@repo/scouting_types"; +import { pipe } from "fp-ts/lib/function"; + +type Alliance = "red" | "blue"; interface ScoreMapProps { currentPoint?: Point; setPosition: Dispatch>; - alliance: "red" | "blue"; - mapZone: "red" | "blue"; + alliance: Alliance; + mapZone: Alliance; } +const ALLIANCE_ZONE_WIDTH_PIXELS = 395; +const TWO_THIRDS_WIDTH_PIXELS = 1010; +const HEIGHT_PIXELS = 652; +const alliancizePosition = ( + alliance: Alliance, + position: Point, + bounds: Point, +): Point => { + if (alliance === "red") { + return position; + } + + return { x: bounds.x - position.x, y: bounds.y - position.y }; +}; + +const otherZone = (point: Point) => { + return { + ...point, + x: point.x + ALLIANCE_ZONE_WIDTH_PIXELS, + }; +}; + +const normalizePosition = (point: Point, bounds: Point) => ({ + x: (point.x * TWO_THIRDS_WIDTH_PIXELS) / bounds.x, + y: (point.y * HEIGHT_PIXELS) / bounds.y, +}); + const dotRadius = 10; const radiusToDiameterRatio = 2; const dotDiameter = dotRadius * radiusToDiameterRatio; @@ -36,7 +66,8 @@ const getRobotPosition = (touch: Touch, bound: DOMRect) => { bound.bottom - dotRadius - bound.top, Math.max(y, dotRadius), ); - return { x: Math.round(boundedX), y: Math.round(boundedY) }; + + return { x: boundedX, y: boundedY }; }; export const ScoreMap: FC = ({ @@ -46,15 +77,33 @@ export const ScoreMap: FC = ({ mapZone, }) => { const [isHolding, setHolding] = useState(false); + + const [mapPoint, setMapPoint] = useState(currentPoint); + const handleMapClick = (event: TouchEvent) => { if (!isHolding) { return; } const rect = event.currentTarget.getBoundingClientRect(); - const touch = event.touches[firstTouchIndex]; - setPosition(getRobotPosition(touch, rect)); + const dotPoint = getRobotPosition(touch, rect); + + setMapPoint(dotPoint); + + setPosition( + pipe( + dotPoint, + (point) => + alliancizePosition(alliance, point, { + x: rect.width, + y: rect.height, + }), + (point) => normalizePosition(point, { x: rect.width, y: rect.height }), + (point) => (alliance === mapZone ? point : otherZone(point)), + (point) => ({ x: Math.round(point.x), y: Math.round(point.y) }), + ), + ); }; return ( @@ -73,12 +122,12 @@ export const ScoreMap: FC = ({ draggable={false} /> - {currentPoint && ( + {mapPoint && (
{ >
diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx index 5eb68b5..1e3d2fe 100644 --- a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -6,8 +6,6 @@ import { ScoreMap } from "../../components/ScoreMap"; import type { Point } from "@repo/scouting_types"; import Stopwatch from "../../../components/stopwatch"; -type Alliance = "red" | "blue"; - interface ShiftTabProps extends TabProps { tabIndex: number; } @@ -19,7 +17,7 @@ export const ShiftTab: FC = ({ originTime, }) => { const [mapPosition, setMapPosition] = useState(); - const [mapZone, setMapZone] = useState(alliance); + const [mapZone, setMapZone] = useState<"red" | "blue">(alliance); return (
From 233b76e599a1c32ec3ee41663c0e8e773f1a2452 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Fri, 23 Jan 2026 14:12:00 +0200 Subject: [PATCH 10/23] Yoni - simplified logic --- .../src/scouter/components/ScoreMap.tsx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx index 66241a2..38b3303 100644 --- a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx +++ b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx @@ -23,16 +23,15 @@ interface ScoreMapProps { const ALLIANCE_ZONE_WIDTH_PIXELS = 395; const TWO_THIRDS_WIDTH_PIXELS = 1010; const HEIGHT_PIXELS = 652; -const alliancizePosition = ( - alliance: Alliance, - position: Point, - bounds: Point, -): Point => { +const alliancizePosition = (alliance: Alliance, position: Point): Point => { if (alliance === "red") { return position; } - return { x: bounds.x - position.x, y: bounds.y - position.y }; + return { + x: TWO_THIRDS_WIDTH_PIXELS - position.x, + y: HEIGHT_PIXELS - position.y, + }; }; const otherZone = (point: Point) => { @@ -94,12 +93,8 @@ export const ScoreMap: FC = ({ setPosition( pipe( dotPoint, - (point) => - alliancizePosition(alliance, point, { - x: rect.width, - y: rect.height, - }), (point) => normalizePosition(point, { x: rect.width, y: rect.height }), + (point) => alliancizePosition(alliance, point), (point) => (alliance === mapZone ? point : otherZone(point)), (point) => ({ x: Math.round(point.x), y: Math.round(point.y) }), ), From 7bc901a41c9bbf7de27e3e0a8a4eba7d18bf6fc8 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Fri, 23 Jan 2026 15:47:55 +0200 Subject: [PATCH 11/23] Yoni - added side button and changed basic css (! changed button css !) --- .../frontend/src/components/stopwatch.tsx | 4 +-- apps/scouting/frontend/src/index.css | 3 +- .../src/scouter/pages/tabs/ShiftTab.tsx | 36 ++++++++++++------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/apps/scouting/frontend/src/components/stopwatch.tsx b/apps/scouting/frontend/src/components/stopwatch.tsx index fde0e7f..b33f176 100644 --- a/apps/scouting/frontend/src/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/components/stopwatch.tsx @@ -100,8 +100,8 @@ const Stopwatch: React.FC = ({
= ({ alliance={alliance} mapZone={mapZone} /> - { - setForm((prevForm) => { - const prevEvents = prevForm.tele.shifts[tabIndex].shootEvents; - prevEvents.push({ - interval: cycle, - startPosition: mapPosition ?? { x: 0, y: 0 }, +
+ { + setForm((prevForm) => { + const prevEvents = prevForm.tele.shifts[tabIndex].shootEvents; + prevEvents.push({ + interval: cycle, + startPosition: mapPosition ?? { x: 0, y: 0 }, + }); + return prevForm; }); - return prevForm; - }); - }} - originTime={originTime} - disabled={mapPosition === undefined} - /> + }} + originTime={originTime} + disabled={mapPosition === undefined} + /> + +
); }; From e2f21cbfedf50be6fd2f10ca9de4eaf35a90df29 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Fri, 23 Jan 2026 16:12:13 +0200 Subject: [PATCH 12/23] Yoni - added hidden color divs --- apps/scouting/frontend/src/scouter/components/ScoreMap.tsx | 1 + apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx index 38b3303..80b46c1 100644 --- a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx +++ b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx @@ -7,6 +7,7 @@ import { useState, type TouchEvent, type Touch, + useEffect, } from "react"; import type { Point } from "@repo/scouting_types"; import { pipe } from "fp-ts/lib/function"; diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx index ee3679f..4fdc3f2 100644 --- a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -42,8 +42,10 @@ export const ShiftTab: FC = ({ originTime={originTime} disabled={mapPosition === undefined} /> +
+
+ +
+
+ +
+
+ ); }; diff --git a/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx b/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx index ff77457..8ecdd75 100644 --- a/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx +++ b/apps/scouting/frontend/src/scouter/pages/ScoutMatch.tsx @@ -14,6 +14,7 @@ import { useLocalStorage } from "@repo/local_storage_hook"; export interface TabProps { setForm: Dispatch>; + currentForm: ScoutingForm; alliance: "red" | "blue"; originTime: number; } @@ -112,6 +113,7 @@ const SideBar: FC = ({ setActiveTab, activeTabIndex }) => { className={` shrink-0 flex w-full py-3 text-sm font-bold rounded-xl transition-all duration-300 text-left relative overflow-hidden group + px-2 ${ activeTabIndex === index ? "highlighted-tab" @@ -175,8 +177,9 @@ export const ScoutMatch: FC = () => { >
diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx index 4fdc3f2..153dcfc 100644 --- a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -5,6 +5,7 @@ import type { TabProps } from "../ScoutMatch"; import { ScoreMap } from "../../components/ScoreMap"; import type { Point } from "@repo/scouting_types"; import Stopwatch from "../../../components/stopwatch"; +import { MovementForm } from "../../components/MovementForm"; interface ShiftTabProps extends TabProps { tabIndex: number; @@ -15,6 +16,7 @@ export const ShiftTab: FC = ({ tabIndex, alliance, originTime, + currentForm, }) => { const [mapPosition, setMapPosition] = useState(); const [mapZone, setMapZone] = useState<"red" | "blue">(alliance); @@ -42,10 +44,19 @@ export const ShiftTab: FC = ({ originTime={originTime} disabled={mapPosition === undefined} /> + { + setForm((prevForm) => ({ + ...prevForm, + tele: { ...prevForm.tele, movement: value }, + })); + }} + currentMovement={currentForm.tele.movement} + />
- -
-
- -
+
+
+ + +
); }; diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx index 153dcfc..40b346e 100644 --- a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -56,12 +56,12 @@ export const ShiftTab: FC = ({
From 79b6b7834f3872f30bb0bb523c446299bbbf3d08 Mon Sep 17 00:00:00 2001 From: YoniKiriaty Date: Sun, 25 Jan 2026 18:06:43 +0200 Subject: [PATCH 16/23] Yoni - redesigned movement and changed direction of teleop scouting --- .../frontend/src/components/stopwatch.tsx | 2 +- .../src/scouter/components/MovementForm.tsx | 26 ++----------------- .../src/scouter/pages/tabs/ShiftTab.tsx | 2 +- packages/scouting_types/rebuilt/Movement.ts | 13 +++++++--- packages/scouting_types/rebuilt/Segments.ts | 4 +-- 5 files changed, 16 insertions(+), 31 deletions(-) diff --git a/apps/scouting/frontend/src/components/stopwatch.tsx b/apps/scouting/frontend/src/components/stopwatch.tsx index df955dc..2eb1f56 100644 --- a/apps/scouting/frontend/src/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/components/stopwatch.tsx @@ -97,7 +97,7 @@ const Stopwatch: React.FC = ({ } return ( -
+
= ({ }) => { return (
-
+
- -
); }; diff --git a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx index 40b346e..5aa8683 100644 --- a/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx +++ b/apps/scouting/frontend/src/scouter/pages/tabs/ShiftTab.tsx @@ -56,7 +56,7 @@ export const ShiftTab: FC = ({