diff --git a/app.js b/app.js index 2d03586..ed41050 100644 --- a/app.js +++ b/app.js @@ -1,9 +1,9 @@ var createError = require("http-errors"); var express = require("express"); -var path = require("path"); +var path = require("path"); //The node:path module provides utilities for working with file and directory paths var cookieParser = require("cookie-parser"); var logger = require("morgan"); -var expressLayouts = require("express-ejs-layouts"); +var expressLayouts = require("express-ejs-layouts"); //Layout support for ejs in express var indexRouter = require("./routes/index"); var protectedRouter = require("./routes/protected"); var sessionAuth = require("./middlewares/sessionAuth"); @@ -12,11 +12,6 @@ var apiauth = require("./middlewares/apiauth"); var session = require("express-session"); var app = express(); -var bodyParser = require("body-parser"); - -app.use(bodyParser.json()); -// for parsing application/xwww- -app.use(bodyParser.urlencoded({ extended: true })); var config = require("config"); app.use( session({ @@ -29,20 +24,30 @@ app.use( // const { startCronJobs } = require("./croneJobs/index"); // view engine setup app.set("views", path.join(__dirname, "views")); -app.set("view engine", "ejs"); -app.use(expressLayouts); +app.set("view engine", "ejs"); //tells express that we are using ejs as rendering engine +//view engine is responsible for rendering dynamic content on the server side +app.use(expressLayouts); //add express layouts middleware app.use(logger("dev")); app.use(express.json()); -app.use(express.urlencoded({ extended: false })); +app.use(express.urlencoded({ extended: false })); //is a middleware function that is used to parse incoming requests with urlencoded payloads e.g. form submissions app.use(cookieParser()); app.use(express.static(path.join(__dirname, "public"))); +// his middleware is used to serve static files, such as images, CSS files, and JavaScript files, directly from a specified directory. + app.use("/api/public/products", require("./routes/api/public/products")); app.use("/api/categories", require("./routes/api/catagories")); app.use("/api/products", apiauth, require("./routes/api/products")); +app.use("/api/calculator", require("./routes/api/calculator")); app.use("/api/auth", require("./routes/api/auth")); -app.use("/", sessionAuth, indexRouter); -app.use("/my-account", sessionAuth, checkSessionAuth, protectedRouter); -app.use("/", sessionAuth, require("./routes/shop")); +app.use(sessionAuth); // add a middleware which should run on all server side rendering routes +app.use("/calculator", require("./routes/calculator")); //add a calculator router on all methods with /calculator prefix +app.use( + "/cookies-session-example", + require("./routes/cookies-session-example") +); //add a calculator router on all methods with /calculator prefix +app.use("/", indexRouter); +app.use("/my-account", checkSessionAuth, protectedRouter); +app.use("/", require("./routes/shop")); app.get("/admin", async (req, res) => { res.sendFile(path.join(__dirname, "admin", "build", "index.html")); }); diff --git a/middlewares/sessionAuth.js b/middlewares/sessionAuth.js index 8640487..141a0d9 100644 --- a/middlewares/sessionAuth.js +++ b/middlewares/sessionAuth.js @@ -1,5 +1,8 @@ -//sets user variable for pug files +//sets user variable for ejs files +let Category = require("../models/Category"); async function sessionAuth(req, res, next) { + let allCategories = await Category.find(); + res.locals.allCategories = allCategories; res.locals.user = req.session.user; res.locals.isAdmin = false; if (req.session.user) { diff --git a/models/CalculatorOperation.js b/models/CalculatorOperation.js new file mode 100644 index 0000000..8930804 --- /dev/null +++ b/models/CalculatorOperation.js @@ -0,0 +1,53 @@ +const mongoose = require("mongoose"); +const calculatorOperationSchema = mongoose.Schema( + { + operand1: String, + operand2: String, + operation: String, + result: String, + description: String, + }, + { timestamps: true, toJSON: { virtuals: true } } +); +// The pre-save middleware is executed before a document is saved to the MongoDB database. +//This is useful for performing tasks or modifications to the document just before it gets persisted to the database. +calculatorOperationSchema.pre("save", function (next) { + this.description = + `You performed ` + + this.operation + + " on " + + this.operand1 + + " and " + + this.operand2; + next(); +}); +// a virtual is a property that is not stored in the MongoDB document itself but is computed or derived from other properties. +//Virtuals are typically used for computed properties or for formatting data in a specific way without persisting it in the database. +calculatorOperationSchema.virtual("virtual_result").get(function () { + switch (this.operation) { + case "+": + return ( + this.operand1 + + this.operation + + this.operand2 + + "=" + + Number(Number(this.operand1) + Number(this.operand2)) + ); + case "-": + return ( + this.operand1 + + this.operation + + this.operand2 + + "=" + + Number(Number(this.operand1) - Number(this.operand2)) + ); + + default: + return 0; + } +}); +const CalculatorOperation = mongoose.model( + "CalculatorOperation", + calculatorOperationSchema +); +module.exports = CalculatorOperation; diff --git a/models/Product.js b/models/Product.js index f3f53b5..49945ad 100644 --- a/models/Product.js +++ b/models/Product.js @@ -6,6 +6,7 @@ const productSchema = mongoose.Schema({ department: String, description: String, image: String, + category: { type: mongoose.Types.ObjectId, ref: "Category" }, }); const Product = mongoose.model("Product", productSchema); module.exports = Product; diff --git a/public/js/calculator.js b/public/js/calculator.js new file mode 100644 index 0000000..26bcaa5 --- /dev/null +++ b/public/js/calculator.js @@ -0,0 +1,79 @@ +console.log("From Calculator"); + +$(function () { + //this function will be executed after page load + $("#calculatorPostFormAjax").on("submit", handleFormSubmit); + // $("#calculationResultsTbl .btn-warning").on("click", deleteBtnClicked); // default binding + $(document).on( + "click", + "#calculationResultsTbl .btn-warning", + deleteBtnClicked + ); // event delegation countered + $("#deleteAllBtn").on("click", function (e) { + e.preventDefault(); + $.ajax({ + url: "/api/calculator/delete-all", + method: "post", + success: function () { + $("#calculationResultsTbl tbody").empty(); + }, + }); + }); +}); + +function handleFormSubmit(e) { + console.log(e); + e.preventDefault(); //stop form submission + let data = { + operand1: $("#operand1").val(), + operand2: $("#operand2").val(), + operation: $("#operation").val(), + }; + + // initiate ajax call + $.ajax({ + url: "/api/calculator", + method: "post", + data, + success: function (res) { + //function to be executed after successful ajax call + console.log(res); + $("#calculationResultsTbl tbody").append(` + Del + ${res.operand1} + ${res.operation} + ${res.operand2} + ${res.result} + ${res.description} + ${res.virtual_result} + `); + // $("#calculationResultsTbl .btn-warning").on("click", deleteBtnClicked); + // above line is required as new table rows are not binded by default + // this is not a good way to do that + }, + error: function (err) { + //function to be executed after ajax call failure + console.log(err); + }, + }); + console.log(data); +} + +function deleteBtnClicked(e) { + // alert("del btn clicked"); + let id = $(this).attr("data-id"); + let tr = $(this).closest("tr"); + $.ajax({ + url: `/api/calculator/${id}`, + method: "delete", + success: function (res) { + tr.remove(); + }, + error: function (err) { + //function to be executed after ajax call failure + console.log(err); + }, + }); + + e.preventDefault(); +} diff --git a/routes/api/calculator.js b/routes/api/calculator.js new file mode 100644 index 0000000..bbe1384 --- /dev/null +++ b/routes/api/calculator.js @@ -0,0 +1,30 @@ +var express = require("express"); +var router = express.Router(); +var CalculatorOperation = require("../../models/CalculatorOperation"); +router.post("/delete-all", async (req, res) => { + let record = await CalculatorOperation.deleteMany({}); + return res.send("Records Deleted"); +}); +router.post("/", async (req, res) => { + let data = req.body; + data.result = 0; + switch (req.body.operation) { + case "+": + data.result = Number(req.body.operand1) + Number(req.body.operand2); + break; + case "-": + data.result = Number(req.body.operand1) - Number(req.body.operand2); + break; + + default: + break; + } + let calculatorOp = new CalculatorOperation(data); + await calculatorOp.save(); + return res.send(calculatorOp); +}); +router.delete("/:id", async (req, res) => { + let record = await CalculatorOperation.findByIdAndDelete(req.params.id); + return res.send("Record Deleted"); +}); +module.exports = router; diff --git a/routes/calculator.js b/routes/calculator.js new file mode 100644 index 0000000..f3590d4 --- /dev/null +++ b/routes/calculator.js @@ -0,0 +1,47 @@ +const express = require("express"); +const CalculatorOperation = require("../models/CalculatorOperation"); +let router = express.Router(); +router.get("/ajax-example", async (req, res) => { + console.log("get method /calculator"); + let results = await CalculatorOperation.find(); + // return res.send(results); + return res.render("calculator/calculator-example-ajax", { results }); +}); +router.get("/delete-all", async (req, res) => { + await CalculatorOperation.deleteMany({}); + return res.redirect("/calculator"); +}); +router.get("/delete/:id", async (req, res) => { + await CalculatorOperation.findByIdAndDelete(req.params.id); + return res.redirect("/calculator"); +}); + +router.get("/", async (req, res) => { + console.log("get method /calculator"); + let results = await CalculatorOperation.find(); + // return res.send(results); + return res.render("calculator/calculator-example", { results }); +}); +router.post("/", async (req, res) => { + // return res.send(req.body); + console.log("post /calculator"); + let data = req.body; + data.result = 0; + switch (req.body.operation) { + case "+": + data.result = Number(req.body.operand1) + Number(req.body.operand2); + break; + case "-": + data.result = Number(req.body.operand1) - Number(req.body.operand2); + break; + + default: + break; + } + let calculatorOp = new CalculatorOperation(data); + await calculatorOp.save(); + // return res.send(data); // we do not send data from server side form submissions + // return res.render("calculator/calculator-example"); //we do not render responses in form submissions either + return res.redirect("/calculator"); +}); +module.exports = router; diff --git a/routes/cookies-session-example.js b/routes/cookies-session-example.js new file mode 100644 index 0000000..b341a08 --- /dev/null +++ b/routes/cookies-session-example.js @@ -0,0 +1,21 @@ +const express = require("express"); +let router = express.Router(); +router.get("/cookies", (req, res) => { + // return res.send(req.cookies); + let city = req.cookies.city; + return res.render("cookies-session-example/cookies", { city }); +}); +router.post("/cookies", (req, res) => { + res.cookie("city", req.body.city); + res.redirect("/cookies-session-example/cookies"); +}); +router.get("/session", (req, res) => { + // return res.send(req.cookies); + let university = req.session.university; + return res.render("cookies-session-example/session", { university }); +}); +router.post("/session", (req, res) => { + req.session.university = req.body.university; + res.redirect("/cookies-session-example/session"); +}); +module.exports = router; diff --git a/routes/shop.js b/routes/shop.js index 27262c5..0104234 100644 --- a/routes/shop.js +++ b/routes/shop.js @@ -1,6 +1,7 @@ var express = require("express"); var router = express.Router(); var Product = require("../models/Product"); +var Category = require("../models/Category"); router.get("/cart", async function (req, res, next) { let cart = req.cookies.cart; if (!cart) cart = []; @@ -21,8 +22,9 @@ router.get("/add-cart/:id", function (req, res, next) { req.flash("success", "Product Added To Cart"); res.redirect("/"); }); - -router.get("/:page?", async function (req, res, next) { +router.get("/collections/:slug/:page?", async function (req, res, next) { + let category = await Category.findOne({ slug: req.params.slug }); + if (!category) return next(); let page = Number(req.params.page); let pageSize = 10; let skip = (page - 1) * pageSize; @@ -30,27 +32,38 @@ router.get("/:page?", async function (req, res, next) { if (!skip) skip = 0; // return res.send({ page, pageSize, skip }); - let products = await Product.find().skip(skip).limit(pageSize); - let totalProducts = await Product.countDocuments(); + let products = await Product.find({ category: category._id }) + .skip(skip) + .limit(pageSize); + let totalProducts = await Product.countDocuments({ category: category._id }); let totalPages = Math.ceil(totalProducts / pageSize); - return res.render("site/homepage", { + + return res.render("site/category", { pagetitle: "Awesome Products", products, page, pageSize, totalPages, + category, }); }); - -router.get("/:Catagory?", async function (req, res, next) { - let Catagory = Number(req.params.page); - let Catagoies_items = [shirts, pants, begs, trousels, dresses]; +router.get("/:page?", async function (req, res, next) { + let page = Number(req.params.page); + let pageSize = 10; + let skip = (page - 1) * pageSize; + if (!page) page = 1; + if (!skip) skip = 0; // return res.send({ page, pageSize, skip }); + let products = await Product.find().skip(skip).limit(pageSize); + let totalProducts = await Product.countDocuments(); + let totalPages = Math.ceil(totalProducts / pageSize); return res.render("site/homepage", { - pagetitle: "All Catagories", - Catagory, - Catagoies_items, + pagetitle: "Awesome Products", + products, + page, + pageSize, + totalPages, }); }); diff --git a/views/calculator/calculator-example-ajax.ejs b/views/calculator/calculator-example-ajax.ejs new file mode 100644 index 0000000..5807ec9 --- /dev/null +++ b/views/calculator/calculator-example-ajax.ejs @@ -0,0 +1,53 @@ + +

