init
|
@ -0,0 +1,9 @@
|
||||||
|
FROM node:14.10.1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
CMD ["npm", "run", "start"]
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Nat & Samira's Rare Earth Metals
|
||||||
|
|
||||||
|
An ecommerce website that may or may not sell rare earth metals
|
||||||
|
|
||||||
|
This project was completed late 2023 for COSC304 as a study in web development and database development it uses:
|
||||||
|
* Express.js on the backend
|
||||||
|
* Vanilla JS and web technologies on the front end
|
||||||
|
* Docker for deployment
|
||||||
|
|
||||||
|
[See our final report][1] for more technical information and a full walkthrough with screenshots.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
With docker installed, `cd` into the directory and run:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
[1]: ./Final-Report.pdf
|
|
@ -0,0 +1,22 @@
|
||||||
|
const sql = require('mssql');
|
||||||
|
|
||||||
|
const auth = {
|
||||||
|
checkAuthentication: function(req, res) {
|
||||||
|
let authenticated = false;
|
||||||
|
|
||||||
|
if (req.session.authenticatedUser) {
|
||||||
|
authenticated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authenticated) {
|
||||||
|
let url = req.protocol + '://' + req.get('host') + req.originalUrl;
|
||||||
|
let loginMessage = "You have not been authorized to access the URL " + url;
|
||||||
|
req.session.loginMessage = loginMessage;
|
||||||
|
res.redirect("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = auth;
|
|
@ -0,0 +1,94 @@
|
||||||
|
CREATE DATABASE IF NOT EXISTS workson;
|
||||||
|
|
||||||
|
USE workson;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
DROP TABLE IF EXISTS workson;
|
||||||
|
DROP TABLE IF EXISTS proj;
|
||||||
|
DROP TABLE IF EXISTS emp;
|
||||||
|
DROP TABLE IF EXISTS dept;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE emp (
|
||||||
|
eno CHAR(5) NOT NULL,
|
||||||
|
ename VARCHAR(30),
|
||||||
|
bdate DATE,
|
||||||
|
title CHAR(2),
|
||||||
|
salary DECIMAL(9,2),
|
||||||
|
supereno CHAR(5),
|
||||||
|
dno CHAR(5),
|
||||||
|
PRIMARY KEY (eno)
|
||||||
|
) ENGINE = InnoDB;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE dept (
|
||||||
|
dno CHAR(5) NOT NULL,
|
||||||
|
dname VARCHAR(40),
|
||||||
|
mgreno CHAR(5),
|
||||||
|
PRIMARY KEY (dno),
|
||||||
|
CONSTRAINT FK_dept_emp FOREIGN KEY (mgreno) REFERENCES emp (eno) ON DELETE SET NULL) ENGINE = InnoDB;
|
||||||
|
|
||||||
|
ALTER TABLE emp ADD CONSTRAINT FK_emp_dept FOREIGN KEY (dno) REFERENCES dept(dno);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE proj (
|
||||||
|
pno CHAR(5) NOT NULL,
|
||||||
|
pname VARCHAR(40),
|
||||||
|
budget DECIMAL(9,2),
|
||||||
|
dno CHAR(5),
|
||||||
|
PRIMARY KEY (pno),
|
||||||
|
CONSTRAINT FK_proj_dept FOREIGN KEY (dno) REFERENCES dept(dno) ON DELETE SET NULL) ENGINE = InnoDB;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE workson (
|
||||||
|
eno CHAR(5) NOT NULL,
|
||||||
|
pno CHAR(5) NOT NULL,
|
||||||
|
resp VARCHAR(20),
|
||||||
|
hours SMALLINT,
|
||||||
|
CONSTRAINT PK_workson PRIMARY KEY (eno,pno),
|
||||||
|
CONSTRAINT FK_workson_emp FOREIGN KEY (eno) REFERENCES emp (eno),
|
||||||
|
CONSTRAINT FK_workson_proj FOREIGN KEY (pno) REFERENCES proj (pno)) ENGINE = InnoDB;
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO dept VALUES ('D1','Management',NULL);
|
||||||
|
INSERT INTO dept VALUES ('D2','Consulting',NULL);
|
||||||
|
INSERT INTO dept VALUES ('D3','Accounting',NULL);
|
||||||
|
INSERT INTO dept VALUES ('D4','Development',NULL);
|
||||||
|
|
||||||
|
INSERT INTO proj VALUES ('P1','Instruments',150000,'D1');
|
||||||
|
INSERT INTO proj VALUES ('P2','DB Develop',135000,'D2');
|
||||||
|
INSERT INTO proj VALUES ('P3','Budget',250000,'D3');
|
||||||
|
INSERT INTO proj VALUES ('P4','Maintenance',310000,'D2');
|
||||||
|
INSERT INTO proj VALUES ('P5','CAD/CAM',500000,'D2');
|
||||||
|
|
||||||
|
INSERT INTO emp VALUES ('E1','J. Doe','1975-01-05','EE',30000,null,null);
|
||||||
|
INSERT INTO emp VALUES ('E2','M. Smith','1966-06-04','SA',50000,null,'D3');
|
||||||
|
INSERT INTO emp VALUES ('E3','A. Lee','1966-07-05','ME',40000,null,'D2');
|
||||||
|
INSERT INTO emp VALUES ('E4','J. Miller','1950-09-01','PR',20000,null,'D3');
|
||||||
|
INSERT INTO emp VALUES ('E5','B. Casey','1971-12-25','SA',50000,null,'D3');
|
||||||
|
INSERT INTO emp VALUES ('E6','L. Chu','1965-11-30','EE',30000,null,'D2');
|
||||||
|
INSERT INTO emp VALUES ('E7','R. Davis','1977-09-08','ME',40000,null,'D1');
|
||||||
|
INSERT INTO emp VALUES ('E8','J. Jones','1972-10-11','SA',50000,null,'D1');
|
||||||
|
|
||||||
|
UPDATE emp SET supereno = 'E2' WHERE eno = 'E1';
|
||||||
|
UPDATE emp SET supereno = 'E5' WHERE eno = 'E2';
|
||||||
|
UPDATE emp SET supereno = 'E7' WHERE eno = 'E3';
|
||||||
|
UPDATE emp SET supereno = 'E6' WHERE eno = 'E4';
|
||||||
|
UPDATE emp SET supereno = 'E8' WHERE eno = 'E5';
|
||||||
|
UPDATE emp SET supereno = 'E7' WHERE eno = 'E6';
|
||||||
|
UPDATE emp SET supereno = 'E8' WHERE eno = 'E7';
|
||||||
|
|
||||||
|
Update dept SET mgreno = 'E8' WHERE dno = 'D1';
|
||||||
|
Update dept SET mgreno = 'E7' WHERE dno = 'D2';
|
||||||
|
Update dept SET mgreno = 'E5' WHERE dno = 'D3';
|
||||||
|
|
||||||
|
INSERT INTO workson VALUES ('E1','P1','Manager',12);
|
||||||
|
INSERT INTO workson VALUES ('E2','P1','Analyst',24);
|
||||||
|
INSERT INTO workson VALUES ('E2','P2','Analyst',6);
|
||||||
|
INSERT INTO workson VALUES ('E3','P3','Consultant',10);
|
||||||
|
INSERT INTO workson VALUES ('E3','P4','Engineer',48);
|
||||||
|
INSERT INTO workson VALUES ('E4','P2','Programmer',18);
|
||||||
|
INSERT INTO workson VALUES ('E5','P2','Manager',24);
|
||||||
|
INSERT INTO workson VALUES ('E6','P4','Manager',48);
|
||||||
|
INSERT INTO workson VALUES ('E7','P3','Engineer',36);
|
|
@ -0,0 +1,97 @@
|
||||||
|
CREATE DATABASE workson;
|
||||||
|
go
|
||||||
|
|
||||||
|
USE workson;
|
||||||
|
go
|
||||||
|
|
||||||
|
-- Drop all tables ignoring foreign key constraints
|
||||||
|
exec sp_MSforeachtable "declare @name nvarchar(max); set @name = parsename('?', 1); exec sp_MSdropconstraints @name";
|
||||||
|
exec sp_MSforeachtable "drop table ?";
|
||||||
|
|
||||||
|
-- DROP TABLE IF EXISTS Emp;
|
||||||
|
-- DROP TABLE IF EXISTS Dept;
|
||||||
|
-- DROP TABLE IF EXISTS Proj;
|
||||||
|
-- DROP TABLE IF EXISTS WorksOn;
|
||||||
|
|
||||||
|
CREATE TABLE Emp (
|
||||||
|
eno CHAR(5) NOT NULL,
|
||||||
|
ename VARCHAR(30),
|
||||||
|
bdate DATETIME,
|
||||||
|
title CHAR(2),
|
||||||
|
salary DECIMAL(9,2),
|
||||||
|
supereno CHAR(5),
|
||||||
|
dno CHAR(5),
|
||||||
|
PRIMARY KEY (eno),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE Dept (
|
||||||
|
dno CHAR(5) NOT NULL,
|
||||||
|
dname VARCHAR(40),
|
||||||
|
mgreno CHAR(5),
|
||||||
|
PRIMARY KEY (dno),
|
||||||
|
CONSTRAINT FK_Dept_Emp FOREIGN KEY (mgreno) REFERENCES Emp (eno));
|
||||||
|
|
||||||
|
ALTER TABLE Emp ADD CONSTRAINT FK_Emp_Dept FOREIGN KEY (dno) REFERENCES Dept(dno);
|
||||||
|
|
||||||
|
CREATE TABLE Proj (
|
||||||
|
pno CHAR(5) NOT NULL,
|
||||||
|
pname VARCHAR(40),
|
||||||
|
budget DECIMAL(9,2),
|
||||||
|
dno CHAR(5),
|
||||||
|
PRIMARY KEY (pno),
|
||||||
|
CONSTRAINT FK_Proj_Dept FOREIGN KEY (dno) REFERENCES Dept(dno));
|
||||||
|
|
||||||
|
CREATE TABLE WorksOn (
|
||||||
|
eno CHAR(5) NOT NULL,
|
||||||
|
pno CHAR(5) NOT NULL,
|
||||||
|
resp VARCHAR(20),
|
||||||
|
hours SMALLINT,
|
||||||
|
CONSTRAINT PK_WorksOn PRIMARY KEY (eno,pno),
|
||||||
|
CONSTRAINT FK_WorksOn_Emp FOREIGN KEY (eno) REFERENCES Emp (eno),
|
||||||
|
CONSTRAINT FK_WorksOn_Proj FOREIGN KEY (pno) REFERENCES Proj (pno));
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO Dept VALUES ('D1','Management',NULL);
|
||||||
|
INSERT INTO Dept VALUES ('D2','Consulting',NULL);
|
||||||
|
INSERT INTO Dept VALUES ('D3','Accounting',NULL);
|
||||||
|
INSERT INTO Dept VALUES ('D4','Development',NULL);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO Proj VALUES ('P1','Instruments',150000,'D1');
|
||||||
|
INSERT INTO Proj VALUES ('P2','DB Develop',135000,'D2');
|
||||||
|
INSERT INTO Proj VALUES ('P3','Budget',250000,'D3');
|
||||||
|
INSERT INTO Proj VALUES ('P4','Maintenance',310000,'D2');
|
||||||
|
INSERT INTO Proj VALUES ('P5','CAD/CAM',500000,'D2');
|
||||||
|
|
||||||
|
INSERT INTO Emp VALUES ('E1','J. Doe','1975-01-05','EE',30000,null,null);
|
||||||
|
INSERT INTO Emp VALUES ('E2','M. Smith','1966-06-04','SA',50000,null,'D3');
|
||||||
|
INSERT INTO Emp VALUES ('E3','A. Lee','1966-07-05','ME',40000,null,'D2');
|
||||||
|
INSERT INTO Emp VALUES ('E4','J. Miller','1950-09-01','PR',20000,null,'D3');
|
||||||
|
INSERT INTO Emp VALUES ('E5','B. Casey','1971-12-25','SA',50000,null,'D3');
|
||||||
|
INSERT INTO Emp VALUES ('E6','L. Chu','1965-11-30','EE',30000,null,'D2');
|
||||||
|
INSERT INTO Emp VALUES ('E7','R. Davis','1977-09-08','ME',40000,null,'D1');
|
||||||
|
INSERT INTO Emp VALUES ('E8','J. Jones','1972-10-11','SA',50000,null,'D1');
|
||||||
|
|
||||||
|
UPDATE Emp SET supereno = 'E2' WHERE eno = 'E1';
|
||||||
|
UPDATE Emp SET supereno = 'E5' WHERE eno = 'E2';
|
||||||
|
UPDATE Emp SET supereno = 'E7' WHERE eno = 'E3';
|
||||||
|
UPDATE Emp SET supereno = 'E6' WHERE eno = 'E4';
|
||||||
|
UPDATE Emp SET supereno = 'E8' WHERE eno = 'E5';
|
||||||
|
UPDATE Emp SET supereno = 'E7' WHERE eno = 'E6';
|
||||||
|
UPDATE Emp SET supereno = 'E8' WHERE eno = 'E7';
|
||||||
|
|
||||||
|
Update Dept SET mgreno = 'E8' WHERE dno = 'D1';
|
||||||
|
Update Dept SET mgreno = 'E7' WHERE dno = 'D2';
|
||||||
|
Update Dept SET mgreno = 'E5' WHERE dno = 'D3';
|
||||||
|
|
||||||
|
INSERT INTO WorksOn VALUES ('E1','P1','Manager',12);
|
||||||
|
INSERT INTO WorksOn VALUES ('E2','P1','Analyst',24);
|
||||||
|
INSERT INTO WorksOn VALUES ('E2','P2','Analyst',6);
|
||||||
|
INSERT INTO WorksOn VALUES ('E3','P3','Consultant',10);
|
||||||
|
INSERT INTO WorksOn VALUES ('E3','P4','Engineer',48);
|
||||||
|
INSERT INTO WorksOn VALUES ('E4','P2','Programmer',18);
|
||||||
|
INSERT INTO WorksOn VALUES ('E5','P2','Manager',24);
|
||||||
|
INSERT INTO WorksOn VALUES ('E6','P4','Manager',48);
|
||||||
|
INSERT INTO WorksOn VALUES ('E7','P3','Engineer',36);
|
|
@ -0,0 +1,141 @@
|
||||||
|
CREATE DATABASE IF NOT EXISTS shipment;
|
||||||
|
|
||||||
|
USE shipment;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
DROP TABLE IF EXISTS customer;
|
||||||
|
DROP TABLE IF EXISTS product;
|
||||||
|
DROP TABLE IF EXISTS shipment;
|
||||||
|
DROP TABLE IF EXISTS shippedproduct;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
|
||||||
|
CREATE TABLE customer (
|
||||||
|
cid integer,
|
||||||
|
cname varchar(30),
|
||||||
|
address varchar(50),
|
||||||
|
city varchar(30),
|
||||||
|
state varchar(2),
|
||||||
|
primary key (cid)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE product (
|
||||||
|
pid integer,
|
||||||
|
pname varchar(30),
|
||||||
|
price decimal(9,2),
|
||||||
|
inventory integer,
|
||||||
|
primary key (pid)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE shipment (
|
||||||
|
sid integer,
|
||||||
|
cid integer,
|
||||||
|
shipdate datetime,
|
||||||
|
primary key (sid),
|
||||||
|
foreign key (cid) REFERENCES customer(cid)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE shippedproduct (
|
||||||
|
sid integer,
|
||||||
|
pid integer,
|
||||||
|
amount integer,
|
||||||
|
primary key (sid, pid),
|
||||||
|
foreign key (sid) REFERENCES shipment(sid),
|
||||||
|
foreign key (pid) REFERENCES product(pid)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO customer VALUES (1,'Fred Smith','101 Evergreen Terrace','Springfield','IL');
|
||||||
|
INSERT INTO customer VALUES (2,'Joe Smithsonian','245 Straight Street','Iowa City','IA');
|
||||||
|
INSERT INTO customer VALUES (3,'Steve Stevenson','24 Michigan Ave.','Chicago','IL');
|
||||||
|
INSERT INTO customer VALUES (4,'Russell Johnson','1 Hollywood Drive','Hollywood','CA');
|
||||||
|
INSERT INTO customer VALUES (5,'John Doe','34 Dead End Lane','Detroit','MI');
|
||||||
|
INSERT INTO customer VALUES (6,'Scott Charles','748 Mayflower','Huntington Beach','CA');
|
||||||
|
INSERT INTO customer VALUES (7,'Robert Dean','234 Greenwood Drive','Morristown','NJ');
|
||||||
|
INSERT INTO customer VALUES (8,'Shannon Rose',null,'Wyandotte','MI');
|
||||||
|
INSERT INTO customer VALUES (9,'Beth Rosebud','1 First Street','Muscatine','IA');
|
||||||
|
INSERT INTO customer VALUES (10,'Suzanne May','2 Second Street','Iowa City','IA');
|
||||||
|
INSERT INTO customer VALUES (11,'Aiden Adams','324 2A Street','Kelowna','BC');
|
||||||
|
INSERT INTO customer VALUES (12,'Betty Bains',null,'Kelowna','BC');
|
||||||
|
INSERT INTO customer VALUES (13,'Cindy Champion','1 1st Street','Calgary','AB');
|
||||||
|
INSERT INTO customer VALUES (14,'David Denter','23456 Main Street','Vernon','BC');
|
||||||
|
INSERT INTO customer VALUES (15,'Elish Elias','3445 Hwy 97 North','Lake Country','BC');
|
||||||
|
|
||||||
|
INSERT INTO product VALUES (1,'Swiss Chocolate',32.99,40);
|
||||||
|
INSERT INTO product VALUES (2,'Wooden Chair',99.99,12);
|
||||||
|
INSERT INTO product VALUES (3,'Teddy Bear',12.99,12);
|
||||||
|
INSERT INTO product VALUES (4,'Chocolate Bar',5.95,40);
|
||||||
|
INSERT INTO product VALUES (5,'Desk',250.99,100);
|
||||||
|
INSERT INTO product VALUES (6,'Table',500,44);
|
||||||
|
INSERT INTO product VALUES (7,'Deluxe Sweet Collection',32.65,83);
|
||||||
|
INSERT INTO product VALUES (8,'Table',2.99,156);
|
||||||
|
INSERT INTO product VALUES (9,'Sports Car',123500,0);
|
||||||
|
INSERT INTO product VALUES (10,'Textbook',250,23);
|
||||||
|
|
||||||
|
INSERT INTO shipment VALUES (1,15,'2021-07-05 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (2,15,'2021-07-12 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (3,2,'2021-09-05 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (4,3,'2022-07-13 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (5,4,'2022-07-17 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (6,4,'2022-08-02 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (7,4,'2021-09-04 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (8,4,'2021-08-19 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (9,2,'2021-07-07 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (10,2,'2021-08-09 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (11,6,'2022-09-04 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (12,7,'2022-07-05 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (13,6,'2022-08-24 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (14,8,'2021-09-22 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (15,9,'2022-08-24 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (16,10,'2022-08-26 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (17,11,'2022-09-24 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (18,12,'2021-08-28 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (19,13,'2022-08-24 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (20,13,'2022-09-12 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (21,15,'2022-08-24 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (22,12,'2021-08-13 00:00:00');
|
||||||
|
INSERT INTO shipment VALUES (23,12,'2022-08-24 00:00:00');
|
||||||
|
|
||||||
|
INSERT INTO shippedproduct VALUES (1,10,1);
|
||||||
|
INSERT INTO shippedproduct VALUES (1,9,2);
|
||||||
|
INSERT INTO shippedproduct VALUES (1,7,5);
|
||||||
|
INSERT INTO shippedproduct VALUES (1,4,6);
|
||||||
|
INSERT INTO shippedproduct VALUES (2,1,3);
|
||||||
|
INSERT INTO shippedproduct VALUES (2,3,8);
|
||||||
|
INSERT INTO shippedproduct VALUES (2,10,1);
|
||||||
|
INSERT INTO shippedproduct VALUES (3,1,20);
|
||||||
|
INSERT INTO shippedproduct VALUES (4,1,5);
|
||||||
|
INSERT INTO shippedproduct VALUES (5,2,13);
|
||||||
|
INSERT INTO shippedproduct VALUES (6,1,4);
|
||||||
|
INSERT INTO shippedproduct VALUES (6,2,1);
|
||||||
|
INSERT INTO shippedproduct VALUES (7,1,3);
|
||||||
|
INSERT INTO shippedproduct VALUES (8,4,25);
|
||||||
|
INSERT INTO shippedproduct VALUES (9,2,32);
|
||||||
|
INSERT INTO shippedproduct VALUES (10,2,2);
|
||||||
|
INSERT INTO shippedproduct VALUES (11,2,5);
|
||||||
|
INSERT INTO shippedproduct VALUES (12,3,1);
|
||||||
|
INSERT INTO shippedproduct VALUES (12,4,10);
|
||||||
|
INSERT INTO shippedproduct VALUES (13,1,5);
|
||||||
|
INSERT INTO shippedproduct VALUES (13,4,2);
|
||||||
|
INSERT INTO shippedproduct VALUES (13,2,7);
|
||||||
|
INSERT INTO shippedproduct VALUES (14,10,1);
|
||||||
|
INSERT INTO shippedproduct VALUES (14,3,2);
|
||||||
|
INSERT INTO shippedproduct VALUES (15,6,5);
|
||||||
|
INSERT INTO shippedproduct VALUES (15,8,25);
|
||||||
|
INSERT INTO shippedproduct VALUES (16,1,4);
|
||||||
|
INSERT INTO shippedproduct VALUES (16,10,2);
|
||||||
|
INSERT INTO shippedproduct VALUES (17,3,4);
|
||||||
|
INSERT INTO shippedproduct VALUES (17,4,5);
|
||||||
|
INSERT INTO shippedproduct VALUES (17,5,6);
|
||||||
|
INSERT INTO shippedproduct VALUES (18,7,5);
|
||||||
|
INSERT INTO shippedproduct VALUES (19,8,12);
|
||||||
|
INSERT INTO shippedproduct VALUES (20,9,1);
|
||||||
|
INSERT INTO shippedproduct VALUES (20,10,3);
|
||||||
|
INSERT INTO shippedproduct VALUES (20,2,5);
|
||||||
|
INSERT INTO shippedproduct VALUES (21,1,3);
|
||||||
|
INSERT INTO shippedproduct VALUES (21,2,7);
|
||||||
|
INSERT INTO shippedproduct VALUES (22,4,11);
|
||||||
|
INSERT INTO shippedproduct VALUES (22,6,13);
|
||||||
|
INSERT INTO shippedproduct VALUES (23,7,1);
|
||||||
|
INSERT INTO shippedproduct VALUES (23,8,1);
|
||||||
|
INSERT INTO shippedproduct VALUES (23,9,1);
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
CREATE DATABASE IF NOT EXISTS university;
|
||||||
|
|
||||||
|
USE university;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
DROP TABLE IF EXISTS student;
|
||||||
|
DROP TABLE IF EXISTS faculty;
|
||||||
|
DROP TABLE IF EXISTS course;
|
||||||
|
DROP TABLE IF EXISTS enrolled;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
|
||||||
|
CREATE TABLE student(
|
||||||
|
snum integer,
|
||||||
|
sname varchar(30),
|
||||||
|
major varchar(25),
|
||||||
|
standing varchar(2),
|
||||||
|
age integer,
|
||||||
|
primary key (snum)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE faculty(
|
||||||
|
fid integer,
|
||||||
|
fname varchar(30),
|
||||||
|
deptid integer,
|
||||||
|
primary key (fid)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE course(
|
||||||
|
cname varchar(40),
|
||||||
|
meets_at varchar(20),
|
||||||
|
room varchar(10),
|
||||||
|
fid integer,
|
||||||
|
primary key (cname),
|
||||||
|
foreign key (fid) references faculty(fid)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE enrolled(
|
||||||
|
snum integer,
|
||||||
|
cname varchar(40),
|
||||||
|
primary key (snum,cname),
|
||||||
|
foreign key (snum) references student(snum),
|
||||||
|
foreign key (cname) references course(cname)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
INSERT INTO student VALUES( 051135593,'Maria White','English','SR',21);
|
||||||
|
INSERT INTO student VALUES( 060839453,'Charles Harris','Architecture','SR',22);
|
||||||
|
INSERT INTO student VALUES( 099354543,'Susan Martin','Law','JR',20);
|
||||||
|
INSERT INTO student VALUES( 112348546,'Joseph Thompson','Computer Science','SO',19);
|
||||||
|
INSERT INTO student VALUES( 115987938,'Christopher Garcia','Computer Science','JR',20);
|
||||||
|
INSERT INTO student VALUES( 132977562,'Angela Martinez','History','SR',20);
|
||||||
|
INSERT INTO student VALUES( 269734834,'Thomas Robinson','Psychology','SO',18);
|
||||||
|
INSERT INTO student VALUES( 280158572,'Margaret Clark','Animal Science','FR',18);
|
||||||
|
INSERT INTO student VALUES( 301221823,'Juan Rodriguez','Psychology','JR',20);
|
||||||
|
INSERT INTO student VALUES( 318548912,'Dorthy Lewis','Finance','FR',18);
|
||||||
|
INSERT INTO student VALUES( 320874981,'Daniel Lee','Electrical Engineering','FR',17);
|
||||||
|
INSERT INTO student VALUES( 322654189,'Lisa Walker','Computer Science','SO',17);
|
||||||
|
INSERT INTO student VALUES( 348121549,'Paul Hall','Computer Science','JR',18);
|
||||||
|
INSERT INTO student VALUES( 351565322,'Nancy Allen','Accounting','JR',19);
|
||||||
|
INSERT INTO student VALUES( 451519864,'Mark Young','Finance','FR',18);
|
||||||
|
INSERT INTO student VALUES( 455798411,'Luis Hernandez','Electrical Engineering','FR',17);
|
||||||
|
INSERT INTO student VALUES( 462156489,'Donald King','Mechanical Engineering','SO',19);
|
||||||
|
INSERT INTO student VALUES( 550156548,'George Wright','Education','SR',21);
|
||||||
|
INSERT INTO student VALUES( 552455318,'Ana Lopez','Computer Engineering','SR',19);
|
||||||
|
INSERT INTO student VALUES( 556784565,'Kenneth Hill','Civil Engineering','SR',21);
|
||||||
|
INSERT INTO student VALUES( 567354612,'Karen Scott','Computer Engineering','FR',18);
|
||||||
|
INSERT INTO student VALUES( 573284895,'Steven Green','Kinesiology','SO',19);
|
||||||
|
INSERT INTO student VALUES( 574489456,'Betty Adams','Economics','JR',20);
|
||||||
|
INSERT INTO student VALUES( 578875478,'Edward Baker','Veterinary Medicine','SR',21);
|
||||||
|
|
||||||
|
INSERT INTO faculty VALUES( 142519864,'Ivana Teach',20);
|
||||||
|
INSERT INTO faculty VALUES( 242518965,'James Smith',68);
|
||||||
|
INSERT INTO faculty VALUES( 141582651,'Mary Johnson',20);
|
||||||
|
INSERT INTO faculty VALUES( 011564812,'John Williams',68);
|
||||||
|
INSERT INTO faculty VALUES( 254099823,'Patricia Jones',68);
|
||||||
|
INSERT INTO faculty VALUES( 356187925,'Robert Brown',12);
|
||||||
|
INSERT INTO faculty VALUES( 489456522,'Linda Davis',20);
|
||||||
|
INSERT INTO faculty VALUES( 287321212,'Michael Miller',12);
|
||||||
|
INSERT INTO faculty VALUES( 248965255,'Barbara Wilson',12);
|
||||||
|
INSERT INTO faculty VALUES( 159542516,'William Moore',33);
|
||||||
|
INSERT INTO faculty VALUES( 090873519,'Elizabeth Taylor',11);
|
||||||
|
INSERT INTO faculty VALUES( 486512566,'David Anderson',20);
|
||||||
|
INSERT INTO faculty VALUES( 619023588,'Jennifer Thomas',11);
|
||||||
|
INSERT INTO faculty VALUES( 489221823,'Richard Jackson',33);
|
||||||
|
INSERT INTO faculty VALUES( 548977562,'Ulysses Teach',20);
|
||||||
|
|
||||||
|
INSERT INTO course VALUES('Data Structures','MWF 10','R128',489456522);
|
||||||
|
INSERT INTO course VALUES('Database Systems','MWF 12:30-1:45','1320 DCL',142519864);
|
||||||
|
INSERT INTO course VALUES('Operating System Design','TuTh 12-1:20','20 AVW',489456522);
|
||||||
|
INSERT INTO course VALUES('Archaeology of the Incas','MWF 3-4:15','R128',248965255);
|
||||||
|
INSERT INTO course VALUES('Aviation Accident Investigation','TuTh 1-2:50','Q3',011564812);
|
||||||
|
INSERT INTO course VALUES('Air Quality Engineering','TuTh 10:30-11:45','R15',011564812);
|
||||||
|
INSERT INTO course VALUES('Introductory Latin','MWF 3-4:15','R12',248965255);
|
||||||
|
INSERT INTO course VALUES('American Political Parties','TuTh 2-3:15','20 AVW',619023588);
|
||||||
|
INSERT INTO course VALUES('Social Cognition','Tu 6:30-8:40','R15',159542516);
|
||||||
|
INSERT INTO course VALUES('Perception','MTuWTh 3','Q3',489221823);
|
||||||
|
INSERT INTO course VALUES('Multivariate Analysis','TuTh 2-3:15','R15',090873519);
|
||||||
|
INSERT INTO course VALUES('Patent Law','F 1-2:50','R128',090873519);
|
||||||
|
INSERT INTO course VALUES('Urban Economics','MWF 11','20 AVW',489221823);
|
||||||
|
INSERT INTO course VALUES('Organic Chemistry','TuTh 12:30-1:45','R12',489221823);
|
||||||
|
INSERT INTO course VALUES('Marketing Research','MW 10-11:15','1320 DCL',489221823);
|
||||||
|
INSERT INTO course VALUES('Seminar in American Art','M 4','R15',489221823);
|
||||||
|
INSERT INTO course VALUES('Orbital Mechanics','MWF 8','1320 DCL',011564812);
|
||||||
|
INSERT INTO course VALUES('Dairy Herd Management','TuTh 12:30-1:45','R128',356187925);
|
||||||
|
INSERT INTO course VALUES('Communication Networks','MW 9:30-10:45','20 AVW',141582651);
|
||||||
|
INSERT INTO course VALUES('Optical Electronics','TuTh 12:30-1:45','R15',254099823);
|
||||||
|
INSERT INTO course VALUES('Intoduction to Math','TuTh 8-9:30','R128',489221823);
|
||||||
|
|
||||||
|
INSERT INTO enrolled VALUES( 112348546,'Database Systems');
|
||||||
|
INSERT INTO enrolled VALUES(115987938,'Database Systems');
|
||||||
|
INSERT INTO enrolled VALUES(348121549,'Database Systems');
|
||||||
|
INSERT INTO enrolled VALUES(322654189,'Database Systems');
|
||||||
|
INSERT INTO enrolled VALUES(552455318,'Database Systems');
|
||||||
|
INSERT INTO enrolled VALUES(455798411,'Operating System Design');
|
||||||
|
INSERT INTO enrolled VALUES(552455318,'Operating System Design');
|
||||||
|
INSERT INTO enrolled VALUES(567354612,'Operating System Design');
|
||||||
|
INSERT INTO enrolled VALUES(112348546,'Operating System Design');
|
||||||
|
INSERT INTO enrolled VALUES(115987938,'Operating System Design');
|
||||||
|
INSERT INTO enrolled VALUES(322654189,'Operating System Design');
|
||||||
|
INSERT INTO enrolled VALUES(567354612,'Data Structures');
|
||||||
|
INSERT INTO enrolled VALUES(552455318,'Communication Networks');
|
||||||
|
INSERT INTO enrolled VALUES(455798411,'Optical Electronics');
|
||||||
|
INSERT INTO enrolled VALUES(301221823,'Perception');
|
||||||
|
INSERT INTO enrolled VALUES(301221823,'Social Cognition');
|
||||||
|
INSERT INTO enrolled VALUES(301221823,'American Political Parties');
|
||||||
|
INSERT INTO enrolled VALUES(556784565,'Air Quality Engineering');
|
||||||
|
INSERT INTO enrolled VALUES(099354543,'Patent Law');
|
||||||
|
INSERT INTO enrolled VALUES(574489456,'Urban Economics');
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
CREATE DATABASE IF NOT EXISTS mydb;
|
||||||
|
|
||||||
|
USE mydb;
|
||||||
|
GRANT ALL privileges ON mydb.* TO testuser;
|
||||||
|
|
||||||
|
USE workson;
|
||||||
|
GRANT ALL privileges ON workson.* TO testuser;
|
||||||
|
|
||||||
|
USE university;
|
||||||
|
GRANT ALL privileges ON university.* TO testuser;
|
||||||
|
|
||||||
|
USE shipment;
|
||||||
|
GRANT ALL privileges ON shipment.* TO testuser;
|
|
@ -0,0 +1,61 @@
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
node:
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: cosc304-node
|
||||||
|
container_name: cosc304-node
|
||||||
|
volumes:
|
||||||
|
- ./:/app/
|
||||||
|
- /app/node_modules
|
||||||
|
networks:
|
||||||
|
network304:
|
||||||
|
aliases:
|
||||||
|
- cosc304_node
|
||||||
|
ports:
|
||||||
|
- 80:3000
|
||||||
|
cosc304-sqlserver:
|
||||||
|
image: mcr.microsoft.com/mssql/server:2019-latest
|
||||||
|
container_name: cosc304-sqlserver
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
ACCEPT_EULA: 'Y'
|
||||||
|
SA_PASSWORD: '304#sa#pw'
|
||||||
|
ports:
|
||||||
|
- '1433:1433'
|
||||||
|
expose:
|
||||||
|
- '1433'
|
||||||
|
volumes:
|
||||||
|
- cosc304-db:/var/lib/mssql
|
||||||
|
- ./ddl:/scripts
|
||||||
|
networks:
|
||||||
|
network304:
|
||||||
|
aliases:
|
||||||
|
- cosc304_sqlserver
|
||||||
|
command:
|
||||||
|
- /bin/bash
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
/opt/mssql/bin/sqlservr &
|
||||||
|
pid=$$!
|
||||||
|
echo "Waiting for MS SQL to be available"
|
||||||
|
/opt/mssql-tools/bin/sqlcmd -l 30 -S localhost -h-1 -V1 -U sa -P $$SA_PASSWORD -Q "SET NOCOUNT ON SELECT \"YAY WE ARE UP\" , @@servername"
|
||||||
|
is_up=$$?
|
||||||
|
while [ $$is_up -ne 0 ] ; do
|
||||||
|
echo -e $$(date)
|
||||||
|
/opt/mssql-tools/bin/sqlcmd -l 30 -S localhost -h-1 -V1 -U sa -P $$SA_PASSWORD -Q "SET NOCOUNT ON SELECT \"YAY WE ARE UP\" , @@servername"
|
||||||
|
is_up=$$?
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
for foo in /scripts/SQLServer*.ddl
|
||||||
|
do /opt/mssql-tools/bin/sqlcmd -U sa -P $$SA_PASSWORD -l 30 -e -i $$foo
|
||||||
|
done
|
||||||
|
trap "kill -15 $$pid" SIGTERM
|
||||||
|
wait $$pid
|
||||||
|
exit 0
|
||||||
|
volumes:
|
||||||
|
cosc304-db:
|
||||||
|
mysql-db:
|
||||||
|
networks:
|
||||||
|
network304:
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "cosc304lab7",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "COSC 304 Lab 7 Node Variant",
|
||||||
|
"repository": "N/A",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "nodemon server.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-handlebars": "^5.1.0",
|
||||||
|
"express-session": "^1.17.1",
|
||||||
|
"moment": "^2.28.0",
|
||||||
|
"mssql": "^6.2.1",
|
||||||
|
"nodemon": "^2.0.4",
|
||||||
|
"tedious": "^9.2.1",
|
||||||
|
"multer": "^1.4.5-lts.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
top: 0; left: 0;
|
||||||
|
background-color: white;
|
||||||
|
border-bottom: 3px double black;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > ul {
|
||||||
|
padding-left: 10px;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > ul > li {
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.nav-right {
|
||||||
|
float: right;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > ul:last-child {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 80%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-search-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-search {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-search > input[type="text"] {
|
||||||
|
width: 90%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-search > input, .product-search > button {
|
||||||
|
font-size: 1.1em;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-search > button, .product-search > input[type="submit"] {
|
||||||
|
padding: 5px;
|
||||||
|
width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: white;
|
||||||
|
border-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-search > button:hover, .product-search > input[type="submit"]:hover {
|
||||||
|
padding: 5px;
|
||||||
|
width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: grey;
|
||||||
|
border-color: black;
|
||||||
|
}
|
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 353 KiB |
|
@ -0,0 +1,44 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.setHeader('Content-Type', 'text/html');
|
||||||
|
// If the product list isn't set in the session,
|
||||||
|
// create a new list.
|
||||||
|
let productList = false;
|
||||||
|
if (!req.session.productList) {
|
||||||
|
productList = [];
|
||||||
|
} else {
|
||||||
|
productList = req.session.productList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new product selected
|
||||||
|
// Get product information
|
||||||
|
let id = false;
|
||||||
|
let name = false;
|
||||||
|
let price = false;
|
||||||
|
if (req.query.id && req.query.name && req.query.price) {
|
||||||
|
id = req.query.id;
|
||||||
|
name = req.query.name;
|
||||||
|
price = req.query.price;
|
||||||
|
} else {
|
||||||
|
res.redirect("/listprod");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update quantity if add same item to order again
|
||||||
|
if (productList.some(p => p.id == id)) {
|
||||||
|
productList = productList.map(p => (p.id == id ? {...p, quantity: p.quantity + 1} : p));
|
||||||
|
} else {
|
||||||
|
productList.push({
|
||||||
|
"id": id,
|
||||||
|
"name": name,
|
||||||
|
"price": price,
|
||||||
|
"quantity": 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
req.session.productList = productList;
|
||||||
|
res.redirect("/showcart");
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -0,0 +1,523 @@
|
||||||
|
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;
|
|
@ -0,0 +1,23 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { getUser } = require('../util.js');
|
||||||
|
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.render('layouts/main', {
|
||||||
|
title: 'Grocery Checkout Line',
|
||||||
|
loggedIn: true,
|
||||||
|
user: getUser(req),
|
||||||
|
spacer: true,
|
||||||
|
content: `
|
||||||
|
<h1> Verify your credentials to continue. </h1>
|
||||||
|
|
||||||
|
<form method="get" action="/order">
|
||||||
|
<input type="text" name="customerId" size="50" placeholder="Customer ID"><br>
|
||||||
|
<input type="password" name="password" size="50" placeholder="Password"><br>
|
||||||
|
<input type="submit" value="Submit"><input type="reset" value="Reset">
|
||||||
|
</form>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -0,0 +1,240 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const sql = require('mssql');
|
||||||
|
const { getUser, getCustomerRecord } = require('../util.js');
|
||||||
|
|
||||||
|
router.get('/', async function(req, res, next) {
|
||||||
|
res.setHeader('Content-Type', 'text/html');
|
||||||
|
|
||||||
|
let pool;
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
const profile = await getCustomerRecord(pool, getUser(req).id);
|
||||||
|
|
||||||
|
res.render('layouts/main', {
|
||||||
|
loggedIn: getUser(req) != null,
|
||||||
|
user: getUser(req),
|
||||||
|
spacer: true,
|
||||||
|
content: `
|
||||||
|
<h1>${profile.firstName} ${profile.lastName}'s Profile</h1>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<td>${profile.customerId}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Username</th>
|
||||||
|
<td>${profile.userid}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>First Name</th>
|
||||||
|
<td>${profile.firstName}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Last Name</th>
|
||||||
|
<td>${profile.lastName}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>email</th>
|
||||||
|
<td>${profile.email}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Phone Number</th>
|
||||||
|
<td>${profile.phonenum}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Address</th>
|
||||||
|
<td>${profile.address}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>City</th>
|
||||||
|
<td>${profile.city}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>State</th>
|
||||||
|
<td>${profile.state}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Postal Code</th>
|
||||||
|
<td>${profile.postalCode}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Country</th>
|
||||||
|
<td>${profile.country}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/listorder">
|
||||||
|
Show all orders
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/customer/update">
|
||||||
|
Edit account details
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/logout">
|
||||||
|
Log out
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
} catch(err) {
|
||||||
|
console.dir(err);
|
||||||
|
res.write(err + "")
|
||||||
|
res.end();
|
||||||
|
} finally {
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/update', async (req, res) => {
|
||||||
|
let pool;
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
const profile = await getCustomerRecord(pool, getUser(req).id);
|
||||||
|
|
||||||
|
res.render('layouts/main', {
|
||||||
|
title: 'Update your Profile',
|
||||||
|
loggedIn: getUser(req) != null,
|
||||||
|
user: getUser(req),
|
||||||
|
spacer: true,
|
||||||
|
content: `
|
||||||
|
<h1>${profile.firstName} ${profile.lastName}'s Profile</h1>
|
||||||
|
|
||||||
|
<form action='/customer/update' method='post'>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<td>${profile.customerId}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Username</th>
|
||||||
|
<td>
|
||||||
|
<input type='text' name='userid' required minlength="2" maxlength="50" value='${profile.userid}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>First Name</th>
|
||||||
|
<td>
|
||||||
|
<input type='text' name='firstName' required minlength="2" maxlength="50" value='${profile.firstName}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Last Name</th>
|
||||||
|
<td>
|
||||||
|
<input type='text' name='lastName' required minlength="2" maxlength="50" value='${profile.lastName}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>email</th>
|
||||||
|
<td>
|
||||||
|
<input type='email' name='email' required value='${profile.email}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Phone Number</th>
|
||||||
|
<td>
|
||||||
|
<input type='tel' name='phonenum' required maxlength="15" value='${profile.phonenum}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Address</th>
|
||||||
|
<td>
|
||||||
|
<input type='text' name='address' required value='${profile.address}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>City</th>
|
||||||
|
<td>
|
||||||
|
<input type='text' name='city' required value='${profile.city}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>State</th>
|
||||||
|
<td>
|
||||||
|
<input type='text' name='state' required value='${profile.state}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Postal Code</th>
|
||||||
|
<td>
|
||||||
|
<input type='text' name='postalCode' required value='${profile.postalCode}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Country</th>
|
||||||
|
<td>
|
||||||
|
<input type='text' name='country' required value='${profile.country}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<input type="submit" value="Update"/>
|
||||||
|
<button onclick="e.preventDefault(); history.back();">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/update', async (req, res) => {
|
||||||
|
let pool;
|
||||||
|
let stmt;
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
|
||||||
|
stmt = new sql.PreparedStatement(pool);
|
||||||
|
stmt.input('userid', sql.VarChar);
|
||||||
|
stmt.input('firstName', sql.VarChar);
|
||||||
|
stmt.input('lastName', sql.VarChar);
|
||||||
|
stmt.input('email', sql.VarChar);
|
||||||
|
stmt.input('phonenum', sql.VarChar);
|
||||||
|
stmt.input('address', sql.VarChar);
|
||||||
|
stmt.input('city', sql.VarChar);
|
||||||
|
stmt.input('state', sql.VarChar);
|
||||||
|
stmt.input('postalCode', sql.VarChar);
|
||||||
|
stmt.input('country', sql.VarChar);
|
||||||
|
stmt.input('customerId', sql.Int);
|
||||||
|
|
||||||
|
await stmt.prepare(`
|
||||||
|
update customer set
|
||||||
|
userid = @userid,
|
||||||
|
firstName = @firstName,
|
||||||
|
lastName = @lastName,
|
||||||
|
email = @email,
|
||||||
|
phonenum = @address,
|
||||||
|
city = @city,
|
||||||
|
state = @state,
|
||||||
|
postalCode = @postalCode,
|
||||||
|
country = @country
|
||||||
|
where customerId = @customerId
|
||||||
|
`);
|
||||||
|
|
||||||
|
await stmt.execute({...req.body, customerId: getUser(req).id});
|
||||||
|
|
||||||
|
res.redirect('/customer');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.write("Failed to update user");
|
||||||
|
} finally {
|
||||||
|
if (stmt) stmt.unprepare();
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const sql = require('mssql');
|
||||||
|
|
||||||
|
router.get('/', async function(req, res, next) {
|
||||||
|
res.setHeader('Content-Type', 'image/jpeg');
|
||||||
|
|
||||||
|
let id = req.query.id;
|
||||||
|
let idVal = parseInt(id);
|
||||||
|
if (isNaN(idVal)) {
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let pool = await sql.connect(dbConfig);
|
||||||
|
|
||||||
|
let sqlQuery = "select productName, productImage from product where productId = @id";
|
||||||
|
|
||||||
|
result = await pool.request()
|
||||||
|
.input('id', sql.Int, idVal)
|
||||||
|
.query(sqlQuery);
|
||||||
|
|
||||||
|
if (result.recordset.length === 0) {
|
||||||
|
console.log("No image record");
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
let productImage = result.recordset[0].productImage;
|
||||||
|
|
||||||
|
res.write(productImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end()
|
||||||
|
} catch(err) {
|
||||||
|
console.dir(err);
|
||||||
|
res.write(err + "")
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { getUser } = require('../util.js');
|
||||||
|
|
||||||
|
// Rendering the main page
|
||||||
|
router.get('/', function (req, res) {
|
||||||
|
console.log(getUser(req));
|
||||||
|
res.render('layouts/main', {
|
||||||
|
title: "Nat & Sami's Rare Earth Metals",
|
||||||
|
loggedIn: getUser(req) != null,
|
||||||
|
user: getUser(req),
|
||||||
|
|
||||||
|
content: `
|
||||||
|
<form action='/listprod' class="product-search-container">
|
||||||
|
<div class="product-search">
|
||||||
|
<h1 align="center">Nat & Samira's Rare Earth Metals</h1>
|
||||||
|
<input type='text' name='productName' id='productNameInput' placeholder="Find what you're looking for..."/><br>
|
||||||
|
<input type='submit' value='Search'/>
|
||||||
|
<button type='button' onclick='document.getElementById("productNameInput").value = "";'>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const sql = require('mssql');
|
||||||
|
const moment = require('moment');
|
||||||
|
const { getUser } = require('../util.js');
|
||||||
|
|
||||||
|
router.get('/', async (req, res, next) => {
|
||||||
|
let pool;
|
||||||
|
let stmt;
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
|
||||||
|
let orderResults;
|
||||||
|
|
||||||
|
if (getUser(req).isAdmin) {
|
||||||
|
orderResults = await pool.request().query(`
|
||||||
|
select o.orderId, o.orderDate, o.customerId, c.firstName, c.lastName, o.totalAmount
|
||||||
|
from ordersummary as o
|
||||||
|
left join customer as c on o.customerId = c.customerId
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
stmt = new sql.PreparedStatement(pool);
|
||||||
|
stmt.input('customerId', sql.Int);
|
||||||
|
await stmt.prepare(`
|
||||||
|
select o.orderId, o.orderDate, o.customerId, c.firstName, c.lastName, o.totalAmount
|
||||||
|
from ordersummary as o
|
||||||
|
left join customer as c on o.customerId = c.customerId
|
||||||
|
where o.customerId = @customerId
|
||||||
|
`);
|
||||||
|
|
||||||
|
orderResults = await stmt.execute({customerId: getUser(req).id});
|
||||||
|
}
|
||||||
|
|
||||||
|
const productResults = await pool.request().query(`
|
||||||
|
select orderId, productId, quantity, price from orderproduct
|
||||||
|
`);
|
||||||
|
|
||||||
|
res.render('layouts/main', {
|
||||||
|
loggedIn: getUser(req) != null,
|
||||||
|
user: getUser(req),
|
||||||
|
spacer: true,
|
||||||
|
content: `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang='en'>
|
||||||
|
<head>
|
||||||
|
<title>Nat & Samira's Grocery Order List</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Order List</h1>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Customer ID</th>
|
||||||
|
<th>Order Date</th>
|
||||||
|
<th>Customer ID</th>
|
||||||
|
<th>Customer Name</th>
|
||||||
|
<th>Total Amount</th>
|
||||||
|
</tr>
|
||||||
|
${orderResults.recordset.map(row => {
|
||||||
|
const products = productResults.recordset.filter(r => r.orderId == row.orderId);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td>${row.orderId}</td>
|
||||||
|
<td>${row.orderDate.toDateString()}</td>
|
||||||
|
<td>${row.customerId}</td>
|
||||||
|
<td>${row.firstName} ${row.lastName}</td>
|
||||||
|
<td>$${row.totalAmount.toFixed(2)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan='5'>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Prouct Id</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
<th>Price</th>
|
||||||
|
</tr>
|
||||||
|
${products.map(productRow => `
|
||||||
|
<tr>
|
||||||
|
<td>${productRow.productId}</td>
|
||||||
|
<td>${productRow.quantity}</td>
|
||||||
|
<td>$${productRow.price.toFixed(2)}</td>
|
||||||
|
</tr>
|
||||||
|
`).join('\n')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}).join('\n')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
pool.close();
|
||||||
|
} catch (err) {
|
||||||
|
res.write(err.toString());
|
||||||
|
res.end();
|
||||||
|
} finally {
|
||||||
|
if (stmt) stmt.unprepare();
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -0,0 +1,88 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const sql = require('mssql');
|
||||||
|
const { getUser } = require('../util.js');
|
||||||
|
|
||||||
|
router.get('/', async function(req, res, next) {
|
||||||
|
// Get the product name to search for
|
||||||
|
let name = req.query.productName;
|
||||||
|
|
||||||
|
let content;
|
||||||
|
let pool;
|
||||||
|
let stmt;
|
||||||
|
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
stmt = new sql.PreparedStatement(pool);
|
||||||
|
stmt.input('name', sql.VarChar)
|
||||||
|
await stmt.prepare(`select productId, productName, productPrice
|
||||||
|
from product
|
||||||
|
where productName like concat('%', @name, '%')
|
||||||
|
`);
|
||||||
|
|
||||||
|
const productResults = await stmt.execute({name:name});
|
||||||
|
|
||||||
|
content = `
|
||||||
|
<h2>All products ${name ? `matching ${name}` : ''}</h2>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Product Name</th>
|
||||||
|
<th>Price</th>
|
||||||
|
</tr>
|
||||||
|
${productResults.recordset.map(row => `
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="/product?id=${row.productId}">
|
||||||
|
${row.productName}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>$${row.productPrice.toFixed(2)}</td>
|
||||||
|
<td>
|
||||||
|
<a href='/addcart?${(new URLSearchParams({id: row.productId, name: row.productName, price: row.productPrice})).toString()}'>
|
||||||
|
Add to Cart
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`).join('\n')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
${ productResults.length === 0 ?
|
||||||
|
`<p>
|
||||||
|
Nothing to show!
|
||||||
|
</p>`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
content = err.toString();
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
if (stmt) stmt.unprepare();
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.render('layouts/main', {
|
||||||
|
loggedIn: getUser(req) != null,
|
||||||
|
user: getUser(req),
|
||||||
|
spacer: true,
|
||||||
|
content: `
|
||||||
|
<h1>Find what you're looking for</h1>
|
||||||
|
<form action='/listprod'>
|
||||||
|
<div class="product-search">
|
||||||
|
<input type='text' name='productName' id='productNameInput' placeholder="Search..."/><br>
|
||||||
|
<input type='submit' value='Search'/>
|
||||||
|
<button type='button' onclick='document.getElementById("productNameInput").value = "";'>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
${content}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -0,0 +1,48 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const sql = require('mssql');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const dbConfigWithoutDB = {
|
||||||
|
server: 'cosc304_sqlserver',
|
||||||
|
authentication: {
|
||||||
|
type: 'default',
|
||||||
|
options: {
|
||||||
|
userName: 'sa',
|
||||||
|
password: '304#sa#pw'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
encrypt: false,
|
||||||
|
enableArithAbort:false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
(async function() {
|
||||||
|
try {
|
||||||
|
let pool = await sql.connect(dbConfigWithoutDB);
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'text/html');
|
||||||
|
res.write('<title>Data Loader</title>');
|
||||||
|
res.write('<h1>Connecting to database.</h1><p>');
|
||||||
|
|
||||||
|
let data = fs.readFileSync("./ddl/SQLServer_orderdb.ddl", { encoding: 'utf8' });
|
||||||
|
let commands = data.split(";");
|
||||||
|
for (let i = 0; i < commands.length; i++) {
|
||||||
|
let command = commands[i];
|
||||||
|
let result = await pool.request()
|
||||||
|
.query(command);
|
||||||
|
res.write('<p>' + JSON.stringify(result) + '</p>')
|
||||||
|
}
|
||||||
|
|
||||||
|
res.write('"<h2>Database loading complete!</h2>')
|
||||||
|
res.end()
|
||||||
|
} catch(err) {
|
||||||
|
console.dir(err);
|
||||||
|
res.send(err)
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -0,0 +1,57 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
if (req.session.user) {
|
||||||
|
res.redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'text/html');
|
||||||
|
|
||||||
|
res.render('layouts/main', {
|
||||||
|
title: "Login Screen",
|
||||||
|
loggedIn: false,
|
||||||
|
content: `
|
||||||
|
<div style="margin:0 auto;text-align:center;display:inline">
|
||||||
|
|
||||||
|
<h3>Please Login to System</h3>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<form name="MyForm" method=post action="/validateLogin">
|
||||||
|
<table style="display:inline">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<font face="Arial, Helvetica, sans-serif" size="2">Username:</font>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="username" size=10 maxlength=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<font face="Arial, Helvetica, sans-serif" size="2">Password:</font>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="password" name="password" size=10 maxlength="10"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Don't have an account?
|
||||||
|
<a href="/register">
|
||||||
|
Register here.
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<input class="submit" type="submit" name="Submit2" value="Log In">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
req.session.user = null;
|
||||||
|
res.redirect("/login");
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const sql = require('mssql');
|
||||||
|
const moment = require('moment');
|
||||||
|
const { getUser } = require('../util.js');
|
||||||
|
|
||||||
|
router.get('/', async function(req, res, next) {
|
||||||
|
res.setHeader('Content-Type', 'text/html');
|
||||||
|
|
||||||
|
/** Make connection and validate **/
|
||||||
|
let pool = false;
|
||||||
|
let customerRequest = false;
|
||||||
|
let orderStatement = false;
|
||||||
|
let orderProductStatement = false;
|
||||||
|
|
||||||
|
let content;
|
||||||
|
|
||||||
|
loadedResources: try {
|
||||||
|
let productList = false;
|
||||||
|
if (req.session.productList && req.session.productList.length > 0) {
|
||||||
|
productList = req.session.productList.filter(p => p != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determine if valid customer id was entered
|
||||||
|
Determine if there are products in the shopping cart
|
||||||
|
If either are not true, display an error message
|
||||||
|
**/
|
||||||
|
|
||||||
|
if (!productList) {
|
||||||
|
content = `
|
||||||
|
<p>
|
||||||
|
You do not currently have any items in your cart.
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
break loadedResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^-?\d+$/.test(req.query.customerId)) {
|
||||||
|
content = `
|
||||||
|
<p>
|
||||||
|
The given ID does not match any known customer.
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
break loadedResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
customerRequest = new sql.PreparedStatement(pool);
|
||||||
|
customerRequest.input('id', sql.Int);
|
||||||
|
await customerRequest.prepare(`
|
||||||
|
select customerId, password from customer where customerId = @id
|
||||||
|
`);
|
||||||
|
const customerResults = await customerRequest.execute({id: req.query.customerId});
|
||||||
|
|
||||||
|
if (customerResults.recordset.length == 0) {
|
||||||
|
content = `
|
||||||
|
<p>
|
||||||
|
The given ID does not match any known customer.
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
break loadedResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
const customer = customerResults.recordset[0];
|
||||||
|
|
||||||
|
if (customer.password != req.query.password) {
|
||||||
|
content = `
|
||||||
|
<p>
|
||||||
|
Id or password did not match!
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
break loadedResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Save order information to database**/
|
||||||
|
/**
|
||||||
|
// Use retrieval of auto-generated keys.
|
||||||
|
sqlQuery = "INSERT INTO <TABLE> OUTPUT INSERTED.orderId VALUES( ... )";
|
||||||
|
let result = await pool.request()
|
||||||
|
.input(...)
|
||||||
|
.query(sqlQuery);
|
||||||
|
// Catch errors generated by the query
|
||||||
|
let orderId = result.recordset[0].orderId;
|
||||||
|
**/
|
||||||
|
|
||||||
|
console.log(productList)
|
||||||
|
const totalAmount = productList.reduce((acc, next) => acc + next.price * next.quantity, 0);
|
||||||
|
|
||||||
|
orderStatement = new sql.PreparedStatement(pool);
|
||||||
|
orderStatement.input('amount', sql.Decimal(10, 2));
|
||||||
|
orderStatement.input('address', sql.VarChar);
|
||||||
|
orderStatement.input('city', sql.VarChar);
|
||||||
|
orderStatement.input('state', sql.VarChar);
|
||||||
|
orderStatement.input('country', sql.VarChar);
|
||||||
|
orderStatement.input('postalCode', sql.VarChar);
|
||||||
|
orderStatement.input('customerId', sql.Int);
|
||||||
|
await orderStatement.prepare(`
|
||||||
|
insert into ordersummary(orderDate, totalAmount, shipToAddress, shipToCity, shipToState, shipToPostalCode, shipToCountry, customerId)
|
||||||
|
output inserted.orderId
|
||||||
|
values (getdate(), @amount, @address, @city, @state, @postalCode, @country, @customerId)
|
||||||
|
`);
|
||||||
|
const orderResults = await orderStatement.execute({
|
||||||
|
amount: totalAmount,
|
||||||
|
address: customer.address,
|
||||||
|
city: customer.city,
|
||||||
|
state: customer.state,
|
||||||
|
country: customer.country,
|
||||||
|
postalCode: customer.postalCode,
|
||||||
|
customerId: customer.customerId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const orderId = orderResults.recordset[0].orderId;
|
||||||
|
|
||||||
|
/** Insert each item into OrderedProduct table using OrderId from previous INSERT **/
|
||||||
|
/** Update total amount for order record **/
|
||||||
|
/** For each entry in the productList is an array with key values: id, name, quantity, price **/
|
||||||
|
|
||||||
|
orderProductStatement = new sql.PreparedStatement(pool);
|
||||||
|
orderProductStatement.input('orderId', sql.Int);
|
||||||
|
orderProductStatement.input('id', sql.Int);
|
||||||
|
orderProductStatement.input('quantity', sql.Int);
|
||||||
|
orderProductStatement.input('price', sql.Decimal(10, 2));
|
||||||
|
await orderProductStatement.prepare(`
|
||||||
|
insert into orderproduct(orderId, productId, quantity, price) values (@orderId, @id, @quantity, @price)
|
||||||
|
`);
|
||||||
|
|
||||||
|
await Promise.all(productList.map(product =>
|
||||||
|
orderProductStatement.execute({
|
||||||
|
...product,
|
||||||
|
orderId
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
content = `
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Product Name</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
</tr>
|
||||||
|
${productList.map(row => `
|
||||||
|
<tr>
|
||||||
|
<td>${row.name}</td>
|
||||||
|
<td>$${Number(row.price).toFixed(2)}</td>
|
||||||
|
<td>${row.quantity}</td>
|
||||||
|
</tr>
|
||||||
|
`).join('\n')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
<strong>Order Total:</strong> $${Number(totalAmount).toFixed(2)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Tracking Number:</strong> ${orderId}
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
content = `
|
||||||
|
<p>
|
||||||
|
${err.toString()}
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
} finally {
|
||||||
|
if (customerRequest) customerRequest.unprepare();
|
||||||
|
if (orderStatement) orderStatement.unprepare();
|
||||||
|
if (orderProductStatement) orderProductStatement.unprepare();
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Print out order summary **/
|
||||||
|
|
||||||
|
/** Clear session/cart **/
|
||||||
|
req.session.productList = [];
|
||||||
|
|
||||||
|
res.render('layouts/main', {
|
||||||
|
title: 'Order Complete',
|
||||||
|
loggedIn: true,
|
||||||
|
user: getUser(req),
|
||||||
|
spacer: true,
|
||||||
|
content: `
|
||||||
|
<h1>Your Order</h1>
|
||||||
|
${content}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -0,0 +1,86 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const sql = require('mssql');
|
||||||
|
const { getUser } = require('../util.js');
|
||||||
|
|
||||||
|
router.get('/', async function(req, res, next) {
|
||||||
|
// Get product name to search for
|
||||||
|
const productId = req.query.id;
|
||||||
|
|
||||||
|
let pool;
|
||||||
|
let stmt;
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
stmt = new sql.PreparedStatement(pool);
|
||||||
|
stmt.input('productId', sql.Int);
|
||||||
|
|
||||||
|
// TO/DO: Retrieve and display info for the product
|
||||||
|
await stmt.prepare(`
|
||||||
|
select * from product
|
||||||
|
join category on product.categoryId = category.categoryId
|
||||||
|
where productId = @productId
|
||||||
|
`);
|
||||||
|
|
||||||
|
const product = (await stmt.execute({productId})).recordset[0];
|
||||||
|
console.log(product);
|
||||||
|
|
||||||
|
if (!product) {
|
||||||
|
throw new Error('Product not found!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TO/DO: If there is a productImageURL, display using IMG tag
|
||||||
|
// TO/DO: Retrieve any image stored directly in database. Note: Call displayImage.jsp with product id as parameter.
|
||||||
|
|
||||||
|
let imageUrl;
|
||||||
|
|
||||||
|
if (product.productImageURL) {
|
||||||
|
imageUrl = product.productImageURL;
|
||||||
|
} else {
|
||||||
|
imageUrl = `/displayImage?id=${productId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TO/DO: Add links to Add to Cart and Continue Shopping
|
||||||
|
|
||||||
|
res.render('layouts/main', {
|
||||||
|
title: product.productName,
|
||||||
|
loggedIn: true,
|
||||||
|
user: getUser(req),
|
||||||
|
spacer: true,
|
||||||
|
content: `
|
||||||
|
<h1>${product.productName}</h1>
|
||||||
|
|
||||||
|
<img src='${imageUrl}' alt='${product.productName}'>
|
||||||
|
<p>
|
||||||
|
<strong>Category:</strong> ${product.categoryName}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
${product.productDesc}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Price:</strong> $${Number(product.productPrice).toFixed(2)}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/addcart?${(new URLSearchParams({id: product.productId, name: product.productName, price: product.productPrice})).toString()}">
|
||||||
|
Add to Cart
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/listprod">Continue Shopping</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
} catch(err) {
|
||||||
|
console.dir(err);
|
||||||
|
res.write(err + "")
|
||||||
|
res.end();
|
||||||
|
} finally {
|
||||||
|
if (stmt) stmt.unprepare();
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const sql = require('mssql');
|
||||||
|
const { getUser } = require('../util.js');
|
||||||
|
|
||||||
|
router.get('/', async (req, res) => {
|
||||||
|
|
||||||
|
if (getUser(req) != null) {
|
||||||
|
res.redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
res.render('layouts/main', {
|
||||||
|
title: 'Register',
|
||||||
|
content: `
|
||||||
|
<div style="margin:0 auto;text-align:center;display:inline">
|
||||||
|
|
||||||
|
<h1>Register</h1>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<form method=post action="/register">
|
||||||
|
<table style="display:inline">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="firstName">First name:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="firstName" required size=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="lastName">Last name:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="lastName" required size=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="email">Email:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="email" name="email" required size=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="phonenum">Phone Number:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="tel" name="phonenum" required size=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="address">Address:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="address" required size=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="city">City:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="city" required size=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="state">State:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="state" required size=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="country">Country:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="country" required size=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="postalCode">postalCode:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="postalCode" required size=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="userid">Username:</font>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="userid" required size=10 maxlength=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="password" name="password" required size=10 maxlength="10"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Already have an account?
|
||||||
|
<a href="/login">
|
||||||
|
Login
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<input class="submit" type="submit" name="Submit" value="Register">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/', async (req, res) => {
|
||||||
|
let pool;
|
||||||
|
let stmt;
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
|
||||||
|
stmt = new sql.PreparedStatement(pool);
|
||||||
|
stmt.input('userid', sql.VarChar);
|
||||||
|
stmt.input('firstName', sql.VarChar);
|
||||||
|
stmt.input('lastName', sql.VarChar);
|
||||||
|
stmt.input('email', sql.VarChar);
|
||||||
|
stmt.input('phonenum', sql.VarChar);
|
||||||
|
stmt.input('address', sql.VarChar);
|
||||||
|
stmt.input('city', sql.VarChar);
|
||||||
|
stmt.input('state', sql.VarChar);
|
||||||
|
stmt.input('postalCode', sql.VarChar);
|
||||||
|
stmt.input('country', sql.VarChar);
|
||||||
|
stmt.input('password', sql.VarChar);
|
||||||
|
|
||||||
|
await stmt.prepare(`
|
||||||
|
insert into customer(userid, firstName, lastName, email, phonenum, address, city, state, postalCode, country, password)
|
||||||
|
values (@userid, @firstName, @lastName, @email, @phonenum, @address, @city, @state, @postalCode, @country, @password)
|
||||||
|
`);
|
||||||
|
|
||||||
|
await stmt.execute({...req.body});
|
||||||
|
|
||||||
|
res.redirect('/login');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.send("Failed to register user: " + err.toString());
|
||||||
|
} finally {
|
||||||
|
if (stmt) stmt.unprepare();
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -0,0 +1,168 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const sql = require('mssql');
|
||||||
|
const moment = require('moment');
|
||||||
|
const { htmlPage } = require('../util.js');
|
||||||
|
|
||||||
|
router.get('/', async function(req, res, next) {
|
||||||
|
res.setHeader('Content-Type', 'text/html');
|
||||||
|
|
||||||
|
// TO/DO: Get order id
|
||||||
|
const orderId = req.query.orderId;
|
||||||
|
|
||||||
|
let pool;
|
||||||
|
let validateIdStatement;
|
||||||
|
let fetchItemsStatement;
|
||||||
|
let createShipmentStatement;
|
||||||
|
let checkInventoryStatement;
|
||||||
|
let updateInventoryStatement;
|
||||||
|
let orderSummaryHTML;
|
||||||
|
let tx;
|
||||||
|
let failed = false;
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
|
||||||
|
// TODO: Check if valid order id
|
||||||
|
validateIdStatement = new sql.PreparedStatement(pool);
|
||||||
|
validateIdStatement.input('orderId', sql.Int)
|
||||||
|
await validateIdStatement.prepare(`select * from ordersummary where orderId = @orderId`);
|
||||||
|
const results = await validateIdStatement.execute({orderId});
|
||||||
|
|
||||||
|
if (results.recordset.length === 0) {
|
||||||
|
throw new Error("The order you're looking for doesn't exist!");
|
||||||
|
}
|
||||||
|
|
||||||
|
const order = results.recordset[0];
|
||||||
|
|
||||||
|
// TO/DO: Start a transaction
|
||||||
|
tx = new sql.Transaction(pool);
|
||||||
|
|
||||||
|
await tx.begin();
|
||||||
|
|
||||||
|
// TO/DO: Retrieve all items in order with given id
|
||||||
|
|
||||||
|
let items;
|
||||||
|
|
||||||
|
fetchItemsStatement = new sql.PreparedStatement(tx);
|
||||||
|
fetchItemsStatement.input('orderId', sql.Int);
|
||||||
|
await fetchItemsStatement.prepare(`
|
||||||
|
select * from orderproduct where orderId = @orderId
|
||||||
|
`);
|
||||||
|
|
||||||
|
items = (await fetchItemsStatement.execute({orderId: order.orderId})).recordset;
|
||||||
|
|
||||||
|
await fetchItemsStatement.unprepare();
|
||||||
|
fetchItemsStatement = false;
|
||||||
|
|
||||||
|
orderSummaryHTML = `
|
||||||
|
<ul>
|
||||||
|
${items.map(item =>`
|
||||||
|
<li>
|
||||||
|
${item.quantity} units of product ${item.productId}
|
||||||
|
</li>
|
||||||
|
`).join('\n')}
|
||||||
|
<ul>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// TO/DO: Create a new shipment record.
|
||||||
|
createShipmentStatement = new sql.PreparedStatement(tx);
|
||||||
|
createShipmentStatement.input('shipmentDesc', sql.VarChar);
|
||||||
|
createShipmentStatement.input('warehouseId', sql.Int);
|
||||||
|
await createShipmentStatement.prepare(`
|
||||||
|
insert into shipment(shipmentDate, shipmentDesc, warehouseId)
|
||||||
|
output inserted.shipmentId
|
||||||
|
values (getdate(), @shipmentDesc, @warehouseId)
|
||||||
|
`);
|
||||||
|
|
||||||
|
const shipmentResults = await createShipmentStatement.execute({
|
||||||
|
shipmentDate: Date.now(),
|
||||||
|
shipmentDesc: order.orderId + "",
|
||||||
|
warehouseId: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
await createShipmentStatement.unprepare();
|
||||||
|
createShipmentStatement = false;
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
// TO/DO: For each item verify sufficient quantity available in warehouse 1.
|
||||||
|
checkInventoryStatement = new sql.PreparedStatement(tx);
|
||||||
|
checkInventoryStatement.input('productId', sql.Int);
|
||||||
|
await checkInventoryStatement.prepare(`
|
||||||
|
select * from productinventory
|
||||||
|
where productId = @productId and warehouseId = 1
|
||||||
|
`);
|
||||||
|
|
||||||
|
const inventoryResults = await checkInventoryStatement.execute({productId: item.productId});
|
||||||
|
const inventory = inventoryResults.recordset[0];
|
||||||
|
|
||||||
|
await checkInventoryStatement.unprepare();
|
||||||
|
checkInventoryStatement = false;
|
||||||
|
|
||||||
|
// TO/DO: If any item does not have sufficient inventory, cancel transaction and rollback. Otherwise, update inventory for each item.
|
||||||
|
if (inventory.quantity < item.quantity) {
|
||||||
|
let err = new Error('Insufficient inventory');
|
||||||
|
err.inventory = true;
|
||||||
|
err.productId = item.productId;
|
||||||
|
err.orderId = item.orderId;
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
updateInventoryStatement = new sql.PreparedStatement(tx);
|
||||||
|
updateInventoryStatement.input('productId', sql.Int);
|
||||||
|
updateInventoryStatement.input('amount', sql.Int);
|
||||||
|
await updateInventoryStatement.prepare(`
|
||||||
|
update productinventory set quantity = (quantity - @amount) where productId = @productId
|
||||||
|
`);
|
||||||
|
|
||||||
|
await updateInventoryStatement.execute({productId: item.productId, amount: item.quantity});
|
||||||
|
|
||||||
|
await updateInventoryStatement.unprepare();
|
||||||
|
updateInventoryStatement = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.commit();
|
||||||
|
|
||||||
|
res.send(htmlPage(`
|
||||||
|
<h1>Order ${order.orderId} shipped!</h1>
|
||||||
|
<p><strong>Tracking number:</strong> ${shipmentResults.recordset[0].shipmentId}</p>
|
||||||
|
|
||||||
|
<h2>Order Summary:</h2>
|
||||||
|
${orderSummaryHTML}
|
||||||
|
<p>
|
||||||
|
<a href="/">Return to main page</a>
|
||||||
|
</p>
|
||||||
|
`, `Shipment`));
|
||||||
|
} catch(err) {
|
||||||
|
console.dir(err);
|
||||||
|
failed = true;
|
||||||
|
if (err.inventory) {
|
||||||
|
res.send(htmlPage(`
|
||||||
|
<h1>Order ${err.orderId} was not shipped</h1>
|
||||||
|
<p>
|
||||||
|
Shipment unsuccessful. Insufficient inventory for product ID: ${err.productId}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Order Summary:</h2>
|
||||||
|
${orderSummaryHTML || "Nothing to show"}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/">Return to main page</a>
|
||||||
|
</p>
|
||||||
|
`, `Shipment`));
|
||||||
|
} else {
|
||||||
|
res.write(err + "")
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (validateIdStatement) await validateIdStatement.unprepare();
|
||||||
|
if (fetchItemsStatement) await fetchItemsStatement.unprepare();
|
||||||
|
if (createShipmentStatement) await createShipmentStatement.unprepare();
|
||||||
|
if (checkInventoryStatement) await checkInventoryStatement.unprepare();
|
||||||
|
if (updateInventoryStatement) await updateInventoryStatement.unprepare();
|
||||||
|
if (failed) tx.rollback();
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { getUser } = require('../util.js');
|
||||||
|
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
if (req.session.productList && req.session.productList.length != 0 && req.query.remove) {
|
||||||
|
req.session.productList = req.session.productList.filter(p => p.id != req.query.remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.session.productList && req.session.productList.length != 0 && req.query.id && req.query.inc) {
|
||||||
|
req.session.productList = req.session.productList.map(p => {
|
||||||
|
if (p.id == req.query.id) {
|
||||||
|
const newQuantity = p.quantity + Number(req.query.inc);
|
||||||
|
|
||||||
|
return newQuantity === 0 ? null : {...p, quantity: p.quantity + Number(req.query.inc)};
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}).filter(p => p !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
let content;
|
||||||
|
if (req.session.productList && req.session.productList.length != 0) {
|
||||||
|
req.session.productList = req.session.productList.filter(p => p);
|
||||||
|
|
||||||
|
const productList = req.session.productList;
|
||||||
|
|
||||||
|
content = `
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Product ID</th>
|
||||||
|
<th>Product Name</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
</tr>
|
||||||
|
${productList.map(product => `
|
||||||
|
<tr>
|
||||||
|
<td>${product.id}</td>
|
||||||
|
<td>${product.name}</td>
|
||||||
|
<td>$${Number(product.price).toFixed(2)}</td>
|
||||||
|
<td>
|
||||||
|
<a href='/showcart?${(new URLSearchParams({id: product.id, inc: -1})).toString()}'>-</a>
|
||||||
|
${product.quantity}
|
||||||
|
<a href='/showcart?${(new URLSearchParams({id: product.id, inc: 1})).toString()}'>+</a>
|
||||||
|
</td>
|
||||||
|
<td align='right'><a href='/showcart?remove=${product.id}'>Delete</a></td>
|
||||||
|
</tr>
|
||||||
|
`).join('\n')}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href='/checkout'>Checkout</a>
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
} else{
|
||||||
|
content = `
|
||||||
|
<p>
|
||||||
|
Your shopping cart is empty!
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
res.render('layouts/main', {
|
||||||
|
title: 'Your Shopping Cart',
|
||||||
|
loggedIn: true,
|
||||||
|
user: getUser(req),
|
||||||
|
spacer: true,
|
||||||
|
content: `
|
||||||
|
<h1>Your Shopping Cart</h1>
|
||||||
|
|
||||||
|
${content}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href='/listprod'>Continue Shopping</a>
|
||||||
|
</p>`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -0,0 +1,69 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const auth = require('../auth.js');
|
||||||
|
const sql = require('mssql');
|
||||||
|
|
||||||
|
router.post('/', async function(req, res) {
|
||||||
|
// Have to preserve async context since we make an async call
|
||||||
|
// to the database in the validateLogin function.
|
||||||
|
|
||||||
|
let authenticatedUser = await validateLogin(req);
|
||||||
|
console.log(authenticatedUser);
|
||||||
|
if (authenticatedUser === false) {
|
||||||
|
res.redirect("/login");
|
||||||
|
} else {
|
||||||
|
req.session.user = authenticatedUser;
|
||||||
|
res.redirect("/");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function validateLogin(req) {
|
||||||
|
if (!req.body || !req.body.username || !req.body.password) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let username = req.body.username;
|
||||||
|
let password = req.body.password;
|
||||||
|
let authenticatedUser = await (async function() {
|
||||||
|
let pool;
|
||||||
|
let stmt;
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(dbConfig);
|
||||||
|
|
||||||
|
// TO/DO: Check if userId and password match some customer account.
|
||||||
|
// If so, set authenticatedUser to be the username.
|
||||||
|
|
||||||
|
stmt = new sql.PreparedStatement(pool);
|
||||||
|
stmt.input('userid', sql.VarChar)
|
||||||
|
await stmt.prepare(`select password, userid, customerId from customer where userid = @userid`);
|
||||||
|
const queryResults = await stmt.execute({userid: req.body.username});
|
||||||
|
|
||||||
|
if (queryResults.recordset.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundUser = queryResults.recordset[0];
|
||||||
|
|
||||||
|
if (foundUser.password != req.body.password) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
username: foundUser.userid,
|
||||||
|
isAdmin: foundUser.customerId === 1,
|
||||||
|
id: foundUser.customerId,
|
||||||
|
};
|
||||||
|
} catch(err) {
|
||||||
|
console.dir(err);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (stmt) stmt.unprepare();
|
||||||
|
if (pool) pool.close();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return authenticatedUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
const express = require('express');
|
||||||
|
const exphbs = require('express-handlebars');
|
||||||
|
const session = require('express-session')
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const { getUser } = require('./util.js');
|
||||||
|
|
||||||
|
let index = require('./routes/index.js');
|
||||||
|
let loadData = require('./routes/loaddata.js');
|
||||||
|
let listOrder = require('./routes/listorder.js');
|
||||||
|
let listProd = require('./routes/listprod.js');
|
||||||
|
let addCart = require('./routes/addcart.js');
|
||||||
|
let showCart = require('./routes/showcart.js');
|
||||||
|
let checkout = require('./routes/checkout.js');
|
||||||
|
let order = require('./routes/order.js');
|
||||||
|
let login = require('./routes/login.js');
|
||||||
|
let register = require('./routes/register.js');
|
||||||
|
let validateLogin = require('./routes/validateLogin.js');
|
||||||
|
let logout = require('./routes/logout.js');
|
||||||
|
let admin = require('./routes/admin.js');
|
||||||
|
let product = require('./routes/product.js');
|
||||||
|
let displayImage = require('./routes/displayImage.js');
|
||||||
|
let customer = require('./routes/customer.js');
|
||||||
|
let ship = require('./routes/ship.js');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// Enable parsing of requests for POST requests
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(bodyParser.urlencoded({extended: true}));
|
||||||
|
|
||||||
|
// This DB Config is accessible globally
|
||||||
|
dbConfig = {
|
||||||
|
server: 'cosc304_sqlserver',
|
||||||
|
database: 'orders',
|
||||||
|
authentication: {
|
||||||
|
type: 'default',
|
||||||
|
options: {
|
||||||
|
userName: 'sa',
|
||||||
|
password: '304#sa#pw'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
encrypt: false,
|
||||||
|
enableArithAbort:false,
|
||||||
|
database: 'orders'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkUser = (req, res, next) => {
|
||||||
|
if (getUser(req) == null) {
|
||||||
|
// DEBUG
|
||||||
|
//req.session.user = { username: "arnold", isAdmin: true, id: 1 };
|
||||||
|
res.redirect('/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkAdmin = (req, res, next) => {
|
||||||
|
if (!getUser(req).isAdmin) {
|
||||||
|
res.send("404: Page not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setting up the session.
|
||||||
|
// This uses MemoryStorage which is not
|
||||||
|
// recommended for production use.
|
||||||
|
app.use(session({
|
||||||
|
secret: 'COSC 304 Rules!',
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: false,
|
||||||
|
cookie: {
|
||||||
|
httpOnly: false,
|
||||||
|
secure: false,
|
||||||
|
maxAge: 60000,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Setting up the rendering engine
|
||||||
|
app.engine('handlebars', exphbs());
|
||||||
|
app.set('view engine', 'handlebars');
|
||||||
|
|
||||||
|
// Setting up where static assets should
|
||||||
|
// be served from.
|
||||||
|
app.use(express.static('public'));
|
||||||
|
|
||||||
|
// Setting up Express.js routes.
|
||||||
|
// These present a "route" on the URL of the site.
|
||||||
|
// Eg: http://127.0.0.1/loaddata
|
||||||
|
app.use('/login', login);
|
||||||
|
app.use('/register', register);
|
||||||
|
app.use('/loaddata', loadData);
|
||||||
|
app.use('/validateLogin', validateLogin);
|
||||||
|
app.use('/displayImage', displayImage);
|
||||||
|
app.use('/ship', ship);
|
||||||
|
|
||||||
|
app.use(checkUser);
|
||||||
|
|
||||||
|
app.use('/', index);
|
||||||
|
app.use('/admin', checkAdmin, admin);
|
||||||
|
app.use('/listorder', listOrder);
|
||||||
|
app.use('/listprod', listProd);
|
||||||
|
app.use('/addcart', addCart);
|
||||||
|
app.use('/showcart', showCart);
|
||||||
|
app.use('/checkout', checkout);
|
||||||
|
app.use('/order', order);
|
||||||
|
app.use('/logout', logout);
|
||||||
|
app.use('/product', product);
|
||||||
|
app.use('/customer', customer);
|
||||||
|
|
||||||
|
// Starting our Express app
|
||||||
|
app.listen(3000)
|
|
@ -0,0 +1,3 @@
|
||||||
|
https://stackoverflow.com/questions/36067767/how-do-i-upload-a-file-with-the-js-fetch-api#40826943
|
||||||
|
https://stackoverflow.com/questions/15839169/how-to-get-the-value-of-a-selected-radio-button#15839451
|
||||||
|
https://www.chartjs.org/docs/latest/
|
|
@ -0,0 +1,36 @@
|
||||||
|
const sql = require('mssql');
|
||||||
|
|
||||||
|
module.exports.htmlPage = (body, title='Nat & Samira\'s Grocery Services') => `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang='en'>
|
||||||
|
<head>
|
||||||
|
<title>${title}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
${body}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
module.exports.getUser = (req) => {
|
||||||
|
return req.session.user;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.getCustomerRecord = async (pool, id) => {
|
||||||
|
let stmt;
|
||||||
|
try {
|
||||||
|
stmt = new sql.PreparedStatement(pool);
|
||||||
|
stmt.input('id', sql.Int);
|
||||||
|
await stmt.prepare(`
|
||||||
|
select * from customer where customerId = @id
|
||||||
|
`);
|
||||||
|
const results = await stmt.execute({id: id});
|
||||||
|
|
||||||
|
return results.recordset[0];
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (stmt) stmt.unprepare();
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,49 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{title}}</title>
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{#if loggedIn}}
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Nat & Samira's Rare Earth Metals
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/listprod">Products</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/listorder">Orders</a>
|
||||||
|
</li>
|
||||||
|
{{#if user.isAdmin}}
|
||||||
|
<li class="nav-right">
|
||||||
|
<a href="/admin">Admin Panel</a>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
<li class="nav-right">
|
||||||
|
<a href="/customer">{{{user.username}}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if spacer}}
|
||||||
|
<div style="margin-top: 10vh"> </div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{{{content}}}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<div style="margin:0 auto;text-align:center;display:inline">
|
||||||
|
|
||||||
|
<h3>Please Login to System</h3>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<form name="MyForm" method=post action="/validateLogin">
|
||||||
|
<table style="display:inline">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<font face="Arial, Helvetica, sans-serif" size="2">Username:</font>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="username" size=10 maxlength=10></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div align="right">
|
||||||
|
<font face="Arial, Helvetica, sans-serif" size="2">Password:</font>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="password" name="password" size=10 maxlength="10"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br />
|
||||||
|
<input class="submit" type="submit" name="Submit2" value="Log In">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|