Add standalone Leads module for prospect outreach before customers.

Import large Excel lists with duplicate checks, assign and kanban follow-up, convert to Person when ready; also fix OTP mobile mask order and SMS brand name to منتکت.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-27 17:05:11 +03:30
co-authored by Cursor
parent 22980e5152
commit c079295ff4
24 changed files with 4357 additions and 9 deletions
+119
View File
@@ -0,0 +1,119 @@
import {
IsArray,
IsBoolean,
IsIn,
IsNumber,
IsOptional,
IsString,
MinLength,
} from 'class-validator';
export class CreateLeadDto {
@IsString()
@MinLength(1)
title!: string;
@IsString()
@MinLength(5)
mobile!: string;
@IsOptional() @IsString() contactName?: string;
@IsOptional() @IsString() phone?: string;
@IsOptional() @IsString() city?: string;
@IsOptional() @IsString() region?: string;
@IsOptional() @IsString() trade?: string;
@IsOptional() @IsString() source?: string;
@IsOptional() @IsString() notes?: string;
@IsOptional() @IsNumber() estimatedValue?: number;
@IsOptional() @IsString() stageId?: string;
@IsOptional() @IsString() assigneeId?: string | null;
@IsOptional() @IsString() nextFollowUpAt?: string | null;
}
export class UpdateLeadDto {
@IsOptional() @IsString() @MinLength(1) title?: string;
@IsOptional() @IsString() @MinLength(5) mobile?: string;
@IsOptional() @IsString() contactName?: string | null;
@IsOptional() @IsString() phone?: string | null;
@IsOptional() @IsString() city?: string | null;
@IsOptional() @IsString() region?: string | null;
@IsOptional() @IsString() trade?: string | null;
@IsOptional() @IsString() source?: string | null;
@IsOptional() @IsString() notes?: string | null;
@IsOptional() @IsNumber() estimatedValue?: number | null;
@IsOptional() @IsString() stageId?: string;
@IsOptional() @IsString() assigneeId?: string | null;
@IsOptional() @IsString() nextFollowUpAt?: string | null;
@IsOptional() @IsString() lastContactAt?: string | null;
}
export class MoveLeadDto {
@IsString() stageId!: string;
@IsOptional() @IsNumber() position?: number;
}
export class AssignLeadsDto {
@IsArray() @IsString({ each: true }) leadIds!: string[];
@IsString() assigneeId!: string;
}
export class BulkLeadStageDto {
@IsArray() @IsString({ each: true }) leadIds!: string[];
@IsString() stageId!: string;
}
export class AutoAssignLeadsDto {
@IsOptional() @IsIn(['round_robin', 'equal_split']) rule?: string;
@IsOptional() @IsArray() @IsString({ each: true }) leadIds?: string[];
@IsOptional() @IsArray() @IsString({ each: true }) assigneeIds?: string[];
@IsOptional() @IsBoolean() onlyUnassigned?: boolean;
}
export class LeadCallLogDto {
@IsOptional() @IsString() outcome?: string;
@IsOptional() @IsString() body?: string;
@IsOptional() @IsString() nextFollowUpAt?: string | null;
@IsOptional() @IsString() stageId?: string;
}
export class LeadNoteDto {
@IsString() @MinLength(1) body!: string;
}
export class LeadFollowUpDto {
@IsString() nextFollowUpAt!: string;
@IsOptional() @IsString() body?: string;
}
export class ConvertLeadDto {
@IsOptional() @IsString() firstName?: string;
@IsOptional() @IsString() lastName?: string;
}
export class CreateLeadStageDto {
@IsString() @MinLength(1) name!: string;
@IsOptional() @IsString() color?: string;
@IsOptional() @IsBoolean() showOnBoard?: boolean;
@IsOptional() @IsBoolean() isConverted?: boolean;
@IsOptional() @IsBoolean() isLost?: boolean;
@IsOptional() @IsBoolean() isUnqualified?: boolean;
}
export class UpdateLeadStageDto {
@IsOptional() @IsString() @MinLength(1) name?: string;
@IsOptional() @IsString() color?: string;
@IsOptional() @IsBoolean() showOnBoard?: boolean;
@IsOptional() @IsBoolean() isConverted?: boolean;
@IsOptional() @IsBoolean() isLost?: boolean;
@IsOptional() @IsBoolean() isUnqualified?: boolean;
@IsOptional() @IsNumber() position?: number;
}
export class ReorderLeadStagesDto {
@IsArray() @IsString({ each: true }) stageIds!: string[];
}
export class ImportLeadsDto {
@IsArray() rows!: Array<Record<string, unknown>>;
@IsOptional() @IsString() fileName?: string;
}