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,140 @@
|
||||
/* Financeiro Carvalho · Arcade Neon
|
||||
* Drop this into src/styles/tokens.css and import once in main.ts:
|
||||
* import './styles/tokens.css'
|
||||
*
|
||||
* All other components MUST consume var(--fc-*) instead of literal hex.
|
||||
*/
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=JetBrains+Mono:wght@400;500;700&family=Inter:wght@400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
/* ─────────────── color tokens ─────────────── */
|
||||
--fc-bg: #0a0418;
|
||||
--fc-bg-raised: #150827;
|
||||
--fc-bg-panel: #1c0d33;
|
||||
--fc-panel-edge: #2d1857;
|
||||
--fc-line: #3a1f6e;
|
||||
|
||||
--fc-text: #f4eaff;
|
||||
--fc-text-dim: #8b75b8;
|
||||
|
||||
--fc-accent: #ff2d95; /* magenta — alerts, "today", primary CTA accent */
|
||||
--fc-accent-2: #00f0ff; /* cyan — links, nav, "weekly" */
|
||||
--fc-accent-3: #a855f7; /* purple — structure, glow, default button */
|
||||
--fc-gold: #ffd84d; /* XP, achievements, "monthly" */
|
||||
--fc-green: #39ff7a; /* goal hit, positive value */
|
||||
--fc-red: #ff3b6b; /* goal missed, critical alert */
|
||||
|
||||
/* rgb fragments for shadows / alphas (no color-mix support? use these) */
|
||||
--fc-accent-rgb: 255 45 149;
|
||||
--fc-accent-2-rgb: 0 240 255;
|
||||
--fc-accent-3-rgb: 168 85 247;
|
||||
--fc-gold-rgb: 255 216 77;
|
||||
--fc-green-rgb: 57 255 122;
|
||||
--fc-red-rgb: 255 59 107;
|
||||
|
||||
/* ─────────────── sprite color slots ─────────────── */
|
||||
--fc-sprite-outline: #10131c;
|
||||
--fc-sprite-skin: #f3c79b;
|
||||
--fc-sprite-skin-shade: #c98863;
|
||||
--fc-sprite-eye: #10131c;
|
||||
--fc-sprite-mouth: #80484b;
|
||||
--fc-sprite-hat: #ffd84d;
|
||||
--fc-sprite-band: #fde68a;
|
||||
--fc-sprite-shirt: #00f0ff;
|
||||
--fc-sprite-shirt-shade: #0369a1;
|
||||
--fc-sprite-pants: #3a1f6e;
|
||||
--fc-sprite-pants-shade: #2d1857;
|
||||
--fc-sprite-boots: #0f172a;
|
||||
|
||||
/* ─────────────── sizing ─────────────── */
|
||||
--fc-radius-sm: 2px;
|
||||
--fc-radius: 4px;
|
||||
--fc-radius-lg: 6px;
|
||||
|
||||
--fc-space-1: 4px;
|
||||
--fc-space-2: 8px;
|
||||
--fc-space-3: 12px;
|
||||
--fc-space-4: 16px;
|
||||
--fc-space-5: 24px;
|
||||
|
||||
/* ─────────────── shadows ─────────────── */
|
||||
--fc-shadow-panel:
|
||||
inset 0 1px 0 rgba(255,255,255,.05),
|
||||
0 0 0 1px rgba(0,0,0,.4),
|
||||
0 8px 24px rgba(0,0,0,.4);
|
||||
|
||||
--fc-shadow-panel-glow:
|
||||
inset 0 1px 0 rgba(255,255,255,.05),
|
||||
0 0 0 1px var(--fc-accent-3),
|
||||
0 0 24px rgba(168,85,247,.25),
|
||||
0 8px 24px rgba(0,0,0,.5);
|
||||
|
||||
/* ─────────────── fonts ─────────────── */
|
||||
--fc-font-pixel: 'Press Start 2P', monospace;
|
||||
--fc-font-mono: 'JetBrains Mono', ui-monospace, monospace;
|
||||
--fc-font-body: 'Inter', system-ui, sans-serif;
|
||||
}
|
||||
|
||||
/* Global reset for the app root */
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--fc-bg);
|
||||
color: var(--fc-text);
|
||||
font-family: var(--fc-font-body);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
/* Util classes */
|
||||
.fc-pixel { font-family: var(--fc-font-pixel); letter-spacing: .02em; }
|
||||
.fc-mono { font-family: var(--fc-font-mono); }
|
||||
.fc-body { font-family: var(--fc-font-body); }
|
||||
|
||||
.fc-text-dim { color: var(--fc-text-dim); }
|
||||
.fc-text-accent { color: var(--fc-accent); }
|
||||
.fc-text-accent2 { color: var(--fc-accent-2); }
|
||||
.fc-text-accent3 { color: var(--fc-accent-3); }
|
||||
.fc-text-gold { color: var(--fc-gold); }
|
||||
.fc-text-green { color: var(--fc-green); }
|
||||
.fc-text-red { color: var(--fc-red); }
|
||||
|
||||
.fc-glow-cyan { text-shadow: 0 0 8px rgba(0,240,255,.6); }
|
||||
.fc-glow-magenta { text-shadow: 0 0 8px rgba(255,45,149,.6); }
|
||||
.fc-glow-purple { text-shadow: 0 0 8px rgba(168,85,247,.6); }
|
||||
.fc-glow-gold { text-shadow: 0 0 8px rgba(255,216,77,.6); }
|
||||
.fc-glow-green { text-shadow: 0 0 8px rgba(57,255,122,.6); }
|
||||
.fc-glow-red { text-shadow: 0 0 8px rgba(255,59,107,.6); }
|
||||
|
||||
/* App shell — wrap your <router-view> in <div class="fc-app"> */
|
||||
.fc-app {
|
||||
background:
|
||||
radial-gradient(ellipse 80% 50% at 50% 0%, rgba(168,85,247,.18), transparent 70%),
|
||||
radial-gradient(ellipse 60% 40% at 80% 100%, rgba(255,45,149,.12), transparent 60%),
|
||||
var(--fc-bg);
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.fc-app::before {
|
||||
content: '';
|
||||
position: absolute; inset: 0;
|
||||
background-image: repeating-linear-gradient(0deg, rgba(255,255,255,.025) 0 1px, transparent 1px 3px);
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
.fc-app::after {
|
||||
content: '';
|
||||
position: absolute; inset: 0;
|
||||
background-image:
|
||||
linear-gradient(var(--fc-line) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--fc-line) 1px, transparent 1px);
|
||||
background-size: 32px 32px;
|
||||
opacity: .12;
|
||||
pointer-events: none;
|
||||
}
|
||||
.fc-app > * { position: relative; z-index: 1; }
|
||||
|
||||
/* Blinking utility for critical alerts (recurrence missing, etc.) */
|
||||
@keyframes fc-blink { 0%,49% { opacity: 1 } 50%,100% { opacity: 0.2 } }
|
||||
.fc-blink { animation: fc-blink 1.2s steps(1) infinite; }
|
||||
Reference in New Issue
Block a user