formulario de capacitacion
This commit is contained in:
@@ -13,13 +13,13 @@ import { ThrottlerGuard } from '@nestjs/throttler';
|
||||
import { DrizzleModule } from './database/drizzle.module';
|
||||
import { AuthModule } from './features/auth/auth.module';
|
||||
import { ConfigurationsModule } from './features/configurations/configurations.module';
|
||||
import { LocationModule} from './features/location/location.module'
|
||||
import { LocationModule } from './features/location/location.module'
|
||||
import { MailModule } from './features/mail/mail.module';
|
||||
import { RolesModule } from './features/roles/roles.module';
|
||||
import { UserRolesModule } from './features/user-roles/user-roles.module';
|
||||
import { SurveysModule } from './features/surveys/surveys.module';
|
||||
import {InventoryModule} from './features/inventory/inventory.module'
|
||||
import { PicturesModule } from './features/pictures/pictures.module';
|
||||
import { InventoryModule } from './features/inventory/inventory.module';
|
||||
import { TrainingModule } from './features/training/training.module';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
@@ -61,7 +61,7 @@ import { PicturesModule } from './features/pictures/pictures.module';
|
||||
SurveysModule,
|
||||
LocationModule,
|
||||
InventoryModule,
|
||||
PicturesModule
|
||||
TrainingModule
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule { }
|
||||
|
||||
37
apps/api/src/database/migrations/0008_plain_scream.sql
Normal file
37
apps/api/src/database/migrations/0008_plain_scream.sql
Normal file
@@ -0,0 +1,37 @@
|
||||
CREATE TABLE "training_surveys" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"firstname" text NOT NULL,
|
||||
"lastname" text NOT NULL,
|
||||
"visit_date" timestamp NOT NULL,
|
||||
"productive_activity" text NOT NULL,
|
||||
"financial_requirement_description" text NOT NULL,
|
||||
"situr_code_commune" text NOT NULL,
|
||||
"communal_council" text NOT NULL,
|
||||
"situr_code_communal_council" text NOT NULL,
|
||||
"osp_name" text NOT NULL,
|
||||
"osp_address" text NOT NULL,
|
||||
"osp_rif" text NOT NULL,
|
||||
"osp_type" text NOT NULL,
|
||||
"current_status" text NOT NULL,
|
||||
"company_constitution_year" integer NOT NULL,
|
||||
"producer_count" integer NOT NULL,
|
||||
"product_description" text NOT NULL,
|
||||
"installed_capacity" text NOT NULL,
|
||||
"operational_capacity" text NOT NULL,
|
||||
"osp_responsible_fullname" text NOT NULL,
|
||||
"osp_responsible_cedula" text NOT NULL,
|
||||
"osp_responsible_rif" text NOT NULL,
|
||||
"osp_responsible_phone" text NOT NULL,
|
||||
"civil_state" text NOT NULL,
|
||||
"family_burden" integer NOT NULL,
|
||||
"number_of_children" integer NOT NULL,
|
||||
"general_observations" text NOT NULL,
|
||||
"photo1" text NOT NULL,
|
||||
"photo2" text NOT NULL,
|
||||
"photo3" text NOT NULL,
|
||||
"paralysis_reason" text NOT NULL,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp (3)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE INDEX "training_surveys_index_00" ON "training_surveys" USING btree ("firstname");
|
||||
1778
apps/api/src/database/migrations/meta/0008_snapshot.json
Normal file
1778
apps/api/src/database/migrations/meta/0008_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,13 @@
|
||||
"when": 1754420096323,
|
||||
"tag": "0007_curved_fantastic_four",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "7",
|
||||
"when": 1764623430844,
|
||||
"tag": "0008_plain_scream",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -44,7 +44,47 @@ export const answersSurveys = t.pgTable(
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
// Tabla training_surveys
|
||||
export const trainingSurveys = t.pgTable(
|
||||
'training_surveys',
|
||||
{
|
||||
id: t.serial('id').primaryKey(),
|
||||
firstname: t.text('firstname').notNull(),
|
||||
lastname: t.text('lastname').notNull(),
|
||||
visitDate: t.timestamp('visit_date').notNull(),
|
||||
productiveActivity: t.text('productive_activity').notNull(),
|
||||
financialRequirementDescription: t.text('financial_requirement_description').notNull(),
|
||||
siturCodeCommune: t.text('situr_code_commune').notNull(),
|
||||
communalCouncil: t.text('communal_council').notNull(),
|
||||
siturCodeCommunalCouncil: t.text('situr_code_communal_council').notNull(),
|
||||
ospName: t.text('osp_name').notNull(),
|
||||
ospAddress: t.text('osp_address').notNull(),
|
||||
ospRif: t.text('osp_rif').notNull(),
|
||||
ospType: t.text('osp_type').notNull(),
|
||||
currentStatus: t.text('current_status').notNull(),
|
||||
companyConstitutionYear: t.integer('company_constitution_year').notNull(),
|
||||
producerCount: t.integer('producer_count').notNull(),
|
||||
productDescription: t.text('product_description').notNull(),
|
||||
installedCapacity: t.text('installed_capacity').notNull(),
|
||||
operationalCapacity: t.text('operational_capacity').notNull(),
|
||||
ospResponsibleFullname: t.text('osp_responsible_fullname').notNull(),
|
||||
ospResponsibleCedula: t.text('osp_responsible_cedula').notNull(),
|
||||
ospResponsibleRif: t.text('osp_responsible_rif').notNull(),
|
||||
ospResponsiblePhone: t.text('osp_responsible_phone').notNull(),
|
||||
civilState: t.text('civil_state').notNull(),
|
||||
familyBurden: t.integer('family_burden').notNull(),
|
||||
numberOfChildren: t.integer('number_of_children').notNull(),
|
||||
generalObservations: t.text('general_observations').notNull(),
|
||||
photo1: t.text('photo1').notNull(),
|
||||
photo2: t.text('photo2').notNull(),
|
||||
photo3: t.text('photo3').notNull(),
|
||||
paralysisReason: t.text('paralysis_reason').notNull(),
|
||||
...timestamps,
|
||||
},
|
||||
(trainingSurveys) => ({
|
||||
trainingSurveysIndex: t.index('training_surveys_index_00').on(trainingSurveys.firstname),
|
||||
})
|
||||
);
|
||||
|
||||
export const viewSurveys = t.pgView('v_surveys', {
|
||||
surverId: t.integer('survey_id'),
|
||||
|
||||
@@ -40,7 +40,7 @@ export class AuthService {
|
||||
private readonly config: ConfigService<Env>,
|
||||
@Inject(DRIZZLE_PROVIDER) private drizzle: NodePgDatabase<typeof schema>,
|
||||
private readonly mailService: MailService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
//Decode Tokens
|
||||
// Método para decodificar el token y obtener los datos completos
|
||||
@@ -89,7 +89,7 @@ export class AuthService {
|
||||
},
|
||||
{
|
||||
secret: envs.access_token_secret,
|
||||
expiresIn: envs.access_token_expiration,
|
||||
expiresIn: envs.access_token_expiration as any,
|
||||
},
|
||||
),
|
||||
this.jwtService.signAsync(
|
||||
@@ -99,7 +99,7 @@ export class AuthService {
|
||||
},
|
||||
{
|
||||
secret: envs.refresh_token_secret,
|
||||
expiresIn: envs.refresh_token_expiration,
|
||||
expiresIn: envs.refresh_token_expiration as any,
|
||||
},
|
||||
),
|
||||
]);
|
||||
@@ -197,7 +197,7 @@ export class AuthService {
|
||||
|
||||
//Sign In User Account
|
||||
async signIn(dto: SignInUserDto): Promise<LoginUserInterface> {
|
||||
|
||||
|
||||
const user = await this.validateUser(dto);
|
||||
const tokens = await this.generateTokens(user);
|
||||
const decodeAccess = this.decodeToken(tokens.access_token);
|
||||
@@ -267,7 +267,7 @@ export class AuthService {
|
||||
// const user_id = 1;
|
||||
|
||||
const validation = await this.jwtService.verifyAsync(refresh_token, {
|
||||
secret: envs.refresh_token_secret,
|
||||
secret: envs.refresh_token_secret,
|
||||
});
|
||||
|
||||
if (!validation) throw new UnauthorizedException('Invalid refresh token');
|
||||
@@ -278,21 +278,21 @@ export class AuthService {
|
||||
.where(
|
||||
and(
|
||||
eq(sessions.userId, user_id) &&
|
||||
eq(sessions.sessionToken, dto.refresh_token),
|
||||
eq(sessions.sessionToken, dto.refresh_token),
|
||||
),
|
||||
);
|
||||
|
||||
// console.log(session.length);
|
||||
|
||||
|
||||
if (session.length === 0) throw new NotFoundException('session not found');
|
||||
const user = await this.findUserById(user_id);
|
||||
if (!user) throw new NotFoundException('User not found');
|
||||
|
||||
|
||||
// Genera token
|
||||
const tokens = await this.generateTokens(user);
|
||||
const decodeAccess = this.decodeToken(tokens.access_token);
|
||||
const decodeRefresh = this.decodeToken(tokens.refresh_token);
|
||||
|
||||
|
||||
// Actualiza session
|
||||
await this.drizzle
|
||||
.update(sessions)
|
||||
@@ -308,75 +308,75 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async singUp(createUserDto: SingUpUserDto): Promise<User> {
|
||||
// Check if username or email exists
|
||||
const data = await this.drizzle
|
||||
// Check if username or email exists
|
||||
const data = await this.drizzle
|
||||
.select({
|
||||
id: users.id,
|
||||
username: users.username,
|
||||
email: users.email
|
||||
})
|
||||
.from(users)
|
||||
.where(or(eq(users.username, createUserDto.username), eq(users.email, createUserDto.email)));
|
||||
|
||||
if (data.length > 0) {
|
||||
if (data[0].username === createUserDto.username) {
|
||||
throw new HttpException('Username already exists', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (data[0].email === createUserDto.email) {
|
||||
throw new HttpException('Email already exists', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
// Hash the password
|
||||
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
|
||||
|
||||
// Start a transaction
|
||||
return await this.drizzle.transaction(async (tx) => {
|
||||
// Create the user
|
||||
const [newUser] = await tx
|
||||
.insert(users)
|
||||
.values({
|
||||
username: createUserDto.username,
|
||||
email: createUserDto.email,
|
||||
password: hashedPassword,
|
||||
fullname: createUserDto.fullname,
|
||||
isActive: true,
|
||||
state: createUserDto.state,
|
||||
municipality: createUserDto.municipality,
|
||||
parish: createUserDto.parish,
|
||||
phone: createUserDto.phone,
|
||||
isEmailVerified: false,
|
||||
isTwoFactorEnabled: false,
|
||||
})
|
||||
.returning();
|
||||
|
||||
// check if user role is admin
|
||||
const role = createUserDto.role <= 2 ? 5 : createUserDto.role;
|
||||
|
||||
// Assign role to user
|
||||
await tx.insert(usersRole).values({
|
||||
userId: newUser.id,
|
||||
roleId: role,
|
||||
});
|
||||
|
||||
// Return the created user with role
|
||||
const [userWithRole] = await tx
|
||||
.select({
|
||||
id: users.id,
|
||||
username: users.username,
|
||||
email: users.email
|
||||
email: users.email,
|
||||
fullname: users.fullname,
|
||||
phone: users.phone,
|
||||
isActive: users.isActive,
|
||||
role: roles.name,
|
||||
})
|
||||
.from(users)
|
||||
.where(or(eq(users.username, createUserDto.username), eq(users.email, createUserDto.email)));
|
||||
|
||||
if (data.length > 0) {
|
||||
if (data[0].username === createUserDto.username) {
|
||||
throw new HttpException('Username already exists', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (data[0].email === createUserDto.email) {
|
||||
throw new HttpException('Email already exists', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
.leftJoin(usersRole, eq(usersRole.userId, users.id))
|
||||
.leftJoin(roles, eq(roles.id, usersRole.roleId))
|
||||
.where(eq(users.id, newUser.id));
|
||||
|
||||
// Hash the password
|
||||
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
|
||||
|
||||
// Start a transaction
|
||||
return await this.drizzle.transaction(async (tx) => {
|
||||
// Create the user
|
||||
const [newUser] = await tx
|
||||
.insert(users)
|
||||
.values({
|
||||
username: createUserDto.username,
|
||||
email: createUserDto.email,
|
||||
password: hashedPassword,
|
||||
fullname: createUserDto.fullname,
|
||||
isActive: true,
|
||||
state: createUserDto.state,
|
||||
municipality: createUserDto.municipality,
|
||||
parish: createUserDto.parish,
|
||||
phone: createUserDto.phone,
|
||||
isEmailVerified: false,
|
||||
isTwoFactorEnabled: false,
|
||||
})
|
||||
.returning();
|
||||
return userWithRole;
|
||||
})
|
||||
|
||||
// check if user role is admin
|
||||
const role = createUserDto.role <= 2 ? 5 : createUserDto.role;
|
||||
|
||||
// Assign role to user
|
||||
await tx.insert(usersRole).values({
|
||||
userId: newUser.id,
|
||||
roleId: role,
|
||||
});
|
||||
|
||||
// Return the created user with role
|
||||
const [userWithRole] = await tx
|
||||
.select({
|
||||
id: users.id,
|
||||
username: users.username,
|
||||
email: users.email,
|
||||
fullname: users.fullname,
|
||||
phone: users.phone,
|
||||
isActive: users.isActive,
|
||||
role: roles.name,
|
||||
})
|
||||
.from(users)
|
||||
.leftJoin(usersRole, eq(usersRole.userId, users.id))
|
||||
.leftJoin(roles, eq(roles.id, usersRole.roleId))
|
||||
.where(eq(users.id, newUser.id));
|
||||
|
||||
return userWithRole;
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Controller, Post, UploadedFiles, UseInterceptors, Body } from '@nestjs/common';
|
||||
import { FilesInterceptor } from '@nestjs/platform-express';
|
||||
import { PicturesService } from './pictures.service';
|
||||
|
||||
@Controller('pictures')
|
||||
export class PicturesController {
|
||||
constructor(private readonly picturesService: PicturesService) {}
|
||||
|
||||
@Post('upload')
|
||||
@UseInterceptors(FilesInterceptor('urlImg'))
|
||||
async uploadFile(@UploadedFiles() files: Express.Multer.File[], @Body() body: any) {
|
||||
// Aquí puedes acceder a los campos del formulario
|
||||
// console.log('Archivos:', files);
|
||||
// console.log('Otros campos del formulario:', body);
|
||||
const result = await this.picturesService.saveImages(files);
|
||||
|
||||
return { data: result };
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PicturesController } from './pictures.controller';
|
||||
import { PicturesService } from './pictures.service';
|
||||
|
||||
@Module({
|
||||
controllers: [PicturesController],
|
||||
providers: [PicturesService],
|
||||
})
|
||||
export class PicturesModule {}
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { writeFile } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
|
||||
@Injectable()
|
||||
export class PicturesService {
|
||||
/**
|
||||
* Guarda una imagen en el directorio de imágenes.
|
||||
* @param file - El archivo de imagen a guardar.
|
||||
* @returns La ruta de la imagen guardada.
|
||||
*/
|
||||
async saveImages(file: Express.Multer.File[]): Promise<string[]> {
|
||||
// Construye la ruta al directorio de imágenes.
|
||||
|
||||
const picturesPath = join(__dirname, '..', '..', '..', '..', 'uploads','pict');
|
||||
|
||||
console.log(picturesPath);
|
||||
|
||||
let images : string[] = [];
|
||||
|
||||
let count = 0;
|
||||
|
||||
file.forEach(async (file) => {
|
||||
count++
|
||||
// Crea un nombre de archivo único para la imagen.
|
||||
const fileName = `${Date.now()}-${count}-${file.originalname}`;
|
||||
images.push(fileName);
|
||||
// console.log(fileName);
|
||||
|
||||
// Construye la ruta completa al archivo de imagen.
|
||||
const filePath = join(picturesPath, fileName);
|
||||
|
||||
// Escribe el archivo de imagen en el disco.
|
||||
await writeFile(filePath, file.buffer);
|
||||
});
|
||||
// Devuelve la ruta de la imagen guardada.
|
||||
// return [file[0].originalname]
|
||||
return images;
|
||||
}
|
||||
}
|
||||
124
apps/api/src/features/training/dto/create-training.dto.ts
Normal file
124
apps/api/src/features/training/dto/create-training.dto.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsInt, IsString, IsDateString, IsOptional } from 'class-validator';
|
||||
|
||||
export class CreateTrainingDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstname: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
lastname: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsDateString()
|
||||
visitDate: Date;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
productiveActivity: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
financialRequirementDescription: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
siturCodeCommune: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
communalCouncil: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
siturCodeCommunalCouncil: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
ospName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
ospAddress: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
ospRif: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
ospType: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
currentStatus: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
companyConstitutionYear: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
producerCount: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
productDescription: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
installedCapacity: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
operationalCapacity: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
ospResponsibleFullname: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
ospResponsibleCedula: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
ospResponsibleRif: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
ospResponsiblePhone: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
civilState: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
familyBurden: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
numberOfChildren: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
generalObservations: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
photo1: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
photo2: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
photo3: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
paralysisReason: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateTrainingDto } from './create-training.dto';
|
||||
|
||||
export class UpdateTrainingDto extends PartialType(CreateTrainingDto) { }
|
||||
58
apps/api/src/features/training/training.controller.ts
Normal file
58
apps/api/src/features/training/training.controller.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
|
||||
import { TrainingService } from './training.service';
|
||||
import { CreateTrainingDto } from './dto/create-training.dto';
|
||||
import { UpdateTrainingDto } from './dto/update-training.dto';
|
||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { PaginationDto } from '../../common/dto/pagination.dto';
|
||||
|
||||
@ApiTags('training')
|
||||
@Controller('training')
|
||||
export class TrainingController {
|
||||
constructor(private readonly trainingService: TrainingService) { }
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Get all training records with pagination and filters' })
|
||||
@ApiResponse({ status: 200, description: 'Return paginated training records.' })
|
||||
async findAll(@Query() paginationDto: PaginationDto) {
|
||||
const result = await this.trainingService.findAll(paginationDto);
|
||||
return {
|
||||
message: 'Training records fetched successfully',
|
||||
data: result.data,
|
||||
meta: result.meta
|
||||
};
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get a training record by ID' })
|
||||
@ApiResponse({ status: 200, description: 'Return the training record.' })
|
||||
@ApiResponse({ status: 404, description: 'Training record not found.' })
|
||||
async findOne(@Param('id') id: string) {
|
||||
const data = await this.trainingService.findOne(+id);
|
||||
return { message: 'Training record fetched successfully', data };
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a new training record' })
|
||||
@ApiResponse({ status: 201, description: 'Training record created successfully.' })
|
||||
async create(@Body() createTrainingDto: CreateTrainingDto) {
|
||||
const data = await this.trainingService.create(createTrainingDto);
|
||||
return { message: 'Training record created successfully', data };
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@ApiOperation({ summary: 'Update a training record' })
|
||||
@ApiResponse({ status: 200, description: 'Training record updated successfully.' })
|
||||
@ApiResponse({ status: 404, description: 'Training record not found.' })
|
||||
async update(@Param('id') id: string, @Body() updateTrainingDto: UpdateTrainingDto) {
|
||||
const data = await this.trainingService.update(+id, updateTrainingDto);
|
||||
return { message: 'Training record updated successfully', data };
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: 'Delete a training record' })
|
||||
@ApiResponse({ status: 200, description: 'Training record deleted successfully.' })
|
||||
@ApiResponse({ status: 404, description: 'Training record not found.' })
|
||||
async remove(@Param('id') id: string) {
|
||||
return await this.trainingService.remove(+id);
|
||||
}
|
||||
}
|
||||
10
apps/api/src/features/training/training.module.ts
Normal file
10
apps/api/src/features/training/training.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TrainingService } from './training.service';
|
||||
import { TrainingController } from './training.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [TrainingController],
|
||||
providers: [TrainingService],
|
||||
exports: [TrainingService],
|
||||
})
|
||||
export class TrainingModule { }
|
||||
118
apps/api/src/features/training/training.service.ts
Normal file
118
apps/api/src/features/training/training.service.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { DRIZZLE_PROVIDER } from 'src/database/drizzle-provider';
|
||||
import { Inject, Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import * as schema from 'src/database/index';
|
||||
import { trainingSurveys } from 'src/database/index';
|
||||
import { eq, like, or, SQL, sql } from 'drizzle-orm';
|
||||
import { CreateTrainingDto } from './dto/create-training.dto';
|
||||
import { UpdateTrainingDto } from './dto/update-training.dto';
|
||||
import { PaginationDto } from '../../common/dto/pagination.dto';
|
||||
|
||||
@Injectable()
|
||||
export class TrainingService {
|
||||
constructor(
|
||||
@Inject(DRIZZLE_PROVIDER) private drizzle: NodePgDatabase<typeof schema>,
|
||||
) { }
|
||||
|
||||
async findAll(paginationDto?: PaginationDto) {
|
||||
const { page = 1, limit = 10, search = '', sortBy = 'id', sortOrder = 'asc' } = paginationDto || {};
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
let searchCondition: SQL<unknown> | undefined;
|
||||
if (search) {
|
||||
searchCondition = or(
|
||||
like(trainingSurveys.firstname, `%${search}%`),
|
||||
like(trainingSurveys.lastname, `%${search}%`),
|
||||
like(trainingSurveys.ospName, `%${search}%`),
|
||||
like(trainingSurveys.ospRif, `%${search}%`)
|
||||
);
|
||||
}
|
||||
|
||||
const orderBy = sortOrder === 'asc'
|
||||
? sql`${trainingSurveys[sortBy as keyof typeof trainingSurveys]} asc`
|
||||
: sql`${trainingSurveys[sortBy as keyof typeof trainingSurveys]} desc`;
|
||||
|
||||
const totalCountResult = await this.drizzle
|
||||
.select({ count: sql<number>`count(*)` })
|
||||
.from(trainingSurveys)
|
||||
.where(searchCondition);
|
||||
|
||||
const totalCount = Number(totalCountResult[0].count);
|
||||
const totalPages = Math.ceil(totalCount / limit);
|
||||
|
||||
const data = await this.drizzle
|
||||
.select()
|
||||
.from(trainingSurveys)
|
||||
.where(searchCondition)
|
||||
.orderBy(orderBy)
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
const meta = {
|
||||
page,
|
||||
limit,
|
||||
totalCount,
|
||||
totalPages,
|
||||
hasNextPage: page < totalPages,
|
||||
hasPreviousPage: page > 1,
|
||||
nextPage: page < totalPages ? page + 1 : null,
|
||||
previousPage: page > 1 ? page - 1 : null,
|
||||
};
|
||||
|
||||
return { data, meta };
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const find = await this.drizzle
|
||||
.select()
|
||||
.from(trainingSurveys)
|
||||
.where(eq(trainingSurveys.id, id));
|
||||
|
||||
if (find.length === 0) {
|
||||
throw new HttpException('Training record not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
return find[0];
|
||||
}
|
||||
|
||||
async create(createTrainingDto: CreateTrainingDto) {
|
||||
const [newRecord] = await this.drizzle
|
||||
.insert(trainingSurveys)
|
||||
.values({
|
||||
...createTrainingDto,
|
||||
visitDate: new Date(createTrainingDto.visitDate),
|
||||
})
|
||||
.returning();
|
||||
|
||||
return newRecord;
|
||||
}
|
||||
|
||||
async update(id: number, updateTrainingDto: UpdateTrainingDto) {
|
||||
await this.findOne(id);
|
||||
|
||||
const updateData: any = { ...updateTrainingDto };
|
||||
if (updateTrainingDto.visitDate) {
|
||||
updateData.visitDate = new Date(updateTrainingDto.visitDate);
|
||||
}
|
||||
|
||||
const [updatedRecord] = await this.drizzle
|
||||
.update(trainingSurveys)
|
||||
.set(updateData)
|
||||
.where(eq(trainingSurveys.id, id))
|
||||
.returning();
|
||||
|
||||
return updatedRecord;
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
await this.findOne(id);
|
||||
|
||||
const [deletedRecord] = await this.drizzle
|
||||
.delete(trainingSurveys)
|
||||
.where(eq(trainingSurveys.id, id))
|
||||
.returning();
|
||||
|
||||
return { message: 'Training record deleted successfully', data: deletedRecord };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user