├── .nvmrc ├── lighthouse.png ├── .gitignore ├── src ├── favicon.png ├── components │ ├── README.md │ ├── SearchFocus.vue │ ├── TransitionComponent.vue │ ├── ThemeSwitcher.vue │ └── SearchInput.vue ├── pages │ ├── README.md │ ├── 404.vue │ ├── Collections.vue │ ├── Index.vue │ └── Cart.vue ├── templates │ ├── README.md │ ├── Collection.vue │ └── Product.vue ├── assets │ └── style │ │ └── notification.css ├── main.js ├── css │ └── main.css └── layouts │ └── Default.vue ├── static ├── README.md └── 404.svg ├── netlify.toml ├── gridsome.server.js ├── package.json ├── LICENSE ├── tailwind.config.js ├── README.md └── gridsome.config.js /.nvmrc: -------------------------------------------------------------------------------- 1 | 12 -------------------------------------------------------------------------------- /lighthouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoinvsalts/gridsome-shopify-starter/HEAD/lighthouse.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .cache 3 | .DS_Store 4 | src/.temp 5 | node_modules 6 | dist 7 | .env 8 | .env.* 9 | -------------------------------------------------------------------------------- /src/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoinvsalts/gridsome-shopify-starter/HEAD/src/favicon.png -------------------------------------------------------------------------------- /src/components/README.md: -------------------------------------------------------------------------------- 1 | Add components that will be imported to Pages and Layouts to this folder. 2 | Learn more about components here: https://gridsome.org/docs/components 3 | 4 | You can delete this file. -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | Add static files here. Files in this directory will be copied directly to `dist` folder during build. For example, /static/robots.txt will be located at https://yoursite.com/robots.txt. 2 | 3 | This file should be deleted. -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "dist" 3 | command = "gridsome build" 4 | 5 | [template.environment] 6 | GRIDSOME_SHOPIFY_STOREFRONT = "Your Shopify Storefront Name" 7 | GRIDSOME_SHOPIFY_STOREFRONT_TOKEN = "Your Shopify Storefront Token" -------------------------------------------------------------------------------- /src/pages/README.md: -------------------------------------------------------------------------------- 1 | Pages are usually used for normal pages or for listing items from a GraphQL collection. 2 | Add .vue files here to create pages. For example **About.vue** will be **site.com/about**. 3 | Learn more about pages: https://gridsome.org/docs/pages 4 | 5 | You can delete this file. -------------------------------------------------------------------------------- /src/templates/README.md: -------------------------------------------------------------------------------- 1 | Templates for **GraphQL collections** should be added here. 2 | To create a template for a collection called `WordPressPost` 3 | create a file named `WordPressPost.vue` in this folder. 4 | 5 | Learn more: https://gridsome.org/docs/templates 6 | 7 | You can delete this file. -------------------------------------------------------------------------------- /src/pages/404.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /gridsome.server.js: -------------------------------------------------------------------------------- 1 | // Server API makes it possible to hook into various parts of Gridsome 2 | // on server-side and add custom data to the GraphQL data layer. 3 | // Learn more: https://gridsome.org/docs/server-api 4 | 5 | // Changes here require a server restart. 6 | // To restart press CTRL + C in terminal and run `gridsome develop` 7 | 8 | module.exports = function (api, options) { 9 | api.loadSource(store => { 10 | }) 11 | } -------------------------------------------------------------------------------- /src/components/SearchFocus.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /src/components/TransitionComponent.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 23 | 24 | 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gridsome-shopify-starter", 3 | "scripts": { 4 | "build": "gridsome build", 5 | "develop": "gridsome develop", 6 | "explore": "gridsome explore" 7 | }, 8 | "dependencies": { 9 | "@gridsome/plugin-google-analytics": "^0.1.1", 10 | "@gridsome/plugin-sitemap": "^0.3.0", 11 | "apollo-boost": "^0.4.9", 12 | "axios": "^0.19.2", 13 | "currency.js": "^1.2.2", 14 | "graphql-tag": "^2.10.3", 15 | "gridsome": "^0.7.19", 16 | "gridsome-plugin-flexsearch": "^1.0.1", 17 | "gridsome-plugin-pwa": "^0.0.18", 18 | "gridsome-plugin-robots": "^0.2.1", 19 | "gridsome-source-shopify": "^0.1.13", 20 | "isomorphic-fetch": "^2.2.1", 21 | "lodash": "^4.17.19", 22 | "vue-apollo": "^3.0.3", 23 | "vue-notification": "^1.3.20", 24 | "vue-scrollto": "^2.18.1", 25 | "vuex": "^3.4.0" 26 | }, 27 | "devDependencies": { 28 | "gridsome-plugin-tailwindcss": "^2.2.48" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Herve Fulchiron 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. HF 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/components/ThemeSwitcher.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 25 | 26 | -------------------------------------------------------------------------------- /src/assets/style/notification.css: -------------------------------------------------------------------------------- 1 | .vue-notification-group { 2 | display: block; 3 | position: fixed; 4 | z-index: 5000; 5 | } 6 | 7 | .vue-notification-wrapper { 8 | display: block; 9 | overflow: hidden; 10 | width: 100%; 11 | margin: 0; 12 | padding: 0; 13 | } 14 | 15 | .notification-title { 16 | font-weight: 600; 17 | } 18 | 19 | .vue-notification-template { 20 | display: block; 21 | box-sizing: border-box; 22 | background: white; 23 | text-align: left; 24 | } 25 | 26 | .vue-notification { 27 | display: block; 28 | box-sizing: border-box; 29 | text-align: left; 30 | font-size: 12px; 31 | padding: 10px; 32 | margin: 0 5px 5px; 33 | color: white; 34 | background: #44A4FC; 35 | border-left: 5px solid #187FE7; 36 | } 37 | 38 | .vue-notification.primary { 39 | background: #44A4FC; 40 | border-left-color: #187FE7; 41 | } 42 | 43 | .vue-notification.warn { 44 | background: #ffb648; 45 | border-left-color: #f48a06; 46 | } 47 | 48 | .vue-notification.error { 49 | background: #E54D42; 50 | border-left-color: #B82E24; 51 | } 52 | 53 | .vue-notification.success { 54 | background: #68CD86; 55 | border-left-color: #42A85F; 56 | } 57 | 58 | .vn-fade-enter-active, .vn-fade-leave-active, .vn-fade-move { 59 | transition: all .5s; 60 | } 61 | 62 | .vn-fade-enter, .vn-fade-leave-to { 63 | opacity: 0; 64 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | extend: { 4 | spacing: { 5 | '80': '20rem', 6 | '108': '27rem', 7 | }, 8 | borderWidth: { 9 | '14': '14px', 10 | } 11 | }, 12 | container: { 13 | padding: '1rem' 14 | }, 15 | colors: { 16 | background: { 17 | primary: 'var(--bg-background-primary)', 18 | secondary: 'var(--bg-background-secondary)', 19 | tertiary: 'var(--bg-background-tertiary)', 20 | 21 | form: 'var(--bg-background-form)', 22 | }, 23 | 24 | copy: { 25 | primary: 'var(--text-copy-primary)', 26 | secondary: 'var(--text-copy-hover)', 27 | }, 28 | 29 | 'border-color': { 30 | primary: 'var(--border-border-color-primary)', 31 | }, 32 | 33 | transparent: 'transparent', 34 | 35 | black: '#000', 36 | white: '#fff', 37 | 38 | green: { 39 | 100: '#f0fff4', 40 | 200: '#c6f6d5', 41 | 300: '#9ae6b4', 42 | 400: '#68d391', 43 | 500: '#48bb78', 44 | 600: '#38a169', 45 | 700: '#2f855a', 46 | 800: '#276749', 47 | 900: '#22543d', 48 | }, 49 | 50 | gray: { 51 | 100: '#f7fafc', 52 | 200: '#edf2f7', 53 | 300: '#e2e8f0', 54 | 400: '#cbd5e0', 55 | 500: '#a0aec0', 56 | 600: '#718096', 57 | 700: '#4a5568', 58 | 800: '#2d3748', 59 | 900: '#1a202c', 60 | }, 61 | }, 62 | fontFamily: { 63 | sans: ['Arial'], 64 | serif: ['Prata'], 65 | mono: ['Arial'], 66 | }, 67 | }, 68 | variants: { 69 | // Some useful comment 70 | }, 71 | plugins: [ 72 | // Some useful comment 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /src/pages/Collections.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 37 | 38 | 39 | query ShopifyProducts { 40 | allShopifyCollection (limit: 100) { 41 | edges { 42 | node { 43 | id 44 | handle 45 | title 46 | descriptionHtml 47 | image { 48 | altText 49 | src: transformedSrc(maxWidth: 800, maxHeight: 800, crop: CENTER) 50 | } 51 | } 52 | } 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // Plugins 2 | import Vuex from 'vuex' 3 | import VueApollo from 'vue-apollo' 4 | 5 | //import Notifications from 'vue-notification' 6 | import Notifications from 'vue-notification/dist/ssr' 7 | import '~/assets/style/notification.css' 8 | 9 | // Dependencies 10 | import ApolloClient from 'apollo-boost' 11 | import fetch from 'isomorphic-fetch' 12 | 13 | import DefaultLayout from '~/layouts/Default.vue' 14 | import VueScrollTo from 'vue-scrollto' 15 | 16 | export default function (Vue, { appOptions, router, head, isClient }) { 17 | // Set default layout as a global component 18 | Vue.component('Layout', DefaultLayout) 19 | 20 | Vue.use(Vuex) 21 | Vue.use(VueApollo) 22 | Vue.use(Notifications) 23 | 24 | Vue.use(VueScrollTo, { 25 | duration: 500, 26 | easing: "ease", 27 | }) 28 | 29 | /* 30 | head.link.push({ 31 | rel: 'stylesheet', 32 | href: 'https://fonts.googleapis.com/css?family=Prata' 33 | }) 34 | */ 35 | 36 | // Create Apollo client 37 | const apolloClient = new ApolloClient({ 38 | fetch, 39 | uri: `https://${process.env.GRIDSOME_SHOPIFY_STOREFRONT}.myshopify.com/api/2020-07/graphql.json`, 40 | headers: { 41 | 'X-Shopify-Storefront-Access-Token': process.env.GRIDSOME_SHOPIFY_STOREFRONT_TOKEN 42 | } 43 | }) 44 | 45 | // Add client to vue-apollo provider 46 | const apolloProvider = new VueApollo({ 47 | defaultClient: apolloClient 48 | }) 49 | 50 | // Add provider to vue app 51 | appOptions.apolloProvider = apolloProvider 52 | 53 | // Create Vuex store 54 | appOptions.store = new Vuex.Store({ 55 | state: { 56 | cart: [] 57 | }, 58 | mutations: { 59 | addToCart: (state, newItem) => { 60 | const itemExists = state.cart.find(item => item.variantId === newItem.variantId) 61 | 62 | if (itemExists) itemExists.qty += newItem.qty 63 | else state.cart.push(newItem) 64 | }, 65 | removeFromCart: (state, itemId) => { 66 | const updatedCart = state.cart.filter(item => item.variantId !== itemId) 67 | state.cart = updatedCart 68 | } 69 | } 70 | }) 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/templates/Collection.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 51 | 52 | 53 | query Collection ($id: ID!) { 54 | shopifyCollection (id: $id) { 55 | id 56 | title 57 | descriptionHtml 58 | products { 59 | id 60 | title 61 | handle 62 | descriptionHtml 63 | priceRange { 64 | minVariantPrice { 65 | amount(format: true, currency: "USD") 66 | } 67 | } 68 | images (limit: 1) { 69 | id 70 | altText 71 | src: transformedSrc (maxWidth: 400, maxHeight: 300, crop: CENTER) 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Netlify Status](https://api.netlify.com/api/v1/badges/d38cd58f-ae41-48e5-8881-85bdd4357695/deploy-status)](https://app.netlify.com/sites/gridsome-shopify/deploys) 2 | 3 | # Shopify PWA starter for Gridsome 4 | 5 | This is a boilerplate starter, optmized to reach the highest scores on Google Lighthouse, aiming to showcase how Gridsome and Shopify can work together, to create an amazingly fast shopping experience for consumers. [View Demo](https://gridsome-shopify.netlify.com) 6 | 7 | 8 | 9 | ## Features 10 | 11 | - 4 x 100% Highest Scores on Google Lighthouse! 12 | - Full SSR (Server Side Redenring) Static Site 13 | - Progressive Web App (PWA) with "Add to Home Screen" button. 14 | - Connected to Shopify backend 15 | - Home Page showing Featured Products 16 | - Product Search 17 | - Product Page 18 | - Collection Search 19 | - All Collections Page 20 | - TailwindCSS with PurgeCSS to remove unused css. 21 | - SEO optimized 22 | - Sitemap 23 | 24 | ## Shopify Setup 25 | 26 | You will need to create a private app in Shopify, and give it access to the Storefront API - the default permissions should be fine for this starter. 27 | Make sure to note the Storefront API key, and your store name. 28 | 29 | ## Installation 30 | 31 | Install the Gridsome CLI. 32 | 33 | ```bash 34 | npm install -g @gridsome/cli # or 35 | yarn global add @gridsome/cli 36 | ``` 37 | 38 | ## Create Project 39 | 40 | You can either directly download this repository, or use Gridsome's CLI to download and install dependencies for you. 41 | 42 | ```bash 43 | # Clone repository 44 | git clone https://github.com/jsappme/gridsome-shopify-starter.git 45 | npm install # or 46 | yarn install 47 | 48 | # Download with CLI 49 | gridsome create my-gridsome-site jsappme/gridsome-shopify-starter 50 | ``` 51 | 52 | ## Developing 53 | 54 | You will need to add your Storefront API shop name (https://\.myshopify.com) & token to an env file before running this starter. 55 | I also recommend you add some collections in Shopify to best show off this starter. 56 | 57 | ``` 58 | # Note env's are prefixed with GRIDSOME_ to make them available to apollo client side 59 | GRIDSOME_SHOPIFY_STOREFRONT= 60 | GRIDSOME_SHOPIFY_STOREFRONT_TOKEN= 61 | ``` 62 | 63 | Or you can manually edit the [Shopify Source Plugin's](https://gridsome.org/plugins/gridsome-source-shopify) configurations in `gridsome.config.js`. 64 | 65 | Enter the site folder, and run `gridsome develop` to start a local development server. 66 | 67 | Happy coding! 68 | 69 | [Gridsome Shopify = ❤️](https://gridsomify.com) 70 | -------------------------------------------------------------------------------- /src/components/SearchInput.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 77 | 78 | 86 | 87 | -------------------------------------------------------------------------------- /src/css/main.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | @tailwind utilities; 6 | 7 | .theme-light { 8 | --bg-background-primary: white; 9 | --bg-background-secondary: #f7fafc; 10 | --bg-background-tertiary: #e2e8f0; 11 | 12 | --bg-background-form: white; 13 | 14 | --text-copy-primary: #2d3748; 15 | --text-copy-secondary: #4a5568; 16 | 17 | --border-border-color-primary: white; 18 | } 19 | 20 | .theme-light .search-highlighted { 21 | background: #f0fff4; 22 | } 23 | 24 | .theme-light .search-hover:hover { 25 | background: #f0fff4; 26 | } 27 | 28 | .theme-dark .search-highlighted { 29 | background: #2d3748; 30 | } 31 | 32 | .theme-dark .search-hover:hover { 33 | background: #2d3748; 34 | } 35 | 36 | .theme-dark { 37 | --bg-background-primary: #0D2438; 38 | --bg-background-secondary: #102C44; 39 | --bg-background-tertiary: #1E3951; 40 | 41 | --bg-background-form: #1a202c; 42 | 43 | --text-copy-primary: #cbd5e0; 44 | --text-copy-secondary: #e2e8f0; 45 | 46 | --border-border-color-primary: #1a202c; 47 | } 48 | 49 | .theme-dark nav .active { 50 | @apply border-white border-b; 51 | } 52 | 53 | .content-wrapper { 54 | transition: background-color .25s; 55 | } 56 | 57 | a { 58 | @apply text-green-700 font-bold; 59 | } 60 | 61 | a:hover { 62 | @apply text-green-800; 63 | } 64 | 65 | nav .active { 66 | font-weight: bold; 67 | @apply border-black border-b; 68 | } 69 | 70 | .container-inner { 71 | padding-left: 1rem; 72 | padding-right: 1rem; 73 | } 74 | 75 | @media (min-width: 640px) { 76 | .container-inner { 77 | max-width: 640px; 78 | } 79 | } 80 | 81 | @media (min-width: 768px) { 82 | .container-inner { 83 | max-width: 768px; 84 | } 85 | } 86 | 87 | @media (min-width: 1024px) { 88 | .container-inner { 89 | max-width: 800px; 90 | } 91 | } 92 | 93 | .checkmark { 94 | background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20width%3D%2229%22%20height%3D%2229%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cpath%20d%3D%22M4.427%2024.93A14.5%2014.5%200%201%201%2024.93%204.427c5.495%205.69%205.416%2014.733-.177%2020.326s-14.637%205.672-20.326.177z%22%20fill%3D%22%232E855A%22/%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M10.234%2012.803l3.67%203.253L20.766%2010%2023%2012l-9.096%208L8%2014.789l2.234-2z%22/%3E%3C/svg%3E"); 95 | background-repeat: no-repeat; 96 | @apply pl-10; 97 | } 98 | 99 | .responsive-container { 100 | position: relative; 101 | overflow: hidden; 102 | padding-top: 56.25%; 103 | } 104 | 105 | .responsive-iframe { 106 | position: absolute; 107 | top: 0; 108 | left: 0; 109 | width: 100%; 110 | height: 100%; 111 | border: 0; 112 | } 113 | -------------------------------------------------------------------------------- /src/pages/Index.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 51 | 52 | 53 | query ShopifyProducts { 54 | allShopifyProduct (limit: 6) { 55 | edges { 56 | node { 57 | id 58 | title 59 | handle 60 | availableForSale 61 | descriptionHtml 62 | priceRange { 63 | minVariantPrice { 64 | amount(format: true) 65 | } 66 | } 67 | images (limit: 1) { 68 | id 69 | altText 70 | src: transformedSrc (maxWidth: 400, maxHeight: 300, crop: CENTER) 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /gridsome.config.js: -------------------------------------------------------------------------------- 1 | // This is where project configuration and plugin options are located. 2 | // Learn more: https://gridsome.org/docs/config 3 | 4 | // Changes here require a server restart. 5 | // To restart press CTRL + C in terminal and run `gridsome develop` 6 | 7 | module.exports = { 8 | siteName: 'Gridsome Shopify Starter', 9 | siteUrl: 'https://gridsome-shopify.netlify.app/', 10 | siteDescription: 'PWA Headless ecommerce Gridsome Shopify starter', 11 | titleTemplate: '%s - Gridsome + Shopify', 12 | icon: { 13 | favicon: "./src/favicon.png", 14 | touchicon: "./src/favicon.png" 15 | }, 16 | plugins: [ 17 | 'gridsome-plugin-robots', 18 | 'gridsome-plugin-tailwindcss', 19 | { 20 | use: 'gridsome-plugin-pwa', 21 | options: { 22 | title: 'Gridsome Shopify Starter', 23 | description: 'PWA Headless ecommerce Gridsome Shopify starter',// Optional 24 | startUrl: '/', 25 | display: 'standalone', 26 | gcm_sender_id: undefined, 27 | statusBarStyle: 'default', 28 | manifestPath: 'manifest.json', 29 | disableServiceWorker: true, 30 | serviceWorkerPath: 'service-worker.js', 31 | cachedFileTypes: 'js,json,css,html,png,jpg,jpeg,svg', 32 | shortName: 'Gridsome Shopify', 33 | themeColor: '#000000', 34 | lang: "en-US", 35 | backgroundColor: '#000000', 36 | icon: './src/favicon.png', // must be provided like 'src/favicon.png' 37 | msTileImage: 'Gridsome Shopify', 38 | msTileColor: '#000000' 39 | } 40 | }, 41 | { 42 | use: '@gridsome/plugin-sitemap', 43 | options: { 44 | exclude: ['/exclude-me'], 45 | config: { 46 | '/collections/*': { 47 | changefreq: 'daily', 48 | priority: 0.5 49 | }, 50 | '/product/*': { 51 | changefreq: 'daily', 52 | priority: 0.5 53 | } 54 | } 55 | } 56 | }, 57 | { 58 | use: 'gridsome-source-shopify', 59 | options: { 60 | storeName: process.env.GRIDSOME_SHOPIFY_STOREFRONT, 61 | storefrontToken: process.env.GRIDSOME_SHOPIFY_STOREFRONT_TOKEN 62 | } 63 | }, 64 | { 65 | use: '@gridsome/plugin-google-analytics', 66 | options: { 67 | id: '' 68 | } 69 | }, 70 | { 71 | use: 'gridsome-plugin-flexsearch', 72 | options: { 73 | flexsearch: { 74 | profile: 'match' 75 | }, 76 | collections: [ 77 | { 78 | typeName: 'ShopifyProduct', 79 | indexName: 'Product', 80 | fields: ['title', 'handle', 'description'] 81 | }, 82 | { 83 | typeName: 'ShopifyCollection', 84 | indexName: 'Collection', 85 | fields: ['title', 'handle', 'description'] 86 | } 87 | ], 88 | searchFields: ['title', 'handle', 'tags'] 89 | } 90 | } 91 | ], 92 | templates: { 93 | ShopifyProduct: [ 94 | { 95 | path: '/product/:handle', 96 | component: './src/templates/Product.vue' 97 | } 98 | ], 99 | ShopifyCollection: [ 100 | { 101 | path: '/collection/:handle', 102 | component: './src/templates/Collection.vue' 103 | } 104 | ] 105 | }, 106 | transformers: { 107 | }, 108 | } 109 | -------------------------------------------------------------------------------- /src/templates/Product.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 122 | 123 | 124 | query Product ($id: ID!) { 125 | shopifyProduct (id: $id) { 126 | id 127 | descriptionHtml 128 | title 129 | tags 130 | images(limit: 4) { 131 | id 132 | altText 133 | src: transformedSrc(maxWidth: 600, maxHeight: 400, crop: CENTER) 134 | thumbnail: transformedSrc(maxWidth: 150, maxHeight: 150, crop: CENTER) 135 | } 136 | options { 137 | id 138 | name 139 | values 140 | } 141 | variants { 142 | id 143 | title 144 | price { 145 | amount(format: true) 146 | } 147 | selectedOptions { 148 | name 149 | value 150 | } 151 | image { 152 | id 153 | altText 154 | thumbnail: transformedSrc(maxWidth: 150, maxHeight: 150, crop: CENTER) 155 | } 156 | } 157 | } 158 | } 159 | 160 | -------------------------------------------------------------------------------- /src/pages/Cart.vue: -------------------------------------------------------------------------------- 1 | 90 | 91 | -------------------------------------------------------------------------------- /src/layouts/Default.vue: -------------------------------------------------------------------------------- 1 | 107 | 108 | 150 | 151 |