├── .github └── ISSUE_TEMPLATE │ └── novo-desafio.md ├── CONTRIBUTING.md ├── INSTALLING.md ├── README.md ├── frontend ├── .editorconfig ├── .env ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── Teste.log ├── debug.log ├── package.json ├── public │ ├── banner.png │ ├── favicon.ico │ ├── fonts │ │ └── FontAwesome.ttf │ ├── index.html │ ├── logo.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.js │ ├── App.test.js │ ├── assets │ │ ├── chevron-down.svg │ │ ├── logo.svg │ │ └── page-under-construction.png │ ├── components │ │ ├── Banner │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── CategoriesList │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── CategoryListItem │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── ChallengeCard │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── ChallengesSkeleton │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── DashboardDefault │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── DevCard │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── Footer │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── Header │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── Logo │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── Newsletter │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── SidebarUser │ │ │ ├── index.js │ │ │ └── styles.js │ │ ├── StatusCard │ │ │ ├── index.js │ │ │ └── styled.js │ │ └── Steps │ │ │ ├── index.js │ │ │ └── styled.js │ ├── index.js │ ├── pages │ │ ├── Challenges │ │ │ ├── README.fr.md │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── ConstructPage │ │ │ └── index.js │ │ ├── Dashboard │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── Detail │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── Devs │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── Home │ │ │ └── index.js │ │ ├── MyChallenges │ │ │ ├── index.js │ │ │ └── styled.js │ │ ├── Submit │ │ │ ├── index.js │ │ │ └── styled.js │ │ └── ToDoChallenge │ │ │ ├── index.js │ │ │ └── styled.js │ ├── routes.js │ ├── serviceWorker.js │ ├── services │ │ ├── api.js │ │ └── challenges.service.js │ ├── setupTests.js │ ├── styles │ │ └── GlobalStyles.js │ └── utils │ │ └── toast.js └── yarn.lock ├── package-lock.json └── translations ├── README.cn.md ├── README.de.md ├── README.es.md ├── README.it.md └── README.md /.github/ISSUE_TEMPLATE/novo-desafio.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Novo desafio 3 | about: Sugestão de um novo desafio para o site 4 | title: "[CHALLENGE]" 5 | labels: challenge 6 | assignees: '' 7 | 8 | --- 9 | 10 | 1 - Tipo: 11 | [ ] Front-end 12 | [ ] Back-end 13 | [ ] Mobile 14 | 15 | 2 - Qual o título do seu desafio? 16 | Ex: Amazing Graph 17 | 18 | 3 - Informe o que deverá ser feito no seu desafio: 19 | Ex: Seu desafio será criar uma landing page sobre um site de criação de gráficos 20 | 21 | 4 - Quais tecnologias o dev precisará utilizar? 22 | Ex: HTML, CSS 23 | 24 | 5 - Informe o link do desafio no github: 25 | O seu desafio deverá ser um template no github, contendo texto utilizado, imagens, fontes e cores. 26 | Ex padrão: Amazing Graph. 27 | Caso você não possua, pode informar mais detalhes do desafio aqui que te auxilio ou crio o repositório pra você :) 28 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribuir 2 | Gostaria de nos ajudar a melhorar o projeto? Veja este guia contribuição para ajudar o DevChallenge a crescer. 3 | 4 | ## Tabela de conteúdos 5 | - [Para começar](#para-começar) 6 | - [Clonando o repositório](#clonando-o-repositório) 7 | - [Criando a nova branch](#criando-a-nova-branch) 8 | - [Nomeando a mensagem de commit](#nomeando-a-mensagem-de-commit) 9 | - [Submetendo suas alterações](#submetendo-suas-alterações) 10 | - [Outras formas de contribuir](#outras-formas-de-contribuir) 11 | 12 | ## Para começar 13 | ### Clonando o repositório 14 | Faça um fork do repositório no GitHub e o clone em sua máquina. 15 | ```bash 16 | $ git clone https://github.com/{Seu nome}/DevChallenge.git 17 | 18 | $ cd DevChallenge 19 | ``` 20 | 21 | ### Criando a nova branch 22 | Crie uma nova branch para a alteração que você deseja submeter. Por exemplo: 23 | ```bash 24 | $ git checkout -b fix-responsive 25 | ``` 26 | Ao executar o comando acima, uma nova branch chamada `fix-responsive` será criada. 27 | 28 | **Nota:** O nome da branch deve ser significativo e deve indicar **apenas** a mudança a ser feita. Requisições que não seguirem esse padrão não serão aceitas. 29 | 30 | ### Nomeando a mensagem de commit 31 | Para elaborar uma boa mensagem de commit, você pode imaginar a seguinte frase: "Se aplicado, este commit irá {sua mensagem de commit}". Por exemplo: 32 | 33 | *Se aplicado, este commit irá **Corrigir responsividade mal formada** 34 | 35 | Ou se preferir, você também pode submeter seus commits em inglês: 36 | 37 | *If applied, this commit will **Fix malformed responsiveness*** 38 | 39 | Lembre-se de começar sua mensagem com uma palavra como: Adicionar, refatorar, deletar, etc.. 40 | 41 | **Nota:** Tente manter suas mensagens com menos de 50 caractéres e detalhar as alterações na descrição do commit. Apesar de não ser obrigatório, esta é uma prática muito conhecida e utilizada na comunidade do Git. 42 | 43 | ## Submetendo suas alterações 44 | Após ter realizado as mudanças, suba suas mudanças para o repositório remoto: 45 | ```bash 46 | $ git push origin fix-responsive 47 | ``` 48 | 49 | Após isso, vá ao seu repositório recém clonado no GitHub, selecione a branch criada e clique em Pull Request. 50 | 51 | **IMPORTANTE:** Faça o Pull Request da sua branch especificamente para a branch `develop`. Pull Requests enviados a branch `master` serão automaticamente recusados. 52 | 53 | Faça um comentário geral sobre as mudanças realizadas e, se necessário dê uma descrição sobre as alterações realizadas e sua justificativa (opcional). Por exemplo: 54 | 55 | "**Corrigir #418** 56 | 57 | Este PR corrige a issue submetida em relação a um erro de responsividade." 58 | 59 | Após isso, seu Pull Request entrará em fase de revisão e, quem sabe, suas alterações sejam integradas ao DevChallenge! 60 | 61 | ## Outras formas de contribuir 62 | Você também pode ajudar o projeto crescer: 63 | 64 | - Criando novos desafios 65 | - Sugerindo mudanças e melhorias 66 | - Fazendo parte de nossa [comunidade](https://discord.gg/yvYXhGj) no Discord 67 | -------------------------------------------------------------------------------- /INSTALLING.md: -------------------------------------------------------------------------------- 1 | # Instalação 2 | Essas instruções vão te levar a uma cópia do projeto rodando em sua máquina local para propósitos de testes e desenvolvimento. 3 | 4 | ## Pré-requisitos 5 | - [Node.js](https://nodejs.org/pt-br/download/) versão 12 ou superior 6 | - Gerenciador de pacotes (Yarn ou NPM) 7 | 8 | ### Clonando o repositório 9 | ```bash 10 | $ git clone https://github.com/Lorenalgm/DevChallenge.git 11 | 12 | $ cd DevChallenge 13 | ``` 14 | 15 | Ou se preferir, faça o [download](https://github.com/Lorenalgm/DevChallenge/archive/master.zip) do projeto. 16 | 17 | ### Instalando dependências 18 | ```bash 19 | $ yarn 20 | ``` 21 | 22 | ou 23 | 24 | ```bash 25 | $ npm install 26 | ``` 27 | 28 | ### Iniciando uma instância local 29 | ```bash 30 | $ yarn start 31 | ``` 32 | 33 | ou 34 | 35 | ```bash 36 | $ npm start 37 | ``` 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Forks][forks-shield]][forks-url] 2 | [![Stargazers][stars-shield]][stars-url] 3 | [![Issues][issues-shield]][issues-url] 4 | 5 |
6 |

7 | 8 | DevChallenge logo, ou seja, quadrado amarelo com D maiusculo na cor preta 14 | 15 |

16 | 17 |

DevChallenge

18 | 19 |

20 | Site para desenvolvedores melhorarem suas habilidades através de desafios de front-end, back-end e mobile! 21 |
22 |
23 | A website for developers to improve their skills by doing front-end and back-end challenges 24 |
25 |
26 | DevChallenge 27 |

28 | 29 | ## Índice 30 | 31 | - [Sobre o Projeto](#sobre-o-projeto) 32 | - [Demo](#demo) 33 | - [Tecnologias](#feito-com) 34 | - [Como Utilizar](#instalação) 35 | - [Como Contribuir](#contribuir) 36 | - [Autores](#autores) 37 | - [Comunidade](#comunidade) 38 | - [Redes sociais](#redes-sociais) 39 | 40 | ## Sobre o Projeto 41 | 42 | O [DevChallenge](https://www.devchallenge.com.br/) é um projeto que visa contribuir com a evolução de desenvolvedores, disponibilizando desafios para que possam praticar, melhorar suas skills e criarem seus portfólios :) 43 | 44 |

45 | Gif de demonstração do site do DevChallenge 52 |

