Files
Mlcavalho1andClaude Sonnet 4.6 acbce4edc0 feat: multi-usuário com autenticação JWT
- Tabela `profiles` + coluna `profile_id` em todas as entidades
  (categories, transactions, recurring_expenses, accounts, player_profile,
   xp_events, player_quests, player_achievements, player_cosmetics)
- Dados existentes migrados para profile_id = 1 (Manoel)
- CLI `./api create-user --name <n> --password <p>` cria perfil com
  seed de categorias e player_profile; faz upsert de senha se já existir
- Auth substituída: cookie+APP_PASSWORD → JWT Bearer 24h (HS256)
- Middleware RequireAuth injeta profile_id no context de todas as rotas
- Todos os repositórios filtram por profile_id do context
- Endpoints: POST /api/auth/login, GET /api/auth/me,
  POST /api/auth/change-password, POST /api/logout
- Frontend: auth store usa localStorage (fc_token/fc_profile),
  api.ts envia Authorization header, LoginView usa campo name
- SettingsView reescrita com troca de senha e logout
- docker-compose.yml: remove APP_USERNAME/APP_PASSWORD, adiciona JWT_SECRET

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-27 20:04:44 -03:00
..

Financeiro Carvalho · Handoff (Arcade Neon)

Pacote de design pronto pra ser entregue ao Claude Code / Cursor / Aider implementar. Direção visual escolhida: Arcade Neon.

O que tem aqui

neon-handoff/
├── STYLE_GUIDE.md              ← leia primeiro
├── preview.html                ← referência visual interativa (abra num servidor local)
├── tokens.css                  ← variáveis CSS + reset + classes utilitárias
├── components/                 ← Vue 3 SFCs de referência
│   ├── NeonPanel.vue
│   ├── XPBar.vue
│   ├── HUDStat.vue
│   └── CharacterSprite.vue
├── sprites/
│   ├── sprite-data.json        ← grade raw 17×24 + lista de cosméticos
│   ├── icons.json              ← heart, coin, star, anchor (8×8 cada)
│   ├── manoel-default.png      (e 4x)
│   ├── manoel-lv01.png         (e 4x)  · cabelo castanho, camisa azul, calça caqui
│   ├── manoel-lv03-bone-verolme.png    · boné azul
│   ├── manoel-lv05-wingspan.png        · camisa verde
│   ├── manoel-lv07-chapeu-sol.png      · look "equipado" atual
│   ├── manoel-lv10-anti-vento.png      · casaco roxo
│   ├── manoel-lv15-capitao.png         · trajado de capitão (vermelho/preto)
│   └── manoel-sheet-4x.png             · todos lado a lado pra referência
└── README.md                   ← este arquivo

Como integrar no apps/web

  1. Copie tokens.css para src/styles/tokens.css. Importe uma vez em src/main.ts:

    import './styles/tokens.css'
    
  2. Copie components/*.vue para src/components/. Eles já consomem var(--fc-*).

  3. Copie sprites/sprite-data.json e sprites/icons.json pra src/assets/. Garanta que tsconfig.json tenha "resolveJsonModule": true (Vite default já tem).

  4. Embrulhe o <router-view> em <div class="fc-app"> — é isso que aplica o fundo + scanlines + grid.

    <!-- App.vue -->
    <template>
      <div class="fc-app">
        <AppHud />
        <RouterView />
      </div>
    </template>
    
  5. As 8 rotas do PRD (/, /transacoes, /importar, /categorias, /recorrencias, /contas, /personagem, /configuracoes) usam o mesmo header HUD. Veja STYLE_GUIDE.md §3.6 pro shape do componente.

Sprites · uso

Renderizar dinâmico (recomendado)

<CharacterSprite>sprite-data.json e aceita theme como prop. Cosméticos equipados são merged no store (Pinia) e passados como theme.

<CharacterSprite :scale="6" :theme="gameStore.equippedTheme" :celebrate="gameStore.justLeveledUp" />

Exemplo de store getter:

// stores/game.ts
const equippedTheme = computed(() => {
  return cosmetics.value
    .filter(c => c.equipped)
    .reduce((acc, c) => ({ ...acc, ...c.colors }), {} as Record<string, string>)
})

Usar PNG estático

Útil pra previews em página de loja (/personagem → seção "cosméticos"):

<img src="@/assets/sprites/manoel-lv03-bone-verolme-4x.png" alt="Boné Verolme" />

Sempre image-rendering: pixelated; no CSS pra não suavizar.

Regenerar PNGs

sprites/sprite-data.json é a fonte da verdade. Se você ajustar uma cor de cosmético lá, regenere os PNGs com um script Node:

// scripts/build-sprites.mjs
import { createCanvas } from '@napi-rs/canvas'  // ou node-canvas
import fs from 'node:fs/promises'

const data = JSON.parse(await fs.readFile('src/assets/sprite-data.json', 'utf8'))
// ... mesmo render do PixelSprite acima, salva PNG por cosmético

Ou peça pro Claude Code regenerar — o algoritmo é trivial (loop pela grade, fillRect por célula com a cor do slot).

Princípios para o Claude implementar

Por ordem de importância:

  1. HUD persistente em todas as rotas. Não use sidebar — o app é um HUD de jogo.
  2. Nunca use hex direto. Sempre var(--fc-*). Se faltar token, adicione-o ao tokens.css e documente no STYLE_GUIDE.md §1.1.
  3. Press Start 2P pra label e número. JetBrains Mono pra dado. Inter pra prosa. Press Start 2P sempre em maiúscula.
  4. Glow é raro. Só nos números heroicos (47%) e no painel principal de cada rota. Se cada card brilha, nada brilha.
  5. Verde/vermelho são binários — meta sim / meta não. Não use vermelho pra valor negativo (transação) — só pra alerta.
  6. Cada animação tem propósito. Ver §6 do guia.
  7. Mobile vira bottom-nav. Não tente espremer a HUD horizontal num iPhone.

Telas a implementar

Conforme STYLE_GUIDE.md + screens visíveis em preview.html (Arcade · 01):

  • / Dashboard (hero poupado, categorias, histórico, transações, recorrências, quests, patrimônio, widget personagem)
  • /transacoes lista + CRUD
  • /importar upload OFX/CSV + preview de deduplicação
  • /categorias CRUD com pixel-icon picker
  • /recorrencias lista mensal com status (paid / due / missing)
  • /contas cards de conta + patrimônio total
  • /personagem painel RPG completo
  • /configuracoes formulário simples (perfil, meta %, prefs visuais)

Para qualquer tela nova: comece copiando a estrutura do dashboard, substitua o conteúdo dos painéis, mantenha a HUD.

Componentes adicionais que faltam

Esses não estão neste pacote — implemente seguindo os tokens já definidos:

  • AppHud.vue (cabeçalho persistente)
  • BottomNav.vue (mobile)
  • TransactionRow.vue
  • QuestCard.vue
  • CategoryBar.vue
  • AchievementCell.vue
  • LevelUpModal.vue (overlay celebratório no level up)
  • MonthSwitcher.vue ( MAIO · 2026 )

Tom de voz

Português brasileiro, direto, com vocabulário de jogo. Não é app corporativo. Ver STYLE_GUIDE.md §8.


Qualquer dúvida sobre intenção visual (não implementação), volte ao STYLE_GUIDE.md ou ao preview.html. A página de design original do projeto (canvas com 4 direções) também segue acessível pra comparação.