Add the user-submission flow for the catalog
Lets users submit a private catalog entry (port type, cable type, device template) for promotion to the public catalog, or suggest an edit to an existing public entry — both go into the catalog_submissions review queue per organized-ideas.md §3. No Admin review UI yet (next sub-phase); this covers the submitter's side only. - data/SubmissionRepository + SupabaseSubmissionRepository: submit, resubmit, withdraw, list-mine, backed by the existing catalog_submissions RLS policies (no schema changes needed). - data/catalogRowMapping.ts: shared domain<->row mappers, in both directions, so a submission's proposed_data is always shaped like the underlying table row (what an eventual admin-approval would write directly) and can be turned back into form-editable fields for revision. - state/submissionStore.ts: mySubmissions + submit/resubmit/withdraw, plus syncProposedData — called from catalogStore's updateCustom* actions so a submission about your own still-private entry never goes stale relative to it (edits from the library and from My Submissions are the same action and always agree). - UI: "Submit"/"Suggest edit" wired into PortTypeManager, CableTypeManager, DevicePalette/DeviceTemplateEditor; new MySubmissionsModal (opened from TopBar, with a pending-count badge) shows status, rejection reasons, and lets you edit/resubmit or withdraw. - Deliberately deferred: device_category submissions (no listing UI to hang a button on yet) and the normalized manufacturer catalog. Verified: tsc -b and oxlint clean; supabase db reset + 23/23 pgTAP RLS tests still pass (no schema changes this round); manually tested submit, suggest-edit, edit-from-either-side sync, reject/resubmit, and withdraw.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { create } from 'zustand'
|
||||
import type { CableType, Catalog, DeviceTemplate, PortType } from '../domain/types'
|
||||
import type { CatalogRepository } from '../data/CatalogRepository'
|
||||
import { cableTypeToRow, deviceTemplateToRow, portTypeToRow } from '../data/catalogRowMapping'
|
||||
import { SupabaseCatalogRepository } from '../data/SupabaseCatalogRepository'
|
||||
import { useSubmissionStore } from './submissionStore'
|
||||
|
||||
const repository: CatalogRepository = new SupabaseCatalogRepository()
|
||||
|
||||
@@ -80,6 +82,8 @@ export const useCatalogStore = create<CatalogStoreState>((set, get) => ({
|
||||
portTypes: state.catalog.portTypes.map((pt) => (pt.id === id ? { ...pt, ...patch } : pt)),
|
||||
},
|
||||
}))
|
||||
const updated = get().catalog.portTypes.find((pt) => pt.id === id)
|
||||
if (updated) useSubmissionStore.getState().syncProposedData('port_type', id, portTypeToRow(updated))
|
||||
},
|
||||
|
||||
addCustomCableType: async (cableType) => {
|
||||
@@ -95,6 +99,8 @@ export const useCatalogStore = create<CatalogStoreState>((set, get) => ({
|
||||
cableTypes: state.catalog.cableTypes.map((ct) => (ct.id === id ? { ...ct, ...patch } : ct)),
|
||||
},
|
||||
}))
|
||||
const updated = get().catalog.cableTypes.find((ct) => ct.id === id)
|
||||
if (updated) useSubmissionStore.getState().syncProposedData('cable_type', id, cableTypeToRow(updated))
|
||||
},
|
||||
|
||||
addCustomDeviceTemplate: async (template) => {
|
||||
@@ -110,6 +116,8 @@ export const useCatalogStore = create<CatalogStoreState>((set, get) => ({
|
||||
deviceTemplates: state.catalog.deviceTemplates.map((t) => (t.id === id ? { ...t, ...patch } : t)),
|
||||
},
|
||||
}))
|
||||
const updated = get().catalog.deviceTemplates.find((t) => t.id === id)
|
||||
if (updated) useSubmissionStore.getState().syncProposedData('device_template', id, deviceTemplateToRow(updated))
|
||||
},
|
||||
|
||||
removeCustomDeviceTemplate: async (id) => {
|
||||
|
||||
Reference in New Issue
Block a user