Files
MOPC-Portal/prisma/seed-mopc-onboarding.mjs
Matt 3be6a743ed Add multiple admin improvements and bug fixes
- Email settings: Add separate sender display name field
- Rounds page: Drag-and-drop reordering with visible order numbers
- Round creation: Auto-assign projects to filtering rounds, auto-activate if voting started
- Round detail: Fix incorrect "voting period ended" message for draft rounds
- Projects page: Add delete option with confirmation dialog
- AI filtering: Add configurable batch size and parallel request settings
- Filtering results: Fix duplicate criteria display
- Add seed scripts for notification settings and MOPC onboarding form

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 23:19:45 +01:00

271 lines
12 KiB
JavaScript

/**
* Seed script for MOPC Onboarding Form (ESM version for production)
* Run with: node prisma/seed-mopc-onboarding.mjs
*/
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
const MOPC_FORM_CONFIG = {
name: 'MOPC Application 2026',
description: 'Monaco Ocean Protection Challenge application form',
publicSlug: 'mopc-2026',
status: 'PUBLISHED',
isPublic: true,
sendConfirmationEmail: true,
sendTeamInviteEmails: true,
confirmationEmailSubject: 'Application Received - Monaco Ocean Protection Challenge',
confirmationEmailBody: `Thank you for applying to the Monaco Ocean Protection Challenge 2026!
We have received your application and our team will review it carefully.
If you have any questions, please don't hesitate to reach out.
Good luck!
The MOPC Team`,
confirmationMessage: 'Thank you for your application! We have sent a confirmation email to the address you provided. Our team will review your submission and get back to you soon.',
}
const STEPS = [
{
name: 'category',
title: 'Competition Category',
description: 'Select your competition track',
sortOrder: 0,
isOptional: false,
fields: [
{
name: 'competitionCategory',
label: 'Which category best describes your project?',
fieldType: 'RADIO',
specialType: 'COMPETITION_CATEGORY',
required: true,
sortOrder: 0,
width: 'full',
projectMapping: 'competitionCategory',
description: 'Choose the category that best fits your stage of development',
optionsJson: [
{ value: 'STARTUP', label: 'Startup', description: 'You have an existing company or registered business entity' },
{ value: 'BUSINESS_CONCEPT', label: 'Business Concept', description: 'You are a student, graduate, or have an idea not yet incorporated' },
],
},
],
},
{
name: 'contact',
title: 'Contact Information',
description: 'Tell us how to reach you',
sortOrder: 1,
isOptional: false,
fields: [
{ name: 'contactName', label: 'Full Name', fieldType: 'TEXT', required: true, sortOrder: 0, width: 'half', placeholder: 'Enter your full name' },
{ name: 'contactEmail', label: 'Email Address', fieldType: 'EMAIL', required: true, sortOrder: 1, width: 'half', placeholder: 'your.email@example.com', description: 'We will use this email for all communications' },
{ name: 'contactPhone', label: 'Phone Number', fieldType: 'PHONE', required: true, sortOrder: 2, width: 'half', placeholder: '+1 (555) 123-4567' },
{ name: 'country', label: 'Country', fieldType: 'SELECT', specialType: 'COUNTRY_SELECT', required: true, sortOrder: 3, width: 'half', projectMapping: 'country' },
{ name: 'city', label: 'City', fieldType: 'TEXT', required: false, sortOrder: 4, width: 'half', placeholder: 'City name' },
],
},
{
name: 'project',
title: 'Project Details',
description: 'Tell us about your ocean protection project',
sortOrder: 2,
isOptional: false,
fields: [
{ name: 'projectName', label: 'Project Name', fieldType: 'TEXT', required: true, sortOrder: 0, width: 'full', projectMapping: 'title', maxLength: 200, placeholder: 'Give your project a memorable name' },
{ name: 'teamName', label: 'Team / Company Name', fieldType: 'TEXT', required: false, sortOrder: 1, width: 'half', projectMapping: 'teamName', placeholder: 'Your team or company name' },
{ name: 'oceanIssue', label: 'Primary Ocean Issue', fieldType: 'SELECT', specialType: 'OCEAN_ISSUE', required: true, sortOrder: 2, width: 'half', projectMapping: 'oceanIssue', description: 'Select the primary ocean issue your project addresses' },
{ name: 'description', label: 'Project Description', fieldType: 'TEXTAREA', required: true, sortOrder: 3, width: 'full', projectMapping: 'description', minLength: 50, maxLength: 2000, placeholder: 'Describe your project, its goals, and how it will help protect the ocean...', description: 'Provide a clear description of your project (50-2000 characters)' },
{ name: 'websiteUrl', label: 'Website URL', fieldType: 'URL', required: false, sortOrder: 4, width: 'half', projectMapping: 'websiteUrl', placeholder: 'https://yourproject.com' },
],
},
{
name: 'team',
title: 'Team Members',
description: 'Add your team members (they will receive email invitations)',
sortOrder: 3,
isOptional: true,
fields: [
{ name: 'teamMembers', label: 'Team Members', fieldType: 'TEXT', specialType: 'TEAM_MEMBERS', required: false, sortOrder: 0, width: 'full', description: 'Add up to 5 team members. They will receive an invitation email to join your application.' },
],
},
{
name: 'additional',
title: 'Additional Details',
description: 'A few more questions about your project',
sortOrder: 4,
isOptional: false,
fields: [
{ name: 'institution', label: 'University / School', fieldType: 'TEXT', required: false, sortOrder: 0, width: 'half', projectMapping: 'institution', placeholder: 'Name of your institution', conditionJson: { field: 'competitionCategory', operator: 'equals', value: 'BUSINESS_CONCEPT' } },
{ name: 'startupCreatedDate', label: 'Startup Founded Date', fieldType: 'DATE', required: false, sortOrder: 1, width: 'half', description: 'When was your company founded?', conditionJson: { field: 'competitionCategory', operator: 'equals', value: 'STARTUP' } },
{ name: 'wantsMentorship', label: 'I am interested in receiving mentorship', fieldType: 'CHECKBOX', required: false, sortOrder: 2, width: 'full', projectMapping: 'wantsMentorship', description: 'Check this box if you would like to be paired with an expert mentor' },
{ name: 'referralSource', label: 'How did you hear about MOPC?', fieldType: 'SELECT', required: false, sortOrder: 3, width: 'half', optionsJson: [
{ value: 'social_media', label: 'Social Media' },
{ value: 'search_engine', label: 'Search Engine' },
{ value: 'word_of_mouth', label: 'Word of Mouth' },
{ value: 'university', label: 'University / School' },
{ value: 'partner', label: 'Partner Organization' },
{ value: 'media', label: 'News / Media' },
{ value: 'event', label: 'Event / Conference' },
{ value: 'other', label: 'Other' },
]},
],
},
{
name: 'review',
title: 'Review & Submit',
description: 'Review your application and accept the terms',
sortOrder: 5,
isOptional: false,
fields: [
{ name: 'instructions', label: 'Review Instructions', fieldType: 'INSTRUCTIONS', required: false, sortOrder: 0, width: 'full', description: 'Please review all the information you have provided. Once submitted, you will not be able to make changes.' },
{ name: 'gdprConsent', label: 'I consent to the processing of my personal data in accordance with the GDPR and the MOPC Privacy Policy', fieldType: 'CHECKBOX', specialType: 'GDPR_CONSENT', required: true, sortOrder: 1, width: 'full' },
{ name: 'termsAccepted', label: 'I have read and accept the Terms and Conditions of the Monaco Ocean Protection Challenge', fieldType: 'CHECKBOX', required: true, sortOrder: 2, width: 'full' },
],
},
]
async function main() {
console.log('Seeding MOPC onboarding form...')
// Check if form already exists
const existingForm = await prisma.applicationForm.findUnique({
where: { publicSlug: MOPC_FORM_CONFIG.publicSlug },
})
if (existingForm) {
console.log('Form with slug "mopc-2026" already exists. Updating...')
// Delete existing steps and fields to recreate them
await prisma.applicationFormField.deleteMany({ where: { formId: existingForm.id } })
await prisma.onboardingStep.deleteMany({ where: { formId: existingForm.id } })
// Update the form
await prisma.applicationForm.update({
where: { id: existingForm.id },
data: {
name: MOPC_FORM_CONFIG.name,
description: MOPC_FORM_CONFIG.description,
status: MOPC_FORM_CONFIG.status,
isPublic: MOPC_FORM_CONFIG.isPublic,
sendConfirmationEmail: MOPC_FORM_CONFIG.sendConfirmationEmail,
sendTeamInviteEmails: MOPC_FORM_CONFIG.sendTeamInviteEmails,
confirmationEmailSubject: MOPC_FORM_CONFIG.confirmationEmailSubject,
confirmationEmailBody: MOPC_FORM_CONFIG.confirmationEmailBody,
confirmationMessage: MOPC_FORM_CONFIG.confirmationMessage,
},
})
// Create steps and fields
for (const stepData of STEPS) {
const step = await prisma.onboardingStep.create({
data: {
formId: existingForm.id,
name: stepData.name,
title: stepData.title,
description: stepData.description,
sortOrder: stepData.sortOrder,
isOptional: stepData.isOptional,
},
})
for (const field of stepData.fields) {
await prisma.applicationFormField.create({
data: {
formId: existingForm.id,
stepId: step.id,
name: field.name,
label: field.label,
fieldType: field.fieldType,
specialType: field.specialType || null,
required: field.required,
sortOrder: field.sortOrder,
width: field.width,
description: field.description || null,
placeholder: field.placeholder || null,
projectMapping: field.projectMapping || null,
minLength: field.minLength || null,
maxLength: field.maxLength || null,
optionsJson: field.optionsJson || undefined,
conditionJson: field.conditionJson || undefined,
},
})
}
console.log(` - Created step: ${stepData.title} (${stepData.fields.length} fields)`)
}
console.log(`\nForm updated: ${existingForm.id}`)
return
}
// Create new form
const form = await prisma.applicationForm.create({
data: {
name: MOPC_FORM_CONFIG.name,
description: MOPC_FORM_CONFIG.description,
publicSlug: MOPC_FORM_CONFIG.publicSlug,
status: MOPC_FORM_CONFIG.status,
isPublic: MOPC_FORM_CONFIG.isPublic,
sendConfirmationEmail: MOPC_FORM_CONFIG.sendConfirmationEmail,
sendTeamInviteEmails: MOPC_FORM_CONFIG.sendTeamInviteEmails,
confirmationEmailSubject: MOPC_FORM_CONFIG.confirmationEmailSubject,
confirmationEmailBody: MOPC_FORM_CONFIG.confirmationEmailBody,
confirmationMessage: MOPC_FORM_CONFIG.confirmationMessage,
},
})
console.log(`Created form: ${form.id}`)
// Create steps and fields
for (const stepData of STEPS) {
const step = await prisma.onboardingStep.create({
data: {
formId: form.id,
name: stepData.name,
title: stepData.title,
description: stepData.description,
sortOrder: stepData.sortOrder,
isOptional: stepData.isOptional,
},
})
for (const field of stepData.fields) {
await prisma.applicationFormField.create({
data: {
formId: form.id,
stepId: step.id,
name: field.name,
label: field.label,
fieldType: field.fieldType,
specialType: field.specialType || null,
required: field.required,
sortOrder: field.sortOrder,
width: field.width,
description: field.description || null,
placeholder: field.placeholder || null,
projectMapping: field.projectMapping || null,
minLength: field.minLength || null,
maxLength: field.maxLength || null,
optionsJson: field.optionsJson || undefined,
conditionJson: field.conditionJson || undefined,
},
})
}
console.log(` - Created step: ${stepData.title} (${stepData.fields.length} fields)`)
}
console.log(`\nMOPC form seeded successfully!`)
console.log(`Form ID: ${form.id}`)
console.log(`Public URL: /apply/${form.publicSlug}`)
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})