# Proposal: redesign-events-admin-grid

## Why

The Events screen renders one full-width card per event, and each card carries its whole
editing surface inline: a three-tab strip, a live settings form, a nested guest list and the
card-design panel. With the three seed events it already runs past two screens; a customer
with five or six events cannot see their wedding at a glance, and comparing two dates means
scrolling between two forms that look identical.

The screen also puts every editable field on permanent display. Nothing on it is a summary —
`starts_at`, venue and capacity are always live inputs, so the page reads as four stacked
forms rather than as a list of events. That is the wrong default for a screen whose most
common use is *looking*, not editing.

## What Changes

**The list becomes a grid of read-only tiles**

- Events render in a responsive grid — three per row on desktop, degrading to two and one on
  narrower viewports — instead of a single full-width column.
- Each tile is a **summary, not a form**: name, ceremony type, date and time in `Asia/Dhaka`,
  venue, capacity (or "No limit"), published state, whether a card design is published, and
  the public `/e/{slug}` address. Every field the current settings tab exposes is represented.
- **BREAKING (admin UI only):** the per-event tab strip is removed. Settings and Card design
  move into the editor modal; the inline Guests tab is replaced by navigation (below).

**An Event Editor modal**

- Each tile carries an **Edit** button opening an Event Editor modal for that event, holding
  the **Settings** section and the **Card design** section together.
- The modal has a **Save** button, a **Close** button and the **✕** close control.
- **Save covers the settings form only** — date, venue, capacity, published. Card design
  actions (upload, publish, delete) act immediately when pressed, as they do today: an upload
  is a file transfer and a publish is a state transition, and neither can be meaningfully
  staged behind a form Save.
- Closing with unsaved settings edits asks for confirmation rather than discarding silently.
- The date-change consequences the current form already surfaces — reminder rescheduling, the
  "announce to accepted guests" offer, and the warning that a published card still prints the
  old date and venue — move into the modal unchanged.
- **Delete this event** moves into the modal, keeping its existing impact-stating confirmation.
  It is deliberately not on the tile, where it would sit one mis-tap from a grid of similar
  buttons.

**Guests become a navigation, not an inline panel**

- Both the tile and the modal carry a **Guests** button. It navigates to
  `/admin/guests?event_id=<id>` — the existing Guests screen, with that event pre-selected.
- **BREAKING (admin UI only):** the inline guest list inside the event card is removed. It was
  a capped, unfiltered, unpaginated 200-row read of a list that the Guests screen already shows
  properly. Navigating there instead gives search, status/side/tag filters, pagination, the
  guest drawer, CSV export and QR generation for free.
- The event-scoped **Add guest** affordance survives the move: the Guests screen already
  enables Add guest only once an event is chosen, so arriving with one pre-selected lands on a
  working add button.
- This requires the Guests screen's filters to be readable from the URL, which they are not
  today — they are local component state, so no link can address a filtered view.

**Per-event invitation messages**

- The Create event and Event editor modals each gain a **Messages** section at the bottom with
  **two boxes — single and family, English** — prefilled with the wording a guest would
  otherwise see, editable in place.
- These **override** the wedding-wide panel for that event. Resolution becomes
  **event → wedding → built-in default**, so an event only departs from the couple's house
  wording when someone types something different into it.
- Prefilled boxes and inheritance are reconciled by storing on difference: a box left at the
  value it inherited stores nothing, so the event keeps following the wedding panel. Editing
  it stores an override on that event alone.
- **Bangla stays wedding-wide.** Only English is editable per event (owner decision,
  15 Aug 2026). Bangla greetings resolve wedding → built-in exactly as they do now, and the
  storage is keyed by locale so adding Bangla later is not a migration.

**Unchanged**

- The wedding-wide Invitation messages panel keeps its place below the grid, and keeps all
  four of its fields. It is now the default that events inherit rather than the only setting.

## Capabilities

### New Capabilities

None. Both capabilities below already exist as spec directories in `add-rsvp-v1` and
`add-event-invitation-card`.

### Modified Capabilities

- `event-management`: how an admin surveys and edits events. Adds requirements for the
  event grid as a read-only summary, the Event Editor modal and its save/close semantics,
  navigation from an event to its guest list, and per-event invitation messages. Supersedes
  the inline per-event tab strip and the inline guest list introduced by
  `add-event-invitation-card`.
- `guest-management`: the Guests screen must accept an event pre-selection from the URL and
  keep its filter state addressable, so an event can link to its own guest list.
- `invitation-presentation`: the greeting a guest reads now resolves through the event before
  the wedding, so an event can carry its own wording.

## Impact

**Code**

- `web/components/admin/EventsView.tsx` — rewritten: grid container plus a read-only tile
  component; the current `EventCard` settings form moves into the modal, and `EventGuests` is
  deleted.
- New `web/components/admin/EventEditorModal.tsx` — settings form, card design section, footer
  with Guests / Delete / Close / Save.
- `web/components/admin/Modal.tsx` — needs a footer slot and a wider size than the existing
  `wide` (`max-w-3xl`) to hold the card design panel comfortably.
- `web/components/admin/GuestsView.tsx` — filter state seeded from and synced to the URL.
- `web/app/admin/(protected)/guests/page.tsx` — reads `searchParams` and passes the initial
  event through.
- `web/components/admin/CardDesignPanel.tsx` — rendered inside a modal rather than a tab; may
  need width/scroll adjustment only.

**API and database** — in scope only because of the per-event messages. The grid, tile, modal
and guest navigation need none of it.

- Migration adding `invitation_messages` JSONB to `event`, mirroring the column already on
  `wedding` (`NOT NULL DEFAULT '{}'`), so existing rows inherit and nothing backfills.
- `api/app/routers/admin_events.py` — `invitation_messages` on `AdminEventRead`, `EventCreate`
  and `EventUpdate`, validated through the existing `greeting.validate_messages`.
- `api/app/services/greeting.py` — `resolve` takes layers in priority order rather than one map.
- `api/app/routers/invitations.py` — `_greeting` resolves through the event before the wedding.
- `web/lib/api/schema.d.ts` — regenerated via `make client`; the CI freshness check now has
  something to react to, so the regenerated file must be committed.

**Not affected**

- Authorization — the tile and modal keep hiding controls behind `edit_content` exactly as
  today, and FastAPI keeps deciding on its own authority. Writing messages is `EDIT_CONTENT`,
  the same permission that already guards event create and update.

**Risks**

- The card-design badge on each tile needs per-event design state, which today costs one
  `GET /admin/events/{id}/card-designs` per event. At a realistic 3–8 events this is
  acceptable; it is called out in design rather than solved with a new endpoint.
- No automated test covers this screen. `web/` has no test runner, and `make walkthrough`
  exercises the API in-process rather than rendering pages, so it will pass regardless.
  Verification is a written manual pass.

**Non-goals**

- No guest counts or RSVP totals on the tiles. Those need an API change and belong with the
  dashboard, not with this layout work.
- No per-event **Bangla** messages. The column is keyed by locale so they can be added without
  a migration, but no UI exposes them and Bangla keeps resolving wedding → built-in.
- No redesign of the wedding-wide messages panel. It keeps its four fields and its place.
