// ============================================================
// JuFlow — Atendimentos (Appointments list)
// ============================================================
const { useState: useStAT, useMemo: useMemoAT } = React;
const UAT = window.JFUtils;
const Atendimentos = ({ appointments, onViewAppt, onNewAppt }) => {
const [search, setSearch] = useStAT('');
const [statusF, setStatusF] = useStAT('all');
const [profF, setProfF] = useStAT('all');
const [period, setPeriod] = useStAT('30');
const filtered = useMemoAT(() => {
const cutoff = UAT.addDays(window.JF.TODAY, -Number(period));
let list = appointments.filter(a => new Date(a.datetime) >= cutoff);
if (statusF !== 'all') list = list.filter(a => a.status === statusF);
if (profF !== 'all') list = list.filter(a => a.professionalId === Number(profF));
if (search) {
const q = search.toLowerCase();
list = list.filter(a => {
const c = UAT.getClient(a.clientId);
const s = UAT.getService(a.serviceId);
return c?.name.toLowerCase().includes(q) || s?.name.toLowerCase().includes(q);
});
}
return list.sort((a,b) => new Date(b.datetime) - new Date(a.datetime));
}, [appointments, statusF, profF, period, search]);
const totalRevenue = filtered.filter(a=>a.status==='completed').reduce((s,a)=>s+a.price,0);
const completedCnt = filtered.filter(a=>a.status==='completed').length;
const cancelledCnt = filtered.filter(a=>a.status==='cancelled'||a.status==='no_show').length;
return (
{/* Metrics */}
{/* Filters */}
{[
{ val:statusF, set:setStatusF, opts:[['all','Todos'],['completed','Realizados'],['confirmed','Confirmados'],['pending','Pendentes'],['cancelled','Cancelados'],['no_show','Faltas']] },
].map(({val,set,opts},i) => (
))}
{filtered.length} registros
{/* Table */}
{filtered.length === 0 ? (
) : (
{['Data/Hora','Cliente','Serviço','Profissional','Duração','Valor','Pagamento','Status','Ações'].map(h => (
| {h} |
))}
{filtered.slice(0,50).map((appt, i) => {
const client = UAT.getClient(appt.clientId);
const service = UAT.getService(appt.serviceId);
const prof = UAT.getProfessional(appt.professionalId);
return (
onViewAppt(appt)}
onMouseEnter={e=>e.currentTarget.style.background='#F9FAFB'}
onMouseLeave={e=>e.currentTarget.style.background='#fff'}
>
|
{UAT.formatDate(appt.datetime)}
{UAT.formatTime(appt.datetime)}
|
|
{service?.name}
|
{prof?.name?.split(' ').slice(0,2).join(' ')}
|
{appt.duration}min |
{UAT.formatCurrency(appt.price)} |
{appt.paymentMethod || '—'} |
|
e.stopPropagation()}>
|
);
})}
{filtered.length > 50 && (
Exibindo 50 de {filtered.length} registros. Use os filtros para refinar.
)}
)}
);
};
Object.assign(window, { Atendimentos });