├── .gitignore ├── .idea ├── .gitignore ├── fashion-cube.iml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo.jpg ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── screen ├── screen1.png ├── screen2.png ├── screen3.png ├── screen4.png ├── screen5.png └── screen6.png ├── server └── ecommerce │ ├── .gitignore │ ├── .idea │ ├── .gitignore │ ├── ecommerce.iml │ ├── misc.xml │ └── modules.xml │ ├── LICENSE │ ├── README.md │ ├── app.js │ ├── bin │ └── www │ ├── configs │ ├── jwt-config.js │ ├── mongo-config.js │ └── paypal-config.js │ ├── models │ ├── Cart.js │ ├── Category.js │ ├── Department.js │ ├── Product.js │ ├── User.js │ └── Variant.js │ ├── modules │ ├── Cart.js │ ├── ErrorHandler.js │ └── ensureAuthenticated.js │ ├── package-lock.json │ ├── package.json │ ├── public │ └── stylesheets │ │ └── style.css │ ├── routes │ ├── index.js │ └── users.js │ └── seed │ └── index.js └── src ├── App.js ├── App.test.js ├── ServerRequest └── index.js ├── assets ├── css │ ├── responsive.css │ └── style.css └── images │ ├── banner_1.jpg │ ├── banner_2.jpg │ ├── banner_3.jpg │ ├── deal_ofthe_week.png │ ├── emptyCart.png │ ├── empty_cart.png │ ├── logo.jpg │ ├── slider_1.jpg │ ├── slider_2.jpg │ └── slider_3.jpg ├── axios └── API.js ├── components ├── Advertisement │ └── index.js ├── Benefit │ └── index.js ├── CategoryBanner │ ├── CategoryBanner.js │ └── index.js ├── Footer │ └── index.js ├── Heading │ └── index.js ├── HomeBanner │ └── index.js ├── HomeCartView │ ├── index.js │ └── style.css ├── HorizontalFilter │ └── index.js ├── LoadingButton │ ├── index.js │ └── style.css ├── LoginRegisterModal │ ├── LoginForm.js │ ├── RegisterForm.js │ ├── index.js │ └── style.css ├── MobileMenu │ └── index.js ├── NavBar │ ├── NavBar.js │ └── NavBarContainer.js ├── Products │ ├── BestSeller.js │ ├── NewArrivals.js │ └── SingleProduct.js └── TopNavBar │ └── index.js ├── index.js ├── layouts └── BaseLayout.js ├── logo.svg ├── modules ├── Auth │ └── index.js ├── Navigation │ └── index.js └── mediaQuery │ └── index.js ├── redux ├── actions │ ├── DepartmentAction.js │ ├── LoginAction.js │ ├── RegisterAction.js │ ├── cartAction.js │ ├── productAction.js │ └── variantsAction.js ├── reducers │ ├── DepartmentReducer.js │ ├── LoginReducer.js │ ├── RegisterReducer.js │ ├── cartReducer.js │ ├── index.js │ ├── productReducer.js │ └── variantsReducer.js └── store │ └── index.js ├── routes ├── HomeRoutes.js ├── PrivateRoutes.js └── index.js ├── serviceWorker.js ├── setupTests.js ├── utils ├── CalculateTax.js ├── Validator │ ├── index.js │ └── rule │ │ └── index.js └── generateFilterString.js └── views ├── Cart ├── Cart.js ├── CartContainer.js └── CartItem.js ├── Category ├── Category.js ├── CategoryContainer.js └── components │ └── Filter.js ├── Checkout └── index.js ├── Home ├── Home.js └── HomeContainer.js ├── PageNotFound.js ├── Product ├── SingleProduct.js └── SingleProductContainer.js ├── ThankYou.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /.idea/fashion-cube.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fashion Cube-Ecommerce Application 2 | 3 | #General Info 4 | 5 | front-end: Reactjs, Redux, Axios, Sass, react-bootstrap 6 | 7 | [Live Demo](https://quintuslabs.github.io/) 8 | 9 | ## This project is created using: 10 | 11 | - [x] Monorepo 12 | - [x] Lerna 13 | - [x] Design System 14 | - [x] Hooks 15 | - [x] Redux 16 | 17 | ## Future Features 18 | 19 | - Home (List products) 20 | - Detail products 21 | - Product category 22 | - Recommended products 23 | - Cart 24 | - Login / Register 25 | 26 | # ScreenShot 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fashioncube", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": "https://quintuslabs.github.io/", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/quintuslabs/" 9 | }, 10 | "keywords": [ 11 | "fashion", 12 | "fashion-cube", 13 | "e-commerce", 14 | "shopping", 15 | "Shopping cart" 16 | ], 17 | "dependencies": { 18 | "@testing-library/jest-dom": "^4.2.4", 19 | "@testing-library/react": "^9.3.2", 20 | "@testing-library/user-event": "^7.1.2", 21 | "aos": "^3.0.0-beta.6", 22 | "axios": "^0.19.2", 23 | "bootstrap": "^4.4.1", 24 | "classnames": "^2.2.6", 25 | "node-sass": "^4.14.1", 26 | "prop-types": "^15.7.2", 27 | "react": "^16.13.1", 28 | "react-bootstrap": "^1.0.0-beta.17", 29 | "react-dom": "^16.13.1", 30 | "react-owl-carousel2": "^0.3.0", 31 | "react-redux": "^7.2.0", 32 | "react-responsive": "^8.0.3", 33 | "react-router-dom": "^5.1.2", 34 | "react-scripts": "3.4.0", 35 | "redux": "^4.0.5", 36 | "redux-thunk": "^2.3.0" 37 | }, 38 | "scripts": { 39 | "start": "react-scripts start", 40 | "build": "react-scripts build", 41 | "test": "react-scripts test", 42 | "eject": "react-scripts eject", 43 | "predeploy": "npm run build", 44 | "deploy": "gh-pages -d build" 45 | }, 46 | "eslintConfig": { 47 | "extends": "react-app" 48 | }, 49 | "browserslist": { 50 | "production": [ 51 | ">0.2%", 52 | "not dead", 53 | "not op_mini all" 54 | ], 55 | "development": [ 56 | "last 1 chrome version", 57 | "last 1 firefox version", 58 | "last 1 safari version" 59 | ] 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 21 | 22 | 31 | Fashion Cube 32 | 33 | 34 | 35 |
36 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /public/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/public/logo.jpg -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /screen/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/screen/screen1.png -------------------------------------------------------------------------------- /screen/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/screen/screen2.png -------------------------------------------------------------------------------- /screen/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/screen/screen3.png -------------------------------------------------------------------------------- /screen/screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/screen/screen4.png -------------------------------------------------------------------------------- /screen/screen5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/screen/screen5.png -------------------------------------------------------------------------------- /screen/screen6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/screen/screen6.png -------------------------------------------------------------------------------- /server/ecommerce/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /server/ecommerce/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /server/ecommerce/.idea/ecommerce.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /server/ecommerce/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /server/ecommerce/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /server/ecommerce/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Zack Yin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /server/ecommerce/README.md: -------------------------------------------------------------------------------- 1 | # Full stack ecommerce online store application 2 | 3 | 4 | 5 | 6 | #### populate data: run `node ./seed` (make sure you fill up `mongo-config` file with mongodb url) 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /server/ecommerce/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var path = require('path'); 3 | var cookieParser = require('cookie-parser'); 4 | var bodyParser = require('body-parser') 5 | var logger = require('morgan'); 6 | var express = require('express'); 7 | var mongoose = require('mongoose'); 8 | var cors = require('cors') 9 | var expressValidator = require('express-validator');//req.checkbody() 10 | const mongoConfig = require('./configs/mongo-config') 11 | var indexRouter = require('./routes/index'); 12 | var usersRouter = require('./routes/users'); 13 | 14 | mongoose.connect(mongoConfig, { useNewUrlParser: true, useCreateIndex: true, },function(error){ 15 | if(error) throw error 16 | console.log(`connect mongodb success`); 17 | }); 18 | 19 | var app = express() 20 | app.use(cors()) 21 | 22 | // Express validator 23 | app.use(expressValidator({ 24 | errorFormatter: function(param, msg, value) { 25 | var namespace = param.split('.'), 26 | root = namespace.shift(), 27 | formParam = root; 28 | 29 | while(namespace.lenght) { 30 | formParam += '[' + namespace.shift() + ']'; 31 | } 32 | return { 33 | param : formParam, 34 | msg : msg, 35 | value : value 36 | }; 37 | } 38 | })); 39 | 40 | // view engine setup 41 | app.set('views', path.join(__dirname, 'views')); 42 | app.set('view engine', 'jade'); 43 | app.use(logger('dev')); 44 | app.use(bodyParser.json()); 45 | app.use(bodyParser.urlencoded({ extended: false })); 46 | app.use(cookieParser()); 47 | 48 | //set static dir 49 | app.use(express.static(path.join(__dirname, 'public'))); 50 | 51 | //routers 52 | app.use('/', indexRouter); 53 | app.use('/users', usersRouter); 54 | 55 | // catch 404 and forward to error handler 56 | app.use(function(req, res, next) { 57 | next(createError(404)); 58 | }); 59 | 60 | // error handler 61 | app.use(function(err, req, res, next) { 62 | // console.log(err); 63 | res.status(err.status || 500).json(err); 64 | }); 65 | 66 | module.exports = app; 67 | -------------------------------------------------------------------------------- /server/ecommerce/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('myapp:server'); 9 | var http = require('http'); 10 | var PORT = process.argv[2]&&process.argv[2].slice(-4) 11 | 12 | /** 13 | * Get port from environment and store in Express. 14 | */ 15 | 16 | var port = normalizePort(process.env.PORT || PORT || '5000'); 17 | app.set('port', port); 18 | 19 | /** 20 | * Create HTTP server. 21 | */ 22 | 23 | var server = http.createServer(app); 24 | 25 | /** 26 | * Listen on provided port, on all network interfaces. 27 | */ 28 | 29 | server.listen(port); 30 | server.on('error', onError); 31 | server.on('listening', onListening); 32 | 33 | /** 34 | * Normalize a port into a number, string, or false. 35 | */ 36 | 37 | function normalizePort(val) { 38 | var port = parseInt(val, 10); 39 | 40 | if (isNaN(port)) { 41 | // named pipe 42 | return val; 43 | } 44 | 45 | if (port >= 0) { 46 | // port number 47 | return port; 48 | } 49 | 50 | return false; 51 | } 52 | 53 | /** 54 | * Event listener for HTTP server "error" event. 55 | */ 56 | 57 | function onError(error) { 58 | if (error.syscall !== 'listen') { 59 | throw error; 60 | } 61 | 62 | var bind = typeof port === 'string' 63 | ? 'Pipe ' + port 64 | : 'Port ' + port; 65 | 66 | // handle specific listen errors with friendly messages 67 | switch (error.code) { 68 | case 'EACCES': 69 | console.error(bind + ' requires elevated privileges'); 70 | process.exit(1); 71 | break; 72 | case 'EADDRINUSE': 73 | console.error(bind + ' is already in use'); 74 | process.exit(1); 75 | break; 76 | default: 77 | throw error; 78 | } 79 | } 80 | 81 | /** 82 | * Event listener for HTTP server "listening" event. 83 | */ 84 | 85 | function onListening() { 86 | var addr = server.address(); 87 | var bind = typeof addr === 'string' 88 | ? 'pipe ' + addr 89 | : 'port ' + addr.port; 90 | console.log(`server listening on ${addr.port}`); 91 | 92 | debug('Listening on ' + bind); 93 | } 94 | -------------------------------------------------------------------------------- /server/ecommerce/configs/jwt-config.js: -------------------------------------------------------------------------------- 1 | //use your secret 2 | module.exports={ 3 | secret:"FashionCubeSecret" 4 | } 5 | -------------------------------------------------------------------------------- /server/ecommerce/configs/mongo-config.js: -------------------------------------------------------------------------------- 1 | // export mongodb url 2 | const MONGO_USERNAME = 'username'; 3 | const MONGO_PASSWORD = 'your_password'; 4 | const MONGO_HOSTNAME = '127.0.0.1'; 5 | const MONGO_PORT = '27017'; 6 | const MONGO_DB = 'fashion-cube'; 7 | module.exports = "mongodb://127.0.0.1:27017/fashion-cube" 8 | -------------------------------------------------------------------------------- /server/ecommerce/configs/paypal-config.js: -------------------------------------------------------------------------------- 1 | // export paypal sandbox configs like 'mode', 'client_id', 'client_secret' 2 | -------------------------------------------------------------------------------- /server/ecommerce/models/Cart.js: -------------------------------------------------------------------------------- 1 | 2 | const mongoose = require('mongoose') 3 | 4 | CartSchema = mongoose.Schema({ 5 | items: { 6 | type: Object 7 | }, 8 | totalQty: { 9 | type: Number 10 | }, 11 | totalPrice: { 12 | type: Number 13 | }, 14 | userId: { 15 | type: String 16 | } 17 | }) 18 | 19 | var Cart = module.exports = mongoose.model('Cart', CartSchema) 20 | 21 | module.exports.getCartByUserId = function (uid, callback) { 22 | let query = { userId: uid } 23 | Cart.find(query, callback) 24 | } 25 | 26 | module.exports.getCartById = function (id, callback) { 27 | Cart.findById(id, callback) 28 | } 29 | 30 | module.exports.updateCartByUserId = function (userId, newCart, callback) { 31 | let query = { userId: userId } 32 | Cart.find(query, function (err, c) { 33 | if (err) throw err 34 | 35 | //exist cart in databse 36 | if (c.length > 0) { 37 | Cart.findOneAndUpdate( 38 | { userId: userId }, 39 | { 40 | $set: { 41 | items: newCart.items, 42 | totalQty: newCart.totalQty, 43 | totalPrice: newCart.totalPrice, 44 | userId: userId 45 | } 46 | }, 47 | { new: true }, 48 | callback 49 | ) 50 | } else { 51 | //no cart in database 52 | newCart.save(callback) 53 | } 54 | }) 55 | } 56 | 57 | module.exports.updateCartByCartId = function (cartId, newCart, callback) { 58 | Cart.findById( 59 | { _id: cartId }, 60 | { 61 | $set: newCart 62 | }, 63 | callback 64 | ) 65 | } 66 | 67 | 68 | 69 | module.exports.createCart = function (newCart, callback) { 70 | newCart.save(callback) 71 | } -------------------------------------------------------------------------------- /server/ecommerce/models/Category.js: -------------------------------------------------------------------------------- 1 | // Object modelling for category. This model will represent in the database and 2 | // we will read the all the information according to this model. 3 | // You can think that this is a representation of the database and we are using that 4 | // for saving, reading, updating information from the database. 5 | 6 | var mongoose = require('mongoose'); 7 | 8 | var categorySchema = mongoose.Schema({ 9 | categoryName: { 10 | type: String, 11 | index : true 12 | } 13 | }); 14 | 15 | var Category = module.exports = mongoose.model('Categories', categorySchema); 16 | 17 | // These are functions to get data from the database. You can even reach the information 18 | // without calling this functions but I just want to show you how you can add some functions 19 | // to your model file to get specific data. 20 | 21 | module.exports.getAllCategories = function(callback){ 22 | Category.find(callback) 23 | } 24 | 25 | module.exports.getCategoryById = function(id, callback){ 26 | Category.findById(id, callback); 27 | } -------------------------------------------------------------------------------- /server/ecommerce/models/Department.js: -------------------------------------------------------------------------------- 1 | // Object modelling for department. This model will represent in the database and 2 | // we will read the all the information according to this model. 3 | // You can think that this is a representation of the database and we are using that 4 | // for saving, reading, updating information from the database. 5 | 6 | var mongoose = require('mongoose'); 7 | 8 | var departmentSchema = mongoose.Schema({ 9 | departmentName: { 10 | type: String, 11 | index: true 12 | }, 13 | categories: { 14 | type: String 15 | } 16 | }); 17 | 18 | var Department = module.exports = mongoose.model('Department', departmentSchema); 19 | 20 | // These are functions to get data from the database. You can even reach the information 21 | // without calling this functions but I just want to show you how you can add some functions 22 | // to your model file to get specific data. 23 | 24 | module.exports.getAllDepartments = function (query, callback) { 25 | Department.find(query, callback) 26 | } 27 | module.exports.getDepartmentById = function (id, callback) { 28 | Department.findById(id, callback); 29 | } -------------------------------------------------------------------------------- /server/ecommerce/models/Product.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var productSchema = mongoose.Schema({ 4 | imagePath: { 5 | type: String 6 | }, 7 | title: { 8 | type: String 9 | }, 10 | description: { 11 | type: String 12 | }, 13 | department: { 14 | type: String 15 | }, 16 | category: { 17 | type: String 18 | }, 19 | price: { 20 | type: Number 21 | }, 22 | color: { 23 | type: String 24 | }, 25 | size: { 26 | type: String 27 | }, 28 | quantity: { 29 | type: Number 30 | }, 31 | date: { 32 | type: Number 33 | } 34 | }); 35 | 36 | var Product = module.exports = mongoose.model('Product', productSchema); 37 | 38 | module.exports.getAllProducts = function (query, sort, callback) { 39 | Product.find(query, null, sort, callback) 40 | } 41 | 42 | module.exports.getProductByDepartment = function (query,sort, callback) { 43 | Product.find(query, null, sort, callback) 44 | } 45 | 46 | module.exports.getProductByCategory = function (query,sort, callback) { 47 | Product.find(query, null, sort, callback) 48 | } 49 | 50 | module.exports.getProductByTitle = function (query,sort, callback) { 51 | Product.find(query, null, sort, callback) 52 | } 53 | 54 | module.exports.filterProductByDepartment = function (department, callback) { 55 | let regexp = new RegExp(`${department}`, 'i') 56 | var query = { department: { $regex: regexp } }; 57 | Product.find(query, callback) 58 | } 59 | 60 | module.exports.filterProductByCategory = function (category, callback) { 61 | let regexp = new RegExp(`${category}`, 'i') 62 | var query = { category: { $regex: regexp } }; 63 | Product.find(query, callback); 64 | } 65 | 66 | module.exports.filterProductByTitle = function (title, callback) { 67 | let regexp = new RegExp(`${title}`, 'i') 68 | var query = { title: { $regex: regexp } }; 69 | Product.find(query, callback); 70 | } 71 | 72 | module.exports.getProductByID = function (id, callback) { 73 | Product.findById(id, callback); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /server/ecommerce/models/User.js: -------------------------------------------------------------------------------- 1 | 2 | var mongoose = require('mongoose'); 3 | var bcrypt = require('bcryptjs'); 4 | 5 | var userSchema = mongoose.Schema({ 6 | email: { 7 | type: String, 8 | index: true 9 | }, 10 | password: { 11 | type: String 12 | }, 13 | fullname: { 14 | type: String 15 | }, 16 | admin: { 17 | type: String 18 | }, 19 | cart: { 20 | type: Object 21 | } 22 | }); 23 | 24 | var User = module.exports = mongoose.model('User', userSchema); 25 | 26 | module.exports.createUser = function (newUser, callback) { 27 | bcrypt.genSalt(10, function (err, salt) { 28 | bcrypt.hash(newUser.password, salt, function (err, hash) { 29 | newUser.password = hash; 30 | newUser.save(callback); 31 | }); 32 | }); 33 | } 34 | 35 | module.exports.getUserByEmail = function (email, callback) { 36 | var query = { email: email }; 37 | User.findOne(query, callback); 38 | } 39 | 40 | 41 | module.exports.getUserById = function (id, callback) { 42 | User.findById(id, callback); 43 | } 44 | module.exports.comparePassword = function (givenPassword, hash, callback) { 45 | bcrypt.compare(givenPassword, hash, function (err, isMatch) { 46 | if (err) throw err; 47 | callback(null, isMatch); 48 | }); 49 | } 50 | 51 | module.exports.getAllUsers = function (callback) { 52 | User.find(callback) 53 | } -------------------------------------------------------------------------------- /server/ecommerce/models/Variant.js: -------------------------------------------------------------------------------- 1 | 2 | var mongoose = require('mongoose'); 3 | 4 | var variantSchema = mongoose.Schema({ 5 | productID: { 6 | type: String 7 | }, 8 | imagePath: { 9 | type: String 10 | }, 11 | color: { 12 | type: String 13 | }, 14 | size: { 15 | type: String 16 | }, 17 | quantity: { 18 | type: Number 19 | }, 20 | title: { 21 | type: String 22 | }, 23 | price: { 24 | type: Number 25 | } 26 | }); 27 | 28 | var Variant = module.exports = mongoose.model('Variant', variantSchema); 29 | 30 | module.exports.getVariantByID = function(id, callback){ 31 | Variant.findById(id, callback); 32 | } 33 | 34 | module.exports.getVariantProductByID = function(id, callback){ 35 | var query = {productID: id}; 36 | Variant.find(query, callback); 37 | } 38 | module.exports.getAllVariants = function(callback){ 39 | Variant.find(callback) 40 | } -------------------------------------------------------------------------------- /server/ecommerce/modules/Cart.js: -------------------------------------------------------------------------------- 1 | const cartModel = require('../models/Cart') 2 | 3 | class Cart { 4 | constructor(oldCart) { 5 | this.items = oldCart.items || {}; 6 | this.totalQty = oldCart.totalQty || 0; 7 | this.totalPrice = oldCart.totalPrice || 0; 8 | this.userId = oldCart.userId || ""; 9 | } 10 | 11 | add(item, id) { 12 | let storedItem = this.items[id]; 13 | if (!storedItem) { 14 | storedItem = this.items[id] = { item: item, qty: 0, price: 0 }; 15 | } 16 | storedItem.qty++; 17 | storedItem.price = parseFloat((storedItem.item.price * storedItem.qty).toFixed(2)); 18 | this.items[id]=storedItem 19 | this.totalQty++; 20 | this.totalPrice += storedItem.item.price; 21 | this.totalPrice = parseFloat(this.totalPrice.toFixed(2)) 22 | return this 23 | } 24 | 25 | generateModel(){ 26 | let newCart = new cartModel({ 27 | items: this.items, 28 | totalQty: this.totalQty, 29 | totalPrice: this.totalPrice, 30 | userId: this.userId 31 | }) 32 | return newCart 33 | } 34 | 35 | decreaseQty(id) { 36 | this.items[id].qty--; 37 | this.items[id].price -= this.items[id].item.price; 38 | this.items[id].price = parseFloat(this.items[id].price.toFixed(2)) 39 | this.totalQty--; 40 | this.totalPrice -= this.items[id].item.price 41 | this.totalPrice = parseFloat(this.totalPrice.toFixed(2)) 42 | if (this.items[id].qty <= 0) { 43 | delete this.items[id]; 44 | } 45 | return this 46 | } 47 | 48 | increaseQty(id) { 49 | this.items[id].qty++; 50 | this.items[id].price += this.items[id].item.price; 51 | this.items[id].price = parseFloat(this.items[id].price.toFixed(2)) 52 | this.totalQty++; 53 | this.totalPrice += this.items[id].item.price 54 | this.totalPrice = parseFloat(this.totalPrice.toFixed(2)) 55 | return this 56 | } 57 | 58 | generateArray() { 59 | let arr = []; 60 | for (let id in this.items) { 61 | arr.push(this.items[id]) 62 | } 63 | return arr; 64 | } 65 | } 66 | 67 | module.exports = Cart -------------------------------------------------------------------------------- /server/ecommerce/modules/ErrorHandler.js: -------------------------------------------------------------------------------- 1 | 2 | class TypedError extends Error{ 3 | constructor(args,status,type,error){ 4 | super(args) 5 | this.status=status 6 | this.type=type 7 | this.error = error 8 | } 9 | } 10 | module.exports=TypedError -------------------------------------------------------------------------------- /server/ecommerce/modules/ensureAuthenticated.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken') 2 | const config = require('../configs/jwt-config') 3 | const TypedError = require('./ErrorHandler') 4 | function ensureAuthenticated(req, res, next) { 5 | let token = '' 6 | if (req.headers['x-access-token'] || req.headers['authorization']) { 7 | token = req.headers['x-access-token'] || req.headers['authorization'] 8 | } 9 | //OAuth 2.0 framework 'bearer' token type 10 | if (token.startsWith('Bearer ')) { 11 | token = token.slice(7, token.length) 12 | } 13 | if (token) { 14 | jwt.verify(token, config.secret, (err, decoded) => { 15 | if (err) { 16 | let err = new TypedError('token', 401, 'invalid_field', { 17 | message: "Token is not valid" 18 | }) 19 | return next(err) 20 | } else { 21 | //bind on request 22 | next() 23 | } 24 | }) 25 | } else { 26 | let err = new TypedError('token', 401, 'invalid_field', { 27 | message: "Token is not supplied" 28 | }) 29 | return next(err) 30 | } 31 | }; 32 | 33 | module.exports = ensureAuthenticated -------------------------------------------------------------------------------- /server/ecommerce/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecommerce-nodejs", 3 | "version": "1.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "bcryptjs": "^2.4.3", 10 | "body-parser": "^1.18.3", 11 | "colour": "^0.7.1", 12 | "connect-flash": "^0.1.1", 13 | "connect-mongo": "^2.0.3", 14 | "cookie-parser": "^1.4.4", 15 | "cors": "^2.8.5", 16 | "debug": "^2.6.9", 17 | "express": "^4.16.4", 18 | "express-handlebars": "^3.0.2", 19 | "express-messages": "^1.0.1", 20 | "express-session": "^1.15.6", 21 | "express-validator": "^5.3.1", 22 | "handlebars.moment": "^1.0.4", 23 | "jade": "^1.11.0", 24 | "jsonwebtoken": "^8.5.1", 25 | "mongodb": "^3.1.13", 26 | "mongoose": "^5.4.19", 27 | "morgan": "^1.9.1", 28 | "multer": "^1.4.1", 29 | "nodemailer": "^4.7.0", 30 | "nodemon": "^1.18.11", 31 | "passport": "^0.4.0", 32 | "passport-local": "^1.0.0", 33 | "paypal-rest-sdk": "^1.8.1", 34 | "serve-favicon": "^2.4.5", 35 | "voucher-code-generator": "^1.1.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /server/ecommerce/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /server/ecommerce/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | const ensureAuthenticated = require('../modules/ensureAuthenticated') 4 | const Product = require('../models/Product') 5 | const Variant = require('../models/Variant') 6 | const Department = require('../models/Department') 7 | const Category = require('../models/Category') 8 | const TypedError = require('../modules/ErrorHandler') 9 | const Cart = require('../models/Cart'); 10 | const CartClass = require('../modules/Cart') 11 | const paypal_config = require('../configs/paypal-config') 12 | const paypal = require('paypal-rest-sdk') 13 | 14 | 15 | //GET /products 16 | router.get('/products', function (req, res, next) { 17 | const { query, order } = categorizeQueryString(req.query) 18 | Product.getAllProducts(query, order, function (e, products) { 19 | if (e) { 20 | e.status = 406; return next(e); 21 | } 22 | if (products.length < 1) { 23 | return res.status(404).json({ message: "products not found" }) 24 | } 25 | res.json({ products: products }) 26 | }) 27 | }); 28 | 29 | //GET /products/:id 30 | router.get('/products/:id', function (req, res, next) { 31 | let productId = req.params.id; 32 | Product.getProductByID(productId, function (e, item) { 33 | if (e) { 34 | e.status = 404; return next(e); 35 | } 36 | else { 37 | res.json({ product: item }) 38 | } 39 | }); 40 | }); 41 | 42 | //GET /variants 43 | router.get('/variants', function (req, res, next) { 44 | let { productId } = req.query 45 | if (productId) { 46 | Variant.getVariantProductByID(productId, function (err, variants) { 47 | if (err) return next(err) 48 | return res.json({ variants }) 49 | }) 50 | } else { 51 | Variant.getAllVariants(function (e, variants) { 52 | if (e) { 53 | if (err) return next(err) 54 | } 55 | else { 56 | return res.json({ variants }) 57 | } 58 | }) 59 | } 60 | }) 61 | 62 | //GET /variants/:id 63 | router.get('/variants/:id', ensureAuthenticated, function (req, res, next) { 64 | let id = req.params.id 65 | if (id) { 66 | Variant.getVariantByID(id, function (err, variants) { 67 | if (err) return next(err) 68 | res.json({ variants }) 69 | }) 70 | } 71 | }) 72 | 73 | //GET /departments 74 | router.get('/departments', function (req, res, next) { 75 | Department.getAllDepartments(req.query, function (err, d) { 76 | if (err) return next(err) 77 | res.status(200).json({ departments: d }) 78 | }) 79 | }) 80 | 81 | //GET /categories 82 | router.get('/categories', function (req, res, next) { 83 | Category.getAllCategories(function (err, c) { 84 | if (err) return next(err) 85 | res.json({ categories: c }) 86 | }) 87 | }) 88 | 89 | //GET /search? 90 | router.get('/search', function (req, res, next) { 91 | const { query, order } = categorizeQueryString(req.query) 92 | query['department'] = query['query'] 93 | delete query['query'] 94 | Product.getProductByDepartment(query, order, function (err, p) { 95 | if (err) return next(err) 96 | if (p.length > 0) { 97 | return res.json({ products: p }) 98 | } else { 99 | query['category'] = query['department'] 100 | delete query['department'] 101 | Product.getProductByCategory(query, order, function (err, p) { 102 | if (err) return next(err) 103 | if (p.length > 0) { 104 | return res.json({ products: p }) 105 | } else { 106 | query['title'] = query['category'] 107 | delete query['category'] 108 | Product.getProductByTitle(query, order, function (err, p) { 109 | if (err) return next(err) 110 | if (p.length > 0) { 111 | return res.json({ products: p }) 112 | } else { 113 | query['id'] = query['title'] 114 | delete query['title'] 115 | Product.getProductByID(query.id, function (err, p) { 116 | let error = new TypedError('search', 404, 'not_found', { message: "no product exist" }) 117 | if (err) { 118 | return next(error) 119 | } 120 | if (p) { 121 | return res.json({ products: p }) 122 | } else { 123 | return next(error) 124 | } 125 | }) 126 | } 127 | }) 128 | } 129 | }) 130 | } 131 | }) 132 | }) 133 | 134 | // GET filter 135 | router.get('/filter', function (req, res, next) { 136 | let result = {} 137 | let query = req.query.query 138 | Product.filterProductByDepartment(query, function (err, p) { 139 | if (err) return next(err) 140 | if (p.length > 0) { 141 | result['department'] = generateFilterResultArray(p, 'department') 142 | } 143 | Product.filterProductByCategory(query, function (err, p) { 144 | if (err) return next(err) 145 | if (p.length > 0) { 146 | result['category'] = generateFilterResultArray(p, 'category') 147 | } 148 | Product.filterProductByTitle(query, function (err, p) { 149 | if (err) return next(err) 150 | if (p.length > 0) { 151 | result['title'] = generateFilterResultArray(p, 'title') 152 | } 153 | if (Object.keys(result).length > 0) { 154 | return res.json({ filter: result }) 155 | } else { 156 | let error = new TypedError('search', 404, 'not_found', { message: "no product exist" }) 157 | return next(error) 158 | } 159 | }) 160 | }) 161 | }) 162 | }) 163 | 164 | //GET /checkout 165 | router.get('/checkout/:cartId', ensureAuthenticated, function (req, res, next) { 166 | const cartId = req.params.cartId 167 | const frontURL = 'https://zack-ecommerce-reactjs.herokuapp.com' 168 | // const frontURL = 'http://localhost:3000' 169 | 170 | Cart.getCartById(cartId, function (err, c) { 171 | if (err) return next(err) 172 | if (!c) { 173 | let err = new TypedError('/checkout', 400, 'invalid_field', { message: 'cart not found' }) 174 | return next(err) 175 | } 176 | const items_arr = new CartClass(c).generateArray() 177 | const paypal_list = [] 178 | for (const i of items_arr) { 179 | paypal_list.push({ 180 | "name": i.item.title, 181 | "price": i.item.price, 182 | "currency": "CAD", 183 | "quantity": i.qty 184 | }) 185 | } 186 | const create_payment_json = { 187 | "intent": "sale", 188 | "payer": { 189 | "payment_method": "paypal" 190 | }, 191 | "redirect_urls": { 192 | "return_url": frontURL + '/success_page', 193 | "cancel_url": frontURL + '/cancel_page' 194 | }, 195 | "transactions": [{ 196 | "item_list": { 197 | "items": paypal_list 198 | }, 199 | "amount": { 200 | "currency": "CAD", 201 | "total": c.totalPrice 202 | }, 203 | "description": "This is the payment description." 204 | }] 205 | } 206 | paypal.configure(paypal_config); 207 | paypal.payment.create(create_payment_json, function (error, payment) { 208 | if (error) { 209 | console.log(JSON.stringify(error)); 210 | return next(error) 211 | } else { 212 | console.log(payment); 213 | for (const link of payment.links) { 214 | if (link.rel === 'approval_url') { 215 | res.json(link.href) 216 | } 217 | } 218 | } 219 | }); 220 | }) 221 | }) 222 | 223 | //GET /payment/success 224 | router.get('/payment/success', ensureAuthenticated, function (req, res, next) { 225 | var paymentId = req.query.paymentId; 226 | var payerId = { payer_id: req.query.PayerID }; 227 | paypal.payment.execute(paymentId, payerId, function (error, payment) { 228 | if (error) { 229 | console.error(JSON.stringify(error)); 230 | return next(error) 231 | } else { 232 | if (payment.state == 'approved') { 233 | console.log('payment completed successfully'); 234 | console.log(payment); 235 | res.json({ payment }) 236 | } else { 237 | console.log('payment not successful'); 238 | } 239 | } 240 | }) 241 | }) 242 | 243 | function generateFilterResultArray(products, targetProp) { 244 | let result_set = new Set() 245 | for (const p of products) { 246 | result_set.add(p[targetProp]) 247 | } 248 | return Array.from(result_set) 249 | } 250 | 251 | function categorizeQueryString(queryObj) { 252 | let query = {} 253 | let order = {} 254 | //extract query, order, filter value 255 | for (const i in queryObj) { 256 | if (queryObj[i]) { 257 | // extract order 258 | if (i === 'order') { 259 | order['sort'] = queryObj[i] 260 | continue 261 | } 262 | // extract range 263 | if (i === 'range') { 264 | let range_arr = [] 265 | let query_arr = [] 266 | // multi ranges 267 | if (queryObj[i].constructor === Array) { 268 | for (const r of queryObj[i]) { 269 | range_arr = r.split('-') 270 | query_arr.push({ 271 | price: { $gt: range_arr[0], $lt: range_arr[1] } 272 | }) 273 | } 274 | } 275 | // one range 276 | if (queryObj[i].constructor === String) { 277 | range_arr = queryObj[i].split('-') 278 | query_arr.push({ 279 | price: { $gt: range_arr[0], $lt: range_arr[1] } 280 | }) 281 | } 282 | Object.assign(query, { $or: query_arr }) 283 | delete query[i] 284 | continue 285 | } 286 | query[i] = queryObj[i] 287 | } 288 | } 289 | return { query, order } 290 | } 291 | 292 | module.exports = router; 293 | -------------------------------------------------------------------------------- /server/ecommerce/routes/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const jwt = require('jsonwebtoken') 4 | const config = require('../configs/jwt-config') 5 | const ensureAuthenticated = require('../modules/ensureAuthenticated') 6 | const User = require('../models/User'); 7 | const Cart = require('../models/Cart'); 8 | const CartClass = require('../modules/Cart') 9 | const Product = require('../models/Product') 10 | const Variant = require('../models/Variant') 11 | const TypedError = require('../modules/ErrorHandler') 12 | 13 | 14 | //POST /signin 15 | router.post('/signin', function (req, res, next) { 16 | const { fullname, email, password, verifyPassword } = req.body 17 | req.checkBody('fullname', 'fullname is required').notEmpty(); 18 | req.checkBody('email', 'Email is required').notEmpty(); 19 | req.checkBody('password', 'Password is required').notEmpty(); 20 | req.checkBody('verifyPassword', 'verifyPassword is required').notEmpty(); 21 | let missingFieldErrors = req.validationErrors(); 22 | if (missingFieldErrors) { 23 | let err = new TypedError('signin error', 400, 'missing_field', { 24 | errors: missingFieldErrors, 25 | }) 26 | return next(err) 27 | } 28 | req.checkBody('email', 'Email is not valid').isEmail(); 29 | req.checkBody('password', 'Passwords have to match').equals(req.body.verifyPassword); 30 | let invalidFieldErrors = req.validationErrors() 31 | if (invalidFieldErrors) { 32 | let err = new TypedError('signin error', 400, 'invalid_field', { 33 | errors: invalidFieldErrors, 34 | }) 35 | return next(err) 36 | } 37 | var newUser = new User({ 38 | fullname: fullname, 39 | password: password, 40 | email: email 41 | }); 42 | User.getUserByEmail(email, function (error, user) { 43 | if (error) return next(err) 44 | if (user) { 45 | let err = new TypedError('signin error', 409, 'invalid_field', { 46 | message: "user is existed" 47 | }) 48 | return next(err) 49 | } 50 | User.createUser(newUser, function (err, user) { 51 | if (err) return next(err); 52 | res.json({ message: 'user created' }) 53 | }); 54 | }) 55 | }); 56 | 57 | //POST /login 58 | router.post('/login', function (req, res, next) { 59 | const { email, password } = req.body.credential || {} 60 | if (!email || !password) { 61 | let err = new TypedError('login error', 400, 'missing_field', { message: "missing username or password" }) 62 | return next(err) 63 | } 64 | User.getUserByEmail(email, function (err, user) { 65 | if (err) return next(err) 66 | if (!user) { 67 | let err = new TypedError('login error', 403, 'invalid_field', { message: "Incorrect email or password" }) 68 | return next(err) 69 | } 70 | User.comparePassword(password, user.password, function (err, isMatch) { 71 | if (err) return next(err) 72 | if (isMatch) { 73 | let token = jwt.sign( 74 | { email: email }, 75 | config.secret, 76 | { expiresIn: '7d' } 77 | ) 78 | res.status(201).json({ 79 | user_token: { 80 | user_id: user.id, 81 | user_name: user.fullname, 82 | token: token, 83 | expire_in: '7d' 84 | } 85 | }) 86 | } else { 87 | let err = new TypedError('login error', 403, 'invalid_field', { message: "Incorrect email or password" }) 88 | return next(err) 89 | } 90 | }) 91 | }) 92 | }) 93 | 94 | //GET cart 95 | router.get('/:userId/cart', ensureAuthenticated, function (req, res, next) { 96 | let userId = req.params.userId 97 | Cart.getCartByUserId(userId, function (err, cart) { 98 | if (err) return next(err) 99 | if (cart.length < 1) { 100 | let err = new TypedError('cart error', 404, 'not_found', { message: "create a cart first" }) 101 | return next(err) 102 | } 103 | return res.json({ cart: cart[0] }) 104 | }) 105 | }) 106 | 107 | //POST cart 108 | router.post('/:userId/cart', ensureAuthenticated, function (req, res, next) { 109 | let userId = req.params.userId 110 | let { productId, increase, decrease } = req.body 111 | 112 | Cart.getCartByUserId(userId, function (err, c) { 113 | if (err) return next(err) 114 | let oldCart = new CartClass(c[0] || { userId }) 115 | // no cart save empty cart to database then return response 116 | if (c.length < 1 && !productId) { 117 | return Cart.createCart(oldCart.generateModel(), function (err, resultCart) { 118 | if (err) return next(err) 119 | return res.status(201).json({ cart: resultCart }) 120 | }) 121 | } 122 | Product.findById(productId, function (e, product) { 123 | if (e) { 124 | e.status = 406; 125 | return next(e); 126 | } 127 | if (product) { 128 | if (decrease) { 129 | oldCart.decreaseQty(product.id); 130 | } else if (increase) { 131 | oldCart.increaseQty(product.id); 132 | } else { 133 | oldCart.add(product, product.id); 134 | } 135 | let newCart = oldCart.generateModel() 136 | Cart.updateCartByUserId( 137 | userId, 138 | newCart, 139 | function (err, result) { 140 | if (err) return next(err) 141 | return res.status(200).json({ cart: result }) 142 | }) 143 | } else { 144 | // apply variant 145 | Variant.getVariantByID(productId, function (e, variant) { 146 | if (e) { 147 | e.status = 406; 148 | return next(e); 149 | } 150 | if (variant) { 151 | Product.getProductByID(variant.productID, function (e, p) { 152 | let color = (variant.color) ? "- " + variant.color : ""; 153 | let size = (variant.size) ? "- " + variant.size : ""; 154 | variant.title = p.title + " " + color + size 155 | variant.price = p.price 156 | if (decrease) { 157 | oldCart.decreaseQty(variant.id); 158 | } else if (increase) { 159 | oldCart.increaseQty(variant.id); 160 | } else { 161 | oldCart.add(variant, variant.id); 162 | } 163 | let newCart = oldCart.generateModel() 164 | Cart.updateCartByUserId( 165 | userId, 166 | newCart, 167 | function (err, result) { 168 | if (err) return next(err) 169 | res.status(200).json({ cart: result }) 170 | }) 171 | }) 172 | } 173 | // no product and no variant find 174 | else { 175 | let err = new TypedError('/cart', 400, 'invalid_field', { 176 | message: "invalid request body" 177 | }) 178 | return next(err) 179 | } 180 | }) 181 | } 182 | }) 183 | }) 184 | }) 185 | 186 | //PUT cart 187 | router.put('/:userId/cart', ensureAuthenticated, function (req, res, next) { 188 | let userId = req.params.userId 189 | let requestProduct = req.body 190 | let { productId, color, size } = requestProduct.product 191 | 192 | Cart.getCartByUserId(userId, function (err, c) { 193 | if (err) return next(err) 194 | let oldCart = new CartClass(c[0] || {}) 195 | Product.getProductByID(productId, function (err, p) { 196 | if (err) return next(err) 197 | let newCart = oldCart.add(p, productId, { color, size }) 198 | 199 | //exist cart in databse 200 | if (c.length > 0) { 201 | Cart.updateCartByUserId( 202 | userId, 203 | { 204 | items: newCart.items, 205 | totalQty: newCart.totalQty, 206 | totalPrice: newCart.totalPrice, 207 | userId: userId 208 | }, 209 | function (err, result) { 210 | if (err) return next(err) 211 | res.json(result) 212 | }) 213 | } else { 214 | //no cart in database 215 | newCart = new Cart({ 216 | items: newCart.items, 217 | totalQty: newCart.totalQty, 218 | totalPrice: newCart.totalPrice, 219 | userId: userId 220 | }) 221 | Cart.createCart(newCart, function (err, resultCart) { 222 | if (err) return next(err) 223 | res.status(201).json(resultCart) 224 | }) 225 | } 226 | }) 227 | }) 228 | }) 229 | 230 | module.exports = router; -------------------------------------------------------------------------------- /server/ecommerce/seed/index.js: -------------------------------------------------------------------------------- 1 | var User = require('../models/User'); 2 | var Category = require('../models/Category'); 3 | var Department = require('../models/Department'); 4 | var Product = require('../models/Product'); 5 | var Variant = require('../models/Variant'); 6 | var mongoose = require('mongoose'); 7 | const mongoConfig = require('../configs/mongo-config') 8 | mongoose.connect(mongoConfig, { useNewUrlParser: true, useCreateIndex: true, }); 9 | 10 | 11 | var categories = 12 | [ 13 | new Category({ 14 | categoryName: 'Basics' 15 | }), 16 | new Category({ 17 | categoryName: 'Blazer' 18 | }), 19 | new Category({ 20 | categoryName: 'Knitwear' 21 | }), 22 | new Category({ 23 | categoryName: 'Jeans' 24 | }), 25 | new Category({ 26 | categoryName: 'Jackets' 27 | }), 28 | new Category({ 29 | categoryName: 'Girl' 30 | }), 31 | new Category({ 32 | categoryName: 'Boy' 33 | }) 34 | ] 35 | 36 | for (let i = 0; i < categories.length; i++) { 37 | categories[i].save(function (e, r) { 38 | if (i === categories.length - 1) { 39 | exit(); 40 | } 41 | }); 42 | } 43 | 44 | var departments = 45 | [ 46 | new Department({ 47 | departmentName: 'Women', 48 | categories: 'Basics,Blazer' 49 | 50 | }), 51 | new Department({ 52 | departmentName: 'Men', 53 | categories: 'Knitwear,Jeans,Jackets' 54 | }), 55 | new Department({ 56 | departmentName: 'Kids', 57 | categories: 'Girl,Boy' 58 | }) 59 | ] 60 | 61 | for (let i = 0; i < departments.length; i++) { 62 | departments[i].save(function (e, r) { 63 | if (i === departments.length - 1) { 64 | exit(); 65 | } 66 | }); 67 | } 68 | 69 | var products = 70 | [ 71 | new Product({ 72 | _id: "5bedf31cc14d7822b39d9d43", 73 | imagePath: 'https://static.zara.net/photos///2018/I/0/1/p/7568/644/802/2/w/1920/7568644802_1_1_1.jpg?ts=1541152091085', 74 | title: 'Oversized Textured Top', 75 | description: 'High collar top with short cuffed sleeves. Asymmetric hem with side slits.', 76 | price: 35.95, 77 | color: 'Gray', 78 | size: 'XS,S,M', 79 | quantity: 10, 80 | department: 'Women', 81 | category: 'Basics', 82 | date: 1581397200000 83 | }), 84 | new Product({ 85 | _id: "5bedf3b9c14d7822b39d9d45", 86 | imagePath: 'https://static.zara.net/photos///2018/I/0/1/p/5644/641/800/2/w/1920/5644641800_2_5_1.jpg?ts=1540395699528', 87 | title: 'Tank Top', 88 | description: 'Flowy V-neck camisole with spaghetti straps.', 89 | price: 29.99, 90 | color: 'Black', 91 | size: 'XS,S,XL', 92 | quantity: 15, 93 | department: 'Women', 94 | category: 'Basics', 95 | date: 1597885814264 96 | }), 97 | new Product({ 98 | _id: "5bedf448c14d7822b39d9d47", 99 | imagePath: 'https://static.zara.net/photos///2018/I/0/1/p/7568/469/251/2/w/1920/7568469251_2_1_1.jpg?ts=1540393989160', 100 | title: 'Basic Top', 101 | description: 'Round neck long sleeved shirt. ', 102 | price: 25.99, 103 | color: 'White', 104 | size: 'XS', 105 | quantity: 90, 106 | department: 'Women', 107 | category: 'Basics', 108 | date: 1581397200000 109 | }), 110 | new Product({ 111 | _id: "5bedf55bc14d7822b39d9d4b", 112 | imagePath: 'https://static.zara.net/photos///2018/I/0/1/p/8197/757/093/4/w/1920/8197757093_2_2_1.jpg?ts=1538393944729', 113 | title: 'Belted Plaid Blazer', 114 | description: 'Flowy blazer with lapel collar and long sleeves. Self belt. Chest patch pockets and welt pockets at hip. Front double-breasted button closure.', 115 | price: 79.99, 116 | color: 'Black', 117 | size: 'S,M,L', 118 | quantity: 4, 119 | department: 'Women', 120 | category: 'Blazer', 121 | date: 1581397200000 122 | }), 123 | new Product({ 124 | _id: "5bedf5eec14d7822b39d9d4e", 125 | imagePath: 'https://static.zara.net/photos///2018/I/0/2/p/1775/300/615/2/w/1920/1775300615_1_1_1.jpg?ts=1539690384503', 126 | title: 'Perl Knit Swear', 127 | description: 'Purl-stitch knit sweater in a combination of textures. Ribbed trim.', 128 | price: 79.99, 129 | color: 'Orange', 130 | size: 'M,L', 131 | quantity: 5, 132 | department: 'Men', 133 | category: 'Knitwear', 134 | date: 1597885814264 135 | }), 136 | new Product({ 137 | _id: "5bedf6b5c14d7822b39d9d51", 138 | imagePath: 'https://static.zara.net/photos///2018/I/0/2/p/6186/352/407/2/w/1920/6186352407_2_1_1.jpg?ts=1540990017618', 139 | title: 'Ripped Jeans', 140 | description: 'Slim fit jeans with five pockets, washed effect, and rips on the legs. Zippered hem at in-seams. Front zip and metal button closure.', 141 | price: 79.99, 142 | color: 'Dark Blue', 143 | size: 'M,L', 144 | quantity: 80, 145 | department: 'Men', 146 | category: 'Jeans', 147 | date: 1597885814264 148 | }), 149 | new Product({ 150 | _id: "5bedf720c14d7822b39d9d52", 151 | imagePath: 'https://static.zara.net/photos///2018/I/0/2/p/5575/380/406/2/c-158-0-2048-3072/w/1920/5575380406_1_1_1.jpg?ts=1527530663760', 152 | title: 'Basic Slim Jeans', 153 | description: 'Basic slim-fit jeans with five pockets. Two side pockets, two back pockets, and one coin pocket. Belt loops. Front hidden zipper and button closure.', 154 | price: 45.99, 155 | color: 'Light Blue', 156 | size: 'XS,S,M', 157 | quantity: 8, 158 | department: 'Men', 159 | category: 'Jeans', 160 | date: 1581397200000 161 | }), 162 | new Product({ 163 | _id: "5bedf7ecc14d7822b39d9d55", 164 | imagePath: 'https://static.zara.net/photos///2018/I/0/2/p/3548/350/700/2/c-192-0-2048-3072/w/1920/3548350700_2_1_1.jpg?ts=1528819649601', 165 | title: 'Faux Leather Perforated Jacket', 166 | description: 'Faux leather perforated jacket with high collar and long sleeves. Two front zip pockets. Lined. Interior pocket. Front zip closure. Ribbed elastic hem and cuffs.', 167 | price: 99.99, 168 | color: 'Brown', 169 | size: 'XS,M,XL', 170 | quantity: 12, 171 | department: 'Men', 172 | category: 'Jackets', 173 | date: 1581397200000 174 | }), 175 | new Product({ 176 | _id: "5f3efb0d034bec1b28de7238", 177 | imagePath: 'https://static.zara.net/photos///2020/I/0/3/p/0257/700/703/102/w/560/0257700703_2_10_1.jpg?ts=1596028794347', 178 | title: 'SWEATSHIRT WITH TEXT', 179 | description: 'Round neck sweatshirt with long sleeves. Front printed text.', 180 | price: 16.90, 181 | color: 'OYSTER WHITE', 182 | size: 'XS,M', 183 | quantity: 23, 184 | department: 'Kids', 185 | category: 'Girl', 186 | date: 1597962874736 187 | }), 188 | new Product({ 189 | // _id: "5f3efb0d034bec1b28de7238", 190 | imagePath: 'https://static.zara.net/photos///2020/I/0/3/p/3183/700/800/2/w/560/3183700800_1_1_1.jpg?ts=1597336424462', 191 | title: 'PADDED BOMBER', 192 | description: 'Quilted bomber jacket with round neck and long sleeves. Front hidden zip and snap button closure. Front pockets.', 193 | price: 45.90, 194 | color: 'BLACK ', 195 | size: 'XS', 196 | quantity: 23, 197 | department: 'Kids', 198 | category: 'Boy', 199 | date: 1597962874736 200 | }), 201 | ]; 202 | 203 | for (let i = 0; i < products.length; i++) { 204 | products[i].save(function (e, r) { 205 | if (i === products.length - 1) { 206 | exit(); 207 | } 208 | }); 209 | } 210 | 211 | var variants = 212 | [ 213 | new Variant({ 214 | productID: '5bedf31cc14d7822b39d9d43', 215 | imagePath: 'https://static.zara.net/photos///2018/I/0/1/p/7568/644/710/2/w/1920/7568644710_1_1_1.jpg?ts=1541151891840', 216 | color: 'Beige', 217 | size: 'S,L', 218 | quantity: 5, 219 | }), 220 | new Variant({ 221 | productID: '5bedf3b9c14d7822b39d9d45', 222 | imagePath: 'https://static.zara.net/photos///2018/I/0/1/p/5644/641/735/2/w/1920/5644641735_2_5_1.jpg?ts=1540395590656', 223 | color: 'Copper', 224 | size: 'S,L,XL', 225 | quantity: 12, 226 | }), 227 | new Variant({ 228 | productID: '5bedf448c14d7822b39d9d47', 229 | imagePath: 'https://static.zara.net/photos///2018/I/0/1/p/7568/469/605/2/w/1920/7568469605_2_1_1.jpg?ts=1540394095062', 230 | color: 'Maroon', 231 | size: 'XS,M,L', 232 | quantity: 4, 233 | }), 234 | new Variant({ 235 | productID: '5bedf448c14d7822b39d9d47', 236 | imagePath: 'https://static.zara.net/photos///2018/I/0/1/p/7568/469/822/2/w/1920/7568469822_2_1_1.jpg?ts=1540394193241', 237 | color: 'Charcool', 238 | size: 'XS,L,XL', 239 | quantity: 5, 240 | }), 241 | new Variant({ 242 | productID: '5bedf5eec14d7822b39d9d4e', 243 | imagePath: 'https://static.zara.net/photos///2018/I/0/2/p/1775/300/806/2/w/1920/1775300806_2_1_1.jpg?ts=1539690394197', 244 | color: 'Stone', 245 | size: 'S,XL', 246 | quantity: 35, 247 | }), 248 | new Variant({ 249 | productID: '5bedf720c14d7822b39d9d52', 250 | imagePath: 'https://static.zara.net/photos///2018/I/0/2/p/5575/380/407/2/c-269-0-2048-3072/w/1920/5575380407_1_1_1.jpg?ts=1527602989905', 251 | color: 'Dark Blue', 252 | size: 'M,XL', 253 | quantity: 5, 254 | }), 255 | new Variant({ 256 | productID: '5f3efb0d034bec1b28de7238', 257 | imagePath: 'https://static.zara.net/photos///2020/I/0/3/p/0257/700/800/102/w/560/0257700800_2_10_1.jpg?ts=1596028794110', 258 | color: 'BLACK', 259 | size: 'M,XL', 260 | quantity: 5, 261 | }) 262 | 263 | ]; 264 | 265 | for (let i = 0; i < variants.length; i++) { 266 | variants[i].save(function (e, r) { 267 | if (i === variants.length - 1) { 268 | exit(); 269 | } 270 | }); 271 | } 272 | 273 | var newUser = new User({ 274 | username: 'admin@admin.com', 275 | password: 'admin', 276 | fullname: 'Cuneyt Celebican', 277 | admin: true 278 | }); 279 | User.createUser(newUser, function (err, user) { 280 | if (err) throw err; 281 | console.log(user); 282 | }); 283 | 284 | function exit() { 285 | mongoose.disconnect(); 286 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import "./assets/css/style.css"; 9 | import "./assets/css/responsive.css"; 10 | import AOS from "aos"; 11 | import "aos/dist/aos.css"; 12 | import Routes from "./routes"; 13 | class App extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = {}; 17 | } 18 | componentDidMount() { 19 | AOS.init(); 20 | } 21 | render() { 22 | return ( 23 |
24 | 25 |
26 | ); 27 | } 28 | } 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/ServerRequest/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import API from "../axios/API"; 8 | import Auth from "../modules/Auth"; 9 | 10 | export const login = async (email, password) => { 11 | const body = { 12 | credential: { 13 | email: email, 14 | password: password 15 | } 16 | }; 17 | return await API({ 18 | method: "POST", 19 | url: "/users/login", 20 | data: body 21 | }).then(res => { 22 | Auth.setUserToken(res.data.user_token); 23 | return res; 24 | }); 25 | }; 26 | export const register = async (fullname, email, password, verifyPassword) => { 27 | return await API({ 28 | method: "POST", 29 | url: "/users/signin", 30 | data: { 31 | fullname, 32 | email, 33 | password, 34 | verifyPassword 35 | } 36 | }).then(res => { 37 | // Auth.setUserToken(res.data.user_token); 38 | console.log(res); 39 | return res; 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /src/assets/images/banner_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/banner_1.jpg -------------------------------------------------------------------------------- /src/assets/images/banner_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/banner_2.jpg -------------------------------------------------------------------------------- /src/assets/images/banner_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/banner_3.jpg -------------------------------------------------------------------------------- /src/assets/images/deal_ofthe_week.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/deal_ofthe_week.png -------------------------------------------------------------------------------- /src/assets/images/emptyCart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/emptyCart.png -------------------------------------------------------------------------------- /src/assets/images/empty_cart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/empty_cart.png -------------------------------------------------------------------------------- /src/assets/images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/logo.jpg -------------------------------------------------------------------------------- /src/assets/images/slider_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/slider_1.jpg -------------------------------------------------------------------------------- /src/assets/images/slider_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/slider_2.jpg -------------------------------------------------------------------------------- /src/assets/images/slider_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/assets/images/slider_3.jpg -------------------------------------------------------------------------------- /src/axios/API.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import Auth from "../modules/Auth"; 8 | import jumpTo from "../modules/Navigation"; 9 | import axios from "axios"; 10 | //const URL = "http://192.168.0.19:3000"; 11 | const URL = "http://192.168.1.100:5000"; 12 | 13 | const API = (config) => { 14 | //header authorization 15 | if (Auth.user_token) { 16 | const token = Auth.getToken(); 17 | config.headers = { 18 | authorization: token, 19 | }; 20 | } 21 | //interceptors handle network error 22 | axios.interceptors.response.use( 23 | (response) => { 24 | return response; 25 | }, 26 | function (error) { 27 | if (!error.response) { 28 | error.response = { 29 | data: "net work error", 30 | status: 500, 31 | }; 32 | } 33 | if (error.response.status === 401) { 34 | Auth.logout(); 35 | // jumpTo("/login"); 36 | throw error; 37 | } 38 | return Promise.reject(error); 39 | } 40 | ); 41 | config.baseURL = URL; 42 | return axios(config); 43 | }; 44 | export default API; 45 | -------------------------------------------------------------------------------- /src/components/Advertisement/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import PropTypes from "prop-types"; 9 | 10 | import DEALOFWEEK from "../../assets/images/deal_ofthe_week.png"; 11 | class Advertisement extends Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | days: 0, 16 | hours: 0, 17 | min: 0, 18 | sec: 0 19 | }; 20 | } 21 | 22 | componentDidMount() { 23 | // update every second 24 | this.interval = setInterval(() => { 25 | const date = this.calculateCountdown(this.props.date); 26 | date ? this.setState(date) : this.stop(); 27 | }, 1000); 28 | } 29 | 30 | componentWillUnmount() { 31 | this.stop(); 32 | } 33 | 34 | calculateCountdown(endDate) { 35 | let diff = (Date.parse(new Date(endDate)) - Date.parse(new Date())) / 1000; 36 | 37 | // clear countdown when date is reached 38 | if (diff <= 0) return false; 39 | 40 | const timeLeft = { 41 | years: 0, 42 | days: 0, 43 | hours: 0, 44 | min: 0, 45 | sec: 0, 46 | millisec: 0 47 | }; 48 | 49 | // calculate time difference between now and expected date 50 | if (diff >= 365.25 * 86400) { 51 | // 365.25 * 24 * 60 * 60 52 | timeLeft.years = Math.floor(diff / (365.25 * 86400)); 53 | diff -= timeLeft.years * 365.25 * 86400; 54 | } 55 | if (diff >= 86400) { 56 | // 24 * 60 * 60 57 | timeLeft.days = Math.floor(diff / 86400); 58 | diff -= timeLeft.days * 86400; 59 | } 60 | if (diff >= 3600) { 61 | // 60 * 60 62 | timeLeft.hours = Math.floor(diff / 3600); 63 | diff -= timeLeft.hours * 3600; 64 | } 65 | if (diff >= 60) { 66 | timeLeft.min = Math.floor(diff / 60); 67 | diff -= timeLeft.min * 60; 68 | } 69 | timeLeft.sec = diff; 70 | 71 | return timeLeft; 72 | } 73 | 74 | stop() { 75 | clearInterval(this.interval); 76 | } 77 | 78 | addLeadingZeros(value) { 79 | value = String(value); 80 | while (value.length < 2) { 81 | value = "0" + value; 82 | } 83 | return value; 84 | } 85 | 86 | render() { 87 | const countDown = this.state; 88 | return ( 89 |
90 |
91 |
92 |
93 |
94 | 95 |
96 |
97 |
98 |
99 |
100 |

Deal Of The Week

101 |
102 |
    103 |
  • 104 |
    105 | {this.addLeadingZeros(countDown.days)}{" "} 106 |
    107 |
    108 | {countDown.days === 1 ? "Day" : "Days"} 109 |
    110 |
  • 111 |
  • 112 |
    113 | {this.addLeadingZeros(countDown.hours)} 114 |
    115 |
    Hours
    116 |
  • 117 |
  • 118 |
    119 | {this.addLeadingZeros(countDown.min)} 120 |
    121 |
    Mins
    122 |
  • 123 |
  • 124 |
    125 | {this.addLeadingZeros(countDown.sec)} 126 |
    127 |
    Sec
    128 |
  • 129 |
130 |
131 | shop now 132 |
133 |
134 |
135 |
136 |
137 |
138 | ); 139 | } 140 | } 141 | 142 | Advertisement.propTypes = { 143 | date: PropTypes.string.isRequired 144 | }; 145 | 146 | Advertisement.defaultProps = { 147 | date: new Date(new Date().getTime() + 5 * 24 * 60 * 60 * 1000).toString() 148 | }; 149 | 150 | export default Advertisement; 151 | -------------------------------------------------------------------------------- /src/components/Benefit/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React from "react"; 8 | 9 | function Benefit(params) { 10 | return ( 11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
free shipping
21 |

Suffered Alteration in Some Form

22 |
23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 |
31 |
cach on delivery
32 |

The Internet Tend To Repeat

33 |
34 |
35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 |
45 days return
43 |

Making it Look Like Readable

44 |
45 |
46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 |
opening all week
54 |

8AM - 09PM

55 |
56 |
57 |
58 |
59 |
60 |
61 | ); 62 | } 63 | 64 | export default Benefit; 65 | -------------------------------------------------------------------------------- /src/components/CategoryBanner/CategoryBanner.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React from "react"; 8 | import Banner1 from "../../assets/images/banner_1.jpg"; 9 | import Banner2 from "../../assets/images/banner_2.jpg"; 10 | import Banner3 from "../../assets/images/banner_3.jpg"; 11 | 12 | function CategoryBanner(props) { 13 | return ( 14 |
15 |
16 |
17 |
18 |
25 |
26 | women's 27 |
28 |
29 |
30 |
31 |
38 |
39 | accessories's 40 |
41 |
42 |
43 |
44 |
51 |
52 | men's 53 |
54 |
55 |
56 |
57 |
58 |
59 | ); 60 | } 61 | 62 | export default CategoryBanner; 63 | -------------------------------------------------------------------------------- /src/components/CategoryBanner/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/components/CategoryBanner/index.js -------------------------------------------------------------------------------- /src/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React from "react"; 8 | 9 | function Footer(props) { 10 | return ( 11 | 72 | ); 73 | } 74 | 75 | export default Footer; 76 | -------------------------------------------------------------------------------- /src/components/Heading/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React from "react"; 8 | 9 | function Heading(props) { 10 | return ( 11 |
12 |
13 |

{props.title}

14 |
15 |
16 | ); 17 | } 18 | 19 | export default Heading; 20 | -------------------------------------------------------------------------------- /src/components/HomeBanner/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React from "react"; 8 | 9 | import BackgroundImage1 from "../../assets/images/slider_1.jpg"; 10 | import BackgroundImage2 from "../../assets/images/slider_2.jpg"; 11 | import BackgroundImage3 from "../../assets/images/slider_3.jpg"; 12 | import { Carousel } from "react-bootstrap"; 13 | 14 | function HomeBanner(props) { 15 | return ( 16 | 17 | 18 |
24 |
25 |
26 |
27 |
28 |
Spring / Summer Collection 2017
29 |

Get up to 30% Off New Arrivals

30 |
31 | shop now 32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 40 |
46 |
47 |
48 |
49 |
50 |
Spring / Summer Collection 2017
51 |

Get up to 30% Off New Arrivals

52 |
53 | shop now 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 62 |
68 |
69 |
70 |
71 |
72 |
Spring / Summer Collection 2017
73 |

Get up to 30% Off New Arrivals

74 |
75 | shop now 76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | ); 85 | } 86 | 87 | export default HomeBanner; 88 | -------------------------------------------------------------------------------- /src/components/HomeCartView/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import { Modal, Button } from "react-bootstrap"; 9 | import "./style.css"; 10 | import Auth from "../../modules/Auth"; 11 | import EmptyCart from "../../assets/images/emptyCart.png"; 12 | import jumpTo from "../../modules/Navigation"; 13 | class HomeCartView extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = {}; 17 | } 18 | 19 | goToChechout = () => { 20 | jumpTo("/cart"); 21 | }; 22 | render() { 23 | const { items, totalPrice } = this.props.cart; 24 | console.log(totalPrice); 25 | 26 | return ( 27 | 28 | 29 | Your Cart 30 | {items !== undefined && items !== null ? ( 31 | this.goToChechout()}> 32 | checkout{" "} 33 | 34 | ) : null} 35 | 36 | 37 | {Auth.getUserDetails() !== undefined && 38 | Auth.getUserDetails() !== null && 39 | Auth.getToken() !== undefined ? ( 40 |
41 | {items !== undefined && items !== null ? null : ( 42 |
43 | 44 |

Empty cart

45 |
46 | )} 47 |
48 | ) : ( 49 |
50 |

Please login to view cart

51 | 52 |
53 | )} 54 | 55 | {items !== undefined && 56 | items !== null && 57 | Object.keys(items).map((id) => { 58 | return ( 59 |
60 |
61 | 62 |
63 |
64 |
65 | {items[id].item.title} 66 |
67 |
68 | Quantity: {items[id].qty} 69 |
70 |
71 | {" "} 72 | Price: ₹{items[id].price} 73 |
74 |
75 |
76 | ); 77 | })} 78 | {items !== undefined && items !== null && ( 79 |
80 |

81 | Total: ₹{totalPrice}{" "} 82 |

83 | 90 |
91 | )} 92 |
93 |
94 | ); 95 | } 96 | } 97 | 98 | export default HomeCartView; 99 | -------------------------------------------------------------------------------- /src/components/HomeCartView/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | .fade.right.modal .modal-dialog { 8 | position: fixed; 9 | margin: auto; 10 | width: 320px; 11 | height: 100%; 12 | right: 0; 13 | -webkit-transform: translate3d(0%, 0, 0); 14 | -ms-transform: translate3d(0%, 0, 0); 15 | -o-transform: translate3d(0%, 0, 0); 16 | transform: translate3d(0%, 0, 0); 17 | } 18 | 19 | .fade.right.modal .modal-content { 20 | height: 100%; 21 | overflow-y: auto; 22 | } 23 | 24 | .fade.right.modal .modal-body { 25 | padding: 15px 15px 80px; 26 | } 27 | 28 | /*Right*/ 29 | .fade.right.modal { 30 | right: -320px; 31 | -webkit-transition: opacity 0.3s linear, right 0.3s ease-out; 32 | -moz-transition: opacity 0.3s linear, right 0.3s ease-out; 33 | -o-transition: opacity 0.3s linear, right 0.3s ease-out; 34 | transition: opacity 0.3s linear, right 0.3s ease-out; 35 | } 36 | 37 | .fade.right.modal.show { 38 | right: 0; 39 | } 40 | 41 | /* ----- MODAL STYLE ----- */ 42 | .fade.right.modal .modal-content { 43 | border-radius: 0; 44 | border: none; 45 | } 46 | 47 | .shopping--cart--container { 48 | margin-top: 25vh; 49 | box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.1); 50 | transition: 0.3s; 51 | border-radius: 10px; 52 | padding-left: 30px; 53 | padding-right: 30px; 54 | padding-bottom: 30px; 55 | } 56 | 57 | .shopping--cart--item { 58 | margin-top: 20px; 59 | margin-bottom: 20px; 60 | padding-bottom: 20px; 61 | border-bottom: 1px solid #eeeeee; 62 | } 63 | 64 | .cart--item--img { 65 | text-align: center; 66 | } 67 | .cart--item--img > img { 68 | max-height: 150px; 69 | max-width: 150px; 70 | } 71 | -------------------------------------------------------------------------------- /src/components/HorizontalFilter/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React from "react"; 8 | 9 | function HorizontalFilter(Props) { 10 | return ( 11 |
12 |
13 |
14 |
    15 |
  • 19 | all 20 |
  • 21 |
  • 25 | women's 26 |
  • 27 |
  • 31 | accessories 32 |
  • 33 |
  • 37 | men's 38 |
  • 39 |
40 |
41 |
42 |
43 | ); 44 | } 45 | 46 | export default HorizontalFilter; 47 | -------------------------------------------------------------------------------- /src/components/LoadingButton/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import PropTypes from "prop-types"; 9 | import classNames from "classnames"; 10 | import "./style.css"; 11 | function LoadingButton(props) { 12 | return ( 13 | 28 | ); 29 | } 30 | 31 | LoadingButton.propTypes = { 32 | className: PropTypes.string, 33 | loading: PropTypes.bool, 34 | onClick: PropTypes.func, 35 | type: PropTypes.string 36 | }; 37 | 38 | LoadingButton.defaultProps = { 39 | className: "btn-primary btn-7", 40 | loading: false, 41 | type: "button" 42 | }; 43 | 44 | export default LoadingButton; 45 | -------------------------------------------------------------------------------- /src/components/LoadingButton/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | .btn-wide { 8 | padding-left: 10px; 9 | padding-right: 10px; 10 | width: auto; 11 | } 12 | .btn-wait { 13 | position: relative; 14 | padding-right: 48px; 15 | background-repeat: no-repeat; 16 | background-position: calc(100% - 12px) 6px; 17 | pointer-events: none; 18 | } 19 | .btn-wide.btn-wait { 20 | padding-left: 12px; 21 | } 22 | .disabled { 23 | pointer-events: none; 24 | } 25 | .btn-wait:after, 26 | .btn-wait:before { 27 | content: ""; 28 | position: absolute; 29 | width: 24px; 30 | height: 24px; 31 | top: calc(50% - 12px); 32 | right: 12px; 33 | opacity: 1; 34 | } 35 | .btn-wait:before { 36 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' opacity='.7' fill='none' stroke='white' stroke-width='3.8' stroke-dasharray='1 31 1 11 1 40' stroke-linecap='round' /%3E%3C/svg%3E"); 37 | animation: spin 3.1s linear infinite; 38 | } 39 | .btn-wait:after { 40 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' fill='none' opacity='.7' stroke='white' stroke-width='3.8' stroke-dasharray='1 6 1 20 1 13' stroke-linecap='round' /%3E%3C/svg%3E"); 41 | animation: spin 2.3s linear infinite; 42 | } 43 | .btn-2:before { 44 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' opacity='1' fill='none' stroke='white' stroke-width='3.8' stroke-dasharray='1 20' stroke-linecap='round' /%3E%3C/svg%3E"); 45 | } 46 | .btn-2:after { 47 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' fill='none' opacity='1' stroke='white' stroke-width='3.8' stroke-dasharray='1 31' stroke-linecap='round' /%3E%3C/svg%3E"); 48 | } 49 | .btn-3:before { 50 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' opacity='1' fill='none' stroke='white' stroke-width='3.8' stroke-dasharray='6 15' stroke-linecap='round' /%3E%3C/svg%3E"); 51 | } 52 | .btn-3:after { 53 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' opacity='1' fill='none' stroke='white' stroke-width='3.8' stroke-dasharray='6 15' stroke-linecap='round' /%3E%3C/svg%3E"); 54 | animation: spin 1.3s linear reverse infinite; 55 | } 56 | .btn-4:before { 57 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='6' fill='none' stroke='white' stroke-width='3.8' stroke-dasharray='12 99' stroke-linecap='round' /%3E%3C/svg%3E"); 58 | animation: spin 1.1s linear infinite; 59 | } 60 | .btn-4:after { 61 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' fill='none' stroke='white' stroke-width='3.8' stroke-dasharray='12 52' stroke-linecap='round' /%3E%3C/svg%3E"); 62 | animation: spin 1.3s linear reverse infinite; 63 | } 64 | .btn-5:before { 65 | background-image: none; 66 | } 67 | .btn-5:after { 68 | background-image: none; 69 | border: 2px solid white; 70 | border-radius: 100%; 71 | border-width: 0 0 3px 3px; 72 | animation: spin 1.3s linear infinite; 73 | } 74 | .btn-6:before { 75 | background-image: none; 76 | } 77 | .btn-6:after { 78 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' opacity='.8' fill='none' stroke='white' stroke-width='3.8' stroke-dasharray='32 52' stroke-linecap='round' /%3E%3C/svg%3E"); 79 | animation: spin 1.3s linear infinite; 80 | } 81 | .btn-7:before { 82 | background-image: none; 83 | } 84 | .btn-7:after { 85 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' opacity='.8' fill='none' stroke='white' stroke-width='3.8' stroke-dasharray='48 52' stroke-linecap='round' /%3E%3C/svg%3E"); 86 | animation: spin 2s cubic-bezier(0, 0.5, 1, 0.5) infinite; 87 | } 88 | .btn-wait.btn-solar { 89 | background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3e%3ccircle cx='12' cy='12' r='3' fill='yellow' stroke-width='0' /%3e%3c/svg%3e"); 90 | background-repeat: no-repeat; 91 | background-position: calc(100% - 12px) 6px; 92 | } 93 | .btn-wait.btn-solar:before { 94 | background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3e%3ccircle cx='12' cy='12' r='10.186' fill='none' stroke='cornflowerblue' stroke-width='3' stroke-dasharray='.1 100' stroke-linecap='round' /%3e%3c/svg%3e"); 95 | } 96 | .btn-wait.btn-solar:after { 97 | background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3e%3ccircle cx='12' cy='12' r='6' fill='none' stroke='grey' stroke-width='2' stroke-dasharray='.1 100' stroke-linecap='round' /%3e%3c/svg%3e"); 98 | } 99 | .btn-step:before { 100 | background-image: none; 101 | } 102 | .btn-step:after { 103 | background-image: url("data:image/svg+xml;charset=UTF-8,%EF%BB%BF%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.186' opacity='.8' fill='none' stroke='white' stroke-width='3.8' stroke-dasharray='.1 5.233 .1 5.233 .1 5.233 .1 5.233 .1 5.233 .1 5.233 .1 10.333' stroke-linecap='round' /%3E%3C/svg%3E"); 104 | animation: spin 1s steps(12) infinite; 105 | } 106 | .btn-step2:before { 107 | background-image: none; 108 | } 109 | .btn-step2:after { 110 | background-image: url("data:image/svg+xml;charset=UTF-8, %3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3e%3cg fill='none' stroke='white' stroke-width='5' stroke-linecap='round' stroke-dasharray='.1 64'%3e%3ccircle cx='12' cy='12' r='8' opacity='.25' /%3e%3ccircle cx='12' cy='12' r='8' opacity='.5' stroke-dashoffset='-6.28' /%3e%3ccircle cx='12' cy='12' r='8' opacity='.75' stroke-dashoffset='-12.56' /%3e%3ccircle cx='12' cy='12' r='8' opacity='1' stroke-dashoffset='-18.84'/%3e%3c/g%3e%3c/svg%3e"); 111 | animation: spin 1s steps(8) infinite; 112 | } 113 | .btn-step3:before { 114 | background-image: none; 115 | } 116 | .btn-step3:after { 117 | background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3e%3cdefs%3e%3clinearGradient id='g' y1='1'%3e%3cstop stop-color='white' offset='0'/%3e%3cstop stop-color='white' stop-opacity='0' offset='1'/%3e%3c/linearGradient%3e%3c/defs%3e%3ccircle cx='12' cy='12' r='8' fill='none' stroke='url(%23g)' stroke-width='5' stroke-linecap='round' stroke-dasharray='.1 6.28 .1 6.28 .1 6.28 .1 99' /%3e%3c/svg%3e"); 118 | animation: spin 1s steps(8) infinite; 119 | } 120 | 121 | @keyframes spin { 122 | from { 123 | transform: rotate(0deg); 124 | } 125 | to { 126 | transform: rotate(360deg); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/components/LoginRegisterModal/LoginForm.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import { connect } from "react-redux"; 9 | import { userLogin } from "../../redux/actions/LoginAction"; 10 | import jumpTo from "../../modules/Navigation"; 11 | import Validator from "../../utils/Validator"; 12 | import { DEFAULT_RULE, EMAIL_RULE } from "../../utils/Validator/rule"; 13 | import PropTypes from "prop-types"; 14 | import LoadingButton from "../LoadingButton"; 15 | 16 | class LoginForm extends Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { 20 | email: "", 21 | password: "", 22 | loading: false 23 | }; 24 | } 25 | 26 | handleChange = e => { 27 | const { name, value } = e.target; 28 | this.setState({ [name]: value }); 29 | }; 30 | 31 | handleSubmit = () => { 32 | // let email = "bob@bob.com"; 33 | // let password = "Ab123123"; 34 | const { email, password } = this.state; 35 | if (!Validator(email, EMAIL_RULE)) { 36 | console.log("email Error"); 37 | return; 38 | } 39 | if (!Validator(password, DEFAULT_RULE)) { 40 | console.log("Password Error"); 41 | return; 42 | } 43 | this.setState({ loading: true }); 44 | this.props 45 | .userLogin(email, password) 46 | .then(res => { 47 | console.log(res); 48 | this.setState({ loading: false }); 49 | window.location.reload(); 50 | }) 51 | .catch(error => { 52 | // console.log('loginsignin error') 53 | console.log(error.response); 54 | this.setState({ loading: false }); 55 | }); 56 | }; 57 | 58 | render() { 59 | return ( 60 |
61 |
62 |

Login

63 |
64 | 74 | 75 |
76 |
77 | 87 | 88 |
89 | Invalid Credentials 90 | 95 | Lost your password? 96 | 97 | this.handleSubmit()} 102 | > 103 | Log in 104 | 105 |
114 | New user ? Please Register 115 |
116 |
117 |
118 | ); 119 | } 120 | } 121 | 122 | LoginForm.propTypes = { 123 | forgotPasswordClicked: PropTypes.func, 124 | registerClicked: PropTypes.func 125 | }; 126 | 127 | const mapDispatchToProps = { 128 | userLogin 129 | }; 130 | const mapStoreToProps = state => ({ 131 | login_loading: state.login.login_loading, 132 | login_error: state.login.error 133 | }); 134 | 135 | export default connect(mapStoreToProps, mapDispatchToProps)(LoginForm); 136 | -------------------------------------------------------------------------------- /src/components/LoginRegisterModal/RegisterForm.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import jumpTo from "../../modules/Navigation"; 9 | import Validator from "../../utils/Validator"; 10 | import { DEFAULT_RULE, EMAIL_RULE } from "../../utils/Validator/rule"; 11 | import PropTypes from "prop-types"; 12 | import { connect } from "react-redux"; 13 | import { userRegister } from "../../redux/actions/RegisterAction"; 14 | import LoadingButton from "../LoadingButton"; 15 | 16 | class RegisterForm extends Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { 20 | name: "", 21 | email: "", 22 | password: "" 23 | }; 24 | } 25 | handleChange = e => { 26 | const { name, value } = e.target; 27 | this.setState({ [name]: value }); 28 | }; 29 | 30 | handleSubmit = () => { 31 | const { name, email, password } = this.state; 32 | if (!Validator(name, DEFAULT_RULE)) { 33 | console.log("Name Error"); 34 | return; 35 | } 36 | if (!Validator(email, EMAIL_RULE)) { 37 | console.log("email Error"); 38 | return; 39 | } 40 | if (!Validator(password, DEFAULT_RULE)) { 41 | console.log("Password Error"); 42 | return; 43 | } 44 | this.setState({ loading: true }); 45 | this.props 46 | .userRegister(name, email, password, password) 47 | .then(res => { 48 | console.log(res); 49 | this.props.loginClicked(); 50 | this.setState({ loading: false }); 51 | }) 52 | .catch(error => { 53 | console.log(error.response); 54 | this.setState({ loading: false }); 55 | }); 56 | }; 57 | 58 | render() { 59 | return ( 60 |
61 |
62 |

Register

63 |
64 | 74 | 75 |
76 | 77 |
78 | 88 | 89 |
90 | 91 |
92 | 102 | 103 |
104 | Invalid Credentials 105 | this.handleSubmit()} 110 | > 111 | Register 112 | 113 |
122 | Already have an account ? Please login. 123 |
124 |
125 |
126 | ); 127 | } 128 | } 129 | 130 | RegisterForm.propTypes = { 131 | loginClicked: PropTypes.func 132 | }; 133 | 134 | const mapDispatchToProps = { 135 | userRegister 136 | }; 137 | const mapStoreToProps = state => ({ 138 | register_loading: state.register.register_loading, 139 | register_error: state.register.error 140 | }); 141 | 142 | export default connect(mapStoreToProps, mapDispatchToProps)(RegisterForm); 143 | -------------------------------------------------------------------------------- /src/components/LoginRegisterModal/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import { Modal, Button } from "react-bootstrap"; 9 | import LoginForm from "./LoginForm"; 10 | import RegisterForm from "./RegisterForm"; 11 | import "./style.css"; 12 | import PropTypes from "prop-types"; 13 | class LoginRegister extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | showlogin: this.props.showlogin 18 | }; 19 | } 20 | 21 | render() { 22 | return ( 23 | 31 | 32 |
33 | 34 |
35 | {this.props.login ? ( 36 | this.props.registerClicked()} /> 37 | ) : ( 38 | this.props.loginClicked()} /> 39 | )} 40 |
41 |
42 | ); 43 | } 44 | } 45 | LoginRegister.propTypes = { 46 | login: PropTypes.bool, 47 | registerClicked: PropTypes.func, 48 | loginClicked: PropTypes.func 49 | }; 50 | export default LoginRegister; 51 | -------------------------------------------------------------------------------- /src/components/LoginRegisterModal/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | .login-form { 8 | width: 100%; 9 | 10 | -moz-border-radius: 4px; 11 | -webkit-border-radius: 4px; 12 | border-radius: 4px; 13 | margin: auto; 14 | } 15 | 16 | .form-group { 17 | position: relative; 18 | margin-bottom: 15px; 19 | } 20 | 21 | .form-control { 22 | width: 100%; 23 | height: 50px; 24 | border: none; 25 | padding: 5px 7px 5px 15px; 26 | background: #fff; 27 | color: #666; 28 | border: 2px solid #ddd; 29 | -moz-border-radius: 4px; 30 | -webkit-border-radius: 4px; 31 | border-radius: 4px; 32 | } 33 | .form-control:focus, 34 | .form-control:focus + .fa { 35 | border-color: #10ce88; 36 | color: #10ce88; 37 | } 38 | 39 | .form-group .fa { 40 | position: absolute; 41 | right: 10px; 42 | top: 12px; 43 | color: #999; 44 | } 45 | 46 | .log-status.wrong-entry { 47 | -moz-animation: wrong-log 0.3s; 48 | -webkit-animation: wrong-log 0.3s; 49 | animation: wrong-log 0.3s; 50 | } 51 | 52 | .log-status.wrong-entry .form-control, 53 | .wrong-entry .form-control + .fa { 54 | border-color: #ed1c24; 55 | color: #ed1c24; 56 | } 57 | 58 | .log-btn { 59 | background-color: #fe4c50 !important; 60 | dispaly: inline-block; 61 | width: 100%; 62 | font-size: 16px; 63 | height: 40px; 64 | color: #fff !important; 65 | text-decoration: none; 66 | border: none; 67 | -moz-border-radius: 20px; 68 | -webkit-border-radius: 20px; 69 | border-radius: 20px !important; 70 | margin-bottom: 10px; 71 | } 72 | 73 | .link { 74 | text-decoration: none; 75 | color: #c6c6c6; 76 | float: right; 77 | font-size: 12px; 78 | margin-bottom: 15px; 79 | } 80 | .link:hover { 81 | text-decoration: underline; 82 | color: #8c918f; 83 | } 84 | 85 | .alert { 86 | display: none; 87 | font-size: 12px; 88 | color: #f00; 89 | float: left; 90 | } 91 | .modal--close--button { 92 | position: absolute; 93 | z-index: 9; 94 | top: 0; 95 | right: 0; 96 | color: #d3d3d3; 97 | margin-right: 10px; 98 | cursor: pointer; 99 | } 100 | 101 | @-moz-keyframes wrong-log { 102 | 0%, 103 | 100% { 104 | left: 0px; 105 | } 106 | 20%, 107 | 60% { 108 | left: 15px; 109 | } 110 | 40%, 111 | 80% { 112 | left: -15px; 113 | } 114 | } 115 | @-webkit-keyframes wrong-log { 116 | 0%, 117 | 100% { 118 | left: 0px; 119 | } 120 | 20%, 121 | 60% { 122 | left: 15px; 123 | } 124 | 40%, 125 | 80% { 126 | left: -15px; 127 | } 128 | } 129 | @keyframes wrong-log { 130 | 0%, 131 | 100% { 132 | left: 0px; 133 | } 134 | 20%, 135 | 60% { 136 | left: 15px; 137 | } 138 | 40%, 139 | 80% { 140 | left: -15px; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/components/MobileMenu/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import PropTypes from "prop-types"; 9 | 10 | class MobileMenu extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = {}; 14 | } 15 | render() { 16 | return ( 17 |
22 |
23 | 24 |
25 |
26 | 105 |
106 |
107 | ); 108 | } 109 | } 110 | MobileMenu.propTypes = { 111 | activeClass: PropTypes.bool, 112 | onClose: PropTypes.func, 113 | }; 114 | 115 | export default MobileMenu; 116 | -------------------------------------------------------------------------------- /src/components/NavBar/NavBar.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import { Link } from "react-router-dom"; 9 | import HomeCartView from "../HomeCartView"; 10 | import MobileMenu from "../MobileMenu"; 11 | import device, { size } from "../../modules/mediaQuery"; 12 | import MediaQuery from "react-responsive"; 13 | 14 | class NavBar extends Component { 15 | constructor(props) { 16 | super(props); 17 | this.state = { 18 | modalShow: false, 19 | activeclass: false, 20 | }; 21 | } 22 | 23 | componentDidMount() { 24 | if (Object.keys(this.props.cart).length < 1) { 25 | this.props.getCartByUserId(); 26 | } 27 | } 28 | 29 | showHideModal = () => { 30 | this.setState({ modalShow: !this.state.modalShow }); 31 | }; 32 | 33 | handleMenuClicked = () => { 34 | this.setState({ activeclass: !this.state.activeclass }); 35 | }; 36 | render() { 37 | const { departments, cart } = this.props; 38 | 39 | return ( 40 |
41 |
42 |
43 |
44 |
45 | 46 | FashionCube 47 | 48 |
49 | 119 |
120 |
121 |
122 | 123 | this.handleMenuClicked()} 126 | /> 127 | 128 | {this.state.modalShow ? ( 129 | this.showHideModal()} 133 | /> 134 | ) : null} 135 |
136 | ); 137 | } 138 | } 139 | 140 | export default NavBar; 141 | -------------------------------------------------------------------------------- /src/components/NavBar/NavBarContainer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { connect } from "react-redux"; 8 | import NavBar from "./NavBar"; 9 | import { getDepartments } from "../../redux/actions/DepartmentAction"; 10 | import { getCartByUserId } from "../../redux/actions/cartAction"; 11 | const mapStoreToProps = state => ({ 12 | departments: state.department.departments, 13 | cart: state.cart.cart 14 | }); 15 | 16 | const mapDispatchToProps = dispatch => ({ 17 | getDepartments: dispatch(getDepartments()), 18 | getCartByUserId: () => dispatch(getCartByUserId()) 19 | }); 20 | 21 | export default connect(mapStoreToProps, mapDispatchToProps)(NavBar); 22 | -------------------------------------------------------------------------------- /src/components/Products/BestSeller.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import SingleProduct from "./SingleProduct"; 9 | import Heading from "../Heading"; 10 | 11 | class BestSeller extends Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | products: this.props.products 16 | }; 17 | } 18 | 19 | render() { 20 | const { products, departments } = this.state; 21 | 22 | return ( 23 |
24 |
25 |
26 | 27 |
28 | 29 |
30 | {products && 31 | products.slice(5, 9).map((item, index) => { 32 | return ( 33 |
38 | 42 |
43 | ); 44 | })} 45 |
46 |
47 |
48 | ); 49 | } 50 | } 51 | 52 | export default BestSeller; 53 | -------------------------------------------------------------------------------- /src/components/Products/NewArrivals.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import SingleProduct from "./SingleProduct"; 9 | import Heading from "../Heading"; 10 | import PropTypes from "prop-types"; 11 | class NewArrivals extends Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | products: this.props.products, 16 | productsBAK: this.props.products, 17 | departments: this.props.departments 18 | }; 19 | } 20 | 21 | optionClicked(option) { 22 | let FilterList = this.state.productsBAK.filter( 23 | item => item.department === option 24 | ); 25 | if (FilterList.length > 0) { 26 | this.setState({ products: FilterList }); 27 | } else { 28 | this.setState({ products: this.state.productsBAK }); 29 | } 30 | this.setState({ selectedOption: option }); 31 | } 32 | 33 | render() { 34 | const { products, departments } = this.state; 35 | 36 | return ( 37 |
38 |
39 |
40 | 41 |
42 |
43 |
44 |
45 |
    46 |
  • this.optionClicked("All")} 48 | className={`grid_sorting_button button d-flex flex-column justify-content-center align-items-center ${ 49 | this.state.selectedOption === "All" 50 | ? "active is-checked" 51 | : null 52 | }`} 53 | > 54 | all 55 |
  • 56 |
  • this.optionClicked("Women")} 63 | > 64 | women's 65 |
  • 66 | 67 |
  • this.optionClicked("Men")} 74 | > 75 | men's 76 |
  • 77 |
