Files
openmonetis/components/pagadores/details/pagador-history-card.tsx
Felipe Coutinho a7f63fb77a refactor: migrate from ESLint to Biome and extract SQL queries to data.ts
- Replace ESLint with Biome for linting and formatting
- Configure Biome with tabs, double quotes, and organized imports
- Move all SQL/Drizzle queries from page.tsx files to data.ts files
- Create new data.ts files for: ajustes, dashboard, relatorios/categorias
- Update existing data.ts files: extrato, fatura (add lancamentos queries)
- Remove all drizzle-orm imports from page.tsx files
- Update README.md with new tooling info

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:15:37 +00:00

108 lines
2.7 KiB
TypeScript

"use client";
import { RiBarChartLine } from "@remixicon/react";
import {
Bar,
BarChart,
CartesianGrid,
LabelList,
type LabelProps,
XAxis,
} from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { WidgetEmptyState } from "@/components/widget-empty-state";
import { currencyFormatter } from "@/lib/lancamentos/formatting-helpers";
import type { PagadorHistoryPoint } from "@/lib/pagadores/details";
const chartConfig = {
despesas: {
label: "Despesas",
color: "hsl(356, 72%, 50%)",
},
};
type PagadorHistoryCardProps = {
data: PagadorHistoryPoint[];
};
const ValueLabel = (props: LabelProps) => {
const { x, y, value, width } = props;
if (typeof x !== "number" || typeof y !== "number" || width === undefined) {
return null;
}
const labelX = x + (Number(width) ?? 0) / 2;
const amount =
typeof value === "number" ? currencyFormatter.format(value) : value;
const labelY = Math.max(y - 6, 12);
return (
<text
x={labelX}
y={labelY}
fill="currentColor"
textAnchor="middle"
className="text-[11px] font-semibold text-muted-foreground"
>
{amount}
</text>
);
};
export function PagadorHistoryCard({ data }: PagadorHistoryCardProps) {
const hasData = data.length > 0;
return (
<Card className="border">
<CardHeader className="gap-1.5 pb-3">
<CardTitle className="text-lg font-semibold">
Evolução (últimos 6 meses)
</CardTitle>
<p className="text-xs text-muted-foreground">
Despesas registradas para este pagador ao longo do tempo.
</p>
</CardHeader>
<CardContent className="pt-0">
{hasData ? (
<ChartContainer
config={chartConfig}
className="mx-auto flex h-[210px] w-full max-w-[520px] items-center justify-center aspect-auto"
>
<BarChart
data={data}
barCategoryGap={16}
margin={{ top: 28, right: 8, left: 8, bottom: 0 }}
>
<CartesianGrid strokeDasharray="3 3" vertical={false} />
<XAxis
dataKey="label"
tickLine={false}
axisLine={false}
tickMargin={8}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Bar
dataKey="despesas"
fill="var(--color-despesas)"
radius={[6, 6, 0, 0]}
>
<LabelList dataKey="despesas" content={<ValueLabel />} />
</Bar>
</BarChart>
</ChartContainer>
) : (
<WidgetEmptyState
icon={<RiBarChartLine className="size-6 text-muted-foreground" />}
title="Sem dados para exibir"
description="Ainda não há movimentações suficientes para gerar este gráfico."
/>
)}
</CardContent>
</Card>
);
}