├── .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 | 
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 |
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 |
10 |
--------------------------------------------------------------------------------
/src/img/cenoura.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
63 |
--------------------------------------------------------------------------------
/src/img/cesta-de-comida.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/img/cesta.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/img/check.svg:
--------------------------------------------------------------------------------
1 |
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 |
24 |
--------------------------------------------------------------------------------
/src/img/ervilha.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 |
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 |
11 |
--------------------------------------------------------------------------------
/src/img/organicos.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/src/img/papeis-papelao.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------