From 91336e5eada26efa5a27dbb70641cb3bd8cc3cc9 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Sat, 11 Apr 2026 23:05:56 +0530 Subject: [PATCH] feat: add optional slug field to UpdateSectionDto and update service to support manual slug overrides --- src/profile-fields/dto/update-section.dto.ts | 18 ++++++++++++++++-- .../profile-sections.service.ts | 19 ++++++++++++------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/profile-fields/dto/update-section.dto.ts b/src/profile-fields/dto/update-section.dto.ts index 98eda8d..371423a 100644 --- a/src/profile-fields/dto/update-section.dto.ts +++ b/src/profile-fields/dto/update-section.dto.ts @@ -1,4 +1,18 @@ -import { PartialType } from '@nestjs/swagger'; +import { PartialType, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString, Matches, MaxLength, MinLength } from 'class-validator'; import { CreateSectionDto } from './create-section.dto'; -export class UpdateSectionDto extends PartialType(CreateSectionDto) {} +export class UpdateSectionDto extends PartialType(CreateSectionDto) { + @ApiPropertyOptional({ + description: 'URL-friendly slug (lowercase letters, digits, hyphens). Auto-regenerated from name if omitted.', + example: 'basic-information', + }) + @IsOptional() + @IsString() + @MinLength(2) + @MaxLength(100) + @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, { + message: 'slug must contain only lowercase letters, digits, and hyphens (no leading/trailing/consecutive hyphens)', + }) + slug?: string; +} diff --git a/src/profile-fields/profile-sections.service.ts b/src/profile-fields/profile-sections.service.ts index 3c5a40f..bcc8d78 100644 --- a/src/profile-fields/profile-sections.service.ts +++ b/src/profile-fields/profile-sections.service.ts @@ -108,22 +108,27 @@ export class ProfileSectionsService { const data: any = { ...updateSectionDto }; - // If name is being updated, update the slug too - if (updateSectionDto.name) { - const newSlug = this.generateSlug(updateSectionDto.name); + // Resolve effective slug: explicit slug from DTO wins; otherwise regenerate from name if name changed + let effectiveSlug: string | undefined; + if (updateSectionDto.slug) { + effectiveSlug = updateSectionDto.slug; + } else if (updateSectionDto.name) { + effectiveSlug = this.generateSlug(updateSectionDto.name); + } - // Check if new slug conflicts with another section + if (effectiveSlug) { + // Check if slug conflicts with another section const existing = await this.prisma.profileSection.findFirst({ where: { - AND: [{ id: { not: id } }, { slug: newSlug }], + AND: [{ id: { not: id } }, { slug: effectiveSlug }], }, }); if (existing) { - throw new ConflictException('A section with this name already exists'); + throw new ConflictException('A section with this slug already exists'); } - data.slug = newSlug; + data.slug = effectiveSlug; } return this.prisma.profileSection.update({