feat: add script and data to update state and city location options for CO, NE, ND, and SD

This commit is contained in:
pradeepkumar
2026-04-12 00:01:21 +05:30
parent 91336e5ead
commit b66b5c963b
3 changed files with 8128 additions and 167 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,17 @@
import { PrismaClient, UserRole, UserStatus, AuthProvider, FieldType } from '@prisma/client';
import {
PrismaClient,
UserRole,
UserStatus,
AuthProvider,
FieldType,
} from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
import { Pool } from 'pg';
import * as argon2 from 'argon2';
import {
US_STATES_CO_NE_ND_SD,
US_CITIES_CO_NE_ND_SD,
} from './data/us-cities-co-ne-nd-sd';
async function main() {
const connectionString = process.env.DATABASE_URL;
@@ -92,8 +102,10 @@ async function main() {
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...',
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,
@@ -162,58 +174,7 @@ async function main() {
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' },
],
options: US_STATES_CO_NE_ND_SD,
},
{
name: 'City',
@@ -232,48 +193,7 @@ async function main() {
allowCustomOptions: true,
maxSelections: 5,
},
options: [
{ label: 'New York', value: 'new_york' },
{ label: 'Los Angeles', value: 'los_angeles' },
{ label: 'Chicago', value: 'chicago' },
{ label: 'Houston', value: 'houston' },
{ label: 'Phoenix', value: 'phoenix' },
{ label: 'Philadelphia', value: 'philadelphia' },
{ label: 'San Antonio', value: 'san_antonio' },
{ label: 'San Diego', value: 'san_diego' },
{ label: 'Dallas', value: 'dallas' },
{ label: 'San Jose', value: 'san_jose' },
{ label: 'Austin', value: 'austin' },
{ label: 'Jacksonville', value: 'jacksonville' },
{ label: 'Fort Worth', value: 'fort_worth' },
{ label: 'Columbus', value: 'columbus' },
{ label: 'Charlotte', value: 'charlotte' },
{ label: 'San Francisco', value: 'san_francisco' },
{ label: 'Indianapolis', value: 'indianapolis' },
{ label: 'Seattle', value: 'seattle' },
{ label: 'Denver', value: 'denver' },
{ label: 'Washington DC', value: 'washington_dc' },
{ label: 'Boston', value: 'boston' },
{ label: 'Nashville', value: 'nashville' },
{ label: 'Detroit', value: 'detroit' },
{ label: 'Portland', value: 'portland' },
{ label: 'Las Vegas', value: 'las_vegas' },
{ label: 'Memphis', value: 'memphis' },
{ label: 'Louisville', value: 'louisville' },
{ label: 'Baltimore', value: 'baltimore' },
{ label: 'Milwaukee', value: 'milwaukee' },
{ label: 'Albuquerque', value: 'albuquerque' },
{ label: 'Tucson', value: 'tucson' },
{ label: 'Fresno', value: 'fresno' },
{ label: 'Sacramento', value: 'sacramento' },
{ label: 'Atlanta', value: 'atlanta' },
{ label: 'Miami', value: 'miami' },
{ label: 'Oakland', value: 'oakland' },
{ label: 'Minneapolis', value: 'minneapolis' },
{ label: 'Cleveland', value: 'cleveland' },
{ label: 'Raleigh', value: 'raleigh' },
{ label: 'Tampa', value: 'tampa' },
],
options: US_CITIES_CO_NE_ND_SD,
},
],
},
@@ -291,7 +211,8 @@ async function main() {
name: 'Documents',
slug: 'documents',
fieldType: FieldType.FILE,
description: 'Upload documentation or verification for licensing credentials',
description:
'Upload documentation or verification for licensing credentials',
placeholder: 'Drag and drop files here or click to upload',
sortOrder: 1,
isActive: true,
@@ -478,7 +399,8 @@ async function main() {
name: 'Age',
slug: 'age',
fieldType: FieldType.RANGE,
description: 'Your age (used for search matching only, not displayed on profile)',
description:
'Your age (used for search matching only, not displayed on profile)',
placeholder: 'Select age range',
sortOrder: 7,
isActive: true,
@@ -494,7 +416,8 @@ async function main() {
name: 'Gender',
slug: 'gender',
fieldType: FieldType.TEXT,
description: 'Your gender (used for search matching only, not displayed on profile)',
description:
'Your gender (used for search matching only, not displayed on profile)',
placeholder: 'Enter gender',
sortOrder: 8,
isActive: true,
@@ -505,17 +428,27 @@ async function main() {
name: 'Race',
slug: 'race',
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Your race (used for search matching only, not displayed on profile)',
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: '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: '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' },
@@ -525,7 +458,8 @@ async function main() {
name: 'Ethnicity',
slug: 'ethnicity',
fieldType: FieldType.CHECKBOX_GROUP,
description: 'Your ethnicity (used for search matching only, not displayed on profile)',
description:
'Your ethnicity (used for search matching only, not displayed on profile)',
placeholder: 'Select ethnicity',
sortOrder: 10,
isActive: true,
@@ -549,14 +483,26 @@ async function main() {
isSearchableOnly: false,
uiConfig: {
allowCustomOptions: true,
suggestions: ['English', 'Spanish', 'Chinese', 'French', 'Vietnamese', 'Korean', 'Tagalog', 'Arabic', 'Russian', 'Portuguese'],
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)',
description:
'LGBTQ+ identification (used for search matching only, not displayed on profile)',
placeholder: 'Select option',
sortOrder: 12,
isActive: true,
@@ -660,7 +606,8 @@ async function main() {
section: {
name: 'Experience',
slug: 'professional-experience',
description: 'Professional experience and track record for real estate agents',
description:
'Professional experience and track record for real estate agents',
icon: 'star',
sortOrder: 4,
isActive: true,
@@ -688,7 +635,8 @@ async function main() {
name: 'Years Operated in Area',
slug: 'years_in_area',
fieldType: FieldType.RADIO,
description: 'How many years have you operated in your service area?',
description:
'How many years have you operated in your service area?',
placeholder: 'Select years',
sortOrder: 2,
isActive: true,
@@ -801,7 +749,10 @@ async function main() {
{ label: 'Empty Lots', value: 'empty_lots' },
{ label: 'Barndaminium', value: 'barndaminium' },
{ label: 'Unique Properties', value: 'unique' },
{ label: 'Apartment / Multifamily', value: 'apartment_multifamily' },
{
label: 'Apartment / Multifamily',
value: 'apartment_multifamily',
},
{ label: 'Duplex / Tri-plex / 4-plex', value: 'multiplex' },
{ label: 'New Build', value: 'new_build' },
],
@@ -821,7 +772,10 @@ async function main() {
{ label: 'FHA', value: 'fha' },
{ label: 'VA', value: 'va' },
{ label: 'USDA', value: 'usda' },
{ label: 'Downpayment Assistance Programs', value: 'downpayment_assistance' },
{
label: 'Downpayment Assistance Programs',
value: 'downpayment_assistance',
},
],
},
{
@@ -937,7 +891,8 @@ async function main() {
name: 'Years Operated in Area',
slug: 'years_in_area',
fieldType: FieldType.RADIO,
description: 'How many years have you operated in your service area?',
description:
'How many years have you operated in your service area?',
placeholder: 'Select years',
sortOrder: 2,
isActive: true,
@@ -993,9 +948,7 @@ async function main() {
isActive: true,
isRequired: false,
isSearchableOnly: false,
options: [
{ label: 'First Time Buyer', value: 'first_time_buyer' },
],
options: [{ label: 'First Time Buyer', value: 'first_time_buyer' }],
},
{
name: 'Property Type',
@@ -1021,11 +974,17 @@ async function main() {
{ label: 'Commercial', value: 'commercial' },
{ label: 'Investment / Income Producing', value: 'investment' },
{ label: 'Undeveloped Land', value: 'undeveloped_land' },
{ label: 'Construction / Construction-to-Perm', value: 'construction' },
{
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: 'Apartment / Multifamily',
value: 'apartment_multifamily',
},
{ label: 'Duplex / Tri-plex / 4-plex', value: 'multiplex' },
{ label: 'New Build', value: 'new_build' },
],
@@ -1045,16 +1004,28 @@ async function main() {
{ label: 'FHA', value: 'fha' },
{ label: 'VA', value: 'va' },
{ label: 'USDA', value: 'usda' },
{ label: 'Downpayment Assistance Programs', value: 'downpayment_assistance' },
{
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 - 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 - FHA Streamline',
value: 'refi_fha_streamline',
},
{ label: 'Refinancing - VA IRRL', value: 'refi_va_irrl' },
{ label: 'Refinancing - Conventional', value: 'refi_conventional' },
{
label: 'Refinancing - Conventional',
value: 'refi_conventional',
},
{ label: 'Jumbo / Non-conforming', value: 'jumbo' },
{ label: 'Bank Statement', value: 'bank_statement' },
{ label: 'ITIN', value: 'itin' },
@@ -1131,12 +1102,17 @@ async function main() {
console.log(` ⚠️ Section "${sectionInfo.name}" already exists`);
// Update section properties if they've changed (like isRepeatable)
if (sectionInfo.isRepeatable !== undefined && existingSection.isRepeatable !== sectionInfo.isRepeatable) {
if (
sectionInfo.isRepeatable !== undefined &&
existingSection.isRepeatable !== sectionInfo.isRepeatable
) {
await prisma.profileSection.update({
where: { id: existingSection.id },
data: { isRepeatable: sectionInfo.isRepeatable },
});
console.log(` ✅ Updated isRepeatable to ${sectionInfo.isRepeatable}`);
console.log(
` ✅ Updated isRepeatable to ${sectionInfo.isRepeatable}`,
);
}
// Still check and create missing fields, or update existing fields with options
@@ -1157,11 +1133,25 @@ async function main() {
});
console.log(` ✅ Created field: ${fieldData.name}`);
} else {
// Update existing field with options if options are defined in seed but missing in DB
// Update existing field with options if options are defined in seed but missing in DB.
// Exception: `state` and `city` options come from an Excel source of truth and should
// always overwrite DB values so re-seeding syncs fresh data.
const updates: Record<string, unknown> = {};
const fd = fieldData as any; // Cast to any to access optional properties
const fd = fieldData as Partial<{
options: unknown;
validation: unknown;
uiConfig: unknown;
}>;
const alwaysOverwriteOptions =
fieldData.slug === 'state' || fieldData.slug === 'city';
if (fd.options && (!existingField.options || (Array.isArray(existingField.options) && existingField.options.length === 0))) {
if (
fd.options &&
(alwaysOverwriteOptions ||
!existingField.options ||
(Array.isArray(existingField.options) &&
existingField.options.length === 0))
) {
updates.options = fd.options;
}
if (fd.validation && !existingField.validation) {
@@ -1176,13 +1166,15 @@ async function main() {
where: { id: existingField.id },
data: updates,
});
console.log(` ✅ Updated field: ${fieldData.name} (${Object.keys(updates).join(', ')})`);
console.log(
` ✅ Updated field: ${fieldData.name} (${Object.keys(updates).join(', ')})`,
);
}
}
}
} else {
// Create section with fields
const section = await prisma.profileSection.create({
await prisma.profileSection.create({
data: {
...sectionInfo,
fields: {
@@ -1190,7 +1182,9 @@ async function main() {
},
},
});
console.log(` ✅ Created section: ${sectionInfo.name} with ${fields.length} fields`);
console.log(
` ✅ Created section: ${sectionInfo.name} with ${fields.length} fields`,
);
}
}
@@ -1221,7 +1215,9 @@ async function main() {
});
if (!agentType) {
console.log(` ⚠️ Agent type "${agentTypeName}" not found, skipping section`);
console.log(
` ⚠️ Agent type "${agentTypeName}" not found, skipping section`,
);
continue;
}
@@ -1231,7 +1227,9 @@ async function main() {
});
if (section) {
console.log(` ⚠️ Section "${sectionInfo.name}" for ${agentTypeName} already exists`);
console.log(
` ⚠️ Section "${sectionInfo.name}" for ${agentTypeName} already exists`,
);
// Still check and create missing fields, or update existing fields with options
for (const fieldData of fields) {
@@ -1253,9 +1251,18 @@ async function main() {
} else {
// Update existing field with options if options are defined in seed but missing in DB
const updates: Record<string, unknown> = {};
const fd = fieldData as any; // Cast to any to access optional properties
const fd = fieldData as Partial<{
options: unknown;
validation: unknown;
uiConfig: unknown;
}>;
if (fd.options && (!existingField.options || (Array.isArray(existingField.options) && existingField.options.length === 0))) {
if (
fd.options &&
(!existingField.options ||
(Array.isArray(existingField.options) &&
existingField.options.length === 0))
) {
updates.options = fd.options;
}
if (fd.validation && !existingField.validation) {
@@ -1270,7 +1277,9 @@ async function main() {
where: { id: existingField.id },
data: updates,
});
console.log(` ✅ Updated field: ${fieldData.name} (${Object.keys(updates).join(', ')})`);
console.log(
` ✅ Updated field: ${fieldData.name} (${Object.keys(updates).join(', ')})`,
);
}
}
}
@@ -1284,7 +1293,9 @@ async function main() {
},
},
});
console.log(` ✅ Created section: ${sectionInfo.name} for ${agentTypeName} with ${fields.length} fields`);
console.log(
` ✅ Created section: ${sectionInfo.name} for ${agentTypeName} with ${fields.length} fields`,
);
}
// Create AgentTypeSection assignment if not exists
@@ -1363,10 +1374,12 @@ async function main() {
pageSlug: 'landing',
sectionKey: 'hero',
content: {
headline: '"Discover verified, top-rated real estate professionals to guide your buying, selling, or investing journey."',
headline:
'"Discover verified, top-rated real estate professionals to guide your buying, selling, or investing journey."',
description: 'Discover verified, top-rated real estate professionals',
ctaButtonText: 'See All',
helperText: 'Connect with trusted local agents to explore homes, compare options, and make smarter decisions.',
helperText:
'Connect with trusted local agents to explore homes, compare options, and make smarter decisions.',
},
},
{
@@ -1374,27 +1387,32 @@ async function main() {
sectionKey: 'features',
content: {
title: 'Find Trusted Real Estate Professionals On Demand',
subtitle: 'Quickly connect with the right agents exactly when you need them.',
subtitle:
'Quickly connect with the right agents exactly when you need them.',
features: [
{
iconPath: '/assets/icons/hourglass-icon.svg',
title: 'Hire Quickly',
description: 'Connect with experienced local real estate agents who match your needs and preferences. Our simple process helps you get started quickly and move forward without delays or unnecessary complexity.',
description:
'Connect with experienced local real estate agents who match your needs and preferences. Our simple process helps you get started quickly and move forward without delays or unnecessary complexity.',
},
{
iconPath: '/assets/icons/verified-badge-icon.svg',
title: 'Verified Agents',
description: 'All agents are carefully identity-verified and background-checked to ensure trust and safety. You can confidently work with professionals who meet quality and reliability standards.',
description:
'All agents are carefully identity-verified and background-checked to ensure trust and safety. You can confidently work with professionals who meet quality and reliability standards.',
},
{
iconPath: '/assets/icons/star-orange-icon.svg',
title: 'Top Rated',
description: 'Work with highly rated agents known for strong expertise and positive client feedback. Their consistent performance helps you make informed and confident property decisions.',
description:
'Work with highly rated agents known for strong expertise and positive client feedback. Their consistent performance helps you make informed and confident property decisions.',
},
{
iconPath: '/assets/icons/trusted-people-icon.svg',
title: 'Trusted by Thousands',
description: 'Thousands of buyers, sellers, and renters trust our platform to find reliable agents. Our professionals help people navigate property journeys with confidence and ease.',
description:
'Thousands of buyers, sellers, and renters trust our platform to find reliable agents. Our professionals help people navigate property journeys with confidence and ease.',
},
],
featuredAgents: [
@@ -1438,7 +1456,14 @@ async function main() {
subtitle: '(Residential Property Expert)',
location: 'San Francisco, CA',
experience: '10+ years in the real estate industry.',
expertise: ['Residential', 'Rental', 'Commercial', 'Inspection', 'Land', 'Rental'],
expertise: [
'Residential',
'Rental',
'Commercial',
'Inspection',
'Land',
'Rental',
],
imageUrl: '/assets/images/professional-1.jpg',
},
{
@@ -1446,7 +1471,14 @@ async function main() {
subtitle: '(Residential Property Expert)',
location: 'San Francisco, CA',
experience: '10+ years in the real estate industry.',
expertise: ['Residential', 'Rental', 'Commercial', 'Inspection', 'Land', 'Rental'],
expertise: [
'Residential',
'Rental',
'Commercial',
'Inspection',
'Land',
'Rental',
],
imageUrl: '/assets/images/professional-2.jpg',
},
{
@@ -1454,7 +1486,14 @@ async function main() {
subtitle: '(Residential Property Expert)',
location: 'San Francisco, CA',
experience: '10+ years in the real estate industry.',
expertise: ['Residential', 'Rental', 'Commercial', 'Inspection', 'Land', 'Rental'],
expertise: [
'Residential',
'Rental',
'Commercial',
'Inspection',
'Land',
'Rental',
],
imageUrl: '/assets/images/professional-3.jpg',
},
],
@@ -1464,7 +1503,13 @@ async function main() {
subtitle: '(Mortgage Specialist)',
location: 'Los Angeles, CA',
experience: '10+ years in mortgage lending.',
expertise: ['FHA Loans', 'Conventional', 'VA Loans', 'Refinancing', 'Jumbo'],
expertise: [
'FHA Loans',
'Conventional',
'VA Loans',
'Refinancing',
'Jumbo',
],
imageUrl: '/assets/images/professional-1.jpg',
},
{
@@ -1472,7 +1517,13 @@ async function main() {
subtitle: '(Senior Loan Officer)',
location: 'Seattle, WA',
experience: '7+ years in lending.',
expertise: ['Jumbo Loans', 'Refinancing', 'Investment', 'First-time', 'USDA'],
expertise: [
'Jumbo Loans',
'Refinancing',
'Investment',
'First-time',
'USDA',
],
imageUrl: '/assets/images/professional-2.jpg',
},
{
@@ -1480,7 +1531,13 @@ async function main() {
subtitle: '(Home Loan Advisor)',
location: 'Austin, TX',
experience: '5+ years in mortgage industry.',
expertise: ['First-time Buyers', 'FHA', 'USDA Loans', 'VA Loans', 'Conventional'],
expertise: [
'First-time Buyers',
'FHA',
'USDA Loans',
'VA Loans',
'Conventional',
],
imageUrl: '/assets/images/professional-3.jpg',
},
],
@@ -1491,8 +1548,10 @@ async function main() {
sectionKey: 'testimonials',
content: {
title: 'Consumer Protection is Our Foundation',
subtitle: "Buying or selling your home is one of the largest decisions you will make in your life. Don't let yourself be chased by salesmen. Find the service provider that matches your unique needs.",
ratingInfo: 'Clients rate our real estate services 4.9 out of 5 on average, based on recent client reviews.',
subtitle:
"Buying or selling your home is one of the largest decisions you will make in your life. Don't let yourself be chased by salesmen. Find the service provider that matches your unique needs.",
ratingInfo:
'Clients rate our real estate services 4.9 out of 5 on average, based on recent client reviews.',
stats: [
{
iconPath: '/assets/icons/cities-icon.svg',
@@ -1509,21 +1568,24 @@ async function main() {
{
rating: 5,
title: 'No Longer Blood in the Water',
content: 'Using other sites to find a realtor ended with my phone ringing constantly. My number was sold off to multiple lists and it was so overwhelming. RE-Quest gave me confidence that my information was protected and I decided who I was in contact with.',
content:
'Using other sites to find a realtor ended with my phone ringing constantly. My number was sold off to multiple lists and it was so overwhelming. RE-Quest gave me confidence that my information was protected and I decided who I was in contact with.',
author: 'Alice T.',
role: '',
},
{
rating: 5,
title: 'Finding Someone Who Worked with Me',
content: 'RE-Quest let me narrow down my search terms, so I knew before I called that they had experience with the loan program I needed. I loved the in-app chat feature to talk to my short list before committing to a phone call.',
content:
'RE-Quest let me narrow down my search terms, so I knew before I called that they had experience with the loan program I needed. I loved the in-app chat feature to talk to my short list before committing to a phone call.',
author: 'Kevin R.',
role: '',
},
{
rating: 5,
title: '',
content: 'The search terms on RE-Quest allowed me to show off my expertise as an agent. Now when leads reach out to me, they already have an idea of what kind of agent I am. Instant connection!',
content:
'The search terms on RE-Quest allowed me to show off my expertise as an agent. Now when leads reach out to me, they already have an idea of what kind of agent I am. Instant connection!',
author: 'Riley S.',
role: '',
},
@@ -1536,14 +1598,16 @@ async function main() {
sectionKey: 'contactDetails',
content: {
title: 'Get In Touch',
description: 'Have a question about a property or need assistance? Fill out the form below and our team will get back to you shortly.',
description:
'Have a question about a property or need assistance? Fill out the form below and our team will get back to you shortly.',
email: '123support@gmail.com',
phone: '1234567890',
phoneHours: 'Mon-Fri 9am-6pm',
officeAddress: '123 Market Street',
officeCity: 'New York CA 234737',
mapUrl: '',
directionsUrl: 'https://maps.google.com/?q=123+Market+Street+New+York+CA+234737',
directionsUrl:
'https://maps.google.com/?q=123+Market+Street+New+York+CA+234737',
},
},
{
@@ -1551,7 +1615,8 @@ async function main() {
sectionKey: 'cta',
content: {
title: 'Ready to find an agent?',
description: 'Discover trusted agents and start your property journey with confidence.',
description:
'Discover trusted agents and start your property journey with confidence.',
buttonText: 'Start Your Search',
buttonLink: '/user/profiles',
},
@@ -1581,7 +1646,9 @@ async function main() {
isPublished: true,
},
});
console.log(` 🔄 Updated CMS content: ${cms.pageSlug}/${cms.sectionKey}`);
console.log(
` 🔄 Updated CMS content: ${cms.pageSlug}/${cms.sectionKey}`,
);
} else {
await prisma.cmsContent.create({
data: {
@@ -1591,7 +1658,9 @@ async function main() {
isPublished: true,
},
});
console.log(` ✅ Created CMS content: ${cms.pageSlug}/${cms.sectionKey}`);
console.log(
` ✅ Created CMS content: ${cms.pageSlug}/${cms.sectionKey}`,
);
}
}
@@ -1630,7 +1699,9 @@ async function main() {
console.log(` ⚠️ Plan "${plan.name}" already exists`);
} else {
await prisma.subscriptionPlan.create({ data: plan });
console.log(` ✅ Created plan: ${plan.name} ($${(plan.amount / 100).toFixed(2)}/${plan.interval})`);
console.log(
` ✅ Created plan: ${plan.name} ($${(plan.amount / 100).toFixed(2)}/${plan.interval})`,
);
}
}
@@ -1640,11 +1711,15 @@ async function main() {
// Summary
// =============================================
const userCount = await prisma.user.count();
const adminCount = await prisma.user.count({ where: { role: UserRole.ADMIN } });
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 systemSectionCount = await prisma.profileSection.count({
where: { isSystem: true },
});
const agentTypeSectionCount = await prisma.agentTypeSection.count();
const cmsContentCount = await prisma.cmsContent.count();
const planCount = await prisma.subscriptionPlan.count();
@@ -1653,7 +1728,9 @@ async function main() {
console.log(` Total Users: ${userCount}`);
console.log(` Admins: ${adminCount}`);
console.log(` Agent Types: ${agentTypeCount}`);
console.log(` Profile Sections: ${sectionCount} (${systemSectionCount} system)`);
console.log(
` Profile Sections: ${sectionCount} (${systemSectionCount} system)`,
);
console.log(` Profile Fields: ${fieldCount}`);
console.log(` Section Assignments: ${agentTypeSectionCount}`);
console.log(` CMS Content: ${cmsContentCount}`);
@@ -1664,8 +1741,7 @@ async function main() {
await pool.end();
}
main()
.catch((e) => {
main().catch((e) => {
console.error('❌ Seeding failed:', e);
process.exit(1);
});

View File

@@ -0,0 +1,54 @@
import { PrismaClient } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
import { Pool } from 'pg';
import {
US_STATES_CO_NE_ND_SD,
US_CITIES_CO_NE_ND_SD,
} from './data/us-cities-co-ne-nd-sd';
const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
console.error('❌ DATABASE_URL env var is required');
process.exit(1);
}
const pool = new Pool({ connectionString });
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });
async function updateFieldOptions(slug: 'state' | 'city', options: unknown[]) {
const fields = await prisma.profileField.findMany({
where: { slug },
select: { id: true, name: true, section: { select: { slug: true } } },
});
if (fields.length === 0) {
console.log(` ⚠️ No ProfileField rows found with slug="${slug}"`);
return;
}
for (const field of fields) {
await prisma.profileField.update({
where: { id: field.id },
data: { options: options as object },
});
console.log(
` ✅ Updated ${field.section.slug}/${slug} (${options.length} options)`,
);
}
}
async function main() {
console.log('🌱 Updating state + city ProfileField options from Excel source...');
await updateFieldOptions('state', US_STATES_CO_NE_ND_SD);
await updateFieldOptions('city', US_CITIES_CO_NE_ND_SD);
console.log('✅ Done.');
}
main()
.catch((e) => {
console.error('❌ Failed to update location options:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});