From ae7d63db3587469e2577a83886da1e86ef572a9b Mon Sep 17 00:00:00 2001 From: Jeroen Vijgen Date: Wed, 19 Feb 2020 00:48:30 +0100 Subject: [PATCH] Finished login component Did the following: - Extract front-end from component; - Added localStorage for remembering API details and access key; - Added routing to login component; Todo: - Remove getApi*FromDTO; - Create ProtectedRoute to prevent unauthorized entry to /dashboard; --- README.md | 25 ++++-- package.json | 6 ++ public/index.html | 1 + src/Components/AppComponent/App.tsx | 12 +-- src/Components/LoginComponent/Login.tsx | 87 +++++++++++-------- src/DTOs/api.dto.ts | 29 +++++++ src/Views/LoginView.tsx | 44 ++++++++++ src/index.tsx | 6 +- yarn.lock | 106 +++++++++++++++++++++--- 9 files changed, 254 insertions(+), 62 deletions(-) create mode 100644 src/DTOs/api.dto.ts create mode 100644 src/Views/LoginView.tsx diff --git a/README.md b/README.md index 8b688a5..284d4d2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Netlify Status](https://api.netlify.com/api/v1/badges/9527e4dc-1bb2-40ef-b6eb-8515b91579bf/deploy-status)](https://app.netlify.com/sites/upbeat-agnesi-aad3fb/deploys) +[![Netlify Status](https://api.netlify.com/api/v1/badges/9527e4dc-1bb2-40ef-b6eb-8515b91579bf/deploy-status)](https://upbeat-agnesi-aad3fb.netlify.com/) ## Available Scripts @@ -9,15 +9,26 @@ In the project directory, you can run: Runs the app in the development mode.
Open [http://localhost:3000](http://localhost:3000) to view it in the browser. -The page will reload if you make edits.
-You will also see any lint errors in the console. +The page will reload if you make edits, you will also see any lint errors in the console. ### `yarn build` Builds the app for production to the `build` folder.
It correctly bundles React in production mode and optimizes the build for the best performance. -The build is minified and the filenames include the hashes.
-Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. +The build is minified and the filenames include the hashes. Your app is ready to be deployed! + +### Requirement list +- [X] Create React web app +- [ ] It should have fields for giving start date, end date and access token as arguments to the app. +- [ ] App should make HTTP GET request to the API to fetch chat counts between those two given dates. +- [ ] The dashboard should render three values from the API. +- [ ] The dashboard should render a Paginated List. + +### Software used +\# | Software | Reason +--- | --- | --- +1. | React | Requirement. +2. | React Router | To route between different components, a react router is used. +3. | TypeScript | To make use of the strong types, I made use of TypeScript +4. | Bootstrap | To structure the front-end, bootstrap is used. diff --git a/package.json b/package.json index e54e32f..5f4d821 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "version": "0.1.0", "private": true, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.27", + "@fortawesome/free-solid-svg-icons": "^5.12.1", + "@fortawesome/react-fontawesome": "^0.1.8", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.4.0", "@testing-library/user-event": "^7.1.2", @@ -13,13 +16,16 @@ "@types/prop-types": "^15.7.3", "@types/react": "^16.9.19", "@types/react-dom": "^16.9.5", + "@types/react-fontawesome": "^1.6.4", "@types/react-router-dom": "^5.1.3", + "axios": "^0.19.2", "bootstrap": "^4.4.1", "jquery": "^3.4.1", "popper.js": "^1.16.1", "react": "^16.12.0", "react-bootstrap": "^1.0.0-beta.16", "react-dom": "^16.12.0", + "react-fontawesome": "^1.7.1", "react-router-dom": "^5.1.2", "react-scripts": "3.3.1", "typescript": "~3.7.2" diff --git a/public/index.html b/public/index.html index 22ef106..c4db272 100644 --- a/public/index.html +++ b/public/index.html @@ -8,6 +8,7 @@ + Demo App - Jeroen Vijgen diff --git a/src/Components/AppComponent/App.tsx b/src/Components/AppComponent/App.tsx index b143361..3ede347 100644 --- a/src/Components/AppComponent/App.tsx +++ b/src/Components/AppComponent/App.tsx @@ -6,18 +6,14 @@ import Login from '../LoginComponent/Login'; import Dashboard from '../DashboardComponent/Dashboard'; export default class App extends React.Component { - componentDidMount() { - if((localStorage.getItem("APILink") !== undefined || localStorage.getItem("APILink") !== '') && - (localStorage.getItem("AccessToken") !== undefined || localStorage.getItem("AccessToken") !== '')) { - return - }; - } render() { return ( - - + + ); diff --git a/src/Components/LoginComponent/Login.tsx b/src/Components/LoginComponent/Login.tsx index e04a10e..8a63bda 100644 --- a/src/Components/LoginComponent/Login.tsx +++ b/src/Components/LoginComponent/Login.tsx @@ -1,44 +1,61 @@ import React from 'react'; -import { Card, Button, InputGroup, FormControl } from 'react-bootstrap'; +import LoginView from '../../Views/LoginView'; +import API_DTO from '../../DTOs/api.dto'; + +export default class Login extends React.Component<{ + history: any +}, { + dto: any +}> { -export default class Login extends React.Component { // eslint-disable-next-line constructor(props: any) { super(props); + this.persistApiLink = this.persistApiLink.bind(this); + this.persistAccessToken = this.persistAccessToken.bind(this); + this.verifyLogin = this.verifyLogin.bind(this); + this.getApiLinkFromDTO = this.getApiLinkFromDTO.bind(this); + this.getApiTokenFromDTO = this.getApiTokenFromDTO.bind(this); } - render () { - return ( -
- - - Please login to see the dashboard. - - Please fill in the following items:

- - - API url - - - - - - API token - - - -
- -
- Your last login was X-X-X @ XX:XX -
-
- ); + componentDidMount() { + const DTO = new API_DTO(); + DTO.setApiLink(localStorage.getItem("APILink")); + DTO.setApiToken(localStorage.getItem("AccessToken")); + this.setState({ dto: DTO }); + } + + verifyLogin() { + console.log("API Link: " + this.state.dto.getApiLink()); + console.log("API Token: " + this.state.dto.getApiToken()); + localStorage.setItem("lastLoggedIn", new Date().toUTCString()); + // eslint-disable-next-line + this.props.history.push('/dashboard'); + }; + + getApiLinkFromDTO(): string { + return this.state.dto.getApiLink(); + } + + getApiTokenFromDTO(): string { + return this.state.dto.getApiToken(); + } + + persistApiLink(e: any): void { + this.state.dto.setApiLink(e.target.value); + } + + persistAccessToken(e: any): void { + this.state.dto.setApiToken(e.target.value); + } + + render() { + return (); } } diff --git a/src/DTOs/api.dto.ts b/src/DTOs/api.dto.ts new file mode 100644 index 0000000..df15a8e --- /dev/null +++ b/src/DTOs/api.dto.ts @@ -0,0 +1,29 @@ +export default class API_DTO { + API_Link: string; + API_Token: string; + + constructor() { + this.API_Link = ""; + this.API_Token = ""; + } + + setApiLink(_api_link: string | null): void { + if (_api_link === null) _api_link = ""; + this.API_Link = _api_link; + localStorage.setItem("APILink", this.API_Link); + } + + setApiToken(_api_token: string | null): void { + if (_api_token === null) _api_token = ""; + this.API_Token = _api_token; + localStorage.setItem("AccessToken", this.API_Token); + } + + getApiLink() { + return this.API_Link; + } + + getApiToken() { + return this.API_Token; + } +} diff --git a/src/Views/LoginView.tsx b/src/Views/LoginView.tsx new file mode 100644 index 0000000..e56dfd7 --- /dev/null +++ b/src/Views/LoginView.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Card, Button, FormControl } from 'react-bootstrap'; + +export default class LoginView extends React.Component<{ + persistApiLink: any, + persistAccessToken: any, + verifyLogin: any, + getApiLinkFromDTO: any, + getApiTokenFromDTO: any +}> { + render () { + return ( +
+ + + Please login to see the dashboard. + + Please fill in the following items:

+ this.props.persistApiLink(e)} + /> + this.props.persistAccessToken(e)} + /> +
+ +
+ { localStorage.getItem("lastLoggedIn") !== null && + Your last login was { localStorage.getItem("lastLoggedIn") } + } +
+
+ ); + } +} diff --git a/src/index.tsx b/src/index.tsx index e6900ee..8d25800 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,4 +4,8 @@ import './index.css'; import { BrowserRouter as Router } from 'react-router-dom'; import App from './Components/AppComponent/App'; -ReactDOM.render(, document.getElementById('App')); +ReactDOM.render( + + + , + document.getElementById('App')); diff --git a/yarn.lock b/yarn.lock index ea4589a..8284eff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1259,6 +1259,43 @@ __metadata: languageName: node linkType: hard +"@fortawesome/fontawesome-common-types@npm:^0.2.27": + version: 0.2.27 + resolution: "@fortawesome/fontawesome-common-types@npm:0.2.27" + checksum: 2eb1f4adbe31a6533b3d3fdd20e37fd7c9e61cf29d144fcb3d225a5d16b57c16fa2a72dd6862382ea7b184aca85fd65b66781de1570a4b911eb4e9b39a74f9de + languageName: node + linkType: hard + +"@fortawesome/fontawesome-svg-core@npm:^1.2.27": + version: 1.2.27 + resolution: "@fortawesome/fontawesome-svg-core@npm:1.2.27" + dependencies: + "@fortawesome/fontawesome-common-types": ^0.2.27 + checksum: 000027a6d1a33a152e4799bd0c3c1507205697f86ddc0961f69f83c8561bcf93327353980efc8794f1dd7e7a1a50a36f710198d9f7e03855d9832fd5df6958f9 + languageName: node + linkType: hard + +"@fortawesome/free-solid-svg-icons@npm:^5.12.1": + version: 5.12.1 + resolution: "@fortawesome/free-solid-svg-icons@npm:5.12.1" + dependencies: + "@fortawesome/fontawesome-common-types": ^0.2.27 + checksum: f966c13f826dfe57b2e2a212d5968ce7d4f9670ffb26d675259a2f0d19dfa777d269e6e4506e16d00da34943ae8526eb1fb2b889467f309ca3dbf371a57aec94 + languageName: node + linkType: hard + +"@fortawesome/react-fontawesome@npm:^0.1.8": + version: 0.1.8 + resolution: "@fortawesome/react-fontawesome@npm:0.1.8" + dependencies: + prop-types: ^15.5.10 + peerDependencies: + "@fortawesome/fontawesome-svg-core": ^1.2.20 + react: 16.x + checksum: c13777b29061dcca6d39ee2f30d820e1221e4293ce87d336197bb6e0c19adbd4bbb2963be03c71f8e093a36e813b8acf2079849e62a7450f1d871a8678ecd5d6 + languageName: node + linkType: hard + "@hapi/address@npm:2.x.x": version: 2.1.4 resolution: "@hapi/address@npm:2.1.4" @@ -1769,7 +1806,7 @@ __metadata: languageName: node linkType: hard -"@types/history@npm:*": +"@types/history@npm:*, @types/history@npm:^4.7.5": version: 4.7.5 resolution: "@types/history@npm:4.7.5" checksum: 16a19c2b1aee6d0ff055940e4ca29db26aa8b0c8ff69161e70dec2151abb4fe3d5be846577c696a5d870a281e362669707ff43323d435a3d84811e6a238348f1 @@ -1876,6 +1913,15 @@ __metadata: languageName: node linkType: hard +"@types/react-fontawesome@npm:^1.6.4": + version: 1.6.4 + resolution: "@types/react-fontawesome@npm:1.6.4" + dependencies: + "@types/react": "*" + checksum: ccf31aafa5c595ae9736ce5a9942c141a75c9ad11f8977c62ed4b06d7e1961ee8f226257fe867bd6df72ee62eed5bf8552250ada06d2fe38f8988a4a775fc0f6 + languageName: node + linkType: hard + "@types/react-router-dom@npm:^5.1.3": version: 5.1.3 resolution: "@types/react-router-dom@npm:5.1.3" @@ -2817,6 +2863,15 @@ __metadata: languageName: node linkType: hard +"axios@npm:^0.19.2": + version: 0.19.2 + resolution: "axios@npm:0.19.2" + dependencies: + follow-redirects: 1.5.10 + checksum: 28c860c203dc1e545a899eea83c95c51bb7de4a21ffef4781de11331207371c9cb1a680bcc7122dda936cd5e51f83f7b0a0eccc50801229b5e4914c8f2dd8907 + languageName: node + linkType: hard + "axobject-query@npm:^2.0.2": version: 2.1.1 resolution: "axobject-query@npm:2.1.1" @@ -4530,6 +4585,15 @@ __metadata: languageName: node linkType: hard +"debug@npm:=3.1.0": + version: 3.1.0 + resolution: "debug@npm:3.1.0" + dependencies: + ms: 2.0.0 + checksum: 31ac62be845b3e2bc49ea566665749e58030123e6d0b2960cb7c730037dc825f46d012e3f63f43fc9d60de3c52edbce705d22cb7e571e2822c81bc5d5ae09da9 + languageName: node + linkType: hard + "debug@npm:^3.0.0, debug@npm:^3.1.1, debug@npm:^3.2.5, debug@npm:^3.2.6": version: 3.2.6 resolution: "debug@npm:3.2.6" @@ -4670,9 +4734,13 @@ __metadata: version: 0.0.0-use.local resolution: "demo-app@workspace:." dependencies: + "@fortawesome/fontawesome-svg-core": ^1.2.27 + "@fortawesome/free-solid-svg-icons": ^5.12.1 + "@fortawesome/react-fontawesome": ^0.1.8 "@testing-library/jest-dom": ^4.2.4 "@testing-library/react": ^9.4.0 "@testing-library/user-event": ^7.1.2 + "@types/history": ^4.7.5 "@types/istanbul-lib-coverage": ^2.0.1 "@types/jest": ^24.9.1 "@types/mocha": ^7.0.1 @@ -4680,23 +4748,26 @@ __metadata: "@types/prop-types": ^15.7.3 "@types/react": ^16.9.19 "@types/react-dom": ^16.9.5 + "@types/react-fontawesome": ^1.6.4 "@types/react-router-dom": ^5.1.3 "@typescript-eslint/eslint-plugin": ^2.19.2 "@typescript-eslint/parser": ^2.19.2 + axios: ^0.19.2 bootstrap: ^4.4.1 eslint: ^6.8.0 eslint-config-prettier: ^6.10.0 eslint-plugin-prettier: ^3.1.2 eslint-plugin-react: ^7.18.3 + history: ^4.10.1 jquery: ^3.4.1 popper.js: ^1.16.1 prettier: ^1.19.1 react: ^16.12.0 react-bootstrap: ^1.0.0-beta.16 react-dom: ^16.12.0 + react-fontawesome: ^1.7.1 react-router-dom: ^5.1.2 react-scripts: 3.3.1 - store: ^2.0.12 typescript: ~3.7.2 languageName: unknown linkType: soft @@ -5994,6 +6065,15 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:1.5.10": + version: 1.5.10 + resolution: "follow-redirects@npm:1.5.10" + dependencies: + debug: =3.1.0 + checksum: 23c6333d60b73b899f9495d67b3dd5b2b99c26cd76278d03def3c39c715f0eed53cbfe1eb1e342a7281041961fe2c69c03daa97279df66a552ab6fb3d22059fc + languageName: node + linkType: hard + "follow-redirects@npm:^1.0.0": version: 1.10.0 resolution: "follow-redirects@npm:1.10.0" @@ -6582,7 +6662,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"history@npm:^4.9.0": +"history@npm:^4.10.1, history@npm:^4.9.0": version: 4.10.1 resolution: "history@npm:4.10.1" dependencies: @@ -11118,7 +11198,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"prop-types@npm:^15.6.2, prop-types@npm:^15.7.2": +"prop-types@npm:^15.5.10, prop-types@npm:^15.5.6, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2": version: 15.7.2 resolution: "prop-types@npm:15.7.2" dependencies: @@ -11425,6 +11505,17 @@ fsevents@^1.2.7: languageName: node linkType: hard +"react-fontawesome@npm:^1.7.1": + version: 1.7.1 + resolution: "react-fontawesome@npm:1.7.1" + dependencies: + prop-types: ^15.5.6 + peerDependencies: + react: ">=0.12.0" + checksum: 9cedcd84255cb13196e7db70d49e928dc26982d059ae72396171f132933a38f50c9454b93f1f127edfd4a7fc7a4d01611f82dd73e86110724884ae9e9c064596 + languageName: node + linkType: hard + "react-is@npm:^16.3.2, react-is@npm:^16.6.0, react-is@npm:^16.7.0, react-is@npm:^16.8.1, react-is@npm:^16.8.4": version: 16.12.0 resolution: "react-is@npm:16.12.0" @@ -12833,13 +12924,6 @@ resolve@1.1.7: languageName: node linkType: hard -"store@npm:^2.0.12": - version: 2.0.12 - resolution: "store@npm:2.0.12" - checksum: 101053f2842b6a186d8556ef5a176feb4494cfe4a182b454c5c7150362c67344abafd8e012c7769ea51745cdff38447c3e0c9d5ef36740bcc65e174adb605309 - languageName: node - linkType: hard - "stream-browserify@npm:^2.0.1": version: 2.0.2 resolution: "stream-browserify@npm:2.0.2"