├── public ├── robots.txt ├── favicon.ico ├── img │ ├── Vue-Icons.png │ └── icons │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── mstile-150x150.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── msapplication-icon-144x144.png │ │ └── safari-pinned-tab.svg ├── manifest.json └── index.html ├── .env.development ├── .env.production ├── src ├── assets │ ├── logo.png │ ├── login-img.png │ └── create-account.svg ├── components │ ├── products │ │ ├── cart │ │ │ ├── ShippingDetails.vue │ │ │ ├── CartCalculator.vue │ │ │ ├── Checkout.vue │ │ │ └── CartProducts.vue │ │ ├── actions │ │ │ ├── CreateProduct.vue │ │ │ ├── EditProduct.vue │ │ │ └── ProductForm.vue │ │ ├── ProductsList.vue │ │ ├── BestProducts.vue │ │ ├── TopProducts.vue │ │ ├── filters │ │ │ └── ProductFilter.vue │ │ └── ProductDetail.vue │ ├── shared │ │ ├── service │ │ │ ├── ErrorHandler.js │ │ │ └── authService.js │ │ ├── CardLoader.vue │ │ ├── Modal.vue │ │ └── CardTemplate.vue │ ├── checkout │ │ └── CheckoutModal.vue │ ├── HelloWorld.vue │ └── CreateAccount.vue ├── main.js ├── store.js ├── registerServiceWorker.js ├── views │ ├── Home.vue │ ├── About.vue │ ├── Products.vue │ └── login.vue ├── router.js └── App.vue ├── babel.config.js ├── .gitignore ├── server ├── mongo │ ├── model │ │ ├── user.js │ │ ├── shipping-detail.js │ │ └── product.js │ └── config.js └── routes │ ├── authApi.js │ ├── shippingDetailApi.js │ ├── api.js │ └── productApi.js ├── .github └── FUNDING.yml ├── LICENSE.md ├── server.js ├── package.json └── README.md /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | VUE_APP_BASE_URL=http://localhost:8080/api -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VUE_APP_BASE_URL=https://vue-shop-cart.herokuapp.com/api -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /public/img/Vue-Icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/Vue-Icons.png -------------------------------------------------------------------------------- /src/assets/login-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/src/assets/login-img.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikismail/Vue-ShoppingCart/HEAD/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | 23 | # Config 24 | /server/mongo/configDummy.js -------------------------------------------------------------------------------- /server/mongo/model/user.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"); 2 | var Schema = mongoose.Schema; 3 | 4 | var UserSchema = new Schema({ 5 | firstName: String, 6 | lastName: String, 7 | fullName: String, 8 | email: String, 9 | isAdmin: Boolean, 10 | password: String, 11 | createdOn: String, 12 | }); 13 | 14 | module.exports = mongoose.model("User", UserSchema); 15 | -------------------------------------------------------------------------------- /src/components/products/cart/ShippingDetails.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /server/mongo/model/shipping-detail.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"); 2 | var Schema = mongoose.Schema; 3 | 4 | var ShippingDetailSchema = new Schema({ 5 | address1: String, 6 | address2: String, 7 | country: String, 8 | zipCode: String, 9 | shippingDate: String, 10 | products: Array, 11 | userId: String, 12 | totalPrice: String, 13 | }); 14 | 15 | module.exports = mongoose.model("ShippingDetail", ShippingDetailSchema); 16 | -------------------------------------------------------------------------------- /server/mongo/model/product.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"); 2 | var Schema = mongoose.Schema; 3 | 4 | var ProductSchema = new Schema({ 5 | productName: String, 6 | productDescription: String, 7 | productCategory: String, 8 | productPrice: String, 9 | productImage: String, 10 | productSeller: String, 11 | isBestProduct: Boolean, 12 | isTopProduct: Boolean, 13 | productRating: Number, 14 | }); 15 | 16 | module.exports = mongoose.model("Product", ProductSchema); 17 | -------------------------------------------------------------------------------- /src/components/shared/service/ErrorHandler.js: -------------------------------------------------------------------------------- 1 | /* Handling Errors through Toastr */ 2 | 3 | 4 | import toastr from "toastr"; 5 | 6 | export const successToaster = (title, desc) => { 7 | return (toastr.success(desc, title)) 8 | } 9 | export const errorToaster = (title, desc) => { 10 | return (toastr.error(desc, title)) 11 | } 12 | 13 | export const warnToaster = (title, desc) => { 14 | return (toastr.warning(desc, title)) 15 | } 16 | 17 | export const infoToaster = (title, desc) => { 18 | return (toastr.info(desc, title)) 19 | } -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-shopping-cart", 3 | "short_name": "vue-shopping-cart", 4 | "icons": [ 5 | { 6 | "src": "/img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "/index.html", 17 | "display": "standalone", 18 | "background_color": "#000000", 19 | "theme_color": "#4DBA87" 20 | } 21 | -------------------------------------------------------------------------------- /src/components/checkout/CheckoutModal.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /server/mongo/config.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | mongoose.connect( 4 | "mongodb+srv://read-only:mlabreadonly@hifdth-track.x6ijp.mongodb.net/vue-shop", 5 | { 6 | useNewUrlParser: true, 7 | useUnifiedTopology: true, 8 | } 9 | ); // connect to our database 10 | 11 | // db.on('error', console.error.bind(console, 'connection error:')); 12 | const db = mongoose.connection; 13 | db.once("open", () => console.info("Connected to MongoDB Sample")); 14 | db.on("error", console.error.bind(console, "connection error:")); 15 | 16 | module.exports = mongoose; 17 | -------------------------------------------------------------------------------- /src/components/shared/CardLoader.vue: -------------------------------------------------------------------------------- 1 | 14 | 23 | 24 | -------------------------------------------------------------------------------- /server/routes/authApi.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | const UserModel = require("../mongo/model/user"); 5 | 6 | router.route("/login").post((req, res) => { 7 | UserModel.find( 8 | { 9 | email: req.body.email, 10 | password: req.body.password, 11 | }, 12 | function(err, user) { 13 | if (err) { 14 | res.send(err); 15 | } 16 | 17 | if (user.length === 0) { 18 | res.status(401).json({ 19 | status: 401, 20 | message: "Unauthorized credentials mismatch", 21 | }); 22 | } else { 23 | res.json(user); 24 | } 25 | } 26 | ); 27 | }); 28 | module.exports = router; 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | custom: ['paypal.me/ikismail7'] 14 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import store from "./store"; 5 | import "./registerServiceWorker"; 6 | import VueAwesomeSwiper from "vue-awesome-swiper"; 7 | import NProgress from "nprogress"; 8 | 9 | // require styles 10 | import "swiper/swiper.min.css"; 11 | import "../node_modules/nprogress/nprogress.css"; 12 | 13 | Vue.use(VueAwesomeSwiper); 14 | 15 | Vue.config.productionTip = false; 16 | 17 | router.beforeResolve((to, from, next) => { 18 | if (to.name) { 19 | NProgress.start(); 20 | } 21 | next(); 22 | }); 23 | 24 | router.afterEach(() => { 25 | NProgress.done(); 26 | }); 27 | // eslint-disable-next-line no-unused-vars 28 | let vm = new Vue({ 29 | router, 30 | store, 31 | render: (h) => h(App), 32 | }).$mount("#app"); 33 | -------------------------------------------------------------------------------- /src/components/products/actions/CreateProduct.vue: -------------------------------------------------------------------------------- 1 | 13 | 37 | 38 | -------------------------------------------------------------------------------- /src/components/products/actions/EditProduct.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 35 | 36 | -------------------------------------------------------------------------------- /src/assets/create-account.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import { 4 | encryptUser 5 | } from './components/shared/service/authService' 6 | 7 | Vue.use(Vuex) 8 | 9 | export default new Vuex.Store({ 10 | state: { 11 | cartProducts: [], 12 | loggedUser: {} 13 | }, 14 | getters: { 15 | cartProducts: state => { 16 | return state.cartProducts 17 | }, 18 | getLoggedUser: state => { 19 | return state.loggedUser 20 | } 21 | }, 22 | mutations: { 23 | ADD_CART_LOCAL: (state, product) => { 24 | state.cartProducts.push(product) 25 | localStorage.setItem('iki-cart', JSON.stringify(state.cartProducts)) 26 | }, 27 | 28 | ADD_LOGGED_USER: (state, user) => { 29 | state.loggedUser = user 30 | localStorage.setItem('_auth', encryptUser(user)) 31 | }, 32 | 33 | SET_CART_PRODUCTS: (state, products) => { 34 | state.cartProducts = [] 35 | state.cartProducts = products 36 | }, 37 | }, 38 | actions: {} 39 | }) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2022 Mohammed Ismail 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /src/components/products/ProductsList.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 35 | 36 | 49 | -------------------------------------------------------------------------------- /src/components/shared/service/authService.js: -------------------------------------------------------------------------------- 1 | /* Encryption User based on base64 */ 2 | export const encryptUser = (user) => { 3 | const objStr = JSON.stringify(user); 4 | const encryptedValue = new Buffer(objStr).toString("base64"); 5 | return encryptedValue; 6 | } 7 | 8 | /* Decrypting User based on base64*/ 9 | export const decryptUser = () => { 10 | const data = localStorage.getItem("_auth"); 11 | if (!data) { 12 | return null; 13 | } 14 | const strObj = new Buffer(data || "", "base64").toString("utf8"); 15 | const value = JSON.parse(strObj); 16 | return value; 17 | } 18 | 19 | /* Verify that person is loggedIn */ 20 | export const isLoggedIn = () => { 21 | const data = decryptUser(); 22 | if (data) { 23 | return true; 24 | } 25 | return false; 26 | } 27 | 28 | /* Verifying that the user is Admin or not */ 29 | export const isAdmin = () => { 30 | const data = decryptUser(); 31 | let hasAdmin = false; 32 | if (data["isAdmin"]) { 33 | hasAdmin = true; 34 | } 35 | return hasAdmin; 36 | } 37 | 38 | /* Returning the logged User */ 39 | export const getLoggedInUser = () => { 40 | const data = decryptUser(); 41 | return data; 42 | } -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { 4 | register 5 | } from 'register-service-worker' 6 | import { 7 | infoToaster, 8 | errorToaster 9 | } from './components/shared/service/ErrorHandler' 10 | 11 | if (process.env.NODE_ENV === 'production') { 12 | register(`${process.env.BASE_URL}service-worker.js`, { 13 | ready() { 14 | console.log( 15 | 'App is being served from cache by a service worker.\n' + 16 | 'For more details, visit https://goo.gl/AFskqB' 17 | ) 18 | }, 19 | cached() { 20 | console.log('Content has been cached for offline use.') 21 | }, 22 | updated() { 23 | console.log('New content is available; please refresh.') 24 | infoToaster('Please refresh', 'New content is available') 25 | }, 26 | offline() { 27 | console.log('No internet connection found. App is running in offline mode.') 28 | }, 29 | error(error) { 30 | console.error('Error during service worker registration:', error) 31 | errorToaster('Error during service worker registration', error) 32 | } 33 | }) 34 | } -------------------------------------------------------------------------------- /src/components/shared/Modal.vue: -------------------------------------------------------------------------------- 1 | 34 | 43 | 49 | -------------------------------------------------------------------------------- /server/routes/shippingDetailApi.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | // const mongodb = require('../mongo/config') 5 | 6 | const ShippingModel = require("../mongo/model/shipping-detail"); 7 | 8 | /* 9 | API for ShippingSchema Schema 10 | Product Create and Read all Policies API 's 11 | 12 | */ 13 | router 14 | .route("/shipping") 15 | 16 | // Create a new ShippingDetail 17 | .post((req, res) => { 18 | const shippingDetail = new ShippingModel(); // create a new instance of the Product model 19 | 20 | shippingDetail.address1 = req.body.address1; 21 | shippingDetail.address2 = req.body.address2; 22 | shippingDetail.country = req.body.country; 23 | shippingDetail.zipCode = req.body.zipCode; 24 | shippingDetail.shippingDate = req.body.shippingDate; 25 | shippingDetail.products = req.body.products; 26 | shippingDetail.userId = req.body.userId; 27 | shippingDetail.totalPrice = req.body.totalPrice; 28 | 29 | // save the bear and check for errors 30 | shippingDetail.save(shippingDetail, (err, shippingDetail) => { 31 | if (err) { 32 | res.send(err); 33 | } 34 | 35 | console.log("**********NEWLY CREATED SITEURL***********"); 36 | console.log(shippingDetail); 37 | res.send(shippingDetail); 38 | }); 39 | }); 40 | 41 | module.exports = router; 42 | -------------------------------------------------------------------------------- /src/components/products/cart/CartCalculator.vue: -------------------------------------------------------------------------------- 1 | 30 | 53 | 55 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 50 | 55 | -------------------------------------------------------------------------------- /src/components/products/BestProducts.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 56 | 57 | 62 | -------------------------------------------------------------------------------- /src/components/products/TopProducts.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 55 | 56 | 65 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | // Get dependencies 2 | const express = require("express"); 3 | const bodyParser = require("body-parser"); 4 | const history = require("connect-history-api-fallback"); 5 | // Get our API routes 6 | // eslint-disable-next-line no-unused-vars 7 | const mongodb = require("./server/mongo/config"); 8 | 9 | const api = require("./server/routes/api"); 10 | const productApi = require("./server/routes/productApi"); 11 | const ShippingDetailApi = require("./server/routes/shippingDetailApi"); 12 | const authApi = require("./server/routes/authApi"); 13 | 14 | const app = express(); 15 | 16 | // Parsers for POST data 17 | app.use(bodyParser.json()); 18 | app.use( 19 | bodyParser.urlencoded({ 20 | extended: false, 21 | }) 22 | ); 23 | 24 | // Create link to Angular build directory 25 | var distDir = __dirname + "/dist"; 26 | app.use(express.static(distDir)); 27 | 28 | /* Access Control Allow Origin */ 29 | app.use((req, res, next) => { 30 | // Website you wish to allow to connect 31 | res.setHeader("Access-Control-Allow-Origin", "*"); 32 | 33 | // Request methods you wish to allow 34 | res.setHeader( 35 | "Access-Control-Allow-Methods", 36 | "GET, POST, OPTIONS, PUT, PATCH, DELETE" 37 | ); 38 | 39 | // Request headers you wish to allow 40 | res.setHeader( 41 | "Access-Control-Allow-Headers", 42 | "X-Requested-With,content-type" 43 | ); 44 | 45 | // Set to true if you need the website to include cookies in the requests sent 46 | // to the API (e.g. in case you use sessions) 47 | res.setHeader("Access-Control-Allow-Credentials", true); 48 | 49 | next(); 50 | }); 51 | 52 | app.use(history({ index: "/index.html" })); 53 | 54 | app.use("/api", [api, productApi, authApi, ShippingDetailApi]); 55 | 56 | // Initialize the app. 57 | var server = app.listen(process.env.PORT || 8080, function() { 58 | var port = server.address().port; 59 | console.log("App now running on port", port); 60 | }); 61 | -------------------------------------------------------------------------------- /src/views/About.vue: -------------------------------------------------------------------------------- 1 | 48 | 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-shopping-cart", 3 | "version": "0.2.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "start": "nodemon server.js", 8 | "dev": "concurrently \"npm:start\" \"npm:serve --fix\"", 9 | "dev:build": "vue-cli-service build --mode development", 10 | "build": "vue-cli-service build", 11 | "lint": "vue-cli-service lint", 12 | "postinstall": "vue-cli-service build" 13 | }, 14 | "dependencies": { 15 | "axios": "^0.21.1", 16 | "body-parser": "^1.19.0", 17 | "bootstrap": "^4.6.0", 18 | "concurrently": "^6.0.0", 19 | "connect-history-api-fallback": "^1.6.0", 20 | "core-js": "^3.6.5", 21 | "express": "^4.17.1", 22 | "lodash": "^4.17.21", 23 | "mongoose": "^5.11.18", 24 | "nodemon": "^2.0.7", 25 | "nprogress": "^0.2.0", 26 | "register-service-worker": "^1.7.2", 27 | "swiper": "^6.4.15", 28 | "toastr": "^2.1.4", 29 | "vue": "^2.6.11", 30 | "vue-awesome-swiper": "^4.1.1", 31 | "vue-content-loading": "^1.6.0", 32 | "vue-router": "^3.5.1", 33 | "vuex": "^3.6.2" 34 | }, 35 | "devDependencies": { 36 | "@types/lodash": "^4.14.168", 37 | "@vue/cli-plugin-babel": "~4.5.0", 38 | "@vue/cli-plugin-eslint": "~4.5.0", 39 | "@vue/cli-service": "~4.5.0", 40 | "babel-eslint": "^10.1.0", 41 | "eslint": "^6.7.2", 42 | "eslint-plugin-vue": "^6.2.2", 43 | "node-sass": "^5.0.0", 44 | "sass-loader": "^11.0.1", 45 | "vue-template-compiler": "^2.6.11" 46 | }, 47 | "engines" : { 48 | "node" : "12" 49 | }, 50 | "eslintConfig": { 51 | "root": true, 52 | "env": { 53 | "node": true 54 | }, 55 | "extends": [ 56 | "plugin:vue/essential", 57 | "eslint:recommended" 58 | ], 59 | "parserOptions": { 60 | "parser": "babel-eslint" 61 | }, 62 | "rules": {} 63 | }, 64 | "browserslist": [ 65 | "> 1%", 66 | "last 2 versions", 67 | "not dead" 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Home from './views/Home.vue' 4 | import { 5 | isLoggedIn 6 | } from './components/shared/service/authService' 7 | 8 | Vue.use(Router) 9 | 10 | export default new Router({ 11 | mode: 'hash', 12 | base: process.env.BASE_URL, 13 | routes: [{ 14 | path: '/', 15 | name: 'home', 16 | component: Home 17 | }, 18 | { 19 | path: '/about', 20 | name: 'about', 21 | // route level code-splitting 22 | // this generates a separate chunk (about.[hash].js) for this route 23 | // which is lazy-loaded when the route is visited. 24 | component: () => import(/* webpackChunkName: "about" */ './views/About.vue') 25 | }, 26 | { 27 | path: '/products', 28 | name: 'products', 29 | component: () => import('./views/Products.vue') 30 | }, 31 | { 32 | path: '/products/:id', 33 | name: 'productDetails', 34 | component: () => import('./components/products/ProductDetail.vue') 35 | }, 36 | { 37 | path: '/cart', 38 | name: 'cart', 39 | component: () => import('./components/products/cart/CartProducts.vue'), 40 | beforeEnter: (to, from, next) => { 41 | if (isLoggedIn()) { 42 | next() 43 | } else { 44 | next({ 45 | name: 'login', 46 | query: { 47 | from: to.name 48 | } 49 | }) 50 | } 51 | } 52 | }, 53 | { 54 | path: '/checkout', 55 | name: 'checkout', 56 | component: () => import('./components/products/cart/Checkout.vue'), 57 | beforeEnter: (to, from, next) => { 58 | if (isLoggedIn()) { 59 | next() 60 | } else { 61 | next({ 62 | name: 'login', 63 | query: { 64 | from: to.name 65 | } 66 | }) 67 | } 68 | } 69 | }, 70 | { 71 | path: '/login', 72 | name: 'login', 73 | component: () => import('./views/login.vue') 74 | } 75 | ] 76 | }) 77 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 32 | vue-shopping-cart 33 | 34 | 35 | 36 | 42 |
43 | 44 | 45 | 50 | 55 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 42 | 43 | 44 | 60 | -------------------------------------------------------------------------------- /server/routes/api.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | const UserModel = require("../mongo/model/user"); 5 | 6 | /* GET api listing. */ 7 | router.get("/", (req, res) => { 8 | res.send("api works"); 9 | }); 10 | 11 | /* 12 | API for User Schema 13 | User Create and Read all Policies API's 14 | 15 | */ 16 | router 17 | .route("/users") 18 | 19 | // Create a new User 20 | .post((req, res) => { 21 | const user = new UserModel(); // create a new instance of the User model 22 | 23 | user.firstName = req.body.firstName; 24 | user.lastName = req.body.lastName; 25 | user.fullName = req.body.firstName + " " + req.body.lastName; 26 | user.password = req.body.password; 27 | user.email = req.body.email; 28 | user.isAdmin = false; 29 | user.createdOn = new Date().toLocaleString(); 30 | 31 | // save the bear and check for errors 32 | user.save(user, (err, user) => { 33 | if (err) { 34 | res.send(err); 35 | } 36 | console.log("**********NEWLY CREATED SITEURL***********"); 37 | res.send(user); 38 | }); 39 | }) 40 | // Get All Users 41 | .get((req, res) => { 42 | UserModel.find((err, data) => { 43 | if (err) { 44 | res.send(err); 45 | } 46 | res.json(data); 47 | }); 48 | }); 49 | 50 | /* USER Update and Read by Id and delete users API's */ 51 | // on routes that end in /users/:user_id 52 | // ---------------------------------------------------- 53 | router 54 | .route("/users/:user_id") 55 | 56 | // get the user with that id (accessed at GET http://localhost:8080/api/users/:user_id) 57 | .get(function(req, res) { 58 | UserModel.findById(req.params.user_id, function(err, data) { 59 | if (err) { 60 | res.send(err); 61 | } 62 | res.json(data); 63 | }); 64 | }) 65 | 66 | // update the user with this id (accessed at PUT http://localhost:8080/api/users/:user_id) 67 | .put(function(req, res) { 68 | // use our user model to find the user we want 69 | UserModel.findById(req.params.user_id, function(err, user) { 70 | if (err) { 71 | res.send(err); 72 | } 73 | 74 | user.name = req.body.name; 75 | user.password = req.body.password; 76 | user.email = req.body.email; 77 | user.isAdmin = false; 78 | user.user_avatar = req.body.user_avatar; 79 | user.phoneNumber = req.body.phoneNumber; 80 | user.createdOn = req.body.createdOn; 81 | 82 | // save the bear 83 | user.save(function(err, data) { 84 | if (err) { 85 | res.send(err); 86 | } 87 | res.send(data); 88 | }); 89 | }); 90 | }); 91 | 92 | module.exports = router; 93 | -------------------------------------------------------------------------------- /src/components/shared/CardTemplate.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 90 | -------------------------------------------------------------------------------- /src/views/Products.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 90 | 91 | 96 | -------------------------------------------------------------------------------- /src/components/products/filters/ProductFilter.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 87 | 88 | 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShoppingCart - Vue.js + Node.js + Express + MongoDB 2 | 3 | [![HitCount](http://hits.dwyl.io/ikismail/Vue-ShoppingCart.svg)](http://hits.dwyl.io/ikismail/Vue-ShoppingCart) 4 | [![GitHub forks](https://img.shields.io/github/forks/ikismail/Vue-ShoppingCart.svg)](https://github.com/ikismail/Vue-ShoppingCart/network) 5 | [![Github Dependencies](https://david-dm.org/ikismail/Vue-ShoppingCart.svg)](https://david-dm.org/ikismail/Vue-ShoppingCart.svg) 6 | [![GitHub stars](https://img.shields.io/github/stars/ikismail/Vue-ShoppingCart.svg)](https://github.com/ikismail/Vue-ShoppingCart/stargazers) 7 | [![GitHub issues](https://img.shields.io/github/issues/ikismail/Vue-ShoppingCart.svg)](https://github.com/ikismail/Vue-ShoppingCart/issues) 8 | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/ikismail/Vue-ShoppingCart/blob/master/LICENSE) 9 | [![npm](https://img.shields.io/badge/demo-online-brightgreen.svg)](https://vue-shop-cart.herokuapp.com/) 10 | [![Twitter](https://img.shields.io/twitter/url/https/github.com/ikismail/Vue-ShoppingCart.svg?style=social)](https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fikismail%2FVue-ShoppingCart) 11 | 12 | Developing a **ShoppingCart (Ecommerce) Application using Vue.js** 13 | 14 | **Live Demo** : [Vue-Shopping-Cart](#) 15 | 16 | This project was generated with [Vue CLI](https://github.com/vuejs/vue-cli) version **3.x** 17 | 18 | ## Functionalitites 19 | 1. User Registeration and Authentication using Passport.js (Email/password | Google Authentication) 20 | 2. CRUD Operations like 21 | * User can add product to his cart 22 | * Admin can add product to the product list. 23 | * Admin can edit/delete the product. 24 | 3. Security 25 | * Implementation of Authentication and Authorization. 26 | 27 | ## Tools and Technologies: 28 | * Technology : Vue.js + Express + Node.js + MongoDb (MEVN), HTML, Bootstrap, PWA. 29 | 30 | ## Installation 31 | 32 | 1. Vue CLI - [Installation of Vue CLI](https://cli.vuejs.org/guide/installation.html) 33 | 2. NodeJs - [Download Nodejs](https://nodejs.org/en/download/) 34 | 3. Package Manager - NPM / Yarn 35 | 4. Clone the repository and run `npm install` if you use **npm** as package manager or `yarn install` if you use **yarn** as package manager. 36 | 5. Configuring MongoDB `server/mongo/config.js` 37 | ``` 38 | mongoose.connect('', { 39 | useNewUrlParser: true 40 | }); // connect to your database 41 | 42 | ``` 43 | 6. Run the server `npm run serve` 44 | 45 | ## Screenshots: 46 | 47 | ### Main Page: 48 | ![Alt text](https://image.ibb.co/cF5D6V/screencapture-localhost-8081-2018-10-28-14-33-47.png) 49 | 50 | ### Product Page: 51 | ![Alt text](https://image.ibb.co/iZxh0q/screencapture-localhost-8081-products-2018-10-28-14-34-08.png) 52 | 53 | ## How can I support the developer ? 54 | 55 | * Star my Github repo ⭐ 56 | * Create pull requests, submit bugs, suggest new features or documentation updates 🛠 57 | 58 | ## Project setup 59 | ``` 60 | npm install 61 | ``` 62 | 63 | ### Compiles and hot-reloads for development 64 | ``` 65 | npm run serve 66 | ``` 67 | 68 | ### Compiles and minifies for production 69 | ``` 70 | npm run build 71 | ``` 72 | 73 | ### Run your tests 74 | ``` 75 | npm run test 76 | ``` 77 | 78 | ### Lints and fixes files 79 | ``` 80 | npm run lint 81 | ``` 82 | -------------------------------------------------------------------------------- /src/components/products/cart/Checkout.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 104 | 105 | 118 | -------------------------------------------------------------------------------- /src/views/login.vue: -------------------------------------------------------------------------------- 1 | 57 | 107 | 145 | -------------------------------------------------------------------------------- /src/components/products/cart/CartProducts.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 107 | 108 | 123 | -------------------------------------------------------------------------------- /src/components/CreateAccount.vue: -------------------------------------------------------------------------------- 1 | 80 | 141 | 142 | 150 | -------------------------------------------------------------------------------- /server/routes/productApi.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | // const mongodb = require('../mongo/config') 5 | 6 | const ProductModel = require("../mongo/model/product"); 7 | 8 | /* 9 | API for Product Schema 10 | Product Create and Read all Policies API 's 11 | 12 | */ 13 | router 14 | .route("/products") 15 | 16 | // Create a new Product 17 | .post((req, res) => { 18 | const product = new ProductModel(); // create a new instance of the Product model 19 | 20 | product.productName = req.body.productName; 21 | product.productDescription = req.body.productDescription; 22 | product.productCategory = req.body.productCategory; 23 | product.productPrice = req.body.productPrice; 24 | product.productImage = req.body.productImage; 25 | product.productSeller = req.body.productSeller; 26 | product.productRating = req.body.productRating; 27 | 28 | // save the bear and check for errors 29 | product.save(product, (err, product) => { 30 | if (err) { 31 | res.send(err); 32 | } 33 | 34 | console.log("**********NEWLY CREATED SITEURL***********"); 35 | console.log(product); 36 | res.send(product); 37 | }); 38 | }) 39 | // Get All products 40 | .get((req, res) => { 41 | ProductModel.find((err, data) => { 42 | if (err) { 43 | res.send(err); 44 | } 45 | console.log("data", data); 46 | res.json(data); 47 | }); 48 | }); 49 | 50 | router.route("/best/products/").get(function(req, res) { 51 | ProductModel.find( 52 | { 53 | isBestProduct: true, 54 | }, 55 | function(err, product) { 56 | if (err) { 57 | res.send(err); 58 | } 59 | console.log("data", product); 60 | res.json(product); 61 | } 62 | ).limit(4); 63 | }); 64 | 65 | router.route("/top/products/").get(function(req, res) { 66 | ProductModel.find( 67 | { 68 | isTopProduct: true, 69 | }, 70 | function(err, product) { 71 | if (err) { 72 | res.send(err); 73 | } 74 | console.log("data", product); 75 | res.json(product); 76 | } 77 | ).limit(4); 78 | }); 79 | 80 | router.route("/products/similarProduct").get(function(req, res) { 81 | ProductModel.find( 82 | { 83 | productSeller: req.query["productSeller"], 84 | }, 85 | function(err, product) { 86 | if (err) { 87 | res.send(err); 88 | } 89 | console.log("data", product); 90 | res.json(product); 91 | } 92 | ).limit(4); 93 | }); 94 | 95 | /* Getting product Categories */ 96 | router.route("/products/productCategories").get((req, res) => { 97 | ProductModel.aggregate( 98 | [ 99 | { 100 | $group: { 101 | _id: "$productCategory", 102 | }, 103 | }, 104 | ], 105 | (err, products) => { 106 | if (err) { 107 | res.send(err); 108 | } 109 | 110 | console.log("data", products); 111 | res.json(products); 112 | } 113 | ); 114 | }); 115 | 116 | router.route("/products/productsByCategory").get((req, res) => { 117 | ProductModel.find( 118 | { 119 | productCategory: req.query["productCategory"], 120 | }, 121 | (err, data) => { 122 | console.log("inside if"); 123 | if (err) { 124 | res.send(err); 125 | } 126 | console.log("data", data); 127 | res.json(data); 128 | } 129 | ); 130 | }); 131 | 132 | /* ProductModel Update and Read by Id and delete products API's */ 133 | // on routes that end in /products/:product_id 134 | // ---------------------------------------------------- 135 | router 136 | .route("/products/:product_id") 137 | 138 | // get the product with that id (accessed at GET http://localhost:8080/api/products/:product_id) 139 | .get(function(req, res) { 140 | ProductModel.findById(req.params.product_id, function(err, data) { 141 | if (err) { 142 | res.send(err); 143 | } 144 | console.log("data", data); 145 | res.json(data); 146 | }); 147 | }) 148 | 149 | // update the product with this id (accessed at PUT http://localhost:8080/api/products/:product_id) 150 | .put(function(req, res) { 151 | // use our product model to find the product we want 152 | ProductModel.findById(req.params.product_id, function(err, product) { 153 | if (err) { 154 | res.send(err); 155 | } 156 | 157 | product.productName = req.body.productName; 158 | product.productDescription = req.body.productDescription; 159 | product.productCategory = req.body.productCategory; 160 | product.productPrice = req.body.productPrice; 161 | product.productImage = req.body.productImage; 162 | product.productSeller = req.body.productSeller; 163 | product.productRating = req.body.productRating; 164 | 165 | // save the bear 166 | product.save(function(err, data) { 167 | if (err) { 168 | res.send(err); 169 | } 170 | console.log("Updating product", data); 171 | res.send(data); 172 | }); 173 | }); 174 | }) 175 | 176 | // delete the product with this id (accessed at DELETE http://localhost:8080/api/products/:product_id) 177 | .delete(function(req, res) { 178 | ProductModel.remove( 179 | { 180 | _id: req.params.product_id, 181 | }, 182 | function(err, product) { 183 | if (err) { 184 | res.send(err); 185 | } 186 | res.send(product); 187 | } 188 | ); 189 | }); 190 | 191 | module.exports = router; 192 | -------------------------------------------------------------------------------- /src/components/products/ProductDetail.vue: -------------------------------------------------------------------------------- 1 | 93 | 94 | 154 | 155 | 197 | -------------------------------------------------------------------------------- /src/components/products/actions/ProductForm.vue: -------------------------------------------------------------------------------- 1 | 69 | 127 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 116 | 117 | 165 | 166 | 167 | 250 | -------------------------------------------------------------------------------- /public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 101 | 102 | 103 | --------------------------------------------------------------------------------