File

apps/recallassess/recallassess-api/src/api/client/package/package.service.ts

Extends

CLBaseService

Index

Properties
Methods

Methods

Private enrichPackageData
enrichPackageData(pkg: Package)

Enrich package data with UI-specific fields

Parameters :
Name Type Optional Description
pkg Package No

Database package object

Returns : CLPackageDto

Enriched package DTO

Async getActivePackages
getActivePackages(specialSlug?: string)

Get all active packages for client consumption If not provided, only returns packages where special_slug is null or empty.

Parameters :
Name Type Optional Description
specialSlug string Yes

Optional special slug filter. If provided, only returns packages with matching special_slug. If not provided, only returns packages where special_slug is null or empty.

Array of packages formatted for the landing page

Protected buildCompanyWhere
buildCompanyWhere(companyId: number, additionalWhere?: Record)
Inherited from CLBaseService
Defined in CLBaseService:82

Build base WHERE clause with company scope Ensures all queries are scoped to the user's company

Parameters :
Name Type Optional Description
companyId number No
  • Company ID to scope queries to
additionalWhere Record<string | any> Yes
  • Additional where conditions to merge
Returns : Record<string, any>

Complete where clause object

Protected buildSearchWhere
buildSearchWhere(searchFields: string[], searchQuery?: string)
Inherited from CLBaseService
Defined in CLBaseService:64

Build a WHERE clause for search functionality Creates OR conditions for multiple fields

Parameters :
Name Type Optional Description
searchFields string[] No
  • Array of field names to search in
searchQuery string Yes
  • Search query string
Returns : [] | undefined

Array of search conditions or undefined if no query

Protected Async findByIdWithCompanyScope
findByIdWithCompanyScope(entityName: string, entityId: number, companyId: number)
Inherited from CLBaseService
Defined in CLBaseService:111

Find entity by ID with company scope verification Common pattern: get entity and ensure it belongs to the company

Parameters :
Name Type Optional Description
entityName string No
  • Prisma model name
entityId number No
  • Entity ID
companyId number No
  • Company ID to verify ownership
Returns : Promise<any | null>

Entity if found and belongs to company, null otherwise

Protected getRepo
getRepo(repoName: string)
Inherited from CLBaseService
Defined in CLBaseService:96

Get a Prisma repository (table) dynamically Useful for generic operations across different entities

Parameters :
Name Type Optional Description
repoName string No
  • The name of the Prisma repository (table)
Returns : any

The Prisma repository instance

Protected toDto
toDto(entity: any, dtoClass: unknown)
Inherited from CLBaseService
Defined in CLBaseService:20
Type parameters :
  • TDto

Transform database entity to DTO using class-transformer

Parameters :
Name Type Optional Description
entity any No
  • Raw database entity
dtoClass unknown No
  • DTO class constructor
Returns : TDto

Transformed DTO instance

Protected toDtoArray
toDtoArray(entities: any[], dtoClass: unknown)
Inherited from CLBaseService
Defined in CLBaseService:30
Type parameters :
  • TDto

Transform array of database entities to DTOs

Parameters :
Name Type Optional Description
entities any[] No
  • Array of raw database entities
dtoClass unknown No
  • DTO class constructor
Returns : TDto[]

Array of transformed DTO instances

Protected Async verifyCompanyOwnership
verifyCompanyOwnership(entityName: string, entityId: number, companyId: number)
Inherited from CLBaseService
Defined in CLBaseService:42

Verify that an entity belongs to a specific company Common security check to prevent cross-company data access

Parameters :
Name Type Optional Description
entityName string No
  • Prisma model name (e.g., 'participant', 'participantGroup')
entityId number No
  • Entity ID to check
companyId number No
  • Company ID to verify ownership
Returns : Promise<boolean>

True if entity belongs to company, false otherwise

Properties

Protected Readonly prisma
Type : BNestPrismaService
Decorators :
@Inject()
Inherited from CLBaseService
Defined in CLBaseService:12
import { Injectable } from "@nestjs/common";
import { Package, PackageType } from "@prisma/client";
import { CLBaseService } from "../../shared/services/base.service";
import { CLPackageDto } from "./dto";
import { VIP_TRIAL_LANDING_COPY } from "./vip-trial-landing-content";

