Export Route Flow (Pop-up & Warning)#175
Conversation
b334a34 to
c7d27bd
Compare
kirillakovalenko
left a comment
There was a problem hiding this comment.
PR #175 — Export Route Flow (Pop-up & Warning)
Author: KesarSidhu | +1,180 / -264 across 19 files | Client-only
Overview
Adds an Export flow to the Results page: an Export button opens a routes selection modal (checkboxes, staggered multi-file download), and a blocking warning modal when edit mode or an unsaved pin drag is active. Also includes a visual redesign of the sidebar/stop cards, colored map markers per route, and a fixed SidebarResultsButton that now navigates to /results when stored routes exist.
Code Quality
Strengths:
Good test coverage for new utilities — recipientSummary.test.ts and new cases in vroomToRoutes.test.ts cover edge cases well.
useSyncExternalStore for useIsClient is the correct Next.js SSR-safe hydration pattern.
Accessibility is largely solid: aria-modal, aria-labelledby, useFocusTrap, aria-hidden on decorative SVGs, aria-current="page" on active nav item.
Blob URL is properly revoked after 1 s; stagger logic for multi-file download is pragmatic.
The SVG injection comment in markerSvgDataUrl (// fillColor is always a route palette hex...) is a good defensive note.
Issues
Blocking / High Priority
useIsClient duplicated (ExportEditWarningModal.tsx and ExportRoutesModal.tsx) — identical hook defined twice. Extract to app/results/hooks/useIsClient.ts or a shared location.
"Save" button always rendered (page.tsx:1528-1533) — The Cancel button is correctly gated on pendingPinMove != null, but the new Save button is always visible. This will confuse users who haven't started a drag. Both buttons should appear together or the Save should also be conditional:
{pendingPinMove != null && (
<>
Cancel
Save
</>
)}
--edit-stone-700 CSS variable (ExportRoutesModal.tsx:886) — The "Export (N)" button uses border-[var(--edit-stone-700)] but this variable isn't added in the edit.module.css diff. If it was already defined elsewhere, this is fine — but it should be verified. A broken CSS variable silently renders as border-transparent.
Typecheck and tests not run — The PR checklist leaves typecheck, test, and build unchecked. For a TypeScript PR with new types and CSS variables, these are important to confirm before merge.
Medium Priority
isSidebarOpen hardcoded to true (page.tsx:1426) — The sidebar toggle button is removed and the state replaced with a constant. This eliminates the ability to collapse the sidebar on narrow screens. If this is a deliberate hi-fi decision for now, a // always open for now comment would clarify intent.
role="status" on warning banner (ExportEditWarningModal.tsx:634) — The amber warning block uses role="status" (a polite live region). For a blocking warning, role="alert" is more appropriate (announces immediately) and aligns better with "unable to export while editing".
Sequence badge uses hardcoded bg-cyan-500 (Sidebar.tsx:1343) — Stop sequence numbers and connector lines use a fixed cyan rather than the per-route accent color. This looks inconsistent with the rest of the card where accent is used for the route icon. Intentional or oversight?
Minor
Module-level mutable markerScaledSize (Map.tsx:913) — The singleton pattern with a module-level let is fine for production but will leak state between tests if the Maps module is ever unit tested.
capacityUsed double condition (vroomToRoutes.ts:228) — address?.deliveryQuantity && address.deliveryQuantity > 0 checks the same value twice. Cleaner: (address?.deliveryQuantity ?? 0) > 0.
downloadRoutesAsJsonFiles no error handling (downloadRouteJson.ts:21) — If URL.createObjectURL or DOM operations fail, the error is silently dropped. A try/catch with a console.error at minimum would surface browser-blocked downloads.
Summary
The core export logic is well-structured and the accessibility/SSR patterns are correct. Fix the always-visible Save button (#2) and verify --edit-stone-700 (#3) before merge, and run typecheck/build (#4). The duplicated useIsClient (#1) is a clean-up item. Low risk overall given the PR is client-only with no API or persistence changes.
Align the results header with the mock by using a compact app label plus Save/Export actions while preserving the existing sidebar toggle and pending-edit cancel behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Refine the sidebar title section, edit action, and footer copy styling to better match the hifi mock while keeping the route list behavior unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
Use route palette colors for map pins and switch marker visuals from plain dots to pin-style markers so map markers align with route styling. Co-authored-by: Cursor <cursoragent@cursor.com>
Match the edit-mode layout with a wider results panel, save-edits CTA styling, and centered map status banner while keeping existing route behavior intact. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Add cyan timeline markers with dotted connectors in expanded routes. Remove duplicate sequence badge from EditableStopItem. Co-authored-by: Cursor <cursoragent@cursor.com>
Extract recipientSummary for tests, hide locked contact row when empty, and use middle-dot formatting on hi-fi address cards. Co-authored-by: Cursor <cursoragent@cursor.com>
Map recipient_phone CSV alias, prefer form delivery quantity for capacityUsed, and add unit tests for recipient fields and capacity fallback. Co-authored-by: Cursor <cursoragent@cursor.com>
Add optional delivery window fields on Stop, format recipient and delivery lines on EditableStopItem, and map form delivery times into routes. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Match hi-fi expanded stop cards with icon rows for recipient and delivery, route-tinted package pill, and rounded card layout. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Add note doc icon row, hi-fi read-only note panel, and teal save control in edit mode for expanded stop cards. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Wire hi-fi results Export button to route JSON downloads, with a warning when edit mode or pending pin moves are active. Rebased on step3-edit-look-3-main-v7; keep existing routeColors palette. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
39bcd68 to
fbf3da1
Compare
Summary
Motivation
Changes
Frontend (
app/ui)page.tsx: Export button in the results header; opens export modal or edit warning depending on state.ExportRoutesModal.tsx: Modal to select routes (checkboxes, all selected by default), Export (N) for checked routes, Export All; per-route color accent in the list.ExportEditWarningModal.tsx: Hi-fi warning when export is blocked — title “Edit Mode Active”, amber banner, Cancel / Done editing (exits edit mode).downloadRouteJson.ts: Downloads one.jsonfile per route (route_{n}_{driver-slug}.json), with a short stagger between files so browsers allow multiple downloads from one click.routeColors.ts: Shared palette for route accent colors in the export list.edit.module.css:--edit-teal-400and--edit-foregroundtokens for export modal styling.Export behavior
pendingPinMove), the Edit Mode Active modal appears instead of the export modal.Routeobject (driver, stops, geometry, etc.).Edit-mode warning behavior
isEditMode === trueorpendingPinMove != null(dragged marker not saved/cancelled).Validation
Frontend
npm --prefix app/ui run lintnpm --prefix app/ui run format:checknpm --prefix app/ui run typechecknpm --prefix app/ui run testnpm --prefix app/ui run build/results→ Export opens modal; Export (N) / Export All download JSONBackend
Skipped — frontend-only PR.
Risk
Rollout and Recovery
High-Signal PR Checklist