From 6926fb96bc3d7dcba66165cd504fab325cf08921 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:52:34 -0700 Subject: [PATCH] Cache-bust logos via Vite ?url import and extract shared stat helpers (#310) Logo SVGs referenced by string path (/logo.svg) were cached by CDNs and browsers after deployments. Import them with Vite's ?url suffix so the build emits content-hashed filenames that force a refresh on change. LeagueRow and StandingsPreview had duplicated StatColumn, StatDivider, and rank-change indicator helpers. Extract them into a shared StatHelpers module so both pages render rank and points identically (#3 style with consistent rank-change arrows). --- app/components/league/LeagueRow.tsx | 43 ++----------- app/components/league/StandingsPreview.tsx | 63 ++----------------- app/components/league/StatHelpers.tsx | 59 +++++++++++++++++ app/components/navbar.tsx | 3 +- app/root.tsx | 3 +- .../$leagueId.draft-board.$seasonId.tsx | 3 +- .../leagues/$leagueId.draft.$seasonId.tsx | 3 +- 7 files changed, 76 insertions(+), 101 deletions(-) create mode 100644 app/components/league/StatHelpers.tsx diff --git a/app/components/league/LeagueRow.tsx b/app/components/league/LeagueRow.tsx index f1ad78b..a0414b6 100644 --- a/app/components/league/LeagueRow.tsx +++ b/app/components/league/LeagueRow.tsx @@ -2,6 +2,7 @@ import { formatDistanceToNow } from "date-fns"; import { Link } from "react-router"; import { Button } from "~/components/ui/button"; import { LeagueAvatar } from "./LeagueAvatar"; +import { StatColumn, StatDivider, RankChangeIndicator } from "./StatHelpers"; export interface LeagueRowProps { leagueId: string; @@ -29,29 +30,6 @@ function ordinal(n: number): string { } -// ─── Shared stat column ─────────────────────────────────────────────────────── - -function StatColumn({ - label, - children, -}: { - label: string; - children: React.ReactNode; -}) { - return ( -
-

- {label} -

-
{children}
-
- ); -} - -function StatDivider() { - return
; -} - // ─── Season progress bar ────────────────────────────────────────────────────── function SeasonProgress({ pct, status }: { pct: number; status: "active" | "completed" }) { @@ -70,21 +48,6 @@ function SeasonProgress({ pct, status }: { pct: number; status: "active" | "comp ); } -// ─── Rank change indicator ──────────────────────────────────────────────────── - -function RankChange({ current, previous }: { current: number; previous: number | undefined }) { - if (previous === undefined || previous === current) return null; - const delta = previous - current; - if (delta > 0) { - return ▲{delta}; - } - return ( - - ▼{Math.abs(delta)} - - ); -} - // ─── Row variants ───────────────────────────────────────────────────────────── function DraftRow({ leagueId, leagueName, seasonId, picksUntilMyTurn }: LeagueRowProps) { @@ -156,7 +119,9 @@ function ActiveRow({ {currentRank !== undefined && ( #{currentRank} - + {previousRank !== undefined && previousRank !== currentRank && ( + + )} )} {currentRank !== undefined && totalPoints !== undefined && } diff --git a/app/components/league/StandingsPreview.tsx b/app/components/league/StandingsPreview.tsx index f377b36..1d313ee 100644 --- a/app/components/league/StandingsPreview.tsx +++ b/app/components/league/StandingsPreview.tsx @@ -4,60 +4,7 @@ import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader } from "~/components/ui/card"; import { GradientIcon } from "~/components/ui/GradientIcon"; import { TeamAvatar } from "~/components/TeamAvatar"; - -// ─── Stat helpers (mirrors LeagueRow) ──────────────────────────────────────── - -function StatColumn({ - label, - children, -}: { - label: string; - children: React.ReactNode; -}) { - return ( -
-

- {label} -

-
{children}
-
- ); -} - -function StatDivider() { - return
; -} - -function RankChangeIndicator({ change }: { change: number }) { - if (change === 0) return null; - if (change > 0) { - return ( - - ▲{change} - - ); - } - return ( - - ▼{Math.abs(change)} - - ); -} - -function PointChangeIndicator({ change }: { change: number }) { - if (change >= 0) { - return ▲{Math.round(change)}; - } - return ( - - ▼{Math.abs(Math.round(change))} - - ); -} +import { StatColumn, StatDivider, RankChangeIndicator, PointChangeIndicator } from "./StatHelpers"; // ─── Row styles ─────────────────────────────────────────────────────────────── @@ -92,9 +39,9 @@ function RowContent({ entry }: { entry: StandingsPreviewEntry }) { {/* Right: stats — second row on mobile */}
- {entry.displayRank} + #{String(entry.displayRank).replace(/^T/, "")} {entry.rankChange !== undefined && entry.rankChange !== 0 && ( - + )} @@ -103,7 +50,7 @@ function RowContent({ entry }: { entry: StandingsPreviewEntry }) { {Math.round(entry.points).toLocaleString("en-US")} {entry.pointChange !== undefined && entry.pointChange !== 0 && ( - + )}
@@ -117,7 +64,7 @@ export interface StandingsPreviewEntry { teamId: string; teamName: string; ownerName?: string | null; - /** Pre-computed display rank, e.g. 1, "T2", "T5". Use getDisplayRank(). */ + /** Pre-computed display rank. T prefix (if any) is stripped at render time. */ displayRank: string | number; /** Numeric rank used to select the gold/silver/bronze row tint (1–3 only). */ currentRank?: number; diff --git a/app/components/league/StatHelpers.tsx b/app/components/league/StatHelpers.tsx new file mode 100644 index 0000000..e1db42a --- /dev/null +++ b/app/components/league/StatHelpers.tsx @@ -0,0 +1,59 @@ +export function StatColumn({ + label, + children, +}: { + label: string; + children: React.ReactNode; +}) { + return ( +
+

+ {label} +

+
{children}
+
+ ); +} + +export function StatDivider() { + return
; +} + +export function RankChangeIndicator({ delta }: { delta: number }) { + if (delta === 0) return null; + if (delta > 0) { + return ( + + ▲{delta} + + ); + } + return ( + + ▼{Math.abs(delta)} + + ); +} + +export function PointChangeIndicator({ delta }: { delta: number }) { + if (delta >= 0) { + return ( + + ▲{Math.round(delta)} + + ); + } + return ( + + ▼{Math.abs(Math.round(delta))} + + ); +} diff --git a/app/components/navbar.tsx b/app/components/navbar.tsx index 40747b7..2e24576 100644 --- a/app/components/navbar.tsx +++ b/app/components/navbar.tsx @@ -10,6 +10,7 @@ import { import { Button } from "~/components/ui/button"; import { SignedIn, SignedOut, SignInButton, UserButton } from "@clerk/react-router"; import { HelpCircle, MenuIcon, Settings } from "lucide-react"; +import logoUrl from "../../public/logo.svg?url"; interface NavbarProps { isAdmin: boolean; @@ -24,7 +25,7 @@ export function Navbar({ isAdmin }: NavbarProps) { {/* Left: Logo + Nav links */}
- Brackt + Brackt