# Contact Edit Approval System — Design

## Goal

Allow registered users to suggest corrections to any contact's core fields. Edits enter an admin approval queue with field-by-field diff view. Users receive email notification when their edit is approved or rejected.

## Data Model

New `contact_edits` table:

| Field | Type | Purpose |
|-------|------|---------|
| id | binary_id | PK |
| contact_id | references contacts | Target contact |
| user_id | references users | Submitter |
| proposed_changes | :map (JSONB) | Only changed fields, e.g. `%{"grid1" => "EM13kk"}` |
| status | Ecto.Enum (:pending, :approved, :rejected) | Review state |
| admin_note | :string | Optional admin explanation |
| reviewed_by_id | references users | Admin who acted |
| reviewed_at | :utc_datetime | When reviewed |
| timestamps | | inserted_at = submission time |

Schema: `ContactEdit` in `lib/microwaveprop/radio/contact_edit.ex`.

## Editable Fields

station1, station2, grid1, grid2, band, mode, qso_timestamp.

Not editable: pos1/pos2, distance_km, enrichment statuses (computed fields).

## User Flow

- Contact detail page (`/contacts/:id`): logged-in users see "Suggest Edit" button.
- Clicking opens inline form pre-filled with current values.
- On submit: backend diffs against current contact, stores only changed fields.
- Validation: same rules as submission_changeset (callsign format, grid format, valid band/mode).
- Reject if nothing changed.
- Flash confirmation on success. Badge shows "pending edit" if user has one.

## Admin Flow

- `/admin/contact-edits`: pending edits, newest first.
- Columns: contact link, submitter callsign, submitted at, fields changed.
- Review view: field-by-field diff table (current vs proposed), only changed fields.
- Optional note textarea, Approve/Reject buttons.
- Approve: update contact fields, re-enqueue enrichment if grid1/grid2/band changed.
- Reject: no contact changes.
- Nav badge shows pending count.

## Multiple Edits

Independent queue — multiple edits per contact allowed. Admin reviews each separately.

## Email Notifications

Plain text via Swoosh (existing mailer). Sent on approve or reject. Includes:
- Contact identifier (station1 <-> station2, band, date)
- What was proposed
- Decision (approved/rejected)
- Admin note if provided

## Re-enrichment

On approve, if any of grid1/grid2/band changed: recalculate pos1/pos2 from grids, clear distance_km (will be recomputed), re-enqueue weather/HRRR/terrain/IEMRE jobs.
