/* ============================================================ EMPRESA > PAINEL FINANCEIRO (dinâmico) Agora com: status de pagamento real, SLA calculado a partir de timestamps reais, filtro de período no gráfico (últimos 6 meses / ano / intervalo personalizado) e exportação em CSV. ============================================================ */ function PageFinanceiro({ tickets, caixa, orcamentos, gastos, terceirizados, onSaveTerceirizado, onDeleteTerceirizado, clientes, equipe }){ const [novoParceiro,setNovoParceiro] = useState(''); const [novoServico,setNovoServico] = useState(''); const [novoCusto,setNovoCusto] = useState(''); function submitTerceirizado(e){ e.preventDefault(); if(!novoParceiro || !novoServico || novoCusto==='') return; onSaveTerceirizado({ parceiro:novoParceiro, servico:novoServico, custo:parseFloat(novoCusto) }); setNovoParceiro(''); setNovoServico(''); setNovoCusto(''); } const emAtendimento = tickets.filter(t=>t.status==='em_atendimento').length; const aguardando = tickets.filter(t=>t.status==='aguardando_retorno').length; const encerrados = tickets.filter(t=>t.status==='encerrado').length; const recentes = tickets.slice(0,4); const comSla = tickets.filter(t=>t.primeiraRespostaEm); const slaMedioMs = comSla.length ? comSla.reduce((s,t)=>s+(new Date(t.primeiraRespostaEm)-new Date(t.criadoEm)),0)/comSla.length : null; const withStatus = caixa.map(e=>({ ...e, statusEfetivo:statusEfetivoCaixa(e), ref:mesReferencia(e.vencimento) })); const recebidoMes = withStatus.filter(e=>e.statusEfetivo==='pago' && e.ref.group==='atual').reduce((s,e)=>s+e.valor,0); const comissoesAtivas = orcamentos.filter(o=>o.status!=='Reprovado').reduce((s,o)=>s+o.comissao,0); const recebimentosTotal = recebidoMes + comissoesAtivas; const gastosMes = gastos.filter(g=>mesReferencia(g.vencimento).group==='atual').reduce((s,g)=>s+g.valor,0); const gastosFuturos = gastos.filter(g=>mesReferencia(g.vencimento).group==='proximo').reduce((s,g)=>s+g.valor,0); const saldoAtual = recebimentosTotal - gastosMes; const aReceberMes = withStatus.filter(e=>e.statusEfetivo==='pendente' && e.ref.group==='atual').reduce((s,e)=>s+e.valor,0); const atrasadoTotal = withStatus.filter(e=>e.statusEfetivo==='atrasado').reduce((s,e)=>s+e.valor,0); // ---------- Filtro de período do gráfico ---------- const anosDisponiveis = Array.from(new Set(caixa.map(e=>e.vencimento.slice(0,4)))).sort(); const anoAtual = String(new Date().getFullYear()); const [modoPeriodo, setModoPeriodo] = useState('recentes'); // 'recentes' | 'ano' | 'intervalo' const [anoSelecionado, setAnoSelecionado] = useState(anosDisponiveis.includes(anoAtual) ? anoAtual : (anosDisponiveis[anosDisponiveis.length-1]||anoAtual)); const [dataDe, setDataDe] = useState(isoDate(-180)); const [dataAte, setDataAte] = useState(isoDate(0)); let series; if(modoPeriodo==='ano') series = yearSeries(caixa, parseInt(anoSelecionado,10)); else if(modoPeriodo==='intervalo') series = rangeSeries(caixa, dataDe, dataAte); else series = monthlySeries(caixa, 6); const maxV = Math.max(1, ...series.map(s=>s.total)); function exportarPeriodo(){ const linhas = withStatus .filter(e=>{ if(modoPeriodo==='ano') return e.vencimento.slice(0,4)===anoSelecionado; if(modoPeriodo==='intervalo') return e.vencimento>=dataDe && e.vencimento<=dataAte; return true; }) .map(e=>({ cliente:e.cliente, valor:e.valor.toFixed(2), vencimento:e.vencimento, status:STATUS_PAGAMENTO_LABELS[e.statusEfetivo], dataPagamento:e.dataPagamento||'' })); exportarCSV('financeiro_periodo.csv', linhas, [ { key:'cliente', label:'Cliente' }, { key:'valor', label:'Valor' }, { key:'vencimento', label:'Vencimento' }, { key:'status', label:'Status' }, { key:'dataPagamento', label:'Data de Pagamento' }, ]); } const seg = { Premium:0, 'Padrão':0, 'Básico':0 }; clientes.forEach(c=>{ const meses = PERIODICIDADE_MESES[c.periodicidade] || 1; const vMensalEquivalente = (c.valorMensal||0) / meses; if(vMensalEquivalente>=800) seg.Premium++; else if(vMensalEquivalente>=400) seg['Padrão']++; else seg['Básico']++; }); const totalClientes = Math.max(1, clientes.length); const segArr = [ { label:'Premium', pct: Math.round(seg.Premium/totalClientes*100), color:'var(--laranja)' }, { label:'Padrão', pct: Math.round(seg['Padrão']/totalClientes*100), color:'var(--rosa)' }, { label:'Básico', pct: Math.round(seg['Básico']/totalClientes*100), color:'var(--carvao-3)' }, ]; function servicoStatus(clienteNome){ const cts = tickets.filter(t=>t.cliente===clienteNome); if(cts.some(t=>t.status==='em_atendimento')) return {label:'Em curso', cls:'b-laranja'}; if(cts.some(t=>t.status==='aguardando_retorno')) return {label:'Pendente', cls:'b-rosa'}; return {label:'Concluído', cls:'b-green'}; } return (

Suporte — Visão Geral

{recentes.map(t=>( ))}
ClienteChamadoStatusAberto em
{t.cliente}#{t.numero} · {t.title}{STATUS_LABELS[t.status]}{fmtDataHora(t.criadoEm)}
=0?'positivo':'negativo'} dir={saldoAtual>=0?'up':'down'}/> 0?'down':'up'} tone="pink"/>

Gráfico Financeiro

{modoPeriodo==='ano' && (
)} {modoPeriodo==='intervalo' && (
setDataDe(e.target.value)}/>
setDataAte(e.target.value)}/>
)}
{series.map((s,i)=>())} {series.length===0 &&

Nenhum lançamento nesse período.

}

Clientes por Segmento

{clientes.length}
{segArr.map(s=>(
{s.label} — {s.pct}%
))}

Orçamentos (Produtos e Comissões)

{orcamentos.slice(0,4).map(o=>())}
ClienteProdutoComissão
{o.cliente}{o.item}{fmtBRL(o.comissao)}

Serviços Prestados

{clientes.slice(0,4).map(c=>{ const st=servicoStatus(c.nome); return ( );})}
ServiçoClienteStatus
{c.servico}{c.nome}{st.label}

Serviços Terceirizados

{terceirizados.map(t=>( ))} {terceirizados.length===0 && }
ParceiroServiçoCusto
{t.parceiro}{t.servico}{fmtBRL(t.custo)}
Nenhum terceirizado cadastrado ainda.
setNovoParceiro(e.target.value)}/> setNovoServico(e.target.value)}/> setNovoCusto(e.target.value)}/>
); }