# SPQ Auth Flow & Page Structure

## File roles

| File | Role |
|------|------|
| `index.html` | **Login/signup page** — the root page. No auth redirect on load. |
| `dashboard.html` | **Dashboard** — the main app (was previously `index.html`). |
| `appointments.html`, `repair-orders.html`, `customers.html` | Sub-pages with shared header/nav pointing to `dashboard.html`. |

## Auth redirects

```
Not logged in:
  / → index.html (login form, NO redirect)
  /dashboard.html → auth guard → redirects to /index.html

Logged in:
  / → detects token in localStorage → redirects to /dashboard.html
  /dashboard.html → stays
  Logout → redirects to /index.html
```

## Why index.html is the login page

Originally `index.html` was the dashboard and `login.html` was the login page. This caused a **redirect loop**: when PocketBase auth state was unstable (stale/expired token), `onAuthStateChanged` on the dashboard saw no user and redirected to `login.html`, whose own `onAuthStateChanged` saw the stored token and redirected back to `index.html` — creating a flip-flop between the two URLs.

**Fix**: Made `index.html` the login page and moved the dashboard to `dashboard.html`. Now visiting the root (`/`) shows the login form directly with zero redirects. The dashboard only redirects when it detects no user (→ `/`), and the login only redirects when it detects a user (→ `/dashboard.html`). No more reciprocal bounce.

## Reference: all redirect targets

When renaming or restructuring pages, update these files:

### Auth failure → `index.html` (login)
- `dashboard.js:122` — `window.location.href = 'index.html'`
- `main.js:44` — `window.location.href = 'index.html'`
- `appointments.js:50` — `window.location.href = 'index.html'`
- `customers.js:78` — `window.location.href = 'index.html'`
- `repair-orders.js:58` — `window.location.href = 'index.html'`
- `shared/header-functionality.js:318` — logout handler → `'index.html'`

### Login success → `dashboard.html`
- `login.js:33` — `window.location.href = 'dashboard.html'`
- `index.html:131` — inline script: `location.replace('dashboard.html?cb='+Date.now())`
- `index.html:577` — login success handler
- `index.html:1035` — signup success handler

### Nav links → `dashboard.html`
All `href="dashboard.html"` in: `appointments.html`, `repair-orders.html`, `customers.html`, `dashboard.html`
- `dashboard.html` nav has `active-page` class on its own link

### Quote deep-link → `dashboard.html?quoteId=...`
- `customers.js:676` — `href="dashboard.html?quoteId=${escapeHtml(quote.id)}"`

### Keyboard shortcut → `dashboard.html`
- `dashboard.js:2144` — case 'q' keyboard shortcut

## Dual-VPS config Host header note

The VPS has two ShopProQuote nginx configs:
- `shopproquote-duckdns` (for `grajmedia.duckdns.org`) — `Host: grajmedia.duckdns.org` ✓ correct
- `shopproquote` (for `shopproquote.graj-media.com`) — `Host: graj-media.com` — **may not match home nginx** which only has `server_name grajmedia.duckdns.org`. See `vps-reverse-proxy` skill's "Host header mismatch → SPA redirect loop" pitfall.
