├── src ├── react-app-env.d.ts ├── App.tsx ├── index.tsx ├── setupTests.ts ├── App.test.tsx └── assets │ └── icons │ ├── have-a-baby.svg │ ├── throw-a-wedding-party.svg │ ├── take-a-vacation.svg │ ├── buy-a-car.svg │ ├── go-to-college.svg │ ├── build-an-emergency-fund.svg │ └── buy-a-house.svg ├── .prettierrc ├── .gitignore ├── public ├── favicon.png └── index.html ├── mockups ├── saving-goal-plan-desk.png └── saving-goal-plan-mobile.png ├── .editorconfig ├── tsconfig.json ├── .circleci └── config.yml ├── package.json ├── .eslintrc └── README.md /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "tabWidth": 2 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .cache 4 | coverage 5 | *.DS_Store 6 | .idea/ 7 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginFinancial/frontend-take-home-assignment/HEAD/public/favicon.png -------------------------------------------------------------------------------- /mockups/saving-goal-plan-desk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginFinancial/frontend-take-home-assignment/HEAD/mockups/saving-goal-plan-desk.png -------------------------------------------------------------------------------- /mockups/saving-goal-plan-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginFinancial/frontend-take-home-assignment/HEAD/mockups/saving-goal-plan-mobile.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function App(): JSX.Element { 4 | return
Welcome to the Origin THA
; 5 | } 6 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { App } from './App'; 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires,@typescript-eslint/naming-convention */ 2 | 3 | import '@testing-library/jest-dom'; 4 | const Adapter = require('@wojtekmaj/enzyme-adapter-react-17'); 5 | const Enzyme = require('enzyme'); 6 | 7 | Enzyme.configure({ adapter: new Adapter() }); 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | import { App } from './App'; 3 | import { render } from '@testing-library/react'; 4 | 5 | describe('App', () => { 6 | describe('using enzyme', () => { 7 | it('returns the text', () => { 8 | const component = shallow(); 9 | 10 | expect(component.find('[data-testid="greetings-container"]').text()).toBe( 11 | 'Welcome to the Origin THA' 12 | ); 13 | }); 14 | }); 15 | 16 | describe('using testing library', () => { 17 | it('returns the text', () => { 18 | const component = render(); 19 | 20 | expect(component.getByTestId('greetings-container').innerHTML).toBe( 21 | 'Welcome to the Origin THA' 22 | ); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | jobs: 3 | test_and_lint: 4 | docker: 5 | - image: circleci/node:latest-browsers 6 | working_directory: /home/circleci/frontend-take-home-assignment 7 | steps: 8 | - checkout 9 | - restore_cache: 10 | keys: 11 | - frontend-take-home-assignment-deps-{{ .Branch }}-{{ checksum "package.json" }} 12 | - frontend-take-home-assignment-deps-develop-{{ checksum "package.json" }} 13 | - frontend-take-home-assignment-deps-develop 14 | - run: 15 | name: Install dependencies 16 | command: | 17 | npm install 18 | - run: 19 | name: Linter 20 | command: | 21 | npm run lint 22 | - run: 23 | name: Run tests 24 | command: | 25 | npm run test 26 | 27 | workflows: 28 | version: 2 29 | pipeline: 30 | jobs: 31 | - test_and_lint 32 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | Saving Goals - Origin 13 | 14 | 15 | 16 |
17 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend-take-home-assignment", 3 | "version": "0.1.0", 4 | "dependencies": { 5 | "@testing-library/jest-dom": "^5.11.4", 6 | "@testing-library/react": "^11.1.0", 7 | "@testing-library/user-event": "^12.1.10", 8 | "@types/jest": "^26.0.15", 9 | "@types/node": "^12.0.0", 10 | "@types/react": "^17.0.0", 11 | "@types/react-dom": "^17.0.0", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "react-scripts": "4.0.3", 15 | "styled-components": "^5.3.0", 16 | "typescript": "^4.1.2", 17 | "web-vitals": "^1.0.1" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject", 24 | "format": "prettier --write src/**/*.{ts,tsx}", 25 | "lint": "eslint './src/**/*.{ts,tsx}' --max-warnings=0" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "react-app", 30 | "react-app/jest" 31 | ] 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | }, 45 | "devDependencies": { 46 | "@types/enzyme": "^3.10.8", 47 | "@typescript-eslint/eslint-plugin": "^4.27.0", 48 | "@typescript-eslint/parser": "^4.27.0", 49 | "@wojtekmaj/enzyme-adapter-react-17": "^0.6.2", 50 | "enzyme": "^3.11.0", 51 | "eslint": "^7.28.0", 52 | "eslint-config-prettier": "^8.3.0", 53 | "eslint-plugin-prettier": "^3.4.0", 54 | "eslint-plugin-react": "^7.24.0", 55 | "eslint-plugin-react-hooks": "^4.2.0", 56 | "prettier": "^2.3.1" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/assets/icons/have-a-baby.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "react-app", 4 | "eslint:recommended", 5 | "plugin:react/recommended", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:prettier/recommended" 8 | ], 9 | "parser": "@typescript-eslint/parser", 10 | "rules": { 11 | "react/prop-types": [0], 12 | "react/react-in-jsx-scope": [0], 13 | "react/jsx-filename-extension": [1, { "extensions": [".ts", ".tsx"] }], 14 | "import/no-named-as-default": 0, 15 | "react-hooks/rules-of-hooks": "error", 16 | "react-hooks/exhaustive-deps": "warn", 17 | "prettier/prettier": ["error", { "singleQuote": true }], 18 | "@typescript-eslint/explicit-member-accessibility": 0, 19 | "@typescript-eslint/explicit-function-return-type": 0, 20 | "@typescript-eslint/no-explicit-any": 1, 21 | "@typescript-eslint/naming-convention": [ 22 | "error", 23 | { 24 | "selector": "default", 25 | "format": ["camelCase"], 26 | "leadingUnderscore": "allow", 27 | "trailingUnderscore": "allow" 28 | }, 29 | { 30 | "selector": "variable", 31 | "format": ["camelCase", "UPPER_CASE"], 32 | "leadingUnderscore": "allow", 33 | "trailingUnderscore": "allow" 34 | }, 35 | { 36 | "selector": "typeLike", 37 | "format": ["PascalCase"] 38 | }, 39 | { 40 | "selector": "function", 41 | "format": ["PascalCase", "camelCase"] 42 | } 43 | ] 44 | }, 45 | "env": { 46 | "browser": true, 47 | "node": true, 48 | "es6": true, 49 | "jest": true 50 | }, 51 | "globals": { 52 | "document": false 53 | }, 54 | "plugins": ["react-hooks", "@typescript-eslint", "prettier"], 55 | "settings": { 56 | "import/resolver": { 57 | "node": { 58 | "paths": ["src"], 59 | "extensions": [".ts", ".tsx"] 60 | } 61 | }, 62 | "react": { 63 | "pragma": "React", 64 | "version": "detect" 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/assets/icons/throw-a-wedding-party.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Origin Frontend Take-Home Assignment 2 | 3 | **IMPORTANT**: you can choose any technology stack to implement this assignment. Using our stack is not a requirement in the selection process - we will consider exclusively the quality of your project (technology and product-wise) to evaluate your work. We've added a project structure in this repository (a buildwith react, redux, jest, styled-components and typescript) to save you time if you want to use it. If you prefer another stack, feel free to use it. 4 | 5 | Origin is a platform that helps our customers' employees put their financial lives on track. 6 | 7 | One key to financial well-being is planning & saving for your goals. Users can have many saving goals (e.g. go to college or throw a wedding party) and it is our job to help them accomplish it. 8 | 9 | You will build a piece of our savings feature by creating the savings plan simulation screen. 10 | 11 | ### The Saving Goal Plan Simulation Screen 12 | 13 | ![Saving Goal Plan Mockup Desktop](https://github.com/OriginFinancial/frontend-take-home-assignment/blob/master/mockups/saving-goal-plan-desk.png) 14 | 15 | You will build a screen where the user will simulate saving towards the "Buy a house" savings goal. 16 | In it, the users choose (i) the value they want to save and (ii) the date they plan to reach the goal. 17 | 18 | When the users change the value of any of the inputs, the monthly deposit value is calculated and displayed to them. 19 | 20 | # Development Instructions 21 | 22 | ### Evaluation 23 | Be aware that Origin will mainly take into consideration the following evaluation criteria: 24 | * How close your page is to the mockups, both on mobile & desktop; 25 | * How clean and organized your code is; 26 | * How good your automated tests are, i.e.: qualitative over quantitative (in case of usage of this base project, feel free to choose between jest or testing library); 27 | * If you implemented the business rules correctly. 28 | 29 | ### Assets 30 | You can find the layout mockups here on our Figma project: 31 | [Layout mockups](https://www.figma.com/file/Axdg0WSJURcxp8Arq3gg9x/Take-Home-Assignment-v2) 32 | 33 | Once you have opened the link you must sign up and log in so you can have access to the colors, fonts, margins and assets information. 34 | 35 | #### Money input 36 | 37 | The money input component should: 38 | 39 | - Allow only numbers 40 | - Display the value formatted as money (e.g 3500.45 should be 3,500.45) 41 | - We recommend you name this input as "amount" 42 | 43 | #### Date input 44 | 45 | The date input component should: 46 | 47 | - Allow only future months 48 | - When clicking on the arrow buttons it should go up and down month by month 49 | - On focused, the users should be able to move the months by typing the Left and Right arrow key on the keyboard 50 | - We recommend you name this input as "reachDate" 51 | 52 | #### Confirm button 53 | 54 | You don't need to add any action on the confirmation button 55 | 56 | # Delivery Instructions 57 | 58 | Don't create a fork, send us the link to your repository and make sure to make it public. 59 | 60 | # Usage 61 | 62 | This project requires the latest LTS version of NodeJS and you may need to install the yarn as global dependency 63 | ```bash 64 | npm install -g yarn 65 | ``` 66 | 67 | After you have cloned this repo and install the yarn, install the dependencies with: 68 | 69 | ``` 70 | yarn install 71 | ``` 72 | 73 | You can then start the application running: 74 | 75 | ``` 76 | yarn start 77 | ``` 78 | 79 | That's it. Just Access `http://localhost:3000` in your browser. 80 | 81 | ### Linting and Format 82 | 83 | ``` 84 | yarn lint 85 | yarn format 86 | ``` 87 | 88 | ### Testing 89 | 90 | ``` 91 | yarn test 92 | ``` 93 | -------------------------------------------------------------------------------- /src/assets/icons/take-a-vacation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/assets/icons/buy-a-car.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/assets/icons/go-to-college.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/assets/icons/build-an-emergency-fund.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/icons/buy-a-house.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------