functioning create and delete functionality

This commit is contained in:
ak 2023-09-09 17:08:00 -07:00
parent d1c0ec6221
commit 2ca5dd5238
11 changed files with 374 additions and 71 deletions

2
app.js
View file

@ -28,7 +28,7 @@ app.set("view engine", "ejs");
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));

View file

@ -1,6 +1,7 @@
const Category = require("../models/category.js");
const Item = require("../models/item.js");
const asyncHandler = require("express-async-handler");
const { body, validationResult } = require("express-validator");
exports.index = asyncHandler(async (req, res, next) => {
// get array of relevant variables for displaying category and its items
@ -24,20 +25,89 @@ exports.index = asyncHandler(async (req, res, next) => {
});
});
exports.category_create_get = asyncHandler(async (req, res, next) => {
res.send("ligma");
exports.category_create_get = (req, res, next) => {
res.render("createcategory");
};
exports.category_create_post = [
// 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);
// 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,
});
exports.category_create_post = asyncHandler(async (req, res, next) => {
res.send("ligma");
// if there are validation errors
if (!errors.isEmpty()) {
// Render the creation form again with sanitized values/error messages.
res.render("createcategory", {
errors: errors.array(),
});
return;
}
// Data from form is valid.
else {
// Check if Category with same name already exists.
const categoryExists = await Category.findOne({
name: req.body.name,
}).exec();
if (categoryExists) {
// Category exists, redirect to its page.
res.redirect(`/${categoryExists.simpleName}`);
}
// else category is unique
else {
await category.save();
// saved. Redirect to new category page.
res.redirect(`/${category.simpleName}`);
}
}
}),
];
exports.category_delete_get = asyncHandler(async (req, res, next) => {
res.send("ligma");
});
exports.category_delete_post = asyncHandler(async (req, res, next) => {
res.send("ligma");
// get current category from URL
const category = await Category.findOne({
simpleName: req.params.category,
})
.lean()
.exec();
// check
if (category === null) {
const error = new Error("Category not found");
return next(error);
}
// else, continue to find all relevant items (if any)
const items = await Item.find({ category: category._id }).lean().exec();
// if items found
if (items.length > 0) {
// delete all items from db - for loop used for practicality reasons (forEach() doesn't play well with async)
for (let i = 0; i < items.length; i++) {
const itemID = items[i]._id;
await Item.findByIdAndDelete(itemID);
}
}
// delete category
await Category.findByIdAndDelete(category._id);
// redirects to Home
res.redirect("/");
});
exports.category_update_get = asyncHandler(async (req, res, next) => {

View file

@ -1,6 +1,7 @@
const Category = require("../models/category.js");
const Item = require("../models/item.js");
const asyncHandler = require("express-async-handler");
const { body, validationResult } = require("express-validator");
exports.index = asyncHandler(async (req, res, next) => {
// get current item from URL
@ -23,24 +24,123 @@ exports.index = asyncHandler(async (req, res, next) => {
});
});
exports.item_detail = asyncHandler(async (req, res, next) => {
res.send("ligma");
});
exports.item_create_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();
// render create item page
res.render("createitem", {
category: category,
});
});
exports.item_create_post = asyncHandler(async (req, res, next) => {
res.send("ligma");
exports.item_create_post = [
// 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 price (formatting has already been taken care of)
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();
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.
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,
});
// if there are validation errors
if (!errors.isEmpty()) {
// Render the creation form again with sanitized values/error messages.
res.render("createcategory", {
errors: errors.array(),
});
return;
}
// Data from form is valid.
else {
// Check if Item with same name already exists.
const itemExists = await Item.findOne({
name: req.body.name,
category: category._id,
})
.lean()
.exec();
if (itemExists) {
// Item exists, redirect to its page.
res.redirect(`/${req.params.category}/${itemExists._id}`);
}
// else Item is unique
else {
// save item and redirect to its ID
item.save().then((uploadedItem) => {
res.redirect(`/${req.params.category}/${uploadedItem._id}`);
});
}
}
}),
];
exports.item_delete_get = asyncHandler(async (req, res, next) => {
res.send("ligma");
});
exports.item_delete_post = asyncHandler(async (req, res, next) => {
res.send("ligma");
// get current item from URL
const item = await Item.findOne({
_id: req.params.item,
})
.lean()
.exec();
// check
if (item === null) {
const error = new Error("Item not found");
return next(error);
}
// delete item
await Item.findByIdAndDelete(item._id);
// redirects to parent category
res.redirect(`/${req.params.category}`);
});
exports.item_update_get = asyncHandler(async (req, res, next) => {

66
package-lock.json generated
View file

@ -15,18 +15,16 @@
"ejs": "^3.1.9",
"express": "^4.18.2",
"express-async-handler": "^1.2.0",
"express-validator": "^7.0.1",
"http-errors": "~1.6.3",
"mongoose": "^7.5.0",
"morgan": "~1.9.1"
},
"devDependencies": {
"mongoose": "^7.5.0"
}
},
"node_modules/@mongodb-js/saslprep": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz",
"integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==",
"dev": true,
"optional": true,
"dependencies": {
"sparse-bitfield": "^3.0.3"
@ -45,20 +43,17 @@
"node_modules/@types/node": {
"version": "20.5.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz",
"integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==",
"dev": true
"integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ=="
},
"node_modules/@types/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==",
"dev": true
"integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog=="
},
"node_modules/@types/whatwg-url": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz",
"integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/webidl-conversions": "*"
@ -222,7 +217,6 @@
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-5.4.0.tgz",
"integrity": "sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA==",
"dev": true,
"engines": {
"node": ">=14.20.1"
}
@ -468,6 +462,18 @@
"resolved": "https://registry.npmjs.org/express-async-handler/-/express-async-handler-1.2.0.tgz",
"integrity": "sha512-rCSVtPXRmQSW8rmik/AIb2P0op6l7r1fMW538yyvTMltCO4xQEWMmobfrIxN2V1/mVrgxB8Az3reYF6yUZw37w=="
},
"node_modules/express-validator": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.0.1.tgz",
"integrity": "sha512-oB+z9QOzQIE8FnlINqyIFA8eIckahC6qc8KtqLdLJcU3/phVyuhXH3bA4qzcrhme+1RYaCSwrq+TlZ/kAKIARA==",
"dependencies": {
"lodash": "^4.17.21",
"validator": "^13.9.0"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/express/node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
@ -719,8 +725,7 @@
"node_modules/ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
"dev": true
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
@ -751,11 +756,15 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
"integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==",
"dev": true,
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -768,7 +777,6 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
"dev": true,
"optional": true
},
"node_modules/merge-descriptors": {
@ -829,7 +837,6 @@
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.8.1.tgz",
"integrity": "sha512-wKyh4kZvm6NrCPH8AxyzXm3JBoEf4Xulo0aUWh3hCgwgYJxyQ1KLST86ZZaSWdj6/kxYUA3+YZuyADCE61CMSg==",
"dev": true,
"dependencies": {
"bson": "^5.4.0",
"mongodb-connection-string-url": "^2.6.0",
@ -870,7 +877,6 @@
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
"integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
"dev": true,
"dependencies": {
"@types/whatwg-url": "^8.2.1",
"whatwg-url": "^11.0.0"
@ -880,7 +886,6 @@
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.5.0.tgz",
"integrity": "sha512-FpOWOb0AJuaVcplmEyIJ2eCbVGe4gOoniPD+pmft5BrGrNrsFcnYXlERdXtBApGHMHPwD7WbxTyhCbUNr72F3Q==",
"dev": true,
"dependencies": {
"bson": "^5.4.0",
"kareem": "2.5.1",
@ -901,8 +906,7 @@
"node_modules/mongoose/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/morgan": {
"version": "1.9.1",
@ -923,7 +927,6 @@
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
"integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
"dev": true,
"engines": {
"node": ">=4.0.0"
}
@ -932,7 +935,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
"integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
"dev": true,
"dependencies": {
"debug": "4.x"
},
@ -944,7 +946,6 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
@ -960,8 +961,7 @@
"node_modules/mquery/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/ms": {
"version": "2.0.0",
@ -1032,7 +1032,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -1239,14 +1238,12 @@
"node_modules/sift": {
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz",
"integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==",
"dev": true
"integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ=="
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"dev": true,
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
@ -1256,7 +1253,6 @@
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
"integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
"dev": true,
"dependencies": {
"ip": "^2.0.0",
"smart-buffer": "^4.2.0"
@ -1270,7 +1266,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
"dev": true,
"optional": true,
"dependencies": {
"memory-pager": "^1.0.2"
@ -1307,7 +1302,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
"integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
"dev": true,
"dependencies": {
"punycode": "^2.1.1"
},
@ -1343,6 +1337,14 @@
"node": ">= 0.4.0"
}
},
"node_modules/validator": {
"version": "13.11.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz",
"integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -1355,7 +1357,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
"dev": true,
"engines": {
"node": ">=12"
}
@ -1364,7 +1365,6 @@
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
"integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
"dev": true,
"dependencies": {
"tr46": "^3.0.0",
"webidl-conversions": "^7.0.0"

View file

@ -14,9 +14,8 @@
"express": "^4.18.2",
"express-async-handler": "^1.2.0",
"http-errors": "~1.6.3",
"morgan": "~1.9.1"
},
"devDependencies": {
"morgan": "~1.9.1",
"express-validator": "^7.0.1",
"mongoose": "^7.5.0"
}
}

View file

@ -17,15 +17,15 @@ router.get("/:category", category_controller.index);
router.get("/:category/update", category_controller.category_update_get);
router.post("/:category/update", category_controller.category_update_post);
router.get("/:category/delete", category_controller.category_delete_get);
router.post("/:category/delete", category_controller.category_delete_post);
// item functions
router.get("/:category/:item", item_controller.index);
// create URL has to go first before :matchers
router.get("/:category/createitem", item_controller.item_create_get);
router.post("/:category/createitem", item_controller.item_create_post);
//
router.get("/:category/:item", item_controller.index);
router.get("/:category/:item/update", item_controller.item_update_get);
router.post("/:category/:item/update", item_controller.item_update_post);
router.get("/:category/:item/delete", item_controller.item_delete_get);
router.post("/:category/:item/delete", item_controller.item_delete_post);
module.exports = router;

View file

@ -19,7 +19,22 @@
</a>
<div class="pb-4"></div>
<% }) %>
<a
href="/<%= category.simpleName %>/createitem"
type="button"
class="btn btn-success px-4"
>
+ Create new item
</a>
<div class="pb-4"></div>
<a
href="/<%= category.simpleName %>/delete"
type="button"
class="btn btn-danger px-4"
>
Delete this category
</a>
<div class="pb-5"></div>
</div>
<footer
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"

46
views/createcategory.ejs Normal file
View file

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head>
<title>Good Pickin's - New Category</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">Create Category</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"
/>
</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"
/>
</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>

77
views/createitem.ejs Normal file
View file

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html>
<head>
<title>Good Pickin's - <%= category.name %> - New Item</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">Create Item</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"
/>
</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"
/>
</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"
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)"
/>
</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"
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>

View file

@ -20,12 +20,8 @@
</a>
<div class="pb-4"></div>
<% }) %>
<a
href="/createcategory"
type="button"
class="btn btn-outline-light px-4"
>
+
<a href="/createcategory" type="button" class="btn btn-success px-4">
+ Create new category
</a>
</div>
</body>

View file

@ -21,7 +21,7 @@
<a
href="/<%= category.simpleName %>/<%= item._id %>/delete"
type="button"
class="btn btn-outline-light px-4"
class="btn btn-danger px-4"
>
Delete this item
</a>