profesores

This commit is contained in:
jorgemike
2025-02-26 23:13:28 -06:00
parent 098c01884e
commit f9c72ced6d
17 changed files with 337 additions and 177 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ import { DatabaseModule } from './database/database.module';
username: configService.get('DB_USER'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_DATABASE'),
dropSchema: true,
//dropSchema: true,
entities: [
Profesor,
User,
+4 -3
View File
@@ -21,9 +21,10 @@ import { registerDto, SignInDto } from './dto/sign-in.dto';
import { Roles } from '../permissions/roles.decorator';
import { RolesGuard } from '../permissions/roles.guard';
import { Role } from '../permissions/role.enum';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { SignUpDto } from './dto/sign-up.dto';
@ApiBearerAuth()
@ApiTags('Auth')
@Controller('auth')
@UseGuards(RolesGuard)
@@ -39,14 +40,14 @@ export class AuthController {
return this.authService.signIn(data);
}
//@UseGuards()
@Post('register')
@ApiOperation({
summary: 'Registro de usuario',
description: 'Registro ded administradores o responsables',
})
@UseGuards()
@Roles(Role.Admin)
async register(@Body() data: registerDto) {
async register(@Body() data: SignUpDto) {
Logger.debug('register user');
return this.authService.register(data);
}
+4 -1
View File
@@ -6,10 +6,13 @@ import { AuthController } from './auth.controller';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/users/entities/user.entity';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from 'src/permissions/jwt.strategy';
@Module({
imports: [
UsersModule,
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
@@ -22,7 +25,7 @@ import { User } from 'src/users/entities/user.entity';
}),
TypeOrmModule.forFeature([User]),
],
providers: [AuthService],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
exports: [AuthService],
})
+17 -12
View File
@@ -14,6 +14,7 @@ import { Repository } from 'typeorm';
import { hash } from 'bcrypt';
import { compare } from 'bcryptjs';
import { ConfigService } from '@nestjs/config';
import { SignUpDto } from './dto/sign-up.dto';
@Injectable()
export class AuthService {
@@ -25,28 +26,33 @@ export class AuthService {
) {}
//register users
async register(data: registerDto) {
const { usuario, contraseña } = data;
async register(data: SignUpDto) {
const { usuario, password } = data;
if (await this.usersService.findOne(usuario)) {
throw new HttpException('User already exist', 403);
const searchedUser = await this.usersService.findOne(usuario);
if (searchedUser) {
throw new HttpException('El usuario ya existe', 409);
}
const hashedpassword = await hash(contraseña, 10);
data = { ...data, contraseña: hashedpassword };
return this.userRepository.save(this.userRepository.create(data));
const hashedpassword = await hash(password, 10);
data = { ...data, password: hashedpassword };
const createdUsuario = await this.userRepository.create(data);
const savedUsuario = await this.userRepository.save(createdUsuario);
return savedUsuario;
}
//singin
async signIn(data: SignInDto) {
const { usuario, contraseña, rememberMe } = data;
const { usuario, password, rememberMe } = data;
const user = await this.usersService.findOne(usuario);
if (!user) {
throw new UnauthorizedException('Credenciales inválidas.');
}
const checkPassword = await compare(contraseña, user.contraseña);
const checkPassword = await compare(password, user.password);
if (!checkPassword) {
throw new UnauthorizedException('Credenciales inválidas.');
}
@@ -56,7 +62,6 @@ export class AuthService {
usuario: user.usuario,
id_tipo_usuario: user.id_tipo_usuario,
};
console.table(payload);
const token = this.jwtService.sign(payload, {
expiresIn: rememberMe ? '31d' : '7h',
@@ -98,7 +103,7 @@ export class AuthService {
const hashedpassword = await hash(contraseña, 10);
data = { ...data, contraseña: hashedpassword };
} else {
data = { ...data, contraseña: user.contraseña };
data = { ...data, contraseña: user.password };
}
return await this.userRepository.update(id_usuario, data);
+19
View File
@@ -0,0 +1,19 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsString } from 'class-validator';
export class JwtPayloadDto {
@ApiProperty({ example: 1, description: 'ID único del usuario' })
@IsNumber()
id_usuario: number;
@ApiProperty({ example: 'juan.perez', description: 'Nombre de usuario' })
@IsString()
usuario: string;
@ApiProperty({
example: 2,
description: 'Tipo de usuario (ejemplo: Admin, Estudiante, etc.)',
})
@IsNumber()
id_tipo_usuario: number;
}
+1 -1
View File
@@ -22,7 +22,7 @@ export class SignInDto {
})
@IsString()
@IsNotEmpty()
contraseña: string;
password: string;
@ApiProperty({
description: 'Indica si se debe recordar la sesión (opcional)',
+3 -3
View File
@@ -37,10 +37,10 @@ export class SignUpDto {
@ApiProperty({
description: 'Contraseña del usuario',
example: 'password123',
example: 'password',
})
@IsString()
@MinLength(6)
@MinLength(2)
@IsNotEmpty()
contraseña: string;
password: string;
}
+13 -2
View File
@@ -3,19 +3,30 @@ import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
import * as path from 'path';
import * as bodyParser from 'body-parser';
import { Logger } from '@nestjs/common';
import { Logger, ValidationPipe } from '@nestjs/common';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// 💡 Agregar validaciones globales con class-validator
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // Elimina propiedades desconocidas
forbidNonWhitelisted: true, // Lanza error si hay propiedades no permitidas
transform: true, // Convierte tipos automáticamente
}),
);
if (process.env.PROD === 'false') {
Logger.log('--- Cargando Documentacion ---')
Logger.log('--- Cargando Documentacion ---');
const config = new DocumentBuilder()
.setTitle('Directorio API')
.setVersion('1.0')
.addTag('Carga de Datos')
.addTag('Auth')
.addTag('Profesor')
.addBearerAuth()
.build();
const documentFactory = () => SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, documentFactory);
+16
View File
@@ -0,0 +1,16 @@
import { Injectable, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
return super.canActivate(context);
}
handleRequest(err, user, info) {
if (err || !user) {
throw new UnauthorizedException('Token inválido o no autorizado');
}
return user;
}
}
+20
View File
@@ -0,0 +1,20 @@
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
import { JwtPayloadDto } from 'src/auth/dto/payload.dto';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: configService.get<string>('JWT_SECRET'),
});
}
async validate(payload: JwtPayloadDto) {
return payload
}
}
+30 -7
View File
@@ -4,6 +4,7 @@ import {
ExecutionContext,
UnauthorizedException,
Logger,
ForbiddenException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
@@ -20,12 +21,13 @@ export class RolesGuard implements CanActivate {
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
Logger.debug('verify roles');
// Obtener los roles requeridos para el endpoint
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
// Si no se requieren roles, permitir acceso
if (!requiredRoles) {
return true;
}
@@ -34,30 +36,51 @@ export class RolesGuard implements CanActivate {
const authHeader = request.headers['authorization'];
if (!authHeader) {
throw new UnauthorizedException('No authorization header provided');
throw new UnauthorizedException(
'No se proporcionó un token de autorización',
);
}
// Extraer el token del header
const [, token] = authHeader.split(' ');
if (!token) {
throw new UnauthorizedException('No token provided');
throw new UnauthorizedException(
'Token de autorización inválido o ausente',
);
}
try {
// Verificar el token con la clave secreta
const userPayload = await this.jwtService.verifyAsync(token, {
secret: this.configService.get<string>('JWT_SECRET'),
});
// Extraer información del usuario autenticado
const userRole: Role = userPayload.id_tipo_usuario;
const userId: number = userPayload.id_usuario;
const paramId = parseInt(request.params.id_usuario, 10);
if (requiredRoles.includes(userRole) || userId === paramId) {
if (!userRole || !userId) {
throw new UnauthorizedException(
'El token no contiene información válida del usuario',
);
}
// Extraer id_usuario de los parámetros de la ruta
const paramId = request.params.id_usuario
? parseInt(request.params.id_usuario, 10)
: null;
// Comprobar si el usuario tiene permisos por rol o si es dueño del recurso
if (requiredRoles.includes(userRole) || (paramId && userId === paramId)) {
return true;
}
return false;
// Si el usuario no cumple los criterios, lanzar error de acceso denegado
throw new ForbiddenException(
'No tienes permisos para acceder a este recurso',
);
} catch (error) {
throw new UnauthorizedException('Invalid token');
throw new UnauthorizedException('Token inválido o expirado');
}
}
}
+56 -37
View File
@@ -9,113 +9,132 @@ import {
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class CreateProfesorDto {
@ApiProperty({ example: 'Juan Pérez', description: 'Nombre del profesor' })
@IsString()
@IsNotEmpty()
@Length(1, 80) // Se asegura de que no sea una cadena vacía
@ApiProperty({
example: 'Juan Pérez',
description: 'Nombre completo del profesor',
})
@IsString({ message: 'El nombre debe ser una cadena de texto' })
@IsNotEmpty({ message: 'El nombre es obligatorio' })
@Length(1, 80, { message: 'El nombre debe tener entre 1 y 80 caracteres' })
nombre: string;
@ApiPropertyOptional({
example: '123456',
description: 'Número de trabajador',
description: 'Número de trabajador único',
})
@IsString()
@IsString({ message: 'El número de trabajador debe ser una cadena de texto' })
@IsOptional()
@Length(1, 10) // Se asegura de que no sea una cadena vacía si se envía
@Length(1, 10, {
message: 'El número de trabajador debe ser maximo de 10 caracteres',
})
num_trabajador?: string;
@ApiPropertyOptional({
example: 'ABC1234567',
description: 'RFC del profesor',
})
@IsString()
@IsString({ message: 'El RFC debe ser una cadena de texto' })
@IsOptional()
@Length(1, 10)
@Length(1, 10, { message: 'El RFC debe tener maximo 10 caracteres' })
rfc?: string;
@ApiPropertyOptional({ example: 'XYZ', description: 'Homoclave del RFC' })
@IsString()
@IsString({ message: 'La homoclave debe ser una cadena de texto' })
@IsOptional()
@Length(1, 3)
@Length(1, 3, { message: 'La homoclave debe tener maximo 3 caracteres' })
homoclave?: string;
@ApiPropertyOptional({
example: '5551234567',
description: 'Teléfono de oficina',
description: 'Teléfono de oficina del profesor',
})
@IsString()
@IsString({ message: 'El teléfono debe ser una cadena de texto' })
@IsOptional()
@Length(1, 14) // Se corrige de 13 a 14
@Length(1, 14, { message: 'El teléfono debe tener entre 1 y 14 caracteres' })
tel_oficina?: string;
@ApiPropertyOptional({
example: '1234',
description: 'Extensión del teléfono',
description: 'Extensión del teléfono de oficina',
})
@IsString()
@IsString({ message: 'La extensión debe ser una cadena de texto' })
@IsOptional()
@Length(1, 10)
@Length(1, 10, { message: 'La extensión debe tener entre 1 y 10 caracteres' })
extension?: string;
@ApiPropertyOptional({
example: 'correo@escuela.edu.mx',
description: 'Correo principal del profesor',
description: 'Correo institucional',
})
@IsString()
@IsString({ message: 'El correo debe ser una cadena de texto' })
@IsOptional()
@Length(1, 65)
@Length(1, 65, { message: 'El correo debe tener entre 1 y 65 caracteres' })
correo_pcp?: string;
@ApiPropertyOptional({ example: 1, description: 'ID de la adscripción' })
@IsInt()
@ApiPropertyOptional({
example: 1,
description: 'ID de la adscripción a la que pertenece el profesor',
})
@IsInt({ message: 'El ID de adscripción debe ser un número entero' })
@IsOptional()
id_adscripcion?: number;
@ApiPropertyOptional({ example: 2, description: 'ID de la categoría' })
@IsInt()
@ApiPropertyOptional({
example: 2,
description: 'ID de la categoría del profesor',
})
@IsInt({ message: 'El ID de categoría debe ser un número entero' })
@IsOptional()
id_categoria?: number;
@ApiPropertyOptional({ example: 3, description: 'ID del edificio' })
@IsInt()
@ApiPropertyOptional({
example: 3,
description: 'ID del edificio donde se ubica el profesor',
})
@IsInt({ message: 'El ID de edificio debe ser un número entero' })
@IsOptional()
id_edificio?: number;
@ApiPropertyOptional({
example: 'Aula 205',
description: 'Ubicación del profesor',
description: 'Ubicación física del profesor',
})
@IsString()
@IsString({ message: 'La ubicación debe ser una cadena de texto' })
@IsOptional()
@Length(1, 256)
ubicacion?: string;
@ApiPropertyOptional({
example: 'https://mi-url.com/foto.jpg',
description: 'URL de la fotografía',
description:
'URL de la fotografía del profesor. \n\n Antes de crear el registro del profesor, la imagen debe ser subida al endpoint correspondiente para almacenar imágenes. \n\n Este endpoint devolverá la URL de la imagen almacenada, la cual debe enviarse en este campo al momento de registrar al profesor.',
})
@IsString({
message: 'La URL de la fotografía debe ser una cadena de texto válida',
})
@IsString()
@IsOptional()
@Length(1, 150)
fotografia?: string;
@ApiPropertyOptional({
example: true,
description: 'Si el profesor está activo o no',
description: 'Indica si el profesor está activo',
})
@IsBoolean({
message: 'El estado activo debe ser un valor booleano (true o false)',
})
@IsBoolean()
@IsOptional()
activo?: boolean;
@ApiPropertyOptional({
example: false,
description: 'Si el profesor debe mostrarse en la lista pública',
description: 'Indica si el profesor debe mostrarse en la lista pública',
})
@IsBoolean({
message:
'El estado de visibilidad debe ser un valor booleano (true o false)',
})
@IsBoolean()
@IsOptional()
mostrar?: boolean;
}
/*
Se retiro informacion personal
+2 -2
View File
@@ -55,8 +55,8 @@ export class Profesor {
@Column({
type: 'timestamp',
nullable: false,
default: () => 'CURRENT_TIMESTAMP',
nullable: false,
})
fecha_alta: Date;
@@ -76,7 +76,7 @@ export class Profesor {
/* @Column({ type: 'varchar', length: 65, nullable: true })
correo_per: string; */
/* Relaciones */
/* RELACIONES */
@Column({ type: 'int', nullable: true })
id_adscripcion: number;
+84 -29
View File
@@ -7,9 +7,11 @@ import {
Logger,
Param,
ParseIntPipe,
Patch,
Post,
Put,
Query,
Req,
UseGuards,
} from '@nestjs/common';
import { ProfesorService } from './profesor.service';
@@ -21,22 +23,36 @@ import { Roles } from '../permissions/roles.decorator';
import { RolesGuard } from '../permissions/roles.guard';
import { Role } from '../permissions/role.enum';
import { Profesor } from './entities/profesor.entity';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import {
ApiBearerAuth,
ApiOperation,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { JwtAuthGuard } from 'src/permissions/jwt.guard';
import { JwtPayloadDto } from 'src/auth/dto/payload.dto';
interface RequestWithUser extends Request {
user?: JwtPayloadDto;
}
@ApiBearerAuth()
@ApiTags('Profesor')
@Controller('profesor')
@UseGuards(JwtAuthGuard)
@UseGuards(RolesGuard)
export class ProfesorController {
constructor(private profesorService: ProfesorService) {}
@Post()
@ApiOperation({ summary: 'Crear un profesor' })
//@Roles(Role.Responsable, Role.Admin)
async register(@Body() data: CreateProfesorDto) {
return this.profesorService.register(data);
@Roles(Role.Responsable, Role.Admin)
async register(@Body() data: CreateProfesorDto, @Req() req: RequestWithUser) {
return this.profesorService.register(data, req.user);
}
@Put(':id_profesor/image')
@Patch('/fotografia/:id_profesor')
@ApiOperation({ summary: 'Actualizar la fotografía de un profesor' })
async updateImage(
@Param('id_profesor') id_profesor: number,
@Body() updateProfesorImageDto: UpdateProfesorImageDto,
@@ -54,27 +70,66 @@ export class ProfesorController {
return this.profesorService.update(profesor);
}
@Get()
findAll() {
Logger.debug('findAllProfesors');
return this.profesorService.findAll();
}
@Get('page')
findAllPaginated(
@Get('filter')
@ApiOperation({
summary: 'Obtener lista paginada de profesores con filtros opcionales',
})
@ApiQuery({
name: 'page',
required: false,
example: 1,
description: 'Número de página (por defecto 1)',
})
@ApiQuery({
name: 'limit',
required: false,
example: 12,
description: 'Cantidad de registros por página (máximo 12)',
})
@ApiQuery({
name: 'nombre',
required: false,
example: 'Juan',
description: 'Filtrar por nombre del profesor',
})
@ApiQuery({
name: 'id_adscripcion',
required: false,
example: 2,
description: 'Filtrar por ID de adscripción',
})
@ApiQuery({
name: 'id_edificio',
required: false,
example: 3,
description: 'Filtrar por ID de edificio',
})
@ApiQuery({
name: 'id_categoria',
required: false,
example: 1,
description: 'Filtrar por ID de categoría',
})
async findAllPaginated(
@Query('page') page: number = 1,
@Query('limit') limit: number = 12,
@Query() filters: any,
@Query('nombre') nombre?: string,
@Query('id_adscripcion') id_adscripcion?: number,
@Query('id_edificio') id_edificio?: number,
@Query('id_categoria') id_categoria?: number,
) {
try {
Logger.debug('Page Profesors');
page = page < 1 ? 1 : page;
limit = limit > 12 || limit < 1 ? 12 : limit;
return this.profesorService.findAllPaginated(page, limit, filters);
} catch (err) {
Logger.error('profesor controller ', err.error);
return [];
}
// Validaciones básicas
page = page < 1 ? 1 : page;
limit = limit > 12 || limit < 1 ? 12 : limit;
// Filtros dinámicos
const filters: any = {};
if (nombre) filters.nombre = nombre;
if (id_adscripcion) filters.id_adscripcion = Number(id_adscripcion);
if (id_edificio) filters.id_edificio = Number(id_edificio);
if (id_categoria) filters.id_categoria = Number(id_categoria);
return await this.profesorService.findAllPaginated(page, limit, filters);
}
@Put(':id_profesor')
@@ -83,23 +138,23 @@ export class ProfesorController {
@Param('id_profesor', ParseIntPipe) id_profesor: number,
@Body() data: CreateProfesorDto,
) {
Logger.debug('put Profesors by id');
return this.profesorService.modify(id_profesor, data);
}
@Delete(':id_profesor')
@Roles(Role.Responsable, Role.Admin)
async remove(@Param('id_profesor', ParseIntPipe) id_profesor: number) {
Logger.debug('delete Profesors by id');
return this.profesorService.remove(id_profesor);
}
@Get(':id_profesor')
async profile(@Param('id_profesor', ParseIntPipe) id_profesor: number) {
Logger.debug('get Profesors by id');
return this.profesorService.profile(id_profesor);
}
@Get()
@ApiOperation({ summary: 'Obtiene todos los profesores' })
findAll() {
return this.profesorService.findAll();
}
}
+66 -78
View File
@@ -9,6 +9,7 @@ import { CreateProfesorDto } from './dto/create-profesor.dto';
import { ProyectosAcademicos } from '../proyectos_academicos/entities/proyectos_academicos.entity';
import { DatosAcademicos } from '../datos_academicos/entities/datos_academicos.entity';
import { LineasInvestigacion } from '../lineas_investigacion/entities/lineas_investigacion.entity';
import { JwtPayloadDto } from 'src/auth/dto/payload.dto';
@Injectable()
export class ProfesorService {
@@ -51,79 +52,64 @@ export class ProfesorService {
}
//register teacher
async register(data: CreateProfesorDto): Promise<any | undefined> {
const { num_trabajador, rfc, ...res } = data;
async register(
data: CreateProfesorDto,
payload: JwtPayloadDto,
): Promise<any | undefined> {
const { num_trabajador, rfc, homoclave, ...res } = data;
// 1.- Validar si ya existe este trabajador por su numero de trabajador o rfc, ya que son numeros unicos
const profesorSearched = await this.profesorRepository.findOne({
where: [{ num_trabajador }, { rfc }],
// 1.- Buscar si ya existe un profesor con ese número de trabajador
const profesorByNumTrabajador = await this.profesorRepository.findOne({
where: { num_trabajador },
});
if (profesorSearched) {
if (profesorByNumTrabajador) {
Logger.error(
`Ya existe un profesor con el número de trabajador ${num_trabajador}.`,
);
throw new HttpException(
`Ya existe un profesor con el número de trabajador ${num_trabajador} o RFC ${rfc}.`,
404,
`Ya existe un profesor con el número de trabajador ${num_trabajador}.`,
409,
);
}
// 2.- Guardar al profesor
// 2.- Buscar si ya existe un profesor con ese RFC
const profesorByRFC = await this.profesorRepository.findOne({
where: { rfc },
});
const profesorSaved = await this.profesorRepository.save(data)
console.table(profesorSaved)
/* const { num_trabajador, rfc, proyectosAcademicos, datosAcademicos, lineasInvestigacion } = data;
if (await this.profesorRepository.findOne({ where: { rfc } })) {
throw new HttpException("rfc already exist", 403);
if (profesorByRFC) {
Logger.error(`Ya existe un profesor con el RFC ${rfc}.`);
throw new HttpException(
`Ya existe un profesor con el RFC ${rfc}.`,
409, // Código 409 (Conflict)
);
}
if (await this.profesorRepository.findOne({ where: { num_trabajador } })) {
throw new HttpException("number of worker already exist", 403);
// 4.- Buscar si ya existe un profesor con ese RFC
const profesorByHomoclave = await this.profesorRepository.findOne({
where: { homoclave },
});
if (profesorByHomoclave) {
Logger.error(`Ya existe un profesor con esa homoclave ${homoclave}.`);
throw new HttpException(
`Ya existe un profesor con esa homoclave ${homoclave}.`,
409, // Código 409 (Conflict)
);
}
const availableId = await this.findAvailableId();
data.id_profesor = availableId;
// 5.- Guardar al profesor
const createdProfesor = this.profesorRepository.create({
id_usuario: payload.id_usuario,
...res,
num_trabajador,
rfc,
});
const savedProfesor = await this.profesorRepository.save(data);
const savedProfesor = await this.profesorRepository.save(createdProfesor);
if (proyectosAcademicos && proyectosAcademicos.length > 0) {
const proyectos = proyectosAcademicos.map(proyecto => {
const newProyecto = this.proyectosAcademicosRepository.create({
...proyecto,
profesor: savedProfesor,
id_profesor: savedProfesor.id_profesor
});
return newProyecto;
});
await this.proyectosAcademicosRepository.save(proyectos);
}
if (lineasInvestigacion && lineasInvestigacion.length > 0) {
const lineas = lineasInvestigacion.map(linea => {
const newLinea = this.lineasInvestigacionRepository.create({
...linea,
profesor: savedProfesor,
id_profesor: savedProfesor.id_profesor
});
return newLinea;
});
await this.lineasInvestigacionRepository.save(lineas);
}
if (datosAcademicos && datosAcademicos.length > 0) {
const datos = datosAcademicos.map(datosAcademicos => {
const newDato = this.datosAcademicosRepository.create({
...datosAcademicos,
profesor: savedProfesor,
id_profesor: savedProfesor.id_profesor
});
return newDato;
});
await this.datosAcademicosRepository.save(datos);
}
return savedProfesor; */
return savedProfesor;
}
//brings all teachers
@@ -232,48 +218,50 @@ export class ProfesorService {
): Promise<{ profesores: Profesor[]; total: number; totalPages: number }> {
const query = this.profesorRepository.createQueryBuilder('profesor');
// 🔹 Filtro por Nombre (LIKE %nombre%)
if (filters.nombre) {
Logger.debug('fiter by name');
query.andWhere('profesor.nombre LIKE :nombre', {
Logger.debug('Filter by name');
query.andWhere('LOWER(profesor.nombre) LIKE LOWER(:nombre)', {
nombre: `%${filters.nombre}%`,
});
}
if (filters.adscripcion) {
Logger.debug('fiter by adscription');
// 🔹 Filtro por Adscripción
if (filters.adscripcion && !isNaN(filters.adscripcion)) {
Logger.debug('Filter by adscription');
query.andWhere('profesor.adscripcion = :adscripcion', {
adscripcion: filters.adscripcion,
adscripcion: Number(filters.adscripcion),
});
}
if (filters.categoria) {
Logger.debug('fiter by category');
// 🔹 Filtro por Categoría
if (filters.categoria && !isNaN(filters.categoria)) {
Logger.debug('Filter by category');
query.andWhere('profesor.categoria = :categoria', {
categoria: filters.categoria,
categoria: Number(filters.categoria),
});
}
if (filters.edificio) {
Logger.debug('fiter by building');
// 🔹 Filtro por Edificio
if (filters.edificio && !isNaN(filters.edificio)) {
Logger.debug('Filter by building');
query.andWhere('profesor.edificio = :edificio', {
edificio: filters.edificio,
edificio: Number(filters.edificio),
});
}
// 🔹 Filtro por Activo
if (typeof filters.activo !== 'undefined') {
Logger.debug('fiter by is active');
query.andWhere('profesor.activo = :activo', { activo: filters.activo });
const isActive = filters.activo === 'true' || filters.activo === true;
Logger.debug('Filter by active status');
query.andWhere('profesor.activo = :activo', { activo: isActive });
} else {
query.orderBy('profesor.activo', 'DESC');
query.orderBy('profesor.activo', 'DESC'); // Si no se especifica, prioriza activos
}
query.addOrderBy('profesor.nombre', 'ASC');
// 🔹 Ejecutar consulta con paginación
const [profesores, total] = await query
.skip((page - 1) * limit)
.take(limit)
+1 -1
View File
@@ -21,7 +21,7 @@ export class User {
usuario: string;
@Column({ type: 'varchar', length: 60, nullable: false })
contraseña: string;
password: string;
@Column({ type: 'varchar', length: 255, nullable: true })
jwt: string;
Binary file not shown.