> Part of the PWA DevGuide (split from pwa_dev_style.md on 2026-07-02 — see that file for the index; ADR-099).

## 11. Admin System

### Auto-unlock
`empId` from `lm360-session` is checked at page load. If it's in `_RECCE_ADMIN_IDS` → `_adminUnlocked = true` (no password needed).

### Password Verification (3-layer fallback)
1. Remote `admin-cfg-url` JSON: `{password:"plain"}` or `{hash:"sha256"}`
2. Local IndexedDB `admin-hash` (SHA-256 of password)
3. Hardcoded `DEFAULT_ADMIN_HASH`

Uses `crypto.subtle.digest('SHA-256', ...)` for all hashing.

### Admin Session
- Unlocked: `gas-locked/slides-gas-locked` div hidden, `gas-unlocked/slides-gas-unlocked` shown
- Auto-locks after 10 minutes

### Admin Home (when unlocked)
- `loadHomeAdmin()` replaces normal home view in-place (injects filter bar, changes stat labels, hides New Recce button)
- Fetches all 500 rows from PostgREST
- 4 filter controls + search input
- 4 grouping modes: Recent (date desc) / Agent / Brand / City
- Tap card → `openAdminRecce(subId)` → if `view_url` → new tab, else → `ardl-overlay` detail sheet

---

## 12. Draft System

### Save (debounced 500ms)
`saveDraft()` → saves `current` to draft store (form state, store selection, GPS, counts) → `saveDraftPhotos()` saves all photo blobs as base64 to `photos` key.

### Resume
`checkDraft()` → shows resume banner if draft has storeName
`resumeDraft()` → restores F, selectedStore/manualMode, gpsCoords, all photo arrays, navigates to form-store

### Exit Dialog (3 options)
- Save Draft & Exit → `saveDraft()` + navigate home
- Discard → clear all state + `dbDel('draft','current')` + navigate home
- Continue Recce → close dialog only

---

## 13. View / Report System

### Offline View (`viewRecce(id)`)
Generates an HTML report in-browser from local base64 data, opens in new tab via blob URL (revoked after 30 seconds).

### Server View
When `entry.viewUrl` exists → `window.open(viewUrl)` directly.

### Edit Mode (`editRecce(id)`)
- Loads entry from IndexedDB
- Restores all arrays from base64 blobs
- Sets `F.editingId = id`
- Reopens form at Step 1 in manual mode
- On re-submit: reuses same `id`, original `timestamp`, adds `editedAt`

---

## 14. Initialization (`init`)

```
init()
  ├─ detectHostBar() — checks hostname/gap for tiiny.host → sets --host-bar CSS var
  ├─ setLang(LANG) — applies stored language preference (en/hi)
  ├─ applyFontSize() — applies stored font-size level (0–4)
  ├─ checkAppUpdate() — compares CACHE_VER vs localStorage; shows update banner if changed
  ├─ initDB() — opens RecceDB v4
  ├─ loadSettings() — loads all cfg keys, restores stores, quality, brands, geo-stamp
  ├─ loadHome() — renders home (admin or field agent view)
  ├─ checkDraft() — shows resume banner if draft exists
  ├─ netStatus() — sets online/offline indicator
  ├─ addEventListener('online') → netStatus + syncNow
  ├─ addEventListener('offline') → netStatus
  ├─ injectHubButtons() — adds 🏡 Hub button to every .hdr
  ├─ sets today-date text
  ├─ updAvailTag() — updates store count display
  └─ auto-fetches stores.json from server (silent, online only)
```

---

