Add employees management module with database schema and validation
- Introduced `EmployeesModule` to manage employee data, including read and write controllers. - Created database migrations for the `employees` table, including constraints and unique indexes. - Implemented validation for employee fields such as name, code, and position with corresponding utility functions. - Developed service and repository layers for handling employee data operations. - Added unit tests for the employees service, repository, and controllers to ensure functionality and correctness. - Updated application module to include the new `EmployeesModule` for better organization.
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import {
|
||||
ArrayNotEmpty,
|
||||
IsArray,
|
||||
IsIn,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
Matches,
|
||||
MaxLength,
|
||||
} from 'class-validator';
|
||||
import { PaginationQueryDto } from '../../../../common/http/response';
|
||||
import { CORE_STATUSES } from '../../../../common/value-objects/status/status';
|
||||
import {
|
||||
EMPLOYEE_CODE_MAX_LENGTH,
|
||||
EMPLOYEE_CODE_PATTERN,
|
||||
EMPLOYEE_NAME_MAX_LENGTH,
|
||||
EMPLOYEE_NAME_PATTERN,
|
||||
EMPLOYEE_POSITIONS,
|
||||
} from '../employee-fields';
|
||||
|
||||
export class CreateEmployeeDto {
|
||||
@ApiProperty({ example: 'EMP_01', maxLength: EMPLOYEE_CODE_MAX_LENGTH })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(EMPLOYEE_CODE_MAX_LENGTH)
|
||||
@Matches(EMPLOYEE_CODE_PATTERN, {
|
||||
message: 'code must contain only letters, numbers, and underscores',
|
||||
})
|
||||
code!: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: 'Ada Lovelace',
|
||||
maxLength: EMPLOYEE_NAME_MAX_LENGTH,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(EMPLOYEE_NAME_MAX_LENGTH)
|
||||
@Matches(EMPLOYEE_NAME_PATTERN, {
|
||||
message: 'name must contain only letters and spaces',
|
||||
})
|
||||
name!: string;
|
||||
|
||||
@ApiProperty({ example: '+6281234567890' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
phone!: string;
|
||||
|
||||
@ApiProperty({ enum: EMPLOYEE_POSITIONS, example: 'sales' })
|
||||
@IsIn([...EMPLOYEE_POSITIONS])
|
||||
position!: string;
|
||||
|
||||
@ApiPropertyOptional({ enum: CORE_STATUSES })
|
||||
@IsOptional()
|
||||
@IsIn([...CORE_STATUSES])
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export class UpdateEmployeeDto {
|
||||
@ApiPropertyOptional({ example: 'EMP_01' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(EMPLOYEE_CODE_MAX_LENGTH)
|
||||
@Matches(EMPLOYEE_CODE_PATTERN, {
|
||||
message: 'code must contain only letters, numbers, and underscores',
|
||||
})
|
||||
code?: string;
|
||||
|
||||
@ApiPropertyOptional({ example: 'Ada Lovelace' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(EMPLOYEE_NAME_MAX_LENGTH)
|
||||
@Matches(EMPLOYEE_NAME_PATTERN, {
|
||||
message: 'name must contain only letters and spaces',
|
||||
})
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional({ example: '+6281234567890' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
phone?: string;
|
||||
|
||||
@ApiPropertyOptional({ enum: EMPLOYEE_POSITIONS })
|
||||
@IsOptional()
|
||||
@IsIn([...EMPLOYEE_POSITIONS])
|
||||
position?: string;
|
||||
}
|
||||
|
||||
export class UpdateEmployeeStatusDto {
|
||||
@ApiProperty({ enum: CORE_STATUSES })
|
||||
@IsIn([...CORE_STATUSES])
|
||||
status!: string;
|
||||
}
|
||||
|
||||
export class BulkIdsDto {
|
||||
@ApiProperty({ type: [String], format: 'uuid' })
|
||||
@IsArray()
|
||||
@ArrayNotEmpty()
|
||||
@IsUUID('4', { each: true })
|
||||
ids!: string[];
|
||||
}
|
||||
|
||||
export class BulkStatusDto {
|
||||
@ApiProperty({ type: [String], format: 'uuid' })
|
||||
@IsArray()
|
||||
@ArrayNotEmpty()
|
||||
@IsUUID('4', { each: true })
|
||||
ids!: string[];
|
||||
|
||||
@ApiProperty({ enum: CORE_STATUSES })
|
||||
@IsIn([...CORE_STATUSES])
|
||||
status!: string;
|
||||
}
|
||||
|
||||
export class ListEmployeesQueryDto extends PaginationQueryDto {
|
||||
@ApiPropertyOptional()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
code?: string;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string;
|
||||
|
||||
@ApiPropertyOptional({ enum: EMPLOYEE_POSITIONS })
|
||||
@IsOptional()
|
||||
@IsIn([...EMPLOYEE_POSITIONS])
|
||||
position?: string;
|
||||
|
||||
@ApiPropertyOptional({ enum: CORE_STATUSES })
|
||||
@IsOptional()
|
||||
@IsIn([...CORE_STATUSES])
|
||||
status?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Case-insensitive match on code or name',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
search?: string;
|
||||
}
|
||||
|
||||
export class EmployeeDto {
|
||||
@ApiProperty({ format: 'uuid' })
|
||||
id!: string;
|
||||
|
||||
@ApiProperty()
|
||||
code!: string;
|
||||
|
||||
@ApiProperty()
|
||||
name!: string;
|
||||
|
||||
@ApiProperty({ example: '+6281234567890' })
|
||||
phone!: string;
|
||||
|
||||
@ApiProperty({ enum: EMPLOYEE_POSITIONS })
|
||||
position!: string;
|
||||
|
||||
@ApiProperty({ enum: CORE_STATUSES })
|
||||
status!: string;
|
||||
|
||||
@ApiProperty({ description: 'Unix ms' })
|
||||
createdAt!: number;
|
||||
|
||||
@ApiProperty({ description: 'Unix ms' })
|
||||
updatedAt!: number;
|
||||
|
||||
@ApiProperty({ format: 'uuid' })
|
||||
createdBy!: string;
|
||||
|
||||
@ApiProperty({ format: 'uuid' })
|
||||
updatedBy!: string;
|
||||
}
|
||||
Reference in New Issue
Block a user