feat(story-1.2): Vue 3 SPA scaffold, base component library, User entity, SpaController
CI / test (push) Has been cancelled

Frontend:
- Vue 3 + Vite + TypeScript strict in frontend/; builds to public/build/
- Vue Router (hash-history, requiresAuth guard → /login) and Pinia
- Global SCSS design tokens with 6 full themes (Warm Craft, Playful Pop, Sage & Cream, Dusty Mauve, Ocean Dusk, Honey & Slate)
- Base components: BaseButton (5 variants), BaseInput (floating label, error state),
  BaseBottomSheet (slide-up, focus trap, tap-outside dismiss), BaseCard, BaseChip,
  BaseToast (2.5s auto-dismiss, aria-live polite), BottomNav (4 tabs, hides at 960px)
- Type stubs for all API shapes: User, Device, Image, StickerLayer, RenderedAsset, Token

Backend:
- SpaController catch-all serves public/build/index.html; excludes api/setup/token/login/register
- User entity (email+password+roles+theme); UserRepository with PasswordUpgrader
- SecurityController with /login, /logout, /register stubs; Twig login form
- security.yaml: form_login firewall, remember_me, role_hierarchy, access_control
- Migration: create user table

Verified: npm run build succeeds, GET / → 302 /login (unauthenticated)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 23:21:29 -04:00
parent a0d39e1c47
commit a957c5cdd0
39 changed files with 3243 additions and 19 deletions
+46
View File
@@ -0,0 +1,46 @@
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'home',
component: () => import('@/views/HomeView.vue'),
meta: { requiresAuth: true },
},
{
path: '/library',
name: 'library',
component: () => import('@/views/LibraryView.vue'),
meta: { requiresAuth: true },
},
{
path: '/shared',
name: 'shared',
component: () => import('@/views/SharedView.vue'),
meta: { requiresAuth: true },
},
{
path: '/settings',
name: 'settings',
component: () => import('@/views/SettingsView.vue'),
meta: { requiresAuth: true },
},
{
path: '/:pathMatch(.*)*',
redirect: '/',
},
],
})
router.beforeEach((to) => {
const auth = useAuthStore()
if (to.meta.requiresAuth && !auth.isAuthenticated) {
window.location.href = '/login'
return false
}
})
export default router