03-Development / 03.12.WUI-Style-Guide-Alt

03.12.WUI Style Guide Alt

03.12. WUI Style Guide Alt

Overview

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.tsinitiateTeslaOAuth(), getStoredReport() API calls - src/stores/app.ts — Pinia store: step, sessionId, error, paymentId, selectedPlan

Step sequence: landing → payment → payment-success → oauth → verify → report


OK Scenario — Successful Authentication & Report Display

 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        │                        │                        │                         │
    ▼                             ▼                        ▼                        ▼                         ▼

NOK Scenarios — Error Paths

NOK-1: OAuth Initiation API Fails (OAuthStep.vue)

 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       │                                 │
    ▼                             ▼                                 ▼

NOK-2: Invalid authUrl Returned

 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"     │                                 │
    ▼                             ▼                                 ▼

NOK-3: User Denies Tesla Access → oauth=error

 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   │                                 │
    ▼                             ▼                                 ▼

NOK-4: OAuth Success but session_id Missing

 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     │
    ▼                             ▼

NOK-5: Payment Data Missing on OAuth Step

 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         │
    ▼                             ▼

NOK-6: Report Fetch Fails (VerificationStep.vue)

 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      │                                 │
    ▼                             ▼                                 ▼

State Management Summary

┌─────────────────────────────────────────────────────────────┐
│                    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