// Clients.jsx
'use strict';
const { useState, useEffect } = React;
function ClientModal({ open, onClose, onSave, editClient }) {
const empty = { name:'', cpf:'', phone:'', email:'', address:'', notes:'' };
const [form, setForm] = useState(empty);
const [errors, setErrors] = useState({});
useEffect(() => { if (open) { setForm(editClient ? {...editClient} : {...empty}); setErrors({}); } }, [open, editClient]);
const set = (k,v) => setForm(f=>({...f,[k]:v}));
const validate = () => {
const e = {};
if (!form.name.trim()) e.name = 'Nome obrigatório';
if (!form.phone.trim()) e.phone = 'Telefone obrigatório';
setErrors(e);
return Object.keys(e).length === 0;
};
const handleSave = () => { if (!validate()) return; onSave(form); onClose(); };
return (
);
}
function ClientDetailModal({ open, onClose, client, workOrders, onEdit, onDelete }) {
if (!client) return null;
const clientOS = workOrders.filter(o => o.clientId === client.id).sort((a,b)=>b.num?.localeCompare(a.num));
return (
{client.name.split(' ').map(w=>w[0]).slice(0,2).join('')}
{client.name}
Cliente desde {fmtDate(client.createdAt)}
{client.phone &&
{client.phone}
}
{client.email &&
{client.email}
}
{client.address &&
{client.address}
}
{client.cpf &&
CPF/CNPJ: {client.cpf}
}
{client.notes &&
{client.notes}
}
{clientOS.length > 0 && (
Histórico de OSs ({clientOS.length})
{clientOS.map(os=>(
#{os.num}{os.equipment?.brand} {os.equipment?.model}
{fmtCurrency(os.total||0)}
))}
)}
Fechar
{ if (onDelete){ onClose(); onDelete(client); } }} className="text-red-500 hover:bg-red-50">Excluir
{onClose();onEdit(client);}}>Editar
);
}
function Clients({ clients, workOrders, onAdd, onUpdate, onDelete, addToast }) {
const [search, setSearch] = useState('');
const [showModal, setShowModal] = useState(false);
const [showDetail, setShowDetail] = useState(false);
const [editClient, setEditClient] = useState(null);
const [detailClient, setDetailClient] = useState(null);
const [confirm, setConfirm] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(()=>{ const t=setTimeout(()=>setLoading(false),500); return()=>clearTimeout(t); },[]);
const filtered = clients.filter(c => {
const q = search.toLowerCase();
return !q || c.name?.toLowerCase().includes(q) || c.phone?.includes(q) || c.email?.toLowerCase().includes(q) || c.cpf?.includes(q);
}).sort((a,b)=>a.name.localeCompare(b.name,'pt-BR'));
const getLastOS = c => {
const cos = workOrders.filter(o=>o.clientId===c.id).sort((a,b)=>b.entryDate?.localeCompare(a.entryDate));
return cos[0];
};
const handleSave = form => {
if (editClient) { onUpdate(editClient.id, form); addToast({type:'success',msg:'Cliente atualizado!'}); }
else { onAdd({...form, id:'CLI'+String(clients.length+1).padStart(3,'0'), createdAt:new Date().toISOString().split('T')[0]}); addToast({type:'success',msg:'Cliente cadastrado!'}); }
};
return (
{setEditClient(null);setShowModal(true);}}>Novo Cliente}/>
{filtered.length} resultado(s)
{loading ? Array.from({length:6}).map((_,i)=>) :
filtered.length===0 ? setShowModal(true)}/> :
| Nome |
Telefone |
E-mail |
OSs |
Última OS |
Ações |
{filtered.map(c => {
const lastOS = getLastOS(c);
const osCount = workOrders.filter(o=>o.clientId===c.id).length;
return (
{setDetailClient(c);setShowDetail(true);}}
className="border-b border-slate-50 hover:bg-slate-50 active:bg-slate-100 transition-colors cursor-pointer">
{c.name.split(' ').map(w=>w[0]).slice(0,2).join('')}
{c.name}
{c.phone}
{c.cpf && {c.cpf} }
|
{c.phone} |
{c.email||'—'} |
0?'#EFF6FF':'#F1F5F9', color: osCount>0?'#3B82F6':'#94A3B8'}}>{osCount}
|
{lastOS ? : —}
|
|
);
})}
}
{setShowModal(false);setEditClient(null);}} onSave={handleSave} editClient={editClient}/>
{setShowDetail(false);setDetailClient(null);}} client={detailClient} workOrders={workOrders}
onEdit={c=>{setEditClient(c);setShowModal(true);}}
onDelete={c=>setConfirm(c.id)}/>
setConfirm(null)}
onConfirm={()=>{onDelete(confirm);addToast({type:'info',msg:'Cliente excluído.'});}}
message="Excluir este cliente? As OSs vinculadas não serão removidas."/>
);
}
Object.assign(window, { Clients });