// Dashboard.jsx 'use strict'; const { useState, useMemo } = React; const PIE_COLORS = { 'Aberta':'#3B82F6','Em andamento':'#F59E0B','Aguardando peça':'#EC4899','Concluída':'#10B981','Entregue':'#6366F1' }; function MetricCard({ label, value, sub, icon, iconBg, trend, trendVal, onClick }) { const up = trend === 'up'; return (
{trendVal && (
{trendVal}
)}
{value}
{label}
{sub &&
{sub}
}
); } function Dashboard({ workOrders, appointments, clients, transactions, revenueChart, setPage }) { const stats = useMemo(() => { const hoje = new Date().toISOString().split('T')[0]; const mesAtual = hoje.slice(0, 7); const abertas = workOrders.filter(o => o.status === 'Aberta' || o.status === 'Em andamento' || o.status === 'Aguardando peça'); const concMes = workOrders.filter(o => o.status === 'Concluída' && o.entryDate?.startsWith(mesAtual)); const receitaMes = transactions.filter(t => t.type === 'entrada' && t.status === 'pago' && t.date?.startsWith(mesAtual)).reduce((s, t) => s + t.amount, 0); const ativos = clients.length; const osStatus = Object.entries( workOrders.reduce((acc, o) => { acc[o.status] = (acc[o.status]||0)+1; return acc; }, {}) ).map(([name, value]) => ({ name, value, fill: PIE_COLORS[name]||'#94A3B8' })); return { abertas: abertas.length, concMes: concMes.length, receitaMes, ativos, osStatus }; }, [workOrders, transactions, clients]); const proxAgendamentos = [...appointments] .filter(a => a.date >= new Date().toISOString().split('T')[0]) .sort((a, b) => (a.date + a.time).localeCompare(b.date + b.time)) .slice(0, 5); const ultimasOS = [...workOrders] .sort((a, b) => b.entryDate?.localeCompare(a.entryDate)) .slice(0, 5); const typeColor = { 'Visita técnica':'#3B82F6','Retirada':'#10B981','Entrega':'#FF6B35','Retorno':'#8B5CF6' }; const equipIcon = { Smartphone:'Smartphone', Notebook:'Laptop', Desktop:'Monitor', TV:'Tv', Tablet:'Smartphone' }; return (
{/* Welcome bar */}

Bom dia, Ademir! 👋

Aqui está o resumo do seu negócio hoje.

{new Date().toLocaleDateString('pt-BR', { weekday:'long', day:'2-digit', month:'long', year:'numeric' })}
{/* KPI cards */}
setPage('os')} /> setPage('os')} /> setPage('financeiro')} /> setPage('clientes')} />
{/* Charts row */}
{/* Revenue chart */}

Receita vs. Despesas

Últimos 6 meses

Receita Despesas
`R$${(v/1000).toFixed(0)}k`} formatTooltip={fmtCurrency}/>
{/* Pie chart */}

OSs por Status

Total: {workOrders.length} ordens

({...s,color:s.fill}))} size={160}/>
{stats.osStatus.map(s => (
{s.name}
{s.value}
))}
{/* Bottom lists */}
{/* Próximos agendamentos */}

Próximos Agendamentos

{proxAgendamentos.length === 0 ? :
{proxAgendamentos.map(a => (

{a.title}

{fmtDate(a.date)} às {a.time}

{a.type}
))}
}
{/* Últimas OSs */}

Últimas Ordens de Serviço

{ultimasOS.map(os => (
#{os.num} {os.clientName}

{os.equipment?.brand} {os.equipment?.model}

))}
); } Object.assign(window, { Dashboard });