"use client"; import { useState, useRef, useCallback, useEffect } from "react"; import { createStopsBatch } from "@/actions/stops"; import GlassModal from "@/components/admin/GlassModal"; import type { ParsedStopRow } from "@/lib/csv-parsers"; type ParsedStop = Omit; type Step = "idle" | "parsing" | "review" | "importing" | "done" | "error"; type Props = { brandId: string; onClose: () => void; onComplete: (count: number) => void; }; export default function ScheduleImportModal({ brandId, onClose, onComplete }: Props) { const [step, setStep] = useState("idle"); const [error, setError] = useState(null); const [parsedStops, setParsedStops] = useState([]); const [warnings, setWarnings] = useState([]); const [dragOver, setDragOver] = useState(false); const [useAI, setUseAI] = useState(false); const [created, setCreated] = useState(0); const fileInputRef = useRef(null); const processFile = useCallback(async (file: File) => { setError(null); setStep("parsing"); let text: string; try { text = await file.text(); } catch { setError("Could not read file. Try a different format."); setStep("idle"); return; } try { const res = await fetch("/api/stops/import", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text, brandId, useAI }), }); const data = await res.json(); if (!res.ok || (data.error && data.stops?.length === 0)) { setError(data.error ?? "Parsing failed. Try a CSV file."); setStep("idle"); return; } if (!data.stops || data.stops.length === 0) { setError("No stops found in file. Check that columns include: city, state, location, date, time."); setStep("idle"); return; } setParsedStops(data.stops); setWarnings(data.warnings ?? []); setStep("review"); } catch { setError("Network error while parsing file."); setStep("idle"); } }, [brandId, useAI]); function handleFile(file: File) { const ext = file.name.split(".").pop()?.toLowerCase() ?? ""; if (!["csv", "txt", "json"].includes(ext)) { setError("Unsupported file type. Please upload a CSV, TXT, or JSON file."); return; } processFile(file); } function handleDrop(e: React.DragEvent) { e.preventDefault(); setDragOver(false); const file = e.dataTransfer.files[0]; if (file) handleFile(file); } function handleChange(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (file) handleFile(file); } function updateStop(idx: number, field: keyof ParsedStop, value: string) { setParsedStops((prev) => { const next = [...prev]; next[idx] = { ...next[idx], [field]: value }; return next; }); } function removeStop(idx: number) { setParsedStops((prev) => prev.filter((_, i) => i !== idx)); } async function handleImport() { if (parsedStops.length === 0) return; setStep("importing"); setError(null); const result = await createStopsBatch( brandId, parsedStops.map(({ city, state, location, date, time, address, zip, notes }) => ({ city, state, location, date, time, address, zip, notes, })) ); if (!result.success) { setError(result.error ?? "Import failed"); setStep("review"); return; } setCreated(result.created); setStep("done"); onComplete(result.created); } const hasWarnings = warnings.length > 0; return (
{step === "idle" && ( <> {error && (
{error}
)} {/* AI toggle */}
{useAI ? "AI will parse unstructured text. Requires OPENAI_API_KEY." : "Best results with CSV."}
{/* Drop zone */} )} {step === "parsing" && (
Parsing file…
)} {step === "review" && (
{error && (
{error}
)} {hasWarnings && (

Parsing warnings:

    {warnings.slice(0, 5).map((w, i) => (
  • {w}
  • ))} {warnings.length > 5 &&
  • …and {warnings.length - 5} more
  • }
)}

{parsedStops.length} stop{parsedStops.length !== 1 ? "s" : ""} found — review and edit

All will be created as drafts
{/* Review table */}
{parsedStops.map((stop, idx) => ( ))}
City State Location Date Time
updateStop(idx, "city", e.target.value)} className="w-full rounded-lg border border-stone-200 bg-white px-2 py-1.5 text-sm outline-none focus:border-emerald-400" /> updateStop(idx, "state", e.target.value)} className="w-12 rounded-lg border border-stone-200 bg-white px-2 py-1.5 text-sm outline-none focus:border-emerald-400" /> updateStop(idx, "location", e.target.value)} className="w-full rounded-lg border border-stone-200 bg-white px-2 py-1.5 text-sm outline-none focus:border-emerald-400" /> updateStop(idx, "date", e.target.value)} className="w-24 rounded-lg border border-stone-200 bg-white px-2 py-1.5 text-sm outline-none focus:border-emerald-400" /> updateStop(idx, "time", e.target.value)} className="w-16 rounded-lg border border-stone-200 bg-white px-2 py-1.5 text-sm outline-none focus:border-emerald-400" />

{parsedStops.length} draft stop{parsedStops.length !== 1 ? "s" : ""} will be created

)} {step === "importing" && (
Creating draft stops…
)} {step === "done" && (

{created} draft stop{created !== 1 ? "s" : ""} created

Review them in the stops list and publish when ready.

)} {step === "error" && (

{error}

)}
); }