├── .env.example ├── .eslintrc.json ├── .gitignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── components ├── atoms │ ├── Alert.js │ ├── Button.js │ ├── CheckAdBlocker.js │ ├── Container.js │ ├── Currency.js │ ├── Date.js │ ├── Heading.js │ ├── Icon │ │ ├── Icon.js │ │ ├── iconList.js │ │ └── index.js │ ├── Label.js │ ├── Logo.js │ ├── Paragraph.js │ ├── Picture.js │ ├── Responsive.js │ └── Subtitle.js ├── layout │ └── GoogleAnalytics.js ├── molecules │ ├── Fieldset.js │ ├── Form.js │ ├── Group.js │ ├── Mate.js │ ├── Speaker.js │ └── Sponsor.js └── organisms │ ├── CheckoutForm.js │ ├── CheckoutResponse.js │ ├── CheckoutSummary.js │ ├── Conduct.js │ ├── Footer.js │ ├── Hero.js │ ├── Navbar.js │ ├── Newsletter.js │ ├── ScheduleBlock.js │ ├── ShoppingCartItem.js │ ├── SimpleNavbar.js │ ├── Speakers.js │ ├── Sponsors.js │ ├── Team.js │ └── What.js ├── config ├── client.js ├── keys.js ├── server.js └── setup.js ├── next.config.js ├── package.json ├── pages ├── _app.js ├── _document.js ├── api │ ├── checkout │ │ ├── confirmation.js │ │ └── process.js │ ├── coupons.js │ ├── orders.js │ ├── products.js │ ├── resources │ │ └── countries.js │ └── subscribe.js ├── index.js ├── schedule.js └── tickets │ ├── index.js │ └── response.js ├── services ├── checkout │ └── index.js ├── epayco │ └── index.js └── woocommerce │ └── index.js ├── static ├── favicons │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ └── site.webmanifest ├── fonts │ ├── Apercu Bold Italic.woff │ ├── Apercu Bold.woff │ ├── Apercu Italic.woff │ ├── Apercu Light Italic.woff │ ├── Apercu Light.woff │ ├── Apercu Medium Italic.woff │ ├── Apercu Medium.woff │ ├── Apercu Mono.woff │ └── Apercu_Regular.woff ├── icons │ ├── cross.svg │ ├── instagram.svg │ └── twitter.svg ├── images │ ├── code-of-conduct-bird.svg │ ├── css-blog-text.svg │ ├── css-lovers-text.svg │ ├── date-tribal-vector.svg │ ├── epayco-payment-methods.png │ ├── face-tribal-vector.svg │ ├── fish-tribal-vector.svg │ ├── fish-tribal-yellow.svg │ ├── frog-tribal-vector.svg │ ├── hero-tribal-bird-vector.svg │ ├── iguana-tribal-vector.svg │ ├── medellin-tribal-vector.svg │ ├── rutan-tribal-vector.svg │ ├── schedule-divider.svg │ ├── speakers │ │ ├── alena-nikolaeva.png │ │ ├── alex-ramirez.png │ │ ├── bramus-vandamme.png │ │ ├── carlos-azaustre.png │ │ ├── carmen-ansio.png │ │ ├── erifranck-nunez.png │ │ ├── evangelina-ferreira.png │ │ ├── facundo-corradini.png │ │ ├── jimena-castro.png │ │ ├── joan-leon.png │ │ ├── laura-gonzalez.png │ │ ├── leonidas-esteban.png │ │ ├── luis-gadea.png │ │ ├── marian-villa.png │ │ ├── next-speaker.png │ │ └── robin-dykema.png │ ├── sponsors-separator.svg │ ├── sponsors │ │ ├── acamica.svg │ │ ├── adevinta.svg │ │ ├── astound.svg │ │ ├── fec.png │ │ ├── gde.png │ │ ├── getonboard.svg │ │ ├── globant.svg │ │ ├── gorilla_logic.svg │ │ ├── ilogica.svg │ │ ├── monoku.svg │ │ ├── mozilla.svg │ │ ├── ruta_n.svg │ │ ├── uruit.png │ │ ├── wolox.png │ │ └── workia.png │ ├── team │ │ ├── agustina │ │ │ ├── agustina-avatar.jpg │ │ │ └── tribal.svg │ │ ├── andrea │ │ │ ├── andrea-avatar.jpg │ │ │ └── tribal.svg │ │ ├── andres │ │ │ ├── andres-avatar.jpg │ │ │ └── tribal.svg │ │ ├── estefa │ │ │ ├── estefa-avatar.jpg │ │ │ └── tribal.svg │ │ ├── guillermo │ │ │ ├── guillermo-avatar.jpg │ │ │ └── tribal.svg │ │ ├── henry │ │ │ ├── henry-avatar.jpg │ │ │ └── tribal.svg │ │ ├── johana │ │ │ ├── johana-avatar.jpg │ │ │ └── tribal.svg │ │ ├── jonathan │ │ │ ├── jonathan-avatar.jpg │ │ │ └── tribal.svg │ │ ├── juan │ │ │ ├── juan-avatar.jpg │ │ │ └── tribal.svg │ │ ├── luis │ │ │ ├── luis-avatar.jpg │ │ │ └── tribal.svg │ │ ├── luisa │ │ │ ├── luisa-avatar.jpg │ │ │ └── tribal.svg │ │ ├── manuela │ │ │ ├── manuela-avatar.jpg │ │ │ └── tribal.svg │ │ ├── maria │ │ │ ├── maria-avatar.jpg │ │ │ └── tribal.svg │ │ ├── paulina │ │ │ ├── paulina-avatar.jpg │ │ │ └── tribal.svg │ │ └── santiago │ │ │ ├── santiago-avatar.jpg │ │ │ └── tribal.svg │ ├── tribal-modal.svg │ └── venue-separator.svg └── logos │ └── css-conf-logo.svg ├── utils ├── analytics.js ├── cleanUrlQueryParams.js ├── constants.js ├── createSignature.js ├── designTokens.js ├── fetchJson.js ├── fontFaces.js ├── getCenteredStyles.js ├── getFlag.js ├── getImageProps.js ├── getTalkType.js ├── globalStyles.js ├── orderParams.js ├── orderStatusMapper.js ├── parseCheckoutResponse.js ├── parseResponse.js ├── redirectToHomePage.js ├── sanitizeString.js ├── scrollToTop.js ├── smoothScroll.js ├── validateEmail.js └── verifyOrderKey.js ├── vercel.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | # GOOGLE ANALYTICS 2 | GOOGLE_ANALYTICS_ID=UX-666666666-6 3 | 4 | # MAILCHIMP 5 | MAILCHIMP_API_KEY=46b2e2b9bd77f2a3bc7cdd089db541ec-us5 6 | MAILCHIMP_AUDIENCE_ID=985ee1577a 7 | 8 | # WORDPRESS 9 | WP_CONSUMER_KEY=ck_a30e03b3c45a52a72d1f723f23ae7e5c3bc7d53e 10 | WP_CONSUMER_SECRET=cs_819d7463bb78f1421f65c4d5070e65404f7e6413 11 | 12 | # EPAYCO 13 | EPAYCO_CLIENT_ID=66669 14 | EPAYCO_SECRET_KEY=e82a1582bf4f6040cac95eaaee29d2c22b13ee68 15 | 16 | # URLS 17 | WORDPRESS_URL=http://localhost:8080 18 | CLIENT_URL=http://localhost:3000 19 | API_URL=http://localhost:3000/api 20 | 21 | # EPAYCO 22 | EPAYCO_VALIDATE_URL=https://secure.epayco.co/validation/v1/reference 23 | EPAYCO_PUBLIC_KEY=ff6417eb22193473d5e64dda60c96080 24 | EPAYCO_ENV=development -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:react/recommended", 6 | "plugin:prettier/recommended" 7 | ], 8 | "env": { 9 | "browser": true, 10 | "es6": true, 11 | "jest": true, 12 | "node": true 13 | }, 14 | "rules": { 15 | "no-console": "warn", 16 | "react/react-in-jsx-scope": "off" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node,linux,macos,windows 3 | # Edit at https://www.gitignore.io/?templates=node,linux,macos,windows 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### macOS ### 21 | # General 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Thumbnails 27 | ._* 28 | 29 | # Files that might appear in the root of a volume 30 | .DocumentRevisions-V100 31 | .fseventsd 32 | .Spotlight-V100 33 | .TemporaryItems 34 | .Trashes 35 | .VolumeIcon.icns 36 | .com.apple.timemachine.donotpresent 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | ### Node ### 46 | # Logs 47 | logs 48 | *.log 49 | npm-debug.log* 50 | yarn-debug.log* 51 | yarn-error.log* 52 | lerna-debug.log* 53 | 54 | # Diagnostic reports (https://nodejs.org/api/report.html) 55 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 56 | 57 | # Runtime data 58 | pids 59 | *.pid 60 | *.seed 61 | *.pid.lock 62 | 63 | # Directory for instrumented libs generated by jscoverage/JSCover 64 | lib-cov 65 | 66 | # Coverage directory used by tools like istanbul 67 | coverage 68 | *.lcov 69 | 70 | # nyc test coverage 71 | .nyc_output 72 | 73 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 74 | .grunt 75 | 76 | # Bower dependency directory (https://bower.io/) 77 | bower_components 78 | 79 | # node-waf configuration 80 | .lock-wscript 81 | 82 | # Compiled binary addons (https://nodejs.org/api/addons.html) 83 | build/Release 84 | 85 | # Dependency directories 86 | node_modules/ 87 | jspm_packages/ 88 | 89 | # TypeScript v1 declaration files 90 | typings/ 91 | 92 | # TypeScript cache 93 | *.tsbuildinfo 94 | 95 | # Optional npm cache directory 96 | .npm 97 | 98 | # Optional eslint cache 99 | .eslintcache 100 | 101 | # Optional REPL history 102 | .node_repl_history 103 | 104 | # Output of 'npm pack' 105 | *.tgz 106 | 107 | # Yarn Integrity file 108 | .yarn-integrity 109 | 110 | # dotenv environment variables file 111 | .env 112 | .env.test 113 | .env.build 114 | 115 | # parcel-bundler cache (https://parceljs.org/) 116 | .cache 117 | 118 | # next.js build output 119 | .next 120 | 121 | # nuxt.js build output 122 | .nuxt 123 | 124 | # vuepress build output 125 | .vuepress/dist 126 | 127 | # Serverless directories 128 | .serverless/ 129 | 130 | # FuseBox cache 131 | .fusebox/ 132 | 133 | # DynamoDB Local files 134 | .dynamodb/ 135 | 136 | ### Windows ### 137 | # Windows thumbnail cache files 138 | Thumbs.db 139 | Thumbs.db:encryptable 140 | ehthumbs.db 141 | ehthumbs_vista.db 142 | 143 | # Dump file 144 | *.stackdump 145 | 146 | # Folder config file 147 | [Dd]esktop.ini 148 | 149 | # Recycle Bin used on file shares 150 | $RECYCLE.BIN/ 151 | 152 | # Windows Installer files 153 | *.cab 154 | *.msi 155 | *.msix 156 | *.msm 157 | *.msp 158 | 159 | # Windows shortcuts 160 | *.lnk 161 | 162 | #package-lock json 163 | package-lock.json 164 | 165 | # End of https://www.gitignore.io/api/node,linux,macos,windows 166 | 167 | 168 | .vercel -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cssconfco-website Web Performance Edition -------------------------------------------------------------------------------- /components/atoms/Alert.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import classNames from 'classnames' 3 | 4 | import { choices } from '../../utils/designTokens' 5 | 6 | const Alert = ({ children, type, isMarginLess, isTop, isCentered }) => ( 7 | <> 8 |
15 | {children} 16 |
17 | 51 | 52 | ) 53 | 54 | Alert.propTypes = { 55 | children: PropTypes.node.isRequired, 56 | type: PropTypes.string, 57 | isMarginLess: PropTypes.bool, 58 | isCentered: PropTypes.bool, 59 | isTop: PropTypes.bool 60 | } 61 | 62 | Alert.defaultProps = { 63 | type: 'info' 64 | } 65 | 66 | export default Alert 67 | -------------------------------------------------------------------------------- /components/atoms/Button.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | import { choices, decisions } from '../../utils/designTokens' 4 | 5 | const Button = ({ 6 | style, 7 | children, 8 | type, 9 | handleClick, 10 | withMargin, 11 | isDisabled, 12 | isLoading 13 | }) => { 14 | return ( 15 | <> 16 | 25 | 48 | 49 | ) 50 | } 51 | 52 | Button.propTypes = { 53 | children: PropTypes.node.isRequired, 54 | type: PropTypes.string, 55 | handleClick: PropTypes.func, 56 | withMargin: PropTypes.bool, 57 | isDisabled: PropTypes.bool, 58 | isLoading: PropTypes.bool, 59 | style: PropTypes.object 60 | } 61 | 62 | Button.defaultProps = { 63 | handleClick: () => {}, 64 | type: 'button' 65 | } 66 | 67 | export default Button 68 | -------------------------------------------------------------------------------- /components/atoms/CheckAdBlocker.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import PropTypes from 'prop-types' 3 | import fetchJson from '../../utils/fetchJson' 4 | 5 | import Paragraph from './Paragraph' 6 | import Alert from './Alert' 7 | 8 | import { links } from '../../utils/constants' 9 | 10 | const CheckAdBlocker = ({ children }) => { 11 | const [haveAdBlocker, setHaveAdBlocker] = useState(false) 12 | 13 | useEffect(() => { 14 | async function checkAdBlocker() { 15 | try { 16 | await fetchJson('https://api.ipify.org/?format=json') 17 | setHaveAdBlocker(false) 18 | } catch (error) { 19 | setHaveAdBlocker(true) 20 | } 21 | } 22 | 23 | checkAdBlocker() 24 | }, []) 25 | 26 | if (haveAdBlocker) { 27 | return ( 28 | 29 | 30 | In order to procced with the payment is neccesasary to{' '} 31 | disable the ad blocker temporarily and{' '} 32 | refresh the page.{' '} 33 | 38 | Why? 39 | 40 | 41 | 42 | ) 43 | } 44 | 45 | return children 46 | } 47 | 48 | CheckAdBlocker.propTypes = { 49 | children: PropTypes.node.isRequired 50 | } 51 | 52 | export default CheckAdBlocker 53 | -------------------------------------------------------------------------------- /components/atoms/Container.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | import { decisions } from '../../utils/designTokens' 4 | 5 | const Container = ({ children, size }) => { 6 | return ( 7 | <> 8 |
{children}
9 | 16 | 17 | ) 18 | } 19 | 20 | Container.propTypes = { 21 | children: PropTypes.node.isRequired, 22 | size: PropTypes.string 23 | } 24 | 25 | Container.defaultProps = { 26 | size: 'large' 27 | } 28 | 29 | export default Container 30 | -------------------------------------------------------------------------------- /components/atoms/Currency.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | const Currency = ({ children }) => { 4 | return ( 5 | <> 6 | {' '} 7 | 8 | {new Intl.NumberFormat('en-US', { 9 | style: 'currency', 10 | currency: 'USD' 11 | }).format(children)} 12 | 13 | 20 | 21 | ) 22 | } 23 | 24 | Currency.propTypes = { 25 | children: PropTypes.number 26 | } 27 | 28 | Currency.defaultProps = { 29 | children: 0 30 | } 31 | 32 | export default Currency 33 | -------------------------------------------------------------------------------- /components/atoms/Date.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import moment from 'moment' 3 | 4 | const Date = ({ children }) => 5 | moment(children) 6 | .locale('EN') 7 | .format('DD MMMM, YYYY') 8 | 9 | Date.propTypes = { 10 | children: PropTypes.string.required 11 | } 12 | 13 | export default Date 14 | -------------------------------------------------------------------------------- /components/atoms/Heading.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import classNames from 'classnames' 3 | 4 | import { choices, decisions } from '../../utils/designTokens' 5 | import { titleStyles } from '../../utils/globalStyles' 6 | 7 | const Heading = ({ 8 | children, 9 | size, 10 | color, 11 | isInverted, 12 | isDisabled, 13 | isInline, 14 | isCentered, 15 | withMargin 16 | }) => { 17 | return ( 18 | <> 19 |

29 | {children} 30 |

31 | 91 | 92 | ) 93 | } 94 | 95 | Heading.propTypes = { 96 | children: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), 97 | isInverted: PropTypes.bool, 98 | isDisabled: PropTypes.bool, 99 | isCentered: PropTypes.bool, 100 | isInline: PropTypes.bool, 101 | withMargin: PropTypes.bool, 102 | size: PropTypes.oneOf([1, 2, 3, 4]), 103 | color: PropTypes.oneOf(['blue', 'red', 'yellow']) 104 | } 105 | 106 | Heading.defaultProps = { 107 | size: 4 108 | } 109 | 110 | export default Heading 111 | -------------------------------------------------------------------------------- /components/atoms/Icon/Icon.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import classNames from 'classnames' 3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' 4 | import { decisions } from '../../../utils/designTokens' 5 | import iconList from './iconList' 6 | const Icon = ({ style, icon, size, color }) => { 7 | return ( 8 |
9 | 17 | 35 |
36 | ) 37 | } 38 | Icon.propTypes = { 39 | icon: PropTypes.string.isRequired, 40 | style: PropTypes.object, 41 | color: PropTypes.string, 42 | size: PropTypes.oneOf(['sm', 'md', 'lg']) 43 | } 44 | Icon.defaultProps = { 45 | size: 'sm' 46 | } 47 | export default Icon 48 | -------------------------------------------------------------------------------- /components/atoms/Icon/iconList.js: -------------------------------------------------------------------------------- 1 | import { 2 | faMoneyBillAlt, 3 | faReceipt, 4 | faFileInvoiceDollar 5 | } from '@fortawesome/free-solid-svg-icons' 6 | const icons = { 7 | money: faMoneyBillAlt, 8 | receipt: faReceipt, 9 | invoice: faFileInvoiceDollar 10 | } 11 | export default icons 12 | -------------------------------------------------------------------------------- /components/atoms/Icon/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Icon' 2 | -------------------------------------------------------------------------------- /components/atoms/Label.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | import { choices, decisions } from '../../utils/designTokens' 4 | 5 | const Label = ({ children, htmlFor }) => { 6 | return ( 7 | <> 8 | 11 | 22 | 23 | ) 24 | } 25 | 26 | Label.propTypes = { 27 | children: PropTypes.node.isRequired, 28 | htmlFor: PropTypes.string 29 | } 30 | 31 | export default Label 32 | -------------------------------------------------------------------------------- /components/atoms/Logo.js: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import PropTypes from 'prop-types' 3 | 4 | import { choices } from '../../utils/designTokens' 5 | import { links } from '../../utils/constants' 6 | 7 | const Logo = ({ width }) => ( 8 | 9 | 10 | 11 | 24 | 25 | 26 | ) 27 | 28 | Logo.propTypes = { 29 | width: PropTypes.number 30 | } 31 | 32 | Logo.defaultProps = { 33 | width: 150 34 | } 35 | 36 | export default Logo 37 | -------------------------------------------------------------------------------- /components/atoms/Paragraph.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import classNames from 'classnames' 3 | 4 | import { choices, decisions } from '../../utils/designTokens' 5 | import { paragraphStyles } from '../../utils/globalStyles' 6 | 7 | const Paragraph = ({ 8 | children, 9 | color, 10 | size, 11 | weight, 12 | isInverted, 13 | isInline, 14 | isCentered, 15 | isJustified, 16 | isFull, 17 | withMargin, 18 | style 19 | }) => { 20 | return ( 21 | <> 22 |

