import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { getDirectusClient, readItems, createItem, updateItem, deleteItem } from '@/lib/directus/client'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Search, Plus, Trash2, Edit2, UserPlus, Mail, Building } from 'lucide-react'; import { toast } from 'sonner'; import { formatDistanceToNow } from 'date-fns'; const client = getDirectusClient(); interface Lead { id: string; name: string; email: string; company: string; niche: string; status: string; source: string; date_created: string; } export default function LeadsManager() { const queryClient = useQueryClient(); const [search, setSearch] = useState(''); const [editorOpen, setEditorOpen] = useState(false); const [editingLead, setEditingLead] = useState>({}); // 1. Fetch const { data: leads = [], isLoading } = useQuery({ queryKey: ['leads'], queryFn: async () => { // @ts-ignore const res = await client.request(readItems('leads', { limit: -1, sort: ['-date_created'] })); return res as unknown as Lead[]; } }); // 2. Mutations const createMutation = useMutation({ mutationFn: async (newItem: Partial) => { // @ts-ignore await client.request(createItem('leads', newItem)); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['leads'] }); toast.success('Lead added'); setEditorOpen(false); } }); const updateMutation = useMutation({ mutationFn: async (updates: Partial) => { // @ts-ignore await client.request(updateItem('leads', updates.id!, updates)); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['leads'] }); toast.success('Lead updated'); setEditorOpen(false); } }); const deleteMutation = useMutation({ mutationFn: async (id: string) => { // @ts-ignore await client.request(deleteItem('leads', id)); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['leads'] }); toast.success('Lead deleted'); } }); const handleSave = () => { if (!editingLead.name || !editingLead.email) { toast.error('Name and Email are required'); return; } if (editingLead.id) { updateMutation.mutate(editingLead); } else { createMutation.mutate({ ...editingLead, status: editingLead.status || 'new', source: 'manual' }); } }; const getStatusColor = (status: string) => { switch (status) { case 'new': return 'bg-blue-500/10 text-blue-500 border-blue-500/20'; case 'contacted': return 'bg-yellow-500/10 text-yellow-500 border-yellow-500/20'; case 'qualified': return 'bg-purple-500/10 text-purple-500 border-purple-500/20'; case 'converted': return 'bg-green-500/10 text-green-500 border-green-500/20'; case 'rejected': return 'bg-red-500/10 text-red-500 border-red-500/20'; default: return 'bg-zinc-500/10 text-zinc-500'; } }; const filtered = leads.filter(l => l.name?.toLowerCase().includes(search.toLowerCase()) || l.company?.toLowerCase().includes(search.toLowerCase()) || l.email?.toLowerCase().includes(search.toLowerCase()) ); return (
{/* Toolbar */}
setSearch(e.target.value)} className="pl-9 bg-zinc-950 border-zinc-800" />
{/* Table */}
Name Contact Company Status Actions {filtered.length === 0 ? ( No leads found. ) : ( filtered.map((lead) => ( {lead.name}
Added {formatDistanceToNow(new Date(lead.date_created), { addSuffix: true })}
{lead.email}
{lead.company || '-'}
{lead.status}
)) )}
{/* Edit Modal */} {editingLead.id ? 'Edit Lead' : 'New Lead'}
setEditingLead({ ...editingLead, name: e.target.value })} className="bg-zinc-950 border-zinc-800" placeholder="John Doe" />
setEditingLead({ ...editingLead, email: e.target.value })} className="bg-zinc-950 border-zinc-800" placeholder="john@example.com" />
setEditingLead({ ...editingLead, company: e.target.value })} className="bg-zinc-950 border-zinc-800" placeholder="Acme Inc" />
); }