- Added 4 entities: DefinitionEntity, AssignmentEntity, ProgressLogEntity, MilestoneNotificationEntity - Added DTOs for definitions and assignments - Added services for definitions and assignments CRUD - Added controllers with full REST API endpoints - Added GoalsModule and registered in AppModule Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
274 lines
7.1 KiB
TypeScript
274 lines
7.1 KiB
TypeScript
import { IsString, IsOptional, IsEnum, IsNumber, IsDate, IsArray, IsObject, Min, ValidateNested, IsBoolean } from 'class-validator';
|
|
import { Type } from 'class-transformer';
|
|
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
|
|
import { GoalType, MetricType, PeriodType, DataSource, GoalStatus, SourceConfig, Milestone } from '../entities/definition.entity';
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Milestone DTO
|
|
// ─────────────────────────────────────────────
|
|
|
|
export class MilestoneDto {
|
|
@ApiProperty({ example: 50 })
|
|
@IsNumber()
|
|
@Min(0)
|
|
percentage: number;
|
|
|
|
@ApiProperty({ example: true })
|
|
@IsBoolean()
|
|
notify: boolean;
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Source Config DTO
|
|
// ─────────────────────────────────────────────
|
|
|
|
export class SourceConfigDto {
|
|
@ApiPropertyOptional({ example: 'sales' })
|
|
@IsOptional()
|
|
@IsString()
|
|
module?: string;
|
|
|
|
@ApiPropertyOptional({ example: 'opportunities' })
|
|
@IsOptional()
|
|
@IsString()
|
|
entity?: string;
|
|
|
|
@ApiPropertyOptional({ example: { status: 'won' } })
|
|
@IsOptional()
|
|
@IsObject()
|
|
filter?: Record<string, unknown>;
|
|
|
|
@ApiPropertyOptional({ enum: ['sum', 'count', 'avg'] })
|
|
@IsOptional()
|
|
@IsString()
|
|
aggregation?: 'sum' | 'count' | 'avg';
|
|
|
|
@ApiPropertyOptional({ example: 'amount' })
|
|
@IsOptional()
|
|
@IsString()
|
|
field?: string;
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Create Definition DTO
|
|
// ─────────────────────────────────────────────
|
|
|
|
export class CreateDefinitionDto {
|
|
@ApiProperty({ example: 'Q1 Sales Target' })
|
|
@IsString()
|
|
name: string;
|
|
|
|
@ApiPropertyOptional({ example: 'Achieve $100,000 in closed deals' })
|
|
@IsOptional()
|
|
@IsString()
|
|
description?: string;
|
|
|
|
@ApiPropertyOptional({ example: 'sales' })
|
|
@IsOptional()
|
|
@IsString()
|
|
category?: string;
|
|
|
|
@ApiPropertyOptional({ enum: GoalType, default: GoalType.TARGET })
|
|
@IsOptional()
|
|
@IsEnum(GoalType)
|
|
type?: GoalType;
|
|
|
|
@ApiPropertyOptional({ enum: MetricType, default: MetricType.NUMBER })
|
|
@IsOptional()
|
|
@IsEnum(MetricType)
|
|
metric?: MetricType;
|
|
|
|
@ApiProperty({ example: 100000 })
|
|
@IsNumber()
|
|
@Min(0)
|
|
targetValue: number;
|
|
|
|
@ApiPropertyOptional({ example: 'USD' })
|
|
@IsOptional()
|
|
@IsString()
|
|
unit?: string;
|
|
|
|
@ApiPropertyOptional({ enum: PeriodType, default: PeriodType.MONTHLY })
|
|
@IsOptional()
|
|
@IsEnum(PeriodType)
|
|
period?: PeriodType;
|
|
|
|
@ApiProperty({ example: '2026-01-01' })
|
|
@Type(() => Date)
|
|
@IsDate()
|
|
startsAt: Date;
|
|
|
|
@ApiProperty({ example: '2026-03-31' })
|
|
@Type(() => Date)
|
|
@IsDate()
|
|
endsAt: Date;
|
|
|
|
@ApiPropertyOptional({ enum: DataSource, default: DataSource.MANUAL })
|
|
@IsOptional()
|
|
@IsEnum(DataSource)
|
|
source?: DataSource;
|
|
|
|
@ApiPropertyOptional({ type: SourceConfigDto })
|
|
@IsOptional()
|
|
@ValidateNested()
|
|
@Type(() => SourceConfigDto)
|
|
sourceConfig?: SourceConfigDto;
|
|
|
|
@ApiPropertyOptional({ type: [MilestoneDto] })
|
|
@IsOptional()
|
|
@IsArray()
|
|
@ValidateNested({ each: true })
|
|
@Type(() => MilestoneDto)
|
|
milestones?: MilestoneDto[];
|
|
|
|
@ApiPropertyOptional({ enum: GoalStatus, default: GoalStatus.DRAFT })
|
|
@IsOptional()
|
|
@IsEnum(GoalStatus)
|
|
status?: GoalStatus;
|
|
|
|
@ApiPropertyOptional({ type: [String], example: ['sales', 'q1'] })
|
|
@IsOptional()
|
|
@IsArray()
|
|
@IsString({ each: true })
|
|
tags?: string[];
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Update Definition DTO
|
|
// ─────────────────────────────────────────────
|
|
|
|
export class UpdateDefinitionDto extends PartialType(CreateDefinitionDto) {}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Update Status DTO
|
|
// ─────────────────────────────────────────────
|
|
|
|
export class UpdateDefinitionStatusDto {
|
|
@ApiProperty({ enum: GoalStatus })
|
|
@IsEnum(GoalStatus)
|
|
status: GoalStatus;
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Definition Response DTO
|
|
// ─────────────────────────────────────────────
|
|
|
|
export class DefinitionResponseDto {
|
|
@ApiProperty()
|
|
id: string;
|
|
|
|
@ApiProperty()
|
|
tenantId: string;
|
|
|
|
@ApiProperty()
|
|
name: string;
|
|
|
|
@ApiPropertyOptional()
|
|
description: string | null;
|
|
|
|
@ApiPropertyOptional()
|
|
category: string | null;
|
|
|
|
@ApiProperty({ enum: GoalType })
|
|
type: GoalType;
|
|
|
|
@ApiProperty({ enum: MetricType })
|
|
metric: MetricType;
|
|
|
|
@ApiProperty()
|
|
targetValue: number;
|
|
|
|
@ApiPropertyOptional()
|
|
unit: string | null;
|
|
|
|
@ApiProperty({ enum: PeriodType })
|
|
period: PeriodType;
|
|
|
|
@ApiProperty()
|
|
startsAt: Date;
|
|
|
|
@ApiProperty()
|
|
endsAt: Date;
|
|
|
|
@ApiProperty({ enum: DataSource })
|
|
source: DataSource;
|
|
|
|
@ApiProperty()
|
|
sourceConfig: SourceConfig;
|
|
|
|
@ApiProperty({ type: [MilestoneDto] })
|
|
milestones: Milestone[];
|
|
|
|
@ApiProperty({ enum: GoalStatus })
|
|
status: GoalStatus;
|
|
|
|
@ApiProperty({ type: [String] })
|
|
tags: string[];
|
|
|
|
@ApiProperty()
|
|
createdAt: Date;
|
|
|
|
@ApiProperty()
|
|
updatedAt: Date;
|
|
|
|
@ApiPropertyOptional()
|
|
createdBy: string | null;
|
|
|
|
@ApiPropertyOptional()
|
|
assignmentCount?: number;
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Definition Filters DTO
|
|
// ─────────────────────────────────────────────
|
|
|
|
export class DefinitionFiltersDto {
|
|
@ApiPropertyOptional({ enum: GoalStatus })
|
|
@IsOptional()
|
|
@IsEnum(GoalStatus)
|
|
status?: GoalStatus;
|
|
|
|
@ApiPropertyOptional({ enum: PeriodType })
|
|
@IsOptional()
|
|
@IsEnum(PeriodType)
|
|
period?: PeriodType;
|
|
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@IsString()
|
|
category?: string;
|
|
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@IsString()
|
|
search?: string;
|
|
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@Type(() => Date)
|
|
@IsDate()
|
|
activeOn?: Date;
|
|
|
|
@ApiPropertyOptional({ example: 'createdAt' })
|
|
@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;
|
|
}
|