├── netlify.toml ├── .prettierrc ├── public ├── images │ ├── 1.jpg │ ├── 10.jpg │ ├── 11.jpg │ ├── 12.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── 7.jpg │ ├── 8.jpg │ ├── 9.jpg │ ├── about.jpg │ ├── dolar.png │ ├── hero.png │ ├── intro.jpg │ ├── 10_thumb.jpg │ ├── 11_thumb.jpg │ ├── 12_thumb.jpg │ ├── 1_thumb.jpg │ ├── 2_thumb.jpg │ ├── 3_thumb.jpg │ ├── 4_thumb.jpg │ ├── 5_thumb.jpg │ ├── 6_thumb.jpg │ ├── 7_thumb.jpg │ ├── 8_thumb.jpg │ ├── 9_thumb.jpg │ ├── author.jpg │ ├── avatar.png │ ├── favicon.png │ ├── feature1.png │ ├── feature2.png │ ├── feature3.png │ ├── review1.jpg │ ├── review2.jpg │ ├── review3.jpg │ ├── logo.svg │ └── logo-alt.svg └── js │ └── plugins.js ├── src ├── sass │ ├── imports │ │ ├── _menus.scss │ │ ├── _functions.scss │ │ ├── _banner.scss │ │ ├── _custom.scss │ │ ├── _tables.scss │ │ ├── _buttons.scss │ │ ├── _structure.scss │ │ ├── _palettes.scss │ │ ├── _helpers.scss │ │ ├── _variables.scss │ │ ├── _forms.scss │ │ ├── _icons.scss │ │ ├── _posts.scss │ │ ├── _footer.scss │ │ ├── _reset.scss │ │ ├── _general.scss │ │ └── _header.scss │ └── main.scss ├── utils │ ├── attribute.js │ ├── pathJoin.js │ ├── markdownify.js │ ├── getPageUrl.js │ ├── classNames.js │ ├── getData.js │ ├── withPrefix.js │ ├── index.js │ ├── getPage.js │ ├── link.js │ └── htmlToReact.js ├── pages │ ├── _app.js │ ├── [[...slug]].js │ └── _document.js ├── layouts │ ├── index.js │ ├── landing.js │ ├── page.js │ ├── post.js │ └── blog.js └── components │ ├── CtaButtons.js │ ├── ActionLink.js │ ├── FooterNav.js │ ├── BlogPostFooter.js │ ├── FooterText.js │ ├── SectionCta.js │ ├── Action.js │ ├── SectionHero.js │ ├── index.js │ ├── SectionContent.js │ ├── SectionReviews.js │ ├── FooterForm.js │ ├── SectionFaq.js │ ├── SectionPricing.js │ ├── FormField.js │ ├── SectionPosts.js │ ├── SectionFeatures.js │ ├── SectionContact.js │ ├── Footer.js │ ├── Layout.js │ ├── Header.js │ └── Icon.js ├── content ├── data │ ├── authors │ │ ├── john-doe.yaml │ │ └── jane-doe.yaml │ └── config.json └── pages │ ├── blog │ ├── index.md │ ├── design-team-collaborates.md │ ├── design-azimuth.md │ ├── azimuth-improvements.md │ ├── customer-service-skills-that-every-employee-needs.md │ ├── sales-as-service.md │ ├── user-journey-mapping.md │ ├── set-big-goals.md │ ├── working-from-home.md │ └── customer-loyalty.md │ ├── terms-of-service.md │ ├── privacy-policy.md │ ├── contact.md │ ├── signup.md │ ├── features.md │ ├── about.md │ ├── pricing.md │ ├── index.md │ └── style-guide.md ├── MOCK_DATA.js ├── .eslintrc.json ├── next.config.js ├── README.md ├── package.json ├── .gitignore ├── sourcebit.js └── LICENSE.md /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "out" 3 | command = "npm run build" 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | tabWidth: 4 2 | singleQuote: true 3 | trailingComma: none 4 | printWidth: 160 5 | -------------------------------------------------------------------------------- /public/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/1.jpg -------------------------------------------------------------------------------- /public/images/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/10.jpg -------------------------------------------------------------------------------- /public/images/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/11.jpg -------------------------------------------------------------------------------- /public/images/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/12.jpg -------------------------------------------------------------------------------- /public/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/2.jpg -------------------------------------------------------------------------------- /public/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/3.jpg -------------------------------------------------------------------------------- /public/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/4.jpg -------------------------------------------------------------------------------- /public/images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/5.jpg -------------------------------------------------------------------------------- /public/images/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/6.jpg -------------------------------------------------------------------------------- /public/images/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/7.jpg -------------------------------------------------------------------------------- /public/images/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/8.jpg -------------------------------------------------------------------------------- /public/images/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/9.jpg -------------------------------------------------------------------------------- /public/images/about.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/about.jpg -------------------------------------------------------------------------------- /public/images/dolar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/dolar.png -------------------------------------------------------------------------------- /public/images/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/hero.png -------------------------------------------------------------------------------- /public/images/intro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/intro.jpg -------------------------------------------------------------------------------- /public/images/10_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/10_thumb.jpg -------------------------------------------------------------------------------- /public/images/11_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/11_thumb.jpg -------------------------------------------------------------------------------- /public/images/12_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/12_thumb.jpg -------------------------------------------------------------------------------- /public/images/1_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/1_thumb.jpg -------------------------------------------------------------------------------- /public/images/2_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/2_thumb.jpg -------------------------------------------------------------------------------- /public/images/3_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/3_thumb.jpg -------------------------------------------------------------------------------- /public/images/4_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/4_thumb.jpg -------------------------------------------------------------------------------- /public/images/5_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/5_thumb.jpg -------------------------------------------------------------------------------- /public/images/6_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/6_thumb.jpg -------------------------------------------------------------------------------- /public/images/7_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/7_thumb.jpg -------------------------------------------------------------------------------- /public/images/8_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/8_thumb.jpg -------------------------------------------------------------------------------- /public/images/9_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/9_thumb.jpg -------------------------------------------------------------------------------- /public/images/author.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/author.jpg -------------------------------------------------------------------------------- /public/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/avatar.png -------------------------------------------------------------------------------- /public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/favicon.png -------------------------------------------------------------------------------- /public/images/feature1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/feature1.png -------------------------------------------------------------------------------- /public/images/feature2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/feature2.png -------------------------------------------------------------------------------- /public/images/feature3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/feature3.png -------------------------------------------------------------------------------- /public/images/review1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/review1.jpg -------------------------------------------------------------------------------- /public/images/review2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/review2.jpg -------------------------------------------------------------------------------- /public/images/review3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Platzi-MasterJam/dinamita/HEAD/public/images/review3.jpg -------------------------------------------------------------------------------- /src/sass/imports/_menus.scss: -------------------------------------------------------------------------------- 1 | .menu { 2 | list-style: none; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | .menu-item { 8 | font-size: 0.88889rem; 9 | line-height: 1.5; 10 | } 11 | -------------------------------------------------------------------------------- /src/sass/imports/_functions.scss: -------------------------------------------------------------------------------- 1 | // Gets a value from a map. 2 | @function map-deep-get($map, $keys...) { 3 | @each $key in $keys { 4 | $map: map-get($map, $key); 5 | } 6 | @return $map; 7 | } -------------------------------------------------------------------------------- /src/utils/attribute.js: -------------------------------------------------------------------------------- 1 | export default function attribute(name, value, condition) { 2 | if (typeof condition === 'undefined') { 3 | condition = true; 4 | } 5 | return condition ? { [name]: value } : null; 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../sass/main.scss'; 2 | 3 | // This default export is required in a new `pages/_app.js` file. 4 | export default function MyApp({ Component, pageProps }) { 5 | return ; 6 | } 7 | -------------------------------------------------------------------------------- /src/sass/imports/_banner.scss: -------------------------------------------------------------------------------- 1 | body.has-theme-bar { 2 | #page { 3 | top: 60px; 4 | 5 | .site-header { 6 | @media only screen and (min-width: 801px) { 7 | top: 60px; 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/pathJoin.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | export default function pathJoin(...pathParts) { 4 | const result = _.compact(pathParts) 5 | .join('/') 6 | .replace(/\/{2,}/g, '/'); 7 | return result || '.'; 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/markdownify.js: -------------------------------------------------------------------------------- 1 | import marked from 'marked'; 2 | import htmlToReact from './htmlToReact'; 3 | 4 | export default function markdownify(markdown) { 5 | if (!markdown) { 6 | return null; 7 | } 8 | return htmlToReact(marked(markdown)); 9 | } 10 | -------------------------------------------------------------------------------- /src/layouts/index.js: -------------------------------------------------------------------------------- 1 | import landing from './landing'; 2 | import page from './page'; 3 | import blog from './blog'; 4 | import post from './post'; 5 | 6 | export { landing, page, blog, post }; 7 | 8 | export default { 9 | landing, 10 | page, 11 | blog, 12 | post 13 | }; 14 | -------------------------------------------------------------------------------- /src/utils/getPageUrl.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import withPrefix from './withPrefix'; 3 | 4 | export default function getPageUrl(post, { withPrefix: addPrefix = false } = {}) { 5 | const urlPath = _.get(post, '__metadata.urlPath'); 6 | return addPrefix ? withPrefix(urlPath) : urlPath; 7 | } 8 | -------------------------------------------------------------------------------- /content/data/authors/john-doe.yaml: -------------------------------------------------------------------------------- 1 | first_name: John 2 | last_name: Doe 3 | bio: >- 4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 5 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 6 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 7 | photo: images/review1.jpg 8 | -------------------------------------------------------------------------------- /src/utils/classNames.js: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | 3 | // A simple wrapper around classNames to return null, if no classes were generated 4 | // Otherwise, original classNames returns empty string which causes class="" to be generated 5 | export default function classNames(...args) { 6 | return classnames.call(this, ...args) || null; 7 | } 8 | -------------------------------------------------------------------------------- /MOCK_DATA.js: -------------------------------------------------------------------------------- 1 | export const json = [ 2 | { 3 | "material":1, 4 | "valor":8 5 | }, 6 | { 7 | "material":2, 8 | "valor":50 9 | }, 10 | { 11 | "material":3, 12 | "valor":0.6 13 | }, 14 | { 15 | "material":4, 16 | "valor":9 17 | } 18 | ] -------------------------------------------------------------------------------- /content/data/authors/jane-doe.yaml: -------------------------------------------------------------------------------- 1 | first_name: Jane 2 | last_name: Doe 3 | bio: >- 4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 5 | incididunt ut labore et dolore magna aliqua. Risus nullam eget felis eget nunc 6 | lobortis. Sodales ut etiam sit amet nisl purus in mollis nunc. Enim ut tellus 7 | elementum sagittis vitae. 8 | photo: images/review2.jpg 9 | -------------------------------------------------------------------------------- /src/components/CtaButtons.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | import Action from './Action'; 5 | 6 | export default class CtaButtons extends React.Component { 7 | render() { 8 | const actions = _.get(this.props, 'actions'); 9 | return _.map(actions, (action, actionIdx) => ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/sass/imports/_custom.scss: -------------------------------------------------------------------------------- 1 | .featureCustom{ 2 | &_space{ 3 | display: flex!important; 4 | &-img{ 5 | width: 50%; 6 | } 7 | &-value{ 8 | margin: auto 0px; 9 | font-size: 140px; 10 | color: #2c2f3b; 11 | font-weight: bolder; 12 | & p { 13 | margin: auto 0px; 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/utils/getData.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | export default function getData(props, dataPath) { 4 | dataPath = _.trim(dataPath, '/'); 5 | if (_.startsWith(dataPath, 'content/data/')) { 6 | dataPath = dataPath.replace('content/data/', ''); 7 | } 8 | // remove extension 9 | dataPath = dataPath.replace(/\.\w+$/, ''); 10 | const path = dataPath.split('/'); 11 | return _.get(props, path); 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/withPrefix.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const pathPrefix = require('../../content/data/config.json').path_prefix; 3 | 4 | export default function withPrefix(url) { 5 | if (!url) { 6 | return url; 7 | } 8 | 9 | if (_.startsWith(url, '#') || _.startsWith(url, 'http://') || _.startsWith(url, 'https://')) { 10 | return url; 11 | } 12 | const basePath = _.trim(pathPrefix, '/'); 13 | return '/' + _.compact([basePath, _.trimStart(url, '/')]).join('/'); 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | export { default as attribute } from './attribute'; 2 | export { default as classNames } from './classNames'; 3 | export { default as getPage } from './getPage'; 4 | export { default as getData } from './getData'; 5 | export { default as htmlToReact } from './htmlToReact'; 6 | export { default as getPageUrl } from './getPageUrl'; 7 | export { default as pathJoin } from './pathJoin'; 8 | export { default as markdownify } from './markdownify'; 9 | export { default as withPrefix } from './withPrefix'; 10 | export { default as Link } from './link'; 11 | -------------------------------------------------------------------------------- /src/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import "imports/functions"; 2 | @import "imports/variables"; 3 | @import "imports/reset"; 4 | @import "imports/general"; 5 | @import "imports/helpers"; 6 | @import "imports/tables"; 7 | @import "imports/forms"; 8 | @import "imports/buttons"; 9 | @import "imports/icons"; 10 | @import "imports/menus"; 11 | @import "imports/structure"; 12 | @import "imports/header"; 13 | @import "imports/posts"; 14 | @import "imports/sections"; 15 | @import "imports/footer"; 16 | @import "imports/palettes"; 17 | @import "imports/banner"; 18 | @import "imports/custom" 19 | -------------------------------------------------------------------------------- /src/utils/getPage.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | /** 4 | * Get the page at the provided `urlPath`. 5 | * 6 | * @param {Array} pages Array of page objects. All pages must have '__metadata.urlPath' field. 7 | * @param {string} urlPath The url path to find the page by 8 | * @return {Object} 9 | */ 10 | export default function getPage(pages, urlPath) { 11 | urlPath = _.trim(urlPath, '/'); 12 | return _.find(pages, (page) => { 13 | const pageUrlPath = _.trim(_.get(page, '__metadata.urlPath'), '/'); 14 | return urlPath === pageUrlPath; 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/link.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import NextLink from 'next/link'; 3 | 4 | export default function Link({ children, href, ...other }) { 5 | // Pass Any internal link to Next.js Link, for anything else, use tag 6 | const internal = /^\/(?!\/)/.test(href); 7 | if (internal) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | 15 | return ( 16 | 17 | {children} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node" : true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "ecmaVersion": 2018 9 | }, 10 | "rules": { 11 | "indent": [ 12 | "error", 13 | 4 14 | ], 15 | "linebreak-style": [ 16 | "error", 17 | "unix" 18 | ], 19 | "quotes": [ 20 | "error", 21 | "single" 22 | ], 23 | "semi": [ 24 | "error", 25 | "always" 26 | ], 27 | "no-unused-vars": 0 28 | } 29 | } -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const sourcebit = require('sourcebit'); 2 | 3 | const sourcebitConfig = require('./sourcebit.js'); 4 | 5 | sourcebit.fetch(sourcebitConfig); 6 | 7 | module.exports = { 8 | trailingSlash: true, 9 | devIndicators: { 10 | autoPrerender: false 11 | }, 12 | webpack: (config, { webpack }) => { 13 | // Tell webpack to ignore watching content files in the content folder. 14 | // Otherwise webpack receompiles the app and refreshes the whole page. 15 | // Instead, the src/pages/[...slug].js uses the "withRemoteDataUpdates" 16 | // function to update the content on the page without refreshing the 17 | // whole page 18 | config.plugins.push(new webpack.WatchIgnorePlugin([[/\/content\//]])); 19 | return config; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /content/pages/blog/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Blog 3 | seo: 4 | title: Blog 5 | description: This is the blog page 6 | extra: 7 | - name: 'og:type' 8 | value: website 9 | keyName: property 10 | - name: 'og:title' 11 | value: Blog 12 | keyName: property 13 | - name: 'og:description' 14 | value: This is the blog page 15 | keyName: property 16 | - name: 'og:image' 17 | value: images/1.jpg 18 | keyName: property 19 | relativeUrl: true 20 | - name: 'twitter:card' 21 | value: summary_large_image 22 | - name: 'twitter:title' 23 | value: Blog 24 | - name: 'twitter:description' 25 | value: This is the blog page 26 | - name: 'twitter:image' 27 | value: images/1.jpg 28 | relativeUrl: true 29 | layout: blog 30 | --- 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dinamita 💥💥 2 | Project About environment for Platzi MasterJam 3 | 4 | 5 | ## About 6 | 7 | The app is the best for collecting waste in big cities. You as a citizen can earn money saving the world. 8 | 9 | ## How does it work? 10 | 11 | We have people for each city, who will pick up waste or recyclables for you if you need to help with this. You just need to come to our page and request a service. 12 | We pay you for the items. We have warehouses and to make the best recycling 13 | 14 | 15 | ## How run this app 16 | 17 | ### Local Enviroment 18 | 1. execute npm install 19 | 2. writting down npm run dev 20 | 21 | If you want to get online access go to: 22 | https://e-collect.netlify.app/ 23 | ## Stack's project in Frontend 24 | - Jamstack 25 | - Git 26 | - SASS 27 | - Next.js 28 | - netlify 29 | ## Stack's project in Backend 30 | - Node.js 31 | - Express.js 32 | - AWS 33 | - PostgreSQL 34 | - BABEL 35 | -------------------------------------------------------------------------------- /src/components/ActionLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | import { Link, withPrefix } from '../utils'; 5 | 6 | export default class ActionLink extends React.Component { 7 | render() { 8 | const action = _.get(this.props, 'action'); 9 | const url = _.get(action, 'url'); 10 | const label = _.get(action, 'label', null); 11 | const newWindow = _.get(action, 'new_window'); 12 | const noFollow = _.get(action, 'no_follow'); 13 | const attrs = {}; 14 | if (newWindow) { 15 | attrs.target = '_blank'; 16 | } 17 | if (newWindow || noFollow) { 18 | attrs.rel = [(newWindow ? 'noopener' : '') + (noFollow ? 'nofollow' : '')].join(' '); 19 | } 20 | 21 | return ( 22 | 23 | {label} 24 | 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/sass/imports/_tables.scss: -------------------------------------------------------------------------------- 1 | table { 2 | border-bottom: 1px solid $gray-200; 3 | border-collapse: collapse; 4 | border-spacing: 0; 5 | line-height: 1.5; 6 | margin: 0; 7 | max-width: 100%; 8 | text-align: left; 9 | width: 100%; 10 | } 11 | 12 | caption { 13 | color: $gray-500; 14 | font-size: 0.83333em; 15 | font-style: normal; 16 | margin-bottom: 1em; 17 | text-align: left; 18 | } 19 | 20 | th, 21 | td { 22 | border-top: 1px solid $gray-200; 23 | padding: 0.5em 5px; 24 | } 25 | 26 | th { 27 | color: $gray-700; 28 | font-weight: bold; 29 | 30 | .font-fira-sans & { 31 | font-weight: 600; 32 | } 33 | } 34 | 35 | :not(.responsive-table) > table { 36 | margin: 1.66667em 0; 37 | 38 | &:first-child { 39 | margin-top: 0; 40 | } 41 | } 42 | 43 | .responsive-table { 44 | display: block; 45 | margin: 1.66667em 0; 46 | overflow-x: auto; 47 | width: 100%; 48 | 49 | &:first-child { 50 | margin-top: 0; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/sass/imports/_buttons.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | -ms-flex-align: center; 3 | align-items: center; 4 | background: $color-primary; 5 | border: 2px solid $color-primary; 6 | border-radius: $border-radius; 7 | box-sizing: border-box; 8 | color: #fff; 9 | cursor: pointer; 10 | display: -ms-inline-flexbox; 11 | display: inline-flex; 12 | font-size: 0.88889em; 13 | font-weight: bold; 14 | -ms-flex-pack: center; 15 | justify-content: center; 16 | line-height: 1.5; 17 | padding: 0.5em 1.875em; 18 | text-decoration: none; 19 | -webkit-transition: opacity .15s ease-in-out; 20 | transition: opacity .15s ease-in-out; 21 | 22 | .font-fira-sans & { 23 | font-weight: 600; 24 | } 25 | 26 | &:hover, 27 | &:focus, 28 | &:active { 29 | color: #fff; 30 | opacity: 0.8; 31 | outline: 0; 32 | } 33 | 34 | &.large { 35 | padding: 0.625em 2.5em; 36 | } 37 | 38 | &.secondary { 39 | background: transparent; 40 | color: $color-primary; 41 | } 42 | } -------------------------------------------------------------------------------- /src/components/FooterNav.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | import Action from './Action'; 5 | 6 | export default class FooterNav extends React.Component { 7 | render() { 8 | const section = _.get(this.props, 'section'); 9 | const title = _.get(section, 'title'); 10 | const navLinks = _.get(section, 'nav_links'); 11 | 12 | return ( 13 |
14 | {title &&

{title}

} 15 | {navLinks && ( 16 |
    17 | {_.map(navLinks, (action, actionIdx) => ( 18 |
  • 19 | 20 |
  • 21 | ))} 22 |
23 | )} 24 |
25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azimuth-nextjs", 3 | "description": "A Stackbit Azimuth theme built with Next.js", 4 | "version": "0.1.0", 5 | "license": "MIT", 6 | "scripts": { 7 | "dev": "next dev", 8 | "develop": "next dev", 9 | "fetch": "sourcebit fetch", 10 | "build": "next build && next export", 11 | "start": "next start" 12 | }, 13 | "dependencies": { 14 | "babel-runtime": "^6.26.0", 15 | "classnames": "^2.2.6", 16 | "lodash": "^4.17.21", 17 | "marked": "^2.0.0", 18 | "moment": "^2.29.1", 19 | "moment-strftime": "^0.5.0", 20 | "next": "^10.0.7", 21 | "node-sass": "^5.0.0", 22 | "react": "17.0.1", 23 | "react-dom": "17.0.1", 24 | "react-helmet": "^6.1.0", 25 | "react-html-parser": "^2.0.2", 26 | "react-script-tag": "^1.1.2", 27 | "sourcebit": "^0.11.0", 28 | "sourcebit-source-filesystem": "^0.1.3", 29 | "sourcebit-target-next": "^0.6.2" 30 | }, 31 | "devDependencies": { 32 | "prettier": "^2.2.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/sass/imports/_structure.scss: -------------------------------------------------------------------------------- 1 | .site { 2 | display: -ms-flexbox; 3 | display: flex; 4 | -ms-flex-direction: column; 5 | flex-direction: column; 6 | min-height: 100vh; 7 | position: relative; 8 | } 9 | 10 | .site-content { 11 | box-sizing: border-box; 12 | -ms-flex-positive: 1; 13 | flex-grow: 1; 14 | width: 100%; 15 | } 16 | 17 | .outer { 18 | padding: 3.33333em $container-padding 2.5em; 19 | } 20 | 21 | @media only screen and (min-width: 601px) { 22 | .outer { 23 | padding-bottom: 3.33333em; 24 | padding-top: 4.16667em; 25 | } 26 | } 27 | 28 | .inner { 29 | margin-left: auto; 30 | margin-right: auto; 31 | max-width: $container; 32 | } 33 | 34 | .inner-large { 35 | margin-left: auto; 36 | margin-right: auto; 37 | max-width: $container-lg; 38 | } 39 | 40 | .inner-medium { 41 | margin-left: auto; 42 | margin-right: auto; 43 | max-width: $container-md; 44 | } 45 | 46 | .inner-small { 47 | margin-left: auto; 48 | margin-right: auto; 49 | max-width: $container-sm; 50 | } -------------------------------------------------------------------------------- /src/components/BlogPostFooter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import moment from 'moment-strftime'; 4 | 5 | import { getData } from '../utils'; 6 | 7 | export default class BlogPostFooter extends React.Component { 8 | render() { 9 | const post = _.get(this.props, 'post'); 10 | const dateType = _.get(this.props, 'dateType'); 11 | const data = _.get(this.props, 'data'); 12 | const date = _.get(post, 'date'); 13 | const dateTimeAttr = moment(date).strftime('%Y-%m-%d %H:%M'); 14 | const formattedDate = dateType === 'short' ? moment(date).strftime('%B %d, %Y') : moment(date).strftime('%A, %B %e, %Y'); 15 | const postAuthorRef = _.get(post, 'author'); 16 | const author = postAuthorRef ? getData(data, postAuthorRef) : null; 17 | const authorName = author ? _.trim(`${author.first_name} ${author.last_name}`) : null; 18 | 19 | return ( 20 |
21 | 24 | {authorName && `, by ${authorName}`} 25 |
26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/pages/[[...slug]].js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import { sourcebitDataClient } from 'sourcebit-target-next'; 4 | import { withRemoteDataUpdates } from 'sourcebit-target-next/with-remote-data-updates'; 5 | 6 | import pageLayouts from '../layouts'; 7 | 8 | class Page extends React.Component { 9 | render() { 10 | const modelName = _.get(this.props, 'page.__metadata.modelName'); 11 | const PageLayout = pageLayouts[modelName]; 12 | if (!PageLayout) { 13 | throw new Error(`no page layout matching the page model: ${modelName}`); 14 | } 15 | return ; 16 | } 17 | } 18 | 19 | export async function getStaticPaths() { 20 | console.log('Page [...slug].js getStaticPaths'); 21 | const paths = await sourcebitDataClient.getStaticPaths(); 22 | return { paths, fallback: false }; 23 | } 24 | 25 | export async function getStaticProps({ params }) { 26 | console.log('Page [...slug].js getStaticProps, params: ', params); 27 | const pagePath = '/' + (params.slug ? params.slug.join('/') : ''); 28 | const props = await sourcebitDataClient.getStaticPropsForPageAtPath(pagePath); 29 | return { props }; 30 | } 31 | 32 | export default withRemoteDataUpdates(Page); 33 | -------------------------------------------------------------------------------- /src/components/FooterText.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | import { Link, withPrefix, markdownify } from '../utils'; 5 | 6 | export default class FooterText extends React.Component { 7 | render() { 8 | const section = _.get(this.props, 'section'); 9 | const image = _.get(section, 'image'); 10 | const imageUrl = _.get(section, 'image_url'); 11 | const imageAlt = _.get(section, 'image_alt'); 12 | const title = _.get(section, 'title'); 13 | const content = _.get(section, 'content'); 14 | 15 | return ( 16 |
17 | {image && 18 | (imageUrl ? ( 19 | 20 | {imageAlt} 21 | 22 | ) : ( 23 |

24 | {imageAlt} 25 |

26 | ))} 27 | {title &&

{title}

} 28 | {markdownify(content)} 29 |
30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/sass/imports/_palettes.scss: -------------------------------------------------------------------------------- 1 | // Color palettes 2 | @each $palette in map-keys($theme-palettes) { 3 | $palette-suffix: "#{$palette}"; 4 | $color-primary: map-deep-get($theme-palettes, $palette, "primary"); 5 | $color-secondary: map-deep-get($theme-palettes, $palette, "secondary"); 6 | 7 | .palette-#{$palette-suffix} { 8 | a:not(.button) { 9 | color: $color-primary; 10 | 11 | &:hover { 12 | color: $gray-600; 13 | } 14 | } 15 | 16 | blockquote, 17 | .underline:after, 18 | .card.highlight { 19 | border-color: $color-primary; 20 | } 21 | 22 | .button { 23 | background: $color-primary; 24 | border-color: $color-primary; 25 | 26 | &.secondary { 27 | background: transparent; 28 | color: $color-primary; 29 | } 30 | } 31 | 32 | .icon-plus { 33 | background: $color-primary; 34 | } 35 | 36 | .review-text { 37 | &:before { 38 | color: $color-primary; 39 | } 40 | } 41 | 42 | .bg-accent { 43 | background: $color-primary; 44 | background: -webkit-gradient(linear, left top, right top, from($color-primary), to($color-secondary)); 45 | background: linear-gradient(to right,$color-primary, $color-secondary); 46 | 47 | .button { 48 | color: $color-primary; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/components/SectionCta.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | import { htmlToReact } from '../utils'; 5 | import CtaButtons from './CtaButtons'; 6 | 7 | export default class SectionCta extends React.Component { 8 | render() { 9 | const section = _.get(this.props, 'section'); 10 | const sectionId = _.get(section, 'section_id'); 11 | const title = _.get(section, 'title'); 12 | const subtitle = _.get(section, 'subtitle'); 13 | const actions = _.get(section, 'actions'); 14 | 15 | return ( 16 |
17 |
18 |
19 |
20 | {title &&

{title}

} 21 | {subtitle &&

{htmlToReact(subtitle)}

} 22 |
23 | {actions && ( 24 |
25 | 26 |
27 | )} 28 |
29 |
30 |
31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/layouts/landing.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | import components, { Layout } from '../components/index'; 5 | import { getPageUrl } from '../utils'; 6 | 7 | export default class Landing extends React.Component { 8 | render() { 9 | const config = _.get(this.props, 'data.config'); 10 | const page = _.get(this.props, 'page'); 11 | const data = _.get(this.props, 'data'); 12 | const posts = _.get(this.props, 'posts'); 13 | const sections = _.get(page, 'sections'); 14 | const pageUrl = getPageUrl(page); 15 | 16 | return ( 17 | 18 | {_.map(sections, (section, index) => { 19 | const sectionType = _.get(section, 'type'); 20 | const component = _.upperFirst(_.camelCase(sectionType)); 21 | if (!component) { 22 | throw new Error(`page section does not have the 'type' property, page: ${pageUrl}`); 23 | } 24 | const Component = components[component]; 25 | if (!Component) { 26 | throw new Error(`no component matching the page section's type: ${sectionType}`); 27 | } 28 | return ; 29 | })} 30 | 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components/Action.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | import { Link, withPrefix, classNames } from '../utils'; 5 | import Icon from './Icon'; 6 | 7 | export default class Action extends React.Component { 8 | render() { 9 | const action = _.get(this.props, 'action'); 10 | const url = _.get(action, 'url'); 11 | const label = _.get(action, 'label'); 12 | const actionStyle = _.get(action, 'style', 'link'); 13 | const hasIcon = _.get(action, 'has_icon'); 14 | const actionIcon = _.get(action, 'icon', 'arrow-left'); 15 | const actionIconPos = _.get(action, 'icon_position', 'left'); 16 | const newWindow = _.get(action, 'new_window'); 17 | const noFollow = _.get(action, 'no_follow'); 18 | const attrs = {}; 19 | if (newWindow) { 20 | attrs.target = '_blank'; 21 | } 22 | if (newWindow || noFollow) { 23 | attrs.rel = [(newWindow ? 'noopener' : '') + (noFollow ? 'nofollow' : '')].join(' '); 24 | } 25 | 26 | return ( 27 | 36 | {hasIcon && } 37 | {label} 38 | 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/utils/htmlToReact.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactHtmlParser, { convertNodeToElement } from 'react-html-parser'; 3 | import ScriptTag from 'react-script-tag'; 4 | import Link from './link'; 5 | import _ from 'lodash'; 6 | 7 | const convertChildren = (children, index) => _.map(children, (childNode) => convertNodeToElement(childNode, index, _.noop())); 8 | 9 | export default function htmlToReact(html) { 10 | if (!html) { 11 | return null; 12 | } 13 | return ReactHtmlParser(html, { 14 | transform: (node, index) => { 15 | if (node.type === 'script') { 16 | if (!_.isEmpty(node.children)) { 17 | return ( 18 | 19 | {convertChildren(node.children, index)} 20 | 21 | ); 22 | } else { 23 | return ; 24 | } 25 | } else if (node.type === 'tag' && node.name === 'a') { 26 | const href = node.attribs.href; 27 | const props = _.omit(node.attribs, 'href'); 28 | // use Link only if there are no custom attributes like style, class, and what's not that might break react 29 | if (_.isEmpty(props)) { 30 | return ( 31 | 32 | {convertChildren(node.children, index)} 33 | 34 | ); 35 | } 36 | } 37 | } 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /src/sass/imports/_helpers.scss: -------------------------------------------------------------------------------- 1 | // Text for screen readers 2 | .screen-reader-text { 3 | clip: rect(1px, 1px, 1px, 1px); 4 | clip-path: polygon(0px 0px, 0px 0px, 0px 0px, 0px 0px); 5 | height: 1px; 6 | overflow: hidden; 7 | position: absolute !important; 8 | width: 1px; 9 | word-wrap: normal !important; 10 | } 11 | 12 | // Flex order 13 | .order-first { 14 | order: -1; 15 | } 16 | 17 | // Heading decorations 18 | .underline { 19 | position: relative; 20 | 21 | &:after { 22 | border-left: 1.5em solid $color-primary; 23 | display: block; 24 | content: ""; 25 | height: 2px; 26 | margin-top: 0.5em; 27 | } 28 | } 29 | 30 | // Responsive videos wrapper 31 | .js-reframe { 32 | margin: 0 0 1.66667em; 33 | } 34 | 35 | // Grid 36 | .grid { 37 | display: -ms-flexbox; 38 | display: flex; 39 | -ms-flex-wrap: wrap; 40 | flex-wrap: wrap; 41 | margin-left: -$grid-gap / 2; 42 | margin-right: -$grid-gap / 2; 43 | } 44 | 45 | .cell { 46 | box-sizing: border-box; 47 | padding-left: $grid-gap / 2; 48 | padding-right: $grid-gap / 2; 49 | position: relative; 50 | width: 100%; 51 | } 52 | 53 | @media only screen and (max-width: 750px) { 54 | .grid { 55 | margin-left: -$container-padding / 2; 56 | margin-right: -$container-padding / 2; 57 | } 58 | 59 | .cell { 60 | padding-left: $container-padding / 2; 61 | padding-right: $container-padding / 2; 62 | } 63 | } 64 | 65 | // Card 66 | .card { 67 | background: #fff; 68 | border: 1px solid $gray-200; 69 | border-radius: $border-radius; 70 | box-shadow: $box-shadow; 71 | box-sizing: border-box; 72 | 73 | &.highlight { 74 | border-color: $color-primary; 75 | border-width: 2px; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/layouts/page.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | import { Layout } from '../components/index'; 5 | import { htmlToReact, withPrefix, markdownify } from '../utils'; 6 | 7 | export default class Page extends React.Component { 8 | render() { 9 | const page = _.get(this.props, 'page'); 10 | const data = _.get(this.props, 'data'); 11 | const config = _.get(data, 'config'); 12 | const title = _.get(page, 'title'); 13 | const subtitle = _.get(page, 'subtitle'); 14 | const image = _.get(page, 'image'); 15 | const imageAlt = _.get(page, 'image_alt'); 16 | const markdownContent = _.get(page, 'markdown_content'); 17 | 18 | return ( 19 | 20 |
21 |
22 |
23 |
24 |

{title}

25 | {subtitle &&
{htmlToReact(subtitle)}
} 26 |
27 | {image && ( 28 |
29 | {imageAlt} 30 |
31 | )} 32 | {markdownContent &&
{markdownify(markdownContent)}
} 33 |
34 |
35 |
36 |
37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/SectionHero.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | import { withPrefix, markdownify } from '../utils'; 5 | import CtaButtons from './CtaButtons'; 6 | 7 | export default class SectionHero extends React.Component { 8 | render() { 9 | const section = _.get(this.props, 'section'); 10 | const sectionId = _.get(section, 'section_id'); 11 | const image = _.get(section, 'image'); 12 | const imageAlt = _.get(section, 'image_alt'); 13 | const title = _.get(section, 'title'); 14 | const content = _.get(section, 'content'); 15 | const actions = _.get(section, 'actions'); 16 | 17 | return ( 18 |
19 |
20 |
21 | {image && ( 22 |
23 | {imageAlt} 24 |
25 | )} 26 |
27 | {title &&

{title}

} 28 | {content &&
{markdownify(content)}
} 29 | {actions && ( 30 |
31 | 32 |
33 | )} 34 |
35 |
36 |
37 |
38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/sass/imports/_variables.scss: -------------------------------------------------------------------------------- 1 | // Fonts 2 | $font-monospace: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace !default; 3 | 4 | // Colors 5 | $color-primary: #0072ff !default; 6 | $color-secondary: #00c6ff !default; 7 | 8 | $gray-100: #f7f9fb !default; 9 | $gray-200: #ebecf0 !default; 10 | $gray-300: #c5c8d4 !default; 11 | $gray-400: #999fb3 !default; 12 | $gray-500: #7b839e !default; 13 | $gray-600: #4c5267 !default; 14 | $gray-700: #2c2f3b !default; 15 | 16 | $yellow-pale: #fcffc4 !default; 17 | 18 | // Border radius 19 | $border-radius: 3px; 20 | 21 | // Box shadow 22 | $box-shadow: 0 3px 10px 0 rgba($gray-600,.1) !default; 23 | $box-shadow-white-bg: 0 3px 10px 0 rgba($gray-600,.15) !default; 24 | $box-shadow-sm: 0 1px 4px 0 rgba($gray-600,.1) !default; 25 | 26 | // Container max width 27 | $container: 1140px !default; 28 | $container-sm: 555px !default; 29 | $container-md: 750px !default; 30 | $container-lg: 945px !default; 31 | 32 | // Container padding 33 | $container-padding: 4vw !default; 34 | 35 | // Grid gap 36 | $grid-gap: 1.66667rem !default; 37 | 38 | // Theme variations 39 | $theme-fonts: ( 40 | nunito-sans: ("Nunito Sans", sans-serif), 41 | fira-sans: ("Fira Sans", sans-serif), 42 | system-sans: (-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif) 43 | ); 44 | 45 | $theme-palettes: ( 46 | cyan: ( 47 | primary: #2cb1bc, 48 | secondary: #54d1db 49 | ), 50 | green: ( 51 | primary: #37b05d, 52 | secondary: #57cb85 53 | ), 54 | orange: ( 55 | primary: #fe8c00, 56 | secondary: #f83600 57 | ), 58 | purple: ( 59 | primary: #653cad, 60 | secondary: #8662c7 61 | ) 62 | ); 63 | -------------------------------------------------------------------------------- /src/pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from 'next/document'; 2 | import { Helmet } from 'react-helmet'; 3 | import { withPrefix } from '../utils'; 4 | 5 | export default class MyDocument extends Document { 6 | static async getInitialProps(ctx) { 7 | const initialProps = await Document.getInitialProps(ctx); 8 | // see https://github.com/nfl/react-helmet#server-usage for more information 9 | // 'head' was occupied by 'renderPage().head', we cannot use it 10 | return { ...initialProps, helmet: Helmet.renderStatic() }; 11 | } 12 | 13 | // should render on 14 | get helmetHtmlAttrComponents() { 15 | return this.props.helmet.htmlAttributes.toComponent(); 16 | } 17 | 18 | // should render on 19 | get helmetBodyAttrComponents() { 20 | return this.props.helmet.bodyAttributes.toComponent(); 21 | } 22 | 23 | // should render on 24 | get helmetHeadComponents() { 25 | return Object.keys(this.props.helmet) 26 | .filter((el) => el !== 'htmlAttributes' && el !== 'bodyAttributes') 27 | .map((el) => this.props.helmet[el].toComponent()); 28 | } 29 | 30 | render() { 31 | // if you don't like Helmet but you still want to set properties on body use this 32 | // const pageProps = _.get(this.props, '__NEXT_DATA__.props.pageProps'); 33 | return ( 34 | 35 | {this.helmetHeadComponents} 36 | 37 |
38 |