Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
04635f9
adding controllers
Jan 13, 2020
3b3ca63
added error controller
Jan 14, 2020
73b9acf
adding product model
Jan 15, 2020
865cba4
storing data in a file via model
Jan 15, 2020
42d76e4
Fetching Data from Files Via the Model
Jan 15, 2020
603f66e
099 Refactoring the File Storage Code
Jan 15, 2020
f8926ed
103 Creating the Shop Structure
Jan 15, 2020
e689388
104 Working on the Navigation
Jan 15, 2020
e858084
105 Registering the Routes
Jan 15, 2020
0da04e7
106 Storing Product Data
Jan 15, 2020
8e381c4
comments added to existing code
Jan 15, 2020
7794e39
107 Displaying Product Data
Jan 15, 2020
16beb58
108 Editing Deleting Products
Jan 15, 2020
43708cc
109 Adding Another Item
Jan 15, 2020
b883241
adjustment made, ready for this module
Jan 15, 2020
86b1c0f
115 Extracting Dynamic Params
Jan 15, 2020
66c11fc
116 Loading Product Detail Data
Jan 15, 2020
8b718a7
117 Rendering the Product Detail View
Jan 15, 2020
bbcaff1
118 Passing Data with POST Requests
Jan 15, 2020
f17f710
119 Adding a Cart Model
Jan 16, 2020
44051ec
120 Using Query Params
Jan 16, 2020
7efdd0f
121 Pre-Populating the Edit Product Page with Data
Jan 16, 2020
2cb5f22
122 Linking to the Edit Page
Jan 16, 2020
da2a4c3
123 Editing the Product Data
Jan 16, 2020
1e2ec47
124 Adding the Product-Delete Functionality
Jan 16, 2020
8c6c876
125 Deleting Cart Items
Jan 17, 2020
59913c8
126 Displaying Cart Items on the Cart Page
Jan 17, 2020
19f1f18
127 Deleting Cart Items
Jan 17, 2020
0ab1ea4
128 Fixing a Delete Product Bug
Jan 17, 2020
3a894d8
136 Connecting our App to the SQL Database
Jan 18, 2020
30b15ff
137 Basic SQL Creating a Table
Jan 18, 2020
0a63447
138 Retrieving Data
Jan 18, 2020
6c059d5
140 Fetching Products - Time to Practice
Jan 19, 2020
6ee5c3b
141 Inserting Data Into the Database
Jan 19, 2020
2a6ca7f
142 Fetching a Single Product with the where Condition
Jan 19, 2020
4d3fc6f
147 Connecting to the Database
Jan 19, 2020
92218a0
148 Defining a Model
Jan 19, 2020
6fa0064
149 Syncing JS Definitions to the Database
Jan 19, 2020
f9a0181
150 Inserting Data Creating a Product
Jan 19, 2020
8603dc9
152 Retrieving Data Finding Products
Jan 19, 2020
6658644
153 Getting a Single Product with the where Condition
Jan 19, 2020
6aab013
154 Fetching Admin Products
Jan 19, 2020
d6c9043
155 Updating Products
Jan 19, 2020
1a43268
156 Deleting Products
Jan 19, 2020
b6232a2
157 Creating a User Model
Jan 19, 2020
d11f3fe
157 Creating a User Model
Jan 19, 2020
31caebf
158 Adding a One-To-Many Relationship
Jan 19, 2020
a4ae894
159 Creating Managing a Dummy User
Jan 19, 2020
5fb2fa5
159 Creating Managing a Dummy User
Jan 20, 2020
128fa8b
159 Creating Managing a Dummy User
Jan 20, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 71 additions & 14 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,87 @@
const path = require('path');
const path = require('path');

const express = require('express');
const bodyParser = require('body-parser');

const adminData = require('./routes/admin');
const adminRoutes = require('./routes/admin');
const shopRoutes = require('./routes/shop');
const rootDir = require('./util/path');
const errorController = require('./controller/error');
const sequelize = require('./util/database');

