fix: remoção dos icones
This commit is contained in:
parent
dd5f92825c
commit
35aca238ea
|
|
@ -1,4 +1,4 @@
|
||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
|
|
@ -25,7 +25,7 @@ import {
|
||||||
SelectItem,
|
SelectItem,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Download, Filter, X } from "lucide-react";
|
import { Download, Filter, X } from "lucide-react";
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from "xlsx";
|
||||||
|
|
||||||
interface AnaliticoItem {
|
interface AnaliticoItem {
|
||||||
codigo_grupo: string;
|
codigo_grupo: string;
|
||||||
|
|
@ -66,7 +66,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
const [globalFilter, setGlobalFilter] = React.useState("");
|
const [globalFilter, setGlobalFilter] = React.useState("");
|
||||||
const [columnFilters, setColumnFilters] = React.useState<any[]>([]);
|
const [columnFilters, setColumnFilters] = React.useState<any[]>([]);
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
const [conditions, setConditions] = React.useState([{ column: "", operator: "contains", value: "" }]);
|
const [conditions, setConditions] = React.useState([
|
||||||
|
{ column: "", operator: "contains", value: "" },
|
||||||
|
]);
|
||||||
|
|
||||||
const fetchData = React.useCallback(async () => {
|
const fetchData = React.useCallback(async () => {
|
||||||
// Só faz a requisição se tiver dataInicio e dataFim
|
// Só faz a requisição se tiver dataInicio e dataFim
|
||||||
|
|
@ -93,10 +95,10 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
setData(result as AnaliticoItem[]);
|
setData(result as AnaliticoItem[]);
|
||||||
} else {
|
} else {
|
||||||
console.error('Erro ao buscar dados:', await response.text());
|
console.error("Erro ao buscar dados:", await response.text());
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao buscar dados:', error);
|
console.error("Erro ao buscar dados:", error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -114,8 +116,8 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
filterFn: "advancedText",
|
filterFn: "advancedText",
|
||||||
cell: ({ getValue }: { getValue: () => string }) => {
|
cell: ({ getValue }: { getValue: () => string }) => {
|
||||||
const value = getValue();
|
const value = getValue();
|
||||||
return new Date(value).toLocaleDateString('pt-BR');
|
return new Date(value).toLocaleDateString("pt-BR");
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "data_vencimento",
|
accessorKey: "data_vencimento",
|
||||||
|
|
@ -123,8 +125,8 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
filterFn: "advancedText",
|
filterFn: "advancedText",
|
||||||
cell: ({ getValue }: { getValue: () => string }) => {
|
cell: ({ getValue }: { getValue: () => string }) => {
|
||||||
const value = getValue();
|
const value = getValue();
|
||||||
return new Date(value).toLocaleDateString('pt-BR');
|
return new Date(value).toLocaleDateString("pt-BR");
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "data_caixa",
|
accessorKey: "data_caixa",
|
||||||
|
|
@ -132,13 +134,29 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
filterFn: "advancedText",
|
filterFn: "advancedText",
|
||||||
cell: ({ getValue }: { getValue: () => string }) => {
|
cell: ({ getValue }: { getValue: () => string }) => {
|
||||||
const value = getValue();
|
const value = getValue();
|
||||||
return new Date(value).toLocaleDateString('pt-BR');
|
return new Date(value).toLocaleDateString("pt-BR");
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "codigo_fornecedor",
|
||||||
|
header: "Cód. Fornec.",
|
||||||
|
filterFn: "advancedText",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "nome_fornecedor",
|
||||||
|
header: "Fornecedor",
|
||||||
|
filterFn: "advancedText",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "codigo_centrocusto",
|
||||||
|
header: "Cód. Centro",
|
||||||
|
filterFn: "advancedText",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "codigo_conta",
|
||||||
|
header: "Cód. Conta",
|
||||||
|
filterFn: "advancedText",
|
||||||
},
|
},
|
||||||
{ accessorKey: "codigo_fornecedor", header: "Cód. Fornec.", filterFn: "advancedText" },
|
|
||||||
{ accessorKey: "nome_fornecedor", header: "Fornecedor", filterFn: "advancedText" },
|
|
||||||
{ accessorKey: "codigo_centrocusto", header: "Cód. Centro", filterFn: "advancedText" },
|
|
||||||
{ accessorKey: "codigo_conta", header: "Cód. Conta", filterFn: "advancedText" },
|
|
||||||
{ accessorKey: "conta", header: "Conta", filterFn: "advancedText" },
|
{ accessorKey: "conta", header: "Conta", filterFn: "advancedText" },
|
||||||
{
|
{
|
||||||
accessorKey: "valor",
|
accessorKey: "valor",
|
||||||
|
|
@ -146,20 +164,28 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
filterFn: "advancedText",
|
filterFn: "advancedText",
|
||||||
cell: ({ getValue }: { getValue: () => number }) => {
|
cell: ({ getValue }: { getValue: () => number }) => {
|
||||||
const value = getValue();
|
const value = getValue();
|
||||||
const formatted = new Intl.NumberFormat('pt-BR', {
|
const formatted = new Intl.NumberFormat("pt-BR", {
|
||||||
style: 'currency',
|
style: "currency",
|
||||||
currency: 'BRL',
|
currency: "BRL",
|
||||||
}).format(value);
|
}).format(value);
|
||||||
const isNegative = value < 0;
|
const isNegative = value < 0;
|
||||||
return (
|
return (
|
||||||
<span className={isNegative ? 'text-red-600' : 'text-gray-900'}>
|
<span className={isNegative ? "text-red-600" : "text-gray-900"}>
|
||||||
{formatted}
|
{formatted}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{ accessorKey: "historico", header: "Histórico", filterFn: "advancedText" },
|
},
|
||||||
{ accessorKey: "historico2", header: "Histórico 2", filterFn: "advancedText" },
|
{
|
||||||
|
accessorKey: "historico",
|
||||||
|
header: "Histórico",
|
||||||
|
filterFn: "advancedText",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "historico2",
|
||||||
|
header: "Histórico 2",
|
||||||
|
filterFn: "advancedText",
|
||||||
|
},
|
||||||
{ accessorKey: "recnum", header: "Recnum", filterFn: "advancedText" },
|
{ accessorKey: "recnum", header: "Recnum", filterFn: "advancedText" },
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
|
|
@ -204,7 +230,6 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -227,7 +252,6 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
|
|
||||||
const virtualRows = rowVirtualizer.getVirtualItems();
|
const virtualRows = rowVirtualizer.getVirtualItems();
|
||||||
|
|
||||||
|
|
||||||
const applyFilters = () => {
|
const applyFilters = () => {
|
||||||
// Agrupar múltiplas condições por coluna
|
// Agrupar múltiplas condições por coluna
|
||||||
const grouped: Record<string, any[]> = {};
|
const grouped: Record<string, any[]> = {};
|
||||||
|
|
@ -235,7 +259,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
conditions.forEach((c) => {
|
conditions.forEach((c) => {
|
||||||
if (
|
if (
|
||||||
c.column &&
|
c.column &&
|
||||||
(c.operator === "empty" || c.operator === "notEmpty" || (c.value ?? "") !== "")
|
(c.operator === "empty" ||
|
||||||
|
c.operator === "notEmpty" ||
|
||||||
|
(c.value ?? "") !== "")
|
||||||
) {
|
) {
|
||||||
if (!grouped[c.column]) grouped[c.column] = [];
|
if (!grouped[c.column]) grouped[c.column] = [];
|
||||||
grouped[c.column].push({ operator: c.operator, value: c.value });
|
grouped[c.column].push({ operator: c.operator, value: c.value });
|
||||||
|
|
@ -260,7 +286,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
|
|
||||||
const totalValor = data.reduce((sum, item) => {
|
const totalValor = data.reduce((sum, item) => {
|
||||||
const valor =
|
const valor =
|
||||||
typeof item.valor === 'string' ? parseFloat(item.valor) : item.valor;
|
typeof item.valor === "string" ? parseFloat(item.valor) : item.valor;
|
||||||
return sum + (isNaN(valor) ? 0 : valor);
|
return sum + (isNaN(valor) ? 0 : valor);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
|
@ -269,23 +295,23 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
|
|
||||||
// Preparar dados para exportação
|
// Preparar dados para exportação
|
||||||
const exportData = data.map((item) => ({
|
const exportData = data.map((item) => ({
|
||||||
'Data Competência': new Date(item.data_competencia).toLocaleDateString(
|
"Data Competência": new Date(item.data_competencia).toLocaleDateString(
|
||||||
'pt-BR'
|
"pt-BR"
|
||||||
),
|
),
|
||||||
'Data Vencimento': new Date(item.data_vencimento).toLocaleDateString(
|
"Data Vencimento": new Date(item.data_vencimento).toLocaleDateString(
|
||||||
'pt-BR'
|
"pt-BR"
|
||||||
),
|
),
|
||||||
'Data Caixa': new Date(item.data_caixa).toLocaleDateString('pt-BR'),
|
"Data Caixa": new Date(item.data_caixa).toLocaleDateString("pt-BR"),
|
||||||
'Código Fornecedor': item.codigo_fornecedor,
|
"Código Fornecedor": item.codigo_fornecedor,
|
||||||
Fornecedor: item.nome_fornecedor,
|
Fornecedor: item.nome_fornecedor,
|
||||||
'Código Centro Custo': item.codigo_centrocusto,
|
"Código Centro Custo": item.codigo_centrocusto,
|
||||||
'Centro Custo': item.codigo_centrocusto, // Assumindo que é o mesmo valor
|
"Centro Custo": item.codigo_centrocusto, // Assumindo que é o mesmo valor
|
||||||
'Código Conta': item.codigo_conta,
|
"Código Conta": item.codigo_conta,
|
||||||
Conta: item.conta,
|
Conta: item.conta,
|
||||||
Valor:
|
Valor:
|
||||||
typeof item.valor === 'string' ? parseFloat(item.valor) : item.valor,
|
typeof item.valor === "string" ? parseFloat(item.valor) : item.valor,
|
||||||
Histórico: item.historico,
|
Histórico: item.historico,
|
||||||
'Histórico 2': item.historico2,
|
"Histórico 2": item.historico2,
|
||||||
Recnum: item.recnum,
|
Recnum: item.recnum,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -295,18 +321,18 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
|
|
||||||
// Adicionar resumo na segunda aba
|
// Adicionar resumo na segunda aba
|
||||||
const resumoData = [
|
const resumoData = [
|
||||||
{ Métrica: 'Total de Registros', Valor: data.length },
|
{ Métrica: "Total de Registros", Valor: data.length },
|
||||||
{ Métrica: 'Valor Total', Valor: totalValor },
|
{ Métrica: "Valor Total", Valor: totalValor },
|
||||||
];
|
];
|
||||||
const wsResumo = XLSX.utils.json_to_sheet(resumoData);
|
const wsResumo = XLSX.utils.json_to_sheet(resumoData);
|
||||||
|
|
||||||
// Adicionar abas ao workbook
|
// Adicionar abas ao workbook
|
||||||
XLSX.utils.book_append_sheet(wb, ws, 'Dados Analíticos');
|
XLSX.utils.book_append_sheet(wb, ws, "Dados Analíticos");
|
||||||
XLSX.utils.book_append_sheet(wb, wsResumo, 'Resumo');
|
XLSX.utils.book_append_sheet(wb, wsResumo, "Resumo");
|
||||||
|
|
||||||
// Gerar nome do arquivo com data e hora
|
// Gerar nome do arquivo com data e hora
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const timestamp = now.toISOString().slice(0, 19).replace(/:/g, '-');
|
const timestamp = now.toISOString().slice(0, 19).replace(/:/g, "-");
|
||||||
const fileName = `analitico_${timestamp}.xlsx`;
|
const fileName = `analitico_${timestamp}.xlsx`;
|
||||||
|
|
||||||
// Fazer download
|
// Fazer download
|
||||||
|
|
@ -318,14 +344,28 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="flex items-center gap-3 mb-4">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
<div className="w-12 h-12 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg">
|
{/* <div className="w-12 h-12 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg">
|
||||||
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
className="w-7 h-7 text-white"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div> */}
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-gray-900">Análise Analítica</h1>
|
<h1 className="text-2xl font-bold text-gray-900">
|
||||||
<p className="text-sm text-gray-500">Relatório detalhado de transações</p>
|
Análise Analítica
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Relatório detalhado de transações
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -334,7 +374,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
<Input
|
<Input
|
||||||
placeholder="Filtrar tudo..."
|
placeholder="Filtrar tudo..."
|
||||||
value={globalFilter ?? ""}
|
value={globalFilter ?? ""}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setGlobalFilter(e.target.value)}
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setGlobalFilter(e.target.value)
|
||||||
|
}
|
||||||
className="w-64 bg-white border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
className="w-64 bg-white border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -344,6 +386,11 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
>
|
>
|
||||||
<Filter className="w-4 h-4 mr-2" />
|
<Filter className="w-4 h-4 mr-2" />
|
||||||
Filtros Avançados
|
Filtros Avançados
|
||||||
|
{columnFilters.length > 0 && (
|
||||||
|
<span className="ml-2 bg-blue-600 text-white text-xs px-2 py-1 rounded-full">
|
||||||
|
{columnFilters.length}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
{(columnFilters.length > 0 || globalFilter) && (
|
{(columnFilters.length > 0 || globalFilter) && (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -393,8 +440,8 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
ref={parentRef}
|
ref={parentRef}
|
||||||
className="max-h-[500px] overflow-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100 hover:scrollbar-thumb-gray-400"
|
className="max-h-[500px] overflow-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100 hover:scrollbar-thumb-gray-400"
|
||||||
style={{
|
style={{
|
||||||
scrollbarWidth: 'thin',
|
scrollbarWidth: "thin",
|
||||||
scrollbarColor: '#cbd5e0 #f7fafc'
|
scrollbarColor: "#cbd5e0 #f7fafc",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
|
@ -408,15 +455,28 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="flex items-center justify-center h-64">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
<div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||||
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
className="w-8 h-8 text-gray-400"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-500">Nenhum dado encontrado</p>
|
<p className="text-gray-500">Nenhum dado encontrado</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative" style={{ height: `${rowVirtualizer.getTotalSize()}px` }}>
|
<div
|
||||||
|
className="relative"
|
||||||
|
style={{ height: `${rowVirtualizer.getTotalSize()}px` }}
|
||||||
|
>
|
||||||
{virtualRows.map((virtualRow) => {
|
{virtualRows.map((virtualRow) => {
|
||||||
const row = table.getRowModel().rows[virtualRow.index];
|
const row = table.getRowModel().rows[virtualRow.index];
|
||||||
return (
|
return (
|
||||||
|
|
@ -425,18 +485,57 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
className="absolute top-0 left-0 w-full grid grid-cols-12 gap-4 px-4 py-3 text-sm border-b border-gray-100 hover:bg-gray-50 transition-colors"
|
className="absolute top-0 left-0 w-full grid grid-cols-12 gap-4 px-4 py-3 text-sm border-b border-gray-100 hover:bg-gray-50 transition-colors"
|
||||||
style={{ transform: `translateY(${virtualRow.start}px)` }}
|
style={{ transform: `translateY(${virtualRow.start}px)` }}
|
||||||
>
|
>
|
||||||
<div className="col-span-1 text-gray-600">{new Date(row.original.data_competencia).toLocaleDateString('pt-BR')}</div>
|
<div className="col-span-1 text-gray-600">
|
||||||
<div className="col-span-1 text-gray-600">{new Date(row.original.data_vencimento).toLocaleDateString('pt-BR')}</div>
|
{new Date(
|
||||||
<div className="col-span-1 text-gray-600">{new Date(row.original.data_caixa).toLocaleDateString('pt-BR')}</div>
|
row.original.data_competencia
|
||||||
<div className="col-span-1 font-medium text-gray-900">{row.original.codigo_fornecedor}</div>
|
).toLocaleDateString("pt-BR")}
|
||||||
<div className="col-span-2 text-gray-700 truncate" title={row.original.nome_fornecedor}>{row.original.nome_fornecedor}</div>
|
</div>
|
||||||
<div className="col-span-1 text-gray-600">{row.original.codigo_centrocusto}</div>
|
<div className="col-span-1 text-gray-600">
|
||||||
<div className="col-span-1 text-gray-600">{row.original.codigo_conta}</div>
|
{new Date(
|
||||||
<div className="col-span-2 text-gray-700 truncate" title={row.original.conta}>{row.original.conta}</div>
|
row.original.data_vencimento
|
||||||
<div className={`col-span-1 text-right font-semibold ${row.original.valor < 0 ? 'text-red-600' : 'text-gray-900'}`}>
|
).toLocaleDateString("pt-BR")}
|
||||||
{new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(row.original.valor)}
|
</div>
|
||||||
|
<div className="col-span-1 text-gray-600">
|
||||||
|
{new Date(row.original.data_caixa).toLocaleDateString(
|
||||||
|
"pt-BR"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 font-medium text-gray-900">
|
||||||
|
{row.original.codigo_fornecedor}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="col-span-2 text-gray-700 truncate"
|
||||||
|
title={row.original.nome_fornecedor}
|
||||||
|
>
|
||||||
|
{row.original.nome_fornecedor}
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 text-gray-600">
|
||||||
|
{row.original.codigo_centrocusto}
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 text-gray-600">
|
||||||
|
{row.original.codigo_conta}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="col-span-2 text-gray-700 truncate"
|
||||||
|
title={row.original.conta}
|
||||||
|
>
|
||||||
|
{row.original.conta}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`col-span-1 text-right font-semibold ${
|
||||||
|
row.original.valor < 0
|
||||||
|
? "text-red-600"
|
||||||
|
: "text-gray-900"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{new Intl.NumberFormat("pt-BR", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "BRL",
|
||||||
|
}).format(row.original.valor)}
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 text-gray-500">
|
||||||
|
{row.original.recnum}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1 text-gray-500">{row.original.recnum}</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -449,28 +548,50 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border-t border-blue-200 p-6">
|
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border-t border-blue-200 p-6">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="w-12 h-12 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-lg flex items-center justify-center">
|
{/* <div className="w-12 h-12 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-lg flex items-center justify-center">
|
||||||
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
className="w-6 h-6 text-white"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div> */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-bold text-gray-900">
|
<h3 className="text-lg font-bold text-gray-900">
|
||||||
Total de Registros: <span className="text-blue-600">{table.getRowModel().rows.length}</span>
|
Total de Registros:{" "}
|
||||||
|
<span className="text-blue-600">
|
||||||
|
{table.getRowModel().rows.length}
|
||||||
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600">Transações encontradas</p>
|
<p className="text-sm text-gray-600">
|
||||||
|
Transações encontradas
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<h3 className="text-lg font-bold">
|
<h3 className="text-lg font-bold">
|
||||||
<span className={totalValor < 0 ? 'text-red-600' : 'text-green-600'}>
|
<span
|
||||||
Valor Total: {new Intl.NumberFormat('pt-BR', {
|
className={
|
||||||
style: 'currency',
|
totalValor < 0 ? "text-red-600" : "text-green-600"
|
||||||
currency: 'BRL',
|
}
|
||||||
|
>
|
||||||
|
Valor Total:{" "}
|
||||||
|
{new Intl.NumberFormat("pt-BR", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "BRL",
|
||||||
}).format(totalValor)}
|
}).format(totalValor)}
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600">Soma de todos os valores</p>
|
<p className="text-sm text-gray-600">
|
||||||
|
Soma de todos os valores
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -482,12 +603,17 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogContent className="max-w-2xl w-full mx-4 bg-white">
|
<DialogContent className="max-w-2xl w-full mx-4 bg-white">
|
||||||
<DialogHeader className="pb-4">
|
<DialogHeader className="pb-4">
|
||||||
<DialogTitle className="text-xl font-semibold text-gray-900">Filtros Avançados</DialogTitle>
|
<DialogTitle className="text-xl font-semibold text-gray-900">
|
||||||
|
Filtros Avançados
|
||||||
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="space-y-4 max-h-96 overflow-y-auto bg-white">
|
<div className="space-y-4 max-h-96 overflow-y-auto bg-white">
|
||||||
{conditions.map((cond, idx) => (
|
{conditions.map((cond, idx) => (
|
||||||
<div key={idx} className="flex gap-3 items-start p-4 bg-gray-50 rounded-lg border border-gray-200">
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="flex gap-3 items-start p-4 bg-gray-50 rounded-lg border border-gray-200"
|
||||||
|
>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Coluna
|
Coluna
|
||||||
|
|
@ -505,7 +631,10 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{columns.map((col) => (
|
{columns.map((col) => (
|
||||||
<SelectItem key={col.accessorKey} value={col.accessorKey}>
|
<SelectItem
|
||||||
|
key={col.accessorKey}
|
||||||
|
value={col.accessorKey}
|
||||||
|
>
|
||||||
{col.header}
|
{col.header}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
@ -522,7 +651,8 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
onValueChange={(v: string) => {
|
onValueChange={(v: string) => {
|
||||||
const next = [...conditions];
|
const next = [...conditions];
|
||||||
next[idx].operator = v;
|
next[idx].operator = v;
|
||||||
if (v === "empty" || v === "notEmpty") next[idx].value = "";
|
if (v === "empty" || v === "notEmpty")
|
||||||
|
next[idx].value = "";
|
||||||
setConditions(next);
|
setConditions(next);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -540,7 +670,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!(cond.operator === "empty" || cond.operator === "notEmpty") && (
|
{!(
|
||||||
|
cond.operator === "empty" || cond.operator === "notEmpty"
|
||||||
|
) && (
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Valor
|
Valor
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import { LoaderPinwheel } from 'lucide-react';
|
import { LoaderPinwheel } from "lucide-react";
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from "react";
|
||||||
import AnaliticoComponent from './analitico';
|
import AnaliticoComponent from "./analitico";
|
||||||
|
|
||||||
interface DREItem {
|
interface DREItem {
|
||||||
codfilial: string;
|
codfilial: string;
|
||||||
|
|
@ -17,7 +17,7 @@ interface DREItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HierarchicalRow {
|
interface HierarchicalRow {
|
||||||
type: 'grupo' | 'subgrupo' | 'centro_custo' | 'conta';
|
type: "grupo" | "subgrupo" | "centro_custo" | "conta";
|
||||||
level: number;
|
level: number;
|
||||||
grupo?: string;
|
grupo?: string;
|
||||||
subgrupo?: string;
|
subgrupo?: string;
|
||||||
|
|
@ -30,7 +30,6 @@ interface HierarchicalRow {
|
||||||
percentuaisPorMes?: Record<string, number>;
|
percentuaisPorMes?: Record<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function Teste() {
|
export default function Teste() {
|
||||||
const [data, setData] = useState<DREItem[]>([]);
|
const [data, setData] = useState<DREItem[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
@ -46,12 +45,12 @@ export default function Teste() {
|
||||||
|
|
||||||
// Estados para analítico
|
// Estados para analítico
|
||||||
const [analiticoFiltros, setAnaliticoFiltros] = useState({
|
const [analiticoFiltros, setAnaliticoFiltros] = useState({
|
||||||
dataInicio: '',
|
dataInicio: "",
|
||||||
dataFim: '',
|
dataFim: "",
|
||||||
centroCusto: '',
|
centroCusto: "",
|
||||||
codigoGrupo: '',
|
codigoGrupo: "",
|
||||||
codigoSubgrupo: '',
|
codigoSubgrupo: "",
|
||||||
codigoConta: '',
|
codigoConta: "",
|
||||||
});
|
});
|
||||||
const [linhaSelecionada, setLinhaSelecionada] = useState<string | null>(null);
|
const [linhaSelecionada, setLinhaSelecionada] = useState<string | null>(null);
|
||||||
|
|
||||||
|
|
@ -63,7 +62,7 @@ export default function Teste() {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
const response = await fetch('/api/dre');
|
const response = await fetch("/api/dre");
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Erro ao carregar dados: ${response.status}`);
|
throw new Error(`Erro ao carregar dados: ${response.status}`);
|
||||||
|
|
@ -79,29 +78,29 @@ export default function Teste() {
|
||||||
const dataCompetencia = new Date(item.data_competencia);
|
const dataCompetencia = new Date(item.data_competencia);
|
||||||
return `${dataCompetencia.getFullYear()}-${String(
|
return `${dataCompetencia.getFullYear()}-${String(
|
||||||
dataCompetencia.getMonth() + 1
|
dataCompetencia.getMonth() + 1
|
||||||
).padStart(2, '0')}`;
|
).padStart(2, "0")}`;
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
].sort() as string[];
|
].sort() as string[];
|
||||||
|
|
||||||
setMesesDisponiveis(meses);
|
setMesesDisponiveis(meses);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Erro desconhecido');
|
setError(err instanceof Error ? err.message : "Erro desconhecido");
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatCurrency = (value: string | number) => {
|
const formatCurrency = (value: string | number) => {
|
||||||
const numValue = typeof value === 'string' ? parseFloat(value) : value;
|
const numValue = typeof value === "string" ? parseFloat(value) : value;
|
||||||
return numValue.toLocaleString('pt-BR', {
|
return numValue.toLocaleString("pt-BR", {
|
||||||
style: 'currency',
|
style: "currency",
|
||||||
currency: 'BRL',
|
currency: "BRL",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatCurrencyWithColor = (value: string | number) => {
|
const formatCurrencyWithColor = (value: string | number) => {
|
||||||
const numValue = typeof value === 'string' ? parseFloat(value) : value;
|
const numValue = typeof value === "string" ? parseFloat(value) : value;
|
||||||
const formatted = formatCurrency(value);
|
const formatted = formatCurrency(value);
|
||||||
const isNegative = numValue < 0;
|
const isNegative = numValue < 0;
|
||||||
return { formatted, isNegative };
|
return { formatted, isNegative };
|
||||||
|
|
@ -110,9 +109,9 @@ export default function Teste() {
|
||||||
// Função para extrair códigos dos grupos e subgrupos
|
// Função para extrair códigos dos grupos e subgrupos
|
||||||
const extractCodes = (grupo: string, subgrupo?: string) => {
|
const extractCodes = (grupo: string, subgrupo?: string) => {
|
||||||
const grupoMatch = grupo.match(/^(\d+)/);
|
const grupoMatch = grupo.match(/^(\d+)/);
|
||||||
const codigoGrupo = grupoMatch ? grupoMatch[1] : '';
|
const codigoGrupo = grupoMatch ? grupoMatch[1] : "";
|
||||||
|
|
||||||
let codigoSubgrupo = '';
|
let codigoSubgrupo = "";
|
||||||
if (subgrupo) {
|
if (subgrupo) {
|
||||||
// Primeiro tenta extrair código numérico (ex: "001.008 - LOJA 8" -> "001.008")
|
// Primeiro tenta extrair código numérico (ex: "001.008 - LOJA 8" -> "001.008")
|
||||||
const subgrupoMatch = subgrupo.match(/^(\d+(?:\.\d+)+)/);
|
const subgrupoMatch = subgrupo.match(/^(\d+(?:\.\d+)+)/);
|
||||||
|
|
@ -140,14 +139,14 @@ export default function Teste() {
|
||||||
const dataFimStr = new Date(dataFim).toISOString().substring(0, 7); // YYYY-MM
|
const dataFimStr = new Date(dataFim).toISOString().substring(0, 7); // YYYY-MM
|
||||||
|
|
||||||
const { codigoGrupo, codigoSubgrupo } = extractCodes(
|
const { codigoGrupo, codigoSubgrupo } = extractCodes(
|
||||||
row.grupo || '',
|
row.grupo || "",
|
||||||
row.subgrupo
|
row.subgrupo
|
||||||
);
|
);
|
||||||
|
|
||||||
// Criar um identificador único para a linha
|
// Criar um identificador único para a linha
|
||||||
const linhaId = `${row.type}-${row.grupo || ''}-${row.subgrupo || ''}-${
|
const linhaId = `${row.type}-${row.grupo || ""}-${row.subgrupo || ""}-${
|
||||||
row.centro_custo || ''
|
row.centro_custo || ""
|
||||||
}-${row.codigo_conta || ''}`;
|
}-${row.codigo_conta || ""}`;
|
||||||
setLinhaSelecionada(linhaId);
|
setLinhaSelecionada(linhaId);
|
||||||
|
|
||||||
// Se um mês específico foi selecionado, usar apenas esse mês
|
// Se um mês específico foi selecionado, usar apenas esse mês
|
||||||
|
|
@ -157,10 +156,10 @@ export default function Teste() {
|
||||||
setAnaliticoFiltros({
|
setAnaliticoFiltros({
|
||||||
dataInicio: dataInicioFiltro,
|
dataInicio: dataInicioFiltro,
|
||||||
dataFim: dataFimFiltro,
|
dataFim: dataFimFiltro,
|
||||||
centroCusto: row.centro_custo || '',
|
centroCusto: row.centro_custo || "",
|
||||||
codigoGrupo,
|
codigoGrupo,
|
||||||
codigoSubgrupo,
|
codigoSubgrupo,
|
||||||
codigoConta: row.codigo_conta?.toString() || '',
|
codigoConta: row.codigo_conta?.toString() || "",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -194,7 +193,6 @@ export default function Teste() {
|
||||||
setExpandedCentros(newExpanded);
|
setExpandedCentros(newExpanded);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const calcularValoresPorMes = (items: DREItem[]): Record<string, number> => {
|
const calcularValoresPorMes = (items: DREItem[]): Record<string, number> => {
|
||||||
const valoresPorMes: Record<string, number> = {};
|
const valoresPorMes: Record<string, number> = {};
|
||||||
|
|
||||||
|
|
@ -202,7 +200,7 @@ export default function Teste() {
|
||||||
const dataCompetencia = new Date(item.data_competencia);
|
const dataCompetencia = new Date(item.data_competencia);
|
||||||
const anoMes = `${dataCompetencia.getFullYear()}-${String(
|
const anoMes = `${dataCompetencia.getFullYear()}-${String(
|
||||||
dataCompetencia.getMonth() + 1
|
dataCompetencia.getMonth() + 1
|
||||||
).padStart(2, '0')}`;
|
).padStart(2, "0")}`;
|
||||||
|
|
||||||
if (!valoresPorMes[anoMes]) {
|
if (!valoresPorMes[anoMes]) {
|
||||||
valoresPorMes[anoMes] = 0;
|
valoresPorMes[anoMes] = 0;
|
||||||
|
|
@ -221,7 +219,7 @@ export default function Teste() {
|
||||||
const percentuais: Record<string, number> = {};
|
const percentuais: Record<string, number> = {};
|
||||||
|
|
||||||
// Se for o grupo 03, retorna 100% para todos os meses
|
// Se for o grupo 03, retorna 100% para todos os meses
|
||||||
if (grupo.includes('03')) {
|
if (grupo.includes("03")) {
|
||||||
Object.keys(valoresPorMes).forEach((mes) => {
|
Object.keys(valoresPorMes).forEach((mes) => {
|
||||||
percentuais[mes] = 100;
|
percentuais[mes] = 100;
|
||||||
});
|
});
|
||||||
|
|
@ -237,8 +235,8 @@ export default function Teste() {
|
||||||
const dataCompetencia = new Date(item.data_competencia);
|
const dataCompetencia = new Date(item.data_competencia);
|
||||||
const anoMes = `${dataCompetencia.getFullYear()}-${String(
|
const anoMes = `${dataCompetencia.getFullYear()}-${String(
|
||||||
dataCompetencia.getMonth() + 1
|
dataCompetencia.getMonth() + 1
|
||||||
).padStart(2, '0')}`;
|
).padStart(2, "0")}`;
|
||||||
return anoMes === mes && item.grupo.includes('03');
|
return anoMes === mes && item.grupo.includes("03");
|
||||||
});
|
});
|
||||||
|
|
||||||
const valorGrupo03 = grupo03Items.reduce(
|
const valorGrupo03 = grupo03Items.reduce(
|
||||||
|
|
@ -262,16 +260,16 @@ export default function Teste() {
|
||||||
// Agrupar por grupo, mas tratar grupo 05 como subgrupo do grupo 04
|
// Agrupar por grupo, mas tratar grupo 05 como subgrupo do grupo 04
|
||||||
const grupos = data.reduce((acc, item) => {
|
const grupos = data.reduce((acc, item) => {
|
||||||
// Se for grupo 05, adicionar ao grupo 04 como subgrupo
|
// Se for grupo 05, adicionar ao grupo 04 como subgrupo
|
||||||
if (item.grupo.includes('05')) {
|
if (item.grupo.includes("05")) {
|
||||||
// Encontrar grupo 04 existente ou criar um
|
// Encontrar grupo 04 existente ou criar um
|
||||||
const grupo04Key = Object.keys(acc).find((key) => key.includes('04'));
|
const grupo04Key = Object.keys(acc).find((key) => key.includes("04"));
|
||||||
if (grupo04Key) {
|
if (grupo04Key) {
|
||||||
acc[grupo04Key].push(item);
|
acc[grupo04Key].push(item);
|
||||||
} else {
|
} else {
|
||||||
// Se não existe grupo 04, criar um grupo especial
|
// Se não existe grupo 04, criar um grupo especial
|
||||||
const grupo04Nome =
|
const grupo04Nome =
|
||||||
Object.keys(acc).find((key) => key.includes('04')) ||
|
Object.keys(acc).find((key) => key.includes("04")) ||
|
||||||
'04 - GRUPO 04';
|
"04 - GRUPO 04";
|
||||||
if (!acc[grupo04Nome]) {
|
if (!acc[grupo04Nome]) {
|
||||||
acc[grupo04Nome] = [];
|
acc[grupo04Nome] = [];
|
||||||
}
|
}
|
||||||
|
|
@ -288,7 +286,9 @@ export default function Teste() {
|
||||||
}, {} as Record<string, DREItem[]>);
|
}, {} as Record<string, DREItem[]>);
|
||||||
|
|
||||||
// Ordenar grupos
|
// Ordenar grupos
|
||||||
const sortedGrupos = Object.entries(grupos).sort(([a], [b]) => a.localeCompare(b));
|
const sortedGrupos = Object.entries(grupos).sort(([a], [b]) =>
|
||||||
|
a.localeCompare(b)
|
||||||
|
);
|
||||||
|
|
||||||
sortedGrupos.forEach(([grupo, items]) => {
|
sortedGrupos.forEach(([grupo, items]) => {
|
||||||
const totalGrupo = items.reduce(
|
const totalGrupo = items.reduce(
|
||||||
|
|
@ -299,7 +299,7 @@ export default function Teste() {
|
||||||
// Linha do grupo
|
// Linha do grupo
|
||||||
const valoresPorMes = calcularValoresPorMes(items);
|
const valoresPorMes = calcularValoresPorMes(items);
|
||||||
rows.push({
|
rows.push({
|
||||||
type: 'grupo',
|
type: "grupo",
|
||||||
level: 0,
|
level: 0,
|
||||||
grupo,
|
grupo,
|
||||||
total: totalGrupo,
|
total: totalGrupo,
|
||||||
|
|
@ -312,7 +312,7 @@ export default function Teste() {
|
||||||
// Agrupar por subgrupo dentro do grupo
|
// Agrupar por subgrupo dentro do grupo
|
||||||
const subgrupos = items.reduce((acc, item) => {
|
const subgrupos = items.reduce((acc, item) => {
|
||||||
// Se o item originalmente era do grupo 05, agrupar tudo sob o nome do grupo 05
|
// Se o item originalmente era do grupo 05, agrupar tudo sob o nome do grupo 05
|
||||||
if (item.grupo.includes('05')) {
|
if (item.grupo.includes("05")) {
|
||||||
const subgrupoKey = item.grupo; // Usar o nome completo do grupo 05
|
const subgrupoKey = item.grupo; // Usar o nome completo do grupo 05
|
||||||
if (!acc[subgrupoKey]) {
|
if (!acc[subgrupoKey]) {
|
||||||
acc[subgrupoKey] = [];
|
acc[subgrupoKey] = [];
|
||||||
|
|
@ -329,7 +329,9 @@ export default function Teste() {
|
||||||
}, {} as Record<string, DREItem[]>);
|
}, {} as Record<string, DREItem[]>);
|
||||||
|
|
||||||
// Ordenar subgrupos
|
// Ordenar subgrupos
|
||||||
const sortedSubgrupos = Object.entries(subgrupos).sort(([a], [b]) => a.localeCompare(b));
|
const sortedSubgrupos = Object.entries(subgrupos).sort(([a], [b]) =>
|
||||||
|
a.localeCompare(b)
|
||||||
|
);
|
||||||
|
|
||||||
sortedSubgrupos.forEach(([subgrupo, subgrupoItems]) => {
|
sortedSubgrupos.forEach(([subgrupo, subgrupoItems]) => {
|
||||||
const totalSubgrupo = subgrupoItems.reduce(
|
const totalSubgrupo = subgrupoItems.reduce(
|
||||||
|
|
@ -340,7 +342,7 @@ export default function Teste() {
|
||||||
// Linha do subgrupo
|
// Linha do subgrupo
|
||||||
const valoresSubgrupoPorMes = calcularValoresPorMes(subgrupoItems);
|
const valoresSubgrupoPorMes = calcularValoresPorMes(subgrupoItems);
|
||||||
rows.push({
|
rows.push({
|
||||||
type: 'subgrupo',
|
type: "subgrupo",
|
||||||
level: 1,
|
level: 1,
|
||||||
grupo,
|
grupo,
|
||||||
subgrupo,
|
subgrupo,
|
||||||
|
|
@ -364,7 +366,9 @@ export default function Teste() {
|
||||||
}, {} as Record<string, DREItem[]>);
|
}, {} as Record<string, DREItem[]>);
|
||||||
|
|
||||||
// Ordenar centros de custo
|
// Ordenar centros de custo
|
||||||
const sortedCentros = Object.entries(centros).sort(([a], [b]) => a.localeCompare(b));
|
const sortedCentros = Object.entries(centros).sort(([a], [b]) =>
|
||||||
|
a.localeCompare(b)
|
||||||
|
);
|
||||||
|
|
||||||
sortedCentros.forEach(([centro, centroItems]) => {
|
sortedCentros.forEach(([centro, centroItems]) => {
|
||||||
const totalCentro = centroItems.reduce(
|
const totalCentro = centroItems.reduce(
|
||||||
|
|
@ -375,7 +379,7 @@ export default function Teste() {
|
||||||
// Linha do centro de custo
|
// Linha do centro de custo
|
||||||
const valoresCentroPorMes = calcularValoresPorMes(centroItems);
|
const valoresCentroPorMes = calcularValoresPorMes(centroItems);
|
||||||
rows.push({
|
rows.push({
|
||||||
type: 'centro_custo',
|
type: "centro_custo",
|
||||||
level: 2,
|
level: 2,
|
||||||
grupo,
|
grupo,
|
||||||
subgrupo,
|
subgrupo,
|
||||||
|
|
@ -402,7 +406,9 @@ export default function Teste() {
|
||||||
}, {} as Record<string, DREItem[]>);
|
}, {} as Record<string, DREItem[]>);
|
||||||
|
|
||||||
// Ordenar contas
|
// Ordenar contas
|
||||||
const sortedContas = Object.entries(contas).sort(([a], [b]) => a.localeCompare(b));
|
const sortedContas = Object.entries(contas).sort(([a], [b]) =>
|
||||||
|
a.localeCompare(b)
|
||||||
|
);
|
||||||
|
|
||||||
sortedContas.forEach(([conta, contaItems]) => {
|
sortedContas.forEach(([conta, contaItems]) => {
|
||||||
const totalConta = contaItems.reduce(
|
const totalConta = contaItems.reduce(
|
||||||
|
|
@ -413,7 +419,7 @@ export default function Teste() {
|
||||||
// Linha da conta (sem ano/mês no nome)
|
// Linha da conta (sem ano/mês no nome)
|
||||||
const valoresContaPorMes = calcularValoresPorMes(contaItems);
|
const valoresContaPorMes = calcularValoresPorMes(contaItems);
|
||||||
rows.push({
|
rows.push({
|
||||||
type: 'conta',
|
type: "conta",
|
||||||
level: 3,
|
level: 3,
|
||||||
grupo,
|
grupo,
|
||||||
subgrupo,
|
subgrupo,
|
||||||
|
|
@ -439,28 +445,30 @@ export default function Teste() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRowStyle = (row: HierarchicalRow) => {
|
const getRowStyle = (row: HierarchicalRow) => {
|
||||||
const baseStyle = 'transition-all duration-200 hover:bg-gradient-to-r hover:from-blue-50/30 hover:to-indigo-50/30';
|
const baseStyle =
|
||||||
|
"transition-all duration-200 hover:bg-gradient-to-r hover:from-blue-50/30 hover:to-indigo-50/30";
|
||||||
|
|
||||||
// Criar identificador único para a linha
|
// Criar identificador único para a linha
|
||||||
const linhaId = `${row.type}-${row.grupo || ''}-${row.subgrupo || ''}-${
|
const linhaId = `${row.type}-${row.grupo || ""}-${row.subgrupo || ""}-${
|
||||||
row.centro_custo || ''
|
row.centro_custo || ""
|
||||||
}-${row.codigo_conta || ''}`;
|
}-${row.codigo_conta || ""}`;
|
||||||
const isSelected = linhaSelecionada === linhaId;
|
const isSelected = linhaSelecionada === linhaId;
|
||||||
|
|
||||||
let style = baseStyle;
|
let style = baseStyle;
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
style += ' bg-gradient-to-r from-blue-100 to-indigo-100 border-l-4 border-blue-500 shadow-lg';
|
style +=
|
||||||
|
" bg-gradient-to-r from-blue-100 to-indigo-100 border-l-4 border-blue-500 shadow-lg";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (row.type) {
|
switch (row.type) {
|
||||||
case 'grupo':
|
case "grupo":
|
||||||
return `${style} bg-gradient-to-r from-blue-50/20 to-indigo-50/20 font-bold text-gray-900 border-b-2 border-blue-200`;
|
return `${style} bg-gradient-to-r from-blue-50/20 to-indigo-50/20 font-bold text-gray-900 border-b-2 border-blue-200`;
|
||||||
case 'subgrupo':
|
case "subgrupo":
|
||||||
return `${style} bg-gradient-to-r from-gray-50/30 to-blue-50/20 font-semibold text-gray-800`;
|
return `${style} bg-gradient-to-r from-gray-50/30 to-blue-50/20 font-semibold text-gray-800`;
|
||||||
case 'centro_custo':
|
case "centro_custo":
|
||||||
return `${style} bg-gradient-to-r from-gray-50/20 to-gray-100/10 font-medium text-gray-700`;
|
return `${style} bg-gradient-to-r from-gray-50/20 to-gray-100/10 font-medium text-gray-700`;
|
||||||
case 'conta':
|
case "conta":
|
||||||
return `${style} bg-white font-normal text-gray-600`;
|
return `${style} bg-white font-normal text-gray-600`;
|
||||||
default:
|
default:
|
||||||
return style;
|
return style;
|
||||||
|
|
@ -473,7 +481,7 @@ export default function Teste() {
|
||||||
|
|
||||||
const renderCellContent = (row: HierarchicalRow) => {
|
const renderCellContent = (row: HierarchicalRow) => {
|
||||||
switch (row.type) {
|
switch (row.type) {
|
||||||
case 'grupo':
|
case "grupo":
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3 whitespace-nowrap">
|
<div className="flex items-center gap-3 whitespace-nowrap">
|
||||||
<button
|
<button
|
||||||
|
|
@ -481,7 +489,7 @@ export default function Teste() {
|
||||||
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-200 flex items-center justify-center w-8 h-8 flex-shrink-0"
|
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-200 flex items-center justify-center w-8 h-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
<span className="text-blue-600 font-bold text-sm">
|
<span className="text-blue-600 font-bold text-sm">
|
||||||
{row.isExpanded ? '▼' : '▶'}
|
{row.isExpanded ? "▼" : "▶"}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|
@ -492,7 +500,7 @@ export default function Teste() {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case 'subgrupo':
|
case "subgrupo":
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3 whitespace-nowrap">
|
<div className="flex items-center gap-3 whitespace-nowrap">
|
||||||
<button
|
<button
|
||||||
|
|
@ -500,18 +508,20 @@ export default function Teste() {
|
||||||
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-200 flex items-center justify-center w-8 h-8 flex-shrink-0"
|
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-200 flex items-center justify-center w-8 h-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
<span className="text-blue-600 font-bold text-sm">
|
<span className="text-blue-600 font-bold text-sm">
|
||||||
{row.isExpanded ? '▼' : '▶'}
|
{row.isExpanded ? "▼" : "▶"}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleRowClick(row)}
|
onClick={() => handleRowClick(row)}
|
||||||
className="flex-1 text-left hover:bg-blue-50/50 p-2 rounded-lg cursor-pointer transition-all duration-200 truncate"
|
className="flex-1 text-left hover:bg-blue-50/50 p-2 rounded-lg cursor-pointer transition-all duration-200 truncate"
|
||||||
>
|
>
|
||||||
<span className="font-semibold text-gray-800">{row.subgrupo}</span>
|
<span className="font-semibold text-gray-800">
|
||||||
|
{row.subgrupo}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case 'centro_custo':
|
case "centro_custo":
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3 whitespace-nowrap">
|
<div className="flex items-center gap-3 whitespace-nowrap">
|
||||||
<button
|
<button
|
||||||
|
|
@ -521,18 +531,20 @@ export default function Teste() {
|
||||||
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-200 flex items-center justify-center w-8 h-8 flex-shrink-0"
|
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-200 flex items-center justify-center w-8 h-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
<span className="text-blue-600 font-bold text-sm">
|
<span className="text-blue-600 font-bold text-sm">
|
||||||
{row.isExpanded ? '▼' : '▶'}
|
{row.isExpanded ? "▼" : "▶"}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleRowClick(row)}
|
onClick={() => handleRowClick(row)}
|
||||||
className="flex-1 text-left hover:bg-blue-50/50 p-2 rounded-lg cursor-pointer transition-all duration-200 truncate"
|
className="flex-1 text-left hover:bg-blue-50/50 p-2 rounded-lg cursor-pointer transition-all duration-200 truncate"
|
||||||
>
|
>
|
||||||
<span className="font-medium text-gray-700">{row.centro_custo}</span>
|
<span className="font-medium text-gray-700">
|
||||||
|
{row.centro_custo}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case 'conta':
|
case "conta":
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3 whitespace-nowrap">
|
<div className="flex items-center gap-3 whitespace-nowrap">
|
||||||
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
|
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
|
||||||
|
|
@ -556,14 +568,28 @@ export default function Teste() {
|
||||||
<div className="w-full max-w-7xl mx-auto p-6">
|
<div className="w-full max-w-7xl mx-auto p-6">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="flex items-center gap-3 mb-4">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
<div className="w-12 h-12 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg">
|
{/* <div className="w-12 h-12 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg">
|
||||||
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
className="w-7 h-7 text-white"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div> */}
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-gray-900">DRE Gerencial</h1>
|
<h1 className="text-2xl font-bold text-gray-900">
|
||||||
<p className="text-sm text-gray-500">Demonstração do Resultado do Exercício</p>
|
DRE Gerencial
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Demonstração do Resultado do Exercício
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -573,8 +599,12 @@ export default function Teste() {
|
||||||
<div className="w-16 h-16 bg-gradient-to-r from-blue-100 to-indigo-100 rounded-full flex items-center justify-center mb-4">
|
<div className="w-16 h-16 bg-gradient-to-r from-blue-100 to-indigo-100 rounded-full flex items-center justify-center mb-4">
|
||||||
<LoaderPinwheel className="h-8 w-8 text-blue-600 animate-spin" />
|
<LoaderPinwheel className="h-8 w-8 text-blue-600 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">Carregando dados...</h3>
|
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
||||||
<p className="text-sm text-gray-500">Aguarde enquanto processamos as informações</p>
|
Carregando dados...
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Aguarde enquanto processamos as informações
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -586,14 +616,28 @@ export default function Teste() {
|
||||||
<div className="w-full max-w-7xl mx-auto p-6">
|
<div className="w-full max-w-7xl mx-auto p-6">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="flex items-center gap-3 mb-4">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
<div className="w-12 h-12 bg-gradient-to-r from-red-600 to-red-500 rounded-xl flex items-center justify-center shadow-lg">
|
{/* <div className="w-12 h-12 bg-gradient-to-r from-red-600 to-red-500 rounded-xl flex items-center justify-center shadow-lg">
|
||||||
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
className="w-7 h-7 text-white"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div> */}
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-gray-900">DRE Gerencial</h1>
|
<h1 className="text-2xl font-bold text-gray-900">
|
||||||
<p className="text-sm text-gray-500">Demonstração do Resultado do Exercício</p>
|
DRE Gerencial
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Demonstração do Resultado do Exercício
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -601,11 +645,23 @@ export default function Teste() {
|
||||||
<div className="bg-white rounded-xl shadow-xl border border-red-200 p-8">
|
<div className="bg-white rounded-xl shadow-xl border border-red-200 p-8">
|
||||||
<div className="flex flex-col items-center text-center">
|
<div className="flex flex-col items-center text-center">
|
||||||
<div className="w-16 h-16 bg-gradient-to-r from-red-100 to-red-50 rounded-full flex items-center justify-center mb-4">
|
<div className="w-16 h-16 bg-gradient-to-r from-red-100 to-red-50 rounded-full flex items-center justify-center mb-4">
|
||||||
<svg className="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
className="w-8 h-8 text-red-600"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg font-semibold text-red-900 mb-2">Erro ao carregar DRE Gerencial</h3>
|
<h3 className="text-lg font-semibold text-red-900 mb-2">
|
||||||
|
Erro ao carregar DRE Gerencial
|
||||||
|
</h3>
|
||||||
<p className="text-sm text-red-600 bg-red-50 border border-red-200 rounded-lg p-3 max-w-md">
|
<p className="text-sm text-red-600 bg-red-50 border border-red-200 rounded-lg p-3 max-w-md">
|
||||||
{error}
|
{error}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -622,14 +678,26 @@ export default function Teste() {
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="flex items-center gap-3 mb-4">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
<div className="w-12 h-12 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg">
|
{/* <div className="w-12 h-12 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg">
|
||||||
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
className="w-7 h-7 text-white"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div> */}
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-gray-900">DRE Gerencial</h1>
|
<h1 className="text-2xl font-bold text-gray-900">DRE Gerencial</h1>
|
||||||
<p className="text-sm text-gray-500">Demonstração do Resultado do Exercício</p>
|
<p className="text-sm text-gray-500">
|
||||||
|
Demonstração do Resultado do Exercício
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -643,7 +711,9 @@ export default function Teste() {
|
||||||
{mesesDisponiveis.map((mes) => (
|
{mesesDisponiveis.map((mes) => (
|
||||||
<div key={mes} className="flex min-w-[200px] max-w-[250px]">
|
<div key={mes} className="flex min-w-[200px] max-w-[250px]">
|
||||||
<div className="flex-1 min-w-[100px] text-right">{mes}</div>
|
<div className="flex-1 min-w-[100px] text-right">{mes}</div>
|
||||||
<div className="flex-1 min-w-[100px] text-left text-xs text-gray-500">%</div>
|
<div className="flex-1 min-w-[100px] text-left text-xs text-gray-500">
|
||||||
|
%
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div className="flex-1 min-w-[120px] text-right">Total</div>
|
<div className="flex-1 min-w-[120px] text-right">Total</div>
|
||||||
|
|
@ -653,8 +723,16 @@ export default function Teste() {
|
||||||
{/* Table Body */}
|
{/* Table Body */}
|
||||||
<div className="max-h-[500px] overflow-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100">
|
<div className="max-h-[500px] overflow-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100">
|
||||||
{hierarchicalData.map((row, index) => (
|
{hierarchicalData.map((row, index) => (
|
||||||
<div key={index} className={`flex items-center gap-4 px-4 py-3 text-sm border-b border-gray-100 hover:bg-gray-50 transition-colors ${getRowStyle(row)}`}>
|
<div
|
||||||
<div className="flex-1 min-w-[300px] max-w-[400px] whitespace-nowrap overflow-hidden" style={getIndentStyle(row.level)}>
|
key={index}
|
||||||
|
className={`flex items-center gap-4 px-4 py-3 text-sm border-b border-gray-100 hover:bg-gray-50 transition-colors ${getRowStyle(
|
||||||
|
row
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="flex-1 min-w-[300px] max-w-[400px] whitespace-nowrap overflow-hidden"
|
||||||
|
style={getIndentStyle(row.level)}
|
||||||
|
>
|
||||||
{renderCellContent(row)}
|
{renderCellContent(row)}
|
||||||
</div>
|
</div>
|
||||||
{mesesDisponiveis.map((mes) => (
|
{mesesDisponiveis.map((mes) => (
|
||||||
|
|
@ -662,39 +740,62 @@ export default function Teste() {
|
||||||
<div
|
<div
|
||||||
className="flex-1 min-w-[100px] text-right font-semibold cursor-pointer hover:bg-blue-50/50 transition-colors duration-200 whitespace-nowrap overflow-hidden"
|
className="flex-1 min-w-[100px] text-right font-semibold cursor-pointer hover:bg-blue-50/50 transition-colors duration-200 whitespace-nowrap overflow-hidden"
|
||||||
onClick={() => handleRowClick(row, mes)}
|
onClick={() => handleRowClick(row, mes)}
|
||||||
title={row.valoresPorMes && row.valoresPorMes[mes] ? formatCurrency(row.valoresPorMes[mes]) : '-'}
|
title={
|
||||||
|
row.valoresPorMes && row.valoresPorMes[mes]
|
||||||
|
? formatCurrency(row.valoresPorMes[mes])
|
||||||
|
: "-"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{row.valoresPorMes && row.valoresPorMes[mes]
|
{row.valoresPorMes && row.valoresPorMes[mes]
|
||||||
? (() => {
|
? (() => {
|
||||||
const { formatted, isNegative } = formatCurrencyWithColor(row.valoresPorMes[mes]);
|
const { formatted, isNegative } =
|
||||||
|
formatCurrencyWithColor(row.valoresPorMes[mes]);
|
||||||
return (
|
return (
|
||||||
<span className={isNegative ? 'text-red-600 font-bold' : 'text-gray-900'}>
|
<span
|
||||||
|
className={
|
||||||
|
isNegative
|
||||||
|
? "text-red-600 font-bold"
|
||||||
|
: "text-gray-900"
|
||||||
|
}
|
||||||
|
>
|
||||||
{formatted}
|
{formatted}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
})()
|
})()
|
||||||
: '-'}
|
: "-"}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="flex-1 min-w-[100px] text-left font-medium cursor-pointer hover:bg-blue-50/50 transition-colors duration-200 whitespace-nowrap overflow-hidden"
|
className="flex-1 min-w-[100px] text-left font-medium cursor-pointer hover:bg-blue-50/50 transition-colors duration-200 whitespace-nowrap overflow-hidden"
|
||||||
onClick={() => handleRowClick(row, mes)}
|
onClick={() => handleRowClick(row, mes)}
|
||||||
title={row.percentuaisPorMes && row.percentuaisPorMes[mes] !== undefined ? `${row.percentuaisPorMes[mes].toFixed(1)}%` : '-'}
|
title={
|
||||||
>
|
row.percentuaisPorMes &&
|
||||||
{row.percentuaisPorMes && row.percentuaisPorMes[mes] !== undefined
|
row.percentuaisPorMes[mes] !== undefined
|
||||||
? `${row.percentuaisPorMes[mes].toFixed(1)}%`
|
? `${row.percentuaisPorMes[mes].toFixed(1)}%`
|
||||||
: '-'}
|
: "-"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{row.percentuaisPorMes &&
|
||||||
|
row.percentuaisPorMes[mes] !== undefined
|
||||||
|
? `${row.percentuaisPorMes[mes].toFixed(1)}%`
|
||||||
|
: "-"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div
|
<div
|
||||||
className="flex-1 min-w-[120px] text-right font-semibold cursor-pointer hover:bg-blue-50/50 transition-colors duration-200 whitespace-nowrap overflow-hidden"
|
className="flex-1 min-w-[120px] text-right font-semibold cursor-pointer hover:bg-blue-50/50 transition-colors duration-200 whitespace-nowrap overflow-hidden"
|
||||||
onClick={() => handleRowClick(row)}
|
onClick={() => handleRowClick(row)}
|
||||||
title={row.total ? formatCurrency(row.total) : '-'}
|
title={row.total ? formatCurrency(row.total) : "-"}
|
||||||
>
|
>
|
||||||
{(() => {
|
{(() => {
|
||||||
const { formatted, isNegative } = formatCurrencyWithColor(row.total!);
|
const { formatted, isNegative } = formatCurrencyWithColor(
|
||||||
|
row.total!
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<span className={isNegative ? 'text-red-600 font-bold' : 'text-gray-900'}>
|
<span
|
||||||
|
className={
|
||||||
|
isNegative ? "text-red-600 font-bold" : "text-gray-900"
|
||||||
|
}
|
||||||
|
>
|
||||||
{formatted}
|
{formatted}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue