# DriverVault — Web App Maintenance tracker for your cars: a **Vue 3 + Vite + Tailwind CSS v4** SPA served by a small **Go backend-for-frontend (BFF)**. The BFF serves the built SPA and reverse-proxies `/api/*` to the API Server, so the browser is always same-origin and all data access still flows through the API Server (never PocketBase directly). ``` Browser ─► Web App BFF (:8090) ──/api/*──► API Server (:8080) ─► PocketBase └── serves embedded Vue SPA ``` ## Layout ``` server/ Go BFF: embeds web/dist, proxies /api -> API_BASE main.go Run-WebApp.ps1 build frontend, then serve .env.example dist/ built SPA (generated; embedded at compile time) web/ Vue 3 + Vite + Tailwind v4 source src/ main.js app bootstrap router.js /login, / (dashboard), /charging, /cars/:id, /settings api.js the only place that calls the API Server (base URL resolution) auth.js token/profile state, isAdmin prefs.js theme/locale/date/font preferences -> i18n/ en / pl / da translation files + loader lib/format.js date/km formatting + next-service status badges lib/attachment.js upload / fetch / open a record's attached file style.css Tailwind v4 entry (+ dark custom-variant) App.vue layout shell + nav (Garage, Charging, Settings) components/ Modal, AttachmentField, CarFormModal, ServiceFormModal, TechnicalCheckFormModal, MaintenanceFormModal, FuelFormModal, DocumentFormModal, ReminderFormModal, PartFormModal, ShareModal, OrgManager, AdminUsers, Logo views/ Login, Dashboard, CarDetail, Charging, Settings ``` ## Requirements - **Node 20.19+ or 22.12+** (Vite 8's floor — Node 18 is end-of-life and will not build) and **Go 1.26+** (the `go.mod` directive). The Docker image builds on `node:22-alpine`. - A running **API Server** (see `../API Server`) ## Develop Two terminals: ```powershell # terminal 1 — API Server (see ../API Server/README.md) cd "../API Server"; .\bin\api-server.exe # terminal 2 — Vite dev server with hot reload (proxies /api -> :8080) cd web; npm install; npm run dev # http://localhost:5173 ``` The dev server proxies `/api/*` to the API Server (default `http://localhost:8080`, override with `VITE_API_TARGET`), so the client uses same-origin relative URLs and avoids CORS. It also listens on all interfaces (`host: true`) so it's reachable on the LAN (e.g. `http://10.2.1.101:5173`). At runtime, users can override the API base URL from the login screen's **Server settings** (persisted in `localStorage` as `cc_server_url`); resolution order is that override → `VITE_API_BASE` → `/api`. ## Build & run (production-style) ```powershell ./server/Run-WebApp.ps1 # builds frontend, then serves on :8090 # or manually: cd web; npm run build # outputs to ../server/dist cd ../server; go run . # http://localhost:8090 ``` Config (`server/.env`, copy from `.env.example`): | Variable | Purpose | Default | |---|---|---| | `WEB_ADDR` | Listen address | `:8090` | | `API_BASE` | API Server base URL | `http://localhost:8080` | ## Features - **Dashboard** — one card per car: last service, odometer, next-due date/km, and a status badge (OK / due soon ≤30d / overdue) from the Excel formulas. Add a car by hand, or **import from service** — pick a vehicle off a connected manufacturer account and have its details filled in (the button appears only once an account is connected). Shared cars are labelled and gated by your access level. - **Car detail** — all car spec fields (engine / transmission / differential oil, brake fluid, coolant, VIN, fuel type, …) plus tabbed histories, each with an optional file attachment and add/edit/delete gated by your access level: - **The connected service** (e.g. **MyToyota**) — the first tab, present for a car linked to a manufacturer account: live readings (odometer, fuel, battery, range, position), the vehicle record, and every section the plugin can fetch with its raw response. Offers the provider's odometer when it is ahead of the stored one. On an unlinked car the tab instead offers to connect it to a vehicle on your account. Read under *your* account, so a car shared from someone else shows data only if that vehicle is on your account too. - **Service history** — date, km, computed next date/km, and changed-parts flags. - **Technical checks** — roadworthiness inspections; result, cost, station and the certificate's valid-until, which drives the next-due date. - **Maintenance** — workshop visits and repairs (type/status, workshop, parts, labour + parts cost, invoice, warranty-until). - **Fuel** — refills with a summary panel (average / best / worst consumption, cost per km, price per litre), measured between full tanks. - **Documents** — insurance, registration, road tax, … with a renewal badge. - **Parts** — the per-car parts catalog. - **Reminders** — date/odometer, one-off or recurring; server-derived ones are read-only. Also **share** the car with other users (read/write, owner only); edit/delete controls are hidden for read-only shares. - **Charging** — the EV charging screen for connected Anker Solix chargers: live status and, in own/proxy control mode, start/stop and charge-limit controls driven by the API Server's OCPP Central System. - **Settings** — split into tabs: *Personal settings* — account (name / email verification / password), appearance (theme light/dark/system, locale, date format, currency, font size), profile (avatar, bio), data **export/import**, and the account-deletion state machine; *Integrations* (Toyota, Anker Solix); *Users* for admins; and *Organization* (create your own — which makes you its admin — or rename/delete the one you administer). - **Users** — user management (list / create / role / reset password / delete) as the admin-only Settings tab; `/admin` redirects there for old links. - **Theming** — light/dark/system app-wide (Tailwind v4 class strategy); `prefs.js` toggles `.dark` on `` and applies the saved theme/locale/date/font. ## Auth & access Login proxies to the API Server, which relays PocketBase's own token — there is no JWT the server mints and no server-side session list. The token is stored client-side and sent as `Authorization` on every call. `auth.js` exposes `isAdmin` and the current profile; the router guards `public` / `admin` routes. Cars are per-user (owned + shared), and the UI mirrors the server's read / write / owner access levels.