all CRUD operations functional
This commit is contained in:
parent
2ca5dd5238
commit
1006d28b71
7 changed files with 272 additions and 35 deletions
13
app.js
13
app.js
|
|
@ -1,4 +1,3 @@
|
||||||
const createError = require("http-errors");
|
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const cookieParser = require("cookie-parser");
|
const cookieParser = require("cookie-parser");
|
||||||
|
|
@ -36,16 +35,4 @@ const router = require("./routes/index");
|
||||||
|
|
||||||
app.use("/", router);
|
app.use("/", router);
|
||||||
|
|
||||||
// catch 404 and forward to error handler
|
|
||||||
app.use(function (req, res, next) {
|
|
||||||
next(createError(404));
|
|
||||||
});
|
|
||||||
|
|
||||||
// error handler
|
|
||||||
app.use(function (err, req, res, next) {
|
|
||||||
// render the error page
|
|
||||||
res.status(err.status || 500);
|
|
||||||
res.render("error");
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
|
|
||||||
|
|
@ -111,9 +111,58 @@ exports.category_delete_get = asyncHandler(async (req, res, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.category_update_get = asyncHandler(async (req, res, next) => {
|
exports.category_update_get = asyncHandler(async (req, res, next) => {
|
||||||
res.send("ligma");
|
// get current category from URL
|
||||||
|
const category = await Category.findOne({
|
||||||
|
simpleName: req.params.category,
|
||||||
|
})
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
res.render("editcategory", { category: category });
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.category_update_post = asyncHandler(async (req, res, next) => {
|
exports.category_update_post = [
|
||||||
res.send("ligma");
|
// Validate and sanitize name
|
||||||
});
|
body("name", "Category must have a name!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Validate and sanitize description
|
||||||
|
body("description", "Category must have a description!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Process request after validation and sanitization.
|
||||||
|
asyncHandler(async (req, res, next) => {
|
||||||
|
// Extract the validation errors from a request.
|
||||||
|
const errors = validationResult(req);
|
||||||
|
|
||||||
|
// find existing category in database
|
||||||
|
const dbCat = await Category.findOne({ simpleName: req.params.category })
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
// Create a new category with escaped and trimmed data.
|
||||||
|
const category = new Category({
|
||||||
|
name: req.body.name,
|
||||||
|
simpleName: req.body.name.toLowerCase().replace(" ", ""),
|
||||||
|
description: req.body.description,
|
||||||
|
_id: dbCat._id, // copy original category ID to prevent overwrite errors
|
||||||
|
});
|
||||||
|
|
||||||
|
// if there are validation errors
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
// Render the creation form again with sanitized values/error messages.
|
||||||
|
res.render("editcategory", {
|
||||||
|
category: dbCat,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Data from form is valid.
|
||||||
|
else {
|
||||||
|
await Category.findByIdAndUpdate(dbCat._id, category, {});
|
||||||
|
res.redirect(`/${category.simpleName}`);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ exports.item_create_post = [
|
||||||
.isLength({ min: 2, max: 2 })
|
.isLength({ min: 2, max: 2 })
|
||||||
.escape(),
|
.escape(),
|
||||||
|
|
||||||
// Validate and sanitize price (formatting has already been taken care of)
|
// Validate and sanitize quantity
|
||||||
body("quantity", "Item must have a quantity!")
|
body("quantity", "Item must have a quantity!")
|
||||||
.trim()
|
.trim()
|
||||||
.isLength({ min: 1 })
|
.isLength({ min: 1 })
|
||||||
|
|
@ -75,15 +75,6 @@ exports.item_create_post = [
|
||||||
.lean()
|
.lean()
|
||||||
.exec();
|
.exec();
|
||||||
|
|
||||||
console.log([
|
|
||||||
req.body.name,
|
|
||||||
category._id,
|
|
||||||
req.body.description,
|
|
||||||
req.body.dollars,
|
|
||||||
req.body.cents,
|
|
||||||
req.body.quantity,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create a new Item with escaped and trimmed data.
|
// Create a new Item with escaped and trimmed data.
|
||||||
const item = new Item({
|
const item = new Item({
|
||||||
name: req.body.name,
|
name: req.body.name,
|
||||||
|
|
@ -96,8 +87,9 @@ exports.item_create_post = [
|
||||||
// if there are validation errors
|
// if there are validation errors
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
// Render the creation form again with sanitized values/error messages.
|
// Render the creation form again with sanitized values/error messages.
|
||||||
res.render("createcategory", {
|
res.render("createitem", {
|
||||||
errors: errors.array(),
|
errors: errors.array(),
|
||||||
|
category: category,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -144,9 +136,81 @@ exports.item_delete_get = asyncHandler(async (req, res, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.item_update_get = asyncHandler(async (req, res, next) => {
|
exports.item_update_get = asyncHandler(async (req, res, next) => {
|
||||||
res.send("ligma");
|
// get current category from URL
|
||||||
|
const category = await Category.findOne({
|
||||||
|
simpleName: req.params.category,
|
||||||
|
})
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
const item = await Item.findOne({ _id: req.params.item });
|
||||||
|
res.render("edititem", { category: category, item: item });
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.item_update_post = asyncHandler(async (req, res, next) => {
|
exports.item_update_post = [
|
||||||
res.send("ligma");
|
// Validate and sanitize name
|
||||||
});
|
body("name", "Item must have a name!").trim().isLength({ min: 1 }).escape(),
|
||||||
|
|
||||||
|
// Validate and sanitize description
|
||||||
|
body("description", "Item must have a description!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Validate and sanitize price (formatting has already been taken care of)
|
||||||
|
body("dollars", "Item must have a dollar amount!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.escape(),
|
||||||
|
body("cents", "Item must have a cent amount!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 2, max: 2 })
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Validate and sanitize quantity
|
||||||
|
body("quantity", "Item must have a quantity!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Process request after validation and sanitization.
|
||||||
|
asyncHandler(async (req, res, next) => {
|
||||||
|
// Extract the validation errors from a request.
|
||||||
|
const errors = validationResult(req);
|
||||||
|
|
||||||
|
// get current category from URL
|
||||||
|
const category = await Category.findOne({
|
||||||
|
simpleName: req.params.category,
|
||||||
|
})
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
// get item in db
|
||||||
|
const dbItem = await Item.findOne({ _id: req.params.item });
|
||||||
|
|
||||||
|
// Create a new Item with escaped and trimmed data.
|
||||||
|
const item = new Item({
|
||||||
|
name: req.body.name,
|
||||||
|
category: category._id,
|
||||||
|
description: req.body.description,
|
||||||
|
price: Number(`${req.body.dollars}.${req.body.cents}`),
|
||||||
|
quantity: req.body.quantity,
|
||||||
|
_id: dbItem._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// if there are validation errors
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
// Render the creation form again with sanitized values/error messages.
|
||||||
|
res.render("edititem", {
|
||||||
|
errors: errors.array(),
|
||||||
|
category: category,
|
||||||
|
item: dbItem,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Data from form is valid.
|
||||||
|
else {
|
||||||
|
await Item.findByIdAndUpdate(dbItem._id, item, {});
|
||||||
|
res.redirect(`/${req.params.category}/${req.params.item}`);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,14 @@
|
||||||
+ Create new item
|
+ Create new item
|
||||||
</a>
|
</a>
|
||||||
<div class="pb-4"></div>
|
<div class="pb-4"></div>
|
||||||
|
<a
|
||||||
|
href="/<%= category.simpleName %>/update"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary px-4"
|
||||||
|
>
|
||||||
|
Edit this category
|
||||||
|
</a>
|
||||||
|
<div class="pb-4"></div>
|
||||||
<a
|
<a
|
||||||
href="/<%= category.simpleName %>/delete"
|
href="/<%= category.simpleName %>/delete"
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
||||||
48
views/editcategory.ejs
Normal file
48
views/editcategory.ejs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Good Pickin's - Editing <%= category.name %></title>
|
||||||
|
<link rel="stylesheet" href="/styles/css/bootstrap.min.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="d-flex flex-column py-4 px-4 align-items-center">
|
||||||
|
<h1 class="mb-4">Editing <%= category.name %></h1>
|
||||||
|
<form class="d-flex flex-column align-items-center w-50" method="post">
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="name">Category Name:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="category name"
|
||||||
|
aria-describedby="name"
|
||||||
|
name="name"
|
||||||
|
value="<%= category.name %>"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="description">
|
||||||
|
Category Description:
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="category description"
|
||||||
|
aria-describedby="description"
|
||||||
|
name="description"
|
||||||
|
value="<%= category.description %>"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-outline-light px-4 py-2">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a href="/" type="button" class="btn btn-outline-light px-4 py-1">
|
||||||
|
Back to Home
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
84
views/edititem.ejs
Normal file
84
views/edititem.ejs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>
|
||||||
|
Good Pickin's - <%= category.name %> - Editing <%= item.name %>
|
||||||
|
</title>
|
||||||
|
<link rel="stylesheet" href="/styles/css/bootstrap.min.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="d-flex flex-column py-4 px-4 align-items-center">
|
||||||
|
<h1 class="mb-4">Editing <%= item.name %></h1>
|
||||||
|
<form class="d-flex flex-column align-items-center w-50" method="post">
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="name">Item Name:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="item name"
|
||||||
|
aria-describedby="name"
|
||||||
|
name="name"
|
||||||
|
value="<%= item.name %>"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="description">
|
||||||
|
Item Description:
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="item description"
|
||||||
|
aria-describedby="description"
|
||||||
|
name="description"
|
||||||
|
value="<%= item.description %>"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="price"> Item Price: </span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="dollars"
|
||||||
|
aria-describedby="price"
|
||||||
|
name="dollars"
|
||||||
|
value="<%= Math.trunc(item.price) %>"
|
||||||
|
oninput="this.value = this.value.replace(/[^0-9]/g, '')"
|
||||||
|
/>
|
||||||
|
<span class="input-group-text" id="price">.</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="cents"
|
||||||
|
aria-describedby="price"
|
||||||
|
name="cents"
|
||||||
|
oninput="this.value < 3 ? this.value = this.value.replace(/[^0-9]{1,2}/g, '') : this.value = this.value.substring(0, 2)"
|
||||||
|
value="<%= Number.parseFloat(item.price).toFixed(2).toString().match(/(?<=\.)(\d+)/gi) %>"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="quantity"> Item Quantity: </span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="item quantity"
|
||||||
|
aria-describedby="quantity"
|
||||||
|
name="quantity"
|
||||||
|
value="<%= item.quantity %>"
|
||||||
|
oninput="this.value = this.value.replace(/[^0-9]/g, '')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-outline-light px-4 py-2">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a href="/" type="button" class="btn btn-outline-light px-4 py-1">
|
||||||
|
Back to Home
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<h1><%= message = 'Error!' %></h1>
|
|
||||||
<h2><%= error.status %></h2>
|
|
||||||
<pre><%= error.stack %></pre>
|
|
||||||
Loading…
Add table
Reference in a new issue