From 025ea41a662b59605fcc8ffde30b9056eba7aee2 Mon Sep 17 00:00:00 2001 From: Yuveer Maharaj Date: Thu, 14 Nov 2019 12:06:43 +0200 Subject: [PATCH 1/2] Onboarding finished --- README.md | 146 ++++++++++++++++++++++++-------------------------- app.css | 97 +++++++++++++++++++++++++++++++++ app.ts | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 33 +++++++++--- package.json | 22 ++++++++ tsconfig.json | 33 +++++------- 6 files changed, 370 insertions(+), 104 deletions(-) create mode 100644 app.css create mode 100644 app.ts create mode 100644 package.json diff --git a/README.md b/README.md index 8e0e19db..6270cf92 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,90 @@ -# Onboarding project for JavaScript +# Onboarding project for JavaScript -This is a JavaScript project for all new developers to complete before venturing into our web frontend codebase. +This is a JavaScript project for all new developers to complete before venturing into our web frontend codebase. Once you complete this project, and have been through code review, you will have a much better understanding of JavaScript -and it's superset, TypeScript, that we use for development. +and it's superset, TypeScript, that we use for development. -## Technologies you will encounter +## Technologies you will encounter -1. HTML -1. CSS -1. JavaScript -1. TypeScript +1. HTML +1. CSS +1. JavaScript +1. TypeScript 1. Node Package Manager (npm) -1. JQuery +1. JQuery -## Brief +## Brief -You task is to build a grid/table in TypeScript that fetches data from a server and displays it. Here are the requirements: +You task is to build a grid/table in TypeScript that fetches data from a server and displays it. Here are the requirements: -1. The grid should cover the entire screen. The browser scrollbar should not appear. -1. The columns widths should be distributed evenly. -1. Display column headings based on the data you get back from the web service. -1. The grid should have controls to navigate through data. You can choose a suitable way of doing this. -1. Display the first page of data when you open up page for first time. -1. Don't use any third-party libraries other than JQuery (already included in `third_party` directory). +1. The grid should cover the entire screen. The browser scrollbar should not appear. +1. The columns widths should be distributed evenly. +1. Display column headings based on the data you get back from the web service. +1. The grid should have controls to navigate through data. You can choose a suitable way of doing this. +1. Display the first page of data when you open up page for first time. +1. Don't use any third-party libraries other than JQuery (already included in `third_party` directory). -## Web Service API +## Web Service API -The web service you will retrieve data from is a little Golang REST service that is packaged with this repository. It runs on port 2050. +The web service you will retrieve data from is a little Golang REST service that is packaged with this repository. It runs on port 2050. Here are the relevant API calls: 1. Get total number of records - Path: `HTTP GET /recordCount` - Response: Integer number in body of response - Example response: - `350` + Path: `HTTP GET /recordCount` + Response: Integer number in body of response + Example response: + `350` 1. Get column names - Path: `HTTP GET /columns` - Response: JSON in body of response - Example response: - `[ - "ID", - "City", - "Population" - ]` + Path: `HTTP GET /columns` + Response: JSON in body of response + Example response: + `[ "ID", "City", "Population" ]` 1. Get records - Path: `HTTP GET /records?from={fromID}&to={toID}` - Response: JSON in body of response - The order of entries in a record corresponds to the order of the columns returned by the `/columns` API - The `from` and `to` parameters correspond to the record index which run from `0` to `record count - 1` - Example response: - `[ - [0, "Cape Town", 3500000], - [1, "New York", 8500000], - [2, "Johannesburg", 4500000] - ]` - -## Code Review - -Once you are done and happy with your solution, submit your code for code review by creating a pull request in GITHUB. The code review will take the following into account: - -1. Was the brief correctly followed, does the grid work as expected -1. Is the code style according to our [JavaScript Style Guide](https://imqssoftware.atlassian.net/wiki/display/AR/Javascript+Style+Guide) -1. User-centric thinking - is the grid easy to use -1. Suitable comments -1. Performance considerations -1. Aesthetics - -## Pre-requisites - -1. You need to have set up your development environment [as described here](https://imqssoftware.atlassian.net/wiki/display/AR/Dev+Environment). -1. We suggest using VSCode, but you can use your IDE of choice. - -## Getting Started + Path: `HTTP GET /records?from={fromID}&to={toID}` + Response: JSON in body of response + The order of entries in a record corresponds to the order of the columns returned by the `/columns` API + The `from` and `to` parameters correspond to the record index which run from `0` to `record count - 1` + Example response: + `[ [0, "Cape Town", 3500000], [1, "New York", 8500000], [2, "Johannesburg", 4500000] ]` + +## Code Review + +Once you are done and happy with your solution, submit your code for code review by creating a pull request in GITHUB. The code review will take the following into account: + +1. Was the brief correctly followed, does the grid work as expected +1. Is the code style according to our [JavaScript Style Guide](https://imqssoftware.atlassian.net/wiki/display/AR/Javascript+Style+Guide) +1. User-centric thinking - is the grid easy to use +1. Suitable comments +1. Performance considerations +1. Aesthetics + +## Pre-requisites + +1. You need to have set up your development environment [as described here](https://imqssoftware.atlassian.net/wiki/display/AR/Dev+Environment). +1. We suggest using VSCode, but you can use your IDE of choice. + +## Getting Started + These steps include just enough detail to guide you. Each step will require some additional research on your part: -1. Fork this GIT repository under your own GIT account + +1. Fork this GIT repository under your own GIT account 1. Start up the backend server: - - Open console and change directory to `server` directory - - Run `env.bat` - - Run `go run main.go` - - Open up your browser and point it to [http://localhost:2050](http://localhost:2050). You should see "Hello" -1. Create the frontend project: - - Open another console in the project root directory - - Run `npm init` to initialise the JavaScript project. You can just use the default options. - - Install the TypeScript npm package. - - Install TypeScript type definitions for JQuery. - - Create an `app.ts` file in the root directory and add the following code to it: - `window.onload = () => { $("body").text("Hello world"); }` - - Add a npm script called "build" to `package.json` that does the TypeScript build (using the `tsconfig.json` included in the project). - - Run `npm run build` - - You will see a new file `app.js` in the project root. Add an entry for this script in `index.html`. - - Refresh [http://localhost:2050](http://localhost:2050). You should see "Hello world" -1. Get coding! + - Open console and change directory to `server` directory + - Run `env.bat` + - Run `go run main.go` + - Open up your browser and point it to [http://localhost:2050](http://localhost:2050). You should see "Hello" +1. Create the frontend project: + - Open another console in the project root directory + - Run `npm init` to initialise the JavaScript project. You can just use the default options. + - Install the TypeScript npm package. + - Install TypeScript type definitions for JQuery. + - Create an `app.ts` file in the root directory and add the following code to it: + `window.onload = () => { $("body").text("Hello world"); }` + - Add a npm script called "build" to `package.json` that does the TypeScript build (using the `tsconfig.json` included in the project). + - Run `npm run build` + - You will see a new file `app.js` in the project root. Add an entry for this script in `index.html`. + - Refresh [http://localhost:2050](http://localhost:2050). You should see "Hello world" +1. Get coding! diff --git a/app.css b/app.css new file mode 100644 index 00000000..3b2a2d8e --- /dev/null +++ b/app.css @@ -0,0 +1,97 @@ +/* table{width: 100%; border: 2px black;} */ + +body{ + background-image: url("https://previews.123rf.com/images/provector/provector1303/provector130300080/18783350-abstract-technology-database-background.jpg") +} + +table { + position: absolute; + top: 0%; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 90%; + table-layout: fixed; +} + +body{ + overflow-y: hidden; +} + +th { + width: 100/11%; + border: 2px solid red; + background-color: red; +} + +tr:hover td{ + background-color: blueviolet; +} + +td { + width: 100/11%; + border: 2px solid blue; + background:black; + color: yellow; +} + +#previousPage{ + position:absolute; + bottom:1%; + right: 80%; + border-radius: 20px; + background-color:blueviolet; + border: none; + color: white; + padding: 16px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + -webkit-transition-duration: 0.4s; /* Safari */ + transition-duration: 0.4s; + cursor: pointer; +} + + #nextPage { + cursor: pointer; + display: inline-block; + position:absolute; + bottom:1%; + left: 80%; + transition: 0.5s; + border-radius: 20px; + background-color: blueviolet; + border: none; + color: white; + padding: 16px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + -webkit-transition-duration: 0.4s; /* Safari */ + transition-duration: 0.4s; + cursor: pointer; + } + +.searchButtons{ + position:absolute; + bottom:1%; + left: 40%; + border-radius: 20px; + background-color: blueviolet; + border: none; + color: white; + padding: 16px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + -webkit-transition-duration: 0.4s; /* Safari */ + transition-duration: 0.4s; + cursor: pointer; +} diff --git a/app.ts b/app.ts new file mode 100644 index 00000000..5dafaa60 --- /dev/null +++ b/app.ts @@ -0,0 +1,143 @@ +//Loads the table to the screen +window.onload = () => GetColumnNames(); + +//Variable declarations +let totalRecordCount; +let columnNames: string[]; +let records: any[]; +let from: number = 1; +let to: number = 30; + +//Function to retrieve total amount of records +function GetNumberOfRecords() { + $.ajax({ + url: "/recordCount", + type: "GET", + timeout: 12000, + dataType: "text" + }) + .done(function(responseText: any) { + totalRecordCount = responseText; + }) + .fail(function() { + alert("Could not retrieve total record number"); + }); +} + +//function to retrieve column names +function GetColumnNames() { + $.ajax({ + url: "/columns", + type: "GET", + timeout: 12000, + dataType: "JSON" + }) + .done(function(responseJSON: any) { + columnNames = responseJSON; + Sync(); + }) + .fail(function() { + alert("Could not display column names"); + }); +} + +//Function to retrieve the record data +function GetActualRecords() { + $.ajax({ + url: "/records?from=" + from + "&to=" + to, + timeout: 12000, + dataType: "JSON" + }) + .done(function(responseRecords: any) { + records = responseRecords; + NextSync(); + }) + .fail(function() { + alert("No available records for your selection"); + }); +} + +//Function to sync API calls +function Sync() { + GetActualRecords(); +} + +//Function to sync API calls +function NextSync() { + BuildTable(); +} + +//Function builds the table and populates with data from the API calls +function BuildTable() { + RemoveTable(); + + const body = document.getElementsByTagName("body")[0]; + + const ourTable = document.createElement("table"); + ourTable.setAttribute("id", "Table"); + + const thead = document.createElement("thead"); + ourTable.appendChild(thead); + + //Column names are created for the table + for (let c = 0; c < columnNames.length; c++) { + thead + .appendChild(document.createElement("th")) + .appendChild(document.createTextNode(columnNames[c])); + } + //Cells are created and populated with data + for (let i = 0; i < records.length; i++) { + const tableRow = document.createElement("tr"); + ourTable.appendChild(tableRow); + + const innerArrayLength = records[i].length; + + for (let j = 0; j < innerArrayLength; j++) { + tableRow + .appendChild(document.createElement("td")) + .appendChild(document.createTextNode(records[i][j])); + } + } + body.appendChild(ourTable); +} + +//Pages to the next set of data in increments of 30 +function NextPage() { + from = from + 30; + to = to + 30; + GetActualRecords(); +} + +//Pages backwards through data in increments of 30 +function PreviousPage() { + from = from - 30; + to = to - 30; + GetActualRecords(); +} + +//Removes current table to enable pagination (Called in build table function) +function RemoveTable() { + if (document.getElementById("Table") != null) { + $("Table").remove(); + } +} + +//Activates the submit button to produce users search results +function SearchBar() { + let userInput = document.getElementById("userInput"); + from = parseInt(userInput.value); + console.log("userInput: " + from); + to = from + 30; + + GetActualRecords(); +} + +//Enables mouse hover over table rows for readability +$("td").hover( + function() { + $(this).css("background-color", "yellow"); + }, + function() { + $(this).css("background-color", "pink"); + } +); diff --git a/index.html b/index.html index add5e736..b4a27d11 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,30 @@ - - JS Onboard Project - - + ONBoardingProject - -

