mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 02:51:46 +00:00
Documenta variáveis S3 opcionais, instruções de self-hosting com anexos e padrão de commit messages no guia do projeto. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.6 KiB
8.6 KiB
CLAUDE.md - OpenMonetis
Self-hosted personal finance app (Next.js 16, React 19, PostgreSQL, Drizzle ORM, Better Auth, Tailwind 4, shadcn/ui). Portuguese UI, English folders/imports. Linter: Biome 2.x. Package manager: pnpm.
Related Projects
- OpenMonetis Companion (
~/github/openmonetis-companion): Android app que captura notificacoes de apps bancarios e envia para o OpenMonetis via API. Os itens chegam na featureinboxpara revisao.
Critical Rules
- Sempre filtrar por
userIdem queries. - Usar
getAdminPayerId(userId)desrc/shared/lib/payers/get-admin-id.tsao inves de JOIN compayerspara descobrir o admin. - Periods usam formato
YYYY-MM(ex:"2025-11"). Utils emsrc/shared/utils/period/. - Moeda: R$ com 2 decimais. DB:
numeric(12, 2). Utils emsrc/shared/utils/currency.ts. - Revalidation: usar
revalidateForEntity("entity")desrc/shared/lib/actions/helpers.tsapos mutations. - Versionamento: registrar mudancas no
CHANGELOG.mdseguindo Keep a Changelog. - Comunicacao: responder em portugues clara e direta com o time.
- Commit messages: agrupar por natureza. em pt-br. seguindo o padrao do sistema.
Architecture
Feature-First
src/app/: roteamento, layouts, loading states e paginas finassrc/features/: codigo de dominio por featuresrc/shared/: tudo que e genuinamente reutilizado entre featuressrc/db/: schema do banco
Regra Feature vs Shared
Use esta pergunta:
Se eu deletar esta feature, este arquivo deveria sumir junto?
- Sim: vai para
src/features/<feature>/ - Nao: vai para
src/shared/
Features nao importam outras features
Se um contrato cruza dominios, ele deve morar em src/shared/.
Exemplos comuns:
- auth:
src/shared/lib/auth/* - db:
src/shared/lib/db.ts - revalidation helpers:
src/shared/lib/actions/* - payers cross-domain helpers:
src/shared/lib/payers/* - period/currency/date:
src/shared/utils/* - shadcn/ui:
src/shared/components/ui/*
Directory Structure
src/
├── app/
│ ├── (auth)/
│ │ ├── login/page.tsx
│ │ └── signup/page.tsx
│ ├── (dashboard)/
│ │ ├── dashboard/
│ │ ├── transactions/
│ │ ├── cards/
│ │ │ └── [cardId]/invoice/
│ │ ├── accounts/
│ │ │ └── [accountId]/statement/
│ │ ├── categories/
│ │ │ ├── [categoryId]/
│ │ │ └── history/
│ │ ├── budgets/
│ │ ├── payers/
│ │ │ └── [payerId]/
│ │ ├── notes/
│ │ ├── insights/
│ │ ├── calendar/
│ │ ├── inbox/
│ │ ├── changelog/
│ │ ├── reports/
│ │ │ ├── category-trends/
│ │ │ ├── card-usage/
│ │ │ ├── installment-analysis/
│ │ │ └── establishments/
│ │ └── settings/
│ ├── (landing-page)/
│ ├── api/
│ ├── globals.css
│ └── layout.tsx
├── features/
│ ├── auth/
│ ├── landing/
│ ├── dashboard/
│ ├── transactions/
│ ├── cards/
│ ├── invoices/
│ ├── accounts/
│ ├── categories/
│ ├── budgets/
│ ├── payers/
│ ├── notes/
│ ├── insights/
│ ├── calendar/
│ ├── inbox/
│ ├── reports/
│ └── settings/
├── shared/
│ ├── components/
│ │ ├── ui/
│ │ ├── navigation/
│ │ ├── providers/
│ │ ├── month-picker/
│ │ ├── logo-picker/
│ │ ├── calculator/
│ │ ├── entity-avatar/
│ │ └── skeletons/
│ ├── hooks/
│ ├── lib/
│ │ ├── actions/
│ │ ├── auth/
│ │ ├── accounts/
│ │ ├── cards/
│ │ ├── calculator/
│ │ ├── categories/
│ │ ├── email/
│ │ ├── installments/
│ │ ├── invoices/
│ │ ├── logo/
│ │ ├── payers/
│ │ ├── schemas/
│ │ ├── transfers/
│ │ ├── types/
│ │ └── db.ts
│ └── utils/
│ ├── period/
│ ├── currency.ts
│ ├── date.ts
│ ├── financial-dates.ts
│ ├── percentage.ts
│ ├── category-colors.ts
│ ├── calendar.ts
│ ├── math.ts
│ ├── number.ts
│ ├── string.ts
│ ├── initials.ts
│ ├── icons.tsx
│ ├── export-branding.ts
│ ├── ui.ts
│ └── calculator.ts
└── db/
└── schema.ts
Import Patterns
Preferidos
import { getUser } from "@/shared/lib/auth/server";
import { revalidateForEntity } from "@/shared/lib/actions/helpers";
import { parsePeriodParam } from "@/shared/utils/period";
import { TransactionsPage } from "@/features/transactions/components/page/transactions-page";
import { fetchLancamentos } from "@/features/transactions/queries";
Evitar
import { Something } from "@/components/...";
import { Something } from "@/lib/...";
import { something } from "@/app/(dashboard)/...";
App Router Pattern
Paginas em src/app/ devem ser finas:
import { getUser } from "@/shared/lib/auth/server";
import { TransactionsPage } from "@/features/transactions/components/page/transactions-page";
import { fetchLancamentos } from "@/features/transactions/queries";
export default async function Page() {
const user = await getUser();
const data = await fetchLancamentos([/* filters */]);
return <TransactionsPage {...data} />;
}
Layouts, loading.tsx e metadata continuam em src/app/.
Naming
Routes / folders
| Portugues | English |
|---|---|
lancamentos |
transactions |
cartoes |
cards |
contas |
accounts |
categorias |
categories |
orcamentos |
budgets |
pagadores |
payers |
anotacoes |
notes |
calendario |
calendar |
ajustes |
settings |
pre-lancamentos |
inbox |
relatorios/tendencias |
reports/category-trends |
relatorios/uso-cartoes |
reports/card-usage |
relatorios/analise-parcelas |
reports/installment-analysis |
relatorios/estabelecimentos |
reports/establishments |
contas/[contaId]/extrato |
accounts/[accountId]/statement |
cartoes/[cartaoId]/fatura |
cards/[cardId]/invoice |
categorias/historico |
categories/history |
changelog |
settings/changelog |
Files
- preferir
kebab-case - preferir nomes em ingles
- manter nomes internos de tipos/funcoes somente quando a troca aumentar risco sem ganho real
Commands
pnpm run dev
pnpm run build
pnpm run lint
pnpm run lint:fix
pnpm exec next typegen
pnpm exec tsc --noEmit
pnpm run db:generate
pnpm run db:push
pnpm run db:studio
pnpm run docker:up:db
Revalidation
Arquivo: src/shared/lib/actions/helpers.ts
- atualizar sempre os paths em ingles
- lembrar de manter a tag
"dashboard"para invalidacoes financeiras
Auth
getUser()/getUserId()emsrc/shared/lib/auth/server.ts- sessao deduplicada por request com
React.cache()
Dashboard Fetcher
Padrao recomendado:
import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id";
export async function fetchData(userId: string, period: string) {
const adminPayerId = await getAdminPayerId(userId);
if (!adminPayerId) return [];
return db.query.transactions.findMany({
where: /* sempre com userId + adminPayerId + period */,
});
}
New Feature Checklist
- Criar a rota fina em
src/app/(dashboard)/<feature>/page.tsx - Criar a feature em
src/features/<feature>/ - Separar:
components/queries.tsactions.tstypes.tsouschemas.tsquando fizer sentido
- Extrair para
src/shared/tudo que for reutilizavel - Atualizar navegacao e
revalidateForEntity()se a feature tiver CRUD - Rodar:
pnpm exec next typegenpnpm exec tsc --noEmitpnpm run lint
Response Style
Quando o time pedir avaliacao de plano ou feature:
- Responder em portugues simples.
- Listar 3-5 problemas principais.
- Fechar com decisao pratica:
- aprova agora
- nao aprova agora
- o que ajustar antes de comecar codigo
Exemplo:
- "Nao aprovaria para comecar codigo imediatamente."
- "Primeiro ajustaria o doc com estes 5 pontos."