Commit graph

12 commits

Author SHA1 Message Date
b2aaeb5733 Merge origin/master into feature/account-settings-dropdown.
All checks were successful
CI / Lint, type check, unit tests, coverage (pull_request) Successful in 2m24s
CI / E2E browser tests (pull_request) Successful in 1m31s
Resolve router conflict: keep /bekrafta-epost confirm route alongside
master's /om-oss about page and /om redirect.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 14:34:38 +02:00
3532e4d486 Add account settings dropdown and verified email change flow.
All checks were successful
CI / Lint, type check, unit tests, coverage (pull_request) Successful in 2m9s
CI / E2E browser tests (pull_request) Successful in 1m55s
Replace the header "Byt lösenord" link with an Inställningar menu for
changing email or password. Email changes are two-step: request with
password, confirmation link to the new address, then password again on
confirm so a wrong inbox cannot take over the account.

- Backend: EmailChangeService, V10 email_change_tokens, confirm API
- Frontend: ChangeEmailPage, ConfirmEmailChangePage, header dropdown
- E2E: account-settings round-trips, Mailpit verification, wrong-password guard
- Flyway: V9 restore for dev DBs, CI migration checks, V10 for email tokens

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 14:33:06 +02:00
a12e07ec1c Register routes for integritetspolicy and villkor legal pages.
All checks were successful
CI / Lint, type check, unit tests, coverage (pull_request) Successful in 2m4s
CI / E2E browser tests (pull_request) Successful in 57s
- Add /integritetspolicy and /villkor to Vue Router
- Add Router tests confirming both public legal routes resolve

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 13:51:20 +02:00
758ace1b92 Redesign about page and move route to /om-oss.
- Replace placeholder about card with hero, prose, steps, and CTA
- Add primary route /om-oss with redirect from legacy /om
- Update footer tagline and Om oss link to match new URL
- Extend AboutPage and AppFooter tests for new content and routing

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 12:47:44 +02:00
3d0b7fe799 Allow users to edit or cancel unpaid orders before payment.
Adds backend endpoints and frontend edit page so pending orders can be updated or soft-cancelled without admin intervention.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 11:21:47 +02:00
86fb946e33 Add password reset, logged-in change password, and Mailpit email dev/E2E.
All checks were successful
CI / Lint, type check, unit tests, coverage (push) Successful in 2m2s
CI / E2E browser tests (push) Successful in 1m55s
Operators can fix prod admin passwords without email via Byt lösenord;
end users can use forgot-password when SMTP is configured. Local and CI
use Mailpit to capture outbound mail and verify reset links end-to-end.

- Backend: V8 password_reset_tokens, PasswordResetService, EmailService,
  POST /api/auth/forgot-password, reset-password, change-password
- Optional testToken in forgot-password response (docker profile only, for E2E)
- Frontend: ForgotPasswordPage, ResetPasswordPage, ChangePasswordPage,
  routes, login link, header Byt lösenord
- Mailpit (ghcr.io/axllent/mailpit:v1.28) in docker-compose + e2e stack
- E2E: password-reset.spec.ts + Mailpit API helper tests SMTP delivery
- Separate dev/e2e Docker image names to avoid overwriting bilhej-frontend
- Docs: README email section, production-email-checklist, .env.example
- Unit/integration tests for reset, change password, and Vitest page specs

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 18:05:15 +02:00
c3c1513ac1 feat: add payment page and wire compose submit to payment flow
- api/payment.ts: payOrder(orderId) calls POST /api/payment/{id}/pay
- api/orders.ts: add amountPaid (number|null) to Order type
- PaymentRedirect.vue: route /betalning/:orderId, shows plate from
  query?plate, amount label (49 kr), green Betalt button, mock note:
  "Detta är en mock-betalning. I framtiden skickas du till Stripe."
  On click: calls payOrder, on success navigates to /orders, on
  failure shows error. Button disables and shows "Bearbetar..." while
  paying.
- ComposePage.vue: after createOrder success, captures returned order
  object and navigates to /betalning/{orderId}?plate=... instead of
  the old direct-to-orders route