@Injectable()
export class CLPackageService extends CLBaseService {
  /**
   * Get all active packages for client consumption
   * @param specialSlug Optional special slug filter. If provided, only returns packages with matching special_slug.
   *                    If not provided, only returns packages where special_slug is null or empty.
   * @returns Array of packages formatted for the landing page
   */
  async getActivePackages(specialSlug?: string): Promise<CLPackageDto[]> {
    const whereClause: any = {
      is_active: true,
    };

    // Filter by special_slug:
    // - If sps parameter is provided, only show packages with matching special_slug
    // - If sps parameter is not provided, only show packages where special_slug is null or empty
    if (specialSlug) {
      whereClause.special_slug = specialSlug;
    } else {
      whereClause.OR = [{ special_slug: null }, { special_slug: "" }];
    }

    const packages = await this.prisma.client.package.findMany({
      where: whereClause,
      orderBy: {
        sort_order: "asc",
      },
    });

    return packages.map((pkg) => this.enrichPackageData(pkg));
  }

  /**
   * Enrich package data with UI-specific fields
   * @param pkg Database package object
   * @returns Enriched package DTO
   */
  private enrichPackageData(pkg: Package): CLPackageDto {
    // Single source of truth for VIP trial offer copy (see vip-trial-landing-content.ts)
    const row: Package =
      pkg.package_type === PackageType.PRIVATE_VIP_TRIAL
        ? {
            ...pkg,
            description: VIP_TRIAL_LANDING_COPY.description,
            features: VIP_TRIAL_LANDING_COPY.features,
          }
        : pkg;

    // Map package type to color
    const colorMap: Record<PackageType, string> = {
      FREE_TRIAL: "purple",
      STARTUP: "orange",
      GROWTH: "blue",
      ENTERPRISE: "green",
      PRIVATE_VIP_TRIAL: "purple",
    };

    // Map package type to icon
    const iconMap: Record<PackageType, string> = {
      FREE_TRIAL: "pi-clock",
      STARTUP: "pi-users",
      GROWTH: "pi-chart-line",
      ENTERPRISE: "pi-star",
      PRIVATE_VIP_TRIAL: "pi-star",
    };

    const color = colorMap[row.package_type] || "purple";
    const icon = iconMap[row.package_type] || "pi-box";

    // Generate badge from license range or trial duration
    let badge: string;
    if (row.is_trial_package) {
      // For trial packages, show trial duration
      const trialDays = row.trial_duration_days || 7; // Default to 7 days if not set
      badge = `${trialDays}-day trial`;
    } else if (row.license_count_end === 0 || row.license_count_end >= 999999) {
      badge = `${row.license_count_start}+ licenses`;
    } else {
      badge = `${row.license_count_start}-${row.license_count_end} licenses`;
    }

    // Format price display
    let priceDisplay: string;
    let pricePrefix = "";
    let priceSuffix = "";

    const priceValue = row.price_per_licence ? Number(row.price_per_licence) : null;
    
    if (priceValue === 0 || priceValue === null) {
      priceDisplay = "FREE";
    } else if (priceValue > 0) {
      priceDisplay = priceValue.toString();
      pricePrefix = "$";
      priceSuffix = "/license/month";
    } else {
      priceDisplay = "Contact Us";
    }

    // Button label
    const buttonLabel = row.is_trial_package ? "Try Now" : "Buy Now";

    // Parse features from features field (line-separated)
    // Expected format: One feature per line
    let features: string[] = [];
    if (row.features) {
      features = row.features
        .split("\n")
        .map((f) => f.trim())
        .filter((f) => f.length > 0);
    }

    return this.toDto(
      {
        ...row,
        price_per_licence: row.price_per_licence ? Number(row.price_per_licence) : null,
        color,
        icon,
        badge,
        price_display: priceDisplay,
        price_prefix: pricePrefix,
        price_suffix: priceSuffix,
        button_label: buttonLabel,
        features,
      },
      CLPackageDto,
    );
  }
}

results matching ""

    No results matching ""