├── SAMPLE.env ├── _headers ├── src ├── img │ ├── logo-footer.png │ ├── logo-header.png │ ├── social │ │ ├── facebook.svg │ │ ├── vimeo.svg │ │ ├── twitter.svg │ │ └── instagram.svg │ └── github-icon.svg ├── components │ ├── Loader.js │ ├── SiteMetadata.js │ ├── bigcommerce │ │ ├── CurrencyFormatter.js │ │ ├── AddToCartButton.js │ │ ├── ProductCard.js │ │ ├── ProductPrices.js │ │ ├── Notify.css │ │ ├── Notify.js │ │ └── Cart.js │ ├── Content.js │ ├── PhotoGrid.js │ ├── Testimonials.js │ ├── loading.css │ ├── PreviewCompatibleImage.js │ ├── Pricing.js │ ├── Layout.js │ ├── BlogRoll.js │ ├── BlogItem.js │ ├── Navbar.js │ ├── Footer.js │ └── all.sass ├── pages │ ├── products │ │ └── index.md │ ├── contact │ │ ├── thanks.js │ │ └── index.js │ ├── 404.js │ ├── about │ │ └── index.md │ ├── cart │ │ └── index.js │ ├── blog │ │ ├── index.js │ │ ├── 2019-10-15-the-cosmos-awaits.md │ │ └── 2019-10-11-shopping-with-business-cat.md │ ├── index.md │ └── tags │ │ └── index.js ├── cms │ ├── preview-templates │ │ ├── AboutPagePreview.js │ │ ├── BlogPostPreview.js │ │ ├── ProductPagePreview.js │ │ └── IndexPagePreview.js │ └── cms.js ├── hooks │ └── useWindow.js ├── context │ ├── PriceProvider.js │ └── CartProvider.js ├── templates │ ├── tags.js │ ├── about-page.js │ ├── blog-post.js │ ├── product-page.js │ ├── product-details.js │ └── index-page.js └── lambda │ └── bigcommerce.js ├── static ├── img │ ├── art-chairs.jpg │ ├── bazaar-home.jpg │ ├── retail-shop.jpg │ ├── accessory-art.jpg │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── arts-and-crafts.jpg │ ├── city-background.jpg │ ├── close-up-cloth.jpg │ ├── architecture-blur.jpg │ ├── assorted-boutique.jpg │ ├── default-bc-product.png │ ├── animal-business-cat.jpg │ ├── background-bags-bows.jpg │ ├── bags-commerce-hanging.jpg │ ├── boutique-bright-business.jpg │ └── safari-pinned-tab.svg └── admin │ └── config.yml ├── netlify.toml ├── .gitignore ├── gatsby-browser.js ├── renovate.json ├── .github └── ISSUE_TEMPLATE │ ├── custom.md │ ├── feature_request.md │ └── bug_report.md ├── LICENSE ├── package.json ├── gatsby-node.js ├── gatsby-config.js └── README.md /SAMPLE.env: -------------------------------------------------------------------------------- 1 | API_STORE_HASH="" 2 | API_CLIENT_ID="" 3 | API_TOKEN="" 4 | API_SECRET="" 5 | CORS_ORIGIN="*" 6 | -------------------------------------------------------------------------------- /_headers: -------------------------------------------------------------------------------- 1 | [[headers]] 2 | for = "/static/*" 3 | [headers.values] 4 | Cache-Control = "public, max-age=360000" -------------------------------------------------------------------------------- /src/img/logo-footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/src/img/logo-footer.png -------------------------------------------------------------------------------- /src/img/logo-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/src/img/logo-header.png -------------------------------------------------------------------------------- /static/img/art-chairs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/art-chairs.jpg -------------------------------------------------------------------------------- /static/img/bazaar-home.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/bazaar-home.jpg -------------------------------------------------------------------------------- /static/img/retail-shop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/retail-shop.jpg -------------------------------------------------------------------------------- /static/img/accessory-art.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/accessory-art.jpg -------------------------------------------------------------------------------- /static/img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/favicon-16x16.png -------------------------------------------------------------------------------- /static/img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/favicon-32x32.png -------------------------------------------------------------------------------- /static/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/apple-touch-icon.png -------------------------------------------------------------------------------- /static/img/arts-and-crafts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/arts-and-crafts.jpg -------------------------------------------------------------------------------- /static/img/city-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/city-background.jpg -------------------------------------------------------------------------------- /static/img/close-up-cloth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/close-up-cloth.jpg -------------------------------------------------------------------------------- /src/components/Loader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './loading.css'; 4 | 5 | export default () =>
; 6 | -------------------------------------------------------------------------------- /static/img/architecture-blur.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/architecture-blur.jpg -------------------------------------------------------------------------------- /static/img/assorted-boutique.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/assorted-boutique.jpg -------------------------------------------------------------------------------- /static/img/default-bc-product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/default-bc-product.png -------------------------------------------------------------------------------- /src/pages/products/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | templateKey: 'product-page' 3 | path: /products 4 | title: Our Products 5 | image: /img/boutique-bright-business.jpg 6 | -------------------------------------------------------------------------------- /static/img/animal-business-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/animal-business-cat.jpg -------------------------------------------------------------------------------- /static/img/background-bags-bows.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/background-bags-bows.jpg -------------------------------------------------------------------------------- /static/img/bags-commerce-hanging.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/bags-commerce-hanging.jpg -------------------------------------------------------------------------------- /static/img/boutique-bright-business.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigcommerce/gatsby-bigcommerce-netlify-cms-starter/HEAD/static/img/boutique-bright-business.jpg -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "public" 3 | command = "npm run build" 4 | functions = "lambda" 5 | [build.environment] 6 | YARN_VERSION = "1.9.4" 7 | YARN_FLAGS = "--no-ignore-optional" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project dependencies 2 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 3 | node_modules 4 | .cache/ 5 | # Build directory 6 | public/ 7 | static/admin/*.bundle.* 8 | .DS_Store 9 | yarn-error.log 10 | .env 11 | lambda 12 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { CartProvider } from './src/context/CartProvider'; 3 | import { PriceProvider } from './src/context/PriceProvider'; 4 | export const wrapRootElement = ({ element }) => ( 5 | 6 | {element} 7 | 8 | ); 9 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "rangeStrategy": "replace", 6 | "lockFileMaintenance": { 7 | "enabled": true, 8 | "extends": "schedule:weekly" 9 | }, 10 | "packageRules": [ 11 | { 12 | "packageNames": ["netlify-cms-app"], 13 | "rangeStrategy": "bump" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/img/social/facebook.svg: -------------------------------------------------------------------------------- 1 | Facebook icon -------------------------------------------------------------------------------- /src/pages/contact/thanks.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Layout from '../../components/Layout' 3 | 4 | export default () => ( 5 | 6 |
7 |
8 |
9 |

