Skip to main content

Backend Requirement: Plan Features & Display Configuration

Context

The admin panel now has an updated UI for creating/editing subscription plans. The form allows admins to:

  1. Set feature limits (all max* fields already on the Plan model)
  2. Configure display features (name, description, included value) shown on pricing cards
  3. Set additional settings (description, creditsPerMonth, trialDays, sortOrder, billingMode)

The frontend is already wired up to send these fields in mutations. The backend needs to accept them.

Required Changes

1. Add description field to Plan model

The Plan model needs a description field (String, optional) for the plan subtitle shown on pricing cards (e.g., "For growing businesses with larger teams").

2. Add features field to Plan model

The Plan needs a features field — a JSON array of display features shown on pricing cards.

PlanFeature structure (stored as JSON):

interface PlanFeature {
name: string // e.g., "Company Accounts"
description: string // e.g., "Number of companies you can manage"
included: boolean | string // false = not included, true = checkmark, string = custom text like "Up to 5"
highlight: boolean // Whether this feature is visually emphasized
}

GraphQL type:

type PlanFeature {
name: String!
description: String!
included: JSON! # Boolean or String — use JSON scalar
highlight: Boolean!
}

input PlanFeatureInput {
name: String!
description: String!
included: JSON! # Boolean or String
highlight: Boolean
}

Alternative: If JSON scalar is not desired, use String! for included and let the frontend serialize/deserialize booleans as "true" / "false".

3. Update CreatePlanInput

Add these fields to the existing CreatePlanInput:

input CreatePlanInput {
name: String!
price: Int!
currency: String!
interval: String!
# NEW fields:
description: String
active: Boolean
billingMode: String # "PREPAID" | "POSTPAID"
trialDays: Int
sortOrder: Int
creditsPerMonth: Int
# Feature limits (all optional, null = unlimited)
maxJobs: Int
maxTalentPools: Int
maxContacts: Int
maxPipelines: Int
maxInterviewsPerMonth: Int
maxMeetingsPerMonth: Int
maxCvCollections: Int
maxSharedTalentLists: Int
maxCollaborationInvites: Int
maxConnectionRequestsPerMonth: Int
maxUsersPerCompany: Int
maxNewsPerMonth: Int
maxContactNotes: Int
maxCvParsesPerMonth: Int
maxAgentMessagesPerMonth: Int
maxTextRewritesPerMonth: Int
maxCompanies: Int
# Display features
features: [PlanFeatureInput!]
}

4. Update UpdatePlanInput

Add the same fields to UpdatePlanInput (all optional):

input UpdatePlanInput {
name: String
price: Int
active: Boolean
# NEW fields:
description: String
billingMode: String
trialDays: Int
sortOrder: Int
creditsPerMonth: Int
# Feature limits
maxJobs: Int
maxTalentPools: Int
maxContacts: Int
maxPipelines: Int
maxInterviewsPerMonth: Int
maxMeetingsPerMonth: Int
maxCvCollections: Int
maxSharedTalentLists: Int
maxCollaborationInvites: Int
maxConnectionRequestsPerMonth: Int
maxUsersPerCompany: Int
maxNewsPerMonth: Int
maxContactNotes: Int
maxCvParsesPerMonth: Int
maxAgentMessagesPerMonth: Int
maxTextRewritesPerMonth: Int
maxCompanies: Int
# Display features (replaces entire array)
features: [PlanFeatureInput!]
}

5. Update Plan type to return new fields

type Plan {
# ... existing fields ...
description: String
features: [PlanFeature!]
}

6. Update createPlan and updatePlan resolvers

  • Accept and persist all the new fields
  • features should be stored as a JSON column (or related table)
  • Feature limits (max* fields) are already columns on the Plan model — just pass them through
  • When features is provided in update, replace the entire array (not merge)

Prisma Schema Changes

model Plan {
// ... existing fields ...
description String?
features Json? @default("[]") // Stores PlanFeature[]
}

Frontend Mutations (Already Updated)

The frontend CREATE_PLAN and UPDATE_PLAN mutations already request all these fields in their return selection. See graphql/operations/payment/mutations.ts.

Feature Display Flow

  1. Admin creates plan with display features via admin panel
  2. Backend stores features as JSON on Plan
  3. Frontend pricing page (PricingCards.tsx) currently uses hardcoded planFeatures.ts config
  4. Future work: Update PricingCards to use plan.features from GraphQL instead of hardcoded config (fallback to config if features array is empty)