├── .editorconfig ├── .env ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── README.md ├── arqgit.gif ├── package-lock.json ├── package.json ├── public ├── index.html └── robots.txt └── src ├── App.js ├── components ├── CardInput.js └── Loader │ ├── index.js │ └── style.js ├── config ├── api.js └── colors.js ├── img ├── abacate.svg ├── alface.svg ├── animation.gif ├── arrow-left.svg ├── banana.svg ├── baterias.svg ├── cenoura.svg ├── cesta-de-comida.svg ├── cesta.svg ├── check.svg ├── conf.json ├── confirm.json ├── eletronicos.svg ├── ervilha.svg ├── fazenda.svg ├── garden.png ├── home-background.svg ├── lampadas.svg ├── legumes.svg ├── log-in.svg ├── logo.svg ├── mel.svg ├── mel_p.jfif ├── mercearia.svg ├── oleo.svg ├── organicos.svg ├── papeis-papelao.svg ├── passaro.svg ├── payment.png ├── search.svg ├── teste.svg └── x.svg ├── index.js ├── pages ├── Home │ ├── index.js │ └── style.js ├── HomePage │ ├── index.js │ └── style.js ├── Login │ ├── index.js │ └── style.js ├── Modal │ ├── index.js │ └── style.js ├── ModalPayment │ ├── index.js │ └── style.js ├── Payment │ ├── index.js │ └── style.js ├── Products │ ├── index.js │ └── style.js ├── Register │ ├── index.js │ └── style.js └── Shopping │ ├── index.js │ └── style.js ├── routes ├── MyRoute.js └── index.js ├── services ├── axios.js └── history.js ├── store ├── index.js └── modules │ ├── auth │ ├── actions.js │ ├── reducer.js │ └── sagas.js │ ├── modal │ ├── actions.js │ ├── reducer.js │ └── sagas.js │ ├── reduxPersist.js │ ├── rootReducer.js │ ├── rootSagas.js │ └── types.js └── styles └── GlobalStyle.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_KEY = pk_test_51Guj7eGFzNYveZ6nEBnKVGwQbtsfWCzBw4nLhxymYepyTKfFDjMLs1O59hWxdT9eaqfWXyo8kkVdYplGcE6rkyzE00ELaXLASH 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2020": true 5 | }, 6 | "extends": [ 7 | "plugin:react/recommended", 8 | "airbnb", 9 | "prettier", 10 | "prettier/react" 11 | ], 12 | parser: 'babel-eslint', 13 | "parserOptions": { 14 | "ecmaFeatures": { 15 | "jsx": true 16 | }, 17 | "ecmaVersion": 11, 18 | "sourceType": "module" 19 | }, 20 | "plugins": [ 21 | "react", 22 | "prettier", 23 | "react-hooks" 24 | ], 25 | "rules": { 26 | "prettier/prettier": "error", 27 | "react/jsx-filename-extension": 0, 28 | "import/prefer-default-export": 0, 29 | "react-hooks/rules-of-hooks": "error", 30 | "react-hooks/exhaustive-deps": "warn", 31 | "react/button-has-type": 0, 32 | 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /.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 | .png 8 | .json 9 | 10 | # testing 11 | /coverage 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "traillingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Índice 2 | 3 | - [Sobre](#sobre) 4 | - [Documentação](#documentacao) 5 | - [Tecnologias Utilizadas](#tecnologias-utilizadas) 6 | - [Relevante](#relevante) 7 | - [Como Usar](#como-usar) 8 | 9 | 10 | 11 | ## :bookmark: Sobre 12 | 13 | O Agro-commerce é a aplicação perfeita para expandir as vendas de produtos orgânicos pelo Brasil. 14 | 15 | Essa aplicação feita em [ReactJS](https://reactjs.org/). 16 | 17 | 18 | 19 | ## :books: Documentação 20 | 21 | Este projeto ainda se encontra em execução, atualmente estou trabalhando em uma API para integra-la a está interface, uma das aplicações futuras para este projeto é também a integração ao Stripe 22 | junto ao NextJs; 23 | 24 | 25 | 26 | ## :rocket: Tecnologias Utilizadas 27 | 28 | O projeto foi desenvolvido utilizando as seguintes tecnologias 29 | 30 | - [JavaScript](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript) 31 | - [Redux](https://redux.js.org/) 32 | - [ReactJS](https://reactjs.org/) 33 | - [Styled-Components](https://styled-components.com/docs) 34 | - [Stripe](https://stripe.com/docs/api) 35 | 36 | 37 | ## :star: Relevante 38 | Esta aplicação está sendo essencial para expandir meus conhecimentos na área de vendas sob a internet. 39 | 40 | - [X] Implementação com Stripe concluído 41 | 42 | ## :heavy_check_mark: :computer: Resultado Web 43 | 44 | - Aqui está 45 | 46 | ![Alt text](/arqgit.gif) 47 | 48 | 49 | 50 | ## :fire: Como usar 51 | 52 | - ### **Pré-requisitos** 53 | 54 | - É **necessário** possuir o **[Node.js](https://nodejs.org/en/)** instalado na máquina 55 | - Também, é **preciso** ter um gerenciador de pacotes seja o **[NPM](https://www.npmjs.com/)** ou **[Yarn](https://yarnpkg.com/)**. 56 | 57 | 1. Faça um clone : 58 | 59 | ```sh 60 | $ git clone https://github.com/thisluis/agro-commerce.git 61 | ``` 62 | 63 | 2. Executando a Aplicação: 64 | 65 | ```sh 66 | # Instale as dependências 67 | $ npm install 68 | 69 | # Inicie a aplicação web 70 | $ npm start 71 | ``` 72 | 73 |

74 | by Luís Felipe 75 |

76 | -------------------------------------------------------------------------------- /arqgit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luisdiaslima/ecommerce-simulator-react/21a1a97e157e05b279f90b92101a8de486f0166d/arqgit.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-base", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.0", 7 | "@stripe/react-stripe-js": "^1.1.2", 8 | "@stripe/stripe-js": "^1.7.0", 9 | "@testing-library/jest-dom": "^4.2.4", 10 | "@testing-library/react": "^9.3.2", 11 | "@testing-library/user-event": "^7.1.2", 12 | "axios": "^0.19.2", 13 | "clsx": "^1.1.1", 14 | "dotenv": "^8.2.0", 15 | "env-cmd": "^10.1.0", 16 | "history": "^4.10.1", 17 | "lodash": "^4.17.15", 18 | "lottie-web": "^5.7.0", 19 | "prop-types": "^15.7.2", 20 | "react": "^16.13.1", 21 | "react-bodymovin": "^2.0.0", 22 | "react-dom": "^16.13.1", 23 | "react-icons": "^3.10.0", 24 | "react-modal": "^3.11.2", 25 | "react-redux": "^7.2.0", 26 | "react-router-dom": "^5.2.0", 27 | "react-scripts": "3.4.1", 28 | "react-toastify": "^6.0.5", 29 | "redux": "^4.0.5", 30 | "redux-persist": "^6.0.0", 31 | "redux-saga": "^1.1.3", 32 | "styled-components": "^5.1.1", 33 | "validator": "^13.1.1" 34 | }, 35 | "scripts": { 36 | "start": "react-scripts start", 37 | "build": "react-scripts build", 38 | "test": "react-scripts test", 39 | "eject": "react-scripts eject" 40 | }, 41 | "eslintConfig": { 42 | "extends": "react-app" 43 | }, 44 | "browserslist": { 45 | "production": [ 46 | ">0.2%", 47 | "not dead", 48 | "not op_mini all" 49 | ], 50 | "development": [ 51 | "last 1 chrome version", 52 | "last 1 firefox version", 53 | "last 1 safari version" 54 | ] 55 | }, 56 | "devDependencies": { 57 | "babel-eslint": "^10.1.0", 58 | "eslint": "^6.8.0", 59 | "eslint-config-airbnb": "^18.1.0", 60 | "eslint-config-prettier": "^6.11.0", 61 | "eslint-plugin-import": "^2.21.2", 62 | "eslint-plugin-jsx-a11y": "^6.2.3", 63 | "eslint-plugin-prettier": "^3.1.3", 64 | "eslint-plugin-react": "^7.20.0", 65 | "eslint-plugin-react-hooks": "^2.5.1", 66 | "prettier": "^2.0.5" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Ecommerce 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Router } from 'react-router-dom'; 3 | import { ToastContainer } from 'react-toastify'; 4 | import { Provider } from 'react-redux'; 5 | import { PersistGate } from 'redux-persist/integration/react'; 6 | import store, { persistor } from './store'; 7 | import history from './services/history'; 8 | import GlobalStyle from './styles/GlobalStyle'; 9 | 10 | import Routes from './routes'; 11 | 12 | function App() { 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/components/CardInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { CardElement } from '@stripe/react-stripe-js'; 3 | 4 | const CARD_ELEMENT_OPTIONS = { 5 | style: { 6 | base: { 7 | color: '#32325d', 8 | fontFamily: '"Helvetica Neue", Helvetica, sans-serif', 9 | fontSmoothing: 'antialiased', 10 | fontSize: '16px', 11 | '::placeholder': { 12 | color: '#aab7c4', 13 | }, 14 | }, 15 | invalid: { 16 | color: '#fa755a', 17 | iconColor: '#fa755a', 18 | }, 19 | }, 20 | }; 21 | 22 | export default function CardInput() { 23 | return ; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Loader/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { LoaderBar } from './style'; 4 | 5 | export default function LoaderHead({ isLoading }) { 6 | if (!isLoading) return <>; 7 | 8 | return ( 9 | 10 |
11 | 12 | ); 13 | } 14 | 15 | LoaderHead.defaultProps = { 16 | isLoading: false, 17 | }; 18 | 19 | LoaderHead.propTypes = { 20 | isLoading: PropTypes.bool, 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/Loader/style.js: -------------------------------------------------------------------------------- 1 | import styled, { keyframes } from 'styled-components'; 2 | 3 | export const flatline = keyframes` 4 | 0%{ 5 | left:0; 6 | width:0; 7 | } 8 | 50%{ 9 | left:25%; 10 | width:75% 11 | } 12 | 75%{ 13 | left:100%; 14 | width:0 15 | } 16 | `; 17 | 18 | export const LoaderBar = styled.div` 19 | } 20 | > div { 21 | margin-top: 1.5px; 22 | position: fixed; 23 | top: 0%; 24 | left: 50%; 25 | transform: translate(-50%, -50%); 26 | width: 100%; 27 | height: 4px; 28 | border-radius: 4px; 29 | background-color: #f0f0f0; 30 | overflow: hidden; 31 | 32 | &:before, 33 | &:after { 34 | content: ''; 35 | position: absolute; 36 | top: 0; 37 | left: 0; 38 | width: 0; 39 | height: 4px; 40 | border-radius: 4px; 41 | z-index: 2; 42 | } 43 | &:before { 44 | background: linear-gradient(90deg, #08aeea, #2af598); 45 | animation: ${flatline} 1.2s linear infinite; 46 | } 47 | 48 | &:after { 49 | background: linear-gradient(90deg, #b721ff, #08aeea); 50 | animation: ${flatline} 1.2s 0.5s linear infinite; 51 | } 52 | `; 53 | -------------------------------------------------------------------------------- /src/config/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const api = axios.create({ 4 | baseURL: 'https://servicodados.ibge.gov.br', 5 | }); 6 | 7 | export default api; 8 | -------------------------------------------------------------------------------- /src/config/colors.js: -------------------------------------------------------------------------------- 1 | export const primaryColor = '#C3073F'; 2 | export const primaryDarkColor = '#1A1A1D'; 3 | 4 | export const successColor = '#0197F6'; 5 | export const infoColor = '#0197F6'; 6 | export const errorColor = '#F2AF29'; 7 | export const warningColor = '#F2AF29'; 8 | -------------------------------------------------------------------------------- /src/img/abacate.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/alface.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luisdiaslima/ecommerce-simulator-react/21a1a97e157e05b279f90b92101a8de486f0166d/src/img/animation.gif -------------------------------------------------------------------------------- /src/img/arrow-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/banana.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/baterias.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/img/cenoura.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 15 | 16 | 19 | 21 | 24 | 25 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/img/cesta-de-comida.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/cesta.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/img/conf.json: -------------------------------------------------------------------------------- 1 | {"v":"5.5.9","fr":29.9700012207031,"ip":0,"op":94.0000038286985,"w":320,"h":320,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[159.533,158.64,0],"ix":2},"a":{"a":0,"k":[51,50.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[102,102],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.299838526109,0.788235294118,0.565875842525,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[51,50.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.641],"y":[0]},"t":45,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":70,"s":[100]},{"t":87.0000035435826,"s":[9]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.328],"y":[-0.46]},"o":{"x":[0.333],"y":[0]},"t":56,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.784],"y":[1.785]},"t":68,"s":[45.593]},{"t":83.0000033806593,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[0]},{"t":83.0000033806593,"s":[1084]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":45.0000018328876,"op":165.000006720588,"st":45.0000018328876,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Layer 2/confirmation Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[159.533,158.64,0],"ix":2},"a":{"a":0,"k":[78,57.5,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.333],"y":[0,0,0]},"t":54,"s":[0,0,100]},{"i":{"x":[0.32,0.32,0.667],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.333],"y":[0,0,0]},"t":57,"s":[22,22,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":58,"s":[20,20,100]},{"t":59.0000024031193,"s":[22,22,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.239,-6.248],[0,0],[0,0],[6.24,-6.248],[-6.249,-6.248],[0,0],[-4.097,0],[-3.12,3.128],[0,0],[6.247,6.248]],"o":[[0,0],[0,0],[-6.241,-6.248],[-6.249,6.248],[0,0],[3.12,3.128],[4.096,0],[0,0],[6.247,-6.248],[-6.24,-6.248]],"v":[[48.689,-50.532],[-19.999,18.148],[-48.687,-10.532],[-71.311,-10.532],[-71.311,12.092],[-31.311,52.092],[-19.999,56.78],[-8.688,52.092],[71.313,-27.908],[71.313,-50.532]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[77.81,57.03],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":54.0000021994651,"op":94.0000038286985,"st":54.0000021994651,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Capa 1/confirmation Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[159.533,158.64,0],"ix":2},"a":{"a":0,"k":[176.25,176.25,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":46,"s":[0,0,100]},{"t":52.0000021180034,"s":[24,24,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-97.202,0],[0,-97.202],[97.202,0],[0,97.202]],"o":[[97.202,0],[0,97.202],[-97.202,0],[0,-97.202]],"v":[[0,-176],[176,0],[0,176],[-176,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.447000002394,0.929000016755,0.709999952129,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[176.25,176.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":46.0000018736184,"op":94.0000038286985,"st":47.0000019143492,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.346,"y":1},"o":{"x":0.016,"y":0},"t":0,"s":[-77,158,0],"to":[12.564,0,0],"ti":[-6.083,0,0]},{"i":{"x":0.052,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[313.949,158,0],"to":[19.679,0,0],"ti":[25.158,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":39,"s":[41.074,158,0],"to":[-0.477,0,0],"ti":[-0.009,0,0]},{"t":51.0000020772726,"s":[163,158,0]}],"ix":2},"a":{"a":0,"k":[-2,165,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.933,0],[0,-1.933],[-1.933,0],[0,1.933]],"o":[[-1.933,0],[0,1.933],[1.933,0],[0,-1.933]],"v":[[0,-3.5],[-3.5,0],[0,3.5],[3.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145743934781,0.952941176471,0.620565795898,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.5,166.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":64.0000026067734,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.823,"y":0},"t":0,"s":[392,156,0],"to":[-59.333,0,0],"ti":[14.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[36,156,0],"to":[-14.5,0,0],"ti":[-21.167,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.343,"y":0},"t":39,"s":[305,156,0],"to":[21.167,0,0],"ti":[30.92,-2.254,0]},{"t":51.0000020772726,"s":[163,156,0]}],"ix":2},"a":{"a":0,"k":[-2,165,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.933,0],[0,-1.933],[-1.933,0],[0,1.933]],"o":[[-1.933,0],[0,1.933],[1.933,0],[0,-1.933]],"v":[[0,-3.5],[-3.5,0],[0,3.5],[3.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145743934781,0.952941176471,0.620565795898,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.5,166.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":64.0000026067734,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 8","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.621,"y":0},"t":0,"s":[-71,244,0],"to":[64.5,-25.333,0],"ti":[-16.5,6.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[316,92,0],"to":[16.5,-6.667,0],"ti":[25.5,-10.167,0]},{"i":{"x":0,"y":1},"o":{"x":0.703,"y":0},"t":39,"s":[28,204,0],"to":[-25.5,10.167,0],"ti":[-22.5,8.5,0]},{"t":51.0000020772726,"s":[163,153,0]}],"ix":2},"a":{"a":0,"k":[-2,165,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.933,0],[0,-1.933],[-1.933,0],[0,1.933]],"o":[[-1.933,0],[0,1.933],[1.933,0],[0,-1.933]],"v":[[0,-3.5],[-3.5,0],[0,3.5],[3.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145743934781,0.952941176471,0.620565795898,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.5,166.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":64.0000026067734,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.503,"y":0},"t":0,"s":[385,258,0],"to":[-58.333,-26.333,0],"ti":[12,5.667,0]},{"i":{"x":0.618,"y":1},"o":{"x":0.797,"y":0},"t":18,"s":[35,100,0],"to":[-12,-5.667,0],"ti":[-21.333,-9.167,0]},{"i":{"x":0.058,"y":1},"o":{"x":0.496,"y":0},"t":39,"s":[313,224,0],"to":[21.333,9.167,0],"ti":[25,11.5,0]},{"t":51.0000020772726,"s":[163,155,0]}],"ix":2},"a":{"a":0,"k":[-2,165,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.933,0],[0,-1.933],[-1.933,0],[0,1.933]],"o":[[-1.933,0],[0,1.933],[1.933,0],[0,-1.933]],"v":[[0,-3.5],[-3.5,0],[0,3.5],[3.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145743934781,0.952941176471,0.620565795898,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.5,166.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":64.0000026067734,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Shape Layer 7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.065,"y":1},"o":{"x":0.296,"y":0},"t":0,"s":[271,408,0],"to":[-29.167,-65.5,0],"ti":[7.167,16.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[96,15,0],"to":[-7.167,-16.833,0],"ti":[-11,-23.5,0]},{"i":{"x":0,"y":1},"o":{"x":0.526,"y":0},"t":39,"s":[228,307,0],"to":[11,23.5,0],"ti":[11,25.167,0]},{"t":51.0000020772726,"s":[162,156,0]}],"ix":2},"a":{"a":0,"k":[-2,165,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.933,0],[0,-1.933],[-1.933,0],[0,1.933]],"o":[[-1.933,0],[0,1.933],[1.933,0],[0,-1.933]],"v":[[0,-3.5],[-3.5,0],[0,3.5],[3.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145743934781,0.952941176471,0.620565795898,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.5,166.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":64.0000026067734,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.124,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[90,412,0],"to":[18,-66.833,0],"ti":[-5,18.167,0]},{"i":{"x":0.44,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[198,11,0],"to":[5,-18.167,0],"ti":[6.167,-23.833,0]},{"i":{"x":0,"y":1},"o":{"x":0.703,"y":0},"t":39,"s":[120,303,0],"to":[-6.167,23.833,0],"ti":[-6.833,24.833,0]},{"t":51.0000020772726,"s":[161,154,0]}],"ix":2},"a":{"a":0,"k":[-2,165,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.933,0],[0,-1.933],[-1.933,0],[0,1.933]],"o":[[-1.933,0],[0,1.933],[1.933,0],[0,-1.933]],"v":[[0,-3.5],[-3.5,0],[0,3.5],[3.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145743934781,0.952941176471,0.620565795898,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.5,166.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":64.0000026067734,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.71,"y":0},"t":0,"s":[372,368,0],"to":[-55.833,-54.833,0],"ti":[11,11,0]},{"i":{"x":0.573,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[37,39,0],"to":[-11,-11,0],"ti":[-20.167,-19.5,0]},{"i":{"x":0.043,"y":1},"o":{"x":0.481,"y":0},"t":39,"s":[306,302,0],"to":[20.167,19.5,0],"ti":[24.667,24.333,0]},{"t":51.0000020772726,"s":[158,156,0]}],"ix":2},"a":{"a":0,"k":[-2,165,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.933,0],[0,-1.933],[-1.933,0],[0,1.933]],"o":[[-1.933,0],[0,1.933],[1.933,0],[0,-1.933]],"v":[[0,-3.5],[-3.5,0],[0,3.5],[3.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145743934781,0.952941176471,0.620565795898,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.5,166.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":64.0000026067734,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.636,"y":0},"t":0,"s":[-37,351,0],"to":[55.833,-54.5,0],"ti":[-12.167,12.667,0]},{"i":{"x":0.362,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[298,24,0],"to":[12.167,-12.667,0],"ti":[23.167,-21.667,0]},{"i":{"x":0,"y":1},"o":{"x":0.644,"y":0},"t":39,"s":[36,275,0],"to":[-23.167,21.667,0],"ti":[-20.5,20.167,0]},{"t":51.0000020772726,"s":[159,154,0]}],"ix":2},"a":{"a":0,"k":[-2,165,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.933,0],[0,-1.933],[-1.933,0],[0,1.933]],"o":[[-1.933,0],[0,1.933],[1.933,0],[0,-1.933]],"v":[[0,-3.5],[-3.5,0],[0,3.5],[3.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145743934781,0.952941176471,0.620565795898,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.5,166.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":64.0000026067734,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /src/img/confirm.json: -------------------------------------------------------------------------------- 1 | {"v":"5.5.9","fr":29.9700012207031,"ip":0,"op":61.0000024845809,"w":150,"h":150,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"garis","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":6,"s":[74.5,121.25,0],"to":[0,-1.996,0],"ti":[0,3.701,0]},{"t":18.000000733155,"s":[74.5,38.5,0]}],"ix":2},"a":{"a":0,"k":[59,-24.75,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[59,-30],[59,-19.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":4,"s":[77]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":6,"s":[0]},{"t":18.000000733155,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":6,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[100]},{"t":18.000000733155,"s":[2]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":19.0000007738859,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"ceklis","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[76,75.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-21.75,-1.5],[-6.25,13.25],[47.5,-40.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[0]},{"t":37.0000015070409,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.290196090937,0.65098041296,0.647058844566,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":899.000036617021,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Bulet","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[74.589,74.15,0],"ix":2},"a":{"a":0,"k":[-2.879,2.621,0],"ix":1},"s":{"a":0,"k":[101.118,101.118,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[84.242,84.242],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":18,"s":[10]},{"t":37.0000015070409,"s":[100]}],"ix":1},"e":{"a":0,"k":10,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.879,2.621],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[95.265,95.265],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":18,"s":[327]},{"t":32.0000013033867,"s":[396]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":18.000000733155,"op":899.000036617021,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /src/img/eletronicos.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/img/ervilha.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | 15 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/img/fazenda.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/garden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luisdiaslima/ecommerce-simulator-react/21a1a97e157e05b279f90b92101a8de486f0166d/src/img/garden.png -------------------------------------------------------------------------------- /src/img/lampadas.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/img/legumes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/img/log-in.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/mel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/mel_p.jfif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luisdiaslima/ecommerce-simulator-react/21a1a97e157e05b279f90b92101a8de486f0166d/src/img/mel_p.jfif -------------------------------------------------------------------------------- /src/img/mercearia.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/oleo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/img/organicos.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/img/papeis-papelao.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/img/passaro.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/payment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luisdiaslima/ecommerce-simulator-react/21a1a97e157e05b279f90b92101a8de486f0166d/src/img/payment.png -------------------------------------------------------------------------------- /src/img/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/teste.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | 11 | 12 | 22 | 26 | 29 | 34 | 35 | 38 | 40 | 43 | 44 | 47 | 49 | 50 | 54 | 56 | 57 | 61 | 63 | 70 | 71 | 75 | 79 | 80 | 85 | 87 | 90 | 92 | 94 | 95 | 96 | 98 | 100 | 102 | 104 | 105 | 115 | 120 | 122 | 131 | 134 | 139 | 144 | 147 | 148 | 151 | 152 | 153 | 155 | 157 | 159 | 161 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /src/img/x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /src/pages/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { useSelector, useDispatch } from 'react-redux'; 4 | import { Container, Content, Main, MyLink } from './style'; 5 | import Logo from '../../img/passaro.svg'; 6 | import * as actions from '../../store/modules/auth/actions'; 7 | import history from '../../services/history'; 8 | 9 | export default function Home() { 10 | const dispatch = useDispatch(); 11 | const isLoggedIn = useSelector((state) => state.auth.isLoggedIn); 12 | 13 | const handleLogout = (e) => { 14 | e.preventDefault(); 15 | dispatch(actions.loginFailure()); 16 | history.push('/login'); 17 | }; 18 | return ( 19 | 20 | 21 |
22 | 23 | 24 |
25 | Montar minha cesta 26 | {isLoggedIn ? ( 27 | 28 | 29 | Sair 30 | 31 | ) : ( 32 | 33 | 34 | Fazer login 35 | 36 | )} 37 |
38 |
39 |
40 |

Seu e-commerce de legumes e vegetais

41 |

Ajudamos pessoas a encontrarem a comida de verdade.

42 | 43 | 44 | 45 | Conheça nossos produtos 46 | 47 |
48 |
49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/pages/Home/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { Link } from 'react-router-dom'; 3 | import Background from '../../img/logo.svg'; 4 | import Login from '../../img/log-in.svg'; 5 | import Search from '../../img/search.svg'; 6 | 7 | export const Container = styled.div` 8 | height: 100vh; 9 | background: url(${Background}) no-repeat; 10 | background-position: 55vw bottom; 11 | background-position-y: 130px; 12 | 13 | > a { 14 | display: flex; 15 | color: var(--title-color); 16 | font-weight: 700; 17 | } 18 | `; 19 | 20 | export const Content = styled.div` 21 | width: 90%; 22 | max-width: 1100px; 23 | margin: 0 auto; 24 | height: 100%; 25 | display: flex; 26 | flex-direction: column; 27 | 28 | > header { 29 | margin-top: 48px; 30 | display: flex; 31 | align-items: center; 32 | justify-content: space-between; 33 | 34 | > img { 35 | width: 54px; 36 | } 37 | 38 | > div { 39 | display: flex; 40 | > a { 41 | display: flex; 42 | padding: 10px; 43 | color: var(--title-color); 44 | font-weight: 700; 45 | } 46 | > a span { 47 | margin-right: 16px; 48 | display: flex; 49 | background-image: url(${Login}); 50 | width: 20px; 51 | height: 20px; 52 | } 53 | } 54 | } 55 | `; 56 | 57 | export const Main = styled.main` 58 | max-width: 560px; 59 | flex: 1; 60 | display: flex; 61 | flex-direction: column; 62 | justify-content: center; 63 | 64 | > h1 { 65 | font-size: 54px; 66 | } 67 | 68 | > p { 69 | color: var(--text); 70 | font-size: 24px; 71 | line-height: 38px; 72 | margin-top: 24px; 73 | } 74 | `; 75 | 76 | export const MyLink = styled(Link)` 77 | cursor: pointer; 78 | width: 100%; 79 | max-width: 360px; 80 | height: 72px; 81 | 82 | border-radius: 8px; 83 | 84 | display: flex; 85 | align-items: center; 86 | margin-top: 40px; 87 | 88 | background: var(--primary-color); 89 | transition: 400ms; 90 | 91 | &:hover { 92 | background-color: #2fb86e; 93 | } 94 | 95 | > span { 96 | width: 72px; 97 | height: 72px; 98 | border-top-left-radius: 8px; 99 | border-bottom-left-radius: 8px; 100 | background-color: rgba(0, 0, 0, 0.08); 101 | display: flex; 102 | align-items: center; 103 | justify-content: center; 104 | } 105 | 106 | > span::after { 107 | content: ''; 108 | background-image: url('${Search}'); 109 | width: 20px; 110 | height: 20px; 111 | } 112 | 113 | > strong { 114 | flex: 1; 115 | color: white; 116 | text-align: center; 117 | } 118 | `; 119 | -------------------------------------------------------------------------------- /src/pages/HomePage/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import axios from 'axios'; 3 | import { useDispatch } from 'react-redux'; 4 | import clsx from 'clsx'; 5 | import { toast } from 'react-toastify'; 6 | 7 | // MUI Components 8 | import Button from '@material-ui/core/Button'; 9 | import Card from '@material-ui/core/Card'; 10 | import CardContent from '@material-ui/core/CardContent'; 11 | 12 | import TextField from '@material-ui/core/TextField'; 13 | // stripe 14 | import { useStripe, useElements, CardElement } from '@stripe/react-stripe-js'; 15 | // Util imports 16 | import { makeStyles } from '@material-ui/core/styles'; 17 | import * as actions from '../../store/modules/modal/actions'; 18 | // Custom Components 19 | 20 | import CardInput from '../../components/CardInput'; 21 | 22 | const useStyles = makeStyles({ 23 | root: { 24 | maxWidth: 500, 25 | margin: '35vh auto', 26 | }, 27 | content: { 28 | display: 'flex', 29 | flexDirection: 'column', 30 | alignContent: 'flex-start', 31 | }, 32 | div: { 33 | display: 'flex', 34 | flexDirection: 'row', 35 | alignContent: 'flex-start', 36 | justifyContent: 'space-between', 37 | }, 38 | button: { 39 | margin: '2em auto 1em', 40 | }, 41 | }); 42 | 43 | function HomePage() { 44 | const dispatch = useDispatch(); 45 | 46 | const classes = useStyles(); 47 | // State 48 | 49 | // teste 50 | const [loading, setLoading] = React.useState(false); 51 | const [success, setSuccess] = React.useState(false); 52 | const timer = React.useRef(); 53 | 54 | const buttonClassname = clsx({ 55 | [classes.buttonSuccess]: success, 56 | }); 57 | 58 | React.useEffect(() => { 59 | return () => { 60 | clearTimeout(timer.current); 61 | }; 62 | }, []); 63 | 64 | const handleButtonClick = () => { 65 | if (!loading) { 66 | setSuccess(false); 67 | setLoading(true); 68 | timer.current = setTimeout(() => { 69 | setSuccess(true); 70 | setLoading(false); 71 | }, 2000); 72 | } 73 | }; 74 | 75 | // teste 76 | 77 | const [email, setEmail] = useState(''); 78 | 79 | const stripe = useStripe(); 80 | const elements = useElements(); 81 | 82 | const handleSubmit = async (e) => { 83 | e.preventDefault(); 84 | setLoading(true); 85 | dispatch(actions.paymentRequest()); 86 | 87 | if (!stripe || !elements) { 88 | // Stripe.js has not yet loaded. 89 | // Make sure to disable form submission until Stripe.js has loaded. 90 | return; 91 | } 92 | if (!email) { 93 | toast.error('Está faltando um e-mail'); 94 | setLoading(false); 95 | } 96 | const res = await axios.post('http://localhost:3000/stripe/payment', { 97 | email, 98 | }); 99 | 100 | const clientSecret = res.data.client_secret; 101 | 102 | const result = await stripe.confirmCardPayment(clientSecret, { 103 | payment_method: { 104 | card: elements.getElement(CardElement), 105 | billing_details: { 106 | email, 107 | }, 108 | }, 109 | }); 110 | 111 | if (result.error) { 112 | setLoading(false); 113 | // Show error to your customer (e.g., insufficient funds) 114 | console.log(result.error.message); 115 | } else { 116 | // The payment has been processed! 117 | // eslint-disable-next-line no-lonely-if 118 | if (result.paymentIntent.status === 'succeeded') { 119 | setLoading(false); 120 | dispatch(actions.paymentSuccess()); 121 | 122 | setTimeout(() => { 123 | dispatch(actions.paymentEnd()); 124 | }, 3000); 125 | } 126 | } 127 | setLoading(false); 128 | }; 129 | 130 | return ( 131 | 132 | 133 | setEmail(e.target.value)} 143 | fullWidth 144 | /> 145 | 146 |
147 | 156 | 163 |
164 |
165 |
166 | ); 167 | } 168 | 169 | export default HomePage; 170 | -------------------------------------------------------------------------------- /src/pages/HomePage/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { Link } from 'react-router-dom'; 3 | import Background from '../../img/logo.svg'; 4 | import Login from '../../img/log-in.svg'; 5 | import Search from '../../img/search.svg'; 6 | 7 | export const Container = styled.div` 8 | height: 100vh; 9 | background: url(${Background}) no-repeat; 10 | background-position: 55vw bottom; 11 | background-position-y: 130px; 12 | 13 | > a { 14 | display: flex; 15 | color: var(--title-color); 16 | font-weight: 700; 17 | } 18 | `; 19 | 20 | export const Content = styled.div` 21 | width: 90%; 22 | max-width: 1100px; 23 | margin: 0 auto; 24 | height: 100%; 25 | display: flex; 26 | flex-direction: column; 27 | 28 | > header { 29 | margin-top: 48px; 30 | display: flex; 31 | align-items: center; 32 | justify-content: space-between; 33 | 34 | > img { 35 | width: 54px; 36 | } 37 | 38 | > div { 39 | display: flex; 40 | > a { 41 | display: flex; 42 | padding: 10px; 43 | color: var(--title-color); 44 | font-weight: 700; 45 | } 46 | > a span { 47 | margin-right: 16px; 48 | display: flex; 49 | background-image: url(${Login}); 50 | width: 20px; 51 | height: 20px; 52 | } 53 | } 54 | } 55 | `; 56 | 57 | export const Main = styled.main` 58 | max-width: 560px; 59 | flex: 1; 60 | display: flex; 61 | flex-direction: column; 62 | justify-content: center; 63 | 64 | > h1 { 65 | font-size: 54px; 66 | } 67 | 68 | > p { 69 | color: var(--text); 70 | font-size: 24px; 71 | line-height: 38px; 72 | margin-top: 24px; 73 | } 74 | `; 75 | 76 | export const MyLink = styled(Link)` 77 | cursor: pointer; 78 | width: 100%; 79 | max-width: 360px; 80 | height: 72px; 81 | 82 | border-radius: 8px; 83 | 84 | display: flex; 85 | align-items: center; 86 | margin-top: 40px; 87 | 88 | background: var(--primary-color); 89 | transition: 400ms; 90 | 91 | &:hover { 92 | background-color: #2fb86e; 93 | } 94 | 95 | > span { 96 | width: 72px; 97 | height: 72px; 98 | border-top-left-radius: 8px; 99 | border-bottom-left-radius: 8px; 100 | background-color: rgba(0, 0, 0, 0.08); 101 | display: flex; 102 | align-items: center; 103 | justify-content: center; 104 | } 105 | 106 | > span::after { 107 | content: ''; 108 | background-image: url('${Search}'); 109 | width: 20px; 110 | height: 20px; 111 | } 112 | 113 | > strong { 114 | flex: 1; 115 | color: white; 116 | text-align: center; 117 | } 118 | `; 119 | -------------------------------------------------------------------------------- /src/pages/Login/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { isEmail } from 'validator'; 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import { get } from 'lodash'; 5 | import { toast } from 'react-toastify'; 6 | import { Link } from 'react-router-dom'; 7 | import { Container, Form, FieldGroup, Field } from './style'; 8 | import LoaderHead from '../../components/Loader'; 9 | import Modal from '../Modal'; 10 | import Logo from '../../img/passaro.svg'; 11 | import api from '../../config/api'; 12 | import * as actions from '../../store/modules/auth/actions'; 13 | 14 | export default function Login(props) { 15 | const dispatch = useDispatch(); 16 | const prevPath = get(props, 'location.state.prevPath', '/'); 17 | 18 | const [email, setEmail] = React.useState(''); 19 | const [password, setPassword] = React.useState(''); 20 | const [check, setCheck] = React.useState(false); 21 | const [isLoading, setLoading] = React.useState(false); 22 | const handleSubmit = (e) => { 23 | e.preventDefault(); 24 | let formErrors = false; 25 | 26 | if (!isEmail(email)) { 27 | formErrors = true; 28 | toast.error('E-mail inválido'); 29 | } 30 | 31 | if (password.length < 6 || password.length > 50) { 32 | formErrors = true; 33 | toast.error('Senha deverá ter entre 6 e 50 caracteres'); 34 | } 35 | 36 | if (formErrors) return; 37 | setLoading(true); 38 | dispatch(actions.loginRequest({ email, password, prevPath })); 39 | }; 40 | 41 | return ( 42 | 43 | 44 | 45 |
46 | 47 | 48 | 49 | Voltar 50 | 51 |
52 |
53 |
54 |

Fazer login

55 | ou crie uma conta 56 |
57 | 58 |
59 | 60 |

Dados para login

61 |
62 | 63 | 64 | 65 | 66 | setEmail(e.target.value)} 70 | required 71 | /> 72 | 73 | 74 | 75 | 76 | 77 | setPassword(e.target.value)} 81 | required 82 | /> 83 | 84 | 85 |
86 | 87 | 88 |
89 |
90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /src/pages/Login/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import Arrow from '../../img/arrow-left.svg'; 3 | 4 | export const Container = styled.div` 5 | width: 100%; 6 | max-width: 1100px; 7 | padding: 1.7px; 8 | /* alinhamento de caixa */ 9 | margin: 0 auto; 10 | 11 | > header { 12 | margin-top: 48px; 13 | display: flex; 14 | justify-content: space-between; 15 | align-items: center; 16 | 17 | > img { 18 | width: 54px; 19 | margin-left: 8.5px; 20 | } 21 | 22 | > a { 23 | color: var(--title-color); 24 | font-weight: bold; 25 | 26 | display: flex; 27 | align-items: center; 28 | 29 | > span { 30 | background-image: url(${Arrow}); 31 | margin-right: 16px; 32 | display: flex; 33 | width: 20px; 34 | height: 20px; 35 | } 36 | } 37 | } 38 | `; 39 | 40 | export const Form = styled.form` 41 | background-color: var(--form); 42 | margin-top: 80px; 43 | /* preenchimento */ 44 | padding: 64px; 45 | border-radius: 8px; 46 | max-width: 730px; 47 | margin: 80px auto; 48 | 49 | > div { 50 | display: flex; 51 | align-items: center; 52 | justify-content: space-between; 53 | > h1 { 54 | font-size: 36px; 55 | } 56 | } 57 | 58 | > fieldset { 59 | margin-top: 64px; 60 | border: 0; 61 | } 62 | 63 | > legend { 64 | margin-bottom: 40px; 65 | display: flex; 66 | align-items: center; 67 | justify-content: space-between; 68 | width: 100%; 69 | 70 | > h2 { 71 | font-size: 24px; 72 | } 73 | 74 | > span { 75 | font-size: 14px; 76 | color: var(--title-color); 77 | } 78 | } 79 | 80 | > select { 81 | -webkit-appearance: none; 82 | -moz-appearance: none; 83 | appearance: none; 84 | } 85 | 86 | > label { 87 | color: var(--text); 88 | font-size: 14px; 89 | margin-bottom: 8px; 90 | } 91 | 92 | > button { 93 | width: 260px; 94 | height: 56px; 95 | background-color: var(--primary-color); 96 | border-radius: 8px; 97 | cursor: pointer; 98 | color: white; 99 | font-weight: bold; 100 | font-size: 16px; 101 | border: 0; 102 | margin-top: 40px; 103 | transition: background-color 400ms; 104 | 105 | &:hover { 106 | background-color: #2fb86e; 107 | } 108 | } 109 | `; 110 | 111 | export const Field = styled.div` 112 | flex: 1; 113 | display: flex; 114 | flex-direction: column; 115 | margin-bottom: 24px; 116 | 117 | > input, 118 | select { 119 | background-color: #f0f0f5; 120 | border: 0; 121 | padding: 16px 24px; 122 | font-size: 16px; 123 | border-radius: 8px; 124 | outline: none; 125 | } 126 | `; 127 | 128 | export const FieldGroup = styled.div` 129 | display: flex; 130 | 131 | > div + div { 132 | margin-left: 24px; 133 | } 134 | `; 135 | 136 | export const ItemsGrid = styled.div` 137 | display: grid; 138 | grid-template-columns: 1fr 1fr 1fr; 139 | gap: 16px; 140 | 141 | > li { 142 | background-color: #f5f5f5; 143 | list-style: none; 144 | border: 2px solid #f5f5f5; 145 | border-radius: 8px; 146 | height: 180px; 147 | padding: 32px 24px 16px; 148 | display: flex; 149 | flex-direction: column; 150 | align-items: center; 151 | justify-content: space-between; 152 | text-align: center; 153 | cursor: pointer; 154 | 155 | > span { 156 | margin-top: 12px; 157 | flex: 1; 158 | display: flex; 159 | align-items: center; 160 | color: var(--title-color); 161 | } 162 | 163 | > img { 164 | display: flex; 165 | width: 150px; 166 | } 167 | 168 | img, 169 | span { 170 | pointer-events: none; 171 | } 172 | } 173 | 174 | .selected { 175 | background-color: red; 176 | background: var(--li-back); 177 | border: 2px solid var(--li-border); 178 | } 179 | `; 180 | -------------------------------------------------------------------------------- /src/pages/Modal/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/alt-text */ 2 | import React, { useEffect, useRef } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import lottie from 'lottie-web'; 5 | import { Modal } from './style'; 6 | import checkImg from '../../img/conf.json'; 7 | 8 | export default function ModalCheck({ check }) { 9 | const container = useRef(null); 10 | 11 | useEffect(() => { 12 | lottie.loadAnimation({ 13 | container: container.current, 14 | renderer: 'svg', 15 | loop: true, 16 | autoplay: true, 17 | animationData: checkImg, 18 | }); 19 | }); 20 | if (!check) return <>; 21 | return ( 22 | 23 |
24 |
25 |

Pagamento concluído

26 |
27 | 28 | ); 29 | } 30 | 31 | ModalCheck.defaultProps = { 32 | check: false, 33 | }; 34 | 35 | ModalCheck.propTypes = { 36 | check: PropTypes.bool, 37 | }; 38 | -------------------------------------------------------------------------------- /src/pages/Modal/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Modal = styled.div` 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | top: 0; 8 | left: 0; 9 | z-index: 1; 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | color: #fff; 14 | font-size: 30px; 15 | background: rgba(0, 0, 0, 0.8); 16 | 17 | .hide { 18 | display: none; 19 | } 20 | 21 | > div { 22 | display: flex; 23 | flex-direction: column; 24 | align-items: center; 25 | 26 | > h1 { 27 | color: white; 28 | margin-top: 32px; 29 | } 30 | } 31 | `; 32 | -------------------------------------------------------------------------------- /src/pages/ModalPayment/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Modal } from './style'; 4 | import checkImg from '../../img/check.svg'; 5 | import Payment from '../Payment'; 6 | 7 | export default function PaymentCheck({ inPayment }) { 8 | if (!inPayment) return <>; 9 | return ( 10 | 11 | 12 | 13 | ); 14 | } 15 | 16 | PaymentCheck.defaultProps = { 17 | inPayment: false, 18 | }; 19 | 20 | PaymentCheck.propTypes = { 21 | inPayment: PropTypes.bool, 22 | }; 23 | -------------------------------------------------------------------------------- /src/pages/ModalPayment/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Modal = styled.div` 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | top: 0; 8 | left: 0; 9 | z-index: 1; 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | color: #fff; 14 | font-size: 30px; 15 | background: rgba(0, 0, 0, 0.8); 16 | 17 | .hide { 18 | display: none; 19 | } 20 | 21 | > div { 22 | display: flex; 23 | flex-direction: column; 24 | align-items: center; 25 | 26 | > h1 { 27 | color: white; 28 | margin-top: 32px; 29 | } 30 | } 31 | `; 32 | -------------------------------------------------------------------------------- /src/pages/Payment/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { loadStripe } from '@stripe/stripe-js'; 4 | import { Elements } from '@stripe/react-stripe-js'; 5 | import axios from '../../services/axios'; 6 | import HomePage from '../HomePage'; 7 | 8 | const stripePromise = loadStripe(process.env.REACT_APP_KEY); 9 | 10 | export default function Payment() { 11 | return ( 12 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/pages/Payment/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import arrow from '../../img/arrow-left.svg'; 3 | 4 | export const Container = styled.div` 5 | width: 90%; 6 | max-width: 1100px; 7 | 8 | /* alinhamento de caixa */ 9 | margin: 0 auto; 10 | 11 | > header { 12 | margin-top: 48px; 13 | display: flex; 14 | justify-content: space-between; 15 | align-items: center; 16 | 17 | > img { 18 | width: 54px; 19 | } 20 | 21 | > a { 22 | color: var(--title-color); 23 | font-weight: bold; 24 | 25 | display: flex; 26 | align-items: center; 27 | 28 | > span { 29 | margin-right: 16px; 30 | background-image: url(${arrow}); 31 | display: flex; 32 | width: 20px; 33 | height: 24px; 34 | } 35 | } 36 | } 37 | 38 | > a { 39 | display: flex; 40 | color: var(--title-color); 41 | font-weight: 700; 42 | } 43 | `; 44 | 45 | export const Cards = styled.div` 46 | display: grid; 47 | grid-template-columns: 1fr 1fr 1fr; 48 | 49 | gap: 24px; 50 | 51 | > div { 52 | > img { 53 | width: 100%; 54 | height: 150px; 55 | object-fit: cover; 56 | border-radius: 8px 8px 0 0; 57 | } 58 | 59 | h1 { 60 | margin-top: 32px; 61 | font-size: 36px; 62 | line-height: 41px; 63 | color: var(--title-color); 64 | } 65 | 66 | h3 { 67 | margin: 24px 0; 68 | font-weight: bold; 69 | font-size: 24px; 70 | line-height: 34px; 71 | 72 | color: var(--primary-color); 73 | } 74 | 75 | p { 76 | font-size: 16px; 77 | line-height: 26px; 78 | color: #6c6c80; 79 | } 80 | } 81 | `; 82 | -------------------------------------------------------------------------------- /src/pages/Products/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { set } from 'lodash'; 4 | import axios from '../../services/axios'; 5 | import { Container, Cards } from './style'; 6 | import LoaderHead from '../../components/Loader'; 7 | 8 | import Logo from '../../img/passaro.svg'; 9 | 10 | export default function Home() { 11 | const [products, setProducts] = React.useState([]); 12 | const [isLoading, setLoading] = React.useState(false); 13 | 14 | React.useEffect(() => { 15 | async function getData() { 16 | setLoading(true); 17 | const response = await axios.get('/product'); 18 | setProducts(response.data); 19 | setLoading(false); 20 | } 21 | getData(); 22 | }, []); 23 | 24 | return ( 25 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | Voltar 33 | 34 |
35 | 36 | 37 | {products.map((product, index) => ( 38 |
39 | 40 |

{product.name}

41 |

{product.type}

42 |

{product.description}

43 |
44 | ))} 45 |
46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /src/pages/Products/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import arrow from '../../img/arrow-left.svg'; 3 | 4 | export const Container = styled.div` 5 | width: 90%; 6 | max-width: 1100px; 7 | 8 | /* alinhamento de caixa */ 9 | margin: 0 auto; 10 | 11 | > header { 12 | margin-top: 48px; 13 | display: flex; 14 | justify-content: space-between; 15 | align-items: center; 16 | 17 | > img { 18 | width: 54px; 19 | } 20 | 21 | > a { 22 | color: var(--title-color); 23 | font-weight: bold; 24 | 25 | display: flex; 26 | align-items: center; 27 | 28 | > span { 29 | margin-right: 16px; 30 | background-image: url(${arrow}); 31 | display: flex; 32 | width: 20px; 33 | height: 24px; 34 | } 35 | } 36 | } 37 | 38 | > a { 39 | display: flex; 40 | color: var(--title-color); 41 | font-weight: 700; 42 | } 43 | `; 44 | 45 | export const Cards = styled.div` 46 | display: grid; 47 | grid-template-columns: 1fr 1fr 1fr; 48 | 49 | gap: 24px; 50 | 51 | > div { 52 | > img { 53 | width: 100%; 54 | height: 150px; 55 | object-fit: cover; 56 | border-radius: 8px 8px 0 0; 57 | } 58 | 59 | h1 { 60 | margin-top: 32px; 61 | font-size: 36px; 62 | line-height: 41px; 63 | color: var(--title-color); 64 | } 65 | 66 | h3 { 67 | margin: 24px 0; 68 | font-weight: bold; 69 | font-size: 24px; 70 | line-height: 34px; 71 | 72 | color: var(--primary-color); 73 | } 74 | 75 | p { 76 | font-size: 16px; 77 | line-height: 26px; 78 | color: #6c6c80; 79 | } 80 | } 81 | `; 82 | -------------------------------------------------------------------------------- /src/pages/Register/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { isEmail } from 'validator'; 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import { get } from 'lodash'; 5 | import { toast } from 'react-toastify'; 6 | import { Link } from 'react-router-dom'; 7 | import { Container, Form, FieldGroup, Field } from './style'; 8 | import Modal from '../Modal'; 9 | import Logo from '../../img/passaro.svg'; 10 | import api from '../../config/api'; 11 | import * as actions from '../../store/modules/auth/actions'; 12 | 13 | export default function Register(props) { 14 | const dispatch = useDispatch(); 15 | 16 | const [check, setCheck] = React.useState(''); 17 | const [name, setName] = React.useState(''); 18 | const [email, setEmail] = React.useState(''); 19 | const [password, setPassword] = React.useState(''); 20 | 21 | async function handleSubmit(e) { 22 | e.preventDefault(); 23 | let formErrors = false; 24 | 25 | if (name.length < 3 || name.length > 255) { 26 | formErrors = true; 27 | toast.error('Nome deverá ter entra 3 e 255 caracteres'); 28 | } 29 | 30 | if (!isEmail(email)) { 31 | formErrors = true; 32 | toast.error('E-mail inválido'); 33 | } 34 | 35 | if (password.length < 6 || password.length > 50) { 36 | formErrors = true; 37 | toast.error('Senha deverpa ter entre 6 e 50 caracteres'); 38 | } 39 | 40 | if (formErrors) return; 41 | 42 | dispatch(actions.registerRequest({ name, email, password })); 43 | } 44 | 45 | return ( 46 | 47 | 48 |
49 | 50 | 51 | 52 | Voltar 53 | 54 |
55 |
56 |
57 |

Fazer registro

58 |
59 | 60 |
61 | 62 |

Dados para o registro

63 |
64 | 65 | 66 | 67 | 68 | setName(e.target.value)} 72 | required 73 | /> 74 | 75 | 76 | 77 | 78 | 79 | setEmail(e.target.value)} 83 | required 84 | /> 85 | 86 | 87 | 88 | 89 | 90 | setPassword(e.target.value)} 94 | required 95 | /> 96 | 97 | 98 |
99 | 100 | 101 |
102 |
103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /src/pages/Register/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import Arrow from '../../img/arrow-left.svg'; 3 | 4 | export const Container = styled.div` 5 | width: 100%; 6 | max-width: 1100px; 7 | 8 | /* alinhamento de caixa */ 9 | margin: 0 auto; 10 | 11 | > header { 12 | margin-top: 48px; 13 | display: flex; 14 | justify-content: space-between; 15 | align-items: center; 16 | 17 | > img { 18 | width: 54px; 19 | margin-left: 8.5px; 20 | } 21 | 22 | > a { 23 | color: var(--title-color); 24 | font-weight: bold; 25 | 26 | display: flex; 27 | align-items: center; 28 | 29 | > span { 30 | background-image: url(${Arrow}); 31 | margin-right: 16px; 32 | display: flex; 33 | width: 20px; 34 | height: 20px; 35 | } 36 | } 37 | } 38 | `; 39 | 40 | export const Form = styled.form` 41 | background-color: var(--form); 42 | margin-top: 80px; 43 | /* preenchimento */ 44 | padding: 64px; 45 | border-radius: 8px; 46 | max-width: 730px; 47 | margin: 80px auto; 48 | 49 | > div { 50 | display: flex; 51 | align-items: center; 52 | justify-content: space-between; 53 | > h1 { 54 | font-size: 36px; 55 | } 56 | } 57 | 58 | > fieldset { 59 | margin-top: 64px; 60 | border: 0; 61 | } 62 | 63 | > legend { 64 | margin-bottom: 40px; 65 | display: flex; 66 | align-items: center; 67 | justify-content: space-between; 68 | width: 100%; 69 | 70 | > h2 { 71 | font-size: 24px; 72 | } 73 | 74 | > span { 75 | font-size: 14px; 76 | color: var(--title-color); 77 | } 78 | } 79 | 80 | > select { 81 | -webkit-appearance: none; 82 | -moz-appearance: none; 83 | appearance: none; 84 | } 85 | 86 | > label { 87 | color: var(--text); 88 | font-size: 14px; 89 | margin-bottom: 8px; 90 | } 91 | 92 | > button { 93 | width: 260px; 94 | height: 56px; 95 | background-color: var(--primary-color); 96 | border-radius: 8px; 97 | cursor: pointer; 98 | color: white; 99 | font-weight: bold; 100 | font-size: 16px; 101 | border: 0; 102 | margin-top: 40px; 103 | transition: background-color 400ms; 104 | 105 | &:hover { 106 | background-color: #2fb86e; 107 | } 108 | } 109 | `; 110 | 111 | export const Field = styled.div` 112 | flex: 1; 113 | display: flex; 114 | flex-direction: column; 115 | margin-bottom: 24px; 116 | 117 | > input, 118 | select { 119 | background-color: #f0f0f5; 120 | border: 0; 121 | padding: 16px 24px; 122 | font-size: 16px; 123 | border-radius: 8px; 124 | outline: none; 125 | } 126 | `; 127 | 128 | export const FieldGroup = styled.div` 129 | display: flex; 130 | 131 | > div + div { 132 | margin-left: 24px; 133 | } 134 | `; 135 | 136 | export const ItemsGrid = styled.div` 137 | display: grid; 138 | grid-template-columns: 1fr 1fr 1fr; 139 | gap: 16px; 140 | 141 | > li { 142 | background-color: #f5f5f5; 143 | list-style: none; 144 | border: 2px solid #f5f5f5; 145 | border-radius: 8px; 146 | height: 180px; 147 | padding: 32px 24px 16px; 148 | display: flex; 149 | flex-direction: column; 150 | align-items: center; 151 | justify-content: space-between; 152 | text-align: center; 153 | cursor: pointer; 154 | 155 | > span { 156 | margin-top: 12px; 157 | flex: 1; 158 | display: flex; 159 | align-items: center; 160 | color: var(--title-color); 161 | } 162 | 163 | > img { 164 | display: flex; 165 | width: 150px; 166 | } 167 | 168 | img, 169 | span { 170 | pointer-events: none; 171 | } 172 | } 173 | 174 | .selected { 175 | background-color: red; 176 | background: var(--li-back); 177 | border: 2px solid var(--li-border); 178 | } 179 | `; 180 | -------------------------------------------------------------------------------- /src/pages/Shopping/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/label-has-associated-control */ 2 | /* eslint-disable jsx-a11y/alt-text */ 3 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 4 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ 5 | import React from 'react'; 6 | import { useDispatch, useSelector } from 'react-redux'; 7 | import { Link } from 'react-router-dom'; 8 | import { toast } from 'react-toastify'; 9 | import { 10 | Container, 11 | Form, 12 | FieldGroup, 13 | Field, 14 | ItemsGrid, 15 | MyModal, 16 | } from './style'; 17 | import Payment from '../Payment'; 18 | import Logo from '../../img/passaro.svg'; 19 | import api from '../../config/api'; 20 | import mel from '../../img/mel.svg'; 21 | import alface from '../../img/alface.svg'; 22 | import cenoura from '../../img/cenoura.svg'; 23 | import banana from '../../img/banana.svg'; 24 | import ervilha from '../../img/ervilha.svg'; 25 | import abacate from '../../img/abacate.svg'; 26 | import * as actions from '../../store/modules/modal/actions'; 27 | import LoaderHead from '../../components/Loader'; 28 | import ModalCheck from '../Modal'; 29 | 30 | MyModal.setAppElement('#root'); 31 | 32 | export default function Shop() { 33 | const dispatch = useDispatch(); 34 | 35 | const isLoggedIn = useSelector((state) => state.auth.isLoggedIn); 36 | const isLoading = useSelector((state) => state.modal.isChecked); 37 | const isChecked = useSelector((state) => state.modal.isChecked); 38 | 39 | const [modalIsOpen, setIsOpen] = React.useState(false); 40 | 41 | const [states, setState] = React.useState([]); 42 | const [cities, setCity] = React.useState([]); 43 | const [items, setItem] = React.useState([]); 44 | 45 | React.useEffect(() => { 46 | async function getData() { 47 | const response = await api.get('/api/v1/localidades/estados/'); 48 | setState(response.data); 49 | } 50 | 51 | getData(); 52 | }, []); 53 | 54 | async function getCities(e) { 55 | e.persist(); 56 | const event = e.target.value; 57 | 58 | const response = await api.get( 59 | `/api/v1/localidades/estados/${event}/municipios` 60 | ); 61 | setCity(response.data); 62 | } 63 | 64 | const handleClick = (event) => { 65 | let itensCopy = Array.from(items); 66 | const itemLi = event.target; 67 | itemLi.classList.toggle('selected'); 68 | const itemId = itemLi.dataset.id; 69 | 70 | const alredySelected = itensCopy.findIndex((item) => { 71 | const itemFound = item === itemId; 72 | return itemFound; 73 | }); 74 | 75 | if (alredySelected >= 0) { 76 | const filteredItems = itensCopy.filter((item) => { 77 | const itemIsDifferent = item !== itemId; 78 | return itemIsDifferent; 79 | }); 80 | 81 | itensCopy = [...filteredItems]; 82 | } else { 83 | itensCopy.push(itemId); 84 | } 85 | setItem(itensCopy); 86 | }; 87 | 88 | const handleSubmit = (e) => { 89 | e.preventDefault(); 90 | console.log(process.env); 91 | 92 | if (isLoggedIn) { 93 | setIsOpen(true); 94 | } else { 95 | toast.warning('Você precisa ter uma conta para concluir sua cesta'); 96 | } 97 | }; 98 | 99 | return ( 100 | 101 | 102 | 103 | setIsOpen(false)}> 104 | 110 | 111 | 112 | 113 |
114 | 115 | 116 | 117 | Voltar 118 | 119 |
120 |
121 |

Montar minha cesta

122 | 123 |
124 | 125 |

Dados pessoas

126 |
127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 150 | 151 | 152 | 153 | 154 | 162 | 163 | 164 | 165 |
166 | 167 |
168 | 169 |

Produtos orgânicos

170 | Selecione um ou mais itens abaixo 171 |
172 | 173 | 174 |
  • 175 | 176 | Mel 500g 177 |
  • 178 |
  • 179 | 180 | Alface 181 |
  • 182 |
  • 183 | 184 | Banana 185 |
  • 186 |
  • 187 | 188 | Ervilha grão 189 |
  • 190 |
  • 191 | 192 | Cenoura 193 |
  • 194 |
  • 195 | 196 | Abacate 197 |
  • 198 | 199 | 200 |
    201 |
    202 | 203 | 204 |
    205 |
    206 | ); 207 | } 208 | -------------------------------------------------------------------------------- /src/pages/Shopping/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import Modal from 'react-modal'; 3 | import Arrow from '../../img/arrow-left.svg'; 4 | import XClose from '../../img/x.svg'; 5 | 6 | export const Container = styled.div` 7 | width: 100%; 8 | max-width: 1100px; 9 | 10 | /* alinhamento de caixa */ 11 | margin: 0 auto; 12 | 13 | > header { 14 | margin-top: 48px; 15 | display: flex; 16 | justify-content: space-between; 17 | align-items: center; 18 | 19 | > img { 20 | width: 54px; 21 | margin-left: 8.5px; 22 | } 23 | 24 | > a { 25 | color: var(--title-color); 26 | font-weight: bold; 27 | 28 | display: flex; 29 | align-items: center; 30 | 31 | > span { 32 | background-image: url(${Arrow}); 33 | margin-right: 16px; 34 | display: flex; 35 | width: 20px; 36 | height: 20px; 37 | } 38 | } 39 | } 40 | `; 41 | 42 | export const Form = styled.form` 43 | background-color: var(--form); 44 | margin-top: 80px; 45 | /* preenchimento */ 46 | padding: 64px; 47 | border-radius: 8px; 48 | max-width: 730px; 49 | margin: 80px auto; 50 | 51 | > h1 { 52 | font-size: 36px; 53 | } 54 | 55 | > fieldset { 56 | margin-top: 64px; 57 | border: 0; 58 | } 59 | 60 | > legend { 61 | margin-bottom: 40px; 62 | display: flex; 63 | align-items: center; 64 | justify-content: space-between; 65 | width: 100%; 66 | 67 | > h2 { 68 | font-size: 24px; 69 | } 70 | 71 | > span { 72 | font-size: 14px; 73 | color: var(--title-color); 74 | } 75 | } 76 | 77 | > select { 78 | -webkit-appearance: none; 79 | -moz-appearance: none; 80 | appearance: none; 81 | } 82 | 83 | > label { 84 | color: var(--text); 85 | font-size: 14px; 86 | margin-bottom: 8px; 87 | } 88 | 89 | > button { 90 | cursor: pointer; 91 | width: 260px; 92 | height: 56px; 93 | background-color: var(--primary-color); 94 | border-radius: 8px; 95 | 96 | color: white; 97 | font-weight: bold; 98 | font-size: 16px; 99 | border: 0; 100 | margin-top: 40px; 101 | transition: background-color 400ms; 102 | 103 | &:hover { 104 | background-color: #2fb86e; 105 | } 106 | } 107 | `; 108 | 109 | export const Field = styled.div` 110 | flex: 1; 111 | display: flex; 112 | flex-direction: column; 113 | margin-bottom: 24px; 114 | 115 | > input, 116 | select { 117 | background-color: #f0f0f5; 118 | border: 0; 119 | padding: 16px 24px; 120 | font-size: 16px; 121 | border-radius: 8px; 122 | outline: none; 123 | } 124 | `; 125 | 126 | export const FieldGroup = styled.div` 127 | display: flex; 128 | 129 | > div + div { 130 | margin-left: 24px; 131 | } 132 | `; 133 | 134 | export const ItemsGrid = styled.div` 135 | display: grid; 136 | grid-template-columns: 1fr 1fr 1fr; 137 | gap: 16px; 138 | 139 | > li { 140 | background-color: #f5f5f5; 141 | list-style: none; 142 | border: 2px solid #f5f5f5; 143 | border-radius: 8px; 144 | height: 180px; 145 | padding: 32px 24px 16px; 146 | display: flex; 147 | flex-direction: column; 148 | align-items: center; 149 | justify-content: space-between; 150 | text-align: center; 151 | cursor: pointer; 152 | 153 | > span { 154 | margin-top: 12px; 155 | flex: 1; 156 | display: flex; 157 | align-items: center; 158 | color: var(--title-color); 159 | } 160 | 161 | > img { 162 | display: flex; 163 | width: 150px; 164 | } 165 | 166 | img, 167 | span { 168 | pointer-events: none; 169 | } 170 | } 171 | 172 | .selected { 173 | background-color: red; 174 | background: var(--li-back); 175 | border: 2px solid var(--li-border); 176 | } 177 | `; 178 | 179 | export const MyModal = styled(Modal)` 180 | 181 | } 182 | > div { 183 | margin-top: 120px; 184 | > a { 185 | margin-left: 100px; 186 | color: var(--title-color); 187 | font-weight: bold; 188 | display: flex; 189 | align-items: center; 190 | cursor: pointer; 191 | 192 | > span { 193 | background-image: url(${Arrow}); 194 | margin-right: 16px; 195 | display: flex; 196 | width: 20px; 197 | height: 20px; 198 | } 199 | } 200 | } 201 | `; 202 | -------------------------------------------------------------------------------- /src/routes/MyRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Redirect } from 'react-router-dom'; 3 | import PropTypes from 'prop-types'; 4 | 5 | export default function MyRoute({ component: Component, isClosed, ...rest }) { 6 | const isLoggedIn = false; 7 | 8 | if (isClosed && !isLoggedIn) { 9 | return ( 10 | 11 | to={{ pathname: '/login', state: { prevPath: rest.location.pathname } }} 12 | 13 | ); 14 | } 15 | return ; 16 | } 17 | 18 | MyRoute.defaultProps = { 19 | isClosed: false, 20 | }; 21 | 22 | MyRoute.propTypes = { 23 | component: PropTypes.oneOfType([PropTypes.element, PropTypes.func]) 24 | .isRequired, 25 | isClosed: PropTypes.bool, 26 | }; 27 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Switch } from 'react-router-dom'; 3 | import { toast } from 'react-toastify'; 4 | 5 | import MyRoute from './MyRoute'; 6 | import Home from '../pages/Home'; 7 | import Shop from '../pages/Shopping'; 8 | import Products from '../pages/Products'; 9 | import Login from '../pages/Login'; 10 | import Register from '../pages/Register'; 11 | 12 | export default function Routes() { 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/services/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export default axios.create({ 4 | baseURL: 'http://localhost:3000', 5 | }); 6 | -------------------------------------------------------------------------------- /src/services/history.js: -------------------------------------------------------------------------------- 1 | import { createBrowserHistory } from 'history'; 2 | 3 | const history = createBrowserHistory(); 4 | 5 | export default history; 6 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { persistStore } from 'redux-persist'; 2 | import { createStore, applyMiddleware } from 'redux'; 3 | import createSagaMiddleware from 'redux-saga'; 4 | 5 | import persistedReducer from './modules/reduxPersist'; 6 | import rootReducer from './modules/rootReducer'; 7 | import rootSaga from './modules/rootSagas'; 8 | 9 | const sagaMiddleware = createSagaMiddleware(); 10 | const store = createStore( 11 | persistedReducer(rootReducer), 12 | applyMiddleware(sagaMiddleware) 13 | ); 14 | 15 | // Config run sagas 16 | sagaMiddleware.run(rootSaga); 17 | 18 | // key persistor to persists 19 | export const persistor = persistStore(store); 20 | export default store; 21 | -------------------------------------------------------------------------------- /src/store/modules/auth/actions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../types'; 2 | 3 | export function loginRequest(payload) { 4 | return { 5 | type: types.LOGIN_REQUEST, 6 | payload, 7 | }; 8 | } 9 | 10 | export function loginSuccess(payload) { 11 | return { 12 | type: types.LOGIN_SUCCESS, 13 | payload, 14 | }; 15 | } 16 | 17 | export function loginFailure(payload) { 18 | return { 19 | type: types.LOGIN_FAILURE, 20 | payload, 21 | }; 22 | } 23 | 24 | export function registerRequest(payload) { 25 | return { 26 | type: types.REGISTER_REQUEST, 27 | payload, 28 | }; 29 | } 30 | 31 | export function registerUpdatedSuccess(payload) { 32 | return { 33 | type: types.REGISTER_UPDATED_SUCCESS, 34 | payload, 35 | }; 36 | } 37 | 38 | export function registerCreatedSuccess(payload) { 39 | return { 40 | type: types.REGISTER_CREATED_SUCCESS, 41 | payload, 42 | }; 43 | } 44 | 45 | export function registerFailure(payload) { 46 | return { 47 | type: types.REGISTER_FAILURE, 48 | payload, 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /src/store/modules/auth/reducer.js: -------------------------------------------------------------------------------- 1 | import * as types from '../types'; 2 | import axios from '../../../services/axios'; 3 | 4 | const initialState = { 5 | isLoggedIn: false, 6 | token: false, 7 | user: {}, 8 | isLoading: false, 9 | isChecked: false, 10 | inPayment: false, 11 | }; 12 | 13 | export default function (state = initialState, action) { 14 | switch (action.type) { 15 | case types.LOGIN_SUCCESS: { 16 | const newState = { ...state }; 17 | newState.isLoggedIn = true; 18 | newState.token = action.payload.token; 19 | newState.user = action.payload.user; 20 | newState.isLoading = false; 21 | return newState; 22 | } 23 | 24 | case types.LOGIN_FAILURE: { 25 | delete axios.defaults.headers.Authorization; 26 | const newState = { ...initialState }; 27 | return newState; 28 | } 29 | 30 | case types.LOGIN_REQUEST: { 31 | const newState = { ...state }; 32 | newState.isLoading = true; 33 | return newState; 34 | } 35 | 36 | case types.REGISTER_REQUEST: { 37 | const newState = { ...state }; 38 | newState.isLoading = true; 39 | return newState; 40 | } 41 | 42 | case types.REGISTER_UPDATED_SUCCESS: { 43 | const newState = { ...state }; 44 | newState.user.nome = action.payload.nome; 45 | newState.user.email = action.payload.email; 46 | newState.isLoading = false; 47 | return newState; 48 | } 49 | 50 | case types.REGISTER_CREATED_SUCCESS: { 51 | const newState = { ...state }; 52 | newState.isLoading = false; 53 | return newState; 54 | } 55 | 56 | case types.REGISTER_FAILURE: { 57 | const newState = { ...state }; 58 | newState.isLoading = false; 59 | return newState; 60 | } 61 | 62 | default: { 63 | return state; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/store/modules/auth/sagas.js: -------------------------------------------------------------------------------- 1 | import { call, put, all, takeLatest } from 'redux-saga/effects'; 2 | import { toast } from 'react-toastify'; 3 | import { get } from 'lodash'; 4 | import * as actions from './actions'; 5 | import * as types from '../types'; 6 | import axios from '../../../services/axios'; 7 | import history from '../../../services/history'; 8 | 9 | function* loginRequest({ payload }) { 10 | try { 11 | const response = yield call(axios.post, '/tokens', payload); 12 | yield put(actions.loginSuccess({ ...response.data })); 13 | 14 | toast.success('Logado com sucesso!'); 15 | 16 | axios.defaults.headers.Authorization = `Bearer ${response.data.token}`; 17 | 18 | history.push(payload.prevPath); 19 | } catch (e) { 20 | toast.error('Usuário ou senha inválido'); 21 | yield put(actions.loginFailure()); 22 | } 23 | } 24 | 25 | function persistRehydrate({ payload }) { 26 | const token = get(payload, 'auth.token', ''); 27 | if (!token) return; 28 | axios.defaults.headers.Authorization = `Bearer ${token}`; 29 | } 30 | 31 | function* registerRequest({ payload }) { 32 | const { name, email, password } = payload; 33 | 34 | try { 35 | yield call(axios.post, '/users', payload); 36 | toast.success('Conta criada com sucesso!'); 37 | yield put(actions.registerCreatedSuccess({ name, email, password })); 38 | history.push('/login'); 39 | } catch (e) { 40 | const errors = get(e, 'response.data.errors', []); 41 | const status = get(e, 'response.status', 0); 42 | 43 | if (status === 401) { 44 | toast.info('Você precisa fazer login novamente'); 45 | yield put(actions.loginFailure()); 46 | return history.push('/login'); 47 | } 48 | 49 | if (errors.length > 0) { 50 | errors.map((error) => toast.error(error)); 51 | } else { 52 | toast.error('Ops, algo deu errado'); 53 | } 54 | 55 | yield put(actions.registerFailure()); 56 | } 57 | } 58 | 59 | export default all([ 60 | takeLatest(types.LOGIN_REQUEST, loginRequest), 61 | takeLatest(types.PERSIST_REHYDRATE, persistRehydrate), 62 | takeLatest(types.REGISTER_REQUEST, registerRequest), 63 | ]); 64 | -------------------------------------------------------------------------------- /src/store/modules/modal/actions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../types'; 2 | 3 | export function paymentRequest(payload) { 4 | return { 5 | type: types.PAYMENT_REQUEST, 6 | payload, 7 | }; 8 | } 9 | 10 | export function paymentSuccess(payload) { 11 | return { 12 | type: types.PAYMENT_SUCCESS, 13 | payload, 14 | }; 15 | } 16 | 17 | export function paymentEnd(payload) { 18 | return { 19 | type: types.PAYMENT_END, 20 | payload, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/store/modules/modal/reducer.js: -------------------------------------------------------------------------------- 1 | import * as types from '../types'; 2 | 3 | const initialState = { 4 | isChecked: false, 5 | }; 6 | 7 | export default function (state = initialState, action) { 8 | switch (action.type) { 9 | case types.PAYMENT_REQUEST: { 10 | const newState = { ...state }; 11 | 12 | newState.isChecked = false; 13 | return newState; 14 | } 15 | case types.PAYMENT_SUCCESS: { 16 | const newState = { ...state }; 17 | newState.isChecked = true; 18 | 19 | return newState; 20 | } 21 | 22 | case types.PAYMENT_END: { 23 | const newState = { ...state }; 24 | newState.isChecked = false; 25 | 26 | return newState; 27 | } 28 | 29 | default: { 30 | return state; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/store/modules/modal/sagas.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luisdiaslima/ecommerce-simulator-react/21a1a97e157e05b279f90b92101a8de486f0166d/src/store/modules/modal/sagas.js -------------------------------------------------------------------------------- /src/store/modules/reduxPersist.js: -------------------------------------------------------------------------------- 1 | import storage from 'redux-persist/lib/storage'; 2 | import { persistReducer } from 'redux-persist'; 3 | 4 | export default (reducers) => { 5 | const persistedReducers = persistReducer( 6 | { 7 | key: 'api', 8 | storage, 9 | whiteList: ['auth'], 10 | }, 11 | reducers 12 | ); 13 | 14 | return persistedReducers; 15 | }; 16 | -------------------------------------------------------------------------------- /src/store/modules/rootReducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import auth from './auth/reducer'; 4 | import modal from './modal/reducer'; 5 | 6 | export default combineReducers({ 7 | auth, 8 | modal, 9 | }); 10 | -------------------------------------------------------------------------------- /src/store/modules/rootSagas.js: -------------------------------------------------------------------------------- 1 | import { all } from 'redux-saga/effects'; 2 | 3 | import auth from './auth/sagas'; 4 | import modal from './modal/sagas'; 5 | 6 | export default function* rootSaga() { 7 | return yield all([auth]); 8 | } 9 | -------------------------------------------------------------------------------- /src/store/modules/types.js: -------------------------------------------------------------------------------- 1 | export const LOGIN_REQUEST = 'LOGIN_REQUEST'; 2 | export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; 3 | export const LOGIN_FAILURE = 'LOGIN_FAILURE'; 4 | 5 | export const REGISTER_REQUEST = 'REGISTER_REQUEST'; 6 | export const REGISTER_FAILURE = 'REGISTER_FAILURE'; 7 | export const REGISTER_UPDATED_SUCCESS = 'REGISTER_UPDATED_SUCCESS'; 8 | export const REGISTER_CREATED_SUCCESS = 'REGISTER_CREATED_SUCCESS'; 9 | 10 | export const PAYMENT_REQUEST = 'PAYMENT_REQUEST'; 11 | export const PAYMENT_SUCCESS = 'PAYMENT_SUCCESS'; 12 | export const PAYMENT_END = 'PAYMENT_END'; 13 | 14 | export const PERSIST_REHYDRATE = 'persist/REHYDRATE'; 15 | -------------------------------------------------------------------------------- /src/styles/GlobalStyle.js: -------------------------------------------------------------------------------- 1 | import styled, { createGlobalStyle } from 'styled-components'; 2 | import * as colors from '../config/colors'; 3 | import 'react-toastify/dist/ReactToastify.css'; 4 | 5 | export default createGlobalStyle` 6 | * { 7 | margin: 0; 8 | padding: 0; 9 | outline: none; 10 | box-sizing: content-box; 11 | 12 | } 13 | 14 | body { 15 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 16 | transition: 700ms; 17 | background: var(--background); 18 | -webkit-font-smoothing: antialiased; 19 | } 20 | 21 | h1, h2, h3, h4, h5, h6 { 22 | font-family: 'Ubuntu', sans-serif; 23 | color: var(--title-color); 24 | } 25 | 26 | :root { 27 | --title-color: #3a275e; 28 | --primary-color: #34cb79; 29 | --background: #f0f0f5; 30 | --form: white; 31 | --text: black; 32 | --li-back: #e1faec; 33 | --li-border: #34cb79; 34 | --bodysearch: #d4d4db; 35 | } 36 | 37 | a { 38 | text-decoration: none; 39 | } 40 | 41 | ul { 42 | list-style: none; 43 | } 44 | 45 | body .Toastify .Toastify__toast-container .Toastify__toast--success { 46 | background: ${colors.successColor} 47 | } 48 | 49 | body .Toastify .Toastify__toast-container .Toastify__toast--error { 50 | background: ${colors.errorColor} 51 | } 52 | 53 | `; 54 | --------------------------------------------------------------------------------