Files
carvalho-finances/neon-handoff/components/XPBar.vue
T
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

58 lines
1.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="fc-bar" :class="[`fc-bar--${size}`, { 'fc-bar--success': success, 'fc-bar--danger': danger }]">
<div class="fc-bar__fill" :style="{ width: clamped + '%' }" />
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const props = withDefaults(defineProps<{
/** Percentual 0100 (ou maior, será clampado). */
pct: number
/** 'sm' (6px) | 'md' (8px) | 'lg' (14px) */
size?: 'sm' | 'md' | 'lg'
/** Pinta de verde quando meta foi atingida. */
success?: boolean
/** Pinta de vermelho. */
danger?: boolean
}>(), { size: 'md' })
const clamped = computed(() => Math.max(0, Math.min(100, props.pct)))
</script>
<style scoped>
.fc-bar {
background: var(--fc-bg);
border: 2px solid var(--fc-panel-edge);
position: relative;
overflow: hidden;
}
.fc-bar--sm { height: 6px; }
.fc-bar--md { height: 8px; }
.fc-bar--lg { height: 14px; }
.fc-bar__fill {
height: 100%;
background: linear-gradient(90deg, var(--fc-accent-2), var(--fc-accent-3), var(--fc-accent));
position: relative;
transition: width .6s cubic-bezier(.2,.7,.3,1);
}
.fc-bar__fill::after {
content: '';
position: absolute;
inset: 0;
background-image: repeating-linear-gradient(90deg, rgba(0,0,0,.2) 0 2px, transparent 2px 6px);
}
.fc-bar--success { border-color: var(--fc-green); }
.fc-bar--success .fc-bar__fill {
background: linear-gradient(90deg, var(--fc-green), var(--fc-accent-2));
}
.fc-bar--danger { border-color: var(--fc-red); }
.fc-bar--danger .fc-bar__fill {
background: linear-gradient(90deg, var(--fc-red), var(--fc-accent));
}
</style>