Costs rendered as bare numbers because the project had no currency to render them with. Add one to the profile, beside the existing appearance preferences: - users.currency in PocketBase, exposed via /api/me, validated against the same 28 codes in the schema, the API and the web app. - Settings offers the European currencies plus the non-European ones the panel already had. Labels come from Intl.DisplayNames rather than a hand-kept table, so the lists read in the user's own language and sort by what is actually on screen. The single "Language & region" picker becomes two controls over the one stored BCP-47 tag, covering European languages and countries, so the two can be mixed (English in Poland). The API now enforces the language-REGION shape: the web app feeds the tag straight to Intl, which throws on a malformed one rather than falling back. formatKm and the km-count labels passed no locale, so kilometres followed the browser while the dates and costs beside them followed the saved preference. They now share one helper that uses the preference. Rename the "Maintenance log" tab to "Maintenance", the name the reminder-type list already used. Amounts are display-only: nothing is converted, so changing currency reinterprets existing records rather than recalculating them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Car Control Project
A car control & service tracking system. Built incrementally — starting with a
car maintenance tracker (modeled on Car Service.xlsx) and growing toward
live integration with the car via an ESP32 device.
Architecture
All clients communicate with the database only through the API Server — nothing talks to PocketBase directly.
┌──────────────────┐
Web App (Vue) ─────▶│ │
Phone App (Flutter)▶│ API Server │────▶ PocketBase
Home Assistant ────▶│ (Go, stdlib) │ (10.2.1.10:8027)
ESP32 device ──────▶│ │
└──────────────────┘
| Component | Stack | Status | Docs |
|---|---|---|---|
| API Server | Go (stdlib) | ✅ built, running, verified | API Server/README.md |
| Database | PocketBase | ✅ running, schema + seed done | — |
| Web App | Vue 3 + Vite + Tailwind v4 | ✅ full feature set (below) | Web App/README.md |
| Phone App | Flutter (Android) | ✅ web parity + biometric login | Phone App/README.md |
| Home Assistant Plugin | — | ⬜ later | — |
| Car Agent Device | ESP32 | ⬜ later | — |
The Web and Phone apps are at feature parity (the phone omits only data export/import).
Features
- Maintenance tracking — cars, service history (date/odometer + which parts were changed), and a per-car parts catalog, with next-due date/km status.
- Accounts & sessions — JWT login, per-device active sessions with remote logout, profile + appearance preferences (theme/locale/date/font), email verification, and account deletion.
- Per-user ownership & sharing — each car has an owner and can be shared with other users as read or write; the UI mirrors the server's access checks.
- Admin — role-gated user management (create / role / reset password / delete).
- Phone biometric login & app lock — fingerprint / face sign-in with an app-lock that requires an unlock on relaunch (with a short grace period for quick app-switches). See the Phone App README.
Auth model
All three apps share one auth model: login via POST /api/auth/login returns a
JWT issued by the API Server (after verifying against PocketBase users), and
every other endpoint requires Authorization: Bearer <token>. Each login also
creates a server-side session whose id is embedded in the token, so sessions can
be listed and revoked. Access to cars/records/parts is gated by per-user
ownership and shares; admin endpoints require the admin role.
Domain (from Car Service.xlsx)
- Cars — one per vehicle (was: one spreadsheet sheet), with spec fields (engine / transmission / differential oil, brake fluid, coolant, VIN, …) and configurable service intervals.
- Service records — date + odometer per service, plus which parts were changed (oil & oil filter, engine air filter, cabin air filter).
- Parts — per-car catalog of part numbers.
Key spreadsheet formulas, reproduced by the API Server on read:
Next Service Date = service date + serviceIntervalDays (default 365; Excel: =A+365)
Next Service Km = service km + serviceIntervalKm (default 15 000; Excel: =B+15000)
Intervals are configurable per car.
Getting started
Bring up the stack in this order — each app's README has the details:
- API Server — configure
.env, runsetup-pocketbase.mjs, start the server. This must be running for either app. - Web App —
npm install && npm run dev(proxies/apito the server). - Phone App —
flutter build apk/flutter runwith--dart-define=API_BASE=http://<server-ip>:8080/api.
Layout
Car Control Project/
├── API Server/ # Go gateway to PocketBase (the only DB client)
├── Web App/ # Vue 3 + Vite + Tailwind v4
├── Phone App/ # Flutter (Android)
├── Home Assistant Plugin/ # later phase
└── Car Agent Device/ # ESP32, later phase