# Route table — required user inputs

**Audience:** Developers, QA, and anyone filling the [upload form](https://mushinsolutions.com/etsyIO/upload.html)  
**Form UI:** `upload.html` + `phase-table.js` + `route-table.js` + `basecamp-table.js`  
**Pipeline:** `upload_api.py` → `order.json` / `trip-plan.md` → `route.py`

The dashboard route spine is built from **trip phases**, **route legs** (with explicit phase per row), and optional **basecamps** for extra lodging detail.

---

## Required inputs (overview)

| Field | Form control | Role in route generation |
|-------|--------------|---------------------------|
| **Trip title** | Text | Top bar + hero headline; URL slug suggestion |
| **Subtitle** | Text | Optional hero line under title (travelers / group) — not shown in top bar |
| **Main destination** | Text | Starter-leg helper; not a separate dashboard headline |
| **Start / end date** | Date | Validation; starter-leg and phase date labels |
| **Trip start / end** | Text | Bookend places; starter-leg helper |
| **Trip phases** | **Table** (#, Label, Name, Dates, Location) | Phase IDs, names, date ranges, colors — serialized as `###` lines |
| **Actual route / transit legs** | **Table** (Date, **Phase**, From, Link, To, Mode) | Authoritative inter-city and transit spine |
| **Basecamps / overnight stays** | Table *(optional)* | Extra lodging anchors; helps **Suggest starter phases** |

Also required for upload (not route spine): Etsy order #, package, photo zip, EXIF consent, confirm checkbox.

Optional: **subtitle**, day-by-day itinerary textarea, notes, photo placements, timezone.

---

## Trip phases table

Each row is one major block of the trip. Labels: **transit**, **basecamp**, **day trip**, **residency**.

| Column | Required | Notes |
|--------|----------|--------|
| **#** | Yes | Phase number (1, 2, 3 …) |
| **Label** | Yes | Transit / Basecamp / Day trip / Residency |
| **Name** | Recommended | e.g. `Paris residency`, `Departure transit` |
| **Dates** | Recommended | `Mar 10`, `Mar 11–15` |
| **Location** | When not transit | City or lodging area — disabled for transit rows |

**Toolbar**

- **+ Add phase** — new blank row  
- **Suggest starter phases** — builds departure transit, optional basecamp rows from the basecamp table, and return transit from trip dates/destination

On submit, rows serialize to `###` lines in the hidden `phases` field:

```text
### 1 | Departure Transit | Mar 10 | In transit | #0c6fa8
### 2 | Paris basecamp | Mar 11–15 | Paris, FR | #148b63
### 4 | Return Transit | Mar 19–20 | In transit | #8f3d97
```

---

## Route legs table — columns

Each row is **one movement** (train hop, flight, drive between bases, etc.).

| Column | Required | Format | Notes |
|--------|----------|--------|--------|
| **Date** | Recommended | `Mar 10`, `Mar 11–15`, `Mar 16-17` | Should fall inside the selected phase’s date span |
| **Phase** | Yes | Dropdown | Phase number from **Trip phases** table — options refresh when phases change |
| **From** | Yes | Place name | e.g. `Richmond, VA`, `Paris`, `Washington, DC (DCA)` |
| **Link** | Yes | Dropdown | See [Link types](#link-types) |
| **To** | Yes | Place name | Required for every row |
| **Mode** | Recommended | Dropdown | See [Travel modes](#travel-modes). Empty = drive / car |

**Toolbar**

- **+ Add leg** — new blank row  
- **Insert starter legs from trip start/end** — pre-fills first/last rows from *Where did the trip start/end?* and dates; suggests phases if the phase table is sparse

Phase dropdown options show as `1 — Departure transit`, `2 — Paris basecamp`, etc.

---

## Link types

| UI label | Stored value | Use when | Serialized example |
|----------|--------------|----------|-------------------|
| **→ to** | `-->` | Direct leg between two places | `Paris --> Colmar` |
| **, then** | `,` | Continue through an intermediate stop on the same line (no arrow) | `Alexandria, VA, Washington, DC (DCA)` |

**→ to** — normal A-to-B movement (train, flight, drive, basecamp relocation).

**, then** — chained local hops on one movement line, common for “city, state, next airport” without typing `-->` between every comma segment.

---

## Travel modes

| UI label | Stored value | Map / totals behavior |
|----------|--------------|------------------------|
| Drive / car | *(empty)* | Routed on roads; counts toward drive distance |
| Train | `train` | Transit leg; excluded from drive totals |
| Flight | `flight` | Great-circle / flight leg |
| Basecamp | `basecamp` | Relocation between bases; may be augmented with GPS day-trip stops |
| Patriot Express | `patriot express` | Military charter; transit |
| Ferry | `ferry` | Transit |
| Regional transfer | `regional transfer` | Short regional move |

Always set the mode for legs **without geotagged photos** (flights, trains, ferries, long transfers).

---

## Basecamps *(optional table)*

One row per lodging anchor when you want extra detail beyond the phase table. **Location** is required per row; **Lodging** and **Dates** are optional.

| Column | Example |
|--------|---------|
| Dates | `Mar 11–15` (optional) |
| Location | `Paris, FR` |
| Lodging | `AirBnB` |

Serialized as one line per row:

```text
Paris, FR — AirBnB
Mar 11–15 — La Wantzenau — Omar's house, 24 Rue de Madrid
```

Transit-only trips can omit this table entirely.

---

## Serialized format (what the API receives)

On submit, the route table converts to a newline list in `routeLegs` (also embedded in `trip-plan.md`):

```text
<date> | <phase> — <from> --> <to> (<mode>)
<date> | <phase> — <from>, <to> (<mode>)
```

- Date, optional phase number, and route are separated by ` — ` (em dash)  
- Phase prefix uses ` | ` before the route: `Mar 10 | 1 — Richmond, VA --> Alexandria, VA (train)`  
- Mode is optional, in parentheses at the end  
- Comma-chain rows omit `-->` between From and To

**Full Spring Break example** (8 rows → 8 lines):

```text
Mar 10 | 1 — Richmond, VA --> Alexandria, VA (train)
Mar 10 | 1 — Alexandria, VA, Washington, DC (DCA) (train)
Mar 10 | 1 — Washington, DC (DCA) --> Paris, FR (flight)
Mar 11–15 | 2 — Paris --> Colmar (basecamp)
Mar 16-17 | 3 — Colmar --> La Wantzenau (basecamp)
Mar 19 | 4 — La Wantzenau --> Ramstein AB (train)
Mar 19 | 4 — Ramstein AB --> BWI (patriot express)
Mar 20 | 4 — BWI --> Richmond, VA (train)
```

Canonical fixture: `test/lib/spring-break-fixture.mjs`

---

## Validation (client)

- At least **one phase row** in the trip phases table (serializes to `###` headers)  
- At least **one route row** with both **From** and **To** filled, and a **Phase** selected on each filled row  
- Every filled route row must have a valid link choice  
- Destination, dates, start/end nodes, zip, and EXIF checkboxes must be present  
- Basecamps are **optional**

Server (`upload_api._validate_trip_itinerary_fields`) re-checks phases and that at least one leg parses as a valid `From --> To` route intent.

---

## How inputs become the dashboard route

```text
upload form
  ├─ phase table   → trip-plan.md ## Phases (### lines)
  ├─ basecamps     → trip-plan.md ## Known bases (optional)
  └─ route table   → order.json routeLegs (with | phase — prefix)
                     → trip-plan.md ## Days (Route segment + Mode per leg)
                           ↓
                     route.py (_order_route_intent_segments)
                           ↓
                     routeData.legs + photo GPS augment (unplanned stops)
```

**User topology is authoritative:** submitted legs define inter-city hops. GPS photos add day-trip pins and dense legs *within* basecamp drives; they do not replace flights or trains you entered.

---

## Dashboard display (built output)

**Authoritative layout:** [`templates/index.html`](../templates/index.html) + [`templates/app-template.js`](../templates/app-template.js). Top to bottom: top bar (trip title) → hero (title, optional subtitle, stats) → map with phase filters → phase card grid.

| Element | Source / rule | Example |
|---------|---------------|---------|
| Top bar brand | `tripTitle` | Spring Break 2026 |
| Hero title | `tripTitle` | Spring Break 2026 |
| Hero subtitle | `tripSubtitle` (optional) | Dad and Mae — omitted when blank |
| Hero stats | Route totals + phases | `837 km mapped` · `13h 24m baseline` · `4 phases · Mar 10` |
| Map | Route legs + GPS | Full-width; legend + All/Phase filter buttons |
| Phase header (left) | Phase type + dates / location | `Phase 1: Depart Mar 10` · `Phase 2: Paris, FR` |
| Phase header (right) | Sunrise/sunset for phase dates | `🌅 6:52 AM · 🌇 8:14 PM` |
| Phase card body | Generated narrative | One paragraph per phase |

**Headline rules:** transit → Depart/Return + dates; stay phases → primary location only (no lodging in headline, no “Transit”/“Residency” labels). **Layout:** map first, phase cards in a grid below; click card or map filter to focus a phase.

---

## Quick checklist for customers

1. Fill **trip title** (and optional **subtitle** for a banner line under the title)  
2. Fill **start/end** places and dates  
3. Add **trip phases** — at least departure (and return for round trips); use **Suggest starter phases**  
4. Add one **route table row per movement**, pick a **phase** on each row, including flights and trains even if you have no photos there  
5. Optionally list **basecamps** (where you slept)  
6. Use **, then** for comma-style station chains; **→ to** for everything else  
7. Set **mode** on every non-driving leg  
8. Upload geotagged photo zip  

---

## Related docs

- [Customer upload guide](../docs/CUSTOMER_UPLOAD_GUIDE.md) — full form reference  
- [CUSTOMER_WORKFLOW.md](../docs/CUSTOMER_WORKFLOW.md) — end-to-end journey  
- [INTAKE_ARCHITECTURE.md](../data_pipeline/INTAKE_ARCHITECTURE.md) — pipeline input ranking  
- [Spring Break E2E fixture](../test/lib/spring-break-fixture.mjs) — reference payload for tests
