## Common plan prefix (include at the top of every coding-agent prompt)

### Product goal (v1)

Integrate defibrillator (DAE) discovery into the app using the embedded SQLite DB query helper `getNearbyDefibs()`.

Deliver:

1) A new left-drawer link to a new view `DAEList`.
2) In an active alert `Situation` view, a button `Afficher les défibrillateurs` that:
   - enables DAE display inside a 10km corridor around the segment between user location and alert location
   - navigates to the alert map tab
3) On alert map, render DAE markers; tapping a DAE opens `DAEItem`.
4) `DAEList` screen with bottom navigation: `Liste` (default) and `Carte`, showing defibs nearest→farthest around the user, within 10km.
5) `DAEItem` screen with bottom navigation: `Infos` (default) and `Carte` (map + itinerary to the selected DAE; reuse alert routing patterns).
6) During alert posting, if cardiac-related keywords are detected in the alert subject, show a persistent modal (must remain visible even after navigation, and must work offline) with:
   - `Chercher un défibrillateur` → go to `DAEList`
   - `Non merci` → dismiss

### v1 decisions (explicit)

1) Availability display: use `horaires_std` (structured JSON) to show open/closed/unknown status and schedule details.
   - `horaires_std` shape: `{ days: number[]|null, slots: {open,close}[]|null, is24h: boolean, businessHours: boolean, nightHours: boolean, events: boolean, notes: string }`
   - `days`: ISO 8601 day numbers (1=Mon … 7=Sun), `null` if unknown.
   - `slots`: `[{open:"HH:MM", close:"HH:MM"}]`, `null` if no specific times.
   - Availability logic (pure function `getDefibAvailability(horaires_std, disponible_24h)` → `{ status: "open"|"closed"|"unknown", label: string }`):
     - `disponible_24h === 1` → always `"open"`, label `"24h/24 7j/7"`
     - `is24h && days includes today` → `"open"`
     - `days` includes today + current time falls within a `slot` → `"open"`
     - `days` includes today + no slots + `businessHours` → approximate Mon-Fri 8h-18h → `"open"` or `"closed"`
     - `days` does not include today → `"closed"`
     - `events === true` → `"unknown"`, label `"Selon événements"`
     - Else → `"unknown"`
   - No hard filter: show all defibs regardless of availability, but display status visually (green/red/grey dot or icon).
2) Corridor filter: points are kept if distance to the user→alert line segment is ≤ `corridorMeters = 10_000`.
3) Alert-map DAE markers: v1 uses a separate, non-clustered layer (do not mix into alert clusters).
4) Location permission denied: use last-known location; if none, show an explanatory empty state (no hard block).
5) DB open failure: the app must not crash; DAE UI shows an error empty state and overlay remains disabled.
6) Keyword detection: normalized-text + regex list (no fuzzy search library) in v1.

### Relevant anchors in codebase

- Defib query wrapper: `src/data/getNearbyDefibs.js` exports `getNearbyDefibs()`.
- Repo query: `src/db/defibsRepo.js` — SELECTs and JSON-parses `horaires_std`.
- Embedded DB bootstrap: `src/db/openDb.js` requires `../assets/db/geodae.db`.
- Drawer navigation: `src/navigation/Drawer.js`.
- Drawer sections: `src/navigation/DrawerNav/DrawerItemList.js`.
- Header titles: `src/navigation/RootStack.js`.
- Alert tabs: `src/scenes/AlertCur/Tabs.js`.
- Alert map feature pipeline: `src/scenes/AlertCurMap/useFeatures.js` + `src/scenes/AlertCurMap/useOnPress.js`.
- Persistent modal pattern: `src/containers/SmsDisclaimerModel/index.js` uses Paper `Portal` + `Modal`.
- Alert posting flow: `src/scenes/SendAlertConfirm/useOnSubmit.js`.
- Preprocessing pipeline: `scripts/dae/` (geodae-to-csv.js → csv-to-sqlite.mjs).

---

## Split tasks (agent-ready)

### Task 1 — Validate embedded DB asset packaging and schema assumptions

Objective: ensure the bundled SQLite `geodae.db` is present and accessible on-device, and confirm expected schema columns exist.

Notes:

- `src/db/openDb.js` calls `require('../assets/db/geodae.db')`.
- In the current workspace, `src/assets/db/` may be missing; find the DB source and ensure it is bundled for Expo.

Acceptance criteria:

- App can open the DB without throwing at the asset require.
- Query `SELECT ... FROM defibs` works with columns: `id, latitude, longitude, nom, adresse, horaires, horaires_std, acces, disponible_24h` and `h3`.
- `horaires_std` is a JSON string parseable by `JSON.parse()` (already handled in `defibsRepo.js`).
- If DB open fails, DAE screens show a non-blocking error empty state and overlay stays disabled.

---

### Task 2 — Implement geo utilities for corridor filtering + availability helper (pure, unit-testable)

Objective: create pure utilities to compute the query radius for the corridor overlay, filter defibs to those inside the corridor, and determine real-time availability from `horaires_std`.

#### 2a — Geo / corridor utilities

