Files
thrilltrack-explorer/supabase/functions/seed-test-data/index.ts
2025-10-10 18:20:38 +00:00

845 lines
32 KiB
TypeScript

import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.57.4';
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
interface SeedOptions {
preset: 'small' | 'medium' | 'large' | 'stress';
entityTypes: string[];
fieldDensity?: 'mixed' | 'minimal' | 'standard' | 'maximum';
includeDependencies?: boolean;
includeConflicts?: boolean;
includeVersionChains?: boolean;
includeEscalated?: boolean;
includeExpiredLocks?: boolean;
stage?: 'companies' | 'parks' | 'rides' | 'photos';
}
interface SeedPlan {
parks: number;
rides: number;
companies: number;
rideModels: number;
photos: number;
}
const PRESETS: Record<string, SeedPlan> = {
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<T>(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;
}
// Registry helper functions for cascading test data
async function registerTestEntity(
supabase: any,
entityType: string,
slug: string,
entityId: string,
submissionItemId: string,
sessionId: string
) {
const { error } = await supabase.from('test_data_registry').insert({
entity_type: entityType,
entity_slug: slug,
entity_id: entityId,
submission_item_id: submissionItemId,
test_session_id: sessionId
});
if (error && error.code !== '23505') { // Ignore unique constraint violations
console.error(`Error registering ${entityType} ${slug}:`, error);
}
}
async function getExistingTestEntities(
supabase: any,
entityType: string
): Promise<Array<{ slug: string; entity_id: string; submission_item_id: string }>> {
const { data, error } = await supabase
.from('test_data_registry')
.select('entity_slug, entity_id, submission_item_id')
.eq('entity_type', entityType);
if (error) {
console.error(`Error fetching existing ${entityType}:`, error);
return [];
}
return data || [];
}
async function getPendingSubmissionItems(
supabase: any,
itemType: string
): Promise<Array<{ id: string; item_data: any }>> {
const { data, error } = await supabase
.from('submission_items')
.select('id, item_data')
.eq('item_type', itemType)
.eq('status', 'pending');
if (error) {
console.error(`Error fetching pending ${itemType} items:`, error);
return [];
}
return data || [];
}
Deno.serve(async (req) => {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const supabase = createClient(supabaseUrl, supabaseServiceKey);
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
return new Response(JSON.stringify({ error: 'No authorization header' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
const token = authHeader.replace('Bearer ', '');
const { data: { user }, error: userError } = await supabase.auth.getUser(token);
if (userError || !user) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
const { data: isMod, error: modError } = await supabase.rpc('is_moderator', { _user_id: user.id });
if (modError || !isMod) {
return new Response(JSON.stringify({ error: 'Insufficient permissions. Moderator role required.' }), {
status: 403,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
const {
preset = 'small',
entityTypes = [],
fieldDensity = 'mixed',
includeDependencies = true,
includeConflicts = false,
includeVersionChains = false,
includeEscalated = false,
includeExpiredLocks = false,
stage
}: SeedOptions = await req.json();
console.info('=== SEED DATA GENERATION DEBUG ===');
console.info('Received entityTypes:', JSON.stringify(entityTypes));
console.info('entityTypes is array:', Array.isArray(entityTypes));
console.info('entityTypes length:', entityTypes?.length);
console.info('Preset:', preset);
console.info('===================================');
const plan = PRESETS[preset];
if (!plan) {
return new Response(JSON.stringify({ error: 'Invalid preset' }), {
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
const startTime = Date.now();
const sessionId = crypto.randomUUID(); // Unique ID for this generation session
const summary = { parks: 0, rides: 0, companies: 0, rideModels: 0, photos: 0, totalPhotoItems: 0, conflicts: 0, versionChains: 0 };
// Track submission item IDs for dependency resolution
const createdSubmissionItems: Record<string, string[]> = {
operator: [],
property_owner: [],
manufacturer: [],
designer: [],
park: [],
ride_model: []
};
const createdParks: string[] = [];
const createdCompanies: Record<string, string[]> = { manufacturer: [], operator: [], designer: [], property_owner: [] };
const createdParkSlugs: string[] = [];
const createdRideSlugs: string[] = [];
// Load existing test entities from registry if includeDependencies is true
if (includeDependencies) {
console.log('Loading existing test entities from registry...');
const existingOperators = await getExistingTestEntities(supabase, 'operator');
const existingOwners = await getExistingTestEntities(supabase, 'property_owner');
const existingManufacturers = await getExistingTestEntities(supabase, 'manufacturer');
const existingDesigners = await getExistingTestEntities(supabase, 'designer');
const existingParks = await getExistingTestEntities(supabase, 'park');
const existingRideModels = await getExistingTestEntities(supabase, 'ride_model');
// Load pending submission items for dependency resolution
const pendingOperators = await getPendingSubmissionItems(supabase, 'operator');
const pendingOwners = await getPendingSubmissionItems(supabase, 'property_owner');
const pendingManufacturers = await getPendingSubmissionItems(supabase, 'manufacturer');
const pendingDesigners = await getPendingSubmissionItems(supabase, 'designer');
const pendingParks = await getPendingSubmissionItems(supabase, 'park');
const pendingRideModels = await getPendingSubmissionItems(supabase, 'ride_model');
// Track both approved and pending items
existingOperators.forEach(op => {
createdCompanies.operator.push(op.slug);
if (op.submission_item_id) createdSubmissionItems.operator.push(op.submission_item_id);
});
existingOwners.forEach(own => {
createdCompanies.property_owner.push(own.slug);
if (own.submission_item_id) createdSubmissionItems.property_owner.push(own.submission_item_id);
});
existingManufacturers.forEach(mfg => {
createdCompanies.manufacturer.push(mfg.slug);
if (mfg.submission_item_id) createdSubmissionItems.manufacturer.push(mfg.submission_item_id);
});
existingDesigners.forEach(des => {
createdCompanies.designer.push(des.slug);
if (des.submission_item_id) createdSubmissionItems.designer.push(des.submission_item_id);
});
existingParks.forEach(park => {
createdParks.push(park.slug);
createdParkSlugs.push(park.slug);
if (park.submission_item_id) createdSubmissionItems.park.push(park.submission_item_id);
});
// Add pending items to tracking
pendingOperators.forEach(item => createdSubmissionItems.operator.push(item.id));
pendingOwners.forEach(item => createdSubmissionItems.property_owner.push(item.id));
pendingManufacturers.forEach(item => createdSubmissionItems.manufacturer.push(item.id));
pendingDesigners.forEach(item => createdSubmissionItems.designer.push(item.id));
pendingParks.forEach(item => createdSubmissionItems.park.push(item.id));
pendingRideModels.forEach(item => createdSubmissionItems.ride_model.push(item.id));
console.log(`Loaded ${existingOperators.length} operators, ${existingOwners.length} owners, ${existingManufacturers.length} manufacturers, ${existingDesigners.length} designers, ${existingParks.length} parks`);
console.log(`Loaded ${pendingOperators.length} pending operators, ${pendingOwners.length} pending owners, ${pendingManufacturers.length} pending manufacturers, ${pendingDesigners.length} pending designers, ${pendingParks.length} pending parks`);
// Add pending parks' slugs to createdParks for ride generation
pendingParks.forEach(item => {
if (item.item_data?.slug) {
createdParks.push(item.item_data.slug);
createdParkSlugs.push(item.item_data.slug);
}
});
console.log(`Total parks available for rides: ${createdParks.length}`);
}
// Helper to create submission
async function createSubmission(
userId: string,
type: string,
itemData: any,
options: { escalated?: boolean; expiredLock?: boolean; dependsOn?: string } = {}
) {
const submissionId = crypto.randomUUID();
const itemId = crypto.randomUUID();
const contentData = {
action: 'create',
metadata: {
is_test_data: true,
generated_at: new Date().toISOString(),
generator_version: '2.0.0',
preset,
fieldDensity
}
};
const submissionData: any = {
id: submissionId,
user_id: userId,
submission_type: type,
status: 'pending',
content: contentData,
submitted_at: new Date().toISOString()
};
if (options.escalated) {
submissionData.escalated = true;
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();
}
const { error: subError } = await supabase.from('content_submissions').insert(submissionData);
if (subError) {
console.error(`Error inserting content_submission for type ${type}:`, subError);
throw subError;
}
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,
depends_on: options.dependsOn || null
});
if (itemError) throw itemError;
const typeTableMap: Record<string, string> = {
park: 'park_submissions',
ride: 'ride_submissions',
manufacturer: 'company_submissions',
operator: 'company_submissions',
designer: 'company_submissions',
property_owner: 'company_submissions',
ride_model: 'ride_model_submissions'
};
const table = typeTableMap[type];
if (table) {
const typeData = { ...itemData, submission_id: submissionId };
if (table === 'company_submissions') {
typeData.company_type = type;
}
const { data: insertedData, error: typeError } = await supabase.from(table).insert(typeData).select('id').single();
if (typeError) {
console.error(`Error inserting into ${table} for type ${type}:`, typeError);
console.error('Data being inserted:', JSON.stringify(typeData, null, 2));
throw typeError;
}
return { submissionId, itemId, typeId: insertedData?.id };
}
return { submissionId, itemId, typeId: null };
}
// Helper function to properly pluralize company types
const pluralizeCompanyType = (singular: string): string => {
const pluralMap: Record<string, string> = {
'manufacturer': 'manufacturers',
'operator': 'operators',
'designer': 'designers',
'property_owner': 'property_owners'
};
return pluralMap[singular] || singular + 's';
};
// Create companies FIRST so parks can reference operators/owners
if (!stage || stage === 'companies') {
console.info('=== COMPANY GENERATION CHECK ===');
console.info('Entity types array:', JSON.stringify(entityTypes));
console.info('Plan calls for companies:', plan.companies);
const companyTypes = ['manufacturer', 'operator', 'designer', 'property_owner'];
// First, determine which company types are selected
const selectedCompanyTypes = companyTypes.filter(compType =>
entityTypes.includes(pluralizeCompanyType(compType))
);
console.info(`✓ Selected company types (${selectedCompanyTypes.length}):`, selectedCompanyTypes);
// Calculate fair distribution: base amount per type + extras
const basePerType = Math.floor(plan.companies / selectedCompanyTypes.length);
const extras = plan.companies % selectedCompanyTypes.length;
console.info(`Distribution plan: ${basePerType} base per type, ${extras} extra(s) to first types`);
let companiesCreatedTotal = 0;
for (let typeIndex = 0; typeIndex < selectedCompanyTypes.length; typeIndex++) {
const compType = selectedCompanyTypes[typeIndex];
const pluralType = pluralizeCompanyType(compType);
// Each type gets base amount, first N types get +1 extra
const count = basePerType + (typeIndex < extras ? 1 : 0);
console.info(`✓ Generating ${count} companies of type ${compType}`);
for (let i = 0; i < count; i++) {
console.info(` Creating company ${i + 1}/${count} (type: ${compType})`);
const level = getPopulationLevel(fieldDensity, i);
const companyData: any = {
name: `Test ${compType.replace('_', ' ')} ${i + 1}`,
slug: `test-${compType}-${i + 1}`,
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`;
}
const { itemId } = await createSubmission(user.id, compType, companyData);
// Track created submission item
createdSubmissionItems[compType].push(itemId);
// Register newly created company in the registry
const companySlug = `test-${compType}-${i + 1}`;
const { data: approvedCompany } = await supabase
.from('companies')
.select('id')
.eq('slug', companySlug)
.maybeSingle();
if (approvedCompany) {
await registerTestEntity(supabase, compType, companySlug, approvedCompany.id, itemId, sessionId);
}
createdCompanies[compType].push(companySlug);
summary.companies++;
companiesCreatedTotal++;
}
}
}
// Create parks
if ((!stage || stage === 'parks') && entityTypes.includes('parks')) {
for (let i = 0; i < plan.parks; i++) {
console.log(` Creating park ${i + 1}/${plan.parks}`);
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) {
slug = randomItem(createdParkSlugs);
summary.conflicts++;
} else if (shouldVersionChain) {
slug = randomItem(createdParkSlugs);
summary.versionChains++;
}
const parkData: any = {
name: shouldVersionChain ? `Test Park ${slug} (Updated)` : `Test Park ${i + 1}`,
slug: slug,
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)}`;
}
let operatorItemId: string | undefined;
let ownerItemId: string | undefined;
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;
// Track operator dependency
if (createdSubmissionItems.operator.length > 0) {
operatorItemId = randomItem(createdSubmissionItems.operator);
}
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;
// Track owner dependency (prefer operator if both exist)
if (createdSubmissionItems.property_owner.length > 0 && !operatorItemId) {
ownerItemId = randomItem(createdSubmissionItems.property_owner);
}
}
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,
dependsOn: operatorItemId || ownerItemId
};
const { itemId } = await createSubmission(user.id, 'park', parkData, options);
// Track created submission item
createdSubmissionItems.park.push(itemId);
// Register newly created park in the registry
const { data: approvedPark } = await supabase
.from('parks')
.select('id')
.eq('slug', slug)
.maybeSingle();
if (approvedPark) {
await registerTestEntity(supabase, 'park', slug, approvedPark.id, itemId, sessionId);
}
createdParks.push(slug);
if (!shouldConflict && !shouldVersionChain) {
createdParkSlugs.push(slug);
}
summary.parks++;
}
}
// Create rides
if ((!stage || stage === 'rides') && entityTypes.includes('rides') && includeDependencies && createdParks.length > 0) {
for (let i = 0; i < plan.rides; i++) {
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 = randomItem(createdRideSlugs);
summary.conflicts++;
} else if (shouldVersionChain) {
slug = randomItem(createdRideSlugs);
summary.versionChains++;
}
const parkSlug = randomItem(createdParks);
const { data: parkData } = await supabase.from('parks').select('id').eq('slug', parkSlug).maybeSingle();
// Get park dependency for depends_on
const parkItemId = createdSubmissionItems.park.length > 0 ? randomItem(createdSubmissionItems.park) : undefined;
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,
category: category,
status: 'operating',
park_id: parkData?.id || null
};
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 options = {
dependsOn: parkItemId
};
const { submissionId, itemId, typeId } = await createSubmission(user.id, 'ride', rideData, options);
// 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
});
}
}
// Register newly created ride in the registry
const { data: approvedRide } = await supabase
.from('rides')
.select('id')
.eq('slug', slug)
.maybeSingle();
if (approvedRide) {
await registerTestEntity(supabase, 'ride', slug, approvedRide.id, itemId, sessionId);
}
if (!shouldConflict && !shouldVersionChain) {
createdRideSlugs.push(slug);
}
summary.rides++;
}
}
// Create ride models
if ((!stage || stage === 'rides') && entityTypes.includes('ride_models') && includeDependencies && createdSubmissionItems.manufacturer.length > 0) {
for (let i = 0; i < plan.rideModels; i++) {
const level = getPopulationLevel(fieldDensity, i);
const { data: mfgData } = await supabase.from('companies').select('id').eq('slug', randomItem(createdCompanies.manufacturer)).maybeSingle();
// Get manufacturer dependency for depends_on
const mfgItemId = createdSubmissionItems.manufacturer.length > 0 ? randomItem(createdSubmissionItems.manufacturer) : undefined;
const category = randomItem(['roller_coaster', 'flat_ride', 'water_ride']);
const rideType = randomItem(['spinning', 'launch', 'suspended', 'family', 'standard']);
const modelData: any = {
name: `Test Model ${i + 1}`,
slug: `test-model-${i + 1}`,
category: category,
ride_type: rideType,
manufacturer_id: mfgData?.id || null
};
if (level >= 1) {
modelData.description = `Popular ${category.replace('_', ' ')} model.`;
}
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`;
}
const options = {
dependsOn: mfgItemId
};
const { itemId } = await createSubmission(user.id, 'ride_model', modelData, options);
// Track created submission item
createdSubmissionItems.ride_model.push(itemId);
// Register newly created ride model in the registry
const modelSlug = `test-model-${i + 1}`;
const { data: approvedModel } = await supabase
.from('ride_models')
.select('id')
.eq('slug', modelSlug)
.maybeSingle();
if (approvedModel) {
await registerTestEntity(supabase, 'ride_model', modelSlug, approvedModel.id, itemId, sessionId);
}
summary.rideModels++;
}
}
// Create photo submissions
if ((!stage || stage === 'photos') && 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: (executionTime / 1000).toFixed(2),
stage: stage || 'all'
}),
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
} catch (error) {
console.error('Seed error:', error);
return new Response(
JSON.stringify({ error: error.message }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
});