├── .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 |
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 |
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 |
11 | {headerText} 12 |
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 |
12 | {headerText} 13 |
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 |
12 | {headerText} 13 |
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 |
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 | 16 | {/* Use an "onChange" listener to redirect the user to the selected tab URL. */} 17 | 27 |
28 |
29 | 47 |
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 | Workflow 22 |
23 | 24 |
27 | 36 |
37 |
38 | 41 | Open main menu 42 | {open ? ( 43 | 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 |
48 | {headerText} 49 |
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","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","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","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","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","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","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","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","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 |
91 | {product.name} 92 |
93 |
96 | {`$${product.price}`} 97 |
98 | 99 | {product.description} 100 | 101 |
102 | {/*
105 | Size 106 |
107 | 110 | {/* /! Use an "onChange" listener to redirect the user to the selected tab URL. !/ */} 111 | {/* 121 |
122 |
*/} 123 |
124 | Quantity 125 |
126 | 133 | 139 | 146 |
147 |
148 |
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 | 110 | ))} 111 |
112 |
113 |
114 |

115 | Phone: 116 | 117 | {phoneNumber} 118 | 119 |

120 |

121 | Address: 122 | {address} 123 |

124 |

125 | Email: 126 | 127 | {emailAddress} 128 | 129 |

130 |
131 |
132 |
133 | 140 |
141 | Find us 142 |
143 |
144 | {socialIcons.social.map((item) => ( 145 |
146 | 150 | 152 |
153 | ))} 154 |
155 |
156 |
157 |
158 |
159 |
162 | {formHeaderText} 163 |
164 |
169 |
170 |
171 | 174 | 183 |
184 |
185 | 188 | 197 |
198 |
199 | 202 |