Suggested exports (e.g. `src/utils/geo/corridor.js`):

- `toLonLat({ latitude, longitude })`
- `computeCorridorQueryRadiusMeters({ userLonLat, alertLonLat, corridorMeters })`
- `filterDefibsInCorridor({ defibs, userLonLat, alertLonLat, corridorMeters })`

Implementation guidance:

- Use the existing Turf usage patterns already present in the alert map stack.
- Corridor definition: keep point if distance to the line segment ≤ `corridorMeters`.
- Query strategy: query around midpoint with radius `segmentLengthMeters / 2 + corridorMeters`, then apply corridor filter in JS and cap markers (e.g. 200) to keep map responsive.

#### 2b — Availability helper

Suggested export (e.g. `src/utils/dae/getDefibAvailability.js`):

- `getDefibAvailability(horaires_std, disponible_24h, now?)` → `{ status: "open"|"closed"|"unknown", label: string }`

The `horaires_std` object has shape:
```
{ days: number[]|null, slots: {open,close}[]|null, is24h, businessHours, nightHours, events, notes }
```

Logic (in priority order):
1. `disponible_24h === 1` → `"open"`, label `"24h/24 7j/7"`
2. `is24h === true` and `days` includes current ISO day → `"open"`, label `"24h/24"`
3. `days !== null` and does not include current ISO day → `"closed"`, label day range from `days`
4. `days` includes today and `slots` is non-empty → check if current time falls within any slot → `"open"` or `"closed"` with next open/close time as label
5. `businessHours === true` (no explicit slots) → approximate Mon-Fri 08:00-18:00 → `"open"` or `"closed"`
6. `nightHours === true` → approximate 20:00-08:00 → `"open"` or `"closed"`
7. `events === true` → `"unknown"`, label `"Selon événements"`
8. Fallback → `"unknown"`, label from `notes` or `"Horaires non renseignés"`

The `now` parameter (defaults to `new Date()`) enables deterministic testing.

Acceptance criteria:

- Given synthetic points, corridor filter is correct and deterministic.
- Given synthetic `horaires_std` + fixed `now`, availability status is correct.

---

### Task 3 — Add a Defibs store (zustand atom) for caching + overlay + selection + modal state

Objective: add centralized state to avoid repeated DB queries and coordinate UI across screens.

State:

- `nearUserDefibs: []`
- `corridorDefibs: []` (or keyed by current alert id if needed)
- `showDefibsOnAlertMap: false`
- `selectedDefib: null`
- `showDaeSuggestModal: false`

Actions (suggested):

- `loadNearUser({ userLonLat })`
- `loadCorridor({ userLonLat, alertLonLat })`
- `setShowDefibsOnAlertMap(bool)`
- `setSelectedDefib(defib)`
- `setShowDaeSuggestModal(bool)`
- `reset()`

Acceptance criteria:

- Other tasks can call actions without duplicating query/filter logic.

---

### Task 4 — Create `DAEList` scene with bottom tabs: `Liste` + `Carte`

Objective: add `DAEList` screen with bottom navigation and two tabs.

UI specifics (from existing patterns):

- Follow the app’s bottom-tab pattern.
- Tab icons:
  - Liste: `format-list-bulleted`
  - Carte: `map-marker-outline`

Behavior:

- Get user location (current if possible; else last-known).
- Query within 10km using `getNearbyDefibs({ radiusMeters: 10_000 })`.
- No hard availability filter: show all defibs. Each row shows real-time availability status via `getDefibAvailability(horaires_std, disponible_24h)`.
- Sort by `distanceMeters` ascending.
- Empty states:
  - no location → explain how to enable permission
  - DB error → explain that DAE database is unavailable

List row content:
- Name (`nom`), distance, address summary.
- Availability status indicator: green dot + "Ouvert" / red dot + "Fermé" / grey dot + "Inconnu" (or label from `getDefibAvailability`).
- If `horaires_std.notes` is non-empty, show it as a secondary line.

Carte tab:
- Map markers colored by availability status (green/red/grey).

Interaction:

- Tap list row or map marker → set `selectedDefib` in store and navigate to `DAEItem`.

Acceptance criteria:

- Drawer link opens `DAEList` and list is sorted nearest→farthest.
- Availability status is displayed per row/marker.

---

### Task 5 — Create `DAEItem` scene with bottom tabs: `Infos` + `Carte`

Objective: create DAE detail view for a selected defib.

Tab icons:

- Infos: `information-outline`
- Carte: `map-marker-outline`

Infos tab content:

- Name (`nom`), address (`adresse`), access (`acces`), distance.
- Availability section:
  - Current status via `getDefibAvailability()` with colored indicator.
  - Schedule details from `horaires_std`:
    - If `is24h`: "Disponible 24h/24"
    - If `days`: show day range (e.g. "Lun-Ven")
    - If `slots`: show time slots (e.g. "08:00 - 18:00")
    - If `businessHours`: "Heures ouvrables"
    - If `nightHours`: "Heures de nuit"
    - If `events`: "Selon événements"
    - If `notes`: show notes text
  - Fallback: show raw `horaires` string if `horaires_std` is null/empty.
