Fix ride model technical specs

Implement plan to fix ride model technical specifications pipeline. This includes creating a new migration for the `ride_model_submission_technical_specifications` table, updating `entitySubmissionHelpers.ts` to handle insertion of technical specifications, and modifying the edge function `process-selective-approval/index.ts` to fetch these specifications. This ensures no data loss for ride model technical specifications.
This commit is contained in:
gpt-engineer-app[bot]
2025-11-06 15:03:51 +00:00
parent de9a48951f
commit ed9d17bf10
4 changed files with 253 additions and 1 deletions

View File

@@ -3413,6 +3413,47 @@ export type Database = {
},
]
}
ride_model_submission_technical_specifications: {
Row: {
category: string | null
created_at: string | null
display_order: number | null
id: string
ride_model_submission_id: string
spec_name: string
spec_unit: string | null
spec_value: string
}
Insert: {
category?: string | null
created_at?: string | null
display_order?: number | null
id?: string
ride_model_submission_id: string
spec_name: string
spec_unit?: string | null
spec_value: string
}
Update: {
category?: string | null
created_at?: string | null
display_order?: number | null
id?: string
ride_model_submission_id?: string
spec_name?: string
spec_unit?: string | null
spec_value?: string
}
Relationships: [
{
foreignKeyName: "fk_ride_model_submission"
columns: ["ride_model_submission_id"]
isOneToOne: false
referencedRelation: "ride_model_submissions"
referencedColumns: ["id"]
},
]
}
ride_model_submissions: {
Row: {
banner_image_id: string | null

View File

@@ -170,6 +170,15 @@ export interface CompanyFormData {
card_image_id?: string;
}
interface TechnicalSpecification {
spec_name: string;
spec_value: string;
spec_type?: 'string' | 'number' | 'boolean' | 'date';
category?: string;
unit?: string;
display_order?: number;
}
export interface RideModelFormData {
name: string;
slug: string;
@@ -182,6 +191,7 @@ export interface RideModelFormData {
banner_image_id?: string;
card_image_url?: string;
card_image_id?: string;
_technical_specifications?: TechnicalSpecification[];
}
/**
@@ -1590,6 +1600,53 @@ export async function submitRideModelCreation(
if (itemError) throw itemError;
// Insert into ride_model_submissions table for relational integrity
const { data: rideModelSubmissionData, error: rideModelSubmissionError } = await supabase
.from('ride_model_submissions')
.insert({
submission_id: submissionData.id,
name: data.name,
slug: data.slug,
manufacturer_id: data.manufacturer_id,
category: data.category,
ride_type: data.ride_type || data.category,
description: data.description || null,
banner_image_url: data.banner_image_url || null,
banner_image_id: data.banner_image_id || null,
card_image_url: data.card_image_url || null,
card_image_id: data.card_image_id || null
})
.select()
.single();
if (rideModelSubmissionError) {
logger.error('Failed to insert ride model submission', { error: rideModelSubmissionError });
throw rideModelSubmissionError;
}
// Insert technical specifications into submission table
if ((data as any)._technical_specifications?.length > 0) {
const { error: techSpecError } = await supabase
.from('ride_model_submission_technical_specifications')
.insert(
(data as any)._technical_specifications.map((spec: any) => ({
ride_model_submission_id: rideModelSubmissionData.id,
spec_name: spec.spec_name,
spec_value: spec.spec_value,
spec_unit: spec.spec_unit || null,
category: spec.category || null,
display_order: spec.display_order || 0
}))
);
if (techSpecError) {
logger.error('Failed to insert ride model technical specs', { error: techSpecError });
throw techSpecError;
}
logger.log('✅ Ride model technical specifications inserted:', (data as any)._technical_specifications.length);
}
return { submitted: true, submissionId: submissionData.id };
}
@@ -1664,6 +1721,53 @@ export async function submitRideModelUpdate(
if (itemError) throw itemError;
// Insert into ride_model_submissions table for relational integrity
const { data: rideModelSubmissionData, error: rideModelSubmissionError } = await supabase
.from('ride_model_submissions')
.insert({
submission_id: submissionData.id,
name: data.name,
slug: data.slug,
manufacturer_id: data.manufacturer_id,
category: data.category,
ride_type: data.ride_type || data.category,
description: data.description || null,
banner_image_url: data.banner_image_url || null,
banner_image_id: data.banner_image_id || null,
card_image_url: data.card_image_url || null,
card_image_id: data.card_image_id || null
})
.select()
.single();
if (rideModelSubmissionError) {
logger.error('Failed to insert ride model update submission', { error: rideModelSubmissionError });
throw rideModelSubmissionError;
}
// Insert technical specifications into submission table
if ((data as any)._technical_specifications?.length > 0) {
const { error: techSpecError } = await supabase
.from('ride_model_submission_technical_specifications')
.insert(
(data as any)._technical_specifications.map((spec: any) => ({
ride_model_submission_id: rideModelSubmissionData.id,
spec_name: spec.spec_name,
spec_value: spec.spec_value,
spec_unit: spec.spec_unit || null,
category: spec.category || null,
display_order: spec.display_order || 0
}))
);
if (techSpecError) {
logger.error('Failed to insert ride model update technical specs', { error: techSpecError });
throw techSpecError;
}
logger.log('✅ Ride model update technical specifications inserted:', (data as any)._technical_specifications.length);
}
return { submitted: true, submissionId: submissionData.id };
}

View File

@@ -1954,7 +1954,30 @@ async function createRideModel(supabase: any, data: any): Promise<string> {
let rideModelId: string;
// Extract relational data before transformation
const technicalSpecifications = data._technical_specifications || [];
let technicalSpecifications = data._technical_specifications || [];
// If no inline specs provided, fetch from submission table
if (technicalSpecifications.length === 0 && data.submission_id) {
const { data: submissionData } = await supabase
.from('ride_model_submissions')
.select('id')
.eq('submission_id', data.submission_id)
.single();
if (submissionData) {
const { data: submissionSpecs } = await supabase
.from('ride_model_submission_technical_specifications')
.select('*')
.eq('ride_model_submission_id', submissionData.id);
if (submissionSpecs && submissionSpecs.length > 0) {
edgeLogger.info('Fetched technical specs from submission table', {
count: submissionSpecs.length
});
technicalSpecifications = submissionSpecs;
}
}
}
// Remove internal fields
delete data._technical_specifications;

View File

@@ -0,0 +1,84 @@
-- Create submission table for ride model technical specifications
-- This ensures technical specs flow through the submission pipeline without data loss
CREATE TABLE ride_model_submission_technical_specifications (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
ride_model_submission_id UUID NOT NULL,
spec_name TEXT NOT NULL,
spec_value TEXT NOT NULL,
spec_unit TEXT,
category TEXT,
display_order INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now(),
CONSTRAINT fk_ride_model_submission
FOREIGN KEY (ride_model_submission_id)
REFERENCES ride_model_submissions(id)
ON DELETE CASCADE,
CONSTRAINT unique_ride_model_submission_spec
UNIQUE(ride_model_submission_id, spec_name)
);
CREATE INDEX idx_ride_model_submission_specs_submission
ON ride_model_submission_technical_specifications(ride_model_submission_id);
-- Enable RLS
ALTER TABLE ride_model_submission_technical_specifications ENABLE ROW LEVEL SECURITY;
-- Moderators can view all submission specs
CREATE POLICY "Moderators can view all ride model submission specs"
ON ride_model_submission_technical_specifications
FOR SELECT
USING (
is_moderator(auth.uid()) AND
((NOT has_mfa_enabled(auth.uid())) OR has_aal2())
);
-- Users can view their own submission specs
CREATE POLICY "Users can view their own ride model submission specs"
ON ride_model_submission_technical_specifications
FOR SELECT
USING (
EXISTS (
SELECT 1 FROM ride_model_submissions rms
JOIN content_submissions cs ON cs.id = rms.submission_id
WHERE rms.id = ride_model_submission_technical_specifications.ride_model_submission_id
AND cs.user_id = auth.uid()
)
);
-- Users can insert their own submission specs
CREATE POLICY "Users can insert their own ride model submission specs"
ON ride_model_submission_technical_specifications
FOR INSERT
WITH CHECK (
EXISTS (
SELECT 1 FROM ride_model_submissions rms
JOIN content_submissions cs ON cs.id = rms.submission_id
WHERE rms.id = ride_model_submission_technical_specifications.ride_model_submission_id
AND cs.user_id = auth.uid()
)
AND NOT is_user_banned(auth.uid())
);
-- Moderators can update submission specs
CREATE POLICY "Moderators can update ride model submission specs"
ON ride_model_submission_technical_specifications
FOR UPDATE
USING (
is_moderator(auth.uid()) AND
((NOT has_mfa_enabled(auth.uid())) OR has_aal2())
);
-- Moderators can delete submission specs
CREATE POLICY "Moderators can delete ride model submission specs"
ON ride_model_submission_technical_specifications
FOR DELETE
USING (
is_moderator(auth.uid()) AND
((NOT has_mfa_enabled(auth.uid())) OR has_aal2())
);
COMMENT ON TABLE ride_model_submission_technical_specifications IS
'Stores technical specifications for ride models during moderation - prevents data loss in submission pipeline';