Per organized-ideas.md §8: the storage layer (DiagramRepository etc.) was
already renamed in an earlier phase; this finishes it everywhere else.
- domain/types.ts: Project -> Diagram. domain/project.ts -> domain/diagram.ts
(createEmptyProject -> createEmptyDiagram, default name "Untitled Diagram").
- domain/compatibility.ts, domain/bom.ts: Project param/type -> Diagram.
- data/exportImport.ts: ProjectImportError -> DiagramImportError,
projectToJson/downloadProjectFile/readProjectFile/normalizeProject -> their
Diagram equivalents.
- state/projectStore.ts -> state/diagramStore.ts: useProjectStore ->
useDiagramStore, the `project` field -> `diagram`, newProject/renameProject/
importProject/applyRestoredProject -> *Diagram, restoredProjectUpdatedAt ->
restoredDiagramUpdatedAt.
- Every component updated to match, compiler-guided (tsc -b enumerated each
remaining call site after the core rename, the same approach used for the
earlier catalog-parameter refactor).
- README updated for the terminology, and to match the repository class
names (which had already been renamed but the README hadn't caught up).
No backend/schema changes: the JSON shape stored in diagrams.data never
changed, only TypeScript-side identifiers, so existing diagrams are
unaffected. One SQL comment fixed for accuracy (no migration needed).
Verified: tsc -b and oxlint clean; grepped src/ for any remaining
Project/project reference (none) after the sweep.
Lets an Admin/Super-Admin review pending catalog submissions and approve
(in place, same id) or reject (with a required reason) them, per
organized-ideas.md §3/§9.
Backend (supabase/migrations/20260910010000_admin_review_queue.sql):
- Per-user pending-submission cap (10), enforced in catalog_submissions'
insert policy rather than trusted to the client.
- catalog_entity_usage_impact(entity_type, entity_id): a SECURITY DEFINER,
admin-gated aggregate function answering "how many diagrams reference
this, and a short sample" by scanning diagrams.data JSONB — never raw
diagram content, and available to regular Admins even though they don't
otherwise have diagram visibility (only Super Admins do, per §6).
- catalog_submission_submitters(ids[]): same admin-gated pattern, batched,
so the queue can show who submitted something without opening general
profile browsing to regular Admins.
- Follow-up migration: a rejected submission had no way out (the delete
policy only allowed withdrawing 'pending') — extended to allow 'rejected'
too, so a submitter can dismiss one they don't intend to revise.
- 12 new pgTAP tests (38/38 total) covering the cap, both privileged
functions (including the non-admin-gets-rejected case), and withdrawing
pending vs. rejected submissions.
Frontend:
- authStore: minimal role awareness, replacing TopBar's local username
fetch, used to gate the Review Queue UI.
- AdminSubmissionRepository/SupabaseAdminSubmissionRepository +
adminReviewStore: list all submissions, approve/reject, usage impact,
submitter usernames.
- AdminReviewModal: per-submission diff view (current vs. proposed, both
row-shaped via the existing catalog<->row mappers), a duplicate-detection
nudge (Levenshtein distance against existing public device names) for
new device submissions, and an inline impact-check for edits to
already-public entries before approving.
- TopBar: role-gated "Review Queue" button with a pending-count badge; "My
Submissions" gets an unseen-outcome badge (localStorage-tracked, like the
existing hidden-template preference) so a submitter notices a decision
without having to keep reopening the modal.
- Deliberately deferred: the site-wide announcement banner (its own
follow-up, per discussion) and the Admin/Super-Admin role-assignment UI
(§9's later phase — becoming an Admin locally still means setting
profiles.role via SQL/Studio).
Verified: tsc -b and oxlint clean; supabase db reset + 38/38 pgTAP tests
pass; confirmed the two new RPC functions are actually reachable through
PostgREST (not just raw SQL) via a live curl call; manually tested
submit -> review -> approve/reject -> (for rejected) dismiss end to end.