feat(02-02): frontend auth store, router guard, Login/Register views
- frontend/src/stores/auth.js: useAuthStore with accessToken in memory only (never browser storage); login() accepts options.backupCode - frontend/src/api/client.js: extended with Bearer token injection, 401 auto-refresh retry, all auth/admin API functions, changePassword - frontend/src/router/index.js: auth routes added (/login, /register, /password-reset, /account, /admin); beforeEach guard redirects unauthenticated users to /login with redirect param - frontend/src/layouts/AuthLayout.vue: centered bare layout for auth pages - frontend/src/views/auth/LoginView.vue: three-step flow (password, TOTP, backup code); "Use a backup code instead" link; UI-SPEC copywriting - frontend/src/views/auth/RegisterView.vue: registration with PasswordStrengthBar; HIBP error display; UI-SPEC copywriting - frontend/src/components/auth/PasswordStrengthBar.vue: 4-segment bar - frontend/src/components/ui/AppSpinner.vue: animate-spin SVG spinner - Stub views: PasswordResetView, NewPasswordView, AccountView, AdminView - .gitignore: exclude frontend/node_modules, dist, package-lock.json npm run build exits 0. All acceptance criteria verified. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="p-8 max-w-4xl mx-auto">
|
||||
<h2 class="text-2xl font-semibold text-gray-900 mb-4">Admin panel</h2>
|
||||
|
||||
<!-- Sub-navigation -->
|
||||
<div class="flex border-b border-gray-200 mb-6">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
@click="activeTab = tab.id"
|
||||
class="px-4 py-2 text-sm font-semibold border-b-2 transition-colors"
|
||||
:class="activeTab === tab.id
|
||||
? 'text-indigo-600 border-indigo-600'
|
||||
: 'text-gray-500 hover:text-gray-700 border-transparent'"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab content (stub — wired in Phase 2 admin plan) -->
|
||||
<div class="text-sm text-gray-500">
|
||||
{{ activeTab === 'users' ? 'User management coming soon.' :
|
||||
activeTab === 'quotas' ? 'Quota management coming soon.' :
|
||||
'AI config management coming soon.' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const tabs = [
|
||||
{ id: 'users', label: 'Users' },
|
||||
{ id: 'quotas', label: 'Quotas' },
|
||||
{ id: 'ai', label: 'AI Config' },
|
||||
]
|
||||
|
||||
const activeTab = ref('users')
|
||||
</script>
|
||||
Reference in New Issue
Block a user