Add modules for case studies, FAQs, podcasts, reading materials, and training materials with corresponding controllers, services, and DTOs
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
Query,
|
||||
ParseIntPipe,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import { TrainingMaterialsService } from '../services/training-materials.service';
|
||||
import { CreateTrainingMaterialDto } from '../dto/create-training-material.dto';
|
||||
import { UpdateTrainingMaterialDto } from '../dto/update-training-material.dto';
|
||||
import { TrainingMaterialResponseDto } from '../dto/training-material-response.dto';
|
||||
import { PaginationDto } from '../../../common/dto/pagination.dto';
|
||||
|
||||
@ApiTags('training-materials')
|
||||
@Controller('training-materials')
|
||||
export class TrainingMaterialsController {
|
||||
constructor(private readonly trainingMaterialsService: TrainingMaterialsService) {}
|
||||
|
||||
@Post()
|
||||
// @UseGuards(JwtAuthGuard, RolesGuard)
|
||||
// @Roles('ADMIN', 'HR')
|
||||
// @ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Create a new training material' })
|
||||
@ApiResponse({ status: 201, description: 'Training material created successfully', type: TrainingMaterialResponseDto })
|
||||
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
||||
@ApiResponse({ status: 403, description: 'Forbidden' })
|
||||
async create(@Body() createTrainingMaterialDto: CreateTrainingMaterialDto): Promise<TrainingMaterialResponseDto> {
|
||||
return this.trainingMaterialsService.create(createTrainingMaterialDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Get all training materials with pagination' })
|
||||
@ApiResponse({ status: 200, description: 'Training materials retrieved successfully' })
|
||||
async findAll(@Query() paginationDto: PaginationDto) {
|
||||
return this.trainingMaterialsService.findAll(paginationDto);
|
||||
}
|
||||
|
||||
@Get('search/title')
|
||||
@ApiOperation({ summary: 'Search training materials by title' })
|
||||
@ApiResponse({ status: 200, description: 'Training materials retrieved successfully' })
|
||||
async searchByTitle(
|
||||
@Query('q') title: string,
|
||||
@Query() paginationDto: PaginationDto,
|
||||
) {
|
||||
return this.trainingMaterialsService.searchByTitle(title, paginationDto);
|
||||
}
|
||||
|
||||
@Get('search/description')
|
||||
@ApiOperation({ summary: 'Search training materials by description' })
|
||||
@ApiResponse({ status: 200, description: 'Training materials retrieved successfully' })
|
||||
async searchByDescription(
|
||||
@Query('q') description: string,
|
||||
@Query() paginationDto: PaginationDto,
|
||||
) {
|
||||
return this.trainingMaterialsService.searchByDescription(description, paginationDto);
|
||||
}
|
||||
|
||||
@Get('tag/:tag')
|
||||
@ApiOperation({ summary: 'Get training materials by tag' })
|
||||
@ApiResponse({ status: 200, description: 'Training materials retrieved successfully' })
|
||||
async findByTag(
|
||||
@Param('tag') tag: string,
|
||||
@Query() paginationDto: PaginationDto,
|
||||
) {
|
||||
return this.trainingMaterialsService.findByTag(tag, paginationDto);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get training material by ID' })
|
||||
@ApiResponse({ status: 200, description: 'Training material retrieved successfully', type: TrainingMaterialResponseDto })
|
||||
@ApiResponse({ status: 404, description: 'Training material not found' })
|
||||
async findOne(@Param('id', ParseIntPipe) id: number): Promise<TrainingMaterialResponseDto> {
|
||||
return this.trainingMaterialsService.findOne(id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
// @UseGuards(JwtAuthGuard, RolesGuard)
|
||||
// @Roles('ADMIN', 'HR')
|
||||
// @ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Update training material' })
|
||||
@ApiResponse({ status: 200, description: 'Training material updated successfully', type: TrainingMaterialResponseDto })
|
||||
@ApiResponse({ status: 404, description: 'Training material not found' })
|
||||
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
||||
@ApiResponse({ status: 403, description: 'Forbidden' })
|
||||
async update(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() updateTrainingMaterialDto: UpdateTrainingMaterialDto,
|
||||
): Promise<TrainingMaterialResponseDto> {
|
||||
return this.trainingMaterialsService.update(id, updateTrainingMaterialDto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
// @UseGuards(JwtAuthGuard, RolesGuard)
|
||||
// @Roles('ADMIN', 'HR')
|
||||
// @ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Delete training material (soft delete)' })
|
||||
@ApiResponse({ status: 200, description: 'Training material deleted successfully' })
|
||||
@ApiResponse({ status: 404, description: 'Training material not found' })
|
||||
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
||||
@ApiResponse({ status: 403, description: 'Forbidden' })
|
||||
async remove(@Param('id', ParseIntPipe) id: number): Promise<{ message: string }> {
|
||||
await this.trainingMaterialsService.remove(id);
|
||||
return { message: 'Training material deleted successfully' };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { IsString, IsOptional, IsArray, IsUrl } from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export class CreateTrainingMaterialDto {
|
||||
@ApiProperty({ description: 'Training material title' })
|
||||
@IsString()
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: 'Training material description' })
|
||||
@IsString()
|
||||
description: string;
|
||||
|
||||
@ApiProperty({ description: 'Training material file URL' })
|
||||
@IsUrl()
|
||||
fileUrl: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Training material tags', type: [String] })
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
tags?: string[];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class TrainingMaterialResponseDto {
|
||||
@ApiProperty({ description: 'Training material ID' })
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ description: 'Training material title' })
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: 'Training material description' })
|
||||
description: string;
|
||||
|
||||
@ApiProperty({ description: 'Training material file URL' })
|
||||
fileUrl: string;
|
||||
|
||||
@ApiProperty({ description: 'Training material tags', type: [String] })
|
||||
tags: string[];
|
||||
|
||||
@ApiProperty({ description: 'Creation date' })
|
||||
createdAt: Date;
|
||||
|
||||
@ApiProperty({ description: 'Last update date' })
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateTrainingMaterialDto } from './create-training-material.dto';
|
||||
|
||||
export class UpdateTrainingMaterialDto extends PartialType(CreateTrainingMaterialDto) {}
|
||||
@@ -0,0 +1,223 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../../../common/database/prisma.service';
|
||||
import { CreateTrainingMaterialDto } from '../dto/create-training-material.dto';
|
||||
import { UpdateTrainingMaterialDto } from '../dto/update-training-material.dto';
|
||||
import { TrainingMaterialResponseDto } from '../dto/training-material-response.dto';
|
||||
import { PaginationDto } from '../../../common/dto/pagination.dto';
|
||||
import { paginate } from '../../../common/utils/pagination.util';
|
||||
|
||||
@Injectable()
|
||||
export class TrainingMaterialsService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async create(createTrainingMaterialDto: CreateTrainingMaterialDto): Promise<TrainingMaterialResponseDto> {
|
||||
const trainingMaterial = await this.prisma.trainingMaterials.create({
|
||||
data: {
|
||||
...createTrainingMaterialDto,
|
||||
tags: createTrainingMaterialDto.tags || [],
|
||||
},
|
||||
});
|
||||
|
||||
return this.mapToResponseDto(trainingMaterial);
|
||||
}
|
||||
|
||||
async findAll(paginationDto: PaginationDto) {
|
||||
const { page, limit } = paginationDto;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const [trainingMaterials, total] = await Promise.all([
|
||||
this.prisma.trainingMaterials.findMany({
|
||||
where: {
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
}),
|
||||
this.prisma.trainingMaterials.count({
|
||||
where: {
|
||||
deletedAt: null,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const mappedTrainingMaterials = trainingMaterials.map(trainingMaterial => this.mapToResponseDto(trainingMaterial));
|
||||
|
||||
return paginate(mappedTrainingMaterials, { page, limit }, total);
|
||||
}
|
||||
|
||||
async findOne(id: number): Promise<TrainingMaterialResponseDto> {
|
||||
const trainingMaterial = await this.prisma.trainingMaterials.findFirst({
|
||||
where: {
|
||||
id,
|
||||
deletedAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
if (!trainingMaterial) {
|
||||
throw new NotFoundException(`Training material with ID ${id} not found`);
|
||||
}
|
||||
|
||||
return this.mapToResponseDto(trainingMaterial);
|
||||
}
|
||||
|
||||
async update(id: number, updateTrainingMaterialDto: UpdateTrainingMaterialDto): Promise<TrainingMaterialResponseDto> {
|
||||
const existingTrainingMaterial = await this.prisma.trainingMaterials.findFirst({
|
||||
where: {
|
||||
id,
|
||||
deletedAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingTrainingMaterial) {
|
||||
throw new NotFoundException(`Training material with ID ${id} not found`);
|
||||
}
|
||||
|
||||
const trainingMaterial = await this.prisma.trainingMaterials.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...updateTrainingMaterialDto,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return this.mapToResponseDto(trainingMaterial);
|
||||
}
|
||||
|
||||
async remove(id: number): Promise<void> {
|
||||
const existingTrainingMaterial = await this.prisma.trainingMaterials.findFirst({
|
||||
where: {
|
||||
id,
|
||||
deletedAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingTrainingMaterial) {
|
||||
throw new NotFoundException(`Training material with ID ${id} not found`);
|
||||
}
|
||||
|
||||
await this.prisma.trainingMaterials.update({
|
||||
where: { id },
|
||||
data: {
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async findByTag(tag: string, paginationDto: PaginationDto) {
|
||||
const { page, limit } = paginationDto;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const [trainingMaterials, total] = await Promise.all([
|
||||
this.prisma.trainingMaterials.findMany({
|
||||
where: {
|
||||
tags: {
|
||||
has: tag,
|
||||
},
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
}),
|
||||
this.prisma.trainingMaterials.count({
|
||||
where: {
|
||||
tags: {
|
||||
has: tag,
|
||||
},
|
||||
deletedAt: null,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const mappedTrainingMaterials = trainingMaterials.map(trainingMaterial => this.mapToResponseDto(trainingMaterial));
|
||||
|
||||
return paginate(mappedTrainingMaterials, { page, limit }, total);
|
||||
}
|
||||
|
||||
async searchByTitle(title: string, paginationDto: PaginationDto) {
|
||||
const { page, limit } = paginationDto;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const [trainingMaterials, total] = await Promise.all([
|
||||
this.prisma.trainingMaterials.findMany({
|
||||
where: {
|
||||
title: {
|
||||
contains: title,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
}),
|
||||
this.prisma.trainingMaterials.count({
|
||||
where: {
|
||||
title: {
|
||||
contains: title,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
deletedAt: null,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const mappedTrainingMaterials = trainingMaterials.map(trainingMaterial => this.mapToResponseDto(trainingMaterial));
|
||||
|
||||
return paginate(mappedTrainingMaterials, { page, limit }, total);
|
||||
}
|
||||
|
||||
async searchByDescription(description: string, paginationDto: PaginationDto) {
|
||||
const { page, limit } = paginationDto;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const [trainingMaterials, total] = await Promise.all([
|
||||
this.prisma.trainingMaterials.findMany({
|
||||
where: {
|
||||
description: {
|
||||
contains: description,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
}),
|
||||
this.prisma.trainingMaterials.count({
|
||||
where: {
|
||||
description: {
|
||||
contains: description,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
deletedAt: null,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const mappedTrainingMaterials = trainingMaterials.map(trainingMaterial => this.mapToResponseDto(trainingMaterial));
|
||||
|
||||
return paginate(mappedTrainingMaterials, { page, limit }, total);
|
||||
}
|
||||
|
||||
private mapToResponseDto(trainingMaterial: any): TrainingMaterialResponseDto {
|
||||
return {
|
||||
id: trainingMaterial.id,
|
||||
title: trainingMaterial.title,
|
||||
description: trainingMaterial.description,
|
||||
fileUrl: trainingMaterial.fileUrl,
|
||||
tags: trainingMaterial.tags,
|
||||
createdAt: trainingMaterial.createdAt,
|
||||
updatedAt: trainingMaterial.updatedAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
12
src/modules/training-materials/training-materials.module.ts
Normal file
12
src/modules/training-materials/training-materials.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TrainingMaterialsController } from './controllers/training-materials.controller';
|
||||
import { TrainingMaterialsService } from './services/training-materials.service';
|
||||
import { PrismaModule } from '../../common/database/prisma.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [TrainingMaterialsController],
|
||||
providers: [TrainingMaterialsService],
|
||||
exports: [TrainingMaterialsService],
|
||||
})
|
||||
export class TrainingMaterialsModule {}
|
||||
Reference in New Issue
Block a user