'use client' import { useState, useEffect } from 'react' import { Plus, Trash2, X, Search, Edit2, Copy, CheckCircle2, Circle, FileSignature } from 'lucide-react' import { formatCurrency } from '@/lib/utils' import { format } from 'date-fns' import { fr } from 'date-fns/locale' interface Client { id: string name: string email: string | null phone: string | null address: string | null siret: string | null isProfessional: boolean | null } interface SaleItem { id: string label: string description: string | null quantity: number unitPrice: number costPrice: number } interface Sale { id: string client: { id: string; name: string } date: string totalAmount: number totalCost: number paid: boolean notes: string | null items: SaleItem[] } export function SalesContent() { const [sales, setSales] = useState([]) const [clients, setClients] = useState([]) const [loading, setLoading] = useState(true) const [showForm, setShowForm] = useState(false) const [search, setSearch] = useState('') const [selectedClient, setSelectedClient] = useState('') const [clientName, setClientName] = useState('') const [clientEmail, setClientEmail] = useState('') const [clientPhone, setClientPhone] = useState('') const [clientAddress, setClientAddress] = useState('') const [clientSiret, setClientSiret] = useState('') const [isProfessional, setIsProfessional] = useState(true) const [saleDate, setSaleDate] = useState(format(new Date(), 'yyyy-MM-dd')) const [notes, setNotes] = useState('') const [items, setItems] = useState<{ id: string; label: string; description: string; quantity: number; unitPrice: number; costPrice: number }[]>([]) const [editingId, setEditingId] = useState(null) const [saving, setSaving] = useState(false) const [saved, setSaved] = useState(false) const isNewClient = !selectedClient useEffect(() => { fetchSales(); fetchClients() }, []) const fetchSales = async () => { setLoading(true) try { const res = await fetch('/api/sales') if (res.ok) { const data = await res.json() setSales(data.sales) } } finally { setLoading(false) } } const fetchClients = async () => { try { const res = await fetch('/api/clients') if (res.ok) { const data = await res.json() setClients(data.clients) } } catch {} } const openNew = () => { setEditingId(null) setSelectedClient('') setClientName('') setClientEmail('') setClientPhone('') setClientAddress('') setClientSiret('') setIsProfessional(true) setSaleDate(format(new Date(), 'yyyy-MM-dd')) setNotes('') setItems([{ id: crypto.randomUUID(), label: '', description: '', quantity: 1, unitPrice: 0, costPrice: 0 }]) setSaved(false) setShowForm(true) } const openEdit = async (sale: Sale) => { setEditingId(sale.id) setSelectedClient(sale.client.id) setClientName('') setClientEmail('') setClientPhone('') setClientAddress('') setClientSiret('') setIsProfessional(true) setSaleDate(format(new Date(sale.date), 'yyyy-MM-dd')) setNotes(sale.notes || '') setItems(sale.items.map(i => ({ id: crypto.randomUUID(), label: i.label, description: i.description || '', quantity: i.quantity, unitPrice: i.unitPrice, costPrice: i.costPrice }))) setSaved(false) setShowForm(true) } const addItem = () => { setItems([...items, { id: crypto.randomUUID(), label: '', description: '', quantity: 1, unitPrice: 0, costPrice: 0 }]) } const updateItem = (id: string, field: string, value: any) => { setItems(items.map(item => item.id === id ? { ...item, [field]: value } : item)) } const removeItem = (id: string) => { setItems(items.filter(item => item.id !== id)) } const totals = items.reduce((acc, item) => { acc.amount += (item.unitPrice || 0) * (item.quantity || 1) acc.cost += (item.costPrice || 0) * (item.quantity || 1) return acc }, { amount: 0, cost: 0 }) const handleSave = async () => { if (!selectedClient && !clientName.trim()) return if (items.length === 0 || !items.some(i => i.label.trim())) return setSaving(true) setSaved(false) try { const body: any = { clientId: selectedClient || null, clientName: isNewClient ? clientName.trim() : undefined, clientEmail: isNewClient ? clientEmail || null : undefined, clientPhone: isNewClient ? clientPhone || null : undefined, clientAddress: isNewClient ? clientAddress || null : undefined, clientSiret: isNewClient ? clientSiret || null : undefined, isProfessional: isNewClient ? isProfessional : undefined, date: saleDate, totalAmount: totals.amount, totalCost: totals.cost, notes: notes || null, items: items.filter(i => i.label.trim()).map(i => ({ label: i.label, description: i.description || null, quantity: i.quantity || 1, unitPrice: i.unitPrice || 0, costPrice: i.costPrice || 0, })), } const method = editingId ? 'PUT' : 'POST' const url = editingId ? `/api/sales/${editingId}` : '/api/sales' const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }) if (res.ok) { setSaved(true) setShowForm(false) setEditingId(null) fetchSales() fetchClients() } } finally { setSaving(false) } } const handleDuplicate = async (sale: Sale) => { if (!confirm('Dupliquer cette vente ?')) return try { const body = { clientId: sale.client.id, date: format(new Date(), 'yyyy-MM-dd'), totalAmount: sale.totalAmount, totalCost: sale.totalCost, notes: sale.notes, items: sale.items.map(i => ({ label: i.label, description: i.description, quantity: i.quantity, unitPrice: i.unitPrice, costPrice: i.costPrice, })), } await fetch('/api/sales', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }) fetchSales() } catch {} } const togglePaid = async (sale: Sale) => { try { await fetch(`/api/sales/${sale.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ paid: !sale.paid }), }) fetchSales() } catch {} } const convertToQuote = async (sale: Sale) => { if (!confirm(`Convertir cette vente en devis ? La vente sera supprimée.`)) return try { const items = sale.items.map(i => ({ type: 'CUSTOM', label: i.label, description: i.description, quantity: i.quantity, unitPrice: i.unitPrice, costPrice: i.costPrice, isRecurring: false, setupPrice: i.unitPrice, setupCost: i.costPrice, monthlyPrice: 0, monthlyCost: 0, })) const res = await fetch('/api/quotes', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ clientId: sale.client.id, name: `Devis - ${sale.client.name}`, setupTotal: sale.totalAmount, costTotal: sale.totalCost, marginTotal: sale.totalAmount - sale.totalCost, notes: sale.notes, items, }), }) if (res.ok) { await fetch(`/api/sales/${sale.id}`, { method: 'DELETE' }) fetchSales() } } catch {} } const handleDelete = async (id: string) => { if (!confirm('Supprimer cette vente ?')) return try { await fetch(`/api/sales/${id}`, { method: 'DELETE' }) fetchSales() } catch {} } const filtered = sales.filter(s => search === '' || s.client.name.toLowerCase().includes(search.toLowerCase()) ) return (

Ventes hors contrat

setSearch(e.target.value)} className="w-full pl-9 pr-4 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-white text-sm" />
{showForm && (

{editingId ? 'Modifier la vente' : 'Nouvelle vente'}

{isNewClient && (
setClientName(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm" />
setClientEmail(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm" />
setClientPhone(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm" />
setClientAddress(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm" />
{isProfessional && (
setClientSiret(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm" />
)}
)}
setSaleDate(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm" />
{items.map((item, idx) => (
updateItem(item.id, 'label', e.target.value)} placeholder={`Prestation ${idx + 1}`} className="w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm" />
updateItem(item.id, 'quantity', parseInt(e.target.value) || 1)} className="w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm text-center" />
updateItem(item.id, 'unitPrice', parseFloat(e.target.value) || 0)} className="w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm" />
updateItem(item.id, 'costPrice', parseFloat(e.target.value) || 0)} className="w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-sm" />
{formatCurrency((item.unitPrice || 0) * (item.quantity || 1))}
))}

Total vente

{formatCurrency(totals.amount)}

Coût total

{formatCurrency(totals.cost)}

Marge

= 0 ? 'text-primary-600' : 'text-red-500'}`}> {formatCurrency(totals.amount - totals.cost)} ({totals.amount > 0 ? ((totals.amount - totals.cost) / totals.amount * 100).toFixed(0) : 0}%)