- Router: add /betalning/:orderId route (name: payment, component:
  PaymentRedirect, meta: { requiresAuth: true })
2026-05-15 20:30:15 +02:00
8d07bb7ab1 feat: add Vue Router auth guards with admin role support
Implement client-side route protection with role-based access control. The auth
store now extracts the role claim from JWT tokens and exposes isAdmin. Router
guards enforce three levels of access: guestOnly (redirect authenticated users),
requiresAuth (redirect unauthenticated to login with redirect param), and
requiresAdmin (redirect non-admin users to home).

Changes:
- utils/jwt.ts: JWT payload parser using base64url decode (new file)
- authStore: add role ref, isAdmin computed, extractRole from JWT payload
- router: add route metadata (requiresAuth, requiresAdmin, guestOnly) and
  beforeEach guard with getActivePinia() safety for test environments
- OrdersPage.vue, AdminPage.vue: placeholder pages (new files)
- LoginPage.vue, RegisterPage.vue: use route.query.redirect after auth
- Router.spec.ts: 14 tests covering all guard scenarios
- authStore.spec.ts: tests for role extraction, isAdmin, role persistence
- LoginPage.spec.ts: test for redirect query param after login
- auth-guards.spec.ts: 7 Playwright E2E tests for guard behavior
- login.spec.ts: fix seed user credentials (test@bilhalsning.se)
2026-05-14 12:39:17 +02:00
491dc99c55 feat: add login page with Playwright E2E tests
Add the frontend login page (LoginPage.vue) with email and password
fields, Swedish UI strings, and integration with the backend login
endpoint. Also sets up Playwright as the E2E testing framework with
browser tests for both login and registration flows.

Frontend login implementation:
- Add LoginPage.vue with form validation, error handling, and link to
  registration page
- Add login() API function in auth.ts
- Add loginUser() method to authStore that stores JWT token
- Add /logga-in route to Vue Router
- Add 'Logga in' nav link to AppHeader alongside existing 'Registrera'
- Add 10 unit tests for LoginPage component
- Add 4 unit tests for loginUser auth store method
- Add 1 route resolution test and 1 AppHeader link test

Playwright E2E setup and tests:
- Install @playwright/test and configure playwright.config.ts
- Add npm scripts: test:e2e (local) and test:e2e:ci (Docker CI)
- Exclude e2e/ directory from Vitest to prevent test runner conflicts
- Add .gitignore entries for test-results/ and playwright-report/
- Add 5 E2E tests for login (navigation, invalid credentials, success
  redirect, navigation to register, input types)
- Add 6 E2E tests for register (navigation, success redirect, validation
  errors for invalid email/short password/mismatched passwords,
  navigation to login)
2026-05-13 19:17:29 +02:00
8e495672d3 feat: add user registration flow (backend + frontend)
Implement end-to-end registration: POST /api/auth/register creates a
user, returns a JWT, and the frontend RegisterPage stores the token
and redirects to home.

Backend:
- Add AuthController with POST /api/auth/register endpoint
- Add RegisterRequest record (@Email, @NotBlank, @Size(min=8))
- Add AuthResponse and ErrorResponse DTOs
- Add GlobalExceptionHandler (@RestControllerAdvice with logging)
  - EmailAlreadyExistsException -> 409 (Swedish message)
  - MethodArgumentNotValidException -> 400 (field errors)
  - Generic Exception -> 500 (Swedish message + server-side log)

Frontend:
- Add api/client.ts: centralized fetch wrapper with Bearer token
  interceptor, ApiError class, JSON error parsing
- Add api/auth.ts: register() function
- Add stores/authStore.ts: Pinia store with token persistence via
  localStorage, registerUser/logout/isAuthenticated
- Add pages/RegisterPage.vue: email + password + confirm password
  form with client-side validation, submit handler, error display,
  redirect to home on success
- Add route /registrera pointing to RegisterPage
- Add 'Registrera' link to AppHeader navigation

Infrastructure:
- Add __tests__/setup.ts: localStorage polyfill for jsdom 29
  (jsdom 29 lacks standard Storage method implementations)
- Register polyfill via vitest config setupFiles

