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 */}
-

+