├── .gitignore
├── .nvmrc
├── LICENSE
├── README.md
├── gatsby-config.js
├── gatsby-node.js
├── netlify.toml
├── package.json
├── renovate.json
├── src
├── cms
│ ├── cms.js
│ └── preview-templates
│ │ ├── AboutPagePreview.js
│ │ ├── FooterPreview.js
│ │ ├── HomePagePreview.js
│ │ ├── MeetupPreview.js
│ │ ├── NavbarPreview.js
│ │ └── PastMeetupsPagePreview.js
├── components
│ ├── Content.js
│ ├── CustomLink.js
│ ├── Footer
│ │ ├── Footer.js
│ │ ├── index.js
│ │ └── styles.scss
│ ├── Layout.js
│ ├── Map.js
│ └── Navbar
│ │ ├── Navbar.js
│ │ ├── index.js
│ │ └── styles.scss
├── img
│ ├── favicon.png
│ ├── headshot-placeholder.svg
│ └── location.svg
├── pages
│ ├── 404.js
│ ├── about
│ │ └── index.md
│ ├── footer
│ │ └── index.md
│ ├── home
│ │ └── index.md
│ ├── index.js
│ ├── meetups
│ │ ├── august-2018.md
│ │ ├── july-2018.md
│ │ ├── november-2018-meetup.md
│ │ ├── october-2018.md
│ │ └── september-2018.md
│ ├── navbar
│ │ └── index.md
│ └── pastMeetups
│ │ └── index.md
├── styles
│ ├── 404.scss
│ ├── about-page.scss
│ ├── generic.scss
│ ├── home.scss
│ ├── index.js
│ ├── meetup.scss
│ ├── mixins.scss
│ ├── past-meetups-page.scss
│ ├── reset.scss
│ └── variables.scss
└── templates
│ ├── about-page.js
│ ├── meetup.js
│ └── past-meetups-page.js
├── static
├── admin
│ └── config.yml
└── img
│ ├── annie-spratt-608001-unsplash.jpg
│ ├── benjamin-parker-736167-unsplash.jpg
│ ├── edward-cisneros-415601-unsplash.jpg
│ ├── email.svg
│ ├── facebook.svg
│ ├── humphrey-muleba-795250-unsplash.jpg
│ ├── jakob-dalbjorn-730178-unsplash.jpg
│ ├── jonas-kakaroto-577554-unsplash.jpg
│ ├── js-wakanda.png
│ ├── logo.svg
│ ├── lucas-sankey-378674-unsplash.jpg
│ ├── marius-ciocirlan-398931-unsplash.jpg
│ ├── meetup.svg
│ ├── michael-dam-258165-unsplash.jpg
│ ├── neonbrand-509131-unsplash.jpg
│ ├── organizer-1.jpg
│ ├── organizer-2.jpg
│ ├── ramy-kabalan-796973-unsplash.jpg
│ ├── teemu-paananen-376238-unsplash.jpg
│ ├── television.png
│ └── twitter.svg
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project dependencies
2 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
3 | node_modules
4 | .cache/
5 | # Build directory
6 | public/
7 | static/admin/*.bundle.*
8 | .DS_Store
9 | yarn-error.log
10 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v9.10.0
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 gatsbyjs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gatsby & Netlify CMS Example
2 |
3 | An example website built using Gatsby V2 and Netlify CMS. The website is a fake JavaScript meetup site that lists upcoming meetups, information about the meetup group, as well as a list of past meetups.
4 |
5 | The purpose of the repository is to provide an idea of how a Gatsby project is structured with Netlify CMS. You can easily deploy your own instance of this application by clicking the button below:
6 |
7 | [](https://app.netlify.com/start/deploy?repository=https://github.com/robertcoopercode/gatsby-netlify-cms)
8 |
9 | ## Local Development
10 |
11 | ### Prerequisites
12 |
13 | - Node (see [.nvmrc](./.nvmrc) for version)
14 |
15 | ### Run the project
16 |
17 | ```
18 | $ git clone git@github.com:robertcoopercode/gatsby-netlify-cms.git
19 | $ cd gatsby-netlify-cms
20 | $ yarn
21 | $ yarn develop
22 | ```
23 |
24 | To test the CMS locally, you'll to need run a production build of the site:
25 |
26 | ```
27 | $ yarn build
28 | $ yarn serve
29 | ```
30 |
31 | ### Setting up the CMS
32 |
33 | For details on how to configure the CMS, take a look at the [Netlify CMS Docs](https://www.netlifycms.org/docs/intro).
34 |
35 | ## Useful Resources
36 | - ["Official" Gatsby and Netlify CMS starter](https://github.com/netlify-templates/gatsby-starter-netlify-cms)
37 | This starter includes a blog built with Gatsby and Netlify CMS. It was actually used as the starting off point for this repository.
38 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | title: "Gatsby + Netlify CMS Starter",
4 | },
5 | plugins: [
6 | "gatsby-plugin-react-helmet",
7 | "gatsby-plugin-sass",
8 | {
9 | resolve: "gatsby-source-filesystem",
10 | options: {
11 | path: `${__dirname}/src/pages`,
12 | name: "pages",
13 | },
14 | },
15 | {
16 | resolve: "gatsby-source-filesystem",
17 | options: {
18 | path: `${__dirname}/src/img`,
19 | name: "images",
20 | },
21 | },
22 | "gatsby-plugin-sharp",
23 | "gatsby-transformer-sharp",
24 | {
25 | resolve: "gatsby-transformer-remark",
26 | options: {
27 | plugins: [],
28 | },
29 | },
30 | {
31 | resolve: "gatsby-plugin-netlify-cms",
32 | options: {
33 | modulePath: `${__dirname}/src/cms/cms.js`,
34 | },
35 | },
36 | {
37 | resolve: `gatsby-plugin-favicon`,
38 | options: {
39 | logo: "./src/img/favicon.png",
40 | },
41 | },
42 | "gatsby-plugin-netlify", // make sure to keep it last in the array
43 | ],
44 | };
45 |
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const _ = require("lodash");
2 | const path = require("path");
3 | const { createFilePath } = require("gatsby-source-filesystem");
4 |
5 | exports.createPages = ({ actions, graphql }) => {
6 | const { createPage } = actions;
7 |
8 | return graphql(`
9 | {
10 | allMarkdownRemark(limit: 1000) {
11 | edges {
12 | node {
13 | id
14 | fields {
15 | slug
16 | }
17 | frontmatter {
18 | path
19 | templateKey
20 | }
21 | }
22 | }
23 | }
24 | }
25 | `).then(result => {
26 | if (result.errors) {
27 | result.errors.forEach(e => console.error(e.toString()));
28 | return Promise.reject(result.errors);
29 | }
30 |
31 | // Filter out the footer, navbar, and meetups so we don't create pages for those
32 | const postOrPage = result.data.allMarkdownRemark.edges.filter(edge => {
33 | if (edge.node.frontmatter.templateKey === "navbar") {
34 | return false;
35 | } else if (edge.node.frontmatter.templateKey === "footer") {
36 | return false;
37 | } else {
38 | return !Boolean(edge.node.fields.slug.match(/^\/meetups\/.*$/));
39 | }
40 | });
41 |
42 | postOrPage.forEach(edge => {
43 | let component, pathName;
44 | if (edge.node.frontmatter.templateKey === "home-page") {
45 | pathName = "/";
46 | component = path.resolve(`src/pages/index.js`);
47 | } else {
48 | pathName = edge.node.frontmatter.path || edge.node.fields.slug;
49 | component = path.resolve(`src/templates/${String(edge.node.frontmatter.templateKey)}.js`);
50 | }
51 | const id = edge.node.id;
52 | createPage({
53 | path: pathName,
54 | component,
55 | // additional data can be passed via context
56 | context: {
57 | id,
58 | },
59 | });
60 | });
61 | });
62 | };
63 |
64 | exports.onCreateNode = ({ node, actions, getNode }) => {
65 | const { createNodeField } = actions;
66 |
67 | if (node.internal.type === `MarkdownRemark`) {
68 | const value = createFilePath({ node, getNode });
69 | createNodeField({
70 | name: `slug`,
71 | node,
72 | value,
73 | });
74 | }
75 | };
76 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = "public"
3 | command = "npm run build"
4 | [build.environment]
5 | YARN_VERSION = "1.3.2"
6 | YARN_FLAGS = "--no-ignore-optional"
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-starter-netlify-cms",
3 | "description": "Example Gatsby, and Netlify CMS project",
4 | "version": "1.1.3",
5 | "author": "Robert Cooper",
6 | "dependencies": {
7 | "date-fns": "^1.29.0",
8 | "gatsby": "^2.0.0",
9 | "gatsby-plugin-favicon": "^3.1.4",
10 | "gatsby-plugin-netlify": "^2.0.0",
11 | "gatsby-plugin-netlify-cms": "^3.0.0",
12 | "gatsby-plugin-react-helmet": "^3.0.0",
13 | "gatsby-plugin-sass": "^2.0.1",
14 | "gatsby-plugin-sharp": "^2.0.5",
15 | "gatsby-remark-images": "^2.0.1",
16 | "gatsby-source-filesystem": "^2.0.1",
17 | "gatsby-transformer-remark": "^2.1.1",
18 | "gatsby-transformer-sharp": "^2.1.1",
19 | "lodash": "^4.17.5",
20 | "lodash-webpack-plugin": "^0.11.4",
21 | "netlify-cms": "^2.1.1",
22 | "node-sass": "^4.9.2",
23 | "parcel-bundler": "^1.9.4",
24 | "prop-types": "^15.6.0",
25 | "react": "^16.2.0",
26 | "react-dom": "^16.4.1",
27 | "react-google-maps": "^9.4.5",
28 | "react-helmet": "^5.2.0",
29 | "react-markdown": "^4.0.3",
30 | "uuid": "^3.2.1"
31 | },
32 | "keywords": [
33 | "gatsby"
34 | ],
35 | "license": "MIT",
36 | "main": "n/a",
37 | "scripts": {
38 | "start": "npm run develop",
39 | "clean": "rimraf .cache public",
40 | "build": "npm run clean && gatsby build",
41 | "develop": "npm run clean && gatsby develop",
42 | "serve": "gatsby serve",
43 | "format": "prettier --trailing-comma es5 --no-semi --single-quote --write \"{gatsby-*.js,src/**/*.js}\"",
44 | "test": "echo \"Error: no test specified\" && exit 1"
45 | },
46 | "devDependencies": {
47 | "prettier": "^1.7.4",
48 | "rimraf": "^2.6.2"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "rangeStrategy": "replace",
6 | "lockFileMaintenance": {
7 | "enabled": true,
8 | "extends": "schedule:weekly"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/cms/cms.js:
--------------------------------------------------------------------------------
1 | import CMS from "netlify-cms";
2 |
3 | import AboutPagePreview from "./preview-templates/AboutPagePreview";
4 | import HomePagePreview from "./preview-templates/HomePagePreview";
5 | import MeetupPreview from "./preview-templates/MeetupPreview";
6 | import FooterPreview from "./preview-templates/FooterPreview";
7 | import NavbarPreview from "./preview-templates/NavbarPreview";
8 | import PastMeetupsPagePreview from "./preview-templates/PastMeetupsPagePreview";
9 |
10 | CMS.registerPreviewTemplate("meetups", MeetupPreview);
11 | CMS.registerPreviewTemplate("footer", FooterPreview);
12 | CMS.registerPreviewTemplate("navbar", NavbarPreview);
13 | CMS.registerPreviewTemplate("about", AboutPagePreview);
14 | CMS.registerPreviewTemplate("home", HomePagePreview);
15 | CMS.registerPreviewTemplate("pastMeetups", PastMeetupsPagePreview);
16 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/AboutPagePreview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { AboutPageTemplate } from "../../templates/about-page";
4 |
5 | const AboutPagePreview = ({ entry, widgetFor }) => (
6 |
13 | );
14 |
15 | AboutPagePreview.propTypes = {
16 | entry: PropTypes.shape({
17 | getIn: PropTypes.func,
18 | }),
19 | widgetFor: PropTypes.func,
20 | };
21 |
22 | export default AboutPagePreview;
23 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/FooterPreview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { FooterTemplate } from "../../components/Footer";
5 |
6 | const FooterPreview = ({ entry }) => {
7 | const data = entry.getIn(["data"]).toJS();
8 | return ;
9 | };
10 |
11 | FooterPreview.propTypes = {
12 | entry: PropTypes.shape({
13 | getIn: PropTypes.func,
14 | }),
15 | };
16 |
17 | export default FooterPreview;
18 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/HomePagePreview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { HomePageTemplate } from "../../pages/index";
5 |
6 | const HomePagePreview = ({ entry }) => {
7 | const home = entry.getIn(["data"]).toJS();
8 | return ;
9 | };
10 |
11 | HomePagePreview.propTypes = {
12 | entry: PropTypes.shape({
13 | getIn: PropTypes.func,
14 | }),
15 | };
16 |
17 | export default HomePagePreview;
18 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/MeetupPreview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import format from "date-fns/format";
4 |
5 | import MeetupTemplate from "../../templates/meetup";
6 |
7 | const MeetupPreview = ({ entry }) => {
8 | const meetup = entry.getIn(["data"]).toJS();
9 | const rawDate = meetup.date;
10 | const formattedDate = format(rawDate, "MMMM Do YYYY @ h:mm A");
11 | return ;
12 | };
13 |
14 | MeetupPreview.propTypes = {
15 | entry: PropTypes.shape({
16 | getIn: PropTypes.func,
17 | }),
18 | };
19 |
20 | export default MeetupPreview;
21 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/NavbarPreview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { NavbarTemplate } from "../../components/Navbar";
5 |
6 | const NavbarPreview = ({ entry }) => {
7 | const data = entry.getIn(["data"]).toJS();
8 | return ;
9 | };
10 |
11 | NavbarPreview.propTypes = {
12 | entry: PropTypes.shape({
13 | getIn: PropTypes.func,
14 | }),
15 | };
16 |
17 | export default NavbarPreview;
18 |
--------------------------------------------------------------------------------
/src/cms/preview-templates/PastMeetupsPagePreview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { PastMeetupsPageTemplate } from "../../templates/past-meetups-page";
4 |
5 | const PastMeetupsPagePreview = ({ entry, widgetFor }) => {
6 | return (
7 |
12 | );
13 | };
14 |
15 | PastMeetupsPagePreview.propTypes = {
16 | entry: PropTypes.shape({
17 | getIn: PropTypes.func,
18 | }),
19 | widgetFor: PropTypes.func,
20 | };
21 |
22 | export default PastMeetupsPagePreview;
23 |
--------------------------------------------------------------------------------
/src/components/Content.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | export const HTMLContent = ({ content, className }) => (
5 |
6 | );
7 |
8 | HTMLContent.propTypes = {
9 | content: PropTypes.node,
10 | className: PropTypes.string,
11 | };
12 |
13 | export default HTMLContent;
14 |
--------------------------------------------------------------------------------
/src/components/CustomLink.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { Link } from "gatsby";
4 |
5 | export const CustomLink = ({ linkType, linkURL, children, className = "" }) => {
6 | if (linkType === "internal") {
7 | return (
8 |
9 | {children}
10 |
11 | );
12 | } else {
13 | return (
14 |
15 | {children}
16 |
17 | );
18 | }
19 | };
20 |
21 | CustomLink.propTypes = {
22 | linkType: PropTypes.string,
23 | };
24 |
25 | export default CustomLink;
26 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./styles.scss";
3 |
4 | export const FooterTemplate = ({ data }) => {
5 | const { logoImage, socialLinks } = data;
6 |
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
{logoImage.tagline}
20 |
21 | {socialLinks.length > 0 && (
22 |
41 | )}
42 |
43 |
44 |
45 |
46 | 🇨🇦
47 |
48 |
49 |
50 |
51 |
52 | );
53 | };
54 |
55 | const Footer = props => {
56 | if (!props.data) {
57 | return null;
58 | }
59 | const data = props.data.edges[0].node.frontmatter;
60 | return ;
61 | };
62 |
63 | export { Footer };
64 |
--------------------------------------------------------------------------------
/src/components/Footer/index.js:
--------------------------------------------------------------------------------
1 | export * from "./Footer";
2 |
--------------------------------------------------------------------------------
/src/components/Footer/styles.scss:
--------------------------------------------------------------------------------
1 | @import "../../styles/variables";
2 | @import "../../styles/mixins";
3 |
4 | .footer {
5 | background-color: $background-dark;
6 | @include medium-font;
7 | color: $body-light;
8 | padding: 80px 20px;
9 |
10 | @include respond-to("small") {
11 | padding: 40px 20px;
12 | height: $footer-height;
13 | }
14 |
15 | &-container {
16 | display: flex;
17 | flex-direction: column;
18 | flex-wrap: wrap;
19 | height: 100%;
20 |
21 | @include respond-to("small") {
22 | flex-direction: row;
23 | }
24 | }
25 |
26 | &-top {
27 | display: flex;
28 | flex-direction: column;
29 | flex-grow: 1;
30 | align-items: center;
31 | width: 100%;
32 |
33 | @include respond-to("small") {
34 | flex-direction: row;
35 | }
36 | }
37 |
38 | &-about {
39 | text-align: center;
40 |
41 | @include respond-to("small") {
42 | text-align: left;
43 | }
44 | }
45 |
46 | &-aboutTitleImg {
47 | max-height: 60px;
48 | height: auto;
49 | margin-bottom: 10px;
50 | }
51 |
52 | &-aboutDescription {
53 | @include medium-font();
54 | }
55 |
56 | &-socialMenu {
57 | margin: 20px;
58 |
59 | @include respond-to("small") {
60 | margin-left: auto;
61 | }
62 | }
63 |
64 | &-socialMenuItem {
65 | padding: 15px 0;
66 | }
67 |
68 | &-socialLink {
69 | @include medium-font;
70 | font-weight: 400;
71 | text-decoration: none;
72 | color: $body-light;
73 | display: flex;
74 | align-items: center;
75 | }
76 |
77 | &-socialLinkIcon {
78 | width: 40px;
79 | margin-right: 20px;
80 | }
81 |
82 | &-flag {
83 | width: 100%;
84 | display: flex;
85 | justify-content: center;
86 | font-size: 2rem;
87 | padding-top: 20px;
88 |
89 | @include respond-to("small") {
90 | margin-left: auto;
91 | }
92 | }
93 |
94 | &-bottom {
95 | margin-top: auto;
96 | width: 100%;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/components/Layout.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Helmet from "react-helmet";
3 | import { graphql } from "gatsby";
4 |
5 | import "../styles";
6 | import { Navbar } from "../components/Navbar";
7 | import { Footer } from "../components/Footer";
8 |
9 | const TemplateWrapper = ({ footerData = null, navbarData = null, children }) => (
10 |
11 |
12 |
13 |
14 |
15 |
16 | {children}
17 |
18 |
19 | );
20 |
21 | export const query = graphql`
22 | fragment LayoutFragment on Query {
23 | footerData: allMarkdownRemark(filter: { frontmatter: { templateKey: { eq: "footer" } } }) {
24 | edges {
25 | node {
26 | id
27 | frontmatter {
28 | logoImage {
29 | image
30 | imageAlt
31 | tagline
32 | }
33 | socialLinks {
34 | image
35 | imageAlt
36 | label
37 | linkURL
38 | }
39 | }
40 | }
41 | }
42 | }
43 | navbarData: allMarkdownRemark(filter: { frontmatter: { templateKey: { eq: "navbar" } } }) {
44 | edges {
45 | node {
46 | id
47 | frontmatter {
48 | logoImage {
49 | image
50 | imageAlt
51 | }
52 | menuItems {
53 | label
54 | linkType
55 | linkURL
56 | }
57 | }
58 | }
59 | }
60 | }
61 | }
62 | `;
63 |
64 | export default TemplateWrapper;
65 |
--------------------------------------------------------------------------------
/src/components/Map.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 | import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps";
4 | import LocationIcon from "../img/location.svg";
5 |
6 | class Map extends Component {
7 | render() {
8 | if (!this.props.latitude || !this.props.longitude || !this.props.link) {
9 | return null;
10 | }
11 | return (
12 |
17 | window.open(this.props.link)}
21 | />
22 |
23 | );
24 | }
25 | }
26 |
27 | Map.propTypes = {
28 | link: PropTypes.string,
29 | latitude: PropTypes.number,
30 | longitude: PropTypes.number,
31 | };
32 |
33 | export default withScriptjs(withGoogleMap(Map));
34 |
35 | const exampleMapStyles = [
36 | {
37 | featureType: "all",
38 | elementType: "labels.text.fill",
39 | stylers: [
40 | {
41 | color: "#ffffff",
42 | },
43 | ],
44 | },
45 | {
46 | featureType: "all",
47 | elementType: "labels.text.stroke",
48 | stylers: [
49 | {
50 | color: "#000000",
51 | },
52 | {
53 | lightness: 13,
54 | },
55 | ],
56 | },
57 | {
58 | featureType: "administrative",
59 | elementType: "geometry.fill",
60 | stylers: [
61 | {
62 | color: "#000000",
63 | },
64 | ],
65 | },
66 | {
67 | featureType: "administrative",
68 | elementType: "geometry.stroke",
69 | stylers: [
70 | {
71 | color: "#144b53",
72 | },
73 | {
74 | lightness: 14,
75 | },
76 | {
77 | weight: 1.4,
78 | },
79 | ],
80 | },
81 | {
82 | featureType: "landscape",
83 | elementType: "all",
84 | stylers: [
85 | {
86 | color: "#08304b",
87 | },
88 | ],
89 | },
90 | {
91 | featureType: "poi",
92 | elementType: "geometry",
93 | stylers: [
94 | {
95 | color: "#0c4152",
96 | },
97 | {
98 | lightness: 5,
99 | },
100 | ],
101 | },
102 | {
103 | featureType: "road.highway",
104 | elementType: "geometry.fill",
105 | stylers: [
106 | {
107 | color: "#000000",
108 | },
109 | ],
110 | },
111 | {
112 | featureType: "road.highway",
113 | elementType: "geometry.stroke",
114 | stylers: [
115 | {
116 | color: "#0b434f",
117 | },
118 | {
119 | lightness: 25,
120 | },
121 | ],
122 | },
123 | {
124 | featureType: "road.arterial",
125 | elementType: "geometry.fill",
126 | stylers: [
127 | {
128 | color: "#000000",
129 | },
130 | ],
131 | },
132 | {
133 | featureType: "road.arterial",
134 | elementType: "geometry.stroke",
135 | stylers: [
136 | {
137 | color: "#0b3d51",
138 | },
139 | {
140 | lightness: 16,
141 | },
142 | ],
143 | },
144 | {
145 | featureType: "road.local",
146 | elementType: "geometry",
147 | stylers: [
148 | {
149 | color: "#000000",
150 | },
151 | ],
152 | },
153 | {
154 | featureType: "transit",
155 | elementType: "all",
156 | stylers: [
157 | {
158 | color: "#146474",
159 | },
160 | ],
161 | },
162 | {
163 | featureType: "water",
164 | elementType: "all",
165 | stylers: [
166 | {
167 | color: "#021019",
168 | },
169 | ],
170 | },
171 | ];
172 |
--------------------------------------------------------------------------------
/src/components/Navbar/Navbar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import "./styles.scss";
4 | import CustomLink from "../CustomLink";
5 |
6 | export const NavbarTemplate = ({ data }) => (
7 |
8 |
9 | {data.menuItems.length > 0 && (
10 |
11 | {data.menuItems.map(menuItem => (
12 |
13 |
18 | {menuItem.label}
19 |
20 |
21 | ))}
22 |
23 | )}
24 |
25 |
26 | );
27 |
28 | const Navbar = props => {
29 | if (!props.data) {
30 | return null;
31 | }
32 | const data = props.data.edges[0].node.frontmatter;
33 | return ;
34 | };
35 |
36 | export { Navbar };
37 |
--------------------------------------------------------------------------------
/src/components/Navbar/index.js:
--------------------------------------------------------------------------------
1 | export * from "./Navbar";
2 |
--------------------------------------------------------------------------------
/src/components/Navbar/styles.scss:
--------------------------------------------------------------------------------
1 | @import "../../styles/variables.scss";
2 |
3 | .navbar {
4 | background-color: $background-dark;
5 | height: $navbar-height;
6 |
7 | &-container {
8 | display: flex;
9 | height: 100%;
10 | }
11 |
12 | &-menu {
13 | display: flex;
14 | flex-grow: 0;
15 | margin-left: auto;
16 | margin-right: -1rem;
17 | }
18 |
19 | &-menuItem {
20 | display: flex;
21 | flex-direction: column;
22 | justify-content: center;
23 | }
24 |
25 | &-menuLink {
26 | color: $menu-inactive;
27 | padding: 0 0.5rem;
28 | margin: 0 0.5rem;
29 | text-decoration: none;
30 | font-weight: 300;
31 | letter-spacing: 0.4px;
32 | transition: all $transition-duration ease-in-out;
33 |
34 | &:hover,
35 | &--active {
36 | color: $menu-active;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/src/img/favicon.png
--------------------------------------------------------------------------------
/src/img/headshot-placeholder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/img/location.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Layout from "../components/Layout";
3 | import Helmet from "react-helmet";
4 | import "../styles/404.scss";
5 |
6 | const NotFoundPage = () => (
7 |
8 |
9 | Page Not Found
10 |
11 |
12 |
NOT FOUND
13 |
{`
14 | (___________________________()6 \`-,
15 | ( ______________________ /''"\`
16 | //\\ //\\
17 | "" "" "" ""
18 | `}
19 |
20 |
21 | );
22 |
23 | export default NotFoundPage;
24 |
--------------------------------------------------------------------------------
/src/pages/about/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: about-page
3 | title: Wakanda JavaScript Developer Meetup
4 | mainImage:
5 | image: /img/teemu-paananen-376238-unsplash.jpg
6 | imageAlt: Wakanda JavaScript developer presenting at a meetup.
7 | gallery:
8 | - image: /img/neonbrand-509131-unsplash.jpg
9 | imageAlt: Wakanda JavaScript developer presenting at a meetup.
10 | - image: /img/jakob-dalbjorn-730178-unsplash.jpg
11 | imageAlt: Wakanda JavaScript developer presenting at a meetup.
12 | - image: /img/annie-spratt-608001-unsplash.jpg
13 | imageAlt: Wakanda developers working together at a table.
14 | developerGroups: |-
15 | ## Other Wakanda Developer Groups
16 |
17 | * [React & React Native Meetup](https://www.google.com)
18 | * [Python Meetup](https://www.google.com)
19 | * [Google Developer Meetup](https://www.google.com)
20 | * [Vue.js Meetup](https://www.google.com)
21 | * [Ruby Meetup](https://www.google.com)
22 | organizers:
23 | title: Group Organizers
24 | gallery:
25 | - image: /img/organizer-1.jpg
26 | imageAlt: Tom Cruise
27 | name: Tom Cruise
28 | - image: /img/organizer-2.jpg
29 | imageAlt: Tom Hanks
30 | name: Tom Hanks
31 | seo:
32 | browserTitle: About | JavaScript Wakanda
33 | description: >-
34 | JavaScript Wakanda is a meetup group that holds monthly meetups where
35 | JavaScript developers get together for presentations and to meet others in
36 | the community.
37 | title: About | JS Wakanda
38 | ---
39 | ## Sharing Ideas and Meeting Others
40 |
41 | JS Wakanda is a user group run by volunteers. We meet in borrowed spaces, graciously provided by different companies and organisations. Our purpose is to provide a place where our members have a good time talking about programming, sharing their knowledge and meeting other passionate folks. We keep it friendly, inclusive and positive.
42 |
--------------------------------------------------------------------------------
/src/pages/footer/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: footer
3 | logoImage:
4 | image: /img/js-wakanda.png
5 | imageAlt: JavaScript Wakanda
6 | tagline: Your friendly local Wakanda meetup
7 | socialLinks:
8 | - image: /img/meetup.svg
9 | imageAlt: Join us on meetup.com
10 | label: meetup.com
11 | linkURL: 'https://www.meetup.com/'
12 | - image: /img/twitter.svg
13 | imageAlt: Follow us on Twitter
14 | label: twitter.com
15 | linkURL: 'https://twitter.com/'
16 | - image: /img/facebook.svg
17 | imageAlt: Join our Facebook group
18 | label: facebook.com
19 | linkURL: 'https://www.facebook.com/'
20 | - image: /img/email.svg
21 | imageAlt: Contact us by email
22 | label: email us
23 | linkURL: 'mailto:contact@js-wakanda.org'
24 | ---
25 |
26 |
--------------------------------------------------------------------------------
/src/pages/home/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: home-page
3 | headerImage:
4 | image: /img/television.png
5 | imageAlt: JavaScript Wakanda
6 | title: Meet other JavaScript Developers in Wakanda
7 | upcomingMeetupHeading: Upcoming Meetup
8 | noUpcomingMeetupText: Details to be announced.
9 | mapsNote: Clicking the pin opens Google Maps in a new tab.
10 | callToActions:
11 | firstCTA:
12 | heading: Past Meetups
13 | linkType: internal
14 | linkURL: /meetups
15 | subHeading: Look at what topics were presented at past meetups.
16 | secondCTA:
17 | heading: Volunteer to Present
18 | linkType: external
19 | linkURL: 'mailto:contact@js-wakanda.org'
20 | subHeading: Want to present at an upcoming meetup? Contact us.
21 | seo:
22 | browserTitle: JS Wakanda
23 | description: >-
24 | JavaScript meetup group in Wakanda where JavaScript developers get together
25 | for presentations and to meet others in the community.
26 | title: JavaScript Wakanda
27 | ---
28 |
29 |
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { graphql } from "gatsby";
4 | import Helmet from "react-helmet";
5 | import isAfter from "date-fns/is_after";
6 |
7 | import Layout from "../components/Layout";
8 | import Map from "../components/Map";
9 | import HeadshotPlaceholder from "../img/headshot-placeholder.svg";
10 | import CustomLink from "../components/CustomLink";
11 | import "../styles/home.scss";
12 |
13 | export const HomePageTemplate = ({ home, upcomingMeetup = null }) => {
14 | const presenters = upcomingMeetup && upcomingMeetup.presenters;
15 | const latitude = upcomingMeetup && parseFloat(upcomingMeetup.location.mapsLatitude);
16 | const longitude = upcomingMeetup && parseFloat(upcomingMeetup.location.mapsLongitude);
17 | return (
18 | <>
19 |
20 |
21 | {home.headerImage &&
}
22 |
23 | {home.title}
24 |
25 |
26 |
27 |
28 |
29 |
{home.upcomingMeetupHeading}
30 | {upcomingMeetup ? (
31 | <>
32 |
33 | Date:
34 | {upcomingMeetup.formattedDate}
35 |
36 |
37 | Location:
38 | {upcomingMeetup.location.name}
39 |
40 | {presenters.length > 0 && (
41 |
42 | {presenters.map(presenter => (
43 |
44 |
49 |
{presenter.name}
50 |
51 | {presenter.presentationTitle}
52 |
53 |
{presenter.text}
54 |
55 | ))}
56 |
57 | )}
58 |
{home.mapsNote}
59 |
60 |
}
64 | containerElement={
}
65 | mapElement={
}
66 | link={upcomingMeetup.location.mapsLink}
67 | latitude={latitude}
68 | longitude={longitude}
69 | />
70 |
71 | >
72 | ) : (
73 |
{home.noUpcomingMeetupText}
74 | )}
75 |
76 |
77 |
78 |
83 |
84 |
{home.callToActions.firstCTA.heading}
85 |
{home.callToActions.firstCTA.subHeading}
86 |
87 |
88 |
93 |
94 |
{home.callToActions.secondCTA.heading}
95 |
{home.callToActions.secondCTA.subHeading}
96 |
97 |
98 |
99 | >
100 | );
101 | };
102 |
103 | class HomePage extends React.Component {
104 | render() {
105 | const { data } = this.props;
106 | const {
107 | data: { footerData, navbarData },
108 | } = this.props;
109 | const { frontmatter: home } = data.homePageData.edges[0].node;
110 | const {
111 | seo: { title: seoTitle, description: seoDescription, browserTitle },
112 | } = home;
113 | let upcomingMeetup = null;
114 | // Find the next meetup that is closest to today
115 | data.allMarkdownRemark.edges.every(item => {
116 | const { frontmatter: meetup } = item.node;
117 | if (isAfter(meetup.rawDate, new Date())) {
118 | upcomingMeetup = meetup;
119 | return true;
120 | } else {
121 | return false;
122 | }
123 | });
124 | return (
125 |
126 |
127 |
128 |
129 | {browserTitle}
130 |
131 |
132 |
133 | );
134 | }
135 | }
136 |
137 | HomePage.propTypes = {
138 | data: PropTypes.shape({
139 | allMarkdownRemark: PropTypes.shape({
140 | edges: PropTypes.array,
141 | }),
142 | }),
143 | };
144 |
145 | export default HomePage;
146 |
147 | export const pageQuery = graphql`
148 | query HomePageQuery {
149 | allMarkdownRemark(
150 | filter: { frontmatter: { presenters: { elemMatch: { text: { ne: null } } } } }
151 | sort: { order: DESC, fields: frontmatter___date }
152 | ) {
153 | edges {
154 | node {
155 | frontmatter {
156 | title
157 | formattedDate: date(formatString: "MMMM Do YYYY @ h:mm A")
158 | rawDate: date
159 | presenters {
160 | name
161 | image
162 | text
163 | presentationTitle
164 | }
165 | location {
166 | mapsLatitude
167 | mapsLongitude
168 | mapsLink
169 | name
170 | }
171 | }
172 | }
173 | }
174 | }
175 | ...LayoutFragment
176 | homePageData: allMarkdownRemark(filter: { frontmatter: { templateKey: { eq: "home-page" } } }) {
177 | edges {
178 | node {
179 | frontmatter {
180 | title
181 | headerImage {
182 | image
183 | imageAlt
184 | }
185 | upcomingMeetupHeading
186 | noUpcomingMeetupText
187 | mapsNote
188 | callToActions {
189 | firstCTA {
190 | heading
191 | subHeading
192 | linkType
193 | linkURL
194 | }
195 | secondCTA {
196 | heading
197 | subHeading
198 | linkType
199 | linkURL
200 | }
201 | }
202 | seo {
203 | browserTitle
204 | title
205 | description
206 | }
207 | }
208 | }
209 | }
210 | }
211 | }
212 | `;
213 |
--------------------------------------------------------------------------------
/src/pages/meetups/august-2018.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: meetup
3 | title: August 2018
4 | date: 2018-08-14T23:00:00.000Z
5 | presenters:
6 | - image: /img/ramy-kabalan-796973-unsplash.jpg
7 | name: Fred Champ
8 | presentationTitle: Network optimizations 101
9 | text: >+
10 | If you have an application that requires network requests to run, you
11 | might want to come watch this presentation. We'll cover congestion
12 | control, caching, batching and a couple advanced strategies to make your
13 | application faster all while saving on infrastructure costs.
14 |
15 | - image: /img/marius-ciocirlan-398931-unsplash.jpg
16 | name: Ben Wilson
17 | presentationTitle: Memoizing functions
18 | text: >-
19 | Ben will tell us all about the power of functional programming,
20 | specifically memoization and how it can help speed up frequently used side
21 | effect-less functions.
22 | location:
23 | mapsLatitude: 64.843779
24 | mapsLink: 'https://goo.gl/maps/LLgicn3zGuy'
25 | mapsLongitude: -147.718189
26 | name: Fairbanks Ice Museum
27 | ---
28 |
29 |
--------------------------------------------------------------------------------
/src/pages/meetups/july-2018.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: meetup
3 | title: July 2018
4 | date: 2018-07-10T23:00:00.000Z
5 | presenters:
6 | - image: /img/lucas-sankey-378674-unsplash.jpg
7 | links:
8 | - linkText: Iterators and Generators
9 | linkURL: 'https://google.com/'
10 | - linkText: Async Proposal
11 | linkURL: 'https://google.com/'
12 | - linkText: Babel Transform for async generators
13 | linkURL: 'https://google.com/'
14 | - linkText: TC39 Spec for the feature.
15 | linkURL: 'https://google.com/'
16 | name: Steve Wozniak
17 | presentationTitle: Async Generators - Looking at the ES2018 Spec
18 | text: >-
19 | Building on Jason's talk from last meet-up, we'll look at Async
20 | Generators, a widely discussed feature documented in the new ES2018 spec.
21 | We'll look at some use cases and live examples.
22 | location:
23 | mapsLatitude: 64.843779
24 | mapsLink: 'https://goo.gl/maps/Rm6ihxVrZGK2'
25 | mapsLongitude: -147
26 | name: Fairbanks Ice Museum
27 | ---
28 |
29 |
--------------------------------------------------------------------------------
/src/pages/meetups/november-2018-meetup.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: November 2060
3 | date: 2060-11-09T23:00:00.000Z
4 | presenters:
5 | - image: /img/jonas-kakaroto-577554-unsplash.jpg
6 | links:
7 | - linkText: Github Example
8 | linkURL: 'https://github.com/'
9 | - linkText: Conference Talk Recording
10 | linkURL: 'https://github.com/'
11 | name: Wes Tanner
12 | presentationTitle: Advanced React
13 | text: >-
14 | Wes is going to guide use on how to build a full stack react and graphql
15 | app using Appolo, GraphQL Yoga, and Prisma.
16 | - image: /img/benjamin-parker-736167-unsplash.jpg
17 | links:
18 | - linkText: Twitter
19 | linkURL: 'https://twitter.com/'
20 | name: Scott Brolinski
21 | presentationTitle: Meteor.js Framework
22 | text: >-
23 | Scott will take use through the Meteor JavaScript framework and how it’s
24 | awesome in every way.
25 | location:
26 | mapsLatitude: 64.843779
27 | mapsLink: 'https://goo.gl/maps/Rm6ihxVrZGK2'
28 | mapsLongitude: -147.718189
29 | name: Fairbanks Ice Museum
30 | ---
31 |
32 |
--------------------------------------------------------------------------------
/src/pages/meetups/october-2018.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: meetup
3 | title: October 2018
4 | date: 2018-10-09T23:00:00.000Z
5 | presenters:
6 | - image: /img/michael-dam-258165-unsplash.jpg
7 | links:
8 | - linkText: Authenticated Systems Workshop (Github)
9 | linkURL: 'https://google.com/'
10 | name: Janet Morisette
11 | presentationTitle: Authenticated Systems Workshop
12 | text: >-
13 | In this talk we will go over a few ways you can implement authentication
14 | (including offline authentication strategies) and then dive into some code
15 | for both a server and a client which you can adapt to suit your own app.
16 | - image: /img/humphrey-muleba-795250-unsplash.jpg
17 | links:
18 | - linkText: Gatsby Repo
19 | linkURL: 'https://google.com/'
20 | name: Sarah Jones
21 | presentationTitle: Gatsby!
22 | text: >-
23 | Get to know more about the basics of using Gatsby with Sarah. She is going
24 | to use Nike's website as an example.
25 | location:
26 | mapsLatitude: 64.843779
27 | mapsLink: 'https://goo.gl/maps/Rm6ihxVrZGK2'
28 | mapsLongitude: -147.718189
29 | name: Fairbanks Ice Museum
30 | ---
31 |
32 |
--------------------------------------------------------------------------------
/src/pages/meetups/september-2018.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: meetup
3 | title: September 2018
4 | date: 2018-09-11T22:00:00.000Z
5 | presenters:
6 | - image: /img/edward-cisneros-415601-unsplash.jpg
7 | links:
8 | - linkText: Async/Await
9 | linkURL: 'https://google.com/'
10 | - linkText: Promises
11 | linkURL: 'https://google.com/'
12 | name: Brittany Vonn
13 | presentationTitle: 'No sugar please: a tearing down of abstractions'
14 | text: >-
15 | To demistify one of JS' hotest bits of syntax sugar and the familiar
16 | Promise, Brittany will break down the mechanics of both async patterns by
17 | building his own naive variations of those language features.
18 | - image: /img/marius-ciocirlan-398931-unsplash.jpg
19 | links:
20 | - linkText: React Native
21 | linkURL: 'https://facebook.github.io/react-native/'
22 | name: Brian Kardashian
23 | presentationTitle: React & React Native
24 | text: >-
25 | The folks at Turbulent have been working hard using React and React-Native
26 | to bring quality, cross platform JS products to market. Today, they will
27 | show us the patterns, structures and challenges of building large scale
28 | cross platform apps.
29 | location:
30 | mapsLatitude: 64.843779
31 | mapsLink: 'https://goo.gl/maps/Rm6ihxVrZGK2'
32 | mapsLongitude: -147.718189
33 | name: Fairbanks Ice Museum
34 | ---
35 |
36 |
--------------------------------------------------------------------------------
/src/pages/navbar/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: navbar
3 | menuItems:
4 | - label: home
5 | linkType: internal
6 | linkURL: /
7 | - label: about
8 | linkType: internal
9 | linkURL: /about
10 | - label: past meetups
11 | linkType: internal
12 | linkURL: /meetups
13 | ---
14 |
15 |
--------------------------------------------------------------------------------
/src/pages/pastMeetups/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | templateKey: past-meetups-page
3 | title: Past Meetups
4 | path: /meetups
5 | seo:
6 | browserTitle: Past Meetups | JS Wakanda
7 | description: View the topics that were presented at past JavaScript Montreal meetups.
8 | title: Past Meetups | JavaScript Wakanda
9 | ---
10 |
11 | Here are some of the subjects we've covered in past meetups. If you're interested in participating by giving a talk, don't worry too much if we've touched on the subject before. New people join every day and there are a lot of subjects warranting a re-visit.
12 |
--------------------------------------------------------------------------------
/src/styles/404.scss:
--------------------------------------------------------------------------------
1 | @import "./variables.scss";
2 | @import "./mixins.scss";
3 |
4 | .pageNotFound {
5 | display: flex;
6 | flex-direction: column;
7 | align-items: center;
8 | padding: $section-padding 0;
9 |
10 | @include respond-to("small") {
11 | padding: $section-padding-mobile 0;
12 | }
13 |
14 | &-title {
15 | @include large-font();
16 | @include title-link-border();
17 | margin-bottom: 40px;
18 | }
19 |
20 | &-description {
21 | font-size: 0.7rem;
22 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
23 | monospace;
24 | white-space: pre;
25 | margin-bottom: 40px;
26 |
27 | @include respond-to("small") {
28 | @include medium-font();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/styles/about-page.scss:
--------------------------------------------------------------------------------
1 | @import "./variables.scss";
2 | @import "./mixins.scss";
3 |
4 | .about {
5 | &-header {
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | padding-top: 40px;
10 |
11 | @include respond-to("medium") {
12 | padding-top: 60px;
13 | flex-direction: row;
14 | justify-content: space-around;
15 | }
16 | }
17 |
18 | &-titleWrapper {
19 | margin-bottom: 40px;
20 | margin-right: 20px;
21 | text-align: center;
22 | flex-shrink: 0;
23 |
24 | @include respond-to("medium") {
25 | flex-basis: 450px;
26 | text-align: left;
27 | margin-bottom: 0;
28 | justify-content: space-around;
29 | }
30 | }
31 |
32 | &-title {
33 | @include xlarge-font();
34 | @include title-link-border();
35 | display: inline;
36 | }
37 |
38 | &-imageWrapper {
39 | width: 400px;
40 | max-width: 100%;
41 |
42 | @include respond-to("medium") {
43 | width: 500px;
44 | }
45 | }
46 |
47 | &-description {
48 | padding: 0 20px 60px;
49 | text-align: justify;
50 | @include medium-font();
51 |
52 | @include respond-to("small") {
53 | text-align: left;
54 | }
55 |
56 | h2 {
57 | text-align: center;
58 | margin-bottom: 20px;
59 | @include large-font();
60 | }
61 | }
62 | }
63 |
64 | .developerGroups {
65 | background-color: $background-medium;
66 | color: $body-light;
67 | @include medium-font();
68 |
69 | h2 {
70 | @include large-font();
71 | margin-bottom: 40px;
72 | }
73 |
74 | ul li {
75 | margin-bottom: 20px;
76 | }
77 |
78 | a {
79 | @include link-border();
80 | }
81 | }
82 |
83 | .galleryList {
84 | display: flex;
85 | flex-wrap: wrap;
86 | justify-content: space-around;
87 | margin-bottom: -20px;
88 | }
89 |
90 | .galleryList-item {
91 | width: 350px;
92 | margin-bottom: 20px;
93 | }
94 |
95 | .organizers {
96 | @include medium-font();
97 |
98 | &-title {
99 | text-align: center;
100 | margin-bottom: 40px;
101 | @include large-font();
102 |
103 | @include respond-to("small") {
104 | margin-bottom: 80px;
105 | }
106 | }
107 |
108 | &-list {
109 | display: flex;
110 | flex-wrap: wrap;
111 | justify-content: space-around;
112 | }
113 |
114 | &-listItem {
115 | display: flex;
116 | flex-direction: column;
117 | align-items: center;
118 | margin: 0 10px;
119 | }
120 |
121 | &-listItemImage {
122 | margin-bottom: 20px;
123 | border-radius: 3px;
124 | height: 200px;
125 |
126 | @include respond-to("small") {
127 | height: 300px;
128 | }
129 | }
130 |
131 | &-listItemName {
132 | font-weight: 400;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/styles/generic.scss:
--------------------------------------------------------------------------------
1 | @import "./variables.scss";
2 | @import "./mixins.scss";
3 |
4 | html {
5 | font-size: 16px;
6 | -moz-osx-font-smoothing: grayscale;
7 | -webkit-font-smoothing: antialiased;
8 | text-rendering: optimizeLegibility;
9 | text-size-adjust: 100%;
10 | background-color: $background-light;
11 | }
12 |
13 | body {
14 | font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
15 | "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
16 | color: $body-dark;
17 | }
18 |
19 | a {
20 | color: inherit;
21 | text-decoration: none;
22 | }
23 |
24 | .container {
25 | max-width: $container-width;
26 | padding: 0 $container-padding;
27 | margin: auto;
28 | }
29 |
30 | .section {
31 | padding: $section-padding-mobile 0;
32 |
33 | @include respond-to("small") {
34 | padding: $section-padding 0;
35 | }
36 | }
37 |
38 | main {
39 | min-height: calc(100vh - #{$navbar-height} - #{$footer-height});
40 | }
41 |
--------------------------------------------------------------------------------
/src/styles/home.scss:
--------------------------------------------------------------------------------
1 | @import "./variables.scss";
2 | @import "./mixins.scss";
3 |
4 | .header {
5 | height: 450px;
6 | background-color: $background-medium;
7 |
8 | @include respond-to("medium") {
9 | height: 575px;
10 | }
11 |
12 | &-container {
13 | display: flex;
14 | flex-direction: column;
15 | height: 100%;
16 | align-items: center;
17 | justify-content: center;
18 | padding-top: 40px;
19 | padding-bottom: 40px;
20 |
21 | @include respond-to("small") {
22 | flex-direction: row;
23 | justify-content: space-around;
24 | }
25 | }
26 |
27 | &-image {
28 | height: 150px;
29 | margin-bottom: 30px;
30 |
31 | @include respond-to("small") {
32 | height: 200px;
33 | margin-bottom: 0;
34 | }
35 |
36 | @include respond-to("medium") {
37 | height: 300px;
38 | }
39 | }
40 |
41 | &-tagline {
42 | @include xlarge-font();
43 | color: $body-light;
44 | width: 400px;
45 | text-align: center;
46 | cursor: default;
47 |
48 | @include respond-to("small") {
49 | flex-basis: 430px;
50 | text-align: left;
51 | width: 50%;
52 | align-items: flex-start;
53 | }
54 |
55 | @include respond-to("medium") {
56 | flex-basis: 500px;
57 | }
58 | }
59 |
60 | &-taglinePart {
61 | display: inline;
62 | @include title-link-border();
63 | }
64 | }
65 |
66 | .upcomingMeetup {
67 | color: $body-dark;
68 | @include medium-font;
69 |
70 | &-container {
71 | display: flex;
72 | flex-direction: column;
73 | align-items: center;
74 | }
75 |
76 | &-title {
77 | @include large-font;
78 | margin-bottom: 30px;
79 | }
80 |
81 | &-detail {
82 | text-align: center;
83 |
84 | &--date {
85 | margin-bottom: 10px;
86 | }
87 | }
88 |
89 | &-detailLabel {
90 | font-weight: 400;
91 | }
92 |
93 | &-presenters {
94 | display: flex;
95 | flex-wrap: wrap;
96 | justify-content: space-around;
97 | padding: 40px 0;
98 | }
99 |
100 | &-presenter {
101 | display: flex;
102 | flex-direction: column;
103 | align-items: center;
104 | width: 300px;
105 | max-width: 100%;
106 | padding: 20px;
107 | border: 3px solid rgba($highlight, 0.2);
108 | margin: 20px;
109 | }
110 |
111 | &-presenterImage {
112 | height: 130px;
113 | border-radius: 100px;
114 | margin-bottom: 10px;
115 | }
116 |
117 | &-presenterName {
118 | font-weight: 400;
119 | margin-bottom: 5px;
120 | }
121 |
122 | &-presenterPresentationTitle {
123 | @include small-font;
124 | font-weight: 400;
125 | align-self: flex-start;
126 | margin-bottom: 5px;
127 | }
128 |
129 | &-presenterDescription {
130 | @include small-font;
131 | }
132 |
133 | &-mapWrapper {
134 | position: relative;
135 | height: 450px;
136 | width: 100%;
137 | }
138 |
139 | &-mapNote {
140 | text-align: center;
141 | margin-bottom: 10px;
142 | @include small-font();
143 | }
144 | }
145 |
146 | .ctaBlock {
147 | position: relative;
148 | color: $body-light;
149 |
150 | @include respond-to("medium") {
151 | height: 300px;
152 | @include medium-font();
153 | }
154 |
155 | &-pattern {
156 | display: flex;
157 | position: relative;
158 |
159 | @include respond-to("medium") {
160 | position: absolute;
161 | align-items: flex-end;
162 | transition: box-shadow $transition-duration cubic-bezier(0.4, 0, 0.3, 1);
163 | }
164 |
165 | &--first {
166 | background-color: $highlight-secondary;
167 |
168 | @include respond-to("medium") {
169 | justify-content: flex-end;
170 | top: 0;
171 | left: 0;
172 | width: 50%;
173 | height: 100%;
174 | }
175 | }
176 | &--second {
177 | background-color: $background-medium;
178 |
179 | @include respond-to("medium") {
180 | bottom: 0;
181 | right: 0;
182 | width: 50%;
183 | height: 100%;
184 | }
185 | }
186 |
187 | &:hover {
188 | z-index: 10;
189 | box-shadow: 0 4px 15px 5px rgba(0, 0, 0, 0.25);
190 | }
191 | }
192 |
193 | &-container {
194 | height: 100%;
195 | position: relative;
196 | }
197 |
198 | &-cta {
199 | display: flex;
200 | flex-direction: column;
201 | justify-content: flex-end;
202 | padding: 40px;
203 | height: 200px;
204 |
205 | @include respond-to("medium") {
206 | position: absolute;
207 | height: unset;
208 | width: 100vw / 2;
209 | }
210 |
211 | @include respond-to("large") {
212 | width: $container-width / 2;
213 | }
214 | }
215 |
216 | &-ctaHeading {
217 | @include large-font();
218 | }
219 |
220 | &-ctaDescription {
221 | @include medium-font();
222 |
223 | @include respond-to("medium") {
224 | margin-bottom: 40px;
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/src/styles/index.js:
--------------------------------------------------------------------------------
1 | import "./generic.scss";
2 | import "./reset.scss";
3 |
--------------------------------------------------------------------------------
/src/styles/meetup.scss:
--------------------------------------------------------------------------------
1 | @import "./variables.scss";
2 | @import "./mixins.scss";
3 |
4 | .pastMeetups {
5 | &-container {
6 | padding: $section-padding-mobile 20px;
7 | display: flex;
8 | flex-direction: column;
9 | margin-bottom: -40px;
10 |
11 | @include respond-to("medium") {
12 | padding: $section-padding 20px;
13 | }
14 | }
15 |
16 | &-title {
17 | @include xlarge-font();
18 | @include title-link-border();
19 | margin-bottom: 20px;
20 | text-align: center;
21 | align-self: center;
22 |
23 | @include respond-to("medium") {
24 | margin-bottom: 40px;
25 | }
26 | }
27 |
28 | &-description {
29 | @include medium-font();
30 | width: 900px;
31 | max-width: 100%;
32 | margin: 0 auto 20px;
33 | }
34 |
35 | &-meetup {
36 | margin: 40px 0;
37 | }
38 | }
39 |
40 | .meetup {
41 | border: 3px solid rgba($highlight, 0.2);
42 | padding: 10px;
43 | @include medium-font();
44 |
45 | @include respond-to("medium") {
46 | padding: 40px;
47 | flex-direction: row;
48 | }
49 |
50 | &-title {
51 | @include large-font();
52 | text-align: center;
53 | margin-bottom: 20px;
54 | }
55 |
56 | &-label {
57 | font-weight: 400;
58 | }
59 |
60 | &-meta {
61 | display: flex;
62 | flex-direction: column;
63 | justify-content: center;
64 | margin-bottom: 30px;
65 |
66 | @include respond-to("medium") {
67 | flex-direction: row;
68 | }
69 | }
70 |
71 | &-metaField {
72 | margin: 0 20px;
73 | text-align: center;
74 |
75 | &--date {
76 | margin-bottom: 10px;
77 |
78 | @include respond-to("medium") {
79 | margin-bottom: 0;
80 | }
81 | }
82 | }
83 |
84 | &-presenters {
85 | display: flex;
86 | flex-wrap: wrap;
87 | justify-content: center;
88 | margin-bottom: -10px;
89 |
90 | @include respond-to("medium") {
91 | margin-bottom: 0x;
92 | margin: 0 -20px;
93 | }
94 | }
95 |
96 | &-presenter {
97 | display: flex;
98 | align-items: flex-start;
99 | padding-bottom: 30px;
100 |
101 | @include respond-to("medium") {
102 | flex-basis: 50%;
103 | padding: 0 20px;
104 | }
105 | }
106 |
107 | &-presenterImageContainer {
108 | display: flex;
109 | flex-direction: column;
110 | align-items: center;
111 | flex-shrink: 0;
112 | margin-right: 20px;
113 | flex-basis: 100px;
114 |
115 | @include respond-to("medium") {
116 | margin-right: 20px;
117 | }
118 | }
119 |
120 | &-presenterInfo {
121 | display: flex;
122 | flex-direction: column;
123 | align-items: flex-start;
124 | }
125 |
126 | &-presenterTitle {
127 | font-weight: 400;
128 | margin-bottom: 10px;
129 | }
130 |
131 | &-presenterImage {
132 | height: 100px;
133 | border-radius: calc(100px / 2);
134 | margin-bottom: 10px;
135 |
136 | @include respond-to("medium") {
137 | height: 100px;
138 | border-radius: calc(100px / 2);
139 | margin-bottom: 20px;
140 | }
141 | }
142 |
143 | &-presenterName {
144 | @include small-font();
145 | font-weight: 400;
146 | text-align: center;
147 | word-break: break-word;
148 | }
149 |
150 | &-presenterText {
151 | @include small-font();
152 | }
153 |
154 | &-presenterLinks {
155 | margin-top: 5px;
156 | }
157 |
158 | &-presenterLinkItem {
159 | margin-bottom: 5px;
160 | }
161 |
162 | &-presenterLink {
163 | @include small-font();
164 | @include link-border();
165 | font-weight: 400;
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/styles/mixins.scss:
--------------------------------------------------------------------------------
1 | @import "./variables.scss";
2 |
3 | $breakpoints: (
4 | "small": 767px,
5 | "medium": 992px,
6 | "large": 1200px,
7 | ) !default;
8 |
9 | @mixin respond-to($breakpoint) {
10 | // If the key exists in the map
11 | @if map-has-key($breakpoints, $breakpoint) {
12 | // Prints a media query based on the value
13 | @media (min-width: map-get($breakpoints, $breakpoint)) {
14 | @content;
15 | }
16 | }
17 |
18 | // If the key doesn't exist in the map
19 | @else {
20 | @warn "Unfortunately, no value could be retrieved from `#{$breakpoint}`. "
21 | + "Available breakpoints are: #{map-keys($breakpoints)}.";
22 | }
23 | }
24 |
25 | @mixin small-font() {
26 | font-weight: 300;
27 | font-size: 0.9rem;
28 | letter-spacing: 0.4px;
29 | line-height: 1.4rem;
30 |
31 | @include respond-to("small") {
32 | font-size: 1rem;
33 | letter-spacing: 0.5px;
34 | line-height: 1.5rem;
35 | }
36 | }
37 |
38 | @mixin medium-font() {
39 | font-weight: 300;
40 | font-size: 1rem;
41 | letter-spacing: 0.5px;
42 | line-height: 1.5rem;
43 |
44 | @include respond-to("small") {
45 | font-size: 1.25rem;
46 | letter-spacing: 1px;
47 | line-height: 1.75rem;
48 | }
49 | }
50 |
51 | @mixin large-font() {
52 | font-weight: 400;
53 | font-size: 1.75rem;
54 | letter-spacing: 1px;
55 | line-height: 3rem;
56 |
57 | @include respond-to("small") {
58 | font-size: 2.25rem;
59 | letter-spacing: 1.2px;
60 | line-height: 3.75rem;
61 | }
62 | }
63 |
64 | @mixin xlarge-font() {
65 | font-weight: 400;
66 | font-size: 2rem;
67 | letter-spacing: 0.5px;
68 | line-height: 3rem;
69 |
70 | @include respond-to("small") {
71 | font-size: 2.5rem;
72 | letter-spacing: 1px;
73 | line-height: 3.5rem;
74 | }
75 |
76 | @include respond-to("medium") {
77 | font-size: 3rem;
78 | line-height: 4rem;
79 | }
80 | }
81 |
82 | @mixin link-border() {
83 | border-bottom: 2px solid $highlight;
84 | transition: all $transition-duration ease-in-out;
85 |
86 | &:hover {
87 | background-color: $highlight;
88 | }
89 | }
90 |
91 | @mixin title-link-border() {
92 | border-bottom: 3px solid $highlight;
93 | }
94 |
--------------------------------------------------------------------------------
/src/styles/past-meetups-page.scss:
--------------------------------------------------------------------------------
1 | @import "./variables.scss";
2 | @import "./mixins.scss";
3 |
4 | .pastMeetups {
5 | &-container {
6 | padding: $section-padding-mobile 20px;
7 | display: flex;
8 | flex-direction: column;
9 | margin-bottom: -40px;
10 |
11 | @include respond-to("medium") {
12 | padding: $section-padding 20px;
13 | }
14 | }
15 |
16 | &-title {
17 | @include xlarge-font();
18 | @include title-link-border();
19 | margin-bottom: 20px;
20 | text-align: center;
21 | align-self: center;
22 |
23 | @include respond-to("medium") {
24 | margin-bottom: 40px;
25 | }
26 | }
27 |
28 | &-description {
29 | @include medium-font();
30 | width: 900px;
31 | max-width: 100%;
32 | margin: 0 auto 20px;
33 | }
34 |
35 | &-meetup {
36 | margin: 20px 0;
37 |
38 | @include respond-to("medium") {
39 | margin-bottom: 40px;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/styles/reset.scss:
--------------------------------------------------------------------------------
1 | /*! minireset.css v0.0.3 | MIT License | github.com/jgthms/minireset.css */
2 | // Blocks
3 | html,
4 | body,
5 | p,
6 | ol,
7 | ul,
8 | li,
9 | dl,
10 | dt,
11 | dd,
12 | blockquote,
13 | figure,
14 | fieldset,
15 | legend,
16 | textarea,
17 | pre,
18 | iframe,
19 | hr,
20 | h1,
21 | h2,
22 | h3,
23 | h4,
24 | h5,
25 | h6 {
26 | margin: 0;
27 | padding: 0;
28 | }
29 |
30 | // Headings
31 | h1,
32 | h2,
33 | h3,
34 | h4,
35 | h5,
36 | h6 {
37 | font-weight: normal;
38 | font-size: 100%;
39 | }
40 |
41 | // List
42 | ul {
43 | list-style: none;
44 | }
45 |
46 | // Form
47 | button,
48 | input,
49 | select,
50 | textarea {
51 | margin: 0;
52 | }
53 |
54 | // Box sizing
55 | html {
56 | box-sizing: border-box;
57 | }
58 |
59 | * {
60 | &,
61 | &::before,
62 | &::after {
63 | box-sizing: inherit;
64 | }
65 | }
66 |
67 | // Media
68 | img,
69 | audio,
70 | video {
71 | height: auto;
72 | max-width: 100%;
73 | }
74 |
75 | // Iframe
76 | iframe {
77 | border: 0;
78 | }
79 |
80 | // Table
81 | table {
82 | border-collapse: collapse;
83 | border-spacing: 0;
84 | }
85 |
86 | td,
87 | th {
88 | padding: 0;
89 | text-align: left;
90 | }
91 |
--------------------------------------------------------------------------------
/src/styles/variables.scss:
--------------------------------------------------------------------------------
1 | $white: #fff;
2 | $american-silver: #cecece;
3 | $sambucus: #1b1d2e;
4 | $black-blueberry: #2d3047;
5 | $coronation: #ececec;
6 | $golden-apricot: #e0a458;
7 | $greenway: #419d78;
8 | $snowy-mountain: #f2efea;
9 |
10 | $highlight: $golden-apricot;
11 | $highlight-secondary: $greenway;
12 |
13 | $background-light: $snowy-mountain;
14 | $background-dark: $sambucus;
15 | $background-medium: $black-blueberry;
16 |
17 | $menu-active: $white;
18 | $menu-inactive: $american-silver;
19 |
20 | $body-light: $coronation;
21 | $body-dark: $black-blueberry;
22 |
23 | $navbar-height: 50px;
24 | $footer-height: 500px;
25 | $container-padding: 20px;
26 | $section-padding: 80px;
27 | $section-padding-mobile: 40px;
28 | $container-width: 1200px;
29 | $transition-duration: 250ms;
30 |
--------------------------------------------------------------------------------
/src/templates/about-page.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { graphql } from "gatsby";
4 | import ReactMarkdown from "react-markdown";
5 | import Helmet from "react-helmet";
6 |
7 | import Layout from "../components/Layout";
8 | import HTMLContent from "../components/Content";
9 | import "../styles/about-page.scss";
10 |
11 | export const AboutPageTemplate = props => {
12 | const { page } = props;
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
{page.frontmatter.title}
20 |
21 |
22 |
23 |
24 |
25 |
26 | {/* The page.html is actually markdown when viewing the page from the netlify CMS,
27 | so we must use the ReactMarkdown component to parse the markdown in that case */}
28 | {page.bodyIsMarkdown ? (
29 |
30 | ) : (
31 |
32 | )}
33 |
34 | {page.frontmatter.gallery.map((galleryImage, index) => (
35 |
36 |
37 |
38 | ))}
39 |
40 |
41 |
42 |
47 |
48 |
49 |
{page.frontmatter.organizers.title}
50 |
51 | {page.frontmatter.organizers.gallery.map((galleryImage, index) => (
52 |
53 |
58 | {galleryImage.name}
59 |
60 | ))}
61 |
62 |
63 |
64 |
65 | );
66 | };
67 |
68 | const AboutPage = ({ data }) => {
69 | const { markdownRemark: page, footerData, navbarData } = data;
70 | const {
71 | frontmatter: {
72 | seo: { title: seoTitle, description: seoDescription, browserTitle },
73 | },
74 | } = page;
75 |
76 | return (
77 |
78 |
79 |
80 |
81 | {browserTitle}
82 |
83 |
84 |
85 | );
86 | };
87 |
88 | AboutPage.propTypes = {
89 | data: PropTypes.object.isRequired,
90 | };
91 |
92 | export default AboutPage;
93 |
94 | export const aboutPageQuery = graphql`
95 | query AboutPage($id: String!) {
96 | markdownRemark(id: { eq: $id }) {
97 | html
98 | frontmatter {
99 | title
100 | mainImage {
101 | image
102 | imageAlt
103 | }
104 | gallery {
105 | image
106 | imageAlt
107 | }
108 | developerGroups
109 | organizers {
110 | title
111 | gallery {
112 | image
113 | imageAlt
114 | name
115 | }
116 | }
117 | seo {
118 | browserTitle
119 | title
120 | description
121 | }
122 | }
123 | }
124 | ...LayoutFragment
125 | }
126 | `;
127 |
--------------------------------------------------------------------------------
/src/templates/meetup.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 | import "../styles/meetup.scss";
4 |
5 | import HeadshotPlaceholder from "../img/headshot-placeholder.svg";
6 |
7 | class MeetupTemplate extends Component {
8 | render() {
9 | return (
10 |
14 | {this.props.meetup.title}
15 |
16 |
17 | Date: {this.props.meetup.formattedDate}
18 |
19 |
20 | Location: {this.props.meetup.location.name}
21 |
22 |
23 |
24 | {this.props.meetup.presenters.map(presenter => (
25 |
26 |
27 |
32 |
{presenter.name}
33 |
34 |
35 | {presenter.presentationTitle && (
36 |
{presenter.presentationTitle}
37 | )}
38 |
{presenter.text}
39 |
49 |
50 |
51 | ))}
52 |
53 |
54 | );
55 | }
56 | }
57 |
58 | MeetupTemplate.propTypes = {
59 | meetup: PropTypes.shape({
60 | title: PropTypes.string,
61 | name: PropTypes.string,
62 | presenters: PropTypes.array,
63 | }),
64 | };
65 |
66 | export default MeetupTemplate;
67 |
--------------------------------------------------------------------------------
/src/templates/past-meetups-page.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { graphql } from "gatsby";
4 | import Helmet from "react-helmet";
5 | import isBefore from "date-fns/is_before";
6 | import ReactMarkdown from "react-markdown";
7 |
8 | import MeetupTemplate from "./meetup";
9 | import Layout from "../components/Layout";
10 | import HTMLContent from "../components/Content";
11 | import "../styles/past-meetups-page.scss";
12 |
13 | export const PastMeetupsPageTemplate = ({
14 | title,
15 | content,
16 | meetups = null,
17 | bodyIsMarkdown = false,
18 | }) => {
19 | return (
20 |
21 |
22 |
{title}
23 | {bodyIsMarkdown ? (
24 |
25 | ) : (
26 |
27 | )}
28 | {meetups &&
29 | meetups.map((meetup, index) => (
30 |
35 | ))}
36 |
37 |
38 | );
39 | };
40 |
41 | PastMeetupsPageTemplate.propTypes = {
42 | title: PropTypes.string.isRequired,
43 | content: PropTypes.string,
44 | meetups: PropTypes.array,
45 | };
46 |
47 | const PastMeetupsPage = ({ data }) => {
48 | const { markdownRemark: page } = data;
49 | const {
50 | frontmatter: {
51 | seo: { title: seoTitle, description: seoDescription, browserTitle },
52 | },
53 | } = page;
54 | let meetups = data.allMarkdownRemark.edges;
55 |
56 | // Find all the meetups that occured in the past
57 | meetups = meetups.filter(meetup => {
58 | return isBefore(meetup.node.frontmatter.rawDate, new Date()) && meetup;
59 | });
60 |
61 | return (
62 |
63 |
64 |
65 |
66 | {browserTitle}
67 |
68 |
73 |
74 | );
75 | };
76 |
77 | PastMeetupsPage.propTypes = {
78 | data: PropTypes.object.isRequired,
79 | };
80 |
81 | export default PastMeetupsPage;
82 |
83 | export const pastMeetupsPageQuery = graphql`
84 | query PastMeetupsPage($id: String!) {
85 | markdownRemark(id: { eq: $id }) {
86 | html
87 | frontmatter {
88 | title
89 | seo {
90 | browserTitle
91 | title
92 | description
93 | }
94 | }
95 | }
96 | ...LayoutFragment
97 | allMarkdownRemark(
98 | filter: { frontmatter: { presenters: { elemMatch: { text: { ne: null } } } } }
99 | sort: { order: DESC, fields: frontmatter___date }
100 | ) {
101 | edges {
102 | node {
103 | frontmatter {
104 | title
105 | formattedDate: date(formatString: "MMMM Do YYYY @ h:mm A")
106 | rawDate: date
107 | presenters {
108 | name
109 | image
110 | text
111 | presentationTitle
112 | links {
113 | linkText
114 | linkURL
115 | }
116 | }
117 | location {
118 | name
119 | }
120 | }
121 | }
122 | }
123 | }
124 | }
125 | `;
126 |
--------------------------------------------------------------------------------
/static/admin/config.yml:
--------------------------------------------------------------------------------
1 | backend:
2 | name: git-gateway
3 | branch: master
4 |
5 | media_folder: static/img
6 | public_folder: /img
7 |
8 | display_url: https://gatsby-netlify-cms-example.netlify.com/
9 |
10 | collections:
11 | - name: "meetups"
12 | label: "Meetups"
13 | description: "Meetup dates, location, and presenter information."
14 | folder: "src/pages/meetups"
15 | create: true
16 | fields:
17 | - { label: "Template Key", name: "templateKey", widget: "hidden", default: "meetup" }
18 | - { label: "Title", name: "title", widget: "string" }
19 | - { label: "Date", name: "date", widget: "datetime" }
20 | - {
21 | label: Presenters,
22 | name: presenters,
23 | required: true,
24 | widget: list,
25 | fields:
26 | [
27 | { label: Name, name: name, required: true, widget: string },
28 | {
29 | label: Presentation Title,
30 | name: presentationTitle,
31 | required: false,
32 | widget: string,
33 | },
34 | {
35 | label: Image,
36 | name: image,
37 | required: false,
38 | hint: "If an image isn't specified, a default headshot placeholder image will be used",
39 | widget: image,
40 | },
41 | { label: Text, name: text, required: true, widget: text },
42 | {
43 | label: Links,
44 | name: links,
45 | required: false,
46 | widget: list,
47 | fields:
48 | [
49 | { label: Text, name: linkText, widget: string },
50 | { label: URL, name: linkURL, widget: string },
51 | ],
52 | },
53 | ],
54 | }
55 | - {
56 | label: "Location",
57 | name: "location",
58 | required: true,
59 | widget: "object",
60 | fields:
61 | [
62 | { label: "Name", name: "name", widget: "string" },
63 | { label: "Google Maps Link", name: "mapsLink", widget: "string" },
64 | { label: "Latitude", name: "mapsLatitude", widget: "number", valueType: "float" },
65 | { label: "Longitude", name: "mapsLongitude", widget: "number", valueType: "float" },
66 | ],
67 | }
68 | - name: "pages"
69 | label: "Pages"
70 | files:
71 | - file: "src/pages/about/index.md"
72 | label: "About"
73 | name: "about"
74 | fields:
75 | - { label: "Template Key", name: "templateKey", widget: "hidden", default: "about-page" }
76 | - { label: "Title", name: "title", widget: "string" }
77 | - {
78 | label: "Main Image",
79 | name: "mainImage",
80 | widget: "object",
81 | fields:
82 | [
83 | { label: "Image", name: "image", widget: "image" },
84 | { label: "Image Description", name: "imageAlt", widget: "string" },
85 | ],
86 | }
87 | - { label: "Body", name: "body", widget: "markdown" }
88 | - {
89 | label: "Image Gallery",
90 | name: "gallery",
91 | widget: "list",
92 | fields:
93 | [
94 | { label: "Image", name: "image", widget: "image" },
95 | { label: "Image Description", name: "imageAlt", widget: "string" },
96 | ],
97 | }
98 | - { label: "Developer Groups", name: "developerGroups", widget: "markdown" }
99 | - {
100 | label: "Meetup Organizers",
101 | name: "organizers",
102 | widget: "object",
103 | fields:
104 | [
105 | { label: "Title", name: "title", widget: "string" },
106 | {
107 | label: "Images",
108 | name: "gallery",
109 | widget: "list",
110 | fields:
111 | [
112 | { label: "Name", name: "name", widget: "string" },
113 | { label: "Image", name: "image", widget: "image" },
114 | { label: "Image Description", name: "imageAlt", widget: "string" },
115 | ],
116 | },
117 | ],
118 | }
119 | - {
120 | label: "SEO & Meta",
121 | name: "seo",
122 | widget: "object",
123 | fields:
124 | [
125 | { label: "Browser Tab Title", name: "browserTitle", widget: "string"},
126 | { label: "Title", name: "title", widget: "string" },
127 | { label: "Description", name: "description", widget: "string" },
128 | ],
129 | }
130 | - file: "src/pages/pastMeetups/index.md"
131 | label: "Past Meetups"
132 | name: "pastMeetups"
133 | fields:
134 | - {
135 | label: "Template Key",
136 | name: "templateKey",
137 | widget: "hidden",
138 | default: "past-meetups-page",
139 | }
140 | - { label: "Title", name: "title", widget: "string" }
141 | - { label: "Body", name: "body", widget: "markdown" }
142 | - {
143 | label: "SEO & Meta",
144 | name: "seo",
145 | widget: "object",
146 | fields:
147 | [
148 | { label: "Browser Tab Title", name: "browserTitle", widget: "string"},
149 | { label: "Title", name: "title", widget: "string" },
150 | { label: "Description", name: "description", widget: "string" },
151 | ],
152 | }
153 | - file: "src/pages/home/index.md"
154 | label: "Home"
155 | name: "home"
156 | fields:
157 | - { label: "Template Key", name: "templateKey", widget: "hidden", default: "home-page" }
158 | - {
159 | label: "Header Image",
160 | name: "headerImage",
161 | widget: "object",
162 | fields:
163 | [
164 | { label: "Image", name: "image", widget: "image" },
165 | { label: "Image Description", name: "imageAlt", widget: "string" },
166 | ],
167 | }
168 | - { label: "Title", name: "title", widget: "string" }
169 | - { label: "Upcoming Meetup Heading", name: "upcomingMeetupHeading", widget: "string" }
170 | - { label: "No Upcoming Meetup Text", name: "noUpcomingMeetupText", hint: "This text will be displayed when there are no upcoming meetups", required: true, widget: "string" }
171 | - { label: "Maps Note", name: "mapsNote", required: true, widget: "string" }
172 | - {
173 | label: "Call to Actions",
174 | name: "callToActions",
175 | required: true,
176 | widget: "object",
177 | fields:
178 | [
179 | {
180 | label: "First CTA",
181 | name: "firstCTA",
182 | required: true,
183 | widget: "object",
184 | fields:
185 | [
186 | { label: "Heading", name: "heading", widget: "string" },
187 | { label: "Sub Heading", name: "subHeading", widget: "string" },
188 | {
189 | label: "Link Type",
190 | name: "linkType",
191 | widget: "select",
192 | options: ["internal", "external"],
193 | },
194 | {
195 | label: "Link URL",
196 | name: "linkURL",
197 | widget: "string",
198 | hint: "Use a relative URL (e.g. /about) if the link is an internal link.",
199 | }
200 | ]
201 | },
202 | {
203 | label: "Second CTA",
204 | name: "secondCTA",
205 | required: true,
206 | widget: "object",
207 | fields:
208 | [
209 | { label: "Heading", name: "heading", widget: "string" },
210 | { label: "Sub Heading", name: "subHeading", widget: "string" },
211 | {
212 | label: "Link Type",
213 | name: "linkType",
214 | widget: "select",
215 | options: ["internal", "external"],
216 | },
217 | {
218 | label: "Link URL",
219 | name: "linkURL",
220 | widget: "string",
221 | hint: "Use a relative URL (e.g. /about) if the link is an internal link.",
222 | }
223 | ]
224 | }
225 | ],
226 | }
227 | - {
228 | label: "SEO & Meta",
229 | name: "seo",
230 | widget: "object",
231 | fields:
232 | [
233 | { label: "Browser Tab Title", name: "browserTitle", widget: "string"},
234 | { label: "Title", name: "title", widget: "string" },
235 | { label: "Description", name: "description", widget: "string" },
236 | ],
237 | }
238 | - name: "navbarAndFooter"
239 | label: "Navbar & Footer"
240 | files:
241 | - file: "src/pages/navbar/index.md"
242 | label: "Navbar"
243 | name: "navbar"
244 | fields:
245 | - { label: "Template Key", name: "templateKey", widget: "hidden", default: "navbar" }
246 | - {
247 | label: "Menu Items",
248 | name: "menuItems",
249 | widget: "list",
250 | fields:
251 | [
252 | { label: "Label", name: "label", widget: "string" },
253 | { label: "Link Type", name: "linkType", widget: "select", options: ["internal", "external"] },
254 | { label: "Link URL", name: "linkURL", widget: "string", hint: "Use a relative URL (e.g. /about) if the link is an internal link." },
255 | ],
256 | }
257 | - file: "src/pages/footer/index.md"
258 | label: "Footer"
259 | name: "footer"
260 | fields:
261 | - { label: "Template Key", name: "templateKey", widget: "hidden", default: "footer" }
262 | - {
263 | label: "Logo Image & Tagline",
264 | name: "logoImage",
265 | widget: "object",
266 | fields:
267 | [
268 | { label: "Image", name: "image", widget: "image" },
269 | { label: "Image Description", name: "imageAlt", widget: "string" },
270 | { label: "Tagline", name: "tagline", widget: "string" },
271 | ],
272 | }
273 | - {
274 | label: "Social Links",
275 | name: "socialLinks",
276 | widget: "list",
277 | fields:
278 | [
279 | { label: "Image", name: "image", widget: "image" },
280 | { label: "Image Description", name: "imageAlt", widget: "string" },
281 | { label: "Label", name: "label", widget: "string" },
282 | { label: "Link URL", name: "linkURL", widget: "string" },
283 | ],
284 | }
--------------------------------------------------------------------------------
/static/img/annie-spratt-608001-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/annie-spratt-608001-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/benjamin-parker-736167-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/benjamin-parker-736167-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/edward-cisneros-415601-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/edward-cisneros-415601-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/email.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/img/facebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
9 |
12 |
22 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/static/img/humphrey-muleba-795250-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/humphrey-muleba-795250-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/jakob-dalbjorn-730178-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/jakob-dalbjorn-730178-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/jonas-kakaroto-577554-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/jonas-kakaroto-577554-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/js-wakanda.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/js-wakanda.png
--------------------------------------------------------------------------------
/static/img/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/static/img/lucas-sankey-378674-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/lucas-sankey-378674-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/marius-ciocirlan-398931-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/marius-ciocirlan-398931-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/meetup.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
12 |
21 |
51 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
80 |
81 |
83 |
85 |
87 |
89 |
91 |
94 |
96 |
98 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/static/img/michael-dam-258165-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/michael-dam-258165-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/neonbrand-509131-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/neonbrand-509131-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/organizer-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/organizer-1.jpg
--------------------------------------------------------------------------------
/static/img/organizer-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/organizer-2.jpg
--------------------------------------------------------------------------------
/static/img/ramy-kabalan-796973-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/ramy-kabalan-796973-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/teemu-paananen-376238-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/teemu-paananen-376238-unsplash.jpg
--------------------------------------------------------------------------------
/static/img/television.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertcoopercode/gatsby-netlify-cms/c9609693887ec2fb986a2d52fc6f94d8716b1cb5/static/img/television.png
--------------------------------------------------------------------------------
/static/img/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
13 |
40 |
42 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------