├── .eslintignore
├── .prettierignore
├── src
├── assets
│ ├── avatar.jpg
│ ├── favicons
│ │ ├── favicon-16x16.png
│ │ └── favicon-32x32.png
│ ├── Logo.svg
│ └── shop-logo.svg
├── style
│ └── global.css
├── components
│ ├── Footer.js
│ ├── ProductCard.js
│ ├── CategoriesChoiceBar.js
│ └── Header.js
├── layouts
│ └── layout.js
├── pages
│ └── 404.js
├── sections
│ ├── CustomersChoice.js
│ ├── Products.js
│ ├── BestSellers.js
│ ├── ReviewsSection.js
│ └── Contact.js
└── templates
│ ├── index.js
│ └── product.js
├── .flotiq
├── .env.dist
├── InternalContentTypeMedia
│ ├── _media-141dc3a8-ea65-4eeb-9646-b2a6a52cf3d6.jpg
│ ├── _media-1500aa65-ab06-46cf-a912-991aa18c5cfc.jpg
│ ├── _media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3.jpg
│ ├── _media-4a805ccc-c489-4e5f-a4a5-0170576df668.jpg
│ ├── _media-67277cc5-d5eb-45da-a2d5-a79008c55698.jpg
│ ├── _media-6df43396-183e-4252-b1c2-c642368a5eb1.jpg
│ ├── _media-c415c7a8-f771-4904-833a-1ec7d6e899d4.jpg
│ ├── _media-cf9becdd-cab4-45e3-a42d-23e90c5fa789.jpg
│ ├── _media-dd3752a8-ba37-45bd-9e2c-fde485f1c47e.jpg
│ ├── _media-eb50be1d-9079-4aa3-b90e-7851886b3a2d.jpg
│ ├── contentObjectMedia.json
│ └── ContentTypeDefinition.json
└── ContentTypeProduct
│ ├── ContentTypeDefinition.json
│ └── contentObjectProduct.json
├── postcss.config.js
├── .prettierrc
├── .github
├── dependabot.yaml
├── ISSUE_TEMPLATE
│ ├── --anything-else.md
│ └── ---bug-report.md
└── workflows
│ ├── push.yml
│ ├── cloudflare_worker.yml
│ └── pull_requests.yml
├── wrangler.toml
├── netlify.toml
├── gatsby-browser.js
├── app.json
├── static.json
├── tailwind.config.js
├── LICENSE
├── .gitignore
├── gatsby-node.js
├── worker-index.js
├── .eslintrc.js
├── package.json
├── gatsby-config.js
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .cache
2 | package.json
3 | package-lock.json
4 | public
5 |
--------------------------------------------------------------------------------
/src/assets/avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/src/assets/avatar.jpg
--------------------------------------------------------------------------------
/.flotiq/.env.dist:
--------------------------------------------------------------------------------
1 | GATSBY_FLOTIQ_API_KEY=
2 | SNIPCART_API_KEY=_YOUR_VALUE_
3 | GA_MEASUREMENT_ID=_YOUR_VALUE_
4 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/src/assets/favicons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/src/assets/favicons/favicon-16x16.png
--------------------------------------------------------------------------------
/src/assets/favicons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/src/assets/favicons/favicon-32x32.png
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": true,
4 | "singleQuote": true,
5 | "tabWidth": 4,
6 | "trailingComma": "es5"
7 | }
8 |
--------------------------------------------------------------------------------
/.github/dependabot.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "npm"
4 | directory: '/'
5 | schedule:
6 | interval: 'monthly'
7 |
--------------------------------------------------------------------------------
/src/style/global.css:
--------------------------------------------------------------------------------
1 | /* Tailwind import */
2 | @tailwind base;
3 | @tailwind components;
4 | @tailwind utilities;
5 |
6 | html {
7 | scroll-behavior: smooth;
8 | }
9 |
--------------------------------------------------------------------------------
/wrangler.toml:
--------------------------------------------------------------------------------
1 | name = "flotiq-gatsby-shop-1"
2 | main = "worker-index.js"
3 | compatibility_date = "2024-03-27"
4 | workers_dev = true
5 |
6 | [site]
7 | bucket = "./public"
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-141dc3a8-ea65-4eeb-9646-b2a6a52cf3d6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-141dc3a8-ea65-4eeb-9646-b2a6a52cf3d6.jpg
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc.jpg
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3.jpg
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-4a805ccc-c489-4e5f-a4a5-0170576df668.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-4a805ccc-c489-4e5f-a4a5-0170576df668.jpg
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-67277cc5-d5eb-45da-a2d5-a79008c55698.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-67277cc5-d5eb-45da-a2d5-a79008c55698.jpg
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-6df43396-183e-4252-b1c2-c642368a5eb1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-6df43396-183e-4252-b1c2-c642368a5eb1.jpg
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-c415c7a8-f771-4904-833a-1ec7d6e899d4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-c415c7a8-f771-4904-833a-1ec7d6e899d4.jpg
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-cf9becdd-cab4-45e3-a42d-23e90c5fa789.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-cf9becdd-cab4-45e3-a42d-23e90c5fa789.jpg
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-dd3752a8-ba37-45bd-9e2c-fde485f1c47e.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-dd3752a8-ba37-45bd-9e2c-fde485f1c47e.jpg
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flotiq/flotiq-gatsby-shop-1/HEAD/.flotiq/InternalContentTypeMedia/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d.jpg
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | command = "gatsby build"
3 | publish = "public/"
4 |
5 | [template.environment]
6 | GATSBY_FLOTIQ_API_KEY="YOUR FLOTIQ API KEY"
7 | SNIPCART_API_KEY="YOUR SNIPCART API KEY"
8 | GA_MEASUREMENT_ID="YOUR MEASUREMENT ID"
9 |
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | import './src/style/global.css';
2 | import '@fontsource/inter/300.css';
3 | import '@fontsource/inter/400.css';
4 | import '@fontsource/inter/600.css';
5 | import '@fontsource/ubuntu/300.css';
6 | import '@fontsource/ubuntu/300-italic.css';
7 | import '@fontsource/ubuntu/400.css';
8 | import '@fontsource/ubuntu/500.css';
9 | import 'react-multi-carousel/lib/styles.css';
10 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Gatsby starter boilerplate",
3 | "env":{
4 | "GATSBY_FLOTIQ_API_KEY": {
5 | "description": "Flotiq API key"
6 | },
7 | "SNIPCART_API_KEY": {
8 | "description": "Snipcart API key"
9 | },
10 | "GA_MEASUREMENT_ID": {
11 | "description": "Google Analytics Measurement ID"
12 | }
13 | },
14 | "buildpacks": [
15 | {
16 | "url": "heroku/nodejs"
17 | },
18 | {
19 | "url": "https://github.com/heroku/heroku-buildpack-static"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/static.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": "public/",
3 | "headers": {
4 | "/**": {
5 | "Cache-Control": "public, max-age=0, must-revalidate"
6 | },
7 | "/**.css": {
8 | "Cache-Control": "public, max-age=31536000, immutable"
9 | },
10 | "/**.js": {
11 | "Cache-Control": "public, max-age=31536000, immutable"
12 | },
13 | "/static/**": {
14 | "Cache-Control": "public, max-age=31536000, immutable"
15 | },
16 | "/icons/*.png": {
17 | "Cache-Control": "public, max-age=31536000, immutable"
18 | }
19 | },
20 | "https_only": true,
21 | "error_page": "404"
22 | }
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/--anything-else.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Anything else"
3 | about: "For help, support, features & ideas - please use https://discord.gg/FwXcHnX"
4 |
5 | ---
6 |
7 | Click "Preview" for a nicer view!
8 |
9 | For support, help, questions, requests and ideas use https://discord.gg/FwXcHnX
10 |
11 | Alternatively, check out these resources below. Thanks!
12 |
13 | - [Discord](https://discord.gg/FwXcHnX)
14 | - [Gatsby API reference](https://docs.ghost.org/api/gatsby/)
15 | - [Content API Docs](https://flotiq.com/docs/panel/)
16 | - [Gatsby.js](https://www.gatsbyjs.org)
17 | - [GraphQL](https://graphql.org/)
18 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { PoweredByFlotiq } from 'flotiq-components-react';
3 |
4 | const Footer = () => (
5 |
18 | );
19 |
20 | export default Footer;
21 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: [
3 | './node_modules/flotiq-components-react/dist/**/*.{js,jsx,ts,tsx}',
4 | './src/**/*.{js,jsx,ts,tsx}',
5 | ],
6 | theme: {
7 | extend: {
8 | colors: {
9 | primary: '#3D3D3D',
10 | red: '#FF0000',
11 | gray: '#8f8f8f',
12 | 'light-gray': '#F9F9F9',
13 | },
14 | fontFamily: {
15 | ubuntu: ['Ubuntu', 'sans-serif'],
16 | inter: ['Inter', 'sans-serif'],
17 | },
18 | },
19 | },
20 | presets: [
21 | require('./node_modules/flotiq-components-react/dist/tailwind.preset'), // Flotiq Component theme presets
22 | ],
23 | plugins: [
24 | require('@tailwindcss/forms'),
25 | ],
26 | safelist: require('./node_modules/flotiq-components-react/dist/tailwind.safelist'),
27 | };
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/---bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Bug report"
3 | about: Report reproducible software issues, so we can improve
4 |
5 | ---
6 |
7 | Welcome to the Gatsby Starter Flotiq for shop GitHub repo!
8 |
9 | For questions related to the usage of Gatsby or GraphQL, please check out their docs at https://www.gatsbyjs.org/ and https://graphql.org/
10 |
11 | For support, help, questions, requests and ideas use https://discord.gg/FwXcHnX
12 |
13 | If your issue is with Gatsby.js itself, please report it at the Gatsby repo https://github.com/gatsbyjs/gatsby/issues/new.
14 |
15 | ### Issue Summary
16 |
17 | A summary of the issue and the browser/OS environment in which it occurs.
18 |
19 | ### To Reproduce
20 |
21 | 1. This is the first step
22 | 2. This is the second step, etc.
23 |
24 | Any other info e.g. Why do you consider this to be a bug? What did you expect to happen instead?
25 |
26 | ### Technical details:
27 |
28 | * Gatsby Version:
29 | * Node Version:
30 | * OS:
31 |
--------------------------------------------------------------------------------
/.github/workflows/push.yml:
--------------------------------------------------------------------------------
1 | name: Push workflow
2 | on:
3 | push:
4 | branches:
5 | - master
6 | schedule:
7 | - cron: "0 8 * * 1"
8 | workflow_dispatch:
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | strategy:
13 | matrix:
14 | node-version: [ 20.x ]
15 | steps:
16 | - name: Checkout 🛎️
17 | uses: actions/checkout@v2
18 | with:
19 | persist-credentials: false
20 | - name: Use Node.js ${{ matrix.node-version }}
21 | uses: actions/setup-node@v1
22 | with:
23 | node-version: ${{ matrix.node-version }}
24 | - run: npm install -g gatsby-cli
25 | - run: npm install -g flotiq-cli
26 | - run: flotiq import .flotiq ${{ secrets.GATSBY_FLOTIQ_API_KEY }}
27 | - run: yarn install
28 | - run: gatsby build
29 | env:
30 | GATSBY_FLOTIQ_API_KEY: ${{ secrets.GATSBY_FLOTIQ_API_KEY }}
31 | SNIPCART_API_KEY: 'test'
32 | GA_MEASUREMENT_ID: ''
33 |
--------------------------------------------------------------------------------
/src/layouts/layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Helmet } from 'react-helmet';
3 | import Header from '../components/Header';
4 | import Footer from '../components/Footer';
5 | import Contact from '../sections/Contact';
6 |
7 | const Layout = ({ children, additionalClass = [] }) => (
8 |
9 |
10 |
11 |
12 |
13 | {children}
14 |
26 |
27 |
28 | );
29 |
30 | export default Layout;
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2022 Flotiq team
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Helmet } from 'react-helmet';
3 | import { Button, Header } from 'flotiq-components-react';
4 | import { Link } from 'gatsby';
5 | import Layout from '../layouts/layout';
6 |
7 | const NotFoundPage = () => (
8 |
9 |
10 | Page not found
11 |
12 |
13 |
14 | Page not found, sorry
15 |
16 |
17 | {/* Example usage of button */}
18 |
19 |
25 |
26 |
27 |
28 |
29 | );
30 |
31 | export default NotFoundPage;
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # dotenv environment variable files
55 | .env*
56 |
57 | # gatsby files
58 | .cache/
59 | public
60 |
61 | # Mac files
62 | .DS_Store
63 |
64 | # Yarn
65 | yarn-error.log
66 | .pnp/
67 | .pnp.js
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | .idea/
72 | .vscode/
73 |
--------------------------------------------------------------------------------
/src/sections/CustomersChoice.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Header } from 'flotiq-components-react';
3 | import ProductCard from '../components/ProductCard';
4 |
5 | const CustomersChoice = ({ products, additionalClass, headerText }) => (
6 |
7 |
8 |
13 |
16 | {products.map((product) => (
17 |
26 | ))}
27 |
28 |
29 |
30 | );
31 |
32 | export default CustomersChoice;
33 |
--------------------------------------------------------------------------------
/src/sections/Products.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { navigate } from 'gatsby';
3 | import { Header } from 'flotiq-components-react';
4 | import ProductCard from '../components/ProductCard';
5 |
6 | const Products = ({ products, additionalClass, headerText }) => (
7 |
8 |
9 |
14 |
15 |
19 | {products.map((product) => (
20 |
{ navigate(`/${product.slug}`); }}
23 | name={product.name}
24 | description={product.description}
25 | price={product.price}
26 | productImage={product.productImage[0] && product.productImage[0].localFile}
27 | buttonLabel="Add to cart"
28 | slug={product.slug}
29 | />
30 | ))}
31 |
32 |
33 | );
34 |
35 | export default Products;
36 |
--------------------------------------------------------------------------------
/.github/workflows/cloudflare_worker.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Cloudflare Worker
2 | on:
3 | repository_dispatch:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | deploy:
8 | runs-on: ubuntu-latest
9 | strategy:
10 | matrix:
11 | node-version: [ 20.x ]
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Use Node.js {{ matrix.node-version }}
15 | uses: actions/setup-node@v1
16 | with:
17 | node-version: ${{ matrix.node-version }}
18 | - name: Check if Flotiq API key is set.
19 | env:
20 | GATSBY_FLOTIQ_API_KEY: ${{ secrets.GATSBY_FLOTIQ_API_KEY }}
21 | run: |
22 | if [ -z "$GATSBY_FLOTIQ_API_KEY" ]; then
23 | echo "Flotiq API Key is not set, using a starter one." && echo "GATSBY_FLOTIQ_API_KEY=74df34da6d8fd27cfc04c35f995a57d4" >> $GITHUB_ENV
24 | else
25 | echo "Using user-provided Flotiq API Key. Importing starter data to Flotiq." && echo "GATSBY_FLOTIQ_API_KEY=$GATSBY_FLOTIQ_API_KEY" >> $GITHUB_ENV
26 | npm install -g flotiq-cli
27 | flotiq import .flotiq $GATSBY_FLOTIQ_API_KEY
28 | fi
29 | - run: npm install -g gatsby-cli
30 | - run: yarn install
31 | - run: gatsby build
32 | - name: Build & Deploy Worker
33 | uses: cloudflare/wrangler-action@v3
34 | with:
35 | apiToken: ${{ secrets.CF_API_TOKEN }}
36 | accountId: ${{ secrets.CF_ACCOUNT_ID }}
37 |
--------------------------------------------------------------------------------
/.github/workflows/pull_requests.yml:
--------------------------------------------------------------------------------
1 | name: PR workflow
2 | on: pull_request
3 |
4 | permissions:
5 | pull-requests: write
6 |
7 | jobs:
8 | test-build:
9 | runs-on: ubuntu-latest
10 | strategy:
11 | matrix:
12 | node-version: [ 20.x ]
13 | steps:
14 | - name: Checkout 🛎️
15 | uses: actions/checkout@v2
16 | with:
17 | persist-credentials: false
18 | - name: Use Node.js ${{ matrix.node-version }}
19 | uses: actions/setup-node@v1
20 | with:
21 | node-version: ${{ matrix.node-version }}
22 | - run: npm install -g gatsby-cli
23 | - run: npm install -g flotiq-cli
24 | - run: flotiq import .flotiq ${{ secrets.GATSBY_FLOTIQ_API_KEY }}
25 | - run: yarn install
26 | - run: gatsby build
27 | env:
28 | GATSBY_FLOTIQ_API_KEY: ${{ secrets.GATSBY_FLOTIQ_API_KEY }}
29 | SNIPCART_API_KEY: 'test'
30 | GA_MEASUREMENT_ID: ''
31 | dependabot:
32 | needs: test-build
33 | runs-on: ubuntu-latest
34 | if: github.actor == 'dependabot[bot]'
35 | steps:
36 | - name: Dependabot metadata
37 | id: metadata
38 | uses: dependabot/fetch-metadata@v1
39 | with:
40 | github-token: "${{ secrets.GITHUB_TOKEN }}"
41 | - name: Approve a PR
42 | run: gh pr review --approve "$PR_URL"
43 | env:
44 | PR_URL: ${{github.event.pull_request.html_url}}
45 | GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
46 |
--------------------------------------------------------------------------------
/src/sections/BestSellers.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { navigate } from 'gatsby';
3 | import { Header } from 'flotiq-components-react';
4 | import ProductCard from '../components/ProductCard';
5 |
6 | const BestSellers = ({ products, additionalClass, headerText }) => (
7 |
8 |
9 |
14 |
17 | {products.slice(0, 4).map((product) => (
18 |
{ navigate(`/${product.slug}`); }}
21 | name={product.name}
22 | description={product.description}
23 | price={product.price}
24 | productImage={product.productImage[0] && product.productImage[0].localFile}
25 | buttonLabel="Add to cart"
26 | slug={product.slug}
27 | />
28 | ))}
29 |
30 |
31 |
32 | );
33 |
34 | export default BestSellers;
35 |
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | exports.createPages = async ({ graphql, actions }) => {
4 | const { createPage } = actions;
5 |
6 | const singleProduct = path.resolve('./src/templates/product.js');
7 | const result = await graphql(`
8 | query GetProducts {
9 | allProduct(sort: {flotiqInternal: {createdAt: DESC}}) {
10 | edges {
11 | node {
12 | slug
13 | }
14 | }
15 | }
16 | }
17 | `);
18 |
19 | if (result.errors) {
20 | throw result.errors;
21 | }
22 | const products = result.data.allProduct.edges;
23 |
24 | // Create paginated index
25 | const productsPerPage = 12;
26 | const numPages = Math.ceil(products.length / productsPerPage);
27 |
28 | Array.from({ length: numPages }).forEach((item, i) => {
29 | createPage({
30 | path: i === 0 ? '/' : `/${i + 1}`,
31 | component: path.resolve('./src/templates/index.js'),
32 | context: {
33 | limit: productsPerPage,
34 | skip: i * productsPerPage,
35 | numPages,
36 | currentPage: i + 1,
37 | },
38 | });
39 | });
40 |
41 | // Create events pages.
42 | products.forEach((product, index) => {
43 | const previous = index === products.length - 1 ? null : products[index + 1].node;
44 | const next = index === 0 ? null : products[index - 1].node;
45 |
46 | createPage({
47 | path: product.node.slug,
48 | component: singleProduct,
49 | context: {
50 | slug: product.node.slug,
51 | previous,
52 | next,
53 | },
54 | });
55 | });
56 | };
57 |
--------------------------------------------------------------------------------
/src/components/ProductCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button, Card } from 'flotiq-components-react';
3 | import { Link } from 'gatsby';
4 | import { GatsbyImage, getImage } from 'gatsby-plugin-image';
5 |
6 | const ProductCard = ({ name, price, productImage, buttonLabel, slug }) => (
7 |
12 |
13 |
18 |
19 |
20 |
21 |
24 | {name}
25 |
26 |
27 |
28 |
40 |
41 |
42 | $
43 | {price}
44 |
45 |
46 |
47 |
48 |
49 |
50 | );
51 |
52 | export default ProductCard;
53 |
--------------------------------------------------------------------------------
/src/components/CategoriesChoiceBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const CategoriesChoiceBar = ({ additionalClass, categoryTabs }) => {
4 | function classNames(...classes) {
5 | return classes.filter(Boolean).join(' ');
6 | }
7 |
8 | return (
9 |
12 |
13 |
14 | Select a tab
15 |
16 | {/* Use an "onChange" listener to redirect the user to the selected tab URL. */}
17 | tab.current).name}
22 | >
23 | {categoryTabs.map((tab) => (
24 | {tab.name}
25 | ))}
26 |
27 |
28 |
48 |
49 | );
50 | };
51 |
52 | export default CategoriesChoiceBar;
53 |
--------------------------------------------------------------------------------
/worker-index.js:
--------------------------------------------------------------------------------
1 | import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'
2 |
3 | /**
4 | * The DEBUG flag will do two things that help during development:
5 | * 1. we will skip caching on the edge, which makes it easier to
6 | * debug.
7 | * 2. we will return an error message on exception in your Response rather
8 | * than the default 404.html page.
9 | */
10 | const DEBUG = false
11 |
12 | addEventListener('fetch', event => {
13 | try {
14 | event.respondWith(handleEvent(event))
15 | } catch (e) {
16 | if (DEBUG) {
17 | return event.respondWith(
18 | new Response(e.message || e.toString(), {
19 | status: 500,
20 | }),
21 | )
22 | }
23 | event.respondWith(new Response('Internal Error', { status: 500 }))
24 | }
25 | })
26 |
27 | async function handleEvent(event) {
28 | const url = new URL(event.request.url)
29 | let options = {}
30 |
31 | /**
32 | * You can add custom logic to how we fetch your assets
33 | * by configuring the function `mapRequestToAsset`
34 | */
35 | // options.mapRequestToAsset = handlePrefix(/^\/docs/)
36 |
37 | try {
38 | if (DEBUG) {
39 | // customize caching
40 | options.cacheControl = {
41 | bypassCache: true,
42 | }
43 | }
44 | return await getAssetFromKV(event, options)
45 | } catch (e) {
46 | // if an error is thrown try to serve the asset at 404.html
47 | if (!DEBUG) {
48 | try {
49 | let notFoundResponse = await getAssetFromKV(event, {
50 | mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/404.html`, req),
51 | })
52 |
53 | return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
54 | } catch (e) {}
55 | }
56 |
57 | return new Response(e.message || e.toString(), { status: 500 })
58 | }
59 | }
60 |
61 | /**
62 | * Here's one example of how to modify a request to
63 | * remove a specific prefix, in this case `/docs` from
64 | * the url. This can be useful if you are deploying to a
65 | * route on a zone, or if you only want your static content
66 | * to exist at a specific path.
67 | */
68 | function handlePrefix(prefix) {
69 | return request => {
70 | // compute the default (e.g. / -> index.html)
71 | let defaultAssetKey = mapRequestToAsset(request)
72 | let url = new URL(defaultAssetKey.url)
73 |
74 | // strip the prefix from the path for lookup
75 | url.pathname = url.pathname.replace(prefix, '/')
76 |
77 | // inherit all other props from the default request
78 | return new Request(url.toString(), defaultAssetKey)
79 | }
80 | }
--------------------------------------------------------------------------------
/src/templates/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Helmet } from 'react-helmet';
3 | import { graphql } from 'gatsby';
4 | import Layout from '../layouts/layout';
5 | // import CategoriesChoiceBar from '../components/CategoriesChoiceBar';
6 | import BestSellers from '../sections/BestSellers';
7 | import Products from '../sections/Products';
8 |
9 | const IndexPage = ({ data }) => {
10 | const products = data.allProduct.nodes;
11 | // const categoryTabs = [
12 | // { name: 'New', href: '#', current: true },
13 | // { name: 'Shoes', href: '#', current: false },
14 | // { name: 'Clothing', href: '#', current: false },
15 | // { name: 'Accessories', href: '#', current: false },
16 | // ];
17 |
18 | return (
19 |
20 |
21 | {data.site.siteMetadata.title}
22 |
26 |
27 | {/* */}
31 |
36 |
41 |
42 | );
43 | };
44 |
45 | export const pageQuery = graphql`
46 | query indexQuery($skip: Int!, $limit: Int!) {
47 | site {
48 | siteMetadata {
49 | title
50 | description
51 | }
52 | }
53 | allProduct(
54 | sort: {flotiqInternal: {createdAt: DESC}}
55 | limit: $limit
56 | skip: $skip
57 | ) {
58 | nodes {
59 | name
60 | price
61 | slug
62 | description
63 | id
64 | productGallery {
65 | extension
66 | url
67 | width
68 | height
69 | localFile {
70 | publicURL
71 | childImageSharp {
72 | gatsbyImageData(layout: FULL_WIDTH, placeholder: NONE)
73 | }
74 | }
75 | }
76 | productImage {
77 | extension
78 | url
79 | width
80 | height
81 | localFile {
82 | publicURL
83 | childImageSharp {
84 | gatsbyImageData(layout: FULL_WIDTH, placeholder: NONE)
85 | }
86 | }
87 | }
88 | }
89 | }
90 | }
91 | `;
92 |
93 | export default IndexPage;
94 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true,
5 | es6: true,
6 | },
7 | parser: '@babel/eslint-parser',
8 | parserOptions: {
9 | requireConfigFile: false,
10 | babelOptions: {
11 | presets: ['@babel/preset-react'],
12 | },
13 | },
14 | extends: [
15 | 'react-app',
16 | ],
17 | plugins: [
18 | 'flowtype',
19 | 'import',
20 | ],
21 | rules: {
22 | /* Restrict file extensions that may contain JSX */
23 | 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }],
24 | /* This rule will warn when it encounters a reference to an identifier that has not yet been declared. */
25 | 'no-use-before-define': ['error', { variables: false }],
26 | /* This rule enforces consistent line breaks inside braces of object literals or destructuring assignments. */
27 | 'object-curly-newline': ['error', { ImportDeclaration: 'never' }],
28 | /* Enforce consistent usage of destructuring assignment of props, state, and context */
29 | 'react/destructuring-assignment': 0,
30 | /* Prevent missing props validation in a React component definition */
31 | 'react/prop-types': 0,
32 | /* Enforce require() on the top-level module scope */
33 | 'global-require': 0,
34 | indent: ['error', 4],
35 | /* This rule is aimed to enforce consistent indentation style */
36 | 'react/jsx-indent': ['error', 4],
37 | /* This rule is aimed to enforce consistent indentation style */
38 | 'react/jsx-indent-props': ['error', 4],
39 | /* Enforces that there is no spreading for any JSX attribute.
40 | This enhances readability of code by being more explicit about what props are received by the component. */
41 | 'react/jsx-props-no-spreading': 'off',
42 | 'max-len': ['error', { code: 120 }],
43 | /* Enforces the rule of https://reactjs.org/docs/hooks-rules.html */
44 | 'react-hooks/rules-of-hooks': 'error',
45 | /* This is a new ESLint rule that verifies the list of dependencies for Hooks like useEffect and similar,
46 | protecting against the stale closure pitfalls. For most cases it has an autofix. */
47 | 'react-hooks/exhaustive-deps': 'warn',
48 | /* This rules enforces an explicit type attribute for all the button elements
49 | and checks that its value is valid per spec */
50 | 'react/button-has-type': 0,
51 | 'react/state-in-constructor': 0,
52 | 'react/jsx-fragments': 0,
53 | 'guard-for-in': 0,
54 | 'no-underscore-dangle': 0,
55 | 'jsx-a11y/click-events-have-key-events': 0,
56 | 'jsx-a11y/no-static-element-interactions': 0,
57 | 'react/function-component-definition': [1, {
58 | namedComponents: 'arrow-function',
59 | }],
60 | },
61 | };
62 |
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/contentObjectMedia.json:
--------------------------------------------------------------------------------
1 | {"id":"_media-141dc3a8-ea65-4eeb-9646-b2a6a52cf3d6","url":"/image/0x0/_media-141dc3a8-ea65-4eeb-9646-b2a6a52cf3d6.jpg","size":155971,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-12ee2c2a-6fa1-49ec-93b5-aced44cf6dad.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
2 | {"id":"_media-1500aa65-ab06-46cf-a912-991aa18c5cfc","url":"/image/0x0/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc.jpg","size":21853,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-67dd1778-7bc4-48d1-bfa7-9c8d49fb2140.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
3 | {"id":"_media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3","url":"/image/0x0/_media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3.jpg","size":20705,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-ff5145e0-7344-447a-a8bf-c2ca06161db6.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
4 | {"id":"_media-4a805ccc-c489-4e5f-a4a5-0170576df668","url":"/image/0x0/_media-4a805ccc-c489-4e5f-a4a5-0170576df668.jpg","size":21607,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-eb926fb8-8b28-4b3f-a39f-5d2c8643476d.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
5 | {"id":"_media-67277cc5-d5eb-45da-a2d5-a79008c55698","url":"/image/0x0/_media-67277cc5-d5eb-45da-a2d5-a79008c55698.jpg","size":30968,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-7b39e02e-4332-4f64-a14a-fd62488a86c9.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
6 | {"id":"_media-6df43396-183e-4252-b1c2-c642368a5eb1","url":"/image/0x0/_media-6df43396-183e-4252-b1c2-c642368a5eb1.jpg","size":23807,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-d5b4371b-464b-4f74-9e77-3a8458e99d3b.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
7 | {"id":"_media-c415c7a8-f771-4904-833a-1ec7d6e899d4","url":"/image/0x0/_media-c415c7a8-f771-4904-833a-1ec7d6e899d4.jpg","size":55133,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-20f81845-d5b3-4cb2-915b-a3f4d968d1d0.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
8 | {"id":"_media-cf9becdd-cab4-45e3-a42d-23e90c5fa789","url":"/image/0x0/_media-cf9becdd-cab4-45e3-a42d-23e90c5fa789.jpg","size":155245,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-fb39c428-79b0-4dda-a61d-2b3430f3e3aa.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
9 | {"id":"_media-dd3752a8-ba37-45bd-9e2c-fde485f1c47e","url":"/image/0x0/_media-dd3752a8-ba37-45bd-9e2c-fde485f1c47e.jpg","size":16498,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-c9e32834-4fff-4487-bcca-7e535664a01d.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
10 | {"id":"_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d","url":"/image/0x0/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d.jpg","size":24896,"type":"image","width":701,"height":817,"source":"disk","fileName":"_media-1d34ad29-c95e-446f-87cd-d400752aa92e.jpg","mimeType":"image/jpeg","extension":"jpg","externalId":""}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flotiq-gatsby-shop-1",
3 | "private": true,
4 | "description": "Flotiq Gatsby shop starter",
5 | "version": "1.0.0",
6 | "license": "MIT",
7 | "author": "Flotiq team (https://flotiq.com/)",
8 | "scripts": {
9 | "build": "gatsby build",
10 | "develop": "gatsby develop",
11 | "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
12 | "start": "gatsby develop",
13 | "serve": "gatsby serve",
14 | "clean": "gatsby clean",
15 | "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1",
16 | "lint": "eslint --ext .js,.jsx ."
17 | },
18 | "dependencies": {
19 | "@fontsource/inter": "^5.1.1",
20 | "@fontsource/ubuntu": "^5.1.1",
21 | "@headlessui/react": "^2.2.0",
22 | "@heroicons/react": "1.0.6",
23 | "@popperjs/core": "^2.11.8",
24 | "@tailwindcss/forms": "^0.5.10",
25 | "flotiq-components-react": "^2.0.2",
26 | "gatsby": "^5.14.1",
27 | "gatsby-codemods": "^4.14.0",
28 | "gatsby-link": "^5.14.1",
29 | "gatsby-plugin-gatsby-cloud": "^5.14.0",
30 | "gatsby-plugin-google-gtag": "^5.14.0",
31 | "gatsby-plugin-image": "^3.14.0",
32 | "gatsby-plugin-manifest": "^5.14.0",
33 | "gatsby-plugin-react-helmet": "^6.14.0",
34 | "gatsby-plugin-robots-txt": "^1.8.0",
35 | "gatsby-plugin-sharp": "^5.14.0",
36 | "gatsby-plugin-sitemap": "^6.14.0",
37 | "gatsby-plugin-snipcart-advanced": "^1.0.3",
38 | "gatsby-source-filesystem": "^5.14.0",
39 | "gatsby-source-flotiq": "^5.3.1",
40 | "gatsby-transformer-sharp": "^5.14.0",
41 | "node": "^23.7.0",
42 | "react": "^18.3.1",
43 | "react-dom": "^18.3.1",
44 | "react-helmet": "^6.1.0",
45 | "react-multi-carousel": "^2.8.5",
46 | "typescript": "^5.7.3",
47 | "webpack": "^5.97.1"
48 | },
49 | "devDependencies": {
50 | "@babel/core": "^7.26.7",
51 | "@babel/eslint-parser": "^7.26.5",
52 | "@babel/plugin-syntax-flow": "^7.26.0",
53 | "@babel/plugin-transform-react-jsx": "^7.25.9",
54 | "@babel/preset-react": "^7.26.3",
55 | "@hot-loader/react-dom": "17.0.2",
56 | "@typescript-eslint/parser": "^8.22.0",
57 | "autoprefixer": "^10.4.20",
58 | "babel-eslint": "^10.1.0",
59 | "cross-env": "^7.0.3",
60 | "dotenv": "^16.4.7",
61 | "eslint": "^8.57.1",
62 | "eslint-config-airbnb": "^19.0.4",
63 | "eslint-config-react-app": "^7.0.1",
64 | "eslint-plugin-flowtype": "^8.0.3",
65 | "eslint-plugin-import": "^2.31.0",
66 | "eslint-plugin-jsx": "^0.1.0",
67 | "eslint-plugin-jsx-a11y": "^6.10.2",
68 | "eslint-plugin-react": "^7.37.4",
69 | "eslint-plugin-react-hooks": "^5.1.0",
70 | "gatsby-plugin-postcss": "^6.14.0",
71 | "postcss": "^8.5.1",
72 | "prettier": "^3.4.2",
73 | "tailwindcss": "^3.4.17"
74 | },
75 | "repository": {
76 | "type": "git",
77 | "url": "https://github.com/flotiq/flotiq-gatsby-shop-1"
78 | },
79 | "bugs": {
80 | "url": "https://github.com/flotiq/flotiq-gatsby-shop-1/issues"
81 | },
82 | "resolutions": {
83 | "webpack": "^5.97.1"
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/.flotiq/ContentTypeProduct/ContentTypeDefinition.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "product",
3 | "label": "Product",
4 | "internal": false,
5 | "schemaDefinition": {
6 | "type": "object",
7 | "allOf": [
8 | {
9 | "$ref": "#/components/schemas/AbstractContentTypeSchemaDefinition"
10 | },
11 | {
12 | "type": "object",
13 | "properties": {
14 | "name": {
15 | "type": "string",
16 | "minLength": 1
17 | },
18 | "slug": {
19 | "type": "string",
20 | "pattern": "^[a-zA-Z0-9-_]*$",
21 | "minLength": 1
22 | },
23 | "price": {
24 | "type": "number",
25 | "minLength": 1
26 | },
27 | "description": {
28 | "type": "string"
29 | },
30 | "productImage": {
31 | "type": "array",
32 | "items": {
33 | "$ref": "#/components/schemas/DataSource"
34 | },
35 | "minItems": 0
36 | },
37 | "productGallery": {
38 | "type": "array",
39 | "items": {
40 | "$ref": "#/components/schemas/DataSource"
41 | },
42 | "minItems": 0
43 | }
44 | }
45 | }
46 | ],
47 | "required": [
48 | "name",
49 | "slug",
50 | "price"
51 | ],
52 | "additionalProperties": false
53 | },
54 | "metaDefinition": {
55 | "order": [
56 | "name",
57 | "slug",
58 | "price",
59 | "description",
60 | "productImage",
61 | "productGallery"
62 | ],
63 | "propertiesConfig": {
64 | "name": {
65 | "label": "Name",
66 | "unique": true,
67 | "helpText": "",
68 | "inputType": "text",
69 | "isTitlePart": true
70 | },
71 | "slug": {
72 | "label": "Slug",
73 | "unique": true,
74 | "helpText": "Slug can only have alphanumerical characters, - and _",
75 | "inputType": "text"
76 | },
77 | "price": {
78 | "label": "Price",
79 | "unique": false,
80 | "helpText": "",
81 | "inputType": "number"
82 | },
83 | "description": {
84 | "label": "Description",
85 | "unique": false,
86 | "helpText": "",
87 | "inputType": "richtext"
88 | },
89 | "productImage": {
90 | "label": "Product image",
91 | "unique": false,
92 | "helpText": "",
93 | "inputType": "datasource",
94 | "validation": {
95 | "relationContenttype": "_media"
96 | }
97 | },
98 | "productGallery": {
99 | "label": "Product gallery",
100 | "unique": false,
101 | "helpText": "",
102 | "inputType": "datasource",
103 | "validation": {
104 | "relationMultiple": true,
105 | "relationContenttype": "_media"
106 | }
107 | }
108 | }
109 | },
110 | "featuredImage": []
111 | }
112 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Disclosure } from '@headlessui/react';
3 | import { ShoppingBagIcon, MenuIcon, XIcon } from '@heroicons/react/outline';
4 | import { Link } from 'gatsby';
5 | import Logo from '../assets/shop-logo.svg';
6 |
7 | const Header = () => (
8 |
9 | {({ open }) => (
10 | <>
11 |
12 |
13 |
14 |
15 |
22 |
23 |
24 |
27 |
32 | View notifications
33 |
34 |
35 |
36 |
37 |
38 |
41 | Open main menu
42 | {open ? (
43 |
44 | ) : (
45 |
46 | )}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
Your menu items here
55 |
56 |
57 | >
58 | )}
59 |
60 |
61 | );
62 |
63 | export default Header;
64 |
--------------------------------------------------------------------------------
/src/sections/ReviewsSection.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Header, Paragraph } from 'flotiq-components-react';
3 | import Carousel from 'react-multi-carousel';
4 | import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid';
5 | import { StaticImage } from 'gatsby-plugin-image';
6 |
7 | const responsive = {
8 | desktop: {
9 | breakpoint: { max: 3000, min: 1024 },
10 | items: 3,
11 | },
12 | tablet: {
13 | breakpoint: { max: 1024, min: 768 },
14 | items: 2,
15 | },
16 | mobile: {
17 | breakpoint: { max: 768, min: 0 },
18 | items: 1,
19 | },
20 | };
21 | const CarouselNavigation = ({ goToSlide, ...rest }) => {
22 | const { carouselState: { currentSlide } } = rest;
23 | return (
24 |
25 |
goToSlide(currentSlide - 1)}
28 | >
29 |
30 |
31 |
goToSlide(currentSlide + 1)}
34 | >
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | const ReviewsSection = ({ headerText, reviews, additionalClass }) => (
42 |
45 |
50 |
51 |
}
58 | className="mx-auto mt-8 re"
59 | renderButtonGroupOutside
60 | >
61 | {reviews.map((review) => (
62 |
63 |
66 | {review.review}
67 |
68 |
69 |
70 |
75 |
78 | {review.author}
79 |
80 |
81 |
5/5
82 |
83 |
84 | ))}
85 |
86 |
87 |
88 | );
89 |
90 | export default ReviewsSection;
91 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Configure your Gatsby site with this file.
3 | *
4 | * See: https://www.gatsbyjs.com/docs/gatsby-config/
5 | */
6 |
7 | // eslint-disable-next-line import/no-extraneous-dependencies
8 | require('dotenv').config({
9 | path: `.env.${process.env.NODE_ENV}`,
10 | });
11 |
12 | module.exports = {
13 | siteMetadata: {
14 | title: 'Flotiq gatsby starter boilerplate',
15 | description: 'Flotiq gatsby starter boilerplate',
16 | siteUrl: 'https://flotiqgatsbyshop1.gatsbyjs.io',
17 | },
18 | plugins: [
19 | {
20 | // How to configure? https://www.gatsbyjs.com/plugins/gatsby-plugin-google-gtag/
21 | // Video: https://www.youtube.com/watch?v=Dwi99jtl3Fs
22 | resolve: 'gatsby-plugin-google-gtag',
23 | options: {
24 | trackingIds: [
25 | process.env.GA_MEASUREMENT_ID || 'test', // GA Measurement
26 | ],
27 | gtagConfig: {
28 | optimize_id: 'OPT_CONTAINER_ID',
29 | anonymize_ip: true,
30 | cookie_expires: 0,
31 | },
32 | pluginConfig: {
33 | head: true,
34 | respectDNT: true,
35 | exclude: ['/preview/**', '/do-not-track/me/too/'],
36 | },
37 | },
38 | },
39 | 'gatsby-plugin-react-helmet',
40 | 'gatsby-plugin-sitemap',
41 | {
42 | resolve: 'gatsby-source-flotiq',
43 | options: { // all options are described here: https://github.com/flotiq/gatsby-source-flotiq#parameters
44 | authToken: process.env.GATSBY_FLOTIQ_API_KEY,
45 | downloadMediaFile: true,
46 | forceReload: false,
47 | includeTypes: [
48 | '_media',
49 | '_tag',
50 | 'product',
51 | ],
52 | },
53 | },
54 | {
55 | resolve: 'gatsby-source-filesystem',
56 | options: {
57 | name: 'placeholder',
58 | path: `${__dirname}/gatsby-config.js`,
59 | },
60 | },
61 | 'gatsby-plugin-image',
62 | {
63 | resolve: 'gatsby-plugin-sharp',
64 | options: {
65 | defaults: {
66 | quality: 80,
67 | },
68 | base64Width: 20,
69 | },
70 | },
71 | 'gatsby-transformer-sharp',
72 | {
73 | resolve: 'gatsby-plugin-robots-txt',
74 | options: {
75 | host: 'https://flotiqgatsbyshop1.gatsbyjs.io',
76 | sitemap: 'https://flotiqgatsbyshop1.gatsbyjs.io/sitemap.xml',
77 | policy: [{ userAgent: '*', allow: '/' }],
78 | },
79 | },
80 | {
81 | resolve: 'gatsby-plugin-manifest',
82 | options: {
83 | icon: 'src/assets/favicons/favicon-16x16.png',
84 | icons: [
85 | {
86 | src: 'src/assets/favicons/favicon-32x32.png',
87 | sizes: '32x32',
88 | type: 'image/png',
89 | },
90 | {
91 | src: 'src/assets/favicons/favicon-16x16.png',
92 | sizes: '16x16',
93 | type: 'image/png',
94 | },
95 | ],
96 | },
97 | },
98 | 'gatsby-plugin-postcss',
99 | {
100 | resolve: 'gatsby-plugin-snipcart-advanced',
101 | options: {
102 | publicApiKey: process.env.SNIPCART_API_KEY,
103 | },
104 | },
105 | {
106 | resolve: 'gatsby-plugin-gatsby-cloud',
107 | options: {
108 | headers: {
109 | '/*': [
110 | 'x-frame-options: allow-from https://jamstackthemes.dev/',
111 | ],
112 | },
113 | },
114 | },
115 | ],
116 | };
117 |
--------------------------------------------------------------------------------
/src/assets/Logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.flotiq/InternalContentTypeMedia/ContentTypeDefinition.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "_media",
3 | "label": "Media (internal)",
4 | "internal": true,
5 | "schemaDefinition": {
6 | "type": "object",
7 | "allOf": [
8 | {
9 | "$ref": "#/components/schemas/AbstractContentTypeSchemaDefinition"
10 | },
11 | {
12 | "type": "object",
13 | "properties": {
14 | "alt": {
15 | "type": "string"
16 | },
17 | "url": {
18 | "type": "string",
19 | "minLength": 1
20 | },
21 | "size": {
22 | "type": "number",
23 | "minLength": 1
24 | },
25 | "tags": {
26 | "type": "array",
27 | "items": {
28 | "$ref": "#/components/schemas/DataSource"
29 | },
30 | "minItems": 0
31 | },
32 | "type": {
33 | "type": "string",
34 | "minLength": 1
35 | },
36 | "width": {
37 | "type": "number"
38 | },
39 | "height": {
40 | "type": "number"
41 | },
42 | "source": {
43 | "type": "string",
44 | "minLength": 1
45 | },
46 | "fileName": {
47 | "type": "string",
48 | "minLength": 1
49 | },
50 | "mimeType": {
51 | "type": "string",
52 | "minLength": 1
53 | },
54 | "variants": {
55 | "type": "array",
56 | "items": {
57 | "type": "object",
58 | "required": [
59 | "name"
60 | ],
61 | "properties": {
62 | "name": {
63 | "type": "string",
64 | "pattern": "^[_a-zA-Z0-9]+$",
65 | "minLength": 1
66 | },
67 | "trim": {
68 | "type": "object",
69 | "required": [
70 | "top",
71 | "left"
72 | ],
73 | "properties": {
74 | "top": {
75 | "type": "number",
76 | "maximum": 9999999,
77 | "minimum": 0
78 | },
79 | "left": {
80 | "type": "number",
81 | "maximum": 9999999,
82 | "minimum": 0
83 | },
84 | "right": {
85 | "type": "number",
86 | "maximum": 9999999,
87 | "minimum": 0
88 | },
89 | "width": {
90 | "type": "number",
91 | "maximum": 9999999,
92 | "minimum": 1
93 | },
94 | "bottom": {
95 | "type": "number",
96 | "maximum": 9999999,
97 | "minimum": 0
98 | },
99 | "height": {
100 | "type": "number",
101 | "maximum": 9999999,
102 | "minimum": 1
103 | }
104 | }
105 | }
106 | }
107 | },
108 | "minItems": 0
109 | },
110 | "extension": {
111 | "type": "string",
112 | "minLength": 1
113 | },
114 | "externalId": {
115 | "type": "string"
116 | }
117 | }
118 | }
119 | ],
120 | "required": [
121 | "fileName",
122 | "mimeType",
123 | "size",
124 | "url",
125 | "source",
126 | "extension",
127 | "type"
128 | ],
129 | "additionalProperties": false
130 | },
131 | "metaDefinition": {
132 | "order": [
133 | "fileName",
134 | "mimeType",
135 | "size",
136 | "width",
137 | "height",
138 | "url",
139 | "externalId",
140 | "source",
141 | "extension",
142 | "type",
143 | "tags",
144 | "alt",
145 | "variants"
146 | ],
147 | "propertiesConfig": {
148 | "alt": {
149 | "label": "ALT",
150 | "unique": false,
151 | "inputType": "text"
152 | },
153 | "url": {
154 | "label": "Url",
155 | "unique": false,
156 | "inputType": "text"
157 | },
158 | "size": {
159 | "label": "Size",
160 | "unique": false,
161 | "inputType": "number"
162 | },
163 | "tags": {
164 | "label": "Tags",
165 | "unique": false,
166 | "helpText": "",
167 | "inputType": "datasource",
168 | "validation": {
169 | "relationMultiple": true,
170 | "relationContenttype": "_tag"
171 | }
172 | },
173 | "type": {
174 | "label": "Type",
175 | "unique": false,
176 | "options": [
177 | "image",
178 | "file"
179 | ],
180 | "inputType": "select"
181 | },
182 | "width": {
183 | "label": "Width",
184 | "unique": false,
185 | "inputType": "number"
186 | },
187 | "height": {
188 | "label": "Height",
189 | "unique": false,
190 | "inputType": "number"
191 | },
192 | "source": {
193 | "label": "Source",
194 | "unique": false,
195 | "options": [
196 | "disk",
197 | "unsplash"
198 | ],
199 | "inputType": "select"
200 | },
201 | "fileName": {
202 | "label": "File name",
203 | "unique": false,
204 | "inputType": "text",
205 | "isTitlePart": true
206 | },
207 | "mimeType": {
208 | "label": "MIME type",
209 | "unique": false,
210 | "inputType": "text"
211 | },
212 | "variants": {
213 | "items": {
214 | "order": [
215 | "name",
216 | "trim"
217 | ],
218 | "propertiesConfig": {
219 | "name": {
220 | "label": "name",
221 | "unique": true,
222 | "inputType": "text"
223 | },
224 | "trim": {
225 | "label": "Trim",
226 | "order": [
227 | "top",
228 | "right",
229 | "bottom",
230 | "left",
231 | "width",
232 | "height"
233 | ],
234 | "unique": false,
235 | "inputType": "custom",
236 | "propertiesConfig": {
237 | "top": {
238 | "label": "Top",
239 | "unique": false,
240 | "inputType": "number"
241 | },
242 | "left": {
243 | "label": "Left",
244 | "unique": false,
245 | "inputType": "number"
246 | },
247 | "right": {
248 | "label": "Right",
249 | "unique": false,
250 | "inputType": "number"
251 | },
252 | "width": {
253 | "label": "Width",
254 | "unique": false,
255 | "inputType": "number"
256 | },
257 | "bottom": {
258 | "label": "Bottom",
259 | "unique": false,
260 | "inputType": "number"
261 | },
262 | "height": {
263 | "label": "Height",
264 | "unique": false,
265 | "inputType": "number"
266 | }
267 | }
268 | }
269 | }
270 | },
271 | "label": "Variants",
272 | "unique": false,
273 | "helpText": "",
274 | "inputType": "object"
275 | },
276 | "extension": {
277 | "label": "Extension",
278 | "unique": false,
279 | "inputType": "text"
280 | },
281 | "externalId": {
282 | "label": "External id",
283 | "unique": false,
284 | "inputType": "text"
285 | }
286 | }
287 | },
288 | "featuredImage": []
289 | }
290 |
--------------------------------------------------------------------------------
/.flotiq/ContentTypeProduct/contentObjectProduct.json:
--------------------------------------------------------------------------------
1 | {"id":"product-249950","name":"T-shirt small logo","slug":"tshirt-small-logo","price":35,"description":"Aliquam lectus arcu, accumsan quis libero vel, placerat faucibus orci. Aenean vitae mattis turpis, id egestas arcu. Integer non purus dui.
\n\n\n\tEtiam porttitor massa id velit semper, vitae posuere leo egestas. \n\tNunc congue, quam vestibulum cursus luctus, turpis sem feugiat odio, eu placerat ex tellus vitae elit. \n\tPhasellus sodales purus sed auctor feugiat. Morbi varius pretium ligula id semper. In ac scelerisque erat. \n\tQuisque a metus ut nibh finibus hendrerit. \n \n","productImage":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc"}],"productGallery":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-4a805ccc-c489-4e5f-a4a5-0170576df668"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc"}]}
2 | {"id":"product-396039","name":"Flask bottle","slug":"flask-bottle","price":16,"description":"Aliquam lectus arcu, accumsan quis libero vel, placerat faucibus orci. Aenean vitae mattis turpis, id egestas arcu. Integer non purus dui.
\n\n\n\tEtiam porttitor massa id velit semper, vitae posuere leo egestas. \n\tNunc congue, quam vestibulum cursus luctus, turpis sem feugiat odio, eu placerat ex tellus vitae elit. \n\tPhasellus sodales purus sed auctor feugiat. Morbi varius pretium ligula id semper. In ac scelerisque erat. \n\tQuisque a metus ut nibh finibus hendrerit. \n \n","productImage":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-4a805ccc-c489-4e5f-a4a5-0170576df668"}],"productGallery":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-4a805ccc-c489-4e5f-a4a5-0170576df668"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-67277cc5-d5eb-45da-a2d5-a79008c55698"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc"}]}
3 | {"id":"product-45380","name":"T-shirt large logo","slug":"tshirt-large-logo","price":40,"description":"Aliquam lectus arcu, accumsan quis libero vel, placerat faucibus orci. Aenean vitae mattis turpis, id egestas arcu. Integer non purus dui.
\n\n\n\tEtiam porttitor massa id velit semper, vitae posuere leo egestas. \n\tNunc congue, quam vestibulum cursus luctus, turpis sem feugiat odio, eu placerat ex tellus vitae elit. \n\tPhasellus sodales purus sed auctor feugiat. Morbi varius pretium ligula id semper. In ac scelerisque erat. \n\tQuisque a metus ut nibh finibus hendrerit. \n \n","productImage":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-67277cc5-d5eb-45da-a2d5-a79008c55698"}],"productGallery":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-4a805ccc-c489-4e5f-a4a5-0170576df668"}]}
4 | {"id":"product-801389","name":"Handbag","slug":"handbag","price":40,"description":"Aliquam lectus arcu, accumsan quis libero vel, placerat faucibus orci. Aenean vitae mattis turpis, id egestas arcu. Integer non purus dui.
\n\n\n\tEtiam porttitor massa id velit semper, vitae posuere leo egestas. \n\tNunc congue, quam vestibulum cursus luctus, turpis sem feugiat odio, eu placerat ex tellus vitae elit. \n\tPhasellus sodales purus sed auctor feugiat. Morbi varius pretium ligula id semper. In ac scelerisque erat. \n\tQuisque a metus ut nibh finibus hendrerit. \n \n","productImage":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-6df43396-183e-4252-b1c2-c642368a5eb1"}],"productGallery":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-4a805ccc-c489-4e5f-a4a5-0170576df668"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-67277cc5-d5eb-45da-a2d5-a79008c55698"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3"}]}
5 | {"id":"product-825584","name":"Sweatshirt logo","slug":"sweatshirt-logo","price":70,"description":"Aliquam lectus arcu, accumsan quis libero vel, placerat faucibus orci. Aenean vitae mattis turpis, id egestas arcu. Integer non purus dui.
\n\n\n\tEtiam porttitor massa id velit semper, vitae posuere leo egestas. \n\tNunc congue, quam vestibulum cursus luctus, turpis sem feugiat odio, eu placerat ex tellus vitae elit. \n\tPhasellus sodales purus sed auctor feugiat. Morbi varius pretium ligula id semper. In ac scelerisque erat. \n\tQuisque a metus ut nibh finibus hendrerit. \n \n","productImage":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d"}],"productGallery":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-4a805ccc-c489-4e5f-a4a5-0170576df668"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-67277cc5-d5eb-45da-a2d5-a79008c55698"}]}
6 | {"id":"product-844783","name":"Formal jacket","slug":"formal-jacket","price":48,"description":"Aliquam lectus arcu, accumsan quis libero vel, placerat faucibus orci. Aenean vitae mattis turpis, id egestas arcu. Integer non purus dui.
\n\n\n\tEtiam porttitor massa id velit semper, vitae posuere leo egestas. \n\tNunc congue, quam vestibulum cursus luctus, turpis sem feugiat odio, eu placerat ex tellus vitae elit. \n\tPhasellus sodales purus sed auctor feugiat. Morbi varius pretium ligula id semper. In ac scelerisque erat. \n\tQuisque a metus ut nibh finibus hendrerit. \n \n","productImage":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-dd3752a8-ba37-45bd-9e2c-fde485f1c47e"}],"productGallery":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-dd3752a8-ba37-45bd-9e2c-fde485f1c47e"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-6df43396-183e-4252-b1c2-c642368a5eb1"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc"}]}
7 | {"id":"product-918265","name":"T-shit logo on back","slug":"tshirt-logo-back","price":25,"description":"Aliquam lectus arcu, accumsan quis libero vel, placerat faucibus orci. Aenean vitae mattis turpis, id egestas arcu. Integer non purus dui.
\n\n\n\tEtiam porttitor massa id velit semper, vitae posuere leo egestas. \n\tNunc congue, quam vestibulum cursus luctus, turpis sem feugiat odio, eu placerat ex tellus vitae elit. \n\tPhasellus sodales purus sed auctor feugiat. Morbi varius pretium ligula id semper. In ac scelerisque erat. \n\tQuisque a metus ut nibh finibus hendrerit. \n \n","productImage":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-c415c7a8-f771-4904-833a-1ec7d6e899d4"}],"productGallery":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-67277cc5-d5eb-45da-a2d5-a79008c55698"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-6df43396-183e-4252-b1c2-c642368a5eb1"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-4a805ccc-c489-4e5f-a4a5-0170576df668"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-c415c7a8-f771-4904-833a-1ec7d6e899d4"}]}
8 | {"id":"product-941806","name":"Sweatshirt","slug":"sweatshirt","price":70,"description":"Aliquam lectus arcu, accumsan quis libero vel, placerat faucibus orci. Aenean vitae mattis turpis, id egestas arcu. Integer non purus dui.
\n\n\n\tEtiam porttitor massa id velit semper, vitae posuere leo egestas. \n\tNunc congue, quam vestibulum cursus luctus, turpis sem feugiat odio, eu placerat ex tellus vitae elit. \n\tPhasellus sodales purus sed auctor feugiat. Morbi varius pretium ligula id semper. In ac scelerisque erat. \n\tQuisque a metus ut nibh finibus hendrerit. \n \n","productImage":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3"}],"productGallery":[{"type":"internal","dataUrl":"/api/v1/content/_media/_media-4a805ccc-c489-4e5f-a4a5-0170576df668"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-eb50be1d-9079-4aa3-b90e-7851886b3a2d"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-1500aa65-ab06-46cf-a912-991aa18c5cfc"},{"type":"internal","dataUrl":"/api/v1/content/_media/_media-3b146c3a-816f-4e9c-b001-7ada3f3cbae3"}]}
--------------------------------------------------------------------------------
/src/templates/product.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Helmet } from 'react-helmet';
3 | import { graphql } from 'gatsby';
4 | import { Paragraph, Header, Button } from 'flotiq-components-react';
5 | import { PlusIcon, MinusIcon } from '@heroicons/react/solid';
6 | import { GatsbyImage, getImage } from 'gatsby-plugin-image';
7 | import Layout from '../layouts/layout';
8 | import CustomersChoice from '../sections/CustomersChoice';
9 | import ReviewsSection from '../sections/ReviewsSection';
10 | import Avatar from '../assets/avatar.jpg';
11 |
12 | const reviews = [
13 | {
14 | review: 'The best store in our town! plants are always in good condition.',
15 | author: 'Joe Jonas',
16 | },
17 | {
18 | review: 'I ordered 3 packs of roses last friday and they all came so fast and fresh',
19 | author: 'Kate Smith',
20 | },
21 | {
22 | review: 'The staff was so sweet they helped me decorate my room with plants!',
23 | author: 'John Doe',
24 | },
25 | {
26 | review: 'The best store in our town! plants are always in good condition.',
27 | author: 'Jane Doe',
28 | },
29 | {
30 | review: 'I ordered 3 packs of roses last friday and they all came so fast and fresh',
31 | author: 'John Kovalski',
32 | },
33 | {
34 | review: 'The staff was so sweet they helped me decorate my room with plants!',
35 | author: 'Joe Novak',
36 | },
37 | ];
38 |
39 | const buttonLabel = 'Add to cart';
40 | // const sizes = [
41 | // { name: 'XS', current: false },
42 | // { name: 'S', current: false },
43 | // { name: 'M', current: false },
44 | // { name: 'L', current: true },
45 | // { name: 'XL', current: false },
46 | // { name: 'XXL', current: false },
47 | // ];
48 | // const sizes = [
49 | // { name: 'XS', current: false },
50 | // { name: 'S', current: false },
51 | // { name: 'M', current: false },
52 | // { name: 'L', current: true },
53 | // { name: 'XL', current: false },
54 | // { name: 'XXL', current: false },
55 | // ];
56 |
57 | /**
58 | * Content of example page
59 | */
60 | const ExamplePage = ({ data }) => {
61 | const [quantity, setQuantity] = useState(1);
62 | // Extracting data from GraphQL query, the query is on the bottom of this file
63 | const { product } = data;
64 | const products = data.allProduct.nodes;
65 | return (
66 |
67 | {/* Content of tag */}
68 |
69 | {product.title}
70 |
74 |
75 |
76 |
79 |
86 |
87 |
88 |
93 |
96 | {`$${product.price}`}
97 |
98 |
99 | {product.description}
100 |
101 |
102 | {/*
105 | Size
106 |
107 |
108 | Select a tab
109 |
110 | {/* /! Use an "onChange" listener to redirect the user to the selected tab URL. !/ */}
111 | {/* tab.current).name}
116 | >
117 | {sizes.map((tab) => (
118 | {tab.name}
119 | ))}
120 |
121 |
122 |
*/}
123 |
124 | Quantity
125 |
126 |
setQuantity(quantity > 1 ? quantity - 1 : 1)}
130 | >
131 |
132 |
133 |
137 | {quantity}
138 |
139 |
setQuantity(quantity + 1)}
143 | >
144 |
145 |
146 |
147 |
148 |
161 |
162 |
163 |
164 |
170 |
175 |
176 | );
177 | };
178 |
179 | export const pageQuery = graphql`
180 | query ProductBySlug($slug: String!) {
181 | site {
182 | siteMetadata {
183 | title
184 | }
185 | }
186 | product(slug: {eq: $slug}) {
187 | name
188 | price
189 | slug
190 | description
191 | id
192 | productImage {
193 | extension
194 | url
195 | width
196 | height
197 | localFile {
198 | publicURL
199 | childImageSharp {
200 | gatsbyImageData(layout: FULL_WIDTH, placeholder: NONE)
201 | }
202 | }
203 | }
204 | productGallery {
205 | localFile {
206 | publicURL
207 | }
208 | }
209 | }
210 | allProduct(
211 | sort: {flotiqInternal: {createdAt: DESC}}
212 | limit: 5
213 | filter: {slug: {ne: $slug}}
214 | ) {
215 | nodes {
216 | name
217 | price
218 | slug
219 | description
220 | id
221 | productGallery {
222 | extension
223 | url
224 | width
225 | height
226 | localFile {
227 | publicURL
228 | childImageSharp {
229 | gatsbyImageData(layout: FULL_WIDTH, placeholder: NONE)
230 | }
231 | }
232 | }
233 | productImage {
234 | extension
235 | url
236 | width
237 | height
238 | localFile {
239 | publicURL
240 | childImageSharp {
241 | gatsbyImageData(layout: FULL_WIDTH, placeholder: NONE)
242 | }
243 | }
244 | }
245 | }
246 | }
247 | }
248 | `;
249 |
250 | export default ExamplePage;
251 |
--------------------------------------------------------------------------------
/src/sections/Contact.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button, Header } from 'flotiq-components-react';
3 | import { StaticImage } from 'gatsby-plugin-image';
4 |
5 | const linkedinPath = 'M4.98 3.5c0 1.381-1.11 2.5-2.48 2.5s-2.48-1.119-2.48-2.5c0-1.38 1.11-2.5 2.4'
6 | + '8-2.5s2.48 1.12 2.48 2.5zm.02 4.5h-5v16h5v-16zm7.982 0h-4.968v16h4.969v-8.399c0-4.67 6.029-5.'
7 | + '052 6.029 0v8.399h4.988v-10.131c0-7.88-8.922-7.593-11.018-3.714v-2.155z';
8 |
9 | const youtubeSvgContent = 'M20.6,7.6c-0.2-0.9-0.9-1.7-1.9-1.8c-1.5-0.2-4-0.5-6.7-0.5c-2.8,0-5.3,0.3-'
10 | + '6.7,0.5C4.4,5.9,3.6,6.6,3.4,7.6C3.2,8.6,3,10.1,3,12s0.2,3.4,0.4,4.4c0.2,0.9,0.9,1.7,1.9,1.8c1'
11 | + '.5,0.2,4,0.5,6.7,0.5c2.8,0,5.3-0.3,6.7-0.5c0.9-0.1,1.7-0.9,1.9-1.8c0.2-1,0.4-2.6,0.4-4.4C21,1'
12 | + '0.1,20.8,8.6,20.6,7.6z M10.2,15.1V8.9l5.4,3.1L10.2,15.1z';
13 |
14 | const socialIcons = {
15 | social: [
16 | {
17 | name: 'Facebook',
18 | href: 'https://www.facebook.com/flotiq',
19 | icon: (props) => (
20 |
21 |
29 |
30 | ),
31 | },
32 | {
33 | name: 'LinkedIn',
34 | href: 'https://www.linkedin.com/company/flotiq/',
35 | icon: (props) => (
36 |
37 |
38 |
39 |
40 | ),
41 | },
42 | {
43 | name: 'Twitter',
44 | href: 'https://twitter.com/flotiq',
45 | icon: (props) => (
46 |
47 |
53 |
54 | ),
55 | },
56 | {
57 | name: 'YouTube',
58 | href: 'https://flotiq.com',
59 | icon: (props) => (
60 |
61 |
62 |
63 | ),
64 | },
65 | ],
66 | };
67 |
68 | const Contact = ({
69 | additionalClass,
70 | socialIconsHeaderText,
71 | formHeaderText,
72 | nameInputLabel,
73 | emailInputLabel,
74 | messageInputLabel,
75 | buttonLabel,
76 | phoneNumber,
77 | address,
78 | emailAddress,
79 | }) => (
80 |
83 |
86 |
94 |
95 |
96 |
99 | {socialIconsHeaderText}
100 |
101 |
102 | {socialIcons.social.map((item) => (
103 |
108 |
109 |
110 | ))}
111 |
112 |
113 |
131 |
132 |
133 |
140 |
143 |
144 | {socialIcons.social.map((item) => (
145 |
153 | ))}
154 |
155 |
156 |
157 |
158 |
159 |
162 | {formHeaderText}
163 |
164 |
219 |
220 |
221 |
222 | );
223 |
224 | export default Contact;
225 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Gatsby shop starter with Flotiq source
6 | ===========================
7 |
8 | Kick off your project with this event boilerplate. This starter ships with the main Gatsby configuration files you might need to get up and running blazing fast with the blazing fast app generator for React.
9 |
10 | Live Demo: https://flotiq-gatsby-shop-1.netlify.app
11 |
12 | This project use:
13 | * [Flotiq](https://flotiq.com) - Headless CMS for string your data (You can create account [here](https://editor.flotiq.com/register.html))
14 | * [Tailwind](https://tailwindcss.com/) - utility-first CSS framework
15 | * [Flotiq source plugin](https://github.com/flotiq/gatsby-source-flotiq) - connector for fetching data from Flotiq
16 | * [Flotiq components for react](https://flotiq.github.io/flotiq-components-react) - components library designed and written to work perfectly with Flotiq Content Objects and display your data beautifully
17 |
18 | This project works on node >=18.0.0, as require Gatsby 5.
19 |
20 | ## Quick start using Flotiq CLI
21 |
22 | Start the project from template using [Flotiq CLI](https://github.com/flotiq/flotiq-cli)
23 |
24 | ```bash
25 | npm install -g flotiq-cli
26 | flotiq start [projectName] https://github.com/flotiq/flotiq-gatsby-shop-1.git [flotiqApiKey]
27 | ```
28 | * `projectName` - Project path (if you wish to start project in the current directory - use `.`)
29 | * `flotiqApKey` - [Read and write API key](https://flotiq.com/docs/API/#application-api-keys) to your Flotiq account
30 |
31 | This command will:
32 | - create project based on template using the `gatsby new` command
33 | - install npm dependencies
34 | - setup variables in the .env file
35 | - import example data to you Flotiq account using the `flotiq import` command
36 | - start development server using the `gatsby develop` command
37 |
38 | 
39 |
40 | ## Configuration
41 |
42 | Environment variables are stored in the `.env` files.
43 | The `.env.development` file inside the root of the directory should have the following structure:
44 |
45 | ```
46 | GATSBY_FLOTIQ_API_KEY=YOUR FLOTIQ API KEY
47 | SNIPCART_API_KEY=YOUR SNIPCART API KEY
48 | GA_MEASUREMENT_ID=YOUR MEASUREMENT ID
49 | ```
50 | To add `SNIPCART API KEY` to your starter you need to log in to the website: https://snipcart.com/
51 |
52 | After this you need to get into your account options and choose `API KEYS` line or you can just click [in the link.]( https://app.snipcart.com/dashboard/account/credentials)
53 |
54 | To see how snipcart shop is working you need to copy `PUBLIC TEST API KEY` and paste it into your `.env` file instead `YOUR SNIPCART API KEY`.
55 |
56 | You need to remember that this is just for test look, to get fully working snipcart shop for commercial use you need to buy it on the website.
57 | `PUBLIC TEST API KEY` below is just an example, it won't work!
58 |
59 | 
60 |
61 | ## Import data (optional)
62 |
63 | This step is optional and is not necessary if you used flotiq-cli to start the project.
64 |
65 | If you wish to import example data to your account, before running `gatsby develop` run:
66 |
67 | ```sh
68 | flotiq import .flotiq [flotiqApiKey]
69 | ```
70 |
71 | It will add four example objects to your Flotiq account.
72 |
73 | _Note: You need to put your Read and write API key as the `flotiqApiKey` for import to work, You don't need any content types in your account._
74 |
75 | ## Developing
76 |
77 | Navigate into your new site’s directory and start it up.
78 |
79 | ```shell
80 | cd flotiq-gatsby-shop-1/
81 | gatsby develop
82 | ```
83 |
84 | Your site is now running at `http://localhost:8000`!
85 |
86 | _Note: You'll also see a second link: _`http://localhost:8000/___graphql`_. This is a tool you can use to experiment with querying your data. Learn more about using this tool in the [Gatsby Tutorial](https://www.gatsbyjs.com/docs/tutorial/part-4/#use-graphiql-to-explore-the-data-layer-and-write-graphql-queries)._
87 |
88 | Open the `flotiq-gatsby-shop-1` directory in your code editor of choice and edit `src/templates/index.js`.
89 | Save your changes and the browser will update in real time!
90 |
91 | ## Manage your content using Flotiq editor
92 |
93 | You can now easily manage your content using [Flotiq editor](https://editor.flotiq.com).
94 |
95 | As Gatsby generates static pages based on content from headless CMS, you have to rebuild site after the data changes.
96 |
97 | ### Update data in development
98 |
99 | When you update the Content Object in Flotiq you have to rerun `gatsby develop`.
100 | When you update the Content Type Definition in Flotiq, you have to run `gatsby clean` command.
101 |
102 | _Note: To simplify this process you can configure [Gatsby Refreshing Content](https://www.gatsbyjs.com/docs/refreshing-content/) endpoint._
103 |
104 | ### Update data in production
105 |
106 | When you update the data in Flotiq you have to rebuild project using `gatsby build` command.
107 |
108 | If you use hosting services listed below you can simplify the process:
109 | - For Gatsby Cloud use [Flotiq Gatsby plugin](https://flotiq.com/docs/panel/Plugins/Gatsby-cloud-integration/#installing-the-gatsby-plugin)
110 | - For Netlify use [Flotiq Netlify plugin](https://flotiq.com/docs/panel/Plugins/Netlify-integration/#installing-the-netlify-plugin)
111 |
112 | For other services you can configure [Webhook](https://flotiq.com/docs/panel/webhooks/examples/) on data change or manually rebuild site in hosting service.
113 |
114 | ## Deploy
115 |
116 | Deploy this project to [Heroku](https://www.heroku.com/) in 3 minutes:
117 |
118 | [](https://heroku.com/deploy?template=https://github.com/flotiq/flotiq-gatsby-shop-1)
119 |
120 | Or to [Netlify](https://www.netlify.com/):
121 |
122 | [](https://app.netlify.com/start/deploy?repository=https://github.com/flotiq/flotiq-gatsby-shop-1)
123 |
124 | Or to [Cloudflare Workers](https://workers.cloudflare.com/):
125 |
126 | [](https://deploy.workers.cloudflare.com/?url=https://github.com/flotiq/flotiq-gatsby-shop-1)
127 |
128 | ### Note
129 | Cloudflare Workers deployment uses Flotiq starter API key, if you want to deploy a worker with your own Flotiq API key, after deployment go to your forked repository on GitHub and add a GATSBY_FLOTIQ_API secret in repository's settings, then in Actions, select "Deploy Cloudflare Worker" workflow and click "Run workflow" to deploy the worker again.
130 |
131 | More information about GitHub secrets can be found [here](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions).
132 |
133 |
134 | ## What's inside?
135 |
136 | A quick look at the top-level files and directories you'll see in a Gatsby project.
137 |
138 | .
139 | ├── .flotiq
140 | ├── .github
141 | ├── node_modules
142 | ├─┬ src
143 | │ ├── assets
144 | │ ├── components
145 | │ ├── layouts
146 | │ ├── pages
147 | │ ├── sections
148 | │ ├── style
149 | │ └── templates
150 | ├── .eslintignore
151 | ├── .eslintrc.js
152 | ├── .gitignore
153 | ├── .prettierignore
154 | ├── .prettierrc
155 | ├── app.json
156 | ├── gatsby-browser.js
157 | ├── gatsby-config.js
158 | ├── gatsby-node.js
159 | ├── LICENSE
160 | ├── netlify.toml
161 | ├── package.json
162 | ├── README.md
163 | ├── static.json
164 | ├── tailwind.config.js
165 | ├── worker-index.js
166 | ├── wrangler.toml
167 | └── yarn.lock
168 |
169 | 1. **`.flotiq`**: This directory contains content types necessary to starter to work.
170 |
171 | 2. **`.github`**: This directory contains templates of GitHub issues.
172 |
173 | 3. **`/node_modules`**: This directory contains all packages that your project depends on (npm packages) and are automatically installed.
174 |
175 | 4. **`/src`**: This directory will contain all the code related to what you will see on the front-end of your site (what you see in the browser) such as your site header or a page template. `src` is a convention for “source code”.
176 |
177 | 5. **`/src/assets`**: This directory will contain all static assets for the project (images, favicons, custom fonts).
178 |
179 | 6. **`/src/components`**: This directory will contain all small build blocks for your templates and layouts.
180 |
181 | 7. **`/src/layouts`**: This directory will contain all layout templates for your pages.
182 |
183 | 8. **`/src/pages`**: This directory will contain all static pages for the project.
184 |
185 | 9. **`/src/sections`**: This directory will contain all big build blocks for your project.
186 |
187 | 10. **`/src/style`**: This directory will contain global styles for the project.
188 |
189 | 11. **`/src/templates`**: This directory will contain all templates for automatically generated pages.
190 |
191 | 12. **`.eslintignore`**: This file tells eslint which files it should not track / not fix.
192 |
193 | 13. **`.eslintrc.js`**: This is a configuration file for [Eslint](https://eslint.org/). Eslint is a tool to help keep the formatting of your code consistent automatically.
194 |
195 | 14. **`.gitignore`**: This file tells git which files it should not track / not maintain a version history for.
196 |
197 | 15. **`.prettierignore`**: This file tells prettier which files it should not track / not fix.
198 |
199 | 16. **`.prettierrc`**: This is a configuration file for [Prettier](https://prettier.io/). Prettier is a tool to help keep the formatting of your code consistent.
200 |
201 | 17. **`app.json`**: Configuration file for Heroku deploy. You can safely remove this file if you do not plan to deploy on Heroku.
202 |
203 | 18. **`gatsby-browser.js`**: This file is where Gatsby expects to find any usage of the [Gatsby browser APIs](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-browser/) (if any). These allow customization/extension of default Gatsby settings affecting the browser.
204 |
205 | 19. **`gatsby-config.js`**: This is the main configuration file for a Gatsby site. This is where you can specify information about your site (metadata) like the site title and description, which Gatsby plugins you’d like to include, etc. (Check out the [config docs](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-config/) for more detail).
206 |
207 | 20. **`gatsby-node.js`**: This file is where Gatsby expects to find any usage of the [Gatsby Node APIs](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/) (if any). These allow customization/extension of default Gatsby settings affecting pieces of the site build process.
208 |
209 | 21. **`LICENSE`**: This Gatsby starter is licensed under the MIT license.
210 |
211 | 22. **`netlify.toml`**: Configuration file for Netlify deploy. You can safely remove this file if you do not plan to deploy on Netlify.
212 |
213 | 23. **`package.json`**: A manifest file for Node.js projects, which includes things like metadata (the project’s name, author, etc.). This manifest is how npm knows which packages to install for your project.
214 |
215 | 24. **`README.md`**: A text file containing useful reference information about your project.
216 |
217 | 25. **`static.json`**: Configuration file for caching the project.
218 |
219 | 26. **`tailwind.config.js`**: Configuration file for tailwind.
220 |
221 | 27. **`worker-index.js`**: Main file for Cloudflare Workers.
222 |
223 | 28. **`wrangler.toml`**: Configuration file for Cloudflare Workers deployment.
224 |
225 | 29. **`yarn.lock`**: This is an automatically generated file based on the exact versions of your yarn dependencies that were installed for your project. **(You won’t change this file directly).**
226 |
227 | ## Learning Gatsby
228 |
229 | Looking for more guidance? Full documentation for Gatsby lives [on the website](https://www.gatsbyjs.com/). Here are some places to start:
230 |
231 | - **For most developers, we recommend starting with our [in-depth tutorial for creating a site with Gatsby](https://www.gatsbyjs.com/tutorial/).** It starts with zero assumptions about your level of ability and walks through every step of the process.
232 |
233 | - **To dive straight into code samples, head [to the gatsby documentation](https://www.gatsbyjs.com/docs/).** In particular, check out the _Guides_, _API Reference_, and _Advanced Tutorials_ sections in the sidebar.
234 |
235 | ## Learning Flotiq
236 |
237 | Full documentation for Flotiq lives [on this website](https://flotiq.com/docs/).
238 |
239 | Documentation for gatsby starters is [here](https://flotiq.com/docs/Universe/gatsby/).
240 |
241 | ## Collaborating
242 |
243 | If you wish to talk with us about this project, feel free to hop on our [](https://discord.gg/FwXcHnX).
244 |
245 | If you found a bug, please report it in [issues](https://github.com/flotiq/flotiq-gatsby-shop-1/issues).
246 |
--------------------------------------------------------------------------------
/src/assets/shop-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------