Calculator Example

+This page demonstrates how a simple form submission is done in express + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +Delete All Records + + + + + + + + + + + + + + <% results.forEach(element => { %> + + + + + + + + + + <% }) %> + +
Operand 1OperationOperand 2ResultDescriptionVirtual Result
Del<%= element.operand1 %><%= element.operation %><%= element.operand2 %><%= element.result %><%= element.description %><%= element.virtual_result %>
\ No newline at end of file diff --git a/views/calculator/calculator-example.ejs b/views/calculator/calculator-example.ejs new file mode 100644 index 0000000..9c0574b --- /dev/null +++ b/views/calculator/calculator-example.ejs @@ -0,0 +1,53 @@ + +

Calculator Example

+This page demonstrates how a simple form submission is done in express + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +Delete All Records + + + + + + + + + + + + + + <% results.forEach(element => { %> + + + + + + + + + + <% }) %> + +
Operand 1OperationOperand 2ResultDescriptionVirtual Result
Del<%= element.operand1 %><%= element.operation %><%= element.operand2 %><%= element.result %><%= element.description %><%= element.virtual_result %>
\ No newline at end of file diff --git a/views/cookies-session-example/cookies.ejs b/views/cookies-session-example/cookies.ejs new file mode 100644 index 0000000..e26e243 --- /dev/null +++ b/views/cookies-session-example/cookies.ejs @@ -0,0 +1,21 @@ +