53 | 54 | Além disso, somos uma projeto open source onde você pode contribuir na evolução do backend, frontend e criação de novos desafios. Para saber mais, participe da nossa comunidade :) 55 | 56 | ### Feito com 57 | 58 | - [ReactJS](https://pt-br.reactjs.org/) - Biblioteca para criar interfaces de usuário 59 | - [Yarn](https://yarnpkg.com/) - Gerenciador de pacotes e dependências 60 | 61 | ## Instalação 62 | 63 | Para clonar o projeto e executá-lo em sua máquina, veja os detalhes em [INSTALLING.md](INSTALLING.md). 64 | 65 | ## Contribuir 66 | 67 | Gostaria de contribuir com o projeto? Por favor acesse [CONTRIBUTING.md](CONTRIBUTING.md) para o manual completo de contribuição. 68 | 69 | ## Autores 70 | 71 | - **Lorena Montes** - _Trabalho inicial_ - [Lorenalgm](https://github.com/Lorenalgm) 72 | 73 | Veja também a lista de [contribuidores](https://www.devchallenge.com.br/devs) que participaram do projeto. 74 | 75 | ## Comunidade 76 | 77 | Nosso objetivo é ajudar cada vez mais na evolução de desenvolvedores! Por isso, temos uma comunidade exclusiva do DevChallenge no Discord. Interessado? [Participe](https://discord.gg/yvYXhGj)! 78 | 79 | Acesse nossa [plataforma](https://www.devchallenge.com.br/)! 80 | 81 | ## Redes sociais 82 | 83 | Nos siga nas redes sociais! 84 | 85 | 86 | 87 | 88 | LinkedIn logo 94 | 95 | 96 | 97 | 98 | Twitter logo 104 | 105 | 106 | 107 | 108 | Instagram logo 114 | 115 | 116 | 117 | 118 | [forks-shield]: https://img.shields.io/github/forks/Lorenalgm/DevChallenge.svg?style=flat-square 119 | [forks-url]: https://github.com/Lorenalgm/DevChallenge/network/members 120 | [stars-shield]: https://img.shields.io/github/stars/Lorenalgm/DevChallenge.svg?style=flat-square 121 | [stars-url]: https://github.com/Lorenalgm/DevChallenge/stargazers 122 | [issues-shield]: https://img.shields.io/github/issues/Lorenalgm/DevChallenge.svg?style=flat-square 123 | [issues-url]: https://github.com/Lorenalgm/DevChallenge/issues 124 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /frontend/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_DEVCHALLENGE_API=http://localhost:3333 -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es6: true, 4 | }, 5 | extends: ['airbnb', 'prettier', 'prettier/react'], 6 | globals: { 7 | Atomics: 'readonly', 8 | SharedArrayBuffer: 'readonly', 9 | __DEV__: 'readonly', 10 | }, 11 | parser: 'babel-eslint', 12 | parserOptions: { 13 | ecmaFeatures: { 14 | jsx: true, 15 | }, 16 | ecmaVersion: 2018, 17 | sourceType: 'module', 18 | }, 19 | plugins: ['react', 'prettier'], 20 | rules: { 21 | 'prettier/prettier': 'error', 22 | 'react/jsx-filename-extension': [ 23 | 'warn', 24 | { 25 | extensions: ['.jsx', '.js'], 26 | }, 27 | ], 28 | 'import/prefer-default-export': 'off', 29 | 'react/state-in-constructor': 'off', 30 | 'react/static-property-placement': 'off', 31 | 'react/jsx-props-no-spreading': 'off', 32 | 'react/prop-types': 'off', 33 | 'no-param-reassign': 'off', 34 | 'no-console': 'off', 35 | 'no-undef': 'off', 36 | 'no-alert': 'off', 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /frontend/.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.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | .vscode 24 | -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/Teste.log: -------------------------------------------------------------------------------- 1 | Testando PR na branch -------------------------------------------------------------------------------- /frontend/debug.log: -------------------------------------------------------------------------------- 1 | [0729/221145.237:ERROR:crash_report_database_win.cc(469)] failed to stat report 2 | [0729/221145.282:ERROR:crash_report_database_win.cc(469)] failed to stat report 3 | [0729/221145.283:ERROR:crash_report_database_win.cc(469)] failed to stat report 4 | [0729/221145.283:ERROR:crash_report_database_win.cc(469)] failed to stat report 5 | [0729/223542.097:ERROR:crash_report_database_win.cc(469)] failed to stat report 6 | [0729/223542.098:ERROR:crash_report_database_win.cc(469)] failed to stat report 7 | [0729/223542.098:ERROR:crash_report_database_win.cc(469)] failed to stat report 8 | [0729/223542.098:ERROR:crash_report_database_win.cc(469)] failed to stat report 9 | [0729/235052.267:ERROR:crash_report_database_win.cc(469)] failed to stat report 10 | [0729/235052.268:ERROR:crash_report_database_win.cc(469)] failed to stat report 11 | [0729/235052.304:ERROR:crash_report_database_win.cc(469)] failed to stat report 12 | [0729/235052.304:ERROR:crash_report_database_win.cc(469)] failed to stat report 13 | [0730/213507.902:ERROR:crash_report_database_win.cc(469)] failed to stat report 14 | [0730/213507.904:ERROR:crash_report_database_win.cc(469)] failed to stat report 15 | [0730/213507.904:ERROR:crash_report_database_win.cc(469)] failed to stat report 16 | [0730/213507.904:ERROR:crash_report_database_win.cc(469)] failed to stat report 17 | [0731/195201.876:ERROR:crash_report_database_win.cc(469)] failed to stat report 18 | [0731/195201.899:ERROR:crash_report_database_win.cc(469)] failed to stat report 19 | [0731/195201.899:ERROR:crash_report_database_win.cc(469)] failed to stat report 20 | [0731/195201.899:ERROR:crash_report_database_win.cc(469)] failed to stat report 21 | [0801/210611.449:ERROR:crash_report_database_win.cc(469)] failed to stat report 22 | [0801/210611.500:ERROR:crash_report_database_win.cc(469)] failed to stat report 23 | [0801/210611.500:ERROR:crash_report_database_win.cc(469)] failed to stat report 24 | [0801/210611.500:ERROR:crash_report_database_win.cc(469)] failed to stat report 25 | [0813/135559.995:ERROR:crash_report_database_win.cc(469)] failed to stat report 26 | [0813/135600.175:ERROR:crash_report_database_win.cc(469)] failed to stat report 27 | [0813/135600.175:ERROR:crash_report_database_win.cc(469)] failed to stat report 28 | [0813/135600.176:ERROR:crash_report_database_win.cc(469)] failed to stat report 29 | [0813/172932.159:ERROR:crash_report_database_win.cc(469)] failed to stat report 30 | [0813/172932.219:ERROR:crash_report_database_win.cc(469)] failed to stat report 31 | [0813/172932.219:ERROR:crash_report_database_win.cc(469)] failed to stat report 32 | [0813/172932.219:ERROR:crash_report_database_win.cc(469)] failed to stat report 33 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@fortawesome/fontawesome-svg-core": "^1.2.28", 7 | "@fortawesome/free-brands-svg-icons": "^5.13.0", 8 | "@fortawesome/free-solid-svg-icons": "^5.13.0", 9 | "@fortawesome/react-fontawesome": "^0.1.9", 10 | "@lottiefiles/react-lottie-player": "^1.0.2", 11 | "@testing-library/jest-dom": "^4.2.4", 12 | "@testing-library/react": "^9.3.2", 13 | "@testing-library/user-event": "^7.1.2", 14 | "axios": "^0.21.2", 15 | "dotenv": "^8.2.0", 16 | "react": "^16.13.1", 17 | "react-awesome-slider": "^4.1.0", 18 | "react-dom": "^16.13.1", 19 | "react-icons": "^3.11.0", 20 | "react-loading-skeleton": "^2.0.1", 21 | "react-router-dom": "^5.2.0", 22 | "react-scripts": "3.4.1", 23 | "react-toastify": "^6.0.5", 24 | "react-web-vector-icons": "^1.0.2", 25 | "styled-components": "^5.1.1", 26 | "styled-media-query": "^2.1.2" 27 | }, 28 | "scripts": { 29 | "start": "react-scripts start", 30 | "build": "react-scripts build", 31 | "test": "react-scripts test", 32 | "eject": "react-scripts eject" 33 | }, 34 | "eslintConfig": { 35 | "extends": "react-app" 36 | }, 37 | "browserslist": { 38 | "production": [ 39 | ">0.2%", 40 | "not dead", 41 | "not op_mini all" 42 | ], 43 | "development": [ 44 | "last 1 chrome version", 45 | "last 1 firefox version", 46 | "last 1 safari version" 47 | ] 48 | }, 49 | "devDependencies": { 50 | "babel-eslint": "^10.1.0", 51 | "eslint": "^6.8.0", 52 | "eslint-config-airbnb": "^18.1.0", 53 | "eslint-config-prettier": "^6.11.0", 54 | "eslint-plugin-import": "^2.20.2", 55 | "eslint-plugin-jsx-a11y": "^6.2.3", 56 | "eslint-plugin-prettier": "^3.1.3", 57 | "eslint-plugin-react": "^7.20.0", 58 | "eslint-plugin-react-hooks": "^2.5.1", 59 | "prettier": "^2.0.5" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /frontend/public/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lorenalgm/DevChallenge/8bbe628ae2140a8759802c7a2fc8b009d18ef2e2/frontend/public/banner.png -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lorenalgm/DevChallenge/8bbe628ae2140a8759802c7a2fc8b009d18ef2e2/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lorenalgm/DevChallenge/8bbe628ae2140a8759802c7a2fc8b009d18ef2e2/frontend/public/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | DevChallenge 39 | 40 | 43 | 44 | 45 | 46 |
47 | 57 | 58 | -------------------------------------------------------------------------------- /frontend/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lorenalgm/DevChallenge/8bbe628ae2140a8759802c7a2fc8b009d18ef2e2/frontend/public/logo.png -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "DevChallenge", 3 | "name": "DevChallenge", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo.png", 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 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Routes from './routes'; 3 | 4 | function App() { 5 | return ; 6 | } 7 | 8 | export default App; 9 | -------------------------------------------------------------------------------- /frontend/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 | -------------------------------------------------------------------------------- /frontend/src/assets/chevron-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/assets/page-under-construction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lorenalgm/DevChallenge/8bbe628ae2140a8759802c7a2fc8b009d18ef2e2/frontend/src/assets/page-under-construction.png -------------------------------------------------------------------------------- /frontend/src/components/Banner/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | import * as S from './styled'; 5 | 6 | export default function Banner() { 7 | return ( 8 | 9 | 10 | Melhore suas habilidades 11 | 12 | 13 | Com desafios de front-end, back-end e mobile 14 | 15 | 16 | Bora codar! 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/components/Banner/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | export const BannerContainer = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | flex-wrap: wrap; 8 | justify-content: center; 9 | align-items: center; 10 | margin-top: 160px; 11 | margin-bottom: 160px; 12 | ${media.lessThan('medium')` 13 | margin-top: 14%; 14 | margin-bottom: 14%; 15 | margin-left: 3%; 16 | margin-right: 3%; 17 | `} 18 | `; 19 | 20 | export const BannerTitle = styled.h1` 21 | color: var(--white); 22 | font-size: 60px; 23 | `; 24 | 25 | export const BannerSubtitle = styled.h2` 26 | color: var(--yellow); 27 | font-size: 20px; 28 | margin-top: 10px; 29 | margin-bottom: 20px; 30 | 31 | ${media.lessThan('medium')` 32 | font-size: 1em; 33 | `} 34 | `; 35 | 36 | export const BannerButton = styled.button` 37 | color: var(--white); 38 | background-color: var(--purple); 39 | width: 250px; 40 | height: 60px; 41 | font-weight: bold; 42 | font-size: 20px; 43 | border-radius: 50px; 44 | cursor: pointer; 45 | transition: 0.3s; 46 | 47 | &:hover { 48 | background-color: var(--dark-purple); 49 | } 50 | `; 51 | 52 | export const Typewriter = styled.div` 53 | h1 { 54 | color: var(--white); 55 | overflow: hidden; 56 | border-right: 0.15em solid var(--purple); 57 | white-space: nowrap; 58 | letter-spacing: 0.02em; 59 | animation: typing 3.5s steps(30, end), 60 | blink-caret 0.5s step-end infinite; 61 | 62 | ${media.lessThan('medium')` 63 | font-size: 1.8em; 64 | `} 65 | } 66 | @keyframes typing { 67 | from { 68 | width: 0; 69 | } 70 | to { 71 | width: 100%; 72 | } 73 | } 74 | 75 | @keyframes blink-caret { 76 | from, 77 | to { 78 | border-color: transparent; 79 | } 80 | 50% { 81 | border-color: var(--purple); 82 | } 83 | } 84 | `; 85 | -------------------------------------------------------------------------------- /frontend/src/components/CategoriesList/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CategoryListItem from '../CategoryListItem'; 3 | 4 | import * as S from './styled'; 5 | 6 | const categories = [ 7 | { _id: 1, name: 'Front-end', icon: 'desktop' }, 8 | { _id: 2, name: 'Back-end', icon: 'code' }, 9 | { _id: 3, name: 'Mobile', icon: 'mobile' }, 10 | ]; 11 | 12 | export default function CategoriesList() { 13 | return ( 14 | 15 | {categories.map((category) => ( 16 | 17 | ))} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/components/CategoriesList/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Techs = styled.section` 4 | display: flex; 5 | flex-direction: row; 6 | flex-wrap: wrap; 7 | align-items: center; 8 | justify-content: center; 9 | `; 10 | -------------------------------------------------------------------------------- /frontend/src/components/CategoryListItem/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Icon from 'react-web-vector-icons'; 4 | import * as S from './styled'; 5 | 6 | export default function CategoryListItem({ category }) { 7 | const type = category.name.replace('-', '').toLowerCase(); 8 | return ( 9 | 16 | 17 | 23 | {category.name} 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/components/CategoryListItem/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | import { Link } from 'react-router-dom'; 5 | 6 | export const Anchor = styled(Link)` 7 | text-decoration: none; 8 | margin: 0 25px 0 25px; 9 | 10 | ${media.lessThan('medium')` 11 | margin: 15px 0px 10px 0px; 12 | `} 13 | `; 14 | 15 | export const Card = styled.div` 16 | width: 300px; 17 | height: 300px; 18 | background-color: var(--secondary); 19 | border-radius: 20px; 20 | 21 | cursor: pointer; 22 | display: flex; 23 | flex-direction: column; 24 | align-items: center; 25 | justify-content: center; 26 | transition: 0.25s; 27 | 28 | &:hover { 29 | background-color: var(--dark-hover); 30 | } 31 | `; 32 | 33 | export const Title = styled.h2` 34 | color: var(--white); 35 | margin-top: 20px; 36 | 37 | ${media.lessThan('medium')` 38 | margin-top: 0.6em; 39 | `} 40 | `; 41 | 42 | 43 | -------------------------------------------------------------------------------- /frontend/src/components/ChallengeCard/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | import * as S from './styled'; 4 | 5 | const colorMatch = { 6 | beginner: 'nephritis', 7 | intermediate: 'pumpkin', 8 | advanced: 'pomegranate', 9 | Mobile: 'blue', 10 | Frontend: 'red', 11 | Backend: 'light-purple', 12 | }; 13 | 14 | function ChallengeCard({ challenge, progress, redirect, buttonText }) { 15 | const [techs, setTechs] = useState([]); 16 | 17 | useEffect(() => { 18 | const techsUnOrder = challenge.techs 19 | .toString() 20 | .split(',') 21 | .map((element) => element.replace(/^[ ]/, '')); 22 | 23 | techsUnOrder.forEach((techItem, i) => { 24 | techsUnOrder.forEach((element, j) => { 25 | if (j > i && element.length > techItem.length) { 26 | const aux = techItem; 27 | techsUnOrder[i] = element; 28 | techsUnOrder[j] = aux; 29 | } 30 | }); 31 | }); 32 | 33 | setTechs(techsUnOrder); 34 | }, [challenge.techs]); 35 | 36 | return ( 37 | 38 | 39 | 40 | 41 | {techs.map((item) => ( 42 |