78 |
79 |
80 |
81 |
82 | {products && 83 | products.slice(0, 8).map((item, index) => { 84 | return ( 85 |
90 | 94 |
95 | ); 96 | })} 97 |
98 |
99 |
100 | ); 101 | } 102 | } 103 | 104 | NewArrivals.propTypes = { 105 | addToCart: PropTypes.func 106 | }; 107 | 108 | export default NewArrivals; 109 | -------------------------------------------------------------------------------- /src/components/Products/SingleProduct.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React from "react"; 8 | import jumpTo from "../../modules/Navigation"; 9 | 10 | function SingleProduct(props) { 11 | const { productItem } = props; 12 | return ( 13 |
14 |
17 | jumpTo(`/single-product/${productItem._id}`) 18 | } 19 | > 20 |
21 | 22 |
23 |
24 | 25 |
26 | {/*
27 | -$20 28 |
*/} 29 |
30 |
31 |
{productItem.title}
32 |
33 |
34 | ₹ {productItem.price} 35 | ₹ {(parseFloat(productItem.price) + 30).toFixed(2)} 36 |
37 |
38 |
39 |
props.addToBag(productItem._id)} 42 | > 43 |
add to cart
44 |
45 |
46 | ); 47 | } 48 | 49 | export default SingleProduct; 50 | -------------------------------------------------------------------------------- /src/components/TopNavBar/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import LoginRegister from "../LoginRegisterModal"; 9 | 10 | import Auth from "../../modules/Auth"; 11 | 12 | class TopNavBar extends Component { 13 | constructor(props) { 14 | super(props); 15 | this.state = { 16 | modalShow: false, 17 | login: true 18 | }; 19 | } 20 | showHideModal = () => { 21 | this.setState({ modalShow: false }); 22 | }; 23 | 24 | loginClicked = () => { 25 | this.setState({ modalShow: true, login: true }); 26 | }; 27 | registerClicked = () => { 28 | this.setState({ modalShow: true, login: false }); 29 | }; 30 | 31 | logout = () => { 32 | Auth.logout(); 33 | window.location.reload(); 34 | }; 35 | 36 | render() { 37 | return ( 38 |
39 |
40 |
41 |
42 |
43 | free shipping on all u.s orders over $50 44 |
45 |
46 |
47 |
48 | 138 |
139 |
140 |
141 |
142 | {this.state.modalShow ? ( 143 | this.registerClicked()} 147 | loginClicked={() => this.loginClicked()} 148 | onHide={() => this.showHideModal()} 149 | /> 150 | ) : null} 151 |
152 | ); 153 | } 154 | } 155 | 156 | export default TopNavBar; 157 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | import App from "./App"; 5 | import * as serviceWorker from "./serviceWorker"; 6 | import "bootstrap/dist/css/bootstrap.min.css"; 7 | 8 | import { Provider } from "react-redux"; 9 | import store from "./redux/store"; 10 | 11 | ReactDOM.render( 12 | 13 | 14 | , 15 | document.getElementById("root") 16 | ); 17 | 18 | // If you want your app to work offline and load faster, you can change 19 | // unregister() to register() below. Note this comes with some pitfalls. 20 | // Learn more about service workers: https://bit.ly/CRA-PWA 21 | serviceWorker.unregister(); 22 | -------------------------------------------------------------------------------- /src/layouts/BaseLayout.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import TopNavBar from "../components/TopNavBar"; 9 | import NavBarContainer from "../components/NavBar/NavBarContainer"; 10 | import MobileMenu from "../components/MobileMenu"; 11 | import Footer from "../components/Footer"; 12 | 13 | class BaseLayout extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | topHaderClass: "show", 18 | }; 19 | this.handleScroll = this.handleScroll.bind(this); 20 | } 21 | componentDidMount() { 22 | window.scrollTo(0, 0); 23 | window.addEventListener("scroll", this.handleScroll); 24 | } 25 | 26 | componentWillMount() { 27 | window.scrollTo(0, 0); 28 | } 29 | 30 | componentWillUnmount() { 31 | window.removeEventListener("scroll", this.handleScroll); 32 | } 33 | 34 | handleScroll = (event) => { 35 | if (window.scrollY >= 50) { 36 | this.setState({ topHaderClass: "hide" }); 37 | } else { 38 | this.setState({ topHaderClass: "show" }); 39 | } 40 | }; 41 | render() { 42 | return ( 43 |
44 |
45 |
46 | 47 | 48 |
49 |
{this.props.children}
50 |
51 |
52 |
53 | ); 54 | } 55 | } 56 | 57 | export default BaseLayout; 58 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/modules/Auth/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | class Auth { 8 | constructor() { 9 | this.user_token = JSON.parse(localStorage.getItem("auth")) || {}; 10 | } 11 | getToken() { 12 | return this.user_token.token; 13 | } 14 | getUserId() { 15 | return this.user_token.user_id; 16 | } 17 | 18 | getUserDetails() { 19 | return this.user_token; 20 | } 21 | 22 | setUserToken(new_token) { 23 | this.user_token = new_token; 24 | localStorage.setItem("auth", JSON.stringify(new_token)); 25 | } 26 | logout() { 27 | localStorage.removeItem("auth"); 28 | } 29 | } 30 | export default new Auth(); 31 | -------------------------------------------------------------------------------- /src/modules/Navigation/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | let history; 8 | export const registerNav = ref => { 9 | history = ref.history; 10 | }; 11 | 12 | const jumpTo = uri => { 13 | history.push(uri); 14 | }; 15 | export const go = uri => { 16 | history.go(uri); 17 | }; 18 | export default jumpTo; 19 | -------------------------------------------------------------------------------- /src/modules/mediaQuery/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | export const size = { 8 | mobileS: "320px", 9 | mobileM: "375px", 10 | mobileL: "425px", 11 | tablet: "768px", 12 | tabletL: "991px", 13 | laptop: "1024px", 14 | laptopL: "1440px", 15 | desktop: "2560px", 16 | }; 17 | const device = { 18 | min: { 19 | mobileS: `(min-width: ${size.mobileS})`, 20 | mobileM: `(min-width: ${size.mobileM})`, 21 | mobileL: `(min-width: ${size.mobileL})`, 22 | tablet: `(min-width: ${size.tablet})`, 23 | tabletL: `(min-width: ${size.tabletL})`, 24 | laptop: `(min-width: ${size.laptop})`, 25 | laptopL: `(min-width: ${size.laptopL})`, 26 | desktop: `(min-width: ${size.desktop})`, 27 | desktopL: `(min-width: ${size.desktop})`, 28 | }, 29 | max: { 30 | mobileS: `(max-width: ${size.mobileS})`, 31 | mobileM: `(max-width: ${size.mobileM})`, 32 | mobileL: `(max-width: ${size.mobileL})`, 33 | tablet: `(max-width: ${size.tablet})`, 34 | tabletL: `(max-width: ${size.tabletL})`, 35 | laptop: `(max-width: ${size.laptop})`, 36 | laptopL: `(max-width: ${size.laptopL})`, 37 | desktop: `(max-width: ${size.desktop})`, 38 | desktopL: `(max-width: ${size.desktop})`, 39 | }, 40 | }; 41 | 42 | export default device; 43 | -------------------------------------------------------------------------------- /src/redux/actions/DepartmentAction.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import API from "../../axios/API"; 8 | 9 | export const getDepartments = () => dispatch => { 10 | dispatch({ 11 | type: GET_DEPARTMENTS_BEGIN 12 | }); 13 | return API({ 14 | method: "GET", 15 | url: "/departments" 16 | }) 17 | .then(res => { 18 | dispatch({ 19 | type: GET_DEPARTMENTS_SUCCESS, 20 | payload: res 21 | }); 22 | return res; 23 | }) 24 | .catch(error => { 25 | dispatch({ 26 | type: GET_DEPARTMENTS_FAIL, 27 | payload: { error } 28 | }); 29 | return error; 30 | }); 31 | }; 32 | 33 | export const GET_DEPARTMENTS_BEGIN = "GET_DEPARTMENTS_BEGIN"; 34 | export const GET_DEPARTMENTS_SUCCESS = "GET_DEPARTMENTS_SUCCESS"; 35 | export const GET_DEPARTMENTS_FAIL = "GET_DEPARTMENTS_FAIL"; 36 | -------------------------------------------------------------------------------- /src/redux/actions/LoginAction.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { login } from "../../ServerRequest"; 8 | 9 | export const userLogin = (email, password) => dispatch => { 10 | dispatch({ 11 | type: LOGIN_BEGIN 12 | }); 13 | return login(email, password) 14 | .then(res => { 15 | dispatch({ 16 | type: LOGIN_SUCCESS, 17 | payload: res 18 | }); 19 | 20 | return res; 21 | }) 22 | .catch(error => { 23 | dispatch({ 24 | type: LOGIN_FAIL, 25 | payload: { error } 26 | }); 27 | 28 | throw error; 29 | }); 30 | }; 31 | 32 | export const insertToken = () => dispatch => { 33 | let token; 34 | if (localStorage.getItem("auth")) { 35 | token = JSON.parse(localStorage.getItem("auth")); 36 | dispatch({ 37 | type: INSERT_TOKEN_SUCCESS, 38 | payload: token 39 | }); 40 | } else { 41 | dispatch({ 42 | type: INSERT_TOKEN_FAIL 43 | }); 44 | } 45 | }; 46 | 47 | export const LOGIN_BEGIN = "LOGIN_BEGIN"; 48 | export const LOGIN_SUCCESS = "LOGIN_SUCCESS"; 49 | export const LOGIN_FAIL = "LOGIN_FAIL"; 50 | export const INSERT_TOKEN_SUCCESS = "INSERT_TOKEN_SUCCESS"; 51 | export const INSERT_TOKEN_FAIL = "INSERT_TOKEN_FAIL"; 52 | -------------------------------------------------------------------------------- /src/redux/actions/RegisterAction.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { register } from "../../ServerRequest"; 8 | 9 | export const userRegister = ( 10 | fullname, 11 | email, 12 | password, 13 | verifyPassword 14 | ) => dispatch => { 15 | dispatch({ 16 | type: POST_REGISTER_BEGIN 17 | }); 18 | 19 | return register(fullname, email, password, verifyPassword) 20 | .then(res => { 21 | dispatch({ 22 | type: POST_REGISTER_SUCCESS, 23 | payload: res 24 | }); 25 | 26 | return res; 27 | }) 28 | .catch(error => { 29 | dispatch({ 30 | type: POST_REGISTER_FAIL, 31 | payload: { error } 32 | }); 33 | 34 | throw error; 35 | }); 36 | }; 37 | 38 | export const POST_REGISTER_BEGIN = "POST_REGISTER_BEGIN"; 39 | export const POST_REGISTER_SUCCESS = "POST_REGISTER_SUCCESS"; 40 | export const POST_REGISTER_FAIL = "POST_REGISTER_FAIL"; 41 | -------------------------------------------------------------------------------- /src/redux/actions/cartAction.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import API from "../../axios/API"; 8 | import Auth from "../../modules/Auth"; 9 | 10 | export const getCartByUserId = () => dispatch => { 11 | let userId = Auth.getUserId(); 12 | dispatch({ 13 | type: GET_CART_BY_USERID_BEGIN 14 | }); 15 | return API({ 16 | method: "GET", 17 | url: `users/${userId}/cart` 18 | }) 19 | .then(res => { 20 | dispatch({ 21 | type: GET_CART_BY_USERID_SUCCESS, 22 | payload: res 23 | }); 24 | return res; 25 | }) 26 | .catch(error => { 27 | dispatch({ 28 | type: GET_CART_BY_USERID_FAIL, 29 | payload: { error } 30 | }); 31 | return error; 32 | }); 33 | }; 34 | 35 | export const postCart = (productId, increase, decrease) => dispatch => { 36 | let userId = Auth.getUserId(); 37 | dispatch({ 38 | type: POST_CART_BEGIN 39 | }); 40 | return API({ 41 | method: "POST", 42 | url: `users/${userId}/cart`, 43 | data: { 44 | userId, 45 | productId, 46 | increase, 47 | decrease 48 | } 49 | }) 50 | .then(res => { 51 | dispatch({ 52 | type: POST_CART_SUCCESS, 53 | payload: res 54 | }); 55 | return res; 56 | }) 57 | .catch(error => { 58 | dispatch({ 59 | type: POST_CART_FAIL, 60 | payload: { error } 61 | }); 62 | return error; 63 | }); 64 | }; 65 | 66 | export const POST_CART_BEGIN = "POST_CART_BEGIN"; 67 | export const POST_CART_SUCCESS = "POST_CART_SUCCESS"; 68 | export const POST_CART_FAIL = "POST_CART_FAIL"; 69 | 70 | export const GET_CART_BY_USERID_BEGIN = "GET_CART_BY_USERID_BEGIN"; 71 | export const GET_CART_BY_USERID_SUCCESS = "GET_CART_BY_USERID_SUCCESS"; 72 | export const GET_CART_BY_USERID_FAIL = "GET_CART_BY_USERID_FAIL"; 73 | -------------------------------------------------------------------------------- /src/redux/actions/productAction.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import API from "../../axios/API"; 8 | 9 | export const getAllProducts = () => dispatch => { 10 | dispatch({ 11 | type: GET_ALL_PRODUCTS_BEGIN 12 | }); 13 | return API({ 14 | method: "GET", 15 | url: `/products` 16 | }) 17 | .then(res => { 18 | dispatch({ 19 | type: GET_ALL_PRODUCTS_SUCCESS, 20 | payload: res 21 | }); 22 | return res; 23 | }) 24 | .catch(error => { 25 | dispatch({ 26 | type: GET_ALL_PRODUCTS_FAIL, 27 | payload: { error } 28 | }); 29 | return error; 30 | }); 31 | }; 32 | 33 | export const getProduct = id => dispatch => { 34 | dispatch({ 35 | type: GET_PRODUCT_BEGIN 36 | }); 37 | return API({ 38 | method: "GET", 39 | url: `/products/${id}` 40 | }) 41 | .then(res => { 42 | dispatch({ 43 | type: GET_PRODUCT_SUCCESS, 44 | payload: res 45 | }); 46 | return res; 47 | }) 48 | .catch(error => { 49 | dispatch({ 50 | type: GET_PRODUCT_FAIL, 51 | payload: { error } 52 | }); 53 | return error; 54 | }); 55 | }; 56 | 57 | export const getProductsByCategory = c => dispatch => { 58 | dispatch({ 59 | type: GET_PRODUCTS_BY_CATEGORY_BEGIN 60 | }); 61 | return API({ 62 | method: "GET", 63 | url: `/products?category=${c}` 64 | }) 65 | .then(res => { 66 | dispatch({ 67 | type: GET_PRODUCTS_BY_CATEGORY_SUCCESS, 68 | payload: res 69 | }); 70 | return res; 71 | }) 72 | .catch(error => { 73 | dispatch({ 74 | type: GET_PRODUCTS_BY_CATEGORY_FAIL, 75 | payload: { error } 76 | }); 77 | return error; 78 | }); 79 | }; 80 | 81 | export const search = text => dispatch => { 82 | dispatch({ 83 | type: SEARCH_BEGIN 84 | }); 85 | return API({ 86 | method: "GET", 87 | url: `/search?query=${text}` 88 | }) 89 | .then(res => { 90 | dispatch({ 91 | type: SEARCH_SUCCESS, 92 | payload: res 93 | }); 94 | return res; 95 | }) 96 | .catch(error => { 97 | dispatch({ 98 | type: SEARCH_FAIL, 99 | payload: { error } 100 | }); 101 | return error; 102 | }); 103 | }; 104 | 105 | export const applyFilters = filter_string => dispatch => { 106 | dispatch({ 107 | type: APPLY_FILTERS_BEGIN 108 | }); 109 | return API({ 110 | method: "GET", 111 | url: `/products?${filter_string}` 112 | }) 113 | .then(res => { 114 | dispatch({ 115 | type: APPLY_FILTERS_SUCCESS, 116 | payload: res 117 | }); 118 | return res; 119 | }) 120 | .catch(error => { 121 | dispatch({ 122 | type: APPLY_FILTERS_FAIL, 123 | payload: { error } 124 | }); 125 | return error; 126 | }); 127 | }; 128 | 129 | export const APPLY_FILTERS_BEGIN = "APPLY_FILTERS_BEGIN"; 130 | export const APPLY_FILTERS_SUCCESS = "APPLY_FILTERS_SUCCESS"; 131 | export const APPLY_FILTERS_FAIL = "APPLY_FILTERS_FAIL"; 132 | 133 | export const SEARCH_BEGIN = "SEARCH_BEGIN"; 134 | export const SEARCH_SUCCESS = "SEARCH_SUCCESS"; 135 | export const SEARCH_FAIL = "SEARCH_FAIL"; 136 | 137 | export const GET_ALL_PRODUCTS_BEGIN = "GET_ALL_PRODUCTS_BEGIN"; 138 | export const GET_ALL_PRODUCTS_SUCCESS = "GET_ALL_PRODUCTS_SUCCESS"; 139 | export const GET_ALL_PRODUCTS_FAIL = "GET_ALL_PRODUCTS_FAIL"; 140 | 141 | export const GET_PRODUCT_BEGIN = "GET_PRODUCT_BEGIN"; 142 | export const GET_PRODUCT_SUCCESS = "GET_PRODUCT_SUCCESS"; 143 | export const GET_PRODUCT_FAIL = "GET_PRODUCT_FAIL"; 144 | 145 | export const GET_PRODUCTS_BY_CATEGORY_BEGIN = "GET_PRODUCTS_BY_CATEGORY_BEGIN"; 146 | export const GET_PRODUCTS_BY_CATEGORY_SUCCESS = 147 | "GET_PRODUCTS_BY_CATEGORY_SUCCESS"; 148 | export const GET_PRODUCTS_BY_CATEGORY_FAIL = "GET_PRODUCTS_BY_CATEGORY_FAIL"; 149 | -------------------------------------------------------------------------------- /src/redux/actions/variantsAction.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import API from "../../axios/API"; 8 | 9 | export const getVariantsByProductId = productId => dispatch => { 10 | dispatch({ 11 | type: GET_VARIANTS_QUERY_BEGIN 12 | }); 13 | return API({ 14 | method: "GET", 15 | url: `/variants?productId=${productId}` 16 | }) 17 | .then(res => { 18 | dispatch({ 19 | type: GET_VARIANTS_QUERY_SUCCESS, 20 | payload: res 21 | }); 22 | return res; 23 | }) 24 | .catch(error => { 25 | dispatch({ 26 | type: GET_VARIANTS_QUERY_FAIL, 27 | payload: { error } 28 | }); 29 | return error; 30 | }); 31 | }; 32 | 33 | export const GET_VARIANTS_QUERY_BEGIN = "GET_VARIANTS_QUERY_BEGIN"; 34 | export const GET_VARIANTS_QUERY_SUCCESS = "GET_VARIANTS_QUERY_SUCCESS"; 35 | export const GET_VARIANTS_QUERY_FAIL = "GET_VARIANTS_QUERY_FAIL"; 36 | -------------------------------------------------------------------------------- /src/redux/reducers/DepartmentReducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { 8 | GET_DEPARTMENTS_BEGIN, 9 | GET_DEPARTMENTS_SUCCESS, 10 | GET_DEPARTMENTS_FAIL 11 | } from "../actions/DepartmentAction"; 12 | 13 | const initialState = { 14 | loading: false, 15 | departments: null, 16 | error: null 17 | }; 18 | 19 | export default (state = initialState, action) => { 20 | switch (action.type) { 21 | case GET_DEPARTMENTS_BEGIN: 22 | return { 23 | loading: true, 24 | error: null 25 | }; 26 | case GET_DEPARTMENTS_SUCCESS: 27 | return { 28 | loading: false, 29 | departments: action.payload.data.departments 30 | }; 31 | case GET_DEPARTMENTS_FAIL: 32 | return { 33 | loading: false, 34 | error: action.payload.error.response.data 35 | }; 36 | default: 37 | return state; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/redux/reducers/LoginReducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { LOGIN_BEGIN, LOGIN_SUCCESS, LOGIN_FAIL } from "../actions/LoginAction"; 8 | 9 | const initialState = { 10 | login_loading: false, 11 | error: {} 12 | }; 13 | 14 | export default (state = initialState, action) => { 15 | switch (action.type) { 16 | case LOGIN_BEGIN: 17 | return { 18 | ...state, 19 | signin_loading: true 20 | }; 21 | case LOGIN_SUCCESS: 22 | return { 23 | ...state, 24 | signin_loading: false 25 | }; 26 | case LOGIN_FAIL: 27 | return { 28 | ...state, 29 | signin_loading: false, 30 | error: action.payload.error.response.data 31 | }; 32 | default: 33 | return state; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/redux/reducers/RegisterReducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { 8 | POST_REGISTER_BEGIN, 9 | POST_REGISTER_SUCCESS, 10 | POST_REGISTER_FAIL 11 | } from "../actions/RegisterAction"; 12 | 13 | const initialState = { 14 | register_loading: false, 15 | error: {} 16 | }; 17 | 18 | export default (state = initialState, action) => { 19 | switch (action.type) { 20 | case POST_REGISTER_BEGIN: 21 | return { 22 | ...state, 23 | register_loading: true 24 | }; 25 | case POST_REGISTER_SUCCESS: 26 | return { 27 | ...state, 28 | register_loading: false 29 | }; 30 | case POST_REGISTER_FAIL: 31 | return { 32 | ...state, 33 | register_loading: false, 34 | error: action.payload.error.response.data 35 | }; 36 | default: 37 | return state; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/redux/reducers/cartReducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { 8 | POST_CART_BEGIN, 9 | POST_CART_SUCCESS, 10 | POST_CART_FAIL, 11 | GET_CART_BY_USERID_BEGIN, 12 | GET_CART_BY_USERID_SUCCESS, 13 | GET_CART_BY_USERID_FAIL 14 | } from "../actions/cartAction"; 15 | 16 | const initialState = { 17 | cart: {}, 18 | loading: false, 19 | error: {} 20 | }; 21 | 22 | export default (state = initialState, action) => { 23 | switch (action.type) { 24 | case POST_CART_BEGIN: 25 | return { 26 | ...state, 27 | loading: true, 28 | error: {} 29 | }; 30 | case POST_CART_SUCCESS: 31 | return { 32 | ...state, 33 | cart: action.payload.data.cart, 34 | loading: false 35 | }; 36 | case POST_CART_FAIL: 37 | return { 38 | ...state, 39 | loading: false, 40 | error: action.payload.error.response.data 41 | }; 42 | case GET_CART_BY_USERID_BEGIN: 43 | return { 44 | ...state, 45 | loading: true, 46 | error: {} 47 | }; 48 | case GET_CART_BY_USERID_SUCCESS: 49 | return { 50 | ...state, 51 | cart: action.payload.data.cart, 52 | loading: false 53 | }; 54 | case GET_CART_BY_USERID_FAIL: 55 | return { 56 | ...state, 57 | loading: false, 58 | error: action.payload.error.response.data 59 | }; 60 | default: 61 | return state; 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /src/redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { combineReducers } from "redux"; 8 | import login from "./LoginReducer"; 9 | import register from "./RegisterReducer"; 10 | import department from "./DepartmentReducer"; 11 | import product from "./productReducer"; 12 | import variant from "./variantsReducer"; 13 | import cart from "./cartReducer"; 14 | // import checkout from './checkoutReducer' 15 | // import filter from './filterReducer' 16 | 17 | export default combineReducers({ 18 | department, 19 | login, 20 | register, 21 | product, 22 | variant, 23 | cart 24 | }); 25 | -------------------------------------------------------------------------------- /src/redux/reducers/productReducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { 8 | GET_ALL_PRODUCTS_BEGIN, 9 | GET_ALL_PRODUCTS_SUCCESS, 10 | GET_ALL_PRODUCTS_FAIL, 11 | GET_PRODUCT_BEGIN, 12 | GET_PRODUCT_SUCCESS, 13 | GET_PRODUCT_FAIL, 14 | GET_PRODUCTS_BY_CATEGORY_BEGIN, 15 | GET_PRODUCTS_BY_CATEGORY_SUCCESS, 16 | GET_PRODUCTS_BY_CATEGORY_FAIL, 17 | SEARCH_BEGIN, 18 | SEARCH_SUCCESS, 19 | SEARCH_FAIL, 20 | APPLY_FILTERS_BEGIN, 21 | APPLY_FILTERS_SUCCESS, 22 | APPLY_FILTERS_FAIL 23 | } from "../actions/productAction"; 24 | 25 | const initialState = { 26 | products: null, 27 | product: null, 28 | loading: false, 29 | error: null 30 | }; 31 | 32 | export default (state = initialState, action) => { 33 | switch (action.type) { 34 | case GET_ALL_PRODUCTS_BEGIN: 35 | return { 36 | ...state, 37 | loading: true, 38 | error: null 39 | }; 40 | case GET_ALL_PRODUCTS_SUCCESS: 41 | return { 42 | ...state, 43 | loading: false, 44 | products: action.payload.data.products 45 | }; 46 | case GET_ALL_PRODUCTS_FAIL: 47 | return { 48 | ...state, 49 | loading: false, 50 | error: action.payload.error.response.data 51 | }; 52 | case GET_PRODUCT_BEGIN: 53 | return { 54 | ...state, 55 | loading: true, 56 | error: null 57 | }; 58 | case GET_PRODUCT_SUCCESS: 59 | return { 60 | ...state, 61 | loading: false, 62 | product: action.payload.data.product 63 | }; 64 | case GET_PRODUCT_FAIL: 65 | return { 66 | ...state, 67 | loading: false, 68 | error: action.payload.error.response.data 69 | }; 70 | case GET_PRODUCTS_BY_CATEGORY_BEGIN: 71 | return { 72 | ...state, 73 | loading: true, 74 | error: null 75 | }; 76 | case GET_PRODUCTS_BY_CATEGORY_SUCCESS: 77 | return { 78 | ...state, 79 | loading: false, 80 | products: action.payload.data.products 81 | }; 82 | case GET_PRODUCTS_BY_CATEGORY_FAIL: 83 | return { 84 | ...state, 85 | loading: false, 86 | error: action.payload.error.response.data 87 | }; 88 | case SEARCH_BEGIN: 89 | return { 90 | ...state, 91 | loading: true, 92 | error: null 93 | }; 94 | case SEARCH_SUCCESS: 95 | return { 96 | ...state, 97 | loading: false, 98 | products: action.payload.data.products 99 | }; 100 | case SEARCH_FAIL: 101 | return { 102 | ...state, 103 | loading: false, 104 | error: action.payload.error.response.data 105 | }; 106 | case APPLY_FILTERS_BEGIN: 107 | return { 108 | ...state, 109 | loading: true, 110 | error: null 111 | }; 112 | case APPLY_FILTERS_SUCCESS: 113 | return { 114 | ...state, 115 | loading: false, 116 | products: action.payload.data.products 117 | }; 118 | case APPLY_FILTERS_FAIL: 119 | return { 120 | ...state, 121 | loading: false, 122 | error: action.payload.error.response.data 123 | }; 124 | default: 125 | return state; 126 | } 127 | }; 128 | -------------------------------------------------------------------------------- /src/redux/reducers/variantsReducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { 8 | GET_VARIANTS_QUERY_BEGIN, 9 | GET_VARIANTS_QUERY_SUCCESS, 10 | GET_VARIANTS_QUERY_FAIL 11 | } from "../actions/variantsAction"; 12 | 13 | const initialState = { 14 | variants: null, 15 | loading: false, 16 | error: null 17 | }; 18 | 19 | export default (state = initialState, action) => { 20 | switch (action.type) { 21 | case GET_VARIANTS_QUERY_BEGIN: 22 | return { 23 | ...state, 24 | loading: true, 25 | error: null 26 | }; 27 | case GET_VARIANTS_QUERY_SUCCESS: 28 | return { 29 | ...state, 30 | loading: false, 31 | variants: action.payload.data.variants 32 | }; 33 | case GET_VARIANTS_QUERY_FAIL: 34 | return { 35 | ...state, 36 | loading: false, 37 | error: action.payload.error 38 | }; 39 | default: 40 | return state; 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /src/redux/store/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import rootReducer from "../reducers"; 8 | import { createStore, applyMiddleware, compose } from "redux"; 9 | import thunk from "redux-thunk"; 10 | 11 | const initialState = {}; 12 | const middlewares = [thunk]; 13 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 14 | 15 | const store = createStore( 16 | rootReducer, 17 | initialState, 18 | composeEnhancers(applyMiddleware(...middlewares)) 19 | ); 20 | 21 | export default store; 22 | -------------------------------------------------------------------------------- /src/routes/HomeRoutes.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import { Redirect } from "react-router-dom"; 9 | 10 | // Layout Types 11 | import BaseLayout from "../layouts/BaseLayout"; 12 | 13 | // Route Views 14 | import Home from "../views/Home/HomeContainer"; 15 | import SingleProductContainer from "../views/Product/SingleProductContainer"; 16 | import CategoryContainer from "../views/Category/CategoryContainer"; 17 | 18 | var routes = [ 19 | { 20 | path: "/", 21 | exact: true, 22 | layout: BaseLayout, 23 | component: Home, 24 | }, 25 | 26 | { 27 | path: "/home", 28 | layout: BaseLayout, 29 | component: () => , 30 | }, 31 | { 32 | path: "/single-product/:id", 33 | layout: BaseLayout, 34 | component: SingleProductContainer, 35 | }, 36 | { 37 | path: "/shops/:category", 38 | layout: BaseLayout, 39 | component: CategoryContainer, 40 | }, 41 | ]; 42 | 43 | export default routes; 44 | -------------------------------------------------------------------------------- /src/routes/PrivateRoutes.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | // Route Views 8 | import React, { Component } from "react"; 9 | import { Redirect } from "react-router-dom"; 10 | 11 | // Layout Types 12 | import BaseLayout from "../layouts/BaseLayout"; 13 | 14 | import CartContainer from "../views/Cart/CartContainer"; 15 | import Checkout from "../views/Checkout"; 16 | import ThankYou from "../views/ThankYou"; 17 | 18 | var PrivateRoutes = [ 19 | { 20 | path: "/cart", 21 | layout: BaseLayout, 22 | component: CartContainer, 23 | }, 24 | { 25 | path: "/checkout", 26 | layout: BaseLayout, 27 | component: Checkout, 28 | }, 29 | { 30 | path: "/thank-you", 31 | layout: BaseLayout, 32 | component: ThankYou, 33 | }, 34 | 35 | 36 | ]; 37 | 38 | export default PrivateRoutes; 39 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import { 9 | BrowserRouter as Router, 10 | Redirect, 11 | Route, 12 | Switch 13 | } from "react-router-dom"; 14 | import { registerNav } from "../modules/Navigation"; 15 | import { createBrowserHistory } from "history"; 16 | import PageNotFound from "../views/PageNotFound"; 17 | import HomeRoutes from "./HomeRoutes"; 18 | import PrivateRoutes from "./PrivateRoutes"; 19 | import Auth from "../modules/Auth"; 20 | 21 | const PrivateRouter = ({ component, ...options }) => { 22 | const finalComponent = 23 | Auth.getUserDetails() !== undefined && 24 | Auth.getUserDetails() !== null && 25 | Auth.getToken() !== undefined ? ( 26 | 27 | ) : ( 28 | 29 | ); 30 | 31 | return finalComponent; 32 | }; 33 | 34 | class Routes extends Component { 35 | constructor(props) { 36 | super(props); 37 | this.state = {}; 38 | } 39 | 40 | render() { 41 | const browserHistory = createBrowserHistory(); 42 | 43 | return ( 44 |
45 | 46 | 47 | {HomeRoutes.map((homeRoute, index) => { 48 | return ( 49 | { 54 | return ( 55 | 56 | 57 | 58 | ); 59 | }} 60 | /> 61 | ); 62 | })} 63 | {PrivateRoutes.map((privateRoute, index) => { 64 | return ( 65 | { 70 | return ( 71 | 72 | 73 | 74 | ); 75 | }} 76 | /> 77 | ); 78 | })} 79 | 80 | 81 | 82 |
83 | ); 84 | } 85 | } 86 | 87 | export default Routes; 88 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' } 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /src/utils/CalculateTax.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | const CalculateTax = subtotal => { 8 | return { 9 | taxes: parseFloat((subtotal * 0.14).toFixed(2)), 10 | total: parseFloat((subtotal * 1.14).toFixed(2)) 11 | }; 12 | }; 13 | export default CalculateTax; 14 | -------------------------------------------------------------------------------- /src/utils/Validator/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | const validate = (value, rules) => { 8 | let isValid = true; 9 | 10 | for (let rule in rules) { 11 | switch (rule) { 12 | case "minLength": 13 | isValid = isValid && minLengthValidator(value, rules[rule]); 14 | break; 15 | 16 | case "maxLength": 17 | isValid = isValid && maxLengthValidator(value, rules[rule]); 18 | break; 19 | 20 | case "isRequired": 21 | isValid = isValid && requiredValidator(value); 22 | break; 23 | 24 | case "isEmail": 25 | isValid = isValid && emailValidator(value); 26 | break; 27 | 28 | default: 29 | isValid = true; 30 | } 31 | } 32 | 33 | return isValid; 34 | }; 35 | 36 | /** 37 | * minLength Val 38 | * @param value 39 | * @param minLength 40 | * @return 41 | */ 42 | const minLengthValidator = (value, minLength) => { 43 | return value.length >= minLength; 44 | }; 45 | 46 | /** 47 | * minLength Val 48 | * @param value 49 | * @param minLength 50 | * @return 51 | */ 52 | const maxLengthValidator = (value, maxLength) => { 53 | return value.length == maxLength; 54 | }; 55 | 56 | /** 57 | * Check to confirm that feild is required 58 | * 59 | * @param value 60 | * @return 61 | */ 62 | const requiredValidator = value => { 63 | return value !== null && value.trim() !== ""; 64 | }; 65 | 66 | /** 67 | * Email validation 68 | * 69 | * @param value 70 | * @return 71 | */ 72 | const emailValidator = value => { 73 | var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 74 | return re.test(String(value).toLowerCase()); 75 | }; 76 | 77 | /** 78 | * Name validation 79 | * 80 | * @param value 81 | * @return 82 | */ 83 | const nameValidator = value => { 84 | var re = /^[a-zA-Z]+((['. ][a-zA-Z ])?[a-zA-Z]*)*$/; 85 | return re.test(String(value).toLowerCase()); 86 | }; 87 | /** 88 | * Name validation 89 | * 90 | * @param value 91 | * @return 92 | */ 93 | const mobileValidator = value => { 94 | var re = /^[0-9]*$/; 95 | return re.test(String(value).toLowerCase()); 96 | }; 97 | /** 98 | * IP validation 99 | * 100 | * @param value 101 | * @return 102 | */ 103 | const ipValidator = value => { 104 | var re = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/; 105 | return re.test(String(value)); 106 | }; 107 | /** 108 | * IP validation 109 | * 110 | * @param value 111 | * @return 112 | */ 113 | const dateValidator = value => { 114 | var re = /^((0?[13578]|10|12)(-|\/)(([1-9])|(0[1-9])|([12])([0-9]?)|(3[01]?))(-|\/)((19)([2-9])(\d{1})|(20)([01])(\d{1})|([8901])(\d{1}))|(0?[2469]|11)(-|\/)(([1-9])|(0[1-9])|([12])([0-9]?)|(3[0]?))(-|\/)((19)([2-9])(\d{1})|(20)([01])(\d{1})|([8901])(\d{1})))$/; 115 | return re.test(String(value)); 116 | }; 117 | 118 | export default validate; 119 | -------------------------------------------------------------------------------- /src/utils/Validator/rule/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | export const DEFAULT_RULE = { 8 | isRequired: true 9 | }; 10 | export const NAME_RULE = { 11 | isRequired: true, 12 | minLength: 3 13 | }; 14 | export const PHONE_RULE = { 15 | isRequired: true, 16 | maxLength: 15, 17 | minLength: 10 18 | }; 19 | export const EMAIL_RULE = { 20 | isRequired: true, 21 | isEmail: true 22 | }; 23 | -------------------------------------------------------------------------------- /src/utils/generateFilterString.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | export default state => { 8 | let result = ""; 9 | let order = ""; 10 | let department = ""; 11 | let range = ""; 12 | for (let name in state) { 13 | // order filter 14 | if (name === "order") { 15 | let temp = ""; 16 | for (const v of state[name]) { 17 | temp = v; 18 | } 19 | if (temp.toUpperCase() === "ASCENDING") { 20 | order = "order=price"; 21 | } 22 | if (temp.toUpperCase() === "DESCENDING") { 23 | order = "order=-price"; 24 | } 25 | } 26 | // department filter 27 | if (name === "department") { 28 | let _base_str = ""; 29 | if (state[name].length > 1) { 30 | _base_str = "&department="; 31 | } else { 32 | _base_str = "department="; 33 | } 34 | for (let d of state[name]) { 35 | department += _base_str + d.charAt(0).concat(d.slice(1).toLowerCase()); 36 | } 37 | } 38 | // price filter 39 | if (name === "price") { 40 | let _price_str_arr = []; 41 | for (let p of state[name]) { 42 | if (p.match(/less/i)) { 43 | p = p.replace(/[\D]+/i, "0 - "); 44 | } 45 | if (p.match(/greater/i)) { 46 | p = p.replace(/[\D]+/i, "").concat(" - 999"); 47 | } 48 | _price_str_arr = _price_str_arr.concat(p.match(/[\d]+/g)); 49 | } 50 | // sort the array 51 | let sorted_matched_arr = _price_str_arr.sort(function(a, b) { 52 | return a - b; 53 | }); 54 | // remove duplicates 55 | for (let index = 0; index < sorted_matched_arr.length; index++) { 56 | if (sorted_matched_arr[index] === sorted_matched_arr[index + 1]) { 57 | sorted_matched_arr.splice(index, 2); 58 | index--; 59 | } 60 | } 61 | // generate price range string 62 | for (let index = 0; index < sorted_matched_arr.length; index++) { 63 | if (index % 2 === 0) { 64 | if (range) { 65 | range += `&range=${sorted_matched_arr[index]}-${ 66 | sorted_matched_arr[index + 1] 67 | }`; 68 | } else { 69 | range += `range=${sorted_matched_arr[index]}-${ 70 | sorted_matched_arr[index + 1] 71 | }`; 72 | } 73 | } 74 | } 75 | } 76 | } 77 | if (range) { 78 | result += range; 79 | } 80 | if (order) { 81 | if (result) { 82 | order = `&${order}`; 83 | } 84 | result += order; 85 | } 86 | if (department) { 87 | if (result) { 88 | department = `&${department}`; 89 | } 90 | result += department; 91 | } 92 | // console.log(result); 93 | return result; 94 | }; 95 | -------------------------------------------------------------------------------- /src/views/Cart/Cart.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import Heading from "../../components/Heading"; 9 | import CartItem from "./CartItem"; 10 | import { Button } from "react-bootstrap"; 11 | import CalculateTax from "../../utils/CalculateTax"; 12 | import EmptyCart from "../../assets/images/empty_cart.png"; 13 | 14 | class Cart extends Component { 15 | constructor(props) { 16 | super(props); 17 | this.state = {}; 18 | } 19 | render() { 20 | const { totalPrice, items } = this.props.cart; 21 | const { postCart } = this.props; 22 | return ( 23 |
24 |
25 |
26 |
27 | 28 |
29 |
30 | 33 | postCart(pid, increase, decrease) 34 | } 35 | /> 36 | {items !== undefined && items !== null ? ( 37 |
41 |

42 | SubTotal :{" "} 43 | ₹{totalPrice} 44 |

45 |

46 | Shipping : Free 47 |

48 | 49 |

50 | Taxes :{" "} 51 | 52 | ₹ {CalculateTax(totalPrice).taxes} 53 | 54 |

55 | 56 |

57 | Total:{" "} 58 | 59 | ₹ {CalculateTax(totalPrice).total} 60 | 61 |

62 |
63 | 64 | 67 |
68 | ) : ( 69 |
70 | 76 |
77 | )} 78 |
79 |
80 |
81 | ); 82 | } 83 | } 84 | 85 | export default Cart; 86 | -------------------------------------------------------------------------------- /src/views/Cart/CartContainer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { connect } from "react-redux"; 8 | import Cart from "./Cart"; 9 | import { getCartByUserId, postCart } from "../../redux/actions/cartAction"; 10 | 11 | const mapStoreToProps = (state) => ({ 12 | cart: state.cart.cart, 13 | }); 14 | const mapDispatchToProps = (dispatch) => ({ 15 | getCartByUserId: dispatch(getCartByUserId()), 16 | postCart: (pid, increase, decrease) => 17 | dispatch(postCart(pid, increase, decrease)), 18 | }); 19 | 20 | export default connect(mapStoreToProps, mapDispatchToProps)(Cart); 21 | -------------------------------------------------------------------------------- /src/views/Cart/CartItem.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | 9 | function CartItem({ items, handleClick }) { 10 | return ( 11 |
12 | {items !== undefined && 13 | items !== null && 14 | Object.keys(items).map(id => ( 15 |
16 |
17 |
18 | 23 |
24 |
25 |
26 |
30 | {items[id].item.title} 31 |
32 |
36 | Quantity: {items[id].qty} 37 |
38 |
42 | {" "} 43 | Price: ₹{items[id].price} 44 |
45 |
46 |
47 |
48 | Quantity: 49 |
50 | handleClick(id, false, true)} 53 | > 54 | 55 | 56 | {items[id].qty} 57 | handleClick(id, true, false)} 60 | > 61 | 62 | 63 |
64 |
65 |
66 |
67 | ))} 68 |
69 | ); 70 | } 71 | 72 | export default CartItem; 73 | -------------------------------------------------------------------------------- /src/views/Category/Category.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import SingleProduct from "../../components/Products/SingleProduct"; 9 | import Auth from "../../modules/Auth"; 10 | import LoginRegister from "../../components/LoginRegisterModal"; 11 | import Filter from "./components/Filter"; 12 | 13 | class Category extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | products: this.props.products, 18 | productsBAK: this.props.products, 19 | departments: this.props.departments, 20 | modalShow: false, 21 | login: true 22 | }; 23 | this.addToBag = this.addToBag.bind(this); 24 | } 25 | componentDidMount() { 26 | if (!this.props.products) { 27 | this.props.getAllProducts(); 28 | } 29 | } 30 | showHideModal = () => { 31 | this.setState({ modalShow: false }); 32 | }; 33 | 34 | loginClicked = () => { 35 | this.setState({ modalShow: true, login: true }); 36 | }; 37 | registerClicked = () => { 38 | this.setState({ modalShow: true, login: false }); 39 | }; 40 | 41 | addToBag = params => { 42 | if ( 43 | Auth.getUserDetails() !== undefined && 44 | Auth.getUserDetails() !== null && 45 | Auth.getToken() !== undefined 46 | ) { 47 | let cart = this.props.postCart(params); 48 | cart.then(res => { 49 | console.log(res); 50 | }); 51 | } else { 52 | this.setState({ modalShow: true }); 53 | } 54 | }; 55 | 56 | render() { 57 | const { products, applyFilters } = this.props; 58 | console.log(this.props); 59 | return ( 60 |
61 |
62 |
63 | 82 | 83 |
84 | 85 |
86 |
87 |
88 |
89 |
90 |
91 |
    92 |
  • 93 | Default Sorting 94 | 95 |
      96 |
    • 100 | Default Sorting 101 |
    • 102 |
    • 106 | Price 107 |
    • 108 |
    • 112 | Product Name 113 |
    • 114 |
    115 |
  • 116 |
  • 117 | Show 118 | 6 119 | 120 |
      121 |
    • 122 | 6 123 |
    • 124 |
    • 125 | 12 126 |
    • 127 |
    • 128 | 24 129 |
    • 130 |
    131 |
  • 132 |
133 |
134 |
135 | 1 136 |
    137 |
  • 138 | 1 139 |
  • 140 |
  • 141 | 2 142 |
  • 143 |
  • 144 | 3 145 |
  • 146 |
147 |
148 |
149 | of 3 150 |
151 |
152 | 153 | 154 | 155 |
156 |
157 |
158 |
159 |
160 | 161 |
162 | {products && 163 | products.slice(0, 8).map((item, index) => { 164 | return ( 165 |
170 | 174 |
175 | ); 176 | })} 177 |
178 |
179 |
    180 |
  • 181 | Show: 182 | 04 183 | 184 |
      185 |
    • 186 | 01 187 |
    • 188 |
    • 189 | 02 190 |
    • 191 |
    • 192 | 03 193 |
    • 194 |
    • 195 | 04 196 |
    • 197 |
    198 |
  • 199 |
200 | Showing 1–3 of 12 results 201 |
202 |
203 | 1 204 |
    205 |
  • 206 | 1 207 |
  • 208 |
  • 209 | 2 210 |
  • 211 |
  • 212 | 3 213 |
  • 214 |
215 |
216 |
217 | of 3 218 |
219 |
220 | 221 | 225 | 226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 | this.registerClicked()} 237 | loginClicked={() => this.loginClicked()} 238 | onHide={() => this.showHideModal()} 239 | /> 240 |
241 | ); 242 | } 243 | } 244 | 245 | export default Category; 246 | -------------------------------------------------------------------------------- /src/views/Category/CategoryContainer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { 8 | getAllProducts, 9 | applyFilters 10 | } from "../../redux/actions/productAction"; 11 | import { connect } from "react-redux"; 12 | import Category from "./Category"; 13 | import { postCart } from "../../redux/actions/cartAction"; 14 | 15 | const mapStoreToProps = state => ({ 16 | products: state.product.products, 17 | loading: state.product.loading 18 | }); 19 | const mapDispatchToProps = dispatch => ({ 20 | getAllProducts: () => dispatch(getAllProducts()), 21 | applyFilters: filter_string => dispatch(applyFilters(filter_string)), 22 | postCart: productId => dispatch(postCart(productId)) 23 | }); 24 | 25 | export default connect(mapStoreToProps, mapDispatchToProps)(Category); 26 | -------------------------------------------------------------------------------- /src/views/Category/components/Filter.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | 9 | class Filter extends Component { 10 | constructor(props) { 11 | super(props); 12 | this.state = {}; 13 | } 14 | render() { 15 | return ( 16 | 46 | ); 47 | } 48 | } 49 | 50 | export default Filter; 51 | -------------------------------------------------------------------------------- /src/views/Home/Home.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import { login } from "../../ServerRequest"; 9 | import API from "../../axios/API"; 10 | import Auth from "../../modules/Auth"; 11 | import HomeBanner from "../../components/HomeBanner"; 12 | import CategoryBanner from "../../components/CategoryBanner/CategoryBanner"; 13 | import NewArrivals from "../../components/Products/NewArrivals"; 14 | import BestSeller from "../../components/Products/BestSeller"; 15 | import Benefit from "../../components/Benefit"; 16 | import Advertisement from "../../components/Advertisement"; 17 | import PropTypes from "prop-types"; 18 | import jumpTo from "../../modules/Navigation"; 19 | import LoginRegister from "../../components/LoginRegisterModal"; 20 | 21 | class Home extends Component { 22 | constructor(props) { 23 | super(props); 24 | this.state = { 25 | data: null, 26 | modalShow: false, 27 | login: true 28 | }; 29 | this.addToBag = this.addToBag.bind(this); 30 | } 31 | 32 | componentDidMount() { 33 | if (!this.props.products) { 34 | this.props.getAllProducts(); 35 | } 36 | } 37 | 38 | showHideModal = () => { 39 | this.setState({ modalShow: false }); 40 | }; 41 | 42 | loginClicked = () => { 43 | this.setState({ modalShow: true, login: true }); 44 | }; 45 | registerClicked = () => { 46 | this.setState({ modalShow: true, login: false }); 47 | }; 48 | 49 | addToBag = params => { 50 | if ( 51 | Auth.getUserDetails() !== undefined && 52 | Auth.getUserDetails() !== null && 53 | Auth.getToken() !== undefined 54 | ) { 55 | let cart = this.props.postCart(params); 56 | cart.then(res => { 57 | console.log(res); 58 | }); 59 | } else { 60 | this.setState({ modalShow: true }); 61 | } 62 | }; 63 | 64 | render() { 65 | const { products, departments } = this.props; 66 | return ( 67 |
68 | 69 | 70 | {products ? ( 71 | 76 | ) : null} 77 | 78 | 79 | {products ? ( 80 | 85 | ) : null} 86 | this.registerClicked()} 90 | loginClicked={() => this.loginClicked()} 91 | onHide={() => this.showHideModal()} 92 | /> 93 |
94 | ); 95 | } 96 | } 97 | 98 | export default Home; 99 | -------------------------------------------------------------------------------- /src/views/Home/HomeContainer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { 8 | getAllProducts, 9 | applyFilters 10 | } from "../../redux/actions/productAction"; 11 | import { connect } from "react-redux"; 12 | import Home from "./Home"; 13 | import { postCart } from "../../redux/actions/cartAction"; 14 | 15 | const mapStoreToProps = state => ({ 16 | products: state.product.products, 17 | departments: state.department.departments, 18 | loading: state.product.loading 19 | }); 20 | const mapDispatchToProps = dispatch => ({ 21 | getAllProducts: () => dispatch(getAllProducts()), 22 | applyFilters: filter_string => dispatch(applyFilters(filter_string)), 23 | postCart: productId => dispatch(postCart(productId)) 24 | }); 25 | 26 | export default connect(mapStoreToProps, mapDispatchToProps)(Home); 27 | -------------------------------------------------------------------------------- /src/views/PageNotFound.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | 9 | class PageNotFound extends Component { 10 | constructor(props) { 11 | super(props); 12 | this.state = {}; 13 | } 14 | render() { 15 | return ( 16 |
17 |
18 |
19 |

Oops!

20 |

404 - PAGE NOT FOUND

21 |
22 |
23 |

24 | The page you are looking for might have been removed had its name 25 | changed or is temporarily unavailable. 26 |

27 | 28 | 32 | GOTO HOME PAGE 33 | 34 |
35 |
36 |
37 | ); 38 | } 39 | } 40 | 41 | export default PageNotFound; 42 | -------------------------------------------------------------------------------- /src/views/Product/SingleProduct.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import React, { Component } from "react"; 8 | import LoginRegister from "../../components/LoginRegisterModal"; 9 | import Auth from "../../modules/Auth"; 10 | 11 | class SingleProduct extends Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | color: "", 16 | size: "", 17 | pic: "", 18 | selectedSize: "", 19 | id: "", 20 | quantity: 1, 21 | modalShow: false, 22 | login: true, 23 | }; 24 | } 25 | componentDidMount() { 26 | this.props.getProduct(this.props.location.pathname.split("/").slice(-1)[0]); 27 | this.props.getVariantsByProductId( 28 | this.props.location.pathname.split("/").slice(-1)[0] 29 | ); 30 | } 31 | 32 | showHideModal = () => { 33 | this.setState({ modalShow: false }); 34 | }; 35 | 36 | loginClicked = () => { 37 | this.setState({ modalShow: true, login: true }); 38 | }; 39 | registerClicked = () => { 40 | this.setState({ modalShow: true, login: false }); 41 | }; 42 | 43 | handleThumbnailClick = (item) => { 44 | this.setState({ 45 | color: item.color, 46 | size: item.size, 47 | pic: item.imagePath, 48 | selectedSize: "", 49 | id: item._id, 50 | cartItem: null, 51 | }); 52 | }; 53 | 54 | onAddClicked = () => { 55 | this.setState({ quantity: this.state.quantity + 1 }); 56 | this.props.postCart( 57 | this.state.id || this.props.location.pathname.split("/").slice(-1)[0], 58 | true, 59 | false 60 | ); 61 | }; 62 | onRemoveClicked = () => { 63 | this.props.postCart( 64 | this.state.id || this.props.location.pathname.split("/").slice(-1)[0], 65 | false, 66 | true 67 | ); 68 | 69 | if (this.state.quantity > 1) { 70 | this.setState({ quantity: this.state.quantity - 1 }); 71 | } 72 | }; 73 | 74 | addToBag = () => { 75 | if ( 76 | Auth.getUserDetails() !== undefined && 77 | Auth.getUserDetails() !== null && 78 | Auth.getToken() !== undefined 79 | ) { 80 | this.props 81 | .postCart( 82 | this.state.id || this.props.location.pathname.split("/").slice(-1)[0] 83 | ) 84 | .then((res) => { 85 | console.log(res); 86 | }); 87 | } else { 88 | this.setState({ modalShow: true }); 89 | } 90 | }; 91 | 92 | productInCart = () => { 93 | let available = false; 94 | // let items = this.props.cart.items; 95 | // if (items !== undefined && items !== null) { 96 | // let itemCheck = Object.keys(items).map( 97 | // id => items[id].item.title === this.props.product.title 98 | // ); 99 | 100 | // if (itemCheck !== undefined && itemCheck !== null) { 101 | // this.setState({ cartItem: itemCheck }); 102 | // available = true; 103 | // } else { 104 | // available = false; 105 | // } 106 | // } 107 | 108 | return available; 109 | }; 110 | 111 | render() { 112 | console.log(this.props); 113 | return ( 114 |
115 | {this.props.product && ( 116 |
117 |
118 |
119 |
120 | 137 |
138 |
139 |
140 | 141 |
142 |
143 |
144 |
145 |
146 |
147 |
    148 | {this.props.variants && 149 | this.props.variants 150 | .slice(0, 4) 151 | .map((item, index) => ( 152 |
  • 155 | this.handleThumbnailClick(item) 156 | } 157 | > 158 | 163 |
  • 164 | ))} 165 |
166 |
167 |
168 |
169 |
170 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |

{this.props.product.title}

187 |

{this.props.product.description}

188 |
189 |
190 | 191 | 192 | 193 | free delivery 194 |
195 |
196 | {" "} 197 | ₹ {(parseFloat(this.props.product.price) + 30).toFixed(2)} 198 |
199 |
200 | ₹ {this.props.product.price} 201 |
202 |
    203 |
  • 204 | 205 |
  • 206 |
  • 207 | 208 |
  • 209 |
  • 210 | 211 |
  • 212 |
  • 213 | 214 |
  • 215 |
  • 216 | 217 |
  • 218 |
219 |
220 | Select Color: 221 |
    222 |
  • 223 |
  • 224 |
  • 225 |
226 |
227 |
228 | Quantity: 229 | 230 |
231 | 1 ? "minus" : "minus disabled" 234 | } 235 | onClick={() => this.onRemoveClicked()} 236 | > 237 | 238 | 239 | {this.state.quantity} 240 | this.onAddClicked()} 243 | > 244 | 245 | 246 |
247 | 248 |
252 | add to cart 253 |
254 | 255 | {/*
256 | 257 | 258 | 259 |
*/} 260 | 261 |
262 | 263 |
264 |
265 |
266 |
267 |
268 |
269 | )} 270 | 271 | this.registerClicked()} 275 | loginClicked={() => this.loginClicked()} 276 | onHide={() => this.showHideModal()} 277 | /> 278 |
279 | ); 280 | } 281 | } 282 | 283 | export default SingleProduct; 284 | -------------------------------------------------------------------------------- /src/views/Product/SingleProductContainer.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Author: Santosh Kumar Dash 3 | ** Author URL: http://santoshdash.epizy.com/ 4 | ** Github URL: https://github.com/quintuslabs/ 5 | */ 6 | 7 | import { connect } from "react-redux"; 8 | import SingleProduct from "./SingleProduct"; 9 | import { getProduct } from "../../redux/actions/productAction"; 10 | import { getVariantsByProductId } from "../../redux/actions/variantsAction"; 11 | import { postCart } from "../../redux/actions/cartAction"; 12 | 13 | const mapStoreToProps = (state) => ({ 14 | product: state.product.product, 15 | variants: state.variant.variants, 16 | }); 17 | const mapDispatchToProps = { 18 | getProduct, 19 | getVariantsByProductId, 20 | postCart, 21 | }; 22 | 23 | export default connect(mapStoreToProps, mapDispatchToProps)(SingleProduct); 24 | -------------------------------------------------------------------------------- /src/views/ThankYou.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | 3 | class ThankYou extends Component { 4 | render() { 5 | return ( 6 |
7 |
8 |
9 |
10 | 11 | 12 | 13 |

Thank you!

14 |

You order was successfuly completed.

15 |

Back to 16 | shop

17 |
18 |
19 |
20 |
21 | ); 22 | } 23 | } 24 | 25 | export default ThankYou; 26 | -------------------------------------------------------------------------------- /src/views/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendsourcecode/fashion-cube/8a65f7804af80179df1d032606757605f3fee6a0/src/views/index.js --------------------------------------------------------------------------------