Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | 1x 1x 1x 1x 1x 16x 10x 11x 5x 5x 2x 3x 3x 3x 2x 1x 15x 15x 4x 4x 15x 15x 15x 34x 34x 26x 25x 8x 8x 15x 15x 15x | import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Plan } from '../entities/plan.entity';
import { PlanResponseDto, PlanDetailResponseDto } from '../dto/plan-response.dto';
@Injectable()
export class PlansService {
constructor(
@InjectRepository(Plan)
private readonly planRepo: Repository<Plan>,
) {}
/**
* Get all visible and active plans
* Returns plans ordered by sort_order ascending
*/
async findAll(): Promise<PlanResponseDto[]> {
const plans = await this.planRepo.find({
where: {
is_active: true,
is_visible: true,
},
order: {
sort_order: 'ASC',
},
});
return plans.map((plan) => this.toResponseDto(plan));
}
/**
* Get a single plan by ID
* Returns detailed plan information including features
*/
async findOne(id: string): Promise<PlanDetailResponseDto> {
const plan = await this.planRepo.findOne({
where: { id },
});
if (!plan) {
throw new NotFoundException(`Plan with ID "${id}" not found`);
}
return this.toDetailResponseDto(plan);
}
/**
* Get a single plan by slug
* Returns detailed plan information including features
*/
async findBySlug(slug: string): Promise<PlanDetailResponseDto> {
const plan = await this.planRepo.findOne({
where: { slug },
});
if (!plan) {
throw new NotFoundException(`Plan with slug "${slug}" not found`);
}
return this.toDetailResponseDto(plan);
}
/**
* Transform Plan entity to PlanResponseDto
* Extracts feature descriptions from the features array
*/
private toResponseDto(plan: Plan): PlanResponseDto {
// Extract feature descriptions from the features array
// The entity has features as Array<{ name, value, highlight }>
// Frontend expects features as string[]
const featureDescriptions = this.extractFeatureDescriptions(plan);
return {
id: plan.id,
name: plan.name,
slug: plan.slug,
display_name: plan.name, // Use name as display_name
description: plan.description || '',
tagline: plan.tagline || undefined,
price_monthly: plan.price_monthly ? Number(plan.price_monthly) : 0,
price_yearly: plan.price_yearly ? Number(plan.price_yearly) : 0,
currency: plan.currency,
features: featureDescriptions,
limits: plan.limits || undefined,
is_popular: plan.is_popular || undefined,
trial_days: plan.trial_days || undefined,
};
}
/**
* Transform Plan entity to PlanDetailResponseDto
* Includes additional fields like is_enterprise and detailed_features
*/
private toDetailResponseDto(plan: Plan): PlanDetailResponseDto {
const baseDto = this.toResponseDto(plan);
return {
...baseDto,
is_enterprise: plan.is_enterprise || undefined,
detailed_features: plan.features || undefined,
metadata: plan.metadata || undefined,
};
}
/**
* Extract feature descriptions from plan
* Combines features array and included_features array
*/
private extractFeatureDescriptions(plan: Plan): string[] {
const descriptions: string[] = [];
// Add features from the features array (name or value as description)
if (plan.features && Array.isArray(plan.features)) {
for (const feature of plan.features) {
if (typeof feature === 'object' && feature.name) {
// For boolean values, just use the name
// For string values, combine name and value
if (typeof feature.value === 'boolean') {
if (feature.value) {
descriptions.push(feature.name);
}
} else if (typeof feature.value === 'string') {
descriptions.push(`${feature.name}: ${feature.value}`);
} else E{
descriptions.push(feature.name);
}
}
}
}
// Add included_features as-is (they are already strings)
if (plan.included_features && Array.isArray(plan.included_features)) {
descriptions.push(...plan.included_features);
}
return descriptions;
}
}
|