Files
backend/prisma/seed.ts

1279 lines
43 KiB
TypeScript
Raw Normal View History

import { PrismaClient, UserRole, UserStatus, AuthProvider, FieldType } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
import { Pool } from 'pg';
import * as argon2 from 'argon2';
async function main() {
const connectionString = process.env.DATABASE_URL;
const pool = new Pool({ connectionString });
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });
console.log('🌱 Starting database seeding...\n');
// =============================================
// Seed Agent Types
// =============================================
console.log('📋 Seeding Agent Types...');
const agentTypes = [
{
name: 'Professional',
description: 'Licensed real estate professionals',
icon: 'briefcase',
sortOrder: 1,
},
{
name: 'Lender',
description: 'Mortgage and loan specialists',
icon: 'bank',
sortOrder: 2,
},
];
for (const agentType of agentTypes) {
const existing = await prisma.agentType.findUnique({
where: { name: agentType.name },
});
if (existing) {
console.log(` ⚠️ Agent type "${agentType.name}" already exists`);
} else {
await prisma.agentType.create({
data: agentType,
});
console.log(` ✅ Created agent type: ${agentType.name}`);
}
}
console.log('');
// =============================================
// Seed Default Profile Sections & Fields
// =============================================
console.log('📋 Seeding Default Profile Sections...');
// Define default system sections with their fields
const defaultSections = [
{
name: 'Basic Information',
slug: 'basic-information',
description: 'Basic profile information',
icon: 'user',
sortOrder: 1,
isActive: true,
isGlobal: true,
isSystem: true,
fields: [
{
name: 'First Name',
slug: 'first_name',
fieldType: FieldType.TEXT,
description: 'Your first name',
placeholder: 'Enter your first name',
sortOrder: 1,
isActive: true,
isRequired: true,
isSearchableOnly: false,
},
{
name: 'Last Name',
slug: 'last_name',
fieldType: FieldType.TEXT,
description: 'Your last name',
placeholder: 'Enter your last name',
sortOrder: 2,
isActive: true,
isRequired: true,
isSearchableOnly: false,
},
2026-01-24 21:39:14 +05:30
{
name: 'Description',
slug: 'description',
fieldType: FieldType.TEXTAREA,
description: 'Tell us about yourself and your professional background',
placeholder: 'Enter a brief description about yourself, your experience, and what makes you unique...',
sortOrder: 3,
isActive: true,
isRequired: false,
isSearchableOnly: false,
validation: {
maxLength: 2000,
},
uiConfig: {
rows: 6,
showCharCount: true,
},
},
],
},
{
name: 'Contact Information',
slug: 'contact-information',
description: 'Contact details',
icon: 'phone',
sortOrder: 2,
isActive: true,
isGlobal: true,
isSystem: true,
fields: [
{
name: 'Email Address',
slug: 'email_address',
fieldType: FieldType.TEXT,
description: 'Your contact email address',
placeholder: 'Enter your email address',
sortOrder: 1,
isActive: true,
isRequired: true,
isSearchableOnly: false,
},
{
name: 'Phone Number',
slug: 'phone_number',
fieldType: FieldType.TEXT,
description: 'Your contact phone number',
placeholder: 'Enter your phone number',
sortOrder: 2,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
],
},
{
name: 'Location',
slug: 'location',
description: 'Service areas and location information',
icon: 'map-pin',
sortOrder: 3,
isActive: true,
isGlobal: true,
isSystem: true,
fields: [
{
name: 'State',
slug: 'state',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'States where you provide services',
placeholder: 'Select states',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Alabama', value: 'AL' },
{ label: 'Alaska', value: 'AK' },
{ label: 'Arizona', value: 'AZ' },
{ label: 'Arkansas', value: 'AR' },
{ label: 'California', value: 'CA' },
{ label: 'Colorado', value: 'CO' },
{ label: 'Connecticut', value: 'CT' },
{ label: 'Delaware', value: 'DE' },
{ label: 'Florida', value: 'FL' },
{ label: 'Georgia', value: 'GA' },
{ label: 'Hawaii', value: 'HI' },
{ label: 'Idaho', value: 'ID' },
{ label: 'Illinois', value: 'IL' },
{ label: 'Indiana', value: 'IN' },
{ label: 'Iowa', value: 'IA' },
{ label: 'Kansas', value: 'KS' },
{ label: 'Kentucky', value: 'KY' },
{ label: 'Louisiana', value: 'LA' },
{ label: 'Maine', value: 'ME' },
{ label: 'Maryland', value: 'MD' },
{ label: 'Massachusetts', value: 'MA' },
{ label: 'Michigan', value: 'MI' },
{ label: 'Minnesota', value: 'MN' },
{ label: 'Mississippi', value: 'MS' },
{ label: 'Missouri', value: 'MO' },
{ label: 'Montana', value: 'MT' },
{ label: 'Nebraska', value: 'NE' },
{ label: 'Nevada', value: 'NV' },
{ label: 'New Hampshire', value: 'NH' },
{ label: 'New Jersey', value: 'NJ' },
{ label: 'New Mexico', value: 'NM' },
{ label: 'New York', value: 'NY' },
{ label: 'North Carolina', value: 'NC' },
{ label: 'North Dakota', value: 'ND' },
{ label: 'Ohio', value: 'OH' },
{ label: 'Oklahoma', value: 'OK' },
{ label: 'Oregon', value: 'OR' },
{ label: 'Pennsylvania', value: 'PA' },
{ label: 'Rhode Island', value: 'RI' },
{ label: 'South Carolina', value: 'SC' },
{ label: 'South Dakota', value: 'SD' },
{ label: 'Tennessee', value: 'TN' },
{ label: 'Texas', value: 'TX' },
{ label: 'Utah', value: 'UT' },
{ label: 'Vermont', value: 'VT' },
{ label: 'Virginia', value: 'VA' },
{ label: 'Washington', value: 'WA' },
{ label: 'West Virginia', value: 'WV' },
{ label: 'Wisconsin', value: 'WI' },
{ label: 'Wyoming', value: 'WY' },
],
},
{
name: 'City',
slug: 'city',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Cities where you provide services (max 5)',
placeholder: 'Enter cities',
sortOrder: 2,
isActive: true,
isRequired: false,
isSearchableOnly: false,
validation: {
maxSelections: 5,
},
uiConfig: {
allowCustomOptions: true,
maxSelections: 5,
},
},
],
},
{
name: 'Upload Documents',
slug: 'upload-documents',
description: 'Upload licensing credentials and verification documents',
icon: 'document',
sortOrder: 4,
isActive: true,
isGlobal: true,
isSystem: true,
fields: [
{
name: 'Documents',
slug: 'documents',
fieldType: FieldType.FILE,
description: 'Upload documentation or verification for licensing credentials',
placeholder: 'Drag and drop files here or click to upload',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
validation: {
allowedTypes: ['pdf', 'doc', 'docx', 'jpg', 'jpeg', 'png'],
maxFileSize: 10485760, // 10MB
maxFiles: 10,
},
uiConfig: {
acceptedFormats: '.pdf, .doc, .docx, .jpg, .png',
showPreview: true,
multiple: true,
},
},
],
},
{
name: 'Certification',
slug: 'certification',
description: 'Professional certifications and credentials',
icon: 'star',
sortOrder: 5,
isActive: true,
isGlobal: true,
isSystem: true,
fields: [
{
name: 'Certification Name',
slug: 'certification_name',
fieldType: FieldType.TEXT,
description: 'Name of your professional certification',
placeholder: 'Enter certification name',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
{
name: 'Issuing Organization',
slug: 'issuing_organization',
fieldType: FieldType.TEXT,
description: 'Organization that issued the certification',
placeholder: 'Enter issuing organization',
sortOrder: 2,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
{
name: 'Issue Date',
slug: 'issue_date',
fieldType: FieldType.DATE,
description: 'Date the certification was issued',
placeholder: 'Select issue date',
sortOrder: 3,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
{
name: 'Expiration Date',
slug: 'expiration_date',
fieldType: FieldType.DATE,
description: 'Date the certification expires (if applicable)',
placeholder: 'Select expiration date',
sortOrder: 4,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
{
name: 'Certification Number',
slug: 'certification_number',
fieldType: FieldType.TEXT,
description: 'Your certification or license number',
placeholder: 'Enter certification number',
sortOrder: 5,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
],
},
{
name: 'Demographic',
slug: 'demographic',
description: 'Demographic and contact information',
icon: 'user',
sortOrder: 6,
isActive: true,
isGlobal: true,
isSystem: true,
fields: [
{
name: 'Name',
slug: 'demographic_name',
fieldType: FieldType.TEXT,
description: 'Your full name',
placeholder: 'Enter your name',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
{
name: 'Office Name',
slug: 'office_name',
fieldType: FieldType.TEXT,
description: 'Name of your office or brokerage',
placeholder: 'Enter office name',
sortOrder: 2,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
{
name: 'Registered / Licensed',
slug: 'registered_licensed',
fieldType: FieldType.RADIO,
description: 'Are you registered or licensed?',
placeholder: 'Select status',
sortOrder: 3,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Registered', value: 'registered' },
{ label: 'Licensed', value: 'licensed' },
{ label: 'Both', value: 'both' },
],
},
{
name: 'Office #',
slug: 'office_number',
fieldType: FieldType.TEXT,
description: 'Your office phone number',
placeholder: 'Enter office number',
sortOrder: 4,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
{
name: 'Cell #',
slug: 'cell_number',
fieldType: FieldType.TEXT,
description: 'Your cell phone number',
placeholder: 'Enter cell number',
sortOrder: 5,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
{
name: 'Availability',
slug: 'availability',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'When are you available?',
placeholder: 'Select availability',
sortOrder: 6,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'M-F 9-5', value: 'mf_9_5' },
{ label: 'Evenings', value: 'evenings' },
{ label: 'Weekends', value: 'weekends' },
],
},
{
name: 'Age',
slug: 'age',
fieldType: FieldType.RANGE,
description: 'Your age (used for search matching only, not displayed on profile)',
placeholder: 'Select age range',
sortOrder: 7,
isActive: true,
isRequired: false,
isSearchableOnly: true,
rangeConfig: {
min: 18,
max: 100,
step: 1,
},
},
{
name: 'Gender',
slug: 'gender',
fieldType: FieldType.TEXT,
description: 'Your gender (used for search matching only, not displayed on profile)',
placeholder: 'Enter gender',
sortOrder: 8,
isActive: true,
isRequired: false,
isSearchableOnly: true,
},
{
name: 'Race',
slug: 'race',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Your race (used for search matching only, not displayed on profile)',
placeholder: 'Select race',
sortOrder: 9,
isActive: true,
isRequired: false,
isSearchableOnly: true,
options: [
{ label: 'American Indian or Alaska Native', value: 'american_indian_alaska_native' },
{ label: 'Asian', value: 'asian' },
{ label: 'Black or African American', value: 'black_african_american' },
{ label: 'Native Hawaiian or Other Pacific Islander', value: 'native_hawaiian_pacific_islander' },
{ label: 'White', value: 'white' },
{ label: 'Two or More Races', value: 'two_or_more' },
{ label: 'Prefer not to say', value: 'prefer_not_to_say' },
],
},
{
name: 'Ethnicity',
slug: 'ethnicity',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Your ethnicity (used for search matching only, not displayed on profile)',
placeholder: 'Select ethnicity',
sortOrder: 10,
isActive: true,
isRequired: false,
isSearchableOnly: true,
options: [
{ label: 'Hispanic or Latino', value: 'hispanic_latino' },
{ label: 'Not Hispanic or Latino', value: 'not_hispanic_latino' },
{ label: 'Prefer not to say', value: 'prefer_not_to_say' },
],
},
{
name: 'Language',
slug: 'language',
fieldType: FieldType.TAG_INPUT,
description: 'Languages you speak',
placeholder: 'Add languages',
sortOrder: 11,
isActive: true,
isRequired: false,
isSearchableOnly: false,
uiConfig: {
allowCustomOptions: true,
suggestions: ['English', 'Spanish', 'Chinese', 'French', 'Vietnamese', 'Korean', 'Tagalog', 'Arabic', 'Russian', 'Portuguese'],
},
},
{
name: 'LGBTQ+',
slug: 'lgbtq',
fieldType: FieldType.RADIO,
description: 'LGBTQ+ identification (used for search matching only, not displayed on profile)',
placeholder: 'Select option',
sortOrder: 12,
isActive: true,
isRequired: false,
isSearchableOnly: true,
options: [
{ label: 'Yes', value: 'yes' },
{ label: 'No', value: 'no' },
{ label: 'Prefer not to say', value: 'prefer_not_to_say' },
],
},
{
name: 'Highest Education Level',
slug: 'highest_education_level',
fieldType: FieldType.RADIO,
description: 'Your highest level of education',
placeholder: 'Select education level',
sortOrder: 13,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'High School/GED', value: 'high_school_ged' },
{ label: 'Some College', value: 'some_college' },
{ label: 'Associates', value: 'associates' },
{ label: 'Bachelors', value: 'bachelors' },
{ label: 'Masters', value: 'masters' },
{ label: 'Doctoral', value: 'doctoral' },
],
},
{
name: 'Veteran Status',
slug: 'veteran_status',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Your military/veteran status',
placeholder: 'Select veteran status',
sortOrder: 14,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Active', value: 'active' },
{ label: 'Reserve', value: 'reserve' },
{ label: 'Guard', value: 'guard' },
{ label: 'Veteran', value: 'veteran' },
],
},
{
name: 'Military Branch',
slug: 'military_branch',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Military branch(es) you served in',
placeholder: 'Select military branch',
sortOrder: 15,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Army', value: 'army' },
{ label: 'Navy', value: 'navy' },
{ label: 'Marines', value: 'marines' },
{ label: 'Air Force', value: 'air_force' },
{ label: 'Space Force', value: 'space_force' },
{ label: 'Coast Guard', value: 'coast_guard' },
],
},
],
},
{
name: 'Expertise',
slug: 'expertise',
description: 'Areas of expertise and specialization',
icon: 'star',
sortOrder: 7,
isActive: true,
isGlobal: true,
isSystem: true,
fields: [
{
name: 'About Me',
slug: 'about_me_expertise',
fieldType: FieldType.TAG_INPUT,
description: 'Add your areas of expertise',
placeholder: 'Add Expertise',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
uiConfig: {
allowCustomOptions: true,
},
},
],
},
];
// Agent-type-specific sections (NOT global)
const agentTypeSections = [
{
agentTypeName: 'Professional',
section: {
name: 'Experience',
slug: 'professional-experience',
description: 'Professional experience and track record for real estate agents',
icon: 'star',
sortOrder: 4,
isActive: true,
isGlobal: false,
isSystem: false,
fields: [
{
name: 'Years in Business',
slug: 'years_in_business',
fieldType: FieldType.RADIO,
description: 'How many years have you been in real estate?',
placeholder: 'Select years',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Less than 2 years', value: '<2' },
{ label: '2+ years', value: '2+' },
{ label: '5+ years', value: '5+' },
{ label: '10+ years', value: '10+' },
],
},
{
name: 'Years Operated in Area',
slug: 'years_in_area',
fieldType: FieldType.RADIO,
description: 'How many years have you operated in your service area?',
placeholder: 'Select years',
sortOrder: 2,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Less than 2 years', value: '<2' },
{ label: '2+ years', value: '2+' },
{ label: '5+ years', value: '5+' },
{ label: '10+ years', value: '10+' },
],
},
{
name: 'Number of Contracts Completed',
slug: 'contracts_completed',
fieldType: FieldType.RADIO,
description: 'Number of contracts completed in the previous year',
placeholder: 'Select range',
sortOrder: 3,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Less than 3', value: '<3' },
{ label: '3-10', value: '3-10' },
{ label: '10-20', value: '10-20' },
{ label: '20-50', value: '20-50' },
{ label: '50+', value: '50+' },
],
},
],
},
},
{
agentTypeName: 'Professional',
section: {
name: 'Specialization',
slug: 'professional-specialization',
description: 'Areas of specialization for real estate agents',
icon: 'briefcase',
sortOrder: 5,
isActive: true,
isGlobal: false,
isSystem: false,
fields: [
{
name: 'Client Specialization',
slug: 'client_specialization',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Types of clients you specialize in',
placeholder: 'Select client types',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Buyers', value: 'buyers' },
{ label: 'Sellers', value: 'sellers' },
{ label: 'First Time Buyer', value: 'first_time_buyer' },
{ label: 'First Time Seller', value: 'first_time_seller' },
{ label: 'Divorce', value: 'divorce' },
{ label: 'Estate', value: 'estate' },
],
},
{
name: 'Price Point',
slug: 'price_point',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Price ranges you typically work with',
placeholder: 'Select price ranges',
sortOrder: 2,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Under $100K', value: '<100K' },
{ label: '$100K - $300K', value: '100-300K' },
{ label: '$300K - $500K', value: '300-500K' },
{ label: '$500K - $700K', value: '500-700K' },
{ label: '$700K - $1M', value: '700K-1M' },
{ label: '$1M+', value: '1M+' },
],
},
{
name: 'Property Type',
slug: 'property_type',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Types of properties you specialize in',
placeholder: 'Select property types',
sortOrder: 3,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'SFR (Single Family Residence)', value: 'sfr' },
{ label: 'Townhome', value: 'townhome' },
{ label: 'Condo', value: 'condo' },
{ label: 'Manufactured', value: 'manufactured' },
{ label: 'Modular', value: 'modular' },
{ label: 'Urban', value: 'urban' },
{ label: 'Rural', value: 'rural' },
{ label: 'Luxury', value: 'luxury' },
{ label: 'Retirement', value: 'retirement' },
{ label: 'Agriculture', value: 'agriculture' },
{ label: 'Commercial', value: 'commercial' },
{ label: 'Investment / Income Producing', value: 'investment' },
{ label: 'Historical', value: 'historical' },
{ label: 'Mountain Property', value: 'mountain' },
{ label: 'Undeveloped Land', value: 'undeveloped_land' },
{ label: 'Empty Lots', value: 'empty_lots' },
{ label: 'Barndaminium', value: 'barndaminium' },
{ label: 'Unique Properties', value: 'unique' },
{ label: 'Apartment / Multifamily', value: 'apartment_multifamily' },
{ label: 'Duplex / Tri-plex / 4-plex', value: 'multiplex' },
{ label: 'New Build', value: 'new_build' },
],
},
{
name: 'Loan Type',
slug: 'loan_type',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Loan types you are familiar with',
placeholder: 'Select loan types',
sortOrder: 4,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Conventional', value: 'conventional' },
{ label: 'FHA', value: 'fha' },
{ label: 'VA', value: 'va' },
{ label: 'USDA', value: 'usda' },
{ label: 'Downpayment Assistance Programs', value: 'downpayment_assistance' },
],
},
{
name: 'Special Interests and Hobbies',
slug: 'special_interests',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Special property features you have expertise in',
placeholder: 'Select special interests',
sortOrder: 5,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Workshop / Trade / Craftspace', value: 'workshop' },
{ label: 'Boating', value: 'boating' },
{ label: 'Automotive', value: 'automotive' },
{ label: 'Aviation', value: 'aviation' },
{ label: 'Home Theater', value: 'home_theater' },
{ label: 'Horses', value: 'horses' },
{ label: 'RV', value: 'rv' },
{ label: 'Smart Home Features', value: 'smart_home' },
{ label: 'Gardening / Landscape', value: 'gardening' },
{ label: 'Hobby Farm', value: 'hobby_farm' },
],
},
],
},
},
2026-01-24 21:36:39 +05:30
// Professional - Areas in Expertise & Years Section
{
agentTypeName: 'Professional',
section: {
name: 'Areas in Expertise & Years',
slug: 'professional-expertise-years',
description: 'Your areas of expertise with years of experience',
icon: 'award',
sortOrder: 6,
isActive: true,
isGlobal: false,
isSystem: false,
fields: [
{
name: 'Expertise Areas',
slug: 'expertise_areas',
fieldType: FieldType.TAG_INPUT,
description: 'Add your areas of expertise with years of experience',
placeholder: '',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
],
},
},
// Professional - Licensing & Areas Section
{
agentTypeName: 'Professional',
section: {
name: 'Licensing & Areas',
slug: 'professional-licensing-areas',
description: 'Areas where you are licensed to practice',
icon: 'file-text',
sortOrder: 7,
isActive: true,
isGlobal: false,
isSystem: false,
fields: [
{
name: 'Licensed Areas',
slug: 'licensed_areas',
fieldType: FieldType.TAG_INPUT,
description: 'Enter areas where you are licensed to practice',
placeholder: 'Type area name, press Enter to add',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
],
},
},
{
agentTypeName: 'Lender',
section: {
name: 'Experience',
slug: 'lender-experience',
description: 'Professional experience and track record for lenders',
icon: 'star',
sortOrder: 4,
isActive: true,
isGlobal: false,
isSystem: false,
fields: [
{
name: 'Years in Business',
slug: 'years_in_business',
fieldType: FieldType.RADIO,
description: 'How many years have you been in lending?',
placeholder: 'Select years',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Less than 2 years', value: '<2' },
{ label: '2+ years', value: '2+' },
{ label: '5+ years', value: '5+' },
{ label: '10+ years', value: '10+' },
],
},
{
name: 'Years Operated in Area',
slug: 'years_in_area',
fieldType: FieldType.RADIO,
description: 'How many years have you operated in your service area?',
placeholder: 'Select years',
sortOrder: 2,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Less than 2 years', value: '<2' },
{ label: '2+ years', value: '2+' },
{ label: '5+ years', value: '5+' },
{ label: '10+ years', value: '10+' },
],
},
{
name: 'Annual Loans',
slug: 'annual_loans',
fieldType: FieldType.RADIO,
description: 'Total value of loans processed annually',
placeholder: 'Select range',
sortOrder: 3,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: '0-1M', value: '0-1M' },
{ label: '1-5M', value: '1-5M' },
{ label: '5-10M', value: '5-10M' },
{ label: '10-30M', value: '10-30M' },
{ label: '30M+', value: '30M+' },
],
},
],
},
},
{
agentTypeName: 'Lender',
section: {
name: 'Specialization',
slug: 'lender-specialization',
description: 'Areas of specialization for lenders',
icon: 'briefcase',
sortOrder: 5,
isActive: true,
isGlobal: false,
isSystem: false,
fields: [
{
name: 'Client Specialization',
slug: 'client_specialization',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Types of clients you specialize in',
placeholder: 'Select client types',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'First Time Buyer', value: 'first_time_buyer' },
],
},
{
name: 'Property Type',
slug: 'property_type',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Types of properties you can finance',
placeholder: 'Select property types',
sortOrder: 2,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'SFR (Single Family Residence)', value: 'sfr' },
{ label: 'Townhome', value: 'townhome' },
{ label: 'Condo', value: 'condo' },
{ label: 'Manufactured', value: 'manufactured' },
{ label: 'Modular', value: 'modular' },
{ label: 'Urban', value: 'urban' },
{ label: 'Rural', value: 'rural' },
{ label: 'Luxury', value: 'luxury' },
{ label: 'Retirement', value: 'retirement' },
{ label: 'Agriculture', value: 'agriculture' },
{ label: 'Commercial', value: 'commercial' },
{ label: 'Investment / Income Producing', value: 'investment' },
{ label: 'Undeveloped Land', value: 'undeveloped_land' },
{ label: 'Construction / Construction-to-Perm', value: 'construction' },
{ label: 'Empty Lots', value: 'empty_lots' },
{ label: 'Barndaminium', value: 'barndaminium' },
{ label: 'Unique Properties', value: 'unique' },
{ label: 'Apartment / Multifamily', value: 'apartment_multifamily' },
{ label: 'Duplex / Tri-plex / 4-plex', value: 'multiplex' },
{ label: 'New Build', value: 'new_build' },
],
},
{
name: 'Loan Type',
slug: 'loan_type',
2026-01-24 21:36:39 +05:30
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Types of loans you offer',
placeholder: 'Select loan types',
sortOrder: 3,
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'Conventional', value: 'conventional' },
{ label: 'FHA', value: 'fha' },
{ label: 'VA', value: 'va' },
{ label: 'USDA', value: 'usda' },
{ label: 'Downpayment Assistance Programs', value: 'downpayment_assistance' },
{ label: 'Rehab Loan - FHA 203k', value: 'rehab_fha_203k' },
{ label: 'Rehab Loan - Conventional Home Style', value: 'rehab_conventional' },
{ label: 'Rehab Loan - VA Reno', value: 'rehab_va_reno' },
{ label: 'Debt Service Cover Ratio (DSCR)', value: 'dscr' },
{ label: 'HELOC', value: 'heloc' },
{ label: 'Standalone Second', value: 'standalone_second' },
{ label: 'Refinancing - FHA Streamline', value: 'refi_fha_streamline' },
{ label: 'Refinancing - VA IRRL', value: 'refi_va_irrl' },
{ label: 'Refinancing - Conventional', value: 'refi_conventional' },
{ label: 'Jumbo / Non-conforming', value: 'jumbo' },
{ label: 'Bank Statement', value: 'bank_statement' },
{ label: 'ITIN', value: 'itin' },
],
},
],
},
},
2026-01-24 21:36:39 +05:30
// Lender - Areas in Expertise & Years Section
{
agentTypeName: 'Lender',
section: {
name: 'Areas in Expertise & Years',
slug: 'lender-expertise-years',
description: 'Your areas of expertise with years of experience',
icon: 'award',
sortOrder: 6,
isActive: true,
isGlobal: false,
isSystem: false,
fields: [
{
name: 'Expertise Areas',
slug: 'expertise_areas',
fieldType: FieldType.TAG_INPUT,
description: 'Add your areas of expertise with years of experience',
placeholder: '',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
],
},
},
// Lender - Licensing & Areas Section
{
agentTypeName: 'Lender',
section: {
name: 'Licensing & Areas',
slug: 'lender-licensing-areas',
description: 'Areas where you are licensed to practice',
icon: 'file-text',
sortOrder: 7,
isActive: true,
isGlobal: false,
isSystem: false,
fields: [
{
name: 'Licensed Areas',
slug: 'licensed_areas',
fieldType: FieldType.TAG_INPUT,
description: 'Enter areas where you are licensed to practice',
placeholder: 'Type area name, press Enter to add',
sortOrder: 1,
isActive: true,
isRequired: false,
isSearchableOnly: false,
},
],
},
},
];
for (const sectionData of defaultSections) {
const { fields, ...sectionInfo } = sectionData;
// Check if section already exists
const existingSection = await prisma.profileSection.findUnique({
where: { slug: sectionInfo.slug },
});
if (existingSection) {
console.log(` ⚠️ Section "${sectionInfo.name}" already exists`);
// Still check and create missing fields
for (const fieldData of fields) {
const existingField = await prisma.profileField.findFirst({
where: {
sectionId: existingSection.id,
slug: fieldData.slug,
},
});
if (!existingField) {
await prisma.profileField.create({
data: {
...fieldData,
sectionId: existingSection.id,
},
});
console.log(` ✅ Created field: ${fieldData.name}`);
}
}
} else {
// Create section with fields
const section = await prisma.profileSection.create({
data: {
...sectionInfo,
fields: {
create: fields,
},
},
});
console.log(` ✅ Created section: ${sectionInfo.name} with ${fields.length} fields`);
}
}
console.log('');
// =============================================
// Seed Agent-Type-Specific Sections
// =============================================
console.log('📋 Seeding Agent-Type-Specific Sections...');
// First, delete old global Experience section if exists
const oldExperienceSection = await prisma.profileSection.findUnique({
where: { slug: 'experience' },
});
if (oldExperienceSection) {
await prisma.profileSection.delete({
where: { slug: 'experience' },
});
console.log(' 🗑️ Deleted old global Experience section');
}
for (const { agentTypeName, section: sectionData } of agentTypeSections) {
const { fields, ...sectionInfo } = sectionData;
// Find the agent type
const agentType = await prisma.agentType.findUnique({
where: { name: agentTypeName },
});
if (!agentType) {
console.log(` ⚠️ Agent type "${agentTypeName}" not found, skipping section`);
continue;
}
// Check if section already exists
let section = await prisma.profileSection.findUnique({
where: { slug: sectionInfo.slug },
});
if (section) {
console.log(` ⚠️ Section "${sectionInfo.name}" for ${agentTypeName} already exists`);
// Still check and create missing fields
for (const fieldData of fields) {
const existingField = await prisma.profileField.findFirst({
where: {
sectionId: section.id,
slug: fieldData.slug,
},
});
if (!existingField) {
await prisma.profileField.create({
data: {
...fieldData,
sectionId: section.id,
},
});
console.log(` ✅ Created field: ${fieldData.name}`);
}
}
} else {
// Create section with fields
section = await prisma.profileSection.create({
data: {
...sectionInfo,
fields: {
create: fields,
},
},
});
console.log(` ✅ Created section: ${sectionInfo.name} for ${agentTypeName} with ${fields.length} fields`);
}
// Create AgentTypeSection assignment if not exists
const existingAssignment = await prisma.agentTypeSection.findUnique({
where: {
agentTypeId_sectionId: {
agentTypeId: agentType.id,
sectionId: section.id,
},
},
});
if (!existingAssignment) {
await prisma.agentTypeSection.create({
data: {
agentTypeId: agentType.id,
sectionId: section.id,
sortOrder: sectionInfo.sortOrder,
isRequired: false,
},
});
console.log(` ✅ Assigned section to ${agentTypeName}`);
}
}
console.log('');
// =============================================
// Seed Admin User
// =============================================
console.log('👤 Seeding Admin User...');
const adminEmail = process.env.ADMIN_EMAIL || 'admin@realestate.com';
const adminPassword = process.env.ADMIN_PASSWORD || 'Admin@123456';
// Hash password with Argon2 (more secure than bcrypt)
const hashedPassword = await argon2.hash(adminPassword);
// Check if admin already exists
const existingAdmin = await prisma.user.findUnique({
where: { email: adminEmail },
});
if (existingAdmin) {
console.log(` ⚠️ Admin user already exists: ${adminEmail}`);
} else {
// Create admin user
const admin = await prisma.user.create({
data: {
email: adminEmail,
password: hashedPassword,
role: UserRole.ADMIN,
status: UserStatus.ACTIVE,
emailVerified: true,
emailVerifiedAt: new Date(),
authProvider: AuthProvider.LOCAL,
},
});
console.log(' ✅ Admin user created successfully!');
console.log(' ───────────────────────────────');
console.log(` 📧 Email: ${adminEmail}`);
console.log(` 🔑 Password: ${adminPassword}`);
console.log(` 👤 Role: ${admin.role}`);
console.log(` 🆔 ID: ${admin.id}`);
console.log(' ───────────────────────────────\n');
}
// =============================================
// Summary
// =============================================
const userCount = await prisma.user.count();
const adminCount = await prisma.user.count({ where: { role: UserRole.ADMIN } });
const agentTypeCount = await prisma.agentType.count();
const sectionCount = await prisma.profileSection.count();
const fieldCount = await prisma.profileField.count();
const systemSectionCount = await prisma.profileSection.count({ where: { isSystem: true } });
const agentTypeSectionCount = await prisma.agentTypeSection.count();
console.log('📊 Database Summary:');
console.log(` Total Users: ${userCount}`);
console.log(` Admins: ${adminCount}`);
console.log(` Agent Types: ${agentTypeCount}`);
console.log(` Profile Sections: ${sectionCount} (${systemSectionCount} system)`);
console.log(` Profile Fields: ${fieldCount}`);
console.log(` Section Assignments: ${agentTypeSectionCount}`);
console.log('\n🌱 Seeding completed!\n');
await prisma.$disconnect();
await pool.end();
}
main()
.catch((e) => {
console.error('❌ Seeding failed:', e);
process.exit(1);
});