46 | {item} 47 |

48 | ))} 49 |
50 | 51 | 52 | {challenge.type} 53 | 54 | 55 | {challenge.level} 56 | 57 | 58 | 59 |
60 | {progress && } 61 |
62 | 63 | 64 |

{challenge.name}

65 |
66 |

{challenge.description}

{' '} 67 |
68 | 69 | {buttonText} 70 | 71 |
72 | ); 73 | } 74 | 75 | export default ChallengeCard; 76 | -------------------------------------------------------------------------------- /frontend/src/components/ChallengeCard/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | export const Anchor = styled(Link)` 5 | text-decoration: none; 6 | cursor: pointer; 7 | 8 | color: var(--white-gray); 9 | `; 10 | 11 | export const Level = styled.span` 12 | background-color: var(--${(props) => props.color}); 13 | padding: 6px 16px; 14 | font-size: 11px; 15 | height: 23px; 16 | border-radius: 20px; 17 | font-weight: bold; 18 | margin-bottom: 5px; 19 | `; 20 | 21 | export const ChallengeCard = styled.div` 22 | height: 350px; 23 | width: 300px; 24 | 25 | background-color: var(--secondary); 26 | border-radius: 16px; 27 | 28 | margin: 1em 1em 1em 1em; 29 | 30 | display: flex; 31 | flex-direction: column; 32 | align-items: center; 33 | 34 | position: relative; 35 | overflow: hidden; 36 | `; 37 | 38 | export const CardImage = styled.div` 39 | overflow: hidden; 40 | height: 150px; 41 | width: 300px; 42 | background-color: #2b3035; 43 | 44 | img { 45 | transition: 0.3s; 46 | width: 100%; 47 | } 48 | img:hover { 49 | transform: scale(1.1); 50 | } 51 | `; 52 | 53 | export const CardContent = styled.div` 54 | margin-top: 10px; 55 | color: var(--white-gray); 56 | text-align: center; 57 | width: 100%; 58 | padding: 0 16px; 59 | 60 | h1 { 61 | font-size: 25px; 62 | margin-top: 0px; 63 | font-weight: 500; 64 | } 65 | 66 | p { 67 | margin-top: 10px; 68 | } 69 | `; 70 | 71 | export const CardTechs = styled.div` 72 | position: absolute; 73 | right: 0; 74 | top: 16px; 75 | padding: 0 16px; 76 | height: auto; 77 | display: flex; 78 | flex-direction: column; 79 | justify-content: space-between; 80 | align-items: flex-end; 81 | z-index: 999; 82 | .tech { 83 | background-color: var(--quaternary); 84 | padding: 5px 15px; 85 | margin-bottom: 5px; 86 | border-radius: 25px; 87 | text-align: center; 88 | font-weight: bold; 89 | color: var(--white); 90 | align-items: center; 91 | font-size: 13px; 92 | } 93 | 94 | .level { 95 | color: var(--yellow); 96 | font-weight: bold; 97 | } 98 | `; 99 | 100 | export const CardPlatforms = styled.div` 101 | position: absolute; 102 | left: 0; 103 | top: 16px; 104 | padding: 0 16px; 105 | height: auto; 106 | display: flex; 107 | flex-direction: column; 108 | justify-content: space-between; 109 | align-items: flex-start; 110 | z-index: 999; 111 | `; 112 | 113 | export const Button = styled.button` 114 | width: 80%; 115 | height: 46px; 116 | 117 | position: absolute; 118 | bottom: 16px; 119 | left: 50%; 120 | transform: translateX(-50%); 121 | 122 | background: var(--purple); 123 | color: var(--white); 124 | font-weight: bold; 125 | font-size: 18px; 126 | 127 | border-radius: 50px; 128 | 129 | cursor: pointer; 130 | 131 | transition: 0.25s; 132 | 133 | &:hover { 134 | width: 83%; 135 | } 136 | `; 137 | 138 | export const ProgressBar = styled.div` 139 | width: 100%; 140 | height: 8px; 141 | background: var(--quinary); 142 | 143 | &::after { 144 | position: absolute; 145 | content: ' '; 146 | width: ${(props) => `${props.progress}%`}; 147 | height: 8px; 148 | background: var(--green); 149 | border-radius: 0 5px 5px 0; 150 | } 151 | `; 152 | -------------------------------------------------------------------------------- /frontend/src/components/ChallengesSkeleton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'; 3 | import * as S from './styled'; 4 | 5 | export default function ChallengesSkeleton({ cards = 4 }) { 6 | const arr = Array.from({ length: cards }, (_, i) => i + 1); 7 | 8 | return ( 9 | 10 | 11 | {arr.map((item) => ( 12 | 13 | 14 | 15 | ))} 16 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/components/ChallengesSkeleton/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Section = styled.section` 4 | margin-top: 3%; 5 | display: flex; 6 | flex-wrap: wrap; 7 | max-width: 100vw; 8 | align-items: center; 9 | justify-content: center; 10 | `; 11 | 12 | export const ChallengeSkeleton = styled.div` 13 | border-radius: 16px; 14 | cursor: pointer; 15 | 16 | height: 350px; 17 | width: 300px; 18 | margin: 0 0.5em 1em 0.5em; 19 | 20 | overflow: hidden; 21 | `; 22 | -------------------------------------------------------------------------------- /frontend/src/components/DashboardDefault/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import SidebarUser from '../SidebarUser'; 4 | import Logo from '../Logo'; 5 | import { DashboardWrapper, DashboardContent, DbHeader } from './styled'; 6 | 7 | function DashboardDefault(props) { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {props.children} 17 | 18 | 19 | ); 20 | } 21 | 22 | export default DashboardDefault; 23 | -------------------------------------------------------------------------------- /frontend/src/components/DashboardDefault/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | export const DashboardWrapper = styled.div` 5 | display: flex; 6 | min-height: calc(100vh - 100px); 7 | `; 8 | 9 | export const DashboardContent = styled.div` 10 | display: flex; 11 | flex: 1; 12 | flex-direction: column; 13 | color: var(--light-gray); 14 | `; 15 | 16 | export const DbHeader = styled.header` 17 | display: flex; 18 | align-items: center; 19 | justify-content: flex-end; 20 | padding: 0px 80px 0 80px; 21 | margin-top: 40px; 22 | 23 | font-weight: normal; 24 | 25 | ${media.between('medium', 'large')` 26 | padding: 0px 60px 0 60px; 27 | `} 28 | 29 | ${media.lessThan('medium')` 30 | display: flex; 31 | align-items: flex-end; 32 | flex-direction: column; 33 | justify-content: center; 34 | padding: 0 20px; 35 | position: relative; 36 | `} 37 | `; 38 | -------------------------------------------------------------------------------- /frontend/src/components/DevCard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 4 | import { faGithubSquare, faLinkedin } from '@fortawesome/free-brands-svg-icons'; 5 | 6 | import * as S from './styled'; 7 | 8 | function DevCard(props) { 9 | return ( 10 | 11 | {`Avatar: 12 | 13 |

{props.name}