Thank you!

10 |

This is a custom thank you page for form submissions

11 |
12 |
13 |
14 |
15 | ) 16 | -------------------------------------------------------------------------------- /src/components/SiteMetadata.js: -------------------------------------------------------------------------------- 1 | import { graphql, useStaticQuery } from 'gatsby' 2 | 3 | const useSiteMetadata = () => { 4 | const { site } = useStaticQuery( 5 | graphql` 6 | query SITE_METADATA_QUERY { 7 | site { 8 | siteMetadata { 9 | title 10 | description 11 | } 12 | } 13 | } 14 | ` 15 | ) 16 | return site.siteMetadata 17 | } 18 | 19 | export default useSiteMetadata 20 | -------------------------------------------------------------------------------- /src/components/bigcommerce/CurrencyFormatter.js: -------------------------------------------------------------------------------- 1 | const CurrencyFormatter = ({ currency, amount }) => { 2 | const languageCode = 3 | typeof window !== 'undefined' 4 | ? window.navigator.language || 'en-US' 5 | : 'en-US'; 6 | const formattedPrice = new Intl.NumberFormat(languageCode, { 7 | style: 'currency', 8 | currency 9 | }).format(amount); 10 | return amount && formattedPrice; 11 | }; 12 | 13 | export default CurrencyFormatter; 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | --- 11 | name: Other 12 | about: Something else 13 | 14 | --- 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/components/Content.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | export const HTMLContent = ({ content, className }) => ( 5 |
6 | ) 7 | 8 | const Content = ({ content, className }) => ( 9 |
{content}
10 | ) 11 | 12 | Content.propTypes = { 13 | content: PropTypes.node, 14 | className: PropTypes.string, 15 | } 16 | 17 | HTMLContent.propTypes = Content.propTypes 18 | 19 | export default Content 20 | -------------------------------------------------------------------------------- /src/cms/preview-templates/AboutPagePreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { AboutPageTemplate } from '../../templates/about-page' 4 | 5 | const AboutPagePreview = ({ entry, widgetFor }) => ( 6 | 11 | ) 12 | 13 | AboutPagePreview.propTypes = { 14 | entry: PropTypes.shape({ 15 | getIn: PropTypes.func, 16 | }), 17 | widgetFor: PropTypes.func, 18 | } 19 | 20 | export default AboutPagePreview 21 | -------------------------------------------------------------------------------- /src/cms/preview-templates/BlogPostPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { BlogPostTemplate } from '../../templates/blog-post' 4 | 5 | const BlogPostPreview = ({ entry, widgetFor }) => ( 6 | 12 | ) 13 | 14 | BlogPostPreview.propTypes = { 15 | entry: PropTypes.shape({ 16 | getIn: PropTypes.func, 17 | }), 18 | widgetFor: PropTypes.func, 19 | } 20 | 21 | export default BlogPostPreview 22 | -------------------------------------------------------------------------------- /src/img/social/vimeo.svg: -------------------------------------------------------------------------------- 1 | Vimeo icon -------------------------------------------------------------------------------- /src/cms/preview-templates/ProductPagePreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { ProductPageTemplate } from '../../templates/product-page' 4 | 5 | const ProductPagePreview = ({ entry, getAsset }) => { 6 | return ( 7 | 13 | ) 14 | } 15 | 16 | ProductPagePreview.propTypes = { 17 | entry: PropTypes.shape({ 18 | getIn: PropTypes.func, 19 | }), 20 | getAsset: PropTypes.func, 21 | } 22 | 23 | export default ProductPagePreview 24 | -------------------------------------------------------------------------------- /src/components/PhotoGrid.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import PreviewCompatibleImage from '../components/PreviewCompatibleImage' 4 | 5 | const PhotoGrid = ({ gridItems }) => ( 6 |
7 | {gridItems.map(item => ( 8 |
9 | 10 |
11 | ))} 12 |
13 | ) 14 | 15 | PhotoGrid.propTypes = { 16 | gridItems: PropTypes.arrayOf( 17 | PropTypes.shape({ 18 | image: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), 19 | text: PropTypes.string, 20 | }) 21 | ), 22 | } 23 | 24 | export default PhotoGrid 25 | -------------------------------------------------------------------------------- /src/components/Testimonials.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { v4 } from 'uuid' 4 | 5 | const Testimonials = ({ testimonials }) => ( 6 |
7 | {testimonials.map(testimonial => ( 8 |
9 |
10 | {testimonial.quote} 11 |
12 | – {testimonial.author} 13 |
14 |
15 | ))} 16 |
17 | ) 18 | 19 | Testimonials.propTypes = { 20 | testimonials: PropTypes.arrayOf( 21 | PropTypes.shape({ 22 | quote: PropTypes.string, 23 | author: PropTypes.string, 24 | }) 25 | ), 26 | } 27 | 28 | export default Testimonials 29 | -------------------------------------------------------------------------------- /src/components/loading.css: -------------------------------------------------------------------------------- 1 | .spinner { 2 | opacity: 1; 3 | position: relative; 4 | transition: opacity linear 0.1s; 5 | } 6 | 7 | @keyframes spinner { 8 | 0% { 9 | transform: translate3d(-50%, -50%, 0) rotate(0deg); 10 | } 11 | 100% { 12 | transform: translate3d(-50%, -50%, 0) rotate(360deg); 13 | } 14 | } 15 | 16 | .spinner::before { 17 | animation: 2s linear infinite spinner; 18 | border: solid 3px #eee; 19 | border-bottom-color: #000; 20 | border-radius: 50%; 21 | content: ''; 22 | height: 40px; 23 | left: 50%; 24 | opacity: inherit; 25 | position: absolute; 26 | top: 50%; 27 | transform: translate3d(-50%, -50%, 0); 28 | transform-origin: center; 29 | width: 40px; 30 | will-change: transform; 31 | } 32 | -------------------------------------------------------------------------------- /src/img/social/twitter.svg: -------------------------------------------------------------------------------- 1 | Twitter icon -------------------------------------------------------------------------------- /src/cms/cms.js: -------------------------------------------------------------------------------- 1 | import CMS from 'netlify-cms-app' 2 | import uploadcare from 'netlify-cms-media-library-uploadcare' 3 | import cloudinary from 'netlify-cms-media-library-cloudinary' 4 | 5 | import AboutPagePreview from './preview-templates/AboutPagePreview' 6 | import BlogPostPreview from './preview-templates/BlogPostPreview' 7 | import ProductPagePreview from './preview-templates/ProductPagePreview' 8 | import IndexPagePreview from './preview-templates/IndexPagePreview' 9 | 10 | CMS.registerMediaLibrary(uploadcare); 11 | CMS.registerMediaLibrary(cloudinary); 12 | 13 | CMS.registerPreviewTemplate('index', IndexPagePreview) 14 | CMS.registerPreviewTemplate('about', AboutPagePreview) 15 | CMS.registerPreviewTemplate('products', ProductPagePreview) 16 | CMS.registerPreviewTemplate('blog', BlogPostPreview) 17 | -------------------------------------------------------------------------------- /src/hooks/useWindow.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default () => { 4 | const [windowSize, setWindowSize] = useState(() => { 5 | return ( 6 | typeof window !== 'undefined' && { 7 | width: window.innerWidth, 8 | height: window.innerHeight 9 | } 10 | ); 11 | }); 12 | 13 | useEffect(() => { 14 | const onResize = () => 15 | typeof window !== 'undefined' && 16 | setWindowSize({ 17 | width: window.innerWidth, 18 | height: window.innerHeight 19 | }); 20 | if (typeof window !== 'undefined') { 21 | window.addEventListener('resize', onResize); 22 | } 23 | return () => 24 | typeof window !== 'undefined' && 25 | window.removeEventListener('resize', onResize); 26 | }); 27 | 28 | return windowSize; 29 | }; 30 | -------------------------------------------------------------------------------- /src/cms/preview-templates/IndexPagePreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { IndexPageTemplate } from '../../templates/index-page' 4 | 5 | const IndexPagePreview = ({ entry, getAsset }) => { 6 | const data = entry.getIn(['data']).toJS() 7 | 8 | if (data) { 9 | return ( 10 | 18 | ) 19 | } else { 20 | return
Loading...
21 | } 22 | } 23 | 24 | IndexPagePreview.propTypes = { 25 | entry: PropTypes.shape({ 26 | getIn: PropTypes.func, 27 | }), 28 | getAsset: PropTypes.func, 29 | } 30 | 31 | export default IndexPagePreview 32 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby'; 3 | import Layout from '../components/Layout' 4 | 5 | const NotFoundPage = () => ( 6 | 7 |
8 |
9 |
10 |
11 |
12 |
13 |