// requiring both the models
const Product = require('./model/product');
const User = require('./model/user');

const app = express();

// no need to register the engin, as exoress auto register it
app.set('view engine', 'ejs');
app.set('views', 'views');

app.use(bodyParser.urlencoded({extended : true}));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(path.join(rootDir, 'public')));

app.use('/admin',adminData.routes);
// registering the new middleware, because we want to store that user in our request so that we can use it from anywhere in our app
app.use((req, res, next) => {
// now we want to reachout to our database and retrive the user
// with the help of
// User.findByPk(1);

// IMPORTANT:
// you might think that will this ever return a user if are craeting down in sync() method
// app only registered the middleware function. so for incoming request, it will then execute this middleware function
// npm start will first run sequelize.sync() and then user ill get registed if it not there, then server runs
// once the server start running, request will be made and this function will get executed

User.findByPk(1)
.then((user) => {
// we want to storethe user in request
req.user = user;
// we can do that, we are just adding the user field to "req" object
// we just have to amke sure, we dont overwrite the exisiting user
// "req.user = user;" here user is undefined by default, so now we are storing user which we retrived from the database
// user which are reterving is not just a JS object with values stored in db and .Its a sequelize object with values stored in the database and with all the utility methods sequelize added like destroy.
// thus, we are storing sequelize object in request object, not just JS object
// hence whenver we user now, we can operate sequelize methods on it.

// we need to call "next()" so taht we can continue with the next step if we get our user and store it.
next();
})
.catch(err => console.log(err));
});

app.use('/admin', adminRoutes);
app.use(shopRoutes);

app.use((req, res, next) => {
res.status(404);
res.setHeader('Content-type','text/html');
// passing the argument to templating engine doesnt change, it remains the same
// "path: 'Error'" we passed becasue ejs engin will check "path" argument in naviagation bar logic of template. if we will not provide, we will get an error
res.render('404', {pageTitle: "404", path: 'Error'});
});

app.listen(3000);
app.use(errorController.get404);

Product.belongsTo(User, {constraints: true, onDelete: 'CASCADE'});
User.hasMany(Product);

// sequelize.sync({force: true})
sequelize.sync()
.then(result => {
// craeting a dummy user, which is logged in as sooon as server starts.
return User.findByPk(1)
})
.then( user => {
if(!user) {
// here are passing promise
return User.create({name: 'max', email: 'test@t.com'});
}
// but here only object will be returned.
// return user;
// we should always return the same so that next "then()" can be executed
// so, returning user promise
// return Promise.resolve(user);
// but no need of that as whatever is returned from "then()" is wrapped inside promise itself. hence
return user;
})
.then(user => {
console.log(user);
// hence, user is registered successfully. and we can start the server
app.listen(3000);
})
.catch(err => {
console.log('err', err);
});
99 changes: 99 additions & 0 deletions controller/admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const Product = require("../model/product");

exports.getAddProduct = (req, res, next) => {
res.render("admin/edit-product", {
path: "/admin/add-product",
pageTitle: "Add Products",
editing: false
});
};

exports.postAddProduct = (req, res, next) => {
const title = req.body.title;
const imageUrl = req.body.imageUrl;
const description = req.body.description;
const price = req.body.price;

Product.create({
title: title,
imageUrl: imageUrl,
price: price,
description: description
})
.then(result => {
// console.log(result);
console.log('product saved ');
res.redirect('/admin/products');
})
.catch(err => {
console.log(err);
});

};

exports.getEditProduct = (req, res, next) => {
const editMode = req.query.edit
if(!editMode) {
res.redirect('/');
}
const productID = req.params.productID;

Product.findByPk(productID)
.then(product => {
res.render("admin/edit-product", {
path: "/admin/edit-product",
pageTitle: "Edit Products",
editing: editMode,
product: product
});
})
.catch(err => console.log(err));
};

