Files
backend/src/profile-fields/profile-sections.service.ts

311 lines
7.4 KiB
TypeScript
Raw Normal View History

2026-01-24 03:33:54 +05:30
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { PrismaService } from '../prisma';
import { CreateSectionDto, UpdateSectionDto, AssignSectionDto, UpdateAssignmentDto } from './dto';
@Injectable()
export class ProfileSectionsService {
constructor(private readonly prisma: PrismaService) {}
/**
* Generate a URL-friendly slug from the name
*/
private generateSlug(name: string): string {
return name
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/(^-|-$)/g, '');
}
/**
* Create a new profile section
*/
async create(createSectionDto: CreateSectionDto) {
const slug = this.generateSlug(createSectionDto.name);
// Check if slug already exists
const existing = await this.prisma.profileSection.findUnique({
where: { slug },
});
if (existing) {
throw new ConflictException('A section with this name already exists');
}
return this.prisma.profileSection.create({
data: {
...createSectionDto,
slug,
},
include: {
_count: {
select: { fields: true, agentTypeSections: true },
},
},
});
}
/**
* Get all profile sections
*/
async findAll(includeInactive = false) {
const where = includeInactive ? {} : { isActive: true };
return this.prisma.profileSection.findMany({
where,
orderBy: [{ sortOrder: 'asc' }, { name: 'asc' }],
include: {
_count: {
select: { fields: true, agentTypeSections: true },
},
agentTypeSections: {
include: {
agentType: {
select: { id: true, name: true },
},
},
},
},
});
}
/**
* Get a single section by ID with all fields
*/
async findOne(id: string) {
const section = await this.prisma.profileSection.findUnique({
where: { id },
include: {
fields: {
orderBy: { sortOrder: 'asc' },
},
agentTypeSections: {
include: {
agentType: {
select: { id: true, name: true },
},
},
orderBy: { sortOrder: 'asc' },
},
_count: {
select: { fields: true, agentTypeSections: true },
},
},
});
if (!section) {
throw new NotFoundException(`Section with ID ${id} not found`);
}
return section;
}
/**
* Update a section
*/
async update(id: string, updateSectionDto: UpdateSectionDto) {
// Check if section exists
await this.findOne(id);
const data: any = { ...updateSectionDto };
// If name is being updated, update the slug too
if (updateSectionDto.name) {
const newSlug = this.generateSlug(updateSectionDto.name);
// Check if new slug conflicts with another section
const existing = await this.prisma.profileSection.findFirst({
where: {
AND: [{ id: { not: id } }, { slug: newSlug }],
},
});
if (existing) {
throw new ConflictException('A section with this name already exists');
}
data.slug = newSlug;
}
return this.prisma.profileSection.update({
where: { id },
data,
include: {
_count: {
select: { fields: true, agentTypeSections: true },
},
},
});
}
/**
* Delete a section
*/
async remove(id: string) {
const section = await this.findOne(id);
// Check if section has any fields
if (section._count.fields > 0) {
throw new ConflictException(
`Cannot delete section. It has ${section._count.fields} field(s). Delete the fields first.`,
);
}
return this.prisma.profileSection.delete({
where: { id },
});
}
/**
* Assign a section to an agent type
*/
async assignToAgentType(sectionId: string, assignDto: AssignSectionDto) {
// Verify section exists
await this.findOne(sectionId);
// Verify agent type exists
const agentType = await this.prisma.agentType.findUnique({
where: { id: assignDto.agentTypeId },
});
if (!agentType) {
throw new NotFoundException(`Agent type with ID ${assignDto.agentTypeId} not found`);
}
// Check if already assigned
const existing = await this.prisma.agentTypeSection.findUnique({
where: {
agentTypeId_sectionId: {
agentTypeId: assignDto.agentTypeId,
sectionId,
},
},
});
if (existing) {
throw new ConflictException('This section is already assigned to this agent type');
}
return this.prisma.agentTypeSection.create({
data: {
sectionId,
agentTypeId: assignDto.agentTypeId,
sortOrder: assignDto.sortOrder ?? 0,
isRequired: assignDto.isRequired ?? false,
},
include: {
section: true,
agentType: true,
},
});
}
/**
* Update a section assignment
*/
async updateAssignment(sectionId: string, agentTypeId: string, updateDto: UpdateAssignmentDto) {
const assignment = await this.prisma.agentTypeSection.findUnique({
where: {
agentTypeId_sectionId: {
agentTypeId,
sectionId,
},
},
});
if (!assignment) {
throw new NotFoundException('Section assignment not found');
}
return this.prisma.agentTypeSection.update({
where: { id: assignment.id },
data: updateDto,
include: {
section: true,
agentType: true,
},
});
}
/**
* Remove a section from an agent type
*/
async removeFromAgentType(sectionId: string, agentTypeId: string) {
const assignment = await this.prisma.agentTypeSection.findUnique({
where: {
agentTypeId_sectionId: {
agentTypeId,
sectionId,
},
},
});
if (!assignment) {
throw new NotFoundException('Section assignment not found');
}
return this.prisma.agentTypeSection.delete({
where: { id: assignment.id },
});
}
/**
* Get all sections for a specific agent type
*/
async getSectionsForAgentType(agentTypeId: string, includeFields = true) {
const agentType = await this.prisma.agentType.findUnique({
where: { id: agentTypeId },
});
if (!agentType) {
throw new NotFoundException(`Agent type with ID ${agentTypeId} not found`);
}
// Get assigned sections
const assignments = await this.prisma.agentTypeSection.findMany({
where: { agentTypeId },
orderBy: { sortOrder: 'asc' },
include: {
section: {
include: includeFields
? {
fields: {
where: { isActive: true },
orderBy: { sortOrder: 'asc' },
},
}
: undefined,
},
},
});
// Also get global sections
const globalSections = await this.prisma.profileSection.findMany({
where: {
isGlobal: true,
isActive: true,
},
include: includeFields
? {
fields: {
where: { isActive: true },
orderBy: { sortOrder: 'asc' },
},
}
: undefined,
orderBy: { sortOrder: 'asc' },
});
return {
agentType,
sections: [
...globalSections.map((s) => ({ ...s, isRequired: false, isGlobal: true })),
...assignments.map((a) => ({
...a.section,
isRequired: a.isRequired,
isGlobal: false,
})),
],
};
}
}