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

@@ -11,21 +11,15 @@ import { toast } from "sonner";
interface PreferencesFormProps {
disableMagnetlines: boolean;
periodMonthsBefore: number;
periodMonthsAfter: number;
}
export function PreferencesForm({
disableMagnetlines,
periodMonthsBefore,
periodMonthsAfter,
}: PreferencesFormProps) {
const router = useRouter();
const [isPending, startTransition] = useTransition();
const [magnetlinesDisabled, setMagnetlinesDisabled] =
useState(disableMagnetlines);
const [monthsBefore, setMonthsBefore] = useState(periodMonthsBefore);
const [monthsAfter, setMonthsAfter] = useState(periodMonthsAfter);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@@ -33,8 +27,6 @@ export function PreferencesForm({
startTransition(async () => {
const result = await updatePreferencesAction({
disableMagnetlines: magnetlinesDisabled,
periodMonthsBefore: monthsBefore,
periodMonthsAfter: monthsAfter,
});
if (result.success) {
@@ -74,58 +66,6 @@ export function PreferencesForm({
disabled={isPending}
/>
</div>
<div className="space-y-4 rounded-lg border border-dashed p-4">
<div>
<h3 className="text-base font-medium mb-2">
Seleção de Período
</h3>
<p className="text-sm text-muted-foreground mb-4">
Configure quantos meses antes e depois do mês atual serão exibidos
nos seletores de período.
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor="monthsBefore" className="text-sm">
Meses anteriores
</Label>
<Input
id="monthsBefore"
type="number"
min={1}
max={24}
value={monthsBefore}
onChange={(e) => setMonthsBefore(Number(e.target.value))}
disabled={isPending}
className="w-full"
/>
<p className="text-xs text-muted-foreground">
1 a 24 meses
</p>
</div>
<div className="space-y-2">
<Label htmlFor="monthsAfter" className="text-sm">
Meses posteriores
</Label>
<Input
id="monthsAfter"
type="number"
min={1}
max={24}
value={monthsAfter}
onChange={(e) => setMonthsAfter(Number(e.target.value))}
disabled={isPending}
className="w-full"
/>
<p className="text-xs text-muted-foreground">
1 a 24 meses
</p>
</div>
</div>
</div>
</div>
<div className="flex justify-end">