fix(mobile): clipping, dropdown-tabs and stale phone metadata
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) <noreply@anthropic.com>
This commit is contained in:
@@ -208,32 +208,40 @@ function ContactRow({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 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. */}
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<div className="w-28 text-xs text-muted-foreground text-right">
|
||||
<InlineEditableField
|
||||
value={
|
||||
contact.label && contact.label.toLowerCase() !== 'primary' ? contact.label : null
|
||||
}
|
||||
emptyText="Add tag"
|
||||
placeholder="work, home…"
|
||||
onSave={async (v) => {
|
||||
await onUpdate({ label: v });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{contact.value ? (
|
||||
<>
|
||||
<div className="w-28 text-xs text-muted-foreground text-right">
|
||||
<InlineEditableField
|
||||
value={
|
||||
contact.label && contact.label.toLowerCase() !== 'primary' ? contact.label : null
|
||||
}
|
||||
emptyText="Add tag"
|
||||
placeholder="work, home…"
|
||||
onSave={async (v) => {
|
||||
await onUpdate({ label: v });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={togglePrimary}
|
||||
title={contact.isPrimary ? 'Primary' : 'Make primary'}
|
||||
className={cn(
|
||||
'p-1 rounded hover:bg-background/60 transition-colors',
|
||||
contact.isPrimary ? 'text-primary' : 'text-muted-foreground/50',
|
||||
)}
|
||||
>
|
||||
<Star className={cn('h-3.5 w-3.5', contact.isPrimary && 'fill-current')} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={togglePrimary}
|
||||
title={contact.isPrimary ? 'Primary' : 'Make primary'}
|
||||
className={cn(
|
||||
'p-1 rounded hover:bg-background/60 transition-colors',
|
||||
contact.isPrimary ? 'text-primary' : 'text-muted-foreground/50',
|
||||
)}
|
||||
>
|
||||
<Star className={cn('h-3.5 w-3.5', contact.isPrimary && 'fill-current')} />
|
||||
</button>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user