- 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>
130 lines
3.0 KiB
TypeScript
130 lines
3.0 KiB
TypeScript
/**
|
|
* 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 };
|