apps/recallassess/recallassess-api/src/api/client/package/package.service.ts
Properties |
|
Methods |
|
| Private enrichPackageData | ||||||||
enrichPackageData(pkg: Package)
|
||||||||
|
Enrich package data with UI-specific fields
Parameters :
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 :
Returns :
Promise<CLPackageDto[]>
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 :
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 :
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 :
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 :
Returns :
any
The Prisma repository instance |
| Protected toDto | ||||||||||||
toDto(entity: any, dtoClass: unknown)
|
||||||||||||
|
Inherited from
CLBaseService
|
||||||||||||
|
Defined in
CLBaseService:20
|
||||||||||||
Type parameters :
|
||||||||||||
|
Transform database entity to DTO using class-transformer
Parameters :
Returns :
TDto
Transformed DTO instance |
| Protected toDtoArray | ||||||||||||
toDtoArray(entities: any[], dtoClass: unknown)
|
||||||||||||
|
Inherited from
CLBaseService
|
||||||||||||
|
Defined in
CLBaseService:30
|
||||||||||||
Type parameters :
|
||||||||||||
|
Transform array of database entities to DTOs
Parameters :
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 :
Returns :
Promise<boolean>
True if entity belongs to company, false otherwise |
| 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,
);
}
}