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

## 8. Store List Management

### 8.1 Upload Flow

```
handleStoreUpload(e)
  ├─ CSV: split by newline/comma, parse headers, map rows
  └─ XLSX: XLSX.utils.sheet_to_json (SheetJS v0.18.5)
  → parseStoreRow() → filter(name.trim) → showStorePreview()
  → confirmStorePreview()
    ├─ has brand column → mergeStores()
    └─ no brand → show brand-assign-dialog (chip selector) → confirmBrandAssign() → mergeStores()
```

### 8.2 Column Mapping (`parseStoreRow`)

| Field | Column aliases tried |
|---|---|
| name | sub dealer name, sub dealer, dealer name, store name, store, name |
| address | address, addr, location, area |
| mobile | contact detail, contact no., contact no, mobile no, mobile, phone, mob, contact |
| brand | **EXACT match only**: brand, brand name, brand_name, brandname |
| distName | dist name, distributor name, distributor, dist, territory, tsm, asm |
| city | city, district, town |
| sales | 2025 full year, 2025, full year sales, fy25, sales |
| proj | 2026 projection, 2026, projection, fy26, target |
| glowSign | glow sign, glow |
| inshop | inshop branding, inshop, in-shop, in shop |

**Note:** Brand column uses exact match intentionally — "Inshop Branding" partial match avoidance.

### 8.3 Deduplication (`dedupeStores`)

Key: `name.toLowerCase() + '__' + brand.toLowerCase() + '__' + address.toLowerCase().slice(0,30)`

On true duplicate: keeps first record, merges missing fields from second.

### 8.4 Auto-fetch on Init

On every startup (when online): fetches `DEFAULT_STORES_URL` and replaces local store cache silently.

---

## 9. GPS Capture

Two modes:

**Auto (on new recce start):**
`autoRequestGPS()` — `enableHighAccuracy:true, timeout:20000, maximumAge:60000`
- Updates `gpsCoords` silently
- Shows quality feedback in Settings geo-status div

**Manual (form step 1):**
`captureGPS()` — `enableHighAccuracy:true, timeout:10000`
- Shows "Getting location…" → "✓ lat, lng (±Xm)"
- Denied → error toast with Chrome instructions

Quality thresholds: ≤15m = Excellent, ≤50m = Good, ≤200m = Fair, >200m = Poor (cell tower)

---

