From 20117166544373e78d35c3fec6a3e1376b70af05 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Apr 2026 14:23:44 -0500 Subject: [PATCH 1/6] Simplify home and profile pages --- PLAN.md | 4 + docs/architecture.md | 1 + frontend/src/pages/DashboardPage.tsx | 108 +++++++++++++-------------- frontend/src/pages/ProfilePage.tsx | 10 --- 4 files changed, 59 insertions(+), 64 deletions(-) diff --git a/PLAN.md b/PLAN.md index c60f83e..be548b9 100644 --- a/PLAN.md +++ b/PLAN.md @@ -14,6 +14,10 @@ - Installable React PWA shell with offline-ready game prep scaffolding. - Docker-based local development stack. +## Completed UI Cleanup +- Home page now acts as a lightweight landing page with direct links to Library and Gameday. +- Removed the old game-list-heavy dashboard content that was not useful as a landing surface. + ## Storage Status - Backend media persists in the `backend-media` named Docker volume. diff --git a/docs/architecture.md b/docs/architecture.md index 54b6a43..8271f08 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -15,6 +15,7 @@ Walkup is a baseball walk-up song app with a React PWA frontend and a FastAPI ba - The app uses React Router for navigation and TanStack Query for server state. - TeamSnap data is loaded through the official JavaScript SDK from the browser after the backend provides an access token. - The UI includes player, gameday, and library views for clip management and gameday playback. +- The home page is a lightweight landing page that orients users and links to the Library and Gameday views. - The app is shipped as a PWA with install and offline-prep behavior. ## Backend diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx index c311f09..c7cdcb0 100644 --- a/frontend/src/pages/DashboardPage.tsx +++ b/frontend/src/pages/DashboardPage.tsx @@ -1,20 +1,45 @@ -import { useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; import { useWalkupContext } from "../hooks/useWalkupContext"; -import { formatGameDate, formatGameTitle, formatMemberName } from "../lib/teamsnapHelpers"; export function DashboardPage() { - const navigate = useNavigate(); const walkup = useWalkupContext(); if (!walkup.isTeamSnap) { return ( -
-
-
-

Player flow

-

Sign in with TeamSnap to resolve your player and team context.

-

The player dashboard depends on your TeamSnap user, roster membership, and upcoming games.

+
+
+
+
+
+

Library

+

Manage walkup clips

+

+ Upload audio, trim clips, reorder them, and pin them to players before game day. +

+
+ + Open Library + +
+
+
+
+
+
+
+

Gameday

+

Run the game-day view

+

+ Review lineups, check availability, and play the right walkup clips during the game. +