Page Not Found

14 |

You've hit a page that doesn't exist. Looking to shop?

15 | Take a look around. 16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ) 24 | 25 | export default NotFoundPage 26 | -------------------------------------------------------------------------------- /src/pages/about/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | templateKey: 'about-page' 3 | path: /about 4 | title: About Us 5 | image: /img/bags-commerce-hanging.jpg 6 | --- 7 | ### Creatively sourced for those that care 8 | We've searched the world to find products that we feel will not only speak to us, but any individuals that desire a positive outlook in a sustainable way. 9 | 10 | ### We're adventurers 11 | Check in and you'll see us focused on an exciting experience that is typically a bold, sometimes risky, undertaking. Our travels have taken us into activities ranging from skydiving to mountain climbing to scuba diving and back. 12 | 13 | ### Retail is our therapy 14 | Taking the scenic route through a city and figuring out what resonates with us is one of our favorite activies. Curation equals relaxation for us. 15 | 16 | ### We started a while ago 17 | Our first retail shop was up and running almost 30 years ago! 18 | -------------------------------------------------------------------------------- /src/context/PriceProvider.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useState, useEffect } from 'react'; 2 | 3 | const PriceContext = createContext(); 4 | 5 | export const PriceProvider = ({ children }) => { 6 | const [prices, setPrices] = useState({}); 7 | 8 | const fetchPrices = () => { 9 | fetch(`/.netlify/functions/bigcommerce?endpoint=catalog/products`, { 10 | credentials: 'same-origin', 11 | mode: 'same-origin' 12 | }) 13 | .then(res => res.json()) 14 | .then(response => { 15 | setPrices( 16 | response.data && 17 | response.data.reduce((acc, item) => { 18 | acc[item.id] = item; 19 | return acc; 20 | }, {}) 21 | ); 22 | }) 23 | .catch(error => { 24 | console.error(error); 25 | setPrices({ error }); 26 | }); 27 | }; 28 | 29 | useEffect(() => fetchPrices(), []); 30 | 31 | return ( 32 | {children} 33 | ); 34 | }; 35 | 36 | export default PriceContext; 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | --- 11 | name: Feature request 12 | about: Suggest an idea for this project 13 | 14 | --- 15 | 16 | 17 | 18 | ## Feature request 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | **What is the expected behavior?** 27 | 28 | 29 | **What is motivation or use case for adding/changing the behavior?** 30 | 31 | 32 | **How should this be implemented in your opinion?** 33 | 34 | 35 | **Are you willing to work on this yourself?** 36 | yes 37 | -------------------------------------------------------------------------------- /src/pages/cart/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Layout from '../../components/Layout' 4 | import Cart from '../../components/bigcommerce/Cart' 5 | 6 | export default class CartIndexPage extends React.Component { 7 | render() { 8 | return ( 9 | 10 |
13 |

