import { PrismaClient, UserRole, UserStatus, ProgramStatus, RoundStatus, SettingType, SettingCategory, } from '@prisma/client' const prisma = new PrismaClient() async function main() { console.log('🌱 Seeding database...') // ========================================================================== // Create System Settings // ========================================================================== console.log('📋 Creating system settings...') const settings = [ // AI Settings { key: 'ai_enabled', value: 'false', type: SettingType.BOOLEAN, category: SettingCategory.AI, description: 'Enable AI-powered jury assignment suggestions', }, { key: 'ai_provider', value: 'openai', type: SettingType.STRING, category: SettingCategory.AI, description: 'AI provider for smart assignment (openai)', }, { key: 'ai_model', value: 'gpt-4o', type: SettingType.STRING, category: SettingCategory.AI, description: 'OpenAI model to use for suggestions', }, { key: 'ai_send_descriptions', value: 'false', type: SettingType.BOOLEAN, category: SettingCategory.AI, description: 'Send anonymized project descriptions to AI', }, // Branding Settings { key: 'platform_name', value: 'Monaco Ocean Protection Challenge', type: SettingType.STRING, category: SettingCategory.BRANDING, description: 'Platform display name', }, { key: 'primary_color', value: '#de0f1e', type: SettingType.STRING, category: SettingCategory.BRANDING, description: 'Primary brand color (hex)', }, { key: 'secondary_color', value: '#053d57', type: SettingType.STRING, category: SettingCategory.BRANDING, description: 'Secondary brand color (hex)', }, { key: 'accent_color', value: '#557f8c', type: SettingType.STRING, category: SettingCategory.BRANDING, description: 'Accent color (hex)', }, // Security Settings { key: 'session_duration_hours', value: '24', type: SettingType.NUMBER, category: SettingCategory.SECURITY, description: 'Session duration in hours', }, { key: 'magic_link_expiry_minutes', value: '15', type: SettingType.NUMBER, category: SettingCategory.SECURITY, description: 'Magic link expiry time in minutes', }, { key: 'rate_limit_requests_per_minute', value: '60', type: SettingType.NUMBER, category: SettingCategory.SECURITY, description: 'API rate limit per minute', }, // Storage Settings { key: 'storage_provider', value: 's3', type: SettingType.STRING, category: SettingCategory.STORAGE, description: 'Storage provider: s3 (MinIO) or local (filesystem)', }, { key: 'local_storage_path', value: './uploads', type: SettingType.STRING, category: SettingCategory.STORAGE, description: 'Base path for local file storage', }, { key: 'max_file_size_mb', value: '500', type: SettingType.NUMBER, category: SettingCategory.STORAGE, description: 'Maximum file upload size in MB', }, { key: 'avatar_max_size_mb', value: '5', type: SettingType.NUMBER, category: SettingCategory.STORAGE, description: 'Maximum avatar image size in MB', }, { key: 'allowed_file_types', value: JSON.stringify(['application/pdf', 'video/mp4', 'video/quicktime', 'image/png', 'image/jpeg']), type: SettingType.JSON, category: SettingCategory.STORAGE, description: 'Allowed MIME types for file uploads', }, { key: 'allowed_image_types', value: JSON.stringify(['image/png', 'image/jpeg', 'image/webp']), type: SettingType.JSON, category: SettingCategory.STORAGE, description: 'Allowed MIME types for avatar/logo uploads', }, // Default Settings { key: 'default_timezone', value: 'Europe/Monaco', type: SettingType.STRING, category: SettingCategory.DEFAULTS, description: 'Default timezone for date displays', }, { key: 'default_page_size', value: '20', type: SettingType.NUMBER, category: SettingCategory.DEFAULTS, description: 'Default pagination size', }, { key: 'autosave_interval_seconds', value: '30', type: SettingType.NUMBER, category: SettingCategory.DEFAULTS, description: 'Autosave interval for evaluation forms', }, // WhatsApp Settings (Phase 2) { key: 'whatsapp_enabled', value: 'false', type: SettingType.BOOLEAN, category: SettingCategory.WHATSAPP, description: 'Enable WhatsApp notifications', }, { key: 'whatsapp_provider', value: 'META', type: SettingType.STRING, category: SettingCategory.WHATSAPP, description: 'WhatsApp provider (META or TWILIO)', }, { key: 'whatsapp_meta_phone_number_id', value: '', type: SettingType.STRING, category: SettingCategory.WHATSAPP, description: 'Meta WhatsApp Phone Number ID', }, { key: 'whatsapp_meta_access_token', value: '', type: SettingType.SECRET, category: SettingCategory.WHATSAPP, description: 'Meta WhatsApp Access Token', isSecret: true, }, { key: 'whatsapp_meta_business_account_id', value: '', type: SettingType.STRING, category: SettingCategory.WHATSAPP, description: 'Meta WhatsApp Business Account ID', }, { key: 'whatsapp_twilio_account_sid', value: '', type: SettingType.SECRET, category: SettingCategory.WHATSAPP, description: 'Twilio Account SID', isSecret: true, }, { key: 'whatsapp_twilio_auth_token', value: '', type: SettingType.SECRET, category: SettingCategory.WHATSAPP, description: 'Twilio Auth Token', isSecret: true, }, { key: 'whatsapp_twilio_phone_number', value: '', type: SettingType.STRING, category: SettingCategory.WHATSAPP, description: 'Twilio WhatsApp Phone Number', }, // OpenAI API Key (Phase 2) { key: 'openai_api_key', value: '', type: SettingType.SECRET, category: SettingCategory.AI, description: 'OpenAI API Key for AI-powered features', isSecret: true, }, ] for (const setting of settings) { await prisma.systemSettings.upsert({ where: { key: setting.key }, update: {}, create: setting, }) } // ========================================================================== // Create Super Admin // ========================================================================== console.log('👤 Creating super admin...') const admin = await prisma.user.upsert({ where: { email: 'matt.ciaccio@gmail.com' }, update: {}, create: { email: 'matt.ciaccio@gmail.com', name: 'Matt Ciaccio', role: UserRole.SUPER_ADMIN, status: UserStatus.ACTIVE, }, }) console.log(` Created admin: ${admin.email}`) // ========================================================================== // Create Sample Program // ========================================================================== console.log('📁 Creating sample program...') const program = await prisma.program.upsert({ where: { name_year: { name: 'Monaco Ocean Protection Challenge', year: 2026 } }, update: {}, create: { name: 'Monaco Ocean Protection Challenge', year: 2026, status: ProgramStatus.ACTIVE, description: 'Annual ocean conservation startup competition supporting innovative solutions for ocean protection.', }, }) console.log(` Created program: ${program.name} ${program.year}`) // ========================================================================== // Create Round 1 // ========================================================================== console.log('🔄 Creating Round 1...') const round1 = await prisma.round.upsert({ where: { id: 'round-1-2026', // Use a deterministic ID for upsert }, update: {}, create: { id: 'round-1-2026', programId: program.id, name: 'Round 1 - Semi-Finalists Selection', status: RoundStatus.DRAFT, requiredReviews: 3, votingStartAt: new Date('2026-02-18T09:00:00Z'), votingEndAt: new Date('2026-02-23T18:00:00Z'), settingsJson: { allowGracePeriods: true, showAggregatesAfterClose: true, juryCanSeeOwnPastEvaluations: true, }, }, }) console.log(` Created round: ${round1.name}`) // ========================================================================== // Create Evaluation Form for Round 1 // ========================================================================== console.log('📝 Creating evaluation form...') await prisma.evaluationForm.upsert({ where: { roundId_version: { roundId: round1.id, version: 1, }, }, update: {}, create: { roundId: round1.id, version: 1, isActive: true, criteriaJson: [ { id: 'need_clarity', label: 'Need Clarity', description: 'How clearly is the problem/need articulated?', scale: '1-5', weight: 1, required: true, }, { id: 'solution_relevance', label: 'Solution Relevance', description: 'How relevant and innovative is the proposed solution?', scale: '1-5', weight: 1, required: true, }, { id: 'gap_analysis', label: 'Gap Analysis', description: 'How well does the project analyze existing gaps in the market?', scale: '1-5', weight: 1, required: true, }, { id: 'target_customers', label: 'Target Customer Clarity', description: 'How clearly are target customers/beneficiaries defined?', scale: '1-5', weight: 1, required: true, }, { id: 'ocean_impact', label: 'Ocean Impact', description: 'What is the potential positive impact on ocean conservation?', scale: '1-5', weight: 2, // Higher weight for ocean impact required: true, }, ], scalesJson: { '1-5': { min: 1, max: 5, labels: { 1: 'Poor', 2: 'Below Average', 3: 'Average', 4: 'Good', 5: 'Excellent', }, }, '1-10': { min: 1, max: 10, labels: { 1: 'Poor', 5: 'Average', 10: 'Excellent', }, }, }, }, }) console.log(' Created evaluation form v1') // ========================================================================== // Create Sample Jury Members // ========================================================================== console.log('👥 Creating sample jury members...') const juryMembers = [ { email: 'jury1@example.com', name: 'Dr. Marine Expert', expertiseTags: ['marine_biology', 'conservation', 'policy'], }, { email: 'jury2@example.com', name: 'Tech Innovator', expertiseTags: ['technology', 'innovation', 'startups'], }, { email: 'jury3@example.com', name: 'Ocean Advocate', expertiseTags: ['conservation', 'sustainability', 'education'], }, ] for (const jury of juryMembers) { await prisma.user.upsert({ where: { email: jury.email }, update: {}, create: { email: jury.email, name: jury.name, role: UserRole.JURY_MEMBER, status: UserStatus.INVITED, expertiseTags: jury.expertiseTags, maxAssignments: 15, }, }) console.log(` Created jury member: ${jury.email}`) } // ========================================================================== // Create Sample Projects // ========================================================================== console.log('📦 Creating sample projects...') const sampleProjects = [ { title: 'OceanAI - Plastic Detection System', teamName: 'BlueWave Tech', description: 'AI-powered system using satellite imagery and drones to detect and map ocean plastic concentrations for targeted cleanup operations.', tags: ['technology', 'ai', 'plastic_pollution'], }, { title: 'Coral Restoration Network', teamName: 'ReefGuard Foundation', description: 'Community-driven coral nursery and transplantation program using innovative 3D-printed substrates.', tags: ['conservation', 'coral', 'community'], }, { title: 'SeaTrack - Sustainable Fishing Tracker', teamName: 'FishRight Solutions', description: 'Blockchain-based supply chain tracking system ensuring sustainable fishing practices from ocean to table.', tags: ['technology', 'sustainable_fishing', 'blockchain'], }, ] for (const project of sampleProjects) { await prisma.project.create({ data: { roundId: round1.id, title: project.title, teamName: project.teamName, description: project.description, tags: project.tags, }, }) console.log(` Created project: ${project.title}`) } console.log('') console.log('✅ Seeding completed successfully!') console.log('') console.log('📧 Admin login: matt.ciaccio@gmail.com') console.log(' (Use magic link authentication)') } main() .catch((e) => { console.error('❌ Seeding failed:', e) process.exit(1) }) .finally(async () => { await prisma.$disconnect() })