From b239212033de1b36423ccb0e2748bd1c36df8635 Mon Sep 17 00:00:00 2001 From: thanhbao922003 Date: Tue, 5 Nov 2024 14:49:06 +0700 Subject: [PATCH] Sign Up Validate --- src/controller/user.controller.ts | 7 ++++ src/entity/dto/register.dto.ts | 51 ++++++++++++++++++++++++ src/public/js/section.js | 27 +++++++++++++ src/routes/user.routes.ts | 4 +- src/views/professor/courseManagement.pug | 28 +------------ src/views/signup.pug | 26 +++++++++--- 6 files changed, 109 insertions(+), 34 deletions(-) create mode 100644 src/entity/dto/register.dto.ts diff --git a/src/controller/user.controller.ts b/src/controller/user.controller.ts index e266ce8a..21f25994 100644 --- a/src/controller/user.controller.ts +++ b/src/controller/user.controller.ts @@ -14,8 +14,15 @@ import { getSectionsWithLessons, countEnrolledUsersInCourse, } from "../service/course.service"; +const { validationResult } = require('express-validator'); export const register = asyncHandler(async (req: Request, res: Response) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + res.status(400).json({ status: 400, errors: errors.array() }); + return; + } + const { name, email, diff --git a/src/entity/dto/register.dto.ts b/src/entity/dto/register.dto.ts new file mode 100644 index 00000000..9ee39934 --- /dev/null +++ b/src/entity/dto/register.dto.ts @@ -0,0 +1,51 @@ +const { body, validationResult } = require('express-validator'); +exports.registerValidation = [ + body('name') + .notEmpty().withMessage('Tên người dùng là bắt buộc.') + .isLength({ min: 3, max: 50 }).withMessage('Tên người dùng phải có từ 3 đến 50 ký tự.'), + + body('email') + .isEmail().withMessage('Email không hợp lệ.') + .notEmpty().withMessage('Email là bắt buộc.'), + + body('phone_number') + .notEmpty().withMessage('Số điện thoại là bắt buộc.') + .matches(/^\d{10,15}$/).withMessage('Số điện thoại không hợp lệ.'), + + body('date_of_birth') + .notEmpty().withMessage('Ngày sinh là bắt buộc.') + .isDate().withMessage('Ngày sinh không hợp lệ.'), + + body('gender') + .notEmpty().withMessage('Giới tính là bắt buộc.') + .isIn(['male', 'female', 'other']).withMessage('Giới tính không hợp lệ.'), + + body('address') + .notEmpty().withMessage('Địa chỉ là bắt buộc.') + .isLength({ max: 100 }).withMessage('Địa chỉ không được vượt quá 100 ký tự.'), + + body('role') + .notEmpty().withMessage('Vai trò là bắt buộc.') + .isIn(['user', 'professor']).withMessage('Vai trò không hợp lệ.'), + + body('department') + .if(body('role').equals('professor')) + .notEmpty().withMessage('Bộ phận là bắt buộc cho vai trò giáo sư.') + .isLength({ max: 50 }).withMessage('Bộ phận không được vượt quá 50 ký tự.'), + + body('years_of_experience') + .if(body('role').equals('professor')) + .notEmpty().withMessage('Số năm kinh nghiệm là bắt buộc cho vai trò giáo sư.') + .isInt({ min: 0 }).withMessage('Số năm kinh nghiệm phải là số nguyên không âm.'), + + body('password') + .notEmpty().withMessage('Mật khẩu là bắt buộc.') + .isLength({ min: 6 }).withMessage('Mật khẩu phải có ít nhất 6 ký tự.') + .matches(/[A-Z]/).withMessage('Mật khẩu phải có ít nhất một ký tự chữ hoa.') + .matches(/[0-9]/).withMessage('Mật khẩu phải có ít nhất một số.'), + + body('identity_card') + .notEmpty().withMessage('Chứng minh thư là bắt buộc.') + .isLength({ min: 9, max: 12 }).withMessage('Chứng minh thư phải có từ 9 đến 12 ký tự.'), + +]; \ No newline at end of file diff --git a/src/public/js/section.js b/src/public/js/section.js index 74e22dc0..61810d82 100644 --- a/src/public/js/section.js +++ b/src/public/js/section.js @@ -75,6 +75,13 @@ function confirmSectionDelete() { let courseIdToDelete = null; +function showSections(courseId) { + document.getElementById(`sections-${courseId}`).classList.remove('hidden'); +} +function hideSections(courseId) { + document.getElementById(`sections-${courseId}`).classList.add('hidden'); +} + function showDeleteForm(id) { courseIdToDelete = id; $('#deleteModal').removeClass('hidden'); @@ -104,3 +111,23 @@ function confirmDelete() { } } +function addSectionForm() { + const container = document.getElementById('sectionsContainer'); + const newForm = document.createElement('div'); + newForm.className = 'section-form'; + newForm.innerHTML = ` + + + + + + + + + + `; + container.appendChild(newForm); +} + diff --git a/src/routes/user.routes.ts b/src/routes/user.routes.ts index c75f2508..882e9796 100644 --- a/src/routes/user.routes.ts +++ b/src/routes/user.routes.ts @@ -1,13 +1,13 @@ import { Router } from "express"; import * as userController from "../controller/user.controller"; - +const { registerValidation } = require('../entity/dto/register.dto'); const router: Router = Router(); router.get("/signup", (req, res) => { res.render("signup", { title: req.t("home.signup"), pageUrl: "/signup" }); }); -router.post("/register", userController.register); +router.post("/register", registerValidation, userController.register); router.get("/login", (req, res) => { res.render("login", { title: req.t("home.login"), pageUrl: "/login" }); diff --git a/src/views/professor/courseManagement.pug b/src/views/professor/courseManagement.pug index ae89ea10..dc2eb7ee 100644 --- a/src/views/professor/courseManagement.pug +++ b/src/views/professor/courseManagement.pug @@ -158,13 +158,6 @@ block content .btn-group-admin .confirmDelete-btn: button(onclick="confirmSectionDelete()") #{t('admin.confirm-delete')} .cancel-btn: button(type="button" onclick="hideSectionDeleteForm()") #{t('admin.cancel')} - script. - function showSections(courseId) { - document.getElementById(`sections-${courseId}`).classList.remove('hidden'); - } - function hideSections(courseId) { - document.getElementById(`sections-${courseId}`).classList.add('hidden'); - } #createModal.hidden @@ -230,23 +223,4 @@ block content .cancel-btn: button(type="button" onclick="hideDeleteForm()") #{t('admin.cancel')} script. - const courses = JSON.parse('!{JSON.stringify(courses)}'); - function addSectionForm() { - const container = document.getElementById('sectionsContainer'); - const newForm = document.createElement('div'); - newForm.className = 'section-form'; - newForm.innerHTML = ` - - - - - - - - - - `; - container.appendChild(newForm); - } \ No newline at end of file + const courses = JSON.parse('!{JSON.stringify(courses)}'); \ No newline at end of file diff --git a/src/views/signup.pug b/src/views/signup.pug index a8029792..d5567e8f 100644 --- a/src/views/signup.pug +++ b/src/views/signup.pug @@ -12,6 +12,7 @@ block content button.ms-2.mb-1.btn-close(type='button' data-bs-dismiss='toast' aria-label='Close') .toast-body | #{t('signup.signup-success-msg')} + #toastNoAutohideError.toast(role='alert' aria-live='assertive' aria-atomic='true' data-bs-delay="2000" style='opacity: 1; display: none') .toast-header.text-danger i(data-feather='alert-octagon').me-2 @@ -28,6 +29,19 @@ block content form#registerForm.shadow.p-4.custom-width(action='/register' method='POST') .text-center.wow.fadeInUp(data-wow-delay='0.1s') h1.mb-5.bg-white.text-center.px-3 #{t('home.signup')} + + .row.g-3 + if errors && errors.length > 0 + .col-12 + .alert.alert-danger + p Vui lòng sửa lỗi: + if (role === 'professor') + if errors.some(error => error.msg.includes('Bộ phận') || error.msg.includes('Số năm kinh nghiệm')) + each error in errors + p= error.msg + each error in errors + p= error.msg + .row.g-3 .col-12 .form-floating @@ -63,11 +77,11 @@ block content option(value='professor') #{t('admin-user.professor')} label(for='role') #{t('signup.role')} .col-12.form-floating.department-field(style='display: none;') - input#department.form-control(type='text' placeholder='Department' name='department') - label(for='department') #{t('signup.department')} + input#department.form-control(type='text' placeholder='Department' name='department') + label(for='department') #{t('signup.department')} .col-12.form-floating.years-experience-field(style='display: none;') - input#years_of_experience.form-control(type='number' placeholder='Years of Experience' name='years_of_experience') - label(for='years_of_experience') #{t('signup.years-of-experience')} + input#years_of_experience.form-control(type='number' placeholder='Years of Experience' name='years_of_experience') + label(for='years_of_experience') #{t('signup.years-of-experience')} .col-12 .form-floating input#password.form-control(type='password' placeholder='Password' name='password' required) @@ -89,6 +103,7 @@ block content include partial/footer + script. document.querySelector("#registerForm").addEventListener("submit", async function(event) { event.preventDefault(); @@ -106,7 +121,6 @@ block content additional_info: formData.get("additional_info"), department: formData.get("department"), years_of_experience: formData.get("years_of_experience"), - }; try { @@ -127,11 +141,13 @@ block content } catch (error) { const toastElement = document.getElementById("toastNoAutohideError"); toastElement.style.removeProperty("display"); + toastElement.querySelector('.toast-body').textContent = error.responseJSON.errors[0].msg; // Chỉ hiển thị lỗi đầu tiên setTimeout(() => { toastElement.style.display = "none"; }, 2000); } }); + document.querySelector("#role").addEventListener("change", function () { const isProfessor = this.value === "professor"; document.querySelector(".department-field").style.display = isProfessor ? "block" : "none";