import { IsString, IsOptional, IsEnum, IsNumber, IsUUID, Min, Max } from 'class-validator'; import { Type } from 'class-transformer'; import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger'; import { AssigneeType, AssignmentStatus } from '../entities/assignment.entity'; import { ProgressSource } from '../entities/progress-log.entity'; // ───────────────────────────────────────────── // Create Assignment DTO // ───────────────────────────────────────────── export class CreateAssignmentDto { @ApiProperty() @IsUUID('4', { message: 'El ID de definicion debe ser un UUID valido' }) definitionId: string; @ApiPropertyOptional({ enum: AssigneeType, default: AssigneeType.USER }) @IsOptional() @IsEnum(AssigneeType, { message: 'El tipo de asignado debe ser un valor valido' }) assigneeType?: AssigneeType; @ApiPropertyOptional() @IsOptional() @IsUUID('4', { message: 'El ID de usuario debe ser un UUID valido' }) userId?: string; @ApiPropertyOptional() @IsOptional() @IsUUID('4', { message: 'El ID de equipo debe ser un UUID valido' }) teamId?: string; @ApiPropertyOptional({ example: 50000 }) @IsOptional() @IsNumber({}, { message: 'El objetivo personalizado debe ser un numero' }) @Min(0, { message: 'El objetivo personalizado debe ser mayor o igual a 0' }) customTarget?: number; @ApiPropertyOptional() @IsOptional() @IsString({ message: 'Las notas deben ser una cadena de texto' }) notes?: string; } // ───────────────────────────────────────────── // Update Assignment DTO // ───────────────────────────────────────────── export class UpdateAssignmentDto extends PartialType(CreateAssignmentDto) {} // ───────────────────────────────────────────── // Update Assignment Status DTO // ───────────────────────────────────────────── export class UpdateAssignmentStatusDto { @ApiProperty({ enum: AssignmentStatus }) @IsEnum(AssignmentStatus) status: AssignmentStatus; } // ───────────────────────────────────────────── // Update Progress DTO // ───────────────────────────────────────────── export class UpdateProgressDto { @ApiProperty({ example: 25000 }) @IsNumber({}, { message: 'El valor debe ser un numero' }) @Min(0, { message: 'El valor debe ser mayor o igual a 0' }) value: number; @ApiPropertyOptional({ enum: ProgressSource, default: ProgressSource.MANUAL }) @IsOptional() @IsEnum(ProgressSource, { message: 'La fuente de progreso debe ser un valor valido' }) source?: ProgressSource; @ApiPropertyOptional() @IsOptional() @IsString({ message: 'La referencia de fuente debe ser una cadena de texto' }) sourceReference?: string; @ApiPropertyOptional() @IsOptional() @IsString({ message: 'Las notas deben ser una cadena de texto' }) notes?: string; } // ───────────────────────────────────────────── // Assignment Response DTO // ───────────────────────────────────────────── export class AssignmentResponseDto { @ApiProperty() id: string; @ApiProperty() tenantId: string; @ApiProperty() definitionId: string; @ApiProperty({ enum: AssigneeType }) assigneeType: AssigneeType; @ApiPropertyOptional() userId: string | null; @ApiPropertyOptional() teamId: string | null; @ApiPropertyOptional() customTarget: number | null; @ApiProperty() currentValue: number; @ApiProperty() progressPercentage: number; @ApiPropertyOptional() lastUpdatedAt: Date | null; @ApiProperty({ enum: AssignmentStatus }) status: AssignmentStatus; @ApiPropertyOptional() achievedAt: Date | null; @ApiPropertyOptional() notes: string | null; @ApiProperty() createdAt: Date; @ApiProperty() updatedAt: Date; // Nested objects @ApiPropertyOptional() definition?: { id: string; name: string; targetValue: number; unit: string | null; startsAt: Date; endsAt: Date; }; @ApiPropertyOptional() user?: { id: string; email: string; firstName: string | null; lastName: string | null; }; } // ───────────────────────────────────────────── // Assignment Filters DTO // ───────────────────────────────────────────── export class AssignmentFiltersDto { @ApiPropertyOptional() @IsOptional() @IsUUID('4', { message: 'El ID de definicion debe ser un UUID valido' }) definitionId?: string; @ApiPropertyOptional() @IsOptional() @IsUUID('4', { message: 'El ID de usuario debe ser un UUID valido' }) userId?: string; @ApiPropertyOptional({ enum: AssignmentStatus }) @IsOptional() @IsEnum(AssignmentStatus, { message: 'El estado debe ser un valor valido' }) status?: AssignmentStatus; @ApiPropertyOptional({ enum: AssigneeType }) @IsOptional() @IsEnum(AssigneeType, { message: 'El tipo de asignado debe ser un valor valido' }) assigneeType?: AssigneeType; @ApiPropertyOptional({ description: 'Minimum progress percentage' }) @IsOptional() @Type(() => Number) @IsNumber({}, { message: 'El progreso minimo debe ser un numero' }) @Min(0, { message: 'El progreso minimo debe ser mayor o igual a 0%' }) @Max(100, { message: 'El progreso minimo debe ser menor o igual a 100%' }) minProgress?: number; @ApiPropertyOptional({ description: 'Maximum progress percentage' }) @IsOptional() @Type(() => Number) @IsNumber({}, { message: 'El progreso maximo debe ser un numero' }) @Min(0, { message: 'El progreso maximo debe ser mayor o igual a 0%' }) @Max(100, { message: 'El progreso maximo debe ser menor o igual a 100%' }) maxProgress?: number; @ApiPropertyOptional({ example: 'progressPercentage' }) @IsOptional() @IsString() sortBy?: string; @ApiPropertyOptional({ enum: ['ASC', 'DESC'], default: 'DESC' }) @IsOptional() @IsString() sortOrder?: 'ASC' | 'DESC'; @ApiPropertyOptional({ default: 1 }) @IsOptional() @Type(() => Number) @IsNumber() page?: number; @ApiPropertyOptional({ default: 20 }) @IsOptional() @Type(() => Number) @IsNumber() limit?: number; } // ───────────────────────────────────────────── // Progress Log Response DTO // ───────────────────────────────────────────── export class ProgressLogResponseDto { @ApiProperty() id: string; @ApiProperty() assignmentId: string; @ApiPropertyOptional() previousValue: number | null; @ApiProperty() newValue: number; @ApiPropertyOptional() changeAmount: number | null; @ApiProperty({ enum: ProgressSource }) source: ProgressSource; @ApiPropertyOptional() sourceReference: string | null; @ApiPropertyOptional() notes: string | null; @ApiProperty() loggedAt: Date; @ApiPropertyOptional() loggedBy: string | null; } // ───────────────────────────────────────────── // My Goals Summary DTO // ───────────────────────────────────────────── export class MyGoalsSummaryDto { @ApiProperty() totalAssignments: number; @ApiProperty() activeAssignments: number; @ApiProperty() achievedAssignments: number; @ApiProperty() failedAssignments: number; @ApiProperty() averageProgress: number; @ApiProperty() atRiskCount: number; // < 50% progress with > 75% time elapsed } // ───────────────────────────────────────────── // Goal Report DTOs // ───────────────────────────────────────────── export class CompletionReportDto { @ApiProperty() totalGoals: number; @ApiProperty() achievedGoals: number; @ApiProperty() failedGoals: number; @ApiProperty() activeGoals: number; @ApiProperty() completionRate: number; @ApiProperty() averageProgress: number; } export class UserReportDto { @ApiProperty() userId: string; @ApiPropertyOptional() userName: string | null; @ApiProperty() totalAssignments: number; @ApiProperty() achieved: number; @ApiProperty() failed: number; @ApiProperty() active: number; @ApiProperty() averageProgress: number; }