# Google Sheets live sync — setup (one-time, ~5 minutes)

The apps talk to your Google Sheet through a **Google Apps Script Web App bridge**. The script runs as you (the sheet owner), so **no Google credentials, API keys or service-account keys are ever in the app code** — only the Web App URL and a shared token you choose.

## 1. Create / open the sheet
Any Google Sheet works. The bridge uses (or creates) a tab called **Contestants**.

## 2. Add the script
1. In the sheet: **Extensions → Apps Script**.
2. Delete anything in the editor and paste the whole contents of `google-apps-script/Code.gs`.
3. At the top, change `ACCESS_TOKEN` to a long random string (e.g. 30+ random characters). Save.

## 3. Deploy as a Web App
1. **Deploy → New deployment → ⚙ → Web app**
2. *Execute as:* **Me** · *Who has access:* **Anyone**
3. Click **Deploy**, approve the permission prompt, and copy the URL ending in **/exec**.

> If you later edit Code.gs, use **Deploy → Manage deployments → ✎ → New version** so the same URL picks up the changes.

## 4. Connect the app
Open **contestant-manager.html → ⇅ Sheets** button:
1. Paste the Web App URL and your token → **Save & test** (should show "Connected ✓").
2. **⬇ Pull from Sheet** — reads the sheet into the app.
3. **⬆ Push to Sheet** — writes the app database into the sheet.

## How syncing behaves
- **Stable IDs** — the `id` column is the record key. Rows without one get an id assigned automatically on first pull. Never edit or delete the `id` cells.
- **No duplicates** — records are matched by `id` first, then by name + country as a fallback, so re-pulling or re-pushing never doubles people up.
- **Newer wins** — on pull, a sheet row only replaces the app's copy if its `updatedAt` is newer.
- **Push never deletes** — pushing updates matching rows and appends new ones; it never removes sheet rows.
- **Manual refresh** — pull/push are explicit button presses, so you always know what moved and when (last pull/push times are shown in the panel).
- Errors (wrong token, script asking for login, timeouts) are shown in plain English in the panel.

## Columns
The bridge maintains these headers on the Contestants tab (order doesn't matter; matching is by header name; extra columns you add by hand are preserved):

`id, regNum, fullName, gender, dob, ageManual, country, city, nationality, passportNo, passportExpiry, category, tafseerLang, qiraah, status, email, phone, whatsapp, emergencyName, emergencyPhone, origin, visaRequired, visaStatus, visaNotes, flightNeeded, flightBooked, arrAirport, arrTerminal, arrAirline, arrFlight, arrDate, arrTime, pickupNeeded, depAirport, depTerminal, depAirline, depFlight, depDate, depTime, dropoffNeeded, travelNotes, withFamily, familyTotal, familyAdults, familyChildren, familyNames, accomNeeded, accomPeople, checkInDate, checkOutDate, room, accomNotes, dietary, allergies, medical, notes, createdAt, updatedAt`

Dates should be `YYYY-MM-DD` (the app also cleans up `dd/mm/yyyy` on CSV import). Yes/no fields are `Yes` / `No`.

## Two ways to use it
- **Sheet as master**: registrations land in the sheet → press *Pull* whenever you want the apps updated.
- **App as master, sheet as mirror**: work in Contestant Manager → press *Push* to keep the sheet as a live export for trustees/committee.

## Security note
The token is visible to anyone who can open contestant-manager.html on your device. It only unlocks this one bridge (read/write of the Contestants tab) — it grants no access to your Google account. Rotate it any time by changing `ACCESS_TOKEN` + redeploying, then re-saving in the Sheets panel.