Cookies

+

Cookies are small pieces of data that a web server sends to a user's browser and are stored on the user's device. They are typically used to store information about the user or their interactions with a website. Cookies serve various purposes, and they play a crucial role in web development.

+

In this example we will demonstrate how a cookie named city will be used

+ +
+
+ +
+
+ +
+
+<% if (city) { %> +
+ Cookie City has value <%= city %> +
+<% } else { %> +
+ Cookie City is not set +
+<% } %> \ No newline at end of file diff --git a/views/cookies-session-example/session.ejs b/views/cookies-session-example/session.ejs new file mode 100644 index 0000000..6027cec --- /dev/null +++ b/views/cookies-session-example/session.ejs @@ -0,0 +1,20 @@ +

Session

+

A "session" in web development refers to a series of interactions or requests that occur between a user and a website within a specific timeframe. Sessions are used to maintain state and information about a user across multiple requests. This is crucial for scenarios like user authentication, where a user logs in once and their session is maintained throughout their interaction with the web application.

+ +
+
+ +
+
+ +
+
+<% if (university) { %> +
+ Session university has value <%= university %> +
+<% } else { %> +
+ Session university is not set +
+<% } %> \ No newline at end of file diff --git a/views/layout.ejs b/views/layout.ejs index a08707c..9576f69 100644 --- a/views/layout.ejs +++ b/views/layout.ejs @@ -21,10 +21,12 @@ integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous" > + - <%-include("./layouts/partials/header") %> <% if(typeof(flash)!="undefined") + <%-include("./layouts/partials/header") %> + <% if(typeof(flash)!="undefined") {%>
<%= flash.message %> diff --git a/views/layouts/partials/header.ejs b/views/layouts/partials/header.ejs index 0fc1f87..05110b3 100644 --- a/views/layouts/partials/header.ejs +++ b/views/layouts/partials/header.ejs @@ -17,6 +17,32 @@ + + + +