35 | {children} 36 |

37 | 97 | 98 | ) 99 | } 100 | 101 | Paragraph.propTypes = { 102 | children: PropTypes.node, 103 | isInverted: PropTypes.bool, 104 | isCentered: PropTypes.bool, 105 | isJustified: PropTypes.bool, 106 | isInline: PropTypes.bool, 107 | isFull: PropTypes.bool, 108 | withMargin: PropTypes.bool, 109 | size: PropTypes.oneOf(['xs', 'sm', 'md']), 110 | weight: PropTypes.oneOf(['normal', 'bold']), 111 | color: PropTypes.oneOf(['blue', 'red', 'yellow']), 112 | style: PropTypes.object 113 | } 114 | 115 | Paragraph.defaultProps = { 116 | size: 'md', 117 | weight: 'normal' 118 | } 119 | 120 | export default Paragraph 121 | -------------------------------------------------------------------------------- /components/atoms/Picture.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | import getCenteredStyles from '../../utils/getCenteredStyles' 4 | import { getImageUrlForLink, getImageAlt } from '../../utils/getImageProps' 5 | 6 | const Picture = ({ image, style, width, isInline, isCentered }) => { 7 | if (!image) { 8 | return null 9 | } 10 | 11 | return ( 12 | <> 13 | 21 | {getImageAlt(image)} 22 | 23 | 33 | 34 | ) 35 | } 36 | 37 | Picture.propTypes = { 38 | image: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), 39 | style: PropTypes.object, 40 | width: PropTypes.number, 41 | isInline: PropTypes.bool, 42 | isCentered: PropTypes.bool 43 | } 44 | 45 | export default Picture 46 | -------------------------------------------------------------------------------- /components/atoms/Responsive.js: -------------------------------------------------------------------------------- 1 | import ReactResponsive from 'react-responsive' 2 | 3 | import { choices } from '../../utils/designTokens' 4 | 5 | const maxXsScreen = Number(choices.screens.max.xs.replace('px', '')) 6 | const maxLgScreen = Number(choices.screens.max.lg.replace('px', '')) 7 | const minLgScreen = Number(choices.screens.lg.replace('px', '')) 8 | 9 | const XSmall = props => 10 | const Mobile = props => 11 | const Desktop = props => 12 | 13 | const Responsive = () => null 14 | 15 | Responsive.XSmall = XSmall 16 | Responsive.Mobile = Mobile 17 | Responsive.Desktop = Desktop 18 | 19 | export default Responsive 20 | -------------------------------------------------------------------------------- /components/atoms/Subtitle.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import classNames from 'classnames' 3 | 4 | import { choices, decisions } from '../../utils/designTokens' 5 | import { subtitleStyles } from '../../utils/globalStyles' 6 | 7 | const Subtitle = ({ 8 | children, 9 | size, 10 | color, 11 | isInverted, 12 | isInline, 13 | isCentered 14 | }) => { 15 | return ( 16 | <> 17 |

26 | {children} 27 |

28 | 71 | 72 | ) 73 | } 74 | 75 | Subtitle.propTypes = { 76 | children: PropTypes.string.isRequired, 77 | isInverted: PropTypes.bool, 78 | isCentered: PropTypes.bool, 79 | isAlt: PropTypes.bool, 80 | isInline: PropTypes.bool, 81 | size: PropTypes.oneOf([1, 2]), 82 | color: PropTypes.oneOf(['blue', 'red', 'yellow']) 83 | } 84 | 85 | Subtitle.defaultProps = { 86 | size: 2 87 | } 88 | 89 | export default Subtitle 90 | -------------------------------------------------------------------------------- /components/layout/GoogleAnalytics.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { initGA, logPageView } from '../../utils/analytics' 4 | import { config } from '../../config/client' 5 | 6 | class GoogleAnalytics extends Component { 7 | componentDidMount() { 8 | if (config.isProduction) { 9 | if (!window.GA_INITIALIZED) { 10 | initGA() 11 | window.GA_INITIALIZED = true 12 | } 13 | logPageView() 14 | } 15 | } 16 | 17 | render() { 18 | return <>{this.props.children} 19 | } 20 | } 21 | 22 | GoogleAnalytics.propTypes = { 23 | children: PropTypes.node.isRequired 24 | } 25 | 26 | export default GoogleAnalytics 27 | -------------------------------------------------------------------------------- /components/molecules/Fieldset.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import classNames from 'classnames' 3 | 4 | import Heading from '../atoms/Heading' 5 | import Subtitle from '../atoms/Subtitle' 6 | 7 | import { choices } from '../../utils/designTokens' 8 | 9 | const Fieldset = ({ title, subtitle, children }) => ( 10 | <> 11 |
14 |
15 | {title && ( 16 | 17 | {title} 18 | 19 | )} 20 | {subtitle && {subtitle}} 21 |
22 |
{children}
23 |
24 | 49 | 50 | ) 51 | 52 | Fieldset.propTypes = { 53 | children: PropTypes.node.isRequired, 54 | title: PropTypes.string, 55 | subtitle: PropTypes.string 56 | } 57 | 58 | export default Fieldset 59 | -------------------------------------------------------------------------------- /components/molecules/Form.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | import Group from './Group' 4 | import Fieldset from './Fieldset' 5 | 6 | const Form = ({ style, children, handleSubmit }) => ( 7 | <> 8 |
9 | {children} 10 |
11 | 17 | 18 | ) 19 | 20 | Form.propTypes = { 21 | children: PropTypes.node.isRequired, 22 | handleSubmit: PropTypes.func.isRequired, 23 | style: PropTypes.object 24 | } 25 | 26 | Form.Group = Group 27 | Form.Fieldset = Fieldset 28 | 29 | export default Form 30 | -------------------------------------------------------------------------------- /components/molecules/Group.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import classNames from 'classnames' 3 | 4 | import { choices, decisions } from '../../utils/designTokens' 5 | 6 | const Group = ({ children, style, isHalf, isCentered, isBlock }) => ( 7 | <> 8 |
16 | {children} 17 |
18 | 64 | 65 | ) 66 | 67 | Group.propTypes = { 68 | children: PropTypes.node.isRequired, 69 | style: PropTypes.object, 70 | isHalf: PropTypes.bool, 71 | isCentered: PropTypes.bool, 72 | isBlock: PropTypes.bool 73 | } 74 | 75 | export default Group 76 | -------------------------------------------------------------------------------- /components/molecules/Mate.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import classNames from 'classnames' 3 | import { choices, decisions } from '../../utils/designTokens' 4 | 5 | const Mate = ({ id, type, children, tribalPosition }) => { 6 | return ( 7 | <> 8 |
9 |
10 | {id} 15 |
20 |
21 | {children &&
{children}
} 22 |
23 | 149 | 150 | ) 151 | } 152 | 153 | Mate.propTypes = { 154 | id: PropTypes.string.isRequired, 155 | type: PropTypes.string.isRequired, 156 | children: PropTypes.node.isRequired, 157 | tribalPosition: PropTypes.string 158 | } 159 | 160 | export default Mate 161 | -------------------------------------------------------------------------------- /components/molecules/Speaker.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { choices, decisions } from '../../utils/designTokens' 4 | 5 | const Speaker = ({ image, onClick, children }) => { 6 | return ( 7 | 8 |
9 | {children &&
{children}
} 10 |
11 | 71 |
72 | ) 73 | } 74 | 75 | Speaker.propTypes = { 76 | onClick: PropTypes.func, 77 | children: PropTypes.node, 78 | image: PropTypes.string 79 | } 80 | 81 | export default Speaker 82 | -------------------------------------------------------------------------------- /components/molecules/Sponsor.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import Link from 'next/link' 3 | 4 | import Subtitle from '../atoms/Subtitle' 5 | import Picture from '../atoms/Picture' 6 | 7 | import { decisions, choices } from '../../utils/designTokens' 8 | 9 | const Sponsor = ({ sponsors, imgSize, title }) => { 10 | if (sponsors.length === 0) return <> 11 | 12 | return ( 13 | <> 14 |
15 | 16 | {title.toUpperCase()} 17 | 18 |
19 | {sponsors.map(sponsor => ( 20 | 21 | 26 | 27 | 28 | 29 | ))} 30 |
31 |
32 | 104 | 105 | ) 106 | } 107 | 108 | Sponsor.defaultProps = { 109 | sponsors: [], 110 | imgSize: 'sm', 111 | title: 'Gold' 112 | } 113 | 114 | Sponsor.propTypes = { 115 | sponsors: PropTypes.arrayOf( 116 | PropTypes.shape({ 117 | id: PropTypes.string.isRequired, 118 | name: PropTypes.string.isRequired, 119 | logo: PropTypes.string.isRequired 120 | }) 121 | ), 122 | imgSize: PropTypes.oneOf(['sm', 'md', 'lg', 'xl']), 123 | title: PropTypes.string 124 | } 125 | 126 | export default Sponsor 127 | -------------------------------------------------------------------------------- /components/organisms/Conduct.js: -------------------------------------------------------------------------------- 1 | import Container from '../atoms/Container' 2 | import Heading from '../atoms/Heading' 3 | import Paragraph from '../atoms/Paragraph' 4 | 5 | import { choices, decisions } from '../../utils/designTokens' 6 | import { links } from '../../utils/constants' 7 | 8 | const Conduct = () => { 9 | return ( 10 |
11 | 12 |
13 | 14 | Code of Conduct 15 | 16 | 17 | We want everyone to enjoy this conference to the fullest, to make 18 | each person feel safe and welcome. To achieve this, we have defined 19 | a{' '} 20 | 21 | 26 | Code of Conduct 27 | 28 | 29 | , an agreement in which you will take an active role. This agreement 30 | is simple: be kind to everyone at all times, regardless of gender, 31 | gender identity, age, sexual orientation, disability, physical 32 | appearance, race, ethnicity, nationality, religion, points of view 33 | or political experience. 34 | 35 |
36 |
37 |
38 | 92 |
93 | ) 94 | } 95 | 96 | export default Conduct 97 | -------------------------------------------------------------------------------- /components/organisms/Footer.js: -------------------------------------------------------------------------------- 1 | import Paragraph from '../atoms/Paragraph' 2 | import Container from '../atoms/Container' 3 | 4 | import { decisions, choices } from '../../utils/designTokens' 5 | import { links } from '../../utils/constants' 6 | 7 | const Footer = () => { 8 | return ( 9 |
10 | 11 | 36 | 58 | 59 | 112 |
113 | ) 114 | } 115 | 116 | export default Footer 117 | -------------------------------------------------------------------------------- /components/organisms/Hero.js: -------------------------------------------------------------------------------- 1 | import Subtitle from '../atoms/Subtitle' 2 | import Container from '../atoms/Container' 3 | 4 | import { decisions } from '../../utils/designTokens' 5 | 6 | const Hero = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 | 📆 April 10th, 2021 17 | 18 |
19 |
20 |
21 | 22 | 📹 Live Streaming 23 | 24 |
25 |
26 |
27 | 28 | 🌈 Open for Everyone 29 | 30 |
31 |
32 |
33 | 149 |
150 | ) 151 | } 152 | 153 | export default Hero 154 | -------------------------------------------------------------------------------- /components/organisms/ShoppingCartItem.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | import Picture from '../atoms/Picture' 4 | import Subtitle from '../atoms/Subtitle' 5 | import Paragraph from '../atoms/Paragraph' 6 | import Currency from '../atoms/Currency' 7 | import { choices } from '../../utils/designTokens' 8 | 9 | const getQuantitiesUnits = quantity => 10 | `${quantity} unit${quantity > 1 ? 's' : ''}` 11 | 12 | const ShoppingCartItem = ({ 13 | image, 14 | name, 15 | description, 16 | price, 17 | regularPrice, 18 | quantity 19 | }) => { 20 | return ( 21 |
22 |
23 | 24 |
25 |
26 | {name} 27 | 28 | 29 | {price !== regularPrice ? ( 30 | <> 31 | 38 | {Number(price)} 39 | 40 | {Number(regularPrice)} 41 | 42 | ) : ( 43 | {Number(regularPrice)} 44 | )} 45 | 46 |
47 | {getQuantitiesUnits(quantity)} 48 |
49 |
50 | 80 |
81 | ) 82 | } 83 | 84 | ShoppingCartItem.propTypes = { 85 | image: PropTypes.object.isRequired, 86 | name: PropTypes.string.isRequired, 87 | price: PropTypes.string.isRequired, 88 | regularPrice: PropTypes.string.isRequired, 89 | quantity: PropTypes.number.isRequired, 90 | description: PropTypes.string 91 | } 92 | 93 | export default ShoppingCartItem 94 | -------------------------------------------------------------------------------- /components/organisms/SimpleNavbar.js: -------------------------------------------------------------------------------- 1 | import Logo from '../atoms/Logo' 2 | import Heading from '../atoms/Heading' 3 | 4 | import { choices, decisions } from '../../utils/designTokens' 5 | 6 | const SimpleNavbar = () => { 7 | return ( 8 | <> 9 |
10 | 11 | CSS Conf Colombia 2021 12 |
13 | 32 | 33 | ) 34 | } 35 | 36 | SimpleNavbar.propTypes = {} 37 | 38 | export default SimpleNavbar 39 | -------------------------------------------------------------------------------- /components/organisms/Sponsors.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | import Container from '../atoms/Container' 4 | import Heading from '../atoms/Heading' 5 | import Sponsor from '../molecules/Sponsor' 6 | 7 | import { decisions } from '../../utils/designTokens' 8 | 9 | const Sponsors = ({ sponsors }) => { 10 | return ( 11 | 12 |
13 |
14 |
15 |
16 |
17 |
18 | 19 | Sponsors 20 | 21 |
22 |
23 |
24 |
25 | 30 | 31 | 32 | 37 |
38 |
39 |
40 | 123 |
124 | ) 125 | } 126 | 127 | Sponsors.defaultProps = { 128 | sponsors: { 129 | platinum: [], 130 | gold: [], 131 | silver: [], 132 | support: [] 133 | } 134 | } 135 | 136 | Sponsors.propTypes = { 137 | sponsors: PropTypes.shape({ 138 | platinum: PropTypes.arrayOf( 139 | PropTypes.shape({ 140 | id: PropTypes.string.isRequired, 141 | name: PropTypes.string.isRequired, 142 | url: PropTypes.string.isRequired, 143 | logo: PropTypes.string.isRequired 144 | }) 145 | ), 146 | gold: PropTypes.arrayOf( 147 | PropTypes.shape({ 148 | id: PropTypes.string.isRequired, 149 | name: PropTypes.string.isRequired, 150 | url: PropTypes.string.isRequired, 151 | logo: PropTypes.string.isRequired 152 | }) 153 | ), 154 | silver: PropTypes.arrayOf( 155 | PropTypes.shape({ 156 | id: PropTypes.string.isRequired, 157 | name: PropTypes.string.isRequired, 158 | url: PropTypes.string.isRequired, 159 | logo: PropTypes.string.isRequired 160 | }) 161 | ), 162 | support: PropTypes.arrayOf( 163 | PropTypes.shape({ 164 | id: PropTypes.string.isRequired, 165 | name: PropTypes.string.isRequired, 166 | url: PropTypes.string.isRequired, 167 | logo: PropTypes.string.isRequired 168 | }) 169 | ) 170 | }) 171 | } 172 | 173 | export default Sponsors 174 | -------------------------------------------------------------------------------- /components/organisms/Team.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import PropTypes from 'prop-types' 3 | 4 | import Container from '../atoms/Container' 5 | import Heading from '../atoms/Heading' 6 | import Paragraph from '../atoms/Paragraph' 7 | import Mate from '../molecules/Mate' 8 | 9 | import { choices, decisions } from '../../utils/designTokens' 10 | 11 | const TeamMember = ({ 12 | id, 13 | name, 14 | title, 15 | type, 16 | twitterHandle, 17 | tribalPosition 18 | }) => ( 19 | <> 20 | 27 | 28 | 29 | {name} 30 | 31 | 32 | {title} 33 | 34 | 35 | {twitterHandle} 36 | 37 | 38 | 39 | 44 | 45 | ) 46 | 47 | const Team = ({ team }) => { 48 | const organizers = team.filter(({ type }) => type === 'organizer') 49 | const designers = team.filter(({ type }) => type === 'designer') 50 | const supporters = team.filter(({ type }) => type === 'supporter') 51 | const specials = team.filter(({ type }) => type === 'special') 52 | 53 | return ( 54 | <> 55 |
56 | 57 |
58 | 59 | Our Amazing Team 60 | 61 |
62 | {organizers.map(member => ( 63 | 64 | ))} 65 | {designers.map(member => ( 66 | 67 | ))} 68 | {supporters.map(member => ( 69 | 70 | ))} 71 |
72 | 73 | Special thanks to{' '} 74 | 81 | {specials[0].name} 82 | {' '} 83 | and{' '} 84 | 91 | {specials[1].name} 92 | 93 | 94 |
95 |
96 |
97 |
98 | 158 | 159 | ) 160 | } 161 | 162 | Team.defaultProps = { 163 | team: [] 164 | } 165 | 166 | Team.propTypes = { 167 | team: PropTypes.arrayOf( 168 | PropTypes.shape({ 169 | id: PropTypes.string.isRequired, 170 | name: PropTypes.string.isRequired, 171 | title: PropTypes.string.isRequired, 172 | twitterHandle: PropTypes.string.isRequired 173 | }) 174 | ) 175 | } 176 | 177 | export default Team 178 | -------------------------------------------------------------------------------- /components/organisms/What.js: -------------------------------------------------------------------------------- 1 | import Container from '../atoms/Container' 2 | import Heading from '../atoms/Heading' 3 | import Paragraph from '../atoms/Paragraph' 4 | 5 | import { choices, decisions } from '../../utils/designTokens' 6 | 7 | const What = () => { 8 | return ( 9 |
10 | 11 | 12 | What is the CSS Conf? 13 | 14 | 15 | The CSS Conf is a worldwide organization dedicated to holding 16 | conferences for designers, programmers and web interface creators, 17 | with the purpose of connecting the community in different countries. 18 | 19 | 20 | CSS Conf Colombia joined the family on January 29th, 2019, for the 21 | first time. 22 | 23 |
24 |
25 | 68 |
69 | ) 70 | } 71 | 72 | export default What 73 | -------------------------------------------------------------------------------- /config/client.js: -------------------------------------------------------------------------------- 1 | export const config = { 2 | isProduction: process.env.NODE_ENV === 'production', 3 | googleAnalyticsId: process.env.GOOGLE_ANALYTICS_ID, 4 | clientUrl: process.env.CLIENT_URL, 5 | apiUrl: process.env.API_URL, 6 | ePaycoValidateUrl: process.env.EPAYCO_VALIDATE_URL, 7 | ePaycoPublicKey: process.env.EPAYCO_PUBLIC_KEY, 8 | ePaycoTest: process.env.EPAYCO_ENV === 'development' 9 | } 10 | -------------------------------------------------------------------------------- /config/keys.js: -------------------------------------------------------------------------------- 1 | const keys = [ 2 | 'GOOGLE_ANALYTICS_ID', 3 | 'CLIENT_URL', 4 | 'API_URL', 5 | 'EPAYCO_VALIDATE_URL', 6 | 'EPAYCO_PUBLIC_KEY', 7 | 'EPAYCO_ENV' 8 | ] 9 | 10 | module.exports = keys 11 | -------------------------------------------------------------------------------- /config/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | const serverConfig = { 4 | isProduction: process.env.NODE_ENV === 'production', 5 | mailchimpApiKey: process.env.MAILCHIMP_API_KEY, 6 | mailchimpAudienceId: process.env.MAILCHIMP_AUDIENCE_ID, 7 | wpConsumerKey: process.env.WP_CONSUMER_KEY, 8 | wpConsumerSecret: process.env.WP_CONSUMER_SECRET, 9 | wordpressUrl: process.env.WORDPRESS_URL, 10 | ePaycoClientId: process.env.EPAYCO_CLIENT_ID, 11 | ePaycoSecretKey: process.env.EPAYCO_SECRET_KEY 12 | } 13 | 14 | module.exports = { config: serverConfig } 15 | -------------------------------------------------------------------------------- /config/setup.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | const keys = require('./keys') 4 | const buildEnvConfig = (acc, cur) => ({ ...acc, [`${cur}`]: process.env[cur] }) 5 | 6 | module.exports = keys.reduce(buildEnvConfig, {}) 7 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const environmentSetup = require('./config/setup') 2 | 3 | module.exports = { env: environmentSetup } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cssconfco-website", 3 | "version": "1.0.0", 4 | "description": "CSSConf Colombia Website", 5 | "main": "pages/index.js", 6 | "scripts": { 7 | "lint": "eslint .", 8 | "dev": "next", 9 | "build": "next build", 10 | "start": "next start" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/cssconfco/cssconfco-website.git" 15 | }, 16 | "keywords": [], 17 | "author": "https://twitter.com/@cssconfco", 18 | "license": "Apache-2.0", 19 | "bugs": { 20 | "url": "https://github.com/cssconfco/cssconfco-website/issues" 21 | }, 22 | "homepage": "https://github.com/cssconfco/cssconfco-website#readme", 23 | "dependencies": { 24 | "@fortawesome/fontawesome-svg-core": "^1.2.26", 25 | "@fortawesome/free-solid-svg-icons": "^5.12.0", 26 | "@fortawesome/react-fontawesome": "^0.1.8", 27 | "@hapi/boom": "^9.0.0", 28 | "base-64": "^0.1.0", 29 | "classnames": "^2.2.6", 30 | "crypto-random-string": "^1.0.0", 31 | "dayjs": "^1.10.4", 32 | "dotenv": "^8.2.0", 33 | "isomorphic-unfetch": "^3.0.0", 34 | "lodash": "^4.17.21", 35 | "mailchimp-api-v3": "^1.13.1", 36 | "moment": "^2.24.0", 37 | "next": "^9.3.2", 38 | "nprogress": "^0.2.0", 39 | "react": "^16.12.0", 40 | "react-dom": "^16.12.0", 41 | "react-ga": "^2.7.0", 42 | "react-responsive": "^8.0.1", 43 | "react-select": "^2.0.0", 44 | "react-text-mask": "^5.4.3", 45 | "sha256": "^0.2.0", 46 | "sweetalert": "^2.1.2", 47 | "woocommerce-api": "^1.5.0" 48 | }, 49 | "devDependencies": { 50 | "babel-eslint": "^10.0.3", 51 | "eslint": "^6.8.0", 52 | "eslint-config-prettier": "^6.9.0", 53 | "eslint-plugin-prettier": "^3.1.2", 54 | "eslint-plugin-react": "^7.17.0", 55 | "husky": ">=1", 56 | "lint-staged": ">=8", 57 | "prettier": "^1.19.1" 58 | }, 59 | "husky": { 60 | "hooks": { 61 | "pre-commit": "lint-staged" 62 | } 63 | }, 64 | "lint-staged": { 65 | "*.js": [ 66 | "eslint --fix", 67 | "git add" 68 | ] 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import App, { Container as NextContainer } from 'next/app' 2 | import Head from 'next/head' 3 | import Router from 'next/router' 4 | import nprogress from 'nprogress' 5 | 6 | Router.onRouteChangeStart = () => { 7 | nprogress.start() 8 | } 9 | Router.onRouteChangeComplete = () => { 10 | nprogress.done() 11 | } 12 | Router.onRouteChangeError = () => { 13 | nprogress.done() 14 | } 15 | 16 | import Footer from '../components/organisms/Footer' 17 | import GoogleAnalytics from '../components/layout/GoogleAnalytics' 18 | 19 | import { 20 | initialStyles, 21 | formStyles, 22 | checkboxStyles, 23 | reactSelectStyles, 24 | nprogressStyles 25 | } from '../utils/globalStyles' 26 | 27 | export default class CustomApp extends App { 28 | static async getInitialProps({ Component, ctx }) { 29 | return { 30 | pageProps: Component.getInitialProps 31 | ? await Component.getInitialProps(ctx) 32 | : {} 33 | } 34 | } 35 | 36 | render() { 37 | const { Component, pageProps } = this.props 38 | return ( 39 | 40 | 41 | 42 | CSS Conf Colombia 2021 — A CSS Lovers Conference 43 | 47 | 51 | 52 |
53 | 54 |
55 |
56 |