Control Panel Specifications
Update
Field editability matrix per status. Strict re-review rule (any edit on published → needs-updates). PDF replacement flow + version history.
The Edit screen shares the same field set as Create. The only structural difference is a permission-aware editability matrix (some fields lock once the file leaves draft) plus a strict re-review rule (any edit on a published file flips it back to needs-updates). The Form preview at the bottom shows exactly how the Edit screen renders.
Edit-only sub-resources
Edit adds two tabs that don't exist on Create — both query existing records, neither creates new ones.
Leads tab (read-only sub-resource)
- Purpose — Lists every DownloadLead row attached to this file. Lets the Admin verify the lead funnel is working + spot-check consent capture.
- DB type — Read of `DownloadLead WHERE FounderFileId = @fileId ORDER BY CreatedAt DESC`. Returns columns: createdAt, leadEmail, leadRole, leadCountry, langDelivered, consentMarketing, consentPartners, deliveredAt.
- Validation — QC tests: submit a real consent download from the public side → row appears within 5 seconds; verify `langDelivered` matches the locale the user was on.
History tab (audit log)
- Purpose — Timeline of every status transition + every editable-field change on this file. Used for incident review and to prove editorial workflow compliance.
- DB type — Read of `FounderFileAuditLog WHERE FounderFileId = @fileId ORDER BY OccurredAt DESC`. Each row: occurredAt, actorId (→AdminUser join), eventType (status-transition / field-change), fromValue, toValue, reason.
- Validation — QC tests: change the file title and save → a row appears with eventType=field-change, fromValue/toValue populated; reject the file → row appears with eventType=status-transition + the reviewer's mandatory comment in `reason`.
Field editability matrix
Editable in ANY status
- Title (per language) · Subtitle (per language)
- FullDescription (per language)
- LearningPoints (per language, 3–5)
- Topics, ReadingTimeMinutes
- Sponsor link (subject to contract validity check)
- Editorial flags (IsFeatured, IsGated, IsDownloadable)
Locked rules
- Category, CoverImageUrl, PDF replacement → editable only in `draft` or `needs-updates`
- `Id` → permanently locked after INSERT (database PK)
- `Slug` → permanently locked the moment Status leaves `draft`
- `FileNumber` → permanently locked after INSERT (sequence)
- `PublishedAt` → never editable (auto-stamped, immutable)
PDF replacement flow
- Purpose — Replace the published PDF without losing version history. Previous versions stay in storage so a Lead delivered yesterday can still reach the exact bytes they were promised.
- DB type — `FounderFileLang.PdfUrl` is UPDATEd to point to `founder-files/{slug}/{lang}/v{N+1}.pdf`. `FounderFileLang.FileSizeBytes` and `.PageCount` are recomputed by the upload service and UPDATEd. No new row inserted.
- Validation — Last 5 versions retained per language. QC tests: replace 6 times → confirm only versions v2–v6 exist in storage (v1 purged); a Lead delivered while v3 was current must still resolve when looking up the audit row (PdfVersionDelivered=3 → readable from Azure even though current pointer is v6).
Side effects on save
- `FounderFile.LastUpdatedAt` UPDATEd to GETUTCDATE().
- `FounderFile.LastUpdatedBy` UPDATEd from the authenticated Admin session.
- Row inserted into `FounderFileAuditLog` for every changed field (one row per field diff).
- If `SponsorId` changed: previous sponsor stops receiving leads instantly; event `founder-file.sponsor-changed` emitted; pro-rata billing adjustment queued.
- If was `published` and any editable field changed: `Status` UPDATEd to `needs-updates`; event `founder-file.content-updated` emitted; Editorial Lead receives email.
Form preview — Edit screen
Edit reuses the Create form 1:1 (Metadata · EN · AR · FR · Sponsor) and adds two tabs at the right: Leads and History. Locked fields render as `disabled` inputs with a lock icon. The example below is a `published` file in the Metadata tab — notice Slug + Category + Cover are locked, while editable fields remain interactive.
