import { IsBoolean, IsDateString, IsEnum, IsIn, IsInt, IsOptional, IsString, IsUUID, Max, Min, } from 'class-validator'; import { Transform } from 'class-transformer'; import { LabTaskStatus, LabCaseOrigin } from '@prisma/client'; const toBoolean = ({ value }: { value: unknown }) => { if (typeof value === 'boolean') return value; if (value === 'true' || value === '1') return true; if (value === 'false' || value === '0') return false; return value; }; export class UpdateLabTaskDto { @IsEnum(LabTaskStatus) status: LabTaskStatus; } export type TaskSortField = | 'date' | 'status' | 'clinic' | 'patient' | 'important' | 'prosthesis' | 'taskType' | 'dueDate'; export class ListLabTasksDto { @IsOptional() @IsString() q?: string; @IsOptional() @IsUUID() clinicOrganizationId?: string; @IsOptional() @IsEnum(LabTaskStatus) status?: LabTaskStatus; @IsOptional() @Transform(toBoolean) @IsBoolean() completed?: boolean; @IsOptional() @Transform(toBoolean) @IsBoolean() important?: boolean; /** When true, important lab cases are listed before others (does not hide non-important). */ @IsOptional() @Transform(toBoolean) @IsBoolean() pinImportant?: boolean; @IsOptional() @IsDateString() sentFrom?: string; @IsOptional() @IsDateString() sentTo?: string; /** Workflow step code (e.g. design) completed within the prosthesis group. */ @IsOptional() @IsString() stepCompleted?: string; /** Narrow list to a single lab case (e.g. show-in-case navigation). */ @IsOptional() @IsUUID() labCaseId?: string; /** When true, only tasks assigned to the current user. */ @IsOptional() @Transform(toBoolean) @IsBoolean() assignedToMe?: boolean; /** Past due date with at least one in-progress task on the case. */ @IsOptional() @Transform(toBoolean) @IsBoolean() overdue?: boolean; /** When true, only tasks with no assignee. */ @IsOptional() @Transform(toBoolean) @IsBoolean() unassignedOnly?: boolean; /** Narrow list to a single prosthesis type code. */ @IsOptional() @IsString() prosthesisTypeCode?: string; /** CLINIC_DISPATCH = received from clinic; LAB_INTERNAL = generated in the lab. */ @IsOptional() @IsEnum(LabCaseOrigin) origin?: LabCaseOrigin; @IsOptional() @IsIn(['date', 'status', 'clinic', 'patient', 'important', 'prosthesis', 'taskType', 'dueDate']) sortBy?: TaskSortField; @IsOptional() @IsIn(['asc', 'desc']) sortDir?: 'asc' | 'desc'; @IsOptional() @Transform(({ value }) => Number(value)) @IsInt() @Min(1) page = 1; @IsOptional() @Transform(({ value }) => Number(value)) @IsInt() @Min(1) @Max(100) limit = 50; } /** Same filters as list (no page) — used to find which page contains a task. */ export class LocateTaskPageDto { @IsUUID() taskId: string; @IsOptional() @IsString() q?: string; @IsOptional() @IsUUID() clinicOrganizationId?: string; @IsOptional() @IsEnum(LabTaskStatus) status?: LabTaskStatus; @IsOptional() @Transform(toBoolean) @IsBoolean() completed?: boolean; @IsOptional() @Transform(toBoolean) @IsBoolean() important?: boolean; @IsOptional() @Transform(toBoolean) @IsBoolean() pinImportant?: boolean; @IsOptional() @IsDateString() sentFrom?: string; @IsOptional() @IsDateString() sentTo?: string; @IsOptional() @IsString() stepCompleted?: string; /** When true, only tasks assigned to the current user. */ @IsOptional() @Transform(toBoolean) @IsBoolean() assignedToMe?: boolean; /** Past due date with at least one in-progress task on the case. */ @IsOptional() @Transform(toBoolean) @IsBoolean() overdue?: boolean; /** When true, only tasks with no assignee. */ @IsOptional() @Transform(toBoolean) @IsBoolean() unassignedOnly?: boolean; /** Narrow list to a single prosthesis type code. */ @IsOptional() @IsString() prosthesisTypeCode?: string; @IsOptional() @IsEnum(LabCaseOrigin) origin?: LabCaseOrigin; @IsOptional() @IsIn(['date', 'status', 'clinic', 'patient', 'important', 'prosthesis', 'taskType', 'dueDate']) sortBy?: TaskSortField; @IsOptional() @IsIn(['asc', 'desc']) sortDir?: 'asc' | 'desc'; @IsOptional() @Transform(({ value }) => Number(value)) @IsInt() @Min(1) @Max(100) limit = 50; }