- Remove unused imports/variables in seed scripts and test files - Replace console.log with console.warn in seed/utility scripts - Replace `as any` with proper Prisma types (InputJsonValue, PaymentStatus, Plan, UserWhereInput) - Fix import-x/no-named-as-default-member warnings in logger, mapbox, eslint config - Prefix unused callback params with underscore in e2e tests Co-Authored-By: Paperclip <noreply@paperclip.ing>
134 lines
3.2 KiB
TypeScript
134 lines
3.2 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 { PrismaPg } from '@prisma/adapter-pg';
|
|
import { PrismaClient, PlanTier } from '@prisma/client';
|
|
import pg from 'pg';
|
|
|
|
const pool = new pg.Pool({ connectionString: process.env['DATABASE_URL'] });
|
|
const adapter = new PrismaPg(pool);
|
|
const prisma = new PrismaClient({ adapter });
|
|
|
|
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.warn('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.warn(` ${plan.tier.padEnd(12)} ${plan.name.padEnd(14)} ${monthly} VND/tháng`);
|
|
}
|
|
|
|
console.warn(`\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 };
|