From cad55e3565e40b5e7eb7016f3d8908a0999a7a51 Mon Sep 17 00:00:00 2001 From: Matt Ciaccio Date: Sun, 3 May 2026 16:03:56 +0200 Subject: [PATCH] fix(mobile): clipping, dropdown-tabs and stale phone metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five mobile-UX issues caught in the 2026-05-03 audit, fixed in one pass: 1. SpecRow on berth detail clipped at right edge on phone widths. "Length 49.21 ft / 15 r" (the "m" cut off). Mobile-first stack: label on top, value full-width below; flex row only from sm up. 2. ResponsiveTabs collapsed to a Select on phone widths, which read like a generic dropdown and obscured the existence of peer tabs. Replaced with a horizontally-scrollable strip that auto-scrolls the active trigger into view (so the user sees neighbors and gets a discovery cue that more exists beyond the edge). Removes the phone-only Select and unifies the tab UI across viewport sizes. 3. Documents page tab strip ("All / EOI queue / Awaiting them / ...") overflowed the 390px viewport because the wrapper was a fixed flex row. Same horizontal-scroll fix as (2); inherits because Documents uses ResponsiveTabs. 4. Berth detail header: "Change Status" + "Edit" buttons crowded the area subtitle on mobile, causing "North Pier" to wrap to two lines ("North" / "Pier"). Stacked vertically on phone widths; from sm up the buttons sit beside the title block as before. 5. Empty contact rows on client detail rendered a stale "Add tag · star" metadata strip even when the contact value was unset, which cluttered the row and offered no useful action. The metadata block now only shows when contact.value is non-empty; the trash icon stays visible so users can clean up the empty placeholder. Verification: - pnpm exec vitest run: 858/858 passing - pnpm exec tsc --noEmit: same 36 errors as baseline (all pre-existing on feat/mobile-foundation, none introduced) - lint clean Defers: - Mobile More sheet last-row alignment / "Email" label specificity - Admin index grouping (Access / System / Configuration / Content) - Interest detail header icon labels (trophy/X discoverability) - Pipeline funnel x-axis label abbreviations - Reminders rail width allocation on dashboard Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/berths/berth-detail-header.tsx | 7 +- src/components/berths/berth-tabs.tsx | 7 +- src/components/clients/contacts-editor.tsx | 56 +++++++----- src/components/shared/responsive-tabs.tsx | 88 ++++++++++--------- 4 files changed, 87 insertions(+), 71 deletions(-) diff --git a/src/components/berths/berth-detail-header.tsx b/src/components/berths/berth-detail-header.tsx index 9558c84..bf21623 100644 --- a/src/components/berths/berth-detail-header.tsx +++ b/src/components/berths/berth-detail-header.tsx @@ -167,7 +167,10 @@ export function BerthDetailHeader({ berth }: BerthDetailHeaderProps) { return ( <> -
+ {/* Stacks vertically on phone widths so the action buttons don't + squeeze the area subtitle into a two-line wrap. From sm up the + title/area block sits side-by-side with the action buttons. */} +

@@ -182,7 +185,7 @@ export function BerthDetailHeader({ berth }: BerthDetailHeaderProps) { {berth.area &&

{berth.area}

}

-
+
- {/* Right: tag + actions */} + {/* Right: tag + actions. + When the contact value is empty (e.g. a row created from a stale + import or an aborted edit), we hide the "Add tag" + Make-primary + controls so the empty placeholder doesn't clutter the row. The + trash icon is always shown so users can clean up the empty entry. */}
-
- { - await onUpdate({ label: v }); - }} - /> -
+ {contact.value ? ( + <> +
+ { + await onUpdate({ label: v }); + }} + /> +
- + + + ) : null}