a55b3bd187
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>
34 lines
1.7 KiB
Twig
34 lines
1.7 KiB
Twig
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Sign in — pictureFrame</title>
|
|
<style>
|
|
body { font-family: sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; background: #fdf6ee; }
|
|
form { width: 100%; max-width: 360px; padding: 2rem; background: #fff; border-radius: 12px; border: 1px solid #e8d9c4; }
|
|
h1 { margin: 0 0 1.5rem; font-size: 1.4rem; }
|
|
label { display: block; margin-bottom: 0.25rem; font-size: 0.875rem; }
|
|
input { width: 100%; padding: 0.75rem; border: 1px solid #ccc; border-radius: 8px; font-size: 1rem; margin-bottom: 1rem; box-sizing: border-box; }
|
|
button { width: 100%; padding: 0.875rem; background: #c97c3a; color: #fff; border: none; border-radius: 9999px; font-size: 1rem; font-weight: 600; cursor: pointer; }
|
|
.error { color: #c0392b; font-size: 0.875rem; margin-bottom: 1rem; }
|
|
a { display: block; text-align: center; margin-top: 1rem; color: #c97c3a; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<form method="post">
|
|
<h1>Sign in</h1>
|
|
{% if error %}
|
|
<p class="error">{{ error.messageKey|trans(error.messageData, 'security') }}</p>
|
|
{% endif %}
|
|
<label for="inputEmail">Email</label>
|
|
<input type="email" id="inputEmail" name="_username" value="{{ last_username }}" required autofocus>
|
|
<label for="inputPassword">Password</label>
|
|
<input type="password" id="inputPassword" name="_password" required>
|
|
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
|
|
<button type="submit">Sign in</button>
|
|
<a href="/register">Create account</a>
|
|
</form>
|
|
</body>
|
|
</html>
|