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]>
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
# 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`:
|
||||
|
||||
```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.
|
||||
|
||||
```vue
|
||||
<!-- 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>` lê `sprite-data.json` e aceita `theme` como prop. Cosméticos
|
||||
equipados são merged no store (Pinia) e passados como `theme`.
|
||||
|
||||
```vue
|
||||
<CharacterSprite :scale="6" :theme="gameStore.equippedTheme" :celebrate="gameStore.justLeveledUp" />
|
||||
```
|
||||
|
||||
Exemplo de store getter:
|
||||
|
||||
```ts
|
||||
// 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"):
|
||||
|
||||
```html
|
||||
<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:
|
||||
|
||||
```js
|
||||
// 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.
|
||||
Reference in New Issue
Block a user