// ============================================================ // JuFlow — Clients List // ============================================================ const { useState: useStCL, useMemo: useMemosCL } = React; const UCL = window.JFUtils; const Clientes = ({ appointments, onViewClient, onNewAppt }) => { const [search, setSearch] = useStCL(''); const [statusF, setStatusF] = useStCL('all'); const [sortBy, setSortBy] = useStCL('name'); const [sortDir, setSortDir] = useStCL('asc'); const [showNew, setShowNew] = useStCL(false); const clients = useMemosCL(() => { let list = window.JF.CLIENTS; if (search) { const q = search.toLowerCase(); list = list.filter(c => c.name.toLowerCase().includes(q) || c.phone.includes(q) || c.email.toLowerCase().includes(q) ); } if (statusF !== 'all') list = list.filter(c => c.status === statusF); list = [...list].sort((a, b) => { let av, bv; if (sortBy === 'name') { av = a.name; bv = b.name; } else if (sortBy === 'spent') { av = a.totalSpent; bv = b.totalSpent; } else if (sortBy === 'visits'){ av = a.visits; bv = b.visits; } else if (sortBy === 'last') { av = a.lastVisit || ''; bv = b.lastVisit || ''; } else { av = a.name; bv = b.name; } if (sortDir === 'asc') return av > bv ? 1 : -1; return av < bv ? 1 : -1; }); return list; }, [search, statusF, sortBy, sortDir]); const toggleSort = (col) => { if (sortBy === col) setSortDir(d => d === 'asc' ? 'desc' : 'asc'); else { setSortBy(col); setSortDir('asc'); } }; const SortIcon = ({ col }) => { if (sortBy !== col) return ; return ; }; const getFrequency = (clientId) => { const count = appointments.filter(a => a.clientId === clientId && a.status === 'completed').length; if (count >= 20) return { label:'Frequente', color:'#059669' }; if (count >= 8) return { label:'Regular', color:'#0891B2' }; if (count >= 3) return { label:'Ocasional', color:'#D97706' }; return { label:'Novo', color:'#7C3AED' }; }; const totalRevenue = window.JF.CLIENTS.reduce((s,c) => s+c.totalSpent, 0); const activeCount = window.JF.CLIENTS.filter(c => c.status==='active').length; const vipCount = window.JF.CLIENTS.filter(c => c.tags.includes('VIP')).length; const inactiveCount = window.JF.CLIENTS.filter(c => c.status==='inactive').length; return (
{/* Top metrics */}
{/* Filters + actions */}
{[['all','Todos'],['active','Ativos'],['inactive','Inativos']].map(([v,l]) => ( ))}
{clients.length} cliente{clients.length!==1?'s':''}
{/* Table */}
{clients.length === 0 ? ( setShowNew(true)}>Novo Cliente} /> ) : (
{[ { label:'Cliente', col:'name', w:'28%' }, { label:'Telefone', col:null, w:'14%' }, { label:'Tags', col:null, w:'14%' }, { label:'Última visita', col:'last', w:'12%' }, { label:'Total gasto', col:'spent', w:'12%' }, { label:'Frequência', col:'visits', w:'11%' }, { label:'Ações', col:null, w:'9%' }, ].map(h => ( ))} {clients.map((client, i) => { const freq = getFrequency(client.id); const daysSince = client.lastVisit ? UCL.daysBetween(client.lastVisit, window.JF.TODAY) : 999; return ( onViewClient(client)} onMouseEnter={e => e.currentTarget.style.background='#F9FAFB'} onMouseLeave={e => e.currentTarget.style.background='#fff'} > ); })}
h.col && toggleSort(h.col)} > {h.label} {h.col && }
{client.name}
{client.email}
{client.phone}
{client.tags.map(tag => )} {client.tags.length === 0 && }
60 ? '#EF4444' : '#374151', fontWeight: daysSince > 60 ? 600 : 400 }}> {client.lastVisit ? UCL.formatDate(client.lastVisit) : '—'}
{daysSince > 60 && (
{daysSince}d atrás
)}
{UCL.formatCurrency(client.totalSpent)}
{client.visits} sessões
{freq.label} e.stopPropagation()}>
)}
); }; // ─── NEW CLIENT MODAL ───────────────────────────────────── const NewClientModal = ({ open, onClose, onSave, addToast }) => { const [form, setForm] = useStCL({ name:'', phone:'', email:'', cpf:'', birthdate:'', notes:'' }); const set = (k,v) => setForm(f => ({...f,[k]:v})); const handleSave = () => { if (!form.name || !form.phone) { addToast('Nome e telefone são obrigatórios.','error'); return; } onSave({ ...form, id:Date.now(), tags:[], status:'active', totalSpent:0, lastVisit:null, visits:0 }); addToast('Cliente cadastrado com sucesso!','success'); onClose(); }; return ( } >
set('name',v)} placeholder="Maria Silva" required style={{ gridColumn:'1/-1' }} /> set('phone',v)} placeholder="(11) 99999-9999" required icon="Phone" /> set('email',v)} placeholder="maria@email.com" icon="Mail" /> set('cpf',v)} placeholder="000.000.000-00" /> set('birthdate',v)} icon="Cake" />