diff --git a/package-lock.json b/package-lock.json index 1706629..bfa3002 100644 --- a/package-lock.json +++ b/package-lock.json @@ -557,6 +557,11 @@ "undici-types": "~6.21.0" } }, + "@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==" + }, "@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -1083,6 +1088,21 @@ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true }, + "class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "class-validator": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.15.1.tgz", + "integrity": "sha512-LqoS80HBBSCVhz/3KloUly0ovokxpdOLR++Al3J3+dHXWt9sTKlKd4eYtoxhxyUjoe5+UcIM+5k9MIxyBWnRTw==", + "requires": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.22" + } + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -2247,6 +2267,11 @@ "safe-buffer": "^5.0.1" } }, + "libphonenumber-js": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.1.tgz", + "integrity": "sha512-GEw0GLL7YUUA6nv21IsCvVjtI5Ejn84sjbdfQ9KxdbqEVOk1PZh7xejn01EEiniKw+dBeCfim+8MGeuvVuE2BA==" + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -3476,6 +3501,11 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index e3e65d2..c160623 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "@nestjs/platform-express": "^10.4.8", "@nestjs/swagger": "^7.4.2", "@nestjs/typeorm": "^10.0.2", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", "dotenv": "^16.4.7", "pg": "^8.13.1", "reflect-metadata": "^0.2.2", diff --git a/src/main.ts b/src/main.ts index 9e5d393..968a1ae 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,10 +1,19 @@ import 'reflect-metadata'; +import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.useGlobalPipes( + new ValidationPipe({ + forbidNonWhitelisted: true, + transform: true, + whitelist: true, + }), + ); + const swaggerConfig = new DocumentBuilder() .setTitle('Montime API') .setDescription('Local services super app API') diff --git a/src/modules/attribute-definitions/dto/create-attribute-definition.dto.ts b/src/modules/attribute-definitions/dto/create-attribute-definition.dto.ts index 6115c62..600d718 100644 --- a/src/modules/attribute-definitions/dto/create-attribute-definition.dto.ts +++ b/src/modules/attribute-definitions/dto/create-attribute-definition.dto.ts @@ -1,13 +1,53 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { + IsArray, + IsBoolean, + IsEnum, + IsOptional, + IsString, + IsUUID, + Matches, + MaxLength, +} from 'class-validator'; import { AttributeType } from '../../../entities/attribute-definition.entity'; export class CreateAttributeDefinitionDto { + @ApiProperty({ example: 'Has Waiting Room' }) + @IsString() + @MaxLength(120) title!: string; + /** یکتا در محدوده همان دسته شغلی */ + @ApiProperty({ example: 'has-waiting-room' }) + @IsString() + @MaxLength(120) + @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, { + message: 'key must be lowercase words separated by hyphens', + }) key!: string; + + @ApiProperty({ enum: AttributeType, example: AttributeType.BOOLEAN }) + @IsEnum(AttributeType) type!: AttributeType; + + @ApiProperty({ format: 'uuid' }) + @IsUUID() categoryId!: string; + /** برای type=select آرایه گزینه‌ها */ + @ApiPropertyOptional({ type: [String], example: ['small', 'medium', 'large'] }) + @IsOptional() + @IsArray() + @IsString({ each: true }) options?: string[]; + + @ApiPropertyOptional({ example: false }) + @IsOptional() + @IsBoolean() required?: boolean; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isFilterable?: boolean; } diff --git a/src/modules/attribute-definitions/dto/update-attribute-definition.dto.ts b/src/modules/attribute-definitions/dto/update-attribute-definition.dto.ts index 06d4cc6..3e35e3d 100644 --- a/src/modules/attribute-definitions/dto/update-attribute-definition.dto.ts +++ b/src/modules/attribute-definitions/dto/update-attribute-definition.dto.ts @@ -1,11 +1,59 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { + IsArray, + IsBoolean, + IsEnum, + IsOptional, + IsString, + IsUUID, + Matches, + MaxLength, +} from 'class-validator'; import { AttributeType } from '../../../entities/attribute-definition.entity'; export class UpdateAttributeDefinitionDto { + @ApiPropertyOptional({ example: 'Has Waiting Room' }) + @IsOptional() + @IsString() + @MaxLength(120) title?: string; + + @ApiPropertyOptional({ example: 'has-waiting-room' }) + @IsOptional() + @IsString() + @MaxLength(120) + @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, { + message: 'key must be lowercase words separated by hyphens', + }) key?: string; + + @ApiPropertyOptional({ enum: AttributeType, example: AttributeType.BOOLEAN }) + @IsOptional() + @IsEnum(AttributeType) type?: AttributeType; + + @ApiPropertyOptional({ format: 'uuid' }) + @IsOptional() + @IsUUID() categoryId?: string; + + @ApiPropertyOptional({ + type: [String], + example: ['small', 'medium', 'large'], + nullable: true, + }) + @IsOptional() + @IsArray() + @IsString({ each: true }) options?: string[] | null; + + @ApiPropertyOptional({ example: false }) + @IsOptional() + @IsBoolean() required?: boolean; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isFilterable?: boolean; } diff --git a/src/modules/auth/dto/request-otp.dto.ts b/src/modules/auth/dto/request-otp.dto.ts index 502dda1..1dde95e 100644 --- a/src/modules/auth/dto/request-otp.dto.ts +++ b/src/modules/auth/dto/request-otp.dto.ts @@ -1,3 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, Matches } from 'class-validator'; + export class RequestOtpDto { + @ApiProperty({ example: '09122222222' }) + @IsString() + @Matches(/^(\+?\d{10,15}|09\d{9})$/, { + message: 'mobile must be a valid mobile number', + }) mobile!: string; } diff --git a/src/modules/auth/dto/verify-otp.dto.ts b/src/modules/auth/dto/verify-otp.dto.ts index da499aa..edbc558 100644 --- a/src/modules/auth/dto/verify-otp.dto.ts +++ b/src/modules/auth/dto/verify-otp.dto.ts @@ -1,4 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, Matches } from 'class-validator'; + export class VerifyOtpDto { + @ApiProperty({ example: '09122222222' }) + @IsString() + @Matches(/^(\+?\d{10,15}|09\d{9})$/, { + message: 'mobile must be a valid mobile number', + }) mobile!: string; + + @ApiProperty({ example: '123456' }) + @IsString() + @Matches(/^\d{4,8}$/, { + message: 'code must be 4 to 8 digits', + }) code!: string; } diff --git a/src/modules/bookings/dto/create-booking.dto.ts b/src/modules/bookings/dto/create-booking.dto.ts index 992303c..b59a9ab 100644 --- a/src/modules/bookings/dto/create-booking.dto.ts +++ b/src/modules/bookings/dto/create-booking.dto.ts @@ -1,8 +1,27 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsISO8601, IsOptional, IsString, IsUUID, MaxLength } from 'class-validator'; + export class CreateBookingDto { + @ApiProperty({ format: 'uuid' }) + @IsUUID() customerId!: string; + + @ApiProperty({ format: 'uuid' }) + @IsUUID() businessId!: string; + + @ApiProperty({ format: 'uuid' }) + @IsUUID() businessServiceId!: string; + /** ISO 8601 datetime string */ + @ApiProperty({ example: '2026-05-13T10:00:00.000Z' }) + @IsISO8601() scheduledAt!: string; + + @ApiPropertyOptional({ example: 'Test booking', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(500) note?: string | null; } diff --git a/src/modules/bookings/dto/update-booking.dto.ts b/src/modules/bookings/dto/update-booking.dto.ts index 965e62e..76cf6dd 100644 --- a/src/modules/bookings/dto/update-booking.dto.ts +++ b/src/modules/bookings/dto/update-booking.dto.ts @@ -1,7 +1,21 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsISO8601, IsOptional, IsString, MaxLength } from 'class-validator'; import { BookingStatus } from '../../../entities/booking.entity'; export class UpdateBookingDto { + @ApiPropertyOptional({ example: '2026-05-13T10:00:00.000Z' }) + @IsOptional() + @IsISO8601() scheduledAt?: string; + + @ApiPropertyOptional({ enum: BookingStatus, example: BookingStatus.CONFIRMED }) + @IsOptional() + @IsEnum(BookingStatus) status?: BookingStatus; + + @ApiPropertyOptional({ example: 'Customer asked for morning slot', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(500) note?: string | null; } diff --git a/src/modules/business-attribute-values/dto/create-business-attribute-value.dto.ts b/src/modules/business-attribute-values/dto/create-business-attribute-value.dto.ts index 589fad4..5c068eb 100644 --- a/src/modules/business-attribute-values/dto/create-business-attribute-value.dto.ts +++ b/src/modules/business-attribute-values/dto/create-business-attribute-value.dto.ts @@ -1,6 +1,19 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString, IsUUID, MaxLength } from 'class-validator'; + export class CreateBusinessAttributeValueDto { + @ApiProperty({ format: 'uuid' }) + @IsUUID() businessId!: string; + + @ApiProperty({ format: 'uuid' }) + @IsUUID() attributeDefinitionId!: string; + /** مقدار به صورت رشته ذخیره می‌شود؛ برای boolean مثلا true/false */ + @ApiPropertyOptional({ example: 'true', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(500) value?: string | null; } diff --git a/src/modules/business-attribute-values/dto/update-business-attribute-value.dto.ts b/src/modules/business-attribute-values/dto/update-business-attribute-value.dto.ts index 8c80d34..5e5a713 100644 --- a/src/modules/business-attribute-values/dto/update-business-attribute-value.dto.ts +++ b/src/modules/business-attribute-values/dto/update-business-attribute-value.dto.ts @@ -1,3 +1,10 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString, MaxLength } from 'class-validator'; + export class UpdateBusinessAttributeValueDto { + @ApiPropertyOptional({ example: 'true', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(500) value?: string | null; } diff --git a/src/modules/business-categories/dto/create-business-category.dto.ts b/src/modules/business-categories/dto/create-business-category.dto.ts index 45142ca..abe7057 100644 --- a/src/modules/business-categories/dto/create-business-category.dto.ts +++ b/src/modules/business-categories/dto/create-business-category.dto.ts @@ -1,5 +1,22 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional, IsString, Matches, MaxLength } from 'class-validator'; + export class CreateBusinessCategoryDto { + @ApiProperty({ example: 'Car Wash' }) + @IsString() + @MaxLength(150) title!: string; + + @ApiProperty({ example: 'car-wash' }) + @IsString() + @MaxLength(120) + @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, { + message: 'slug must be lowercase words separated by hyphens', + }) slug!: string; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isActive?: boolean; } diff --git a/src/modules/business-categories/dto/update-business-category.dto.ts b/src/modules/business-categories/dto/update-business-category.dto.ts index 9d6fde6..feefbf4 100644 --- a/src/modules/business-categories/dto/update-business-category.dto.ts +++ b/src/modules/business-categories/dto/update-business-category.dto.ts @@ -1,5 +1,24 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional, IsString, Matches, MaxLength } from 'class-validator'; + export class UpdateBusinessCategoryDto { + @ApiPropertyOptional({ example: 'Car Wash' }) + @IsOptional() + @IsString() + @MaxLength(150) title?: string; + + @ApiPropertyOptional({ example: 'car-wash' }) + @IsOptional() + @IsString() + @MaxLength(120) + @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, { + message: 'slug must be lowercase words separated by hyphens', + }) slug?: string; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isActive?: boolean; } diff --git a/src/modules/business-services/dto/create-business-service.dto.ts b/src/modules/business-services/dto/create-business-service.dto.ts index 95c78e0..bda7db8 100644 --- a/src/modules/business-services/dto/create-business-service.dto.ts +++ b/src/modules/business-services/dto/create-business-service.dto.ts @@ -1,9 +1,31 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsInt, IsNumber, IsOptional, IsUUID, Max, Min } from 'class-validator'; + export class CreateBusinessServiceDto { + @ApiProperty({ format: 'uuid' }) + @IsUUID() businessId!: string; + + @ApiProperty({ format: 'uuid' }) + @IsUUID() serviceTemplateId!: string; + /** قیمت به واحد پول (عدد اعشاری مجاز است) */ + @ApiProperty({ example: 100000 }) + @IsNumber({ maxDecimalPlaces: 2 }) + @Min(0) price!: number; + /** اگر خالی باشد از template.defaultDurationMinutes استفاده می‌شود */ + @ApiPropertyOptional({ example: 30, nullable: true }) + @IsOptional() + @IsInt() + @Min(1) + @Max(1440) durationMinutes?: number | null; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isActive?: boolean; } diff --git a/src/modules/business-services/dto/update-business-service.dto.ts b/src/modules/business-services/dto/update-business-service.dto.ts index d9755a3..57764a6 100644 --- a/src/modules/business-services/dto/update-business-service.dto.ts +++ b/src/modules/business-services/dto/update-business-service.dto.ts @@ -1,6 +1,27 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsInt, IsNumber, IsOptional, IsUUID, Max, Min } from 'class-validator'; + export class UpdateBusinessServiceDto { + @ApiPropertyOptional({ format: 'uuid' }) + @IsOptional() + @IsUUID() serviceTemplateId?: string; + + @ApiPropertyOptional({ example: 100000 }) + @IsOptional() + @IsNumber({ maxDecimalPlaces: 2 }) + @Min(0) price?: number; + + @ApiPropertyOptional({ example: 30, nullable: true }) + @IsOptional() + @IsInt() + @Min(1) + @Max(1440) durationMinutes?: number | null; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isActive?: boolean; } diff --git a/src/modules/businesses/dto/assign-business-owner.dto.ts b/src/modules/businesses/dto/assign-business-owner.dto.ts index 2c16c03..906cca4 100644 --- a/src/modules/businesses/dto/assign-business-owner.dto.ts +++ b/src/modules/businesses/dto/assign-business-owner.dto.ts @@ -1,4 +1,17 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString, Matches, MaxLength } from 'class-validator'; + export class AssignBusinessOwnerDto { + @ApiProperty({ example: '09111111111' }) + @IsString() + @Matches(/^(\+?\d{10,15}|09\d{9})$/, { + message: 'mobile must be a valid mobile number', + }) mobile!: string; + + @ApiPropertyOptional({ example: 'Business Admin', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(150) fullName?: string | null; } diff --git a/src/modules/businesses/dto/create-business.dto.ts b/src/modules/businesses/dto/create-business.dto.ts index e9fb391..7fd2987 100644 --- a/src/modules/businesses/dto/create-business.dto.ts +++ b/src/modules/businesses/dto/create-business.dto.ts @@ -1,12 +1,68 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { + IsBoolean, + IsLatitude, + IsLongitude, + IsOptional, + IsString, + IsUUID, + Matches, + MaxLength, +} from 'class-validator'; + export class CreateBusinessDto { + @ApiProperty({ example: 'Shine Carwash' }) + @IsString() + @MaxLength(150) title!: string; + + @ApiProperty({ example: 'shine-carwash' }) + @IsString() + @MaxLength(120) + @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, { + message: 'slug must be lowercase words separated by hyphens', + }) slug!: string; + + @ApiProperty({ format: 'uuid' }) + @IsUUID() categoryId!: string; + + @ApiPropertyOptional({ example: 'Premium carwash service' }) + @IsOptional() + @IsString() + @MaxLength(500) description?: string; + + @ApiPropertyOptional({ example: '09123456789' }) + @IsOptional() + @IsString() + @MaxLength(20) contactPhone?: string; + + @ApiPropertyOptional({ example: 'Main street' }) + @IsOptional() + @IsString() + @MaxLength(500) address?: string; + + @ApiPropertyOptional({ example: 35.6892 }) + @IsOptional() + @IsLatitude() latitude?: number; + + @ApiPropertyOptional({ example: 51.389 }) + @IsOptional() + @IsLongitude() longitude?: number; + + @ApiPropertyOptional({ format: 'uuid' }) + @IsOptional() + @IsUUID() ownerId?: string; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isActive?: boolean; } diff --git a/src/modules/businesses/dto/update-business.dto.ts b/src/modules/businesses/dto/update-business.dto.ts index db97b8d..2063b7a 100644 --- a/src/modules/businesses/dto/update-business.dto.ts +++ b/src/modules/businesses/dto/update-business.dto.ts @@ -1,12 +1,71 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { + IsBoolean, + IsLatitude, + IsLongitude, + IsOptional, + IsString, + IsUUID, + Matches, + MaxLength, +} from 'class-validator'; + export class UpdateBusinessDto { + @ApiPropertyOptional({ example: 'Shine Carwash' }) + @IsOptional() + @IsString() + @MaxLength(150) title?: string; + + @ApiPropertyOptional({ example: 'shine-carwash' }) + @IsOptional() + @IsString() + @MaxLength(120) + @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, { + message: 'slug must be lowercase words separated by hyphens', + }) slug?: string; + + @ApiPropertyOptional({ format: 'uuid' }) + @IsOptional() + @IsUUID() categoryId?: string; + + @ApiPropertyOptional({ example: 'Premium carwash service', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(500) description?: string | null; + + @ApiPropertyOptional({ example: '09123456789', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(20) contactPhone?: string | null; + + @ApiPropertyOptional({ example: 'Main street', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(500) address?: string | null; + + @ApiPropertyOptional({ example: 35.6892, nullable: true }) + @IsOptional() + @IsLatitude() latitude?: number | null; + + @ApiPropertyOptional({ example: 51.389, nullable: true }) + @IsOptional() + @IsLongitude() longitude?: number | null; + + @ApiPropertyOptional({ format: 'uuid', nullable: true }) + @IsOptional() + @IsUUID() ownerId?: string | null; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isActive?: boolean; } diff --git a/src/modules/service-templates/dto/create-service-template.dto.ts b/src/modules/service-templates/dto/create-service-template.dto.ts index 3841f30..582a2dc 100644 --- a/src/modules/service-templates/dto/create-service-template.dto.ts +++ b/src/modules/service-templates/dto/create-service-template.dto.ts @@ -1,8 +1,49 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { + IsBoolean, + IsInt, + IsOptional, + IsString, + IsUUID, + Matches, + Max, + MaxLength, + Min, +} from 'class-validator'; + export class CreateServiceTemplateDto { + @ApiProperty({ example: 'Basic Wash' }) + @IsString() + @MaxLength(150) title!: string; + + @ApiProperty({ example: 'basic-wash' }) + @IsString() + @MaxLength(120) + @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, { + message: 'slug must be lowercase words separated by hyphens', + }) slug!: string; + + @ApiProperty({ format: 'uuid' }) + @IsUUID() categoryId!: string; + + @ApiPropertyOptional({ example: 'Exterior wash' }) + @IsOptional() + @IsString() + @MaxLength(500) description?: string; + + @ApiPropertyOptional({ example: 30 }) + @IsOptional() + @IsInt() + @Min(1) + @Max(1440) defaultDurationMinutes?: number; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isActive?: boolean; } diff --git a/src/modules/service-templates/dto/update-service-template.dto.ts b/src/modules/service-templates/dto/update-service-template.dto.ts index e06b126..3260a7e 100644 --- a/src/modules/service-templates/dto/update-service-template.dto.ts +++ b/src/modules/service-templates/dto/update-service-template.dto.ts @@ -1,8 +1,52 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { + IsBoolean, + IsInt, + IsOptional, + IsString, + IsUUID, + Matches, + Max, + MaxLength, + Min, +} from 'class-validator'; + export class UpdateServiceTemplateDto { + @ApiPropertyOptional({ example: 'Basic Wash' }) + @IsOptional() + @IsString() + @MaxLength(150) title?: string; + + @ApiPropertyOptional({ example: 'basic-wash' }) + @IsOptional() + @IsString() + @MaxLength(120) + @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, { + message: 'slug must be lowercase words separated by hyphens', + }) slug?: string; + + @ApiPropertyOptional({ format: 'uuid' }) + @IsOptional() + @IsUUID() categoryId?: string; + + @ApiPropertyOptional({ example: 'Exterior wash', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(500) description?: string | null; + + @ApiPropertyOptional({ example: 30 }) + @IsOptional() + @IsInt() + @Min(1) + @Max(1440) defaultDurationMinutes?: number; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isActive?: boolean; } diff --git a/src/modules/users/dto/update-user.dto.ts b/src/modules/users/dto/update-user.dto.ts index af75af4..f5a9eaa 100644 --- a/src/modules/users/dto/update-user.dto.ts +++ b/src/modules/users/dto/update-user.dto.ts @@ -1,7 +1,21 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsOptional, IsString, MaxLength } from 'class-validator'; import { UserRole } from '../../../entities/user.entity'; export class UpdateUserDto { + @ApiPropertyOptional({ example: 'Mahdi', nullable: true }) + @IsOptional() + @IsString() + @MaxLength(150) fullName?: string | null; + + @ApiPropertyOptional({ enum: UserRole, example: UserRole.BUSINESS_OWNER }) + @IsOptional() + @IsEnum(UserRole) role?: UserRole; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() isActive?: boolean; }