import { useEffect, useMemo, useState } from 'react'; import { Alert, Box, Button, Card, CardContent, Chip, CircularProgress, Collapse, Divider, IconButton, MenuItem, Pagination, Paper, Stack, TextField, ToggleButton, ToggleButtonGroup, Tooltip, Typography, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, useMediaQuery, useTheme, } from '@mui/material'; import AddIcon from '@mui/icons-material/Add'; import EditIcon from '@mui/icons-material/Edit'; import FilterAltIcon from '@mui/icons-material/FilterAlt'; import RestartAltIcon from '@mui/icons-material/RestartAlt'; import SearchIcon from '@mui/icons-material/Search'; import SwapHorizIcon from '@mui/icons-material/SwapHoriz'; import DeleteIcon from '@mui/icons-material/Delete'; import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import UndoIcon from '@mui/icons-material/Undo'; import HelpIcon from '@mui/icons-material/Help'; import AccountBalanceIcon from '@mui/icons-material/AccountBalance'; import CalendarMonthIcon from '@mui/icons-material/CalendarMonth'; import CategoryIcon from '@mui/icons-material/Category'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import TableRowsIcon from '@mui/icons-material/TableRows'; import ViewAgendaIcon from '@mui/icons-material/ViewAgenda'; import { Link as RouterLink } from 'react-router-dom'; import { deletarMovimento, listarMovimentos, type DataCampo, type ListarMovimentosParams, } from '../services/movimentosService'; import type { Movimento, MovimentoStatus, MovimentoTipo, MovimentosPagination, MovimentosSummary, } from '../types/movimentoTypes'; import { listarBancos, listarCentrosCusto, listarClientes, } from '../../referencias/services/referenciasService'; import type { Banco, CentroCusto, Cliente, } from '../../referencias/types/referenciasTypes'; const LIMITE_PADRAO = 20; const DIAS_ALERTA_VENCIMENTO = 5; const LIMITE_AGRUPADO = 500; type ModoVisualizacao = 'tabela' | 'agrupado'; type GrupoCentroMovimentos = { chave: string; nome: string; totalEntradas: number; totalSaidas: number; saldo: number; quantidade: number; movimentos: Movimento[]; }; type GrupoBancoMovimentos = { chave: string; nome: string; totalEntradas: number; totalSaidas: number; saldo: number; quantidade: number; centros: GrupoCentroMovimentos[]; }; type GrupoDiaMovimentos = { chave: string; dataLabel: string; totalEntradas: number; totalSaidas: number; saldo: number; quantidade: number; bancos: GrupoBancoMovimentos[]; }; function inicioMesAtual() { const hoje = new Date(); return new Date(hoje.getFullYear(), hoje.getMonth(), 1).toISOString().slice(0, 10); } function fimMesAtual() { const hoje = new Date(); return new Date(hoje.getFullYear(), hoje.getMonth() + 1, 0).toISOString().slice(0, 10); } function formatarValor(valor: number) { return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL', }).format(Number(valor || 0)); } function formatarData(data: string | null) { if (!data) return '-'; return new Date(`${String(data).slice(0, 10)}T00:00:00`).toLocaleDateString('pt-BR'); } function campoDataLabel(campo: DataCampo) { const labels: Record = { dataentrada: 'Entrada', datavencimento: 'Vencimento', databaixa: 'Baixa', insert_date: 'Cadastro', update_date: 'Atualização', deleted_at: 'Exclusão', }; return labels[campo]; } function movimentoChipColor(movimento: string) { if (movimento === 'Entrada') return 'success'; if (movimento === 'Saida') return 'error'; if (movimento === 'Sangria') return 'warning'; return 'default'; } function statusChipColor(status: string) { if (status === 'Pago' || status === 'Recebido') return 'success'; if (status === 'A pagar' || status === 'A receber') return 'warning'; return 'default'; } function getDataPrincipal(item: Movimento, campo: DataCampo) { if (campo === 'dataentrada') return item.dataentrada; if (campo === 'datavencimento') return item.datavencimento; if (campo === 'databaixa') return item.databaixa; if (campo === 'insert_date') return item.insert_date; if (campo === 'update_date') return item.update_date; return item.datavencimento; } function getReferencia(item: Movimento) { if (item.cliente_nome) return item.cliente_nome; if (item.banco_referencia_descricao) return item.banco_referencia_descricao; return '-'; } function dataSomenteDia(data: string | null | undefined) { if (!data) return null; const texto = String(data).slice(0, 10); if (!/^\d{4}-\d{2}-\d{2}$/.test(texto)) { return null; } return new Date(`${texto}T00:00:00`); } function diferencaDias(dataAlvo: Date, dataBase: Date) { const umDiaMs = 24 * 60 * 60 * 1000; const alvo = new Date( dataAlvo.getFullYear(), dataAlvo.getMonth(), dataAlvo.getDate() ); const base = new Date( dataBase.getFullYear(), dataBase.getMonth(), dataBase.getDate() ); return Math.floor((alvo.getTime() - base.getTime()) / umDiaMs); } function movimentoCompensado(item: Movimento) { return item.status === 'Pago' || item.status === 'Recebido'; } function movimentoEmAberto(item: Movimento) { return item.status === 'A pagar' || item.status === 'A receber'; } function getMovimentoRowVisual(item: Movimento) { if (movimentoExcluido(item)) { return { backgroundColor: 'rgba(148,163,184,0.10)', hoverColor: 'rgba(148,163,184,0.16)', borderLeftColor: 'rgba(100,116,139,0.65)', }; } if (movimentoCompensado(item)) { return { backgroundColor: 'rgba(34,197,94,0.08)', hoverColor: 'rgba(34,197,94,0.13)', borderLeftColor: 'rgba(34,197,94,0.75)', }; } if (movimentoEmAberto(item)) { const vencimento = dataSomenteDia(item.datavencimento); if (vencimento) { const dias = diferencaDias(vencimento, new Date()); if (dias < 0) { return { backgroundColor: 'rgba(239,68,68,0.08)', hoverColor: 'rgba(239,68,68,0.14)', borderLeftColor: 'rgba(239,68,68,0.78)', }; } if (dias <= DIAS_ALERTA_VENCIMENTO) { return { backgroundColor: 'rgba(245,158,11,0.10)', hoverColor: 'rgba(245,158,11,0.16)', borderLeftColor: 'rgba(245,158,11,0.85)', }; } } } return { backgroundColor: '#FFFFFF', hoverColor: 'rgba(15,23,42,0.02)', borderLeftColor: 'transparent', }; } function getMovimentoIconVisual(movimento: MovimentoTipo) { if (movimento === 'Entrada') { return { icon: , label: 'Entrada', color: 'success' as const, }; } if (movimento === 'Saida') { return { icon: , label: 'Saída', color: 'error' as const, }; } if (movimento === 'Sangria') { return { icon: , label: 'Sangria', color: 'warning' as const, }; } if (movimento === 'Estorno') { return { icon: , label: 'Estorno', color: 'info' as const, }; } return { icon: , label: movimento || 'Movimento', color: 'default' as const, }; } function formatarDataGrupo(dataISO: string) { if (!dataISO || dataISO === 'sem-data') return 'Sem data'; return new Date(`${dataISO}T00:00:00`).toLocaleDateString('pt-BR', { weekday: 'short', day: '2-digit', month: '2-digit', year: 'numeric', }); } function getDataGrupo(item: Movimento, campo: DataCampo) { const data = getDataPrincipal(item, campo); const texto = data ? String(data).slice(0, 10) : ''; if (!/^\d{4}-\d{2}-\d{2}$/.test(texto)) { return 'sem-data'; } return texto; } function valorAssinadoMovimento(item: Movimento) { const valor = Number(item.valor || 0); if (item.movimento === 'Entrada' || item.movimento === 'Estorno') { return valor; } return valor * -1; } function criarResumoGrupo() { return { totalEntradas: 0, totalSaidas: 0, saldo: 0, quantidade: 0, }; } function aplicarMovimentoNoResumo>( resumo: T, item: Movimento ) { const valorAssinado = valorAssinadoMovimento(item); if (valorAssinado >= 0) { resumo.totalEntradas += valorAssinado; } else { resumo.totalSaidas += Math.abs(valorAssinado); } resumo.saldo += valorAssinado; resumo.quantidade += 1; } function agruparMovimentos(movimentos: Movimento[], campo: DataCampo): GrupoDiaMovimentos[] { const gruposDia = new Map; bancos: Map; centros: Map; movimentos: Movimento[]; }>; }>; }>(); for (const item of movimentos) { const chaveDia = getDataGrupo(item, campo); const bancoNome = item.banco_descricao || 'Sem banco'; const centroNome = item.centro_custo_descricao || 'Sem centro de custo'; const chaveBanco = String(item.idbancos || bancoNome); const chaveCentro = String(item.idcentrodecustos || centroNome); if (!gruposDia.has(chaveDia)) { gruposDia.set(chaveDia, { resumo: criarResumoGrupo(), bancos: new Map(), }); } const grupoDia = gruposDia.get(chaveDia)!; aplicarMovimentoNoResumo(grupoDia.resumo, item); if (!grupoDia.bancos.has(chaveBanco)) { grupoDia.bancos.set(chaveBanco, { nome: bancoNome, resumo: criarResumoGrupo(), centros: new Map(), }); } const grupoBanco = grupoDia.bancos.get(chaveBanco)!; aplicarMovimentoNoResumo(grupoBanco.resumo, item); if (!grupoBanco.centros.has(chaveCentro)) { grupoBanco.centros.set(chaveCentro, { nome: centroNome, resumo: criarResumoGrupo(), movimentos: [], }); } const grupoCentro = grupoBanco.centros.get(chaveCentro)!; aplicarMovimentoNoResumo(grupoCentro.resumo, item); grupoCentro.movimentos.push(item); } return Array .from(gruposDia.entries()) .sort(([a], [b]) => b.localeCompare(a)) .map(([chaveDia, dia]) => ({ chave: chaveDia, dataLabel: formatarDataGrupo(chaveDia), totalEntradas: dia.resumo.totalEntradas, totalSaidas: dia.resumo.totalSaidas, saldo: dia.resumo.saldo, quantidade: dia.resumo.quantidade, bancos: Array .from(dia.bancos.entries()) .sort(([, a], [, b]) => a.nome.localeCompare(b.nome)) .map(([chaveBanco, banco]) => ({ chave: chaveBanco, nome: banco.nome, totalEntradas: banco.resumo.totalEntradas, totalSaidas: banco.resumo.totalSaidas, saldo: banco.resumo.saldo, quantidade: banco.resumo.quantidade, centros: Array .from(banco.centros.entries()) .sort(([, a], [, b]) => a.nome.localeCompare(b.nome)) .map(([chaveCentro, centro]) => ({ chave: chaveCentro, nome: centro.nome, totalEntradas: centro.resumo.totalEntradas, totalSaidas: centro.resumo.totalSaidas, saldo: centro.resumo.saldo, quantidade: centro.resumo.quantidade, movimentos: centro.movimentos.sort((a, b) => { const dataA = String(getDataPrincipal(a, campo) || ''); const dataB = String(getDataPrincipal(b, campo) || ''); return dataB.localeCompare(dataA); }), })), })), })); } function movimentoExcluido(item: Movimento) { return Boolean(item.deleted_at); } export function MovimentosPage() { const [sucesso, setSucesso] = useState(''); const [movimentos, setMovimentos] = useState([]); const [pagination, setPagination] = useState({ total: 0, limite: LIMITE_PADRAO, offset: 0, page: 1, totalPages: 1, }); const [summary, setSummary] = useState({ quantidade: 0, totalEntradas: 0, totalSaidas: 0, totalSangrias: 0, totalEstornos: 0, totalBaixado: 0, totalAberto: 0, saldoProcessado: 0, saldo: 0, }); const [modoVisualizacao, setModoVisualizacao] = useState('tabela'); const [gruposAbertos, setGruposAbertos] = useState>({}); const [bancos, setBancos] = useState([]); const [centrosCusto, setCentrosCusto] = useState([]); const [clientes, setClientes] = useState([]); const [loading, setLoading] = useState(true); const [loadingRefs, setLoadingRefs] = useState(true); const [erro, setErro] = useState(''); const [page, setPage] = useState(1); const [dataCampo, setDataCampo] = useState('dataentrada'); const [dataInicio, setDataInicio] = useState(inicioMesAtual()); const [dataFim, setDataFim] = useState(fimMesAtual()); const [busca, setBusca] = useState(''); const [movimento, setMovimento] = useState(''); const [status, setStatus] = useState(''); const [idBanco, setIdBanco] = useState(''); const [idCentroCusto, setIdCentroCusto] = useState(''); const [idCliente, setIdCliente] = useState(''); const [referenciaTipo, setReferenciaTipo] = useState(''); const [competencia, setCompetencia] = useState(''); const [origem, setOrigem] = useState(''); const [saldoProcessado, setSaldoProcessado] = useState(''); const [incluirExcluidos, setIncluirExcluidos] = useState(''); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); const filtrosAtivos = useMemo(() => { let count = 0; if (busca.trim()) count += 1; if (movimento) count += 1; if (status) count += 1; if (idBanco) count += 1; if (idCentroCusto) count += 1; if (idCliente) count += 1; if (referenciaTipo) count += 1; if (dataInicio || dataFim) count += 1; if (competencia.trim()) count += 1; if (origem) count += 1; if (saldoProcessado !== '') count += 1; if (incluirExcluidos !== '') count += 1; return count; }, [ busca, movimento, status, idBanco, idCentroCusto, idCliente, referenciaTipo, dataInicio, dataFim, competencia, origem, saldoProcessado, incluirExcluidos, ]); const movimentosAgrupados = useMemo(() => { return agruparMovimentos(movimentos, dataCampo); }, [movimentos, dataCampo]); const totalMaiorQueLimiteAgrupado = modoVisualizacao === 'agrupado' && pagination.total > LIMITE_AGRUPADO; async function carregarReferencias() { try { setLoadingRefs(true); const [bancosData, centrosData, clientesData] = await Promise.all([ listarBancos(), listarCentrosCusto(), listarClientes(), ]); setBancos(bancosData); setCentrosCusto(centrosData); setClientes(clientesData); } finally { setLoadingRefs(false); } } async function carregarMovimentos( pageToLoad = page, modoParaCarregar = modoVisualizacao ) { try { setLoading(true); setErro(''); const params: ListarMovimentosParams = { limite: modoParaCarregar === 'agrupado' ? LIMITE_AGRUPADO : LIMITE_PADRAO, page: modoParaCarregar === 'agrupado' ? 1 : pageToLoad, dataCampo, dataInicio, dataFim, busca: busca.trim() || undefined, movimento: movimento || undefined, status: status || undefined, idbancos: idBanco || undefined, idcentrodecustos: idCentroCusto || undefined, idclientes: idCliente || undefined, referenciaTipo: referenciaTipo || undefined, competencia: competencia.trim() || undefined, origem: origem || undefined, saldo_processado: saldoProcessado, incluirExcluidos, orderBy: dataCampo, orderDirection: 'DESC', }; const response = await listarMovimentos(params); setMovimentos(response.data); setPagination(response.pagination); setSummary(response.summary); setPage(response.pagination.page); } catch (error: any) { const message = error?.response?.data?.message || 'Não foi possível carregar os movimentos.'; setErro(message); } finally { setLoading(false); } } function aplicarFiltros() { setGruposAbertos({}); setPage(1); carregarMovimentos(1, modoVisualizacao); } function limparFiltros() { setDataCampo('dataentrada'); setDataInicio(inicioMesAtual()); setDataFim(fimMesAtual()); setBusca(''); setMovimento(''); setStatus(''); setIdBanco(''); setIdCentroCusto(''); setIdCliente(''); setReferenciaTipo(''); setCompetencia(''); setOrigem(''); setSaldoProcessado(''); setIncluirExcluidos(''); setGruposAbertos({}); setTimeout(() => { setPage(1); carregarMovimentos(1, modoVisualizacao); }, 0); } function origemLabel(origem: string | null | undefined) { if (!origem) return 'Manual'; const labels: Record = { manual: 'Manual', parcelamento: 'Parcelamento', movimento_fixo: 'Movimento fixo', quitacao: 'Quitação', ajuste_saldo: 'Ajuste de saldo', }; return labels[origem] || origem; } function origemChipColor(origem: string | null | undefined) { if (origem === 'movimento_fixo') return 'info'; if (origem === 'parcelamento') return 'secondary'; if (origem === 'quitacao') return 'success'; if (origem === 'ajuste_saldo') return 'warning'; return 'default'; } function saldoProcessadoLabel(valor: number | null | undefined) { return Number(valor) === 1 ? 'Saldo processado' : 'Sem impacto'; } function saldoProcessadoColor(valor: number | null | undefined) { return Number(valor) === 1 ? 'success' : 'default'; } async function handleDeletarMovimento(item: Movimento) { const confirmou = window.confirm( `Deseja realmente excluir "${item.descricao}"?\n\n` + 'Se o movimento já impactou saldo, a API vai reverter automaticamente.' ); if (!confirmou) return; try { setLoading(true); setErro(''); setSucesso(''); await deletarMovimento(item.idcontasapagar); setSucesso('Movimento excluído com sucesso. O saldo foi revertido se necessário.'); await carregarMovimentos(page); } catch (error: any) { const message = error?.response?.data?.message || 'Não foi possível excluir o movimento.'; setErro(message); } finally { setLoading(false); } } useEffect(() => { carregarReferencias(); carregarMovimentos(1); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); function toggleGrupo(chave: string) { setGruposAbertos((estadoAtual) => ({ ...estadoAtual, [chave]: !estadoAtual[chave], })); } function grupoAberto(chave: string) { return gruposAbertos[chave] === true; } function mudarModoVisualizacao(novoModo: ModoVisualizacao | null) { if (!novoModo) return; setModoVisualizacao(novoModo); setGruposAbertos({}); setPage(1); setTimeout(() => { carregarMovimentos(1, novoModo); }, 0); } function ResumoGrupoValores({ entradas, saidas, saldo, }: { entradas: number; saidas: number; saldo: number; }) { return ( = 0 ? 'success' : 'error'} /> ); } function renderExtratoAgrupado() { if (movimentosAgrupados.length === 0) { return ( Nenhum movimento encontrado. Ajuste os filtros ou cadastre um novo movimento. ); } return ( {movimentosAgrupados.map((dia) => { const chaveDia = `dia:${dia.chave}`; const abertoDia = grupoAberto(chaveDia); return ( toggleGrupo(chaveDia)} sx={{ cursor: 'pointer', padding: { xs: 1.5, md: 2 }, backgroundColor: 'rgba(15,23,42,0.035)', }} > {dia.dataLabel} {dia.quantidade} movimento(s) {abertoDia ? : } {dia.bancos.map((banco) => { const chaveBanco = `${chaveDia}:banco:${banco.chave}`; const abertoBanco = grupoAberto(chaveBanco); return ( toggleGrupo(chaveBanco)} sx={{ cursor: 'pointer', padding: 1.5, }} > {banco.nome} {banco.quantidade} movimento(s) {abertoBanco ? : } {banco.centros.map((centro) => { const chaveCentro = `${chaveBanco}:centro:${centro.chave}`; const abertoCentro = grupoAberto(chaveCentro); return ( toggleGrupo(chaveCentro)} sx={{ cursor: 'pointer', padding: 1.25, }} > {centro.nome} {centro.quantidade} movimento(s) {abertoCentro ? : } {centro.movimentos.map((item) => { const rowVisual = getMovimentoRowVisual(item); const movimentoIcon = getMovimentoIconVisual(item.movimento); const dataPrincipal = getDataPrincipal(item, dataCampo); return ( {item.descricao} {campoDataLabel(dataCampo)}: {formatarData(dataPrincipal)} ·{' '} Ref.: {getReferencia(item)} · {origemLabel(item.origem)} {item.movimento === 'Entrada' || item.movimento === 'Estorno' ? '+' : '-'} {formatarValor(item.valor)} {!movimentoExcluido(item) && ( )} ); })} ); })} ); })} ); })} ); } return ( } label="Central de movimentos" color="primary" variant="outlined" sx={{ marginBottom: 1.25, fontWeight: 700 }} /> Movimentos Consulte, filtre e edite os lançamentos financeiros. {erro && ( {erro} )} {sucesso && ( {sucesso} )} Registros encontrados {summary.quantidade} Entradas {formatarValor(summary.totalEntradas)} Saídas {formatarValor(summary.totalSaidas + summary.totalSangrias)} Saldo filtrado = 0 ? 'success.main' : 'error.main'} > {formatarValor(summary.saldo)} Em aberto {formatarValor(summary.totalAberto)} Baixados {formatarValor(summary.totalBaixado)} Filtros setBusca(event.target.value)} placeholder="Descrição, observação, banco, cliente..." fullWidth /> setDataCampo(event.target.value as DataCampo)} fullWidth > Data de vencimento Data de entrada Data de baixa Data de cadastro Data de atualização Data de exclusão setDataInicio(event.target.value)} InputLabelProps={{ shrink: true }} fullWidth /> setDataFim(event.target.value)} InputLabelProps={{ shrink: true }} fullWidth /> setMovimento(event.target.value as MovimentoTipo | '')} fullWidth > Todos Entrada Saída Sangria Estorno setStatus(event.target.value as MovimentoStatus | '')} fullWidth > Todas Pago A pagar Recebido A receber setIdBanco(event.target.value === '' ? '' : Number(event.target.value)) } disabled={loadingRefs} fullWidth > Todos {bancos.map((banco) => ( {banco.descricao} ))} setIdCentroCusto(event.target.value === '' ? '' : Number(event.target.value)) } disabled={loadingRefs} fullWidth > Todos {centrosCusto.map((centro) => ( {centro.descricao} ))} setIdCliente(event.target.value === '' ? '' : Number(event.target.value)) } disabled={loadingRefs} fullWidth > Todos {clientes.map((cliente) => ( {cliente.nome} ))} setReferenciaTipo(event.target.value)} fullWidth > Todas Com cliente Com banco referência Sem referência setCompetencia(event.target.value)} InputLabelProps={{ shrink: true }} fullWidth /> setOrigem(event.target.value)} fullWidth > Todas Manual Parcelamento Movimento fixo Quitação Ajuste de saldo setSaldoProcessado(event.target.value === '' ? '' : Number(event.target.value)) } fullWidth > Todos Sim Não setIncluirExcluidos(event.target.value === '' ? '' : Number(event.target.value)) } fullWidth > Somente ativos Incluir excluídos Resultado Alterne entre a tabela detalhada e o extrato agrupado por dia, banco e centro. mudarModoVisualizacao(novoModo)} > Tabela Extrato {totalMaiorQueLimiteAgrupado && ( Exibindo os primeiros {LIMITE_AGRUPADO} movimentos do filtro no modo extrato. Refine o período para uma análise completa. )} {loading ? ( Carregando movimentos... ) : modoVisualizacao === 'agrupado' ? ( renderExtratoAgrupado() ) : movimentos.length === 0 ? ( Nenhum movimento encontrado. Ajuste os filtros ou cadastre um novo movimento. ) : isMobile ? ( }> {movimentos.map((item) => { const dataPrincipal = getDataPrincipal(item, dataCampo); const rowVisual = getMovimentoRowVisual(item); const movimentoIcon = getMovimentoIconVisual(item.movimento); return ( {item.descricao} {campoDataLabel(dataCampo)}: {formatarData(dataPrincipal)} {formatarValor(item.valor)} {movimentoExcluido(item) && ( )} Banco: {item.banco_descricao || '-'} Centro: {item.centro_custo_descricao || '-'} Referência: {getReferencia(item)} Competência: {item.competencia || '-'} Origem: {origemLabel(item.origem)} ); })} ) : ( Tipo Descrição Entrada Vencimento Baixa Valor Parcela Parcelas Situação Movimento Competência Origem Saldo Centro de custo Banco Cliente Banco ref. Observação Exclusão Ações {movimentos.map((item) => { const dataPrincipal = getDataPrincipal(item, dataCampo); const rowVisual = getMovimentoRowVisual(item); const movimentoIcon = getMovimentoIconVisual(item.movimento); return ( {item.descricao} {formatarData(item.dataentrada)} {formatarData(item.datavencimento)} {formatarData(item.databaixa)} {formatarValor(item.valor)} {item.parcela} {item.parcelas} {item.competencia || '-'} {item.centro_custo_descricao || '-'} {item.banco_descricao || '-'} {item.cliente_nome || '-'} {item.banco_referencia_descricao || '-'} {item.observacao || '-'} {formatarData(item.deleted_at)} {!movimentoExcluido(item) && ( <> handleDeletarMovimento(item)} > )} ); })}
)}
{modoVisualizacao === 'tabela' && pagination.totalPages > 1 && ( { setPage(novaPagina); carregarMovimentos(novaPagina); }} /> )} {modoVisualizacao === 'agrupado' ? ( <> Exibindo {movimentos.length} movimento(s) no extrato agrupado, de um total de {pagination.total} registro(s) filtrado(s). ) : ( <> Exibindo página {pagination.page} de {pagination.totalPages}, total de{' '} {pagination.total} registro(s). )}
); }