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

## 10. Sync Architecture (3-Stage)

```
syncNow()
  ├─ offline → toast 'Offline'
  ├─ fetch all local subs → filter pending/failed
  ├─ also backfill: synced entries without gasAt
  │
  ├─ For each pending:
  │   ├─ fireGas(s) — no-cors POST to GAS URL (fire-and-forget)
  │   │   └── GAS: uploads photos to Drive, writes Google Sheet, generates Slides
  │   └─ syncToPostgres(s):
  │       ├─ Step 1: POST /slides-proxy → type:'save-photos'
  │       │   └── uploads all photos → returns {photoUrls: {front,inside,brandings,addl,doc}}
  │       ├─ Step 2: POST /slides-proxy → type:'save-view'
  │       │   └── generates rich HTML report → returns {url: view_url}
  │       └─ Step 3: POST /db/submissions (PostgREST upsert)
  │           └── stores metadata + photo URLs + view_url
  │
  ├─ On success: status='synced', syncedAt, viewUrl saved to IndexedDB
  └─ On failure: status='failed'
```

**Backfill:** Entries already synced (status='synced') but without `gasAt` → re-fired to GAS to catch older entries.

### PostgREST Schema (submissions table, recce schema)

```
sub_id, store_name, brand, visit_date, address, mobile, city,
has_branding, branding_count, has_video,
front_photos, inside_photos, addl_photos,
gps_lat, gps_lng,
submitted_by, submitted_at,
photo_urls (JSON), view_url, status,
meta: { brandings:[{type,w,h,qty,sqft}], videoNote, videoCmt }
```

### Google Slides Generation

`makeSlides(id)` (from submission card "📊 Slides" button):
- Requires `slides-gas-url` configured (admin-locked setting)
- POSTs to `/slides-proxy/` with `type:'recce'`, all photo groups as base64, store info
- On success: opens Google Slides URL in new tab, saves `entry.slideUrl`

### Rich HTML Report (`generateRecceReportHtml`)

Generated client-side for offline view, OR server-hosted after sync.
- Dark gradient hero: store name, address, brand, visit date, agent, mobile, GPS Maps link, submitted timestamp
- Photo grids: 1-col / 2-col / 3-col / auto layout based on count
- Branding items: number badge, type, dimensions spec row, 1:1 aspect photo
- Video section (text note if video present)
- Document preview (image with print-ready size)
- Print / Save as PDF button

---

