diff --git a/front/components/poke/plans/form.tsx b/front/components/poke/plans/form.tsx index 247c1bf1e80df..38f2ee326c0b1 100644 --- a/front/components/poke/plans/form.tsx +++ b/front/components/poke/plans/form.tsx @@ -39,6 +39,7 @@ export type EditingPlanType = { maxUsers: string | number; billingType: FreeBillingType | PaidBillingType; isNewPlan?: boolean; + trialPeriodDays: number; }; export const fromPlanType = (plan: PlanType): EditingPlanType => { @@ -60,6 +61,7 @@ export const fromPlanType = (plan: PlanType): EditingPlanType => { dataSourcesDocumentsSizeMb: plan.limits.dataSources.documents.sizeMb, maxUsers: plan.limits.users.maxUsers, billingType: plan.billingType, + trialPeriodDays: plan.trialPeriodDays ?? 0, }; }; @@ -97,6 +99,7 @@ export const toPlanType = (editingPlan: EditingPlanType): PlanType => { }, }, billingType: editingPlan.billingType, + trialPeriodDays: parseInt(editingPlan.trialPeriodDays.toString(), 10), }; }; @@ -119,6 +122,7 @@ const getEmptyPlan = (): EditingPlanType => ({ maxUsers: "", isNewPlan: true, billingType: "fixed", + trialPeriodDays: 0, }); export const useEditingPlan = () => { @@ -281,6 +285,18 @@ export const PLAN_FIELDS = { title: "Billing", error: (plan: EditingPlanType) => (plan.billingType ? null : "Required"), }, + trialPeriodDays: { + type: "number", + width: "small", + title: "Trial Days", + error: (plan: EditingPlanType) => { + if (plan.billingType === "free") { + return null; + } + + return errorCheckNumber(plan.trialPeriodDays); + }, + }, } as const; type FieldProps = { diff --git a/front/lib/models/plan.ts b/front/lib/models/plan.ts index 5665791de280f..9d158ee85f62a 100644 --- a/front/lib/models/plan.ts +++ b/front/lib/models/plan.ts @@ -33,6 +33,7 @@ export class Plan extends Model< declare name: string; declare stripeProductId: string | null; declare billingType: FreeBillingType | PaidBillingType; + declare trialPeriodDays: number | null; // workspace limitations declare maxMessages: number; @@ -86,6 +87,10 @@ Plan.init( isIn: [[...FREE_BILLING_TYPES, ...PAID_BILLING_TYPES]], }, }, + trialPeriodDays: { + type: DataTypes.INTEGER, + allowNull: true, + }, maxMessages: { type: DataTypes.INTEGER, allowNull: false, diff --git a/front/lib/plans/stripe.ts b/front/lib/plans/stripe.ts index 02e035a4a6e75..87d0da38fc710 100644 --- a/front/lib/plans/stripe.ts +++ b/front/lib/plans/stripe.ts @@ -111,6 +111,7 @@ export const createCheckoutSession = async ({ planCode: planCode, workspaceId: owner.sId, }, + trial_period_days: plan.trialPeriodDays || undefined, }, metadata: { planCode: planCode, diff --git a/front/pages/api/poke/plans.ts b/front/pages/api/poke/plans.ts index 38968ca320e29..ca3f45c715878 100644 --- a/front/pages/api/poke/plans.ts +++ b/front/pages/api/poke/plans.ts @@ -45,6 +45,7 @@ export const PlanTypeSchema = t.type({ t.literal("per_seat"), t.literal("free"), ]), + trialPeriodDays: t.union([t.number, t.null]), }); export type UpsertPokePlanResponseBody = { @@ -108,6 +109,7 @@ async function handler( }, }, billingType: plan.billingType, + trialPeriodDays: plan.trialPeriodDays, })); const stripeProductIds = plans @@ -190,6 +192,7 @@ async function handler( maxDataSourcesDocumentsSizeMb: body.limits.dataSources.documents.sizeMb, maxUsersInWorkspace: body.limits.users.maxUsers, billingType: body.billingType, + trialPeriodDays: body.trialPeriodDays || null, }); res.status(200).json({ plan: body, diff --git a/types/src/front/plan.ts b/types/src/front/plan.ts index 21c3a7baa3f8a..ebb034ab002de 100644 --- a/types/src/front/plan.ts +++ b/types/src/front/plan.ts @@ -49,6 +49,7 @@ export type PlanType = { limits: LimitsType; stripeProductId: string | null; billingType: FreeBillingType | PaidBillingType; + trialPeriodDays: number | null; }; export type SubscriptionType = {