+
+ + Open Gameday + +
+
+
@@ -23,61 +48,36 @@ export function DashboardPage() { return (
-
-
-

Player dashboard

-

{walkup.nextGame ? formatGameTitle(walkup.nextGame) : "No upcoming game found yet."}

-

- {walkup.currentPlayer - ? `${formatMemberName(walkup.currentPlayer)} is ready for the selected team.` - : "Your TeamSnap user is connected, but no matching player record was found on the selected team."} -

-
-
-
+
-

Next game

- {walkup.nextGame ? ( - <> - {formatGameTitle(walkup.nextGame)} -
{formatGameDate(walkup.nextGame)}
- {walkup.nextGame.locationName ?
{walkup.nextGame.locationName}
: null} -
- +

Library

+

Manage walkup clips

+

+ Upload audio, trim clips, reorder them, and pin them to players before game day. +

+
+ + Open Library +
- - ) : ( -
No upcoming games were returned for this team.
- )}
-
+
-

Upcoming games

-
- {walkup.eventsQuery.isLoading ?
Loading games...
: null} - {walkup.games.slice(0, 8).map((game) => ( -
-
- {formatGameTitle(game)} -
{formatGameDate(game)}
-
- {String(game.id) === String(walkup.nextGame?.id) ? "Next" : "Browse"} +

Gameday

+

Run the game-day view

+

+ Review lineups, check availability, and play the right walkup clips during the game. +

+
+ + Open Gameday +
- ))} - {!walkup.eventsQuery.isLoading && !walkup.games.length ? ( -
No games were returned for the selected team.
- ) : null} -
diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx index 0edb0bf..fd829dd 100644 --- a/frontend/src/pages/ProfilePage.tsx +++ b/frontend/src/pages/ProfilePage.tsx @@ -25,16 +25,6 @@ export function ProfilePage() { return (
-
-
-

Profile

-

{walkup.hasSelectedTeam ? formatTeamLabel(walkup.selectedTeam) : "Choose your team"}

-

- Session details and the selected team live here. The team choice is stored on this device and reused on the - next visit. -

-
-
From 2b462a6bd99ca534106186b3aedc62e82bf12b72 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Apr 2026 14:30:16 -0500 Subject: [PATCH 2/6] Append day labels to game titles --- PLAN.md | 1 + frontend/src/lib/teamsnapHelpers.ts | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/PLAN.md b/PLAN.md index be548b9..8ee0ecd 100644 --- a/PLAN.md +++ b/PLAN.md @@ -17,6 +17,7 @@ ## Completed UI Cleanup - Home page now acts as a lightweight landing page with direct links to Library and Gameday. - Removed the old game-list-heavy dashboard content that was not useful as a landing surface. +- Game titles in the UI now include a day parenthetical such as `(sun 5/3)` wherever the shared formatter is used. ## Storage Status - Backend media persists in the `backend-media` named Docker volume. diff --git a/frontend/src/lib/teamsnapHelpers.ts b/frontend/src/lib/teamsnapHelpers.ts index 1cedc79..16427c5 100644 --- a/frontend/src/lib/teamsnapHelpers.ts +++ b/frontend/src/lib/teamsnapHelpers.ts @@ -75,14 +75,15 @@ export function findCurrentPlayer(externalUserId: string | number | null | undef export function formatGameTitle(game: TeamSnapEvent): string { const name = asDisplayText(game.name); + const dayLabel = formatGameDayLabel(game); if (name) { - return name; + return `${name}${dayLabel}`; } const opponentName = asDisplayText(game.opponentName); if (opponentName) { - return `vs ${opponentName}`; + return `vs ${opponentName}${dayLabel}`; } - return `Game ${game.id}`; + return `Game ${game.id}${dayLabel}`; } export function formatGameDate(game: TeamSnapEvent): string { @@ -98,6 +99,20 @@ export function formatGameDate(game: TeamSnapEvent): string { }); } +function formatGameDayLabel(game: TeamSnapEvent): string { + const date = toDate(game.startDate); + if (!date) { + return ""; + } + + const weekday = date + .toLocaleDateString("en-US", { weekday: "short" }) + .replace(/\./g, "") + .replace(/^./, (character) => character.toUpperCase()); + const monthDay = date.toLocaleDateString("en-US", { month: "numeric", day: "numeric" }); + return ` (${weekday} ${monthDay})`; +} + export function sortGames(events: TeamSnapEvent[]): TeamSnapEvent[] { return [...events] .filter((event) => event.isGame) From 5cc4e3feac2109feef0cebd36bb110c9477ea892 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Apr 2026 14:42:32 -0500 Subject: [PATCH 3/6] Use grouped controls for clip ordering --- frontend/src/pages/LibraryPage.tsx | 6 +++--- frontend/src/styles.css | 7 ------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/frontend/src/pages/LibraryPage.tsx b/frontend/src/pages/LibraryPage.tsx index c878377..5be44f8 100644 --- a/frontend/src/pages/LibraryPage.tsx +++ b/frontend/src/pages/LibraryPage.tsx @@ -980,10 +980,10 @@ function WalkupClipCard({ titleExtras={isHidden ? Hidden : null} actions={ <> -
+
-
From 93875564908173129ea73d8103225d6803f0114a Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Apr 2026 15:09:24 -0500 Subject: [PATCH 5/6] Refine clip editor flow --- frontend/src/pages/LibraryPage.tsx | 46 ++++++++++++++++++------------ frontend/src/styles.css | 35 ++++++++++++----------- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/frontend/src/pages/LibraryPage.tsx b/frontend/src/pages/LibraryPage.tsx index 1bc5ed0..c7ea12b 100644 --- a/frontend/src/pages/LibraryPage.tsx +++ b/frontend/src/pages/LibraryPage.tsx @@ -16,6 +16,7 @@ const MEDIA_ACCEPT = ".mp3,.m4a,.aac,.wav,.ogg,.oga,.flac,.mp4,.m4v,.mov,audio/*,video/*,application/octet-stream"; const DEFAULT_CLIP_LENGTH_MS = 30_000; const END_SHORTCUT_LENGTH_MS = 90_000; +const SAVE_FADE_OUT_MS = 1000; const TRIM_NUDGE_MS = 100; const TRIM_STEP_MS = 100; const TRIM_ZOOM_WINDOW_MS = 3_000; @@ -64,6 +65,7 @@ export function LibraryPage() { const { activeKey: previewKey, currentTimeMs: previewTimeMs, + fadeOutClip, playClip: playClipPreview, stopClip: stopPreview, } = useClipPlayback(); @@ -315,6 +317,7 @@ export function LibraryPage() { teamId={teamId} playerId={playerId} previewTimeMs={previewTimeMs} + fadeOutPreview={fadeOutClip} playPreview={playPreview} onClose={closeCreateWalkupClip} stopPreview={stopPreview} @@ -374,6 +377,7 @@ function WalkupClipModal({ teamId, playerId, previewTimeMs, + fadeOutPreview, playPreview, onClose, stopPreview, @@ -385,6 +389,7 @@ function WalkupClipModal({ teamId: string; playerId: string; previewTimeMs: number | null; + fadeOutPreview: (durationMs?: number) => void; playPreview: (clip: AudioClip, startMs?: number, endMs?: number) => Promise; onClose: () => void; stopPreview: () => void; @@ -594,23 +599,19 @@ function WalkupClipModal({
-
-
1. Source
-
2. Trim and metadata
-
+ {step === "source" ? (
-
  • @@ -637,7 +638,7 @@ function WalkupClipModal({ disabled={createSourceMutation.isPending} onClick={() => setSourceMode("url")} > - Import URL + URL
  • @@ -651,7 +652,7 @@ function WalkupClipModal({ disabled={createSourceMutation.isPending} onClick={() => setSourceMode("existing")} > - Existing media + Existing
@@ -663,6 +664,7 @@ function WalkupClipModal({ className={`tab-pane fade${sourceMode === "upload" ? " show active" : ""}`} >
+
Upload a local audio file to create a new walkup clip.