├── .cypress.env ├── .dockerignore ├── .env.example ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── test.yml ├── .gitignore ├── .vscode └── settings.json ├── Dockerfile ├── README.md ├── cypress.json ├── cypress ├── fixtures │ └── example.json ├── integration │ └── pages │ │ ├── Dashboard │ │ └── index.test.js │ │ ├── Forgot │ │ └── index.test.js │ │ ├── Login │ │ ├── form_validation.test.js │ │ └── viewport.test.js │ │ └── Signup │ │ └── index.test.js ├── libs │ └── user.builder.js ├── plugins │ └── index.js ├── support │ ├── commands.js │ └── index.js └── videos │ └── pages │ └── Signup │ └── index.test.js.mp4 ├── docker-compose.yml ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html └── robots.txt ├── readme-files └── collabcode.png └── src ├── App.js ├── Router.js ├── components ├── ActionCollab │ ├── index.js │ └── styles.js ├── ButtonCollab │ ├── index.js │ └── styles.js ├── FieldCollab │ ├── index.js │ ├── styles.js │ └── useFieldCollab.js ├── InputCollab │ ├── index.js │ └── styles.js ├── LabelCollab │ ├── index.js │ └── styles.js ├── LogoCollab │ ├── index.js │ └── styles.js ├── PhotoUserCollab │ ├── index.js │ └── styles.js └── TitleCollab │ ├── index.js │ └── styles.js ├── containers ├── ContentAuth │ ├── index.js │ └── styles.js ├── FormAuth │ ├── index.js │ └── styles.js ├── FormForgot │ ├── index.js │ └── validation.js ├── FormLogin │ ├── index.js │ └── validation.js ├── FormResendConfirmAccount │ └── index.js ├── FormSignup │ ├── index.js │ └── validation.js ├── HeaderAuth │ ├── index.js │ └── styles.js ├── HeaderDashboard │ ├── index.js │ └── styles.js └── PageAuth │ ├── index.js │ └── styles.js ├── icons └── actions │ ├── visibility.svg │ └── visibility_off.svg ├── img ├── ball.svg ├── ball_text.svg ├── ball_text_light.svg ├── horizontal.svg ├── horizontal_light.svg └── userdefault.jpg ├── index.js ├── libs └── validation │ ├── email.js │ ├── index.js │ ├── minLength.js │ ├── required.js │ └── useValidation.js ├── pages ├── Dashboard │ ├── index.js │ └── styles.js ├── Forgot │ └── index.js ├── Login │ └── index.js ├── ResendConfirmAccount │ └── index.js └── Signup │ ├── index.js │ └── styles.js ├── services └── AuthService.js └── styles ├── elements └── Base.js ├── generic └── Reset.js ├── index.js ├── settings ├── Colors.js ├── Gaps.js └── Radius.js └── tools ├── Gradients.js ├── Separator.js ├── Shadow.js └── Typography.js /.cypress.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | PORT=3000 3 | 4 | REACT_APP_API_AUTH=http://auth:3001 -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | PORT=3000 3 | 4 | REACT_APP_API_AUTH=http://localhost:3001 -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": [ 7 | "plugin:react/recommended", 8 | "airbnb", 9 | "plugin:prettier/recommended" 10 | ], 11 | "globals": { 12 | "Atomics": "readonly", 13 | "SharedArrayBuffer": "readonly" 14 | }, 15 | "parserOptions": { 16 | "ecmaFeatures": { 17 | "jsx": true 18 | }, 19 | "ecmaVersion": 2018, 20 | "sourceType": "module" 21 | }, 22 | "plugins": ["react"], 23 | "rules": {} 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # What's the issue linked? 2 | 3 | Resolve #XX (issue number). 4 | 5 | # Test cases 6 | 7 | Report the test cases that you did in list items: 8 | 9 | - [x] Open page 10 | - [x] Validation name field 11 | - [x] Validation email field 12 | - [x] Send form 13 | 14 | # Did you test this feature on all browsers? 15 | 16 | Yes, tested with all the following browsers: 17 | 18 | - [x] Firefox 19 | - [x] Chrome 20 | 21 | # Description 22 | 23 | Only this is no required, here you add anything that you consider important. 24 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | pull_request: 5 | branches: [develop] 6 | 7 | pull_request_review: 8 | branches: [develop] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-18.04 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Test Cypress 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: "12.x" 20 | - run: npm ci 21 | - run: cp .cypress.env .env 22 | - run: docker-compose up -d 23 | - run: npm run cy:ci 24 | env: 25 | CI: true 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "editor.formatOnSave": true, 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll.eslint": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #FROM node:12 as training-frontend 2 | FROM cypress/included:4.3.0 as training-frontend 3 | WORKDIR /app 4 | COPY . ./ 5 | RUN npm i --silent 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CollabCode](readme-files/collabcode.png "Logo da CollabCode") 2 | 3 | # CollabCode Training 4 | 5 | An Open Source online course platform. It's the FrontEnd that use React. 6 | 7 | ## Getting Started 8 | 9 | 1. Fork this repo and clone in your machine; 10 | 11 | 2. Change directory to `training-frontend` where you cloned it: 12 | 13 | ```bash 14 | git clone https://github.com/CollabCodeTech/training-frontend 15 | ``` 16 | 17 | 3. At the terminal, run: 18 | 19 | ```bash 20 | cd training-frontend 21 | npm i 22 | cp .env.example .env 23 | docker-compose up -d 24 | ``` 25 | 26 | 4. Open up [localhost:3000](http://localhost:3000) and start using it 27 | 28 | ### Prerequisites 29 | 30 | - Npm 31 | - Node (>=12.16.1) 32 | - Docker 33 | - Docker compose 34 | 35 | ## Running the tests when you are developing 36 | 37 | ```bash 38 | npm run cy:open 39 | ``` 40 | 41 | ## Running all tests 42 | 43 | ```bash 44 | npm test 45 | ``` 46 | 47 | ## Built With 48 | 49 | - [ReactJS](http://reactjs.org) 50 | 51 | ## Contributing 52 | 53 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. 54 | 55 | ## Authors 56 | 57 | - **Joviane Jardim** - [@joviane](https://twitter.com/jovianejardim) 58 | - **Marco Bruno** - [@marcobrunobr](https://twitter.com/marcobrunobr) 59 | 60 | See also the list of [contributors](https://github.com/CollabCodeTech/training-frontend/contributors) who participated in this project. 61 | 62 | ## License 63 | 64 | This project is licensed under the MPL 2.0 License - see the [LICENSE](LICENSE.md) file for details 65 | 66 | ## Acknowledgments 67 | 68 | Thanks to all members of CollabCode's community for the support! We love you! 69 | 70 | ## Next Releases 71 | 72 | - [ ] One course and video player (**15 March 2020**) _ashe_ 73 | - [ ] Panel to other person that we will invite to send courses (**15 May 2020**) _aphelios_ 74 | - [ ] Open panel to community to send courses (**15 July 2020**) _anivia_ 75 | 76 | ## Questions 77 | 78 | - [Discord CollabCode](http://bit.ly/discord-collabcode) 79 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "viewportWidth": 1920, 3 | "viewportHeight": 1080, 4 | "baseUrl": "http://localhost:3000", 5 | "video": false, 6 | "defaultCommandTimeout": 8000, 7 | "pageLoadTimeout": 90000, 8 | "numTestsKeptInMemory": 5, 9 | "chromeWebSecurity": false, 10 | "env": { 11 | "RETRIES": 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /cypress/integration/pages/Dashboard/index.test.js: -------------------------------------------------------------------------------- 1 | describe("Page Dashboard", function() { 2 | it("Open page on Desktop", function() { 3 | cy.visit("/dashboard"); 4 | }); 5 | 6 | it("Open page on Mobile", function() { 7 | cy.visit("/dashboard"); 8 | cy.viewport("iphone-5"); 9 | }); 10 | 11 | it("Open page on TV 4K", function() { 12 | cy.visit("/dashboard"); 13 | cy.viewport(3840, 2160); 14 | }); 15 | 16 | it("Verify if exist header on Web", function() { 17 | cy.visit("/dashboard"); 18 | 19 | cy.get("header"); 20 | cy.get("header > img"); 21 | cy.get("header > div"); 22 | }); 23 | 24 | it("Verify if exist header on Mobile", function() { 25 | cy.visit("/dashboard"); 26 | cy.viewport("iphone-5"); 27 | 28 | cy.get("header"); 29 | cy.get("header > img"); 30 | cy.get("header > div").should("be.not.visible"); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /cypress/integration/pages/Forgot/index.test.js: -------------------------------------------------------------------------------- 1 | describe("Page Forgot", function() { 2 | it("Verify if exist the field E-mail", function() { 3 | cy.visit("/auth/forgot"); 4 | 5 | cy.contains("E-mail:"); 6 | cy.get("input[name=email]"); 7 | }); 8 | 9 | it("Send form without filling in the email input", function() { 10 | cy.visit("/auth/forgot"); 11 | 12 | cy.contains("Enviar").click(); 13 | cy.contains("E-mail é obrigatório"); 14 | }); 15 | 16 | it("Send form with email invalid", function() { 17 | cy.visit("/auth/forgot"); 18 | 19 | cy.get("input[name=email]").type("empix"); 20 | cy.contains("Enviar").click(); 21 | cy.contains("Preencha com email válido"); 22 | }); 23 | 24 | it("Send form with email field valid", function() { 25 | cy.visit("/auth/forgot"); 26 | 27 | cy.get("input[name=email]").type("any.email@gmail.com"); 28 | cy.contains("Enviar").click(); 29 | 30 | cy.get("input[name=email] + span").should("not.exist"); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /cypress/integration/pages/Login/form_validation.test.js: -------------------------------------------------------------------------------- 1 | describe("Login form validation", function() { 2 | let $emailField, $passwordField, $submitButton; 3 | 4 | before(function() { 5 | cy.visit("/auth/login"); 6 | $emailField = cy.get("label[for=email]"); 7 | $passwordField = cy.get("label[for=password]"); 8 | $submitButton = cy.get("button[content=Entrar]"); 9 | }); 10 | 11 | it("Verify if exist the fields: E-mail and Password", function() { 12 | $emailField.contains("E-mail:").get("input[name=email]"); 13 | $passwordField.contains("Senha:").get("input[name=password]"); 14 | }); 15 | 16 | it("Send form without filling in the inputs", function() { 17 | $submitButton.click(); 18 | 19 | $emailField.get("span:last-of-type").contains("E-mail é obrigatório"); 20 | $passwordField.get("span:last-of-type").contains("Senha é obrigatória"); 21 | }); 22 | 23 | it("Send form with email invalid", function() { 24 | $emailField.get("input[name=email]").type("santiael"); 25 | 26 | $submitButton.click(); 27 | 28 | $emailField.get("span:last-of-type").contains("Preencha com email válido"); 29 | }); 30 | 31 | it("Send form with password invalid", function() { 32 | $passwordField.get("input[name=password]").type("1234567"); 33 | 34 | $submitButton.click(); 35 | $passwordField 36 | .get("span:last-of-type") 37 | .contains("Senha tem que ter 8 ou mais caracteres"); 38 | }); 39 | 40 | it("Send form with all fields valid", function() { 41 | $emailField.get("input[name=email]").type("rafaelsantiagods@gmail.com"); 42 | $passwordField.get("input[name=password]").type("12345678"); 43 | 44 | $submitButton.click(); 45 | 46 | $emailField.get("span").should("have.length", 1); 47 | $passwordField.get("span").should("have.length", 1); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /cypress/integration/pages/Login/viewport.test.js: -------------------------------------------------------------------------------- 1 | describe("Login page in different screen sizes", function() { 2 | it("Open page on Desktop", function() { 3 | cy.visit("/auth/login"); 4 | }); 5 | 6 | it("Open page on Mobile", function() { 7 | cy.visit("/auth/login"); 8 | cy.viewport("iphone-5"); 9 | }); 10 | 11 | it("Open page on TV 4K", function() { 12 | cy.visit("/auth/login"); 13 | cy.viewport(3840, 2160); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /cypress/integration/pages/Signup/index.test.js: -------------------------------------------------------------------------------- 1 | import UserBuilder from "../../../libs/user.builder"; 2 | 3 | describe("Page Signup", function () { 4 | before(function () { 5 | Cypress.Cookies.debug(true); 6 | }); 7 | 8 | it("Open page on Desktop", function () { 9 | cy.visit("/auth/signup"); 10 | }); 11 | 12 | it("Open page on Mobile", function () { 13 | cy.visit("/auth/signup"); 14 | cy.viewport("iphone-5"); 15 | }); 16 | 17 | it("Open page on TV 4K", function () { 18 | cy.visit("/auth/signup"); 19 | cy.viewport(3840, 2160); 20 | }); 21 | 22 | it("Verify if exist the fields: Name, E-mail and Password", function () { 23 | cy.visit("/auth/signup"); 24 | 25 | cy.contains("Nome:"); 26 | cy.get("input[name=name]"); 27 | 28 | cy.contains("E-mail:"); 29 | cy.get("input[name=email]"); 30 | 31 | cy.contains("Senha:"); 32 | cy.get("input[name=password]"); 33 | }); 34 | 35 | it("Send the form without filling in the inputs", function () { 36 | cy.visit("/auth/signup"); 37 | cy.contains("Enviar").click(); 38 | 39 | cy.contains("Nome é obrigatório"); 40 | cy.contains("E-mail é obrigatório"); 41 | cy.contains("Senha é obrigatória"); 42 | }); 43 | 44 | it("Send the form with name invalid that has only one char", function () { 45 | const { name } = UserBuilder.nameInvalid(); 46 | 47 | cy.visit("/auth/signup"); 48 | cy.get("input[name=name]").type(name); 49 | cy.contains("Enviar").click(); 50 | cy.contains("Nome tem que ter 2 ou mais caracteres"); 51 | }); 52 | 53 | it("Send the form with email invalid", function () { 54 | const { email } = UserBuilder.emailInvalid(); 55 | 56 | cy.visit("/auth/signup"); 57 | cy.get("input[name=email]").type(email); 58 | cy.contains("Enviar").click(); 59 | cy.contains("Preencha com email válido"); 60 | }); 61 | 62 | it("Send the form with password invalid", function () { 63 | const { password } = UserBuilder.passwordInvalid(); 64 | 65 | cy.visit("/auth/signup"); 66 | cy.get("input[name=password]").type(password); 67 | cy.contains("Enviar").click(); 68 | cy.contains("Senha tem que ter 8 ou mais caracteres"); 69 | }); 70 | 71 | it("Send the form with the fields name, email and password valid", function () { 72 | const { name, email, password } = UserBuilder.randomUserInfo(); 73 | 74 | cy.visit("/auth/signup"); 75 | cy.get("input[name=name]").type(name); 76 | cy.get("input[name=email]").type(email); 77 | cy.get("input[name=password]").type(password); 78 | cy.contains("Enviar").click(); 79 | cy.location("pathname").should("include", "dashboard"); 80 | }); 81 | 82 | it("Verify if the cookie jwt was create", function () { 83 | const { name, email, password } = UserBuilder.randomUserInfo(); 84 | 85 | cy.clearCookies(); 86 | cy.visit("/auth/signup"); 87 | cy.get("input[name=name]").type(name); 88 | cy.get("input[name=email]").type(email); 89 | cy.get("input[name=password]").type(password); 90 | cy.contains("Enviar").click(); 91 | cy.location("pathname").should("include", "dashboard"); 92 | // cy.contains("Dashboard").then(function() { 93 | // cy.getCookie("jwt") 94 | // .should("have.property", "value") 95 | // .and( 96 | // "match", 97 | // /^[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*$/ 98 | // ); 99 | // }); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /cypress/libs/user.builder.js: -------------------------------------------------------------------------------- 1 | import faker from "faker"; 2 | 3 | const generateName = () => { 4 | const firstName = faker.name.firstName(); 5 | const lastName = faker.name.lastName(); 6 | 7 | return { name: `${firstName} ${lastName}` }; 8 | }; 9 | 10 | const nameInvalid = () => ({ name: faker.internet.password(1) }); 11 | const emailInvalid = () => ({ email: faker.lorem.word() }); 12 | const passwordInvalid = () => ({ password: faker.internet.password(7) }); 13 | const emailValid = () => ({ email: faker.internet.email() }); 14 | const passwordValid = () => ({ password: faker.internet.password() }); 15 | 16 | const randomUserInfo = (options = {}) => { 17 | const blank = {}; 18 | 19 | return Object.assign( 20 | blank, 21 | { 22 | name: generateName().name, 23 | email: emailValid().email, 24 | password: passwordValid().password 25 | }, 26 | { ...options } 27 | ); 28 | }; 29 | 30 | export default { 31 | generateName, 32 | randomUserInfo, 33 | nameInvalid, 34 | emailInvalid, 35 | passwordInvalid, 36 | emailValid, 37 | passwordValid 38 | }; 39 | -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | module.exports = (on, config) => { 19 | // `on` is used to hook into various events Cypress emits 20 | // `config` is the resolved Cypress config 21 | } 22 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /cypress/videos/pages/Signup/index.test.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CollabCodeTech/training-frontend/c45676c1a567844004a5741ba0c61d822302c7ad/cypress/videos/pages/Signup/index.test.js.mp4 -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | email: 5 | image: collabcode/training-email 6 | container_name: "email" 7 | networks: 8 | - internal 9 | 10 | auth: 11 | image: collabcode/training-auth 12 | container_name: "auth" 13 | environment: 14 | - NODE_ENV 15 | ports: 16 | - "3001:3001" 17 | networks: 18 | - internal 19 | depends_on: 20 | - mongo 21 | 22 | mongo: 23 | image: mongo 24 | container_name: "mongo" 25 | environment: 26 | MONGO_INITDB_ROOT_USERNAME: root 27 | MONGO_INITDB_ROOT_PASSWORD: root 28 | ports: 29 | - "27017:27017" 30 | networks: 31 | - internal 32 | 33 | frontend: 34 | image: "cypress/included:4.3.0" 35 | container_name: "frontend" 36 | ipc: host 37 | environment: 38 | - NODE_ENV=local 39 | - QT_X11_NO_MITSHM=1 40 | - _X11_NO_MITSHM=1 41 | - _MITSHM=0 42 | - ELECTRON_ENABLE_STACK_DUMPING=1 43 | ports: 44 | - "3000:${PORT}" 45 | expose: 46 | - "3000" 47 | working_dir: /app 48 | networks: 49 | - internal 50 | volumes: 51 | - .:/app/ 52 | entrypoint: npm start 53 | depends_on: 54 | - auth 55 | - email 56 | networks: 57 | internal: 58 | driver: bridge 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "training-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.4.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "cypress": "^4.0.1", 10 | "react": "^16.12.0", 11 | "react-dom": "^16.12.0", 12 | "react-router-dom": "^5.1.2", 13 | "react-scripts": "^3.4.0", 14 | "styled-components": "^5.0.0", 15 | "superagent": "^5.2.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "cypress run", 21 | "eject": "react-scripts eject", 22 | "cy:open": "cypress open", 23 | "cy:ci": "docker exec frontend cypress run" 24 | }, 25 | "eslintConfig": { 26 | "extends": "react-app" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | }, 40 | "devDependencies": { 41 | "cypress-plugin-retries": "^1.5.2", 42 | "eslint": "^6.8.0", 43 | "eslint-config-airbnb": "^18.0.1", 44 | "eslint-config-prettier": "^6.10.0", 45 | "eslint-plugin-import": "^2.20.1", 46 | "eslint-plugin-jsx-a11y": "^6.2.3", 47 | "eslint-plugin-prettier": "^3.1.2", 48 | "eslint-plugin-react": "^7.18.3", 49 | "eslint-plugin-react-hooks": "^1.7.0", 50 | "faker": "^4.1.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CollabCodeTech/training-frontend/c45676c1a567844004a5741ba0c61d822302c7ad/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | 28 | 32 | 33 | CollabCode Training 34 | 35 | 36 | 37 |
38 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /readme-files/collabcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CollabCodeTech/training-frontend/c45676c1a567844004a5741ba0c61d822302c7ad/readme-files/collabcode.png -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Styles from "./styles"; 4 | import Router from "./Router"; 5 | 6 | export default function App() { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/Router.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { BrowserRouter as Routes, Switch, Route } from "react-router-dom"; 3 | 4 | import Login from "./pages/Login"; 5 | import Signup from "./pages/Signup"; 6 | import Forgot from "./pages/Forgot"; 7 | import ResendConfirmAccount from "./pages/ResendConfirmAccount"; 8 | 9 | import Dashboard from "./pages/Dashboard"; 10 | 11 | function Router() { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | ); 27 | } 28 | 29 | export default Router; 30 | -------------------------------------------------------------------------------- /src/components/ActionCollab/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Action } from "./styles"; 4 | 5 | function ActionCollab(props) { 6 | return {props.content}; 7 | } 8 | 9 | export default ActionCollab; 10 | -------------------------------------------------------------------------------- /src/components/ActionCollab/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | import { Link } from "react-router-dom"; 4 | import { _body1 } from "../../styles/tools/Typography"; 5 | 6 | export const Action = styled(Link)` 7 | ${_body1}; 8 | text-decoration: none; 9 | color: var(--color-floral-white); 10 | 11 | &:hover, 12 | &:focus { 13 | color: var(--color-peach); 14 | } 15 | `; 16 | -------------------------------------------------------------------------------- /src/components/ButtonCollab/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button, Content } from "./styles"; 3 | 4 | function ButtonCollab(props) { 5 | return ( 6 | 9 | ); 10 | } 11 | 12 | export default ButtonCollab; 13 | -------------------------------------------------------------------------------- /src/components/ButtonCollab/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import { _h6 } from "../../styles/tools/Typography"; 5 | 6 | export const Button = styled(Link).attrs({ as: "button" })` 7 | ${_h6}; 8 | cursor: pointer; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | position: relative; 13 | text-decoration: none; 14 | border: none; 15 | border-radius: var(--radius-big); 16 | background-color: var(--color-fiery-rose); 17 | color: var(--color-floral-white); 18 | opacity: 0.9; 19 | transition: opacity 100ms linear, transform 50ms linear; 20 | z-index: 1; 21 | 22 | &:hover, 23 | &:focus { 24 | opacity: 1; 25 | } 26 | 27 | &:active { 28 | transform: translateY(2px); 29 | } 30 | `; 31 | 32 | export const Content = styled.span``; 33 | -------------------------------------------------------------------------------- /src/components/FieldCollab/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import iconVisibilityOff from "../../icons/actions/visibility_off.svg"; 3 | import iconVisibility from "../../icons/actions/visibility.svg"; 4 | import LabelCollab from "../LabelCollab"; 5 | import InputCollab from "../InputCollab"; 6 | import useFieldCollab from "./useFieldCollab"; 7 | import { Content, Icon, Error } from "./styles"; 8 | 9 | const FieldCollab = ({ 10 | content, 11 | id, 12 | name, 13 | value, 14 | htmlFor, 15 | type = "text", 16 | msgError, 17 | onChange 18 | }) => { 19 | const { show, showPassword } = useFieldCollab(); 20 | 21 | function showIconVisibility(type) { 22 | if (type === "password") { 23 | return ( 24 | 28 | ); 29 | } 30 | } 31 | 32 | function showError(msgError) { 33 | return msgError && {msgError}; 34 | } 35 | 36 | function defineType(type) { 37 | if (type === "password" && !show) { 38 | return "password"; 39 | } else if (type === "password" && show) { 40 | return "text"; 41 | } 42 | return type; 43 | } 44 | 45 | return ( 46 | 47 | {content} 48 | 55 | {showIconVisibility(type)} 56 | {showError(msgError)} 57 | 58 | ); 59 | }; 60 | 61 | export default FieldCollab; 62 | -------------------------------------------------------------------------------- /src/components/FieldCollab/styles.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { _caption } from '../../styles/tools/Typography'; 3 | 4 | export const Content = styled.span` 5 | display: block; 6 | margin-bottom: var(--gap-smaller); 7 | `; 8 | 9 | export const Icon = styled.img` 10 | position: absolute; 11 | bottom: var(--gap-smaller); 12 | right: var(--gap-smaller); 13 | overflow: hidden; 14 | width: 30px; 15 | transition: transform 100ms linear; 16 | cursor: pointer; 17 | 18 | &:hover { 19 | transform: scale(1.1); 20 | } 21 | `; 22 | 23 | export const Error = styled.span` 24 | ${_caption}; 25 | color: var(--color-sandstorm); 26 | position: absolute; 27 | bottom: 0; 28 | left: 0; 29 | transform: translateY(100%); 30 | `; 31 | -------------------------------------------------------------------------------- /src/components/FieldCollab/useFieldCollab.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | function useFieldCollab() { 4 | const [show, setShow] = useState(false); 5 | 6 | function showPassword() { 7 | setShow(old => !old); 8 | } 9 | 10 | return { 11 | show, 12 | showPassword 13 | }; 14 | } 15 | 16 | export default useFieldCollab; 17 | -------------------------------------------------------------------------------- /src/components/InputCollab/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Input } from "./styles"; 4 | 5 | function InputCollab(props) { 6 | return ; 7 | } 8 | 9 | export default InputCollab; 10 | -------------------------------------------------------------------------------- /src/components/InputCollab/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | import { _h6 } from "../../styles/tools/Typography"; 4 | 5 | export const Input = styled.input` 6 | --widthIcon: 24px; 7 | ${_h6}; 8 | box-sizing: border-box; 9 | color: var(--color-platinum); 10 | background-color: transparent; 11 | width: 100%; 12 | border: none; 13 | outline: none; 14 | border-bottom: 1px solid var(--color-floral-white); 15 | padding: var(--gap-smaller) calc(var(--widthIcon) + var(--gap-smaller) * 2) 16 | var(--gap-smaller) 0; 17 | 18 | &:hover, 19 | &:focus { 20 | border-bottom-color: var(--color-peach); 21 | border-bottom-witth: 2px; 22 | } 23 | `; 24 | -------------------------------------------------------------------------------- /src/components/LabelCollab/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Label } from "./styles"; 4 | 5 | function LabelCollab({ htmlFor, children, content, warning = false }) { 6 | return ( 7 | 10 | ); 11 | } 12 | 13 | export default LabelCollab; 14 | -------------------------------------------------------------------------------- /src/components/LabelCollab/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | import { _h6 } from "../../styles/tools/Typography"; 4 | 5 | export const Label = styled.label` 6 | ${_h6}; 7 | color: ${({ warning }) => 8 | warning ? "var(--color-sandstorm)" : "var(--color-floral-white)"}; 9 | position: relative; 10 | display: block; 11 | 12 | &:hover { 13 | color: var(--color-peach); 14 | } 15 | `; 16 | -------------------------------------------------------------------------------- /src/components/LogoCollab/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Logo } from "./styles"; 4 | 5 | import ball from "../../img/ball.svg"; 6 | import ballText from "../../img/ball_text.svg"; 7 | import ballTextLight from "../../img/ball_text_light.svg"; 8 | import horizontal from "../../img/horizontal.svg"; 9 | import horizontalLight from "../../img/horizontal_light.svg"; 10 | 11 | const LogoCollab = function({ type = "horizontal" }) { 12 | const types = { ball, ballText, ballTextLight, horizontal, horizontalLight }; 13 | 14 | return ; 15 | }; 16 | 17 | export default LogoCollab; 18 | -------------------------------------------------------------------------------- /src/components/LogoCollab/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Logo = styled.img``; 4 | -------------------------------------------------------------------------------- /src/components/PhotoUserCollab/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Container, PhotoUser } from "./styles"; 4 | import ImageUserDefault from "../../img/userdefault.jpg"; 5 | 6 | const PhotoUserCollab = ({ img }) => ( 7 | 8 | 9 | 10 | ); 11 | 12 | export default PhotoUserCollab; 13 | -------------------------------------------------------------------------------- /src/components/PhotoUserCollab/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Container = styled.div` 4 | height: 40px; 5 | width: 40px; 6 | border-radius: 50%; 7 | vertical-align: top; 8 | justify-content: center; 9 | align-items: center; 10 | overflow: hidden; 11 | text-decoration: none; 12 | border: 0; 13 | margin: 10px; 14 | position: relative; 15 | 16 | @media (max-width: 767.98px) { 17 | display: none; 18 | } 19 | `; 20 | 21 | export const PhotoUser = styled.img` 22 | margin: 0; 23 | position: absolute; 24 | top: 50%; 25 | left: 50%; 26 | margin-right: -50%; 27 | transform: translate(-50%, -50%) 28 | `; 29 | -------------------------------------------------------------------------------- /src/components/TitleCollab/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Title } from "./styles"; 4 | 5 | function TitleCollab({ content }) { 6 | return {content}; 7 | } 8 | 9 | export default TitleCollab; 10 | -------------------------------------------------------------------------------- /src/components/TitleCollab/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | import { _h5 } from "../../styles/tools/Typography"; 4 | 5 | export const Title = styled.h1` 6 | ${_h5}; 7 | color: var(--color-floral-white); 8 | `; 9 | -------------------------------------------------------------------------------- /src/containers/ContentAuth/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Content } from "./styles"; 4 | 5 | function ContentAuth({ children }) { 6 | return {children}; 7 | } 8 | 9 | export default ContentAuth; 10 | -------------------------------------------------------------------------------- /src/containers/ContentAuth/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Content = styled.div` 4 | width: 400px; 5 | 6 | @media (min-height: 570px) { 7 | height: auto; 8 | } 9 | 10 | @media (min-width: 1596px) and (min-height: 900px) { 11 | display: flex; 12 | width: 1596px; 13 | height: 900px; 14 | overflow: hidden; 15 | background-color: var(--color-arsenic); 16 | } 17 | 18 | @media (min-width: 1920px) and (min-height: 1080px) { 19 | width: 75%; 20 | height: 80%; 21 | } 22 | `; 23 | -------------------------------------------------------------------------------- /src/containers/FormAuth/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Form } from "./styles"; 4 | 5 | function FormAuth(props) { 6 | return
; 7 | } 8 | 9 | export default FormAuth; 10 | -------------------------------------------------------------------------------- /src/containers/FormAuth/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | import { _shadowBottom } from "../../styles/tools/Shadow"; 4 | import { Title } from "../../components/TitleCollab/styles"; 5 | import { Label } from "../../components/LabelCollab/styles"; 6 | import { Button } from "../../components/ButtonCollab/styles"; 7 | import { Action } from "../../components/ActionCollab/styles"; 8 | import { _h4 } from "../../styles/tools/Typography"; 9 | 10 | export const Form = styled.form` 11 | box-sizing: border-box; 12 | padding: 0 var(--gap-small) var(--gap-medium); 13 | 14 | & > ${Title} { 15 | text-align: center; 16 | margin-bottom: var(--gap-bigger); 17 | } 18 | 19 | & > ${Label}:not(:last-of-type) { 20 | display: block; 21 | margin-bottom: var(--gap-big); 22 | } 23 | 24 | & > ${Button} { 25 | ${_shadowBottom}; 26 | width: 100%; 27 | height: 42px; 28 | margin-top: var(--gap-bigger); 29 | } 30 | 31 | & > ${Action} { 32 | display: block; 33 | text-align: center; 34 | margin-top: var(--gap-small); 35 | 36 | &:active { 37 | box-sizing: none; 38 | } 39 | } 40 | 41 | @media (min-width: 1596px) and (min-height: 900px) { 42 | box-sizing: border-box; 43 | width: 60%; 44 | padding-left: 18%; 45 | padding-right: 5%; 46 | display: flex; 47 | flex-direction: column; 48 | justify-content: center; 49 | 50 | & > ${Title} { 51 | ${_h4}; 52 | margin-bottom: calc(var(--gap-bigger) * 2); 53 | } 54 | 55 | & > ${Label}:not(:last-of-type) { 56 | margin-bottom: var(--gap-bigger); 57 | } 58 | 59 | & > ${Button} { 60 | height: 50px; 61 | margin-top: calc(var(--gap-bigger) + var(--gap-medium)); 62 | } 63 | } 64 | `; 65 | -------------------------------------------------------------------------------- /src/containers/FormForgot/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import TitleCollab from "../../components/TitleCollab"; 4 | import FieldCollab from "../../components/FieldCollab"; 5 | import ButtonCollab from "../../components/ButtonCollab"; 6 | 7 | import FormAuth from "../../containers/FormAuth"; 8 | import LabelCollab from "../../components/LabelCollab"; 9 | 10 | import useValidation from "../../libs/validation/useValidation"; 11 | import validation from "./validation"; 12 | 13 | function FormForgot() { 14 | const { value, handleChange, handleSubmit, errors } = useValidation( 15 | validation, 16 | sendFormForgot, 17 | "email" 18 | ); 19 | 20 | function sendFormForgot() {} 21 | 22 | return ( 23 | 24 | 25 | 26 | 30 | 31 | 40 | 41 | 42 | 43 | ); 44 | } 45 | 46 | export default FormForgot; 47 | -------------------------------------------------------------------------------- /src/containers/FormForgot/validation.js: -------------------------------------------------------------------------------- 1 | import { required, isEmail } from "../../libs/validation"; 2 | 3 | function validation(values) { 4 | const { email } = values; 5 | let errors = {}; 6 | 7 | errors.email = 8 | required(email, "E-mail é obrigatório") || 9 | isEmail(email, "Preencha com email válido"); 10 | 11 | return errors; 12 | } 13 | 14 | export default validation; 15 | -------------------------------------------------------------------------------- /src/containers/FormLogin/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import TitleCollab from "../../components/TitleCollab"; 4 | import FieldCollab from "../../components/FieldCollab"; 5 | import ButtonCollab from "../../components/ButtonCollab"; 6 | import ActionCollab from "../../components/ActionCollab"; 7 | 8 | import FormAuth from "../../containers/FormAuth"; 9 | 10 | import useValidation from "../../libs/validation/useValidation"; 11 | import validation from "./validation"; 12 | 13 | function FormLogin() { 14 | const { value, handleChange, handleSubmit, errors } = useValidation( 15 | validation, 16 | signIn, 17 | "email", 18 | "password" 19 | ); 20 | 21 | function signIn() {} 22 | 23 | return ( 24 | 25 | 26 | 27 | 36 | 37 | 47 | 48 | 49 | 50 | 51 | 52 | ); 53 | } 54 | 55 | export default FormLogin; 56 | -------------------------------------------------------------------------------- /src/containers/FormLogin/validation.js: -------------------------------------------------------------------------------- 1 | import { required, isEmail, minLength } from "../../libs/validation"; 2 | 3 | function validation(values) { 4 | const { email, password } = values; 5 | let errors = {}; 6 | 7 | errors.email = 8 | required(email, "E-mail é obrigatório") || 9 | isEmail(email, "Preencha com email válido"); 10 | 11 | errors.password = 12 | required(password, "Senha é obrigatória") || 13 | minLength(8, password, "Senha tem que ter 8 ou mais caracteres"); 14 | 15 | return errors; 16 | } 17 | 18 | export default validation; 19 | -------------------------------------------------------------------------------- /src/containers/FormResendConfirmAccount/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import TitleCollab from "../../components/TitleCollab"; 4 | import FieldCollab from "../../components/FieldCollab"; 5 | import ButtonCollab from "../../components/ButtonCollab"; 6 | 7 | import FormAuth from "../FormAuth"; 8 | import LabelCollab from "../../components/LabelCollab"; 9 | 10 | function FormResendConfirmAccount() { 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | 27 | export default FormResendConfirmAccount; 28 | -------------------------------------------------------------------------------- /src/containers/FormSignup/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | 4 | import TitleCollab from "../../components/TitleCollab"; 5 | import FieldCollab from "../../components/FieldCollab"; 6 | import ButtonCollab from "../../components/ButtonCollab"; 7 | import ActionCollab from "../../components/ActionCollab"; 8 | import FormAuth from "../../containers/FormAuth"; 9 | import useValidation from "../../libs/validation/useValidation"; 10 | import validation from "./validation"; 11 | import AuthService from "../../services/AuthService"; 12 | 13 | function FormLogin() { 14 | const { 15 | value, 16 | handleChange, 17 | handleSubmit, 18 | errors, 19 | setErrors 20 | } = useValidation(validation, sendUser, "name", "email", "password"); 21 | const history = useHistory(); 22 | 23 | function sendUser() { 24 | AuthService.signup(value) 25 | .withCredentials() 26 | .then(function() { 27 | history.replace("/dashboard"); 28 | }) 29 | .catch(function({ response }) { 30 | const { field, error } = response.body[0]; 31 | setErrors({ [field]: error }); 32 | }); 33 | } 34 | 35 | return ( 36 | 37 | 38 | 39 | 48 | 49 | 58 | 59 | 69 | 70 | 71 | 72 | 73 | ); 74 | } 75 | 76 | export default FormLogin; 77 | -------------------------------------------------------------------------------- /src/containers/FormSignup/validation.js: -------------------------------------------------------------------------------- 1 | import { required, isEmail, minLength } from "../../libs/validation"; 2 | 3 | function validation(values) { 4 | const { name, email, password } = values; 5 | let errors = {}; 6 | 7 | errors.name = 8 | required(name, "Nome é obrigatório") || 9 | minLength(2, name, "Nome tem que ter 2 ou mais caracteres"); 10 | 11 | errors.email = 12 | required(email, "E-mail é obrigatório") || 13 | isEmail(email, "Preencha com email válido"); 14 | 15 | errors.password = 16 | required(password, "Senha é obrigatória") || 17 | minLength(8, password, "Senha tem que ter 8 ou mais caracteres"); 18 | 19 | return errors; 20 | } 21 | 22 | export default validation; 23 | -------------------------------------------------------------------------------- /src/containers/HeaderAuth/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import LogoCollab from "../../components/LogoCollab"; 4 | 5 | import { Header } from "./styles"; 6 | 7 | function HeaderLogin() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | 15 | export default HeaderLogin; 16 | -------------------------------------------------------------------------------- /src/containers/HeaderAuth/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | import { Logo } from "../../components/LogoCollab/styles.js"; 4 | 5 | export const Header = styled.header` 6 | margin-bottom: var(--gap-big); 7 | text-align: center; 8 | 9 | ${Logo} { 10 | height: 120px; 11 | } 12 | 13 | @media (min-width: 1596px) and (min-height: 900px) { 14 | position: relative; 15 | width: 40%; 16 | height: 100%; 17 | background-color: var(--color-floral-white); 18 | 19 | &::after { 20 | content: ""; 21 | position: absolute; 22 | top: 0; 23 | right: 0; 24 | height: 110%; 25 | width: 30%; 26 | background-color: var(--color-floral-white); 27 | z-index: 0; 28 | transform: translateX(50%) rotate(10deg) translateY(-5%); 29 | } 30 | 31 | ${Logo} { 32 | height: 80%; 33 | position: absolute; 34 | z-index: 1; 35 | padding: 0; 36 | margin: 0; 37 | transform: translateX(-25%); 38 | } 39 | } 40 | `; 41 | -------------------------------------------------------------------------------- /src/containers/HeaderDashboard/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import LogoCollab from "../../components/LogoCollab"; 4 | import PhotoUserCollab from "../../components/PhotoUserCollab"; 5 | 6 | import { Header } from "./styles"; 7 | 8 | export default function HeaderDashboard() { 9 | return ( 10 |
11 | 12 | 13 |
14 | ); 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/containers/HeaderDashboard/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Header = styled.header` 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | 8 | padding: 10px 20px; 9 | 10 | img { 11 | height: 40px; 12 | } 13 | 14 | @media (max-width: 767.98px) { 15 | justify-content: center; 16 | } 17 | `; 18 | -------------------------------------------------------------------------------- /src/containers/PageAuth/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Page } from "./styles"; 4 | 5 | function PageAuth({ children }) { 6 | return {children}; 7 | } 8 | 9 | export default PageAuth; 10 | -------------------------------------------------------------------------------- /src/containers/PageAuth/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Page = styled.main` 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | padding-top: var(--gap-medium); 8 | 9 | @media (min-height: 570px) { 10 | height: 100vh; 11 | } 12 | 13 | @media (min-width: 1596px) and (min-height: 900px) { 14 | padding: 0; 15 | } 16 | `; 17 | -------------------------------------------------------------------------------- /src/icons/actions/visibility.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/icons/actions/visibility_off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/img/ball.svg: -------------------------------------------------------------------------------- 1 | 7 | Logo da CollabCode 8 | 9 | Tem o desenho de um passáro com cabelo vermelho chamado Gueio 10 | 11 | 12 | 17 | 32 | 37 | 96 | 102 | 108 | 156 | 161 | 172 | 173 | -------------------------------------------------------------------------------- /src/img/ball_text.svg: -------------------------------------------------------------------------------- 1 | 7 | Logo da CollabCode 8 | 9 | Tem o desenho de um passáro com cabelo vermelho chamado Gueio e sobre ele 10 | tem o texto CollabCode 11 | 12 | 13 | 18 | 26 | 34 | 38 | 42 | 52 | 62 | 69 | 76 | 84 | 92 | 107 | 112 | 171 | 177 | 183 | 232 | 237 | 248 | 256 | 257 | -------------------------------------------------------------------------------- /src/img/ball_text_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/img/horizontal.svg: -------------------------------------------------------------------------------- 1 | 7 | Logo da CollabCode 8 | 9 | Tem o desenho de um passáro com cabelo vermelho chamado Gueio e na frente 10 | dele um texto escrito CollabCode na cor cinza 11 | 12 | 13 | 18 | 32 | 37 | 96 | 102 | 108 | 157 | 162 | 173 | 181 | 189 | 190 | 191 | 199 | 209 | 215 | 221 | 228 | 234 | 235 | -------------------------------------------------------------------------------- /src/img/horizontal_light.svg: -------------------------------------------------------------------------------- 1 | 7 | Logo da CollabCode 8 | 9 | Tem o desenho de um passáro com cabelo vermelho chamado Gueio e na frente 10 | dele um texto escrito CollabCode na cor bege 11 | 12 | 13 | 18 | 33 | 38 | 97 | 103 | 109 | 158 | 163 | 174 | 182 | 190 | 191 | 192 | 201 | 211 | 217 | 223 | 230 | 236 | 237 | -------------------------------------------------------------------------------- /src/img/userdefault.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CollabCodeTech/training-frontend/c45676c1a567844004a5741ba0c61d822302c7ad/src/img/userdefault.jpg -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | 5 | ReactDOM.render(, document.getElementById("root")); 6 | -------------------------------------------------------------------------------- /src/libs/validation/email.js: -------------------------------------------------------------------------------- 1 | function isEmail(value, msgError) { 2 | return !/\S+@\S+\.\S+/.test(value) && msgError; 3 | } 4 | 5 | export default isEmail; 6 | -------------------------------------------------------------------------------- /src/libs/validation/index.js: -------------------------------------------------------------------------------- 1 | import required from "./required"; 2 | import isEmail from "./email"; 3 | import minLength from "./minLength"; 4 | 5 | export { required, isEmail, minLength }; 6 | -------------------------------------------------------------------------------- /src/libs/validation/minLength.js: -------------------------------------------------------------------------------- 1 | function minLength(length, value, msgError) { 2 | return value.length < length && msgError; 3 | } 4 | 5 | export default minLength; 6 | -------------------------------------------------------------------------------- /src/libs/validation/required.js: -------------------------------------------------------------------------------- 1 | function required(value, msgError) { 2 | return !value && msgError; 3 | } 4 | 5 | export default required; 6 | -------------------------------------------------------------------------------- /src/libs/validation/useValidation.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | function useValidation(validation = false, callback, ...values) { 4 | const [value, setValue] = useState( 5 | Object.assign(...values.map(value => ({ [value]: "" }))) 6 | ); 7 | const [errors, setErrors] = useState({}); 8 | const [isSubmitting, setIsSubmitting] = useState(false); 9 | 10 | function handleChange({ target }) { 11 | const { name, value, type, checked } = target; 12 | 13 | setValue(oldValue => { 14 | const newValue = 15 | type === "checkbox" ? { [name]: checked } : { [name]: value }; 16 | 17 | return Object.assign({ ...oldValue }, newValue); 18 | }); 19 | } 20 | 21 | function handleSubmit(event) { 22 | event.preventDefault(); 23 | 24 | setErrors(validation instanceof Object ? validation(value) : {}); 25 | setIsSubmitting(true); 26 | } 27 | 28 | function sendFormIsValid() { 29 | if ( 30 | Object.values(errors).filter(error => error).length === 0 && 31 | isSubmitting 32 | ) { 33 | callback(); 34 | } 35 | } 36 | 37 | useEffect(sendFormIsValid, [errors]); 38 | 39 | return { value, setValue, handleChange, handleSubmit, errors, setErrors }; 40 | } 41 | 42 | export default useValidation; 43 | -------------------------------------------------------------------------------- /src/pages/Dashboard/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import HeaderDashboard from "../../containers/HeaderDashboard"; 4 | import { SectionFake } from "./styles"; 5 | 6 | function Dashboard() { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default Dashboard; 16 | -------------------------------------------------------------------------------- /src/pages/Dashboard/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { _separator } from "../../styles/tools/Separator"; 3 | 4 | export const SectionFake = styled.section` 5 | height: 300px; 6 | ${_separator}; 7 | `; 8 | -------------------------------------------------------------------------------- /src/pages/Forgot/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import PageAuth from "../../containers/PageAuth"; 4 | import ContentAuth from "../../containers/ContentAuth"; 5 | import HeaderAuth from "../../containers/HeaderAuth"; 6 | import FormForgot from "../../containers/FormForgot"; 7 | 8 | function Forgot() { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default Forgot; 20 | -------------------------------------------------------------------------------- /src/pages/Login/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import PageAuth from "../../containers/PageAuth"; 4 | import ContentAuth from "../../containers/ContentAuth"; 5 | import HeaderAuth from "../../containers/HeaderAuth"; 6 | import FormLogin from "../../containers/FormLogin"; 7 | 8 | function Login() { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default Login; 20 | -------------------------------------------------------------------------------- /src/pages/ResendConfirmAccount/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import PageAuth from "../../containers/PageAuth"; 4 | import ContentAuth from "../../containers/ContentAuth"; 5 | import HeaderAuth from "../../containers/HeaderAuth"; 6 | import FormResendConfirmAccount from "../../containers/FormResendConfirmAccount"; 7 | 8 | function ResendConfirmAccount() { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default ResendConfirmAccount; 20 | -------------------------------------------------------------------------------- /src/pages/Signup/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import PageAuth from "../../containers/PageAuth"; 4 | import ContentAuth from "../../containers/ContentAuth"; 5 | import HeaderAuth from "../../containers/HeaderAuth"; 6 | import FormSignup from "../../containers/FormSignup"; 7 | 8 | function Signup() { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default Signup; 20 | -------------------------------------------------------------------------------- /src/pages/Signup/styles.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CollabCodeTech/training-frontend/c45676c1a567844004a5741ba0c61d822302c7ad/src/pages/Signup/styles.js -------------------------------------------------------------------------------- /src/services/AuthService.js: -------------------------------------------------------------------------------- 1 | import request from "superagent"; 2 | 3 | function signup(user) { 4 | const { REACT_APP_API_AUTH } = process.env; 5 | 6 | return request 7 | .post(`${REACT_APP_API_AUTH}/api/user`) 8 | .set("Content-Type", "application/json") 9 | .send(user); 10 | } 11 | 12 | export default { signup }; 13 | -------------------------------------------------------------------------------- /src/styles/elements/Base.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from "styled-components"; 2 | 3 | import { _gradientBg } from "../tools/Gradients"; 4 | 5 | const Base = createGlobalStyle` 6 | body { 7 | ${_gradientBg} 8 | height: 100vh; 9 | font-family: 'Comfortaa', sans-serif; 10 | line-height: 1.5em; 11 | } 12 | `; 13 | 14 | export default Base; 15 | -------------------------------------------------------------------------------- /src/styles/generic/Reset.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from "styled-components"; 2 | 3 | const Reset = createGlobalStyle` 4 | /* http://meyerweb.com/eric/tools/css/reset/ 5 | v2.0 | 20110126 6 | License: none (public domain) 7 | */ 8 | 9 | html, body, div, span, applet, object, iframe, 10 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 11 | a, abbr, acronym, address, big, cite, code, 12 | del, dfn, em, img, ins, kbd, q, s, samp, 13 | small, strike, strong, sub, sup, tt, var, 14 | b, u, i, center, 15 | dl, dt, dd, ol, ul, li, 16 | fieldset, form, label, legend, 17 | table, caption, tbody, tfoot, thead, tr, th, td, 18 | article, aside, canvas, details, embed, 19 | figure, figcaption, footer, header, hgroup, 20 | menu, nav, output, ruby, section, summary, 21 | time, mark, audio, video { 22 | margin: 0; 23 | padding: 0; 24 | border: 0; 25 | font-size: 100%; 26 | font: inherit; 27 | vertical-align: baseline; 28 | } 29 | /* HTML5 display-role reset for older browsers */ 30 | article, aside, details, figcaption, figure, 31 | footer, header, hgroup, menu, nav, section { 32 | display: block; 33 | } 34 | body { 35 | line-height: 1; 36 | } 37 | ol, ul { 38 | list-style: none; 39 | } 40 | blockquote, q { 41 | quotes: none; 42 | } 43 | blockquote:before, blockquote:after, 44 | q:before, q:after { 45 | content: ''; 46 | content: none; 47 | } 48 | table { 49 | border-collapse: collapse; 50 | border-spacing: 0; 51 | } 52 | input, button { 53 | font: inherit; 54 | } 55 | html,body { 56 | height: 100%; 57 | } 58 | `; 59 | 60 | export default Reset; 61 | -------------------------------------------------------------------------------- /src/styles/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Colors from "./settings/Colors"; 4 | import Gaps from "./settings/Gaps"; 5 | import Radius from "./settings/Radius"; 6 | import Reset from "./generic/Reset"; 7 | import Base from "./elements/Base"; 8 | 9 | const Styles = () => ( 10 | <> 11 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | 19 | export default Styles; 20 | -------------------------------------------------------------------------------- /src/styles/settings/Colors.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from "styled-components"; 2 | 3 | const Colors = createGlobalStyle` 4 | :root { 5 | /* Gueio */ 6 | --color-arsenic: #3a4042; 7 | --color-old-silver: #828282; 8 | --color-eggshell: #eae6da; 9 | --color-floral-white: #fffcee; 10 | --color-fiery-rose: #f25a70; 11 | 12 | /* Intermediate */ 13 | --color-dark-slate-grey: #385062; 14 | --color-purple-navy: #4c5b84; 15 | --color-pomp-and-power: #7c5e99; 16 | --color-pearly-purple: #b65a97; 17 | --color-pale-violet-red: #d5658c; 18 | --color-tango-pink: #ea767f; 19 | --color-tea-rose: #f78c74; 20 | --color-vivid-tangerine: #f6ad85; 21 | --color-peach: #f5caa1; 22 | --color-bisque: #f7e4c5; 23 | 24 | /* Status */ 25 | --color-mantis: #62c370; 26 | --color-tea-green: #c2f9bb; 27 | --color-cardinal: #ba1f33; 28 | --color-rose-gold: #c37472; 29 | --color-sandstorm: #f5d547; 30 | --color-blonde: #f4f1bb; 31 | 32 | /* Links */ 33 | --color-lapis-lazuli: #2274a5; 34 | --color-dodger-bue: #20a4f3; 35 | 36 | /* Greyscale */ 37 | --color-eerie-black: #191919; 38 | --color-granite-grey: #646165; 39 | --color-pastel-grey: #ced0ce; 40 | --color-platinum: #e6e8e6; 41 | } 42 | `; 43 | 44 | export default Colors; 45 | -------------------------------------------------------------------------------- /src/styles/settings/Gaps.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from "styled-components"; 2 | 3 | const Gaps = createGlobalStyle` 4 | :root { 5 | --gap-smaller: 5px; 6 | --gap-small: 15px; 7 | --gap-medium: 20px; 8 | --gap-big: 40px; 9 | --gap-bigger: 50px; 10 | } 11 | `; 12 | 13 | export default Gaps; 14 | -------------------------------------------------------------------------------- /src/styles/settings/Radius.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from "styled-components"; 2 | 3 | export const Radius = createGlobalStyle` 4 | :root { 5 | --radius-big: 14px; 6 | } 7 | `; 8 | 9 | export default Radius; 10 | -------------------------------------------------------------------------------- /src/styles/tools/Gradients.js: -------------------------------------------------------------------------------- 1 | import { css } from "styled-components"; 2 | 3 | export const _gradientBg = css` 4 | background-image: linear-gradient( 5 | 180deg, 6 | var(--color-dark-slate-grey) 0%, 7 | var(--color-purple-navy) 20.83% 8 | ); 9 | background-repeat: no-repeat; 10 | background-color: var(--color-purple-navy); 11 | `; 12 | -------------------------------------------------------------------------------- /src/styles/tools/Separator.js: -------------------------------------------------------------------------------- 1 | import { css } from "styled-components"; 2 | 3 | export const _separator = css` 4 | border-bottom: 2px solid var(--color-old-silver); 5 | `; 6 | -------------------------------------------------------------------------------- /src/styles/tools/Shadow.js: -------------------------------------------------------------------------------- 1 | import { css } from "styled-components"; 2 | 3 | export const _shadowBottom = css` 4 | box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25); 5 | `; 6 | -------------------------------------------------------------------------------- /src/styles/tools/Typography.js: -------------------------------------------------------------------------------- 1 | import { css } from "styled-components"; 2 | 3 | const weight = { 4 | light: 300, 5 | regular: 400, 6 | medium: 500, 7 | semiBold: 600, 8 | bold: 700 9 | }; 10 | 11 | export const _h1 = css` 12 | font-weight: ${weight.light}; 13 | font-size: 96px; 14 | letter-spacing: -1.5px; 15 | text-transform: none; 16 | `; 17 | 18 | export const _h2 = css` 19 | font-weight: ${weight.light}; 20 | font-size: 60px; 21 | letter-spacing: -0.5; 22 | text-transform: none; 23 | `; 24 | 25 | export const _h3 = css` 26 | font-weight: ${weight.regular}; 27 | font-size: 48px; 28 | letter-spacing: 0px; 29 | text-transform: none; 30 | `; 31 | 32 | export const _h4 = css` 33 | font-weight: ${weight.regular}; 34 | font-size: 34px; 35 | letter-spacing: 0.25px; 36 | text-transform: none; 37 | `; 38 | 39 | export const _h5 = css` 40 | font-weight: ${weight.regular}; 41 | font-size: 24px; 42 | letter-spacing: 0; 43 | text-transform: none; 44 | `; 45 | 46 | export const _h6 = css` 47 | font-weight: ${weight.medium}; 48 | font-size: 20px; 49 | letter-spacing: 0.15px; 50 | text-transform: none; 51 | `; 52 | 53 | export const _subtitle1 = css` 54 | font-weight: ${weight.regular}; 55 | font-size: 16px; 56 | letter-spacing: 0.15px; 57 | text-transform: none; 58 | `; 59 | 60 | export const _subtitle2 = css` 61 | font-weight: ${weight.medium}; 62 | font-size: 14px; 63 | letter-spacing: 0.1px; 64 | text-transform: none; 65 | `; 66 | 67 | export const _body1 = css` 68 | font-weight: ${weight.regular}; 69 | font-size: 16px; 70 | letter-spacing: 0.5px; 71 | text-transform: none; 72 | `; 73 | 74 | export const _body2 = css` 75 | font-weight: ${weight.regular}; 76 | font-size: 14px; 77 | letter-spacing: 0.25px; 78 | text-transform: none; 79 | `; 80 | 81 | export const _button = css` 82 | font-weight: ${weight.medium}; 83 | font-size: 14px; 84 | letter-spacing: 1.25px; 85 | text-transform: none; 86 | `; 87 | 88 | export const _caption = css` 89 | font-weight: ${weight.regular}; 90 | font-size: 12px; 91 | letter-spacing: 0.4px; 92 | text-transform: none; 93 | `; 94 | 95 | export const _overline = css` 96 | font-weight: ${weight.bold}; 97 | font-size: 10px; 98 | letter-spacing: 1.5px; 99 | text-transform: none; 100 | `; 101 | --------------------------------------------------------------------------------