# Journey editor tracking + version history

## Goal

For the flow/blast editor (`flow.jsx`):

1. Record **who last edited** each journey on `custom_journeys` ("last saved by" = same field).
2. Keep a **version history** of each journey's flowchart in a new table, snapshotting on every save (keep all).
3. Show the **last editor** in the unified flow/blast listing.

Store-only: no restore UI/endpoints yet.

## Context

- Editor renders at `admin/{channel}/flowchart/{id}` behind `auth` + `2fa` (session guard `web`).
- Saves POST to unauthenticated `/api/v1/{create-journey,create-blast,silent-save-journey}`. The `api` guard is Passport (token) and the editor sends **no token** — so `auth:api` would 401. The editor holds a session cookie, so authenticate via the **session (`web`) guard**.
- Controller: `app/Http/Controllers/Journey/JourneyController.php` — save methods `createJourney`, `createBlast`, `silentSaveJourney`. Each writes `payload` (raw flowchart) + `save_payload` (full request incl. API tokens).
- Listing: `flowchart-list.jsx` (unified flow+blast, Type badge from `is_blast`) fed by `FlowchartController::list` (`flow/{id}/list`).

## Changes

### 1. Auth on save routes (session guard)
`routes/api.php`: move the three save routes into a group with `middleware => ['web', 'auth']` (URLs unchanged). `web` provides session + CSRF; `auth` uses default `web` guard so `auth()->id()` = logged-in admin.

`flow.jsx`: add `headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content }` to the three `axios.post` save calls (create-journey, create-blast, silent-save). Meta tag already exists in `layouts/admin.blade.php`.

### 2. `last_edited_by` on `custom_journeys`
Migration: add nullable `last_edited_by` unsignedBigInteger, FK → `users` `nullOnDelete`.
- `CustomJourney`: add `last_edited_by` to `$fillable`; add relation `lastEditor()` → `belongsTo(User::class, 'last_edited_by')`.
- In `createJourney` / `createBlast` / `silentSaveJourney`: set `$journey->last_edited_by = auth()->id()` before save.

### 3. `custom_journey_versions` table
Migration creates:

| column | type | note |
|---|---|---|
| id | id | |
| custom_journey_id | FK → custom_journeys, indexed, cascade on delete | |
| payload | longText | snapshot of the raw flowchart (`payload`) |
| edited_by | nullable FK → users, nullOnDelete | who saved this version |
| created_at | timestamp nullable | immutable; no `updated_at` |

Model `CustomJourneyVersion` (`$fillable`, no timestamps update — set `created_at` explicitly or use `$timestamps=false` with manual `created_at`).

On each save method, after the journey is saved, insert one row `{custom_journey_id, payload: <new payload json>, edited_by: auth id, created_at: now}`. Full chronological log; "previous version" = row before latest.

Not snapshotting `save_payload` (carries API tokens, bloats every row). `payload` is enough to restore flow content later.

### 4. Listing shows editor
- `FlowchartController::list`: `->with('lastEditor:id,name')` on the paginated query (serializes as `last_editor`).
- `flowchart-list.jsx`: add "Last edited by" column rendering `flowchart.last_editor?.name` with "—" fallback.

## Out of scope / deferred (`ponytail:`)
- Restore UI / list-versions endpoint — add when restore is built.
- Snapshotting `save_payload` — add if full-request restore needed.
- Version retention/pruning — keep all for now.
- `JourneyController::index` eager-load — only `FlowchartController::list` feeds the UI.

## Notes
- `saveOtherJourney` / `savePrimaryJourney` re-save *other* journeys within one request; those also get `last_edited_by` = current user + a version row. Accepted under "snapshot every save, keep all".
- Old rows / user-2 fallback: `last_edited_by` null → listing shows "—".
