// Reports.jsx
'use strict';
const { useState, useMemo } = React;
const REPORT_TYPES = [
{ id:'faturamento', label:'Faturamento por Período', icon:'BarChart2', desc:'Receita e despesas nos últimos meses', color:'#FF6B35' },
{ id:'servicos', label:'Serviços Mais Realizados', icon:'Wrench', desc:'Ranking dos serviços com maior volume', color:'#3B82F6' },
{ id:'clientes', label:'Clientes que Mais Geram Receita',icon:'Users', desc:'Top clientes por valor total de OSs', color:'#8B5CF6' },
{ id:'status', label:'OSs por Status', icon:'FileText', desc:'Distribuição das ordens por status', color:'#10B981' },
{ id:'tecnicos', label:'Performance por Técnico', icon:'User', desc:'OSs concluídas por técnico', color:'#F59E0B' },
];
const PIE_COLORS_REPORT = ['#FF6B35','#3B82F6','#8B5CF6','#10B981','#F59E0B','#EC4899'];
function ReportCard({ r, active, onClick }) {
return (
);
}
function Reports({ workOrders, transactions, clients }) {
const [activeReport, setActiveReport] = useState('faturamento');
const [period, setPeriod] = useState('6m');
const today = new Date();
const activeR = REPORT_TYPES.find(r => r.id === activeReport);
const data = useMemo(() => {
// Faturamento por período
const byMonth = {};
transactions.filter(t=>t.status==='pago').forEach(t=>{
const mo = t.date?.slice(0,7)||'';
if(!byMonth[mo]) byMonth[mo]={month:mo,label:`${t.date?.slice(5,7)}/${t.date?.slice(2,4)}`,receita:0,despesas:0};
if(t.type==='entrada') byMonth[mo].receita+=t.amount;
else byMonth[mo].despesas+=t.amount;
});
const faturamento = Object.values(byMonth).sort((a,b)=>a.month.localeCompare(b.month)).slice(-6);
// Serviços mais realizados
const svcCount = {};
workOrders.forEach(os=>(os.items||[]).forEach(item=>{
const k=item.desc||'Sem descrição';
if(!svcCount[k])svcCount[k]={name:k,count:0,total:0};
svcCount[k].count++;
svcCount[k].total+=item.total||0;
}));
const servicos = Object.values(svcCount).sort((a,b)=>b.count-a.count).slice(0,8)
.map(s=>({...s,name:s.name.length>22?s.name.slice(0,22)+'…':s.name}));
// Clientes top
const cliRevenue = {};
workOrders.forEach(os=>{
if(!cliRevenue[os.clientId])cliRevenue[os.clientId]={name:os.clientName,total:0,count:0};
cliRevenue[os.clientId].total+=os.total||0;
cliRevenue[os.clientId].count++;
});
const topClientes = Object.values(cliRevenue).sort((a,b)=>b.total-a.total).slice(0,6)
.map(c=>({...c,name:c.name.split(' ').slice(0,2).join(' ')}));
// OS por status
const statusData = ['Aberta','Em andamento','Aguardando peça','Concluída','Entregue'].map(s=>({
name:s, value:workOrders.filter(o=>o.status===s).length,
fill: {Aberta:'#3B82F6','Em andamento':'#F59E0B','Aguardando peça':'#EC4899',Concluída:'#10B981',Entregue:'#6366F1'}[s]
})).filter(d=>d.value>0);
// Por técnico
const techData = {};
workOrders.filter(o=>o.status==='Concluída'||o.status==='Entregue').forEach(os=>{
const t=os.tech||'Não informado';
if(!techData[t])techData[t]={name:t.split(' ')[0],concluidas:0,total:0};
techData[t].concluidas++;
techData[t].total+=os.total||0;
});
const tecnicos = Object.values(techData).sort((a,b)=>b.concluidas-a.concluidas);
return { faturamento, servicos, topClientes, statusData, tecnicos };
}, [workOrders, transactions]);
const renderChart = () => {
if (!BarChart) return
Carregando gráficos...
;
switch(activeReport) {
case 'faturamento': return (
{[
{label:'Receita Total (6m)', value:fmtCurrency(data.faturamento.reduce((s,d)=>s+d.receita,0)), color:'#FF6B35'},
{label:'Despesas Totais (6m)', value:fmtCurrency(data.faturamento.reduce((s,d)=>s+d.despesas,0)), color:'#EF4444'},
{label:'Lucro Bruto (6m)', value:fmtCurrency(data.faturamento.reduce((s,d)=>s+d.receita-d.despesas,0)), color:'#10B981'},
].map((c,i)=>(
))}
`R$${(v/1000).toFixed(0)}k`} formatTooltip={fmtCurrency}/>
);
case 'servicos': return (
Top {data.servicos.length} serviços realizados
`${v}×`}/>
);
case 'clientes': return (
Top {data.topClientes.length} clientes por receita gerada
fmtCurrency(v)}/>
{data.topClientes.map((c,i)=>(
{c.name}
{c.count} OS
{fmtCurrency(c.total)}
))}
);
case 'status': return (
({...s,color:s.fill}))} size={200}/>
{data.statusData.map(s=>(
{s.name}
{s.value}
{Math.round((s.value/workOrders.length)*100)}%
))}
);
case 'tecnicos': return (
OSs concluídas e receita gerada por técnico
{data.tecnicos.map((t,i)=>(
{t.name.slice(0,2).toUpperCase()}
{t.name}
OSs Concluídas
{t.concluidas}
Receita
{fmtCurrency(t.total)}
))}
);
default: return null;
}
};
return (
{REPORT_TYPES.map(r=>(
setActiveReport(r.id)}/>
))}
{activeR?.label}
{activeR?.desc}
Exportar PDF
{renderChart()}
);
}
Object.assign(window, { Reports });