"use client"; import { useState, useCallback, useRef, useEffect } from "react"; import { createStop } from "@/actions/stops/create-stop"; import GlassModal from "@/components/admin/GlassModal"; type Props = { isOpen: boolean; onClose: () => void; brandId: string; duplicateFrom?: { city: string; state: string; location: string; date: string; time: string; address?: string | null; zip?: string | null; cutoff_time?: string | null; } | null; onSuccess?: (stopId: string) => void; }; /* Pin icon for the modal header */ const PinIcon = () => ( ); export default function AddStopModal({ isOpen, onClose, brandId, duplicateFrom, onSuccess }: Props) { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [city, setCity] = useState(""); const [stateField, setStateField] = useState(""); const [location, setLocation] = useState(""); const [date, setDate] = useState(""); const [time, setTime] = useState(""); const [address, setAddress] = useState(""); const [zip, setZip] = useState(""); const [cutoffTime, setCutoffTime] = useState(""); const [status, setStatus] = useState<"draft" | "active">("draft"); const cityRef = useRef(null); // Reset form when modal opens (or duplicateFrom changes) — derived // during render per React docs: "Adjusting some state when a prop // changes". See https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes const [prevOpenKey, setPrevOpenKey] = useState(null); const openKey = isOpen ? `open:${duplicateFrom ? "dup" : "new"}` : "closed"; if (openKey !== prevOpenKey) { setPrevOpenKey(openKey); setCity(duplicateFrom?.city ?? ""); setStateField(duplicateFrom?.state ?? ""); setLocation(duplicateFrom?.location ?? ""); setDate(duplicateFrom?.date ?? ""); setTime(duplicateFrom?.time ?? ""); setAddress(duplicateFrom?.address ?? ""); setZip(duplicateFrom?.zip ?? ""); setCutoffTime(duplicateFrom?.cutoff_time ?? ""); setStatus("draft"); setError(null); } // Focus the first field once the modal has actually opened. This is // a DOM side-effect, so it must run after the commit (i.e. in an // effect) — accessing `cityRef.current` during render is illegal. // The `prevOpenKey === "open:..."` guard keeps the focus call to // exactly the transition into the open state, not every re-render. useEffect(() => { if (prevOpenKey?.startsWith("open:")) { const id = requestAnimationFrame(() => cityRef.current?.focus()); return () => cancelAnimationFrame(id); } return undefined; }, [prevOpenKey]); const handleSubmit = useCallback( async (e: React.FormEvent) => { e.preventDefault(); setError(null); if (!city.trim() || !stateField.trim() || !location.trim() || !date) { setError("City, state, location, and date are required."); return; } setLoading(true); try { const result = await createStop(brandId, { city: city.trim(), state: stateField.trim(), location: location.trim(), date, time: time || "08:00", address: address || undefined, zip: zip || undefined, cutoff_time: cutoffTime || undefined, active: status === "active", }); if (result.success) { onSuccess?.(result.id); onClose(); } else { setError(result.error ?? "Failed to create stop"); } } catch { setError("Network error. Please try again."); } finally { setLoading(false); } }, [ brandId, city, stateField, location, date, time, address, zip, cutoffTime, status, onSuccess, onClose, ] ); const isDuplicate = Boolean(duplicateFrom); const title = isDuplicate ? "Duplicate Stop" : "Add Stop"; const eyebrow = isDuplicate && duplicateFrom ? `From ${duplicateFrom.city}, ${duplicateFrom.state}` : "New stop on the route"; const submitLabel = loading ? isDuplicate ? "Duplicating…" : "Creating…" : isDuplicate ? "Duplicate Stop" : status === "active" ? "Create & Publish" : "Save as Draft"; if (!isOpen) return null; return (
{error && (
{error}
)} {/* Row 1 — Where: City + State */}
setCity(e.target.value)} placeholder="Denver" className="ha-field-input" required autoComplete="address-level2" />
setStateField(e.target.value.toUpperCase())} placeholder="CO" maxLength={2} className="ha-field-input ha-field-input-mono text-center" style={{ textTransform: "uppercase" }} required autoComplete="address-level1" />
{/* Row 2 — Where at: Location (full) */}
setLocation(e.target.value)} placeholder="Whole Foods Market — Highlands" className="ha-field-input" required />
{/* Row 3 — When: Date + Time (with small clock icon) */}
setDate(e.target.value)} className="ha-field-input ha-field-input-mono" required />
setTime(e.target.value)} className="ha-field-input ha-field-input-mono" />
{/* Row 4 — Address + ZIP + Cutoff in 3 columns */}
setAddress(e.target.value)} placeholder="123 Main St" className="ha-field-input" autoComplete="street-address" />
setZip(e.target.value)} placeholder="80202" maxLength={10} className="ha-field-input ha-field-input-mono" autoComplete="postal-code" />
setCutoffTime(e.target.value)} className="ha-field-input ha-field-input-mono" />
{/* Row 5 — Status: segmented control (compact, replaces two giant buttons) */}

Visibility

Draft is hidden from customers
{/* Footer */}
Esc to close
); } // Hook for easy integration export function useAddStopModal(brandId: string, duplicateFrom?: Props["duplicateFrom"]) { const [isOpen, setIsOpen] = useState(false); const open = useCallback(() => setIsOpen(true), []); const close = useCallback(() => setIsOpen(false), []); const Modal = useCallback(() => ( ), [isOpen, close, brandId, duplicateFrom]); return { open, close, Modal }; }