football2801
062c52eec7
feat: visual orientation picker on configure page + fix 404→setup QR in firmware
...
Replace orientation <select> dropdown on setup/configure with the same
visual two-button picker used in the SPA (SVG frame diagrams, ribbon
indicator, active highlight). Hidden input carries the value on submit.
Firmware: normal_operation() now calls show_setup_qr(mac) on 404 instead
of epd_fill(COLOR_RED) — device shows scannable QR with its own MAC when
not yet registered.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-04 21:59:37 -04:00
football2801
7408dc9d1a
fix: render plainPassword first/second fields separately in setup form
...
RepeatedType renders as a wrapper div when called as a single widget,
producing unstyled inputs with no mismatch error handling.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-04 21:30:02 -04:00
football2801
6c891d6fad
feat: orientation model, password confirm, frontend build
...
- Collapse orientation to landscape/portrait (ribbon left = portrait standard)
- Add OrientationPicker component and wire settings sheet in HomeView
- Add password confirmation field to registration form (RepeatedType)
- Build frontend SPA to public/build/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-04 16:59:03 -04:00
football2801
fb380c45bd
feat(story-2.2+2.3): device setup page, account linking, naming & configuration
...
Story 2.2 — /setup/{mac} Twig page (no Vue, works JS-disabled):
- Register tab: creates account + logs in + links device → /setup/{mac}/configure
- Login tab: manual credential check via UserPasswordHasherInterface + Security::login()
+ links device → /setup/{mac}/configure
- Re-provisioning: DeviceService.linkToUser() atomically transfers ownership + stubs
purgeDeviceHistory() (completed in Epic 3 when Image/Approval entities exist)
Story 2.3 — /setup/{mac}/configure (requires auth):
- GET: device name, orientation (landscape/portrait), rotation interval (6/12/24/48/168h),
uniqueness window (5/10/20/50 cycles)
- POST: validates name, saves to Device entity, redirects to Vue SPA
- Device entity: mac, name, orientation (Orientation enum), rotationIntervalHours,
uniquenessWindow, user (ManyToOne), linkedAt
- PATCH /api/devices/{id}: Vue SPA can edit any device field (Story 2.3 "edit from app")
- GET /api/devices: list authenticated user's devices
- Migration: create device table with Orientation enum column
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-28 00:47:14 -04:00
football2801
3c1d5f0eae
feat(story-1.4): user login with remember_me, inline error, logout
...
- Login Twig template: styled to match register page; inline "Incorrect email or
password" on both fields (no email-existence disclosure); aria-invalid on error
- security.yaml: always_remember_me: true — REMEMBERME cookie set on every login
- Logout: /logout → session invalidated → 302 /login (Symfony firewall handles it)
Verified: correct creds → 302 / + REMEMBERME cookie; wrong creds → 302 /login +
inline error on re-render; logout → 302 /login; GET / after logout → 302 /login
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-27 23:36:39 -04:00
football2801
694843bdf0
feat(story-1.3): user registration with auto-login and inline validation
...
- RegistrationFormType: email + plainPassword, NotBlank/Email/Length(min=8) constraints
- SecurityController: register action hashes password, persists user, auto-logs in via Security::login()
- User entity: UniqueEntity constraint — "An account with this email already exists"
- Register Twig template: inline errors per field (role=alert), blur-validation JS
(client fires on blur not keystroke; server-error flag prevents blur clobbering server messages)
- csrf.yaml: switched from stateless UX-dependent tokens to standard session CSRF
(stateless token IDs require Stimulus JS to inject the real value — we removed Stimulus)
Verified: happy path → 302 + auto-login; duplicate email → 422 + inline error;
short password → 422 + inline error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-27 23:25:42 -04:00
football2801
a55b3bd187
feat(story-1.2): Vue 3 SPA scaffold, base component library, User entity, SpaController
...
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 >
2026-04-27 23:21:29 -04:00
football2801
378b0b858b
feat(story-1.1): scaffold Symfony 7.4 LTS app with DDEV, Messenger, Scheduler
...
- DDEV config: PHP 8.4, PostgreSQL 16, nginx-fpm, Imagick via webimage_extra_packages
- Symfony 7.4 LTS skeleton + webapp pack scaffolded via Composer
- Removed AssetMapper, Stimulus, UX-Turbo (replaced by Vue 3 SPA per architecture)
- Added symfony/messenger + symfony/scheduler (Doctrine transport)
- Gitea CI workflow: PHP 8.4 container + PostgreSQL 16 service, runs phpunit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-27 22:57:09 -04:00