The WUI implements a step-based wizard that guides users through payment → OAuth → report. Authentication state is managed via Pinia store with selective localStorage persistence. The frontend never touches Tesla tokens — only session IDs for one-time report retrieval.
Key files:
- src/components/OAuthStep.vue — OAuth initiation button, error display
- src/components/VerificationStep.vue — Report fetching with loading indicator
- src/App.vue — OAuth callback detection on mount (lines 283-331)
- src/services/api.ts — initiateTeslaOAuth(), getStoredReport() API calls
- src/stores/app.ts — Pinia store: step, sessionId, error, paymentId, selectedPlan
Step sequence: landing → payment → payment-success → oauth → verify → report
Browser OAuthStep.vue App.vue API Tesla
─────── ───────────── ─────── ─── ─────
│ │ │ │ │
│ User clicks │ │ │ │
│ "Connect my Tesla" │ │ │ │
│ ──────────────────────► │ │ │ │
│ │ │ │ │
│ │ store.setProcessing │ │ │
│ │ (true) │ │ │
│ │ store.setError(null) │ │ │
│ │ │ │ │
│ │ initiateTeslaOAuth │ │ │
│ │ (paymentId, plan) │ │ │
│ │ ──────────────────────────────────────────────► │ │
│ │ │ │ │
│ │ │ POST /tesla/oauth/ │ │
│ │ │ initiate │ │
│ │ │ { paymentId, plan } │ │
│ │ │ │ │
│ │ 200 { authUrl } │ │ │
│ │ ◄────────────────────────────────────────────── │ │
│ │ │ │ │
│ │ validate authUrl │ │ │
│ │ (string, non-empty) │ │ │
│ │ │ │ │
│ window.location.href │ │ │ │
│ = authUrl │ │ │ │
│ ◄────────────────────── │ │ │ │
│ │ │ │ │
│ ════════════════════════ HARD REDIRECT TO TESLA ════════════════════════► │ │
│ │ │ │ │
│ [User logs in, grants consent on Tesla] │
│ │ │ │ │
│ ════════════ TESLA REDIRECTS TO API CALLBACK WITH ?code=...&state=... ═════════════════════════► │
│ │ │ │ │
│ │ │ [API exchanges code, │ │
│ │ │ fetches vehicle data,│ │
│ │ │ builds report, │ │
│ │ │ discards token] │ │
│ │ │ │ │
│ ════════ 302 REDIRECT: /?oauth=success&session_id=SESSION_ID ═══════════ │ │
│ │ │ │ │
│ Page loads with │ │ │ │
│ query params │ │ │ │
│ ──────────────────────────────────────────────► │ │ │
│ │ │ │ │
│ │ │ onMounted() { │ │
│ │ │ urlParams.get │ │
│ │ │ ('oauth') │ │
│ │ │ → 'success' │ │
│ │ │ │ │
│ │ │ sessionId = │ │
│ │ │ urlParams.get │ │
│ │ │ ('session_id') │ │
│ │ │ │ │
│ │ │ store.setSessionId │ │
│ │ │ (sessionId) │ │
│ │ │ store.setStep │ │
│ │ │ ('verify') │ │
│ │ │ │ │
│ │ │ window.history │ │
│ │ │ .replaceState() │ │
│ │ │ [clean URL] │ │
│ │ │ } │ │
│ │ │ │ │
│ │ │ │ │
│ VerificationStep.vue │ │ │
│ ──────────────────── │ │ │
│ │ │ │ │
│ │ onMounted() { │ │ │
│ │ sessionId = │ │ │
│ │ store.sessionId │ │ │
│ │ │ │ │
│ [spinner: "Querying │ getStoredReport │ │ │
│ Tesla..."] │ (sessionId) │ │ │
│ │ ──────────────────────────────────────────────► │ │
│ │ │ │ │
│ │ │ GET /tesla/report/ │ │
│ │ │ {session_id} │ │
│ │ │ │ │
│ │ │ _reports.pop() │ │
│ │ │ age < 15 min → OK │ │
│ │ │ │ │
│ │ 200 { vehicle, │ │ │
│ │ vehicleData, │ │ │
│ │ chargingHistory, │ │ │
│ │ ... } │ │ │
│ │ ◄────────────────────────────────────────────── │ │
│ │ │ │ │
│ │ store.setVehicle │ │ │
│ │ Report(report) │ │ │
│ │ store.setStep │ │ │
│ │ ('report') │ │ │
│ │ } │ │ │
│ │ │ │ │
│ Report dashboard │ │ │ │
│ displayed with │ │ │ │
│ PDF download option │ │ │ │
▼ ▼ ▼ ▼ ▼
Browser OAuthStep.vue API
─────── ───────────── ───
│ │ │
│ Click "Connect my Tesla" │ │
│ ──────────────────────► │ │
│ │ │
│ │ initiateTeslaOAuth() │
│ │ ──────────────────────────────► │
│ │ │
│ │ 503 "Tesla API not configured" │
│ │ OR network error │
│ │ ◄────────────────────────────── │
│ │ │
│ │ catch (error) { │
│ │ store.setError( │
│ │ "Failed to initiate │
│ │ Tesla OAuth") │
│ │ store.setProcessing(false) │
│ │ } │
│ │ │
│ Error banner shown │ │
│ "Connect" button │ │
│ re-enabled for retry │ │
▼ ▼ ▼
Browser OAuthStep.vue API
─────── ───────────── ───
│ │ │
│ Click "Connect" │ │
│ ──────────────────────► │ │
│ │ │
│ │ initiateTeslaOAuth() │
│ │ ──────────────────────────────► │
│ │ │
│ │ 200 { authUrl: null } │
│ │ ◄────────────────────────────── │
│ │ │
│ │ if (!authUrl || │
│ │ typeof authUrl !== 'string')
│ │ throw Error('Invalid OAuth │
│ │ URL received from server') │
│ │ │
│ Error: "Invalid OAuth URL │ │
│ received from server" │ │
▼ ▼ ▼
Browser App.vue API
─────── ─────── ───
│ │ │
│ [User denies on Tesla] │ │
│ │ │
│ 302 → /?oauth=error │ │
│ ────────────────────────► │ │
│ │ │
│ │ onMounted() { │
│ │ oauthStatus = 'error' │
│ │ │
│ │ store.setError( │
│ │ "Tesla authentication │
│ │ failed. Please try │
│ │ again.") │
│ │ store.setStep('oauth') │
│ │ │
│ │ window.history │
│ │ .replaceState() │
│ │ [clean URL] │
│ │ } │
│ │ │
│ OAuth step shown with │ │
│ error banner + retry btn │ │
▼ ▼ ▼
Browser App.vue
─────── ───────
│ │
│ 302 → /?oauth=success │
│ (no session_id param!) │
│ ────────────────────────► │
│ │
│ │ onMounted() {
│ │ oauthStatus = 'success'
│ │ sessionId = null
│ │
│ │ store.setError(
│ │ "Tesla authentication
│ │ succeeded but session
│ │ data is missing.
│ │ Please try again.")
│ │ store.setStep('oauth')
│ │ }
│ │
│ OAuth step with error │
│ message + retry button │
▼ ▼
Browser OAuthStep.vue
─────── ─────────────
│ │
│ [page reload / direct │
│ navigation to oauth] │
│ ────────────────────────► │
│ │
│ │ onMounted() {
│ │ paymentId = store.paymentId
│ │ → null
│ │
│ │ // Try localStorage recovery
│ │ savedPaymentId =
│ │ localStorage.getItem(
│ │ 'cpt-payment-id')
│ │ → null
│ │
│ │ store.setError(
│ │ "Payment information
│ │ is missing. Please
│ │ start over.")
│ │ }
│ │
│ Error: missing payment │
│ data, must restart │
▼ ▼
Browser VerificationStep.vue API
─────── ──────────────────── ───
│ │ │
│ [verify step activated] │ │
│ ────────────────────────► │ │
│ │ │
│ │ onMounted() { │
│ [spinner shown] │ getStoredReport(sessionId) │
│ │ ──────────────────────────────► │
│ │ │
│ │ 404 "Report not found or │
│ │ already retrieved" │
│ │ OR 410 "Report expired" │
│ │ ◄────────────────────────────── │
│ │ │
│ │ catch (error) { │
│ │ store.setError( │
│ │ error.message) │
│ │ store.setStep('oauth') │
│ │ } │
│ │ } │
│ │ │
│ Redirected back to │ │
│ OAuth step with error │ │
│ "Try again" available │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Pinia Store (app.ts) │
├──────────────────┬──────────────────────────────────────────┤
│ PERSISTED │ TRANSIENT (memory only) │
│ (localStorage) │ │
├──────────────────┼──────────────────────────────────────────┤
│ paymentId │ sessionId │
│ selectedPlan │ isProcessing │
│ currentStep │ error │
│ │ vehicleReport │
└──────────────────┴──────────────────────────────────────────┘
Why sessionId is NOT persisted: - One-time use — backend deletes report after first retrieval - 15-minute TTL — stale session IDs are useless - Security — prevents replay of report fetching
Recovery on page reload: - paymentId + selectedPlan survive → can re-initiate OAuth - sessionId lost → must re-authenticate with Tesla - currentStep persisted → user lands back on correct step