From b05d262b5263b81f3f99a32f52a4199039e702c0 Mon Sep 17 00:00:00 2001 From: Fable Integrator Date: Fri, 10 Jul 2026 12:11:39 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20app-tab=20Rule-8=20sweep=20=E2=80=94=20r?= =?UTF-8?q?oster/stats/pipeline/scorecard/qualifier=20tables=20become=20re?= =?UTF-8?q?al=20phone=20layouts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Bridge-sweep scouts found the same disease on the app side: min-w tables and unwrapped control rows forcing horizontal scroll at 390px on phone-primary surfaces. Each fix keeps md+ byte-identical: - Golf calendar hero: phone composition = grid nav row (prev/today/next) + full-width primary action in the thumb zone (was a single unwrapping 4-control row that overflowed the Surface at 390px). - Baseball roster wall (default surface): full-width PlayerRowPlate rows with AVG/OPS below md; the 5-column min-w-[680px] wall stays at md+. - Baseball stats center record book: 2-stat self-labeled card rows below md (AVG/OPS hitting, ERA/WHIP pitching), leaders computed per column set; ghost ready-rows get matching placeholders. - Baseball pipeline list view: card rows below lg mirroring the Academics split; desktop table unchanged except the list row's avatar no longer stacks above the name (Button single-child contract — real desktop render bug, wrapped in one flex child). - Golf round scorecard: real phone scorecard — per-hole strips (hole chip · par · tone-colored score + to-par pill · putts · FW/GIR marks) + nine-total rows; the min-w table stays md+. Qualifier round-by-round becomes rank/name/total card rows with per-round chips. - Baseball dev-plan goal filter tabs ride a contained edge-bled scroller below md (4 nowrap triggers exceeded 390px content width). Verification: every packet adversarially verified (all correct / correct-with-nits); tsc, eslint --max-warnings 0, vitest 975 tests green. Local production build hit ENOSPC (disk, not code) — CI build is the authoritative gate on this PR. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01J2E46URHkhCnEQSYDtZLJX --- .../dashboard/dev-plan/DevPlanClient.tsx | 11 +- .../dashboard/pipeline/PipelineClient.tsx | 199 +++++++++++- .../dashboard/roster/RosterFairway.tsx | 74 +++-- .../stats-center/StatsCenterClient.tsx | 119 +++++++- .../pages/calendar/FairwayCalendarHero.tsx | 58 +++- .../qualifiers/FairwayQualifierDetail.tsx | 240 ++++++++++----- .../pages/rounds/FairwayRoundDetail.tsx | 283 +++++++++++++----- 7 files changed, 785 insertions(+), 199 deletions(-) diff --git a/src/app/baseball/(dashboard)/dashboard/dev-plan/DevPlanClient.tsx b/src/app/baseball/(dashboard)/dashboard/dev-plan/DevPlanClient.tsx index eb1dc90b4..9930c9df0 100644 --- a/src/app/baseball/(dashboard)/dashboard/dev-plan/DevPlanClient.tsx +++ b/src/app/baseball/(dashboard)/dashboard/dev-plan/DevPlanClient.tsx @@ -559,9 +559,15 @@ export default function DevPlanClient() { ) : null} - {/* Goals with Tabs */} + {/* Goals with Tabs. The four icon+label+badge triggers are + whitespace-nowrap and sum past the ~338px content width at + 390px, and TabsList has no scroll handling of its own — so + below md the list rides a contained edge-bled scroller + (matching the page's px-4 shell) instead of overflowing the + page. md+ is untouched (overflow-visible, no bleed). */} - +
+ } badge={activeCount > 0 ? activeCount : undefined}> Active @@ -575,6 +581,7 @@ export default function DevPlanClient() { All +
- -
- {name} - {item.player?.high_school_name || 'No school'} + {/* ONE pre-composed flex child — Button wraps children in a bare + , so Avatar + div as siblings stack vertically (see + Button's CHILDREN CONTRACT doc). Same pattern as the card row. */} +
+ +
+ {name} + {item.player?.high_school_name || 'No school'} +
@@ -607,6 +612,147 @@ function PipelineListRow({ ); } +// Rule 8 (docs/MOBILE_DOCTRINE.md) — a `min-w` table wrapped only in +// `overflow-x-auto` is not the phone treatment on a reading surface. Below +// `lg` each row becomes a composed card (identity + position/grad-year + +// status + updated + tap-through/actions), mirroring the `hidden lg:block` / +// `lg:hidden` split already shipped in AcademicsClient.tsx:291/294 in this +// same app. The desktop `` above stays byte-identical; this card +// consumes the exact same row props/callbacks — no new read/write path. +function PipelineListCard({ + item, + focused, + selected, + editing, + noteValue, + onFocus, + onOpenPeek, + onToggleSelect, + onStatusChange, + onStartEditNote, + onNoteValueChange, + onSaveNote, + onCancelNote, + onViewProfile, + onRemove, +}: PipelineListRowProps) { + const name = getFullName(item.player?.first_name, item.player?.last_name); + const location = item.player?.city && item.player?.state ? `${item.player.city}, ${item.player.state}` : 'N/A'; + + return ( + +
+ {/* Long-press multi-select isn't available on a card row — a compact, + always-visible checkbox keeps single-tap selection reachable. */} + + +
+ +
+ {item.player?.primary_position || '—'} + · + {item.player?.grad_year ?? '—'} + · + {location} +
+ + + +
+
+