├── .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 |
23 | {children}
24 |
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 |
9 | {children}
10 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
56 |
57 |
72 |
75 |
78 |
81 |
84 |
87 |
88 |
89 | )
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/pages/_document.js:
--------------------------------------------------------------------------------
1 | import Document, { Html, Head, Main, NextScript } from 'next/document'
2 |
3 | class CustomDocument extends Document {
4 | static async getInitialProps(ctx) {
5 | const initialProps = await Document.getInitialProps(ctx)
6 | return { ...initialProps }
7 | }
8 |
9 | render() {
10 | return (
11 |
12 |
13 |
17 |
18 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | )
52 | }
53 | }
54 |
55 | export default CustomDocument
56 |
--------------------------------------------------------------------------------
/pages/api/checkout/confirmation.js:
--------------------------------------------------------------------------------
1 | import CheckoutService from '../../../services/checkout'
2 | import { parseOrder } from '../../../utils/parseResponse'
3 |
4 | const checkoutService = new CheckoutService()
5 |
6 | export default async (req, res) => {
7 | if (req.method === 'GET') {
8 | try {
9 | const order = await checkoutService.confirmOrder(req.query)
10 | const response = parseOrder(order, ['status'])
11 |
12 | res.send(response)
13 | } catch (error) {
14 | console.error(error)
15 | res.status(500).send(error)
16 | }
17 | } else {
18 | res.status(404).end()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/pages/api/checkout/process.js:
--------------------------------------------------------------------------------
1 | import CheckoutService from '../../../services/checkout'
2 |
3 | const checkoutService = new CheckoutService()
4 |
5 | export default async (req, res) => {
6 | if (req.method === 'POST') {
7 | const { userInfo, shoppingCartItems, couponCode } = req.body
8 |
9 | try {
10 | const response = await checkoutService.processCheckout({
11 | userInfo,
12 | shoppingCartItems,
13 | couponCode
14 | })
15 |
16 | res.send(response)
17 | } catch (error) {
18 | console.error(error)
19 | res.status(500).send(error)
20 | }
21 | } else {
22 | res.status(404).end()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/pages/api/coupons.js:
--------------------------------------------------------------------------------
1 | import { get } from 'lodash'
2 | import WooCommerceService from '../../services/woocommerce'
3 | import CheckoutService from '../../services/checkout'
4 |
5 | import { parseCoupon } from '../../utils/parseResponse'
6 |
7 | const wooCommerceService = new WooCommerceService()
8 | const checkoutService = new CheckoutService()
9 |
10 | export default async (req, res) => {
11 | if (req.method === 'GET') {
12 | try {
13 | const code = get(req, 'query.code')
14 | const coupons = await wooCommerceService.getCoupons({ code })
15 | const coupon = checkoutService.getValidatedCuopon({ coupons })
16 | const response = parseCoupon(coupon)
17 |
18 | res.send(response)
19 | } catch (error) {
20 | console.error(error)
21 | res.status(500).send(error)
22 | }
23 | } else {
24 | res.status(404).end()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/pages/api/orders.js:
--------------------------------------------------------------------------------
1 | import { get } from 'lodash'
2 | import WooCommerceService from '../../services/woocommerce'
3 | import { parseOrder } from '../../utils/parseResponse'
4 | import verifyOrderKey from '../../utils/verifyOrderKey'
5 |
6 | const ORDER_GET_ATTRIBUTES = ['line_items']
7 |
8 | const wooCommerceService = new WooCommerceService()
9 |
10 | export default async (req, res) => {
11 | if (req.method === 'GET') {
12 | try {
13 | const orderId = get(req, 'query.orderId')
14 | const orderKey = get(req, 'query.orderKey')
15 |
16 | const order = await wooCommerceService.getOrder({ orderId })
17 | const verifiedOrder = verifyOrderKey(order, orderKey)
18 | const response = parseOrder(verifiedOrder, ORDER_GET_ATTRIBUTES)
19 |
20 | res.send(response)
21 | } catch (error) {
22 | console.error(error)
23 | res.status(500).send(error)
24 | }
25 | } else {
26 | res.status(404).end()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/pages/api/products.js:
--------------------------------------------------------------------------------
1 | import WooCommerceService from '../../services/woocommerce'
2 | import { parseProduct } from '../../utils/parseResponse'
3 |
4 | const wooCommerceService = new WooCommerceService()
5 |
6 | const mapProduct = product => parseProduct(product)
7 | const isInStock = product => product.in_stock
8 | const isPublished = product => product.status === 'publish'
9 |
10 | export default async (req, res) => {
11 | if (req.method === 'GET') {
12 | try {
13 | const products = await wooCommerceService.getProducts()
14 | const response = products
15 | .map(mapProduct)
16 | .filter(isInStock)
17 | .filter(isPublished)
18 |
19 | res.send(response)
20 | } catch (error) {
21 | console.error(error)
22 | res.status(500).send(error)
23 | }
24 | } else {
25 | res.status(404).end()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/pages/api/resources/countries.js:
--------------------------------------------------------------------------------
1 | import fetchJson from '../../../utils/fetchJson'
2 |
3 | const mapCountry = ({ name, alpha2Code }) => ({
4 | name,
5 | alpha2Code
6 | })
7 |
8 | export default async (req, res) => {
9 | if (req.method === 'GET') {
10 | try {
11 | const countries = await fetchJson('https://restcountries.eu/rest/v2/all')
12 | const response = countries.map(mapCountry)
13 | res.send(response)
14 | } catch (error) {
15 | console.error(error)
16 | res.status(500).send(error)
17 | }
18 | } else {
19 | res.status(404).end()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pages/api/subscribe.js:
--------------------------------------------------------------------------------
1 | import Mailchimp from 'mailchimp-api-v3'
2 | import crypto from 'crypto'
3 | import { config } from '../../config/server'
4 |
5 | const mailchimp = new Mailchimp(config.mailchimpApiKey)
6 |
7 | const getMd5Hash = email =>
8 | crypto
9 | .createHash('md5')
10 | .update(email, 'utf8')
11 | .digest('hex')
12 |
13 | export default async (req, res) => {
14 | if (req.method === 'POST') {
15 | const { name = '', email } = req.body
16 |
17 | const [FNAME, ...lastNames] = name.trim().split(' ')
18 |
19 | try {
20 | const hashedEmail = getMd5Hash(email)
21 | const results = await mailchimp.put(
22 | `/lists/${config.mailchimpAudienceId}/members/${hashedEmail}`,
23 | {
24 | status_if_new: 'subscribed',
25 | status: 'subscribed',
26 | email_address: email,
27 | merge_fields: { FNAME, LNAME: lastNames.join(' ') }
28 | }
29 | )
30 | res.send(results)
31 | } catch (error) {
32 | console.error(error)
33 | res.status(500).send({
34 | error: 'An error has ocurred, please contact us at hello@cssconf.co'
35 | })
36 | }
37 | } else {
38 | res.status(404).end()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import swal from 'sweetalert'
3 |
4 | import Conduct from '../components/organisms/Conduct'
5 | import Hero from '../components/organisms/Hero'
6 | import Navbar from '../components/organisms/Navbar'
7 | import Newsletter from '../components/organisms/Newsletter'
8 | import Speakers from '../components/organisms/Speakers'
9 | import Team from '../components/organisms/Team'
10 | import What from '../components/organisms/What'
11 | import Sponsors from '../components/organisms/Sponsors'
12 | import Alert from '../components/atoms/Alert'
13 | import Paragraph from '../components/atoms/Paragraph'
14 |
15 | import fetchJson from '../utils/fetchJson'
16 | import { logEvent } from '../utils/analytics'
17 | import { speakers, team, sponsors } from '../utils/constants'
18 |
19 | import { config } from '../config/client'
20 |
21 | class Home extends Component {
22 | state = { name: '', email: '', loading: false }
23 |
24 | startLoading = () => {
25 | this.setState({ loading: true })
26 | }
27 |
28 | stopLoading = () => {
29 | this.setState({ loading: false })
30 | }
31 |
32 | resetForm = () => {
33 | this.setState({ name: '', email: '', loading: false })
34 | }
35 |
36 | handleSubmit = async event => {
37 | event && event.preventDefault()
38 | this.startLoading()
39 |
40 | try {
41 | const { name, email } = this.state
42 |
43 | await fetchJson(`${config.apiUrl}/subscribe`, {
44 | method: 'POST',
45 | body: JSON.stringify({ name, email }),
46 | headers: {
47 | 'Content-Type': 'application/json'
48 | }
49 | })
50 |
51 | swal(
52 | "That's it!",
53 | 'You will receive an email the day before of the event with more information.',
54 | 'success'
55 | )
56 | logEvent({ category: 'signup', action: 'click', label: 'newsletter' })
57 |
58 | this.resetForm()
59 | } catch (error) {
60 | swal('Opps!', 'Something went wrong, please try again', 'error')
61 | this.stopLoading()
62 | }
63 | }
64 |
65 | handleChange = name => event => {
66 | this.setState({ [`${name}`]: event.currentTarget.value })
67 | }
68 |
69 | render() {
70 | const { name, email, loading } = this.state
71 |
72 | return (
73 | <>
74 |
75 |
76 | 💬 We have a Discord Server {' '}
77 |
82 | join us
83 | {' '}
84 |
85 |
86 |
87 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | >
101 | )
102 | }
103 | }
104 |
105 | export default Home
106 |
--------------------------------------------------------------------------------
/pages/schedule.js:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 |
3 | import Container from '../components/atoms/Container'
4 | import Heading from '../components/atoms/Heading'
5 | import Logo from '../components/atoms/Logo'
6 |
7 | import { choices } from '../utils/designTokens'
8 | import { speakers, conferenceDate } from '../utils/constants'
9 | import ScheduleBlock from '../components/organisms/ScheduleBlock'
10 | import Subtitle from '../components/atoms/Subtitle'
11 |
12 | const normalizedSpeakers = speakers.reduce((prev, cur) => {
13 | prev[cur.id] = cur
14 | return prev
15 | }, {})
16 |
17 | const scheduleData = [
18 | {
19 | time: `${conferenceDate}T08:00-05:00`,
20 | title: 'Welcome 🦄'
21 | },
22 | {
23 | time: `${conferenceDate}T08:15-05:00`,
24 | speaker: normalizedSpeakers['evangelina-ferreira']
25 | },
26 | {
27 | time: `${conferenceDate}T08:55-05:00`,
28 | speaker: normalizedSpeakers['carlos-azaustre']
29 | },
30 | {
31 | time: `${conferenceDate}T09:15-05:00`,
32 | speaker: normalizedSpeakers['laura-gonzalez']
33 | },
34 | {
35 | time: `${conferenceDate}T09:55-05:00`,
36 | speaker: normalizedSpeakers['alena-nikolaeva']
37 | },
38 | {
39 | time: `${conferenceDate}T10:15-05:00`,
40 | speaker: normalizedSpeakers['carmen-ansio']
41 | },
42 | {
43 | time: `${conferenceDate}T10:55-05:00`,
44 | speaker: normalizedSpeakers['facundo-corradini']
45 | },
46 | {
47 | time: `${conferenceDate}T11:15-05:00`,
48 | speaker: normalizedSpeakers['joan-leon']
49 | },
50 | {
51 | time: `${conferenceDate}T11:55-05:00`,
52 | speaker: normalizedSpeakers['jimena-castro']
53 | },
54 | {
55 | time: `${conferenceDate}T12:15-05:00`,
56 | title: 'Meal Break 🍕',
57 | isBreak: true
58 | },
59 | {
60 | time: `${conferenceDate}T12:55-05:00`,
61 | title: 'SWAG Raffle 🎁'
62 | },
63 | {
64 | time: `${conferenceDate}T13:05-05:00`,
65 | speaker: normalizedSpeakers['robin-dykema']
66 | },
67 | {
68 | time: `${conferenceDate}T13:45-05:00`,
69 | speaker: normalizedSpeakers['erifranck-nunez']
70 | },
71 | {
72 | time: `${conferenceDate}T14:05-05:00`,
73 | speaker: normalizedSpeakers['alex-ramirez']
74 | },
75 | {
76 | time: `${conferenceDate}T14:45-05:00`,
77 | speaker: normalizedSpeakers['marian-villa']
78 | },
79 | {
80 | time: `${conferenceDate}T15:05-05:00`,
81 | speaker: normalizedSpeakers['leonidas-esteban']
82 | },
83 | {
84 | time: `${conferenceDate}T15:45-05:00`,
85 | speaker: normalizedSpeakers['bramus-vandamme']
86 | },
87 | {
88 | time: `${conferenceDate}T16:05-05:00`,
89 | title: 'Closing Remarks 🌈'
90 | },
91 | {
92 | time: `${conferenceDate}T16:15-05:00`,
93 | title: 'Virtual Party 🎉',
94 | isBreak: true
95 | }
96 | ]
97 |
98 | const schedule = () => {
99 | return (
100 |
101 |
102 |
103 |
104 | {' '}
105 |
106 | Schedule
107 |
108 |
109 | CSS Conf Colombia, April 10th
110 |
111 |
112 |
113 |
114 | < Back
115 |
116 |
117 |
118 |
119 | {scheduleData.map(schedule => (
120 |
126 | ))}
127 |
128 |
129 |
130 |
131 |
151 |
152 | )
153 | }
154 |
155 | export default schedule
156 |
--------------------------------------------------------------------------------
/pages/tickets/response.js:
--------------------------------------------------------------------------------
1 | import { get, isEmpty } from 'lodash'
2 |
3 | import { config } from '../../config/client'
4 |
5 | import SimpleNavbar from '../../components/organisms/SimpleNavbar'
6 | import CheckoutResponse from '../../components/organisms/CheckoutResponse'
7 |
8 | import fetchJson from '../../utils/fetchJson'
9 | import parseCheckoutResponse from '../../utils/parseCheckoutResponse'
10 | import { decodeOrderParams } from '../../utils/orderParams'
11 | import redirectToHomePage from '../../utils/redirectToHomePage'
12 |
13 | async function getEpaycoData({ query }) {
14 | const ePaycoReference = get(query, 'ref_payco')
15 | const ePaycoReferenceX = get(query, 'x_ref_payco')
16 |
17 | if (ePaycoReference) {
18 | try {
19 | const { data: ePaycoData } = await fetchJson(
20 | `${config.ePaycoValidateUrl}/${ePaycoReference}`
21 | )
22 |
23 | return parseCheckoutResponse(ePaycoData)
24 | } catch (error) {
25 | console.error(
26 | 'Error getting ePayco data in checkout response page',
27 | error
28 | )
29 | }
30 | } else if (ePaycoReferenceX) {
31 | return parseCheckoutResponse(query)
32 | }
33 | }
34 |
35 | function getOrderParams({ ePayco, query }) {
36 | if (isEmpty(ePayco)) {
37 | const ref = get(query, 'ref')
38 | return decodeOrderParams(ref)
39 | } else {
40 | const extra1 = get(ePayco, 'extra1')
41 | return decodeOrderParams(extra1)
42 | }
43 | }
44 |
45 | const Response = props => (
46 | <>
47 |
48 |
49 | >
50 | )
51 |
52 | Response.getInitialProps = async ({ res, query }) => {
53 | try {
54 | const ePayco = await getEpaycoData({ query })
55 |
56 | const { orderNumber, orderKey } = getOrderParams({ ePayco, query })
57 |
58 | const order = await fetchJson(
59 | `${config.apiUrl}/orders?orderId=${orderNumber}&orderKey=${orderKey}`
60 | )
61 |
62 | return {
63 | ePayco,
64 | order
65 | }
66 | } catch (error) {
67 | console.error(
68 | 'Error getting initial props in checkout response page',
69 | error
70 | )
71 |
72 | redirectToHomePage(res)
73 | }
74 | }
75 |
76 | export default Response
77 |
--------------------------------------------------------------------------------
/services/epayco/index.js:
--------------------------------------------------------------------------------
1 | /* global ePayco */
2 | import { config } from '../../config/client'
3 | import { encodeOrderParams } from '../../utils/orderParams'
4 |
5 | const defaultValues = {
6 | name: 'CSS Conf Colombia',
7 | description: 'CSS Conf Colombia 2021',
8 | currency: 'cop',
9 | tax_base: '0',
10 | country: 'co',
11 | lang: 'en',
12 | test: `${config.ePaycoTest}`,
13 | confirmation: `${config.apiUrl}/checkout/confirmation`,
14 | response: `${config.clientUrl}/tickets/response`,
15 | method: 'get',
16 | p_confirm_method: 'get'
17 | }
18 |
19 | class EPaycoService {
20 | constructor(key, test) {
21 | this.key = key
22 | this.test = test
23 | }
24 |
25 | configure() {
26 | this.onPageCheckout = ePayco.checkout.configure({
27 | key: this.key,
28 | test: this.test
29 | })
30 | }
31 |
32 | formatData({ order = {} } = {}) {
33 | const {
34 | number: orderNumber,
35 | order_key: orderKey,
36 | total,
37 | total_tax,
38 | currency
39 | } = order
40 |
41 | const {
42 | first_name,
43 | last_name,
44 | country,
45 | address_1,
46 | phone,
47 | email
48 | } = order.billing
49 |
50 | return {
51 | ...defaultValues,
52 | name_billing: `${first_name} ${last_name}`,
53 | country: country,
54 | currency: currency,
55 | tax: total_tax,
56 | amount: total,
57 | address_billing: address_1,
58 | mobilephone_billing: phone,
59 | invoice: orderNumber,
60 | email_billing: email,
61 | extra1: encodeOrderParams({
62 | orderNumber,
63 | orderKey
64 | })
65 | }
66 | }
67 |
68 | openDialog(data) {
69 | try {
70 | this.configure()
71 | this.onPageCheckout.open(this.formatData(data))
72 | } catch (error) {
73 | console.error('Error opening OnPage checkout in ePayco service', error)
74 | }
75 | }
76 | }
77 |
78 | export default EPaycoService
79 |
--------------------------------------------------------------------------------
/services/woocommerce/index.js:
--------------------------------------------------------------------------------
1 | import WooCommerceAPI from 'woocommerce-api'
2 | import { config } from '../../config/server'
3 | import sanitizeString from '../../utils/sanitizeString'
4 | import validateEmail from '../../utils/validateEmail'
5 |
6 | class WooCommerceService {
7 | constructor(version = 'wc/v2') {
8 | this.WooCommerce = new WooCommerceAPI({
9 | version,
10 | url: config.wordpressUrl,
11 | consumerKey: config.wpConsumerKey,
12 | consumerSecret: config.wpConsumerSecret,
13 | wpAPI: true
14 | })
15 | }
16 |
17 | request(resource, method = 'get', data) {
18 | switch (method) {
19 | case 'post':
20 | return this.WooCommerce.postAsync(resource, data).then(result =>
21 | JSON.parse(result.toJSON().body)
22 | )
23 | case 'put':
24 | return this.WooCommerce.putAsync(resource, data).then(result =>
25 | JSON.parse(result.toJSON().body)
26 | )
27 | case 'get':
28 | return this.WooCommerce.getAsync(resource).then(result =>
29 | JSON.parse(result.toJSON().body)
30 | )
31 | }
32 | }
33 |
34 | // Products
35 | getProducts() {
36 | return this.request('products')
37 | }
38 |
39 | getProduct({ productId } = {}) {
40 | return this.request(`products/${productId}`)
41 | }
42 |
43 | // Orders
44 | getOrders({ customer, page = 1, perPage = 10 }) {
45 | return customer
46 | ? this.request(
47 | `orders?page=${page}&per_page=${perPage}&customer=${customer}`
48 | )
49 | : this.request(`orders?page=${page}&per_page=${perPage}`)
50 | }
51 |
52 | getOrder({ orderId = '' } = {}) {
53 | const sanitizedOrderId = sanitizeString(orderId)
54 |
55 | return sanitizedOrderId
56 | ? this.request(`orders/${orderId}`)
57 | : Promise.resolve({})
58 | }
59 |
60 | createOrder({ order } = {}) {
61 | return this.request('orders', 'post', order)
62 | }
63 |
64 | updateOrder(
65 | orderId,
66 | { billing, shipping, shipping_lines, customer_note, status, meta_data } = {}
67 | ) {
68 | return this.request(`orders/${orderId}`, 'put', {
69 | billing,
70 | shipping,
71 | shipping_lines,
72 | customer_note,
73 | status,
74 | meta_data
75 | })
76 | }
77 |
78 | // Customers
79 | getCustomers({ customerEmail, customerId } = {}) {
80 | if (customerEmail) {
81 | validateEmail(customerEmail)
82 | return this.request(`customers?email=${customerEmail}`)
83 | }
84 |
85 | const sanitizedCustomerId = sanitizeString(customerId)
86 |
87 | if (sanitizedCustomerId) {
88 | return this.request(`customers/${sanitizedCustomerId}`)
89 | }
90 |
91 | return Promise.resolve([])
92 | }
93 |
94 | createCustomer({ customer }) {
95 | return this.request('customers', 'post', customer)
96 | }
97 |
98 | // Coupons
99 | getCoupons({ code }) {
100 | const sanitizedCode = sanitizeString(code)
101 |
102 | return sanitizedCode
103 | ? this.request(`coupons?code=${sanitizedCode}`)
104 | : Promise.resolve([])
105 | }
106 | }
107 |
108 | export default WooCommerceService
109 |
--------------------------------------------------------------------------------
/static/favicons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/favicons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/static/favicons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/favicons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/static/favicons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/favicons/apple-touch-icon.png
--------------------------------------------------------------------------------
/static/favicons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/favicons/favicon-16x16.png
--------------------------------------------------------------------------------
/static/favicons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/favicons/favicon-32x32.png
--------------------------------------------------------------------------------
/static/favicons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/favicons/favicon.ico
--------------------------------------------------------------------------------
/static/favicons/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/static/favicons/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/static/favicons/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/static/fonts/Apercu Bold Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/fonts/Apercu Bold Italic.woff
--------------------------------------------------------------------------------
/static/fonts/Apercu Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/fonts/Apercu Bold.woff
--------------------------------------------------------------------------------
/static/fonts/Apercu Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/fonts/Apercu Italic.woff
--------------------------------------------------------------------------------
/static/fonts/Apercu Light Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/fonts/Apercu Light Italic.woff
--------------------------------------------------------------------------------
/static/fonts/Apercu Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/fonts/Apercu Light.woff
--------------------------------------------------------------------------------
/static/fonts/Apercu Medium Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/fonts/Apercu Medium Italic.woff
--------------------------------------------------------------------------------
/static/fonts/Apercu Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/fonts/Apercu Medium.woff
--------------------------------------------------------------------------------
/static/fonts/Apercu Mono.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/fonts/Apercu Mono.woff
--------------------------------------------------------------------------------
/static/fonts/Apercu_Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/fonts/Apercu_Regular.woff
--------------------------------------------------------------------------------
/static/icons/cross.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/icons/instagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/icons/twitter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/css-blog-text.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/epayco-payment-methods.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/epayco-payment-methods.png
--------------------------------------------------------------------------------
/static/images/frog-tribal-vector.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/iguana-tribal-vector.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/medellin-tribal-vector.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/rutan-tribal-vector.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/speakers/alena-nikolaeva.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/alena-nikolaeva.png
--------------------------------------------------------------------------------
/static/images/speakers/alex-ramirez.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/alex-ramirez.png
--------------------------------------------------------------------------------
/static/images/speakers/bramus-vandamme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/bramus-vandamme.png
--------------------------------------------------------------------------------
/static/images/speakers/carlos-azaustre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/carlos-azaustre.png
--------------------------------------------------------------------------------
/static/images/speakers/carmen-ansio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/carmen-ansio.png
--------------------------------------------------------------------------------
/static/images/speakers/erifranck-nunez.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/erifranck-nunez.png
--------------------------------------------------------------------------------
/static/images/speakers/evangelina-ferreira.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/evangelina-ferreira.png
--------------------------------------------------------------------------------
/static/images/speakers/facundo-corradini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/facundo-corradini.png
--------------------------------------------------------------------------------
/static/images/speakers/jimena-castro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/jimena-castro.png
--------------------------------------------------------------------------------
/static/images/speakers/joan-leon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/joan-leon.png
--------------------------------------------------------------------------------
/static/images/speakers/laura-gonzalez.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/laura-gonzalez.png
--------------------------------------------------------------------------------
/static/images/speakers/leonidas-esteban.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/leonidas-esteban.png
--------------------------------------------------------------------------------
/static/images/speakers/luis-gadea.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/luis-gadea.png
--------------------------------------------------------------------------------
/static/images/speakers/marian-villa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/marian-villa.png
--------------------------------------------------------------------------------
/static/images/speakers/next-speaker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/next-speaker.png
--------------------------------------------------------------------------------
/static/images/speakers/robin-dykema.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/speakers/robin-dykema.png
--------------------------------------------------------------------------------
/static/images/sponsors/acamica.svg:
--------------------------------------------------------------------------------
1 | Artboard Created with Sketch.
--------------------------------------------------------------------------------
/static/images/sponsors/adevinta.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/sponsors/astound.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/sponsors/fec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/sponsors/fec.png
--------------------------------------------------------------------------------
/static/images/sponsors/gde.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/sponsors/gde.png
--------------------------------------------------------------------------------
/static/images/sponsors/globant.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/sponsors/ilogica.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/sponsors/mozilla.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/sponsors/uruit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/sponsors/uruit.png
--------------------------------------------------------------------------------
/static/images/sponsors/wolox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/sponsors/wolox.png
--------------------------------------------------------------------------------
/static/images/sponsors/workia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/sponsors/workia.png
--------------------------------------------------------------------------------
/static/images/team/agustina/agustina-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/agustina/agustina-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/agustina/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/andrea/andrea-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/andrea/andrea-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/andres/andres-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/andres/andres-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/estefa/estefa-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/estefa/estefa-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/estefa/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/guillermo/guillermo-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/guillermo/guillermo-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/guillermo/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/henry/henry-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/henry/henry-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/henry/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/johana/johana-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/johana/johana-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/johana/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/jonathan/jonathan-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/jonathan/jonathan-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/jonathan/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/juan/juan-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/juan/juan-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/juan/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/luis/luis-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/luis/luis-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/luisa/luisa-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/luisa/luisa-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/luisa/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/manuela/manuela-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/manuela/manuela-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/manuela/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/maria/maria-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/maria/maria-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/maria/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/paulina/paulina-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/paulina/paulina-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/paulina/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/team/santiago/santiago-avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cssconfco/website/d2b74cb6af53e47d858ff00e925a719aa8193f73/static/images/team/santiago/santiago-avatar.jpg
--------------------------------------------------------------------------------
/static/images/team/santiago/tribal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/utils/analytics.js:
--------------------------------------------------------------------------------
1 | import ReactGA from 'react-ga'
2 | import { config } from '../config/client'
3 |
4 | export const initGA = () => {
5 | console.log('GA', 'Initialized!')
6 | ReactGA.initialize(config.googleAnalyticsId)
7 | }
8 | export const logPageView = () => {
9 | console.log('GA', `Page viewed ${window.location.pathname}`)
10 | ReactGA.set({ page: window.location.pathname })
11 | ReactGA.pageview(window.location.pathname)
12 | }
13 | export const logEvent = ({ category = '', action = '', label = '', value }) => {
14 | if (category && action) {
15 | ReactGA.event({ category, action, label, value })
16 | }
17 | }
18 | export const logException = (description = '', fatal = false) => {
19 | if (description) {
20 | ReactGA.exception({ description, fatal })
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/utils/cleanUrlQueryParams.js:
--------------------------------------------------------------------------------
1 | export default function cleanUrlQueryParams(baseUrl) {
2 | return history.replaceState({}, document.title, baseUrl || location.pathname)
3 | }
4 |
--------------------------------------------------------------------------------
/utils/createSignature.js:
--------------------------------------------------------------------------------
1 | import sha256 from 'sha256'
2 |
3 | export default (
4 | {
5 | x_ref_payco: reference,
6 | x_transaction_id: transaction,
7 | x_amount: amount,
8 | x_currency_code: currency
9 | },
10 | id,
11 | key
12 | ) => sha256(`${id}^${key}^${reference}^${transaction}^${amount}^${currency}`)
13 |
--------------------------------------------------------------------------------
/utils/fetchJson.js:
--------------------------------------------------------------------------------
1 | import fetch from 'isomorphic-unfetch'
2 |
3 | const fetchJson = (url, options) => fetch(url, options).then(res => res.json())
4 |
5 | export default fetchJson
6 |
--------------------------------------------------------------------------------
/utils/fontFaces.js:
--------------------------------------------------------------------------------
1 | import css from 'styled-jsx/css'
2 |
3 | const fontFaces = css.global`
4 | @font-face {
5 | font-family: 'Apercu Regular';
6 | font-style: normal;
7 | font-weight: normal;
8 | src: local('Apercu Regular'),
9 | url('/static/fonts/Apercu_Regular.woff') format('woff');
10 | }
11 |
12 | @font-face {
13 | font-family: 'Apercu Italic';
14 | font-style: normal;
15 | font-weight: normal;
16 | src: local('Apercu Italic'),
17 | url('/static/fonts/Apercu Italic.woff') format('woff');
18 | }
19 |
20 | @font-face {
21 | font-family: 'Apercu Light';
22 | font-style: normal;
23 | font-weight: normal;
24 | src: local('Apercu Light'),
25 | url('/static/fonts/Apercu Light.woff') format('woff');
26 | }
27 |
28 | @font-face {
29 | font-family: 'Apercu Light Italic';
30 | font-style: normal;
31 | font-weight: normal;
32 | src: local('Apercu Light Italic'),
33 | url('/static/fonts/Apercu Light Italic.woff') format('woff');
34 | }
35 |
36 | @font-face {
37 | font-family: 'Apercu Medium';
38 | font-style: normal;
39 | font-weight: normal;
40 | src: local('Apercu Medium'),
41 | url('/static/fonts/Apercu Medium.woff') format('woff');
42 | }
43 |
44 | @font-face {
45 | font-family: 'Apercu Medium Italic';
46 | font-style: normal;
47 | font-weight: normal;
48 | src: local('Apercu Medium Italic'),
49 | url('/static/fonts/Apercu Medium Italic.woff') format('woff');
50 | }
51 |
52 | @font-face {
53 | font-family: 'Apercu Bold';
54 | font-style: normal;
55 | font-weight: normal;
56 | src: local('Apercu Bold'),
57 | url('/static/fonts/Apercu Bold.woff') format('woff');
58 | }
59 |
60 | @font-face {
61 | font-family: 'Apercu Bold Italic';
62 | font-style: normal;
63 | font-weight: normal;
64 | src: local('Apercu Bold Italic'),
65 | url('/static/fonts/Apercu Bold Italic.woff') format('woff');
66 | }
67 |
68 | @font-face {
69 | font-family: 'Apercu Mono Regular';
70 | font-style: normal;
71 | font-weight: normal;
72 | src: local('Apercu Mono Regular'),
73 | url('/static/fonts/Apercu Mono.woff') format('woff');
74 | }
75 | `
76 |
77 | export default fontFaces
78 |
--------------------------------------------------------------------------------
/utils/getCenteredStyles.js:
--------------------------------------------------------------------------------
1 | const getCenteredStyles = (isCentered, defaultStyles = {}) =>
2 | isCentered
3 | ? { marginLeft: 'auto', marginRight: 'auto', textAlign: 'center' }
4 | : defaultStyles
5 |
6 | export default getCenteredStyles
7 |
--------------------------------------------------------------------------------
/utils/getFlag.js:
--------------------------------------------------------------------------------
1 | const getFlag = speaker => (speaker?.talkLanguage === 'English' ? '🇬🇧' : '🇪🇸')
2 |
3 | export default getFlag
4 |
--------------------------------------------------------------------------------
/utils/getImageProps.js:
--------------------------------------------------------------------------------
1 | export const getImageAlt = img => img && img.alt
2 | export const getImageUrlForLink = img => img && (img.url || img.src)
3 | export const getImageUrlForCSS = img => img && `url(${img.url || img.src})`
4 |
--------------------------------------------------------------------------------
/utils/getTalkType.js:
--------------------------------------------------------------------------------
1 | const getTalkType = speaker =>
2 | speaker?.isLightningTalk ? '⚡️ Lightning talk' : '🎙 Main talk'
3 |
4 | export default getTalkType
5 |
--------------------------------------------------------------------------------
/utils/orderParams.js:
--------------------------------------------------------------------------------
1 | import base64 from 'base-64'
2 |
3 | function encode(value) {
4 | return typeof btoa !== 'undefined' ? btoa(value) : base64.encode(value)
5 | }
6 |
7 | function decode(value) {
8 | return typeof atob !== 'undefined' ? atob(value) : base64.decode(value)
9 | }
10 |
11 | export function cleanOrderKey(key) {
12 | if (key) {
13 | return String(key).replace('wc_order_', '')
14 | }
15 | }
16 |
17 | export function encodeOrderParams({ orderNumber, orderKey }) {
18 | const token = {
19 | ord: orderNumber,
20 | ork: cleanOrderKey(orderKey)
21 | }
22 |
23 | return encode(JSON.stringify(token))
24 | }
25 |
26 | export function decodeOrderParams(token) {
27 | const { ord, ork } = JSON.parse(decode(token))
28 |
29 | return {
30 | orderNumber: ord,
31 | orderKey: ork
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/utils/orderStatusMapper.js:
--------------------------------------------------------------------------------
1 | const epaycoCode = {
2 | ACCEPTED: '1',
3 | REJECTED: '2',
4 | PENDING: '3',
5 | FAILED: '4',
6 | REVERSED: '6',
7 | RETAINED: '7',
8 | STARTED: '8',
9 | EXPIRED: '9',
10 | ABANDONED: '10',
11 | CANCELED: '11',
12 | ANTIFRAUD: '12'
13 | }
14 |
15 | const DEFAULT_ORDER_STATUS = 'on-hold'
16 |
17 | const ePaycoToWoocommerceStatusMap = {
18 | [epaycoCode.ACCEPTED]: 'completed',
19 | [epaycoCode.REJECTED]: 'failed',
20 | [epaycoCode.PENDING]: 'pending',
21 | [epaycoCode.FAILED]: 'failed',
22 | [epaycoCode.REVERSED]: 'refunded',
23 | [epaycoCode.RETAINED]: 'on-hold',
24 | [epaycoCode.STARTED]: 'pending',
25 | [epaycoCode.EXPIRED]: 'failed',
26 | [epaycoCode.ABANDONED]: 'cancelled',
27 | [epaycoCode.CANCELED]: 'cancelled',
28 | [epaycoCode.ANTIFRAUD]: 'failed'
29 | }
30 |
31 | export const epaycoToWooCommerce = key =>
32 | ePaycoToWoocommerceStatusMap[`${key}`] || DEFAULT_ORDER_STATUS
33 |
--------------------------------------------------------------------------------
/utils/parseCheckoutResponse.js:
--------------------------------------------------------------------------------
1 | import { omit, camelCase } from 'lodash'
2 |
3 | const REPLACE_EPAYCO_PREFIXES_REGEX = /x_customer_|x_/gi
4 |
5 | const FORBIDDEN_EPAYCO_KEYS = [
6 | 'x_bussines',
7 | 'x_cardnumber',
8 | 'x_signature',
9 | 'x_tax',
10 | 'x_test_request',
11 | 'x_amount_base',
12 | 'x_approval_code',
13 | 'x_amount_country',
14 | 'x_amount_ok',
15 | 'x_bank_name',
16 | 'x_customer_ip',
17 | 'x_extra2',
18 | 'x_extra3',
19 | 'x_id_factura',
20 | 'x_quotas',
21 | 'x_respuesta',
22 | 'x_fecha_transaccion'
23 | ]
24 |
25 | function buildKey(key = '') {
26 | return camelCase(key.replace(REPLACE_EPAYCO_PREFIXES_REGEX, ''))
27 | }
28 |
29 | function buildReponseObject(response) {
30 | return function(acc, cur) {
31 | return { ...acc, [buildKey(cur)]: response[cur] }
32 | }
33 | }
34 |
35 | function cleanResponse(response) {
36 | return omit(response, FORBIDDEN_EPAYCO_KEYS)
37 | }
38 |
39 | export default (response = {}) =>
40 | Object.keys(cleanResponse(response)).reduce(buildReponseObject(response), {})
41 |
--------------------------------------------------------------------------------
/utils/parseResponse.js:
--------------------------------------------------------------------------------
1 | import { pick } from 'lodash'
2 |
3 | const orderParams = [
4 | 'number',
5 | 'currency',
6 | 'date_created',
7 | 'discount_total',
8 | 'shipping_total',
9 | 'total',
10 | 'total_tax',
11 | 'billing'
12 | ]
13 |
14 | const productParams = [
15 | 'id',
16 | 'type',
17 | 'slug',
18 | 'name',
19 | 'description',
20 | 'price',
21 | 'regular_price',
22 | 'featured',
23 | 'status',
24 | 'in_stock',
25 | 'virtual'
26 | ]
27 |
28 | const couponParams = ['code', 'amount']
29 |
30 | export const parseOrder = (order = {}, extraParams = []) =>
31 | pick(order, [...orderParams, ...extraParams])
32 |
33 | export const parseProduct = (product = {}, extraParams = []) => ({
34 | ...pick(product, [...productParams, ...extraParams]),
35 | image: pick(product.images[0], ['src', 'alt'])
36 | })
37 |
38 | export const parseCoupon = (coupon = {}, extraParams = []) =>
39 | pick(coupon, [...couponParams, ...extraParams])
40 |
--------------------------------------------------------------------------------
/utils/redirectToHomePage.js:
--------------------------------------------------------------------------------
1 | import Router from 'next/router'
2 |
3 | const HOME_PAGE = '/'
4 |
5 | const redirectToHomePage = res => {
6 | if (res) {
7 | res.writeHead(302, {
8 | Location: HOME_PAGE
9 | })
10 | res.end()
11 | } else {
12 | Router.push(HOME_PAGE)
13 | }
14 | }
15 |
16 | export default redirectToHomePage
17 |
--------------------------------------------------------------------------------
/utils/sanitizeString.js:
--------------------------------------------------------------------------------
1 | const sanitizeString = (string = '') => string.replace(/\W/g, '')
2 |
3 | export default sanitizeString
4 |
--------------------------------------------------------------------------------
/utils/scrollToTop.js:
--------------------------------------------------------------------------------
1 | export default function scrollToTop(behavior) {
2 | window.scroll({ top: 0, left: 0, behavior })
3 | }
4 |
--------------------------------------------------------------------------------
/utils/smoothScroll.js:
--------------------------------------------------------------------------------
1 | const smoothScroll = selector => event => {
2 | event && event.preventDefault()
3 |
4 | document.querySelector(selector).scrollIntoView({
5 | behavior: 'smooth'
6 | })
7 | }
8 |
9 | export default smoothScroll
10 |
--------------------------------------------------------------------------------
/utils/validateEmail.js:
--------------------------------------------------------------------------------
1 | import boom from '@hapi/boom'
2 |
3 | const validateEmail = email => {
4 | if (!/[\w+.]+@[\w]+\.[\w]{2,}/.test(email)) {
5 | throw boom.forbidden('Forbiden customer email').output.payload
6 | }
7 |
8 | return true
9 | }
10 |
11 | export default validateEmail
12 |
--------------------------------------------------------------------------------
/utils/verifyOrderKey.js:
--------------------------------------------------------------------------------
1 | import boom from '@hapi/boom'
2 | import sanitizeString from './sanitizeString'
3 |
4 | const verifyOrderKey = (order, key) => {
5 | if (sanitizeString(order.order_key) === `wc_order_${sanitizeString(key)}`) {
6 | return order
7 | }
8 |
9 | throw boom.forbidden('Forbiden order').output.payload
10 | }
11 |
12 | export default verifyOrderKey
13 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "build": {
4 | "env": {
5 | "GOOGLE_ANALYTICS_ID": "@cssconfco-google-analytics-id",
6 | "CLIENT_URL": "@cssconfco-client-url",
7 | "API_URL": "@cssconfco-api-url",
8 | "EPAYCO_VALIDATE_URL": "@cssconfco-epayco-validate-url",
9 | "EPAYCO_PUBLIC_KEY": "@cssconfco-epayco-public-key",
10 | "EPAYCO_ENV": "@cssconfco-epayco-env"
11 | }
12 | },
13 | "env": {
14 | "MAILCHIMP_API_KEY": "@cssconfco-mailchimp-api-key",
15 | "MAILCHIMP_AUDIENCE_ID": "@cssconfco-mailchimp-audience-id",
16 | "WP_CONSUMER_KEY": "@cssconfco-wp-consumer-key",
17 | "WP_CONSUMER_SECRET": "@cssconfco-wp-consumer-secret",
18 | "WORDPRESS_URL": "@cssconfco-wordpress-url",
19 | "EPAYCO_CLIENT_ID": "@cssconfco-epayco-client-id",
20 | "EPAYCO_SECRET_KEY": "@cssconfco-epayco-secret-key"
21 | }
22 | }
--------------------------------------------------------------------------------