exports.postEditProduct = (req, res, next) => {
const productID = req.body.productID;
const updatedTitle = req.body.title;
const updatedImageUrl = req.body.imageUrl;
const updatedPrice = req.body.price;
const upadatedDesciption = req.body.description;

Product.findByPk(productID)
.then(product => {
product.title = updatedTitle;
product.imageUrl = updatedImageUrl;
product.price = updatedPrice;
product.description = upadatedDesciption;

product.save()
})
.then(() => {
res.redirect('/admin/products');
})
.catch(err => console.log(err));
};

exports.postDeleteProduct = (req, res, next) => {
const productID = req.body.productID;
Product.findByPk(productID)
.then(product => {
product.destroy()
})
.then(() => {
res.redirect('/admin/products');
})
.catch(err => console.log(err));
};

exports.getProducts = (req, res, next) => {
Product.findAll()
.then(products => {
res.render("admin/products", {
path: "/admin/products",
pageTitle: "Admin Add Products",
prods: products,
});
})
.catch(err => {
console.log(err);
});
};
5 changes: 5 additions & 0 deletions controller/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
exports.get404 = (req, res, next) => {
res.status(404);
res.setHeader('Content-type', 'text/html');
res.render('404', { pageTitle: "404", path: 'Error' });
};
108 changes: 108 additions & 0 deletions controller/shop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const Product = require('../model/product');
const Cart = require('../model/cart');

exports.getProduct = (req, res, next) => {
Product.findAll()
.then(products => {
res.render("shop/product-list", {
pageTitle: "Shop Products",
prods: products,
path: "/products"
});
})
.catch(err => console.log(err));
};

exports.getDetails = (req, res, next) => {
const productID = req.params.productId;

// Product.findByPk(productID)
// .then(product => {
// console.log(product);
// res.render(
// 'shop/product-detail',
// { pageTitle: product.title,
// product: product,
// path: '/products'
// }
// );
// })
// .catch(err => console.log(err));

// or

Product.findAll({where :
{id: productID}
})
.then(product => {
res.render(
'shop/product-detail',
{
pageTitle: product[0].title,
product: product[0],
path: '/products'
}
);
})
.catch(err => console.log(err));
};

exports.getCart = (req, res, next) => {
Cart.getCart(cart => {
Product.fetchAll(products => {
const cartProducts = [];
for(product of products) {
const CartproductData = cart.products.find(prod => prod.id === product.id)
if(CartproductData) {
cartProducts.push({productData: product, qty: CartproductData.qty});
}
}
res.render('shop/cart', {
pageTitle: 'Cart',
path: '/cart',
products: cartProducts
});
});
});
};

exports.postCart = (req, res, next) => {
const productID = req.body.productID
console.log(productID);
Product.findById(productID, product => {
Cart.addProduct(productID, product.price);
})
res.redirect('/');
}

exports.postCartDeleteProduct = (req, res, next) => {
const productID = req.body.productID;
Product.findById(productID, product => {
Cart.deleteProduct(productID, product.price);
res.redirect('/cart');
});
};

exports.getOrders = (req, res, next) => {
res.render('shop/orders', {
pageTitle: 'Orders',
path: '/orders'
});
};

exports.getIndex = (req, res, next) => {
Product.findAll()
.then(products => {
res.render("shop/index", {
pageTitle: "Shop",
prods: products,
path: "/"
});
})
.catch(err => console.log(err));
};





