import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsIn, IsOptional, IsString, Matches, MinLength } from 'class-validator'; export class SignContractDto { @ApiProperty({ enum: ['CUSTOMER', 'STAFF', 'DIRECTOR', 'CEO'] }) @IsIn(['CUSTOMER', 'STAFF', 'DIRECTOR', 'CEO']) role!: 'CUSTOMER' | 'STAFF' | 'DIRECTOR' | 'CEO'; @ApiPropertyOptional({ description: 'PNG signature image as base64 (with or without data URL prefix). ' + 'Optional: when omitted, the signer\'s reusable saved signature from their ' + 'profile is used instead.', }) @IsOptional() @IsString() @MinLength(20) signatureImageBase64?: string; @ApiPropertyOptional({ description: 'PNG company stamp/seal image as base64 (with or without data URL prefix). ' + 'Required for the CUSTOMER and STAFF roles — both parties must seal the ' + 'contract before it is fully executed.', }) @IsOptional() @IsString() @MinLength(20) stampImageBase64?: string; @ApiProperty() @IsString() @MinLength(1) signerDisplayName!: string; @ApiPropertyOptional() @IsOptional() @IsString() consentText?: string; // Sudo-mode OTP challenge. Required when role=CUSTOMER: a fresh 6-digit code // SMS'd to the signer's registered phone, verified server-side before the // signature is applied. The number itself is deliberately NOT part of this // DTO — the server resolves it from the authenticated user id, so a caller // cannot redirect the challenge to a phone they control. @ApiPropertyOptional({ description: '6-digit OTP; required when role=CUSTOMER' }) @IsOptional() @IsString() @Matches(/^\d{6}$/, { message: 'otp must be 6 digits' }) otp?: string; }