feat(anexos): página de galeria de comprovantes e documentos

Adiciona rota `/attachments` com visualização de todos os anexos do
usuário em grade, visualização inline de imagem e PDF, navegação entre
arquivos do mesmo lançamento e download direto.

Inclui também:
- API REST em `/api/attachments` para servir os arquivos
- Actions `fetch-by-id` e `fetch-dialog-options` em transactions
- Item "Anexos" adicionado à navbar
- `formatBytes` extraído para `src/shared/utils/number.ts`
- Migrations de banco atualizadas
- Fix: uploads e remoções de anexo agora funcionam para todos os
  lançamentos, não apenas os pertencentes a séries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Felipe Coutinho
2026-04-01 14:13:54 +00:00
parent cad41680eb
commit 0ab3298cef
19 changed files with 3898 additions and 3095 deletions

View File

@@ -1,6 +1,7 @@
import {
RiArrowLeftRightLine,
RiAtLine,
RiAttachmentLine,
RiBankCard2Line,
RiBankLine,
RiBarChart2Line,
@@ -110,6 +111,14 @@ export const NAV_SECTIONS: NavSection[] = [
icon: <RiTodoLine className="size-4" />,
iconClass: "text-primary",
},
{
href: "/attachments",
label: "anexos",
description: "Comprovantes e documentos",
icon: <RiAttachmentLine className="size-4" />,
iconClass: "text-primary",
preservePeriod: true,
},
],
},
{

View File

@@ -32,8 +32,9 @@ export const revalidateConfig = {
payers: ["/payers"],
notes: ["/notes", "/notes/archived", "/dashboard"],
notifications: ["/dashboard"],
transactions: ["/transactions", "/accounts"],
transactions: ["/transactions", "/accounts", "/attachments"],
inbox: ["/inbox", "/transactions", "/dashboard"],
attachments: ["/attachments"],
} as const;
/** Entities whose mutations should invalidate the dashboard cache */

View File

@@ -1,7 +1,13 @@
/**
* Utility functions for safe number conversions
* Utility functions for safe number conversions and formatting
*/
export function formatBytes(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
/**
* Safely converts unknown value to number
* @param value - Value to convert