1 change: 1 addition & 0 deletions data/cart.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"products":[],"totalPrice":null}
1 change: 1 addition & 0 deletions data/products.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"id":"0.21217683817981658","title":"book","imageUrl":" data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBw8QDxIPDxAQEA8PDQ0PDw8PDw8PEBAPFREWFhUVFRUYHSggGBolHRUVITEhJSkrLi4uFx8zODMtNygtLisBCgoKDg0OGhAQGC8iHSUtLSsrLS0rLS0rMC4rLTAtLS0rLSstLS0tLS0rLS0tLzUtLS0tLSstLS0tLS0tLS0rK//AABEIALcBEwMBIgACEQEDEQH/xAAbAAACAgMBAAAAAAAAAAAAAAADBAACAQUGB//EAEkQAAIBAgIFBgoGCAYBBQAAAAECAAMRBCEFEjFBUQYTYXGBkQciMlKSobHB0fBCVHKT0uEWFyNDYoKj8RREU2Oy4lUVM0Vzg//EABkBAQEBAQEBAAAAAAAAAAAAAAABAgMEBf/EACgRAQACAQQBBAIBBQAAAAAAAAABAhEDEyFREjFBUmEUkQQiMkKBof/aAAwDAQACEQMRAD8A8vUQirIohVWBFWECyKsKqwMKsIqyyrCKsCqrCqksqQirAqqy4WXVYRUgUCS4SEVIQJAEElgkKElwkAISZCQ4SWCQF9SZ1IxqSakBbUk1IzqSakBXUmCkZ1JgpAVKShSNlJQpAUKShWNlIMpAUKyjLGmSDZYCrJBssaZYNlgKssEyxplg2WAqywTLGmWCZYC+rJCFZmBFWEVZFWFVYEVYVVmVWFVYGFWFVZlVhVWBhVhVSWRIVVgVVIRUl1SEVIFFSXCQqpCKkAQSXFOGCS4SAAU5YJGAksEgLakzqRnm5nm4CupMaka5uTm4ChSVKRwpKlICRpyhSOlINkgJMkoyRxkg2SAmywTJHGSCZICbLBsscZIFkgKMsEyxtlgmWAoywTLGmWCZYC1pIUrJAirCqswohkWBlFhVWRVhkWBFWGRZEWGVYGFWGVZlVhkSBVUhlSWVIZUgUVIRUnUaD0ErUi9UZ1B4o4Lxmv0loh6LcU3N8YyNWqQgpwqpCBIAAksKcOElwk","description":"1","price":"11"}]
72 changes: 72 additions & 0 deletions model/cart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const fs = require('fs');
const path = require('path');

const rootDir = require('../util/path');

const p = path.join(rootDir, 'data', 'cart.json');

module.exports = class Cart {
static addProduct(id, productPrice) {
// fetch the previous product
fs.readFile(p, (err, fileContent) => {
let cart = { products: [], totalPrice: 0 };
if (!err) {
cart = JSON.parse(fileContent);
}
const exisitingProductIndex = cart.products.findIndex(prod => prod.id === id);
const exisitingProduct = cart.products[exisitingProductIndex];
let updatedProduct;

// add new product / incraese the quantity
if (exisitingProduct) {
updatedProduct = { ...exisitingProduct };
updatedProduct.qty = updatedProduct.qty + 1;
cart.products = [...cart.products];
cart.products[exisitingProductIndex] = updatedProduct;
} else {
updatedProduct = { id: id, qty: 1 };
// updating the cart after adding new product to the cart
cart.products = [...cart.products, updatedProduct];
}
cart.totalPrice = cart.totalPrice + +productPrice;
fs.writeFile(p, JSON.stringify(cart), err => {
console.log(err);
});
});
}

static deleteProduct(id, productPrice) {
fs.readFile(p, (err, fileContent) => {
if (err) {
return;
}
const updatedProduct = { ...JSON.parse(fileContent) };
const product = updatedProduct.products.find(prods => prods.id === id);
// if we delete a product from admin page and that product is not inn the cart, then it will give us error.
// SOLUTION:- to delete, we first need to check if the given product is in cart, if it is not there then we simply need to return . we dont want to try to edit it as it is not there.
if(!product) {
return;
}
const productQty = product.qty;
updatedProduct.products = updatedProduct.products.filter(prods => prods.id !== id);
updatedProduct.totalPrice = updatedProduct - (productPrice * productQty);

fs.writeFile(p, JSON.stringify(updatedProduct), (err) => {
console.log(err);
});
});
}

static getCart(cb) {
fs.readFile(p, (err, fileContent) => {
const cart = JSON.parse(fileContent);
if(err) {
cb(null);
} else {
cb(cart);
}
});
}

}

Loading