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

@@ -24,10 +24,9 @@ import {
SelectValue,
} from "@/components/ui/select";
import { Label } from "@/components/ui/label";
import { PeriodPicker } from "@/components/period-picker";
import { useControlledState } from "@/hooks/use-controlled-state";
import { useFormState } from "@/hooks/use-form-state";
import type { PeriodPreferences } from "@/lib/user-preferences/period";
import { createMonthOptions } from "@/lib/utils/period";
import {
useCallback,
useEffect,
@@ -45,7 +44,6 @@ interface BudgetDialogProps {
budget?: Budget;
categories: BudgetCategory[];
defaultPeriod: string;
periodPreferences: PeriodPreferences;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}
@@ -68,7 +66,6 @@ export function BudgetDialog({
budget,
categories,
defaultPeriod,
periodPreferences,
open,
onOpenChange,
}: BudgetDialogProps) {
@@ -110,16 +107,6 @@ export function BudgetDialog({
}
}, [dialogOpen]);
const periodOptions = useMemo(
() =>
createMonthOptions(
formState.period,
periodPreferences.monthsBefore,
periodPreferences.monthsAfter
),
[formState.period, periodPreferences.monthsBefore, periodPreferences.monthsAfter]
);
const handleSubmit = useCallback(
(event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@@ -244,21 +231,11 @@ export function BudgetDialog({
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor="budget-period">Período</Label>
<Select
<PeriodPicker
value={formState.period}
onValueChange={(value) => updateField("period", value)}
>
<SelectTrigger id="budget-period" className="w-full">
<SelectValue placeholder="Selecione o período" />
</SelectTrigger>
<SelectContent className="max-h-64">
{periodOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
onChange={(value) => updateField("period", value)}
className="w-full"
/>
</div>
<div className="space-y-2">

View File

@@ -4,7 +4,6 @@ import { deleteBudgetAction } from "@/app/(dashboard)/orcamentos/actions";
import { ConfirmActionDialog } from "@/components/confirm-action-dialog";
import { EmptyState } from "@/components/empty-state";
import { Button } from "@/components/ui/button";
import type { PeriodPreferences } from "@/lib/user-preferences/period";
import { RiAddCircleLine, RiFundsLine } from "@remixicon/react";
import { useCallback, useState } from "react";
import { toast } from "sonner";
@@ -18,7 +17,6 @@ interface BudgetsPageProps {
categories: BudgetCategory[];
selectedPeriod: string;
periodLabel: string;
periodPreferences: PeriodPreferences;
}
export function BudgetsPage({
@@ -26,7 +24,6 @@ export function BudgetsPage({
categories,
selectedPeriod,
periodLabel,
periodPreferences,
}: BudgetsPageProps) {
const [editOpen, setEditOpen] = useState(false);
const [selectedBudget, setSelectedBudget] = useState<Budget | null>(null);
@@ -94,7 +91,6 @@ export function BudgetsPage({
mode="create"
categories={categories}
defaultPeriod={selectedPeriod}
periodPreferences={periodPreferences}
trigger={
<Button disabled={categories.length === 0}>
<RiAddCircleLine className="size-4" />
@@ -132,7 +128,6 @@ export function BudgetsPage({
budget={selectedBudget ?? undefined}
categories={categories}
defaultPeriod={selectedPeriod}
periodPreferences={periodPreferences}
open={editOpen && !!selectedBudget}
onOpenChange={handleEditOpenChange}
/>