├── .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 |
5 |
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 |
5 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
49 |
{this.props.children}
50 |
51 |
52 |
53 | );
54 | }
55 | }
56 |
57 | export default BaseLayout;
58 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
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 |
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 |
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 |
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 |
254 |
255 | {/*
*/}
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
--------------------------------------------------------------------------------