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,5 +1,6 @@
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import type { Catalog, CableType, DeviceCategoryDef, DeviceTemplate, Port, PortType } from '../domain/types'
|
||||
import { cableTypeToRow, deviceTemplateToRow, portTypeToRow } from './catalogRowMapping'
|
||||
import type { CatalogRepository } from './CatalogRepository'
|
||||
import { supabase } from './supabaseClient'
|
||||
|
||||
@@ -156,16 +157,9 @@ export class SupabaseCatalogRepository implements CatalogRepository {
|
||||
async addPortType(portType: Omit<PortType, 'id' | 'custom'>): Promise<PortType> {
|
||||
const ownerId = await this.currentUserId()
|
||||
const id = uuid()
|
||||
const { error } = await supabase.from('port_types').insert({
|
||||
id,
|
||||
name: portType.name,
|
||||
category: portType.category,
|
||||
family: portType.family,
|
||||
compatible_family_ids: portType.compatibleFamilyIds ?? [],
|
||||
max_connections: portType.maxConnections ?? null,
|
||||
is_public: false,
|
||||
owner_id: ownerId,
|
||||
})
|
||||
const { error } = await supabase
|
||||
.from('port_types')
|
||||
.insert({ id, ...portTypeToRow(portType), is_public: false, owner_id: ownerId })
|
||||
if (error) console.error('Failed to add port type to Supabase', error)
|
||||
return { ...portType, id, custom: true }
|
||||
}
|
||||
@@ -184,16 +178,9 @@ export class SupabaseCatalogRepository implements CatalogRepository {
|
||||
async addCableType(cableType: Omit<CableType, 'id' | 'custom'>): Promise<CableType> {
|
||||
const ownerId = await this.currentUserId()
|
||||
const id = uuid()
|
||||
const { error } = await supabase.from('cable_types').insert({
|
||||
id,
|
||||
name: cableType.name,
|
||||
family: cableType.family,
|
||||
family2: cableType.family2 ?? null,
|
||||
unit: cableType.unit,
|
||||
cost_per_unit: cableType.costPerUnit ?? null,
|
||||
is_public: false,
|
||||
owner_id: ownerId,
|
||||
})
|
||||
const { error } = await supabase
|
||||
.from('cable_types')
|
||||
.insert({ id, ...cableTypeToRow(cableType), is_public: false, owner_id: ownerId })
|
||||
if (error) console.error('Failed to add cable type to Supabase', error)
|
||||
return { ...cableType, id, custom: true }
|
||||
}
|
||||
@@ -212,16 +199,8 @@ export class SupabaseCatalogRepository implements CatalogRepository {
|
||||
async addDeviceTemplate(template: Omit<DeviceTemplate, 'id' | 'custom'>): Promise<DeviceTemplate> {
|
||||
const ownerId = await this.currentUserId()
|
||||
const id = uuid()
|
||||
const { error } = await supabase.from('device_templates').insert({
|
||||
id,
|
||||
name: template.name,
|
||||
category_id: template.category,
|
||||
manufacturer: template.manufacturer ?? null,
|
||||
model: template.model ?? null,
|
||||
cost: template.cost ?? null,
|
||||
is_public: false,
|
||||
owner_id: ownerId,
|
||||
})
|
||||
const { ports: _ports, ...templateRow } = deviceTemplateToRow(template)
|
||||
const { error } = await supabase.from('device_templates').insert({ id, ...templateRow, is_public: false, owner_id: ownerId })
|
||||
if (error) {
|
||||
console.error('Failed to add device template to Supabase', error)
|
||||
return { ...template, id, custom: true }
|
||||
|
||||
Reference in New Issue
Block a user