14 | {props.position} 15 |
16 | 17 | {props.github && ( 18 | 24 | 25 | 26 | )} 27 | {props.linkedin && ( 28 | 34 | 35 | 36 | )} 37 | 38 |
39 | ); 40 | } 41 | 42 | export default DevCard; 43 | -------------------------------------------------------------------------------- /frontend/src/components/DevCard/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | 5 | export const Card = styled.div` 6 | min-width: 300px; 7 | height: 90px; 8 | 9 | box-sizing: border-box; 10 | padding: 0px 10px; 11 | 12 | background: var(--secondary); 13 | border-radius: 16px; 14 | 15 | display: flex; 16 | align-items: center; 17 | 18 | position: relative; 19 | 20 | > img { 21 | height: 70px; 22 | width: 70px; 23 | border-radius: 50%; 24 | } 25 | 26 | ${media.lessThan('medium')` 27 | margin-bottom: 5%; 28 | `} 29 | `; 30 | 31 | 32 | export const Infos = styled.div` 33 | max-width: 200px; 34 | flex-wrap: wrap; 35 | 36 | display: flex; 37 | flex-direction: column; 38 | justify-content: center; 39 | align-items: left; 40 | 41 | margin: 0 16px; 42 | 43 | > h1 { 44 | font-size: 18px; 45 | font-weight: bold; 46 | color: var(--white); 47 | } 48 | 49 | > span { 50 | font-size: 14px; 51 | font-weight: 500; 52 | color: var(--yellow); 53 | } 54 | `; 55 | 56 | 57 | export const Social = styled.div` 58 | display: flex; 59 | flex-direction: column; 60 | 61 | position: absolute; 62 | right: 16px; 63 | `; 64 | 65 | export const Anchor = styled.a` 66 | text-decoration: none; 67 | color: var(--white); 68 | font-size: 20px; 69 | 70 | display: flex; 71 | align-items: center; 72 | justify-content: center; 73 | 74 | transition: .25s; 75 | 76 | &:hover{ 77 | color: var(--yellow); 78 | } 79 | `; -------------------------------------------------------------------------------- /frontend/src/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 4 | import { faGithub, faDiscord, faInstagram, faTwitter } from '@fortawesome/free-brands-svg-icons'; 5 | 6 | import * as S from './styled'; 7 | 8 | function Footer() { 9 | return ( 10 | 11 | 12 | 18 | 19 | 20 | 26 | 27 | 28 | 34 | 35 | 36 | 42 | 43 | 44 | 45 | 46 | DevChallenge 47 | 48 | 49 | ); 50 | } 51 | 52 | export default Footer; -------------------------------------------------------------------------------- /frontend/src/components/Footer/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | export const Container = styled.footer` 5 | width: 100%; 6 | height: 100px; 7 | 8 | padding: 40px 100px; 9 | 10 | background-color: var(--tertiary); 11 | 12 | display: flex; 13 | justify-content: space-between; 14 | 15 | ${media.lessThan('small')` 16 | flex-direction: column-reverse; 17 | align-items: center; 18 | justify-content: center; 19 | `} 20 | `; 21 | 22 | export const Social = styled.div` 23 | display: flex; 24 | 25 | > a ~ a { 26 | margin-left: 20px; 27 | } 28 | `; 29 | 30 | export const Title = styled.div` 31 | font-size: 24px; 32 | font-weight: bold; 33 | color: var(--white); 34 | 35 | span { 36 | color: var(--yellow); 37 | } 38 | 39 | ${media.lessThan('small')` 40 | margin-bottom: 25px; 41 | `} 42 | `; 43 | 44 | export const Anchor = styled.a` 45 | text-decoration: none; 46 | font-size: 22px; 47 | color: var(--quinary); 48 | 49 | transition: 0.25s; 50 | 51 | &:hover { 52 | color: var(--yellow); 53 | } 54 | `; 55 | -------------------------------------------------------------------------------- /frontend/src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { useLocation } from 'react-router-dom'; 3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 4 | import { faBars, faTimes } from '@fortawesome/free-solid-svg-icons'; 5 | import { faGithub } from '@fortawesome/free-brands-svg-icons'; 6 | import * as S from './styled'; 7 | 8 | export default function Header() { 9 | const location = useLocation(); 10 | const [clicked, setClicked] = useState(false); 11 | 12 | useEffect(() => { 13 | setClicked(false); 14 | }, [location]); 15 | 16 | return ( 17 | 18 | 19 | 20 | DevChallenge 21 | 22 | 23 | 24 | setClicked(!clicked)}> 25 | {!clicked ? ( 26 | 27 | ) : ( 28 | 29 | )} 30 | 31 | 32 | 33 |
    34 |
  • 35 | 36 | Início 37 | 38 |
  • 39 |
  • 40 | 44 | Desafios 45 | 46 |
  • 47 |
  • 48 | 49 | Comunidade 50 | 51 |
  • 52 |
  • 53 | 54 | Entrar 55 | 59 | 60 |
  • 61 |
62 |
63 |
64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /frontend/src/components/Header/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | import { NavLink } from 'react-router-dom'; 4 | 5 | export const Header = styled.header` 6 | display: flex; 7 | align-items: center; 8 | justify-content: space-between; 9 | 10 | padding: 0px 80px 0 80px; 11 | margin-top: 40px; 12 | 13 | font-weight: normal; 14 | 15 | ${media.between('medium', 'large')` 16 | padding: 0px 60px 0 60px; 17 | `} 18 | 19 | ${media.lessThan('medium')` 20 | display: flex; 21 | align-items: flex-start; 22 | flex-direction: column; 23 | justify-content: center; 24 | padding: 0; 25 | position: relative; 26 | `} 27 | `; 28 | 29 | export const Title = styled.div` 30 | font-size: 30px; 31 | font-weight: bold; 32 | color: var(--white); 33 | 34 | span { 35 | color: var(--yellow); 36 | } 37 | 38 | ${media.lessThan('medium')` 39 | margin-left: 20px; 40 | `} 41 | `; 42 | 43 | export const MenuMobile = styled.div` 44 | display: none; 45 | 46 | transition: 0.25s; 47 | 48 | ${media.lessThan('medium')` 49 | display: block; 50 | position: absolute; 51 | font-size: 25px; 52 | color: var(--white); 53 | top: 2; 54 | right: 0; 55 | margin-right: 20px; 56 | cursor: pointer; 57 | 58 | z-index: 9999; 59 | `} 60 | `; 61 | 62 | export const Menu = styled.div` 63 | ul { 64 | display: flex; 65 | align-items: center; 66 | 67 | list-style-type: none; 68 | 69 | font-size: 20px; 70 | color: var(--white); 71 | font-weight: 400; 72 | 73 | li ~ li { 74 | margin-left: 20px; 75 | } 76 | 77 | li:hover { 78 | opacity: 0.8; 79 | } 80 | 81 | ${media.lessThan('medium')` 82 | background-color: var(--primary); 83 | flex-direction: column; 84 | width: 100%; 85 | position: absolute; 86 | justify-content: space-around; 87 | height: 300px; 88 | align-items: center; 89 | margin-top: 0.5em; 90 | top: -100%; 91 | transform: ${({ open }) => 92 | open ? 'translateY(-100%)' : 'translateY(0)'}; 93 | transition: transform 0.5s ease; 94 | z-index: 1000; 95 | li ~ li { 96 | margin-left: 0; 97 | } 98 | `} 99 | } 100 | `; 101 | 102 | export const StyledLink = styled(NavLink)` 103 | text-decoration: none; 104 | color: var(--white); 105 | 106 | cursor: pointer; 107 | 108 | &.is-active { 109 | color: var(--yellow); 110 | } 111 | `; 112 | 113 | export const ButtonLink = styled(NavLink)` 114 | background-color: var(--quaternary); 115 | 116 | width: 100px; 117 | padding: 5px 30px; 118 | border-radius: 25px; 119 | 120 | text-align: center; 121 | text-decoration: none; 122 | color: var(--white); 123 | 124 | &.is-active { 125 | background-color: var(--yellow); 126 | color: var(--tertiary); 127 | } 128 | `; 129 | -------------------------------------------------------------------------------- /frontend/src/components/Logo/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { StyledLink } from '../Header/styled'; 4 | import { Title } from './styled'; 5 | 6 | function Logo() { 7 | return ( 8 | 9 | <StyledLink to="/" exact> 10 | Dev<span>Challenge</span> 11 | </StyledLink> 12 | 13 | ); 14 | } 15 | 16 | export default Logo; 17 | -------------------------------------------------------------------------------- /frontend/src/components/Logo/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Title = styled.div` 4 | font-size: 30px; 5 | font-weight: bold; 6 | color: var(--white); 7 | span { 8 | color: var(--yellow); 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /frontend/src/components/Newsletter/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Player } from '@lottiefiles/react-lottie-player'; 3 | import api from '../../services/api'; 4 | import ToastNotification from '../../utils/toast'; 5 | 6 | import * as S from './styled'; 7 | 8 | export default function Newsletter() { 9 | const [email, setEmail] = useState(''); 10 | 11 | async function handleSubscribe(e) { 12 | e.preventDefault(); 13 | 14 | const data = { 15 | email, 16 | }; 17 | 18 | try { 19 | await api.post('newsletter', data); 20 | ToastNotification.notify( 21 | 'success', 22 | 'Feito! Você será o primeiro a saber sobre novos desafios :)' 23 | ); 24 | } catch (err) { 25 | ToastNotification.notify( 26 | 'error', 27 | 'Opa, algo deu errado! Pode tentar novamente? :c' 28 | ); 29 | } 30 | } 31 | 32 | return ( 33 | 34 |
35 | 42 |
43 |
44 | 45 | Seja notificado sobre novos desafios! 46 | 47 | 48 | Inscreva-se para ser o primeiro a saber sobre novos desafios 49 | :) 50 | 51 | 52 | setEmail(e.target.value)} 57 | required 58 | /> 59 | 60 | 61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /frontend/src/components/Newsletter/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | export const NewsletterContainer = styled.section` 5 | display: flex; 6 | flex-direction: row; 7 | justify-content: center; 8 | align-items: center; 9 | 10 | ${media.lessThan('medium')` 11 | flex-direction: column; 12 | `} 13 | 14 | div { 15 | display: flex; 16 | flex-direction: column; 17 | align-items: left; 18 | margin-left: 2em; 19 | 20 | ${media.lessThan('medium')` 21 | align-items: center; 22 | justify-content: center; 23 | text-align: center; 24 | margin-left: 0; 25 | `} 26 | } 27 | `; 28 | 29 | export const NewsletterTitle = styled.h1` 30 | color: var(--white); 31 | font-size: 2em; 32 | margin-bottom: 0.1em; 33 | 34 | ${media.lessThan('medium')` 35 | font-size: 1.3em; 36 | margin-bottom: 0.1em; 37 | `} 38 | `; 39 | 40 | export const NewsletterParagraph = styled.p` 41 | color: var(--yellow); 42 | font-size: 1em; 43 | 44 | ${media.lessThan('medium')` 45 | margin: 1em; 46 | `} 47 | `; 48 | 49 | export const NewsletterForm = styled.form` 50 | display: flex; 51 | flex-direction: column; 52 | 53 | ${media.lessThan('medium')` 54 | display: flex; 55 | align-content: center; 56 | justify-content: center; 57 | text-align: center; 58 | margin-bottom: 3em; 59 | `} 60 | 61 | input { 62 | height: 3.5em; 63 | width: 20em; 64 | margin-top: 1em; 65 | margin-bottom: 1em; 66 | border-radius: 0.5em; 67 | padding: 20px; 68 | font-size: 18px; 69 | 70 | ${media.lessThan('medium')` 71 | height: 3em; 72 | width: 18em; 73 | `} 74 | } 75 | 76 | button { 77 | color: var(--white); 78 | background-color: var(--purple); 79 | width: 350px; 80 | height: 60px; 81 | font-weight: bold; 82 | font-size: 20px; 83 | border-radius: 50px; 84 | cursor: pointer; 85 | transition: 0.3s; 86 | 87 | ${media.lessThan('medium')` 88 | width: 16em; 89 | height: 3em; 90 | `} 91 | 92 | &:hover { 93 | background-color: var(--dark-purple); 94 | } 95 | } 96 | `; 97 | -------------------------------------------------------------------------------- /frontend/src/components/SidebarUser/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useLocation } from 'react-router-dom'; 3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 4 | import { faBars, faTimes } from '@fortawesome/free-solid-svg-icons'; 5 | 6 | import Icon from 'react-web-vector-icons'; 7 | import { Aside, Img, UserInfo, StyledLink, Menu, MenuMobile } from './styles'; 8 | 9 | function SidebarUser() { 10 | const location = useLocation(); 11 | const [clicked, setClicked] = useState(false); 12 | const [user, setUser] = useState({}); 13 | 14 | useEffect(() => { 15 | setClicked(false); 16 | setUser({ 17 | name: 'Dev Challenge', 18 | username: '@devChallenge', 19 | img: '', 20 | }); 21 | }, [location]); 22 | 23 | return ( 24 | <> 25 | setClicked(!clicked)}> 26 | {!clicked ? ( 27 | 28 | ) : ( 29 | 30 | )} 31 | 32 | 93 | 94 | ); 95 | } 96 | 97 | export default SidebarUser; 98 | -------------------------------------------------------------------------------- /frontend/src/components/SidebarUser/styles.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | import { NavLink } from 'react-router-dom'; 4 | 5 | export const Aside = styled.aside` 6 | display: flex; 7 | align-items: center; 8 | flex-direction: column; 9 | text-align: center; 10 | 11 | width: 350px; 12 | background: var(--secondary); 13 | color: var(--white); 14 | 15 | ${media.between('medium', 'large')` 16 | width: 278px; 17 | `} 18 | 19 | ${media.lessThan('860px')` 20 | position: absolute; 21 | top: 0; 22 | bottom: 0; 23 | z-index: 9998; 24 | 25 | transform: ${({ open }) => 26 | open ? 'translateX(0)' : 'translateX(-350px)'}; 27 | transition: 0.5s ease; 28 | `} 29 | `; 30 | 31 | export const UserInfo = styled.div` 32 | margin-top: 40px; 33 | 34 | & > p { 35 | font-size: 16px; 36 | font-family: Roboto; 37 | } 38 | 39 | & > span { 40 | font-family: Roboto; 41 | font-size: 12px; 42 | } 43 | `; 44 | 45 | export const Img = styled.img` 46 | height: 90px; 47 | width: 90px; 48 | border-radius: 50%; 49 | margin-bottom: 10px; 50 | border: 3px solid var(--yellow); 51 | `; 52 | 53 | export const Menu = styled.div` 54 | ul { 55 | display: flex; 56 | flex-direction: column; 57 | align-items: center; 58 | justify-content: center; 59 | width: 350px; 60 | margin-top: 60px; 61 | list-style: none; 62 | 63 | ${media.between('medium', 'large')` 64 | width: 278px; 65 | `} 66 | 67 | li { 68 | width: 200px; 69 | height: 45px; 70 | display: flex; 71 | align-items: center; 72 | font-size: 18px; 73 | } 74 | 75 | li:hover { 76 | opacity: 0.8; 77 | } 78 | } 79 | `; 80 | 81 | export const StyledLink = styled(NavLink)` 82 | text-decoration: none; 83 | color: var(--white); 84 | 85 | &.is-active { 86 | color: var(--yellow); 87 | } 88 | `; 89 | 90 | export const MenuMobile = styled.div` 91 | display: none; 92 | 93 | transition: 0.25s; 94 | 95 | ${media.lessThan('860px')` 96 | display: block; 97 | position: absolute; 98 | font-size: 25px; 99 | color: var(--white); 100 | top: 50px; 101 | left: 20px; 102 | margin-right: 20px; 103 | cursor: pointer; 104 | 105 | z-index: 9999; 106 | `} 107 | `; 108 | -------------------------------------------------------------------------------- /frontend/src/components/StatusCard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { CardWrapper } from './styled'; 4 | 5 | function StatusCard(props) { 6 | return ( 7 | 8 |

{props.title}

9 |

{props.count}

10 |
11 | ); 12 | } 13 | 14 | export default StatusCard; 15 | -------------------------------------------------------------------------------- /frontend/src/components/StatusCard/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const CardWrapper = styled.div` 4 | display: flex; 5 | width: 48%; 6 | max-width: 350px; 7 | height: 110px; 8 | background: var(--secondary); 9 | border-radius: 11px; 10 | border: 2px solid; 11 | padding: 25px; 12 | align-items: center; 13 | justify-content: space-between; 14 | color: var(--white); 15 | 16 | & > p { 17 | width: 150px; 18 | font-weight: 400; 19 | font-size: 20px; 20 | padding: 10px; 21 | } 22 | 23 | & > h1 { 24 | font-size: 50px; 25 | } 26 | `; 27 | -------------------------------------------------------------------------------- /frontend/src/components/Steps/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | faSearch, 4 | faCode, 5 | faShareSquare, 6 | } from '@fortawesome/free-solid-svg-icons'; 7 | 8 | import * as S from './styled'; 9 | 10 | export default function Steps() { 11 | return ( 12 | 13 | 14 | 15 |

Escolha

16 |
17 | 18 | 19 |

Desenvolva

20 |
21 | 22 | 23 |

Compartilhe

24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/components/Steps/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 5 | 6 | export const StepsContainer = styled.section` 7 | margin-top: 100px; 8 | 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | 13 | ${media.lessThan('medium')` 14 | padding: 0; 15 | `} 16 | `; 17 | 18 | export const Step = styled.div` 19 | width: 300px; 20 | height:300px; 21 | 22 | display: flex; 23 | flex-direction: column; 24 | align-items: center; 25 | justify-content: center; 26 | 27 | h2 { 28 | color: var(--yellow); 29 | margin-top: 20px; 30 | 31 | ${media.lessThan('medium')` 32 | font-size: 1em; 33 | `} 34 | } 35 | `; 36 | 37 | export const Icon = styled(FontAwesomeIcon)` 38 | color: var(--white); 39 | width: 60px !important; 40 | height: 60px; 41 | 42 | ${media.lessThan('medium')` 43 | height: 3em; 44 | width: 3em; 45 | `} 46 | `; 47 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import * as serviceWorker from './serviceWorker'; 5 | import { GlobalStyles } from './styles/GlobalStyles'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | 15 | // If you want your app to work offline and load faster, you can change 16 | // unregister() to register() below. Note this comes with some pitfalls. 17 | // Learn more about service workers: https://bit.ly/CRA-PWA 18 | serviceWorker.unregister(); 19 | -------------------------------------------------------------------------------- /frontend/src/pages/Challenges/README.fr.md: -------------------------------------------------------------------------------- 1 | [![Forks][forks-shield]][forks-url] 2 | [![Stargazers][stars-shield]][stars-url] 3 | [![Issues][issues-shield]][issues-url] 4 | 5 |
6 |

7 | 8 | Logo 9 | 10 | 11 |

DevChallenge

12 | 13 |

14 | Site permettant aux développeurs d'améliorer leurs compétences grâce à des défis Front-end, Back-end et Mobile!

15 |
16 |
17 | DevChallenge 18 |

19 | 20 | ## Sommaire 21 | 22 | - [Sommaire](#sommaire) 23 | - [A propos du projet](#a-propos-du-projet) 24 | - [Demo:](#demo) 25 | - [Technologies:](#technologies) 26 | - [Utilisation](#utilisation) 27 | - [Contribution](#contribution) 28 | - [Prérequis](#prérequis) 29 | - [Installation](#installation) 30 | - [Communauté](#communauté) 31 | 32 | ## A propos du projet 33 | Le DevChallenge est un projet contribuant à l'évolution des développeurs, en leur fournissant des défis afin qu'ils puissent s'entraîner, améliorer leurs compétences et créer leur portfolio :) 34 | 35 | ### Demo: 36 |

37 | DevChallenge 38 |

39 | 40 | ### Technologies: 41 | - Backend: Nodejs 42 | - Frontend: Reactjs 43 | - Banque de données: MongoDB 44 | 45 | ## Utilisation 46 | 1 - Utilisez un des modèles DevChallenge comme base.
47 | 2 - Lisez les instructions du readme.md.
48 | 3 - Codez.
49 | 4 - Partagez le résultat avec la communauté :) 50 | 51 | ## Contribution 52 | Participez au projet:
53 | - Suggérez et / ou créez de nouveaux défis: ces défis seront alors disponibles et pourront aider tous les développeurs à s'entraîner. 54 | - Améliorez ou corrigez le site: vous pouvez envoyer une *Pull Request* afin que votre modification soit disponible à tous les développeurs! 55 | 56 | ### Prérequis 57 | 1. Nodejs 58 | 59 | ### Installation 60 | 61 | Front-end 62 | 63 | 1. Téléchargez ou clonez le répertoire DevChallenge 64 | ```sh 65 | git clone git@github.com:Lorenalgm/DevChallenge.git 66 | ``` 67 | 2. Ouvrez le projet avec votre éditeur. 68 | 3. A partir du terminal, installez les dépendances (packages) nécessaires: 69 | ```sh 70 | yarn 71 | ``` 72 | ou 73 | ```sh 74 | npm install 75 | ``` 76 | 4. Parfait! Vous pouvez maintenant vous rendre dans le dossier `frontend` e inicier votre serveur: 77 | ```sh 78 | cd frontend 79 | ``` 80 | ```sh 81 | yarn start 82 | ``` 83 | 1. Ouvrez votre navigateur! Le DevChallenge sera disponible à `http://localhost:3000/` :) 84 | 85 | 86 | Back-end 87 | 88 | Le Backend est disponible dans le répertoire: DevChallengeAPI 89 | 90 | 91 | ## Communauté 92 | Notre objectif est d'aider au maximum les développeurs dans leur évolution! Nous avons donc une communauté DevChallenge exclusive sur Discord. On se retrouve là-bas? https://discord.gg/yvYXhGj
93 |
94 | Site: https://www.devchallenge.com.br/
95 | 96 | 97 | 99 | 100 | 102 | 103 |
98 | Discord LinkedinTwitter 101 | Instagram
104 | 105 | [forks-shield]: https://img.shields.io/github/forks/Lorenalgm/DevChallenge.svg?style=flat-square 106 | [forks-url]: https://github.com/Lorenalgm/DevChallenge/network/members 107 | [stars-shield]: https://img.shields.io/github/stars/Lorenalgm/DevChallenge.svg?style=flat-square 108 | [stars-url]: https://github.com/Lorenalgm/DevChallenge/stargazers 109 | [issues-shield]: https://img.shields.io/github/issues/Lorenalgm/DevChallenge.svg?style=flat-square 110 | [issues-url]: https://github.com/Lorenalgm/DevChallenge/issues 111 | -------------------------------------------------------------------------------- /frontend/src/pages/Challenges/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import api from '../../services/api'; 3 | import ChallengesSkeleton from '../../components/ChallengesSkeleton'; 4 | import ChallengeCard from '../../components/ChallengeCard'; 5 | import Header from '../../components/Header'; 6 | import * as S from './styled'; 7 | 8 | const languages = [ 9 | { id: 1, name: 'React Native' }, 10 | { id: 2, name: 'Free Choice' }, 11 | { id: 3, name: 'Javascript' }, 12 | { id: 4, name: 'HTML' }, 13 | { id: 5, name: 'CSS' }, 14 | ]; 15 | 16 | const types = [ 17 | { id: 1, name: 'Frontend' }, 18 | { id: 2, name: 'Backend' }, 19 | { id: 3, name: 'Mobile' }, 20 | ]; 21 | 22 | export default function Challenges({ location }) { 23 | const [challenges, setChallenges] = useState([]); 24 | const [filteredChallenges, setFilteredChallenges] = useState([]); 25 | const [loading, setLoading] = useState(true); 26 | const [languageFilter, setLanguageFilter] = useState(''); 27 | const [typeFilter, setTypeFilter] = useState(location.search.split('=')[1]); 28 | 29 | function capitalize(s) { 30 | return s && s[0].toUpperCase() + s.slice(1); 31 | } 32 | 33 | useEffect(() => { 34 | window.scrollTo(0, 0); 35 | async function loadChallenges() { 36 | const response = await api.get('/challenges'); 37 | 38 | setChallenges(response.data); 39 | setFilteredChallenges(response.data); 40 | 41 | setLoading(false); 42 | } 43 | 44 | loadChallenges(); 45 | }, [location]); 46 | 47 | useEffect(() => { 48 | let filtered = challenges; 49 | if (typeFilter) { 50 | filtered = filtered.filter( 51 | (challenge) => 52 | challenge.type.toLowerCase() === typeFilter.toLowerCase() 53 | ); 54 | } 55 | if (languageFilter) { 56 | filtered = filtered.filter((challenge) => { 57 | const [ techs ] = challenge.techs; 58 | const serializedTechs = techs.split(', '); 59 | const hasSelectedTech = 60 | serializedTechs.includes(languageFilter) || 61 | serializedTechs.includes('Free Choice'); 62 | 63 | return hasSelectedTech; 64 | }); 65 | } 66 | setFilteredChallenges(filtered); 67 | }, [typeFilter, languageFilter, challenges]); 68 | 69 | return ( 70 | <> 71 |
72 | 73 |

Desafios

74 | 75 | 76 | 77 | 78 | 92 | 93 | 94 | 95 | 96 | 97 | 111 | 112 | 113 | 114 |
115 | 116 | {loading && } 117 | {!loading && ( 118 | 119 | {filteredChallenges.map((challenge) => { 120 | return ( 121 | 127 | ); 128 | })} 129 | 130 | )} 131 | 132 | ); 133 | } 134 | -------------------------------------------------------------------------------- /frontend/src/pages/Challenges/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | import chevronDown from '../../assets/chevron-down.svg'; 4 | 5 | export const Alert = styled.form` 6 | color: white; 7 | text-align: center; 8 | margin-top: 3em; 9 | `; 10 | 11 | export const Section = styled.section` 12 | margin-top: 3%; 13 | display: flex; 14 | flex-wrap: wrap; 15 | max-width: 100vw; 16 | align-items: center; 17 | justify-content: center; 18 | `; 19 | 20 | export const Head = styled.div` 21 | display: flex; 22 | justify-content: space-between; 23 | align-items: center; 24 | border-bottom: 1px solid var(--quaternary); 25 | margin-top: 60px; 26 | padding: 0 80px; 27 | font-size: 18px; 28 | 29 | h1 { 30 | color: whitesmoke; 31 | ${media.lessThan('medium')` 32 | font-size: 30px; 33 | `} 34 | } 35 | 36 | ${media.lessThan('medium')` 37 | padding: 0 20px; 38 | `} 39 | `; 40 | 41 | export const Filters = styled.form` 42 | color: white; 43 | display: flex; 44 | `; 45 | 46 | export const InputGroup = styled.div` 47 | margin: 0 0 0 14px; 48 | 49 | label { 50 | padding-left: 4px; 51 | font-size: 14px; 52 | font-weight: bold; 53 | display: block; 54 | } 55 | `; 56 | 57 | export const Select = styled.div` 58 | padding-right: 20px; 59 | height: 34px; 60 | overflow: hidden; 61 | background: url(${chevronDown}) no-repeat right var(--primary); 62 | 63 | select { 64 | width: 120%; 65 | padding-right: 20px; 66 | background: transparent; 67 | padding: 5px; 68 | font-size: 16px; 69 | line-height: 1; 70 | border: 0; 71 | border-radius: 0; 72 | height: 34px; 73 | -webkit-appearance: none; 74 | -webkit-user-select: none; 75 | -moz-user-select: none; 76 | -ms-user-select: none; 77 | color: white; 78 | } 79 | 80 | option { 81 | background-color: var(--primary); 82 | border: 0; 83 | } 84 | `; 85 | -------------------------------------------------------------------------------- /frontend/src/pages/ConstructPage/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import pageInConstruction from '../../assets/page-under-construction.png'; 4 | import Header from '../../components/Header'; 5 | 6 | function ConstructPage() { 7 | return ( 8 | <> 9 |
10 |
19 | 24 |

Pagina em Construção

25 |
26 | 27 | ); 28 | } 29 | 30 | export default ConstructPage; 31 | -------------------------------------------------------------------------------- /frontend/src/pages/Dashboard/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | import DashboardDefault from '../../components/DashboardDefault'; 4 | import StatusCard from '../../components/StatusCard'; 5 | import { StatusCardWrapper, Title, Container } from './styled'; 6 | import { ChallengeCard } from '../../components/ChallengeCard/styled'; 7 | 8 | const fakeData = { 9 | complete: 5, 10 | inProgress: 2, 11 | }; 12 | 13 | function Dashboard() { 14 | const [statusCount, setStatusCount] = useState({}); 15 | 16 | useEffect(() => { 17 | setStatusCount(fakeData); 18 | }, []); 19 | 20 | return ( 21 | 22 | 23 | 24 | 29 | 34 | 35 | #WeeklyDevChallenge 36 | 37 | 38 | 39 | ); 40 | } 41 | 42 | export default Dashboard; 43 | -------------------------------------------------------------------------------- /frontend/src/pages/Dashboard/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | export const Container = styled.div` 5 | padding: 0px 80px 0 80px; 6 | 7 | ${media.between('medium', 'large')` 8 | padding: 0px 60px 0 60px; 9 | `} 10 | 11 | ${media.lessThan('medium')` 12 | padding: 0px 20px 0 20px; 13 | `} 14 | `; 15 | 16 | export const StatusCardWrapper = styled.div` 17 | display: flex; 18 | justify-content: space-between; 19 | margin-top: 30px; 20 | margin-bottom: 50px; 21 | 22 | ${media.greaterThan('large')` 23 | justify-content: space-around; 24 | `} 25 | `; 26 | 27 | export const Title = styled.h2` 28 | margin-bottom: 20px; 29 | `; 30 | -------------------------------------------------------------------------------- /frontend/src/pages/Detail/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { useParams } from 'react-router-dom'; 3 | import { faCheck } from '@fortawesome/free-solid-svg-icons'; 4 | import AwesomeSlider from 'react-awesome-slider'; 5 | import 'react-awesome-slider/dist/styles.css'; 6 | import api from '../../services/api'; 7 | import DevCard from '../../components/DevCard'; 8 | 9 | import * as S from './styled'; 10 | import Header from '../../components/Header'; 11 | 12 | const includes = [ 13 | { 14 | id: 1, 15 | instruction: 16 | 'Readme com instruções de requisitos e as rotas da aplicação', 17 | }, 18 | { 19 | id: 2, 20 | instruction: 'Imagens para adicionar no projeto', 21 | }, 22 | { 23 | id: 3, 24 | instruction: 'Modelo como design para utilizar como referência', 25 | }, 26 | { 27 | id: 4, 28 | instruction: 'Arquivo contendo o texto que será utilizado', 29 | }, 30 | ]; 31 | 32 | const starts = [ 33 | { 34 | id: 1, 35 | steps: 'Clone o projeto com o código inicial', 36 | }, 37 | { 38 | id: 2, 39 | steps: 'Leia as instruções disponíveis no readme', 40 | }, 41 | { 42 | id: 3, 43 | steps: 'Inicie o desenvolvimento!', 44 | }, 45 | { 46 | id: 4, 47 | steps: 'Compartilhe seus resultados com a comunidade', 48 | }, 49 | ]; 50 | 51 | const colorMatch = { 52 | beginner: 'nephritis', 53 | intermediate: 'pumpkin', 54 | advanced: 'pomegranate', 55 | Mobile: 'blue', 56 | Frontend: 'red', 57 | Backend: 'light-purple', 58 | }; 59 | 60 | export default function Detail() { 61 | const [challenge, setChallenge] = useState({}); 62 | const [techs, setTechs] = useState([]); 63 | const [dev, setDev] = useState({}); 64 | const [images, setImages] = useState([]); 65 | const { id } = useParams(); 66 | 67 | useEffect(() => { 68 | window.scrollTo(0, 0); 69 | async function loadChallenge() { 70 | const response = await api.get(`/challenges/${id}`); 71 | setChallenge(response.data); 72 | setDev(response.data.dev_id); 73 | setImages(response.data.images); 74 | setTechs(response.data.techs); 75 | } 76 | 77 | loadChallenge(); 78 | }, [id]); 79 | 80 | return ( 81 | <> 82 |
83 | 84 | 85 | 86 | 87 |

{challenge.name}

88 |
89 | 90 | 91 | 92 | {challenge.level} 93 | 94 | 95 | {challenge.type} 96 | 97 | {techs[0]?.split(', ').map((item, idx) => ( 98 | {item} 99 | ))} 100 | 101 | 102 | 103 | {challenge.description} 104 | 105 | 106 | 112 | Iniciar desafio 113 | 114 |
115 | 116 | 117 | {images.map((image) => ( 118 |
119 | Challenge 124 |
125 | ))} 126 |
127 |
128 |
129 | 130 | 131 | 132 | 133 |

Sobre o desafio

134 |

Seu desafio é {challenge.brief}.

135 |
136 | 137 | 138 |

O que está incluso?

139 | 140 | {challenge.type === 'Backend' ? ( 141 | 142 | Readme com 143 | instruções de requisitos e as rotas da 144 | aplicação 145 | 146 | ) : ( 147 | <> 148 | {includes.map((include) => ( 149 | 150 | {' '} 151 | {include.instruction} 152 | 153 | ))} 154 | 155 | )} 156 |
157 | 158 |

Como iniciar?

159 | {starts.map((start) => ( 160 |

161 | {start.id} - 162 | {start.steps} 163 |

164 | ))} 165 |
166 |
167 |
168 | 175 |
176 |
177 | 178 | ); 179 | } 180 | -------------------------------------------------------------------------------- /frontend/src/pages/Detail/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 3 | import media from 'styled-media-query'; 4 | 5 | export const Container = styled.div` 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: center; 9 | margin: 2%; 10 | flex: 1; 11 | 12 | ${media.lessThan('medium')` 13 | flex-direction: column; 14 | align-items: center; 15 | justify-content: center; 16 | width: 100%; 17 | margin: 0%; 18 | margin-top: 5%; 19 | `} 20 | `; 21 | 22 | export const Banner = styled.div` 23 | display: flex; 24 | align-items: center; 25 | justify-content: center; 26 | margin-top: 3em; 27 | 28 | ${media.lessThan('medium')` 29 | flex-wrap: wrap-reverse; 30 | `} 31 | `; 32 | 33 | export const LeftColumn = styled.div` 34 | display: flex; 35 | flex-direction: column; 36 | align-items: left; 37 | justify-content: center; 38 | width: 40%; 39 | text-align: left; 40 | 41 | ${media.lessThan('medium')` 42 | display: flex; 43 | width: 100%; 44 | margin-top: 8%; 45 | flex-direction: column; 46 | align-items: center; 47 | justify-content: center; 48 | margin-left: 0; 49 | margin-right: 0; 50 | `} 51 | 52 | ${media.lessThan('small')` 53 | margin-right: 4%; 54 | margin-left: 4%; 55 | `} 56 | `; 57 | 58 | export const InfosType = styled.div` 59 | filter: brightness(90%); 60 | width: 10em; 61 | border-radius: 20px; 62 | text-align: left; 63 | font-weight: bold; 64 | margin-left: 4%; 65 | margin-bottom: 1%; 66 | color: var(--purple); 67 | ${media.lessThan('medium')` 68 | text-align: center; 69 | margin: 0; 70 | width: 5em; 71 | `}; 72 | `; 73 | 74 | export const TitleContainer = styled.div` 75 | margin-left: 4%; 76 | margin-bottom: 1%; 77 | h1 { 78 | color: var(--yellow); 79 | text-align: left; 80 | } 81 | 82 | ${media.lessThan('medium')` 83 | margin: 0; 84 | margin-top: 4%; 85 | text-align: center; 86 | `} 87 | `; 88 | 89 | export const ChallengeDescription = styled.div` 90 | color: var(--white); 91 | font-size: 1.2em; 92 | margin-left: 4%; 93 | margin-bottom: 4%; 94 | 95 | ${media.lessThan('medium')` 96 | text-align: left; 97 | margin-left: 0; 98 | margin-top: 2%; 99 | margin-bottom: 4%; 100 | `} 101 | `; 102 | 103 | export const Infos = styled.div` 104 | display: flex; 105 | flex-wrap: wrap; 106 | align-items: center; 107 | justify-content: left; 108 | padding: 0 4%; 109 | font-size: 14px; 110 | margin-bottom: 10px; 111 | 112 | ${media.lessThan('medium')` 113 | align-items: start; 114 | width: 100%; 115 | padding: 0; 116 | `} 117 | `; 118 | 119 | export const InfosLevel = styled.div` 120 | background-color: var(--${(props) => props.color}); 121 | filter: brightness(90%); 122 | padding: 7px 15px; 123 | margin-right: 2%; 124 | margin-bottom: 2%; 125 | border-radius: 20px; 126 | text-align: center; 127 | font-weight: bold; 128 | color: var(--white); 129 | `; 130 | 131 | export const InfosTechs = styled.div` 132 | background-color: var(--quaternary); 133 | filter: brightness(90%); 134 | padding: 7px 15px; 135 | margin-right: 2%; 136 | margin-bottom: 2%; 137 | border-radius: 20px; 138 | text-align: center; 139 | font-weight: bold; 140 | color: var(--white); 141 | `; 142 | 143 | export const ChallengeLink = styled.a` 144 | color: var(--white); 145 | background-color: var(--purple); 146 | width: 15em; 147 | height: 3em; 148 | font-weight: bold; 149 | font-size: 20px; 150 | border-radius: 50px; 151 | cursor: pointer; 152 | transition: 0.3s; 153 | margin: 4%; 154 | text-decoration: none; 155 | display: flex; 156 | justify-content: center; 157 | align-items: center; 158 | 159 | &:hover { 160 | background-color: var(--dark-purple); 161 | } 162 | 163 | ${media.lessThan('medium')` 164 | margin: 0; 165 | margin-top: 5%; 166 | margin-bottom: 2%; 167 | `} 168 | 169 | ${media.between('large', 'huge')` 170 | width: 20em; 171 | `} 172 | `; 173 | 174 | export const Demo = styled.div` 175 | width: 47em; 176 | border-radius: 10px; 177 | 178 | .image { 179 | height: 100%; 180 | } 181 | 182 | .slider { 183 | width: 100%; 184 | height: 100%; 185 | } 186 | 187 | .awssld__wrapper { 188 | border-radius: 10px; 189 | } 190 | 191 | .awssld__bullets { 192 | bottom: 0; 193 | z-index: 9999; 194 | padding: 10px 0; 195 | transition: 0.4s; 196 | } 197 | 198 | .awssld__bullets:hover { 199 | background-color: rgba(0, 0, 0, 0.3); 200 | } 201 | 202 | .awssld__bullets button { 203 | border: 3px solid var(--yellow); 204 | background-color: rgba(0, 0, 0, 0); 205 | } 206 | 207 | .awssld__bullets .awssld__bullets--active { 208 | background-color: var(--yellow); 209 | } 210 | 211 | .image { 212 | height: 100%; 213 | width: 100%; 214 | } 215 | 216 | ${media.lessThan('medium')` 217 | width: 90%; 218 | height: 100%; 219 | margin-bottom: 20px; 220 | `} 221 | `; 222 | 223 | export const FlexContainer = styled.div` 224 | display: flex; 225 | flex-direction: column; 226 | align-items: center; 227 | 228 | ${media.lessThan('medium')` 229 | align-items: center; 230 | justify-content: center; 231 | max-width: 100%; 232 | `} 233 | `; 234 | 235 | export const Content = styled.div` 236 | margin-top: 3%; 237 | `; 238 | 239 | export const ChallengeAbout = styled.div` 240 | width: 100%; 241 | margin-top: 5%; 242 | margin-bottom: 4%; 243 | text-align: left; 244 | 245 | h1 { 246 | color: var(--purple); 247 | font-size: 1.6em; 248 | } 249 | 250 | p { 251 | margin-top: 1%; 252 | color: var(--white); 253 | } 254 | 255 | ${media.lessThan('medium')` 256 | align-items: center; 257 | justify-content: center; 258 | margin-left: 4%; 259 | margin-right: 4%; 260 | max-width: 95%; 261 | `} 262 | `; 263 | 264 | export const ChallengeContainer = styled.div` 265 | width: 100%; 266 | display: flex; 267 | align-items: center; 268 | justify-content: center; 269 | line-height: 1.5em; 270 | margin-bottom: 4%; 271 | p { 272 | color: var(--white); 273 | } 274 | 275 | ${media.lessThan('medium')` 276 | text-align: left; 277 | flex-wrap: wrap; 278 | `} 279 | `; 280 | 281 | export const ChallengeInclude = styled.div` 282 | display: flex; 283 | margin-right: 8%; 284 | flex-direction: column; 285 | 286 | h3 { 287 | color: var(--purple); 288 | margin-top: 0.3em; 289 | padding: 0.2em; 290 | font-size: 1.6em; 291 | } 292 | 293 | span { 294 | color: var(--white); 295 | padding: 0.2em; 296 | display: block; 297 | } 298 | 299 | ${media.lessThan('medium')` 300 | margin-right: 0; 301 | `} 302 | 303 | ${media.lessThan('small')` 304 | margin-right: 4%; 305 | margin-left: 4%; 306 | `} 307 | `; 308 | 309 | export const Icon = styled(FontAwesomeIcon)` 310 | color: var(--green); 311 | `; 312 | 313 | export const ChallengeStart = styled.div` 314 | display: flex; 315 | flex-direction: column; 316 | 317 | h3 { 318 | color: var(--purple); 319 | padding: 0.2em; 320 | margin-top: 0.3em; 321 | font-size: 1.6em; 322 | } 323 | 324 | p { 325 | color: var(--white); 326 | padding: 0.2em; 327 | } 328 | 329 | span { 330 | color: var(--yellow); 331 | font-weight: bold; 332 | margin: 0; 333 | padding: 0; 334 | } 335 | 336 | ${media.lessThan('small')` 337 | margin-right: 4%; 338 | margin-left: 4%; 339 | `} 340 | `; 341 | -------------------------------------------------------------------------------- /frontend/src/pages/Devs/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { 3 | faLightbulb, 4 | faCodeBranch, 5 | faComment, 6 | } from '@fortawesome/free-solid-svg-icons'; 7 | // import { Link } from 'react-router-dom'; 8 | 9 | import * as S from './styled'; 10 | 11 | import api from '../../services/api'; 12 | 13 | import DevCard from '../../components/DevCard'; 14 | import Header from '../../components/Header'; 15 | 16 | export default function Challenges() { 17 | const [devs, setDevs] = useState([]); 18 | const [loading, setLoading] = useState(true); 19 | 20 | useEffect(() => { 21 | async function loadChallenges() { 22 | const response = await api.get('/devs'); 23 | // console.log(response.data); 24 | setDevs(response.data); 25 | 26 | setLoading(false); 27 | } 28 | 29 | loadChallenges(); 30 | }, []); 31 | 32 | return ( 33 | <> 34 |
35 | 36 | 37 | 43 | 44 | 48 | 49 | 50 | Submeter 51 |
52 | novo desafio 53 |
54 |
55 |
56 | 62 | 63 | 67 | 68 | Participar 69 |
70 | da comunidade 71 |
72 |
73 |
74 | 80 | 81 | 85 | 86 | Contribuir 87 |
88 | open source 89 |
90 |
91 |
92 |
93 | {!loading && ( 94 | 95 |

Últimas contribuições

96 | 97 | {devs.map((dev) => ( 98 | 106 | ))} 107 | 108 |
109 | )} 110 |
111 | 112 | ); 113 | } 114 | -------------------------------------------------------------------------------- /frontend/src/pages/Devs/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 4 | 5 | export const Container = styled.div` 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: center; 9 | margin: 6%; 10 | flex: 1 1; 11 | 12 | ${media.lessThan('medium')` 13 | flex-direction: column; 14 | align-items: center; 15 | justify-content: center; 16 | width: 100%; 17 | margin: 0%; 18 | margin-top: 5%; 19 | `} 20 | `; 21 | 22 | export const OptionsContainer = styled.div` 23 | display: flex; 24 | flex-direction: row; 25 | flex-wrap: wrap; 26 | align-items: center; 27 | justify-content: center; 28 | a { 29 | text-decoration: none; 30 | } 31 | `; 32 | 33 | export const Option = styled.div` 34 | width: 300px; 35 | height: 300px; 36 | 37 | background-color: var(--secondary); 38 | 39 | margin: 20px; 40 | border-radius: 10px; 41 | 42 | cursor: pointer; 43 | 44 | display: flex; 45 | flex-direction: column; 46 | flex-wrap: wrap; 47 | align-items: center; 48 | justify-content: center; 49 | 50 | transition: .25s; 51 | 52 | &:hover { 53 | transform: scale(1.05); 54 | background-color: var(--dark-hover); 55 | } 56 | `; 57 | 58 | export const OptionIcon = styled(FontAwesomeIcon)` 59 | color: var(--yellow) !important; 60 | font-size: 60px !important; 61 | height: 60px; 62 | `; 63 | 64 | export const OptionTitle = styled.h2` 65 | color: var(--white); 66 | margin-top: 20px; 67 | text-align: center; 68 | `; 69 | 70 | export const Help = styled.div` 71 | display: flex; 72 | flex-direction: column; 73 | justify-content: center; 74 | align-items: center; 75 | 76 | h3 { 77 | color: var(--white); 78 | font-size: 18px; 79 | font-weight: normal; 80 | margin-bottom: 0.5em; 81 | } 82 | `; 83 | 84 | export const DevsContainer = styled.div` 85 | margin-top: 4em; 86 | color: var(--white); 87 | 88 | display: flex; 89 | flex-direction: column; 90 | align-items: center; 91 | 92 | >h1 { 93 | margin-bottom: 1em; 94 | } 95 | `; 96 | 97 | export const Devs = styled.div` 98 | display: flex; 99 | flex-direction: row; 100 | 101 | justify-content: space-evenly; 102 | flex-wrap: wrap; 103 | 104 | > div { 105 | margin-bottom: 2em; 106 | } 107 | `; 108 | -------------------------------------------------------------------------------- /frontend/src/pages/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CategoriesList from '../../components/CategoriesList'; 3 | import Newsletter from '../../components/Newsletter'; 4 | import Steps from '../../components/Steps'; 5 | import Banner from '../../components/Banner'; 6 | import Header from '../../components/Header'; 7 | 8 | export default function Home() { 9 | return ( 10 | <> 11 |
12 | 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/pages/MyChallenges/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | import DashboardDefault from '../../components/DashboardDefault'; 4 | import { ProgressBar } from '../../components/ChallengeCard/styled'; 5 | import ChallengeCard from '../../components/ChallengeCard'; 6 | 7 | import { Title, Section, Container } from './styled'; 8 | 9 | const fakeData = { 10 | inProgress: [ 11 | { 12 | techs: ['CSS, React Native'], 13 | images: [ 14 | 'https://i.imgur.com/nAsuQSs.png', 15 | 'https://i.imgur.com/A9sWFtn.png', 16 | ], 17 | _id: '5ec1cd5b9cd83622b185db7f', 18 | type: 'Mobile', 19 | name: 'Fisiotherapp', 20 | description: 'Ajude pacientes com exercícios de fisioterapia!', 21 | level: 'beginner', 22 | background: 'https://i.imgur.com/4FgywHQ.png', 23 | github_url: 'https://github.com/Lorenalgm/fisiotheapp', 24 | brief: 25 | 'criar um aplicativo em React Native para ajudar pessoas com exercícios de fisioterapia', 26 | dev_id: '5ec31e81e8051f63faefdf5e', 27 | createdAt: '2020-05-17T23:48:43.265Z', 28 | updatedAt: '2020-08-09T00:31:57.252Z', 29 | __v: 0, 30 | }, 31 | ], 32 | complete: [ 33 | { 34 | techs: ['CSS, React Native'], 35 | images: [ 36 | 'https://i.imgur.com/nAsuQSs.png', 37 | 'https://i.imgur.com/A9sWFtn.png', 38 | ], 39 | _id: '5ec1cd5b9cd83622b185db7f', 40 | type: 'Mobile', 41 | name: 'Fisiotherapp', 42 | description: 'Ajude pacientes com exercícios de fisioterapia!', 43 | level: 'beginner', 44 | background: 'https://i.imgur.com/4FgywHQ.png', 45 | github_url: 'https://github.com/Lorenalgm/fisiotheapp', 46 | brief: 47 | 'criar um aplicativo em React Native para ajudar pessoas com exercícios de fisioterapia', 48 | dev_id: '5ec31e81e8051f63faefdf5e', 49 | createdAt: '2020-05-17T23:48:43.265Z', 50 | updatedAt: '2020-08-09T00:31:57.252Z', 51 | __v: 0, 52 | }, 53 | ], 54 | }; 55 | 56 | function MyChallenges() { 57 | const [challengesList, setChallengesList] = useState({}); 58 | 59 | useEffect(() => { 60 | setChallengesList(fakeData); 61 | }, []); 62 | 63 | return ( 64 | 65 | 66 | Em andamento 67 |
68 | {challengesList.inProgress?.map((challenge) => ( 69 | 76 | 77 | 78 | ))} 79 |
80 | 81 | Concluídos 82 | 83 |
84 | {challengesList.complete?.map((challenge) => ( 85 | 92 | 93 | 94 | ))} 95 |
96 |
97 |
98 | ); 99 | } 100 | 101 | export default MyChallenges; 102 | -------------------------------------------------------------------------------- /frontend/src/pages/MyChallenges/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | export const Container = styled.div` 5 | padding: 0px 80px 0 80px; 6 | 7 | ${media.between('medium', 'large')` 8 | padding: 0px 60px 0 60px; 9 | `} 10 | `; 11 | 12 | export const Title = styled.h2` 13 | margin-bottom: 20px; 14 | `; 15 | 16 | export const Section = styled.section` 17 | margin-top: 3%; 18 | display: flex; 19 | flex-wrap: wrap; 20 | max-width: 100vw; 21 | `; 22 | -------------------------------------------------------------------------------- /frontend/src/pages/Submit/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { FiXCircle } from 'react-icons/fi'; 3 | 4 | import * as S from './styled'; 5 | import Header from '../../components/Header'; 6 | 7 | const levels = [ 8 | { id: 1, title: 'iniciante' }, 9 | { id: 2, title: 'intermediário' }, 10 | { id: 3, title: 'avançado' }, 11 | ]; 12 | const categories = [ 13 | { id: 1, title: 'Frontend' }, 14 | { id: 2, title: 'Backend' }, 15 | { id: 3, title: 'Mobile' }, 16 | ]; 17 | 18 | const linkVerify = /^https:\/\/[A-z]/; 19 | 20 | export default function Submit() { 21 | const [title, setTitle] = useState(''); 22 | const [level, setLevel] = useState('iniciante'); 23 | const [category, setCategory] = useState(''); 24 | const [description, setDescription] = useState(''); 25 | const [languages, setLanguages] = useState(''); 26 | const [link, setLink] = useState(''); 27 | const [imageLink, setImageLink] = useState([]); 28 | const [imageInput, setImageInput] = useState(''); 29 | 30 | function set(data) { 31 | let newString = ''; 32 | if (category.includes(data)) { 33 | newString = category.replace(data, ''); 34 | } else { 35 | newString = category.concat(` ${data}`); 36 | } 37 | setCategory(newString.trim()); 38 | } 39 | 40 | async function addLink() { 41 | console.log(linkVerify.test(imageInput)); 42 | if (imageInput !== '' && linkVerify.test(imageInput)) { 43 | await fetch(`${imageInput}`).then((response) => { 44 | if (response.ok) { 45 | setImageLink([...imageLink, imageInput]); 46 | setImageInput(''); 47 | } 48 | }); 49 | } 50 | } 51 | 52 | function removeImageItem(index) { 53 | const updatedLinkImages = imageLink.filter((item, i) => i !== index); 54 | setImageLink(updatedLinkImages); 55 | } 56 | 57 | function submitForm() {} 58 | 59 | function clearForm() { 60 | setCategory(''); 61 | setDescription(''); 62 | setTitle(''); 63 | setLink(''); 64 | setImageLink([]); 65 | setImageInput(''); 66 | } 67 | 68 | return ( 69 | <> 70 |
71 | 72 | 73 | Submissão de desafios 74 | 75 | 76 | Categoria 77 | 78 | {categories.map((categoryItem) => ( 79 | set(categoryItem.title)} 85 | key={categoryItem.id} 86 | > 87 | {categoryItem.title} 88 | 89 | ))} 90 | 91 | 92 | 93 | Nível 94 | 95 | {levels.map((levelItem) => ( 96 | 100 | setLevel(levelItem.title) 101 | } 102 | key={levelItem.id} 103 | > 104 | {levelItem.title} 105 | 106 | ))} 107 | 108 | 109 | 110 | Título 111 | setTitle(e.target.value)} 115 | /> 116 | 117 | 118 | Descrição 119 | setDescription(e.target.value)} 124 | /> 125 | 126 | 127 | 128 | Imagens 129 | 130 | setImageInput(e.target.value)} 134 | onKeyPress={(e) => { 135 | if (e.key === 'Enter') { 136 | addLink(); 137 | } 138 | }} 139 | required 140 | value={imageInput} 141 | /> 142 | 143 | 144 | Add Image + 145 | 146 | 147 | 148 | {imageLink.map((linkItem, i) => ( 149 |
  • 150 | 151 | 157 |
  • 158 | ))} 159 |
    160 |
    161 | 162 | 163 | Linguagens 164 | setLanguages(e.target.value)} 168 | /> 169 | 170 | 171 | 172 | Informe o link do desafio no github 173 | 174 | setLink(e.target.value)} 178 | /> 179 | 180 | 181 | 182 | Limpar 183 | 184 | 185 | Enviar 186 | 187 | 188 |
    189 |
    190 |
    191 | 192 | ); 193 | } 194 | -------------------------------------------------------------------------------- /frontend/src/pages/Submit/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | export const Section = styled.section` 5 | margin-top: 3%; 6 | margin-bottom: 10%; 7 | display: flex; 8 | flex-wrap: wrap; 9 | max-width: 100vw; 10 | align-items: center; 11 | justify-content: center; 12 | 13 | ${media.lessThan('medium')` 14 | margin-top: 15%; 15 | margin-bottom: 15%; 16 | `} 17 | `; 18 | 19 | export const Container = styled.div` 20 | background-color: var(--secondary); 21 | width: 50%; 22 | max-width: 800px; 23 | height: 50%; 24 | padding: 36px 48px 31px; 25 | border-radius: 10px; 26 | 27 | ${media.between('medium', 'large')` 28 | width: 70%; 29 | height: 70%; 30 | `} 31 | 32 | ${media.between('small', 'medium')` 33 | width: 80%; 34 | height: 80%; 35 | `} 36 | 37 | ${media.lessThan('small')` 38 | width: 80%; 39 | padding: 20px 28px 18px; 40 | `} 41 | `; 42 | 43 | export const Title = styled.p` 44 | color: var(--white); 45 | font-size: 20px; 46 | margin-bottom: 20px; 47 | font-weight: bold; 48 | `; 49 | 50 | export const Form = styled.div` 51 | width: 100%; 52 | margin: 0 0 15px; 53 | `; 54 | export const Field = styled.div` 55 | display: flex; 56 | flex-direction: column; 57 | justify-content: space-between; 58 | margin-bottom: 15px; 59 | 60 | ${media.lessThan('small')` 61 | flex-direction: column; 62 | align-items: flex-start; 63 | `} 64 | `; 65 | export const Label = styled.p` 66 | margin-bottom: 7px; 67 | color: var(--quaternary); 68 | font-size: 18px; 69 | font-weight: bold; 70 | `; 71 | export const Input = styled.input` 72 | width: 100%; 73 | background: var(--quaternary); 74 | height: 40px; 75 | display: flex; 76 | align-items: center; 77 | color: white; 78 | font-size: 14px; 79 | padding: 0 15px; 80 | border-radius: 40px; 81 | 82 | & + Input { 83 | margin-top: 10px; 84 | } 85 | 86 | ${media.lessThan('small')` 87 | width: 100%; 88 | `} 89 | `; 90 | 91 | export const Textarea = styled.textarea` 92 | width: 100%; 93 | min-height: 200px; 94 | background: var(--quaternary); 95 | padding: 10px 15px; 96 | font-size: 16px; 97 | color: white; 98 | border-radius: 20px; 99 | 100 | resize: vertical; 101 | `; 102 | 103 | export const Select = styled.div` 104 | display: flex; 105 | height: 40px; 106 | 107 | ${media.lessThan('small')` 108 | margin-bottom: 60px; 109 | width: 100%; 110 | flex-direction: column; 111 | `} 112 | `; 113 | 114 | const colorMatch = { 115 | category: [ 116 | 'var(--quaternary)', 117 | 'var(--red)', 118 | 'var(--light-purple)', 119 | 'var(--blue)', 120 | ], 121 | level: [ 122 | 'var(--quaternary)', 123 | 'var(--nephritis)', 124 | 'var(--pumpkin)', 125 | 'var(--pomegranate)', 126 | ], 127 | }; 128 | 129 | export const Item = styled.button` 130 | width: 100%; 131 | padding: 5px 10px; 132 | 133 | text-transform: uppercase; 134 | color: var(--tertiary); 135 | font-size: 12px; 136 | font-weight: bold; 137 | 138 | cursor: pointer; 139 | 140 | &:first-child { 141 | background-color: ${(props) => 142 | props.selected 143 | ? colorMatch[props.type][1] 144 | : colorMatch[props.type][0]}; 145 | border-top-left-radius: 25px; 146 | border-bottom-left-radius: 25px; 147 | } 148 | 149 | &:nth-child(2) { 150 | background-color: ${(props) => 151 | props.selected 152 | ? colorMatch[props.type][2] 153 | : colorMatch[props.type][0]}; 154 | border-style: none solid; 155 | } 156 | 157 | &:last-child { 158 | background-color: ${(props) => 159 | props.selected 160 | ? colorMatch[props.type][3] 161 | : colorMatch[props.type][0]}; 162 | border-top-right-radius: 25px; 163 | border-bottom-right-radius: 25px; 164 | } 165 | 166 | ${media.lessThan('small')` 167 | padding: 10px 0; 168 | &:first-child { 169 | border-top-right-radius: 20px; 170 | border-top-left-radius: 20px; 171 | border-bottom-left-radius: 0; 172 | } 173 | &:nth-child(2) { 174 | border-style: solid none; 175 | } 176 | &:last-child { 177 | border-top-right-radius: 0; 178 | border-bottom-right-radius: 20px; 179 | border-bottom-left-radius: 20px; 180 | } 181 | `} 182 | `; 183 | 184 | export const Actions = styled.div` 185 | display: flex; 186 | width: 100%; 187 | margin-top: 30px; 188 | align-items: center; 189 | justify-content: space-around; 190 | `; 191 | 192 | export const Button = styled.button` 193 | padding: 10px 15px; 194 | border-radius: 40px; 195 | font-size: 18px; 196 | cursor: pointer; 197 | 198 | background-color: ${(props) => 199 | props.type === 'submit' ? 'var(--yellow)' : 'var(--quaternary)'}; 200 | `; 201 | 202 | export const AddLinkButton = styled.button` 203 | background-color: rgba(0, 0, 0, 0); 204 | cursor: pointer; 205 | margin-top: 10px; 206 | align-self: flex-end; 207 | padding: 0 20px; 208 | font-weight: bold; 209 | 210 | color: var(--quaternary); 211 | `; 212 | 213 | export const ImagePreview = styled.img` 214 | //width: 50px; 215 | height: 50px; 216 | border-radius: 10px; 217 | `; 218 | 219 | export const PreviewList = styled.ul` 220 | display: flex; 221 | flex-wrap: wrap; 222 | list-style: none; 223 | max-width: 100%; 224 | 225 | li { 226 | width: 50px; 227 | margin: 10px 30px; 228 | display: flex; 229 | align-items: center; 230 | justify-content: space-between; 231 | } 232 | 233 | button { 234 | position: relative; 235 | background-color: rgba(0, 0, 0, 0); 236 | color: var(--red); 237 | top: 25px; 238 | right: 15px; 239 | cursor: pointer; 240 | } 241 | `; 242 | -------------------------------------------------------------------------------- /frontend/src/pages/ToDoChallenge/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import { HeaderImg, Container, ToDoDiv, AddToDo, Content } from './styled'; 4 | import DashboardDefault from '../../components/DashboardDefault'; 5 | 6 | function ToDoChallenge() { 7 | const [todos, setTodos] = useState([ 8 | { 9 | id: 2, 10 | title: 'teste', 11 | checked: 1, 12 | subtodo: [{ id: 1, title: 'teste', checked: 1, subtodo: [] }], 13 | }, 14 | ]); 15 | 16 | function renameTodo(todo, value) { 17 | return { ...todo, title: value }; 18 | } 19 | 20 | function changeChecked(todo, value) { 21 | const newValue = value === '1' ? 0 : 1; 22 | console.log(newValue); 23 | return { ...todo, checked: newValue }; 24 | } 25 | 26 | function createTodo(todo) { 27 | const newValue = { title: '', id: '', checked: 0, subtodo: [] }; 28 | return { ...todo, newValue }; 29 | } 30 | 31 | const modifiers = { 32 | rename: renameTodo, 33 | check: changeChecked, 34 | add: createTodo, 35 | }; 36 | 37 | function findToDo(toDoList, id, modify, value) { 38 | const newTodoList = toDoList.map((todo) => { 39 | if (`${todo.id}` === id) { 40 | const todoItem = modifiers[modify](todo, value); 41 | return todoItem; 42 | } 43 | if (todo.subtodo.length !== 0) { 44 | const subtodos = findToDo(todo.subtodo, id, modify, value); 45 | return { ...todo, subtodo: subtodos }; 46 | } 47 | return todo; 48 | }); 49 | return newTodoList; 50 | } 51 | 52 | function toggleCheckedStatus(e, modify) { 53 | const { id, value } = e.target; 54 | const newTodoList = findToDo(todos, id, modify, value); 55 | setTodos(newTodoList); 56 | console.log(newTodoList); 57 | } 58 | 59 | function renderToDo(todo) { 60 | return ( 61 | <> 62 | 63 | toggleCheckedStatus(e, 'check')} 69 | value={todo.checked} 70 | /> 71 | 72 | toggleCheckedStatus(e, 'rename')} 77 | /> 78 | 79 | {todo.subtodo.map((newTodo) => renderToDo(newTodo))} 80 | 81 | {todo.subtodo.length === 0 && ( 82 | toggleCheckedStatus(e)} 85 | > 86 | + Add To-Do 87 | 88 | )} 89 | 90 | ); 91 | } 92 | 93 | return ( 94 | 95 | 96 | 97 |

    Titulo

    98 | 99 | {todos.map((todo) => renderToDo(todo))} 100 | toggleCheckedStatus(e)}> 101 | + Add To-Do 102 | 103 | 104 |
    105 |
    106 | ); 107 | } 108 | 109 | export default ToDoChallenge; 110 | -------------------------------------------------------------------------------- /frontend/src/pages/ToDoChallenge/styled.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import media from 'styled-media-query'; 3 | 4 | export const Container = styled.div` 5 | padding: 40px 100px 0 100px; 6 | 7 | ${media.between('medium', 'large')` 8 | padding: 40px 60px 0 60px; 9 | `} 10 | `; 11 | 12 | export const HeaderImg = styled.div` 13 | width: 100%; 14 | height: 200px; 15 | background: ${(props) => `url(${props.img}) no-repeat`}; 16 | background-size: cover; 17 | margin-top: 20px; 18 | `; 19 | 20 | export const Content = styled.div` 21 | padding: 30px; 22 | `; 23 | export const ToDoDiv = styled.div` 24 | display: flex; 25 | padding-left: 30px; 26 | padding-top: 10px; 27 | width: 100%; 28 | display: block; 29 | 30 | input[type='checkbox'] { 31 | display: none; 32 | } 33 | 34 | input[type='text'] { 35 | background: var(--primary); 36 | color: var(--quinary); 37 | font-size: 18px; 38 | width: 80%; 39 | } 40 | 41 | label { 42 | cursor: pointer; 43 | font-size: 0; 44 | } 45 | 46 | input + label::before { 47 | border: 1px solid var(--quinary); 48 | content: '\\00a0'; 49 | display: inline-block; 50 | font: 16px/1em sans-serif; 51 | height: 16px; 52 | margin: 0 10px 0 0; 53 | padding: 0; 54 | vertical-align: top; 55 | width: 16px; 56 | } 57 | 58 | input[type='checkbox']:checked + label:before { 59 | color: var(--quinary); 60 | content: '\\2713'; 61 | text-align: center; 62 | } 63 | 64 | input[type='checkbox']:checked + label:after { 65 | font-weight: bold; 66 | } 67 | `; 68 | 69 | export const AddToDo = styled.button` 70 | background: var(--primary); 71 | color: var(--quinary); 72 | opacity: 0; 73 | transition: 0.3s; 74 | padding-left: 30px; 75 | 76 | &:hover { 77 | opacity: 1; 78 | } 79 | `; 80 | -------------------------------------------------------------------------------- /frontend/src/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'; 3 | 4 | import { ToastContainer } from 'react-toastify'; 5 | import 'react-toastify/dist/ReactToastify.css'; 6 | import Home from './pages/Home'; 7 | import Challenges from './pages/Challenges'; 8 | import Detail from './pages/Detail'; 9 | import Devs from './pages/Devs'; 10 | import Submit from './pages/Submit'; 11 | import MyChallenges from './pages/MyChallenges'; 12 | import Dashboard from './pages/Dashboard'; 13 | import Footer from './components/Footer'; 14 | import ToDoChallenge from './pages/ToDoChallenge'; 15 | 16 | import { Container } from './styles/GlobalStyles'; 17 | import ConstructPage from './pages/ConstructPage'; 18 | 19 | const logged = false; 20 | 21 | const LoggedRoute = ({ component: Component, ...rest }) => ( 22 | 25 | logged ? ( 26 | 27 | ) : ( 28 | 31 | ) 32 | } 33 | /> 34 | ); 35 | 36 | function Routes() { 37 | return ( 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 55 | 56 | 57 | 58 | 59 |