├── src
├── components
│ ├── forms
│ │ ├── index.js
│ │ └── ContactForm
│ │ │ ├── validationSchema.js
│ │ │ └── index.js
│ ├── Share
│ │ ├── styles.sass
│ │ └── index.js
│ ├── Content
│ │ └── index.js
│ ├── Disqus
│ │ └── index.js
│ ├── Footer
│ │ └── index.js
│ ├── ProgressiveImageContainer
│ │ └── index.js
│ ├── Testimonials
│ │ └── index.js
│ ├── Offerings
│ │ └── index.js
│ ├── PostCard
│ │ └── index.js
│ ├── ArticleTemplate
│ │ └── index.js
│ ├── Pricing
│ │ └── index.js
│ ├── AboutPageTemplate
│ │ └── index.js
│ ├── ContactPageTemplate
│ │ └── index.js
│ ├── SearchBox
│ │ └── index.js
│ ├── Layout
│ │ └── index.js
│ ├── PricingPageTemplate
│ │ └── index.js
│ ├── NavBar
│ │ └── index.js
│ ├── HomePageTemplate
│ │ └── index.js
│ └── SEO
│ │ └── index.js
├── pages
│ ├── contact
│ │ ├── success
│ │ │ └── index.js
│ │ └── index.md
│ ├── 404.js
│ ├── pricing
│ │ └── index.md
│ ├── blog
│ │ └── lorem-ipsum-dolor-situm.md
│ ├── tags
│ │ └── index.js
│ ├── about
│ │ └── index.md
│ └── index.md
├── cms
│ ├── preview-templates
│ │ ├── AboutPagePreview.js
│ │ ├── ContactPagePreview.js
│ │ ├── ArticlePreview.js
│ │ ├── PricingPagePreview.js
│ │ └── HomePagePreview.js
│ └── cms.js
├── assets
│ └── sass
│ │ └── styles.sass
└── templates
│ ├── contact-page.js
│ ├── about-page.js
│ ├── pricing-page.js
│ ├── home-page.js
│ ├── tags.js
│ ├── blog.js
│ └── article-page.js
├── static
├── _headers
├── favicon.ico
├── img
│ ├── coffee.png
│ ├── tutorials.png
│ ├── coffee-gear.png
│ ├── meeting-space.png
│ └── products-grid3.jpg
├── icons
│ ├── icon-192x192.png
│ └── icon-512x512.png
├── robots.txt
└── admin
│ └── config.yml
├── netlify.toml
├── .eslintrc
├── LICENSE
├── .gitignore
├── config.js
├── package.json
├── README.md
├── gatsby-node.js
└── gatsby-config.js
/src/components/forms/index.js:
--------------------------------------------------------------------------------
1 | export * from './ContactForm'
2 |
--------------------------------------------------------------------------------
/static/_headers:
--------------------------------------------------------------------------------
1 | /sw.js # Gatsby's default service worker file path
2 | Cache-Control: no-cache
3 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v4iv/gatsby-starter-business/HEAD/static/favicon.ico
--------------------------------------------------------------------------------
/static/img/coffee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v4iv/gatsby-starter-business/HEAD/static/img/coffee.png
--------------------------------------------------------------------------------
/static/img/tutorials.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v4iv/gatsby-starter-business/HEAD/static/img/tutorials.png
--------------------------------------------------------------------------------
/static/img/coffee-gear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v4iv/gatsby-starter-business/HEAD/static/img/coffee-gear.png
--------------------------------------------------------------------------------
/static/icons/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v4iv/gatsby-starter-business/HEAD/static/icons/icon-192x192.png
--------------------------------------------------------------------------------
/static/icons/icon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v4iv/gatsby-starter-business/HEAD/static/icons/icon-512x512.png
--------------------------------------------------------------------------------
/static/img/meeting-space.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v4iv/gatsby-starter-business/HEAD/static/img/meeting-space.png
--------------------------------------------------------------------------------
/static/img/products-grid3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v4iv/gatsby-starter-business/HEAD/static/img/products-grid3.jpg
--------------------------------------------------------------------------------
/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: */admin/
3 | Disallow: */tags/
4 |
5 | sitemap: https://gatsby-starter-business.netlify.com/sitemap.xml
6 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = "public"
3 | command = "gatsby build"
4 | [build.environment]
5 | YARN_VERSION = "1.22.4"
6 | YARN_FLAGS = "--no-ignore-optional"
7 | RUBY_VERSION = "2.6.2"
8 | NODE_VERSION = "14.12.0"
9 |
--------------------------------------------------------------------------------
/src/components/Share/styles.sass:
--------------------------------------------------------------------------------
1 | .social-links
2 | display: flex
3 | flex-direction: row
4 | flex-wrap: wrap
5 | justify-content: center
6 | align-content: center
7 | align-items: center
8 | margin: 15px 0
9 |
10 | .social-links > div
11 | margin: 5px 15px
12 |
13 | .share-count
14 | text-align: center
15 |
--------------------------------------------------------------------------------
/src/components/Content/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export const HTMLContent = (props) => {
4 | const { content, className } = props
5 |
6 | return (
7 |
8 | )
9 | }
10 |
11 | const Content = ({ content, className }) => (
12 | {content}
13 | )
14 |
15 | export default Content
16 |
--------------------------------------------------------------------------------
/src/components/forms/ContactForm/validationSchema.js:
--------------------------------------------------------------------------------
1 | import * as Yup from 'yup'
2 |
3 | const validationSchema = Yup.object().shape({
4 | name: Yup.string()
5 | .min(2, 'Too Short!')
6 | .max(50, 'Too Long!')
7 | .required('Name is Required!'),
8 | email: Yup.string()
9 | .email('Enter a Valid Email!')
10 | .required('Email is Required!'),
11 | message: Yup.string()
12 | .required('Message is Required!'),
13 | })
14 |
15 | export default validationSchema
16 |
--------------------------------------------------------------------------------
/src/pages/contact/success/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Layout from '../../../components/Layout'
3 |
4 | const SuccessPage = () => {
5 | return (
6 |
7 |
8 |
9 |
10 | Success
11 |
12 |
13 |
14 |
15 | )
16 | }
17 |
18 | export default SuccessPage
19 |
--------------------------------------------------------------------------------
/src/pages/contact/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: 'contact-page'
3 | title: Contact Us
4 | subtitle: We'd Love To Help You, Feel Free To Drop A Mail
5 | meta_title: Contact Us | Gatsby Starter Business
6 | meta_description: >-
7 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur
8 | ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam
9 | venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis
10 | consectetur purus sit amet fermentum.
11 | ---
12 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": [
4 | "standard",
5 | "standard-trailing-commas",
6 | "standard-react"
7 | ],
8 | "globals": {
9 | "graphql": true,
10 | "__PATH_PREFIX__": true,
11 | "fetch": true,
12 | "sessionStorage": true,
13 | "localStorage": true
14 | },
15 | "rules": {
16 | "react/no-unused-prop-types": "off",
17 | "react/prop-types": "off",
18 | "camelcase": "off",
19 | "react/jsx-handler-names": "off",
20 | "react/jsx-closing-bracket-location": "off",
21 | "react/jsx-closing-tag-location": "off"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/Disqus/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDisqusComments from 'react-disqus-comments'
3 |
4 | const Disqus = (props) => {
5 | const { title, slug, siteUrl, disqusShortname } = props
6 |
7 | if (!disqusShortname) {
8 | return null
9 | }
10 |
11 | const url = siteUrl + slug
12 |
13 | return
14 | console.log('New Comment Available!:\n', comment.text)}
20 | />
21 |
22 | }
23 |
24 | export default Disqus
25 |
--------------------------------------------------------------------------------
/src/components/Footer/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Footer = (props) => {
4 | const { copyright } = props
5 |
6 | return (
7 |
17 | )
18 | }
19 |
20 | export default Footer
21 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/AboutPagePreview.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import AboutPageTemplate from '../../components/AboutPageTemplate'
4 |
5 | const AboutPagePreview = ({ entry, widgetFor }) => (
6 |
12 | )
13 |
14 | AboutPagePreview.propTypes = {
15 | entry: PropTypes.shape({
16 | getIn: PropTypes.func,
17 | }),
18 | widgetFor: PropTypes.func,
19 | }
20 |
21 | export default AboutPagePreview
22 |
--------------------------------------------------------------------------------
/src/components/ProgressiveImageContainer/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import _ from 'lodash'
3 | import Img from 'gatsby-image'
4 |
5 | const ProgressiveImageContainer = ({ image, alt, className }) => (typeof image === 'string')
6 | ?
11 | : (_.get(image, ['childImageSharp', 'fluid']))
12 | ?
17 | :
21 |
22 | export default ProgressiveImageContainer
23 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/ContactPagePreview.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import ContactPageTemplate from '../../components/ContactPageTemplate'
4 |
5 | const ContactPagePreview = ({ entry, getAsset }) => {
6 | return (
7 |
13 | )
14 | }
15 |
16 | ContactPagePreview.propTypes = {
17 | entry: PropTypes.shape({
18 | getIn: PropTypes.func,
19 | }),
20 | getAsset: PropTypes.func,
21 | }
22 |
23 | export default ContactPagePreview
24 |
--------------------------------------------------------------------------------
/src/components/Testimonials/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const Testimonials = (props) => {
5 | const { testimonials } = props
6 |
7 | return (
8 |
9 | {testimonials.map((testimonial, idx) => (
10 |
11 |
12 | {testimonial.quote}
13 |
14 | – {testimonial.author}
15 |
16 |
17 | ))}
18 |
19 | )
20 | }
21 | Testimonials.propTypes = {
22 | testimonials: PropTypes.arrayOf(
23 | PropTypes.shape({
24 | quote: PropTypes.string,
25 | author: PropTypes.string,
26 | }),
27 | ),
28 | }
29 |
30 | export default Testimonials
31 |
--------------------------------------------------------------------------------
/src/assets/sass/styles.sass:
--------------------------------------------------------------------------------
1 | $primary: #00d1b2
2 |
3 | $primary-invert: #ffffff
4 |
5 | html,
6 | body,
7 | #___gatsby,
8 | #gatsby-focus-wrapper,
9 | #layout-wrapper
10 | height: 100%
11 |
12 | #layout-wrapper
13 | width: 100%
14 | display: flex
15 | flex-direction: column
16 |
17 | #content-wrapper
18 | flex: 1
19 |
20 | nav
21 | border-bottom: lightgray solid 0.1vmin
22 |
23 | .content .taglist
24 | list-style: none
25 | margin-bottom: 0
26 | margin-left: 0
27 | margin-right: 1.5rem
28 | margin-top: 1.5rem
29 | display: flex
30 | flex-wrap: wrap
31 | justify-content: left
32 | align-items: center
33 | li
34 | padding: 0 2rem 1rem 0
35 | margin-bottom: 1.5rem
36 | margin-top: 0
37 |
38 | .margin-top-0
39 | margin-top: 0 !important
40 |
41 | @import "~bulma"
42 |
--------------------------------------------------------------------------------
/src/cms/cms.js:
--------------------------------------------------------------------------------
1 | import CMS from 'netlify-cms-app'
2 | import '../assets/sass/styles.sass'
3 | import HomePagePreview from './preview-templates/HomePagePreview'
4 | import AboutPagePreview from './preview-templates/AboutPagePreview'
5 | import ArticlePreview from './preview-templates/ArticlePreview'
6 | import PricingPagePreview from './preview-templates/PricingPagePreview'
7 | import ContactPagePreview from './preview-templates/ContactPagePreview'
8 |
9 | CMS.init({
10 | config: {
11 | backend: {
12 | name: 'git-gateway',
13 | },
14 | },
15 | })
16 | CMS.registerPreviewStyle('/styles.css')
17 | CMS.registerPreviewTemplate('home', HomePagePreview)
18 | CMS.registerPreviewTemplate('about', AboutPagePreview)
19 | CMS.registerPreviewTemplate('pricing', PricingPagePreview)
20 | CMS.registerPreviewTemplate('contact', ContactPagePreview)
21 | CMS.registerPreviewTemplate('blog', ArticlePreview)
22 |
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Layout from '../components/Layout'
3 |
4 | const NotFoundPage = () => (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 404: NOT FOUND
14 |
15 |
16 | You just hit a route that doesn't exist... the
17 | sadness.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | )
27 |
28 | export default NotFoundPage
29 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/ArticlePreview.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import ArticleTemplate from '../../components/ArticleTemplate'
4 |
5 | const ArticlePreview = ({ entry, widgetFor }) => {
6 | return (
7 |
21 | )
22 | }
23 |
24 | ArticlePreview.propTypes = {
25 | entry: PropTypes.shape({
26 | getIn: PropTypes.func,
27 | }),
28 | widgetFor: PropTypes.func,
29 | }
30 |
31 | export default ArticlePreview
32 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/PricingPagePreview.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import PricingPageTemplate from '../../components/PricingPageTemplate'
4 |
5 | const PricingPagePreivew = ({ entry, getAsset }) => {
6 | const entryPricingPlans = entry.getIn(['data', 'pricing', 'plans'])
7 | const pricingPlans = entryPricingPlans ? entryPricingPlans.toJS() : []
8 |
9 | return (
10 |
20 | )
21 | }
22 |
23 | PricingPagePreivew.propTypes = {
24 | entry: PropTypes.shape({
25 | getIn: PropTypes.func,
26 | }),
27 | getAsset: PropTypes.func,
28 | }
29 |
30 | export default PricingPagePreivew
31 |
--------------------------------------------------------------------------------
/src/components/Offerings/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import ProgressiveImageContainer from '../ProgressiveImageContainer'
4 |
5 | const Offerings = (props) => {
6 | const { gridItems } = props
7 | return (
8 |
9 | {gridItems.map((item, idx) => (
10 |
11 |
12 |
13 |
17 |
18 | {item.text}
19 |
20 |
21 | ))}
22 |
23 | )
24 | }
25 | Offerings.propTypes = {
26 | gridItems: PropTypes.arrayOf(
27 | PropTypes.shape({
28 | image: PropTypes.string,
29 | text: PropTypes.string,
30 | }),
31 | ),
32 | }
33 |
34 | export default Offerings
35 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/HomePagePreview.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import HomePageTemplate from '../../components/HomePageTemplate'
4 |
5 | const HomePagePreview = ({ entry, getAsset }) => {
6 | const entryBlurbs = entry.getIn(['data', 'offerings', 'blurbs'])
7 | const blurbs = entryBlurbs ? entryBlurbs.toJS() : []
8 |
9 | const entryTestimonials = entry.getIn(['data', 'testimonials'])
10 | const testimonials = entryTestimonials ? entryTestimonials.toJS() : []
11 |
12 | return (
13 |
22 | )
23 | }
24 |
25 | HomePagePreview.propTypes = {
26 | entry: PropTypes.shape({
27 | getIn: PropTypes.func,
28 | }),
29 | getAsset: PropTypes.func,
30 | }
31 |
32 | export default HomePagePreview
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018-20 Vaibhav Sharma
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/components/PostCard/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'gatsby'
3 |
4 | const PostCard = (props) => {
5 | const { posts } = props
6 |
7 | return (
8 |
9 | {posts
10 | .filter(post => post.node.frontmatter.templateKey === 'article-page')
11 | .map(({ node: post }) => (
12 |
17 |
18 |
19 | {post.frontmatter.title}
20 |
21 | •
22 | {post.frontmatter.date}
23 |
24 |
25 | {post.excerpt}
26 |
27 |
28 |
29 | Keep Reading →
30 |
31 |
32 |
33 | ))}
34 |
35 | )
36 | }
37 |
38 | export default PostCard
39 |
--------------------------------------------------------------------------------
/src/templates/contact-page.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { graphql } from 'gatsby'
4 | import ContactPageTemplate from '../components/ContactPageTemplate'
5 | import Layout from '../components/Layout'
6 |
7 | const ContactPage = (props) => {
8 | const { data: { markdownRemark: { frontmatter: { title, subtitle, meta_title, meta_description } } } } = props
9 |
10 | return (
11 |
12 |
18 |
19 | )
20 | }
21 |
22 | ContactPage.propTypes = {
23 | data: PropTypes.shape({
24 | markdownRemark: PropTypes.shape({
25 | frontmatter: PropTypes.object,
26 | }),
27 | }),
28 | }
29 |
30 | export default ContactPage
31 |
32 | export const contactPageQuery = graphql`
33 | query ContactPage($id: String!) {
34 | markdownRemark(id: { eq: $id }) {
35 | frontmatter {
36 | title
37 | subtitle
38 | meta_title
39 | meta_description
40 | heading
41 | }
42 | }
43 | }
44 | `
45 |
--------------------------------------------------------------------------------
/src/components/ArticleTemplate/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Content from '../Content'
3 | import { kebabCase } from 'lodash'
4 | import { Link } from 'gatsby'
5 | import ProgressiveImageContainer from '../ProgressiveImageContainer'
6 |
7 | const ArticleTemplate = (props) => {
8 | const { content, contentComponent, cover, tags, title } = props
9 | const PostContent = contentComponent || Content
10 |
11 | return (
12 |
13 |
14 | {title}
15 |
16 |
20 |
21 |
22 |
Tags
23 |
24 | {(tags && tags.length)
25 | ? tags.map(tag => (
26 |
27 | {tag}
28 |
29 | ))
30 | : null}
31 |
32 |
33 |
34 |
35 | )
36 | }
37 |
38 | export default ArticleTemplate
39 |
--------------------------------------------------------------------------------
/src/templates/about-page.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Helmet from 'react-helmet'
4 | import { graphql } from 'gatsby'
5 | import { HTMLContent } from '../components/Content'
6 | import AboutPageTemplate from '../components/AboutPageTemplate'
7 | import Layout from '../components/Layout'
8 |
9 | const AboutPage = (props) => {
10 | const { data: { markdownRemark: post } } = props
11 |
12 | return (
13 |
14 |
15 | {post.frontmatter.meta_title}
16 |
17 |
18 |
23 |
24 | )
25 | }
26 |
27 | AboutPage.propTypes = {
28 | data: PropTypes.object.isRequired,
29 | }
30 |
31 | export default AboutPage
32 |
33 | export const aboutPageQuery = graphql`
34 | query AboutPage($id: String!) {
35 | markdownRemark(id: { eq: $id }) {
36 | html
37 | frontmatter {
38 | title
39 | meta_title
40 | meta_description
41 | }
42 | }
43 | }
44 | `
45 |
--------------------------------------------------------------------------------
/.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 variables file
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 | #IDE
72 | .idea/
73 | .vscode/
--------------------------------------------------------------------------------
/src/templates/pricing-page.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { graphql } from 'gatsby'
4 | import PricingPageTemplate from '../components/PricingPageTemplate'
5 | import Layout from '../components/Layout'
6 |
7 | const PricingPage = (props) => {
8 | const { data: { markdownRemark: { frontmatter: { title, meta_title, meta_description, pricing } } } } = props
9 |
10 | return (
11 |
12 |
18 |
19 | )
20 | }
21 |
22 | PricingPage.propTypes = {
23 | data: PropTypes.shape({
24 | markdownRemark: PropTypes.shape({
25 | frontmatter: PropTypes.object,
26 | }),
27 | }),
28 | }
29 |
30 | export default PricingPage
31 |
32 | export const pricingPageQuery = graphql`
33 | query PricingPage($id: String!) {
34 | markdownRemark(id: { eq: $id }) {
35 | frontmatter {
36 | title
37 | meta_title
38 | meta_description
39 | pricing {
40 | heading
41 | description
42 | plans {
43 | description
44 | items
45 | plan
46 | price
47 | }
48 | }
49 | }
50 | }
51 | }
52 | `
53 |
--------------------------------------------------------------------------------
/src/components/Pricing/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const Pricing = (props) => {
5 | const { data } = props
6 |
7 | return (
8 |
9 | {data.map(price => (
10 |
11 |
12 |
13 | {price.plan}
14 |
15 |
16 | ${price.price}
17 |
18 | {price.description}
19 |
20 | {price.items.map(item => (
21 |
22 | {item}
23 |
24 | ))}
25 |
26 |
27 |
28 | ))}
29 |
30 | )
31 | }
32 |
33 | Pricing.propTypes = {
34 | data: PropTypes.arrayOf(
35 | PropTypes.shape({
36 | plan: PropTypes.string,
37 | price: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
38 | description: PropTypes.string,
39 | items: PropTypes.array,
40 | }),
41 | ),
42 | }
43 |
44 | export default Pricing
45 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteTitle: 'Gatsby Starter Business', // Site title.
3 | siteTitleAlt: 'Business', // Alternative site title for SEO.
4 | siteLogo: '/icons/icon-512x512.png', // Logo used for SEO and manifest.
5 | siteUrl: 'https://gatsby-starter-business.netlify.com', // Domain of your website without pathPrefix.
6 | pathPrefix: '', // Prefixes all links. For cases when deployed to example.github.io/gatsby-starter-business/.
7 | siteDescription: 'Leverage Gatsby Business Starter for your Business.', // Website description used for RSS feeds/meta description tag.
8 | siteRss: '/rss.xml',
9 | siteFBAppID: '', // FB Application ID for using app insights
10 | googleTagManagerID: '', // GTM tracking ID.
11 | disqusShortname: 'gatsby-business-starter', // Disqus shortname.
12 | userName: 'Vaibhav Sharma',
13 | userTwitter: 'vaibhaved',
14 | userLocation: 'Delhi NCR, India',
15 | userDescription: '',
16 | copyright: 'Copyright © Gatsby Starter Business 2018-2019. All Rights Reserved.', // Copyright string for the footer of the website and RSS feed.
17 | themeColor: '#00d1b2', // Used for setting manifest and progress theme colors.
18 | backgroundColor: '#ffffff', // Used for setting manifest background color.
19 | cookieConsent: 'This website uses cookies which are used to collect anonymous information to improve your browsing experience and for analytics and metrics.', // @TODO Add GDPR Cookie Consent
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/AboutPageTemplate/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Content from '../Content'
3 | import PropTypes from 'prop-types'
4 |
5 | const AboutPageTemplate = (props) => {
6 | const { title, content, contentComponent } = props
7 | const PageContent = contentComponent || Content
8 |
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | {title}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
37 |
38 | )
39 | }
40 |
41 | AboutPageTemplate.propTypes = {
42 | title: PropTypes.string.isRequired,
43 | content: PropTypes.string,
44 | contentComponent: PropTypes.func,
45 | }
46 |
47 | export default AboutPageTemplate
48 |
--------------------------------------------------------------------------------
/src/components/ContactPageTemplate/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Helmet from 'react-helmet'
3 | import PropTypes from 'prop-types'
4 | import { ContactForm } from '../forms'
5 |
6 | const ContactPageTemplate = (props) => {
7 | const { title, subtitle, meta_title, meta_description } = props
8 |
9 | return (
10 |
11 |
12 | {meta_title}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {title}
23 |
24 |
25 | {subtitle}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
38 |
39 | )
40 | }
41 |
42 | ContactPageTemplate.propTypes = {
43 | title: PropTypes.string,
44 | subtitle: PropTypes.string,
45 | meta_title: PropTypes.string,
46 | meta_description: PropTypes.string,
47 | }
48 |
49 | export default ContactPageTemplate
50 |
--------------------------------------------------------------------------------
/src/components/SearchBox/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { Link } from 'gatsby'
3 | import { Index } from 'elasticlunr'
4 |
5 | const SearchBox = props => {
6 | let index = null
7 |
8 | const { searchIndex } = props
9 |
10 | const [query, setQuery] = useState('')
11 | const [results, setResults] = useState([])
12 | const [active, setActive] = useState(false)
13 |
14 | const search = evt => {
15 | const query = evt.target.value
16 |
17 | index = index || Index.load(searchIndex)
18 |
19 | setQuery(query)
20 | setActive(!!query)
21 | setResults(
22 | index
23 | .search(query, { expand: true }) // Accept partial matches
24 | // Map over each ID and return the full document
25 | .map(({ ref }) => index.documentStore.getDoc(ref)),
26 | )
27 | }
28 |
29 | return (
30 |
31 |
38 |
39 |
40 | {active && results.length
41 | ? results
42 | .filter(page => page.templateKey === 'article-page')
43 | .map(page => (
44 |
45 | {page.title}
46 |
47 | ))
48 | : null}
49 |
50 |
51 | )
52 | }
53 |
54 | export default SearchBox
55 |
--------------------------------------------------------------------------------
/src/pages/pricing/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: pricing-page
3 | title: Pricing
4 | meta_title: Pricing | Gatsby Starter Business
5 | meta_description: >-
6 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur
7 | ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam
8 | venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis
9 | consectetur purus sit amet fermentum.
10 | pricing:
11 | description: >-
12 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec porta justo justo,
13 | non semper odio cursus in. Curabitur ligula tortor, tristique non odio nec, imperdiet
14 | mattis leo. Vivamus aliquam rhoncus tortor vitae convallis. Aliquam non dui nibh. Nam
15 | a velit at enim sagittis pellentesque.
16 | heading: Monthly subscriptions
17 | plans:
18 | - description: Nulla faucibus, leo a condimentum aliquam, libero leo vehicula arcu
19 | items:
20 | - Lorem ipsum dolor sit amet
21 | - consectetur adipiscing elit
22 | - Nunc finibus sem a sem ultrices
23 | plan: Pro
24 | price: '50'
25 | - description: Mauris vitae dolor eu mauris malesuada cursus.
26 | items:
27 | - eget sagittis magna tempor
28 | - Quisque pulvinar lorem molestie
29 | - Proin at sagittis ex
30 | plan: Enterprise
31 | price: '80'
32 | - description: Praesent elit lectus, iaculis vel odio vitae, bibendum auctor lacus.
33 | items:
34 | - Pellentesque luctus neque id mauris accumsan
35 | - nec imperdiet justo eleifend
36 | - Sed eget ornare orci
37 | plan: Custom
38 | price: '??'
39 | ---
40 |
41 |
--------------------------------------------------------------------------------
/src/templates/home-page.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { graphql } from 'gatsby'
4 | import HomePageTemplate from '../components/HomePageTemplate'
5 | import Layout from '../components/Layout'
6 |
7 | const HomePage = (props) => {
8 | const { data: { markdownRemark: { frontmatter: { title, meta_title, meta_description, heading, description, offerings, testimonials } } } } = props
9 |
10 | return (
11 |
12 |
21 |
22 | )
23 | }
24 |
25 | HomePage.propTypes = {
26 | data: PropTypes.shape({
27 | markdownRemark: PropTypes.shape({
28 | frontmatter: PropTypes.object,
29 | }),
30 | }),
31 | }
32 |
33 | export default HomePage
34 |
35 | export const pageQuery = graphql`
36 | query IndexPage($id: String!) {
37 | markdownRemark(id: { eq: $id }) {
38 | frontmatter {
39 | title
40 | meta_title
41 | meta_description
42 | heading
43 | description
44 | offerings {
45 | blurbs {
46 | image {
47 | childImageSharp {
48 | fluid(maxWidth: 500, quality: 72) {
49 | ...GatsbyImageSharpFluid
50 | }
51 | }
52 | publicURL
53 | }
54 | text
55 | }
56 | }
57 | testimonials {
58 | author
59 | quote
60 | }
61 | }
62 | }
63 | }
64 | `
65 |
--------------------------------------------------------------------------------
/src/components/Share/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | FacebookShareButton,
4 | LinkedinShareButton,
5 | TwitterShareButton,
6 | TelegramShareButton,
7 | RedditShareButton,
8 | FacebookShareCount,
9 | RedditShareCount,
10 | FacebookIcon,
11 | TwitterIcon,
12 | TelegramIcon,
13 | LinkedinIcon,
14 | RedditIcon,
15 | } from 'react-share'
16 | import './styles.sass'
17 |
18 | const Share = (props) => {
19 | const { title, slug, excerpt, mobile, siteUrl, pathPrefix } = props
20 | const realPrefix = pathPrefix === '/' ? '' : pathPrefix
21 | const url = siteUrl + realPrefix + slug
22 |
23 | const iconSize = mobile ? 36 : 48
24 | const filter = count => (count > 0 ? count : '')
25 |
26 | return (
27 |
28 |
29 |
30 |
31 | {count => {filter(count)}
}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {count => {filter(count)}
}
41 |
42 |
43 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | )
55 | }
56 |
57 | export default Share
58 |
--------------------------------------------------------------------------------
/src/components/Layout/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Helmet from 'react-helmet'
3 | import '../../assets/sass/styles.sass'
4 | import config from '../../../config'
5 | import NavBar from '../NavBar'
6 | import Footer from '../Footer'
7 |
8 | const Layout = (props) => {
9 | return (
10 | <>
11 |
60 |
61 | <>{props.children}>
62 |
63 | >
64 | )
65 | }
66 |
67 | export default Layout
68 |
--------------------------------------------------------------------------------
/src/pages/blog/lorem-ipsum-dolor-situm.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: article-page
3 | title: Lorem Ipsum Dolor Situm
4 | slug: lorem-ipsum-dolor-situm
5 | date: 2018-03-29T03:55:49.370Z
6 | cover: /img/products-grid3.jpg
7 | meta_title: Lorem Ipsum Dolor Situm
8 | meta_description: >-
9 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur
10 | ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam
11 | venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis
12 | consectetur purus sit amet fermentum.
13 | tags:
14 | - lorem
15 | - ipsum
16 | ---
17 |
18 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.
19 |
20 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.
21 |
22 | ## Most Amazing Blogging Experience.
23 |
24 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.
25 |
26 | > Curabitur blandit tempus porttitor. Nullam quis risus eget urna mollis ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit.
27 |
28 | Etiam porta sem malesuada magna mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.
29 |
30 | Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
31 |
--------------------------------------------------------------------------------
/src/templates/tags.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Helmet from 'react-helmet'
3 | import { Link, graphql } from 'gatsby'
4 | import Layout from '../components/Layout'
5 |
6 | const TagRoute = (props) => {
7 | const { data: { allMarkdownRemark: { edges: posts, totalCount }, site: { siteMetadata: { title } } }, pageContext: { tag } } = props
8 |
9 | const postLinks = posts.map(post => (
10 |
11 |
12 | {post.node.frontmatter.title}
13 |
14 |
15 | ))
16 |
17 | const tagHeader = `${totalCount} post${
18 | totalCount === 1 ? '' : 's'
19 | } tagged with “${tag}”`
20 |
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
31 |
{tagHeader}
32 |
33 |
34 | Browse all tags
35 |
36 |
37 |
38 |
39 |
40 |
41 | )
42 | }
43 |
44 | export default TagRoute
45 |
46 | export const tagPageQuery = graphql`
47 | query TagPage($tag: String) {
48 | site {
49 | siteMetadata {
50 | title
51 | }
52 | }
53 | allMarkdownRemark(
54 | limit: 1000
55 | sort: { fields: [frontmatter___date], order: DESC }
56 | filter: { frontmatter: { tags: { in: [$tag] } } }
57 | ) {
58 | totalCount
59 | edges {
60 | node {
61 | fields {
62 | slug
63 | }
64 | frontmatter {
65 | title
66 | }
67 | }
68 | }
69 | }
70 | }
71 | `
72 |
--------------------------------------------------------------------------------
/src/components/PricingPageTemplate/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Helmet from 'react-helmet'
3 | import Pricing from '../Pricing'
4 | import PropTypes from 'prop-types'
5 |
6 | const PricingPageTemplate = (props) => {
7 | const { title, meta_title, meta_description, pricing } = props
8 |
9 | return (
10 |
11 |
12 | {meta_title}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {title}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {pricing.heading}
38 |
39 |
{pricing.description}
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | )
49 | }
50 | PricingPageTemplate.propTypes = {
51 | title: PropTypes.string,
52 | meta_title: PropTypes.string,
53 | meta_description: PropTypes.string,
54 | pricing: PropTypes.shape({
55 | heading: PropTypes.string,
56 | description: PropTypes.string,
57 | plans: PropTypes.array,
58 | }),
59 | }
60 |
61 | export default PricingPageTemplate
62 |
--------------------------------------------------------------------------------
/src/pages/tags/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { kebabCase } from 'lodash'
3 | import Helmet from 'react-helmet'
4 | import { Link, graphql } from 'gatsby'
5 | import Layout from '../../components/Layout'
6 |
7 | const TagsPage = (props) => {
8 | const { data: { allMarkdownRemark: { group }, site: { siteMetadata: { title } } } } = props
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Tags
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
35 |
36 | {group.map(tag => (
37 |
38 |
39 | {tag.fieldValue} ({tag.totalCount})
40 |
41 |
42 | ))}
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 | export default TagsPage
52 |
53 | export const tagPageQuery = graphql`
54 | query TagsQuery {
55 | site {
56 | siteMetadata {
57 | title
58 | }
59 | }
60 | allMarkdownRemark(limit: 1000) {
61 | group(field: frontmatter___tags) {
62 | fieldValue
63 | totalCount
64 | }
65 | }
66 | }
67 | `
68 |
--------------------------------------------------------------------------------
/src/pages/about/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: about-page
3 | title: About Us
4 | meta_title: About Us | Gatsby Starter Business
5 | meta_description: >-
6 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur
7 | ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam
8 | venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis
9 | consectetur purus sit amet fermentum.
10 | ---
11 | ## Lorem ipsum dolor sit amet, consectetuer
12 |
13 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.
14 | Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus
15 | mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa
16 | quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.
17 |
18 | ##In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo.
19 |
20 | Nullam dictum felis eu pede mollis
21 | pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus.
22 | Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra
23 | quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet.
24 | Etiam ultricies nisi vel augue. Nam eget dui. *Etiam rhoncus*. Maecenas
25 | tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.
26 | Nam quam nunc, blandit vel, luctus pulvinar
27 |
28 | ###Curabitur ullamcorper ultricies nisi.
29 |
30 | Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt.
31 | Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales,
32 | augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut,
33 | mollis sed, nonummy id, metus.
34 | * Cras ultricies mi eu turpis hendrerit fringilla.
35 | * Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
36 | * In ac dui quis mi consectetuer lacinia.
37 |
38 | **Etiam ultricies nisi vel augue!**
39 |
--------------------------------------------------------------------------------
/src/components/NavBar/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { Link, graphql, StaticQuery } from 'gatsby'
3 | import SearchBox from '../SearchBox'
4 |
5 | const NavBar = () => {
6 | const [active, setActive] = useState(false)
7 |
8 | const toggleNavBar = () => {
9 | setActive(!active)
10 | }
11 |
12 | return (
13 | (
22 |
23 |
24 |
25 | Gatsby Starter Business
26 |
27 |
32 |
33 |
34 |
35 |
36 |
37 |
63 |
64 | )}
65 | />
66 | )
67 | }
68 |
69 | export default NavBar
70 |
--------------------------------------------------------------------------------
/src/templates/blog.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'gatsby'
3 | import config from '../../config'
4 | import Helmet from 'react-helmet'
5 | import PostCard from '../components/PostCard'
6 | import Layout from '../components/Layout'
7 |
8 | const PaginationLink = props => {
9 | if (!props.test) {
10 | return (
11 |
12 | {props.text}
13 |
14 | )
15 | } else {
16 | return (
17 |
18 | {props.text}
19 |
20 | )
21 | }
22 | }
23 |
24 | const BlogPage = (props) => {
25 | const { pageContext: { first, group, index, last } } = props
26 | const previousUrl = index - 1 === 1 ? '' : (index - 1).toString()
27 | const nextUrl = (index + 1).toString() + '/'
28 |
29 | const websiteSchemaOrgJSONLD = {
30 | '@context': 'http://schema.org',
31 | '@type': 'WebSite',
32 | url: config.siteUrl,
33 | name: config.siteTitle,
34 | alternateName: config.siteTitleAlt ? config.siteTitleAlt : '',
35 | }
36 |
37 | return (
38 |
39 |
40 | Blog | Gatsby Starter Business
41 | {/* Schema.org tags */}
42 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | Blog
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
70 |
71 | )
72 | }
73 |
74 | export default BlogPage
75 |
--------------------------------------------------------------------------------
/src/components/HomePageTemplate/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Helmet from 'react-helmet'
3 | import Offerings from '../Offerings'
4 | import Testimonials from '../Testimonials'
5 | import PropTypes from 'prop-types'
6 |
7 | const HomePageTemplate = (props) => {
8 | const { title, heading, description, offerings, meta_title, meta_description, testimonials } = props
9 |
10 | return (
11 |
12 |
13 | {meta_title}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | {title}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {heading}
41 |
42 |
{description}
43 |
44 |
45 |
Testimonials
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | )
55 | }
56 | HomePageTemplate.propTypes = {
57 | title: PropTypes.string,
58 | meta_title: PropTypes.string,
59 | meta_description: PropTypes.string,
60 | heading: PropTypes.string,
61 | description: PropTypes.string,
62 | offerings: PropTypes.shape({
63 | blurbs: PropTypes.array,
64 | }),
65 | testimonials: PropTypes.array,
66 |
67 | }
68 |
69 | export default HomePageTemplate
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-starter-business",
3 | "version": "2.9.0",
4 | "description": "Gatsby Business Website Starter",
5 | "main": "index.js",
6 | "scripts": {
7 | "develop": "gatsby develop",
8 | "start": "npm run develop",
9 | "build": "gatsby build",
10 | "serve": "gatsby serve",
11 | "lint": "eslint --ext js,jsx --ignore-path .gitignore .",
12 | "lint:fix": "eslint --ext js,jsx --ignore-path .gitignore . --fix",
13 | "clean": "rm -rf public/ .cache/"
14 | },
15 | "repository": "https://github.com/v4iv/gatsby-starter-business",
16 | "author": "Vaibhav Sharma ",
17 | "license": "MIT",
18 | "private": false,
19 | "remarkConfig": {
20 | "plugins": [
21 | "remark-preset-lint-recommended"
22 | ]
23 | },
24 | "dependencies": {
25 | "@gatsby-contrib/gatsby-plugin-elasticlunr-search": "^2.4.2",
26 | "bulma": "^0.9.1",
27 | "formik": "^2.2.6",
28 | "gatsby": "^2.29.1",
29 | "gatsby-image": "^2.4.19",
30 | "gatsby-paginate": "^1.1.1",
31 | "gatsby-plugin-feed": "^2.10.0",
32 | "gatsby-plugin-google-tagmanager": "^2.8.0",
33 | "gatsby-plugin-manifest": "^2.9.0",
34 | "gatsby-plugin-netlify": "^2.8.0",
35 | "gatsby-plugin-netlify-cms": "^4.7.0",
36 | "gatsby-plugin-nprogress": "^2.7.0",
37 | "gatsby-plugin-offline": "^3.7.0",
38 | "gatsby-plugin-react-helmet": "^3.7.0",
39 | "gatsby-plugin-sass": "^2.8.0",
40 | "gatsby-plugin-sharp": "^2.11.1",
41 | "gatsby-plugin-sitemap": "^2.9.0",
42 | "gatsby-remark-copy-linked-files": "^2.7.0",
43 | "gatsby-remark-images": "^3.8.0",
44 | "gatsby-remark-prismjs": "^3.10.0",
45 | "gatsby-remark-relative-images": "^2.0.2",
46 | "gatsby-remark-smartypants": "^2.3.11",
47 | "gatsby-source-filesystem": "^2.8.0",
48 | "gatsby-transformer-remark": "^2.13.0",
49 | "gatsby-transformer-sharp": "^2.9.0",
50 | "lodash": "^4.17.20",
51 | "lodash-webpack-plugin": "^0.11.5",
52 | "netlify-cms-app": "^2.14.6",
53 | "node-sass": "4.14.1",
54 | "prop-types": "^15.7.2",
55 | "query-string": "^6.13.7",
56 | "react": "^17.0.1",
57 | "react-disqus-comments": "^1.4.0",
58 | "react-dom": "^17.0.1",
59 | "react-helmet": "^6.1.0",
60 | "react-share": "^4.3.1",
61 | "trim-right": "^1.0.1",
62 | "uglifyjs-webpack-plugin": "^2.2.0",
63 | "yup": "^0.27.0"
64 | },
65 | "devDependencies": {
66 | "babel-eslint": "^10.1.0",
67 | "eslint": "^7.9.0",
68 | "eslint-config-standard": "^14.1.1",
69 | "eslint-config-standard-react": "^9.2.0",
70 | "eslint-config-standard-trailing-commas": "^6.1.0",
71 | "eslint-plugin-import": "^2.22.0",
72 | "eslint-plugin-node": "^11.1.0",
73 | "eslint-plugin-promise": "^4.2.1",
74 | "eslint-plugin-react": "^7.21.1",
75 | "eslint-plugin-standard": "^4.0.1",
76 | "remark-cli": "^8.0.1",
77 | "remark-preset-lint-recommended": "^4.0.1"
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/components/forms/ContactForm/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Formik, Field } from 'formik'
3 | import { navigate } from 'gatsby'
4 | import validationSchema from './validationSchema'
5 |
6 | const encode = (data) => {
7 | return Object.keys(data)
8 | .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
9 | .join('&')
10 | }
11 |
12 | const ContactForm = () => {
13 | return (
14 | {
18 | fetch("/?no-cache=1", { //eslint-disable-line
19 | method: 'POST',
20 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
21 | body: encode({
22 | 'form-name': 'contact',
23 | ...values,
24 | }),
25 | })
26 | .then(() => {
27 | navigate('/contact/success')
28 | setSubmitting(false)
29 | })
30 | .catch(error => {
31 | console.log(error)
32 | alert("Error: Please Try Again!"); //eslint-disable-line
33 | setSubmitting(false)
34 | })
35 | }}
36 | render={({
37 | errors,
38 | touched,
39 | isSubmitting,
40 | handleSubmit,
41 | handleReset,
42 | }) => ()}
82 | />
83 | )
84 | }
85 |
86 | export { ContactForm }
87 |
--------------------------------------------------------------------------------
/src/templates/article-page.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { graphql } from 'gatsby'
4 | import config from '../../config'
5 | import { HTMLContent } from '../components/Content'
6 | import ArticleTemplate from '../components/ArticleTemplate'
7 | import SE0 from '../components/SEO'
8 | import Share from '../components/Share'
9 | import Disqus from '../components/Disqus'
10 | import Layout from '../components/Layout'
11 |
12 | const ArticlePage = (props) => {
13 | const { data: { markdownRemark: { html, fields: { slug }, frontmatter: { title, meta_title, meta_description, cover, date, tags } } } } = props
14 |
15 | return (
16 |
17 |
63 |
64 | )
65 | }
66 |
67 | ArticlePage.propTypes = {
68 | data: PropTypes.shape({
69 | markdownRemark: PropTypes.object,
70 | }),
71 | }
72 |
73 | export default ArticlePage
74 |
75 | export const pageQuery = graphql`
76 | query ArticleByID($id: String!) {
77 | markdownRemark(id: { eq: $id }) {
78 | id
79 | html
80 | fields {
81 | slug
82 | }
83 | frontmatter {
84 | date(formatString: "MMMM DD, YYYY")
85 | title
86 | cover {
87 | childImageSharp {
88 | fluid(maxWidth: 1075, quality: 72) {
89 | ...GatsbyImageSharpFluid
90 | }
91 | }
92 | publicURL
93 | }
94 | meta_title
95 | meta_description
96 | tags
97 | }
98 | }
99 | }
100 | `
101 |
--------------------------------------------------------------------------------
/src/components/SEO/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Helmet from 'react-helmet'
3 |
4 | const SE0 = (props) => {
5 | const { title, meta_title, meta_desc, cover, slug, date, siteTitleAlt, userName, siteTitle, siteUrl, siteFBAppID, userTwitter, pathPrefix } = props
6 | const postURL = siteUrl + slug
7 | const realPrefix = pathPrefix === '/' ? '' : pathPrefix
8 | const image = siteUrl + realPrefix + cover
9 |
10 | const breadcrumbSchemaOrgJSONLD = {
11 | '@context': 'http://schema.org',
12 | '@type': 'BreadcrumbList',
13 | itemListElement: [
14 | {
15 | '@type': 'ListItem',
16 | position: 1,
17 | item: {
18 | '@id': siteUrl,
19 | name: 'Home',
20 | image: siteUrl + '/icons/icon-512x512.png',
21 | },
22 | },
23 | {
24 | '@type': 'ListItem',
25 | position: 2,
26 | item: {
27 | '@id': postURL,
28 | name: title,
29 | image,
30 | },
31 | },
32 | ],
33 | }
34 |
35 | const blogPostingSchemaOrgJSONLD = {
36 | '@context': 'http://schema.org',
37 | '@type': 'BlogPosting',
38 | url: postURL,
39 | name: title,
40 | alternateName: siteTitleAlt || '',
41 | headline: title,
42 | mainEntityOfPage: {
43 | '@type': 'WebPage',
44 | '@id': postURL,
45 | },
46 | author: {
47 | '@type': 'Person',
48 | name: userName,
49 | },
50 | image: {
51 | '@type': 'ImageObject',
52 | url: image,
53 | },
54 | datePublished: date,
55 | dateModified: date,
56 | publisher: {
57 | '@type': 'Organization',
58 | name: siteTitle,
59 | logo: {
60 | '@type': 'ImageObject',
61 | url: siteUrl + '/icons/icon-512x512.png',
62 | },
63 | },
64 | description: meta_desc,
65 | }
66 |
67 | return (
68 |
69 | {meta_title}
70 | {/* General tags */}
71 |
72 |
73 | {/* Schema.org tags */}
74 |
77 |
80 | {/* OpenGraph tags */}
81 |
82 |
83 |
84 |
85 |
86 |
90 | {/* Twitter Card tags */}
91 |
92 |
96 |
97 |
98 |
99 |
100 | )
101 | }
102 |
103 | export default SE0
104 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gatsby-starter-business
2 | A [Gatsby v2](https://www.gatsbyjs.org/) and [Netlify CMS](https://www.netlifycms.org) powered generic business website.
3 |
4 | It follows the [JAMstack architecture](https://jamstack.org) by using Git as a single source of truth, and [Netlify](https://www.netlify.com) for continuous deployment, and CDN distribution.
5 |
6 | ## Demo
7 | [Gatsby Starter Business](https://gatsby-starter-business.netlify.com)
8 |
9 | ## Features
10 | * Complete Business Website Suite - Home Page, About Page, Pricing Page, Contact Page and Blog
11 | * Netlify CMS for Content Management
12 | * SEO Friendly (Sitemap, Schemas, Meta Tags, GTM etc)
13 | * Bulma and Sass Support for styling
14 | * Progressive Web App & Offline Support
15 | * Tags and RSS Feed for Blog
16 | * Disqus and Share Support
17 | * Elastic-Lunr Search (NEW)
18 | * Pagination (NEW)
19 | * Contact Form (Netlify Forms)
20 | * Easy Configuration using `config.js` file
21 |
22 | ## Prerequisite
23 | * Node
24 | * Gatsby CLI (globally installed)
25 |
26 | ## Getting Started
27 | Create your own project with Gatsby CLI:
28 | ```shell
29 | gatsby new yourbusinessname https://github.com/v4iv/gatsby-starter-business.git
30 | ```
31 |
32 | ## Available Scripts
33 |
34 | ### Develop
35 | Start a hot-reloading development environment accessible at `localhost:8000`
36 | ```shell
37 | yarn start
38 | ```
39 |
40 | ### Build
41 | Get an optimized production build for your site generating static HTML and per-route JavaScript code bundles.
42 | ```shell
43 | yarn build
44 | ```
45 |
46 | ### Serve
47 | gatsby serve — Gatsby starts a local HTML server for testing your built site.
48 | ```shell
49 | yarn serve
50 | ```
51 |
52 | ### Lint
53 | Lint the code according to eslintrc file, for consistency.
54 | ```shell
55 | yarn lint
56 | ```
57 |
58 | ### Clean
59 | Remove the .cache and public for a scratch compile.
60 | ```shell
61 | yarn clean
62 | ```
63 |
64 | ## Configuration
65 | To personalize and configure this Starter open `config.js` file and replace the default values.
66 |
67 | ```javascript
68 | module.exports = {
69 | siteTitle: 'Gatsby Starter Business', // Site title.
70 | siteTitleAlt: 'Business', // Alternative site title for SEO.
71 | siteLogo: '/icons/icon-512x512.png', // Logo used for SEO and manifest.
72 | siteUrl: 'https://gatsby-starter-business.netlify.com', // Domain of your website without pathPrefix.
73 | // Do not use trailing slash!
74 | pathPrefix: '/', // Prefixes all links. For cases when deployed to example.github.io/gatsby-starter-business/.
75 | siteDescription: 'Leverage Gatsby Business Starter for your Business.', // Website description used for RSS feeds/meta description tag.
76 | siteRss: '/rss.xml',
77 | siteFBAppID: '', // FB Application ID for using app insights
78 | googleTagManagerID: '', // GTM tracking ID.
79 | disqusShortname: 'gatsby-business-starter', // Disqus shortname.
80 | userName: 'Vaibhav Sharma',
81 | userTwitter: 'vaibhaved',
82 | userLocation: 'Delhi NCR, India',
83 | userDescription: '',
84 | copyright: 'Copyright © Gatsby Starter Business 2018. All Rights Reserved.', // Copyright string for the footer of the website and RSS feed.
85 | themeColor: '#00d1b2', // Used for setting manifest and progress theme colors.
86 | backgroundColor: '#ffffff', // Used for setting manifest background color.
87 | }
88 |
89 | ```
90 |
91 | ## Deployment
92 | Clicking the button will ask for authentication via Github, which will create a repo in your github account with this starter. Then, it will build and deploy the site to Netlify.
93 |
94 |
95 |
96 | You can read up on how to set up Identity(Authentication for CMS User) here [How To Set Up Netlify CMS](https://www.netlifycms.org/docs/add-to-your-site/)
97 |
--------------------------------------------------------------------------------
/static/admin/config.yml:
--------------------------------------------------------------------------------
1 | backend:
2 | name: git-gateway
3 | branch: master
4 |
5 | site_url: https://gatsby-business-starter.netlify.app
6 | display_url: https://gatsby-business-starter.netlify.app
7 | logo_url: https://gatsby-business-starter.netlify.app/icons/icon-512x512.png
8 | publish_mode: editorial_workflow
9 | media_folder: static/img
10 | public_folder: /img
11 |
12 | collections:
13 | - name: "pages"
14 | label: "Pages"
15 | files:
16 | - file: "src/pages/index.md"
17 | label: "Home Page"
18 | name: "home"
19 | fields:
20 | - {label: "Template Key", name: "templateKey", widget: "hidden", default: "home-page"}
21 | - {label: Title, name: title, widget: string}
22 | - {label: Heading, name: heading, widget: string}
23 | - {label: Description, name: description, widget: string}
24 | - {label: Offerings, name: offerings, widget: object, fields: [{label: Blurbs, name: blurbs, widget: list, fields: [{label: Image, name: image, widget: image}, {label: Text, name: text, widget: text}]}]}
25 | - {label: Testimonials, name: testimonials, widget: list, fields: [{label: Quote, name: quote, widget: string}, {label: Author, name: author, widget: string}]}
26 | - {label: "Meta Title", name: "meta_title", widget: "string"}
27 | - {label: "Meta Description", name: "meta_description", widget: "text"}
28 | - file: "src/pages/about/index.md"
29 | label: "About"
30 | name: "about"
31 | fields:
32 | - {label: "Template Key", name: "templateKey", widget: "hidden", default: "about-page"}
33 | - {label: "Title", name: "title", widget: "string"}
34 | - {label: "Body", name: "body", widget: "markdown"}
35 | - {label: "Meta Title", name: "meta_title", widget: "string"}
36 | - {label: "Meta Description", name: "meta_description", widget: "text"}
37 | - file: "src/pages/pricing/index.md"
38 | label: "Pricing Page"
39 | name: "pricing"
40 | fields:
41 | - {label: "Template Key", name: "templateKey", widget: "hidden", default: "pricing-page"}
42 | - {label: Title, name: title, widget: string}
43 | - {label: Image, name: image, widget: image}
44 | - {label: Pricing, name: pricing, widget: object, fields: [{label: Heading, name: heading, widget: string}, {label: Description, name: description, widget: string}, {label: Plans, name: plans, widget: list, fields: [{label: Plan, name: plan, widget: string}, {label: Price, name: price, widget: string}, {label: Description, name: description, widget: string}, {label: Items, name: items, widget: list}]}]}
45 | - {label: "Meta Title", name: "meta_title", widget: "string"}
46 | - {label: "Meta Description", name: "meta_description", widget: "text"}
47 | - file: "src/pages/contact/index.md"
48 | label: "Contact Page"
49 | name: "contact"
50 | fields:
51 | - {label: "Template Key", name: "templateKey", widget: "hidden", default: "contact-page"}
52 | - {label: Title, name: title, widget: string}
53 | - {label: Subtitle, name: subtitle, widget: string}
54 | - {label: "Meta Title", name: "meta_title", widget: "string"}
55 | - {label: "Meta Description", name: "meta_description", widget: "text"}
56 | - name: "blog"
57 | label: "Blog"
58 | folder: "src/pages/blog"
59 | create: true
60 | slug: "{{slug}}"
61 | fields:
62 | - {label: "Template Key", name: "templateKey", widget: "hidden", default: "article-page"}
63 | - {label: "Title", name: "title", widget: "string"}
64 | - {label: "Slug", name: "slug", widget: "string"}
65 | - {label: "Publish Date", name: "date", widget: "datetime"}
66 | - {label: "Cover", name: "cover", widget: "image"}
67 | - {label: "Body", name: "body", widget: "markdown"}
68 | - {label: "Tags", name: "tags", widget: "list"}
69 | - {label: "Meta Title", name: "meta_title", widget: "string"}
70 | - {label: "Meta Description", name: "meta_description", widget: "text"}
71 |
--------------------------------------------------------------------------------
/src/pages/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: 'home-page'
3 | title: Gatsby Starter Business
4 | meta_title: Home | Gatsby Starter Business
5 | meta_description: >-
6 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur
7 | ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam
8 | venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis
9 | consectetur purus sit amet fermentum.
10 | heading: Lorem ipsum dolor sit amet
11 | description: >-
12 | Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur,
13 | adipisci velit...
14 | offerings:
15 | blurbs:
16 | - image: /img/coffee.png
17 | text: >
18 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc finibus
19 | sem a sem ultrices, eget sagittis magna tempor. Quisque pulvinar lorem
20 | molestie sapien ornare cursus. Praesent eget volutpat est. Proin at
21 | sagittis ex. Duis quis dui magna. Nullam urna purus, blandit vitae tincidunt ut,
22 | scelerisque eu sem. Etiam porttitor elit eget mi luctus, vitae blandit enim pretium.
23 | Aenean nec hendrerit leo, a bibendum magna. In hac habitasse platea dictumst.
24 | Suspendisse sapien magna, vestibulum non vehicula id, pellentesque in ante. Nullam
25 | sed auctor tellus. Sed ipsum sem, dapibus nec eros in, feugiat sagittis mi.
26 | Nullam et dui interdum, varius nibh eu, efficitur metus.
27 | - image: /img/coffee-gear.png
28 | text: >
29 | Fusce semper turpis sed tortor consectetur condimentum. Nulla facilisi. Nam
30 | ipsum nulla, dapibus eu mi non, commodo commodo sapien. Pellentesque luctus
31 | neque id mauris accumsan, nec imperdiet justo eleifend. Nulla viverra, ipsum
32 | sit amet interdum pharetra, felis lorem sollicitudin felis, vehicula finibus
33 | enim nunc facilisis sapien. Donec nulla nisi, dictum quis nibh et, euismod
34 | semper eros. Praesent nunc tortor, consequat eu justo ac, dictum viverra enim.
35 | Etiam sed dui dapibus mauris congue facilisis. Nulla convallis, lectus vel
36 | vehicula interdum, turpis nunc aliquet sem, ac iaculis ligula mauris id tortor.
37 | Sed eget ornare orci, quis dignissim nulla. Pellentesque aliquam consectetur congue.
38 | - image: /img/tutorials.png
39 | text: >
40 | Sed in consequat leo, sit amet ullamcorper lacus. Duis lacinia, metus vitae sollicitudin
41 | pharetra, ipsum augue tristique urna, in rhoncus quam tortor eget sem. Maecenas eu
42 | pharetra orci, ut malesuada nisl. Aliquam erat volutpat. Curabitur egestas eros tincidunt,
43 | scelerisque lectus ac, congue turpis. Fusce egestas sit amet elit et fringilla. Aliquam
44 | erat volutpat. Vivamus ultrices venenatis maximus. Donec volutpat vitae quam at fringilla.
45 | Sed luctus lacus vel tempus posuere. Ut suscipit auctor tortor. Phasellus leo dui, elementum
46 | non sollicitudin eget, porta vehicula odio. Sed mollis, metus sit amet porttitor vehicula,
47 | quam augue pretium erat, at commodo nisl tellus non risus.
48 | - image: /img/meeting-space.png
49 | text: >
50 | Vestibulum libero lectus, dignissim eget magna sit amet, malesuada tincidunt mi. Vivamus
51 | sed erat iaculis mauris efficitur vehicula. Aliquam sed urna at tellus ullamcorper
52 | venenatis molestie ut mi. Duis vel libero ac lectus cursus tempus. Nullam in dictum felis.
53 | Nam sed laoreet turpis. Sed pretium urna consequat lorem tincidunt, ac scelerisque nisi
54 | sodales. Cras tristique laoreet tempor. Mauris vitae dolor eu mauris malesuada cursus.
55 | Praesent elit lectus, iaculis vel odio vitae, bibendum auctor lacus. Suspendisse potenti.
56 | In tempor, massa quis euismod convallis, felis elit sodales urna, at aliquet mi elit auctor
57 | risus.
58 | testimonials:
59 | - author: Vaibhav Sharma
60 | quote: >-
61 | Donec scelerisque magna nec condimentum porttitor. Aliquam vel diam sed diam luctus pretium.
62 | Sed quis egestas libero. Vestibulum nec venenatis ligula.
63 | - author: Subarashi San
64 | quote: >-
65 | Fusce porttitor vulputate enim, nec blandit magna gravida et. Etiam et dignissim ligula.
66 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
67 | ---
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash')
2 | const path = require('path')
3 | const createPaginatedPages = require('gatsby-paginate')
4 |
5 | exports.onCreateNode = ({ node, actions, getNode }) => {
6 | const { createNodeField } = actions
7 | let slug
8 |
9 | if (node.internal.type === `MarkdownRemark`) {
10 | const fileNode = getNode(node.parent)
11 | const parsedFilePath = path.parse(fileNode.relativePath)
12 |
13 | if (Object.prototype.hasOwnProperty.call(node, 'frontmatter')) {
14 | if (Object.prototype.hasOwnProperty.call(node.frontmatter, 'slug') && Object.prototype.hasOwnProperty.call(node.frontmatter, 'cover')) {
15 | slug = `/blog/${_.kebabCase(node.frontmatter.slug)}`
16 | } else if (Object.prototype.hasOwnProperty.call(node.frontmatter, 'slug')) {
17 | slug = `/${_.kebabCase(node.frontmatter.slug)}`
18 | } else if (parsedFilePath.name !== 'index' && parsedFilePath.dir !== '') {
19 | slug = `/${parsedFilePath.dir}/${parsedFilePath.name}/`
20 | } else if (parsedFilePath.dir === '') {
21 | slug = `/`
22 | } else {
23 | slug = `/${parsedFilePath.dir}/`
24 | }
25 | }
26 |
27 | createNodeField({
28 | name: `slug`,
29 | node,
30 | value: slug,
31 | })
32 | }
33 | }
34 |
35 | exports.createPages = ({ actions, graphql }) => {
36 | const { createPage } = actions
37 |
38 | return graphql(`
39 | {
40 | allMarkdownRemark(limit: 1000, sort: { order: DESC, fields: [frontmatter___date] }) {
41 | edges {
42 | node {
43 | excerpt(pruneLength: 400)
44 | id
45 | fields {
46 | slug
47 | }
48 | frontmatter {
49 | title
50 | cover {
51 | childImageSharp{
52 | fluid (maxWidth:500, quality:50){
53 | src
54 | srcSet
55 | aspectRatio
56 | sizes
57 | base64
58 | }
59 | }
60 | publicURL
61 | }
62 | tags
63 | templateKey
64 | date(formatString: "MMMM DD, YYYY")
65 | }
66 | }
67 | }
68 | }
69 | }
70 | `).then(result => {
71 | if (result.errors) {
72 | result.errors.forEach(e => console.error(e.toString()))
73 | return Promise.reject(result.errors)
74 | }
75 |
76 | const postsAndPages = result.data.allMarkdownRemark.edges
77 |
78 | // Post pages:
79 | let posts = []
80 | // Iterate through each post/page, putting all found posts (where templateKey = article-page) into `posts`
81 | postsAndPages.forEach(edge => {
82 | if (_.isMatch(edge.node.frontmatter, { templateKey: 'article-page' })) {
83 | posts = posts.concat(edge)
84 | }
85 | })
86 |
87 | createPaginatedPages({
88 | edges: posts,
89 | createPage: createPage,
90 | pageTemplate: 'src/templates/blog.js',
91 | pageLength: 6, // This is optional and defaults to 10 if not used
92 | pathPrefix: 'blog', // This is optional and defaults to an empty string if not used
93 | context: {}, // This is optional and defaults to an empty object if not used
94 | })
95 | postsAndPages.forEach(edge => {
96 | const id = edge.node.id
97 | createPage({
98 | path: edge.node.fields.slug,
99 | tags: edge.node.frontmatter.tags,
100 | component: path.resolve(
101 | `src/templates/${String(edge.node.frontmatter.templateKey)}.js`,
102 | ),
103 | // additional data can be passed via context
104 | context: {
105 | id,
106 | },
107 | })
108 | })
109 |
110 | // Tag pages:
111 | let tags = []
112 | // Iterate through each post, putting all found tags into `tags`
113 | postsAndPages.forEach(edge => {
114 | if (_.get(edge, `node.frontmatter.tags`)) {
115 | tags = tags.concat(edge.node.frontmatter.tags)
116 | }
117 | })
118 | // Eliminate duplicate tags
119 | tags = _.uniq(tags)
120 |
121 | // Make tag pages
122 | tags.forEach(tag => {
123 | const tagPath = `/tags/${_.kebabCase(tag)}/`
124 |
125 | createPage({
126 | path: tagPath,
127 | component: path.resolve(`src/templates/tags.js`),
128 | context: {
129 | tag,
130 | },
131 | })
132 | })
133 | })
134 | }
135 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | const config = require('./config')
2 |
3 | const pathPrefix = config.pathPrefix === '/' ? '' : config.pathPrefix
4 |
5 | module.exports = {
6 | siteMetadata: {
7 | title: config.siteTitle,
8 | siteUrl: config.siteUrl,
9 | rssMetadata: {
10 | site_url: config.siteUrl + pathPrefix,
11 | feed_url: config.siteUrl + pathPrefix + config.siteRss,
12 | title: config.siteTitle,
13 | description: config.siteDescription,
14 | image_url: `${config.siteUrl + pathPrefix}/icons/icon-512x512.png`,
15 | author: config.userName,
16 | copyright: config.copyright,
17 | },
18 | },
19 | plugins: [
20 | {
21 | // keep as first gatsby-source-filesystem plugin for gatsby image support
22 | resolve: 'gatsby-source-filesystem',
23 | options: {
24 | path: `${__dirname}/static/img`,
25 | name: 'uploads',
26 | },
27 | },
28 | {
29 | resolve: 'gatsby-source-filesystem',
30 | options: {
31 | path: `${__dirname}/static/img`,
32 | name: 'images',
33 | },
34 | },
35 | {
36 | resolve: 'gatsby-source-filesystem',
37 | options: {
38 | path: `${__dirname}/src/pages`,
39 | name: 'pages',
40 | },
41 | },
42 | {
43 | resolve: `gatsby-plugin-sitemap`,
44 | options: {
45 | exclude: [`/tags`, `/tags/*`, `/success`],
46 | },
47 | },
48 | `gatsby-transformer-sharp`,
49 | `gatsby-plugin-sharp`,
50 | {
51 | resolve: 'gatsby-transformer-remark',
52 | options: {
53 | plugins: [
54 | {
55 | resolve: 'gatsby-remark-relative-images',
56 | options: {
57 | name: 'uploads',
58 | },
59 | },
60 | {
61 | resolve: 'gatsby-remark-images',
62 | options: {
63 | // It's important to specify the maxWidth (in pixels) of
64 | // the content container as this plugin uses this as the
65 | // base for generating different widths of each image.
66 | maxWidth: 2048,
67 | },
68 | },
69 | `gatsby-remark-copy-linked-files`,
70 | `gatsby-remark-smartypants`,
71 | ],
72 | },
73 | },
74 | `gatsby-plugin-react-helmet`,
75 | {
76 | resolve: `gatsby-plugin-sass`,
77 | options: {
78 | indentedSyntax: true
79 | },
80 | },
81 | {
82 | resolve: `gatsby-plugin-nprogress`,
83 | options: {
84 | color: config.themeColor,
85 | showSpinner: false,
86 | },
87 | },
88 | {
89 | resolve: `gatsby-plugin-google-tagmanager`,
90 | options: {
91 | id: process.env.GTM_ID,
92 | includeInDevelopment: false,
93 | },
94 | },
95 | {
96 | resolve: `gatsby-plugin-manifest`,
97 | options: {
98 | name: config.siteTitle,
99 | short_name: config.siteTitleAlt,
100 | start_url: '/index.html',
101 | background_color: config.backgroundColor,
102 | theme_color: config.themeColor,
103 | display: 'standalone',
104 | icons: [
105 | {
106 | src: `/icons/icon-192x192.png`,
107 | sizes: `192x192`,
108 | type: `image/png`,
109 | },
110 | {
111 | src: `/icons/icon-512x512.png`,
112 | sizes: `512x512`,
113 | type: `image/png`,
114 | },
115 | ],
116 | cache_busting_mode: 'none',
117 | },
118 | },
119 | {
120 | resolve: `gatsby-plugin-offline`,
121 | options: {
122 | precachePages: [`/blog/*`, `/about`, `/pricing`, `/contact`, `/`],
123 | },
124 | },
125 | {
126 | resolve: 'gatsby-plugin-feed',
127 | options: {
128 | setup (ref) {
129 | const ret = ref.query.site.siteMetadata.rssMetadata
130 | ret.allMarkdownRemark = ref.query.allMarkdownRemark
131 | ret.generator = config.siteTitle
132 | return ret
133 | },
134 | query: `
135 | {
136 | site {
137 | siteMetadata {
138 | rssMetadata {
139 | site_url
140 | feed_url
141 | title
142 | description
143 | image_url
144 | author
145 | copyright
146 | }
147 | }
148 | }
149 | }
150 | `,
151 | feeds: [
152 | {
153 | serialize (ctx) {
154 | const rssMetadata = ctx.query.site.siteMetadata.rssMetadata
155 | return ctx.query.allMarkdownRemark.edges
156 | .filter(
157 | edge => edge.node.frontmatter.templateKey === 'article-page',
158 | )
159 | .map(edge => ({
160 | categories: edge.node.frontmatter.tags,
161 | date: edge.node.frontmatter.date,
162 | title: edge.node.frontmatter.title,
163 | image: edge.node.frontmatter.cover,
164 | description: edge.node.excerpt,
165 | author: rssMetadata.author,
166 | url: rssMetadata.site_url + edge.node.fields.slug,
167 | guid: rssMetadata.site_url + edge.node.fields.slug,
168 | custom_elements: [{ 'content:encoded': edge.node.html }],
169 | }))
170 | },
171 | query: `
172 | {
173 | allMarkdownRemark(
174 | limit: 1000,
175 | sort: { order: DESC, fields: [frontmatter___date] },
176 | ) {
177 | edges {
178 | node {
179 | excerpt(pruneLength: 400)
180 | html
181 | id
182 | fields { slug }
183 | frontmatter {
184 | title
185 | templateKey
186 | cover {
187 | publicURL
188 | }
189 | date(formatString: "MMMM DD, YYYY")
190 | tags
191 | }
192 | }
193 | }
194 | }
195 | }
196 | `,
197 | output: config.siteRss,
198 | title: config.siteTitle,
199 | },
200 | ],
201 | },
202 | },
203 | {
204 | resolve: `@gatsby-contrib/gatsby-plugin-elasticlunr-search`,
205 | options: {
206 | // Fields to index
207 | fields: [`title`, `tags`, `author`, `slug`],
208 | // How to resolve each field`s value for a supported node type
209 | resolvers: {
210 | // For any node of type MarkdownRemark, list how to resolve the fields` values
211 | MarkdownRemark: {
212 | title: node => node.frontmatter.title,
213 | author: node => node.frontmatter.author,
214 | tags: node => node.frontmatter.tags,
215 | slug: node => node.fields.slug,
216 | templateKey: node => node.frontmatter.templateKey,
217 | },
218 | },
219 | },
220 | },
221 | {
222 | resolve: 'gatsby-plugin-netlify-cms',
223 | options: {
224 | modulePath: `${__dirname}/src/cms/cms.js`,
225 | stylesPath: `${__dirname}/src/assets/sass/styles.sass`,
226 | enableIdentityWidget: true,
227 | htmlTitle: `Gatsby Starter Business Content Manager`,
228 | },
229 | },
230 | {
231 | resolve: `gatsby-plugin-netlify`,
232 | options: {
233 | mergeSecurityHeaders: false,
234 | headers: {
235 | '/*.js': [
236 | 'cache-control: public, max-age=31536000, immutable',
237 | ],
238 | '/*.css': [
239 | 'cache-control: public, max-age=31536000, immutable',
240 | ],
241 | '/sw.js': [
242 | 'cache-control: public, max-age=0, must-revalidate',
243 | ],
244 | '/*': [
245 | `X-Frame-Options: DENY`,
246 | `X-XSS-Protection: 1; mode=block`,
247 | `X-Content-Type-Options: nosniff`,
248 | `Referrer-Policy: no-referrer-when-downgrade`,
249 | ],
250 | },
251 | },
252 | },
253 | ],
254 | }
255 |
--------------------------------------------------------------------------------