├── .env.example
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── README.md
├── package-lock.json
├── package.json
├── public
├── 404.png
├── _redirects
├── avatar.svg
├── black_logo.png
├── companies
│ ├── colorbeats.png
│ ├── ela-sustentable.png
│ ├── horus-sc.svg
│ ├── koombea.png
│ ├── powerpay.png
│ └── simple-legal.jpg
├── favicon.png
├── favicon.svg
├── hero.svg
├── hover_avatar.svg
├── index.html
├── manifest.json
├── projects
│ ├── horushotel.png
│ └── shopylive.png
├── robots.txt
├── success.svg
└── white_logo.png
├── src
├── App.js
├── App.test.js
├── components
│ ├── avatar
│ │ └── index.js
│ ├── button
│ │ └── index.js
│ ├── container
│ │ └── index.js
│ ├── footer
│ │ └── index.js
│ ├── header
│ │ ├── drawer.js
│ │ └── index.js
│ ├── image
│ │ └── index.js
│ ├── layout
│ │ └── index.js
│ ├── link
│ │ └── index.js
│ ├── list
│ │ └── index.js
│ ├── seo
│ │ └── index.js
│ └── typography
│ │ └── index.js
├── index.css
├── index.js
├── pages
│ ├── 404
│ │ └── index.js
│ ├── about
│ │ ├── card.js
│ │ └── index.js
│ ├── contact
│ │ └── index.js
│ ├── landing
│ │ ├── index.js
│ │ └── paper.js
│ └── success
│ │ └── index.js
├── serviceWorker.js
├── services
│ ├── careerInfo.js
│ ├── companies.js
│ ├── projects.js
│ └── router.js
└── setupTests.js
└── yarn.lock
/.env.example:
--------------------------------------------------------------------------------
1 | REACT_APP_BASE_URL = http://localhost:3000
2 | REACT_APP_REQUEST_URL = https://getform.io/
3 | REACT_APP_RESUME_URL = https://docs.google.com/
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 |
2 | src/registerServiceWorker.js
3 | node_modules/*
4 | public/*
5 | build/*
6 | config/*
7 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['airbnb', 'plugin:prettier/recommended', 'prettier/react'],
3 | env: {
4 | browser: true,
5 | commonjs: true,
6 | es6: true,
7 | jest: true,
8 | node: true,
9 | },
10 | rules: {
11 | 'import/prefer-default-export': 'off',
12 | 'react/prop-types': 'off',
13 | 'react/jsx-props-no-spreading': 'off',
14 | 'no-nested-ternary': 'off',
15 | 'jsx-a11y/href-no-hash': ['off'],
16 | 'react/jsx-filename-extension': ['warn', { extensions: ['.js', '.jsx'] }],
17 | 'max-len': [
18 | 'warn',
19 | {
20 | code: 80,
21 | tabWidth: 2,
22 | comments: 80,
23 | ignoreComments: false,
24 | ignoreTrailingComments: true,
25 | ignoreUrls: true,
26 | ignoreStrings: true,
27 | ignoreTemplateLiterals: true,
28 | ignoreRegExpLiterals: true,
29 | },
30 | ],
31 | 'no-use-before-define': 'off',
32 | 'import/no-unresolved': 'off',
33 | 'import/extensions': 'off',
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "trailingComma": "all"
5 | }
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `yarn start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ## Deployment
16 |
17 | This website is deployed through Netlify in [https://dsantiagomj.dev](dsantiagomj.dev).
18 |
19 | ## ENV File
20 |
21 | This project has 3 env variables. When cloned, the project will contain an env.example file with the variables, for security reasons they have default values, this won't prevent the project to launch, but will limit it's
22 | functionallity, although you can assign your own values to the variables in order to the project fully works.
23 |
24 | - **REACT_APP_BASE_URL**: localhost:3000 by default, you can replace it with your custom domain URL.
25 |
26 | - **REACT_APP_REQUEST_URL**: this project uses [https://getform.io/](getform) as endpoint for the contact page, you can create your own endpoint and set it in this variable or use another service.
27 |
28 | - **REACT_APP_RESUME_URL**: Link to your CV.
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-portfolio",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reach/router": "^1.3.3",
7 | "@testing-library/jest-dom": "^4.2.4",
8 | "@testing-library/react": "^9.3.2",
9 | "@testing-library/user-event": "^7.1.2",
10 | "eslint-config-prettier": "^6.11.0",
11 | "eslint-import-resolver-webpack": "^0.12.1",
12 | "eslint-plugin-prettier": "^3.1.3",
13 | "prettier": "^2.0.5",
14 | "prop-types": "^15.7.2",
15 | "react": "^16.13.1",
16 | "react-dom": "^16.13.1",
17 | "react-helmet": "^6.1.0",
18 | "react-icons": "^3.10.0",
19 | "react-scripts": "3.4.1",
20 | "styled-components": "^5.1.1"
21 | },
22 | "scripts": {
23 | "start": "react-scripts start",
24 | "build": "react-scripts build",
25 | "test": "react-scripts test",
26 | "lint": "eslint src -c .eslintrc.json --ext js,jsx",
27 | "eject": "react-scripts eject"
28 | },
29 | "eslintConfig": {
30 | "extends": "react-app"
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | },
44 | "devDependencies": {
45 | "eslint": "^6.8.0",
46 | "eslint-config-airbnb": "^18.1.0",
47 | "eslint-plugin-import": "^2.21.2",
48 | "eslint-plugin-jsx-a11y": "^6.2.3",
49 | "eslint-plugin-react": "^7.20.0",
50 | "eslint-plugin-react-hooks": "^2.5.1"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/public/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/404.png
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
2 |
--------------------------------------------------------------------------------
/public/avatar.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/black_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/black_logo.png
--------------------------------------------------------------------------------
/public/companies/colorbeats.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/companies/colorbeats.png
--------------------------------------------------------------------------------
/public/companies/ela-sustentable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/companies/ela-sustentable.png
--------------------------------------------------------------------------------
/public/companies/horus-sc.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
101 |
--------------------------------------------------------------------------------
/public/companies/koombea.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/companies/koombea.png
--------------------------------------------------------------------------------
/public/companies/powerpay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/companies/powerpay.png
--------------------------------------------------------------------------------
/public/companies/simple-legal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/companies/simple-legal.jpg
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/favicon.png
--------------------------------------------------------------------------------
/public/hero.svg:
--------------------------------------------------------------------------------
1 |
2 |
96 |
--------------------------------------------------------------------------------
/public/hover_avatar.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Santiago M | Software developer
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.svg",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "favicon.svg",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "favicon.svg",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/projects/horushotel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/projects/horushotel.png
--------------------------------------------------------------------------------
/public/projects/shopylive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/projects/shopylive.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/success.svg:
--------------------------------------------------------------------------------
1 |
2 |
40 |
--------------------------------------------------------------------------------
/public/white_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dsantiagomj/react-portfolio/894b6b96ea69398c3c314b8fca4ecfba1242bffb/public/white_logo.png
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Router from './services/router';
4 |
5 | function App() {
6 | return ;
7 | }
8 |
9 | export default App;
10 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render();
7 | const linkElement = getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/avatar/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 |
6 | const Avatar = styled.div`
7 | background-image: url(${(props) => props.defaultImage});
8 | height: 280px;
9 | margin: 0 auto;
10 | margin-top: 2rem;
11 | transition: background-image 0.2s ease-in-out;
12 | width: 260px;
13 | &:hover {
14 | background-image url(${(props) => props.hoverImage || props.defaultImage});
15 | }
16 | `;
17 |
18 | const AvatarComponent = ({ defaultImage, hoverImage }) => (
19 |
20 | );
21 |
22 | AvatarComponent.propTypes = {
23 | defaultImage: PropTypes.string.isRequired,
24 | hoverImage: PropTypes.string,
25 | };
26 |
27 | AvatarComponent.defaultProps = {
28 | hoverImage: '',
29 | };
30 |
31 | export default AvatarComponent;
32 |
--------------------------------------------------------------------------------
/src/components/button/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 |
6 | const DefaultButtonComponent = styled.input`
7 | background: transparent;
8 | border: solid 2px #0bd8a2;
9 | border-radius: 0.25rem;
10 | color: #0bd8a2;
11 | margin: 0 auto;
12 | min-width: ${(props) => props.minWidth};
13 | padding: 0.5rem;
14 | &:hover {
15 | background: #0bd8a2;
16 | color: white;
17 | cursor: pointer;
18 | }
19 | `;
20 |
21 | const TransitionButtonComponent = styled(DefaultButtonComponent)`
22 | transition: padding 0.3s ease-in-out;
23 | &:hover {
24 | padding: 0.5rem 1rem;
25 | }
26 | `;
27 |
28 | export const DefaultButton = ({ value, type, minWidth }) => (
29 |
30 | );
31 |
32 | DefaultButton.propTypes = {
33 | value: PropTypes.string.isRequired,
34 | type: PropTypes.string,
35 | minWidth: PropTypes.string,
36 | };
37 |
38 | DefaultButton.defaultProps = {
39 | type: 'submit',
40 | minWidth: 'auto',
41 | };
42 |
43 | export const TransitionButton = ({ value, type, minWidth }) => (
44 |
45 | );
46 |
47 | TransitionButton.propTypes = {
48 | value: PropTypes.string.isRequired,
49 | type: PropTypes.string,
50 | minWidth: PropTypes.string,
51 | };
52 |
53 | TransitionButton.defaultProps = {
54 | type: 'submit',
55 | minWidth: 'auto',
56 | };
57 |
--------------------------------------------------------------------------------
/src/components/container/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 |
6 | const ContentWrapperComponent = styled.div`
7 | animation: fadeInAnimation ease 0.75s;
8 | animation-iteration-count: 1;
9 | animation-fill-mode: forwards;
10 | display: flex;
11 | flex-direction: column;
12 | height: 100vh;
13 | justify-content: space-between;
14 | width: 100vw;
15 | @keyframes fadeInAnimation {
16 | 0% {
17 | opacity: 0;
18 | }
19 | 100% {
20 | opacity: 1;
21 | }
22 | }
23 | `;
24 |
25 | const CenterContentWrapperComponent = styled(ContentWrapperComponent)`
26 | animation: fadeInAnimation ease 1s;
27 | align-items: center;
28 | justify-content: center;
29 | overflow: hidden;
30 | `;
31 |
32 | export const ContentWrapper = ({ children }) => (
33 | {children}
34 | );
35 | ContentWrapper.propTypes = {
36 | children: PropTypes.node.isRequired,
37 | };
38 |
39 | export const CenterContentWrapper = ({ children }) => (
40 | {children}
41 | );
42 | CenterContentWrapper.propTypes = {
43 | children: PropTypes.node.isRequired,
44 | };
45 |
--------------------------------------------------------------------------------
/src/components/footer/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from '@reach/router';
3 |
4 | import styled from 'styled-components';
5 | import { AiOutlineMail, AiOutlineLinkedin, AiFillGithub } from 'react-icons/ai';
6 | import { GrReactjs } from 'react-icons/gr';
7 |
8 | import Image from '../image';
9 | import { DefaultButton } from '../button';
10 | import { Typography } from '../typography';
11 |
12 | const Container = styled.div`
13 | background-color: #00343d;
14 | margin-top: 5rem;
15 | padding: 4rem 0;
16 | `;
17 | const Paper = styled.div`
18 | align-items: center;
19 | background: #141c3a;
20 | border-radius: 12px;
21 | box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px #141c3a;
22 | display: flex;
23 | flex-direction: column;
24 | justify-content: space-evenly;
25 | margin: 0 auto;
26 | margin-top: -9rem;
27 | padding: 3.5rem 1.25rem;
28 | max-width: 60rem;
29 | width: 90%;
30 | @media (min-width: 1024px) {
31 | flex-direction: row;
32 | }
33 | `;
34 | const ImageWrapper = styled.div`
35 | display: flex;
36 | justify-content: center;
37 | margin-top: 5rem;
38 | margin-bottom: 1rem;
39 | width: 100vw;
40 | `;
41 |
42 | const List = styled.ul`
43 | align-items: center;
44 | display: flex;
45 | justify-content: space-evenly;
46 | list-style: none;
47 | margin: auto;
48 | margin-bottom: 2rem;
49 | margin-top: 2rem;
50 | max-width: 25rem;
51 | `;
52 | const ListItem = styled.li`
53 | border: solid 2px rgba(255, 255, 255, 0.3);
54 | border-radius: 100%;
55 | color: white;
56 | height: 48px;
57 | position: relative;
58 | transition: background-color 0.1s ease-in-out;
59 | width: 48px;
60 | &:hover {
61 | background-color: white;
62 | color: #0bd8a2;
63 | }
64 | `;
65 | const Icon = styled.div`
66 | align-items: center;
67 | display: inline-flex;
68 | font-size: 1.25rem;
69 | justify-content: center;
70 | position: absolute;
71 | top: 12px;
72 | left: 12px;
73 | @media (min-width: 768px) {
74 | top: 10px;
75 | left: 10px;
76 | }
77 | `;
78 | const Copyright = styled.div`
79 | align-items: center;
80 | display: flex;
81 | font-weight: 600;
82 | justify-content: center;
83 | `;
84 |
85 | const Footer = ({ location }) => {
86 | const date = new Date();
87 | const currentYear = date.getFullYear();
88 |
89 | return (
90 |
91 | {location !== '/contact' && (
92 |
93 |
99 | Start a project
100 |
101 |
107 | Interested in working together? Let's schedule a call.
108 |
109 |
110 |
111 |
112 |
113 | )}
114 |
115 |
121 |
122 |
123 | Living, learning & leveling up one day at a time.
124 |
125 |
126 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | Handcrafted by @dsantiagomj - © {currentYear}
159 |
160 |
161 |
162 |
163 | Made with{' '}
164 |
165 |
174 |
175 |
176 | );
177 | };
178 |
179 | export default Footer;
180 |
--------------------------------------------------------------------------------
/src/components/header/drawer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from '@reach/router';
3 |
4 | import styled from 'styled-components';
5 |
6 | import { DefaultButton } from '../button';
7 |
8 | const ResponsiveMenu = styled.ul`
9 | align-items: center;
10 | background: white;
11 | box-shadow: 0 5px 5px 0 rgba(233, 240, 243, 0.5), 0 1px 0 0 #e6ecf8;
12 | display: flex;
13 | flex-direction: column;
14 | height: calc(40vh - 6rem);
15 | justify-content: space-evenly;
16 | left: 0;
17 | list-style: none;
18 | margin: ${(props) => (props.isOpen ? '0 auto' : '-2500px')};
19 | position: absolute;
20 | top: 5rem;
21 | transition: margin 0.5s ease-in-out;
22 | width: 100vw;
23 | z-index: 2;
24 | @media (min-width: 1024px) {
25 | display: none;
26 | }
27 | `;
28 |
29 | const ResponsiveItem = styled.li`
30 | text-align: center;
31 | width: 100vw;
32 | &:hover {
33 | color: #0bd8a2;
34 | cursor: pointer;
35 | }
36 | `;
37 |
38 | const Drawer = ({ isOpen }) => (
39 |
40 |
47 | About
48 |
49 |
56 |
57 |
58 |
59 |
60 |
61 | );
62 |
63 | export default Drawer;
64 |
--------------------------------------------------------------------------------
/src/components/header/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Link } from '@reach/router';
3 |
4 | import styled from 'styled-components';
5 | import { AiOutlineMenu, AiOutlineClose } from 'react-icons/ai';
6 |
7 | import Drawer from './drawer';
8 | import { DefaultButton } from '../button';
9 |
10 | const Container = styled.div`
11 | align-items: center;
12 | background: white;
13 | display: flex;
14 | height: 5rem;
15 | justify-content: space-between;
16 | margin: 0 auto;
17 | margin-bottom: 1.45rem;
18 | max-width: 80rem;
19 | padding: 0 0.5rem;
20 | padding-top: 3rem;
21 | width: 100%;
22 | zindex: 3;
23 | @media (min-width: 1024px) {
24 | padding-top: 2.5rem;
25 | }
26 | `;
27 |
28 | const Menu = styled.ul`
29 | display: none;
30 | list-style: none;
31 | margin: 0;
32 | width: 16rem;
33 | @media (min-width: 1024px) {
34 | align-items: center;
35 | display: flex;
36 | justify-content: space-around;
37 | }
38 | `;
39 |
40 | const MenuItem = styled.li`
41 | color: #00343d;
42 | font-size: 0.875rem;
43 | margin: 0;
44 | text-decoration: none;
45 | &:hover {
46 | color: #0bd8a2;
47 | cursor: pointer;
48 | }
49 | `;
50 |
51 | const ResponsiveButton = styled.button`
52 | background: transparent;
53 | border: none;
54 | cursor: pointer;
55 | display: block;
56 | font-size: 1.5rem;
57 | z-index: 3;
58 | @media (min-width: 1024px) {
59 | display: none;
60 | }
61 | `;
62 |
63 | const Header = () => {
64 | // responsive navbar status
65 | const [navbarStatus, setNavbarStatus] = useState(false);
66 |
67 | return (
68 |
69 |
77 |
83 |
84 |
106 |
107 | setNavbarStatus(!navbarStatus)}>
108 | {navbarStatus ? : }
109 |
110 |
111 |
112 |
113 | );
114 | };
115 |
116 | export default Header;
117 |
--------------------------------------------------------------------------------
/src/components/image/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 |
6 | const ImageComponent = styled.img`
7 | height: ${(props) => props.height};
8 | margin: 0;
9 | max-height: ${(props) => props.maxHeight};
10 | max-width: ${(props) => props.maxWidth};
11 | min-height: ${(props) => props.minHeight};
12 | min-width: ${(props) => props.minWidth};
13 | padding: 0;
14 | width: ${(props) => props.width};
15 | `;
16 |
17 | const Image = ({
18 | src,
19 | alt,
20 | height,
21 | width,
22 | maxHeight,
23 | maxWidth,
24 | minHeight,
25 | minWidth,
26 | }) => (
27 |
37 | );
38 |
39 | Image.propTypes = {
40 | src: PropTypes.string.isRequired,
41 | alt: PropTypes.string.isRequired,
42 | };
43 |
44 | Image.defaultProps = {
45 | height: 'auto',
46 | maxHeight: 'auto',
47 | minHeight: 'auto',
48 | maxWidth: 'auto',
49 | minWidth: 'auto',
50 | width: 'auto',
51 | };
52 |
53 | export default Image;
54 |
--------------------------------------------------------------------------------
/src/components/layout/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 |
6 | import Header from '../header';
7 | import Footer from '../footer';
8 | import { ContentWrapper } from '../container';
9 |
10 | const Main = styled.main`
11 | margin: 0 auto;
12 | `;
13 |
14 | const Layout = ({ children, location }) => {
15 | return (
16 |
17 |
18 | {children}
19 |
20 |
21 | );
22 | };
23 |
24 | Layout.propTypes = {
25 | children: PropTypes.node.isRequired,
26 | };
27 |
28 | export default Layout;
29 |
--------------------------------------------------------------------------------
/src/components/link/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 |
6 | const LinkComponent = styled.a`
7 | cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')};
8 | color: #0bd8a2;
9 | margin: 0 auto;
10 | text-decoration: none;
11 | `;
12 |
13 | const Link = ({ children, href, disabled }) => (
14 |
20 | {children}
21 |
22 | );
23 |
24 | Link.propTypes = {
25 | children: PropTypes.node.isRequired,
26 | href: PropTypes.string.isRequired,
27 | disabled: PropTypes.bool,
28 | };
29 |
30 | Link.defaultProps = {
31 | disabled: false,
32 | };
33 |
34 | export default Link;
35 |
--------------------------------------------------------------------------------
/src/components/list/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 |
6 | const ListWrapperComponent = styled.div`
7 | align-items: center;
8 | display: flex;
9 | flex-wrap: wrap;
10 | justify-content: ${(props) => props.justifyContent};
11 | margin: 5rem auto;
12 | max-width: ${(props) => props.maxWidth};
13 | `;
14 |
15 | const ListWrapper = ({ children, justifyContent, maxWidth }) => (
16 |
17 | {children}
18 |
19 | );
20 |
21 | ListWrapper.propTypes = {
22 | children: PropTypes.node.isRequired,
23 | justifyContent: PropTypes.string,
24 | maxWidth: PropTypes.string,
25 | };
26 |
27 | ListWrapper.defaultProps = {
28 | justifyContent: 'center',
29 | maxWidth: 'auto',
30 | };
31 |
32 | export default ListWrapper;
33 |
--------------------------------------------------------------------------------
/src/components/seo/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Helmet } from 'react-helmet';
4 |
5 | const SEO = ({ description, lang, meta, title }) => (
6 |
46 | );
47 |
48 | SEO.defaultProps = {
49 | lang: `en`,
50 | meta: [],
51 | description: ``,
52 | };
53 |
54 | SEO.propTypes = {
55 | description: PropTypes.string,
56 | lang: PropTypes.string,
57 | meta: PropTypes.arrayOf(PropTypes.object),
58 | title: PropTypes.string.isRequired,
59 | };
60 |
61 | export default SEO;
62 |
--------------------------------------------------------------------------------
/src/components/typography/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 |
6 | const TitleComponent = styled.h2`
7 | color: ${(props) => props.color};
8 | font-size: ${(props) => props.fontSize};
9 | font-weight: ${(props) => props.fontWeight};
10 | margin: 1rem 0;
11 | text-align: center;
12 | `;
13 |
14 | const TypographyComponent = styled.p`
15 | color: ${(props) => props.color};
16 | font-size: ${(props) => props.fontSize};
17 | font-weight: ${(props) => props.fontWeight};
18 | margin: ${(props) => props.margin};
19 | text-align: ${(props) => props.textAlign};
20 | `;
21 |
22 | export const Title = ({ text, color, fontSize, fontWeight, children }) => (
23 |
24 | {text || children}
25 |
26 | );
27 |
28 | Title.propTypes = {
29 | text: PropTypes.string,
30 | color: PropTypes.string,
31 | fontSize: PropTypes.string,
32 | fontWeight: PropTypes.number,
33 | children: PropTypes.node,
34 | };
35 |
36 | Title.defaultProps = {
37 | text: '',
38 | color: '#00343d',
39 | fontSize: '1rem',
40 | fontWeight: 500,
41 | children: '',
42 | };
43 |
44 | export const Typography = ({
45 | text,
46 | color,
47 | fontSize,
48 | fontWeight,
49 | textAlign,
50 | margin,
51 | children,
52 | }) => (
53 |
60 | {text || children}
61 |
62 | );
63 |
64 | Typography.propTypes = {
65 | text: PropTypes.string,
66 | color: PropTypes.string,
67 | fontSize: PropTypes.string,
68 | fontWeight: PropTypes.number,
69 | textAlign: PropTypes.string,
70 | margin: PropTypes.string,
71 | children: PropTypes.node,
72 | };
73 |
74 | Typography.defaultProps = {
75 | text: '',
76 | color: '#00343d',
77 | fontSize: '1rem',
78 | fontWeight: 500,
79 | textAlign: 'left',
80 | margin: '0 0 1em',
81 | children: '',
82 | };
83 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want your app to work offline and load faster, you can change
15 | // unregister() to register() below. Note this comes with some pitfalls.
16 | // Learn more about service workers: https://bit.ly/CRA-PWA
17 | serviceWorker.unregister();
18 |
--------------------------------------------------------------------------------
/src/pages/404/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from '@reach/router';
3 |
4 | import Image from '../../components/image';
5 | import { CenterContentWrapper } from '../../components/container';
6 | import { Title, Typography } from '../../components/typography';
7 | import { DefaultButton } from '../../components/button';
8 |
9 | const NotFoundPage = () => (
10 |
11 |
12 |
13 |
14 | You just hit a route that doesn't exist... the sadness.
15 |
16 |
17 |
18 |
19 |
20 | );
21 |
22 | export default NotFoundPage;
23 |
--------------------------------------------------------------------------------
/src/pages/about/card.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 | import { GoTools } from 'react-icons/go';
6 |
7 | import Image from '../../components/image';
8 | import Link from '../../components/link';
9 | import { Typography } from '../../components/typography';
10 |
11 | const CardWrapper = styled.div`
12 | align-items: center;
13 | border: 1px solid #e6ecf8;
14 | border-radius: 12px;
15 | box-shadow: 0 5px 5px 0 rgba(233, 240, 243, 0.5), 0 1px 0 0 #e6ecf8;
16 | display: flex;
17 | flex-direction: column;
18 | height: 25.5rem;
19 | justify-content: space-around;
20 | margin: 1rem;
21 | max-width: 21rem;
22 | padding: 2rem;
23 | text-align: center;
24 | width: 100%;
25 | `;
26 | const ImageWrapper = styled.div`
27 | align-items: center;
28 | display: flex;
29 | justify-content: center;
30 | height: 6rem;
31 | `;
32 | const CardContent = styled.div`
33 | display: flex;
34 | flex-direction: column;
35 | justify-content: center;
36 | height: 10rem;
37 | `;
38 |
39 | const Card = ({ image, descriptions, link, id }) => (
40 |
41 |
42 |
48 |
49 |
50 | {descriptions.map((description) => (
51 |
52 | {description}
53 |
54 | ))}
55 |
56 |
62 | {link.isDisabled ? (
63 |
64 | In development
65 |
66 | ) : (
67 |
68 | {link.textLink}
69 |
70 | )}
71 |
72 |
73 | );
74 |
75 | Card.propTypes = {
76 | image: PropTypes.shape({
77 | src: PropTypes.string.isRequired,
78 | alt: PropTypes.string.isRequired,
79 | }).isRequired,
80 | descriptions: PropTypes.arrayOf(PropTypes.string).isRequired,
81 | link: PropTypes.shape({
82 | href: PropTypes.string.isRequired,
83 | isDisabled: PropTypes.bool.isRequired,
84 | textLink: PropTypes.string.isRequired,
85 | }).isRequired,
86 | id: PropTypes.number.isRequired,
87 | };
88 |
89 | export default Card;
90 |
--------------------------------------------------------------------------------
/src/pages/about/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from '@reach/router';
3 |
4 | import styled from 'styled-components';
5 |
6 | import projects from '../../services/projects';
7 | import companies from '../../services/companies';
8 |
9 | import SEO from '../../components/seo';
10 | import Card from './card';
11 | import Layout from '../../components/layout';
12 | import Image from '../../components/image';
13 | import ListWrapper from '../../components/list';
14 | import { Title, Typography } from '../../components/typography';
15 |
16 | const Container = styled.div`
17 | background-color: white;
18 | margin: 0 auto;
19 | min-height: 46rem;
20 | width: 100vw;
21 | `;
22 |
23 | const Wrapper = styled.div`
24 | margin: 0 auto;
25 | margin-top: 5rem;
26 | margin-bottom: 5rem;
27 | max-width: 80rem;
28 | `;
29 |
30 | const TextWrapper = styled.h2`
31 | margin: 0 auto;
32 | max-width: 36rem;
33 | `;
34 |
35 | const CallToAction = styled.span`
36 | color: #0bd8a2;
37 | cursor: pointer;
38 | font-weight: 550;
39 | `;
40 |
41 | const Divider = styled.div`
42 | background-color: #e6ecf8;
43 | height: 1px;
44 | width: 100vw;
45 | `;
46 |
47 | const Company = styled.div`
48 | align-items: center;
49 | display: flex;
50 | justify-content: center;
51 | margin: 2rem 0rem;
52 | width: 200px;
53 | @media (min-width: 968px) {
54 | margin: 1rem;
55 | }
56 | `;
57 |
58 | const About = ({ location: { pathname } }) => {
59 | return (
60 |
61 |
65 |
66 |
67 |
68 |
69 | Here a few projects i've worked on in the past. Want to see
70 | more?
71 |
72 | Contact me
73 |
74 |
75 |
76 | {projects.map((project) => (
77 |
84 | ))}
85 |
86 |
87 |
88 |
89 |
90 |
91 | I'm proud to have collaborated with some awesome companies:
92 |
93 |
94 |
95 | {companies.map((company) => (
96 |
97 |
98 |
99 | ))}
100 |
101 |
102 |
103 |
104 | );
105 | };
106 |
107 | export default About;
108 |
--------------------------------------------------------------------------------
/src/pages/contact/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import styled from 'styled-components';
4 |
5 | import SEO from '../../components/seo';
6 | import Layout from '../../components/layout';
7 | import Avatar from '../../components/avatar';
8 | import { DefaultButton } from '../../components/button';
9 | import { Title } from '../../components/typography';
10 |
11 | const SectionTitle = styled.div`
12 | margin: 0 auto;
13 | max-width: 39rem;
14 | padding: 2rem 0.5rem 0;
15 | `;
16 |
17 | const Form = styled.form`
18 | margin: 5rem auto 10rem;
19 | max-width: 35rem;
20 | min-height: 25rem;
21 | padding: 0 1.5rem;
22 | text-align: center;
23 | `;
24 |
25 | const Row = styled.div`
26 | align-items: center;
27 | display: flex;
28 | flex-direction: column;
29 | justify-content: center;
30 | @media (min-width: 768px) {
31 | flex-direction: row;
32 | justify-content: space-between;
33 | }
34 | `;
35 |
36 | const FieldWrapper = styled.p`
37 | align-items: flex-start;
38 | display: flex;
39 | flex-direction: column;
40 | margin-right: ${(props) => (props.last ? '0' : '0.5rem')};
41 | width: 100%;
42 | `;
43 | const Label = styled.label`
44 | color: #2833478a;
45 | margin-bottom: 0.75rem;
46 | `;
47 | const Input = styled.input`
48 | background-color: white;
49 | background-clip: padding-box;
50 | border: solid 1px #e6ecf8;
51 | border-radius: 6px;
52 | box-shadow: none;
53 | padding: 0.5rem;
54 | width: 100%;
55 | @media (min-width: 768px) {
56 | max-width: 275px;
57 | }
58 | `;
59 | const TextArea = styled.textarea`
60 | background-color: white;
61 | background-clip: padding-box;
62 | border: solid 1px #e6ecf8;
63 | border-radius: 6px;
64 | box-shadow: none;
65 | max-width: 100%;
66 | min-width: 100%;
67 | width: 100%;
68 | `;
69 |
70 | const Contact = ({ location: { pathname } }) => {
71 | const [name, setName] = useState('');
72 | const [email, setEmail] = useState('');
73 | const [message, setMessage] = useState('');
74 |
75 | const handleSubmit = (event) => {
76 | event.preventDefault();
77 | const formData = new FormData();
78 | formData.append('name', name);
79 | formData.append('email', email);
80 | formData.append('message', message);
81 |
82 | const request = new Request(`${process.env.REACT_APP_REQUEST_URL}`);
83 | const data = {
84 | method: 'POST',
85 | headers: {
86 | Accept: 'application/json',
87 | },
88 | body: formData,
89 | };
90 |
91 | fetch(request, data)
92 | .then(() => {
93 | window.location.replace(`${process.env.REACT_APP_BASE_URL}/success`);
94 | })
95 | .catch((e) => console.error(e));
96 | };
97 |
98 | return (
99 |
100 |
101 |
105 |
106 |
107 | Thanks for taking the time to reach out. How can I help you today?
108 |
109 |
110 |
149 |
150 | );
151 | };
152 |
153 | export default Contact;
154 |
--------------------------------------------------------------------------------
/src/pages/landing/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from '@reach/router';
3 |
4 | import styled from 'styled-components';
5 |
6 | import careerInfo from '../../services/careerInfo';
7 |
8 | import Paper from './paper';
9 |
10 | import SEO from '../../components/seo';
11 | import Layout from '../../components/layout';
12 | import Avatar from '../../components/avatar';
13 | import Image from '../../components/image';
14 | import { TransitionButton } from '../../components/button';
15 | import { Title, Typography } from '../../components/typography';
16 |
17 | const Container = styled.div`
18 | background-color: ${(props) =>
19 | props.variantBackground ? '#20C98B' : 'white'};
20 | margin: 0 auto;
21 | min-height: ${(props) => (props.variantBackground ? '34rem' : '46rem')};
22 | padding: 2rem;
23 | padding-bottom: ${(props) => (props.variantBackground ? '6rem' : '0')};
24 | width: 100vw;
25 | `;
26 | const CenteredContainer = styled(Container)`
27 | display: flex;
28 | `;
29 | const Wrapper = styled.div`
30 | margin: 0 auto;
31 | max-width: 80rem;
32 | `;
33 | const CenteredWrapper = styled(Wrapper)`
34 | display: flex;
35 | flex-direction: column;
36 | justify-content: center;
37 | max-width: 42rem;
38 | `;
39 | const TitleWrapper = styled.div`
40 | margin-top: 4rem;
41 | margin-bottom: 2rem;
42 | `;
43 | const ButtonWrapper = styled.div`
44 | display: flex;
45 | width: 100%;
46 | `;
47 | const ImgWrapper = styled.div`
48 | display: flex;
49 | justify-content: center;
50 | padding-top: 10rem;
51 | `;
52 | const TextWrapper = styled.div`
53 | margin-top: 1rem;
54 | height: 8rem;
55 | `;
56 | const ColumnWrapper = styled.div`
57 | display: flex;
58 | flex-direction: column;
59 | padding-bottom: 10rem;
60 | @media (min-width: 768px) {
61 | align-items: center;
62 | flex-direction: row;
63 | justify-content: center;
64 | }
65 | `;
66 | const PaperComponent = styled.div`
67 | background-color: white;
68 | border-radius: 12px;
69 | box-shadow: 0 5px 5px 0 rgba(233, 240, 243, 0.5), 0 0 0 1px #e6ecf8;
70 | display: flex;
71 | flex-direction: column;
72 | justify-content: center;
73 | margin: 0 auto;
74 | margin-top: -9rem;
75 | width: 100%;
76 | @media (min-width: 1080px) {
77 | flex-direction: row;
78 | padding: 3rem 1rem 1.5rem;
79 | }
80 | `;
81 | const Column = styled.div`
82 | border-bottom: ${(props) =>
83 | props.lastColumn ? '0px solid #e6ecf8' : '1px solid #e6ecf8'};
84 | display: flex;
85 | flex-direction: column;
86 | height: 12rem;
87 | padding-bottom: 2rem;
88 | padding-top: 1rem;
89 | @media (min-width: 768px) {
90 | border-bottom: 0px solid #e6ecf8;
91 | border-right: ${(props) =>
92 | props.lastColumn ? '0px solid #e6ecf8' : '1px solid #e6ecf8'};
93 | width: 30rem;
94 | }
95 | `;
96 |
97 | const titleText = '< Hello World />';
98 |
99 | const LandingPage = ({ location: { pathname } }) => (
100 |
101 |
105 |
106 |
107 |
108 |
109 | {titleText}
110 |
111 |
112 |
113 | I'm a Web Developer, passionate about what I do, and always
114 | learning something new.
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | Hi, I'm Santiago. Pleasure to meet you.
127 |
128 |
129 | Coming from Barranquilla, Colombia, I started my journey as a
130 | developer in 2018. I have special interest in front-end and mobile
131 | development, where I have worked most of my projects and potentialized
132 | my skills. A fan of learning and interacting with new technologies,
133 | these are the skills I have learned and improved during my journey.
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | {careerInfo.map((data, index) => (
142 |
150 | ))}
151 |
152 |
153 |
154 | Learn more about me
155 |
156 |
157 |
158 |
159 |
160 |
161 | Check some of the projects and companies i've been involved
162 | with.
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 | Or download a summary of my work.
175 |
176 |
177 |
178 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | );
193 |
194 | export default LandingPage;
195 |
--------------------------------------------------------------------------------
/src/pages/landing/paper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import styled from 'styled-components';
5 |
6 | import { Title, Typography } from '../../components/typography';
7 |
8 | const PaperColumn = styled.div`
9 | border-bottom: ${(props) =>
10 | props.lastColumn ? '0px solid #e6ecf8' : '1px solid #e6ecf8'};
11 | margin: 0 auto;
12 | padding: 2rem;
13 | width: 100%;
14 | @media (min-width: 1080px) {
15 | border-bottom: 0px solid #e6ecf8;
16 | border-right: ${(props) =>
17 | props.lastColumn ? '0px solid #e6ecf8' : '1px solid #e6ecf8'};
18 | flex-direction: row;
19 | }
20 | `;
21 | const PaperIcon = styled.div`
22 | color: #0bd8a2;
23 | font-size: 54px;
24 | margin-bottom: 1.45rem;
25 | text-align: center;
26 | `;
27 |
28 | const Paper = ({
29 | icon,
30 | columnTitle,
31 | description,
32 | knowledgeList,
33 | lastColumn,
34 | }) => (
35 |
36 | {icon}
37 | {columnTitle}
38 |
39 | {description}
40 |
41 |
42 | Languages & tools I've learned and used:
43 |
44 |
45 | {knowledgeList}
46 |
47 |
48 | );
49 |
50 | Paper.propTypes = {
51 | icon: PropTypes.node.isRequired,
52 | columnTitle: PropTypes.string.isRequired,
53 | description: PropTypes.string.isRequired,
54 | knowledgeList: PropTypes.string.isRequired,
55 | lastColumn: PropTypes.bool.isRequired,
56 | };
57 |
58 | export default Paper;
59 |
--------------------------------------------------------------------------------
/src/pages/success/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from '@reach/router';
3 |
4 | import Image from '../../components/image';
5 | import { CenterContentWrapper } from '../../components/container';
6 | import { DefaultButton } from '../../components/button';
7 | import { Title, Typography } from '../../components/typography';
8 |
9 | const Success = () => (
10 |
11 |
12 | Message received. Thanks!
13 |
14 | I'll be in touch with you shortly.
15 |
16 |
17 |
18 |
19 |
20 | );
21 |
22 | export default Success;
23 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { 'Service-Worker': 'script' },
105 | })
106 | .then(response => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get('content-type');
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf('javascript') === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then(registration => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | 'No internet connection found. App is running in offline mode.'
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ('serviceWorker' in navigator) {
133 | navigator.serviceWorker.ready
134 | .then(registration => {
135 | registration.unregister();
136 | })
137 | .catch(error => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/services/careerInfo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AiOutlineMobile } from 'react-icons/ai';
3 | import { BsTerminal } from 'react-icons/bs';
4 | import { GoBrowser } from 'react-icons/go';
5 |
6 | const careerInfo = [
7 | {
8 | id: 1,
9 | icon: ,
10 | title: 'Mobile Development',
11 | description:
12 | 'I have crossed ways with this path a couple of times. I'm very curious about this and i'm looking forward to digging into it.',
13 | knowledgeList: 'JavaScript, React Native, Expo, Cordova',
14 | },
15 | {
16 | id: 2,
17 | icon: ,
18 | title: 'Frontend Development',
19 | description:
20 | 'My main path, I've worked mostly as a frontend developer and I'm loving it. I like to bring ideas to life in the browser.',
21 | knowledgeList:
22 | 'HTML5, CSS3, Sass, BEM, JavaScript, JQuery, ReactJs, Redux, GatsbyJs, Shopify',
23 | },
24 | {
25 | id: 3,
26 | icon: ,
27 | title: 'Backend Develoment & Others',
28 | description:
29 | 'Technologies that I've learned on my journey. Some of them crucial on my day by day, others looking forward to using again.',
30 | knowledgeList:
31 | 'Terminal, Git, Github, Ruby, Ruby on Rails, JavaScript, NodeJs, ExpressJs, Python, Django, MongoDB, PostgreSQL.',
32 | },
33 | ];
34 |
35 | export default careerInfo;
36 |
--------------------------------------------------------------------------------
/src/services/companies.js:
--------------------------------------------------------------------------------
1 | const companies = [
2 | {
3 | id: 1,
4 | name: 'Horus Smart Control',
5 | logo: '/companies/horus-sc.svg',
6 | },
7 | {
8 | id: 2,
9 | name: 'Ela Sustentable',
10 | logo: '/companies/ela-sustentable.png',
11 | },
12 | {
13 | id: 3,
14 | name: 'Simple Legal',
15 | logo: '/companies/simple-legal.jpg',
16 | },
17 | {
18 | id: 4,
19 | name: 'Colorbeats',
20 | logo: '/companies/colorbeats.png',
21 | },
22 | {
23 | id: 5,
24 | name: 'Koombea',
25 | logo: '/companies/koombea.png',
26 | },
27 | ];
28 |
29 | export default companies;
30 |
--------------------------------------------------------------------------------
/src/services/projects.js:
--------------------------------------------------------------------------------
1 | const projects = [
2 | {
3 | id: 1,
4 | image: {
5 | src: '/projects/horushotel.png',
6 | alt: 'Horus Hotel',
7 | },
8 | description: [
9 | 'Mobile application using Apache Cordova and Laravel.',
10 | 'Connection to Firebase for push notifications and deployed on Google Cloud.',
11 | ],
12 | link: {
13 | href: 'https://www.horus-sc.com',
14 | isDisabled: false,
15 | textLink: 'www.horus-sc.com',
16 | },
17 | },
18 | {
19 | id: 2,
20 | image: {
21 | src: '/projects/shopylive.png',
22 | alt: 'Shopylive',
23 | },
24 | description: [
25 | 'Mobile application using React Native and NodeJs.',
26 | 'Sync with Firebase for OAuth and push notifications.',
27 | ],
28 | link: {
29 | href: 'https://www.shopylive.com',
30 | isDisabled: false,
31 | textLink: 'www.shopylive.com',
32 | },
33 | },
34 | {
35 | id: 3,
36 | image: {
37 | src: '/companies/simple-legal.jpg',
38 | alt: 'SimpleLegal',
39 | },
40 | description: [
41 | 'Single page application using ReactJS.',
42 | 'Firebase Kit for Real time database, auth and cloud functions.',
43 | ],
44 | link: {
45 | href: '',
46 | isDisabled: true,
47 | textLink: '',
48 | },
49 | },
50 | {
51 | id: 4,
52 | image: {
53 | src: '/companies/ela-sustentable.png',
54 | alt: 'Ela Sustentable',
55 | },
56 | description: [
57 | 'Single page application using ReactJS.',
58 | 'Django & Django rest framework for Rest API.',
59 | ],
60 | link: {
61 | href: 'https://www.elasustentable.com',
62 | isDisabled: false,
63 | textLink: 'www.elsasustentable.com',
64 | },
65 | },
66 | {
67 | id: 5,
68 | image: {
69 | src: '/companies/colorbeats.png',
70 | alt: 'Colorbeats',
71 | },
72 | description: ['E-Commerce webpage using Shopify.'],
73 | link: {
74 | href: 'https://www.colorbeats.co',
75 | isDisabled: false,
76 | textLink: 'www.colorbeats.co',
77 | },
78 | },
79 | {
80 | id: 6,
81 | image: {
82 | src: '/companies/powerpay.png',
83 | alt: 'Powerpay',
84 | },
85 | description: [
86 | 'Single page application using ReactJS.',
87 | 'REST API developed using Ruby on Rails.',
88 | ],
89 | link: {
90 | href: 'https://www.getpowerpay.com',
91 | isDisabled: false,
92 | textLink: 'www.getpowerpay.com',
93 | },
94 | },
95 | ];
96 |
97 | export default projects;
98 |
--------------------------------------------------------------------------------
/src/services/router.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Router } from '@reach/router';
3 |
4 | import LandingPage from '../pages/landing';
5 | import AboutPage from '../pages/about';
6 | import ContactPage from '../pages/contact';
7 | import SuccessPage from '../pages/success';
8 | import ErrorPage from '../pages/404';
9 |
10 | const AppRouter = () => (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 |
20 | export default AppRouter;
21 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------