From 1ea025b079e44c6ce8d78a88f0644878bb8b7c0e Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sat, 16 Jan 2021 13:10:25 -0300 Subject: [PATCH 01/42] chore(package): adds needed modules to package.json --- package.json | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0a862aa13..0017e984b 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,16 @@ "version": "0.1.0", "private": true, "dependencies": { - "@testing-library/jest-dom": "^4.2.4", - "@testing-library/react": "^9.3.2", - "@testing-library/user-event": "^7.1.2", + "@reduxjs/toolkit": "^1.5.0", "react": "^17.0.1", "react-dom": "^17.0.1", - "react-scripts": "3.4.4" + "react-hook-form": "^6.14.1", + "react-redux": "^7.2.2", + "react-router-dom": "^5.2.0", + "react-scripts": "3.4.4", + "react-text-mask": "^5.4.3", + "redux": "^4.0.5", + "styled-components": "^5.2.1" }, "scripts": { "start": "react-scripts start", @@ -43,8 +47,19 @@ ] }, "devDependencies": { + "@testing-library/jest-dom": "^4.2.4", + "@testing-library/react": "^9.3.2", + "@testing-library/user-event": "^7.1.2", + "@types/react": "^16.9.53", + "@types/react-dom": "^16.9.8", + "@types/react-redux": "^7.1.9", + "@types/react-router-dom": "^5.1.6", + "@types/react-text-mask": "^5.4.6", + "@types/styled-components": "^5.1.4", "husky": "^4.3.0", "lint-staged": "^10.4.2", - "prettier": "^2.1.2" + "prettier": "^2.1.2", + "typedoc": "^0.19.2", + "typescript": "4.0.3" } } From d7981716da42bd4759e5bb20df976a13367c4f08 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sat, 16 Jan 2021 13:10:43 -0300 Subject: [PATCH 02/42] chore(config): adds lint and tsconfig --- .eslintrc.json | 42 ++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 24 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 .eslintrc.json create mode 100644 tsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..7e5f3c27c --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,42 @@ +{ + "env": { + "browser": true, + "es6": true + }, + "settings": { + "react": { + "version": "detect" + } + }, + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:prettier/recommended" + ], + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "ecmaVersion": 2018, + "sourceType": "module" + }, + "plugins": [ + "react", + "@typescript-eslint", + "react-hooks" + ], + "rules": { + "no-unused-vars": "off", + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", + "react/prop-types": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-unused-vars": "error" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..fa3008edf --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve" + }, + "exclude": ["node_modules"], + "include": ["react-app-env.d.ts", "**/*.ts", "**/*.tsx"] +} From 682a96bcdcde3e8598a6bf8bfb5cf68c6b495ace Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sat, 16 Jan 2021 17:05:31 -0300 Subject: [PATCH 03/42] chore(test-setup): adds styled components --- src/setupTests.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/setupTests.js b/src/setupTests.js index 264828a90..2e61632de 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -1 +1,2 @@ import '@testing-library/jest-dom/extend-expect' +import 'jest-styled-components' From 8abc649f5ac0c281fda39a7caec2d09549139ca3 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sat, 16 Jan 2021 17:06:58 -0300 Subject: [PATCH 04/42] chore(index): renames the file name --- src/{index.js => index.tsx} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{index.js => index.tsx} (93%) diff --git a/src/index.js b/src/index.tsx similarity index 93% rename from src/index.js rename to src/index.tsx index ef4821588..08b223bb7 100644 --- a/src/index.js +++ b/src/index.tsx @@ -1,7 +1,7 @@ import React, { StrictMode } from 'react' import ReactDOM from 'react-dom' -import './index.css' import App from './App' + import * as serviceWorker from './serviceWorker' const Strict = () => ( From f5f60b4ad97941f338198b74e3397ca4461c654d Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sat, 16 Jan 2021 17:07:28 -0300 Subject: [PATCH 05/42] chore(app): renames App's file name --- src/App.js | 6 ------ src/App.tsx | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) delete mode 100644 src/App.js create mode 100644 src/App.tsx diff --git a/src/App.js b/src/App.js deleted file mode 100644 index 49c8a20b6..000000000 --- a/src/App.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react' -import './App.css' - -const App = () =>
Boa sorte! 🚀
- -export default App diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 000000000..3b2bfec13 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import { BrowserRouter } from 'react-router-dom' +import { Provider } from 'react-redux' +import { ThemeProvider } from 'styled-components' + +import Routes from './routes' +import store from './redux/store' +import theme from './ui/theme' +import GlobalStyle from './ui/theme/global-style' +import Layout from './ui/layouts/checkout' + +function App() { + return ( + + + + + + + + + + + ) +} + +export default App From 7cd98f8d5e5fc9320032ef134521ac674c136b2e Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sat, 16 Jan 2021 17:13:04 -0300 Subject: [PATCH 06/42] chore(package): fixes lint, changes some scripts and add new libraries --- package.json | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0017e984b..df6bfb240 100644 --- a/package.json +++ b/package.json @@ -15,18 +15,19 @@ "styled-components": "^5.2.1" }, "scripts": { - "start": "react-scripts start", "build": "react-scripts build", - "test": "react-scripts test", "coverage": "react-scripts test --coverage --watchAll=false", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "lint": "eslint --fix --max-warnings=0", + "start": "react-scripts start", + "test": "react-scripts test --env=jest-environment-jsdom-sixteen" }, "eslintConfig": { "extends": "react-app" }, "lint-staged": { - "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [ - "prettier --write" + "*.{ts,tsx}": [ + "npm run lint" ] }, "husky": { @@ -48,15 +49,23 @@ }, "devDependencies": { "@testing-library/jest-dom": "^4.2.4", - "@testing-library/react": "^9.3.2", + "@testing-library/react": "^11.2.3", "@testing-library/user-event": "^7.1.2", + "@types/jest": "^26.0.15", "@types/react": "^16.9.53", "@types/react-dom": "^16.9.8", "@types/react-redux": "^7.1.9", "@types/react-router-dom": "^5.1.6", "@types/react-text-mask": "^5.4.6", "@types/styled-components": "^5.1.4", + "@types/testing-library__jest-dom": "^5.9.5", + "eslint-config-prettier": "^6.13.0", + "eslint-plugin-prettier": "^3.1.4", + "eslint-plugin-react": "^7.21.5", + "eslint-plugin-react-hooks": "^4.2.0", "husky": "^4.3.0", + "jest-environment-jsdom-sixteen": "^1.0.3", + "jest-styled-components": "^7.0.3", "lint-staged": "^10.4.2", "prettier": "^2.1.2", "typedoc": "^0.19.2", From c2a1f6191900d26ba9335868a36f8ee177fbecbf Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sat, 16 Jan 2021 17:13:34 -0300 Subject: [PATCH 07/42] chore(tsconfig): includes all typing files --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index fa3008edf..58b9e4f0d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,5 +20,5 @@ "jsx": "preserve" }, "exclude": ["node_modules"], - "include": ["react-app-env.d.ts", "**/*.ts", "**/*.tsx"] + "include": ["react-app-env.d.ts", "src/**/*.d.ts", "**/*.ts", "**/*.tsx"] } From f7155c0f41b88dd90866f4787d24c3eaf339e4ae Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sat, 16 Jan 2021 21:19:08 -0300 Subject: [PATCH 08/42] style(theme): creates theme structure --- src/ui/theme/global-style.ts | 30 ++++++++++++++++++++++++++++++ src/ui/theme/index.d.ts | 7 +++++++ src/ui/theme/index.ts | 17 +++++++++++++++++ src/ui/theme/types.ts | 17 +++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 src/ui/theme/global-style.ts create mode 100644 src/ui/theme/index.d.ts create mode 100644 src/ui/theme/index.ts create mode 100644 src/ui/theme/types.ts diff --git a/src/ui/theme/global-style.ts b/src/ui/theme/global-style.ts new file mode 100644 index 000000000..3400f15f8 --- /dev/null +++ b/src/ui/theme/global-style.ts @@ -0,0 +1,30 @@ +import { createGlobalStyle } from 'styled-components' + +const GlobalStyle = createGlobalStyle` + * { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: ${({ theme }) => theme.font}; + } + + html { + font-size: 62.5%; + } + + body { + background-color: ${({ theme }) => theme.colors.secondaryDark}; + padding-bottom: 8rem; + } + + img { + max-width: 100%; + display: block; + } + + li { + list-style: none; + } +` + +export default GlobalStyle diff --git a/src/ui/theme/index.d.ts b/src/ui/theme/index.d.ts new file mode 100644 index 000000000..b381323b2 --- /dev/null +++ b/src/ui/theme/index.d.ts @@ -0,0 +1,7 @@ +import 'styled-components' + +import { ITheme } from './types' + +declare module 'styled-components' { + export interface DefaultTheme extends ITheme { } +} diff --git a/src/ui/theme/index.ts b/src/ui/theme/index.ts new file mode 100644 index 000000000..58ac69c21 --- /dev/null +++ b/src/ui/theme/index.ts @@ -0,0 +1,17 @@ +export default { + font: 'Helvetica Neue, Helvetica, Arial, sans-serif', + colors: { + base: '#CCCCCC', + baseAux: '#999999', + baseDark: '#212122', + baseLight: '#FFFFFF', + error: '#FF3300', + focus: '#A43287', + primary: '#FF6C00', + primaryLight: '#FF7800', + primaryDark: '#D45A00', + secondary: '#E7E7E7', + secondaryLight: '#E0E7EE', + secondaryDark: '#EEEEEE' + } +} diff --git a/src/ui/theme/types.ts b/src/ui/theme/types.ts new file mode 100644 index 000000000..c1f8b2f4e --- /dev/null +++ b/src/ui/theme/types.ts @@ -0,0 +1,17 @@ +export interface ITheme { + font: string; + colors: { + base: string, + baseAux: string, + baseDark: string, + baseLight: string, + error: string, + focus: string, + primary: string, + primaryLight: string, + primaryDark: string, + secondary: string, + secondaryLight: string, + secondaryDark: string + }; +} From 03dca53023f6dfed2896952b961e81734f389066 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sat, 16 Jan 2021 21:24:51 -0300 Subject: [PATCH 09/42] chore(images): adds success image --- src/ui/assets/img/success.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/ui/assets/img/success.svg diff --git a/src/ui/assets/img/success.svg b/src/ui/assets/img/success.svg new file mode 100644 index 000000000..d127cfc4a --- /dev/null +++ b/src/ui/assets/img/success.svg @@ -0,0 +1 @@ + \ No newline at end of file From 3b978174549b7058261628cdf4f8a6e87ed21484 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sun, 17 Jan 2021 20:48:32 -0300 Subject: [PATCH 10/42] feat(store): creates redux structure with cart module --- src/redux/modules/cart/actionTypes.ts | 4 + src/redux/modules/cart/actions.spec.ts | 34 ++++++++ src/redux/modules/cart/actions.ts | 24 ++++++ src/redux/modules/cart/mocks.ts | 103 +++++++++++++++++++++++++ src/redux/modules/cart/reducer.spec.ts | 24 ++++++ src/redux/modules/cart/reducer.ts | 24 ++++++ src/redux/modules/cart/saga.spec.ts | 20 +++++ src/redux/modules/cart/saga.ts | 34 ++++++++ src/redux/modules/cart/types/IState.ts | 39 ++++++++++ src/redux/reducers.ts | 5 ++ src/redux/sagas.ts | 14 ++++ src/redux/shared/mockedStore.ts | 18 +++++ src/redux/store.ts | 20 +++++ src/redux/types.ts | 5 ++ 14 files changed, 368 insertions(+) create mode 100644 src/redux/modules/cart/actionTypes.ts create mode 100644 src/redux/modules/cart/actions.spec.ts create mode 100644 src/redux/modules/cart/actions.ts create mode 100644 src/redux/modules/cart/mocks.ts create mode 100644 src/redux/modules/cart/reducer.spec.ts create mode 100644 src/redux/modules/cart/reducer.ts create mode 100644 src/redux/modules/cart/saga.spec.ts create mode 100644 src/redux/modules/cart/saga.ts create mode 100644 src/redux/modules/cart/types/IState.ts create mode 100644 src/redux/reducers.ts create mode 100644 src/redux/sagas.ts create mode 100644 src/redux/shared/mockedStore.ts create mode 100644 src/redux/store.ts create mode 100644 src/redux/types.ts diff --git a/src/redux/modules/cart/actionTypes.ts b/src/redux/modules/cart/actionTypes.ts new file mode 100644 index 000000000..c2edb205a --- /dev/null +++ b/src/redux/modules/cart/actionTypes.ts @@ -0,0 +1,4 @@ +const moduleName = 'CART' + +export const GET_PRODUCTS: string = `${moduleName}/GET_PRODUCTS` +export const SAVE: string = `${moduleName}/SAVE` diff --git a/src/redux/modules/cart/actions.spec.ts b/src/redux/modules/cart/actions.spec.ts new file mode 100644 index 000000000..f2457c848 --- /dev/null +++ b/src/redux/modules/cart/actions.spec.ts @@ -0,0 +1,34 @@ +import { Action, BaseAction } from 'redux-actions' + +import * as type from './actionTypes' +import * as actions from './actions' + +import { ICart } from './types/IState' + +import mockStore from '../../shared/mockedStore' +import { cart } from './mocks' + +describe('Cart Actions', () => { + it(type.GET_PRODUCTS, () => { + const expected: BaseAction[] = [ + { + type: type.GET_PRODUCTS + } + ] + const store = mockStore() + store.dispatch(actions.getProducts()) + expect(store.getActions()).toEqual(expected) + }) + + it(type.SAVE, () => { + const expected: Action[] = [ + { + type: type.SAVE, + payload: cart + } + ] + const store = mockStore() + store.dispatch(actions.save(cart)) + expect(store.getActions()).toEqual(expected) + }) +}) diff --git a/src/redux/modules/cart/actions.ts b/src/redux/modules/cart/actions.ts new file mode 100644 index 000000000..dbf6f5faa --- /dev/null +++ b/src/redux/modules/cart/actions.ts @@ -0,0 +1,24 @@ +import { Action, BaseAction } from 'redux-actions' + +import { ICart } from './types/IState' +import * as type from './actionTypes' + +/** + * Get cart data. This action will be intercepted by your saga. + * @returns Action + */ + +export const getProducts = (): BaseAction => ({ + type: type.GET_PRODUCTS +}) + +/** + * Save card data + * @returns Action + * @param { ICart } data + */ + +export const save = (data: ICart): Action => ({ + type: type.SAVE, + payload: data +}) diff --git a/src/redux/modules/cart/mocks.ts b/src/redux/modules/cart/mocks.ts new file mode 100644 index 000000000..706c01c23 --- /dev/null +++ b/src/redux/modules/cart/mocks.ts @@ -0,0 +1,103 @@ +export const cart = { + id: '5b15c171e4b0023bb624f616', + items: [ + { + quantity: 1, + product: { + sku: '24410', + name: + "L'Oréal Professionnel Expert Absolut Repair Cortex Lipidium - Máscara de Reconstrução 500g", + imageObjects: [ + { + featured: true, + thumbnail: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/1/loreal-professionnel-expert-absolut-repair-cortex-lipidium-mascara-de-reconstrucao-500g-24410-963234120108391775.png', + small: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/2/loreal-professionnel-expert-absolut-repair-cortex-lipidium-mascara-de-reconstrucao-500g-24410-963234120108391775.png', + medium: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/6/loreal-professionnel-expert-absolut-repair-cortex-lipidium-mascara-de-reconstrucao-500g-24410-963234120108391775.png', + large: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/4/loreal-professionnel-expert-absolut-repair-cortex-lipidium-mascara-de-reconstrucao-500g-24410-963234120108391775.png', + extraLarge: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/5/loreal-professionnel-expert-absolut-repair-cortex-lipidium-mascara-de-reconstrucao-500g-24410-963234120108391775.png', + valid: true + } + ], + priceSpecification: { + sku: '24410', + price: 225.9, + originalPrice: 225.9, + maxPrice: 243.9, + percent: 7, + discount: 18 + } + } + }, + { + quantity: 1, + product: { + sku: '38273', + name: 'Good Girl Carolina Herrera Eau de Parfum - Perfume Feminino 30ml', + imageObjects: [ + { + featured: true, + thumbnail: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/1/good-girl-carolina-herrera-eau-de-parfum-perfume-feminino-30ml-38273-1960525940762131267.jpg', + small: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/2/good-girl-carolina-herrera-eau-de-parfum-perfume-feminino-30ml-38273-1960525940762131267.jpg', + medium: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/6/good-girl-carolina-herrera-eau-de-parfum-perfume-feminino-30ml-38273-1960525940762131267.jpg', + large: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/4/good-girl-carolina-herrera-eau-de-parfum-perfume-feminino-30ml-38273-1960525940762131267.jpg', + extraLarge: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/5/good-girl-carolina-herrera-eau-de-parfum-perfume-feminino-30ml-38273-1960525940762131267.jpg', + valid: true + } + ], + priceSpecification: { + sku: '38273', + price: 299, + originalPrice: 299, + maxPrice: 299, + percent: 0, + discount: 0 + } + } + }, + { + quantity: 1, + product: { + sku: '3019', + name: 'Senscience Inner Restore Intensif - Máscara Capilar 50ml', + imageObjects: [ + { + featured: true, + thumbnail: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/1/senscience-inner-restore-intesif-deep-repairing-masque-mascara-de-tratamento-50ml-3019-7903251722539384904.png', + small: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/2/senscience-inner-restore-intesif-deep-repairing-masque-mascara-de-tratamento-50ml-3019-7903251722539384904.png', + medium: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/6/senscience-inner-restore-intesif-deep-repairing-masque-mascara-de-tratamento-50ml-3019-7903251722539384904.png', + large: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/4/senscience-inner-restore-intesif-deep-repairing-masque-mascara-de-tratamento-50ml-3019-7903251722539384904.png', + extraLarge: + 'https://res.cloudinary.com/beleza-na-web/image/upload/f_auto,fl_progressive,q_auto:best/v1/imagens/5/senscience-inner-restore-intesif-deep-repairing-masque-mascara-de-tratamento-50ml-3019-7903251722539384904.png', + valid: true + } + ], + priceSpecification: { + sku: '3019', + price: 99.9, + originalPrice: 99.9, + maxPrice: 99.9, + percent: 0, + discount: 0 + } + } + } + ], + subTotal: 624.8, + shippingTotal: 5.3, + discount: 30, + total: 618.9 +} diff --git a/src/redux/modules/cart/reducer.spec.ts b/src/redux/modules/cart/reducer.spec.ts new file mode 100644 index 000000000..0062a722f --- /dev/null +++ b/src/redux/modules/cart/reducer.spec.ts @@ -0,0 +1,24 @@ +import reducer, { initialState } from './reducer' + +import * as type from './actionTypes' + +import { cart } from './mocks' + +describe('Cart Reducer', () => { + it('should return the initial state', () => { + const action = { + type: 'DUMMY_TYPE' + } + expect(reducer(undefined, action)).toEqual(initialState) + }) + it('should get cart data', () => { + const action = { + type: type.SAVE, + payload: cart + } + expect(reducer(undefined, action)).toEqual({ + ...initialState, + ...cart + }) + }) +}) diff --git a/src/redux/modules/cart/reducer.ts b/src/redux/modules/cart/reducer.ts new file mode 100644 index 000000000..3b24b4767 --- /dev/null +++ b/src/redux/modules/cart/reducer.ts @@ -0,0 +1,24 @@ +// TODO: Analisar utilização do @types/redux-actions +// afim de enformar validação de TS +import { handleActions, Action } from 'redux-actions' + +import * as type from './actionTypes' +import { ICart } from './types/IState' + +export const initialState: ICart = { + id: '', + items: [], + subTotal: 0, + shippingTotal: 0, + discount: 0, + total: 0 +} + +const reducer = { + [type.SAVE]: (state: ICart, action: Action) => ({ + ...state, + ...action.payload + }) +} + +export default handleActions < ICart > (reducer, initialState) diff --git a/src/redux/modules/cart/saga.spec.ts b/src/redux/modules/cart/saga.spec.ts new file mode 100644 index 000000000..bf34cde4d --- /dev/null +++ b/src/redux/modules/cart/saga.spec.ts @@ -0,0 +1,20 @@ +/* eslint-env jest */ +import { call, put } from 'redux-saga/effects' + +import * as saga from './saga' +import * as actions from './actions' + +import { cart } from './mocks' + +describe('Cart Saga', () => { + it('should get cart data and save it on store', () => { + const generator = saga.getProducts() + + expect(generator.next().value).toEqual(call(saga.fetchCartData)) + expect(generator.next(cart).value).toEqual(put(actions.save(cart))) + expect(generator.next()).toEqual({ + value: undefined, + done: true + }) + }) +}) diff --git a/src/redux/modules/cart/saga.ts b/src/redux/modules/cart/saga.ts new file mode 100644 index 000000000..3f7efafcc --- /dev/null +++ b/src/redux/modules/cart/saga.ts @@ -0,0 +1,34 @@ +import { call, put, takeLatest } from 'redux-saga/effects' + +import * as type from './actionTypes' +import * as actions from './actions' + +import { ICart } from './types/IState' + +// In a real application, the cart id should be received as parameter +export const fetchCartData = () => + fetch('http://www.mocky.io/v2/5b15c4923100004a006f3c07') + .then((response) => response.json()) + .then((data) => data) + +/** + * Gets cart's data and save it on store + * + * @export + */ +export function* getProducts() { + try { + const data: ICart = yield call(fetchCartData) + yield put(actions.save(data)) + } catch (error) { + // Here we can handle errors + console.log(error) + } +} + +/* + * Intercept listed actions and execute each associated methods + */ +export default function* cartSaga() { + yield takeLatest(type.GET_PRODUCTS, getProducts) +} diff --git a/src/redux/modules/cart/types/IState.ts b/src/redux/modules/cart/types/IState.ts new file mode 100644 index 000000000..de53ad87f --- /dev/null +++ b/src/redux/modules/cart/types/IState.ts @@ -0,0 +1,39 @@ +export interface ICart { + id: string; + items: IItem[]; + subTotal: number; + shippingTotal: number; + discount: number; + total: number; +} + +interface IItem { + quantity: number; + product: IProduct; +} + +interface IProduct { + sku: string; + name: string; + imageObjects: IImageObject[]; + priceSpecification: IPriceSpecification; +} + +interface IPriceSpecification { + sku: string; + price: number; + originalPrice: number; + maxPrice: number; + percent: number; + discount: number; +} + +interface IImageObject { + featured: boolean; + thumbnail: string; + small: string; + medium: string; + large: string; + extraLarge: string; + valid: boolean; +} diff --git a/src/redux/reducers.ts b/src/redux/reducers.ts new file mode 100644 index 000000000..65100331e --- /dev/null +++ b/src/redux/reducers.ts @@ -0,0 +1,5 @@ +import CartReducer from './modules/cart/reducer' + +export default { + cart: CartReducer +} diff --git a/src/redux/sagas.ts b/src/redux/sagas.ts new file mode 100644 index 000000000..25c631b5e --- /dev/null +++ b/src/redux/sagas.ts @@ -0,0 +1,14 @@ +import { all } from 'redux-saga/effects' + +import cartSaga from './modules/cart/saga' + +export const allSagas = [cartSaga()] + +/** + * Builds a list of sagas that will be used to make a proxy for some redux actions. + * See more at https://redux-saga.js.org/. + */ +export default () => + function* rootSaga() { + yield all(allSagas) + } diff --git a/src/redux/shared/mockedStore.ts b/src/redux/shared/mockedStore.ts new file mode 100644 index 000000000..0cd15e075 --- /dev/null +++ b/src/redux/shared/mockedStore.ts @@ -0,0 +1,18 @@ +import configureMockStore from 'redux-mock-store' +import { Middleware } from 'redux' +import { Action, BaseAction } from 'redux-actions' + +const middlewares: Middleware[] = [] +const mockStore = configureMockStore(middlewares) + +// Represents the store. +export interface IStore { + dispatch: (action: BaseAction) => void; + getActions(): BaseAction[] | Action; +} + +// Exports a function to allow runtime store creation +export default (initialState = {}): IStore => { + const store = mockStore(initialState) + return store +} diff --git a/src/redux/store.ts b/src/redux/store.ts new file mode 100644 index 000000000..6cfe85fc8 --- /dev/null +++ b/src/redux/store.ts @@ -0,0 +1,20 @@ +import { createStore, applyMiddleware, combineReducers, compose } from 'redux' +import createSagaMiddleware from 'redux-saga' +import { composeWithDevTools } from 'redux-devtools-extension' + +import AllReducers from './reducers' +import getSagas from './sagas' + +const sagaMiddleware = createSagaMiddleware() +const composeEnhancers = composeWithDevTools({}) || compose + +const store = createStore( + combineReducers(AllReducers), + composeEnhancers(applyMiddleware(sagaMiddleware)) +) + +sagaMiddleware.run(getSagas()) + +export type RootState = ReturnType + +export default store diff --git a/src/redux/types.ts b/src/redux/types.ts new file mode 100644 index 000000000..eeae74d82 --- /dev/null +++ b/src/redux/types.ts @@ -0,0 +1,5 @@ +import { ICart } from './modules/cart/types/IState' + +export default interface IState { + cart?: ICart +} From 05926adc4c564fde37159d56debdb1a81f48f934 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sun, 17 Jan 2021 20:50:20 -0300 Subject: [PATCH 11/42] chore(typing): adds styled components types to global scope --- src/styled.d.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/styled.d.ts diff --git a/src/styled.d.ts b/src/styled.d.ts new file mode 100644 index 000000000..66289f429 --- /dev/null +++ b/src/styled.d.ts @@ -0,0 +1,9 @@ +// import original module declarations +import 'styled-components' +// import your custom theme +import { ITheme } from './ui/theme/types' + +declare module 'styled-components' { + // eslint-disable-next-line prettier/prettier + export interface DefaultTheme extends ITheme { } +} From 70e084a5cde01d8d182880fdbc7ee1e60156d8fa Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sun, 17 Jan 2021 20:50:40 -0300 Subject: [PATCH 12/42] chore(tsconfig): removes src directory from include --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 58b9e4f0d..fa3008edf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,5 +20,5 @@ "jsx": "preserve" }, "exclude": ["node_modules"], - "include": ["react-app-env.d.ts", "src/**/*.d.ts", "**/*.ts", "**/*.tsx"] + "include": ["react-app-env.d.ts", "**/*.ts", "**/*.tsx"] } From 6eacfc7d12a1585835131a52b5aa36dd094f147b Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Sun, 17 Jan 2021 21:34:46 -0300 Subject: [PATCH 13/42] feat(store): adds payment module --- src/redux/modules/cart/reducer.ts | 2 -- src/redux/modules/payment/actionTypes.ts | 3 +++ src/redux/modules/payment/actions.spec.ts | 23 ++++++++++++++++++++++ src/redux/modules/payment/actions.ts | 15 ++++++++++++++ src/redux/modules/payment/mocks.ts | 6 ++++++ src/redux/modules/payment/reducer.spec.ts | 24 +++++++++++++++++++++++ src/redux/modules/payment/reducer.ts | 20 +++++++++++++++++++ src/redux/modules/payment/types/IState.ts | 6 ++++++ 8 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/redux/modules/payment/actionTypes.ts create mode 100644 src/redux/modules/payment/actions.spec.ts create mode 100644 src/redux/modules/payment/actions.ts create mode 100644 src/redux/modules/payment/mocks.ts create mode 100644 src/redux/modules/payment/reducer.spec.ts create mode 100644 src/redux/modules/payment/reducer.ts create mode 100644 src/redux/modules/payment/types/IState.ts diff --git a/src/redux/modules/cart/reducer.ts b/src/redux/modules/cart/reducer.ts index 3b24b4767..4a35351b8 100644 --- a/src/redux/modules/cart/reducer.ts +++ b/src/redux/modules/cart/reducer.ts @@ -1,5 +1,3 @@ -// TODO: Analisar utilização do @types/redux-actions -// afim de enformar validação de TS import { handleActions, Action } from 'redux-actions' import * as type from './actionTypes' diff --git a/src/redux/modules/payment/actionTypes.ts b/src/redux/modules/payment/actionTypes.ts new file mode 100644 index 000000000..d843df6f4 --- /dev/null +++ b/src/redux/modules/payment/actionTypes.ts @@ -0,0 +1,3 @@ +const moduleName = 'PAYMENT' + +export const SAVE: string = `${moduleName}/SAVE` diff --git a/src/redux/modules/payment/actions.spec.ts b/src/redux/modules/payment/actions.spec.ts new file mode 100644 index 000000000..e637ad4d9 --- /dev/null +++ b/src/redux/modules/payment/actions.spec.ts @@ -0,0 +1,23 @@ +import { Action } from 'redux-actions' + +import * as type from './actionTypes' +import * as actions from './actions' + +import { IPayment } from './types/IState' + +import mockStore from '../../shared/mockedStore' +import { payment } from './mocks' + +describe('Cart Actions', () => { + it(type.SAVE, () => { + const expected: Action[] = [ + { + type: type.SAVE, + payload: payment + } + ] + const store = mockStore() + store.dispatch(actions.save(payment)) + expect(store.getActions()).toEqual(expected) + }) +}) diff --git a/src/redux/modules/payment/actions.ts b/src/redux/modules/payment/actions.ts new file mode 100644 index 000000000..505c8afed --- /dev/null +++ b/src/redux/modules/payment/actions.ts @@ -0,0 +1,15 @@ +import { Action } from 'redux-actions' + +import { IPayment } from './types/IState' +import * as type from './actionTypes' + +/** + * Save card data + * @returns Action + * @param { IPayment } data + */ + +export const save = (data: IPayment): Action => ({ + type: type.SAVE, + payload: data +}) diff --git a/src/redux/modules/payment/mocks.ts b/src/redux/modules/payment/mocks.ts new file mode 100644 index 000000000..1ec4d3a02 --- /dev/null +++ b/src/redux/modules/payment/mocks.ts @@ -0,0 +1,6 @@ +export const payment = { + holder: 'Dummy user', + number: 1234567812345678, + expirationDate: '07/22', + cvv: 545 +} diff --git a/src/redux/modules/payment/reducer.spec.ts b/src/redux/modules/payment/reducer.spec.ts new file mode 100644 index 000000000..80c602fd5 --- /dev/null +++ b/src/redux/modules/payment/reducer.spec.ts @@ -0,0 +1,24 @@ +import reducer, { initialState } from './reducer' + +import * as type from './actionTypes' + +import { payment } from './mocks' + +describe('Cart Reducer', () => { + it('should return the initial state', () => { + const action = { + type: 'DUMMY_TYPE' + } + expect(reducer(undefined, action)).toEqual(initialState) + }) + it('should save payment data', () => { + const action = { + type: type.SAVE, + payload: payment + } + expect(reducer(undefined, action)).toEqual({ + ...initialState, + ...payment + }) + }) +}) diff --git a/src/redux/modules/payment/reducer.ts b/src/redux/modules/payment/reducer.ts new file mode 100644 index 000000000..b0dc0d901 --- /dev/null +++ b/src/redux/modules/payment/reducer.ts @@ -0,0 +1,20 @@ +import { handleActions, Action } from 'redux-actions' + +import * as type from './actionTypes' +import { IPayment } from './types/IState' + +export const initialState: IPayment = { + holder: '', + number: 0, + expirationDate: '', + cvv: 0 +} + +const reducer = { + [type.SAVE]: (state: IPayment, action: Action) => ({ + ...state, + ...action.payload + }) +} + +export default handleActions < IPayment > (reducer, initialState) diff --git a/src/redux/modules/payment/types/IState.ts b/src/redux/modules/payment/types/IState.ts new file mode 100644 index 000000000..21c3d74ea --- /dev/null +++ b/src/redux/modules/payment/types/IState.ts @@ -0,0 +1,6 @@ +export interface IPayment { + holder: string; + number: number; + expirationDate: string; + cvv: number; +} From 37b1a041f802b1bacf6f48a7048cbd32cea304fe Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:19:45 -0300 Subject: [PATCH 14/42] chore(package): adds react-input-mask --- package.json | 11 ++++++++--- src/ui/theme/index.d.ts | 7 ------- 2 files changed, 8 insertions(+), 10 deletions(-) delete mode 100644 src/ui/theme/index.d.ts diff --git a/package.json b/package.json index df6bfb240..a497c58b8 100644 --- a/package.json +++ b/package.json @@ -3,15 +3,16 @@ "version": "0.1.0", "private": true, "dependencies": { - "@reduxjs/toolkit": "^1.5.0", "react": "^17.0.1", "react-dom": "^17.0.1", "react-hook-form": "^6.14.1", + "react-input-mask": "^2.0.4", "react-redux": "^7.2.2", "react-router-dom": "^5.2.0", "react-scripts": "3.4.4", - "react-text-mask": "^5.4.3", "redux": "^4.0.5", + "redux-actions": "^2.6.5", + "redux-saga": "^1.1.3", "styled-components": "^5.2.1" }, "scripts": { @@ -54,9 +55,11 @@ "@types/jest": "^26.0.15", "@types/react": "^16.9.53", "@types/react-dom": "^16.9.8", + "@types/react-input-mask": "^3.0.0", "@types/react-redux": "^7.1.9", "@types/react-router-dom": "^5.1.6", - "@types/react-text-mask": "^5.4.6", + "@types/redux-actions": "^2.6.1", + "@types/redux-mock-store": "^1.0.2", "@types/styled-components": "^5.1.4", "@types/testing-library__jest-dom": "^5.9.5", "eslint-config-prettier": "^6.13.0", @@ -68,6 +71,8 @@ "jest-styled-components": "^7.0.3", "lint-staged": "^10.4.2", "prettier": "^2.1.2", + "redux-devtools-extension": "^2.13.8", + "redux-mock-store": "^1.5.4", "typedoc": "^0.19.2", "typescript": "4.0.3" } diff --git a/src/ui/theme/index.d.ts b/src/ui/theme/index.d.ts deleted file mode 100644 index b381323b2..000000000 --- a/src/ui/theme/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import 'styled-components' - -import { ITheme } from './types' - -declare module 'styled-components' { - export interface DefaultTheme extends ITheme { } -} From 425df6c16f80662559f00065ca21f2912f226628 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:22:44 -0300 Subject: [PATCH 15/42] feat(layout): creates a default layout with header and steps --- src/ui/components/Header/Header.test.tsx | 29 ++++++++++ src/ui/components/Header/Header.tsx | 14 +++++ .../Header/__snapshots__/Header.test.tsx.snap | 3 + .../Header/__snapshots__/styled.test.tsx.snap | 13 +++++ src/ui/components/Header/index.tsx | 1 + src/ui/components/Header/styled.test.tsx | 23 ++++++++ src/ui/components/Header/styled.tsx | 5 ++ src/ui/components/Steps/Steps.test.tsx | 34 ++++++++++++ src/ui/components/Steps/Steps.tsx | 16 ++++++ .../Steps/__snapshots__/Steps.test.tsx.snap | 53 ++++++++++++++++++ .../Steps/__snapshots__/styled.test.tsx.snap | 55 +++++++++++++++++++ src/ui/components/Steps/index.tsx | 1 + src/ui/components/Steps/styled.test.tsx | 50 +++++++++++++++++ src/ui/components/Steps/styled.tsx | 20 +++++++ src/ui/components/Steps/types.ts | 12 ++++ src/ui/layouts/checkout/Checkout.test.tsx | 0 src/ui/layouts/checkout/Checkout.tsx | 15 +++++ .../__snapshots__/styled.test.tsx.snap | 25 +++++++++ src/ui/layouts/checkout/index.tsx | 1 + src/ui/layouts/checkout/styled.test.tsx | 26 +++++++++ src/ui/layouts/checkout/styled.tsx | 13 +++++ 21 files changed, 409 insertions(+) create mode 100644 src/ui/components/Header/Header.test.tsx create mode 100644 src/ui/components/Header/Header.tsx create mode 100644 src/ui/components/Header/__snapshots__/Header.test.tsx.snap create mode 100644 src/ui/components/Header/__snapshots__/styled.test.tsx.snap create mode 100644 src/ui/components/Header/index.tsx create mode 100644 src/ui/components/Header/styled.test.tsx create mode 100644 src/ui/components/Header/styled.tsx create mode 100644 src/ui/components/Steps/Steps.test.tsx create mode 100644 src/ui/components/Steps/Steps.tsx create mode 100644 src/ui/components/Steps/__snapshots__/Steps.test.tsx.snap create mode 100644 src/ui/components/Steps/__snapshots__/styled.test.tsx.snap create mode 100644 src/ui/components/Steps/index.tsx create mode 100644 src/ui/components/Steps/styled.test.tsx create mode 100644 src/ui/components/Steps/styled.tsx create mode 100644 src/ui/components/Steps/types.ts create mode 100644 src/ui/layouts/checkout/Checkout.test.tsx create mode 100644 src/ui/layouts/checkout/Checkout.tsx create mode 100644 src/ui/layouts/checkout/__snapshots__/styled.test.tsx.snap create mode 100644 src/ui/layouts/checkout/index.tsx create mode 100644 src/ui/layouts/checkout/styled.test.tsx create mode 100644 src/ui/layouts/checkout/styled.tsx diff --git a/src/ui/components/Header/Header.test.tsx b/src/ui/components/Header/Header.test.tsx new file mode 100644 index 000000000..df8092466 --- /dev/null +++ b/src/ui/components/Header/Header.test.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { MemoryRouter, Route } from 'react-router' +import { render } from '@testing-library/react' +import { ThemeProvider } from 'styled-components' + +import Header from './' +// import * as S from './styled' +import theme from '../../theme' + +let container: any + +describe('
', () => { + beforeAll(() => { + container = render( + + +
} path="/" /> + + + ) + }) + + it('should match the snapshot', () => { + expect(container.firstChild).toMatchSnapshot() + }) + + it('should have styled header', () => {}) + it('should have step component inside styled header', () => {}) +}) diff --git a/src/ui/components/Header/Header.tsx b/src/ui/components/Header/Header.tsx new file mode 100644 index 000000000..7ea23591e --- /dev/null +++ b/src/ui/components/Header/Header.tsx @@ -0,0 +1,14 @@ +import React, { FC } from 'react' + +import * as S from './styled' +import Steps from '../Steps' + +const Header: FC = () => { + return ( + + + + ) +} + +export default Header diff --git a/src/ui/components/Header/__snapshots__/Header.test.tsx.snap b/src/ui/components/Header/__snapshots__/Header.test.tsx.snap new file mode 100644 index 000000000..b1bf76498 --- /dev/null +++ b/src/ui/components/Header/__snapshots__/Header.test.tsx.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`
should match the snapshot 1`] = `undefined`; diff --git a/src/ui/components/Header/__snapshots__/styled.test.tsx.snap b/src/ui/components/Header/__snapshots__/styled.test.tsx.snap new file mode 100644 index 000000000..01f3f2446 --- /dev/null +++ b/src/ui/components/Header/__snapshots__/styled.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`
styles should have expected style rules 1`] = ` +.c0 { + box-shadow: 0.1rem 0.1rem 0.5rem 0 rgba(0,0,29,0.22); +} + +
+ Dummy content +
+`; diff --git a/src/ui/components/Header/index.tsx b/src/ui/components/Header/index.tsx new file mode 100644 index 000000000..6f8c57905 --- /dev/null +++ b/src/ui/components/Header/index.tsx @@ -0,0 +1 @@ +export { default } from './Header' diff --git a/src/ui/components/Header/styled.test.tsx b/src/ui/components/Header/styled.test.tsx new file mode 100644 index 000000000..dbe4e1087 --- /dev/null +++ b/src/ui/components/Header/styled.test.tsx @@ -0,0 +1,23 @@ +import React from 'react' +import { render } from '@testing-library/react' +import { ThemeProvider } from 'styled-components' + +import * as S from './styled' + +import theme from '../../theme' + +describe('
styles', () => { + it(' should have expected style rules', () => { + const { container } = render( + + Dummy content + + ) + + expect(container.firstChild).toMatchSnapshot() + expect(container.firstChild).toHaveStyleRule( + 'box-shadow', + '0.1rem 0.1rem 0.5rem 0 rgba(0,0,29,0.22)' + ) + }) +}) diff --git a/src/ui/components/Header/styled.tsx b/src/ui/components/Header/styled.tsx new file mode 100644 index 000000000..1afc31fde --- /dev/null +++ b/src/ui/components/Header/styled.tsx @@ -0,0 +1,5 @@ +import styled from 'styled-components/macro' + +export const Header = styled.header` + box-shadow: 0.1rem 0.1rem 0.5rem 0 rgba(0, 0, 29, 0.22); +` diff --git a/src/ui/components/Steps/Steps.test.tsx b/src/ui/components/Steps/Steps.test.tsx new file mode 100644 index 000000000..a66ae5a53 --- /dev/null +++ b/src/ui/components/Steps/Steps.test.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import { render, screen } from '@testing-library/react' +import { MemoryRouter, Route } from 'react-router' +import { ThemeProvider } from 'styled-components' + +import theme from '../../theme' + +import StepBar from './' + +describe('', () => { + it('should render StepBar component', () => { + render( + + + } path="/" /> + + + ) + + expect(screen.getByText(/Sacola/i)).toBeInTheDocument() + }) + + it('should match a snapshot', () => { + const { container } = render( + + + } path="/" /> + + + ) + + expect(container.firstChild).toMatchSnapshot() + }) +}) diff --git a/src/ui/components/Steps/Steps.tsx b/src/ui/components/Steps/Steps.tsx new file mode 100644 index 000000000..3c6ad0580 --- /dev/null +++ b/src/ui/components/Steps/Steps.tsx @@ -0,0 +1,16 @@ +import React, { FC } from 'react' +import { useRouteMatch } from 'react-router-dom' + +import * as S from './styled' + +const Steps: FC = () => { + return ( + + Sacola + Pagamento + Confirmação + + ) +} + +export default Steps diff --git a/src/ui/components/Steps/__snapshots__/Steps.test.tsx.snap b/src/ui/components/Steps/__snapshots__/Steps.test.tsx.snap new file mode 100644 index 000000000..2e308c209 --- /dev/null +++ b/src/ui/components/Steps/__snapshots__/Steps.test.tsx.snap @@ -0,0 +1,53 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should match a snapshot 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 1.2rem 2.5rem; +} + +.c1 { + color: #FF7800; + font-size: 1.3rem; + font-weight: bold; + -webkit-transition: color 0.2s ease 0s; + transition: color 0.2s ease 0s; + text-transform: uppercase; +} + +.c2 { + color: #CCCCCC; + font-size: 1.3rem; + font-weight: bold; + -webkit-transition: color 0.2s ease 0s; + transition: color 0.2s ease 0s; + text-transform: uppercase; +} + +
    +
  • + Sacola +
  • +
  • + Pagamento +
  • +
  • + Confirmação +
  • +
+`; diff --git a/src/ui/components/Steps/__snapshots__/styled.test.tsx.snap b/src/ui/components/Steps/__snapshots__/styled.test.tsx.snap new file mode 100644 index 000000000..773c82652 --- /dev/null +++ b/src/ui/components/Steps/__snapshots__/styled.test.tsx.snap @@ -0,0 +1,55 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` styles should have expected style rules with active status 1`] = ` +.c0 { + color: #FF7800; + font-size: 1.3rem; + font-weight: bold; + -webkit-transition: color 0.2s ease 0s; + transition: color 0.2s ease 0s; + text-transform: uppercase; +} + +
  • + Dummy content +
  • +`; + +exports[` styles should use base color when is inactive 1`] = ` +.c0 { + color: #CCCCCC; + font-size: 1.3rem; + font-weight: bold; + -webkit-transition: color 0.2s ease 0s; + transition: color 0.2s ease 0s; + text-transform: uppercase; +} + +
  • + Dummy content +
  • +`; + +exports[` styles should have expected style rules 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 1.2rem 2.5rem; +} + +
      + Dummy content +
    +`; diff --git a/src/ui/components/Steps/index.tsx b/src/ui/components/Steps/index.tsx new file mode 100644 index 000000000..04b067f3e --- /dev/null +++ b/src/ui/components/Steps/index.tsx @@ -0,0 +1 @@ +export { default } from './Steps' diff --git a/src/ui/components/Steps/styled.test.tsx b/src/ui/components/Steps/styled.test.tsx new file mode 100644 index 000000000..6f702c324 --- /dev/null +++ b/src/ui/components/Steps/styled.test.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import { render } from '@testing-library/react' +import { ThemeProvider } from 'styled-components' + +import * as S from './styled' + +import theme from '../../theme' + +describe(' styles', () => { + it(' should have expected style rules', () => { + const { container } = render( + + Dummy content + + ) + + expect(container.firstChild).toMatchSnapshot() + expect(container.firstChild).toHaveStyleRule('display', 'flex') + expect(container.firstChild).toHaveStyleRule('justify-content', 'space-between') + expect(container.firstChild).toHaveStyleRule('padding', '1.2rem 2.5rem') + }) + + describe('', () => { + it('should have expected style rules with active status', () => { + const { container } = render( + + Dummy content + + ) + + expect(container.firstChild).toMatchSnapshot() + expect(container.firstChild).toHaveStyleRule('color', theme.colors.primaryLight) + expect(container.firstChild).toHaveStyleRule('font-size', '1.3rem') + expect(container.firstChild).toHaveStyleRule('font-weight', 'bold') + expect(container.firstChild).toHaveStyleRule('transition', 'color 0.2s ease 0s') + expect(container.firstChild).toHaveStyleRule('text-transform', 'uppercase') + }) + + it('should use base color when is inactive', () => { + const { container } = render( + + Dummy content + + ) + + expect(container.firstChild).toMatchSnapshot() + expect(container.firstChild).toHaveStyleRule('color', theme.colors.base) + }) + }) +}) diff --git a/src/ui/components/Steps/styled.tsx b/src/ui/components/Steps/styled.tsx new file mode 100644 index 000000000..047a9c6eb --- /dev/null +++ b/src/ui/components/Steps/styled.tsx @@ -0,0 +1,20 @@ +import styled from 'styled-components/macro' + +import { IStyledStep } from './types' + +export const Steps = styled.ul` + display: flex; + justify-content: space-between; + padding: 1.2rem 2.5rem; +` + +export const Step = + styled.li < + IStyledStep > + ` + color: ${({ isActive, theme }) => (isActive ? theme.colors.primaryLight : theme.colors.base)}; + font-size: 1.3rem; + font-weight: bold; + transition: color 0.2s ease 0s; + text-transform: uppercase; +` diff --git a/src/ui/components/Steps/types.ts b/src/ui/components/Steps/types.ts new file mode 100644 index 000000000..51ac94296 --- /dev/null +++ b/src/ui/components/Steps/types.ts @@ -0,0 +1,12 @@ +interface ISteps { + name: string + isActive?: boolean +} + +export interface IStep { + steps: ISteps[] +} + +export interface IStyledStep { + isActive?: boolean +} diff --git a/src/ui/layouts/checkout/Checkout.test.tsx b/src/ui/layouts/checkout/Checkout.test.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/ui/layouts/checkout/Checkout.tsx b/src/ui/layouts/checkout/Checkout.tsx new file mode 100644 index 000000000..4884ac675 --- /dev/null +++ b/src/ui/layouts/checkout/Checkout.tsx @@ -0,0 +1,15 @@ +import React, { FC } from 'react' +import Header from '../../components/Header' + +import * as S from './styled' + +const Checkout: FC = ({ children }) => { + return ( + <> +
    + {children} + + ) +} + +export default Checkout diff --git a/src/ui/layouts/checkout/__snapshots__/styled.test.tsx.snap b/src/ui/layouts/checkout/__snapshots__/styled.test.tsx.snap new file mode 100644 index 000000000..91addce6c --- /dev/null +++ b/src/ui/layouts/checkout/__snapshots__/styled.test.tsx.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`
    styles should have expected style rules 1`] = ` +.c0 { + padding: 0 1rem; +} + +@media (min-width:360px) { + .c0 { + padding: 0 2rem; + } +} + +@media (min-width:768px) { + .c0 { + padding: 0 3rem; + } +} + +
    + Dummy content +
    +`; diff --git a/src/ui/layouts/checkout/index.tsx b/src/ui/layouts/checkout/index.tsx new file mode 100644 index 000000000..ed3da5028 --- /dev/null +++ b/src/ui/layouts/checkout/index.tsx @@ -0,0 +1 @@ +export { default } from './Checkout' diff --git a/src/ui/layouts/checkout/styled.test.tsx b/src/ui/layouts/checkout/styled.test.tsx new file mode 100644 index 000000000..8e76cee28 --- /dev/null +++ b/src/ui/layouts/checkout/styled.test.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import { render } from '@testing-library/react' +import { ThemeProvider } from 'styled-components' + +import * as S from './styled' + +import theme from '../../theme' + +describe('
    styles', () => { + it(' should have expected style rules', () => { + const { container } = render( + + Dummy content + + ) + + expect(container.firstChild).toMatchSnapshot() + expect(container.firstChild).toHaveStyleRule('padding', '0 1rem') + expect(container.firstChild).toHaveStyleRule('padding', '0 2rem', { + media: '(min-width: 360px)' + }) + expect(container.firstChild).toHaveStyleRule('padding', '0 3rem', { + media: '(min-width:768px)' + }) + }) +}) diff --git a/src/ui/layouts/checkout/styled.tsx b/src/ui/layouts/checkout/styled.tsx new file mode 100644 index 000000000..8bd73a573 --- /dev/null +++ b/src/ui/layouts/checkout/styled.tsx @@ -0,0 +1,13 @@ +import styled from 'styled-components/macro' + +export const Main = styled.main` + padding: 0 1rem; + + @media (min-width: 360px) { + padding: 0 2rem; + } + + @media (min-width: 768px) { + padding: 0 3rem; + } +` From d4b8101a7fff70264b22e521443c3f40b1b51807 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:23:44 -0300 Subject: [PATCH 16/42] chore(store): adds payment module to Redux --- src/redux/reducers.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/redux/reducers.ts b/src/redux/reducers.ts index 65100331e..2c1206c4f 100644 --- a/src/redux/reducers.ts +++ b/src/redux/reducers.ts @@ -1,5 +1,7 @@ import CartReducer from './modules/cart/reducer' +import PaymentReducer from './modules/payment/reducer' export default { - cart: CartReducer + cart: CartReducer, + payment: PaymentReducer } From e565d4b69fb6b4fa2540e89a887fa1625b511343 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:32:57 -0300 Subject: [PATCH 17/42] feat(store): creates containers to wrap pages --- .../CartContainer/CartContainer.tsx | 22 +++++++++++++++++ src/redux/containers/CartContainer/index.tsx | 1 + .../PaymentContainer/PaymentContainer.tsx | 24 +++++++++++++++++++ .../containers/PaymentContainer/index.tsx | 1 + .../SummaryContainer/SummaryContainer.tsx | 16 +++++++++++++ .../containers/SummaryContainer/index.tsx | 1 + src/routes/index.tsx | 22 +++++++++++++++++ 7 files changed, 87 insertions(+) create mode 100644 src/redux/containers/CartContainer/CartContainer.tsx create mode 100644 src/redux/containers/CartContainer/index.tsx create mode 100644 src/redux/containers/PaymentContainer/PaymentContainer.tsx create mode 100644 src/redux/containers/PaymentContainer/index.tsx create mode 100644 src/redux/containers/SummaryContainer/SummaryContainer.tsx create mode 100644 src/redux/containers/SummaryContainer/index.tsx create mode 100644 src/routes/index.tsx diff --git a/src/redux/containers/CartContainer/CartContainer.tsx b/src/redux/containers/CartContainer/CartContainer.tsx new file mode 100644 index 000000000..2658ae75d --- /dev/null +++ b/src/redux/containers/CartContainer/CartContainer.tsx @@ -0,0 +1,22 @@ +import React, { useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' + +import { RootState } from '../../store' +import { getProducts } from '../../modules/cart/actions' + +import Cart from '../../../screens/Cart' + +const CartContainer = () => { + const dispatch = useDispatch() + const data = useSelector((state: RootState) => state.cart) + + const { items, ...rest } = data + + useEffect(() => { + dispatch(getProducts()) + }, [dispatch]) + + return +} + +export default CartContainer diff --git a/src/redux/containers/CartContainer/index.tsx b/src/redux/containers/CartContainer/index.tsx new file mode 100644 index 000000000..e52975de4 --- /dev/null +++ b/src/redux/containers/CartContainer/index.tsx @@ -0,0 +1 @@ +export { default } from './CartContainer' diff --git a/src/redux/containers/PaymentContainer/PaymentContainer.tsx b/src/redux/containers/PaymentContainer/PaymentContainer.tsx new file mode 100644 index 000000000..1d60b80e5 --- /dev/null +++ b/src/redux/containers/PaymentContainer/PaymentContainer.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { useSelector, useDispatch } from 'react-redux' +import { useHistory } from 'react-router-dom' + +import { save } from '../../modules/payment/actions' +import { IPayment } from '../../modules/payment/types/IState' +import { RootState } from '../../store' +import Payment from '../../../screens/Payment' + +const PaymentContainer = () => { + const dispatch = useDispatch() + const history = useHistory() + const data = useSelector((state: RootState) => state.cart) + + const handleSubmit = (formData: IPayment) => { + console.log(formData) + dispatch(save(formData)) + history.push('/summary') + } + + return +} + +export default PaymentContainer diff --git a/src/redux/containers/PaymentContainer/index.tsx b/src/redux/containers/PaymentContainer/index.tsx new file mode 100644 index 000000000..916a42581 --- /dev/null +++ b/src/redux/containers/PaymentContainer/index.tsx @@ -0,0 +1 @@ +export { default } from './PaymentContainer' diff --git a/src/redux/containers/SummaryContainer/SummaryContainer.tsx b/src/redux/containers/SummaryContainer/SummaryContainer.tsx new file mode 100644 index 000000000..be702c568 --- /dev/null +++ b/src/redux/containers/SummaryContainer/SummaryContainer.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import { useSelector } from 'react-redux' + +import { RootState } from '../../store' +import Summary from '../../../screens/Summary' + +const SummaryContainer = () => { + const data = useSelector((state: RootState) => state.cart) + const cardData = useSelector((state: RootState) => state.payment) + + const { items, ...props } = data + + return +} + +export default SummaryContainer diff --git a/src/redux/containers/SummaryContainer/index.tsx b/src/redux/containers/SummaryContainer/index.tsx new file mode 100644 index 000000000..115f0eb7b --- /dev/null +++ b/src/redux/containers/SummaryContainer/index.tsx @@ -0,0 +1 @@ +export { default } from './SummaryContainer' diff --git a/src/routes/index.tsx b/src/routes/index.tsx new file mode 100644 index 000000000..e03774a82 --- /dev/null +++ b/src/routes/index.tsx @@ -0,0 +1,22 @@ +import React, { Suspense } from 'react' +import { Route, Switch } from 'react-router-dom' + +// Dynamic imports for performace improvement +const CartContainer = React.lazy(() => import('../redux/containers/CartContainer')) +const PaymentContainer = React.lazy(() => import('../redux/containers/PaymentContainer')) +const SummaryContainer = React.lazy(() => import('../redux/containers/SummaryContainer')) + +export default () => { + return ( + Carregando...}> + + + + + + <>Not Found! + + + + ) +} From 18f70cdde38e4da59ed9ea55198a72cedbc39176 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:43:57 -0300 Subject: [PATCH 18/42] feat(button): creates button component --- src/ui/components/Button/Button.test.tsx | 55 +++++++++++++++++++ src/ui/components/Button/Button.tsx | 9 +++ .../Button/__snapshots__/Button.test.tsx.snap | 44 +++++++++++++++ .../Button/__snapshots__/styled.test.tsx.snap | 44 +++++++++++++++ src/ui/components/Button/index.tsx | 1 + src/ui/components/Button/styled.test.tsx | 55 +++++++++++++++++++ src/ui/components/Button/styled.tsx | 30 ++++++++++ 7 files changed, 238 insertions(+) create mode 100644 src/ui/components/Button/Button.test.tsx create mode 100644 src/ui/components/Button/Button.tsx create mode 100644 src/ui/components/Button/__snapshots__/Button.test.tsx.snap create mode 100644 src/ui/components/Button/__snapshots__/styled.test.tsx.snap create mode 100644 src/ui/components/Button/index.tsx create mode 100644 src/ui/components/Button/styled.test.tsx create mode 100644 src/ui/components/Button/styled.tsx diff --git a/src/ui/components/Button/Button.test.tsx b/src/ui/components/Button/Button.test.tsx new file mode 100644 index 000000000..abff2051c --- /dev/null +++ b/src/ui/components/Button/Button.test.tsx @@ -0,0 +1,55 @@ +import React from 'react' +import { render, screen, fireEvent } from '@testing-library/react' +import { ThemeProvider } from 'styled-components' + +import theme from '../../theme' + +import Button from './' + +describe(' + + ) + + expect(screen.getByText(/button/i)).toBeInTheDocument() + }) + + it('should trigger onClick callback function', () => { + const handleClick = jest.fn() + render( + + + + ) + + fireEvent.click(screen.getByText(/button/i)) + expect(handleClick).toHaveBeenCalled() + }) + + it('should not trigger onClick callback function when disabled', () => { + const handleClick = jest.fn() + render( + + + + ) + + fireEvent.click(screen.getByText(/button/i)) + expect(handleClick).not.toBeCalled() + }) + + it('should match a snapshot', () => { + const { container } = render( + + + + ) + + expect(container.firstChild).toMatchSnapshot() + }) +}) diff --git a/src/ui/components/Button/Button.tsx b/src/ui/components/Button/Button.tsx new file mode 100644 index 000000000..82e2294da --- /dev/null +++ b/src/ui/components/Button/Button.tsx @@ -0,0 +1,9 @@ +import React, { ButtonHTMLAttributes, FC } from 'react' + +import * as S from './styled' + +const Button: FC> = ({ children, ...props }) => ( + {children} +) + +export default Button diff --git a/src/ui/components/Button/__snapshots__/Button.test.tsx.snap b/src/ui/components/Button/__snapshots__/Button.test.tsx.snap new file mode 100644 index 000000000..0aa54e27a --- /dev/null +++ b/src/ui/components/Button/__snapshots__/Button.test.tsx.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` +`; diff --git a/src/ui/components/Button/__snapshots__/styled.test.tsx.snap b/src/ui/components/Button/__snapshots__/styled.test.tsx.snap new file mode 100644 index 000000000..93019a9bc --- /dev/null +++ b/src/ui/components/Button/__snapshots__/styled.test.tsx.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`
    styles should have expected style rules 1`] = ` +.c0 { + background-color: #FF6C00; + border: none; + border-radius: 0.3rem; + box-shadow: inset 0 -0.3rem 0 0 #D45A00,0 0.2rem 0.4rem 0 rgba(0,0,0,0.25); + color: #FFFFFF; + cursor: pointer; + font-size: 2rem; + font-weight: bold; + -webkit-letter-spacing: 0.05rem; + -moz-letter-spacing: 0.05rem; + -ms-letter-spacing: 0.05rem; + letter-spacing: 0.05rem; + line-height: 2.4rem; + margin: auto; + padding: 1.8rem 0; + -webkit-transition: background-color 0.3s ease; + transition: background-color 0.3s ease; + text-align: center; + -webkit-text-decoration: none; + text-decoration: none; + text-transform: uppercase; + width: 100%; +} + +.c0:hover { + background-color: #D45A00; +} + +.c0:disabled { + background-color: #CCCCCC; + box-shadow: none; + cursor: not-allowed; +} + + +`; diff --git a/src/ui/components/Button/index.tsx b/src/ui/components/Button/index.tsx new file mode 100644 index 000000000..3389ecb83 --- /dev/null +++ b/src/ui/components/Button/index.tsx @@ -0,0 +1 @@ +export { default } from './Button' diff --git a/src/ui/components/Button/styled.test.tsx b/src/ui/components/Button/styled.test.tsx new file mode 100644 index 000000000..b0001e09e --- /dev/null +++ b/src/ui/components/Button/styled.test.tsx @@ -0,0 +1,55 @@ +import React from 'react' +import { render } from '@testing-library/react' +import { ThemeProvider } from 'styled-components' + +import * as S from './styled' + +import theme from '../../theme' + +describe('
    styles', () => { + it(' should have expected style rules', () => { + const { container } = render( + + Dummy content + + ) + + expect(container.firstChild).toMatchSnapshot() + expect(container.firstChild).toHaveStyleRule('background-color', theme.colors.primary) + expect(container.firstChild).toHaveStyleRule('border', 'none') + expect(container.firstChild).toHaveStyleRule('border-radius', '0.3rem') + expect(container.firstChild).toHaveStyleRule( + 'box-shadow', + `inset 0 -0.3rem 0 0 ${theme.colors.primaryDark}, 0 0.2rem 0.4rem 0` + ) + expect(container.firstChild).toHaveStyleRule('color', theme.colors.baseLight) + expect(container.firstChild).toHaveStyleRule('cursor', 'pointer') + expect(container.firstChild).toHaveStyleRule('font-size', '2rem') + expect(container.firstChild).toHaveStyleRule('font-weight', 'bold') + expect(container.firstChild).toHaveStyleRule('letter-spacing', '0.05rem') + expect(container.firstChild).toHaveStyleRule('line-height', '2.4rem') + expect(container.firstChild).toHaveStyleRule('margin', 'auto') + expect(container.firstChild).toHaveStyleRule('padding', '1.8rem 0') + expect(container.firstChild).toHaveStyleRule('transition', 'background-color 0.3s ease') + expect(container.firstChild).toHaveStyleRule('text-align', 'center') + expect(container.firstChild).toHaveStyleRule('text-decoration', 'none') + expect(container.firstChild).toHaveStyleRule('text-transform', 'uppercase') + expect(container.firstChild).toHaveStyleRule('width', '100%') + + // Hover + expect(container.firstChild).toHaveStyleRule('background-color', theme.colors.primaryDark, { + modifier: ':hover' + }) + + // Disabled + expect(container.firstChild).toHaveStyleRule('background-color', theme.colors.base, { + modifier: ':disabled' + }) + expect(container.firstChild).toHaveStyleRule('box-shadow', 'none', { + modifier: ':disabled' + }) + expect(container.firstChild).toHaveStyleRule('cursor', 'not-allowed', { + modifier: ':disabled' + }) + }) +}) diff --git a/src/ui/components/Button/styled.tsx b/src/ui/components/Button/styled.tsx new file mode 100644 index 000000000..def1fb5bf --- /dev/null +++ b/src/ui/components/Button/styled.tsx @@ -0,0 +1,30 @@ +import styled from 'styled-components/macro' + +export const Button = styled.button` + background-color: ${({ theme }) => theme.colors.primary}; + border: none; + border-radius: 0.3rem; + box-shadow: inset 0 -0.3rem 0 0 ${({ theme }) => theme.colors.primaryDark}, + 0 0.2rem 0.4rem 0 rgba(0, 0, 0, 0.25); + color: ${({ theme }) => theme.colors.baseLight}; + cursor: pointer; + font-size: 2rem; + font-weight: bold; + letter-spacing: 0.05rem; + line-height: 2.4rem; + margin: auto; + padding: 1.8rem 0; + transition: background-color 0.3s ease; + text-align: center; + text-decoration: none; + text-transform: uppercase; + width: 100%; + &:hover { + background-color: ${({ theme }) => theme.colors.primaryDark}; + } + &:disabled { + background-color: ${({ theme }) => theme.colors.base}; + box-shadow: none; + cursor: not-allowed; + } +` From 6990bde29d519590cc65eff14a5c827e351f8491 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:44:24 -0300 Subject: [PATCH 19/42] feat(content-box): creates ContentBox component --- .../components/ContentBox/ContentBox.test.tsx | 18 ++++++++++++++++++ src/ui/components/ContentBox/ContentBox.tsx | 7 +++++++ .../__snapshots__/ContentBox.test.tsx.snap | 14 ++++++++++++++ src/ui/components/ContentBox/index.tsx | 1 + src/ui/components/ContentBox/styled.tsx | 8 ++++++++ 5 files changed, 48 insertions(+) create mode 100644 src/ui/components/ContentBox/ContentBox.test.tsx create mode 100644 src/ui/components/ContentBox/ContentBox.tsx create mode 100644 src/ui/components/ContentBox/__snapshots__/ContentBox.test.tsx.snap create mode 100644 src/ui/components/ContentBox/index.tsx create mode 100644 src/ui/components/ContentBox/styled.tsx diff --git a/src/ui/components/ContentBox/ContentBox.test.tsx b/src/ui/components/ContentBox/ContentBox.test.tsx new file mode 100644 index 000000000..44aff5b80 --- /dev/null +++ b/src/ui/components/ContentBox/ContentBox.test.tsx @@ -0,0 +1,18 @@ +import React from 'react' +import { render } from '@testing-library/react' +import { ThemeProvider } from 'styled-components' + +import theme from '../../theme' +import ContentBox from './' + +describe('', () => { + it('should match a snapshot', () => { + const { container } = render( + + + + ) + + expect(container.firstChild).toMatchSnapshot() + }) +}) diff --git a/src/ui/components/ContentBox/ContentBox.tsx b/src/ui/components/ContentBox/ContentBox.tsx new file mode 100644 index 000000000..e5b39f577 --- /dev/null +++ b/src/ui/components/ContentBox/ContentBox.tsx @@ -0,0 +1,7 @@ +import React, { FC } from 'react' + +import * as S from './styled' + +const ContentBox: FC = ({ children }) => {children} + +export default ContentBox diff --git a/src/ui/components/ContentBox/__snapshots__/ContentBox.test.tsx.snap b/src/ui/components/ContentBox/__snapshots__/ContentBox.test.tsx.snap new file mode 100644 index 000000000..22fc8f4a5 --- /dev/null +++ b/src/ui/components/ContentBox/__snapshots__/ContentBox.test.tsx.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should match a snapshot 1`] = ` +.c0 { + background-color: #FFFFFF; + border-radius: 0.3rem; + box-shadow: 0.1rem 0.1rem 0.5rem 0 rgba(0,0,29,0.22); + padding: 1.2rem; +} + +
    +`; diff --git a/src/ui/components/ContentBox/index.tsx b/src/ui/components/ContentBox/index.tsx new file mode 100644 index 000000000..f34a461b2 --- /dev/null +++ b/src/ui/components/ContentBox/index.tsx @@ -0,0 +1 @@ +export { default } from './ContentBox' diff --git a/src/ui/components/ContentBox/styled.tsx b/src/ui/components/ContentBox/styled.tsx new file mode 100644 index 000000000..e9dca770c --- /dev/null +++ b/src/ui/components/ContentBox/styled.tsx @@ -0,0 +1,8 @@ +import styled from 'styled-components/macro' + +export const ContentBox = styled.section` + background-color: ${({ theme }) => theme.colors.baseLight}; + border-radius: 0.3rem; + box-shadow: 0.1rem 0.1rem 0.5rem 0 rgba(0, 0, 29, 0.22); + padding: 1.2rem; +` From 99b72c4ac891188ebcdf1e26e72063608b0d0fb7 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:44:50 -0300 Subject: [PATCH 20/42] feat(product): creates ProductCard component --- .../ProductCard/ProductCard.test.tsx | 44 +++++++++++ src/ui/components/ProductCard/ProductCard.tsx | 26 ++++++ .../__snapshots__/ProductCard.test.tsx.snap | 79 +++++++++++++++++++ src/ui/components/ProductCard/index.tsx | 1 + src/ui/components/ProductCard/styled.tsx | 42 ++++++++++ src/ui/components/ProductCard/types.tsx | 5 ++ 6 files changed, 197 insertions(+) create mode 100644 src/ui/components/ProductCard/ProductCard.test.tsx create mode 100644 src/ui/components/ProductCard/ProductCard.tsx create mode 100644 src/ui/components/ProductCard/__snapshots__/ProductCard.test.tsx.snap create mode 100644 src/ui/components/ProductCard/index.tsx create mode 100644 src/ui/components/ProductCard/styled.tsx create mode 100644 src/ui/components/ProductCard/types.tsx diff --git a/src/ui/components/ProductCard/ProductCard.test.tsx b/src/ui/components/ProductCard/ProductCard.test.tsx new file mode 100644 index 000000000..974dd77fb --- /dev/null +++ b/src/ui/components/ProductCard/ProductCard.test.tsx @@ -0,0 +1,44 @@ +import React from 'react' +import { render } from '@testing-library/react' +import { ThemeProvider } from 'styled-components' + +import ProductCard from './' +import theme from '../../theme' + +describe('', () => { + it('should match a snapshot', () => { + const { container } = render( + + + + ) + + expect(container.firstChild).toMatchSnapshot() + }) +}) diff --git a/src/ui/components/ProductCard/ProductCard.tsx b/src/ui/components/ProductCard/ProductCard.tsx new file mode 100644 index 000000000..50a08915b --- /dev/null +++ b/src/ui/components/ProductCard/ProductCard.tsx @@ -0,0 +1,26 @@ +import React from 'react' + +import { IProductCard } from './types' +import { formatCurrency } from '../../../shared/helpers' + +import * as S from './styled' + +const ProductCard = ({ images, priceData, name }: IProductCard) => { + const [imagesList] = images + + return ( + + + + {name} + {priceData && {formatCurrency(priceData.price)}} + + + ) +} + +export default ProductCard diff --git a/src/ui/components/ProductCard/__snapshots__/ProductCard.test.tsx.snap b/src/ui/components/ProductCard/__snapshots__/ProductCard.test.tsx.snap new file mode 100644 index 000000000..09fe3e1ff --- /dev/null +++ b/src/ui/components/ProductCard/__snapshots__/ProductCard.test.tsx.snap @@ -0,0 +1,79 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should match a snapshot 1`] = ` +.c0 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + background-color: #FFFFFF; + border: 1px solid #CCCCCC; + border-radius: 0.3rem; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 1rem; + margin-bottom: 1.5rem; +} + +.c0:last-child { + margin-bottom: 0; +} + +.c1 { + margin-right: 1.5rem; +} + +.c2 { + color: #212122; + font-size: 1.3rem; + font-weight: normal; + line-height: 1.6rem; + margin-bottom: 1.5rem; +} + +.c3 { + color: #212122; + display: block; + font-size: 1.4rem; + font-weight: bold; + line-height: 1.7rem; + text-align: right; +} + +@media (min-width:768px) { + .c1 { + width: 20rem; + } +} + +
    + Good Girl Carolina Herrera Eau de Parfum - Perfume Feminino 30ml +
    +

    + Good Girl Carolina Herrera Eau de Parfum - Perfume Feminino 30ml +

    + + R$299.00 + +
    +
    +`; diff --git a/src/ui/components/ProductCard/index.tsx b/src/ui/components/ProductCard/index.tsx new file mode 100644 index 000000000..4559faa14 --- /dev/null +++ b/src/ui/components/ProductCard/index.tsx @@ -0,0 +1 @@ +export { default } from './ProductCard' diff --git a/src/ui/components/ProductCard/styled.tsx b/src/ui/components/ProductCard/styled.tsx new file mode 100644 index 000000000..1b941a4f7 --- /dev/null +++ b/src/ui/components/ProductCard/styled.tsx @@ -0,0 +1,42 @@ +import styled from 'styled-components/macro' + +export const ProductCard = styled.div` + align-items: center; + background-color: ${({ theme }) => theme.colors.baseLight}; + border: 1px solid ${({ theme }) => theme.colors.base}; + border-radius: 0.3rem; + display: flex; + justify-content: space-between; + padding: 1rem; + margin-bottom: 1.5rem; + + &:last-child { + margin-bottom: 0; + } +` + +export const ProductImage = styled.img` + margin-right: 1.5rem; + @media (min-width: 768px) { + width: 20rem; + } +` + +export const ProductDescWrapper = styled.div`` + +export const ProductTitle = styled.h2` + color: ${({ theme }) => theme.colors.baseDark}; + font-size: 1.3rem; + font-weight: normal; + line-height: 1.6rem; + margin-bottom: 1.5rem; +` + +export const ProductPrice = styled.span` + color: ${({ theme }) => theme.colors.baseDark}; + display: block; + font-size: 1.4rem; + font-weight: bold; + line-height: 1.7rem; + text-align: right; +` diff --git a/src/ui/components/ProductCard/types.tsx b/src/ui/components/ProductCard/types.tsx new file mode 100644 index 000000000..13e983c21 --- /dev/null +++ b/src/ui/components/ProductCard/types.tsx @@ -0,0 +1,5 @@ +export interface IProductCard { + name: string; + images: any[]; + priceData?: any; +} From 0baa83000390609a866454e75339573c3ebc89b5 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:45:24 -0300 Subject: [PATCH 21/42] feat(title): creates Title component --- src/ui/components/Title/Title.test.tsx | 29 +++++++++++++++++++ src/ui/components/Title/Title.tsx | 7 +++++ .../Title/__snapshots__/Title.test.tsx.snap | 17 +++++++++++ src/ui/components/Title/index.tsx | 1 + src/ui/components/Title/styled.tsx | 9 ++++++ 5 files changed, 63 insertions(+) create mode 100644 src/ui/components/Title/Title.test.tsx create mode 100644 src/ui/components/Title/Title.tsx create mode 100644 src/ui/components/Title/__snapshots__/Title.test.tsx.snap create mode 100644 src/ui/components/Title/index.tsx create mode 100644 src/ui/components/Title/styled.tsx diff --git a/src/ui/components/Title/Title.test.tsx b/src/ui/components/Title/Title.test.tsx new file mode 100644 index 000000000..3ff4a381d --- /dev/null +++ b/src/ui/components/Title/Title.test.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { render, screen } from '@testing-library/react' +import { ThemeProvider } from 'styled-components' + +import Title from '.' + +import theme from '../../theme' + +describe('', () => { + it('should render Title component', () => { + render( + <ThemeProvider theme={theme}> + <Title>Produtos + + ) + + expect(screen.getByText(/produtos/i)).toBeInTheDocument() + }) + + it('should match a snapshot', () => { + const { container } = render( + + Produtos + + ) + + expect(container.firstChild).toMatchSnapshot() + }) +}) diff --git a/src/ui/components/Title/Title.tsx b/src/ui/components/Title/Title.tsx new file mode 100644 index 000000000..34f367cfa --- /dev/null +++ b/src/ui/components/Title/Title.tsx @@ -0,0 +1,7 @@ +import React, { FC } from 'react' + +import * as S from './styled' + +const Title: FC = ({ children }) => {children} + +export default Title diff --git a/src/ui/components/Title/__snapshots__/Title.test.tsx.snap b/src/ui/components/Title/__snapshots__/Title.test.tsx.snap new file mode 100644 index 000000000..45e6231dc --- /dev/null +++ b/src/ui/components/Title/__snapshots__/Title.test.tsx.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should match a snapshot 1`] = ` +.c0 { + color: #999999; + font-size: 1.4rem; + line-height: 1.7rem; + margin: 1rem 0; + text-transform: uppercase; +} + +<h1 + class="c0" +> + Produtos +</h1> +`; diff --git a/src/ui/components/Title/index.tsx b/src/ui/components/Title/index.tsx new file mode 100644 index 000000000..6b4c6c130 --- /dev/null +++ b/src/ui/components/Title/index.tsx @@ -0,0 +1 @@ +export { default } from './Title' diff --git a/src/ui/components/Title/styled.tsx b/src/ui/components/Title/styled.tsx new file mode 100644 index 000000000..1c12fab81 --- /dev/null +++ b/src/ui/components/Title/styled.tsx @@ -0,0 +1,9 @@ +import styled from 'styled-components/macro' + +export const Title = styled.h1` + color: ${({ theme }) => theme.colors.baseAux}; + font-size: 1.4rem; + line-height: 1.7rem; + margin: 1rem 0; + text-transform: uppercase; +` From dc936427164c596a056078b8abf5173ccff24a5d Mon Sep 17 00:00:00 2001 From: Diego Martins <diego.santos@ingresso.com> Date: Mon, 18 Jan 2021 00:46:03 -0300 Subject: [PATCH 22/42] feat(cart): creates ProductList component --- src/ui/components/ProductList/ProductList.tsx | 38 +++++++++++++++++++ src/ui/components/ProductList/index.tsx | 1 + src/ui/components/ProductList/styled.tsx | 30 +++++++++++++++ src/ui/components/ProductList/types.tsx | 6 +++ 4 files changed, 75 insertions(+) create mode 100644 src/ui/components/ProductList/ProductList.tsx create mode 100644 src/ui/components/ProductList/index.tsx create mode 100644 src/ui/components/ProductList/styled.tsx create mode 100644 src/ui/components/ProductList/types.tsx diff --git a/src/ui/components/ProductList/ProductList.tsx b/src/ui/components/ProductList/ProductList.tsx new file mode 100644 index 000000000..3e51a1bd1 --- /dev/null +++ b/src/ui/components/ProductList/ProductList.tsx @@ -0,0 +1,38 @@ +import React from 'react' + +import { IProductList } from './types' +import * as S from './styled' +import { formatCurrency } from '../../../shared/helpers' + +const ProductList = (props: IProductList) => { + return ( + <S.ProductList> + {props.subTotal && ( + <S.ProductListItem> + <S.ProductItem>Produtos</S.ProductItem> + <S.ProductItem>{formatCurrency(props.subTotal)}</S.ProductItem> + </S.ProductListItem> + )} + {props.shippingTotal && ( + <S.ProductListItem> + <S.ProductItem>Frete</S.ProductItem> + <S.ProductItem>{formatCurrency(props.shippingTotal)}</S.ProductItem> + </S.ProductListItem> + )} + {props.discount && ( + <S.ProductActiveItem> + <S.ProductItem>Desconto</S.ProductItem> + <S.ProductItem>- {formatCurrency(props.discount)}</S.ProductItem> + </S.ProductActiveItem> + )} + {props.total && ( + <S.ProductHighlightedItem> + <S.ProductItem>Total</S.ProductItem> + <S.ProductItem>{formatCurrency(props.total)}</S.ProductItem> + </S.ProductHighlightedItem> + )} + </S.ProductList> + ) +} + +export default ProductList diff --git a/src/ui/components/ProductList/index.tsx b/src/ui/components/ProductList/index.tsx new file mode 100644 index 000000000..5e7a087ae --- /dev/null +++ b/src/ui/components/ProductList/index.tsx @@ -0,0 +1 @@ +export { default } from './ProductList' diff --git a/src/ui/components/ProductList/styled.tsx b/src/ui/components/ProductList/styled.tsx new file mode 100644 index 000000000..05fd02236 --- /dev/null +++ b/src/ui/components/ProductList/styled.tsx @@ -0,0 +1,30 @@ +import styled from 'styled-components/macro' + +export const ProductList = styled.section` + border: 1px solid ${({ theme }) => theme.colors.base}; + border-radius: 0.3rem; + padding: 1.5rem; + margin: 2rem 0; +` + +export const ProductListItem = styled.p` + color: ${({ theme }) => theme.colors.baseDark}; + display: flex; + font-size: 1.4rem; + justify-content: space-between; + line-height: 1.7rem; + margin-bottom: 0.8rem; + text-transform: uppercase; +` + +export const ProductActiveItem = styled(ProductListItem)` + color: ${({ theme }) => theme.colors.primaryLight}; +` + +export const ProductHighlightedItem = styled(ProductListItem)` + font-weight: bold; + margin-bottom: 0; + margin-top: 1.8rem; +` + +export const ProductItem = styled.span`` diff --git a/src/ui/components/ProductList/types.tsx b/src/ui/components/ProductList/types.tsx new file mode 100644 index 000000000..444bec435 --- /dev/null +++ b/src/ui/components/ProductList/types.tsx @@ -0,0 +1,6 @@ +export interface IProductList { + discount: number; + subTotal: number; + shippingTotal: number; + total: number; +} From 7bdf85a8375f5226ed0e8d01bd12d5bee0f02610 Mon Sep 17 00:00:00 2001 From: Diego Martins <diego.santos@ingresso.com> Date: Mon, 18 Jan 2021 00:46:49 -0300 Subject: [PATCH 23/42] style(input): creates Input styled component --- src/ui/components/Input/styled.tsx | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/ui/components/Input/styled.tsx diff --git a/src/ui/components/Input/styled.tsx b/src/ui/components/Input/styled.tsx new file mode 100644 index 000000000..04e36852b --- /dev/null +++ b/src/ui/components/Input/styled.tsx @@ -0,0 +1,40 @@ +import styled from 'styled-components/macro' +import InputMask from 'react-input-mask' + +export const Input = styled.input<{error?: boolean}>` + background-color: ${({ theme }) => theme.colors.baseLight}; + border-radius: 0.3rem; + border: 0.1rem solid ${({ theme }) => theme.colors.secondary}; + box-shadow: inset 0 0.1rem 0.2rem 0 rgba(0, 0, 0, 0.2); + padding: 1rem; + color: ${({ theme }) => theme.colors.baseDark}; + transition: border 0.2s ease-out; + width: 100%; + &::placeholder { + color: ${({ theme }) => theme.colors.secondaryLight}; + } + &:focus { + border: 1px solid ${({ theme }) => theme.colors.focus}; + outline: 0; + } + ${({ theme, error }) => error && `border: 1px solid ${theme.colors.error};`} +` + +export const MaskedInput = styled(InputMask)<{error?: boolean}>` + background-color: ${({ theme }) => theme.colors.baseLight}; + border-radius: 0.3rem; + border: 0.1rem solid ${({ theme }) => theme.colors.secondary}; + box-shadow: inset 0 0.1rem 0.2rem 0 rgba(0, 0, 0, 0.2); + padding: 1rem; + color: ${({ theme }) => theme.colors.baseDark}; + transition: border 0.2s ease-out; + width: 100%; + &::placeholder { + color: ${({ theme }) => theme.colors.secondaryLight}; + } + &:focus { + border: 1px solid ${({ theme }) => theme.colors.focus}; + outline: 0; + } + ${({ theme, error }) => error && `border: 1px solid ${theme.colors.error};`} +` From 3d2c65a196399b31b46c7bb4f3a492ed656ae024 Mon Sep 17 00:00:00 2001 From: Diego Martins <diego.santos@ingresso.com> Date: Mon, 18 Jan 2021 00:48:06 -0300 Subject: [PATCH 24/42] feat(helpers): creates currency helper --- src/shared/helpers/index.ts | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/shared/helpers/index.ts diff --git a/src/shared/helpers/index.ts b/src/shared/helpers/index.ts new file mode 100644 index 000000000..c02a18846 --- /dev/null +++ b/src/shared/helpers/index.ts @@ -0,0 +1,2 @@ +export const formatCurrency = (value: number) => + new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(value) From dee6b67d383bcb4d1d178edc562b6598ca0459f4 Mon Sep 17 00:00:00 2001 From: Diego Martins <diego.santos@ingresso.com> Date: Mon, 18 Jan 2021 00:48:52 -0300 Subject: [PATCH 25/42] feat(helpers): adds credit card number helper --- src/shared/helpers/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/shared/helpers/index.ts b/src/shared/helpers/index.ts index c02a18846..52e10c8ba 100644 --- a/src/shared/helpers/index.ts +++ b/src/shared/helpers/index.ts @@ -1,2 +1,5 @@ export const formatCurrency = (value: number) => new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(value) + +export const hideCreditCardNumbers = (value: string) => + `****.****.****.${value.substring(value.length - 4)}` From bcf3087a5b3a8606866a2770d0d78c4949c02518 Mon Sep 17 00:00:00 2001 From: Diego Martins <diego.santos@ingresso.com> Date: Mon, 18 Jan 2021 00:49:52 -0300 Subject: [PATCH 26/42] feat(cart): creates cart screen --- src/screens/Cart/Cart.tsx | 53 +++++++++++++++++++++++++++++++++++++ src/screens/Cart/index.tsx | 1 + src/screens/Cart/styled.tsx | 24 +++++++++++++++++ src/screens/Cart/types.tsx | 12 +++++++++ 4 files changed, 90 insertions(+) create mode 100644 src/screens/Cart/Cart.tsx create mode 100644 src/screens/Cart/index.tsx create mode 100644 src/screens/Cart/styled.tsx create mode 100644 src/screens/Cart/types.tsx diff --git a/src/screens/Cart/Cart.tsx b/src/screens/Cart/Cart.tsx new file mode 100644 index 000000000..a86ddec4b --- /dev/null +++ b/src/screens/Cart/Cart.tsx @@ -0,0 +1,53 @@ +import React from 'react' +import { useHistory } from 'react-router-dom' + +import { ICartScreen } from './types' + +import Button from '../../ui/components/Button' +import ContentBox from '../../ui/components/ContentBox' +import ProductCard from '../../ui/components/ProductCard' +import ProductList from '../../ui/components/ProductList' +import Title from '../../ui/components/Title' + +import * as S from './styled' + +const Cart = ({ products, productData }: ICartScreen) => { + const history = useHistory() + + const handleClick = () => { + history.push('/payment') + } + + return ( + <> + <Title>Produtos + + + + {products.map((product) => { + return ( + + ) + })} + + + + + + + + + ) +} + +export default Cart diff --git a/src/screens/Cart/index.tsx b/src/screens/Cart/index.tsx new file mode 100644 index 000000000..85749d502 --- /dev/null +++ b/src/screens/Cart/index.tsx @@ -0,0 +1 @@ +export { default } from './Cart' diff --git a/src/screens/Cart/styled.tsx b/src/screens/Cart/styled.tsx new file mode 100644 index 000000000..291a746d5 --- /dev/null +++ b/src/screens/Cart/styled.tsx @@ -0,0 +1,24 @@ +import styled from 'styled-components/macro' + +export const Wrapper = styled.div` + @media (min-width: 768px) { + display: flex; + } +` + +export const ProductsListWrapper = styled.div` + @media (min-width: 768px) { + margin-right: 2rem; + width: 60%; + } +` + +export const TotalWrapper = styled.div` + @media (min-width: 768px) { + width: 40%; + + section { + margin-top: 0; + } + } +` diff --git a/src/screens/Cart/types.tsx b/src/screens/Cart/types.tsx new file mode 100644 index 000000000..f69bf87b8 --- /dev/null +++ b/src/screens/Cart/types.tsx @@ -0,0 +1,12 @@ +interface IProductDataPage { + discount: number; + id: string; + shippingTotal: number; + subTotal: number; + total: number; +} + +export interface ICartScreen { + products: any[]; + productData: IProductDataPage; +} From 435010a62c40bae179a3eb91a95af32bdaa6d303 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:50:55 -0300 Subject: [PATCH 27/42] feat(payment): creates Payment page --- src/screens/Payment/Payment.tsx | 101 ++++++++++++++++++ .../Payment/components/Form/styled.tsx | 55 ++++++++++ src/screens/Payment/index.tsx | 1 + src/screens/Payment/styled.tsx | 11 ++ src/screens/Payment/types.tsx | 12 +++ 5 files changed, 180 insertions(+) create mode 100644 src/screens/Payment/Payment.tsx create mode 100644 src/screens/Payment/components/Form/styled.tsx create mode 100644 src/screens/Payment/index.tsx create mode 100644 src/screens/Payment/styled.tsx create mode 100644 src/screens/Payment/types.tsx diff --git a/src/screens/Payment/Payment.tsx b/src/screens/Payment/Payment.tsx new file mode 100644 index 000000000..22b4cc945 --- /dev/null +++ b/src/screens/Payment/Payment.tsx @@ -0,0 +1,101 @@ +import React from 'react' +import { useForm } from 'react-hook-form' + +import { IPaymentPage } from './types' + +import Button from '../../ui/components/Button' +import { Input } from '../../ui/components/Input/styled' +import ProductList from '../../ui/components/ProductList' +import Title from '../../ui/components/Title' + +import * as SP from './styled' +import * as S from './components/Form/styled' + +const Payment = ({ productData, onSubmit }: IPaymentPage) => { + const { errors, register, handleSubmit } = useForm() + + const submitForm = (data: any) => { + onSubmit(data) + } + + return ( + <> + Cartão de crédito + + + + Número do cartão: + + {errors.number?.type === 'required' && ( + O número do cartão é obrigatório + )} + {errors.number?.type === 'minLength' && ( + O número do cartão precisa ter 16 dígitos + )} + {errors.number?.type === 'maxLength' && ( + O número do cartão precisa ter no máximo 16 dígitos + )} + + + Nome do titular: + + {errors.holder && O nome do titular é obrigatório} + + + Validade (mês/ano): + + {errors.expirationDate && A validade é obritória} + + + CVV: + + {errors.cvv?.type === 'required' && ( + O CVV é obrigatório + )} + {errors.cvv?.type === 'minLength' && ( + O CVV precisa ter pelo menos 3 dígitos + )} + {errors.cvv?.type === 'maxLength' && ( + O CVV precisa ter no máximo 4 dígitos + )} + + + + + + + + + + ) +} + +export default Payment diff --git a/src/screens/Payment/components/Form/styled.tsx b/src/screens/Payment/components/Form/styled.tsx new file mode 100644 index 000000000..be92e104b --- /dev/null +++ b/src/screens/Payment/components/Form/styled.tsx @@ -0,0 +1,55 @@ +import styled from 'styled-components/macro' + +export const Form = styled.form` + @media (min-width: 768px) { + display: flex; + } +` + +export const FormContent = styled.section` + background-color: ${({ theme }) => theme.colors.baseLight}; + border-radius: 0.3rem; + box-shadow: 0.1rem 0.1rem 0.5rem 0 rgba(0, 0, 29, 0.22); + display: grid; + grid-template-columns: 50% 50%; + grid-template-rows: auto; + grid-template-areas: + 'row1 row1' + 'row2 row2' + 'row3a row3b'; + .input-number { + grid-area: row1; + } + .input-holder { + grid-area: row2; + } + .input-expirationDate { + grid-area: row3a; + margin-right: 2.5rem; + } + .input-cvv { + grid-area: row3b; + } + padding: 1.2rem; + + @media (min-width: 768px) { + margin-right: 2rem; + width: 60%; + } +` + +export const InputMessage = styled.span` + color: ${({ theme }) => theme.colors.error}; +` +export const Label = styled.label` + color: ${({ theme }) => theme.colors.base}; + display: block; + font-size: 1.2rem; + font-weight: bold; + line-height: 1.4rem; + margin-bottom: 0.5rem; +` + +export const InputWrapper = styled.div` + margin-bottom: 2.5rem; +` diff --git a/src/screens/Payment/index.tsx b/src/screens/Payment/index.tsx new file mode 100644 index 000000000..c74ace664 --- /dev/null +++ b/src/screens/Payment/index.tsx @@ -0,0 +1 @@ +export { default } from './Payment' diff --git a/src/screens/Payment/styled.tsx b/src/screens/Payment/styled.tsx new file mode 100644 index 000000000..ff3449308 --- /dev/null +++ b/src/screens/Payment/styled.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components/macro' + +export const TotalWrapper = styled.div` + @media (min-width: 768px) { + width: 40%; + + section { + margin-top: 0; + } + } +` diff --git a/src/screens/Payment/types.tsx b/src/screens/Payment/types.tsx new file mode 100644 index 000000000..ed472d1f6 --- /dev/null +++ b/src/screens/Payment/types.tsx @@ -0,0 +1,12 @@ +interface IProductDataPage { + discount: number; + id: string; + shippingTotal: number; + subTotal: number; + total: number; +} + +export interface IPaymentPage { + productData: IProductDataPage; + onSubmit: any; +} From 6ff89ecc9ff26f41f3f0d286d3b13054bd0b6f1b Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:52:01 -0300 Subject: [PATCH 28/42] feat(payment): creates Payment screen --- src/screens/Summary/Summary.tsx | 57 +++++++++++++++++++++++++++++++++ src/screens/Summary/index.tsx | 1 + src/screens/Summary/styled.tsx | 45 ++++++++++++++++++++++++++ src/screens/Summary/types.ts | 13 ++++++++ 4 files changed, 116 insertions(+) create mode 100644 src/screens/Summary/Summary.tsx create mode 100644 src/screens/Summary/index.tsx create mode 100644 src/screens/Summary/styled.tsx create mode 100644 src/screens/Summary/types.ts diff --git a/src/screens/Summary/Summary.tsx b/src/screens/Summary/Summary.tsx new file mode 100644 index 000000000..1044c55de --- /dev/null +++ b/src/screens/Summary/Summary.tsx @@ -0,0 +1,57 @@ +import React from 'react' +import { ReactComponent as SuccessIcon } from '../../ui/assets/img/success.svg' + +import { ISummaryPage } from './types' + +import ProductList from '../../ui/components/ProductList' +import ContentBox from '../../ui/components/ContentBox' +import ProductCard from '../../ui/components/ProductCard' +import Title from '../../ui/components/Title' +import { hideCreditCardNumbers } from '../../shared/helpers' + +import theme from '../../ui/theme' +import * as S from './styled' + +const Summary = ({ productData, products, cardData }: ISummaryPage) => { + return ( + <> + + + Compra efetuada com sucesso + + + Pagamento + + {hideCreditCardNumbers(cardData.number.toString())} + {cardData.holder} + {cardData.expirationDate} + + Produtos + + + + {products.map((product) => { + return ( + + ) + })} + + + + + + + + ) +} + +export default Summary diff --git a/src/screens/Summary/index.tsx b/src/screens/Summary/index.tsx new file mode 100644 index 000000000..d1f3c0dac --- /dev/null +++ b/src/screens/Summary/index.tsx @@ -0,0 +1 @@ +export { default } from './Summary' diff --git a/src/screens/Summary/styled.tsx b/src/screens/Summary/styled.tsx new file mode 100644 index 000000000..3c1f70860 --- /dev/null +++ b/src/screens/Summary/styled.tsx @@ -0,0 +1,45 @@ +import styled from 'styled-components/macro' + +export const SummaryWrapper = styled.div` + padding: 2rem 0; + text-align: center; +` + +export const SummaryText = styled.p` + color: ${({ theme }) => theme.colors.primaryLight}; + font-size: 1.4rem; + font-weight: bold; + line-height: 1.7rem; + margin-top: 1.2rem; + text-transform: uppercase; +` + +export const PaymentText = styled.p` + color: ${({ theme }) => theme.colors.baseDark}; + font-size: 1.4rem; + line-height: 1.7rem; +` + +export const SummaryData = styled.div` + @media (min-width: 768px) { + display: flex; + flex-direction: row; + } +` + +export const SummaryProduct = styled.div` + @media (min-width: 768px) { + margin-right: 2rem; + width: 60%; + } +` + +export const SummaryTable = styled.div` + @media (min-width: 768px) { + width: 40%; + + section { + margin-top: 0; + } + } +` diff --git a/src/screens/Summary/types.ts b/src/screens/Summary/types.ts new file mode 100644 index 000000000..87d992fed --- /dev/null +++ b/src/screens/Summary/types.ts @@ -0,0 +1,13 @@ +interface IProductData { + discount: number; + id: string; + shippingTotal: number; + subTotal: number; + total: number; +} + +export interface ISummaryPage { + cardData: any; + productData: IProductData; + products: any[]; +} From e16b3e02b8e931d6bbad6cca4287ee5f1ca4c0dc Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 00:57:45 -0300 Subject: [PATCH 29/42] feat(payment-form): disables payment button when has errors --- src/screens/Payment/Payment.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/screens/Payment/Payment.tsx b/src/screens/Payment/Payment.tsx index 22b4cc945..af1c7dc83 100644 --- a/src/screens/Payment/Payment.tsx +++ b/src/screens/Payment/Payment.tsx @@ -89,7 +89,11 @@ const Payment = ({ productData, onSubmit }: IPaymentPage) => { discount={productData.discount} total={productData.total} /> - From cc94cf3dd00fb776eaa07de77a1bd57544c2057a Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 01:05:04 -0300 Subject: [PATCH 30/42] chore(style): removes useless files --- src/App.css | 10 ---------- src/App.test.js | 9 --------- src/index.css | 14 -------------- 3 files changed, 33 deletions(-) delete mode 100644 src/App.css delete mode 100644 src/App.test.js delete mode 100644 src/index.css diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 48869871e..000000000 --- a/src/App.css +++ /dev/null @@ -1,10 +0,0 @@ -.App { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} diff --git a/src/App.test.js b/src/App.test.js deleted file mode 100644 index ac29fa496..000000000 --- a/src/App.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' -import { render } from '@testing-library/react' -import App from './App' - -test('renders learn react link', () => { - const { getByText } = render() - const linkElement = getByText(/beleza/i) - expect(linkElement).toBeInTheDocument() -}) diff --git a/src/index.css b/src/index.css deleted file mode 100644 index cee5f348f..000000000 --- a/src/index.css +++ /dev/null @@ -1,14 +0,0 @@ -body { - margin: 0; - padding: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; -} From aadb0f01ddb3a965537d6f05d25d7c04375f1cad Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 01:05:33 -0300 Subject: [PATCH 31/42] chore(payment-container): removes console log --- src/redux/containers/PaymentContainer/PaymentContainer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/redux/containers/PaymentContainer/PaymentContainer.tsx b/src/redux/containers/PaymentContainer/PaymentContainer.tsx index 1d60b80e5..2388e234b 100644 --- a/src/redux/containers/PaymentContainer/PaymentContainer.tsx +++ b/src/redux/containers/PaymentContainer/PaymentContainer.tsx @@ -13,7 +13,6 @@ const PaymentContainer = () => { const data = useSelector((state: RootState) => state.cart) const handleSubmit = (formData: IPayment) => { - console.log(formData) dispatch(save(formData)) history.push('/summary') } From 20bde931f9451ffe6c8def969b456a754b86968f Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 09:44:40 -0300 Subject: [PATCH 32/42] docs(build): adds typedoc to postbuild --- .gitignore | 3 +++ package.json | 2 ++ typedoc.json | 7 +++++++ 3 files changed, 12 insertions(+) create mode 100644 typedoc.json diff --git a/.gitignore b/.gitignore index 4d29575de..5d855ef60 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ .env.test.local .env.production.local +# doc +/docs + npm-debug.log* yarn-debug.log* yarn-error.log* diff --git a/package.json b/package.json index a497c58b8..7c6fe0a8d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,9 @@ }, "scripts": { "build": "react-scripts build", + "postbuild": "npm run docs", "coverage": "react-scripts test --coverage --watchAll=false", + "docs": "typedoc --options typedoc.json ./src", "eject": "react-scripts eject", "lint": "eslint --fix --max-warnings=0", "start": "react-scripts start", diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 000000000..dc3290284 --- /dev/null +++ b/typedoc.json @@ -0,0 +1,7 @@ +{ + "mode": "file", + "out": "docs", + "target": "es6", + "theme": "minimal", + "exclude": "**/*+(.test|.spec|.e2e).*" +} From 8ed8aff912936a9e544609b3230c6ace5087e4d3 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 09:54:34 -0300 Subject: [PATCH 33/42] chore(package): adds "serve" script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c6fe0a8d..77f9cb7e7 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "eject": "react-scripts eject", "lint": "eslint --fix --max-warnings=0", "start": "react-scripts start", - "test": "react-scripts test --env=jest-environment-jsdom-sixteen" + "test": "react-scripts test --env=jest-environment-jsdom-sixteen", + "serve": "npm run build && npx serve -s build" }, "eslintConfig": { "extends": "react-app" From 8f1c83f09bf67d8604aff1cb68d13617ddb254a6 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 10:17:14 -0300 Subject: [PATCH 34/42] docs(readme): updates readme file --- README.md | 169 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 139 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 87b3efc8f..b11407874 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,150 @@ -## Frontend Test +# Boticário's Challenge -Faça um fork deste repositório e finalizar o teste, submeta um pull request para o repositório que nosso time será notificado. +![Boticário](https://i.pinimg.com/originals/b5/e2/eb/b5e2eb7bbd8afbcc5c8e41c84188cfc5.png 'Boticário challenge') -O teste consiste em um checkout simples contendo 3 passos (carrinho, pagamento e sucesso) [Veja o Layout](https://projects.invisionapp.com/prototype/font-test-cji0j0khf005c1t0132358e8k) +## Powered by -**Faça quando quiser/puder (madrugada, fim de semana, etc)** +[![Written in TypeScript](https://cdn.iconscout.com/icon/free/png-128/typescript-1-1175078.png 'Written in TypeScript')](http://www.typescriptlang.org) +[![Tested with Jest](https://d2eip9sf3oo6c2.cloudfront.net/tags/images/000/000/940/square_128/jestlogo.png 'Tested with Jest')](https://jestjs.io/) +![Uses Javascript as programming language](https://sabe.io/classes/javascript/icon.png) +[![Documentation built by TypeDoc](https://typedoc.org/images/logo-128.png 'Documentation built by TypeDoc')](https://typedoc.org) -### Requerimentos +| Build Status | Built By | We Love | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| ![BuildStatus](https://img.shields.io/badge/Build-Passing-brightgreen.svg 'Building Status') | ![BuiltBy](https://img.shields.io/badge/TypeScript-Lovers-black.svg 'img.shields.io') | ![ForTheBadge](https://img.shields.io/badge/Using-Badges-red.svg 'ForTheBadge') | -- Pixel perfect ([nesse link](https://projects.invisionapp.com/prototype/font-test-cji0j0khf005c1t0132358e8k), você pode inspecionar para ver espaçamentos, fonte, tamanho, etc) -- A aplicação precisa ser responsiva, utilizando o conceito de mobile-first. Use sua imaginação para entregar uma experiência boa no desktop. -- Renderize cada passo em uma URL única (lib de rotas). +[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) +[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) +This application is a checkout simulation based on three steps. -### Passo 1 - Carrinho: - - Consuma o [esse endpoint](http://www.mocky.io/v2/5b15c4923100004a006f3c07) e liste os itens do carrinho, bem como o resumo do carrinho; - - Persista o conteúdo do JSON para ser usado nas próximas etapas; +In the initial page, cart data is requested from the API and saved on store. In the second step, we have a payment form to receive user's payment data. When user inputs your payment data and submit the form, the application is redirected to the last step: confirmation. In this step, we show a summary of order for the user. -### Passo 2 - Pagamento: - - Exiba um form com campos de cartão de crédito com validação em cada campo; - - Habilite o botão de Finalizar Pedido apenas se o form esteja válido; +## Built With -### Passo 3 - Sucesso: - - Todo o conteúdo deverá ser exibido a partir dos dados persistidos; - -### O que vamos avaliar: - - Organização do código; - - Mensagens (em inglês) e mudanças nos commits; - - Composição/reutilização de componentes; - - Testes unitários; - - O motivo de ter escolhido cada tech da stack; - - Como rodar sua aplicação ;) +- [React](https://reactjs.org/) - The Javascript library used to build user interface +- [Create React App](https://create-react-app.dev/docs/getting-started/) - A React Starter Kit +- [Styled Components](https://www.styled-components.com/) - A tool to create Styled React components +- [Typescript](https://www.typescriptlang.org/) - A typed superset of Javascript that compiles to plain Javascript +- [Jest](https://jestjs.io/) - A tool to test Javascript code +- [Hook Form](https://react-hook-form.com/) - A complementary tool to make easy the creation of React forms -### Diferenciais: - - Split bundle por rota (cada step ter um bundle separado para otimizar a performance); - - CSS in JS; - - React; +## Getting Started -### Fim: -Ao finalizar o teste, submeta um pull request para o repositório que nosso time será notificado. Se tiver alguma observação, escreva no pull request. +These instructions will guide you to run on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. + +### Prerequisites + +Before start, you need to install NodeJS (you can download files [here](https://nodejs.org/en/download/)). + +> **_NOTE:_** The use of newer versions of Node is recommended + +If you need specific instructions to install NodeJS for a specific Operational System, see more on the following links: + +Windows: https://treehouse.github.io/installation-guides/windows/node-windows.html + +Linux: https://tecadmin.net/install-latest-nodejs-npm-on-ubuntu/ + +MacOS: https://blog.teamtreehouse.com/install-node-js-npm-mac + +### Installing & Running + +Go to the project's folder and install project's dependencies using the following command in a terminal of your choice: + +```bash +$ cd YOURFOLDERNAME +$ npm install +``` + +Finally we can run the project using the following command: + +```bash +$ npm start +``` + +## Running the tests + +To test the code I'm using [Jest](https://jestjs.io/docs/en/getting-started) and [Testing Library](https://testing-library.com/) (for React components). + +I made the decision to place test files near from your source files, instead of in a "\_\_test\_\_" folder. For this reason, a "component.test.tsx" will be near, in the same folder of "component.tsx", for example. + +To run all of our unit tests you need to run the following command: + +```bash +$ npm test +``` + +> **_NOTE:_** The test will be running in watch mode. To run all test's files, press "a" key. + +> **_NOTE:_** In watch mode, you can filter tests by parameters such as file name, test name and other options... To know more about this options, see this section: https://jestjs.io/docs/en/watch-plugins#watch-menu-integration + +To get test coverage you need to run the following command: + +```bash +$ npm run coverage +``` + +> **_NOTE:_** By default, a coverage table will be generated in YOURFOLDERNAME/coverage/Icov-report/index.html + +## Deployment + +To create a production build, you must run the following command: + +```bash +$ npm run build +``` + +> **_NOTE:_** After builds the application, the script will create the project's documentation. + +To build the application and serve the built version you must run the following command: + +```bash +$ npm run serve +``` + +### NPM Scripts + +- `npm run build`: Creates a production build using react-app-rewired. +- `npm run coverage`: Generates a folder with test coverage. +- `npm start`: Runs the application for development. +- `npm serve`: Creates a production build and serve the application. +- `npm run docs`: Creates code's documentation using TypeDoc. +- `npm run lint`: Run lint tools to check the code. +- `npm run postbuild`: Generates application's documentation. +- `npm test`: Runs Jest testing. +- `npm run eject`: Encapsulates all of the npm modules it is using internally, so that your package.json will be very clean and simple without you having to worry about it. + +### Folder's structure (main files and folders): + +``` +project +├── build - Production build +├── coverage - Coverage files by Jest +├── docs - Code's documentation +├── node_modules +├── public +├── src +│ ├── redux - Redux modules and containers +│ ├── routes - React Router's definitions +│ ├── screens - App's pages +│ ├── shared - Shared code (eg.: helpers) +│ ├── ui +│ ├── assets - Image files +│ ├── components - React Components +│ ├── layouts - Layouts for App's pages + └── theme - Colors and fonts definitions +│ ├── App.tsx - App main component +│ ├── index.tsx - App entry point +│ ├── serviceWorker.js - Service Worker config +│ ├── setupTests.js - A cfg to run before tests +│ └── react-app-env.d.ts +├── package.json +├── paths.json - Typescript path aliases +├── README.md +├── tsconfig.json +└── typedoc.json +``` + +### Theming + +To improve the application's maitenance I made the decision to create a theme's file to be provided for . Thus, future developments related to design system become easier. From fa5db07936b2eec3fb8b9b92ffc8aa5551976e9c Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Mon, 18 Jan 2021 10:19:43 -0300 Subject: [PATCH 35/42] docs(readme): updates readme file --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index b11407874..407eaafbf 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,7 @@ ![Uses Javascript as programming language](https://sabe.io/classes/javascript/icon.png) [![Documentation built by TypeDoc](https://typedoc.org/images/logo-128.png 'Documentation built by TypeDoc')](https://typedoc.org) -| Build Status | Built By | We Love | -| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| ![BuildStatus](https://img.shields.io/badge/Build-Passing-brightgreen.svg 'Building Status') | ![BuiltBy](https://img.shields.io/badge/TypeScript-Lovers-black.svg 'img.shields.io') | ![ForTheBadge](https://img.shields.io/badge/Using-Badges-red.svg 'ForTheBadge') | +![BuildStatus](https://img.shields.io/badge/Build-Passing-brightgreen.svg 'Building Status') ![BuiltBy](https://img.shields.io/badge/TypeScript-Lovers-black.svg 'img.shields.io') ![ForTheBadge](https://img.shields.io/badge/Using-Badges-red.svg 'ForTheBadge') [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) From 1fa8f374534d7879615f0fc015112363f2770d12 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Thu, 21 Jan 2021 12:29:19 -0300 Subject: [PATCH 36/42] feat(payment-form): adds masks to some fields --- src/screens/Payment/Payment.tsx | 42 ++++++++++++------- .../Payment/components/Form/styled.tsx | 21 +++++++++- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/screens/Payment/Payment.tsx b/src/screens/Payment/Payment.tsx index af1c7dc83..050213501 100644 --- a/src/screens/Payment/Payment.tsx +++ b/src/screens/Payment/Payment.tsx @@ -1,5 +1,6 @@ import React from 'react' -import { useForm } from 'react-hook-form' +import { useForm, Controller } from 'react-hook-form' +import InputMask from 'react-input-mask' import { IPaymentPage } from './types' @@ -12,12 +13,18 @@ import * as SP from './styled' import * as S from './components/Form/styled' const Payment = ({ productData, onSubmit }: IPaymentPage) => { - const { errors, register, handleSubmit } = useForm() + const { control, errors, register, handleSubmit } = useForm() const submitForm = (data: any) => { onSubmit(data) } + // Registers controlled fields after component did mount + React.useEffect(() => { + register({ name: 'number', type: 'text' }, { required: true, pattern: /[0-9 ]{19}/ }) + register({ name: 'expirationDate', type: 'text' }, { required: true, pattern: /[0-9/]{5}/ }) + }, [register]) + return ( <> Cartão de crédito @@ -25,21 +32,19 @@ const Payment = ({ productData, onSubmit }: IPaymentPage) => { Número do cartão: - } + control={control} + mask="9999 9999 9999 9999" name="number" - error={errors.number} + hasErrors={!!errors.number} /> {errors.number?.type === 'required' && ( O número do cartão é obrigatório )} - {errors.number?.type === 'minLength' && ( + {errors.number?.type === 'pattern' && ( O número do cartão precisa ter 16 dígitos )} - {errors.number?.type === 'maxLength' && ( - O número do cartão precisa ter no máximo 16 dígitos - )} Nome do titular: @@ -54,13 +59,19 @@ const Payment = ({ productData, onSubmit }: IPaymentPage) => { Validade (mês/ano): - } + control={control} + mask="99/99" name="expirationDate" - error={errors.expirationDate} + hasErrors={!!errors.number} /> - {errors.expirationDate && A validade é obritória} + {errors.expirationDate?.type === 'required' && ( + A validade é obritória + )} + {errors.expirationDate?.type === 'pattern' && ( + A validade precisa ter o mês e o ano (MM/AA) + )} CVV: @@ -69,6 +80,7 @@ const Payment = ({ productData, onSubmit }: IPaymentPage) => { type="text" name="cvv" error={errors.cvv} + maxLength={4} /> {errors.cvv?.type === 'required' && ( O CVV é obrigatório diff --git a/src/screens/Payment/components/Form/styled.tsx b/src/screens/Payment/components/Form/styled.tsx index be92e104b..303597685 100644 --- a/src/screens/Payment/components/Form/styled.tsx +++ b/src/screens/Payment/components/Form/styled.tsx @@ -1,6 +1,25 @@ import styled from 'styled-components/macro' -export const Form = styled.form` +export const Form = styled.form<{error?: boolean}>` + .input { + background-color: ${({ theme }) => theme.colors.baseLight}; + border-radius: 0.3rem; + border: 0.1rem solid ${({ theme }) => theme.colors.secondary}; + box-shadow: inset 0 0.1rem 0.2rem 0 rgba(0, 0, 0, 0.2); + padding: 1rem; + color: ${({ theme }) => theme.colors.baseDark}; + transition: border 0.2s ease-out; + width: 100%; + &::placeholder { + color: ${({ theme }) => theme.colors.secondaryLight}; + } + &:focus { + border: 1px solid ${({ theme }) => theme.colors.focus}; + outline: 0; + } + ${({ theme, error }) => error && `border: 1px solid ${theme.colors.error};`} + } + @media (min-width: 768px) { display: flex; } From 861a72450153f22151363df7e8794d7580b462b6 Mon Sep 17 00:00:00 2001 From: Diego Martins Date: Thu, 21 Jan 2021 17:55:59 -0300 Subject: [PATCH 37/42] chore(containers): creates micro containers for performance improve --- .../CardDataContainer/CardDataContainer.tsx | 21 ++++++++++++ .../containers/CardDataContainer/index.tsx | 1 + .../containers/CardDataContainer/styled.tsx | 7 ++++ src/redux/containers/CartContainer/index.tsx | 1 - .../CartValuesContainer.tsx} | 17 ++++++---- .../containers/CartValuesContainer/index.tsx | 1 + .../PaymentContainer/PaymentContainer.tsx | 6 ++-- .../ProductListContainer.tsx | 31 ++++++++++++++++++ .../containers/ProductListContainer/index.tsx | 1 + .../SummaryContainer/SummaryContainer.tsx | 16 ---------- .../containers/SummaryContainer/index.tsx | 1 - src/routes/index.tsx | 8 ++--- src/screens/Cart/Cart.tsx | 27 ++++------------ src/screens/Cart/types.tsx | 12 ------- src/screens/Payment/Payment.tsx | 12 +++---- src/screens/Payment/types.tsx | 9 ------ src/screens/Summary/Summary.tsx | 32 +++++-------------- src/screens/Summary/styled.tsx | 6 ---- src/screens/Summary/types.ts | 13 -------- src/ui/components/Input/styled.tsx | 20 ------------ 20 files changed, 97 insertions(+), 145 deletions(-) create mode 100644 src/redux/containers/CardDataContainer/CardDataContainer.tsx create mode 100644 src/redux/containers/CardDataContainer/index.tsx create mode 100644 src/redux/containers/CardDataContainer/styled.tsx delete mode 100644 src/redux/containers/CartContainer/index.tsx rename src/redux/containers/{CartContainer/CartContainer.tsx => CartValuesContainer/CartValuesContainer.tsx} (54%) create mode 100644 src/redux/containers/CartValuesContainer/index.tsx create mode 100644 src/redux/containers/ProductListContainer/ProductListContainer.tsx create mode 100644 src/redux/containers/ProductListContainer/index.tsx delete mode 100644 src/redux/containers/SummaryContainer/SummaryContainer.tsx delete mode 100644 src/redux/containers/SummaryContainer/index.tsx delete mode 100644 src/screens/Cart/types.tsx delete mode 100644 src/screens/Summary/types.ts diff --git a/src/redux/containers/CardDataContainer/CardDataContainer.tsx b/src/redux/containers/CardDataContainer/CardDataContainer.tsx new file mode 100644 index 000000000..fb066ff50 --- /dev/null +++ b/src/redux/containers/CardDataContainer/CardDataContainer.tsx @@ -0,0 +1,21 @@ +import React from 'react' +import { useSelector } from 'react-redux' + +import { RootState } from '../../store' +import { hideCreditCardNumbers } from '../../../shared/helpers' + +import * as S from './styled' + +const CardDataContainer = () => { + const cardData = useSelector((state: RootState) => state.payment) + + return ( + <> + {hideCreditCardNumbers(cardData.number.toString())} + {cardData.holder} + {cardData.expirationDate} + + ) +} + +export default CardDataContainer diff --git a/src/redux/containers/CardDataContainer/index.tsx b/src/redux/containers/CardDataContainer/index.tsx new file mode 100644 index 000000000..e1e75c09d --- /dev/null +++ b/src/redux/containers/CardDataContainer/index.tsx @@ -0,0 +1 @@ +export { default } from './CardDataContainer' diff --git a/src/redux/containers/CardDataContainer/styled.tsx b/src/redux/containers/CardDataContainer/styled.tsx new file mode 100644 index 000000000..37b346164 --- /dev/null +++ b/src/redux/containers/CardDataContainer/styled.tsx @@ -0,0 +1,7 @@ +import styled from 'styled-components/macro' + +export const PaymentText = styled.p` + color: ${({ theme }) => theme.colors.baseDark}; + font-size: 1.4rem; + line-height: 1.7rem; +` diff --git a/src/redux/containers/CartContainer/index.tsx b/src/redux/containers/CartContainer/index.tsx deleted file mode 100644 index e52975de4..000000000 --- a/src/redux/containers/CartContainer/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from './CartContainer' diff --git a/src/redux/containers/CartContainer/CartContainer.tsx b/src/redux/containers/CartValuesContainer/CartValuesContainer.tsx similarity index 54% rename from src/redux/containers/CartContainer/CartContainer.tsx rename to src/redux/containers/CartValuesContainer/CartValuesContainer.tsx index 2658ae75d..addfa9183 100644 --- a/src/redux/containers/CartContainer/CartContainer.tsx +++ b/src/redux/containers/CartValuesContainer/CartValuesContainer.tsx @@ -4,19 +4,24 @@ import { useDispatch, useSelector } from 'react-redux' import { RootState } from '../../store' import { getProducts } from '../../modules/cart/actions' -import Cart from '../../../screens/Cart' +import ProductList from '../../../ui/components/ProductList' -const CartContainer = () => { +const CartValuesContainer = () => { const dispatch = useDispatch() const data = useSelector((state: RootState) => state.cart) - const { items, ...rest } = data - useEffect(() => { dispatch(getProducts()) }, [dispatch]) - return + return ( + + ) } -export default CartContainer +export default CartValuesContainer diff --git a/src/redux/containers/CartValuesContainer/index.tsx b/src/redux/containers/CartValuesContainer/index.tsx new file mode 100644 index 000000000..fe798cf0e --- /dev/null +++ b/src/redux/containers/CartValuesContainer/index.tsx @@ -0,0 +1 @@ +export { default } from './CartValuesContainer' diff --git a/src/redux/containers/PaymentContainer/PaymentContainer.tsx b/src/redux/containers/PaymentContainer/PaymentContainer.tsx index 2388e234b..2e979597f 100644 --- a/src/redux/containers/PaymentContainer/PaymentContainer.tsx +++ b/src/redux/containers/PaymentContainer/PaymentContainer.tsx @@ -1,23 +1,21 @@ import React from 'react' -import { useSelector, useDispatch } from 'react-redux' +import { useDispatch } from 'react-redux' import { useHistory } from 'react-router-dom' import { save } from '../../modules/payment/actions' import { IPayment } from '../../modules/payment/types/IState' -import { RootState } from '../../store' import Payment from '../../../screens/Payment' const PaymentContainer = () => { const dispatch = useDispatch() const history = useHistory() - const data = useSelector((state: RootState) => state.cart) const handleSubmit = (formData: IPayment) => { dispatch(save(formData)) history.push('/summary') } - return + return } export default PaymentContainer diff --git a/src/redux/containers/ProductListContainer/ProductListContainer.tsx b/src/redux/containers/ProductListContainer/ProductListContainer.tsx new file mode 100644 index 000000000..ffe4239ed --- /dev/null +++ b/src/redux/containers/ProductListContainer/ProductListContainer.tsx @@ -0,0 +1,31 @@ +import React, { useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' + +import { RootState } from '../../store' +import { getProducts } from '../../modules/cart/actions' + +import ProductCard from '../../../ui/components/ProductCard' + +const ProductListContainer = () => { + const dispatch = useDispatch() + const items = useSelector((state: RootState) => state.cart.items) + + useEffect(() => { + dispatch(getProducts()) + }, [dispatch]) + + return ( +
    + {items.map((item) => ( + + ))} +
    + ) +} + +export default ProductListContainer diff --git a/src/redux/containers/ProductListContainer/index.tsx b/src/redux/containers/ProductListContainer/index.tsx new file mode 100644 index 000000000..44ed38269 --- /dev/null +++ b/src/redux/containers/ProductListContainer/index.tsx @@ -0,0 +1 @@ +export { default } from './ProductListContainer' diff --git a/src/redux/containers/SummaryContainer/SummaryContainer.tsx b/src/redux/containers/SummaryContainer/SummaryContainer.tsx deleted file mode 100644 index be702c568..000000000 --- a/src/redux/containers/SummaryContainer/SummaryContainer.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' - -import { RootState } from '../../store' -import Summary from '../../../screens/Summary' - -const SummaryContainer = () => { - const data = useSelector((state: RootState) => state.cart) - const cardData = useSelector((state: RootState) => state.payment) - - const { items, ...props } = data - - return -} - -export default SummaryContainer diff --git a/src/redux/containers/SummaryContainer/index.tsx b/src/redux/containers/SummaryContainer/index.tsx deleted file mode 100644 index 115f0eb7b..000000000 --- a/src/redux/containers/SummaryContainer/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from './SummaryContainer' diff --git a/src/routes/index.tsx b/src/routes/index.tsx index e03774a82..ee6914ce8 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -2,17 +2,17 @@ import React, { Suspense } from 'react' import { Route, Switch } from 'react-router-dom' // Dynamic imports for performace improvement -const CartContainer = React.lazy(() => import('../redux/containers/CartContainer')) +const CartScreen = React.lazy(() => import('../screens/Cart')) const PaymentContainer = React.lazy(() => import('../redux/containers/PaymentContainer')) -const SummaryContainer = React.lazy(() => import('../redux/containers/SummaryContainer')) +const SummaryScreen = React.lazy(() => import('../screens/Summary')) export default () => { return ( Carregando...}> - + - + <>Not Found! diff --git a/src/screens/Cart/Cart.tsx b/src/screens/Cart/Cart.tsx index a86ddec4b..911afdf03 100644 --- a/src/screens/Cart/Cart.tsx +++ b/src/screens/Cart/Cart.tsx @@ -1,17 +1,16 @@ import React from 'react' import { useHistory } from 'react-router-dom' -import { ICartScreen } from './types' - import Button from '../../ui/components/Button' import ContentBox from '../../ui/components/ContentBox' -import ProductCard from '../../ui/components/ProductCard' -import ProductList from '../../ui/components/ProductList' import Title from '../../ui/components/Title' +import ProductListContainer from '../../redux/containers/ProductListContainer' +import CartValuesContainer from '../../redux/containers/CartValuesContainer' + import * as S from './styled' -const Cart = ({ products, productData }: ICartScreen) => { +const Cart = () => { const history = useHistory() const handleClick = () => { @@ -24,25 +23,11 @@ const Cart = ({ products, productData }: ICartScreen) => { - {products.map((product) => { - return ( - - ) - })} + - + diff --git a/src/screens/Cart/types.tsx b/src/screens/Cart/types.tsx deleted file mode 100644 index f69bf87b8..000000000 --- a/src/screens/Cart/types.tsx +++ /dev/null @@ -1,12 +0,0 @@ -interface IProductDataPage { - discount: number; - id: string; - shippingTotal: number; - subTotal: number; - total: number; -} - -export interface ICartScreen { - products: any[]; - productData: IProductDataPage; -} diff --git a/src/screens/Payment/Payment.tsx b/src/screens/Payment/Payment.tsx index 050213501..f549b1e1e 100644 --- a/src/screens/Payment/Payment.tsx +++ b/src/screens/Payment/Payment.tsx @@ -6,13 +6,14 @@ import { IPaymentPage } from './types' import Button from '../../ui/components/Button' import { Input } from '../../ui/components/Input/styled' -import ProductList from '../../ui/components/ProductList' import Title from '../../ui/components/Title' +import CartValuesContainer from '../../redux/containers/CartValuesContainer' + import * as SP from './styled' import * as S from './components/Form/styled' -const Payment = ({ productData, onSubmit }: IPaymentPage) => { +const Payment = ({ onSubmit }: IPaymentPage) => { const { control, errors, register, handleSubmit } = useForm() const submitForm = (data: any) => { @@ -95,12 +96,7 @@ const Payment = ({ productData, onSubmit }: IPaymentPage) => { - +