├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── assets ├── README.md └── main.scss ├── components ├── AppCard.vue ├── AppCartDisplay.vue ├── AppCartSteps.vue ├── AppFeaturedProducts.vue ├── AppFooter.vue ├── AppFooterLinks.vue ├── AppLoader.vue ├── AppNav.vue ├── AppSalesBoxes.vue ├── AppStoreGrid.vue └── AppTextlockup.vue ├── functions ├── create-payment-intent.js └── handle-payment-succeeded.js ├── layouts └── default.vue ├── middleware └── README.md ├── netlify.toml ├── nuxt.config.js ├── package.json ├── pages ├── all.vue ├── cart.vue ├── index.vue ├── men.vue ├── product │ └── _id.vue └── women.vue ├── plugins ├── README.md └── currency-filter.js ├── static ├── bag.jpg ├── callout.jpg ├── favicon.ico ├── icon-cal.svg ├── icon-package.svg ├── icon-service.svg ├── products │ ├── 1.jpg │ ├── 10.jpg │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.jpg │ ├── 17.png │ ├── 18.jpg │ ├── 19.png │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png ├── shoe1.jpg └── storedata.json ├── store └── index.js └── yarn.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [sdras] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | .editorconfig 83 | 84 | # Service worker 85 | sw.* 86 | 87 | # Mac OSX 88 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🛍 Ecommerce Store with Netlify Functions and Stripe 2 | 3 | > A serverless function to process stripe payments with Nuxt, Netlify, and Lambda 4 | 5 | Demo site is here: [E-Commerce Store](https://ecommerce-netlify.netlify.com/) 6 | 7 | ![screenshot of site](https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/ecommerce-screenshot.jpg "E-Commerce Netlify Site") 8 | 9 | There are two articles explaining how this site is set up: 10 | * Explanation of Netlify Functions and Stripe: [Let's Build a JAMstack E-Commerce Store with Netlify Functions](https://css-tricks.com/lets-build-a-jamstack-e-commerce-store-with-netlify-functions/) 11 | * Explanation of dynamic routing in Nuxt for the individual product pages: [Creating Dynamic Routes in Nuxt Application](https://css-tricks.com/creating-dynamic-routes-in-a-nuxt-application/) 12 | 13 | ## Build Setup 14 | 15 | ``` bash 16 | # install dependencies 17 | $ yarn install or npm run install 18 | 19 | # serve with hot reload at localhost:3000 20 | $ yarn dev or npm run dev 21 | 22 | # build for production and launch server 23 | $ yarn build or npm run build 24 | $ yarn start or npm run start 25 | 26 | # generate static project 27 | $ yarn generate or npm run generate 28 | ``` 29 | 30 | For detailed explanation on how things work, checkout [Nuxt.js docs](https://nuxtjs.org). 31 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /assets/main.scss: -------------------------------------------------------------------------------- 1 | /*------------ Variables -----------*/ 2 | 3 | $brandprimary: #d96528; 4 | $brandsecondary: #03c1c1; 5 | 6 | /*------------ Global -----------*/ 7 | 8 | body { 9 | border: 10px solid #ccc; 10 | min-height: 100vh; 11 | font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", 12 | Roboto, "Helvetica Neue", Arial, sans-serif; 13 | font-size: 16px; 14 | line-height: 1.4; 15 | word-spacing: 1px; 16 | -ms-text-size-adjust: 100%; 17 | -webkit-text-size-adjust: 100%; 18 | -moz-osx-font-smoothing: grayscale; 19 | -webkit-font-smoothing: antialiased; 20 | box-sizing: border-box; 21 | } 22 | 23 | h1, 24 | h2, 25 | h3 { 26 | font-family: "PT Serif", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 27 | "Helvetica Neue", Arial, sans-serif; 28 | font-weight: normal; 29 | } 30 | 31 | h1 { 32 | font-size: 40px; 33 | } 34 | 35 | p { 36 | margin: 20px 0; 37 | } 38 | 39 | *, 40 | *:before, 41 | *:after { 42 | box-sizing: border-box; 43 | margin: 0; 44 | } 45 | 46 | a, 47 | a:active, 48 | a:visited { 49 | color: $brandprimary; 50 | text-decoration: none; 51 | transition: 0.3s all ease; 52 | } 53 | 54 | button { 55 | border: 1px solid #ccc; 56 | background: white; 57 | padding: 10px 14px; 58 | cursor: pointer; 59 | color: black; 60 | font-weight: 700; 61 | font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", 62 | Roboto, "Helvetica Neue", Arial, sans-serif; 63 | transition: 0.3s all ease; 64 | &:hover { 65 | background: black; 66 | border: 1px solid black; 67 | color: white; 68 | } 69 | } 70 | 71 | button.purchase { 72 | background: $brandprimary; 73 | color: white; 74 | font-size: 16px; 75 | border: none; 76 | &:hover { 77 | background: #c14103; 78 | } 79 | } 80 | 81 | button.pay-with-stripe { 82 | background: black; 83 | color: white; 84 | font-size: 16px; 85 | border: none; 86 | &:hover { 87 | background: #c14103; 88 | } 89 | 90 | &:disabled { 91 | background: #999; 92 | } 93 | } 94 | 95 | .price { 96 | color: $brandprimary; 97 | font-size: 20px; 98 | margin: 5px 0; 99 | font-weight: normal; 100 | font-family: "PT Serif", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 101 | "Helvetica Neue", Arial, sans-serif; 102 | } 103 | 104 | hr { 105 | border-top: 1px solid #eee; 106 | margin: 30px 0; 107 | } 108 | 109 | label { 110 | font-weight: 600; 111 | text-transform: uppercase; 112 | font-size: 14px; 113 | letter-spacing: 0.1em; 114 | margin-top: 20px; 115 | display: inline-block; 116 | } 117 | 118 | input { 119 | font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", 120 | Roboto, "Helvetica Neue", Arial, sans-serif; 121 | font-size: 16px; 122 | padding: 5px 10px; 123 | } 124 | 125 | .center { 126 | text-align: center; 127 | } 128 | 129 | /*------------ Store Grid -----------*/ 130 | 131 | .storegrid { 132 | width: 95%; 133 | display: grid; 134 | grid-template-columns: 3fr 1fr; 135 | grid-template-rows: 1fr; 136 | grid-column-gap: 40px; 137 | grid-row-gap: 0px; 138 | } 139 | 140 | /* no grid support */ 141 | aside { 142 | float: left; 143 | width: 19.1489%; 144 | } 145 | 146 | .content { 147 | /*no grid support*/ 148 | float: right; 149 | width: 79.7872%; 150 | /* grid */ 151 | display: grid; 152 | grid-template-columns: repeat(3, 1fr); 153 | grid-gap: 10px; 154 | padding: 0 !important; 155 | } 156 | 157 | @media (max-width: 600px) { 158 | aside { 159 | width: 100% !important; 160 | margin-bottom: 10px !important; 161 | } 162 | 163 | .content { 164 | width: 100% !important; 165 | grid-template-columns: 1fr !important; 166 | } 167 | } 168 | 169 | @media (min-width: 601px) and (max-width: 900px) { 170 | .content { 171 | grid-template-columns: repeat(2, 1fr) !important; 172 | } 173 | } 174 | 175 | @media screen and (max-width: 550px) { 176 | .storegrid { 177 | width: 90%; 178 | display: grid; 179 | grid-template-columns: 2fr 1fr; 180 | grid-template-rows: 1fr; 181 | grid-column-gap: 10px; 182 | } 183 | } 184 | 185 | /* --- items animation --- */ 186 | 187 | .items-leave-active { 188 | transition: opacity 0.2s ease-out, scale 0.2s ease-out; 189 | } 190 | 191 | .items-move { 192 | transition: opacity 0.2s ease-out, scale 0.2s ease-out; 193 | } 194 | 195 | .items-enter-active { 196 | transition: opacity 0.2s ease-out, scale 0.2s ease-out; 197 | } 198 | 199 | .items-enter, 200 | .items-leave-to { 201 | opacity: 0; 202 | transform: scale(0.9); 203 | transform-origin: 50% 50%; 204 | } 205 | 206 | /* --- range --- */ 207 | 208 | input[type="range"].slider { 209 | -webkit-appearance: none; 210 | width: 100%; 211 | margin: 25px 0 5px; 212 | } 213 | 214 | input[type="range"].slider:focus { 215 | outline: none; 216 | } 217 | 218 | input[type="range"].slider::-webkit-slider-runnable-track { 219 | width: 100%; 220 | height: 4.3px; 221 | cursor: pointer; 222 | box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0); 223 | background: $brandprimary; 224 | border-radius: 13.7px; 225 | border: 0px solid rgba(1, 1, 1, 0); 226 | } 227 | 228 | input[type="range"].slider::-webkit-slider-thumb { 229 | box-shadow: 0px 0px 0px rgba(0, 0, 62, 0), 0px 0px 0px rgba(0, 0, 88, 0); 230 | border: 1.9px solid $brandprimary; 231 | height: 17px; 232 | width: 17px; 233 | border-radius: 31px; 234 | background: #ffffff; 235 | cursor: pointer; 236 | -webkit-appearance: none; 237 | margin-top: -6.35px; 238 | } 239 | 240 | input[type="range"].slider:focus::-webkit-slider-runnable-track { 241 | background: $brandprimary; 242 | } 243 | 244 | input[type="range"].slider::-moz-range-track { 245 | width: 100%; 246 | height: 4.3px; 247 | cursor: pointer; 248 | box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0); 249 | background: $brandprimary; 250 | border-radius: 13.7px; 251 | border: 0px solid rgba(1, 1, 1, 0); 252 | } 253 | 254 | input[type="range"].slider::-moz-range-thumb { 255 | box-shadow: 0px 0px 0px rgba(0, 0, 62, 0), 0px 0px 0px rgba(0, 0, 88, 0); 256 | border: 1.9px solid $brandprimary; 257 | height: 17px; 258 | width: 17px; 259 | border-radius: 31px; 260 | background: #ffffff; 261 | cursor: pointer; 262 | } 263 | 264 | input[type="range"].slider::-ms-track { 265 | width: 100%; 266 | height: 4.3px; 267 | cursor: pointer; 268 | background: transparent; 269 | border-color: transparent; 270 | color: transparent; 271 | } 272 | 273 | input[type="range"].slider::-ms-fill-lower { 274 | background: $brandprimary; 275 | border: 0px solid rgba(1, 1, 1, 0); 276 | border-radius: 27.4px; 277 | box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0); 278 | } 279 | 280 | input[type="range"].slider::-ms-fill-upper { 281 | background: $brandprimary; 282 | border: 0px solid rgba(1, 1, 1, 0); 283 | border-radius: 27.4px; 284 | box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0); 285 | } 286 | 287 | input[type="range"].slider::-ms-thumb { 288 | box-shadow: 0px 0px 0px rgba(0, 0, 62, 0), 0px 0px 0px rgba(0, 0, 88, 0); 289 | border: 1.9px solid $brandprimary; 290 | height: 17px; 291 | width: 17px; 292 | border-radius: 31px; 293 | background: #ffffff; 294 | cursor: pointer; 295 | height: 4.3px; 296 | } 297 | 298 | input[type="range"].slider:focus::-ms-fill-lower { 299 | background: $brandprimary; 300 | } 301 | 302 | input[type="range"].slider:focus::-ms-fill-upper { 303 | background: $brandprimary; 304 | } 305 | -------------------------------------------------------------------------------- /components/AppCard.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 121 | 122 | -------------------------------------------------------------------------------- /components/AppCartDisplay.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 92 | 93 | -------------------------------------------------------------------------------- /components/AppCartSteps.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /components/AppFeaturedProducts.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 28 | 29 | 112 | -------------------------------------------------------------------------------- /components/AppFooter.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 32 | 33 | -------------------------------------------------------------------------------- /components/AppFooterLinks.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /components/AppLoader.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 84 | 85 | -------------------------------------------------------------------------------- /components/AppNav.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 36 | 37 | -------------------------------------------------------------------------------- /components/AppSalesBoxes.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 36 | 37 | -------------------------------------------------------------------------------- /components/AppStoreGrid.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 73 | 74 | -------------------------------------------------------------------------------- /components/AppTextlockup.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /functions/create-payment-intent.js: -------------------------------------------------------------------------------- 1 | // An endpoint that calculates the order total and creates a 2 | // PaymentIntent on Stripe 3 | 4 | require("dotenv").config(); 5 | const axios = require("axios"); 6 | const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY), 7 | headers = { 8 | "Access-Control-Allow-Origin": "*", 9 | "Access-Control-Allow-Headers": "Content-Type" 10 | }; 11 | 12 | exports.handler = async (event, context) => { 13 | // CORS 14 | if (event.httpMethod === "OPTIONS") { 15 | return { 16 | statusCode: 200, 17 | headers 18 | }; 19 | } 20 | 21 | const data = JSON.parse(event.body); 22 | console.log(data); 23 | 24 | if (!data.items) { 25 | console.error("List of items to purchase is missing."); 26 | 27 | return { 28 | statusCode: 400, 29 | headers, 30 | body: JSON.stringify({ 31 | status: "missing information" 32 | }) 33 | }; 34 | } 35 | 36 | // Stripe payment processing begins here 37 | try { 38 | // Always calculate the order amount on your server to prevent customers 39 | // from manipulating the order amount from the client 40 | // Here we will use a simple json file to represent inventory 41 | // but you could replace this with a DB lookup 42 | const storeDatabase = await axios.get( 43 | "https://ecommerce-netlify.netlify.app/storedata.json" 44 | ); 45 | 46 | const amount = data.items.reduce((prev, item) => { 47 | // lookup item information from "database" and calculate total amount 48 | const itemData = storeDatabase.data.find( 49 | storeItem => storeItem.id === item.id 50 | ); 51 | return prev + itemData.price * 100 * item.quantity; 52 | }, 0); 53 | 54 | // Create a PaymentIntent on Stripe 55 | // A PaymentIntent represents your customer's intent to pay 56 | // and needs to be confirmed on the client to finalize the payment 57 | const paymentIntent = await stripe.paymentIntents.create({ 58 | currency: "usd", 59 | amount: amount, 60 | description: "Order from store" 61 | }); 62 | 63 | // Send the client_secret to the client 64 | // The client secret has a limited set of permissions that 65 | // let you finalize the payment and update some details from the client 66 | return { 67 | statusCode: 200, 68 | headers, 69 | body: JSON.stringify({ 70 | clientSecret: paymentIntent.client_secret 71 | }) 72 | }; 73 | } catch (err) { 74 | console.log(err); 75 | 76 | return { 77 | statusCode: 400, 78 | headers, 79 | body: JSON.stringify({ 80 | status: err 81 | }) 82 | }; 83 | } 84 | }; 85 | -------------------------------------------------------------------------------- /functions/handle-payment-succeeded.js: -------------------------------------------------------------------------------- 1 | // Webhook that listens for events sent from Stripe 2 | // Requires configuration in the Stripe Dashboard 3 | // For more information read https://stripe.com/docs/webhooks 4 | require("dotenv").config(); 5 | 6 | const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY); 7 | 8 | // Create your webhook in the Stripe dashboard at https://dashboard.stripe.com/webhooks 9 | // Use the secret listed in the "Signing secret" section 10 | const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET; 11 | 12 | exports.handler = async (event, context) => { 13 | const sig = event.headers["stripe-signature"]; 14 | let stripeEvent; 15 | 16 | try { 17 | // Verifies that the event was sent by Stripe and deserializes the event 18 | stripeEvent = stripe.webhooks.constructEvent( 19 | event.body, 20 | sig, 21 | endpointSecret 22 | ); 23 | } catch (err) { 24 | return { statusCode: 400 }; 25 | } 26 | 27 | // Handle the event 28 | switch (stripeEvent.type) { 29 | case "payment_intent.succeeded": 30 | const paymentIntent = stripeEvent.data.object; 31 | console.log('object', paymentIntent) 32 | console.log( 33 | "Payment was successful! Charge information:", 34 | paymentIntent.charges.data.filter(charge => charge.status === "succeeded") 35 | ); 36 | break; 37 | case "charge.dispute.created": 38 | const charge = stripeEvent.data.object; 39 | console.log("Someone disputed a payment!"); 40 | break; 41 | // ... handle other event types 42 | default: 43 | // Unexpected event type 44 | return { statusCode: 400 }; 45 | } 46 | 47 | // Return a 200 response to acknowledge receipt of the event 48 | return { statusCode: 200 }; 49 | }; 50 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 22 | 23 | 60 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | functions = "functions" -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | import data from './static/storedata.json' 2 | let dynamicRoutes = () => { 3 | return new Promise(resolve => { 4 | resolve(data.map(el => `product/${el.id}`)) 5 | }) 6 | } 7 | 8 | export default { 9 | mode: 'universal', 10 | /* 11 | ** Headers of the page 12 | */ 13 | head: { 14 | title: process.env.npm_package_name || '', 15 | script: [{ src: 'https://js.stripe.com/v3/' }], 16 | meta: [ 17 | { charset: 'utf-8' }, 18 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 19 | 20 | { 21 | hid: 'description', 22 | name: 'description', 23 | content: process.env.npm_package_description || '' 24 | } 25 | ], 26 | link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }], 27 | link: [ 28 | { 29 | rel: 'stylesheet', 30 | href: 31 | 'https://fonts.googleapis.com/css?family=Montserrat:300,600|PT+Serif&display=swap' 32 | } 33 | ] 34 | }, 35 | generate: { 36 | routes: dynamicRoutes 37 | }, 38 | /* 39 | ** Customize the progress-bar color 40 | */ 41 | loading: { color: '#fff' }, 42 | /* 43 | ** Global CSS 44 | */ 45 | css: ['normalize.css', { src: '~/assets/main.scss', lang: 'sass' }], 46 | /* 47 | ** Plugins to load before mounting the App 48 | */ 49 | plugins: [`~/plugins/currency-filter.js`], 50 | /* 51 | ** Nuxt.js modules 52 | */ 53 | modules: [], 54 | /* 55 | ** Build configuration 56 | */ 57 | build: { 58 | /* 59 | ** You can extend webpack config here 60 | */ 61 | extend(config, ctx) {} 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecommerce-netlify", 3 | "version": "1.0.0", 4 | "description": "A serverless function to process stripe payments with Nuxt, Netlify, and Lambda", 5 | "author": "sdras", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate" 12 | }, 13 | "dependencies": { 14 | "axios": "^0.19.0", 15 | "dotenv": "^8.0.0", 16 | "gsap": "^2.1.3", 17 | "node-sass": "^4.13.1", 18 | "normalize.css": "^8.0.1", 19 | "nuxt": "^2.0.0", 20 | "sass-loader": "^7.1.0", 21 | "stripe": "^7.4.0", 22 | "uuid": "^3.3.2", 23 | "vue-star-rating": "^1.6.1", 24 | "vue-stripe-elements-plus": "^0.3.2" 25 | }, 26 | "devDependencies": { 27 | "nodemon": "^1.18.9" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pages/all.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /pages/cart.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 51 | 52 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 31 | 32 | 41 | -------------------------------------------------------------------------------- /pages/men.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /pages/product/_id.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 106 | 107 | 174 | -------------------------------------------------------------------------------- /pages/women.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /plugins/currency-filter.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue" 2 | 3 | Vue.filter("dollar", function(value) { 4 | // Using a template literal here, that's why there are two dollar signs. 5 | // The first is an actual dollar. 6 | return `$${parseFloat(value).toFixed(2)}` 7 | }) 8 | -------------------------------------------------------------------------------- /static/bag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/bag.jpg -------------------------------------------------------------------------------- /static/callout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/callout.jpg -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/favicon.ico -------------------------------------------------------------------------------- /static/icon-cal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /static/icon-package.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /static/icon-service.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /static/products/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/1.jpg -------------------------------------------------------------------------------- /static/products/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/10.jpg -------------------------------------------------------------------------------- /static/products/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/11.png -------------------------------------------------------------------------------- /static/products/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/12.png -------------------------------------------------------------------------------- /static/products/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/13.png -------------------------------------------------------------------------------- /static/products/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/14.png -------------------------------------------------------------------------------- /static/products/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/15.png -------------------------------------------------------------------------------- /static/products/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/16.jpg -------------------------------------------------------------------------------- /static/products/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/17.png -------------------------------------------------------------------------------- /static/products/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/18.jpg -------------------------------------------------------------------------------- /static/products/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/19.png -------------------------------------------------------------------------------- /static/products/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/2.jpg -------------------------------------------------------------------------------- /static/products/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/3.jpg -------------------------------------------------------------------------------- /static/products/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/4.jpg -------------------------------------------------------------------------------- /static/products/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/5.png -------------------------------------------------------------------------------- /static/products/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/6.png -------------------------------------------------------------------------------- /static/products/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/7.png -------------------------------------------------------------------------------- /static/products/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/8.png -------------------------------------------------------------------------------- /static/products/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/products/9.png -------------------------------------------------------------------------------- /static/shoe1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdras/ecommerce-netlify/b6407875682c2b0db637f19c3b82f017fd45a6f5/static/shoe1.jpg -------------------------------------------------------------------------------- /static/storedata.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "9d436e98-1dc9-4f21-9587-76d4c0255e33", 4 | "color": "Goldenrod", 5 | "description": "Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.", 6 | "gender": "Male", 7 | "name": "Desi Avramovitz", 8 | "review": "productize virtual markets", 9 | "starrating": 3, 10 | "price": 50.4, 11 | "img": "1.jpg" 12 | }, 13 | { 14 | "id": "bfa86b1c-9ebf-4555-987f-7b8bf7d27be7", 15 | "color": "Puce", 16 | "description": "Phasellus in felis. Donec semper sapien a libero. Nam dui.", 17 | "gender": "Male", 18 | "name": "Addy Alldre", 19 | "review": "deploy efficient mindshare", 20 | "starrating": 4, 21 | "price": 33.99, 22 | "img": "2.jpg" 23 | }, 24 | { 25 | "id": "eca6b1c7-6b8c-4416-aae0-98cecdc18e92", 26 | "color": "Orange", 27 | "description": "Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.", 28 | "gender": "Female", 29 | "name": "Bernie Gledhill", 30 | "review": "mesh sticky content", 31 | "starrating": 3, 32 | "price": 102.99, 33 | "img": "3.jpg" 34 | }, 35 | { 36 | "id": "440e2eac-67ed-4407-993a-d9f6b7f15087", 37 | "color": "Teal", 38 | "description": "Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\n\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.", 39 | "gender": "Female", 40 | "name": "Gwendolen Bickerstaffe", 41 | "review": "integrate front-end infomediaries", 42 | "starrating": 5, 43 | "price": 64.5, 44 | "sizes": [ 45 | "Small", 46 | "Medium", 47 | "Large" 48 | ], 49 | "img": "4.jpg" 50 | }, 51 | { 52 | "id": "38543b28-3b0e-4886-a108-b58419bcef94", 53 | "color": "Yellow", 54 | "description": "Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\n\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\n\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", 55 | "gender": "Male", 56 | "name": "Cristian Gilbanks", 57 | "review": "integrate extensible methodologies", 58 | "starrating": 5, 59 | "price": 30.99, 60 | "img": "5.png" 61 | }, 62 | { 63 | "id": "3d16728f-4b1d-449a-b002-4dc23f32c0cd", 64 | "color": "Red", 65 | "description": "Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\n\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\n\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.", 66 | "gender": "Female", 67 | "name": "Kalila Gooms", 68 | "review": "utilize end-to-end functionalities", 69 | "starrating": 4, 70 | "price": 19.99, 71 | "sizes": [ 72 | "Small", 73 | "Medium", 74 | "Large" 75 | ], 76 | "img": "6.png" 77 | }, 78 | { 79 | "id": "0d5c91e6-8dbe-4c5b-b9fa-9e82986b1bdc", 80 | "color": "Maroon", 81 | "description": "Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\n\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\n\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.", 82 | "gender": "Male", 83 | "name": "Bartolemo Peckitt", 84 | "review": "enable cutting-edge e-services", 85 | "starrating": 3, 86 | "price": 130.0, 87 | "sizes": [ 88 | "Small", 89 | "Medium", 90 | "Large" 91 | ], 92 | "img": "7.png" 93 | }, 94 | { 95 | "id": "80175256-93a1-41b8-9895-a890ed0c61d1", 96 | "color": "Puce", 97 | "description": "Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.", 98 | "gender": "Female", 99 | "name": "Kathye Haith", 100 | "review": "innovate sexy portals", 101 | "starrating": 4, 102 | "price": 120.99, 103 | "sizes": [ 104 | "Small", 105 | "Medium", 106 | "Large" 107 | ], 108 | "img": "8.png" 109 | }, 110 | { 111 | "id": "b5438034-168a-4b51-9d2e-dd9fc336a242", 112 | "color": "Red", 113 | "description": "Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\n\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\n\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.", 114 | "gender": "Male", 115 | "name": "Armin Basilio", 116 | "review": "transform robust mindshare", 117 | "starrating": 5, 118 | "price": 20.99, 119 | "sizes": [ 120 | "Small", 121 | "Medium", 122 | "Large" 123 | ], 124 | "img": "9.png" 125 | }, 126 | { 127 | "id": "a5f39b32-e103-4e4a-ab73-0f719817b455", 128 | "color": "Fuscia", 129 | "description": "Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\n\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\n\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.", 130 | "gender": "Male", 131 | "name": "Cal Sterman", 132 | "review": "benchmark synergistic bandwidth", 133 | "starrating": 3, 134 | "price": 50.0, 135 | "img": "10.jpg" 136 | }, 137 | { 138 | "id": "19bdee56-525a-4cb7-bfeb-c6f46b6d639a", 139 | "color": "Mauv", 140 | "description": "Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.", 141 | "gender": "Female", 142 | "name": "Ardine Imlaw", 143 | "review": "repurpose robust e-business", 144 | "starrating": 4, 145 | "price": 65.0, 146 | "sizes": [ 147 | "Small", 148 | "Medium", 149 | "Large" 150 | ], 151 | "img": "11.png" 152 | }, 153 | { 154 | "id": "f8b11f7c-1306-4658-a66c-d1704e728349", 155 | "color": "Teal", 156 | "description": "Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.", 157 | "gender": "Female", 158 | "name": "Noella Ruddom", 159 | "review": "redefine 24/365 eyeballs", 160 | "starrating": 3, 161 | "price": 72.0, 162 | "sizes": [ 163 | "Small", 164 | "Medium", 165 | "Large" 166 | ], 167 | "img": "12.png" 168 | }, 169 | { 170 | "id": "14fcaf19-465e-4275-8c2e-c69219e68f8c", 171 | "color": "Khaki", 172 | "description": "Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\n\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.", 173 | "gender": "Female", 174 | "name": "Kirstyn Espadate", 175 | "review": "empower sticky web-readiness", 176 | "starrating": 4, 177 | "price": 59.99, 178 | "img": "13.png" 179 | }, 180 | { 181 | "id": "b1913a24-2556-4414-87cd-c6dcc3b98b53", 182 | "color": "Turquoise", 183 | "description": "Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.", 184 | "gender": "Male", 185 | "name": "Rourke Greet", 186 | "review": "deliver end-to-end solutions", 187 | "starrating": 5, 188 | "price": 49.99, 189 | "img": "14.png" 190 | }, 191 | { 192 | "id": "143badca-e3f0-452f-b973-c3917a5f0f70", 193 | "color": "Teal", 194 | "description": "Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\n\nPhasellus in felis. Donec semper sapien a libero. Nam dui.", 195 | "gender": "Female", 196 | "name": "Arden Stockbridge", 197 | "review": "orchestrate value-added infrastructures", 198 | "starrating": 4, 199 | "price": 100.0, 200 | "img": "15.png" 201 | }, 202 | { 203 | "id": "5cdef591-6b7f-4511-b59f-d0f55bdceeb9", 204 | "color": "Orange", 205 | "description": "Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.", 206 | "gender": "Male", 207 | "name": "Sheffy Gunter", 208 | "review": "revolutionize vertical systems", 209 | "starrating": 5, 210 | "price": 33.99, 211 | "img": "16.jpg" 212 | }, 213 | { 214 | "id": "0bdf8654-7d44-46f5-bb1f-115b16b42344", 215 | "color": "Orange", 216 | "description": "Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\n\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.", 217 | "gender": "Female", 218 | "name": "Alexine Mulligan", 219 | "review": "architect real-time partnerships", 220 | "starrating": 3, 221 | "price": 13.99, 222 | "sizes": [ 223 | "Small", 224 | "Medium", 225 | "Large" 226 | ], 227 | "img": "17.png" 228 | }, 229 | { 230 | "id": "a89719a0-4ee2-4ba0-8a75-12a1654a66b1", 231 | "color": "Purple", 232 | "description": "In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.", 233 | "gender": "Female", 234 | "name": "Micheline Charlson", 235 | "review": "innovate holistic markets", 236 | "starrating": 4, 237 | "price": 12.99, 238 | "img": "18.jpg" 239 | }, 240 | { 241 | "id": "8aa13b42-b074-43d2-8dcc-f3f1010f3adb", 242 | "color": "Purple", 243 | "description": "Phasellus in felis. Donec semper sapien a libero. Nam dui.\n\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\n\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.", 244 | "gender": "Male", 245 | "name": "Hughie Jeffryes", 246 | "review": "facilitate innovative portals", 247 | "starrating": 3, 248 | "price": 154.99, 249 | "sizes": [ 250 | "Small", 251 | "Medium", 252 | "Large" 253 | ], 254 | "img": "19.png" 255 | }, 256 | { 257 | "id": "c8b03500-5f07-4fb3-a933-1ee205ccd903", 258 | "color": "Puce", 259 | "description": "In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.", 260 | "gender": "Male", 261 | "name": "Chet Corke", 262 | "review": "unleash synergistic models", 263 | "starrating": 5, 264 | "price": 33.99, 265 | "img": "1.jpg" 266 | }, 267 | { 268 | "id": "85641bb6-04e5-4393-be3e-1d4dea46cd54", 269 | "color": "Orange", 270 | "description": "Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\n\nPhasellus in felis. Donec semper sapien a libero. Nam dui.", 271 | "gender": "Female", 272 | "name": "Kamila Yggo", 273 | "review": "unleash strategic platforms", 274 | "starrating": 4, 275 | "price": 50.99, 276 | "img": "2.jpg" 277 | }, 278 | { 279 | "id": "1c8665b9-87cd-40d4-bc90-b28943825bd0", 280 | "color": "Purple", 281 | "description": "Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\n\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.", 282 | "gender": "Female", 283 | "name": "Kandace Strickett", 284 | "review": "orchestrate visionary paradigms", 285 | "starrating": 4, 286 | "price": 50.99, 287 | "img": "3.jpg" 288 | }, 289 | { 290 | "id": "e0bce42b-e2cf-4aa2-805b-f4bbd8517d2a", 291 | "color": "Khaki", 292 | "description": "Phasellus in felis. Donec semper sapien a libero. Nam dui.", 293 | "gender": "Male", 294 | "name": "Kellby Arlott", 295 | "review": "enhance rich initiatives", 296 | "starrating": 4, 297 | "price": 19.99, 298 | "sizes": [ 299 | "Small", 300 | "Medium", 301 | "Large" 302 | ], 303 | "img": "4.jpg" 304 | }, 305 | { 306 | "id": "cc6998b6-4150-4234-b678-28460afc3a91", 307 | "color": "Purple", 308 | "description": "Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\n\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\n\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.", 309 | "gender": "Male", 310 | "name": "Bogey Cheyne", 311 | "review": "benchmark mission-critical convergence", 312 | "starrating": 5, 313 | "price": 110.0, 314 | "img": "5.png" 315 | }, 316 | { 317 | "id": "69d95efa-af3a-40d2-8da1-1c8f8700a131", 318 | "color": "Crimson", 319 | "description": "Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\n\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.", 320 | "gender": "Female", 321 | "name": "Sybila Ehlerding", 322 | "review": "aggregate granular systems", 323 | "starrating": 3, 324 | "price": 19.99, 325 | "sizes": [ 326 | "Small", 327 | "Medium", 328 | "Large" 329 | ], 330 | "img": "6.png" 331 | }, 332 | { 333 | "id": "ab614297-fff2-4b34-bcf5-bc3c10127c63", 334 | "color": "Purple", 335 | "description": "Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\n\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\n\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.", 336 | "gender": "Male", 337 | "name": "Hadrian Hambatch", 338 | "review": "transition granular markets", 339 | "starrating": 3, 340 | "price": 190.99, 341 | "sizes": [ 342 | "Small", 343 | "Medium", 344 | "Large" 345 | ], 346 | "img": "7.png" 347 | }, 348 | { 349 | "id": "18d4cd08-134f-4df5-a19a-b782a59ae52b", 350 | "color": "Orange", 351 | "description": "Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\n\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\n\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.", 352 | "gender": "Female", 353 | "name": "Franciska Hallett", 354 | "review": "generate customized e-services", 355 | "starrating": 4, 356 | "price": 20.0, 357 | "sizes": [ 358 | "Small", 359 | "Medium", 360 | "Large" 361 | ], 362 | "img": "8.png" 363 | }, 364 | { 365 | "id": "92f8048f-3ee2-4428-96b0-66fd54b6e98f", 366 | "color": "Orange", 367 | "description": "Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", 368 | "gender": "Male", 369 | "name": "Andrej Wilfing", 370 | "review": "e-enable viral web services", 371 | "starrating": 3, 372 | "price": 15.99, 373 | "sizes": [ 374 | "Small", 375 | "Medium", 376 | "Large" 377 | ], 378 | "img": "9.png" 379 | }, 380 | { 381 | "id": "4cee7b59-30dd-4378-b1c0-e4ad055a6a19", 382 | "color": "Aquamarine", 383 | "description": "Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\n\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\n\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.", 384 | "gender": "Female", 385 | "name": "Rori Marishenko", 386 | "review": "recontextualize B2B markets", 387 | "starrating": 3, 388 | "price": 190.0, 389 | "img": "10.jpg" 390 | }, 391 | { 392 | "id": "f92c7d89-78e9-4616-8876-e0088c53483e", 393 | "color": "Pink", 394 | "description": "Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.", 395 | "gender": "Male", 396 | "name": "Silas Cornwell", 397 | "review": "architect collaborative initiatives", 398 | "starrating": 3, 399 | "price": 35.99, 400 | "sizes": [ 401 | "Small", 402 | "Medium", 403 | "Large" 404 | ], 405 | "img": "11.png" 406 | } 407 | ] -------------------------------------------------------------------------------- /store/index.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import data from "~/static/storedata.json"; 3 | 4 | export const state = () => ({ 5 | cartUIStatus: "idle", 6 | storedata: data, 7 | cart: [], 8 | clientSecret: "" // Required to initiate the payment from the client 9 | }); 10 | 11 | export const getters = { 12 | featuredProducts: state => state.storedata.slice(0, 3), 13 | women: state => state.storedata.filter(el => el.gender === "Female"), 14 | men: state => state.storedata.filter(el => el.gender === "Male"), 15 | cartCount: state => { 16 | if (!state.cart.length) return 0; 17 | return state.cart.reduce((ac, next) => ac + next.quantity, 0); 18 | }, 19 | cartTotal: state => { 20 | if (!state.cart.length) return 0; 21 | return state.cart.reduce((ac, next) => ac + next.quantity * next.price, 0); 22 | }, 23 | cartItems: state => { 24 | if (!state.cart.length) return []; 25 | return state.cart.map(item => { 26 | return { 27 | id: item.id, 28 | quantity: item.quantity 29 | }; 30 | }); 31 | }, 32 | clientSecret: state => state.clientSecret 33 | }; 34 | 35 | export const mutations = { 36 | updateCartUI: (state, payload) => { 37 | state.cartUIStatus = payload; 38 | }, 39 | clearCart: state => { 40 | //this clears the cart 41 | (state.cart = []), (state.cartUIStatus = "idle"); 42 | }, 43 | addToCart: (state, payload) => { 44 | let itemfound = state.cart.find(el => el.id === payload.id); 45 | itemfound 46 | ? (itemfound.quantity += payload.quantity) 47 | : state.cart.push(payload) 48 | }, 49 | setClientSecret: (state, payload) => { 50 | state.clientSecret = payload; 51 | }, 52 | addOneToCart: (state, payload) => { 53 | let itemfound = state.cart.find(el => el.id === payload.id) 54 | itemfound ? itemfound.quantity++ : state.cart.push(payload) 55 | }, 56 | removeOneFromCart: (state, payload) => { 57 | let index = state.cart.findIndex(el => el.id === payload.id) 58 | state.cart[index].quantity 59 | ? state.cart[index].quantity-- 60 | : state.cart.splice(index, 1) 61 | }, 62 | removeAllFromCart: (state, payload) => { 63 | state.cart = state.cart.filter(el => el.id !== payload.id) 64 | } 65 | }; 66 | 67 | export const actions = { 68 | async createPaymentIntent({ getters, commit }) { 69 | try { 70 | // Create a PaymentIntent with the information about the order 71 | const result = await axios.post( 72 | "https://ecommerce-netlify.netlify.app/.netlify/functions/create-payment-intent", 73 | { 74 | items: getters.cartItems 75 | }, 76 | { 77 | headers: { 78 | "Content-Type": "application/json" 79 | } 80 | } 81 | ); 82 | 83 | if (result.data.clientSecret) { 84 | // Store a reference to the client secret created by the PaymentIntent 85 | // This secret will be used to finalize the payment from the client 86 | commit("setClientSecret", result.data.clientSecret); 87 | } 88 | } catch (e) { 89 | console.log("error", e); 90 | } 91 | } 92 | }; 93 | --------------------------------------------------------------------------------