- 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`
75 lines
3.4 KiB
Markdown
75 lines
3.4 KiB
Markdown
# 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"
|
|
---
|