Iniciativa Cidadã Independente · Transparência Parlamentar
TSX184 linhas8.653 bytes

apps/web/src/components/senador/AbaDespesas.tsx

SHA-256

b2a90bca8091a530cf89f561b0c253b2d823e0ec144a15f0223c1f73fa394cbc

Somente leiturafonte-acf635076da2
'use client'

import { useEffect, useState } from 'react'
import { getDespesasDetalhadas } from '@/lib/api'
import type { DespesasResponse } from '@/lib/types'
import Pagination from './Pagination'

const MESES = ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez']

const fmt = new Intl.NumberFormat('pt-BR', {
  style: 'currency', currency: 'BRL', maximumFractionDigits: 2,
})

export default function AbaDespesas({ codigo }: { codigo: string }) {
  const [ano, setAno] = useState<number>(new Date().getFullYear())
  const [pagina, setPagina] = useState(1)
  const [tipoFiltro, setTipoFiltro] = useState('')
  const [forn, setForn] = useState('')
  const [data, setData] = useState<DespesasResponse | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    setLoading(true)
    getDespesasDetalhadas(codigo, { ano, pagina, porPagina: 30, tipo: tipoFiltro || undefined, fornecedor: forn || undefined })
      .then(setData)
      .finally(() => setLoading(false))
  }, [codigo, ano, pagina, tipoFiltro, forn])

  return (
    <div className="space-y-6">
      <div className="flex flex-wrap items-center gap-3 text-sm">
        <label className="text-muted">Ano:</label>
        <select
          value={ano}
          onChange={(e) => { setAno(Number(e.target.value)); setPagina(1) }}
          className="rounded-sm border border-border bg-surface px-2 py-1 text-sm"
        >
          {[2026, 2025, 2024, 2023].map((a) => <option key={a} value={a}>{a}</option>)}
        </select>
        <input
          placeholder="Filtrar por tipo"
          value={tipoFiltro}
          onChange={(e) => { setTipoFiltro(e.target.value); setPagina(1) }}
          className="rounded-sm border border-border bg-surface px-2 py-1 text-sm w-44"
        />
        <input
          placeholder="Filtrar por fornecedor"
          value={forn}
          onChange={(e) => { setForn(e.target.value); setPagina(1) }}
          className="rounded-sm border border-border bg-surface px-2 py-1 text-sm w-52"
        />
      </div>

      {loading ? (
        <div className="space-y-3">
          <div className="h-24 rounded-sm bg-surface border border-border animate-pulse" />
          <div className="h-64 rounded-sm bg-surface border border-border animate-pulse" />
        </div>
      ) : data ? (
        <>
          <section className="grid grid-cols-2 sm:grid-cols-3 gap-px bg-border border border-border rounded-sm overflow-hidden">
            <Stat label="Total no ano" value={fmt.format(data.resumo.totalAno)} />
            <Stat label="Transações" value={String(data.resumo.totalTransacoes)} />
            <Stat label="Mensal médio" value={fmt.format(data.resumo.totalAno / 12)} />
          </section>

          {data.resumo.porTipo.length > 0 && (
            <section>
              <h3 className="font-serif text-sm font-semibold text-ink mb-2">Por tipo de despesa</h3>
              <ul className="divide-y divide-border rounded-sm border border-border bg-surface">
                {data.resumo.porTipo.map((t) => {
                  const pct = data.resumo.totalAno > 0 ? (t.total / data.resumo.totalAno) * 100 : 0
                  return (
                    <li key={t.tipo} className="px-3 py-2 text-sm">
                      <div className="flex items-center justify-between gap-3">
                        <span className="text-ink line-clamp-1 flex-1">{t.tipo}</span>
                        <span className="font-mono text-muted shrink-0">{fmt.format(t.total)}</span>
                        <span className="font-mono text-xs text-subtle shrink-0 w-12 text-right">{pct.toFixed(1)}%</span>
                      </div>
                      <div className="mt-1 h-1 rounded-full bg-border">
                        <div className="h-1 rounded-full bg-primary/40" style={{ width: `${pct}%` }} />
                      </div>
                    </li>
                  )
                })}
              </ul>
            </section>
          )}

          {data.resumo.topFornecedores.length > 0 && (
            <section>
              <h3 className="font-serif text-sm font-semibold text-ink mb-2">Top fornecedores</h3>
              <ul className="divide-y divide-border rounded-sm border border-border bg-surface">
                {data.resumo.topFornecedores.slice(0, 10).map((f) => (
                  <li key={f.cpfCnpj || f.nome} className="px-3 py-2 text-sm flex items-center justify-between gap-3">
                    <div className="min-w-0 flex-1">
                      <p className="text-ink truncate">{f.nome}</p>
                      <p className="font-mono text-[11px] text-subtle">{f.cpfCnpj} · {f.transacoes} transações</p>
                    </div>
                    <span className="font-mono text-muted shrink-0">{fmt.format(f.total)}</span>
                  </li>
                ))}
              </ul>
            </section>
          )}

          {data.resumo.porMes.length > 0 && (
            <section>
              <h3 className="font-serif text-sm font-semibold text-ink mb-2">Por mês</h3>
              <ul className="grid grid-cols-3 sm:grid-cols-6 gap-px bg-border border border-border rounded-sm overflow-hidden">
                {Array.from({ length: 12 }).map((_, idx) => {
                  const mesData = data.resumo.porMes.find((m) => m.mes === idx + 1)
                  return (
                    <li key={idx} className="bg-surface px-2 py-2 text-center">
                      <p className="text-[10px] uppercase tracking-wider text-muted">{MESES[idx]}</p>
                      <p className="font-mono text-xs text-ink mt-0.5">{mesData ? fmt.format(mesData.total) : '—'}</p>
                    </li>
                  )
                })}
              </ul>
            </section>
          )}

          <section>
            <h3 className="font-serif text-sm font-semibold text-ink mb-2">
              Transações ({data.total} total)
            </h3>
            {data.data.length === 0 ? (
              <p className="text-muted text-sm">Nenhuma transação para os filtros aplicados.</p>
            ) : (
              <>
                <div className="overflow-x-auto rounded-sm border border-border">
                  <table className="w-full text-xs">
                    <thead className="bg-panel">
                      <tr className="text-left text-[10px] uppercase tracking-wider text-muted">
                        <th className="px-2 py-1.5 font-medium">Data</th>
                        <th className="px-2 py-1.5 font-medium">Tipo</th>
                        <th className="px-2 py-1.5 font-medium">Fornecedor</th>
                        <th className="px-2 py-1.5 font-medium">CPF/CNPJ</th>
                        <th className="px-2 py-1.5 font-medium text-right">Valor</th>
                      </tr>
                    </thead>
                    <tbody className="divide-y divide-border bg-surface">
                      {data.data.map((d) => (
                        <tr key={d.id} className="hover:bg-panel/30">
                          <td className="px-2 py-1.5 font-mono text-muted whitespace-nowrap">{d.data ? formatBR(d.data) : `${MESES[d.mes - 1]}/${d.ano}`}</td>
                          <td className="px-2 py-1.5 text-muted line-clamp-1 max-w-[24ch]" title={d.tipoDespesa}>{d.tipoDespesa}</td>
                          <td className="px-2 py-1.5 text-ink line-clamp-1 max-w-[24ch]" title={d.fornecedor}>{d.fornecedor}</td>
                          <td className="px-2 py-1.5 font-mono text-subtle whitespace-nowrap">{d.cpfCnpj}</td>
                          <td className="px-2 py-1.5 font-mono text-right text-ink whitespace-nowrap">{fmt.format(d.valor)}</td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
                <div className="mt-3">
                  <Pagination pagina={data.pagina} porPagina={data.porPagina} total={data.total} onChange={setPagina} />
                </div>
              </>
            )}
          </section>
        </>
      ) : (
        <p className="text-muted text-sm">Sem dados.</p>
      )}
    </div>
  )
}

function Stat({ label, value }: { label: string; value: string }) {
  return (
    <div className="bg-surface px-3 py-3 text-center">
      <p className="text-[10px] uppercase tracking-wider text-muted">{label}</p>
      <p className="font-serif text-lg text-ink mt-0.5">{value}</p>
    </div>
  )
}

function formatBR(d: string): string {
  if (!d) return ''
  const [y, m, day] = d.split('-')
  return `${day}/${m}/${y}`
}