Hello

- + + + + + + + + +
+ + +
+ - diff --git a/package.json b/package.json new file mode 100644 index 00000000..6841655b --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "onboard-javascript", + "version": "1.0.0", + "description": "This is a JavaScript project for all new developers to complete before venturing into our web frontend codebase.", + "main": "app.js", + "dependencies": {}, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsc" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/YuveerMaharaj/onboard-javascript.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/YuveerMaharaj/onboard-javascript/issues" + }, + "homepage": "https://github.com/YuveerMaharaj/onboard-javascript#readme" +} diff --git a/tsconfig.json b/tsconfig.json index dbf43618..64380699 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,15 @@ { - "compilerOptions": { - "sourceMap": true, - "strict": true, - "noImplicitThis": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "module": "es2015", - "moduleResolution": "node", - "target": "es5", - "typeRoots": [ - "./node_modules/@types" - ], - "lib": [ - "dom", - "es2016" - ], - }, - "include": [ - "./**/*.ts" - ] + "compilerOptions": { + "sourceMap": true, + "strict": true, + "noImplicitThis": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "module": "es2015", + "moduleResolution": "node", + "target": "es5", + "typeRoots": ["./node_modules/@types"], + "lib": ["dom", "es2016"] + }, + "include": ["./**/*.ts", "test.js"] } From 7e3f9b91fde09e7b1adaa286bbe5ef83abc92121 Mon Sep 17 00:00:00 2001 From: Yuveer Maharaj Date: Tue, 19 Nov 2019 15:03:10 +0200 Subject: [PATCH 2/2] Onboarding project for code review --- app.css | 91 ++++++++++++++++--------------- app.ts | 154 +++++++++++++++++++++++++++++++++++------------------ index.html | 27 +++++----- 3 files changed, 166 insertions(+), 106 deletions(-) diff --git a/app.css b/app.css index 3b2a2d8e..9ad51aa7 100644 --- a/app.css +++ b/app.css @@ -1,22 +1,27 @@ -/* table{width: 100%; border: 2px black;} */ + + body{ - background-image: url("https://previews.123rf.com/images/provector/provector1303/provector130300080/18783350-abstract-technology-database-background.jpg") + background-image: url("https://previews.123rf.com/images/provector/provector1303/provector130300080/18783350-abstract-technology-database-background.jpg"); } + table { - position: absolute; - top: 0%; - bottom: 0; - left: 0; - right: 0; - width: 100%; - height: 90%; + + top: 6%; + bottom: 20; + left: 0; + right: 0; + width: 100%; + height: 100%; table-layout: fixed; + position: relative; + text-align: left; } body{ - overflow-y: hidden; + + overflow: hidden; } th { @@ -36,62 +41,64 @@ td { color: yellow; } -#previousPage{ - position:absolute; - bottom:1%; - right: 80%; + + + + +.searchButtons{ + top:1%; + left: 50%; border-radius: 20px; - background-color:blueviolet; + background-color: blueviolet; border: none; color: white; - padding: 16px 32px; + padding: 8px 16px; text-align: center; - text-decoration: none; - display: inline-block; + display: inline-flex; font-size: 16px; - margin: 4px 2px; + margin: 0%; -webkit-transition-duration: 0.4s; /* Safari */ transition-duration: 0.4s; cursor: pointer; + height: 20px; + } - #nextPage { - cursor: pointer; - display: inline-block; - position:absolute; - bottom:1%; - left: 80%; - transition: 0.5s; +#previousPage{ + + top:1%; + float: left; border-radius: 20px; - background-color: blueviolet; border: none; + background-color:blueviolet; color: white; - padding: 16px 32px; + padding: 8px 16px; text-align: center; - text-decoration: none; - display: inline-block; + /* display: inline-flex; */ font-size: 16px; - margin: 4px 2px; -webkit-transition-duration: 0.4s; /* Safari */ transition-duration: 0.4s; cursor: pointer; - } -.searchButtons{ - position:absolute; - bottom:1%; - left: 40%; + + +} + +#nextPage { + + + top:1%; + float: right; + transition: 0.5s; border-radius: 20px; - background-color: blueviolet; border: none; + background-color: blueviolet; color: white; - padding: 16px 32px; + padding: 8px 16px; text-align: center; - text-decoration: none; - display: inline-block; + /* display: inline-flex; */ font-size: 16px; - margin: 4px 2px; -webkit-transition-duration: 0.4s; /* Safari */ transition-duration: 0.4s; cursor: pointer; -} + } diff --git a/app.ts b/app.ts index 5dafaa60..279620f6 100644 --- a/app.ts +++ b/app.ts @@ -1,15 +1,19 @@ //Loads the table to the screen -window.onload = () => GetColumnNames(); +window.onload = () => GetTotalNumberOfRecords(); +//Resizes page during window resizing +window.onresize = () => Resize(); //Variable declarations -let totalRecordCount; -let columnNames: string[]; +let totalRecordCount: number; +let columnNames: any[]; let records: any[]; -let from: number = 1; -let to: number = 30; +let from: number = 0; +let to: number = 0; +let NumberOfRows = Math.floor((window.innerHeight - 50) / 24) - 1; +let timeout: any; //Function to retrieve total amount of records -function GetNumberOfRecords() { +function GetTotalNumberOfRecords() { $.ajax({ url: "/recordCount", type: "GET", @@ -18,7 +22,10 @@ function GetNumberOfRecords() { }) .done(function(responseText: any) { totalRecordCount = responseText; + console.log(totalRecordCount); + Resize(); }) + .fail(function() { alert("Could not retrieve total record number"); }); @@ -30,51 +37,45 @@ function GetColumnNames() { url: "/columns", type: "GET", timeout: 12000, - dataType: "JSON" + dataType: "json" }) - .done(function(responseJSON: any) { - columnNames = responseJSON; - Sync(); + .done((responsejson: any) => { + columnNames = responsejson; + GetActualRecordsAmount(); }) - .fail(function() { + .fail(() => { alert("Could not display column names"); }); } //Function to retrieve the record data -function GetActualRecords() { +function GetActualRecordsAmount() { $.ajax({ url: "/records?from=" + from + "&to=" + to, timeout: 12000, dataType: "JSON" }) - .done(function(responseRecords: any) { + .done((responseRecords: any) => { records = responseRecords; - NextSync(); + BuildTable(); }) - .fail(function() { + .fail(() => { alert("No available records for your selection"); }); } -//Function to sync API calls -function Sync() { - GetActualRecords(); -} - -//Function to sync API calls -function NextSync() { - BuildTable(); -} - //Function builds the table and populates with data from the API calls function BuildTable() { - RemoveTable(); + if (document.getElementById("Table") != null) { + $("Table").remove(); + } const body = document.getElementsByTagName("body")[0]; const ourTable = document.createElement("table"); ourTable.setAttribute("id", "Table"); + //ourTable.style.borderCollapse = "collapse"; + ourTable.style.borderSpacing = "0"; const thead = document.createElement("thead"); ourTable.appendChild(thead); @@ -85,9 +86,11 @@ function BuildTable() { .appendChild(document.createElement("th")) .appendChild(document.createTextNode(columnNames[c])); } + //Cells are created and populated with data for (let i = 0; i < records.length; i++) { const tableRow = document.createElement("tr"); + tableRow.setAttribute("id", "rows"); ourTable.appendChild(tableRow); const innerArrayLength = records[i].length; @@ -101,43 +104,90 @@ function BuildTable() { body.appendChild(ourTable); } -//Pages to the next set of data in increments of 30 +//Pages to the next set of data function NextPage() { - from = from + 30; - to = to + 30; - GetActualRecords(); + let NumberOfRows = Math.floor((window.innerHeight - 50) / 24) - 1; + if (from < from - NumberOfRows) { + from = totalRecordCount - NumberOfRows; + to = Math.min(from + (NumberOfRows - 1), totalRecordCount); + console.log(from + " " + to); + //Resize(); + GetActualRecordsAmount(); + } else { + from = from + NumberOfRows; + to = to + NumberOfRows; + //Resize(); + GetActualRecordsAmount(); + } } -//Pages backwards through data in increments of 30 +//Pages backwards through data function PreviousPage() { - from = from - 30; - to = to - 30; - GetActualRecords(); -} - -//Removes current table to enable pagination (Called in build table function) -function RemoveTable() { - if (document.getElementById("Table") != null) { - $("Table").remove(); + let NumberOfRows = Math.floor((window.innerHeight - 50) / 24) - 1; + if (NumberOfRows > from) { + from = Math.max(0, from - NumberOfRows); + to = NumberOfRows - from; + console.log(from); + Resize(); + GetActualRecordsAmount(); + } else { + from = from - NumberOfRows; + to = from + NumberOfRows; + Resize(); + GetActualRecordsAmount(); } } //Activates the submit button to produce users search results function SearchBar() { let userInput = document.getElementById("userInput"); - from = parseInt(userInput.value); + let NumberOfRows = Math.floor((window.innerHeight - 50) / 24) - 1; + + from = Math.min(parseInt(userInput.value), totalRecordCount - NumberOfRows); + console.log("userInput: " + from); - to = from + 30; - GetActualRecords(); + to = Math.min(from + NumberOfRows, totalRecordCount - 1); + + console.log("NumberOfRows:" + NumberOfRows); + + Resize(); + GetActualRecordsAmount(); } -//Enables mouse hover over table rows for readability -$("td").hover( - function() { - $(this).css("background-color", "yellow"); - }, - function() { - $(this).css("background-color", "pink"); - } -); +function Resize() { + if (timeout) clearTimeout(timeout); + timeout = setTimeout(() => { + let NumberOfRows = Math.floor((window.innerHeight - 50 - 24) / 24) - 1; + if (!from) { + from = 0; + } else { + from = from; + } + console.log(from); + + if (NumberOfRows * (totalRecordCount - 1) > to) { + to = Math.min(from + NumberOfRows, totalRecordCount - 1); + from = to - NumberOfRows; + } + console.log(from + " " + to); + GetColumnNames(); + + if (to > from + NumberOfRows) { + to = Math.min(from + NumberOfRows, totalRecordCount - 1); + from = to - NumberOfRows; + } else if (to <= from + NumberOfRows) { + to = from + NumberOfRows; + } + + console.log("userInput: " + from); + GetColumnNames(); + }, 500); +} + +function restrictAlphabets(e: any) { + var x = e.which || e.keycode; + if ((x >= 48 && x <= 57) || x == 8 || (x >= 35 && x <= 40) || x == 46) + return true; + else return false; +} diff --git a/index.html b/index.html index b4a27d11..8dd60719 100644 --- a/index.html +++ b/index.html @@ -12,19 +12,22 @@ - - +
+ + -
- - +
+ + + +