"use client";
import { useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import {
DndContext,
DragEndEvent,
DragOverlay,
PointerSensor,
KeyboardSensor,
useSensor,
useSensors,
useDroppable,
useDraggable,
} from "@dnd-kit/core";
import { updateLeadField } from "@/app/admin/leads/actions";
import type { LeadWithTags } from "@/lib/admin-queries";
type LeadStage = "contacted" | "qualified" | "proposal_sent" | "negotiating" | "won" | "lost";
const LEAD_COLUMNS: {
id: LeadStage;
label: string;
headerClass: string;
dotClass: string;
}[] = [
{ id: "contacted", label: "Contattato", headerClass: "text-[#71717a]", dotClass: "bg-[#d4d4d8]" },
{ id: "qualified", label: "Qualificato", headerClass: "text-purple-700", dotClass: "bg-purple-400" },
{ id: "proposal_sent", label: "Offerta inviata", headerClass: "text-amber-700", dotClass: "bg-amber-400" },
{ id: "negotiating", label: "Trattativa", headerClass: "text-orange-700", dotClass: "bg-orange-400" },
{ id: "won", label: "Vinto", headerClass: "text-green-700", dotClass: "bg-green-500" },
{ id: "lost", label: "Perso", headerClass: "text-red-700", dotClass: "bg-red-400" },
];
const VALID_STAGES = LEAD_COLUMNS.map((c) => c.id) as string[];
function DroppableColumn({
id, label, headerClass, dotClass, leads, activeId,
}: {
id: LeadStage;
label: string;
headerClass: string;
dotClass: string;
leads: LeadWithTags[];
activeId: string | null;
}) {
const { setNodeRef, isOver } = useDroppable({ id });
return (
{leads.map((lead) => (
))}
{leads.length === 0 && (
Nessun lead
)}
);
}
function DraggableLeadCard({ lead, isActive }: { lead: LeadWithTags; isActive: boolean }) {
const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({ id: lead.id });
const style = transform
? { transform: `translate3d(${transform.x}px, ${transform.y}px, 0)` }
: undefined;
return (
{lead.name}
{lead.company && (
{lead.company}
)}
{lead.next_action && (
{lead.next_action}
)}
);
}
export function LeadsKanbanBoard({ leads }: { leads: LeadWithTags[] }) {
const router = useRouter();
const [, startTransition] = useTransition();
const [activeId, setActiveId] = useState(null);
const [leadStatuses, setLeadStatuses] = useState>(() =>
Object.fromEntries(leads.map((l) => [l.id, l.status as LeadStage]))
);
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
useSensor(KeyboardSensor)
);
const leadsByStage = LEAD_COLUMNS.reduce(
(acc, col) => {
acc[col.id] = leads.filter((l) => (leadStatuses[l.id] ?? l.status) === col.id);
return acc;
},
{} as Record
);
const activeLead = activeId ? leads.find((l) => l.id === activeId) : null;
function handleDragEnd(event: DragEndEvent) {
const { active, over } = event;
setActiveId(null);
if (!over) return;
const leadId = active.id as string;
const newStage = over.id as LeadStage;
const currentStage = leadStatuses[leadId];
if (!VALID_STAGES.includes(newStage)) return;
if (newStage === currentStage) return;
setLeadStatuses((prev) => ({ ...prev, [leadId]: newStage }));
startTransition(async () => {
await updateLeadField(leadId, "status", newStage);
router.refresh();
});
}
return (
setActiveId(e.active.id as string)}
onDragEnd={handleDragEnd}
>
{LEAD_COLUMNS.map((col) => (
))}
{activeLead && (
{activeLead.name}
{activeLead.company && (
{activeLead.company}
)}
)}
);
}