- Add an `Itinéraire` button that switches to the `Carte` tab.

Carte tab:

- Map + itinerary to the defib coordinates.
- Reuse alert routing implementation patterns (OSRM fetch etc.).
- Offline behavior: show a clear message that routing is unavailable offline.

Acceptance criteria:

- From list or alert map marker, `DAEItem` shows the correct selected defib.

---

### Task 6 — Navigation wiring: `DAEList` in drawer + `DAEItem` hidden

Objective: register new screens and keep drawer sections consistent.

Implementation notes:

- Add `<Drawer.Screen name='DAEList'>` with label `Défibrillateurs`.
- Drawer icon: `MaterialCommunityIcons` name `heart-pulse`.
- Add `<Drawer.Screen name='DAEItem'>` hidden (not shown in menu) and only navigated programmatically.
- Adjust section indices in `DrawerItemList.js` if adding a screen shifts boundaries.
- Add header title cases for `DAEList` and `DAEItem`.

Acceptance criteria:

- Drawer link appears under the intended section and opens `DAEList`.
- `DAEItem` can be navigated to programmatically.

---

### Task 7 — Add Situation button: enable corridor overlay and navigate to alert map

Objective: in active alert Situation view, add `Afficher les défibrillateurs`.

UI specifics:

- Use the app’s existing action button pattern.
- Icon: `MaterialCommunityIcons` name `heart-pulse`.
- Position: next to/after existing main actions (align with current layout conventions).

Behavior:

1) Get user coords.
2) Get alert coords.
3) Load corridor defibs using midpoint query radius + corridor filter.
4) Store results and set overlay enabled.
5) Navigate to map tab using existing nested navigation pattern.

Acceptance criteria:

- Tap leads to alert map and DAE markers appear.

---

### Task 8 — Alert map overlay: render DAE markers (separate layer) and open `DAEItem` on tap

Objective: add a DAE marker layer to the alert map.

Implementation notes:

- Extend the existing feature pipeline to include DAE features when overlay is enabled.
- Add DAE icon images to map images (3 variants: green/red/grey for open/closed/unknown).
- Compute availability via `getDefibAvailability()` to select marker color.
- Update press handling to detect DAE features and navigate to `DAEItem`.
- v1 does not cluster DAE markers.

Acceptance criteria:

- Markers appear/disappear based on overlay flag.
- Tapping a marker opens `DAEItem`.

---

### Task 9 — Keyword detection during alert posting + persistent DAE suggestion modal

Objective: detect cardiac-related terms in alert subject and display a global modal that remains visible even after navigation.

Detection (v1):

- Normalize text: lowercase + remove diacritics.
- Regex list covering common terms and typos, e.g.:
  - cardiaque, cardiac, cardique
  - coeur, cœur
  - malaise, mailaise, mallaise
  - inconscient
  - evanoui, évanoui (variants)
  - arret, arrêt (especially arrêt cardiaque)
  - defibrillateur, défibrillateur
  - reanimation, réanimation
  - massage cardiaque
  - ne respire plus

Modal:

- Use Paper `Portal` + `Modal` mounted high in the tree so it persists across navigations.
- CTA:
  - `Chercher un défibrillateur` → dismiss + navigate to `DAEList`
  - `Non merci` → dismiss

Acceptance criteria:

- Modal shows offline.
- Modal remains visible after redirect to current alert view.

---

### Task 10 — Verification checklist (manual)

Objective: provide a deterministic checklist.

Checklist:

1) Drawer: link visible, opens list.
2) DAEList:
   - permission granted → list populated and sorted
   - permission denied + last-known available → list uses last-known
   - permission denied + no last-known → empty state
   - DB missing/unavailable → non-blocking error empty state
   - each row shows availability status (open/closed/unknown) with colored indicator
3) Situation button: enables overlay and opens alert map.
4) Alert map: DAE markers render with availability-colored icons; tap → DAEItem.
5) DAEItem:
   - Infos tab shows schedule details from `horaires_std` (days, slots, 24h, notes).
   - Availability status is prominently displayed.
   - Routing: online route works; offline shows message.
6) Keyword modal:
   - trigger terms → modal shows
   - modal persists across navigation
   - CTA navigates to DAEList
7) Availability logic:
   - 24h/24 defib → shows "open" at any time
   - Defib with Lun-Ven slots → shows "open" during those hours, "closed" on weekends
   - Defib with `events` flag → shows "unknown" / "Selon événements"

---

## Mermaid overview

```mermaid
flowchart TD
  A[User posts alert] --> B[Keyword detection]
  B -->|match| M[Show persistent DAE modal]
  B -->|no match| C[Navigate to AlertCur Situation]
  M --> C
  M -->|Chercher un defibrillateur| L[DAEList]
  M -->|Non merci| C
  C --> S[Button Afficher les defibrillateurs]
  S --> E[Enable overlay in store]
  E --> MAP[AlertCurMap]
  MAP -->|tap DAE marker| ITEM[DAEItem]
  L -->|tap list item| ITEM
```

