From 9ae7940a0481f03f3643b6ad485fde960c73059c Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 22 May 2026 14:06:50 +0200 Subject: [PATCH] fix(layout): migrate date pickers to useViewportTier mobile-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final Bucket 1 visual-audit follow-up. Audit of all 4 useIsMobile callers: - pipeline-chart.tsx + pipeline-funnel-chart.tsx → keep useIsMobile (short x-axis stage labels apply on tablet too — bar charts can't fit full "Reservation" / "Deposit Paid" text at narrow widths). - date-picker.tsx + date-time-picker.tsx → migrate to useViewportTier. Tablet (768-1023) has plenty of room for the desktop Popover Calendar; only the smallest phone widths now fall back to the native datepicker input. 1454/1454 vitest, tsc clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/superpowers/audits/alpha-uat-master.md | 2 +- src/components/ui/date-picker.tsx | 9 ++++++--- src/components/ui/date-time-picker.tsx | 8 +++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/superpowers/audits/alpha-uat-master.md b/docs/superpowers/audits/alpha-uat-master.md index 32ba7dab..45757d31 100644 --- a/docs/superpowers/audits/alpha-uat-master.md +++ b/docs/superpowers/audits/alpha-uat-master.md @@ -25,7 +25,7 @@ _Copy tweaks, alignment, single-prop edits, obvious typos._ > > - **SHIPPED in 2f1e1b5:** Tablet topbar logo trigger doesn't render visibly — center grid column changed to `minmax(280px, 800px)` at base with `lg:` override back to 420px min; search-container `sm:-translate-x-...` gated to `lg:` so it only kicks in when sidebar is inline. Verified at 768 — hamburger affordance visible top-left. > - **SHIPPED in 2f1e1b5:** Dashboard title strip crushed at 1024 viewport — PageHeader horizontal-stack breakpoint moved from `lg:` (1024) to `xl:` (1280) so the strip stays stacked through tablet AND narrowest desktop width. Verified at 1024 — title reads cleanly with action row stacked below. -> - **`useIsMobile()` returns `true` on tablet — call-site audit needed** — _src/hooks/use-is-mobile.ts_ + every component currently importing `useIsMobile` — the new `useViewportTier()` is the precise hook; `useIsMobile` was kept as `tier !== 'desktop'` for back-compat, which is what most call sites want ("show short stage labels", "stack vertically"). But a handful intend strict mobile-only behaviour (e.g. drawer-instead-of-popover, bottom-sheet picker) and should migrate to `useViewportTier() === 'mobile'`. Grep `useIsMobile` and triage each per actual intent. ~30 min for the audit + targeted migrations. Captured 2026-05-22. +> - **SHIPPED in 2f1e1b5 + follow-up:** `useIsMobile()` call-site audit — only 4 callers: pipeline-chart + pipeline-funnel-chart correctly want `tier !== desktop` (short-label x-axis applies on tablet too, kept as-is); date-picker + date-time-picker were strict mobile-only and now use `useViewportTier() === 'mobile'` so tablet gets the desktop Popover Calendar instead of the native input. > **Outstanding quick-fixes (rapid UAT capture — not yet shipped):** > diff --git a/src/components/ui/date-picker.tsx b/src/components/ui/date-picker.tsx index e235ac31..725103b2 100644 --- a/src/components/ui/date-picker.tsx +++ b/src/components/ui/date-picker.tsx @@ -8,7 +8,7 @@ import { Button, type ButtonProps } from '@/components/ui/button'; import { Calendar } from '@/components/ui/calendar'; import { Input } from '@/components/ui/input'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; -import { useIsMobile } from '@/hooks/use-is-mobile'; +import { useViewportTier } from '@/hooks/use-is-mobile'; import { cn } from '@/lib/utils'; export interface DatePickerProps { @@ -76,8 +76,11 @@ export function DatePicker({ name, forceVariant, }: DatePickerProps) { - const isMobile = useIsMobile(); - const variant = forceVariant ?? (isMobile ? 'mobile' : 'desktop'); + // Strict mobile only — tablet (768-1023) has room for the desktop + // Popover Calendar; only the smallest phone widths fall back to the + // native datepicker input. + const tier = useViewportTier(); + const variant = forceVariant ?? (tier === 'mobile' ? 'mobile' : 'desktop'); const [open, setOpen] = React.useState(false); const selected = parseIso(value); diff --git a/src/components/ui/date-time-picker.tsx b/src/components/ui/date-time-picker.tsx index e35cf765..235bde09 100644 --- a/src/components/ui/date-time-picker.tsx +++ b/src/components/ui/date-time-picker.tsx @@ -8,7 +8,7 @@ import { Button, type ButtonProps } from '@/components/ui/button'; import { Calendar } from '@/components/ui/calendar'; import { Input } from '@/components/ui/input'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; -import { useIsMobile } from '@/hooks/use-is-mobile'; +import { useViewportTier } from '@/hooks/use-is-mobile'; import { cn } from '@/lib/utils'; export interface DateTimePickerProps { @@ -63,8 +63,10 @@ export function DateTimePicker({ name, forceVariant, }: DateTimePickerProps) { - const isMobile = useIsMobile(); - const variant = forceVariant ?? (isMobile ? 'mobile' : 'desktop'); + // Strict mobile only — tablet has room for the desktop Popover; only + // the smallest phone widths fall back to the native datetime-local input. + const tier = useViewportTier(); + const variant = forceVariant ?? (tier === 'mobile' ? 'mobile' : 'desktop'); const [open, setOpen] = React.useState(false); const { date, time } = parseDateTime(value); const displayTime = time || '09:00';