Reimagine the admin stops surface under a 'Harvest Almanac' editorial
direction. Establishes the type system (Fraunces serif display, Geist
sans, JetBrains Mono numerics) loaded via next/font, and a compact
modal pattern used across the new components.
Stop modal (AddStopModal)
- Compact 2-col grid: City|State, Date|Time, Address|ZIP|Cutoff
- Status replaced with slim segmented control (Draft / Publish Now)
- Tighter inputs (36px), single-line footer with Esc hint
- Auto-focus on City field, reactive submit label
GlassModal
- New 'compact' prop: tighter padding, eyebrow text, no accent bar,
capped max-height for dense forms
Stop product assignment (StopProductAssignment)
- Reimagine the <select> dropdown as an editorial card-grid picker
- Big Fraunces numeral + small-caps eyebrow showing live count
- Click-to-toggle assign/remove; green check for assigned, red X on
hover-to-remove intent
- Search + filter chips (All / Available / On this stop) with
mono counters
- '/' keyboard shortcut focuses search
- Summary footer with 'Remove all' action
Stops page
- New StopsViewClient wrapper with shared search/status filter and
Calendar/Table view toggle (default = Calendar)
- New StopsCalendarClient: month grid almanac with editorial header,
color-coded event chips, today highlight, prev/next/Today nav
- Event popover with auto-edge-detection positioning, full stop
details, Publish/View Route/Edit actions
- Day-route drawer: slides in from right with route spine (numbered
markers + connecting hairline), 3-card summary (stops/live/brands),
per-stop Publish/Edit/Duplicate actions
- StopsTableClient refactored to accept hideInternalFilterBar prop
so the wrapper can own shared filtering
Design tokens
- New utility classes in admin-design-system.css: ha-display,
ha-eyebrow, ha-field, ha-segment, ha-picker, ha-product-card,
ha-calendar, ha-event-popover, ha-drawer, ha-route-stop, etc.
- All uses CSS variables (--font-fraunces, --font-geist,
--font-jetbrains-mono) so the type system cascades
Clicking a stop (or the Edit link) in /admin/stops now opens a modal
with Details/Products/Message tabs instead of navigating to
/admin/stops/[id].
- Add getStopDetails server action (returns stop, brand, candidate
products, assigned products, brand list in one round trip).
- Add StopDetailModal component with tabbed panels that reuse the
existing StopEditForm, StopProductAssignment, and
MessageCustomersSection.
- Add an optional onSaved callback to StopEditForm so the modal can
refetch + router.refresh() after a save.
- StopTableClient now opens the modal on row click; the /admin/stops/[id]
route is kept in place for direct links/bookmarks.
Tier 1 — Layout consistency:
* Move Stops tab action buttons (Upload Schedule, Add Stop) into the
filter bar, matching the Locations tab. One muscle memory for
'where the Add button is'. Removes the page-level StopsHeaderActions
component.
* Make the card a single cohesive unit. Tabs sit INSIDE the card on
top with a border-b, content fills the rest. No more 'rounded-b-2xl
rounded-t-none border-t-0' shape.
* New StopsLocationsTabs: pill-style tabs with icons + count badges +
sublabel ('269 · 7 cities'). Instant orientation. URL-driven via
Link so refresh + deep-linking work.
Tier 2 — Easier to scan:
* New StatsStrip under the page header: '269 stops · 7 cities · 4
states · 4 active · 5 draft · 0 inactive' (different stats for the
Locations tab). Context before the table loads.
* Stops table collapses 8 columns → 6: drop Brand, merge City+State
into 'Where', merge Date+Time into 'When' (date + weekday on one
line, time on the next). 30% more breathing room per row.
* Replace font-mono for dates/times with tabular-nums — same column
alignment, type family stays consistent.
* Click anywhere on a Stops row to edit. Redundant 'Edit' link is
gone. Kebab menu still handles Publish/Duplicate/Delete.
* Match Locations' empty state (icon + title + subtitle) on the Stops
side.
Polish:
* Consistent status pills (Active = emerald, Inactive = stone, Draft
= amber) in both tabs.
* Tab-switch fade via framer-motion AnimatePresence — subtle y/opacity
transition between Stops and Locations content.
Server-side / caching refactor (Grok):
- New RPC get_public_stops_for_brand (migration 148) for public storefront stops
- New server action getPublicStopsForBrand with revalidate=300 + tags
- Add revalidateTag invalidation to createStopsBatch + publishStop
- Convert /tuxedo/stops and /indian-river-direct/stops to Server Components
- Extract TuxedoStopsList + IndianRiverStopsList as client islands (GSAP only)
- Removes supabase-js from browser bundle on those routes
- Both pages now statically prerendered (5m ISR)
Parallel agent changes also staged:
- AI provider model list refresh (claude-sonnet-4-5, etc.)
- ESLint directive patches for react-hooks/set-state-in-effect
- Admin + storefront + checkout + cart updates
- New admin_create_stop_rpcs migration (147)
- Misc fixes across ~90 files
Build verified: typecheck clean, lint clean on new files, production build succeeds.