@@ -219,6 +259,9 @@ export function TestDataGenerator() {
• Created {results.summary.rides} ride submissions
• Created {results.summary.companies} company submissions
• Created {results.summary.rideModels} ride model submissions
+ {results.summary.photos > 0 && (
+ • Created {results.summary.photos} photo submissions ({results.summary.totalPhotoItems || 0} photos)
+ )}
Time taken: {results.time}s
diff --git a/src/lib/testDataGenerator.ts b/src/lib/testDataGenerator.ts
index 2c138f15..5bd91bde 100644
--- a/src/lib/testDataGenerator.ts
+++ b/src/lib/testDataGenerator.ts
@@ -3,10 +3,10 @@ import type { ParkSubmissionData, RideSubmissionData, CompanySubmissionData, Rid
// Preset configurations
export const PRESETS = {
- small: { parks: 5, rides: 10, companies: 3, rideModels: 2, photos: 0 },
- medium: { parks: 20, rides: 50, companies: 20, rideModels: 10, photos: 0 },
- large: { parks: 100, rides: 250, companies: 100, rideModels: 50, photos: 0 },
- stress: { parks: 400, rides: 1000, companies: 400, rideModels: 200, photos: 0 }
+ small: { parks: 5, rides: 10, companies: 3, rideModels: 2, photos: 5 },
+ medium: { parks: 20, rides: 50, companies: 20, rideModels: 10, photos: 25 },
+ large: { parks: 100, rides: 250, companies: 100, rideModels: 50, photos: 100 },
+ stress: { parks: 400, rides: 1000, companies: 400, rideModels: 200, photos: 500 }
} as const;
// Word lists for realistic names
diff --git a/supabase/functions/seed-test-data/index.ts b/supabase/functions/seed-test-data/index.ts
index 77db4572..9966e56b 100644
--- a/supabase/functions/seed-test-data/index.ts
+++ b/supabase/functions/seed-test-data/index.ts
@@ -8,11 +8,12 @@ const corsHeaders = {
interface SeedOptions {
preset: 'small' | 'medium' | 'large' | 'stress';
entityTypes: string[];
- includeDependencies: boolean;
- includeConflicts: boolean;
- includeVersionChains: boolean;
- includeEscalated: boolean;
- includeExpiredLocks: boolean;
+ fieldDensity?: 'mixed' | 'minimal' | 'standard' | 'maximum';
+ includeDependencies?: boolean;
+ includeConflicts?: boolean;
+ includeVersionChains?: boolean;
+ includeEscalated?: boolean;
+ includeExpiredLocks?: boolean;
}
interface SeedPlan {
@@ -20,33 +21,65 @@ interface SeedPlan {
rides: number;
companies: number;
rideModels: number;
+ photos: number;
}
const PRESETS: Record = {
- small: { parks: 5, rides: 10, companies: 3, rideModels: 2 },
- medium: { parks: 20, rides: 50, companies: 20, rideModels: 10 },
- large: { parks: 100, rides: 250, companies: 100, rideModels: 50 },
- stress: { parks: 400, rides: 1000, companies: 400, rideModels: 200 }
+ small: { parks: 5, rides: 10, companies: 3, rideModels: 2, photos: 5 },
+ medium: { parks: 20, rides: 50, companies: 20, rideModels: 10, photos: 25 },
+ large: { parks: 100, rides: 250, companies: 100, rideModels: 50, photos: 100 },
+ stress: { parks: 400, rides: 1000, companies: 400, rideModels: 200, photos: 500 }
};
+const CITIES = [
+ { city: 'Orlando', state: 'Florida', country: 'USA' },
+ { city: 'Anaheim', state: 'California', country: 'USA' },
+ { city: 'Paris', state: 'Île-de-France', country: 'France' },
+ { city: 'Tokyo', state: 'Tokyo', country: 'Japan' },
+ { city: 'Berlin', state: 'Berlin', country: 'Germany' }
+];
+
+// Helper functions
+function randomInt(min: number, max: number): number {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+}
+
+function randomItem(array: T[]): T {
+ return array[randomInt(0, array.length - 1)];
+}
+
+function randomDate(startYear: number, endYear: number): string {
+ const year = randomInt(startYear, endYear);
+ const month = randomInt(1, 12);
+ const day = randomInt(1, 28);
+ return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
+}
+
+function getPopulationLevel(fieldDensity: string, index: number): number {
+ if (fieldDensity === 'mixed') {
+ const rand = Math.random();
+ if (rand < 0.1) return 0; // 10% minimal
+ if (rand < 0.3) return 1; // 20% basic
+ if (rand < 0.7) return 2; // 40% standard
+ if (rand < 0.9) return 3; // 20% complete
+ return 4; // 10% maximum
+ }
+ if (fieldDensity === 'minimal') return 0;
+ if (fieldDensity === 'standard') return 2;
+ if (fieldDensity === 'maximum') return 4;
+ return 2;
+}
+
Deno.serve(async (req) => {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
- // SECURITY: Service Role Key Usage
- // ---------------------------------
- // This function uses the service role key to seed test data bypassing RLS.
- // This is required because:
- // 1. Test data generation needs to create entities in protected tables
- // 2. Moderator role is verified via is_moderator() RPC call before proceeding
- // Scope: Limited to moderators only, for test/development purposes
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const supabase = createClient(supabaseUrl, supabaseServiceKey);
- // Get auth header
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
return new Response(JSON.stringify({ error: 'No authorization header' }), {
@@ -55,7 +88,6 @@ Deno.serve(async (req) => {
});
}
- // Verify user is moderator
const token = authHeader.replace('Bearer ', '');
const { data: { user }, error: userError } = await supabase.auth.getUser(token);
@@ -67,25 +99,25 @@ Deno.serve(async (req) => {
}
const { data: isMod, error: modError } = await supabase.rpc('is_moderator', { _user_id: user.id });
- if (modError) {
- console.error('Failed to check moderator status:', modError);
- return new Response(JSON.stringify({ error: 'Failed to verify permissions' }), {
- status: 500,
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
- });
- }
-
- if (!isMod) {
+ if (modError || !isMod) {
return new Response(JSON.stringify({ error: 'Insufficient permissions. Moderator role required.' }), {
status: 403,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
- // Parse request
- const { preset = 'small', entityTypes = [], includeDependencies = true, includeConflicts = false, includeVersionChains = false, includeEscalated = false, includeExpiredLocks = false }: SeedOptions = await req.json();
+ const {
+ preset = 'small',
+ entityTypes = [],
+ fieldDensity = 'mixed',
+ includeDependencies = true,
+ includeConflicts = false,
+ includeVersionChains = false,
+ includeEscalated = false,
+ includeExpiredLocks = false
+ }: SeedOptions = await req.json();
+
const plan = PRESETS[preset];
-
if (!plan) {
return new Response(JSON.stringify({ error: 'Invalid preset' }), {
status: 400,
@@ -94,7 +126,7 @@ Deno.serve(async (req) => {
}
const startTime = Date.now();
- const summary = { parks: 0, rides: 0, companies: 0, rideModels: 0, conflicts: 0, versionChains: 0 };
+ const summary = { parks: 0, rides: 0, companies: 0, rideModels: 0, photos: 0, totalPhotoItems: 0, conflicts: 0, versionChains: 0 };
const createdParks: string[] = [];
const createdCompanies: Record = { manufacturer: [], operator: [], designer: [], property_owner: [] };
const createdParkSlugs: string[] = [];
@@ -102,11 +134,6 @@ Deno.serve(async (req) => {
// Helper to create submission
async function createSubmission(userId: string, type: string, itemData: any, options: { escalated?: boolean; expiredLock?: boolean } = {}) {
- // Ensure crypto.randomUUID is available
- if (typeof crypto === 'undefined' || typeof crypto.randomUUID !== 'function') {
- throw new Error('crypto.randomUUID is not available in this environment');
- }
-
const submissionId = crypto.randomUUID();
const itemId = crypto.randomUUID();
@@ -115,12 +142,12 @@ Deno.serve(async (req) => {
metadata: {
is_test_data: true,
generated_at: new Date().toISOString(),
- generator_version: '1.0.0',
- preset
+ generator_version: '2.0.0',
+ preset,
+ fieldDensity
}
};
- // Create content_submission
const submissionData: any = {
id: submissionId,
user_id: userId,
@@ -128,40 +155,32 @@ Deno.serve(async (req) => {
status: 'pending',
content: contentData,
submitted_at: new Date().toISOString(),
- priority: options.escalated ? 10 : Math.floor(Math.random() * 5) + 1
+ priority: options.escalated ? 10 : randomInt(1, 5)
};
if (options.escalated) {
submissionData.escalated = true;
- submissionData.escalation_reason = 'Test escalation';
+ submissionData.escalation_reason = 'Test escalation for priority testing';
}
if (options.expiredLock) {
submissionData.assigned_to = userId;
- submissionData.locked_until = new Date(Date.now() - 1000 * 60 * 30).toISOString(); // 30 min ago
+ submissionData.locked_until = new Date(Date.now() - 1000 * 60 * 30).toISOString();
}
- const { error: subError } = await supabase
- .from('content_submissions')
- .insert(submissionData);
-
+ const { error: subError } = await supabase.from('content_submissions').insert(submissionData);
if (subError) throw subError;
- // Create submission_item
- const { error: itemError } = await supabase
- .from('submission_items')
- .insert({
- id: itemId,
- submission_id: submissionId,
- item_type: type,
- item_data: itemData,
- status: 'pending',
- order_index: 0
- });
-
+ const { error: itemError } = await supabase.from('submission_items').insert({
+ id: itemId,
+ submission_id: submissionId,
+ item_type: type,
+ item_data: itemData,
+ status: 'pending',
+ order_index: 0
+ });
if (itemError) throw itemError;
- // Create type-specific submission record
const typeTableMap: Record = {
park: 'park_submissions',
ride: 'ride_submissions',
@@ -179,43 +198,68 @@ Deno.serve(async (req) => {
typeData.company_type = type;
}
- const { error: typeError } = await supabase
- .from(table)
- .insert(typeData);
-
+ const { data: insertedData, error: typeError } = await supabase.from(table).insert(typeData).select('id').single();
if (typeError) throw typeError;
+ return { submissionId, typeId: insertedData?.id };
}
- return submissionId;
+ return { submissionId, typeId: null };
}
// Create parks
if (entityTypes.includes('parks')) {
for (let i = 0; i < plan.parks; i++) {
- // Determine if this should be a conflict or version chain
+ const level = getPopulationLevel(fieldDensity, i);
const shouldConflict = includeConflicts && createdParkSlugs.length > 0 && Math.random() < 0.15;
const shouldVersionChain = includeVersionChains && createdParkSlugs.length > 0 && Math.random() < 0.15;
let slug = `test-park-${i + 1}`;
if (shouldConflict) {
- // Reuse an existing slug to create a conflict
- slug = createdParkSlugs[Math.floor(Math.random() * createdParkSlugs.length)];
+ slug = randomItem(createdParkSlugs);
summary.conflicts++;
} else if (shouldVersionChain) {
- // Reuse an existing slug for a version chain with different data
- slug = createdParkSlugs[Math.floor(Math.random() * createdParkSlugs.length)];
+ slug = randomItem(createdParkSlugs);
summary.versionChains++;
}
- const parkData = {
+ const parkData: any = {
name: shouldVersionChain ? `Test Park ${slug} (Updated)` : `Test Park ${i + 1}`,
slug: slug,
- description: shouldVersionChain ? 'Updated test park description' : 'Test park description',
- park_type: ['theme_park', 'amusement_park', 'water_park'][Math.floor(Math.random() * 3)],
- status: 'operating',
- opening_date: '2000-01-01'
+ park_type: randomItem(['theme_park', 'amusement_park', 'water_park']),
+ status: 'operating'
};
+ if (level >= 1) {
+ parkData.opening_date = randomDate(1950, 2024);
+ parkData.description = `A ${parkData.park_type === 'theme_park' ? 'themed' : 'exciting'} park for all ages with various attractions.`;
+ }
+
+ if (level >= 2) {
+ parkData.website_url = `https://test-park-${i + 1}.example.com`;
+ parkData.phone = `+1-555-${randomInt(100, 999)}-${randomInt(1000, 9999)}`;
+ }
+
+ if (level >= 3 && createdCompanies.operator.length > 0) {
+ const { data: operatorData } = await supabase.from('companies').select('id').eq('slug', randomItem(createdCompanies.operator)).maybeSingle();
+ if (operatorData) parkData.operator_id = operatorData.id;
+ parkData.email = `info@test-park-${i + 1}.example.com`;
+ parkData.card_image_id = `test-park-card-${i + 1}`;
+ parkData.card_image_url = `https://imagedelivery.net/test/park-${i + 1}/card`;
+ }
+
+ if (level >= 4) {
+ if (createdCompanies.property_owner.length > 0) {
+ const { data: ownerData } = await supabase.from('companies').select('id').eq('slug', randomItem(createdCompanies.property_owner)).maybeSingle();
+ if (ownerData) parkData.property_owner_id = ownerData.id;
+ }
+ if (Math.random() > 0.9) {
+ parkData.closing_date = randomDate(2000, 2024);
+ parkData.status = 'closed';
+ }
+ parkData.banner_image_id = `test-park-banner-${i + 1}`;
+ parkData.banner_image_url = `https://imagedelivery.net/test/park-${i + 1}/banner`;
+ }
+
const options = {
escalated: includeEscalated && Math.random() < 0.1,
expiredLock: includeExpiredLocks && Math.random() < 0.1
@@ -233,18 +277,39 @@ Deno.serve(async (req) => {
// Create companies
const companyTypes = ['manufacturer', 'operator', 'designer', 'property_owner'];
for (const compType of companyTypes) {
- if (entityTypes.includes(compType)) {
+ if (entityTypes.includes(compType + 's') || entityTypes.includes(compType === 'manufacturer' ? 'manufacturers' : compType === 'property_owner' ? 'property_owners' : compType + 's')) {
const count = Math.floor(plan.companies / 4);
for (let i = 0; i < count; i++) {
- const companyData = {
- name: `Test ${compType} ${i + 1}`,
+ const level = getPopulationLevel(fieldDensity, i);
+ const companyData: any = {
+ name: `Test ${compType.replace('_', ' ')} ${i + 1}`,
slug: `test-${compType}-${i + 1}`,
- description: `Test ${compType} description`,
- company_type: compType,
- person_type: 'company',
- founded_year: 2000
+ company_type: compType
};
+ if (level >= 1) {
+ companyData.description = `Leading ${compType.replace('_', ' ')} in the amusement industry.`;
+ companyData.person_type = compType === 'designer' && Math.random() > 0.5 ? 'individual' : 'company';
+ }
+
+ if (level >= 2) {
+ companyData.founded_year = randomInt(1950, 2020);
+ const location = randomItem(CITIES);
+ companyData.headquarters_location = `${location.city}, ${location.country}`;
+ }
+
+ if (level >= 3) {
+ companyData.website_url = `https://test-${compType}-${i + 1}.example.com`;
+ companyData.logo_url = `https://imagedelivery.net/test/${compType}-${i + 1}/logo`;
+ }
+
+ if (level >= 4) {
+ companyData.card_image_id = `test-${compType}-card-${i + 1}`;
+ companyData.card_image_url = `https://imagedelivery.net/test/${compType}-${i + 1}/card`;
+ companyData.banner_image_id = `test-${compType}-banner-${i + 1}`;
+ companyData.banner_image_url = `https://imagedelivery.net/test/${compType}-${i + 1}/banner`;
+ }
+
await createSubmission(user.id, compType, companyData);
createdCompanies[compType].push(`test-${compType}-${i + 1}`);
summary.companies++;
@@ -252,41 +317,112 @@ Deno.serve(async (req) => {
}
}
- // Create rides (with dependencies if enabled)
+ // Create rides
if (entityTypes.includes('rides') && includeDependencies && createdParks.length > 0) {
for (let i = 0; i < plan.rides; i++) {
- // Determine if this should be a conflict or version chain
+ const level = getPopulationLevel(fieldDensity, i);
const shouldConflict = includeConflicts && createdRideSlugs.length > 0 && Math.random() < 0.15;
const shouldVersionChain = includeVersionChains && createdRideSlugs.length > 0 && Math.random() < 0.15;
let slug = `test-ride-${i + 1}`;
if (shouldConflict) {
- slug = createdRideSlugs[Math.floor(Math.random() * createdRideSlugs.length)];
+ slug = randomItem(createdRideSlugs);
summary.conflicts++;
} else if (shouldVersionChain) {
- slug = createdRideSlugs[Math.floor(Math.random() * createdRideSlugs.length)];
+ slug = randomItem(createdRideSlugs);
summary.versionChains++;
}
- // Get random park ID from database
- const parkSlug = createdParks[Math.floor(Math.random() * createdParks.length)];
- const { data: parkData } = await supabase
- .from('parks')
- .select('id')
- .eq('slug', parkSlug)
- .maybeSingle();
+ const parkSlug = randomItem(createdParks);
+ const { data: parkData } = await supabase.from('parks').select('id').eq('slug', parkSlug).maybeSingle();
- const rideData = {
+ const category = randomItem(['roller_coaster', 'flat_ride', 'water_ride', 'dark_ride']);
+ const rideData: any = {
name: shouldVersionChain ? `Test Ride ${slug} (Updated)` : `Test Ride ${i + 1}`,
slug: slug,
- description: shouldVersionChain ? 'Updated test ride description' : 'Test ride description',
- category: ['roller_coaster', 'flat_ride', 'water_ride'][Math.floor(Math.random() * 3)],
+ category: category,
status: 'operating',
- park_id: parkData?.id || null,
- opening_date: '2010-01-01'
+ park_id: parkData?.id || null
};
- await createSubmission(user.id, 'ride', rideData);
+ if (level >= 1) {
+ rideData.opening_date = randomDate(2000, 2024);
+ rideData.description = `An exciting ${category.replace('_', ' ')} attraction.`;
+ rideData.height_requirement = randomInt(100, 140);
+ }
+
+ if (level >= 2) {
+ rideData.max_speed_kmh = randomInt(40, 120);
+ rideData.max_height_meters = randomInt(20, 100);
+ rideData.duration_seconds = randomInt(60, 240);
+ rideData.capacity_per_hour = randomInt(500, 2000);
+ rideData.intensity_level = randomItem(['family', 'moderate', 'high', 'extreme']);
+ }
+
+ if (level >= 3) {
+ if (createdCompanies.manufacturer.length > 0) {
+ const { data: mfgData } = await supabase.from('companies').select('id').eq('slug', randomItem(createdCompanies.manufacturer)).maybeSingle();
+ if (mfgData) rideData.manufacturer_id = mfgData.id;
+ }
+ if (category === 'roller_coaster') {
+ rideData.inversions = randomInt(0, 7);
+ rideData.coaster_type = randomItem(['steel', 'wooden', 'hybrid']);
+ rideData.seating_type = randomItem(['sit_down', 'inverted', 'flying', 'stand_up']);
+ }
+ rideData.card_image_id = `test-ride-card-${i + 1}`;
+ rideData.card_image_url = `https://imagedelivery.net/test/ride-${i + 1}/card`;
+ }
+
+ if (level >= 4) {
+ if (createdCompanies.designer.length > 0) {
+ const { data: designerData } = await supabase.from('companies').select('id').eq('slug', randomItem(createdCompanies.designer)).maybeSingle();
+ if (designerData) rideData.designer_id = designerData.id;
+ }
+ rideData.length_meters = randomInt(500, 2000);
+ rideData.drop_height_meters = randomInt(10, 80);
+ rideData.max_g_force = (Math.random() * 4 + 2).toFixed(1);
+ rideData.banner_image_id = `test-ride-banner-${i + 1}`;
+ rideData.banner_image_url = `https://imagedelivery.net/test/ride-${i + 1}/banner`;
+ }
+
+ const { submissionId, typeId } = await createSubmission(user.id, 'ride', rideData);
+
+ // Add technical specs and stats for level 4
+ if (level >= 4 && typeId && category === 'roller_coaster') {
+ // Add coaster stats
+ for (let s = 0; s < randomInt(2, 3); s++) {
+ await supabase.from('ride_submission_coaster_statistics').insert({
+ ride_submission_id: typeId,
+ stat_name: randomItem(['Airtime Duration', 'Zero-G Time', 'Track Gauge']),
+ stat_value: Math.random() * 100,
+ unit: randomItem(['seconds', 'mm']),
+ category: 'technical'
+ });
+ }
+
+ // Add technical specs
+ for (let t = 0; t < randomInt(3, 5); t++) {
+ await supabase.from('ride_submission_technical_specifications').insert({
+ ride_submission_id: typeId,
+ spec_name: randomItem(['Lift System', 'Brake System', 'Train Count', 'Track Material']),
+ spec_value: randomItem(['Chain lift', 'Magnetic brakes', '3 trains', 'Steel tubular']),
+ spec_type: 'system',
+ display_order: t
+ });
+ }
+
+ // Add former name
+ if (Math.random() > 0.7) {
+ await supabase.from('ride_submission_name_history').insert({
+ ride_submission_id: typeId,
+ former_name: `Original Name ${i + 1}`,
+ date_changed: randomDate(2010, 2020),
+ reason: 'Rebranding',
+ order_index: 0
+ });
+ }
+ }
+
if (!shouldConflict && !shouldVersionChain) {
createdRideSlugs.push(slug);
}
@@ -297,43 +433,128 @@ Deno.serve(async (req) => {
// Create ride models
if (entityTypes.includes('ride_models') && includeDependencies && createdCompanies.manufacturer.length > 0) {
for (let i = 0; i < plan.rideModels; i++) {
- const mfgSlug = createdCompanies.manufacturer[Math.floor(Math.random() * createdCompanies.manufacturer.length)];
- const { data: mfgData } = await supabase
- .from('companies')
- .select('id')
- .eq('slug', mfgSlug)
- .maybeSingle();
+ const level = getPopulationLevel(fieldDensity, i);
+ const { data: mfgData } = await supabase.from('companies').select('id').eq('slug', randomItem(createdCompanies.manufacturer)).maybeSingle();
- const modelData = {
+ const category = randomItem(['roller_coaster', 'flat_ride', 'water_ride']);
+ const modelData: any = {
name: `Test Model ${i + 1}`,
slug: `test-model-${i + 1}`,
- manufacturer_id: mfgData?.id || null,
- category: 'roller_coaster',
- ride_type: 'steel',
- description: 'Test ride model'
+ category: category,
+ manufacturer_id: mfgData?.id || null
};
+ if (level >= 1) {
+ modelData.description = `Popular ${category.replace('_', ' ')} model.`;
+ modelData.ride_type = randomItem(['spinning', 'launch', 'suspended', 'family']);
+ }
+
+ if (level >= 2) {
+ modelData.card_image_id = `test-model-card-${i + 1}`;
+ modelData.card_image_url = `https://imagedelivery.net/test/model-${i + 1}/card`;
+ }
+
+ if (level >= 3) {
+ modelData.banner_image_id = `test-model-banner-${i + 1}`;
+ modelData.banner_image_url = `https://imagedelivery.net/test/model-${i + 1}/banner`;
+ }
+
await createSubmission(user.id, 'ride_model', modelData);
summary.rideModels++;
}
}
- const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(2);
+ // Create photo submissions
+ if (entityTypes.includes('photos') && plan.photos > 0) {
+ const { data: approvedParks } = await supabase.from('parks').select('id').limit(Math.min(20, plan.photos));
+ const { data: approvedRides } = await supabase.from('rides').select('id, park_id').limit(Math.min(20, plan.photos));
+
+ for (let i = 0; i < plan.photos; i++) {
+ const photoCount = randomInt(1, Math.min(10, Math.ceil(plan.photos / 50) + 3));
+ const submissionId = crypto.randomUUID();
+ const photoSubmissionId = crypto.randomUUID();
+
+ let entityType = 'park';
+ let entityId = null;
+ let parentId = null;
+
+ if (Math.random() > 0.5 && approvedParks && approvedParks.length > 0) {
+ entityType = 'park';
+ entityId = randomItem(approvedParks).id;
+ } else if (approvedRides && approvedRides.length > 0) {
+ entityType = 'ride';
+ const ride = randomItem(approvedRides);
+ entityId = ride.id;
+ parentId = ride.park_id;
+ }
+
+ if (!entityId) continue;
+
+ // Create content_submission
+ await supabase.from('content_submissions').insert({
+ id: submissionId,
+ user_id: user.id,
+ submission_type: 'photo',
+ status: 'pending',
+ content: {
+ action: 'create',
+ metadata: {
+ is_test_data: true,
+ generated_at: new Date().toISOString(),
+ generator_version: '2.0.0',
+ preset
+ }
+ },
+ submitted_at: new Date().toISOString()
+ });
+
+ // Create photo_submission
+ await supabase.from('photo_submissions').insert({
+ id: photoSubmissionId,
+ submission_id: submissionId,
+ entity_type: entityType,
+ entity_id: entityId,
+ parent_id: parentId,
+ title: Math.random() > 0.5 ? `${entityType} Photos Collection ${i + 1}` : null
+ });
+
+ // Create photo_submission_items
+ for (let p = 0; p < photoCount; p++) {
+ const imageId = `test-photo-${crypto.randomUUID()}`;
+ await supabase.from('photo_submission_items').insert({
+ photo_submission_id: photoSubmissionId,
+ cloudflare_image_id: imageId,
+ cloudflare_image_url: `https://imagedelivery.net/test/${imageId}/public`,
+ caption: Math.random() > 0.3 ? `Test photo ${p + 1} - Great view of the ${entityType}` : null,
+ title: Math.random() > 0.7 ? `Photo ${p + 1}` : null,
+ filename: `photo-${p + 1}.jpg`,
+ order_index: p,
+ file_size: randomInt(500000, 5000000),
+ mime_type: 'image/jpeg',
+ date_taken: Math.random() > 0.5 ? randomDate(2015, 2024) : null
+ });
+ summary.totalPhotoItems++;
+ }
+
+ summary.photos++;
+ }
+ }
+
+ const executionTime = Date.now() - startTime;
return new Response(
JSON.stringify({
success: true,
summary,
- time: elapsedTime
+ time: (executionTime / 1000).toFixed(2)
}),
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
} catch (error) {
console.error('Seed error:', error);
- const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
return new Response(
- JSON.stringify({ error: errorMessage }),
+ JSON.stringify({ error: error.message }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}