# Participant Guest Collection – Implementation Progress

This document records the steps taken to move guest data from `Participant.person_data.dataGrid` into a dedicated **ParticipantGuest** collection, with normalized fields and a single source of truth.

---

## Summary

| Step | Description | Status |
|------|-------------|--------|
| 1 | New ParticipantGuest model | Done |
| 2 | Guest API (GET/PUT …/participants/:id/guests) | Done |
| 3 | Participant PUT only strips guest keys from person_data; only PUT /:id/guests writes to ParticipantGuest | Done |
| 4 | UserFormView loads guests from GET …/guests; PanelDataService uses progress_breakdown.guest | Done |
| 5 | UserFormView save: call PUT …/guests, send person_data without guest keys | Done |
| 6 | Participants slim response + AllocationModal use guest list from ParticipantGuest | Done |
| Migration | One-time script to backfill existing person_data.dataGrid into ParticipantGuest | Skipped for now |

---

## Step 1: New ParticipantGuest Model

**Goal:** Introduce a dedicated collection for participant guests, linked by `participant_id`.

**File created:** `dashboard/models/ParticipantGuest.js`

**Schema:**
- `participant_id` (ObjectId, ref Participant, required)
- `display_name` (String) – normalized from fullName1 / fullName / name / email
- `email` (String)
- `phone_number` (String) – normalized from row phoneNumber object/string
- `meta` (Object, default {}) – raw form row for round-trip
- `order` (Number, default 0)
- `timestamps: true`, collection: `participant_guests`

**Indexes:** `participant_id`, `(participant_id, order)`

---

## Step 2: Guest API for Participants

**Goal:** Expose GET and PUT for `.../participants/:id/guests`.

**File changed:** `dashboard/Routes/ParticipantRoutes.js`

**Added:**
- `ParticipantGuest` require and `normalizeGuestRow(row, order)` helper (normalizes display_name, email, phone_number, meta from form row).
- **GET /:id/guests** – validate id, return list of ParticipantGuest for that participant, sorted by `order`.
- **PUT /:id/guests** – validate id and body (array); replace all guests for that participant (deleteMany + insertMany); return new list.

Routes registered **before** `GET /:id` so `/:id/guests` is matched correctly.

---

## Step 3: Participant PUT Strips Guest Keys Only; Guests Written Only via PUT /:id/guests

**Goal:** `person_data` must not store guest data. Guest list is stored only in `participant_guests`. When referencing a guest elsewhere, use `participant_guest._id`.

**File changed:** `dashboard/Routes/ParticipantRoutes.js` (PUT `/:id` handler)

**Behavior:**
1. When handling `person_data`, delete `dataGrid`, `guestDetails`, and `guest` from `safePersonData` so they are never persisted.
2. Do **not** read or write ParticipantGuest in this handler. Only **PUT /:id/guests** writes to `participant_guests` (replace-all: deleteMany then insertMany).
3. Guest progress for the badge comes from the client’s `progress_breakdown` or from the PUT /guests flow.

---

## Step 4: UserFormView Loads Guests from API; PanelDataService Uses progress_breakdown.guest

**Goal:** Populate the Guest form grid from the new collection and stop deriving guest progress from `person_data.dataGrid` (which is no longer stored).

**Files changed:**
- `src/components/UserFormView.js` – In `fetchFormAndUser()`, when `normalizedSegment === 'guest'`, fetch `GET .../ems/participants/${recordToSubmit._id}/guests` and set `autoMappedData.dataGrid` from the response (using `meta` when present, else display_name/email/phone_number).
- `src/services/PanelDataService.js` – In `loadSavedProgress`, use `progress_breakdown.guest` directly instead of `getPersonDataGridCount(p.person_data, "dataGrid")`; removed the stale guest-progress persistence block.

---

## Step 5: UserFormView Save – PUT Guests Explicitly, person_data Without Guest Keys

**Goal:** On Guest form submit, save guests via the dedicated API and send participant PUT without guest keys.

**File changed:** `src/components/UserFormView.js` (`handleFormSubmit`)

**Behavior:** When `normalizedSegment === 'guest'`:
1. Extract guest rows from `submission.data` (dataGrid / guestDetails.dataGrid / guest.dataGrid).
2. Call `PUT .../ems/participants/${participantRecordId}/guests` with that array.
3. Build `person_data` for the participant PUT by copying `submission.data` and deleting `dataGrid`, `guestDetails`, `guest`.
4. Send the participant PUT with this trimmed `person_data` and `progress_breakdown`.

---

## Step 6: Participants Slim + AllocationModal Use Guest List from ParticipantGuest

**Goal:** AllocationModal (and any consumer of the slim participants list) receives guest data from the new collection instead of from `person_data.dataGrid`.

**File changed:** `dashboard/Routes/ParticipantRoutes.js` (GET `/` with `slim=1`)

**Behavior:** After the existing slim trim of `person_data`:
1. Fetch all ParticipantGuest documents for the participant IDs in the response.
2. Group by `participant_id`.
3. For each participant, set `person_data.dataGrid` to the list of rows (from `meta` or fallback to display_name/email/phone_number).

No frontend change: AllocationModal still reads `meta?.dataGrid || meta?.guestDetails?.dataGrid || meta?.guest?.dataGrid` and gets the list from the slim response.

---

## Migration (Skipped for Now)

A one-time migration script could:
- Find participants that still have `person_data.dataGrid` (or guestDetails.dataGrid / guest.dataGrid).
- Create ParticipantGuest documents using the same normalization as the PUT handler.
- Optionally remove those keys from `person_data`.

**Decision:** Not needed for now. New and updated data flows through PUT /:id/guests. Existing documents with guest data in `person_data` are unchanged; re-saving the Guest form writes to ParticipantGuest and the participant PUT strips guest keys from `person_data`. Migration can be added later to backfill and optionally clean old `person_data`.

---

## Guest ID (participant_guest._id)

- **Do not** store guest rows in `person_data`; store only in `participant_guests`.
- When you need to reference a specific guest (e.g. in another form, booking, or UI), use the document `_id` from the `participant_guests` collection (`participant_guest._id`). Resolve the full guest by `GET .../participants/:participantId/guests` or by querying `ParticipantGuest.findById(guestId)`.

---

## Key Files Reference

| Purpose | File |
|--------|------|
| ParticipantGuest model | `dashboard/models/ParticipantGuest.js` |
| Guest API + normalization + PUT extraction/slim attach | `dashboard/Routes/ParticipantRoutes.js` |
| Form load/save guests | `src/components/UserFormView.js` |
| Guest progress from progress_breakdown | `src/services/PanelDataService.js` |
| Room allocation guest list (reads slim response) | `src/components/ems/AllocationModal.js` |
