diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index f3e14c7..00d1060 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import Papa from "papaparse"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/lib/auth"; @@ -7,6 +7,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Upload, CheckCircle2, AlertCircle, Loader2, FileSpreadsheet } from "lucide-react"; import { toast } from "sonner"; @@ -51,6 +52,8 @@ interface ImportCtx { // Name → uuid lookups (for files that reference by name only) clientByName: Map; caseByNumber: Map; + // Optional fallback client id (e.g. for address-only locations CSV) + defaultClientId?: string | null; } const norm = (s: string) => @@ -208,7 +211,7 @@ const IMPORTERS: ImporterConfig[] = [ transform: (r, ctx) => { const cid = r._clientext ? ctx.clientByExt.get(String(r._clientext)) : null; const cidByName = r._clientname ? ctx.clientByName.get(String(r._clientname).toLowerCase()) : null; - r.client_id = cid ?? cidByName ?? null; + r.client_id = cid ?? cidByName ?? ctx.defaultClientId ?? null; delete r._clientext; delete r._clientname; if (!r.client_id) return null; if (!r.first_name && r._ownername) { @@ -216,6 +219,10 @@ const IMPORTERS: ImporterConfig[] = [ r.first_name = first_name; r.last_name = last_name; } delete r._ownername; + // Allow address-only rows: synthesize a placeholder owner name from the address + if (!r.first_name) { + r.first_name = r.address || r.unit_number || "Unit"; + } if (!r.last_name) r.last_name = ""; return r; }, @@ -509,6 +516,8 @@ interface ImportResult { function ImportPage() { const { user } = useAuth(); const [results, setResults] = useState>({}); + const [clientList, setClientList] = useState<{ id: string; name: string }[]>([]); + const [defaultClientId, setDefaultClientId] = useState(""); const ctx = useMemo(() => ({ userId: user?.id ?? "", clientByExt: new Map(), @@ -518,8 +527,18 @@ function ImportPage() { invoiceByExt: new Map(), clientByName: new Map(), caseByNumber: new Map(), + defaultClientId: null, }), [user?.id]); + useEffect(() => { + void supabase + .from("clients") + .select("id,name") + .is("archived_at", null) + .order("name") + .then(({ data }) => setClientList((data ?? []) as { id: string; name: string }[])); + }, []); + // Pre-load lookup caches from existing rows so subsequent imports can join. // Always reload (and paginate past Supabase's 1000-row default limit) so caches // pick up rows added since the page mounted (e.g., from a prior CSV import). @@ -569,6 +588,7 @@ function ImportPage() { const handleFile = async (cfg: ImporterConfig, file: File) => { if (!user?.id) return; setResults((r) => ({ ...r, [cfg.key]: "running" })); + ctx.defaultClientId = cfg.key === "locations" ? (defaultClientId || null) : null; await ensureLookupsLoaded(); const parsed = await new Promise>>((resolve, reject) => { @@ -746,7 +766,19 @@ function ImportPage() {

{cfg.description}

-
+
+ {cfg.key === "locations" && ( + + )} -