22 | Your Cart 23 |

24 |
25 |
26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/components/bigcommerce/AddToCartButton.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import CartContext from '../../context/CartProvider'; 3 | 4 | const AddToCartButton = ({ children, productId, variantId }) => { 5 | const value = useContext(CartContext); 6 | const addToCart = value && value.addToCart; 7 | const addingToCart = value && value.state.addingToCart; 8 | 9 | return ( 10 |
11 |
12 |
13 |
14 | 21 |
22 |
23 |
24 | ); 25 | }; 26 | 27 | export default AddToCartButton; 28 | -------------------------------------------------------------------------------- /src/components/PreviewCompatibleImage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Img from 'gatsby-image' 4 | 5 | const PreviewCompatibleImage = ({ imageInfo }) => { 6 | const imageStyle = { borderRadius: '5px' } 7 | const { alt = '', childImageSharp, image } = imageInfo 8 | 9 | if (!!image && !!image.childImageSharp) { 10 | return ( 11 | {alt} 12 | ) 13 | } 14 | 15 | if (!!childImageSharp) { 16 | return {alt} 17 | } 18 | 19 | if (!!image && typeof image === 'string') 20 | return {alt} 21 | 22 | return null 23 | } 24 | 25 | PreviewCompatibleImage.propTypes = { 26 | imageInfo: PropTypes.shape({ 27 | alt: PropTypes.string, 28 | childImageSharp: PropTypes.object, 29 | image: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired, 30 | style: PropTypes.object, 31 | }).isRequired, 32 | } 33 | 34 | export default PreviewCompatibleImage 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 gatsbyjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /static/img/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 11 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/pages/blog/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Layout from '../../components/Layout' 4 | import BlogRoll from '../../components/BlogRoll' 5 | 6 | export default class BlogIndexPage extends React.Component { 7 | render() { 8 | return ( 9 | 10 |
16 |

25 | The Store Blog 26 |

27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Pricing.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const Pricing = ({ data }) => ( 5 |
6 | {data.map(price => ( 7 |
8 |
9 |

10 | {price.plan} 11 |

12 |

13 | ${price.price} 14 |

15 |

{price.description}

16 |
    17 | {price.items.map(item => ( 18 |
  • 19 | {item} 20 |
  • 21 | ))} 22 |
23 |
24 |
25 | ))} 26 |
27 | ) 28 | 29 | Pricing.propTypes = { 30 | data: PropTypes.arrayOf( 31 | PropTypes.shape({ 32 | plan: PropTypes.string, 33 | price: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 34 | description: PropTypes.string, 35 | items: PropTypes.array, 36 | }) 37 | ), 38 | } 39 | 40 | export default Pricing 41 | -------------------------------------------------------------------------------- /src/pages/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | templateKey: index-page 3 | title: The world brought to your doorstep 4 | subtitle: Your one-stop shop for unique products you'll love 5 | image: /img/city-background.jpg 6 | heading: The world brought to your doorstep 7 | mainpitch: 8 | title: What is My Store? 9 | description: > 10 | My Store is a journey waiting to be had, from our journeys in the past. An 11 | ever changing collection of products that are meant to help broaden your 12 | daily life through the creation of others. 13 | bigimage: 14 | image: /img/bazaar-home.jpg 15 | alt: Bazaar 16 | intro: 17 | blurbs: 18 | - image: /img/close-up-cloth.jpg 19 | text: > 20 | Close up of cloth 21 | - image: /img/art-chairs.jpg 22 | text: > 23 | Artistic chairs 24 | - image: /img/arts-and-crafts.jpg 25 | text: > 26 | Colorful arts and crafts 27 | - image: /img/assorted-boutique.jpg 28 | text: > 29 | Assorted boutique 30 | heading: Purpose 31 | description: > 32 | Looking for a final set piece for your home that brings you back to a certain memort? Something 33 | for a rainy day to help open your mind? A piece of apparel that tells a story all it's 34 | own? That's what we seek out and hope you'll find here. 35 | --- 36 | 37 | -------------------------------------------------------------------------------- /src/components/bigcommerce/ProductCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'gatsby'; 3 | import AddToCartButton from './AddToCartButton'; 4 | import ProductPrices from './ProductPrices'; 5 | 6 | class ProductCard extends React.Component { 7 | render() { 8 | const product = this.props.product; 9 | 10 | return ( 11 |
12 | 13 |
14 | {product.name} 22 |
23 | 24 | 25 |
26 |

27 | {product.name} 28 |

29 | 30 | 31 |
32 | 33 | 36 | Add to Cart 37 | 38 |
39 | ) 40 | } 41 | } 42 | 43 | export default ProductCard; 44 | -------------------------------------------------------------------------------- /src/pages/tags/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { kebabCase } from 'lodash' 3 | import Helmet from 'react-helmet' 4 | import { Link, graphql } from 'gatsby' 5 | import Layout from '../../components/Layout' 6 | 7 | const TagsPage = ({ 8 | data: { 9 | allMarkdownRemark: { group }, 10 | site: { 11 | siteMetadata: { title }, 12 | }, 13 | }, 14 | }) => ( 15 | 16 |
17 | 18 |
19 |
20 |
24 |

Tags

25 |
    26 | {group.map(tag => ( 27 |
  • 28 | 29 | {tag.fieldValue} ({tag.totalCount}) 30 | 31 |
  • 32 | ))} 33 |
34 |
35 |
36 |
37 |
38 |
39 | ) 40 | 41 | export default TagsPage 42 | 43 | export const tagPageQuery = graphql` 44 | query TagsQuery { 45 | site { 46 | siteMetadata { 47 | title 48 | } 49 | } 50 | allMarkdownRemark(limit: 1000) { 51 | group(field: frontmatter___tags) { 52 | fieldValue 53 | totalCount 54 | } 55 | } 56 | } 57 | ` 58 | -------------------------------------------------------------------------------- /src/img/social/instagram.svg: -------------------------------------------------------------------------------- 1 | Instagram icon -------------------------------------------------------------------------------- /src/components/Layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import Footer from './Footer'; 4 | import Navbar from './Navbar'; 5 | import Notify from './bigcommerce/Notify'; 6 | import './all.sass'; 7 | import './Layout.css'; 8 | import useSiteMetadata from './SiteMetadata'; 9 | 10 | const TemplateWrapper = ({ children }) => { 11 | const { title, description } = useSiteMetadata(); 12 | return ( 13 |
14 | 15 | 16 | {title} 17 | 18 | 19 | 24 | 30 | 36 | 37 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
{children}
53 |
54 |
55 | ); 56 | }; 57 | 58 | export default TemplateWrapper; 59 | -------------------------------------------------------------------------------- /src/components/BlogRoll.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { graphql, StaticQuery } from 'gatsby' 4 | import BlogItem from './BlogItem' 5 | 6 | class BlogRoll extends React.Component { 7 | render() { 8 | const { data } = this.props 9 | const { edges: posts } = data.allMarkdownRemark 10 | 11 | return ( 12 |
13 | {posts && 14 | posts.map(({ node: post }) => ( 15 | 16 | ))} 17 |
18 | ) 19 | } 20 | } 21 | 22 | BlogRoll.propTypes = { 23 | data: PropTypes.shape({ 24 | allMarkdownRemark: PropTypes.shape({ 25 | edges: PropTypes.array, 26 | }), 27 | }), 28 | } 29 | 30 | export default () => ( 31 | } 64 | /> 65 | ) 66 | -------------------------------------------------------------------------------- /src/components/bigcommerce/ProductPrices.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import CurrencyFormatter from './CurrencyFormatter'; 3 | import PriceContext from '../../context/PriceProvider'; 4 | 5 | const currencyCode = 'USD'; // TODO: Move this to use settings merchant sets up in BigCommerce (v2 Currencies API) 6 | 7 | const ProductPrices = ({ product }) => { 8 | const prices = useContext(PriceContext); 9 | const latestProduct = 10 | prices && prices[product.bigcommerce_id] 11 | ? prices[product.bigcommerce_id] 12 | : { 13 | price: null, 14 | calculated_price: null, 15 | sale_price: null 16 | }; 17 | return ( 18 |
19 | {latestProduct.sale_price !== 0 ? ( 20 |

21 | 22 | 26 | 27 | 28 | 32 | 33 |

34 | ) : ( 35 |

36 | 37 | 41 | 42 |

43 | )} 44 |
45 | ); 46 | }; 47 | 48 | export default ProductPrices; 49 | -------------------------------------------------------------------------------- /src/components/bigcommerce/Notify.css: -------------------------------------------------------------------------------- 1 | .Notify { 2 | position: fixed; 3 | top: 0; 4 | right: 0; 5 | z-index: 100; 6 | } 7 | @keyframes fadeIn { 8 | 0% { 9 | opacity: 0; 10 | } 11 | 100% { 12 | opacity: 1; 13 | } 14 | } 15 | .Animate { 16 | animation: fadeIn; 17 | animation-duration: 300ms; 18 | } 19 | .Notification { 20 | bottom: 0; 21 | right: 0; 22 | height: 100vh; 23 | padding: 1rem; 24 | max-width: 40rem; 25 | min-height: 20px; 26 | background-color: white; 27 | font-weight: 600; 28 | box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.25); 29 | display: flex; 30 | flex-direction: column; 31 | } 32 | .bc-cart-header div { 33 | padding: .4rem 0 !important; 34 | } 35 | .Notification .bc-cart-item { 36 | padding: 0.8rem 0; 37 | } 38 | .Notification .bc-cart-item-meta .bc-cart-item__product-title, .Notification .bc-cart-item-quantity div, .Notification .bc-cart-item-total-price { 39 | font-size: 1rem; 40 | } 41 | .Notification .bc-btn { 42 | margin-top: 0.5rem; 43 | min-width: 100%; 44 | } 45 | .Content { 46 | display: flex; 47 | font-size: 1rem; 48 | width: 100%; 49 | height: 70vh; 50 | } 51 | .Title { 52 | display: flex; 53 | font-size: 1.4rem; 54 | } 55 | .Title .Text { 56 | flex-grow: 1; 57 | } 58 | .Icon { 59 | padding: 2px; 60 | } 61 | .Icon img { 62 | cursor: pointer; 63 | } 64 | .Message { 65 | flex: 1; 66 | flex-basis: auto; 67 | } 68 | .Message .container { 69 | overflow: scroll; 70 | height: 100%; 71 | } 72 | .Actions { 73 | flex: 1; 74 | } 75 | .Close { 76 | margin-right: 1rem; 77 | cursor: pointer; 78 | position: absolute; 79 | right: 0; 80 | } 81 | 82 | @media only screen and (max-width: 600px) { 83 | .Notify { 84 | top: 0; 85 | left: 0; 86 | right: 0; 87 | } 88 | .Notification { 89 | width: 100vw; 90 | } 91 | .Message .container { 92 | height: 70%; 93 | } 94 | .bc-cart-header div { 95 | min-width: 4.6rem; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/components/BlogItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'gatsby'; 3 | import PreviewCompatibleImage from './PreviewCompatibleImage' 4 | 5 | class BlogItem extends React.Component { 6 | render() { 7 | const post = this.props.post; 8 | const columnWidth = this.props.columnWidth ? this.props.columnWidth : 'is-6'; 9 | 10 | return ( 11 |
12 |
17 |
18 | {post.frontmatter.featuredimage ? ( 19 |
20 | 28 |
29 | ) : null} 30 |

31 | 35 | {post.frontmatter.title} 36 | 37 | 38 | 39 | {post.frontmatter.date} 40 | 41 |

42 |
43 |

44 | {post.excerpt} 45 |
46 |
47 | 48 | Keep Reading → 49 | 50 |

51 |
52 |
53 | ) 54 | } 55 | } 56 | 57 | export default BlogItem; 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | --- 11 | name: Bug report 12 | about: Create a report to help us improve 13 | --- 14 | 15 | 16 | 17 | 18 | # Bug report 19 | 20 | 21 | 22 | 23 | 24 | 25 | **What is the current behavior?** 26 | 27 | 28 | **If the current behavior is a bug, please provide the steps to reproduce.** 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | **What is the expected behavior?** 38 | 39 | 40 | 41 | 42 | 43 | **Other relevant information:** 44 | 45 | 46 | 47 | Node.js version: 48 | NPM/Yarn version 49 | Operating System: 50 | Additional tools: 51 | -------------------------------------------------------------------------------- /src/templates/tags.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Helmet from 'react-helmet' 3 | import { Link, graphql } from 'gatsby' 4 | import Layout from '../components/Layout' 5 | 6 | class TagRoute extends React.Component { 7 | render() { 8 | const posts = this.props.data.allMarkdownRemark.edges 9 | const postLinks = posts.map(post => ( 10 |
  • 11 | 12 |

    {post.node.frontmatter.title}

    13 | 14 |
  • 15 | )) 16 | const tag = this.props.pageContext.tag 17 | const title = this.props.data.site.siteMetadata.title 18 | const totalCount = this.props.data.allMarkdownRemark.totalCount 19 | const tagHeader = `${totalCount} post${ 20 | totalCount === 1 ? '' : 's' 21 | } tagged with “${tag}”` 22 | 23 | return ( 24 | 25 |
    26 | 27 |
    28 |
    29 |
    33 |

    {tagHeader}

    34 |
      {postLinks}
    35 |

    36 | Browse all tags 37 |

    38 |
    39 |
    40 |
    41 |
    42 |
    43 | ) 44 | } 45 | } 46 | 47 | export default TagRoute 48 | 49 | export const tagPageQuery = graphql` 50 | query TagPage($tag: String) { 51 | site { 52 | siteMetadata { 53 | title 54 | } 55 | } 56 | allMarkdownRemark( 57 | limit: 1000 58 | sort: { fields: [frontmatter___date], order: DESC } 59 | filter: { frontmatter: { tags: { in: [$tag] } } } 60 | ) { 61 | totalCount 62 | edges { 63 | node { 64 | fields { 65 | slug 66 | } 67 | frontmatter { 68 | title 69 | } 70 | } 71 | } 72 | } 73 | } 74 | ` 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-bigcommerce-netlify-cms-starter", 3 | "description": "Example Gatsby, BigCommerce and Netlify CMS project meant to jump start ecommerce sites.", 4 | "version": "0.5.0", 5 | "author": "BigCommerce", 6 | "dependencies": { 7 | "aws-sdk": "^2.488.0", 8 | "axios": "^0.19.0", 9 | "bulma": "^0.7.0", 10 | "cookie": "^0.4.0", 11 | "gatsby": "^2.1.34", 12 | "gatsby-image": "^2.0.23", 13 | "gatsby-plugin-netlify": "^2.0.6", 14 | "gatsby-plugin-netlify-cms": "^4.1.21", 15 | "gatsby-plugin-purgecss": "^4.0.0", 16 | "gatsby-plugin-react-helmet": "^3.0.4", 17 | "gatsby-plugin-sass": "^2.0.7", 18 | "gatsby-plugin-sharp": "^2.0.15", 19 | "gatsby-remark-copy-linked-files": "^2.0.7", 20 | "gatsby-remark-images": "^3.0.1", 21 | "gatsby-remark-relative-images": "^0.2.1", 22 | "gatsby-source-bigcommerce": "^0.3.3", 23 | "gatsby-source-filesystem": "^2.0.26", 24 | "gatsby-transformer-remark": "^2.3.4", 25 | "gatsby-transformer-sharp": "^2.1.9", 26 | "lodash": "^4.17.5", 27 | "lodash-webpack-plugin": "^0.11.4", 28 | "netlify-cms-app": "^2.9.5", 29 | "netlify-cms-media-library-cloudinary": "^1.3.2", 30 | "netlify-cms-media-library-uploadcare": "^0.5.2", 31 | "node-sass": "^4.11.0", 32 | "parcel-bundler": "^1.9.4", 33 | "prop-types": "^15.6.0", 34 | "qs": "^6.7.0", 35 | "react": "^16.8.4", 36 | "react-dom": "^16.8.4", 37 | "react-helmet": "^5.2.0", 38 | "set-cookie-parser": "^2.3.5", 39 | "uuid": "^3.2.1" 40 | }, 41 | "keywords": [ 42 | "gatsby", 43 | "ecommerce", 44 | "bigcommerce", 45 | "netlify" 46 | ], 47 | "license": "MIT", 48 | "main": "n/a", 49 | "scripts": { 50 | "clean": "gatsby clean", 51 | "start:app": "npm run develop", 52 | "start:lambda": "netlify-lambda serve src/lambda", 53 | "start": "concurrently \"yarn start:lambda\" \"yarn develop\"", 54 | "build:app": "npm run clean && gatsby build", 55 | "build:lambda": "netlify-lambda build src/lambda", 56 | "build": "run-p build:**", 57 | "develop": "npm run clean && gatsby develop", 58 | "serve": "gatsby serve", 59 | "format": "prettier --trailing-comma es5 --no-semi --single-quote --write \"{gatsby-*.js,src/**/*.js}\"", 60 | "test": "echo \"Error: no test specified\" && exit 1" 61 | }, 62 | "devDependencies": { 63 | "concurrently": "^5.0.0", 64 | "dotenv": "^8.0.0", 65 | "http-proxy-middleware": "^0.20.0", 66 | "netlify-lambda": "^1.4.3", 67 | "npm-run-all": "^4.1.5", 68 | "prettier": "^1.15.3" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/img/github-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |