feat(seed): add standalone seed scripts for districts, plans, and market data
- scripts/seed-districts.ts: Vietnam district/ward data for HCM, Hanoi, Da Nang with sample properties - scripts/seed-plans.ts: Subscription plans (FREE, AGENT_PRO, INVESTOR, ENTERPRISE) - scripts/import-market-data.ts: Market index data across all 3 cities with realistic pricing - All scripts are idempotent (upsert/ON CONFLICT DO NOTHING) - Refactored prisma/seed.ts to import shared data from scripts, removing duplication Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
129
scripts/seed-plans.ts
Normal file
129
scripts/seed-plans.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Seed subscription plans (FREE, AGENT_PRO, INVESTOR, ENTERPRISE).
|
||||
*
|
||||
* Usage: npx tsx scripts/seed-plans.ts
|
||||
* Idempotent: uses upsert on PlanTier unique constraint.
|
||||
*/
|
||||
|
||||
import { PrismaClient, PlanTier } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export const PLANS = [
|
||||
{
|
||||
tier: PlanTier.FREE,
|
||||
name: 'Miễn phí',
|
||||
priceMonthlyVND: BigInt(0),
|
||||
priceYearlyVND: BigInt(0),
|
||||
maxListings: 3,
|
||||
maxSavedSearches: 5,
|
||||
features: {
|
||||
basicSearch: true,
|
||||
listingPost: true,
|
||||
maxPhotos: 5,
|
||||
analytics: false,
|
||||
prioritySupport: false,
|
||||
aiValuation: false,
|
||||
featuredListing: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
tier: PlanTier.AGENT_PRO,
|
||||
name: 'Agent Pro',
|
||||
priceMonthlyVND: BigInt(499_000),
|
||||
priceYearlyVND: BigInt(4_990_000),
|
||||
maxListings: 50,
|
||||
maxSavedSearches: 30,
|
||||
features: {
|
||||
basicSearch: true,
|
||||
listingPost: true,
|
||||
maxPhotos: 30,
|
||||
analytics: true,
|
||||
prioritySupport: true,
|
||||
aiValuation: true,
|
||||
featuredListing: true,
|
||||
leadManagement: true,
|
||||
agentProfile: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
tier: PlanTier.INVESTOR,
|
||||
name: 'Investor',
|
||||
priceMonthlyVND: BigInt(999_000),
|
||||
priceYearlyVND: BigInt(9_990_000),
|
||||
maxListings: 20,
|
||||
maxSavedSearches: 100,
|
||||
features: {
|
||||
basicSearch: true,
|
||||
listingPost: true,
|
||||
maxPhotos: 15,
|
||||
analytics: true,
|
||||
prioritySupport: true,
|
||||
aiValuation: true,
|
||||
featuredListing: false,
|
||||
marketReports: true,
|
||||
priceAlerts: true,
|
||||
portfolioTracking: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
tier: PlanTier.ENTERPRISE,
|
||||
name: 'Enterprise',
|
||||
priceMonthlyVND: BigInt(4_990_000),
|
||||
priceYearlyVND: BigInt(49_900_000),
|
||||
maxListings: null,
|
||||
maxSavedSearches: null,
|
||||
features: {
|
||||
basicSearch: true,
|
||||
listingPost: true,
|
||||
maxPhotos: 100,
|
||||
analytics: true,
|
||||
prioritySupport: true,
|
||||
aiValuation: true,
|
||||
featuredListing: true,
|
||||
leadManagement: true,
|
||||
agentProfile: true,
|
||||
marketReports: true,
|
||||
priceAlerts: true,
|
||||
portfolioTracking: true,
|
||||
apiAccess: true,
|
||||
whiteLabel: true,
|
||||
dedicatedSupport: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
async function seedPlans() {
|
||||
console.log('Seeding subscription plans...\n');
|
||||
|
||||
for (const plan of PLANS) {
|
||||
const result = await prisma.plan.upsert({
|
||||
where: { tier: plan.tier },
|
||||
update: {
|
||||
name: plan.name,
|
||||
priceMonthlyVND: plan.priceMonthlyVND,
|
||||
priceYearlyVND: plan.priceYearlyVND,
|
||||
maxListings: plan.maxListings,
|
||||
maxSavedSearches: plan.maxSavedSearches,
|
||||
features: plan.features,
|
||||
},
|
||||
create: plan,
|
||||
});
|
||||
|
||||
const monthly = Number(plan.priceMonthlyVND).toLocaleString('vi-VN');
|
||||
console.log(` ${plan.tier.padEnd(12)} ${plan.name.padEnd(14)} ${monthly} VND/tháng`);
|
||||
}
|
||||
|
||||
console.log(`\n${PLANS.length} plans seeded.`);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
seedPlans()
|
||||
.catch((e) => {
|
||||
console.error('Seed plans failed:', e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => prisma.$disconnect());
|
||||
}
|
||||
|
||||
export { seedPlans };
|
||||
Reference in New Issue
Block a user