Files
av-planner/src/data/SubmissionRepository.ts
T
aarbitandClaude Sonnet 5 29c3ed46a3 Add manufacturer catalog, category/manufacturer library management, and built-in cable flag
Normalizes manufacturer as a shared catalog entity (like device categories)
instead of free text on each device, giving the admin duplicate-detection
nudge a reliable signal. Adds full CRUD (including Admin direct-publish,
bypassing the submission queue) for categories, manufacturers, port types,
and cable types, plus a Categories & Manufacturers library modal and a
browse-by-manufacturer/search view in the device palette. Adds
Port.builtInCable so a captive/permanently-attached cable (a keyboard's USB
lead, a budget AVR's power cord) can be flagged and excluded from the BOM.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DUU6CnxECCDeqDNYJgr5x
2026-09-28 10:04:05 -05:00

58 lines
3.1 KiB
TypeScript

/** Which catalog table a submission is about. Mirrors `catalog_submissions.entity_type`'s
* check constraint. */
export type CatalogEntityType = 'device_template' | 'port_type' | 'cable_type' | 'device_category' | 'manufacturer'
/**
* A user's request to change the public catalog — either promoting one of
* their own private entries to public, or proposing an edit to an existing
* public entry. Regular users can never write a public catalog row directly
* (RLS only allows Admins to), so this is the only path either kind of
* change can take; an Admin review queue (not yet built — organized-ideas.md
* §9's next sub-phase) is what actually applies an approved one.
*/
export interface CatalogSubmission {
id: string
entityType: CatalogEntityType
/** The row this submission is about — always an existing row in this
* app's flow: either your own private entry (being promoted, possibly
* with edits) or a public entry (you're suggesting an edit to). */
entityId: string
/** Proposed field values, shaped like the underlying table's row (see
* data/catalogRowMapping.ts) — so applying an approval is just writing
* this object onto the row at `entityId`, not a separate translation
* step future Admin-approval logic would otherwise have to redo. */
proposedData: Record<string, unknown>
submitterId: string
status: 'pending' | 'approved' | 'rejected'
reviewerId?: string
/** Set by an Admin on rejection — organized-ideas.md §3's "notified either
* way, with a reason attached on rejection rather than a silent disappearance." */
reviewReason?: string
createdAt: string
updatedAt: string
}
/** Storage abstraction for the catalog submission/review queue — mirrors
* DiagramRepository/CatalogRepository's role for this concern. Only covers
* the submitter's-eye view (create, revise, withdraw your own) for now; an
* Admin's view (list all pending, approve/reject) is the next sub-phase. */
export interface SubmissionRepository {
/** Every submission the current user has made, most recent first. */
listMine(): Promise<CatalogSubmission[]>
/** Submits `entityId` (a row you own privately, or a public row you're
* proposing an edit to) for Admin review. */
submit(entityType: CatalogEntityType, entityId: string, proposedData: Record<string, unknown>): Promise<CatalogSubmission>
/** Revises a still-pending or previously-rejected submission of your own
* and puts it (back) into the pending queue. */
resubmit(id: string, proposedData: Record<string, unknown>): Promise<void>
/** Updates only `proposed_data`, leaving status/reviewer/reason untouched.
* Used to keep a submission's snapshot in sync with the live private
* entry it's about (see catalogStore) — a plain edit to your library
* entry shouldn't silently pull a rejected submission back into the
* pending queue, so status changes stay a separate, explicit action
* (`resubmit`), while the data itself is kept fresh automatically. */
updateProposedData(id: string, proposedData: Record<string, unknown>): Promise<void>
/** Withdraws your own still-pending submission. */
withdraw(id: string): Promise<void>
}