Tests (17 new, 2 extended):
- AuthControllerTest (@SpringBootTest + @AutoConfigureMockMvc):
  5 backend tests (success 201, duplicate 409, invalid email 400,
  short password 400, missing email 400)
- authStore.spec.ts: 5 tests (unauthenticated start, localStorage
  restore, register success, register failure, logout)
- RegisterPage.spec.ts: 12 tests (render, validation, submit,
  redirect, error display, login link)
- AppHeader.spec.ts: added 'Registrera' link test
- Router.spec.ts: added /registrera route resolution test

Build: 95 tests pass (57 frontend + 38 backend), lint clean.
2026-05-01 19:37:39 +02:00
4c6094446b feat: add app shell with header, footer, and compose flow
Add AppHeader and AppFooter to give the site a consistent chrome
around the core page content. Add ComposePage stub reachable via
"Skicka ett brev till ägaren" CTA on HomePage after vehicle lookup
succeeds. Add stub pages for about, contact, and privacy.

- Create AppHeader.vue with logo link (BilHälsning) and Hem nav link
- Create AppFooter.vue with 4 links: Om oss, Kontakt, Integritetspolicy, Villkor
- Create ComposePage.vue stub that reads plate from route query params
- Create AboutPage.vue and ContactPage.vue stub pages
- Add 4 new routes: /compose, /om, /kontakt, /integritetspolicy
- Update App.vue to render AppHeader + <main> + AppFooter around RouterView
- Add home__cta RouterLink button to HomePage, visible only when vehicle
  lookup succeeds, linking to /compose?plate=<plate>
- Remove BilHälsning h1 from HomePage (moved to header)
- Add 17 new tests: AppHeader (2), AppFooter (1), ComposePage (3),
  AboutPage (1), ContactPage (1), HomePage rewrite (6), App update (2)
- Update App.spec.ts to verify header/footer components render
2026-05-01 18:19:53 +02:00
9931061cb6 feat: scaffold Vue 3 + Vite frontend with TypeScript, Router, Pinia, Vitest, ESLint, Prettier
- Scaffold via npm create vite@latest --template vue-ts (create-vue interactive
  prompts require manual selection; create-vite supports non-interactive flags)
- Dependencies: vue-router (SPA routing, createWebHistory for clean URLs),
  pinia (centralised state management), vitest + @vue/test-utils + jsdom
  (unit testing with browser DOM simulation)
- Dev tooling: eslint (v10 flat config) + eslint-plugin-vue + @vue/eslint-config-typescript
  + @vue/eslint-config-prettier (ESLint-Prettier integration via vueTsConfigs),
  prettier (semi: false, singleQuote, trailingComma: all), jiti (bridges ESLint
  with TypeScript config files)
- vite.config.ts: dev server on port 3000, @ alias resolving to src/, vitest
  with jsdom environment
- eslint.config.ts: defineConfigWithVueTs wraps tseslint.config with Vue SFC
  support (vue-eslint-parser, <script setup lang="ts">), vue/multi-word off
- tsconfig.app.json: path alias @/* -> src/* for TypeScript module resolution
- src/router/index.ts: single route mapping / to HomePage
- src/pages/HomePage.vue: minimal <script setup lang="ts"> placeholder
- src/main.ts: bootstraps app with Pinia plugin + Vue Router
- src/App.vue: delegates rendering to <RouterView />
- src/__tests__/HomePage.spec.ts: smoke test verifying component mounts
- Directory structure: src/stores/, src/api/, src/composables/ with .gitkeep
  placeholders matching AGENTS.md convention (PascalCase pages, camelCase stores/composables)
- index.html: lang="sv", title BilHälsning (Swedish UI convention)
- Cleaned up: HelloWorld.vue, style.css, template boilerplate SVGs/PNGs
- Update AGENTS.md + CODING_GUIDELINES.md: .js extensions → .ts across all
  file naming examples (useXxx.ts, authStore.ts, orders.ts, client.ts)
- Verification: npm run dev serves blank page on http://localhost:3000,
  npm run lint passes (0 errors, 0 warnings), npm test passes (1 test, 1 file)
2026-05-01 00:52:38 +02:00