diff --git a/src/components/admin/leads/LeadTable.tsx b/src/components/admin/leads/LeadTable.tsx index 37b8768..efbfb94 100644 --- a/src/components/admin/leads/LeadTable.tsx +++ b/src/components/admin/leads/LeadTable.tsx @@ -1,20 +1,21 @@ "use client"; import Link from "next/link"; -import { Lead } from "@/db/schema"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; +import { useState, useRef, useEffect, useTransition } from "react"; +import { useRouter } from "next/navigation"; +import { Check } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { formatDistanceToNow } from "date-fns"; -import { it } from "date-fns/locale"; -import { LEAD_STAGES } from "@/lib/lead-validators"; +import { EditableCell } from "@/components/ui/editable-cell"; +import { OptionMultiSelect } from "@/components/ui/option-multi-select"; +import { + updateLeadField, + addLeadTag, + removeLeadTag, + renameLeadTag, +} from "@/app/admin/leads/actions"; +import type { LeadWithTags, LeadFieldOptions } from "@/lib/admin-queries"; +import { cn } from "@/lib/utils"; const STAGE_COLOR: Record = { contacted: "bg-blue-100 text-blue-800", @@ -25,54 +26,213 @@ const STAGE_COLOR: Record = { lost: "bg-red-100 text-red-800", }; -export function LeadTable({ leads }: { leads: Lead[] }) { +const COLUMN_HEADERS = [ + "Nome", + "Email", + "Telefono", + "Azienda", + "Stato", + "Prossima Azione", + "Tag", + "Azioni", +]; +const COL_COUNT = COLUMN_HEADERS.length; + +function StatusCell({ + lead, + options, + run, +}: { + lead: LeadWithTags; + options: LeadFieldOptions; + run: (fn: () => Promise) => void; +}) { + const [isOpen, setIsOpen] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + function handleClickOutside(e: MouseEvent) { + if (containerRef.current && !containerRef.current.contains(e.target as Node)) { + setIsOpen(false); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + return ( -
- - - - Nome - Email - Azienda - Stato - Ultimo Contatto - Prossima Azione - - - - - {leads.map((lead) => ( - - - - {lead.name} - - - {lead.email || "—"} - {lead.company || "—"} - - - {lead.status.replace(/_/g, " ")} +
+
setIsOpen((v) => !v)} + className="flex items-center gap-1 px-2 py-1 rounded transition-colors duration-150 min-h-[28px] cursor-pointer hover:bg-[#f0f0f0]" + > + + {lead.status.replace(/_/g, " ")} + +
+ {isOpen && ( +
+
+ {options.status.map((stage) => ( + - - - ))} - -
+ + ))} +
+ + )} + + ); +} + +function LeadRow({ + lead, + options, +}: { + lead: LeadWithTags; + options: LeadFieldOptions; +}) { + const router = useRouter(); + const [, startTransition] = useTransition(); + const [error, setError] = useState(null); + + function run(fn: () => Promise) { + setError(null); + startTransition(async () => { + try { + await fn(); + router.refresh(); + } catch (e) { + setError(e instanceof Error ? e.message : "Errore nel salvataggio"); + } + }); + } + + return ( + <> + + + run(() => updateLeadField(lead.id, "name", v))} + /> + + + run(() => updateLeadField(lead.id, "email", v))} + /> + + + run(() => updateLeadField(lead.id, "phone", v))} + /> + + + run(() => updateLeadField(lead.id, "company", v))} + /> + + + + + + run(() => updateLeadField(lead.id, "next_action", v))} + /> + + + run(() => addLeadTag(lead.id, v))} + onRemove={(v) => run(() => removeLeadTag(lead.id, v))} + onRename={(o, n) => run(() => renameLeadTag(o, n))} + /> + + + + + + {error && ( + + + {error} + + + )} + + ); +} + +export function LeadTable({ + leads, + options, +}: { + leads: LeadWithTags[]; + options: LeadFieldOptions; +}) { + return ( +
+
+ + + + {COLUMN_HEADERS.map((header) => ( + + ))} + + + + {leads.map((lead) => ( + + ))} + +
+ {header} +
+
+ {leads.length === 0 && (
Nessun lead trovato
)}