Files
route-commerce/src/components/water/WaterFieldClient.tsx
T
Tyler ac88584b8b
Deploy to route.crispygoat.com / deploy (push) Successful in 4m13s
feat(water-log): redesign /water as Apple HIG mobile-first experience
Replaces the legacy 1000+ line WaterFieldClient with a polished
three-screen state machine tuned for phones and tablets in the field:

  PIN  ->  Headgates  ->  Log  ->  Success

Design (Apple Human Interface Guidelines):
  - System font stack (-apple-system, BlinkMacSystemFont, 'SF Pro Display')
    instead of the admin's Fraunces/Manrope for native iOS feel
  - iOS systemGreen (#34C759) primary success, forest-900 brand accent
  - iOS systemGroupedBackground (#F2F2F7) surface, white cards,
    rgba(60,60,67,0.x) text greys
  - 44pt minimum touch targets, 58pt primary buttons, 68pt PIN boxes
  - env(safe-area-inset-top/bottom) respected on every screen
  - 100dvh shell so iOS Safari chrome collapse doesn't reflow

Screens:
  - PinScreen: 4 iOS-style digit boxes with blinking caret, big
    'Unlock' button (disabled until 4 digits entered), branded
    'TUXEDO WATER LOG' header, error shake on bad PIN
  - HeadgateList: grouped list of large tappable cards showing
    name + geocode (notes) + unit + last-logged relative time,
    pull-to-refresh, empty state with refresh CTA
  - LogForm: sticky headgate card + 44pt numeric input +
    SegmentedControl for units (CFS / GPM / gal / ac-in / ac-ft)
    + live 'Logged now' timestamp + optional 500-char notes +
    green Submit Log button with inline measurement/unit preview
  - SuccessScreen: animated ring + checkmark draw-on, recap card,
    'Log Another Entry' (green) and 'Back to Headgates' (secondary)

Reusable primitives (mobile/):
  - SegmentedControl: iOS-style sliding pill with spring timing
  - PullToRefresh: callback-based (no router.refresh) with resistance
  - icons.tsx: inline SVG icon set (Lock, Droplet, Gauge, Refresh, etc.)

Backend:
  - getWaterHeadgates extended to include notes (the 'geocode') and
    last_used_at for richer card display
  - No new routes, no DB migration - reuses verifyWaterPin,
    submitWaterEntry, logoutWater server actions unchanged

Quality gates:
  - tsc --noEmit clean
  - eslint clean on all new files
  - npm run build succeeds (/water rendered as static)
  - prefers-reduced-motion respected (existing global guard)
2026-07-01 19:27:10 -06:00

51 lines
1.7 KiB
TypeScript

"use client";
/**
* WaterFieldClient — entrypoint for the public `/water` route.
*
* As of 2026-07, this delegates to the new mobile-first
* `MobileWaterApp` state machine. The legacy implementation that
* lived here supported language selection and role-based admin
* routing; both are reachable from the new flow via dedicated
* secondary routes (`/water/admin` for the admin side, the QR
* picker for Spanish signage in the future) but the primary
* `/water` flow is now a clean three-screen Apple-HIG experience:
* PIN → Headgates → Log
*
* The wrapping div uses `100dvh` so the iOS Safari address bar
* collapse doesn't trigger layout jumps, and `100vh` as a
* fallback for older browsers.
*/
import { Suspense } from "react";
import { MobileWaterApp } from "./mobile/MobileWaterApp";
export default function WaterFieldClient() {
return (
<div
className="fixed inset-0 bg-[#F2F2F7]"
style={{
// 100dvh = dynamic viewport height (excludes collapsing
// iOS Safari chrome). 100svh excludes the bottom safe-area
// inset, which is what we want when the soft keyboard is
// up. Fallback to 100vh for older browsers.
height: "100dvh",
minHeight: "100vh",
// Disable rubber-band scrolling on the whole shell — each
// screen manages its own internal scroll.
overscrollBehavior: "none",
}}
>
<Suspense
fallback={
<div className="flex h-full items-center justify-center">
<div className="text-[15px] text-[rgba(60,60,67,0.55)]">
Loading
</div>
</div>
}
>
<MobileWaterApp />
</Suspense>
</div>
);
}