import { Injectable, NotFoundException, ForbiddenException, BadRequestException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { AssessmentTemplate } from '../entities/assessment-template.entity'; import { CreateTemplateDto } from '../dto/create-template.dto'; import { UpdateTemplateDto } from '../dto/update-template.dto'; import { TenantService } from '../../tenant/tenant.service'; @Injectable() export class TemplateService { constructor( @InjectRepository(AssessmentTemplate) private readonly templateRepository: Repository, private readonly tenantService: TenantService, ) {} private validateRequiredFields(data: { linkedGroupIds?: string[] | null; dimensions?: Array<{ name: string; label?: string; weight?: number }> | null; }) { if (data.linkedGroupIds != null && Array.isArray(data.linkedGroupIds)) { if (data.linkedGroupIds.length === 0) { throw new BadRequestException('At least one knowledge group must be linked'); } } if (data.dimensions != null && Array.isArray(data.dimensions)) { if (data.dimensions.length === 0) { throw new BadRequestException('At least one dimension must be defined'); } for (const d of data.dimensions) { if (!d.name) { throw new BadRequestException('Each dimension must have a name'); } } } } async create( createDto: CreateTemplateDto, userId: string, tenantId: string, ): Promise { this.validateRequiredFields(createDto); const { ...data } = createDto; const template = this.templateRepository.create({ ...data, createdBy: userId, tenantId, }); return this.templateRepository.save(template); } async findAll(tenantId: string): Promise { return this.templateRepository.find({ where: { tenantId, isActive: true }, relations: ['knowledgeGroup'], order: { createdAt: 'DESC' }, }); } async findOne( id: string, userId: string, tenantId: string, ): Promise { const template = await this.templateRepository.findOne({ where: { id }, relations: ['knowledgeGroup'], }); if (!template) { throw new NotFoundException(`Template with ID "${id}" not found`); } // Check permission using TenantService const hasAccess = await this.tenantService.canAccessTenant( userId, template.tenantId, tenantId, ); if (!hasAccess) { throw new ForbiddenException( `You do not have permission to access this template`, ); } return template; } async update( id: string, updateDto: UpdateTemplateDto, userId: string, tenantId: string, ): Promise { const template = await this.findOne(id, userId, tenantId); const merged = { ...template, ...updateDto }; this.validateRequiredFields(merged); Object.assign(template, updateDto); return this.templateRepository.save(template); } async remove(id: string, userId: string, tenantId: string): Promise { const template = await this.findOne(id, userId, tenantId); // Soft delete by setting isActive to false template.isActive = false; await this.templateRepository.save(template); } }