diff --git a/app.js b/app.js index 4946f2f..00e3092 100644 --- a/app.js +++ b/app.js @@ -6,6 +6,7 @@ let logger = require("morgan"); let config = require("./config/config.json"); let indexRouter = require("./routes/index"); +let registerRouter = require("./routes/register"); let loginRouter = require("./routes/login"); let logoutRouter = require("./routes/logout"); let commandRouter = require("./routes/command"); @@ -36,6 +37,7 @@ app.use(session(sess)); app.use(express.static(path.join(__dirname, "public"))); app.use("/", indexRouter); +app.use("/register", registerRouter); app.use("/login", loginRouter); app.use("/logout", logoutRouter); app.use("/command", commandRouter); diff --git a/models/department.js b/models/department.js index 4a29790..10fc552 100644 --- a/models/department.js +++ b/models/department.js @@ -6,6 +6,7 @@ module.exports = (sequelize, DataTypes) => { class Department extends Model { static associate(models) { Department.hasMany(models.Command); + Department.hasMany(models.User); } } Department.init({ diff --git a/models/user.js b/models/user.js index f318601..9236d7c 100644 --- a/models/user.js +++ b/models/user.js @@ -5,6 +5,7 @@ const { module.exports = (sequelize, DataTypes) => { class User extends Model { static associate(models) { + User.belongsTo(models.Department); } checkPassword(password) { @@ -19,6 +20,23 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.STRING, primaryKey: true }, + email: { + type: DataTypes.STRING, + allowNull: false, + validate: { + isEmail: true + } + }, + firstName: { + type: DataTypes.STRING, + allowNull: false, + unique: "userFullName" + }, + lastName: { + type: DataTypes.STRING, + allowNull: false, + unique: "userFullName" + }, passwordHash: { type: DataTypes.STRING, allowNull: false, diff --git a/public/javascripts/index.js b/public/javascripts/index.js index d79dd83..4d73b50 100644 --- a/public/javascripts/index.js +++ b/public/javascripts/index.js @@ -1,9 +1,5 @@ const commandAction = document.getElementById("command-action"); const rmButton = document.getElementById("remove-command"); -const more = document.getElementById("more"); -const dark = document.getElementById("dark"); -const about = document.getElementById("about"); -const contact = document.getElementById("contact"); function lastCommandId() { let list = document.querySelectorAll("div.command h2"); @@ -33,21 +29,3 @@ rmButton.addEventListener("click", () => { if (id === 2) rmButton.classList.add("hide"); }); - -more.firstChild.addEventListener("click", () => { - dark.classList.remove("hide"); - about.classList.remove("hide"); -}); - -more.lastChild.addEventListener("click", () => { - dark.classList.remove("hide"); - contact.classList.remove("hide") -}); - -dark.addEventListener("click", () => { - dark.classList.add("hide"); - if (! about.classList.contains("hide")) - about.classList.add("hide"); - else - contact.classList.add("hide"); -}); diff --git a/public/javascripts/layout.js b/public/javascripts/layout.js new file mode 100644 index 0000000..638faf3 --- /dev/null +++ b/public/javascripts/layout.js @@ -0,0 +1,23 @@ +const more = document.getElementById("more"); +const dark = document.getElementById("dark"); +const about = document.getElementById("about"); +const contact = document.getElementById("contact"); + + +more.firstChild.addEventListener("click", () => { + dark.classList.remove("hide"); + about.classList.remove("hide"); +}); + +more.lastChild.addEventListener("click", () => { + dark.classList.remove("hide"); + contact.classList.remove("hide") +}); + +dark.addEventListener("click", () => { + dark.classList.add("hide"); + if (! about.classList.contains("hide")) + about.classList.add("hide"); + else + contact.classList.add("hide"); +}); diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index e790b26..f95c987 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -56,8 +56,8 @@ a { font-size: 100%; } -.field input[type="text"], .field input[type="list"], .field input[type="date"], .field input[type="number"], -.field input[type="checkbox"] { +.field input[type="text"], .field input[type="list"], .field input[type="date"], .field input[type="password"], +.field input[type="number"], .field input[type="checkbox"], .field input[type="email"] { height: 2.5vh; border-top: none; border-left: none; @@ -165,6 +165,26 @@ p.before-link a::before { justify-content: space-around; } +#user { + position: fixed; + top: 0; + right: 0; + display: flex; + padding: 0.3em; + background-color: white; + border-radius: 0 0 0.3em 0.3em; + box-shadow: 0.5em 0.5em 0.5em rgba(0, 0, 255, .2); +} + +#user>p, #user>a { + margin: 0.1em; +} + +#user>a { + text-decoration: underline; + color: black; +} + @media (hover: none) and (pointer: coarse) { body { font-size: xx-large; diff --git a/routes/admin.js b/routes/admin.js index 5fff6dd..7eed48f 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -5,7 +5,7 @@ let models = require("../models"); router.get("/", sessionCheck(3), async (req, res) => { - res.render("admin", {title: "SOD", sandwiches: await models.Sandwich.findAll(), users: await models.User.findAll()}); + res.render("admin", {title: "SOD", user: req.session.user, sandwiches: await models.Sandwich.findAll(), users: await models.User.findAll()}); }); module.exports = router; diff --git a/routes/index.js b/routes/index.js index 5ae3fda..87bec13 100644 --- a/routes/index.js +++ b/routes/index.js @@ -5,7 +5,7 @@ let models = require("../models"); router.get("/", async (req, res) => { let departments = await models.Department.findAll(); let sandwiches = await models.Sandwich.findAll(); - res.render("index", { title: "SOD", departments: departments, sandwiches: sandwiches }); + res.render("index", { title: "SOD", user: req.session.user, departments: departments, sandwiches: sandwiches }); }); module.exports = router; diff --git a/routes/orders.js b/routes/orders.js index 8c23fb8..f2d5265 100644 --- a/routes/orders.js +++ b/routes/orders.js @@ -23,7 +23,7 @@ router.get("/", sessionCheck(2), async (req, res) => { commands[i.Command.DepartmentName][name][i.Command.id] = [] commands[i.Command.DepartmentName][name][i.Command.id].push(i); } - res.render("orders", {title: "SOD", commands: commands, date: date}); + res.render("orders", {title: "SOD", user: req.session.user, commands: commands, date: date}); }); module.exports = router; diff --git a/routes/register.js b/routes/register.js new file mode 100644 index 0000000..2ca5969 --- /dev/null +++ b/routes/register.js @@ -0,0 +1,50 @@ +let express = require("express"); +let router = express.Router(); +let models = require("../models"); + + +router.get("/", async (req, res) => { + if (req.session.user) + res.redirect("/"); + else + res.render("register", {title: "SOD", departments: await models.Department.findAll()}); +}) + .post("/", async (req, res) => { + if (!req.body.username || !req.body.email || !req.body.firstName || !req.body.lastName || + !req.body.department || !req.body.password) + res.render("error", {message: "Invalid register !", error: {status: "Missing args"}}); + else if (await models.User.findByPk(req.body.username)) + res.render("error", {message: "Invalid register !", + error: {status: "Username already taken"}}); + else if (await models.User.findOne({where: {firstName: req.body.firstName, + lastName: req.body.lastName}})) + res.render("error", {message: "Invalid register !", + error: {status: "First & last name already register"}}); + else if (await models.User.findOne({where: {email: req.body.email}})) + res.render("error", {message: "Invalid register !", error: {status: "Email already used"}}); + else { + let department = await models.Department.findByPk(req.body.department); + if (!department) + res.render("error", {message: "Invalid register !", + error: {status: "Invalid department"}}); + else { + try { + let user = await models.User.create({ + username: req.body.username, + email: req.body.email, + firstName: req.body.firstName, + lastName: req.body.lastName, + passwordHash: req.body.password + }); + await user.setDepartment(department); + req.session.user = user; + res.redirect("/"); + } catch (e) { + res.render("error", {message: "Registration fail !", error: {}}); + throw e; + } + } + } + }); + +module.exports = router; diff --git a/routes/sandwiches.js b/routes/sandwiches.js index 2b50a43..0d90d19 100644 --- a/routes/sandwiches.js +++ b/routes/sandwiches.js @@ -10,6 +10,7 @@ router.get("/", sessionCheck(1), async (req, res) => { res.render("sandwiches", { title: "SOD", + user: req.session.user, sandwiches: await models.SandwichCommand.findAll({ attributes: ["SandwichName", [sequelize.fn("COUNT", sequelize.col("SandwichName")), "number"]], where: {date: date}, diff --git a/views/index.pug b/views/index.pug index d807c5c..bcd6d2a 100644 --- a/views/index.pug +++ b/views/index.pug @@ -7,17 +7,17 @@ block content form#command(action="/command" method="POST") div.field label(for="department") Department: - input#department(type="list" list="department-list" name="department" autocomplete="off" required) + input#department(type="list" list="department-list" name="department" value=user ? user.DepartmentName : "" autocomplete="off" required) datalist#department-list each department in departments option(value=department.name) div.field label(for="firstname") First name: - input#firstname(type="text" name="firstName" required) + input#firstname(type="text" name="firstName" value=user ? user.firstName : "" required) div.field label(for="lastname") Last name: - input#lastname(type="text" name="lastName" required) + input#lastname(type="text" name="lastName" value=user ? user.lastName : "" required) div#command1.command h2 Command 1 @@ -39,50 +39,4 @@ block content each sandwich in sandwiches option(value=sandwich.name) - div#more - a About - a Contact - - div#dark.hide - div#about.popup.card.hide - h1 About - p This software respond to the COVID-19 health crisis for the following students' offices food supply - div.images - div - img(src="images/logoBio.png") - p Bio student office - div - img(src="images/logoChimie.png") - p Chemical student office - div - img(src="images/logoGC.png") - p Civil engineering student office - div - img(src="images/logoGCGP.png") - p Chemical and process engineering student office - div - img(src="images/logoGEA.png") - p Management of companies and administrations student office - div - img(src="images/logoInfo.png") - p IT student office - p.before-link Made with ❤️ by - a(href="https://www.linkedin.com/in/florian-charlaix" target="_blank") Florian Charlaix - div#contact.popup.card.hide - h1 Contact - p.before-link Order issue: - a(href="mailto: ") test@test.fr - p.before-link Bio student office: - a(href="mailto: ") - p.before-link Chemical student office: - a(href="mailto: ") - p.before-link Civil engineering student office: - a(href="mailto: ") - p.before-link Chemical and process engineering student office: - a(href="mailto: bde.gcgp.lyon1@gmail.com") bde.gcgp.lyon1@gmail.com - p.before-link Management of companies and administrations student office: - a(href="mailto: ") - p.before-link IT student office: - a(href="mailto: contact@bde-info.org") contact@bde-info.org - script(src="javascripts/index.js") diff --git a/views/layout.pug b/views/layout.pug index 1fe5414..14cabc7 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -5,3 +5,59 @@ html link(rel="stylesheet", href="/stylesheets/style.css") body block content + + div#user + if user + p=user.username + a(href="/logout") Logout + else + a(href="/login") Login + a(href="/register") Register + + div#more + a About + a Contact + + div#dark.hide + div#about.popup.card.hide + h1 About + p This software respond to the COVID-19 health crisis for the following students' offices food supply + div.images + div + img(src="images/logoBio.png") + p Bio student office + div + img(src="images/logoChimie.png") + p Chemical student office + div + img(src="images/logoGC.png") + p Civil engineering student office + div + img(src="images/logoGCGP.png") + p Chemical and process engineering student office + div + img(src="images/logoGEA.png") + p Management of companies and administrations student office + div + img(src="images/logoInfo.png") + p IT student office + p.before-link Made with ❤️ by + a(href="https://www.linkedin.com/in/florian-charlaix" target="_blank") Florian Charlaix + div#contact.popup.card.hide + h1 Contact + p.before-link Order issue: + a(href="mailto: ") test@test.fr + p.before-link Bio student office: + a(href="mailto: ") + p.before-link Chemical student office: + a(href="mailto: ") + p.before-link Civil engineering student office: + a(href="mailto: ") + p.before-link Chemical and process engineering student office: + a(href="mailto: bde.gcgp.lyon1@gmail.com") bde.gcgp.lyon1@gmail.com + p.before-link Management of companies and administrations student office: + a(href="mailto: ") + p.before-link IT student office: + a(href="mailto: contact@bde-info.org") contact@bde-info.org + + script(src="javascripts/layout.js") diff --git a/views/login.pug b/views/login.pug index 412b511..a29ed31 100644 --- a/views/login.pug +++ b/views/login.pug @@ -6,8 +6,8 @@ block content div.field label(for="username") Username: input#username(type="text" name="username" required) - dov.field - label(for="password") Username: + div.field + label(for="password") Password: input#password(type="password" name="password" required) div.field input(type="submit" value="Login") diff --git a/views/register.pug b/views/register.pug new file mode 100644 index 0000000..d7e1cba --- /dev/null +++ b/views/register.pug @@ -0,0 +1,29 @@ +extends layout + +block content + form.card(action="/register" method="POST") + h1 Register + div.field + label(for="username") Username: + input#username(type="text" name="username" required) + div.field + label(for="email") Email: + input#email(type="email" name="email" required) + div.field + label(for="firstName") First name: + input#firstName(type="text" name="firstName" required) + div.field + label(for="lastName") Last name: + input#lastName(type="text" name="lastName" required) + div.field + label(for="department") Department: + input#department(type="list" list="department-list" name="department" required=) + div.field + label(for="password") Password: + input#password(type="password" name="password" required) + div.field + input(type="submit" value="Register") + + datalist#department-list + each department in departments + option(value=department.name)