524 lines
17 KiB
JavaScript
524 lines
17 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const auth = require('../auth');
|
|
const sql = require('mssql');
|
|
const { getUser } = require('../util.js');
|
|
const multer = require('multer');
|
|
const storage = multer.memoryStorage();
|
|
const uploadFile = multer({ dest: 'public/img' });
|
|
const uploadDB = multer({ storage });
|
|
|
|
router.get('/', async function(req, res, next) {
|
|
res.setHeader('Content-Type', 'text/html');
|
|
|
|
if (!req.session.user.isAdmin) {
|
|
res.send(htmlPage(`
|
|
<p>404: Not Found</p>
|
|
`, `404: Not Found`));
|
|
return;
|
|
}
|
|
|
|
let pool;
|
|
try {
|
|
let pool = await sql.connect(dbConfig);
|
|
// TO/DO: Write SQL query that prints out total order amount by day
|
|
let results = await pool.request().query(`
|
|
select cast(orderDate as date) as dateOfOrder, sum(totalAmount) as valueOfOrders
|
|
from ordersummary
|
|
group by cast(orderDate as date)
|
|
`);
|
|
|
|
const chartData = JSON.stringify({
|
|
type: 'bar',
|
|
data: {
|
|
labels: results.recordset.map(r => r.dateOfOrder),
|
|
datasets: [{
|
|
label: 'Income',
|
|
data: results.recordset.map(r => r.valueOfOrders),
|
|
borderWidth: 1
|
|
}]
|
|
},
|
|
options: {
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
res.render('layouts/main', {
|
|
loggedIn: getUser(req) != null,
|
|
user: getUser(req),
|
|
spacer: true,
|
|
content: `
|
|
<h1>Administrator Panel</h2>
|
|
|
|
<ul>
|
|
<li>
|
|
<a href="/admin/listCustomers">
|
|
Customer Directory
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="/admin/addProduct">
|
|
Add New Product
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="/admin/productDirectory">
|
|
Edit Products
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<h2> Fulfill an order </h2>
|
|
<form method="get" action="/ship">
|
|
<input type="number" name="orderId" placeholder="Order#"><br>
|
|
<input type="submit">
|
|
</form>
|
|
|
|
<h2>Sales By Day</h2>
|
|
|
|
<table>
|
|
<tbody>
|
|
<tr>
|
|
<th>Order Date</th>
|
|
<th>Income</th>
|
|
</tr>
|
|
${results.recordset.map(row =>`
|
|
<tr>
|
|
<td>${(new Date(row.dateOfOrder)).toDateString()}</td>
|
|
<td>$${Number(row.valueOfOrders).toFixed(2)}</td>
|
|
</tr>
|
|
`).join("\n")}
|
|
</tbody>
|
|
</table>
|
|
|
|
<div>
|
|
<canvas id="salesChart"></canvas>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
|
|
<script>
|
|
const chart = document.getElementById("salesChart");
|
|
const chartData = JSON.parse('${chartData}');
|
|
console.log(chartData);
|
|
new Chart(chart, chartData);
|
|
</script>
|
|
`,
|
|
});
|
|
|
|
} catch(err) {
|
|
console.dir(err);
|
|
res.write(err + "");
|
|
res.end();
|
|
} finally {
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
router.get('/listCustomers', async (req, res) => {
|
|
let pool;
|
|
try {
|
|
let pool = await sql.connect(dbConfig);
|
|
let customers = (await pool.request().query(`
|
|
select * from customer
|
|
`)).recordset;
|
|
|
|
res.render('layouts/main', {
|
|
title: 'Registered Customers',
|
|
loggedIn: getUser(req) != null,
|
|
user: getUser(req),
|
|
spacer: true,
|
|
content: `
|
|
<h1> Customer Directory </h1>
|
|
<table>
|
|
<tbody>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Username</th>
|
|
<th>Email</th>
|
|
</tr>
|
|
${customers.map(customer =>`
|
|
<tr>
|
|
<td>
|
|
${customer.firstName} ${customer.lastName}
|
|
</td>
|
|
<td>
|
|
${customer.userid}
|
|
</td>
|
|
<td>
|
|
${customer.email}
|
|
</td>
|
|
</tr>
|
|
`).join("\n")}
|
|
</tbody>
|
|
</table>
|
|
`,
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.send("Unable to retrieve customer list");
|
|
} finally {
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
router.get('/productDirectory', async (req, res) => {
|
|
let pool;
|
|
try {
|
|
pool = await sql.connect(dbConfig);
|
|
const products = (await pool.request().query(`
|
|
select * from product
|
|
`)).recordset;
|
|
|
|
res.render('layouts/main', {
|
|
title: 'Admin Product Directory',
|
|
spacer: true,
|
|
loggedIn: true,
|
|
user: getUser(req),
|
|
content: `
|
|
<h1> Admin Product Directory </h1>
|
|
<table>
|
|
<tbody>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Name</th>
|
|
</tr>
|
|
${products.map(p => `
|
|
<tr>
|
|
<td>${p.productId}</td>
|
|
<td>${p.productName}</td>
|
|
<td>
|
|
<a href="/admin/productUpdate?id=${p.productId}">
|
|
Update
|
|
</a>
|
|
</td>
|
|
<td>
|
|
<a href="/admin/productAddImage?id=${p.productId}">
|
|
Add Image
|
|
</a>
|
|
</td>
|
|
<td>
|
|
<a href="/admin/productDelete?id=${p.productId}">
|
|
X
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
`).join('\n')}
|
|
</tbody>
|
|
</table>
|
|
`,
|
|
});
|
|
} catch (err) {
|
|
res.send("Could not retrieve categories: " + err.toString());
|
|
console.error(err);
|
|
} finally {
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
router.get('/productDelete', async (req, res) => {
|
|
let pool;
|
|
let stmt;
|
|
try {
|
|
pool = await sql.connect(dbConfig);
|
|
stmt = new sql.PreparedStatement(pool);
|
|
stmt.input('productId', sql.VarChar);
|
|
await stmt.prepare(`
|
|
delete from product where productId = @productId
|
|
`);
|
|
|
|
await stmt.execute({productId: req.query.id});
|
|
res.redirect('/admin/productDirectory');
|
|
|
|
} catch (err) {
|
|
res.send("Could not retrieve categories: " + err.toString());
|
|
console.error(err);
|
|
} finally {
|
|
if (stmt) stmt.unprepare();
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
router.get('/productUpdate', async (req, res) => {
|
|
let pool;
|
|
let stmt;
|
|
try {
|
|
pool = await sql.connect(dbConfig);
|
|
stmt = new sql.PreparedStatement(pool);
|
|
stmt.input('productId', sql.VarChar);
|
|
|
|
await stmt.prepare(`
|
|
select * from product where productId = @productId
|
|
`);
|
|
|
|
const product = (await stmt.execute({productId: req.query.id})).recordset[0];
|
|
|
|
const categories = (await pool.request().query(`
|
|
select * from category
|
|
`)).recordset;
|
|
|
|
res.render('layouts/main', {
|
|
title: 'Add a New Product',
|
|
loggedIn: true,
|
|
user: getUser(req),
|
|
spacer: true,
|
|
content: `
|
|
<h1>Update Product</h1>
|
|
<form method="post" action="/admin/productUpdate?id=${product.productId}">
|
|
<label for="productName">Name: </label>
|
|
<input type="text" name="productName" value="${product.productName}" required><br>
|
|
|
|
<label for="productPrice">Price: </label>
|
|
<input type="number" name="productPrice" value="${product.productPrice}" required><br>
|
|
|
|
<label for="categoryId">Category: </label>
|
|
<select name="categoryId">
|
|
${categories.map(cat =>`
|
|
<option value="${cat.categoryId}" ${cat.categoryId == product.categoryId ? 'selected="selected"' : ''}>${cat.categoryName}</option>
|
|
`).join("\n")}
|
|
</select><br>
|
|
|
|
<label for="productDesc">Description: </label><br>
|
|
<textarea name="productDesc">${product.productDesc}</textarea><br>
|
|
|
|
<input type="submit" value="Submit">
|
|
</form>
|
|
`,
|
|
});
|
|
} catch (err) {
|
|
res.send("Could not retrieve data: " + err.toString());
|
|
console.error(err);
|
|
} finally {
|
|
if (stmt) stmt.unprepare();
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
router.post('/productUpdate', async (req, res) => {
|
|
let pool;
|
|
let stmt;
|
|
try {
|
|
pool = await sql.connect(dbConfig);
|
|
|
|
stmt = new sql.PreparedStatement(pool);
|
|
stmt.input('productName', sql.VarChar);
|
|
stmt.input('productPrice', sql.Decimal(10, 2));
|
|
stmt.input('productDesc', sql.VarChar);
|
|
stmt.input('categoryId', sql.Int);
|
|
stmt.input('productId', sql.Int);
|
|
|
|
await stmt.prepare(`
|
|
update product set
|
|
productName = @productName,
|
|
productPrice = @productPrice,
|
|
productDesc = @productDesc,
|
|
categoryId = @categoryId
|
|
where productId = @productId
|
|
`);
|
|
|
|
console.log(req.query.id);
|
|
await stmt.execute({...req.body, productId: req.query.id});
|
|
|
|
res.redirect(`/admin/productDirectory`);
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.send("Could not add product: " + err.toString());
|
|
} finally {
|
|
if (stmt) stmt.unprepare();
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
router.get('/productAddImage', async (req, res) => {
|
|
res.render('layouts/main', {
|
|
title: 'Add a New Product',
|
|
loggedIn: true,
|
|
user: getUser(req),
|
|
spacer: true,
|
|
content: `
|
|
<h1> Add an image to this product</h1>
|
|
<form onsubmit="onSubmit(event)" id="imageForm">
|
|
<label for="productImage">Image: </label>
|
|
<input type="file" name="productImage" id="fileInput"><br>
|
|
|
|
<p>
|
|
Save this file in:
|
|
</p>
|
|
|
|
<input type="radio" id="storeFile" name="storageLocation" value="file" checked>
|
|
<label for="storeFile">The filesystem</label><br>
|
|
|
|
<input type="radio" id="storeDB" name="storageLocation" value="db">
|
|
<label for="storeDB">The DB</label><br>
|
|
|
|
<input type="submit" value="Submit">
|
|
</form>
|
|
|
|
<script>
|
|
function onSubmit(e) {
|
|
e.preventDefault();
|
|
const storageLocation = document.querySelector('input[name="storageLocation"]:checked').value;
|
|
|
|
const input = document.getElementById('fileInput');
|
|
let dest = 'productAddDBImage';
|
|
if (storageLocation == "file") {
|
|
dest = 'productAddFileImage';
|
|
}
|
|
|
|
const data = new FormData(document.getElementById("imageForm"));
|
|
|
|
fetch('/admin/' + dest + '?id=' + ${req.query.id}, {
|
|
method: 'POST',
|
|
body: data
|
|
}).then(res => {
|
|
window.location = "/admin/productDirectory";
|
|
});
|
|
|
|
return false;
|
|
}
|
|
</script>
|
|
`,
|
|
});
|
|
});
|
|
|
|
router.post('/productAddFileImage', uploadFile.single('productImage'), async (req, res) => {
|
|
let pool;
|
|
let stmt;
|
|
try {
|
|
pool = await sql.connect(dbConfig);
|
|
stmt = new sql.PreparedStatement(pool);
|
|
stmt.input('productId', sql.Int);
|
|
stmt.input('productImageURL', sql.VarChar);
|
|
|
|
await stmt.prepare(`
|
|
update product set
|
|
productImageURL = @productImageURL
|
|
where productId = @productId
|
|
`);
|
|
|
|
await stmt.execute({productId: req.query.id, productImageURL: `img/${req.file.filename}`});
|
|
res.redirect('/admin/productDirectory');
|
|
} catch (err) {
|
|
res.send("Could not update product: " + err.toString());
|
|
console.error(err);
|
|
} finally {
|
|
if (stmt) stmt.unprepare();
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
router.post('/productAddDBImage', uploadDB.single('productImage'), async (req, res) => {
|
|
let pool;
|
|
let stmt;
|
|
try {
|
|
pool = await sql.connect(dbConfig);
|
|
stmt = new sql.PreparedStatement(pool);
|
|
stmt.input('productId', sql.Int);
|
|
stmt.input('productImage', sql.VarBinary);
|
|
|
|
await stmt.prepare(`
|
|
update product set
|
|
productImage = @productImage
|
|
where productId = @productId
|
|
`);
|
|
|
|
await stmt.execute({productId: req.query.id, productImage: req.file.buffer});
|
|
res.redirect('/admin/productDirectory');
|
|
} catch (err) {
|
|
res.send("Could not update product: " + err.toString());
|
|
console.error(err);
|
|
} finally {
|
|
if (stmt) stmt.unprepare();
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
router.get('/addProduct', async (req, res) => {
|
|
let pool;
|
|
try {
|
|
pool = await sql.connect(dbConfig);
|
|
const categories = (await pool.request().query(`
|
|
select * from category
|
|
`)).recordset;
|
|
|
|
res.render('layouts/main', {
|
|
title: 'Add a New Product',
|
|
loggedIn: true,
|
|
user: getUser(req),
|
|
spacer: true,
|
|
content: `
|
|
<h1>Add a New Product</h1>
|
|
<form method="post" action="/admin/addProduct" enctype="multipart/form-data">
|
|
<label for="productName">Name: </label>
|
|
<input type="text" name="productName" required><br>
|
|
|
|
<label for="productPrice">Price: </label>
|
|
<input type="number" name="productPrice" required><br>
|
|
|
|
<label for="categoryId">Category: </label>
|
|
<select name="categoryId">
|
|
${categories.map(cat =>`
|
|
<option value="${cat.categoryId}">${cat.categoryName}</option>
|
|
`).join("\n")}
|
|
</select><br>
|
|
|
|
<label for="productImage">Image: </label>
|
|
<input type="file" name="productImage"><br>
|
|
|
|
<label for="productDesc">Description: </label><br>
|
|
<textarea name="productDesc"></textarea><br>
|
|
|
|
<input type="submit" value="Submit">
|
|
</form>
|
|
`,
|
|
});
|
|
} catch (err) {
|
|
res.send("Could not retrieve categories: " + err.toString());
|
|
console.error(err);
|
|
} finally {
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
router.post('/addProduct', uploadDB.single('productImage'), async (req, res) => {
|
|
let pool;
|
|
let stmt;
|
|
try {
|
|
pool = await sql.connect(dbConfig);
|
|
|
|
stmt = new sql.PreparedStatement(pool);
|
|
stmt.input('productName', sql.VarChar);
|
|
stmt.input('productPrice', sql.Decimal(10, 2));
|
|
stmt.input('productDesc', sql.VarChar);
|
|
stmt.input('categoryId', sql.Int);
|
|
|
|
stmt.input('image', sql.VarBinary);
|
|
await stmt.prepare(`
|
|
insert into product(productName, productPrice, productDesc, productImage, categoryId)
|
|
output inserted.productId
|
|
values (@productName, @productPrice, @productDesc, @image, @categoryId)
|
|
`);
|
|
|
|
const recordToInsert = {
|
|
...req.body,
|
|
image: req.body.storageLocation == "file" ? `img/${req.file.filename}` : req.file.buffer,
|
|
};
|
|
|
|
const stmtResults = await stmt.execute(recordToInsert);
|
|
|
|
res.redirect(`/product/?id=${stmtResults.recordset[0].productId}`);
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.send("Could not add product: " + err.toString());
|
|
} finally {
|
|
if (stmt) stmt.unprepare();
|
|
if (pool) pool.close();
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|