From 94ffc47d7de4cf74bf52a93bb03b4081742dac14 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Fri, 10 Nov 2023 10:27:56 -0600 Subject: [PATCH 01/19] Added registration request to register controller --- caeq-backend/controllers/auth.controller.js | 103 ++++++++---------- .../controllers/caeq.user.controller.js | 40 +++---- caeq-backend/models/architect.user.model.js | 1 - .../models/regiesterRequests.model.js | 34 ++++++ caeq-backend/utils/apiFeatures.js | 3 - 5 files changed, 99 insertions(+), 82 deletions(-) create mode 100644 caeq-backend/models/regiesterRequests.model.js diff --git a/caeq-backend/controllers/auth.controller.js b/caeq-backend/controllers/auth.controller.js index 733751d8..d50eafa2 100644 --- a/caeq-backend/controllers/auth.controller.js +++ b/caeq-backend/controllers/auth.controller.js @@ -1,5 +1,6 @@ const CaeqUser = require('../models/caeq.user.model'); const ArchitectUser = require('../models/architect.user.model'); +const RegisterRequest = require('../models/regiesterRequests.model'); const jwt = require('jsonwebtoken'); const catchAsync = require('./../utils/catchAsync'); const AppError = require('./../utils/appError'); @@ -85,6 +86,31 @@ exports.signUpCaeqUser = catchAsync(async (req, res, next) => { }); }); +/** + * Asynchronously creates a registration request for an architect. + * + * @param {Object} req - The request object containing the registration information. + * @param {Object} existingUser - The existing user object, if any. + * @param {Object} res - The response object used to send the registration status. + * @returns {Promise} - A Promise that resolves when the registration process is complete. + */ +async function createRegistrationRequest(req, existingUser, res) { + const updatedArchitect = await ArchitectUser.create(req.body); + + await RegisterRequest.create({ + overwrites: existingUser, + newInfo: updatedArchitect, + architectNumber: updatedArchitect.collegiateNumber, + }); + + res.status(200).json({ + status: 'success', + message: `Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto número ${updatedArchitect.collegiateNumber} perfil y te de acceso al portal.`, + }); + + return; +} + /** * Creates a new architect user. * @@ -94,58 +120,19 @@ exports.signUpCaeqUser = catchAsync(async (req, res, next) => { */ exports.signUpArchitectUser = catchAsync(async (req, res, next) => { const { collegiateNumber } = req.body; - let newUser; + const password = req.body.password; + const passwordConfirm = req.body.passwordConfirm; + + if (password !== passwordConfirm) { + return next(new AppError('Tus contraseñas deben coincidir.')); + } // Check if user already exists const existingUser = await ArchitectUser.findOne({ collegiateNumber }); if (existingUser) { - if (existingUser.isLegacy === true && existingUser.isOverwritten === false) { - const password = req.body.password; - const passwordConfirm = req.body.passwordConfirm; - - if (password !== passwordConfirm) { - return next(new AppError('Tus contraseñas deben coincidir.')); - } - - delete req.body.password; - delete req.body.passwordConfirm; - - // Update existing user - newUser = await ArchitectUser.findOneAndUpdate( - { _id: existingUser._id }, - { $set: req.body }, - { - new: true, - runValidators: true, - useFindAndModify: true, - } - ); - - // Update password - newUser = await ArchitectUser.findOneAndUpdate( - { _id: existingUser._id }, - { - $set: { - password: await bcrypt.hash(password, 12), - isOverwritten: true, - }, - }, - { - new: true, - runValidators: false, - useFindAndModify: true, - } - ); - } else if ( - existingUser.isLegacy === true && - existingUser.isOverwritten === true - ) { - return next( - new AppError( - 'Una persona ya se ha inscrito en el portal con estos datos. Crea una nueva cuenta o si crees que es un error contacta a gerencia.' - ) - ); + if (existingUser.isLegacy === true) { + return await createRegistrationRequest(req, existingUser, res); } else if ( existingUser.isLegacy === false && existingUser.isOverwritten === true @@ -162,20 +149,20 @@ exports.signUpArchitectUser = catchAsync(async (req, res, next) => { ) ); } - } else { - // Create new user - newUser = await ArchitectUser.create(req.body); } + let newUser; + newUser = await ArchitectUser.create(req.body); + // Uncomment after emails after payed - // // Send welcome email - // try { - // await new Email(newUser, process.env.LANDING_URL).sendWelcomeUser(); - // } catch (error) { - // return next( - // new AppError('Hemos tenido problemas enviando un correo de bienvenida.', 500) - // ); - // } + // Send welcome email + try { + await new Email(newUser, process.env.LANDING_URL).sendWelcomeUser(); + } catch (error) { + return next( + new AppError('Hemos tenido problemas enviando un correo de bienvenida.', 500) + ); + } // Send JWT token return createSendToken(newUser, 'architect', 201, req, res); diff --git a/caeq-backend/controllers/caeq.user.controller.js b/caeq-backend/controllers/caeq.user.controller.js index 4674935d..705bf9ce 100644 --- a/caeq-backend/controllers/caeq.user.controller.js +++ b/caeq-backend/controllers/caeq.user.controller.js @@ -44,16 +44,16 @@ exports.acceptCaeqUser = catchAsync(async (req, res, next) => { await CaeqUser.findByIdAndUpdate(adminId, { verified: true }); // Uncomment after emails after payed - // try { - // await new Email(caeqUser).sendAdminAccepted(); - // } catch (error) { - // return next( - // new AppError( - // 'Hemos tenido problemas enviando un correo de verificacion. El usuario ha sido verificado.', - // 500 - // ) - // ); - // } + try { + await new Email(caeqUser).sendAdminAccepted(); + } catch (error) { + return next( + new AppError( + 'Hemos tenido problemas enviando un correo de verificacion. El usuario ha sido verificado.', + 500 + ) + ); + } res.status(200).json({ status: 'success', @@ -82,16 +82,16 @@ exports.rejectCaeqUser = catchAsync(async (req, res, next) => { const caeqUser = await CaeqUser.findByIdAndDelete(adminId); // Uncomment after emails after payed - // try { - // await new Email(caeqUser).sendAdminRejected(); - // } catch (error) { - // return next( - // new AppError( - // 'Hemos tenido problemas enviando un correo de verificacion. El usuario ha sido eliminado.', - // 500 - // ) - // ); - // } + try { + await new Email(caeqUser).sendAdminRejected(); + } catch (error) { + return next( + new AppError( + 'Hemos tenido problemas enviando un correo de verificacion. El usuario ha sido eliminado.', + 500 + ) + ); + } res.status(200).json({ status: 'success', diff --git a/caeq-backend/models/architect.user.model.js b/caeq-backend/models/architect.user.model.js index e31a8c9f..972d2ae3 100644 --- a/caeq-backend/models/architect.user.model.js +++ b/caeq-backend/models/architect.user.model.js @@ -6,7 +6,6 @@ const crypto = require('crypto'); // UPDATE TEST DATA AFTER UPDATING ARCHITECT MODEL const ArchitectUserSchema = new mongoose.Schema({ collegiateNumber: { - unique: true, type: Number, required: [true, 'Por favor dinos tu número de colegiado!'], }, diff --git a/caeq-backend/models/regiesterRequests.model.js b/caeq-backend/models/regiesterRequests.model.js new file mode 100644 index 00000000..81da994a --- /dev/null +++ b/caeq-backend/models/regiesterRequests.model.js @@ -0,0 +1,34 @@ +const mongoose = require('mongoose'); + +const registerRequest = new mongoose.Schema( + { + overwrites: { + type: mongoose.Schema.ObjectId, + ref: 'architect.user', + required: [ + true, + 'Se necesita nueva información para sobreescribir a un colegiado.', + ], + }, + newInfo: { + type: mongoose.Schema.ObjectId, + ref: 'Course', + required: [ + true, + 'Se necesita nueva información para sobreescribir a un colegiado.', + ], + }, + architectNumber: { + type: Number, + required: [ + true, + 'Una petición de registro necesita de un número de colegiado.', + ], + }, + }, + { timestamps: true } +); + +const RegisterRequest = mongoose.model('RegisterRequest', registerRequest); + +module.exports = RegisterRequest; diff --git a/caeq-backend/utils/apiFeatures.js b/caeq-backend/utils/apiFeatures.js index 66cdc67b..5d072c11 100644 --- a/caeq-backend/utils/apiFeatures.js +++ b/caeq-backend/utils/apiFeatures.js @@ -29,13 +29,10 @@ class APIFeatures { /\b(gte|gt|lte|lt|regex|np|in )\b/g, (match) => `$${match}` ); - console.log(typeof queryString); - console.log(queryString); queryString = queryString.replace( /"\$regex":"([^"]*)"/g, '"$regex": "$1", "$options": "i"' ); - console.log(queryString); let query = JSON.parse(queryString); this.query.find(query); From b114fc05d3b9e419404c02eb2ce3d1ee66231755 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 10:26:33 -0600 Subject: [PATCH 02/19] Refactored auth controller --- .../controllers/architect.user.controller.js | 94 ++++++++++++++++++- caeq-backend/controllers/auth.controller.js | 19 ++-- 2 files changed, 100 insertions(+), 13 deletions(-) diff --git a/caeq-backend/controllers/architect.user.controller.js b/caeq-backend/controllers/architect.user.controller.js index 89ffc56a..8b1770b2 100644 --- a/caeq-backend/controllers/architect.user.controller.js +++ b/caeq-backend/controllers/architect.user.controller.js @@ -1,6 +1,8 @@ const factory = require('./handlerFactory.controller'); const ArchitectUser = require('../models/architect.user.model'); +const RegisterRequest = require('../models/regiesterRequests.model'); const APIFeatures = require(`../utils/apiFeatures`); +const catchAsync = require('../utils/catchAsync'); exports.getAllArchitectUsers = factory.getAll(ArchitectUser, 'specialties'); exports.getArchitectUser = factory.getOne(ArchitectUser, 'specialties'); @@ -11,7 +13,7 @@ exports.deleteArchitectUser = factory.deleteOne(ArchitectUser); /** * This is a function that gets all architects with authorizationToShareInfo set to true. */ -exports.getAllPublicArchitectUsers = async (req, res) => { +exports.getAllPublicArchitectUsers = catchAsync(async (req, res) => { let filter = {}; let query = ArchitectUser.find({ authorizationToShareInfo: true, @@ -29,4 +31,92 @@ exports.getAllPublicArchitectUsers = async (req, res) => { results: documents.length, data: { documents }, }); -}; +}); + +/** + * Accepts an architect user's request to become a member. + * + * @function + * @async + * @param {Object} req - The request object. + * @param {Object} res - The response object. + * @param {function} next - The next middleware function. + * @returns {Promise} A Promise that resolves when the operation is complete. + * @throws {AppError} If the request cannot be processed. + * + * @example + * // Example usage: + * acceptArchitectUser(req, res, next); + */ +exports.acceptArchitectUser = catchAsync(async (req, res, next) => { + const architectId = req.params.id; + + const architect = ( + await ArchitectUser.find({ _id: architectId }).select({ + verified: 1, + fullName: 1, + email: 1, + }) + )[0]; + if (!architect || architect.verified === true) { + return next( + new AppError('No se puede realizar esta petición en este administrador.', 400) + ); + } + + await ArchitectUser.findByIdAndUpdate(architectId, { verified: true }); + + // Uncomment after emails after payed + try { + await new Email(architect).sendAdminAccepted(); + } catch (error) { + return next( + new AppError( + 'Hemos tenido problemas enviando un correo de verificacion. El usuario ha sido verificado.', + 500 + ) + ); + } + + res.status(200).json({ + status: 'success', + message: 'Arquitecto verificado. El usuario ahora cuenta con acceso al portal.', + }); +}); + +/** + * Rejects a Architect user's request to become a member and deletes the request. + * + * @function + * @async + * @param {Object} req - The request object. + * @param {Object} res - The response object. + * @param {function} next - The next middleware function. + * @returns {Promise} A Promise that resolves when the operation is complete. + * + * @example + * // Example usage: + * rejectArchitectUser(req, res, next); + */ +exports.rejectArchitectUser = catchAsync(async (req, res, next) => { + const architectId = req.params.id; + + const architect = await ArchitectUser.findByIdAndDelete(architectId); + + // Uncomment after emails after payed + try { + await new Email(architect).sendAdminRejected(); + } catch (error) { + return next( + new AppError( + 'Hemos tenido problemas enviando un correo de verificacion. El usuario ha sido eliminado.', + 500 + ) + ); + } + + res.status(200).json({ + status: 'success', + message: 'Solicitud de acceso al sistema rechazada. Se ha eliminado la petición.', + }); +}); diff --git a/caeq-backend/controllers/auth.controller.js b/caeq-backend/controllers/auth.controller.js index d50eafa2..ef419479 100644 --- a/caeq-backend/controllers/auth.controller.js +++ b/caeq-backend/controllers/auth.controller.js @@ -70,13 +70,12 @@ exports.signUpCaeqUser = catchAsync(async (req, res, next) => { }); // Uncomment after emails after payed - // try { - // await new Email(newUser).sendWelcomeAdmin(); - // } catch (error) { - // return next( - // new AppError('Hemos tenido problemas enviando un correo de bienvenida.', 500) - // ); - // } + try { + await new Email(newUser).sendWelcomeAdmin(); + } catch (error) { + // Prod logging + console.log(error); + } // After signup a verified admin must approve the new admin res.status(200).json({ @@ -154,14 +153,12 @@ exports.signUpArchitectUser = catchAsync(async (req, res, next) => { let newUser; newUser = await ArchitectUser.create(req.body); - // Uncomment after emails after payed // Send welcome email try { await new Email(newUser, process.env.LANDING_URL).sendWelcomeUser(); } catch (error) { - return next( - new AppError('Hemos tenido problemas enviando un correo de bienvenida.', 500) - ); + // Prod logging + console.log(error); } // Send JWT token From 04b051164994a64ff7576b405111d642eb1729cd Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 10:27:02 -0600 Subject: [PATCH 03/19] Merged develop --- .vscode/settings.json | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index da456d4a..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "workbench.colorCustomizations": { - "activityBar.activeBackground": "#a493aa", - "activityBar.background": "#a493aa", - "activityBar.foreground": "#15202b", - "activityBar.inactiveForeground": "#15202b99", - "activityBarBadge.background": "#786328", - "activityBarBadge.foreground": "#e7e7e7", - "commandCenter.border": "#15202b99", - "sash.hoverBorder": "#a493aa", - "statusBar.background": "#8c7793", - "statusBar.foreground": "#15202b", - "statusBarItem.hoverBackground": "#725f78", - "statusBarItem.remoteBackground": "#8c7793", - "statusBarItem.remoteForeground": "#15202b", - "titleBar.activeBackground": "#8c7793", - "titleBar.activeForeground": "#15202b", - "titleBar.inactiveBackground": "#8c779399", - "titleBar.inactiveForeground": "#15202b99" - }, - "peacock.color": "#8C7793" -} \ No newline at end of file From fc85b241471558f10327160299077bf8895f9437 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 11:46:39 -0600 Subject: [PATCH 04/19] Added reject and accept controllers --- .../controllers/architect.user.controller.js | 71 ++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/caeq-backend/controllers/architect.user.controller.js b/caeq-backend/controllers/architect.user.controller.js index dbfdd1d7..f9d2bc49 100644 --- a/caeq-backend/controllers/architect.user.controller.js +++ b/caeq-backend/controllers/architect.user.controller.js @@ -51,33 +51,34 @@ exports.getAllPublicArchitectUsers = catchAsync(async (req, res) => { * acceptArchitectUser(req, res, next); */ exports.acceptArchitectUser = catchAsync(async (req, res, next) => { - const architectId = req.params.id; - - const architect = ( - await ArchitectUser.find({ _id: architectId }).select({ - verified: 1, - fullName: 1, - email: 1, - }) - )[0]; - if (!architect || architect.verified === true) { - return next( - new AppError('No se puede realizar esta petición en este administrador.', 400) - ); + const registrationRequestId = req.params.id; + + const registerRequest = await RegisterRequest.findById( + registrationRequestId + ).populate('newInfo'); + + if (!registerRequest) { + return next(new AppError('La petición de registro ya no existe.', 400)); } - await ArchitectUser.findByIdAndUpdate(architectId, { verified: true }); + const newArchitectInfo = registerRequest.newInfo; + + await ArchitectUser.findByIdAndUpdate(registerRequest.overwrites, { + ...newArchitectInfo, + isLegacy: true, + overwritten: true, + }); + + await ArchitectUser.findByIdAndDelete(newArchitectInfo._id); + + await RegisterRequest.findByIdAndDelete(registrationRequestId); - // Uncomment after emails after payed + // Uncomment after emails are payed try { - await new Email(architect).sendAdminAccepted(); + await new Email(newArchitectInfo).sendAdminAccepted(); } catch (error) { - return next( - new AppError( - 'Hemos tenido problemas enviando un correo de verificacion. El usuario ha sido verificado.', - 500 - ) - ); + // Production logging + console.log(error); } res.status(200).json({ @@ -101,24 +102,28 @@ exports.acceptArchitectUser = catchAsync(async (req, res, next) => { * rejectArchitectUser(req, res, next); */ exports.rejectArchitectUser = catchAsync(async (req, res, next) => { - const architectId = req.params.id; + const registrationRequestId = req.params.id; - const architect = await ArchitectUser.findByIdAndDelete(architectId); + const registerRequest = await RegisterRequest.findById(registrationRequestId); - // Uncomment after emails after payed + if (!registerRequest) { + return next(new AppError('La petición de registro ya no existe.', 400)); + } + + await ArchitectUser.findByIdAndDelete(registerRequest.newInfo); + + await RegisterRequest.findByIdAndDelete(registrationRequestId); + + // Uncomment after emails are payed try { - await new Email(architect).sendAdminRejected(); + await new Email(newArchitectInfo).sendAdminRejected(); } catch (error) { - return next( - new AppError( - 'Hemos tenido problemas enviando un correo de verificacion. El usuario ha sido eliminado.', - 500 - ) - ); + // Production logging + console.log(error); } res.status(200).json({ status: 'success', - message: 'Solicitud de acceso al sistema rechazada. Se ha eliminado la petición.', + message: 'Arquitecto verificado. El usuario ahora cuenta con acceso al portal.', }); }); From 3499cd10e41f3b8ccf798db5691f749e7855c8cb Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 13:02:19 -0600 Subject: [PATCH 05/19] Fixed auth unit tests --- .../controllers/architect.user.controller.js | 5 ++ caeq-backend/controllers/auth.controller.js | 17 +++-- caeq-backend/models/architect.user.model.js | 3 + caeq-backend/models/testdata.setup.js | 47 ++++++++++---- caeq-backend/routes/architect.user.route.js | 12 +++- .../architect.user.auth.test.js | 65 ++++++++++++++++--- .../architect.user.register.test.js | 0 7 files changed, 125 insertions(+), 24 deletions(-) create mode 100644 caeq-backend/tests/architectUsers/architect.user.register.test.js diff --git a/caeq-backend/controllers/architect.user.controller.js b/caeq-backend/controllers/architect.user.controller.js index f9d2bc49..7d6f471b 100644 --- a/caeq-backend/controllers/architect.user.controller.js +++ b/caeq-backend/controllers/architect.user.controller.js @@ -5,6 +5,10 @@ const APIFeatures = require(`../utils/apiFeatures`); const catchAsync = require('../utils/catchAsync'); exports.getAllArchitectUsers = factory.getAll(ArchitectUser, 'specialties'); +exports.getAllRegistrationRequests = factory.getAll(RegisterRequest, [ + 'newInfo', + 'overwrites', +]); exports.getArchitectUser = factory.getOne(ArchitectUser, 'specialties'); exports.createArchitectUser = factory.createOne(ArchitectUser); exports.updateArchitectUser = factory.updateOne(ArchitectUser); @@ -65,6 +69,7 @@ exports.acceptArchitectUser = catchAsync(async (req, res, next) => { await ArchitectUser.findByIdAndUpdate(registerRequest.overwrites, { ...newArchitectInfo, + email: newArchitectInfo.email, isLegacy: true, overwritten: true, }); diff --git a/caeq-backend/controllers/auth.controller.js b/caeq-backend/controllers/auth.controller.js index 21e0fc09..0f73a77c 100644 --- a/caeq-backend/controllers/auth.controller.js +++ b/caeq-backend/controllers/auth.controller.js @@ -94,7 +94,13 @@ exports.signUpCaeqUser = catchAsync(async (req, res, next) => { * @returns {Promise} - A Promise that resolves when the registration process is complete. */ async function createRegistrationRequest(req, existingUser, res) { - const updatedArchitect = await ArchitectUser.create(req.body); + const email = req.body.email; + delete req.body.email; + const updatedArchitect = await ArchitectUser.create({ + ...req.body, + email: `${Date.now()}${email}`, + newEmail: email, + }); await RegisterRequest.create({ overwrites: existingUser, @@ -123,7 +129,7 @@ exports.signUpArchitectUser = catchAsync(async (req, res, next) => { const passwordConfirm = req.body.passwordConfirm; if (password !== passwordConfirm) { - return next(new AppError('Tus contraseñas deben coincidir.')); + return next(new AppError('Tus contraseñas deben coincidir.', 400)); } // Check if user already exists @@ -202,13 +208,16 @@ exports.loginArchitectUser = catchAsync(async (req, res, next) => { if (!user) { return next( new AppError( - 'Email incorrecto. No hay un usuario registrado con este correo.', + 'Email incorrecto. No hay un usuario registrado con este correo. Si te registraste recientemente, por favor espera a que un administrador verifique tu perfil.', 401 ) ); } else if (!(await user.correctPassword(password, user.password))) { return next( - new AppError('Contraseña incorrecta. Intente de nuevo por favor.', 401) + new AppError( + 'Contraseña incorrecta. Intente de nuevo por favor. Si te registraste recientemente, por favor espera a que un administrador verifique tu perfil.', + 401 + ) ); } diff --git a/caeq-backend/models/architect.user.model.js b/caeq-backend/models/architect.user.model.js index 972d2ae3..1b363070 100644 --- a/caeq-backend/models/architect.user.model.js +++ b/caeq-backend/models/architect.user.model.js @@ -138,6 +138,9 @@ const ArchitectUserSchema = new mongoose.Schema({ trim: true, validate: [validator.isEmail, 'Necesitas un correo válido.'], }, + newEmail: { + type: String, + }, password: { type: String, required: [true, 'Por favor provee una contraseña.'], diff --git a/caeq-backend/models/testdata.setup.js b/caeq-backend/models/testdata.setup.js index 3981a1b9..e0cefbd5 100644 --- a/caeq-backend/models/testdata.setup.js +++ b/caeq-backend/models/testdata.setup.js @@ -16,9 +16,9 @@ const AttendeesData = require('./data/attendee.js'); const Gathering = require('./gathering.model'); const GatheringData = require('./data/gathering.js'); const Inscription = require('./inscription.model'); -const Services = require('./roomOffer.model.js') +const Services = require('./roomOffer.model.js'); const ServicesData = require('./data/services.js'); - +const RegisterRequests = require('./regiesterRequests.model'); /** * Set up 'CaeqUser' data by populating the database with the provided test data. @@ -66,15 +66,13 @@ const setUpSessionData = catchAsync(async () => { SessionsData[4].course = courses[1]._id; SessionsData[5].course = courses[1]._id; SessionsData[6].course = courses[2]._id; - + await populateDb(Session, SessionsData); }); const setUpAttendeesData = catchAsync(async () => { const gatherings = await Gathering.find(); - const architect = await ArchitectUser.find() - // console.log(architect[1].fullName) - // console.log(architect[1]._id) + const architect = await ArchitectUser.find(); AttendeesData[0].idGathering = gatherings[0]._id; AttendeesData[1].idGathering = gatherings[1]._id; AttendeesData[2].idGathering = gatherings[2]._id; @@ -92,7 +90,7 @@ const setUpAttendeesData = catchAsync(async () => { AttendeesData[5].idArchitect = architect[1]._id; AttendeesData[6].idArchitect = architect[1]._id; AttendeesData[7].idArchitect = architect[2]._id; - + await populateDb(Attendees, AttendeesData); //console.log(AttendeesData) }); @@ -106,6 +104,32 @@ const setUpCourseData = catchAsync(async () => { await populateDb(Course, CourseData); }); +/** + * Set up 'RegisterRequest' data by populating the database with the provided test data. + * + * This function is wrapped in 'catchAsync' to handle any asynchronous errors that may occur during execution. + */ +const setUpregisterRequests = catchAsync(async () => { + const user1 = await ArchitectUser.findOne({ email: 'jcastr@tec.mx' }); + const user2 = await ArchitectUser.findOne({ email: 'javier@example.com' }); + const user3 = await ArchitectUser.findOne({ email: 'isabel@example.com' }); + + const registerRequestData = [ + { + overwrites: user1._id, + newInfo: user2._id, + architectNumber: 45672, + }, + { + overwrites: user1._id, + newInfo: user3._id, + architectNumber: 45672, + }, + ]; + + await populateDb(RegisterRequests, registerRequestData); +}); + /** * Set up 'Specialty' data by populating the database with the provided test data. * @@ -119,13 +143,13 @@ const setUpGatheringData = catchAsync(async () => { await populateDb(Gathering, GatheringData); }); - const setUpServicesData = catchAsync(async () => { +const setUpServicesData = catchAsync(async () => { await populateDb(Services, ServicesData); }); /** * Set up 'Inscription' data by populating the database with the provided test data. - * + * * This function is wrapped in 'catchAsync' to handle any asynchronous errors that may occur during execution. */ const setUpInsciptionData = catchAsync(async () => { @@ -135,11 +159,11 @@ const setUpInsciptionData = catchAsync(async () => { const inscriptionData = [ { course: course._id, - user: user1._id + user: user1._id, }, { course: course._id, - user: user2._id + user: user2._id, }, ]; await populateDb(Inscription, inscriptionData); @@ -161,6 +185,7 @@ exports.setUpDbWithMuckData = catchAsync(async () => { await setUpAttendeesData(); await setUpInsciptionData(); await setUpServicesData(); + await setUpregisterRequests(); console.log('Test data uploaded to DB'); }); diff --git a/caeq-backend/routes/architect.user.route.js b/caeq-backend/routes/architect.user.route.js index 90b0d70a..cb067e21 100644 --- a/caeq-backend/routes/architect.user.route.js +++ b/caeq-backend/routes/architect.user.route.js @@ -7,6 +7,9 @@ const { updateArchitectUser, deleteArchitectUser, getAllPublicArchitectUsers, + getAllRegistrationRequests, + acceptArchitectUser, + rejectArchitectUser, } = require(`${__dirname}/../controllers/architect.user.controller.js`); const { @@ -27,8 +30,15 @@ router.post('/auth/signup', fileParser, filesController.formatCV, signUpArchitec router.post('/auth/login', loginArchitectUser); router.post('/forgot-password', forgotPasswordArchitectUser); router.patch('/reset-password/:token', resetPasswordArchitectUser); -router.route('/').get(getAllArchitectUsers).post(createArchitectUser); +router.route('/accept-architect/:id').patch(acceptArchitectUser); +router.route('/reject-architect/:id').patch(rejectArchitectUser); + +router + .route('/registration-requests') + .get(protect, restrictTo('caeq'), getAllRegistrationRequests); + +router.route('/').get(getAllArchitectUsers).post(createArchitectUser); router .route('/:id') .get(protect, restrictTo('caeq', 'self'), getArchitectUser) diff --git a/caeq-backend/tests/architectUsers/architect.user.auth.test.js b/caeq-backend/tests/architectUsers/architect.user.auth.test.js index b1a9f5c7..3b9ac4bd 100644 --- a/caeq-backend/tests/architectUsers/architect.user.auth.test.js +++ b/caeq-backend/tests/architectUsers/architect.user.auth.test.js @@ -22,7 +22,7 @@ const testArchitectLogin = async () => { expect(resTest2.statusCode).toEqual(401); expect(resTest2.body.message).toEqual( - 'Contraseña incorrecta. Intente de nuevo por favor.' + 'Contraseña incorrecta. Intente de nuevo por favor. Si te registraste recientemente, por favor espera a que un administrador verifique tu perfil.' ); }; @@ -33,8 +33,7 @@ const testArchitectSignUp = async () => { email: 'cesar@example.com', password: password, passwordConfirm: password, - collegiateNumber: 45672, - fullName: 'Luis García', + collegiateNumber: 90101, memberType: 'Miembro de número', classification: 'Docente', DRONumber: 'DRO98765', @@ -63,7 +62,6 @@ const testArchitectSignUp = async () => { expect(resTest1.statusCode).toEqual(201); expect(resTest1.body).toBeTruthy(); - expect(resTest1.body.data.user.email).toEqual('cesar@example.com'); const resLoginTest = await agent.post('/architectusers/auth/login').send({ email: 'cesar@example.com', @@ -72,7 +70,6 @@ const testArchitectSignUp = async () => { expect(resLoginTest.statusCode).toEqual(201); expect(resLoginTest.body).toBeTruthy(); - expect(resLoginTest.body.data.user.email).toEqual('cesar@example.com'); const resTest2 = await agent.post('/architectusers/auth/signup').send({ fullName: 'Pablo Jimenez', @@ -146,10 +143,9 @@ const testArchitectSignUp = async () => { }); expect(resTest3.statusCode).toEqual(400); - expect(resTest3.body.message).toEqual( - 'Datos inválidos: Por favor ingresa la misma contraseña.' - ); + expect(resTest3.body.message).toEqual('Tus contraseñas deben coincidir.'); }; + const testArchitectSignUpNew = async () => { const password = 'password789'; const resTest1 = await agent.post('/architectusers/auth/signup').send({ @@ -199,6 +195,58 @@ const testArchitectSignUpNew = async () => { expect(resLoginTest.body.data.user.email).toEqual('sesar@example.com'); }; +const testRegistrationCreation = async () => { + const password = 'password789'; + const resTest1 = await agent.post('/architectusers/auth/signup').send({ + fullName: 'Pablo Jimenez', + email: 'pablito@example.com', + password: password, + passwordConfirm: password, + collegiateNumber: 45672, + memberType: 'Miembro de número', + classification: 'Docente', + DRONumber: 'DRO98765', + authorizationToShareInfo: true, + lifeInsurance: false, + lifeInsureID: '9937557b', + age: 40, + gender: 'Hombre', + cellphone: 5551112222, + homePhone: 5553334444, + officePhone: 5555556666, + emergencyContact: 'Ana García 5557778888', + mainProfessionalActivity: 'Ingeniero Civil', + dateOfAdmission: 2002, + dateOfBirth: new Date('1983-07-20'), + municipalityOfLabor: 'Querétaro', + linkCV: 'https://example.com/luisgarcia-cv', + university: 'Universidad Autónoma de Querétaro', + professionalLicense: 'P98765', + workAddress: '123 Avenida Principal, Querétaro', + homeAddress: '456 Calle Secundaria, Querétaro', + specialty: 'Corresponsable en seguridad estructural', + positionsInCouncil: 'Vocal', + capacitationHours: 90, + }); + + expect(resTest1.statusCode).toEqual(200); + expect(resTest1.body).toBeTruthy(); + expect(resTest1.body.message).toEqual( + 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto número 45672 perfil y te de acceso al portal.' + ); + + const resLoginTest = await agent.post('/architectusers/auth/login').send({ + email: 'pablito@example.com', + password: password, + }); + + expect(resLoginTest.statusCode).toEqual(401); + expect(resLoginTest.body).toBeTruthy(); + expect(resLoginTest.body.message).toEqual( + 'Email incorrecto. No hay un usuario registrado con este correo. Si te registraste recientemente, por favor espera a que un administrador verifique tu perfil.' + ); +}; + beforeAll(async () => { await connectDB(); await setUpDbWithMuckData(); @@ -208,4 +256,5 @@ describe('Architect login successful', () => { test('successful', () => testArchitectLogin()); test('successful', () => testArchitectSignUp()); test('successful', () => testArchitectSignUpNew()); + test('successful', () => testRegistrationCreation()); }); diff --git a/caeq-backend/tests/architectUsers/architect.user.register.test.js b/caeq-backend/tests/architectUsers/architect.user.register.test.js new file mode 100644 index 00000000..e69de29b From de56df12649d90665d595f0db3347dc75d46f26f Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 16:23:37 -0600 Subject: [PATCH 06/19] Added unit tetsing to the backend --- .../controllers/architect.user.controller.js | 25 ++- caeq-backend/controllers/auth.controller.js | 11 +- caeq-backend/models/data/architect.user.js | 12 +- .../models/regiesterRequests.model.js | 7 +- caeq-backend/models/testdata.setup.js | 8 +- .../architect.user.register.test.js | 168 ++++++++++++++++++ 6 files changed, 209 insertions(+), 22 deletions(-) diff --git a/caeq-backend/controllers/architect.user.controller.js b/caeq-backend/controllers/architect.user.controller.js index 7d6f471b..bb738962 100644 --- a/caeq-backend/controllers/architect.user.controller.js +++ b/caeq-backend/controllers/architect.user.controller.js @@ -3,6 +3,8 @@ const ArchitectUser = require('../models/architect.user.model'); const RegisterRequest = require('../models/regiesterRequests.model'); const APIFeatures = require(`../utils/apiFeatures`); const catchAsync = require('../utils/catchAsync'); +const AppError = require('../utils/appError'); +const Email = require('../utils/email'); exports.getAllArchitectUsers = factory.getAll(ArchitectUser, 'specialties'); exports.getAllRegistrationRequests = factory.getAll(RegisterRequest, [ @@ -59,7 +61,10 @@ exports.acceptArchitectUser = catchAsync(async (req, res, next) => { const registerRequest = await RegisterRequest.findById( registrationRequestId - ).populate('newInfo'); + ).populate({ + path: 'newInfo', + select: '-_id -email -isLegacy -overwritten -collegiateNumber +password', + }); if (!registerRequest) { return next(new AppError('La petición de registro ya no existe.', 400)); @@ -68,13 +73,15 @@ exports.acceptArchitectUser = catchAsync(async (req, res, next) => { const newArchitectInfo = registerRequest.newInfo; await ArchitectUser.findByIdAndUpdate(registerRequest.overwrites, { - ...newArchitectInfo, - email: newArchitectInfo.email, + email: newArchitectInfo.newEmail, isLegacy: true, overwritten: true, + collegiateNumber: registerRequest.architectNumber, + ...newArchitectInfo._doc, }); - await ArchitectUser.findByIdAndDelete(newArchitectInfo._id); + const registerRequestNewInfo = await RegisterRequest.findById(registrationRequestId); + await ArchitectUser.findByIdAndDelete(registerRequestNewInfo.newInfo); await RegisterRequest.findByIdAndDelete(registrationRequestId); @@ -109,19 +116,21 @@ exports.acceptArchitectUser = catchAsync(async (req, res, next) => { exports.rejectArchitectUser = catchAsync(async (req, res, next) => { const registrationRequestId = req.params.id; - const registerRequest = await RegisterRequest.findById(registrationRequestId); + const registerRequest = await RegisterRequest.findById( + registrationRequestId + ).populate('newInfo'); if (!registerRequest) { return next(new AppError('La petición de registro ya no existe.', 400)); } - await ArchitectUser.findByIdAndDelete(registerRequest.newInfo); + await ArchitectUser.findByIdAndDelete(registerRequest.newInfo._id); await RegisterRequest.findByIdAndDelete(registrationRequestId); // Uncomment after emails are payed try { - await new Email(newArchitectInfo).sendAdminRejected(); + await new Email(registerRequest.newInfo).sendAdminRejected(); } catch (error) { // Production logging console.log(error); @@ -129,6 +138,6 @@ exports.rejectArchitectUser = catchAsync(async (req, res, next) => { res.status(200).json({ status: 'success', - message: 'Arquitecto verificado. El usuario ahora cuenta con acceso al portal.', + message: 'Solicitud eliminada. El usuario no fue aceptado en el portal.', }); }); diff --git a/caeq-backend/controllers/auth.controller.js b/caeq-backend/controllers/auth.controller.js index 0f73a77c..c6ca3f03 100644 --- a/caeq-backend/controllers/auth.controller.js +++ b/caeq-backend/controllers/auth.controller.js @@ -133,7 +133,10 @@ exports.signUpArchitectUser = catchAsync(async (req, res, next) => { } // Check if user already exists - const existingUser = await ArchitectUser.findOne({ collegiateNumber }); + const existingUser = await ArchitectUser.findOne({ + collegiateNumber, + }); + console.log('User', existingUser); if (existingUser) { if (existingUser.isLegacy === true) { @@ -144,13 +147,15 @@ exports.signUpArchitectUser = catchAsync(async (req, res, next) => { ) { return next( new AppError( - 'El colegiado que intentas sobreescribir se inscribió recientemente y no forma parte del viejo sistema.' + 'El colegiado que intentas sobreescribir se inscribió recientemente y no forma parte del viejo sistema.', + 400 ) ); } else { return next( new AppError( - 'Algo salió muy mal.No hemos podido sobreescribir los datos.' + 'Algo salió muy mal.No hemos podido sobreescribir los datos.', + 400 ) ); } diff --git a/caeq-backend/models/data/architect.user.js b/caeq-backend/models/data/architect.user.js index 68f2a4f9..76ed9121 100644 --- a/caeq-backend/models/data/architect.user.js +++ b/caeq-backend/models/data/architect.user.js @@ -128,7 +128,7 @@ const architectUserTestData = [ passwordConfirm: 'passwordabc', }, { - collegiateNumber: 24680, + collegiateNumber: 7838, fullName: 'Javier López', memberType: 'Miembro Adherente', classification: 'Docente', @@ -155,12 +155,15 @@ const architectUserTestData = [ positionsInCouncil: 'Secretario', capacitationHours: 80, annuity: false, - email: 'javier@example.com', + email: '97et9et7e90rt7javier@example.com', + newEmail: 'javier@example.com', password: 'password123', passwordConfirm: 'password123', + isLegacy: true, + isOverwritten: false, }, { - collegiateNumber: 13579, + collegiateNumber: 1329, fullName: 'Isabel Torres', memberType: 'Miembro Pasante', classification: 'Convenio', @@ -187,7 +190,8 @@ const architectUserTestData = [ positionsInCouncil: 'Vocal', capacitationHours: 95, annuity: true, - email: 'isabel@example.com', + email: '2654874682754723isabel@example.com', + newEmail: 'isabel@example.com', password: 'password456', passwordConfirm: 'password456', }, diff --git a/caeq-backend/models/regiesterRequests.model.js b/caeq-backend/models/regiesterRequests.model.js index 81da994a..6fdfa8b4 100644 --- a/caeq-backend/models/regiesterRequests.model.js +++ b/caeq-backend/models/regiesterRequests.model.js @@ -5,14 +5,11 @@ const registerRequest = new mongoose.Schema( overwrites: { type: mongoose.Schema.ObjectId, ref: 'architect.user', - required: [ - true, - 'Se necesita nueva información para sobreescribir a un colegiado.', - ], + required: [true, 'Se necesita un colegiado al que sobreescribir.'], }, newInfo: { type: mongoose.Schema.ObjectId, - ref: 'Course', + ref: 'architect.user', required: [ true, 'Se necesita nueva información para sobreescribir a un colegiado.', diff --git a/caeq-backend/models/testdata.setup.js b/caeq-backend/models/testdata.setup.js index e0cefbd5..3e8c7d69 100644 --- a/caeq-backend/models/testdata.setup.js +++ b/caeq-backend/models/testdata.setup.js @@ -111,8 +111,12 @@ const setUpCourseData = catchAsync(async () => { */ const setUpregisterRequests = catchAsync(async () => { const user1 = await ArchitectUser.findOne({ email: 'jcastr@tec.mx' }); - const user2 = await ArchitectUser.findOne({ email: 'javier@example.com' }); - const user3 = await ArchitectUser.findOne({ email: 'isabel@example.com' }); + const user2 = await ArchitectUser.findOne({ + email: '97et9et7e90rt7javier@example.com', + }); + const user3 = await ArchitectUser.findOne({ + email: '2654874682754723isabel@example.com', + }); const registerRequestData = [ { diff --git a/caeq-backend/tests/architectUsers/architect.user.register.test.js b/caeq-backend/tests/architectUsers/architect.user.register.test.js index e69de29b..06d15518 100644 --- a/caeq-backend/tests/architectUsers/architect.user.register.test.js +++ b/caeq-backend/tests/architectUsers/architect.user.register.test.js @@ -0,0 +1,168 @@ +const request = require('supertest'); +const app = require('../../app'); +const RegistrationRequest = require('../../models/regiesterRequests.model'); +const { connectDB } = require('../config/databaseTest'); +const { setUpDbWithMuckData } = require('../../models/testdata.setup'); +const { loginAdmin } = require('../config/authSetUp'); + +const agent = request.agent(app); +let registrationRequests; + +const testAcceptNewUser = async () => { + const endpoint = `/architectusers/accept-architect/${registrationRequests[0]._id}`; + const res = await agent.patch(endpoint).send(); + + expect(res.statusCode).toEqual(200); + expect(res.body.message).toEqual( + 'Arquitecto verificado. El usuario ahora cuenta con acceso al portal.' + ); + + const res2 = await agent.patch(endpoint).send(); + + expect(res2.statusCode).toEqual(400); + expect(res2.body.message).toEqual('La petición de registro ya no existe.'); +}; + +const testRejectNewUser = async () => { + const endpoint = `/architectusers/reject-architect/${registrationRequests[1]._id}`; + const res = await agent.patch(endpoint).send(); + + expect(res.statusCode).toEqual(200); + expect(res.body.message).toEqual( + 'Solicitud eliminada. El usuario no fue aceptado en el portal.' + ); + + const res2 = await agent.patch(endpoint).send(); + + expect(res2.statusCode).toEqual(400); + expect(res2.body.message).toEqual('La petición de registro ya no existe.'); +}; + +const testPendingProfileNoLogin = async () => { + const password = 'password789'; + const resTest1 = await agent.post('/architectusers/auth/signup').send({ + fullName: 'Jorge Castro', + email: 'pablin@tec.mx', + password: password, + passwordConfirm: password, + collegiateNumber: 45672, + memberType: 'Miembro de número', + classification: 'Docente', + DRONumber: 'DRO98765', + authorizationToShareInfo: true, + lifeInsurance: false, + lifeInsureID: '9937557b', + age: 40, + gender: 'Hombre', + cellphone: 5551112222, + homePhone: 5553334444, + officePhone: 5555556666, + emergencyContact: 'Ana García 5557778888', + mainProfessionalActivity: 'Ingeniero Civil', + dateOfAdmission: 2002, + dateOfBirth: new Date('1983-07-20'), + municipalityOfLabor: 'Querétaro', + linkCV: 'https://example.com/luisgarcia-cv', + university: 'Universidad Autónoma de Querétaro', + professionalLicense: 'P98765', + workAddress: '123 Avenida Principal, Querétaro', + homeAddress: '456 Calle Secundaria, Querétaro', + specialty: 'Corresponsable en seguridad estructural', + positionsInCouncil: 'Vocal', + capacitationHours: 90, + }); + + expect(resTest1.statusCode).toEqual(200); + expect(resTest1.body).toBeTruthy(); + expect(resTest1.body.message).toEqual( + 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto número 45672 perfil y te de acceso al portal.' + ); + + const resLoginTest = await agent.post('/architectusers/auth/login').send({ + email: 'pablito@example.com', + password: password, + }); + + expect(resLoginTest.statusCode).toEqual(401); + expect(resLoginTest.body).toBeTruthy(); + expect(resLoginTest.body.message).toEqual( + 'Email incorrecto. No hay un usuario registrado con este correo. Si te registraste recientemente, por favor espera a que un administrador verifique tu perfil.' + ); +}; + +const testAcceptedProfileLogin = async () => { + const password = 'password789'; + const resTest1 = await agent.post('/architectusers/auth/signup').send({ + fullName: 'Pablo Jimenez', + email: 'correoaceptado@example.com', + password: password, + passwordConfirm: password, + collegiateNumber: 45672, + memberType: 'Miembro de número', + classification: 'Docente', + DRONumber: 'DRO98765', + authorizationToShareInfo: true, + lifeInsurance: false, + lifeInsureID: '9937557b', + age: 40, + gender: 'Hombre', + cellphone: 5551112222, + homePhone: 5553334444, + officePhone: 5555556666, + emergencyContact: 'Ana García 5557778888', + mainProfessionalActivity: 'Ingeniero Civil', + dateOfAdmission: 2002, + dateOfBirth: new Date('1983-07-20'), + municipalityOfLabor: 'Querétaro', + linkCV: 'https://example.com/luisgarcia-cv', + university: 'Universidad Autónoma de Querétaro', + professionalLicense: 'P98765', + workAddress: '123 Avenida Principal, Querétaro', + homeAddress: '456 Calle Secundaria, Querétaro', + specialty: 'Corresponsable en seguridad estructural', + positionsInCouncil: 'Vocal', + capacitationHours: 90, + }); + + expect(resTest1.statusCode).toEqual(200); + expect(resTest1.body).toBeTruthy(); + expect(resTest1.body.message).toEqual( + 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto número 45672 perfil y te de acceso al portal.' + ); + + await loginAdmin(agent, 'john@example.com', 'password123'); + const registrationRequest = await RegistrationRequest.find().populate('newInfo'); + const registrationId = registrationRequest.filter( + (registration) => registration.newInfo.newEmail === 'correoaceptado@example.com' + )[0]._id; + + const endpoint = `/architectusers/accept-architect/${registrationId}`; + const resApprove = await agent.patch(endpoint).send(); + + expect(resApprove.statusCode).toEqual(200); + expect(resApprove.body.message).toEqual( + 'Arquitecto verificado. El usuario ahora cuenta con acceso al portal.' + ); + + const resLoginTest = await agent.post('/architectusers/auth/login').send({ + email: 'correoaceptado@example.com', + password: password, + }); + + expect(resLoginTest.statusCode).toEqual(201); + expect(resLoginTest.body).toBeTruthy(); +}; + +beforeAll(async () => { + await connectDB(); + await setUpDbWithMuckData(); + await loginAdmin(agent, 'john@example.com', 'password123'); + registrationRequests = await RegistrationRequest.find(); +}); + +describe('Architect login successful', () => { + test('successful', () => testAcceptedProfileLogin()); + test('successful', () => testPendingProfileNoLogin()); + test('successful', () => testAcceptNewUser()); + test('successful', () => testRejectNewUser()); +}); From b825ccfaa3b3d8cbbc9d40dd05bb108797d05571 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 21:29:03 -0600 Subject: [PATCH 07/19] Implemented frontend functionality --- .../controllers/architect.user.controller.js | 3 +- caeq-backend/controllers/auth.controller.js | 1 + caeq-backend/models/architect.user.model.js | 4 + .../ArchitectUser/ArchitecUser.PATCH.js | 34 ++- .../client/ArchitectUser/ArchitectUser.GET.js | 18 +- .../attendeesButton/AttendeesButton.jsx | 71 +++--- .../src/components/cards/RegistrationCard.jsx | 57 +++++ .../components/cards/RegistrationCard.scss | 45 ++++ caeq-web-portal/src/routes.js | 8 +- .../src/screens/AcceptAdmin/AcceptAdmin.js | 204 +++++++++++++++--- .../src/screens/AcceptAdmin/AcceptAdmin.scss | 9 + .../src/screens/Profile/Profile.js | 2 +- 12 files changed, 379 insertions(+), 77 deletions(-) create mode 100644 caeq-web-portal/src/components/cards/RegistrationCard.jsx create mode 100644 caeq-web-portal/src/components/cards/RegistrationCard.scss diff --git a/caeq-backend/controllers/architect.user.controller.js b/caeq-backend/controllers/architect.user.controller.js index bb738962..70c319a7 100644 --- a/caeq-backend/controllers/architect.user.controller.js +++ b/caeq-backend/controllers/architect.user.controller.js @@ -63,7 +63,7 @@ exports.acceptArchitectUser = catchAsync(async (req, res, next) => { registrationRequestId ).populate({ path: 'newInfo', - select: '-_id -email -isLegacy -overwritten -collegiateNumber +password', + select: '-_id -email -isLegacy -overwritten -collegiateNumber -isRequest +password', }); if (!registerRequest) { @@ -77,6 +77,7 @@ exports.acceptArchitectUser = catchAsync(async (req, res, next) => { isLegacy: true, overwritten: true, collegiateNumber: registerRequest.architectNumber, + isRequest: false, ...newArchitectInfo._doc, }); diff --git a/caeq-backend/controllers/auth.controller.js b/caeq-backend/controllers/auth.controller.js index c6ca3f03..f346e28f 100644 --- a/caeq-backend/controllers/auth.controller.js +++ b/caeq-backend/controllers/auth.controller.js @@ -100,6 +100,7 @@ async function createRegistrationRequest(req, existingUser, res) { ...req.body, email: `${Date.now()}${email}`, newEmail: email, + isRequest: true, }); await RegisterRequest.create({ diff --git a/caeq-backend/models/architect.user.model.js b/caeq-backend/models/architect.user.model.js index 1b363070..bed090c4 100644 --- a/caeq-backend/models/architect.user.model.js +++ b/caeq-backend/models/architect.user.model.js @@ -170,6 +170,10 @@ const ArchitectUserSchema = new mongoose.Schema({ type: Boolean, default: true, }, + isRequest: { + type: Boolean, + default: false, + }, }); // Indexing admin properties for optimized search diff --git a/caeq-web-portal/src/client/ArchitectUser/ArchitecUser.PATCH.js b/caeq-web-portal/src/client/ArchitectUser/ArchitecUser.PATCH.js index 043a9846..334e6019 100644 --- a/caeq-web-portal/src/client/ArchitectUser/ArchitecUser.PATCH.js +++ b/caeq-web-portal/src/client/ArchitectUser/ArchitecUser.PATCH.js @@ -1,6 +1,5 @@ -import axios from "axios"; -import baseApiEndpoint from "../backendConfig"; - +import axios from 'axios'; +import baseApiEndpoint from '../backendConfig'; /** * Sends a PATCH request to the '/architectusers/resetpassword/:token' endpoint to reset the user's password. @@ -25,7 +24,6 @@ export async function patchResetPasswordArchitec(token, newPassword, passwordCon return response.data; } - /** * Updates an architect user by ID. * @@ -48,3 +46,31 @@ export async function updateArchitectUserByID(id, data) { }); return response.data; } + +/** + * Accepts an architect user's request to become a member. + * @async + * @function getArchitectUsers + * @returns {Promise} A promise that resolves to an array of architect user documents. + */ +export async function patchAcceptRegistration(id) { + let endpoint = `${baseApiEndpoint}/architectusers/accept-architect/${id}`; + + const response = await axios.patch(endpoint); + + return response.data; +} + +/** + * Rejects an architect user's request to become a member. + * @async + * @function getArchitectUsers + * @returns {Promise} A promise that resolves to an array of architect user documents. + */ +export async function patchRejectRegistration(id) { + let endpoint = `${baseApiEndpoint}/architectusers/reject-architect/${id}`; + + const response = await axios.patch(endpoint); + + return response.data; +} diff --git a/caeq-web-portal/src/client/ArchitectUser/ArchitectUser.GET.js b/caeq-web-portal/src/client/ArchitectUser/ArchitectUser.GET.js index 62480619..200102fb 100644 --- a/caeq-web-portal/src/client/ArchitectUser/ArchitectUser.GET.js +++ b/caeq-web-portal/src/client/ArchitectUser/ArchitectUser.GET.js @@ -11,14 +11,14 @@ export async function getAllArchitectUsers( filtersParams = '', pageLimit = 100 ) { - let endpoint = `${baseApiEndpoint}/architectusers?page=${page}&limit=${pageLimit}&${filtersParams}`; + let endpoint = `${baseApiEndpoint}/architectusers?page=${page}&limit=${pageLimit}&isRequest=false&${filtersParams}`; const response = await axios.get(endpoint); return response.data.data.documents; } export async function getAllPublicArchitectUsers(page = 1, filtersParams = '') { - let endpoint = `${baseApiEndpoint}/architectusers/public?page=${page}&limit=${paginationPageLimit}&${filtersParams}`; + let endpoint = `${baseApiEndpoint}/architectusers/public?page=${page}&limit=${paginationPageLimit}&isRequest=false&${filtersParams}`; const response = await axios.get(endpoint); return response.data.data.documents; @@ -62,3 +62,17 @@ export async function getArchitectUsers() { return response.data.data.documents; } + +/** + * Retrieves a list of architect user registration requests from the server. + * @async + * @function getArchitectUsers + * @returns {Promise} A promise that resolves to an array of architect user documents. + */ +export async function getArchitectRegistrationRequest() { + let endpoint = `${baseApiEndpoint}/architectusers/registration-requests`; + + const response = await axios.get(endpoint); + + return response.data.data.documents; +} diff --git a/caeq-web-portal/src/components/attendeesButton/AttendeesButton.jsx b/caeq-web-portal/src/components/attendeesButton/AttendeesButton.jsx index 85294227..9d2a4a1b 100644 --- a/caeq-web-portal/src/components/attendeesButton/AttendeesButton.jsx +++ b/caeq-web-portal/src/components/attendeesButton/AttendeesButton.jsx @@ -20,42 +20,45 @@ function AttendancesComponent({ attendances }) { return (

Asistencias a Asambleas

-
- {uniqueYears.map((year) => ( -
- handleYearClick(year)} - > - {year} - - {selectedYear === year && ( -
- {attendances - .filter( - (asistencia) => - asistencia.idGathering.year === year && - asistencia.attended - ) - .map((asistencia) => { - const date = new Date( - asistencia.idGathering.date - ); +
+ {uniqueYears.length > 0 ? ( + uniqueYears.map((year) => ( +
+ handleYearClick(year)}> + {year} + + {selectedYear === year && ( +
+ {attendances + .filter( + (asistencia) => + asistencia.idGathering.year === year && + asistencia.attended + ) + .map((asistencia) => { + const date = new Date( + asistencia.idGathering.date + ); - date.setDate(date.getDate() + 1); + date.setDate(date.getDate() + 1); - return ( -

- {date.toLocaleDateString('en-GB')} - - Modalidad: {asistencia.modality} -

- ); - })} -
- )} -
- ))} + return ( +

+ {date.toLocaleDateString('en-GB')} - + Modalidad: {asistencia.modality} +

+ ); + })} +
+ )} +
+ )) + ) : ( +

No hay asistencias registradas

+ )}
); diff --git a/caeq-web-portal/src/components/cards/RegistrationCard.jsx b/caeq-web-portal/src/components/cards/RegistrationCard.jsx new file mode 100644 index 00000000..951a83bc --- /dev/null +++ b/caeq-web-portal/src/components/cards/RegistrationCard.jsx @@ -0,0 +1,57 @@ +import './AdminCard.scss'; +import './RegistrationCard.scss'; +import AcceptIcon from '../icons/AcceptIcon.png'; +import RejectIcon from '../icons/RejectIcon.png'; + +const RegistrationCard = ({ + id, + acceptRegistration, + rejectRegistration, + collegiateNumber, + overwrites, + newInfo, +}) => { + return ( +
+

Arquitecto número {collegiateNumber}

+
+
+

Nuevo

+

Nombre: {newInfo.fullName}

+

Correo: {newInfo.newEmail}

+

Ingreso: {newInfo.dateOfAdmission || 'Sin fecha de admisión'}

+

DRO: {newInfo.DRONumber || 'Sin número'}

+

Celular: {newInfo.cellphone || 'Sin número celular'}

+

+ INE: Descargar +

+
+
+

Antes

+

Nombre: {overwrites.fullName}

+

Correo: {overwrites.email}

+

+ Ingreso: {overwrites.dateOfAdmission || 'Sin fecha de admisión'} +

+

DRO: {overwrites.DRONumber || 'Sin número'}

+

Celular: {overwrites.cellphone || 'Sin número celular'}

+
+
+ +
+ acceptRegistration(id)} + src={AcceptIcon} + alt={`Accept Icon`} + /> + rejectRegistration(id)} + src={RejectIcon} + alt={`Reject Icon`} + /> +
+
+ ); +}; + +export default RegistrationCard; diff --git a/caeq-web-portal/src/components/cards/RegistrationCard.scss b/caeq-web-portal/src/components/cards/RegistrationCard.scss new file mode 100644 index 00000000..15ef5121 --- /dev/null +++ b/caeq-web-portal/src/components/cards/RegistrationCard.scss @@ -0,0 +1,45 @@ +.registration-card { + padding: 10px; + display: flex; + flex-direction: column; + min-width: 300px; + width: 45%; + border-radius: 20px; + background: #fff; + box-shadow: 4px 7px 15px 0px rgba(0, 0, 0, 0.35); + + .registration-card-info { + display: flex; + flex-direction: row; + width: 100%; + + @media screen and (max-width: 768px) { + flex-direction: column; + } + + .registration-card-details { + display: flex; + flex-direction: column; + width: 50%; + + @media screen and (max-width: 768px) { + width: 100%; + } + + p { + align-self: flex-start; + margin-left: 15px; + } + } + } + + .admin-card-buttons { + align-self: end; + height: 57px; + } + + img { + margin-left: 5px; + cursor: pointer; + } +} diff --git a/caeq-web-portal/src/routes.js b/caeq-web-portal/src/routes.js index 7258054e..91b2cf92 100644 --- a/caeq-web-portal/src/routes.js +++ b/caeq-web-portal/src/routes.js @@ -36,7 +36,7 @@ import AnouncementIcon from '../src/components/icons/AnuncioIcon.png'; import AnouncementIconWhite from '../src/components/icons/AnuncioWhite.png'; import PrincipalIcon from '../src/components/icons/PrincipalIcon.png'; import ServicesIcon from '../src/components/icons/ServicesIcon.png'; -import ServicesIconWhite from '../src/components/icons/ServicesIconWhite.png'; +import ServicesIconWhite from '../src/components/icons/ServicesIconWhite.png'; import RestrictByRole from './components/restrictAccess/RestrictByRole.jsx'; import PrincipalIconWhite from '../src/components/icons/PrincipalIconWHite.png'; @@ -153,8 +153,8 @@ const routes = [ roles: ['caeq'], }, { - path: '/Admins', - name: 'Admins', + path: '/Usuarios', + name: 'Usuarios', icon: AdminIcon, iconWhite: AdminIconWhite, Component: AcceptAdmin, @@ -225,7 +225,7 @@ const routes = [ name: 'Modificar Salón', icon: ServicesIcon, iconWhite: ServicesIconWhite, - Component:CreateRoomOffer, + Component: CreateRoomOffer, isPrivate: true, inNavbar: false, roles: ['caeq'], diff --git a/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js b/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js index b1bb7cb3..ab29ce69 100644 --- a/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js +++ b/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js @@ -3,6 +3,12 @@ import './AcceptAdmin.scss'; import AcceptIcon from '../../components/icons/AcceptIcon.png'; import RejectIcon from '../../components/icons/RejectIcon.png'; import AdminCard from '../../components/cards/AdminCard'; +import RegistrationCard from '../../components/cards/RegistrationCard'; +import { getArchitectRegistrationRequest } from '../../client/ArchitectUser/ArchitectUser.GET'; +import { + patchAcceptRegistration, + patchRejectRegistration, +} from '../../client/ArchitectUser/ArchitecUser.PATCH'; import { getCaeqUsers } from '../../client/CaeqUser/CaeqUser.GET'; import { patchAcceptAdmin, patchRejectAdmin } from '../../client/CaeqUser/CaeqUser.PATCH'; import { @@ -25,6 +31,7 @@ import { useNavigate } from 'react-router-dom'; */ const AcceptAdmin = () => { const [admins, setAdmins] = useState([]); + const [registrations, setRegistrations] = useState([]); const navigate = useNavigate(); useEffect(() => { @@ -33,6 +40,10 @@ const AcceptAdmin = () => { const admins = await getCaeqUsers(false); setAdmins(admins); + + const registrations = await getArchitectRegistrationRequest(); + + setRegistrations(registrations); } catch (error) { FireError(error.response.data.message); } @@ -43,7 +54,7 @@ const AcceptAdmin = () => { * Handles the approval of an administrator. * @param {string} id - The ID of the administrator to be approved. */ - const handleAccept = async (id) => { + const handleAcceptAdmin = async (id) => { try { const confirmation = await FireQuestion( '¿Está seguro que desea aprobar al administrador?', @@ -76,7 +87,7 @@ const AcceptAdmin = () => { * Handles the rejection of an administrator. * @param {string} id - The ID of the administrator to be rejected. */ - const handleReject = async (id) => { + const handleRejectAdmin = async (id) => { try { const confirmation = await FireQuestion( '¿Está seguro que desea rechazar al administrador?', @@ -105,37 +116,168 @@ const AcceptAdmin = () => { } }; + /** + * Handles the approval of a registration. + * @param {string} id - The ID of the registration to be approved. + */ + const handleAcceptRegistration = async (id) => { + try { + const registration = registrations.filter( + (registration) => registration._id === id + )[0]; + + if (registration.overwrites.overwritten) { + const confirmation = await FireQuestion( + '¿Está seguro que desea aprobar al nuevo usuario?', + 'Este usuario ya ha sido sobreescrito anteriormente. Aprobar esta solicitud podría dejar sin acceso al portal a un usuario existente.' + ); + + if (!confirmation.isConfirmed) { + return; + } + } else { + const confirmation = await FireQuestion( + '¿Está seguro que desea aprobar al nuevo usuario?', + 'Esta acción no se puede deshacer. El usuario tendrá acceso a la aplicación.' + ); + + if (!confirmation.isConfirmed) { + return; + } + } + + const swal = FireLoading('Aceptando usuario...'); + const response = await patchAcceptRegistration(id); + + setRegistrations( + registrations.filter((registration) => registration._id !== id) + ); + + swal.close(); + FireSucess(response.message); + } catch (error) { + console.log(error); + FireError(error.response.data.message); + if ( + error.response.data.message === + 'Hemos tenido problemas enviando un correo de verificación. El usuario ha sido verificado.' + ) { + setRegistrations( + registrations.filter((registration) => registration._id !== id) + ); + } + } + }; + + /** + * Handles the rejection of a registration. + * @param {string} id - The ID of the registration to be rejected. + */ + const handleRejectRegistration = async (id) => { + try { + const confirmation = await FireQuestion( + '¿Está seguro que desea rechazar al administrador?', + 'Esta acción no se puede deshacer. El usuario que intenta acceder será eliminado.' + ); + + if (!confirmation.isConfirmed) { + return; + } + + const swal = FireLoading('Rechazando usuario...'); + const response = await patchRejectRegistration(id); + + setRegistrations( + registrations.filter((registration) => registration._id !== id) + ); + + swal.close(); + FireSucess(response.message); + } catch (error) { + FireError(error.response.data.message); + if ( + error.response.data.message === + 'Hemos tenido problemas enviando un correo de verificación. El usuario ha sido eliminado.' + ) { + setRegistrations( + registrations.filter((registration) => registration._id !== id) + ); + } + } + }; + return ( -
-

Administradores por registrar

-

- El propósito de esta sección es conceder autorizaciones a otras cuentas - para permitirles el acceso al portal de administración. -

-
- {`Accept -

- Da click a para aceptar la petición de otorgar a una cuenta acceso al - portal de administración. -

-
-
- {`Reject -

- Da click para rechazar la petición de otorgar a una cuenta acceso al - portal de administración. -

+
+
+

Administradores por registrar

+

+ El propósito de esta sección es conceder autorizaciones a otras + cuentas para permitirles el acceso al portal de administración. +

+
+ {`Accept +

+ Da click a para aceptar la petición de otorgar a una cuenta acceso + al portal de administración. +

+
+
+ {`Reject +

+ Da click para rechazar la petición de otorgar a una cuenta acceso + al portal de administración. +

+
+
+ {admins.map((admin) => ( + + ))} +
-
- {admins.map((admin) => ( - - ))} +
+

Usuarios por registrar

+

+ El propósito de esta sección es conceder autorizaciones a otras + cuentas para permitirles el acceso al portal de arquitectos. Las + peticiones de acceso son creadas cuando un arquitecto se registra con + un número de colegiado que ya existe en la base de datos. +

+
+ {`Accept +

+ Da click a para aceptar la petición de sobreescribir la + información anterior del arquitecto. +

+
+
+ {`Reject +

+ Da click a para rechazar la petición de sobreescribir la + información anterior del arquitecto. +

+
+
+ {registrations.length > 0 ? ( + registrations.map((registration) => ( + + )) + ) : ( +

No hay usuarios por aceptar

+ )} +
); diff --git a/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.scss b/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.scss index 17b64f32..8e92ab4f 100644 --- a/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.scss +++ b/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.scss @@ -42,4 +42,13 @@ flex-wrap: wrap; justify-content: space-around; } + + .registration-cards { + margin-top: 2rem; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-around; + gap: 30px; + } } diff --git a/caeq-web-portal/src/screens/Profile/Profile.js b/caeq-web-portal/src/screens/Profile/Profile.js index 30689982..75dcc6c7 100644 --- a/caeq-web-portal/src/screens/Profile/Profile.js +++ b/caeq-web-portal/src/screens/Profile/Profile.js @@ -219,7 +219,7 @@ const Profile = (props) => {

Municipio: - list' {profile.municipalityOfLabor} + {profile.municipalityOfLabor}

From ece8c6949e2ee1eaf0ee6174a30a4678a54cf028 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 23:36:19 -0600 Subject: [PATCH 08/19] Improved user interactiosn with clearer messages --- .../controllers/architect.user.controller.js | 6 ++-- caeq-backend/controllers/auth.controller.js | 14 +++++++- caeq-backend/utils/email.js | 33 ++++++++++++++++--- .../views/emails/architectAccepted.pug | 7 ++++ .../views/emails/architectRejected.pug | 7 ++++ .../emails/welcomeUserRegistrationPending.pug | 10 ++++++ .../SignupArchitect/SignupArchitect.js | 20 +++++++---- 7 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 caeq-backend/views/emails/architectAccepted.pug create mode 100644 caeq-backend/views/emails/architectRejected.pug create mode 100644 caeq-backend/views/emails/welcomeUserRegistrationPending.pug diff --git a/caeq-backend/controllers/architect.user.controller.js b/caeq-backend/controllers/architect.user.controller.js index 70c319a7..636f4671 100644 --- a/caeq-backend/controllers/architect.user.controller.js +++ b/caeq-backend/controllers/architect.user.controller.js @@ -88,7 +88,8 @@ exports.acceptArchitectUser = catchAsync(async (req, res, next) => { // Uncomment after emails are payed try { - await new Email(newArchitectInfo).sendAdminAccepted(); + newArchitectInfo.email = newArchitectInfo.newEmail; + await new Email(newArchitectInfo).sendArchitectAccepted(); } catch (error) { // Production logging console.log(error); @@ -131,7 +132,8 @@ exports.rejectArchitectUser = catchAsync(async (req, res, next) => { // Uncomment after emails are payed try { - await new Email(registerRequest.newInfo).sendAdminRejected(); + registerRequest.newInfo.email = registerRequest.newInfo.newEmail; + await new Email(registerRequest.newInfo).sendArchitectRejected(); } catch (error) { // Production logging console.log(error); diff --git a/caeq-backend/controllers/auth.controller.js b/caeq-backend/controllers/auth.controller.js index f346e28f..18f78a4c 100644 --- a/caeq-backend/controllers/auth.controller.js +++ b/caeq-backend/controllers/auth.controller.js @@ -109,9 +109,21 @@ async function createRegistrationRequest(req, existingUser, res) { architectNumber: updatedArchitect.collegiateNumber, }); + updatedArchitect.email = email; + // Send welcome email + try { + await new Email( + updatedArchitect, + process.env.LANDING_URL + ).sendWelcomeUserRegistrationRequested(); + } catch (error) { + // Prod logging + console.log(error); + } + res.status(200).json({ status: 'success', - message: `Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto número ${updatedArchitect.collegiateNumber} perfil y te de acceso al portal.`, + message: `Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto con el número de colegiado ${updatedArchitect.collegiateNumber} y te de acceso al portal.`, }); return; diff --git a/caeq-backend/utils/email.js b/caeq-backend/utils/email.js index 2ae9c61f..74983a7e 100644 --- a/caeq-backend/utils/email.js +++ b/caeq-backend/utils/email.js @@ -91,11 +91,20 @@ module.exports = class Email { await this.send('welcomeUser', 'Bienvenido a la familia CAEQ!'); } + /** + * Send a welcome email to a user trying to access the system. + */ + async sendWelcomeUserRegistrationRequested() { + await this.send( + 'welcomeUserRegistrationPending', + 'Bienvenido a la familia CAEQ! Pronto verificaremos tu perfil.' + ); + } + /** * Send a welcome email to an administrator. */ async sendWelcomeAdmin() { - // esto va a ser una pug template await this.send( 'welcomeAdmin', 'Bienvenido a la familia CAEQ! Un administrador revisará tu perfil.' @@ -106,7 +115,6 @@ module.exports = class Email { * Send an email to notify that an administrator's request is accepted. */ async sendAdminAccepted() { - // esto va a ser una pug template await this.send( 'adminAccepted', 'Hemos verificado tu perfil! Bienvenido a la familia CAEQ!' @@ -117,15 +125,30 @@ module.exports = class Email { * Send an email to notify that an administrator's request is rejected. */ async sendAdminRejected() { - // esto va a ser una pug template - await this.send('adminRejected', 'Hemos rechazado tu perfil de acceso.'); + await this.send('adminRejected', 'Hemos rechazado tu solicitud de acceso.'); + } + + /** + * Send an email to notify that an architect's request is accepted. + */ + async sendArchitectAccepted() { + await this.send( + 'architectAccepted', + 'Hemos verificado tu perfil! Bienvenido a la familia CAEQ!' + ); + } + + /** + * Send an email to notify that an architect's request is rejected. + */ + async sendArchitectRejected() { + await this.send('architectRejected', 'Hemos rechazado tu perfil de acceso.'); } /* * Send a password reset email to the user. * Note: This method is commented out in the original code. */ - async sendPasswordReset() { await this.send( 'passwordReset', diff --git a/caeq-backend/views/emails/architectAccepted.pug b/caeq-backend/views/emails/architectAccepted.pug new file mode 100644 index 00000000..1c696f1c --- /dev/null +++ b/caeq-backend/views/emails/architectAccepted.pug @@ -0,0 +1,7 @@ +extends baseEmail + +block content + p Hola Arq. #{firstName}, + p Bienvenido a la familia del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 + p Hemos verificado tu perfil. Ahora cuentas con acceso a la plataforma. + p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-backend/views/emails/architectRejected.pug b/caeq-backend/views/emails/architectRejected.pug new file mode 100644 index 00000000..edf402a4 --- /dev/null +++ b/caeq-backend/views/emails/architectRejected.pug @@ -0,0 +1,7 @@ +extends baseEmail + +block content + p Hola Arq. #{firstName}, + p Lo sentimos, tu perfil no ha sido verificado por los administradores. Tus datos no coinciden con nuestro registro del número de colegiado que elegiste. + p Tu solicitud de acceso ha sido rechazada. Si crees que esto es un error, contáctanos para recibir más información. + p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-backend/views/emails/welcomeUserRegistrationPending.pug b/caeq-backend/views/emails/welcomeUserRegistrationPending.pug new file mode 100644 index 00000000..eae8fe7e --- /dev/null +++ b/caeq-backend/views/emails/welcomeUserRegistrationPending.pug @@ -0,0 +1,10 @@ +extends baseEmail + +block content + p Hola Arq. #{firstName}, + P Un miembro del equipo de CAEQ ha recibido tu solicitud de registro. + p Cuando te registraste usaste un número de colegiado ya en uso. Verificaremos tu información y te contactaremos para darte acceso al sistema. + p Bienvenido a la familia del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 + p Somos una gran familia con muchos cursos y oportunidades de aprendizaje que ofrecer. Empieza revisando nuestra pagina oficial! + p Si necesitas ayuda con alguno de nuestros cursos o servicios no dudes en contactarme! + p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js b/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js index 384f17aa..44a97c4d 100644 --- a/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js +++ b/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js @@ -156,10 +156,10 @@ const Signup = () => { } if (user) { const continueSignUp = await FireQuestion( - 'Arquitecto ya existente', + 'Número de colegiado ya registrado.', `El arquitecto con número de colegiado ${collegiateNumber} ya existe. - ¿Es usted ${user.fullName}? - ¿Desea continuar y actualizar con la información proporcionada?` + ¿Es usted ${user.fullName}? Si no es usted, por favor verifique que el número de colegiado que ingresó es correcto. + ¿Desea continuar y actualizar con la información proporcionada? Un administrador revisará sus datos y le dará acceso a la plataforma en breve.` ); if (!continueSignUp.isConfirmed) return; } @@ -168,16 +168,22 @@ const Signup = () => { try { const swal = FireLoading('Registrando arquitecto...'); const response = await postSignupArchitectUsers(form); - if (response.status === 'success') { + console.log(response); + if (response.status === 'success' && response.statusCode === 201) { const token = response.token; setUserType(token); setToken(token); setArchitectUserSaved(response.data.user); + + swal.close(); + FireSucess('Te has registrado con éxito'); + navigate('/Principal'); + } else { + swal.close(); + FireSucess(response.message); + navigate('/'); } - swal.close(); - FireSucess('Te has registrado con éxito'); - navigate('/Principal'); } catch (error) { FireError(error.response.data.message); } From c4e77f61e073eafd6df981f0f80478bb17c7f85a Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 23:41:31 -0600 Subject: [PATCH 09/19] Documented card component --- .../src/components/cards/RegistrationCard.jsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/caeq-web-portal/src/components/cards/RegistrationCard.jsx b/caeq-web-portal/src/components/cards/RegistrationCard.jsx index 951a83bc..cbb973b0 100644 --- a/caeq-web-portal/src/components/cards/RegistrationCard.jsx +++ b/caeq-web-portal/src/components/cards/RegistrationCard.jsx @@ -3,6 +3,19 @@ import './RegistrationCard.scss'; import AcceptIcon from '../icons/AcceptIcon.png'; import RejectIcon from '../icons/RejectIcon.png'; +/** + * RegistrationCard component for displaying registration details and actions. + * + * @component + * @param {Object} props - The component props. + * @param {string} props.id - The unique identifier for the registration card. + * @param {Function} props.acceptRegistration - Callback function for accepting the registration. + * @param {Function} props.rejectRegistration - Callback function for rejecting the registration. + * @param {string} props.collegiateNumber - The collegiate number of the architect. + * @param {Object} props.overwrites - Information before the update. + * @param {Object} props.newInfo - Information after the update. + * @returns {JSX.Element} JSX representation of the RegistrationCard component. + */ const RegistrationCard = ({ id, acceptRegistration, From d6ec3c0ab3010aa6dd4de1ec494ccc4e4b319a6f Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 23:49:11 -0600 Subject: [PATCH 10/19] Fixed data overflowing --- caeq-web-portal/src/components/cards/RegistrationCard.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/caeq-web-portal/src/components/cards/RegistrationCard.scss b/caeq-web-portal/src/components/cards/RegistrationCard.scss index 15ef5121..46843982 100644 --- a/caeq-web-portal/src/components/cards/RegistrationCard.scss +++ b/caeq-web-portal/src/components/cards/RegistrationCard.scss @@ -4,6 +4,8 @@ flex-direction: column; min-width: 300px; width: 45%; + max-height: 600px; + overflow: auto; border-radius: 20px; background: #fff; box-shadow: 4px 7px 15px 0px rgba(0, 0, 0, 0.35); @@ -21,6 +23,7 @@ display: flex; flex-direction: column; width: 50%; + overflow: auto; @media screen and (max-width: 768px) { width: 100%; From ba694b00c52b5fafe2b3e4316e54633662293626 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Sun, 12 Nov 2023 23:51:52 -0600 Subject: [PATCH 11/19] Fixed unit tests with registration message --- caeq-backend/tests/architectUsers/architect.user.auth.test.js | 2 +- .../tests/architectUsers/architect.user.register.test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/caeq-backend/tests/architectUsers/architect.user.auth.test.js b/caeq-backend/tests/architectUsers/architect.user.auth.test.js index 3b9ac4bd..82240b42 100644 --- a/caeq-backend/tests/architectUsers/architect.user.auth.test.js +++ b/caeq-backend/tests/architectUsers/architect.user.auth.test.js @@ -232,7 +232,7 @@ const testRegistrationCreation = async () => { expect(resTest1.statusCode).toEqual(200); expect(resTest1.body).toBeTruthy(); expect(resTest1.body.message).toEqual( - 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto número 45672 perfil y te de acceso al portal.' + 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto con el número de colegiado 45672 y te de acceso al portal.' ); const resLoginTest = await agent.post('/architectusers/auth/login').send({ diff --git a/caeq-backend/tests/architectUsers/architect.user.register.test.js b/caeq-backend/tests/architectUsers/architect.user.register.test.js index 06d15518..87f3f04c 100644 --- a/caeq-backend/tests/architectUsers/architect.user.register.test.js +++ b/caeq-backend/tests/architectUsers/architect.user.register.test.js @@ -75,7 +75,7 @@ const testPendingProfileNoLogin = async () => { expect(resTest1.statusCode).toEqual(200); expect(resTest1.body).toBeTruthy(); expect(resTest1.body.message).toEqual( - 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto número 45672 perfil y te de acceso al portal.' + 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto con el número de colegiado 45672 perfil y te de acceso al portal.' ); const resLoginTest = await agent.post('/architectusers/auth/login').send({ @@ -127,7 +127,7 @@ const testAcceptedProfileLogin = async () => { expect(resTest1.statusCode).toEqual(200); expect(resTest1.body).toBeTruthy(); expect(resTest1.body.message).toEqual( - 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto número 45672 perfil y te de acceso al portal.' + 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto con el número de colegiado 45672 perfil y te de acceso al portal.' ); await loginAdmin(agent, 'john@example.com', 'password123'); From bd5b27acf898694c4a27b141b131647f38fbd3da Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Mon, 13 Nov 2023 01:02:22 -0600 Subject: [PATCH 12/19] Fixed typo on unit tests --- .../tests/architectUsers/architect.user.register.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/caeq-backend/tests/architectUsers/architect.user.register.test.js b/caeq-backend/tests/architectUsers/architect.user.register.test.js index 87f3f04c..9a7a0164 100644 --- a/caeq-backend/tests/architectUsers/architect.user.register.test.js +++ b/caeq-backend/tests/architectUsers/architect.user.register.test.js @@ -75,7 +75,7 @@ const testPendingProfileNoLogin = async () => { expect(resTest1.statusCode).toEqual(200); expect(resTest1.body).toBeTruthy(); expect(resTest1.body.message).toEqual( - 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto con el número de colegiado 45672 perfil y te de acceso al portal.' + 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto con el número de colegiado 45672 y te de acceso al portal.' ); const resLoginTest = await agent.post('/architectusers/auth/login').send({ @@ -127,7 +127,7 @@ const testAcceptedProfileLogin = async () => { expect(resTest1.statusCode).toEqual(200); expect(resTest1.body).toBeTruthy(); expect(resTest1.body.message).toEqual( - 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto con el número de colegiado 45672 perfil y te de acceso al portal.' + 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto con el número de colegiado 45672 y te de acceso al portal.' ); await loginAdmin(agent, 'john@example.com', 'password123'); From fba61a5795d6180f54e67f81d5ffa683cc74053d Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Mon, 13 Nov 2023 10:19:29 -0600 Subject: [PATCH 13/19] Removed unused console.logs --- caeq-backend/controllers/auth.controller.js | 1 - caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/caeq-backend/controllers/auth.controller.js b/caeq-backend/controllers/auth.controller.js index 18f78a4c..ae9b00d1 100644 --- a/caeq-backend/controllers/auth.controller.js +++ b/caeq-backend/controllers/auth.controller.js @@ -149,7 +149,6 @@ exports.signUpArchitectUser = catchAsync(async (req, res, next) => { const existingUser = await ArchitectUser.findOne({ collegiateNumber, }); - console.log('User', existingUser); if (existingUser) { if (existingUser.isLegacy === true) { diff --git a/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js b/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js index 44a97c4d..94f676e7 100644 --- a/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js +++ b/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js @@ -168,7 +168,7 @@ const Signup = () => { try { const swal = FireLoading('Registrando arquitecto...'); const response = await postSignupArchitectUsers(form); - console.log(response); + if (response.status === 'success' && response.statusCode === 201) { const token = response.token; From f44bc9c63560a2199516a5bb2695931cc57d941c Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Mon, 13 Nov 2023 12:39:39 -0600 Subject: [PATCH 14/19] Fixed reload --- .../src/screens/AcceptAdmin/AcceptAdmin.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js b/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js index ab29ce69..2b7a2efc 100644 --- a/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js +++ b/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js @@ -149,8 +149,21 @@ const AcceptAdmin = () => { const swal = FireLoading('Aceptando usuario...'); const response = await patchAcceptRegistration(id); + const newUser = registration.newInfo; + const oldUser = registration.overwrites; + setRegistrations( - registrations.filter((registration) => registration._id !== id) + registrations + .filter((registration) => registration._id !== id) + .map((registration) => { + if (registration.overwrites._id === oldUser._id) { + registration.overwrites = newUser; + registration.overwrites.email = newUser.newEmail; + } + console.log(registration); + + return registration; + }) ); swal.close(); From ef14a6e23abe3ba26737d431350a6ca454acbc74 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Mon, 13 Nov 2023 14:26:42 -0600 Subject: [PATCH 15/19] Fixed language to make it formal --- caeq-backend/views/emails/architectAccepted.pug | 2 +- caeq-backend/views/emails/architectRejected.pug | 4 ++-- caeq-backend/views/emails/welcomeUserRegistrationPending.pug | 4 ++-- caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/caeq-backend/views/emails/architectAccepted.pug b/caeq-backend/views/emails/architectAccepted.pug index 1c696f1c..3c4bc76d 100644 --- a/caeq-backend/views/emails/architectAccepted.pug +++ b/caeq-backend/views/emails/architectAccepted.pug @@ -3,5 +3,5 @@ extends baseEmail block content p Hola Arq. #{firstName}, p Bienvenido a la familia del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 - p Hemos verificado tu perfil. Ahora cuentas con acceso a la plataforma. + p Hemos verificado los datos que usted ingresó. Ahora cuenta con acceso a la plataforma. p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-backend/views/emails/architectRejected.pug b/caeq-backend/views/emails/architectRejected.pug index edf402a4..08238d3b 100644 --- a/caeq-backend/views/emails/architectRejected.pug +++ b/caeq-backend/views/emails/architectRejected.pug @@ -2,6 +2,6 @@ extends baseEmail block content p Hola Arq. #{firstName}, - p Lo sentimos, tu perfil no ha sido verificado por los administradores. Tus datos no coinciden con nuestro registro del número de colegiado que elegiste. - p Tu solicitud de acceso ha sido rechazada. Si crees que esto es un error, contáctanos para recibir más información. + p Lo sentimos, los datos que ingresaste han sido verificados por los administradores. Los datos no coinciden con nuestro registro del número de colegiado que elegiste. + p La solicitud de acceso ha sido rechazada. Si usted cree que esto es un error, contáctanos para recibir más información. p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-backend/views/emails/welcomeUserRegistrationPending.pug b/caeq-backend/views/emails/welcomeUserRegistrationPending.pug index eae8fe7e..31284165 100644 --- a/caeq-backend/views/emails/welcomeUserRegistrationPending.pug +++ b/caeq-backend/views/emails/welcomeUserRegistrationPending.pug @@ -3,8 +3,8 @@ extends baseEmail block content p Hola Arq. #{firstName}, P Un miembro del equipo de CAEQ ha recibido tu solicitud de registro. - p Cuando te registraste usaste un número de colegiado ya en uso. Verificaremos tu información y te contactaremos para darte acceso al sistema. + p Cuando se registró usó un número de colegiado ya registrado. Verificaremos la información que ingresó y le contactaremos para darle acceso al sistema. p Bienvenido a la familia del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 p Somos una gran familia con muchos cursos y oportunidades de aprendizaje que ofrecer. Empieza revisando nuestra pagina oficial! - p Si necesitas ayuda con alguno de nuestros cursos o servicios no dudes en contactarme! + p Si necesita ayuda con alguno de nuestros cursos o servicios no dude en contactarnos! p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js b/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js index 2b7a2efc..3778d995 100644 --- a/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js +++ b/caeq-web-portal/src/screens/AcceptAdmin/AcceptAdmin.js @@ -254,7 +254,7 @@ const AcceptAdmin = () => {
-

Usuarios por registrar

+

Colegiados por registrar

El propósito de esta sección es conceder autorizaciones a otras cuentas para permitirles el acceso al portal de arquitectos. Las From 761ff0d1db18163b321c4b76d19b9c7829663ae0 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Tue, 14 Nov 2023 13:33:07 -0600 Subject: [PATCH 16/19] Fixed email message and question message --- caeq-backend/views/emails/architectAccepted.pug | 2 +- caeq-backend/views/emails/architectRejected.pug | 2 +- caeq-backend/views/emails/welcomeUser.pug | 2 +- .../views/emails/welcomeUserRegistrationPending.pug | 6 +++--- .../src/screens/SignupArchitect/SignupArchitect.js | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/caeq-backend/views/emails/architectAccepted.pug b/caeq-backend/views/emails/architectAccepted.pug index 3c4bc76d..0ebd616d 100644 --- a/caeq-backend/views/emails/architectAccepted.pug +++ b/caeq-backend/views/emails/architectAccepted.pug @@ -2,6 +2,6 @@ extends baseEmail block content p Hola Arq. #{firstName}, - p Bienvenido a la familia del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 + p Bienvenido al portal del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 p Hemos verificado los datos que usted ingresó. Ahora cuenta con acceso a la plataforma. p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-backend/views/emails/architectRejected.pug b/caeq-backend/views/emails/architectRejected.pug index 08238d3b..dedf8d33 100644 --- a/caeq-backend/views/emails/architectRejected.pug +++ b/caeq-backend/views/emails/architectRejected.pug @@ -2,6 +2,6 @@ extends baseEmail block content p Hola Arq. #{firstName}, - p Lo sentimos, los datos que ingresaste han sido verificados por los administradores. Los datos no coinciden con nuestro registro del número de colegiado que elegiste. + p Lo sentimos, los datos que ingresó han sido verificados por los administradores. Los datos no coinciden con nuestro registro del número de colegiado que eligió. p La solicitud de acceso ha sido rechazada. Si usted cree que esto es un error, contáctanos para recibir más información. p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-backend/views/emails/welcomeUser.pug b/caeq-backend/views/emails/welcomeUser.pug index 843bb136..15473fca 100644 --- a/caeq-backend/views/emails/welcomeUser.pug +++ b/caeq-backend/views/emails/welcomeUser.pug @@ -2,7 +2,7 @@ extends baseEmail block content p Hola Arq. #{firstName}, - p Bienvenido a la familia del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 + p Bienvenido al portal del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 p Somos una gran familia con muchos cursos y oportunidades de aprendizaje que ofrecer. Empieza revisando nuestra pagina oficial! p Si necesitas ayuda con alguno de nuestros cursos o servicios no dudes en contactarme! p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-backend/views/emails/welcomeUserRegistrationPending.pug b/caeq-backend/views/emails/welcomeUserRegistrationPending.pug index 31284165..d1afd323 100644 --- a/caeq-backend/views/emails/welcomeUserRegistrationPending.pug +++ b/caeq-backend/views/emails/welcomeUserRegistrationPending.pug @@ -2,9 +2,9 @@ extends baseEmail block content p Hola Arq. #{firstName}, - P Un miembro del equipo de CAEQ ha recibido tu solicitud de registro. - p Cuando se registró usó un número de colegiado ya registrado. Verificaremos la información que ingresó y le contactaremos para darle acceso al sistema. - p Bienvenido a la familia del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 + P Un miembro del equipo de CAEQ ha recibido su solicitud de registro. + p Cuando hizo su registro usó un número de colegiado ya registrado. Verificaremos la información que ingresó y le contactaremos para darle acceso al sistema. + p Bienvenido al portal del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 p Somos una gran familia con muchos cursos y oportunidades de aprendizaje que ofrecer. Empieza revisando nuestra pagina oficial! p Si necesita ayuda con alguno de nuestros cursos o servicios no dude en contactarnos! p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js b/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js index 94f676e7..5c8e298e 100644 --- a/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js +++ b/caeq-web-portal/src/screens/SignupArchitect/SignupArchitect.js @@ -158,8 +158,8 @@ const Signup = () => { const continueSignUp = await FireQuestion( 'Número de colegiado ya registrado.', `El arquitecto con número de colegiado ${collegiateNumber} ya existe. - ¿Es usted ${user.fullName}? Si no es usted, por favor verifique que el número de colegiado que ingresó es correcto. - ¿Desea continuar y actualizar con la información proporcionada? Un administrador revisará sus datos y le dará acceso a la plataforma en breve.` + ¿Es usted ${user.fullName}? Si no es usted, por favor verifique que el número de colegiado que ingresó es correcto. Presione cancelar. + ¿Desea continuar y actualizar con la información proporcionada? Presione aceptar. Un administrador revisará sus datos y le dará acceso a la plataforma en breve.` ); if (!continueSignUp.isConfirmed) return; } From 1fed2377e38b7dd61a7fec561f91cc2b22484add Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Wed, 15 Nov 2023 18:46:17 -0600 Subject: [PATCH 17/19] Fixed PR comments --- caeq-backend/controllers/architect.user.controller.js | 2 +- caeq-backend/controllers/auth.controller.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/caeq-backend/controllers/architect.user.controller.js b/caeq-backend/controllers/architect.user.controller.js index e0a8f7f6..944b7486 100644 --- a/caeq-backend/controllers/architect.user.controller.js +++ b/caeq-backend/controllers/architect.user.controller.js @@ -19,7 +19,7 @@ exports.deleteArchitectUser = factory.deleteOne(ArchitectUser); /** * This is a function that gets all architects with authorizationToShareInfo set to true. */ -exports.getAllPublicArchitectUsers = async (req, res) => { +exports.getAllPublicArchitectUsers = catchAsync(async (req, res) => { let query = ArchitectUser.find({ authorizationToShareInfo: true, }) diff --git a/caeq-backend/controllers/auth.controller.js b/caeq-backend/controllers/auth.controller.js index ae9b00d1..07e304b6 100644 --- a/caeq-backend/controllers/auth.controller.js +++ b/caeq-backend/controllers/auth.controller.js @@ -232,7 +232,7 @@ exports.loginArchitectUser = catchAsync(async (req, res, next) => { } else if (!(await user.correctPassword(password, user.password))) { return next( new AppError( - 'Contraseña incorrecta. Intente de nuevo por favor. Si te registraste recientemente, por favor espera a que un administrador verifique tu perfil.', + 'Contraseña incorrecta. Intente de nuevo por favor. Si te registraste recientemente, por favor espere a que un administrador verifique su perfil.', 401 ) ); From c6bf07af18ef4a4898b520bfb8037effeb5289a1 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Wed, 15 Nov 2023 19:05:04 -0600 Subject: [PATCH 18/19] Fixed PR comments and language --- caeq-backend/controllers/auth.controller.js | 2 +- .../tests/architectUsers/architect.user.auth.test.js | 2 +- caeq-backend/utils/email.js | 2 +- caeq-backend/views/emails/welcomeUserRegistrationPending.pug | 4 ++-- caeq-web-portal/src/components/cards/RegistrationCard.jsx | 5 ++++- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/caeq-backend/controllers/auth.controller.js b/caeq-backend/controllers/auth.controller.js index 07e304b6..79789e17 100644 --- a/caeq-backend/controllers/auth.controller.js +++ b/caeq-backend/controllers/auth.controller.js @@ -225,7 +225,7 @@ exports.loginArchitectUser = catchAsync(async (req, res, next) => { if (!user) { return next( new AppError( - 'Email incorrecto. No hay un usuario registrado con este correo. Si te registraste recientemente, por favor espera a que un administrador verifique tu perfil.', + 'Email incorrecto. No hay un usuario registrado con este correo. Si se registró recientemente, por favor espere a que un administrador verifique su perfil.', 401 ) ); diff --git a/caeq-backend/tests/architectUsers/architect.user.auth.test.js b/caeq-backend/tests/architectUsers/architect.user.auth.test.js index d966ef99..a3c3b365 100644 --- a/caeq-backend/tests/architectUsers/architect.user.auth.test.js +++ b/caeq-backend/tests/architectUsers/architect.user.auth.test.js @@ -241,7 +241,7 @@ const testRegistrationCreation = async () => { expect(resLoginTest.statusCode).toEqual(401); expect(resLoginTest.body).toBeTruthy(); expect(resLoginTest.body.message).toEqual( - 'Email incorrecto. No hay un usuario registrado con este correo. Si te registraste recientemente, por favor espera a que un administrador verifique tu perfil.' + 'Email incorrecto. No hay un usuario registrado con este correo. Si se registró recientemente, por favor espere a que un administrador verifique su perfil.' ); }; diff --git a/caeq-backend/utils/email.js b/caeq-backend/utils/email.js index 37d86e2e..08b1c4fb 100644 --- a/caeq-backend/utils/email.js +++ b/caeq-backend/utils/email.js @@ -125,7 +125,7 @@ module.exports = class Email { async sendWelcomeUserRegistrationRequested() { await this.send( 'welcomeUserRegistrationPending', - 'Bienvenido a la familia CAEQ! Pronto verificaremos tu perfil.' + 'Bienvenido a la familia CAEQ! Pronto verificaremos su perfil.' ); } diff --git a/caeq-backend/views/emails/welcomeUserRegistrationPending.pug b/caeq-backend/views/emails/welcomeUserRegistrationPending.pug index d1afd323..45a45ea5 100644 --- a/caeq-backend/views/emails/welcomeUserRegistrationPending.pug +++ b/caeq-backend/views/emails/welcomeUserRegistrationPending.pug @@ -5,6 +5,6 @@ block content P Un miembro del equipo de CAEQ ha recibido su solicitud de registro. p Cuando hizo su registro usó un número de colegiado ya registrado. Verificaremos la información que ingresó y le contactaremos para darle acceso al sistema. p Bienvenido al portal del Colegio de Arquitectos del Estado de Querétaro (CAEQ), nos alegra que nos acompañes 🎉🙏 - p Somos una gran familia con muchos cursos y oportunidades de aprendizaje que ofrecer. Empieza revisando nuestra pagina oficial! - p Si necesita ayuda con alguno de nuestros cursos o servicios no dude en contactarnos! + p Somos una gran familia con muchos cursos y oportunidades de aprendizaje que ofrecer. Empieza revisando nuestra página oficial! + p ¡Si necesita ayuda con alguno de nuestros cursos o servicios no dude en contactarnos! p - El equipo de CAEQ. \ No newline at end of file diff --git a/caeq-web-portal/src/components/cards/RegistrationCard.jsx b/caeq-web-portal/src/components/cards/RegistrationCard.jsx index cbb973b0..969f22cd 100644 --- a/caeq-web-portal/src/components/cards/RegistrationCard.jsx +++ b/caeq-web-portal/src/components/cards/RegistrationCard.jsx @@ -32,7 +32,10 @@ const RegistrationCard = ({

Nuevo

Nombre: {newInfo.fullName}

Correo: {newInfo.newEmail}

-

Ingreso: {newInfo.dateOfAdmission || 'Sin fecha de admisión'}

+

+ Ingreso al colegio:{' '} + {newInfo.dateOfAdmission || 'Sin fecha de admisión'} +

DRO: {newInfo.DRONumber || 'Sin número'}

Celular: {newInfo.cellphone || 'Sin número celular'}

From 0cb76f8f8b07d1c22f229777b0142be4e65f0a35 Mon Sep 17 00:00:00 2001 From: CesarJimenezVilleda02 Date: Wed, 15 Nov 2023 22:01:11 -0600 Subject: [PATCH 19/19] Fixed Unit tests --- .../architect.user.auth.test.js | 71 +++++----- .../architect.user.register.test.js | 128 +++++++++--------- 2 files changed, 100 insertions(+), 99 deletions(-) diff --git a/caeq-backend/tests/architectUsers/architect.user.auth.test.js b/caeq-backend/tests/architectUsers/architect.user.auth.test.js index a3c3b365..7847aac3 100644 --- a/caeq-backend/tests/architectUsers/architect.user.auth.test.js +++ b/caeq-backend/tests/architectUsers/architect.user.auth.test.js @@ -22,7 +22,7 @@ const testArchitectLogin = async () => { expect(resTest2.statusCode).toEqual(401); expect(resTest2.body.message).toEqual( - 'Contraseña incorrecta. Intente de nuevo por favor. Si te registraste recientemente, por favor espere a que un administrador verifique perfil.' + 'Contraseña incorrecta. Intente de nuevo por favor. Si te registraste recientemente, por favor espere a que un administrador verifique su perfil.' ); }; @@ -35,7 +35,7 @@ const testArchitectSignUp = async () => { .field('email', 'cesar@example.com') .field('password', password) .field('passwordConfirm', password) - .field('collegiateNumber', 45672) + .field('collegiateNumber', 90801) .field('memberType', 'Miembro de número') .field('classification', 'Docente') .field('DRONumber', 'DRO98765') @@ -103,10 +103,9 @@ const testArchitectSignUp = async () => { .field('positionsInCouncil', 'Vocal') .field('capacitationHours', 90); - expect(resTest2.statusCode).toEqual(500); + expect(resTest2.statusCode).toEqual(200); expect(resTest2.body.message).toEqual( - 'Una persona ya se ha inscrito en el portal con estos datos. ' + - 'Crea una nueva cuenta o si crees que es un error contacta a gerencia.' + 'Te has registrado con éxito, espera a que un administrador verifique que eres el arquitecto con el número de colegiado 45672 y te de acceso al portal.' ); const resTest3 = await agent @@ -195,37 +194,37 @@ const testArchitectSignUpNew = async () => { const testRegistrationCreation = async () => { const password = 'password789'; - const resTest1 = await agent.post('/architectusers/auth/signup').send({ - fullName: 'Pablo Jimenez', - email: 'pablito@example.com', - password: password, - passwordConfirm: password, - collegiateNumber: 45672, - memberType: 'Miembro de número', - classification: 'Docente', - DRONumber: 'DRO98765', - authorizationToShareInfo: true, - lifeInsurance: false, - lifeInsureID: '9937557b', - age: 40, - gender: 'Hombre', - cellphone: 5551112222, - homePhone: 5553334444, - officePhone: 5555556666, - emergencyContact: 'Ana García 5557778888', - mainProfessionalActivity: 'Ingeniero Civil', - dateOfAdmission: 2002, - dateOfBirth: new Date('1983-07-20'), - municipalityOfLabor: 'Querétaro', - linkCV: 'https://example.com/luisgarcia-cv', - university: 'Universidad Autónoma de Querétaro', - professionalLicense: 'P98765', - workAddress: '123 Avenida Principal, Querétaro', - homeAddress: '456 Calle Secundaria, Querétaro', - specialty: 'Corresponsable en seguridad estructural', - positionsInCouncil: 'Vocal', - capacitationHours: 90, - }); + const resTest1 = await agent + .post('/architectusers/auth/signup') + .type('multipart/form-data') + .field('fullName', 'Pablo Jimenez') + .field('email', 'pablito@example.com') + .field('password', password) + .field('passwordConfirm', password) + .field('collegiateNumber', 45672) + .field('memberType', 'Miembro de número') + .field('classification', 'Docente') + .field('DRONumber', 'DRO98765') + .field('authorizationToShareInfo', true) + .field('lifeInsurance', false) + .field('lifeInsureID', '9937557b') + .field('age', 40) + .field('gender', 'Hombre') + .field('cellphone', 5551112222) + .field('homePhone', 5553334444) + .field('officePhone', 5555556666) + .field('emergencyContact', 'Ana García 5557778888') + .field('mainProfessionalActivity', 'Ingeniero Civil') + .field('dateOfAdmission', 2002) + .field('dateOfBirth', new Date('1983-07-20').toISOString()) + .field('municipalityOfLabor', 'Querétaro') + .field('university', 'Universidad Autónoma de Querétaro') + .field('professionalLicense', 'P98765') + .field('workAddress', '123 Avenida Principal, Querétaro') + .field('homeAddress', '456 Calle Secundaria, Querétaro') + .field('specialty', 'Corresponsable en seguridad estructural') + .field('positionsInCouncil', 'Vocal') + .field('capacitationHours', 90); expect(resTest1.statusCode).toEqual(200); expect(resTest1.body).toBeTruthy(); diff --git a/caeq-backend/tests/architectUsers/architect.user.register.test.js b/caeq-backend/tests/architectUsers/architect.user.register.test.js index 9a7a0164..3fbab330 100644 --- a/caeq-backend/tests/architectUsers/architect.user.register.test.js +++ b/caeq-backend/tests/architectUsers/architect.user.register.test.js @@ -40,37 +40,38 @@ const testRejectNewUser = async () => { const testPendingProfileNoLogin = async () => { const password = 'password789'; - const resTest1 = await agent.post('/architectusers/auth/signup').send({ - fullName: 'Jorge Castro', - email: 'pablin@tec.mx', - password: password, - passwordConfirm: password, - collegiateNumber: 45672, - memberType: 'Miembro de número', - classification: 'Docente', - DRONumber: 'DRO98765', - authorizationToShareInfo: true, - lifeInsurance: false, - lifeInsureID: '9937557b', - age: 40, - gender: 'Hombre', - cellphone: 5551112222, - homePhone: 5553334444, - officePhone: 5555556666, - emergencyContact: 'Ana García 5557778888', - mainProfessionalActivity: 'Ingeniero Civil', - dateOfAdmission: 2002, - dateOfBirth: new Date('1983-07-20'), - municipalityOfLabor: 'Querétaro', - linkCV: 'https://example.com/luisgarcia-cv', - university: 'Universidad Autónoma de Querétaro', - professionalLicense: 'P98765', - workAddress: '123 Avenida Principal, Querétaro', - homeAddress: '456 Calle Secundaria, Querétaro', - specialty: 'Corresponsable en seguridad estructural', - positionsInCouncil: 'Vocal', - capacitationHours: 90, - }); + const resTest1 = await agent + .post('/architectusers/auth/signup') + .type('multipart/form-data') + .field('fullName', 'Jorge Castro') + .field('email', 'pablin@tec.mx') + .field('password', password) + .field('passwordConfirm', password) + .field('collegiateNumber', 45672) + .field('memberType', 'Miembro de número') + .field('classification', 'Docente') + .field('DRONumber', 'DRO98765') + .field('authorizationToShareInfo', true) + .field('lifeInsurance', false) + .field('lifeInsureID', '9937557b') + .field('age', 40) + .field('gender', 'Hombre') + .field('cellphone', 5551112222) + .field('homePhone', 5553334444) + .field('officePhone', 5555556666) + .field('emergencyContact', 'Ana García 5557778888') + .field('mainProfessionalActivity', 'Ingeniero Civil') + .field('dateOfAdmission', 2002) + .field('dateOfBirth', new Date('1983-07-20').toISOString()) + .field('municipalityOfLabor', 'Querétaro') + .field('linkCV', 'https://example.com/luisgarcia-cv') + .field('university', 'Universidad Autónoma de Querétaro') + .field('professionalLicense', 'P98765') + .field('workAddress', '123 Avenida Principal, Querétaro') + .field('homeAddress', '456 Calle Secundaria, Querétaro') + .field('specialty', 'Corresponsable en seguridad estructural') + .field('positionsInCouncil', 'Vocal') + .field('capacitationHours', 90); expect(resTest1.statusCode).toEqual(200); expect(resTest1.body).toBeTruthy(); @@ -86,43 +87,44 @@ const testPendingProfileNoLogin = async () => { expect(resLoginTest.statusCode).toEqual(401); expect(resLoginTest.body).toBeTruthy(); expect(resLoginTest.body.message).toEqual( - 'Email incorrecto. No hay un usuario registrado con este correo. Si te registraste recientemente, por favor espera a que un administrador verifique tu perfil.' + 'Email incorrecto. No hay un usuario registrado con este correo. Si se registró recientemente, por favor espere a que un administrador verifique su perfil.' ); }; const testAcceptedProfileLogin = async () => { const password = 'password789'; - const resTest1 = await agent.post('/architectusers/auth/signup').send({ - fullName: 'Pablo Jimenez', - email: 'correoaceptado@example.com', - password: password, - passwordConfirm: password, - collegiateNumber: 45672, - memberType: 'Miembro de número', - classification: 'Docente', - DRONumber: 'DRO98765', - authorizationToShareInfo: true, - lifeInsurance: false, - lifeInsureID: '9937557b', - age: 40, - gender: 'Hombre', - cellphone: 5551112222, - homePhone: 5553334444, - officePhone: 5555556666, - emergencyContact: 'Ana García 5557778888', - mainProfessionalActivity: 'Ingeniero Civil', - dateOfAdmission: 2002, - dateOfBirth: new Date('1983-07-20'), - municipalityOfLabor: 'Querétaro', - linkCV: 'https://example.com/luisgarcia-cv', - university: 'Universidad Autónoma de Querétaro', - professionalLicense: 'P98765', - workAddress: '123 Avenida Principal, Querétaro', - homeAddress: '456 Calle Secundaria, Querétaro', - specialty: 'Corresponsable en seguridad estructural', - positionsInCouncil: 'Vocal', - capacitationHours: 90, - }); + const resTest1 = await agent + .post('/architectusers/auth/signup') + .type('multipart/form-data') + .field('fullName', 'Pablo Jimenez') + .field('email', 'correoaceptado@example.com') + .field('password', password) + .field('passwordConfirm', password) + .field('collegiateNumber', 45672) + .field('memberType', 'Miembro de número') + .field('classification', 'Docente') + .field('DRONumber', 'DRO98765') + .field('authorizationToShareInfo', true) + .field('lifeInsurance', false) + .field('lifeInsureID', '9937557b') + .field('age', 40) + .field('gender', 'Hombre') + .field('cellphone', 5551112222) + .field('homePhone', 5553334444) + .field('officePhone', 5555556666) + .field('emergencyContact', 'Ana García 5557778888') + .field('mainProfessionalActivity', 'Ingeniero Civil') + .field('dateOfAdmission', 2002) + .field('dateOfBirth', new Date('1983-07-20').toISOString()) + .field('municipalityOfLabor', 'Querétaro') + .field('linkCV', 'https://example.com/luisgarcia-cv') + .field('university', 'Universidad Autónoma de Querétaro') + .field('professionalLicense', 'P98765') + .field('workAddress', '123 Avenida Principal, Querétaro') + .field('homeAddress', '456 Calle Secundaria, Querétaro') + .field('specialty', 'Corresponsable en seguridad estructural') + .field('positionsInCouncil', 'Vocal') + .field('capacitationHours', 90); expect(resTest1.statusCode).toEqual(200); expect(resTest1.body).toBeTruthy();