feat: implementar relatórios de categorias e substituir seleção de período por picker visual

BREAKING CHANGE: Remove feature de seleção de período das preferências do usuário

  Alterações principais:

  - Adiciona sistema completo de relatórios por categoria
    - Cria página /relatorios/categorias com filtros e visualizações
    - Implementa tabela e gráfico de evolução mensal
    - Adiciona funcionalidade de exportação de dados
    - Cria skeleton otimizado para melhor UX de loading

  - Remove feature de seleção de período das preferências
    - Deleta lib/user-preferences/period.ts
    - Remove colunas periodMonthsBefore e periodMonthsAfter do schema
    - Remove todas as referências em 16+ arquivos
    - Atualiza database schema via Drizzle

  - Substitui Select de período por MonthPicker visual
    - Implementa componente PeriodPicker reutilizável
    - Integra shadcn MonthPicker customizado (português, Remix icons)
    - Substitui createMonthOptions em todos os formulários
    - Mantém formato "YYYY-MM" no banco de dados

  - Melhora design da tabela de relatórios
    - Mescla colunas Categoria e Tipo em uma única coluna
    - Substitui badge de tipo por dot colorido discreto
    - Reduz largura da tabela em ~120px
    - Atualiza skeleton para refletir nova estrutura

  - Melhorias gerais de UI
    - Reduz espaçamento entre títulos da sidebar (p-2 → px-2 py-1)
    - Adiciona MonthNavigation para navegação entre períodos
    - Otimiza loading states com skeletons detalhados
This commit is contained in:
Felipe Coutinho
2026-01-04 03:03:09 +00:00
parent d192f47bc7
commit 4237062bde
54 changed files with 2987 additions and 472 deletions

View File

@@ -0,0 +1,63 @@
"use client";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { TypeBadge } from "@/components/type-badge";
import { getIconComponent } from "@/lib/utils/icons";
import { formatPeriodLabel, formatCurrency } from "@/lib/relatorios/utils";
import type { CategoryReportData } from "@/lib/relatorios/types";
import { CategoryCell } from "./category-cell";
interface CategoryReportCardsProps {
data: CategoryReportData;
}
export function CategoryReportCards({ data }: CategoryReportCardsProps) {
const { categories, periods } = data;
return (
<div className="md:hidden space-y-4">
{categories.map((category) => {
const Icon = category.icon ? getIconComponent(category.icon) : null;
return (
<Card key={category.categoryId}>
<CardHeader>
<CardTitle className="flex items-center gap-2">
{Icon && <Icon className="h-5 w-5 shrink-0" />}
<span className="flex-1 truncate">{category.name}</span>
<TypeBadge type={category.type} />
</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
{periods.map((period, periodIndex) => {
const monthData = category.monthlyData.get(period);
const isFirstMonth = periodIndex === 0;
return (
<div
key={period}
className="flex items-center justify-between py-2 border-b last:border-b-0"
>
<span className="text-sm text-muted-foreground">
{formatPeriodLabel(period)}
</span>
<CategoryCell
value={monthData?.amount ?? 0}
previousValue={monthData?.previousAmount ?? 0}
categoryType={category.type}
isFirstMonth={isFirstMonth}
/>
</div>
);
})}
<div className="flex items-center justify-between pt-2 font-semibold">
<span>Total</span>
<span>{formatCurrency(category.total)}</span>
</div>
</CardContent>
</Card>
);
})}
</div>
);
}