Files
fuel-alert/.claude/rules/frontend.md
Ovidiu U df4ebdb7c6
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
Remove Livewire docs, add Vue SPA frontend docs, document log-masking runbook
- Delete `.claude/rules/livewire.md` (Livewire is vestigial – only starter-kit auth screens remain)
- Add `.claude/rules/frontend.md` documenting Vue 3 SPA stack, router, composables, search-URL mirroring, and Leaflet integration
- Add `docs/ops/nginx-log-masking.md` runbook for redacting lat/lng from Nginx access/error logs and PHP-FPM on IONOS VPS
- Update `CLAUDE.md` and `architecture.md` to reflect Vue SPA as primary frontend, REST API as backend contract, and Livewire's reduced role
- Note stale service names in legacy domain docs (`scoring.md`, `prediction.md`, `notifications.md`) now refactored into `Services/Forecasting/`, `Services/StationSearch/`, and `PlanFeatures`
2026-06-10 10:55:34 +01:00

3.4 KiB

Frontend — Vue 3 SPA

The entire user-facing app (landing, station search, dashboard, settings) is a Vue 3 single-page application under resources/js/. It is served from the Blade shell resources/views/app.blade.php via the SPA catch-all in routes/web.php and consumes the REST API (routes/api.php). There is no Livewire in the product UI — do not add Livewire / Volt / Alpine components for new features. Build Vue.

Stack

  • Vue 3.5 with <script setup> (Composition API), Vue Router 4 (createWebHistory)
  • Vite 8 (npm run dev / npm run build), @vitejs/plugin-vue
  • Tailwind CSS v4 (@tailwindcss/vite)
  • Leaflet for maps, iconify-icon + Lucide for icons, axios for HTTP

Layout

resources/js/
├── app.js              # Entry — createApp(App).use(router).mount('#app')
├── App.vue             # Root — <RouterView/>, fetches the user on mount
├── axios.js            # Configured axios instance (baseURL '/api', XSRF + credentials)
├── router/index.js     # Routes + requiresAuth guard (redirects to /login)
├── views/
│   ├── Home.vue            # Landing + station search (mirrors state to the URL query)
│   └── dashboard/          # Overview, SavedStations, Preferences, settings/{Profile,Security,Appearance}
├── components/         # LeafletMap, StationCard, StationList, PredictionCard/Full,
│                       #   PostSearchFilters, UpsellBanner, landing/*
├── composables/        # useAuth, useStations, useSavedStations
└── constants/          # fuelTypes.js — shared fuel-type source of truth (front + back)

Conventions

  • Composition API + <script setup> only. No Options API.
  • All server state goes through composables that call the configured api axios instance (axios.js) — never fetch() or a bare axios call inside a component.
  • Auth is cookie/session (Sanctum SPA mode). axios is configured with withCredentials + withXSRFToken; don't add bearer-token handling.
  • Route guards live in router/index.js (meta.requiresAuth). Hard navigations (/login, /logout) use window.location.href, not the router.
  • Keep business logic in the API / Services. Vue components render and orchestrate.

Search & the shareable URL

Home.vue is the search surface. Search params (postcode or lat/lng, fuel_type, radius, sort) are mirrored into the URL query via router.push (queryFromParams) so searches are shareable and bookmarkable, and restored on load (paramsFromQuery). Because the URL is shareable, any coordinates written to it must be coarsened — a precise GPS pair from "Use my location" (HeroSearch.vue → browser Geolocation) would otherwise broadcast the sharer's exact position to everyone they send the link to.

Maps (Leaflet)

components/LeafletMap.vue wraps Leaflet directly (Leaflet is a plain JS library, not a Vue plugin). Station and user markers and the Google-Maps directions links are built from station.lat / station.lng; the user marker comes from the browser Geolocation API.

Prediction

The fuel prediction ships inside the /api/stations response under the prediction key and is rendered by PredictionCard.vue / PredictionFull.vue (useStations reads response.data.prediction). See prediction.md for the payload shape and the tier gate.


paths:

  • "resources/js/**/*.vue"
  • "resources/js/**/*.js"