From 9de206db2f6924ee7cb88a986df666ec717b821d Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 1 Aug 2023 15:23:43 +0200 Subject: [PATCH 01/90] structure and some functionalality --- Styles.css | 56 ++++++++++++++++++ app.ts | 146 ++++++++++++++++++++++++++++++++++++++++++++++ index.html | 15 ++++- package-lock.json | 26 +++++++++ package.json | 25 ++++++++ 5 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 Styles.css create mode 100644 app.ts create mode 100644 package-lock.json create mode 100644 package.json diff --git a/Styles.css b/Styles.css new file mode 100644 index 00000000..a0ec4d04 --- /dev/null +++ b/Styles.css @@ -0,0 +1,56 @@ +/* body with overflow hidden for no scrolling */ +body, html { + margin: 0; + padding: 0; + height: 100%; + overflow: hidden; + } + + .grid-container { + height: 100%; + display: flex; + flex-direction: column; + + } + + .grid { + flex: 1; + display: flex; + flex-direction: column; + overflow: auto; + border: 1px solid #36454F; + } + + .grid-header, .grid-body { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 1px; + } + + .grid-header { + background-color: #36454F; + font-weight: bold; + padding: 10px; + color: aliceblue; + } + + .grid-body { + padding: 10px; + } + + .grid-controls { + display: flex; + align-items: center; + justify-content: center; + padding: 10px; + color: white; + background-color: #36454F; + } + + button { + cursor: pointer; + padding: 5px 10px; + margin: 0 5px; + color: white; + background-color: #36454F; + } \ No newline at end of file diff --git a/app.ts b/app.ts new file mode 100644 index 00000000..7b3f6b24 --- /dev/null +++ b/app.ts @@ -0,0 +1,146 @@ +interface GridData { + [key: string]: any; + } + + interface ColumnName { + name: string; + } + + const PAGE_SIZE = 10; + let currentPage = 1; + let totalItems = 0; + let data: GridData[] = []; + let columnNames: ColumnName[] = []; + + + + $(document).ready(() => { + fetchRecordCount(); + fetchColumns(); + fetchRecords(); + setupControls(); + }); + + + + function fetchRecordCount() { + $.ajax({ + url: 'http://localhost:2050/recordCount', + method: 'GET', + success: (response: number) => { + totalItems = response; + renderGrid(); + console.log(response); + + }, + error: () => { + console.error('Failed to fetch record count'); + } + }); + } + + function fetchColumns() { + $.ajax({ + url: 'http://localhost:2050/columns', + method: 'GET', + success: (response) => { + let res = JSON.parse(response) + columnNames = res.map((columnName: any) => ({ name: columnName })); + data = new Array(columnNames.length); + renderGrid(); + // console.log(response); + console.log(data); + }, + error: () => { + console.error('Failed to fetch columns'); + } + }); + } + + + + function fetchRecords() { + const from = (currentPage - 1) * PAGE_SIZE; + const to = from + PAGE_SIZE - 1; + $.ajax({ + url: `http://localhost:2050/records?from=${from}&to=${to}`, + method: 'GET', + success: (response) => { + let res = JSON.parse(response) + data = res.map((record: string | any[]) => { + const obj: GridData = {}; + for (let i = 0; i < record.length; i++) { + const columnName = columnNames[i].name; + obj[columnName] = record[i]; + } + return obj; + + }); + renderGrid() + console.log(data) + }, + error: () => { + console.error('Failed to fetch records'); + } + }); + } + fetchRecords(); + + + function renderGrid() { + const gridHeader = $('.grid-header'); + const gridBody = $('.grid-body'); + gridHeader.empty(); + gridBody.empty(); + + if (data.length === 0) { + gridHeader.text('No data found.'); + return; + } + const headers = columnNames.map((column) => column.name); + headers.forEach((header) => { + gridHeader.append(`
${header}
`); + }); + + + + const startIndex = (currentPage - 1) * PAGE_SIZE; + const endIndex = Math.min(startIndex + PAGE_SIZE, totalItems); + for (let i = startIndex; i < endIndex; i++) { + const row = data[i]; + const rowContent = headers.map((header) => `
${row[header]}
`).join(''); + gridBody.append(`
${rowContent}
`); + } + + updatePageInfo(); + } + + + + function setupControls() { + $('#prevBtn').on('click', () => { + if (currentPage > 1) { + currentPage--; + fetchRecords(); + } + }); + + + + $('#nextBtn').on('click', () => { + const totalPages = Math.ceil(totalItems / PAGE_SIZE); + if (currentPage < totalPages) { + currentPage++; + fetchRecords(); + } + }); + } + + + + function updatePageInfo() { + const totalPages = Math.ceil(totalItems / PAGE_SIZE); + const pageInfo = `Page ${currentPage} of ${totalPages}`; + $('#pageInfo').text(pageInfo); + } + diff --git a/index.html b/index.html index add5e736..d88532f7 100644 --- a/index.html +++ b/index.html @@ -3,10 +3,23 @@ JS Onboard Project + + -

Hello

+
+
+
+
+
+
+ + + +
+
+ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..2d1ab408 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,26 @@ +{ + "name": "onboard-javascript", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/jquery": { + "version": "3.5.16", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.16.tgz", + "integrity": "sha512-bsI7y4ZgeMkmpG9OM710RRzDFp+w4P1RGiIt30C1mSBT+ExCleeh4HObwgArnDFELmRrOpXgSYN9VF1hj+f1lw==", + "requires": { + "@types/sizzle": "*" + } + }, + "@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==" + }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..229da5c7 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "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": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build":"tsc", + "start": "npm run build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Ntobe99/onboard-javascript.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/Ntobe99/onboard-javascript/issues" + }, + "homepage": "https://github.com/Ntobe99/onboard-javascript#readme", + "dependencies": { + "@types/jquery": "^3.5.16", + "typescript": "^3.8.3" + } +} From feddf76ea0618d55e4f74d66193029617c368887 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 2 Aug 2023 14:16:18 +0200 Subject: [PATCH 02/90] grid and controls added --- Styles.css | 40 ++++++++------ app.ts | 151 +++++++++++++++++++++++++++++------------------------ index.html | 5 +- 3 files changed, 107 insertions(+), 89 deletions(-) diff --git a/Styles.css b/Styles.css index a0ec4d04..18720f02 100644 --- a/Styles.css +++ b/Styles.css @@ -4,47 +4,50 @@ body, html { padding: 0; height: 100%; overflow: hidden; + font-family: ui-rounded; } + .grid-container { height: 100%; display: flex; flex-direction: column; - + overflow: hidden; + text-align: center; } - .grid { + + #grid { flex: 1; display: flex; flex-direction: column; overflow: auto; - border: 1px solid #36454F; + border: 1px solid #333333; } - .grid-header, .grid-body { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 1px; - } - .grid-header { - background-color: #36454F; + table{ + overflow: hidden; + } + thead{ + background-color: #333333; font-weight: bold; - padding: 10px; - color: aliceblue; + padding: 30px; + color: white; } - .grid-body { - padding: 10px; + td { + padding: 15px; + align-content: center; } .grid-controls { display: flex; align-items: center; justify-content: center; - padding: 10px; + padding: 15px; color: white; - background-color: #36454F; + background-color: #333333; } button { @@ -52,5 +55,8 @@ body, html { padding: 5px 10px; margin: 0 5px; color: white; - background-color: #36454F; + background-color: #5a5f61; + } + #pageInfo{ + font-weight: 100; } \ No newline at end of file diff --git a/app.ts b/app.ts index 7b3f6b24..c005b162 100644 --- a/app.ts +++ b/app.ts @@ -1,37 +1,37 @@ +// Define an interface for the grid data, allowing any string keys and any values. interface GridData { [key: string]: any; } - + + // Define an interface for column names with a single property 'name' of type string. interface ColumnName { name: string; } - + + // Constants and variables used for pages and data storage. const PAGE_SIZE = 10; let currentPage = 1; let totalItems = 0; let data: GridData[] = []; let columnNames: ColumnName[] = []; - - + // Entry point after the DOM has loaded. $(document).ready(() => { - fetchRecordCount(); - fetchColumns(); - fetchRecords(); - setupControls(); + fetchRecordCount(); // Fetch the total number of records. + fetchColumns(); // Fetch the column names for the grid. + fetchRecords(); // Fetch the initial records to render the grid. + setupControls(); // Set up pagination controls with event handlers. }); - - + // Fetch the total number of records. function fetchRecordCount() { + $.ajax({ url: 'http://localhost:2050/recordCount', method: 'GET', success: (response: number) => { totalItems = response; - renderGrid(); - console.log(response); - + // console.log(response); }, error: () => { console.error('Failed to fetch record count'); @@ -39,108 +39,121 @@ interface GridData { }); } + // Fetch the column names for the grid. function fetchColumns() { $.ajax({ url: 'http://localhost:2050/columns', method: 'GET', success: (response) => { - let res = JSON.parse(response) + let res = JSON.parse(response); + // Convert the column names to ColumnName objects and store in columnNames array. columnNames = res.map((columnName: any) => ({ name: columnName })); - data = new Array(columnNames.length); - renderGrid(); - // console.log(response); - console.log(data); + data = new Array(columnNames.length); // Initialize the data array with the number of columns. + }, + error: () => { console.error('Failed to fetch columns'); } }); } - - + // Fetch records based on the current pagination settings. function fetchRecords() { - const from = (currentPage - 1) * PAGE_SIZE; - const to = from + PAGE_SIZE - 1; + const from = (currentPage - 1)*PAGE_SIZE; + const to = from + PAGE_SIZE ; $.ajax({ url: `http://localhost:2050/records?from=${from}&to=${to}`, method: 'GET', success: (response) => { - let res = JSON.parse(response) - data = res.map((record: string | any[]) => { - const obj: GridData = {}; - for (let i = 0; i < record.length; i++) { - const columnName = columnNames[i].name; - obj[columnName] = record[i]; + let res = JSON.parse(response); + + data = []; // Reset the data array for the current page + for (let i = 0; i < res.length; i++) { + const record = res[i]; + if (Array.isArray(record)) { // Ensure the record is an array + const obj: GridData = {}; + for (let j = 0; j < columnNames.length && j < record.length; j++) { + const columnName = columnNames[j].name; + const columnValue = record[j]; + obj[columnName] = columnValue; // Map column names to their corresponding values. + } + data.push(obj); // Add the row to the data array + } else { + console.error(`Invalid record format at index ${i}`); + } } - return obj; - - }); - renderGrid() - console.log(data) + console.table(data); + createGrid() }, error: () => { console.error('Failed to fetch records'); } }); } - fetchRecords(); - - - function renderGrid() { - const gridHeader = $('.grid-header'); - const gridBody = $('.grid-body'); - gridHeader.empty(); - gridBody.empty(); - - if (data.length === 0) { - gridHeader.text('No data found.'); - return; - } - const headers = columnNames.map((column) => column.name); - headers.forEach((header) => { - gridHeader.append(`
${header}
`); - }); + // Render the grid with the fetched data. + function createGrid() { + const gridElement = document.getElementById('grid'); + if (gridElement) { + gridElement.innerHTML = ''; // Clear the existing grid . + + // Create table element + const table = document.createElement('table'); + + // Create table header + const thead = document.createElement('thead'); + const headerRow = document.createElement('tr'); + columnNames.forEach((column) => { + const th = document.createElement('th'); + th.textContent = column.name; + headerRow.appendChild(th); + }); + thead.appendChild(headerRow); + table.appendChild(thead); + + // Create table body + const tbody = document.createElement('tbody'); + data.forEach((row) => { + const tr = document.createElement('tr'); + columnNames.forEach((column) => { + const td = document.createElement('td'); + td.textContent = row[column.name]; + tr.appendChild(td); + }); + tbody.appendChild(tr); + }); + table.appendChild(tbody); - const startIndex = (currentPage - 1) * PAGE_SIZE; - const endIndex = Math.min(startIndex + PAGE_SIZE, totalItems); - for (let i = startIndex; i < endIndex; i++) { - const row = data[i]; - const rowContent = headers.map((header) => `
${row[header]}
`).join(''); - gridBody.append(`
${rowContent}
`); + // Append the table to the grid element + gridElement.appendChild(table); } - updatePageInfo(); } - - + // Set up pagination controls with event handlers. function setupControls() { $('#prevBtn').on('click', () => { if (currentPage > 1) { - currentPage--; - fetchRecords(); + currentPage--; // Go to the previous page. + fetchRecords(); // Fetch records for the new page. } }); - - $('#nextBtn').on('click', () => { const totalPages = Math.ceil(totalItems / PAGE_SIZE); if (currentPage < totalPages) { - currentPage++; - fetchRecords(); + currentPage++; // Go to the next page. + fetchRecords(); // Fetch records for the new page. } }); } - - + // Update the page information display. function updatePageInfo() { const totalPages = Math.ceil(totalItems / PAGE_SIZE); const pageInfo = `Page ${currentPage} of ${totalPages}`; - $('#pageInfo').text(pageInfo); + $('#pageInfo').text(pageInfo); // Update the page information text. } - + \ No newline at end of file diff --git a/index.html b/index.html index d88532f7..82b6ef07 100644 --- a/index.html +++ b/index.html @@ -9,9 +9,8 @@
-
-
-
+

Onboarding Project

+
From 20ab5f00cfedf8f0a9e603d0b490d498ed9edc35 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 11 Aug 2023 07:48:36 +0200 Subject: [PATCH 03/90] search using id working --- Styles.css | 78 +++++++++++++++++++++++++++++++++----------- app.ts | 91 +++++++++++++++++++++++++++++++++++++++++----------- index.html | 18 +++++++---- spinner.gif | Bin 0 -> 72191 bytes 4 files changed, 144 insertions(+), 43 deletions(-) create mode 100644 spinner.gif diff --git a/Styles.css b/Styles.css index 18720f02..9d46b84f 100644 --- a/Styles.css +++ b/Styles.css @@ -6,42 +6,74 @@ body, html { overflow: hidden; font-family: ui-rounded; } - +.top{ +text-align: center; +} + /* search form styling */ +input[type="text"] { + display: flex; + flex-direction: row; + border: 1px solid #333333; + border-radius: 0px; + padding: 5px; + width: 40%; + margin-left: 30%; + + } +#searchButton { + border: none; + border-radius: 0px; + background-color: #747679; + color: white; + cursor: pointer; + padding: 5px 10px; + margin: 5px; + color: white; + } .grid-container { height: 100%; display: flex; flex-direction: column; overflow: hidden; text-align: center; + overflow: hidden; } - #grid { +#grid { flex: 1; display: flex; flex-direction: column; overflow: auto; border: 1px solid #333333; } - - - table{ + /* table */ +table{ + height: 100%; overflow: hidden; + border: 1px solid #333333; + } - thead{ +tr{ + border: 1px solid #333333; + } + +thead{ background-color: #333333; font-weight: bold; - padding: 30px; + padding: 25px; color: white; + width: 100px; } - td { - padding: 15px; +td { + padding: 10px; align-content: center; + } - .grid-controls { +.grid-controls { display: flex; align-items: center; justify-content: center; @@ -50,13 +82,21 @@ body, html { background-color: #333333; } - button { - cursor: pointer; - padding: 5px 10px; - margin: 0 5px; - color: white; - background-color: #5a5f61; + +#pageInfo{ + font-weight: 300; + background-color: #333333; + } - #pageInfo{ - font-weight: 100; - } \ No newline at end of file + + +/* button */ +.btn{ + cursor: pointer; + padding: 5px 10px; + margin: 0 5px; + color: white; + background-color: #5a5f61; +} + + diff --git a/app.ts b/app.ts index c005b162..c05fe6c2 100644 --- a/app.ts +++ b/app.ts @@ -9,6 +9,7 @@ interface GridData { } // Constants and variables used for pages and data storage. + const PAGE_SIZE = 10; let currentPage = 1; let totalItems = 0; @@ -16,11 +17,13 @@ interface GridData { let columnNames: ColumnName[] = []; // Entry point after the DOM has loaded. + $(document).ready(() => { fetchRecordCount(); // Fetch the total number of records. fetchColumns(); // Fetch the column names for the grid. fetchRecords(); // Fetch the initial records to render the grid. - setupControls(); // Set up pagination controls with event handlers. + setupControls(); // Set up page controls with event handlers. + }); // Fetch the total number of records. @@ -58,33 +61,33 @@ interface GridData { }); } - // Fetch records based on the current pagination settings. function fetchRecords() { - const from = (currentPage - 1)*PAGE_SIZE; - const to = from + PAGE_SIZE ; + const from = (currentPage - 1) * PAGE_SIZE; + const to = from + PAGE_SIZE; $.ajax({ url: `http://localhost:2050/records?from=${from}&to=${to}`, method: 'GET', success: (response) => { let res = JSON.parse(response); - + data = []; // Reset the data array for the current page for (let i = 0; i < res.length; i++) { - const record = res[i]; - if (Array.isArray(record)) { // Ensure the record is an array - const obj: GridData = {}; - for (let j = 0; j < columnNames.length && j < record.length; j++) { - const columnName = columnNames[j].name; - const columnValue = record[j]; - obj[columnName] = columnValue; // Map column names to their corresponding values. - } - data.push(obj); // Add the row to the data array - } else { - console.error(`Invalid record format at index ${i}`); + const record = res[i]; + if (Array.isArray(record)) { // Ensure the record is an array + const obj: GridData = {}; + for (let j = 0; j < columnNames.length && j < record.length; j++) { + const columnName = columnNames[j].name; + const columnValue = record[j]; + obj[columnName] = columnValue; // Map column names to their corresponding values. } + data.push(obj); // Add the row to the data array + } else { + console.error(`Invalid record format at index ${i}`); } + } + console.table(data); - createGrid() + createGrid(); }, error: () => { console.error('Failed to fetch records'); @@ -92,6 +95,55 @@ interface GridData { }); } + function updateGrid() { + const fromVal = $('#searchInputFrom').val(); + const toVal = $('#searchInputTo').val(); + + if (typeof fromVal === 'string' && typeof toVal === 'string') { + const from = parseInt(fromVal, 10); + const to = parseInt(toVal, 10); + + if (!isNaN(from) && !isNaN(to)) { + $.ajax({ + url: `http://localhost:2050/records?from=${from}&to=${to}`, + method: 'GET', + success: (response) => { + let newData = JSON.parse(response); + console.table(newData); + if (Array.isArray(newData)) { + data = []; + for (let i = 0; i < newData.length; i++) { + const record = newData[i]; + if (Array.isArray(record)) { // Ensure the record is an array + const obj: GridData = {}; + for (let j = 0; j < columnNames.length && j < record.length; j++) { + const columnName = columnNames[j].name; + const columnValue = record[j]; + obj[columnName] = columnValue; // Map column names to their corresponding values. + } + data.push(obj); // Add the row to the data array + } else { + console.error(`Invalid record format at index ${i}`); + } + } + createGrid(); // Update the grid with the new data + console.log(data); + + } else { + console.warn('Invalid response format. Expected an array.'); + } + }, + error: () => { + console.error('Failed to fetch records'); + alert('Someone needs to go back to the creche!'); + } + }); + } + } + } + + + // Render the grid with the fetched data. function createGrid() { @@ -130,9 +182,12 @@ interface GridData { gridElement.appendChild(table); } updatePageInfo(); + + } - // Set up pagination controls with event handlers. + + // Set up page controls with event handlers. function setupControls() { $('#prevBtn').on('click', () => { if (currentPage > 1) { diff --git a/index.html b/index.html index 82b6ef07..5f970004 100644 --- a/index.html +++ b/index.html @@ -3,19 +3,25 @@ JS Onboard Project - - + + +
-

Onboarding Project

+
+

Onboarding-project

+ + + +
- - - + + +
diff --git a/spinner.gif b/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..aaac0761f39798b83f7e421627652254da5276b2 GIT binary patch literal 72191 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 literal 0 HcmV?d00001 From 161dba82c82a3ff31af8a4a25eea84c2243213ba Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 11 Aug 2023 10:35:11 +0200 Subject: [PATCH 04/90] spinner added --- Styles.css | 47 +++++++++++++++++++++++++++++------------------ app.ts | 46 +++++++++++++++++++++++++++++++++++++++++----- index.html | 7 ++++++- 3 files changed, 76 insertions(+), 24 deletions(-) diff --git a/Styles.css b/Styles.css index 9d46b84f..951262b2 100644 --- a/Styles.css +++ b/Styles.css @@ -4,10 +4,12 @@ body, html { padding: 0; height: 100%; overflow: hidden; - font-family: ui-rounded; + font-family: system-ui; } + .top{ -text-align: center; + text-align: center; + background-color: #7d8081; } /* search form styling */ input[type="text"] { @@ -21,16 +23,17 @@ input[type="text"] { } -#searchButton { - border: none; - border-radius: 0px; - background-color: #747679; - color: white; - cursor: pointer; - padding: 5px 10px; - margin: 5px; - color: white; - } + #searchButton { + border: none; + border-radius: 0px; + background-color: #747679; + color: white; + cursor: pointer; + padding: 5px 10px; + margin: 5px; + color: white; + } + .grid-container { height: 100%; display: flex; @@ -47,7 +50,7 @@ input[type="text"] { flex-direction: column; overflow: auto; border: 1px solid #333333; - } + } /* table */ table{ height: 100%; @@ -65,13 +68,13 @@ thead{ padding: 25px; color: white; width: 100px; - } + } td { padding: 10px; align-content: center; - } +} .grid-controls { display: flex; @@ -87,9 +90,7 @@ td { font-weight: 300; background-color: #333333; - } - - +} /* button */ .btn{ cursor: pointer; @@ -99,4 +100,14 @@ td { background-color: #5a5f61; } +#reloadBtn{ + border: none; + border-radius: 0px; + background-color: #747679; + color: white; + cursor: pointer; + padding: 5px 10px; + margin: 5px; + color: white; +} diff --git a/app.ts b/app.ts index c05fe6c2..03b2a1df 100644 --- a/app.ts +++ b/app.ts @@ -3,7 +3,7 @@ interface GridData { [key: string]: any; } - // Define an interface for column names with a single property 'name' of type string. + // Define an interface for column names interface ColumnName { name: string; } @@ -28,6 +28,10 @@ interface GridData { // Fetch the total number of records. function fetchRecordCount() { + // Show the spinner before making the request and hide the input elements + $('#spinner').show(); + $('.top').hide(); + $.ajax({ url: 'http://localhost:2050/recordCount', @@ -38,12 +42,20 @@ interface GridData { }, error: () => { console.error('Failed to fetch record count'); + }, + complete: () => { + // Hide the spinner after the AJAX request is completed + $('#spinner').hide(); + $('.top').show(); } }); } // Fetch the column names for the grid. function fetchColumns() { + $('#spinner').show(); + $('.top').hide(); + $.ajax({ url: 'http://localhost:2050/columns', method: 'GET', @@ -57,11 +69,20 @@ interface GridData { error: () => { console.error('Failed to fetch columns'); + }, + complete: () => { + // Hide the spinner after the AJAX request is completed + $('#spinner').hide(); + $('.top').show(); } }); } function fetchRecords() { + + $('#spinner').show(); + $('.top').hide(); + const from = (currentPage - 1) * PAGE_SIZE; const to = from + PAGE_SIZE; $.ajax({ @@ -91,11 +112,17 @@ interface GridData { }, error: () => { console.error('Failed to fetch records'); + }, + complete:() =>{ + $('#spinner').hide(); + $('.top').show(); } }); } - + // update grid to display search by ID items function updateGrid() { + $('#spinner').show(); + $('.top').hide(); const fromVal = $('#searchInputFrom').val(); const toVal = $('#searchInputTo').val(); @@ -114,19 +141,20 @@ interface GridData { data = []; for (let i = 0; i < newData.length; i++) { const record = newData[i]; - if (Array.isArray(record)) { // Ensure the record is an array + if (Array.isArray(record)) { const obj: GridData = {}; for (let j = 0; j < columnNames.length && j < record.length; j++) { const columnName = columnNames[j].name; const columnValue = record[j]; obj[columnName] = columnValue; // Map column names to their corresponding values. } - data.push(obj); // Add the row to the data array + data.push(obj); } else { console.error(`Invalid record format at index ${i}`); } } createGrid(); // Update the grid with the new data + updatePageInfo(); console.log(data); } else { @@ -136,6 +164,10 @@ interface GridData { error: () => { console.error('Failed to fetch records'); alert('Someone needs to go back to the creche!'); + }, + complete: () => { + $('#spinner').hide(); + $('.top').show(); } }); } @@ -146,7 +178,6 @@ interface GridData { // Render the grid with the fetched data. function createGrid() { - const gridElement = document.getElementById('grid'); if (gridElement) { gridElement.innerHTML = ''; // Clear the existing grid . @@ -211,4 +242,9 @@ interface GridData { const pageInfo = `Page ${currentPage} of ${totalPages}`; $('#pageInfo').text(pageInfo); // Update the page information text. } + + // reload page + function pageReload(){ + location.reload(); + } \ No newline at end of file diff --git a/index.html b/index.html index 5f970004..0bab3942 100644 --- a/index.html +++ b/index.html @@ -9,12 +9,17 @@ -
+
+ +

Onboarding-project

+
From 4db8da4f1fbf44900cffcf611d504f8640df7485 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 11 Aug 2023 13:58:55 +0200 Subject: [PATCH 05/90] some changes --- Styles.css | 32 +++++++++++++++++--------------- app.ts | 3 +-- index.html | 3 +-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Styles.css b/Styles.css index 951262b2..b625b9c2 100644 --- a/Styles.css +++ b/Styles.css @@ -9,17 +9,18 @@ body, html { .top{ text-align: center; - background-color: #7d8081; + background-color: #cbd5d9; } - /* search form styling */ + /* search inputs styling */ input[type="text"] { - display: flex; - flex-direction: row; + display: flex; + flex-direction: row; border: 1px solid #333333; border-radius: 0px; padding: 5px; width: 40%; margin-left: 30%; + margin-bottom: 5px; } @@ -33,6 +34,16 @@ input[type="text"] { margin: 5px; color: white; } + #reloadBtn{ + border: none; + border-radius: 0px; + background-color: #747679; + color: white; + cursor: pointer; + padding: 5px 10px; + margin: 5px; + color: white; + } .grid-container { height: 100%; @@ -63,7 +74,7 @@ tr{ } thead{ - background-color: #333333; + background-color: #333333e0; font-weight: bold; padding: 25px; color: white; @@ -100,14 +111,5 @@ td { background-color: #5a5f61; } -#reloadBtn{ - border: none; - border-radius: 0px; - background-color: #747679; - color: white; - cursor: pointer; - padding: 5px 10px; - margin: 5px; - color: white; -} + diff --git a/app.ts b/app.ts index 03b2a1df..7e139d9d 100644 --- a/app.ts +++ b/app.ts @@ -154,11 +154,10 @@ interface GridData { } } createGrid(); // Update the grid with the new data - updatePageInfo(); console.log(data); } else { - console.warn('Invalid response format. Expected an array.'); + console.warn('Invalid response format. Unexpected output .'); } }, error: () => { diff --git a/index.html b/index.html index 0bab3942..b35cf5dc 100644 --- a/index.html +++ b/index.html @@ -13,13 +13,12 @@ -

Onboarding-project

- +
From 4379d035304f7ae399f79ab0e0d601da8be9b705 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 14 Aug 2023 08:03:51 +0200 Subject: [PATCH 06/90] changes to code format --- Styles.css | 2 +- app.ts | 30 +++++++----------------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/Styles.css b/Styles.css index b625b9c2..c518db74 100644 --- a/Styles.css +++ b/Styles.css @@ -9,7 +9,7 @@ body, html { .top{ text-align: center; - background-color: #cbd5d9; + } /* search inputs styling */ input[type="text"] { diff --git a/app.ts b/app.ts index 7e139d9d..0dbe9e03 100644 --- a/app.ts +++ b/app.ts @@ -55,7 +55,6 @@ interface GridData { function fetchColumns() { $('#spinner').show(); $('.top').hide(); - $.ajax({ url: 'http://localhost:2050/columns', method: 'GET', @@ -64,9 +63,7 @@ interface GridData { // Convert the column names to ColumnName objects and store in columnNames array. columnNames = res.map((columnName: any) => ({ name: columnName })); data = new Array(columnNames.length); // Initialize the data array with the number of columns. - }, - error: () => { console.error('Failed to fetch columns'); }, @@ -79,10 +76,8 @@ interface GridData { } function fetchRecords() { - $('#spinner').show(); $('.top').hide(); - const from = (currentPage - 1) * PAGE_SIZE; const to = from + PAGE_SIZE; $.ajax({ @@ -90,7 +85,6 @@ interface GridData { method: 'GET', success: (response) => { let res = JSON.parse(response); - data = []; // Reset the data array for the current page for (let i = 0; i < res.length; i++) { const record = res[i]; @@ -102,11 +96,8 @@ interface GridData { obj[columnName] = columnValue; // Map column names to their corresponding values. } data.push(obj); // Add the row to the data array - } else { - console.error(`Invalid record format at index ${i}`); - } + } } - console.table(data); createGrid(); }, @@ -129,7 +120,7 @@ interface GridData { if (typeof fromVal === 'string' && typeof toVal === 'string') { const from = parseInt(fromVal, 10); const to = parseInt(toVal, 10); - + if (!isNaN(from) && !isNaN(to)) { $.ajax({ url: `http://localhost:2050/records?from=${from}&to=${to}`, @@ -149,16 +140,12 @@ interface GridData { obj[columnName] = columnValue; // Map column names to their corresponding values. } data.push(obj); - } else { - console.error(`Invalid record format at index ${i}`); - } + } } createGrid(); // Update the grid with the new data console.log(data); - } else { - console.warn('Invalid response format. Unexpected output .'); - } + } }, error: () => { console.error('Failed to fetch records'); @@ -173,8 +160,6 @@ interface GridData { } } - - // Render the grid with the fetched data. function createGrid() { const gridElement = document.getElementById('grid'); @@ -215,8 +200,7 @@ interface GridData { } - - + // Set up page controls with event handlers. function setupControls() { $('#prevBtn').on('click', () => { @@ -230,11 +214,11 @@ interface GridData { const totalPages = Math.ceil(totalItems / PAGE_SIZE); if (currentPage < totalPages) { currentPage++; // Go to the next page. - fetchRecords(); // Fetch records for the new page. + fetchRecords(); } }); } - + // Update the page information display. function updatePageInfo() { const totalPages = Math.ceil(totalItems / PAGE_SIZE); From 1574a995f5eb3e4f60a5c4ce998e625d88c82973 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 14 Aug 2023 16:30:02 +0200 Subject: [PATCH 07/90] scrollbar removed and empty array debug --- Styles.css | 26 ++++++++++++++++++------- app.ts | 56 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/Styles.css b/Styles.css index c518db74..f5790cd0 100644 --- a/Styles.css +++ b/Styles.css @@ -59,15 +59,22 @@ input[type="text"] { flex: 1; display: flex; flex-direction: column; - overflow: auto; + overflow:auto; border: 1px solid #333333; } /* table */ -table{ + table{ + -ms-overflow-style: none; + scrollbar-width: none; height: 100%; - overflow: hidden; + overflow: none; border: 1px solid #333333; - + display: block; + overflow: auto; + /* border-collapse: collapse; */ + } + table::-webkit-scrollbar { + display: none; } tr{ border: 1px solid #333333; @@ -78,13 +85,16 @@ thead{ font-weight: bold; padding: 25px; color: white; - width: 100px; + position: sticky; + top: 0; + padding: 15px; + z-index: 2; } td { - padding: 10px; - align-content: center; + align-content: center; + width: 350px; } .grid-controls { @@ -94,6 +104,8 @@ td { padding: 15px; color: white; background-color: #333333; + position: sticky; + bottom: 0; } diff --git a/app.ts b/app.ts index 0dbe9e03..6e02c33e 100644 --- a/app.ts +++ b/app.ts @@ -10,7 +10,7 @@ interface GridData { // Constants and variables used for pages and data storage. - const PAGE_SIZE = 10; + const PAGE_SIZE = 30; let currentPage = 1; let totalItems = 0; let data: GridData[] = []; @@ -19,15 +19,15 @@ interface GridData { // Entry point after the DOM has loaded. $(document).ready(() => { - fetchRecordCount(); // Fetch the total number of records. - fetchColumns(); // Fetch the column names for the grid. - fetchRecords(); // Fetch the initial records to render the grid. - setupControls(); // Set up page controls with event handlers. - + fetchRecordCount(); + fetchColumns(); + fetchRecords(); + setupControls(); + createGrid(); }); // Fetch the total number of records. - function fetchRecordCount() { +function fetchRecordCount() { // Show the spinner before making the request and hide the input elements $('#spinner').show(); $('.top').hide(); @@ -44,7 +44,7 @@ interface GridData { console.error('Failed to fetch record count'); }, complete: () => { - // Hide the spinner after the AJAX request is completed + // Hide the spinner after request is completed $('#spinner').hide(); $('.top').show(); } @@ -75,7 +75,7 @@ interface GridData { }); } - function fetchRecords() { +function fetchRecords() { $('#spinner').show(); $('.top').hide(); const from = (currentPage - 1) * PAGE_SIZE; @@ -88,7 +88,7 @@ interface GridData { data = []; // Reset the data array for the current page for (let i = 0; i < res.length; i++) { const record = res[i]; - if (Array.isArray(record)) { // Ensure the record is an array + if (Array.isArray(record)) { const obj: GridData = {}; for (let j = 0; j < columnNames.length && j < record.length; j++) { const columnName = columnNames[j].name; @@ -96,7 +96,9 @@ interface GridData { obj[columnName] = columnValue; // Map column names to their corresponding values. } data.push(obj); // Add the row to the data array - } + } else{ + console.log('array is empty'); + } } console.table(data); createGrid(); @@ -116,12 +118,32 @@ interface GridData { $('.top').hide(); const fromVal = $('#searchInputFrom').val(); const toVal = $('#searchInputTo').val(); + + if (!fromVal || !toVal) { + $('#spinner').hide(); + $('.top').show(); + alert('Please enter both "From" and "To" values.'); + return; + } if (typeof fromVal === 'string' && typeof toVal === 'string') { const from = parseInt(fromVal, 10); const to = parseInt(toVal, 10); - if (!isNaN(from) && !isNaN(to)) { + if (isNaN(from) || isNaN(to)) { + $('#spinner').hide(); + $('.top').show(); + alert('Please enter valid numeric values.'); + return; + } + + if (from > to || to - from > 10000) { + $('#spinner').hide(); + $('.top').show(); + alert('Please enter a valid range (1-999999) with a maximum of 10000 records.'); + return; + } + if (!isNaN(from) && !isNaN(to)) { $.ajax({ url: `http://localhost:2050/records?from=${from}&to=${to}`, method: 'GET', @@ -149,7 +171,9 @@ interface GridData { }, error: () => { console.error('Failed to fetch records'); - alert('Someone needs to go back to the creche!'); + $('#spinner').hide() + $('.top').show() + alert('Please enter a valid range! (1-999999)'); }, complete: () => { $('#spinner').hide(); @@ -161,7 +185,7 @@ interface GridData { } // Render the grid with the fetched data. - function createGrid() { + function createGrid() { const gridElement = document.getElementById('grid'); if (gridElement) { gridElement.innerHTML = ''; // Clear the existing grid . @@ -184,9 +208,9 @@ interface GridData { const tbody = document.createElement('tbody'); data.forEach((row) => { const tr = document.createElement('tr'); - columnNames.forEach((column) => { + columnNames.forEach(async (column) => { const td = document.createElement('td'); - td.textContent = row[column.name]; + td.textContent = await(row[column.name]); tr.appendChild(td); }); tbody.appendChild(tr); From d7cc5a71a274ae1a02f7cb8144fa1efac75ab564 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 17 Aug 2023 13:10:20 +0200 Subject: [PATCH 08/90] dyanmic grid and some search functionality --- Styles.css | 98 +++--------- app.ts | 445 ++++++++++++++++++++++++++------------------------- index.html | 28 ++-- package.json | 2 +- 4 files changed, 272 insertions(+), 301 deletions(-) diff --git a/Styles.css b/Styles.css index f5790cd0..345198ac 100644 --- a/Styles.css +++ b/Styles.css @@ -6,93 +6,30 @@ body, html { overflow: hidden; font-family: system-ui; } - -.top{ - text-align: center; - -} - /* search inputs styling */ -input[type="text"] { - display: flex; - flex-direction: row; - border: 1px solid #333333; - border-radius: 0px; - padding: 5px; - width: 40%; - margin-left: 30%; - margin-bottom: 5px; - - } - - #searchButton { - border: none; - border-radius: 0px; - background-color: #747679; - color: white; - cursor: pointer; - padding: 5px 10px; - margin: 5px; - color: white; - } - #reloadBtn{ - border: none; - border-radius: 0px; - background-color: #747679; - color: white; - cursor: pointer; - padding: 5px 10px; - margin: 5px; - color: white; - } - - .grid-container { - height: 100%; - display: flex; - flex-direction: column; - overflow: hidden; + .grid-container{ text-align: center; - overflow: hidden; } - - -#grid { - flex: 1; - display: flex; - flex-direction: column; - overflow:auto; + #grid { border: 1px solid #333333; + text-align: center; + background-color: #d4e1e7; } /* table */ table{ - -ms-overflow-style: none; - scrollbar-width: none; height: 100%; - overflow: none; - border: 1px solid #333333; - display: block; - overflow: auto; - /* border-collapse: collapse; */ - } - table::-webkit-scrollbar { - display: none; + border-collapse: collapse; } tr{ border: 1px solid #333333; } - thead{ background-color: #333333e0; font-weight: bold; - padding: 25px; color: white; - position: sticky; - top: 0; - padding: 15px; - z-index: 2; } td { - + padding: 2px; align-content: center; width: 350px; } @@ -101,7 +38,7 @@ td { display: flex; align-items: center; justify-content: center; - padding: 15px; + padding: 10px; color: white; background-color: #333333; position: sticky; @@ -110,9 +47,8 @@ td { #pageInfo{ - font-weight: 300; + font-weight: 350; background-color: #333333; - } /* button */ .btn{ @@ -122,6 +58,24 @@ td { color: white; background-color: #5a5f61; } +#toInput{ + padding: 5px 10px; + margin: 0 5px; + background-color: #232425; + color: white; +} +#overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(231, 238, 238, 0.753); + z-index: 1000; + display: none; +} + + diff --git a/app.ts b/app.ts index 6e02c33e..d5c16847 100644 --- a/app.ts +++ b/app.ts @@ -1,257 +1,274 @@ -// Define an interface for the grid data, allowing any string keys and any values. interface GridData { - [key: string]: any; - } + [key: string]: any; +} + +interface ColumnName { + name: string; +} + +class ApiData { + pageSize: number; + currentPage: number = 1; + data: GridData[] = []; + totalItems: number = 0; + columnNames: ColumnName[] = []; + maxGridHeight: number = 0; - // Define an interface for column names - interface ColumnName { - name: string; + + constructor(pageSize: number) { + this.pageSize = pageSize; } - - // Constants and variables used for pages and data storage. - const PAGE_SIZE = 30; - let currentPage = 1; - let totalItems = 0; - let data: GridData[] = []; - let columnNames: ColumnName[] = []; - - // Entry point after the DOM has loaded. - - $(document).ready(() => { - fetchRecordCount(); - fetchColumns(); - fetchRecords(); - setupControls(); - createGrid(); - }); - - // Fetch the total number of records. -function fetchRecordCount() { - // Show the spinner before making the request and hide the input elements - $('#spinner').show(); - $('.top').hide(); - - - $.ajax({ - url: 'http://localhost:2050/recordCount', - method: 'GET', - success: (response: number) => { - totalItems = response; - // console.log(response); - }, - error: () => { - console.error('Failed to fetch record count'); - }, - complete: () => { - // Hide the spinner after request is completed - $('#spinner').hide(); - $('.top').show(); - } - }); + async initialize() { + try { + this.adjustGridHeight(); + await this.recordCount(); + await this.fetchColumns(); + await this.fetchRecords(); + this.setupControls(); + + + } catch (error) { + console.error('Error during initialization:', error); + } } - // Fetch the column names for the grid. - function fetchColumns() { - $('#spinner').show(); - $('.top').hide(); - $.ajax({ - url: 'http://localhost:2050/columns', - method: 'GET', - success: (response) => { - let res = JSON.parse(response); - // Convert the column names to ColumnName objects and store in columnNames array. - columnNames = res.map((columnName: any) => ({ name: columnName })); - data = new Array(columnNames.length); // Initialize the data array with the number of columns. - }, - error: () => { - console.error('Failed to fetch columns'); - }, - complete: () => { - // Hide the spinner after the AJAX request is completed - $('#spinner').hide(); - $('.top').show(); - } - }); + async recordCount() { + try { + const response = await this.fetchData('http://localhost:2050/recordCount'); + this.totalItems = response; + } catch (error) { + console.error('Failed to fetch record count', error); + throw error; + } } - -function fetchRecords() { + + async fetchColumns() { + try { + const response = await this.fetchData('http://localhost:2050/columns'); + const res = JSON.parse(response); + this.columnNames = res.map((columnName: any) => ({ name: columnName })); + this.data = new Array(this.columnNames.length); + } catch (error) { + console.error('Failed to fetch columns', error); + throw error; + } + } + + async fetchRecords() { + try { + const from = (this.currentPage - 1) * this.pageSize; + const to = from + this.pageSize; + // Display spinner while fetching records $('#spinner').show(); - $('.top').hide(); - const from = (currentPage - 1) * PAGE_SIZE; - const to = from + PAGE_SIZE; - $.ajax({ - url: `http://localhost:2050/records?from=${from}&to=${to}`, - method: 'GET', - success: (response) => { - let res = JSON.parse(response); - data = []; // Reset the data array for the current page - for (let i = 0; i < res.length; i++) { - const record = res[i]; - if (Array.isArray(record)) { - const obj: GridData = {}; - for (let j = 0; j < columnNames.length && j < record.length; j++) { - const columnName = columnNames[j].name; - const columnValue = record[j]; - obj[columnName] = columnValue; // Map column names to their corresponding values. - } - data.push(obj); // Add the row to the data array - } else{ - console.log('array is empty'); - } + const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); + const res = JSON.parse(response); + this.data = res.map((record: any) => { + const obj: GridData = {}; + for (let j = 0; j < this.columnNames.length && j < record.length; j++) { + const columnName = this.columnNames[j].name; + const columnValue = record[j]; + obj[columnName] = columnValue; } - console.table(data); - createGrid(); - }, - error: () => { - console.error('Failed to fetch records'); - }, - complete:() =>{ - $('#spinner').hide(); - $('.top').show(); - } - }); + return obj; + }); + $('#spinner').hide(); + this.displayRecords(); + } catch (error) { + console.error('Failed to fetch records', error); + throw error; + } } - // update grid to display search by ID items - function updateGrid() { - $('#spinner').show(); - $('.top').hide(); - const fromVal = $('#searchInputFrom').val(); - const toVal = $('#searchInputTo').val(); + adjustGridHeight() { + const gridElement = document.getElementById('grid'); + const pageCntrl = $('.grid-controls').innerHeight(); + const screenHeight = $(window).innerHeight(); + if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { + this.maxGridHeight = screenHeight - pageCntrl; + gridElement.style.height = `${this.maxGridHeight}px`; + gridElement.style.overflow = 'none'; + } + } + + async searchRecords(from: number, to: number) { + try { + // Display spinner while fetching records + $('#spinner').show(); + + const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); + const res = JSON.parse(response); + this.data = res.map((record: any) => { + const obj: GridData = {}; + for (let j = 0; j < this.columnNames.length && j < record.length; j++) { + const columnName = this.columnNames[j].name; + const columnValue = record[j]; + obj[columnName] = columnValue; + } + return obj; + }); - if (!fromVal || !toVal) { $('#spinner').hide(); - $('.top').show(); - alert('Please enter both "From" and "To" values.'); - return; + this.displayRecords(); + // this.updatePageInfo(); + } catch (error) { + console.error('Failed to fetch records', error); + alert('please enter values in the range (0-999999)') + + throw error; } - - if (typeof fromVal === 'string' && typeof toVal === 'string') { - const from = parseInt(fromVal, 10); - const to = parseInt(toVal, 10); - - if (isNaN(from) || isNaN(to)) { - $('#spinner').hide(); - $('.top').show(); - alert('Please enter valid numeric values.'); - return; } - - if (from > to || to - from > 10000) { - $('#spinner').hide(); - $('.top').show(); - alert('Please enter a valid range (1-999999) with a maximum of 10000 records.'); - return; + + private async fetchData(url: string): Promise { + try { + $('#overlay').show(); + const response = await $.ajax({ + url, + method: 'GET', + }); + $('#overlay').hide(); + return response; + } catch (error) { + throw error; + } } - if (!isNaN(from) && !isNaN(to)) { - $.ajax({ - url: `http://localhost:2050/records?from=${from}&to=${to}`, - method: 'GET', - success: (response) => { - let newData = JSON.parse(response); - console.table(newData); - if (Array.isArray(newData)) { - data = []; - for (let i = 0; i < newData.length; i++) { - const record = newData[i]; - if (Array.isArray(record)) { - const obj: GridData = {}; - for (let j = 0; j < columnNames.length && j < record.length; j++) { - const columnName = columnNames[j].name; - const columnValue = record[j]; - obj[columnName] = columnValue; // Map column names to their corresponding values. - } - data.push(obj); - } - } - createGrid(); // Update the grid with the new data - console.log(data); - - } - }, - error: () => { - console.error('Failed to fetch records'); - $('#spinner').hide() - $('.top').show() - alert('Please enter a valid range! (1-999999)'); - }, - complete: () => { - $('#spinner').hide(); - $('.top').show(); - } - }); - } + + private setupControls() { + $('#prevBtn').on('click', () => this.handlePageChange(-1)); + $('#nextBtn').on('click', () => this.handlePageChange(1)); + $(window).on('resize', debounce(this.handleResize, 350)); + + + } + + private handlePageChange(delta: number) { + const totalPages = Math.ceil(this.totalItems / this.pageSize); + const newPage = this.currentPage + delta; + + if (newPage >= 1 && newPage <= totalPages) { + this.currentPage = newPage; + this.fetchRecords().then(this.displayRecords); + this.updatePageInfo(); } } - - // Render the grid with the fetched data. - function createGrid() { + + private handleResize = () => { + const newWindowHeight = Math.floor($(window).innerHeight() as number); + const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; + + if (newGridSize >= 0) { + this.pageSize = newGridSize; + this.fetchRecords().then(this.displayRecords); + this.adjustGridHeight(); + } + }; + + + + private displayRecords = () => { + const gridTemplate = new GridTemplate(this.columnNames, this.data); + gridTemplate.displayRecords(); + this.updatePageInfo(); + + }; + + updatePageInfo() { + const totalPages = Math.ceil(this.totalItems / this.pageSize); + const pageInfo = `Page ${this.currentPage} of ${totalPages}`; + const from = (this.currentPage - 1) * this.pageSize; + const to = (this.currentPage)*this.pageSize + $('#pageInfo').text(pageInfo); + $('.records').text(`Showing records ${from} to ${to}`); + } +} + +class GridTemplate { + private columnNames: ColumnName[] = []; + private dataRecords: GridData[] = []; + + constructor(columnNames: ColumnName[], dataRecords: GridData[]) { + this.columnNames = columnNames; + this.dataRecords = dataRecords; + } + + setDataRecords(dataRecords: GridData[]): void { + this.dataRecords = dataRecords; + } + + displayRecords(): void { const gridElement = document.getElementById('grid'); if (gridElement) { - gridElement.innerHTML = ''; // Clear the existing grid . - - // Create table element + gridElement.innerHTML = ''; + const table = document.createElement('table'); - - // Create table header const thead = document.createElement('thead'); const headerRow = document.createElement('tr'); - columnNames.forEach((column) => { + this.columnNames.forEach((column) => { const th = document.createElement('th'); th.textContent = column.name; headerRow.appendChild(th); }); thead.appendChild(headerRow); table.appendChild(thead); - - // Create table body + const tbody = document.createElement('tbody'); - data.forEach((row) => { + this.dataRecords.forEach((row) => { const tr = document.createElement('tr'); - columnNames.forEach(async (column) => { + this.columnNames.forEach((column) => { const td = document.createElement('td'); - td.textContent = await(row[column.name]); + td.textContent = row[column.name]; tr.appendChild(td); }); tbody.appendChild(tr); }); table.appendChild(tbody); - - // Append the table to the grid element gridElement.appendChild(table); + } - updatePageInfo(); - - } + +} - // Set up page controls with event handlers. - function setupControls() { - $('#prevBtn').on('click', () => { - if (currentPage > 1) { - currentPage--; // Go to the previous page. - fetchRecords(); // Fetch records for the new page. - } - }); - - $('#nextBtn').on('click', () => { - const totalPages = Math.ceil(totalItems / PAGE_SIZE); - if (currentPage < totalPages) { - currentPage++; // Go to the next page. - fetchRecords(); - } +const PAGE_SIZE = 35; +const gridRatio = 0.46; +const rowHeight = 16; + +function debounce any>(func: F, waitFor: number) { + let timeout: number; + + return (...args: Parameters): Promise> => { + clearTimeout(timeout); + + return new Promise((resolve) => { + timeout = setTimeout(() => { + resolve(func(...args)); + }, waitFor); }); - } + }; +} + +$(document).ready(() => { + const windowHeight = Math.floor($(window).innerHeight() as number); + const initialGridSize = Math.floor((windowHeight * gridRatio) / rowHeight) - 1; + const apidata = new ApiData(initialGridSize); + $('#searchBtn').on('click', () => { + const to = parseInt($('#toInput').val() as string); + + if (!isNaN(to)) { + const from = Math.max(0, to - apidata.pageSize + 1); + apidata.searchRecords(from, to); + // const totalPages = Math.ceil(apidata.totalItems / apidata.pageSize); + // apidata.currentPage = Math.ceil(to / apidata.pageSize); + // apidata.currentPage = Math.min(apidata.currentPage, totalPages); + // apidata.updatePageInfo(); + } + $('#toInput').val(''); + }); + + + apidata.initialize(); + const overlay = $('
'); + $('body').append(overlay); +}); + - // Update the page information display. - function updatePageInfo() { - const totalPages = Math.ceil(totalItems / PAGE_SIZE); - const pageInfo = `Page ${currentPage} of ${totalPages}`; - $('#pageInfo').text(pageInfo); // Update the page information text. - } - // reload page - function pageReload(){ - location.reload(); - } - \ No newline at end of file diff --git a/index.html b/index.html index b35cf5dc..9dc16040 100644 --- a/index.html +++ b/index.html @@ -10,24 +10,24 @@
-
-
- - - -
+ +
+
+ + + +
+ + +
+ +
diff --git a/package.json b/package.json index 229da5c7..ff2e01bb 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build":"tsc", - "start": "npm run build" + "start": "npm run build -- -w" }, "repository": { "type": "git", From 68d6fdc9987a2af308224426de80d0566c443d40 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 21 Aug 2023 16:42:01 +0200 Subject: [PATCH 09/90] grid has range (0-999999) and some search functionality --- Styles.css | 5 ++-- app.ts | 73 +++++++++++++++++++++++++++++++++--------------------- index.html | 2 +- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/Styles.css b/Styles.css index 345198ac..1cc135b0 100644 --- a/Styles.css +++ b/Styles.css @@ -20,7 +20,7 @@ body, html { border-collapse: collapse; } tr{ - border: 1px solid #333333; + border: 1px solid #333333; } thead{ background-color: #333333e0; @@ -30,6 +30,7 @@ thead{ td { padding: 2px; + align-content: center; width: 350px; } @@ -58,7 +59,7 @@ td { color: white; background-color: #5a5f61; } -#toInput{ +#fromInput{ padding: 5px 10px; margin: 0 5px; background-color: #232425; diff --git a/app.ts b/app.ts index d5c16847..c232bf98 100644 --- a/app.ts +++ b/app.ts @@ -13,7 +13,7 @@ class ApiData { totalItems: number = 0; columnNames: ColumnName[] = []; maxGridHeight: number = 0; - + constructor(pageSize: number) { this.pageSize = pageSize; @@ -22,7 +22,7 @@ class ApiData { async initialize() { try { this.adjustGridHeight(); - await this.recordCount(); + await this.recordCount(); await this.fetchColumns(); await this.fetchRecords(); this.setupControls(); @@ -94,7 +94,6 @@ class ApiData { try { // Display spinner while fetching records $('#spinner').show(); - const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); const res = JSON.parse(response); this.data = res.map((record: any) => { @@ -104,17 +103,18 @@ class ApiData { const columnValue = record[j]; obj[columnName] = columnValue; } + return obj; }); $('#spinner').hide(); this.displayRecords(); - // this.updatePageInfo(); + + } catch (error) { console.error('Failed to fetch records', error); alert('please enter values in the range (0-999999)') - - throw error; + return; } } @@ -136,33 +136,36 @@ class ApiData { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); $(window).on('resize', debounce(this.handleResize, 350)); - } private handlePageChange(delta: number) { const totalPages = Math.ceil(this.totalItems / this.pageSize); const newPage = this.currentPage + delta; - if (newPage >= 1 && newPage <= totalPages) { this.currentPage = newPage; this.fetchRecords().then(this.displayRecords); this.updatePageInfo(); + } } private handleResize = () => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; - if (newGridSize >= 0) { this.pageSize = newGridSize; + // console.log(newGridSize); + this.updatePageInfo(); this.fetchRecords().then(this.displayRecords); this.adjustGridHeight(); + }else if (newGridSize > this.maxGridHeight){ + } + + }; - private displayRecords = () => { const gridTemplate = new GridTemplate(this.columnNames, this.data); @@ -170,15 +173,17 @@ class ApiData { this.updatePageInfo(); }; + updatePageInfo() { const totalPages = Math.ceil(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; const from = (this.currentPage - 1) * this.pageSize; - const to = (this.currentPage)*this.pageSize + const to = (this.currentPage)*(this.pageSize) $('#pageInfo').text(pageInfo); $('.records').text(`Showing records ${from} to ${to}`); } + } class GridTemplate { @@ -194,11 +199,10 @@ class GridTemplate { this.dataRecords = dataRecords; } - displayRecords(): void { + displayRecords(): void { const gridElement = document.getElementById('grid'); if (gridElement) { gridElement.innerHTML = ''; - const table = document.createElement('table'); const thead = document.createElement('thead'); const headerRow = document.createElement('tr'); @@ -228,8 +232,7 @@ class GridTemplate { } -const PAGE_SIZE = 35; -const gridRatio = 0.46; +const gridRatio = 0.45; const rowHeight = 16; function debounce any>(func: F, waitFor: number) { @@ -249,20 +252,35 @@ function debounce any>(func: F, waitFor: number) { $(document).ready(() => { const windowHeight = Math.floor($(window).innerHeight() as number); const initialGridSize = Math.floor((windowHeight * gridRatio) / rowHeight) - 1; - const apidata = new ApiData(initialGridSize); + const apidata = new ApiData(initialGridSize); + $('#searchBtn').on('click', () => { - const to = parseInt($('#toInput').val() as string); - - if (!isNaN(to)) { - const from = Math.max(0, to - apidata.pageSize + 1); - apidata.searchRecords(from, to); - // const totalPages = Math.ceil(apidata.totalItems / apidata.pageSize); - // apidata.currentPage = Math.ceil(to / apidata.pageSize); - // apidata.currentPage = Math.min(apidata.currentPage, totalPages); - // apidata.updatePageInfo(); + const from = parseInt($('#fromInput').val() as string); + const pageSize = apidata.pageSize; + const maxRange = apidata.totalItems - 1; + + if (!isNaN(from) && from >= 0 && from <= maxRange) { + let to = Math.min(from + pageSize - 1, maxRange); + let adjustedFrom = from; + if (adjustedFrom + pageSize > maxRange) { + adjustedFrom = Math.max(0, maxRange - pageSize); + to = maxRange; + } + apidata.searchRecords(adjustedFrom, to); + + + }else if (from < 0 || from > maxRange) { + alert('please enter values in the range (0-999999)'); + return; + }else if (isNaN(from)){ + alert('Please enter a numerical value ') + }else{ + console.error('error') } - $('#toInput').val(''); - }); + + $('#fromInput').val('') ; + +}); apidata.initialize(); @@ -271,4 +289,3 @@ $(document).ready(() => { }); - diff --git a/index.html b/index.html index 9dc16040..f8297ef6 100644 --- a/index.html +++ b/index.html @@ -23,7 +23,7 @@
- +
From 55906080f37302d5b93bbbde2052f165916a604f Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 24 Aug 2023 14:35:31 +0200 Subject: [PATCH 10/90] stationary firstVal and improved search functionality --- Styles.css | 7 +- app.ts | 489 +++++++++++++++++++++++++++++------------------------ 2 files changed, 272 insertions(+), 224 deletions(-) diff --git a/Styles.css b/Styles.css index 1cc135b0..3c86c274 100644 --- a/Styles.css +++ b/Styles.css @@ -30,7 +30,7 @@ thead{ td { padding: 2px; - + border: 1px solid #333333; align-content: center; width: 350px; } @@ -75,7 +75,10 @@ td { z-index: 1000; display: none; } - +.highlighted-row{ + background-color:#464e53; + color: black; +} diff --git a/app.ts b/app.ts index c232bf98..321355d9 100644 --- a/app.ts +++ b/app.ts @@ -1,291 +1,336 @@ +// Interface to define the structure of grid data interface GridData { [key: string]: any; -} - -interface ColumnName { + } + // Interface to define column names + interface ColumnName { name: string; -} - -class ApiData { + } + // Class to manage data fetching and grid operations + class ApiData { pageSize: number; currentPage: number = 1; data: GridData[] = []; totalItems: number = 0; columnNames: ColumnName[] = []; maxGridHeight: number = 0; + firstVal : number = 0; + lastVal!: number; - + + constructor(pageSize: number) { - this.pageSize = pageSize; + this.pageSize = pageSize; + } - + + + // Initialize method to set up the grid async initialize() { - try { - this.adjustGridHeight(); - await this.recordCount(); - await this.fetchColumns(); - await this.fetchRecords(); - this.setupControls(); - - - } catch (error) { - console.error('Error during initialization:', error); - } + try { + this.adjustGridHeight(); + await this.recordCount(); + await this.fetchColumns(); + await this.fetchRecords(); + this.setupControls(); + } catch (error) { + console.error('Error during initialization:', error); } - + } + // Method to fetch total record count from the server async recordCount() { - try { - const response = await this.fetchData('http://localhost:2050/recordCount'); - this.totalItems = response; - } catch (error) { - console.error('Failed to fetch record count', error); - throw error; - } + try { + const response = await this.fetchData('http://localhost:2050/recordCount'); + this.totalItems = response; + } catch (error) { + console.error('Failed to fetch record count', error); + throw error; } - + } + //fectch column names async fetchColumns() { - try { - const response = await this.fetchData('http://localhost:2050/columns'); - const res = JSON.parse(response); - this.columnNames = res.map((columnName: any) => ({ name: columnName })); - this.data = new Array(this.columnNames.length); - } catch (error) { - console.error('Failed to fetch columns', error); - throw error; - } + try { + const response = await this.fetchData('http://localhost:2050/columns'); + const res = JSON.parse(response); + this.columnNames = res.map((columnName: any) => ({ name: columnName })); + this.data = new Array(this.columnNames.length); + } catch (error) { + console.error('Failed to fetch columns', error); + throw error; } - + } + //get records from API + async fetchAndProcessRecords(from: number = this.firstVal, to: number =this.lastVal) { + try { + $('#spinner').show() + $('#grid').hide() + const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); + const res = JSON.parse(response); + + + const processedData = res.map((record: any) => { + const obj: GridData = {}; + for (let j = 0; j < this.columnNames.length && j < record.length; j++) { + const columnName = this.columnNames[j].name; + const columnValue = record[j]; + obj[columnName] = columnValue; + } + return obj; + + + }); + $('#spinner').hide(); + $('#grid').show() + return processedData; + } catch (error) { + console.error('Failed to fetch records', error); + throw error; + } + } + + async fetchRecords() { + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize , maxRange); + if (to >= maxRange) { + this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page + to = maxRange; + } try { - const from = (this.currentPage - 1) * this.pageSize; - const to = from + this.pageSize; - // Display spinner while fetching records - $('#spinner').show(); - const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); - const res = JSON.parse(response); - this.data = res.map((record: any) => { - const obj: GridData = {}; - for (let j = 0; j < this.columnNames.length && j < record.length; j++) { - const columnName = this.columnNames[j].name; - const columnValue = record[j]; - obj[columnName] = columnValue; - } - return obj; - }); - $('#spinner').hide(); - this.displayRecords(); + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.displayRecords(); + this.updatePageInfo(); } catch (error) { - console.error('Failed to fetch records', error); - throw error; - } - } - adjustGridHeight() { - const gridElement = document.getElementById('grid'); - const pageCntrl = $('.grid-controls').innerHeight(); - const screenHeight = $(window).innerHeight(); - if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { - this.maxGridHeight = screenHeight - pageCntrl; - gridElement.style.height = `${this.maxGridHeight}px`; - gridElement.style.overflow = 'none'; + alert('failed to fetch records '); } } - async searchRecords(from: number, to: number) { + + async searchRecords(searchValue: number) { try { - // Display spinner while fetching records - $('#spinner').show(); - const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); - const res = JSON.parse(response); - this.data = res.map((record: any) => { - const obj: GridData = {}; - for (let j = 0; j < this.columnNames.length && j < record.length; j++) { - const columnName = this.columnNames[j].name; - const columnValue = record[j]; - obj[columnName] = columnValue; - } - - return obj; - }); + const maxRange = this.totalItems - 1; // Maximum allowed Value - $('#spinner').hide(); - this.displayRecords(); - - + if (searchValue >= 0 && searchValue <= maxRange) { + const from = searchValue; + const to = Math.min(from + this.pageSize, maxRange); + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.currentPage = Math.floor(from / this.pageSize) + this.firstVal = from; // Set firstVal to searched value + this.lastVal = from + this.pageSize ; // Calculate lastVal based on pageSize + this.displayRecords(); + this.updatePageInfo(); + } else { + alert('Please enter values in the range (0-999999)'); + } } catch (error) { - console.error('Failed to fetch records', error); - alert('please enter values in the range (0-999999)') - return; + alert('Failed to fetch records'); } } - + adjustGridHeight() { + const gridElement = document.getElementById('grid'); + const pageCntrl = $('.grid-controls').innerHeight(); + const screenHeight = $(window).innerHeight(); + if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { + this.maxGridHeight = screenHeight - pageCntrl; + gridElement.style.height = `${this.maxGridHeight}px`; + gridElement.style.overflow = 'none'; + } + } + + private async fetchData(url: string): Promise { - try { - $('#overlay').show(); - const response = await $.ajax({ - url, - method: 'GET', - }); - $('#overlay').hide(); - return response; - } catch (error) { - throw error; - } + try { + $('#overlay').show(); + const response = await $.ajax({ + url, + method: 'GET', + }); + $('#overlay').hide(); + return response; + } catch (error) { + throw error; } - + } + + private setupControls() { - $('#prevBtn').on('click', () => this.handlePageChange(-1)); - $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize, 350)); - + $('#prevBtn').on('click', () => this.handlePageChange(-1)); + $('#nextBtn').on('click', () => this.handlePageChange(1)); + $(window).on('resize', debounce(this.handleResize, 350)); } - + + + private handlePageChange(delta: number) { - const totalPages = Math.ceil(this.totalItems / this.pageSize); - const newPage = this.currentPage + delta; - if (newPage >= 1 && newPage <= totalPages) { - this.currentPage = newPage; - this.fetchRecords().then(this.displayRecords); - this.updatePageInfo(); - + const newFirstVal = this.firstVal + delta * this.pageSize; + if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { + this.firstVal = newFirstVal; + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); } } - + + private handleResize = () => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; + if (newGridSize >= 0) { - this.pageSize = newGridSize; - // console.log(newGridSize); + const oldPageSize = this.pageSize; + const totalPages = Math.floor(this.totalItems / oldPageSize); + const newPageSize = newGridSize; + + const newPageNumber = Math.max(1, Math.floor(this.firstVal / oldPageSize) + 1); // Ensure newPageNumber is at least 1 + + let newFirstValueIndex = this.firstVal; + + // Adjust firstVal for the first page and the last page + if ( newFirstValueIndex + newPageSize > this.totalItems) { + newFirstValueIndex = Math.max(0, this.totalItems - newPageSize); + }else if(newPageNumber === 1){ + newFirstValueIndex= 0 + } + + // Update firstVal, lastVal, and page size + this.pageSize = newPageSize; + this.firstVal = newFirstValueIndex; + this.lastVal = newFirstValueIndex + newPageSize - 1; + + // Fetch records, update page info, and adjust grid height + this.fetchRecords(); this.updatePageInfo(); - this.fetchRecords().then(this.displayRecords); this.adjustGridHeight(); - }else if (newGridSize > this.maxGridHeight){ - } - - - }; - - + } + + private displayRecords = () => { - const gridTemplate = new GridTemplate(this.columnNames, this.data); - gridTemplate.displayRecords(); - this.updatePageInfo(); - + const gridTemplate = new GridTemplate(this.columnNames, this.data); + gridTemplate.displayRecords(); + this.updatePageInfo(); }; - - updatePageInfo() { - const totalPages = Math.ceil(this.totalItems / this.pageSize); + + updatePageInfo() { + const totalPages = Math.floor(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; - const from = (this.currentPage - 1) * this.pageSize; - const to = (this.currentPage)*(this.pageSize) - $('#pageInfo').text(pageInfo); + const from = this.firstVal; + this.lastVal = from + this.pageSize; + const to = this.lastVal; + $('#pageInfo').text(`${pageInfo}`); $('.records').text(`Showing records ${from} to ${to}`); } } + + class GridTemplate { + -class GridTemplate { private columnNames: ColumnName[] = []; private dataRecords: GridData[] = []; - + + constructor(columnNames: ColumnName[], dataRecords: GridData[]) { - this.columnNames = columnNames; - this.dataRecords = dataRecords; + this.columnNames = columnNames; + this.dataRecords = dataRecords; } - + + setDataRecords(dataRecords: GridData[]): void { - this.dataRecords = dataRecords; + this.dataRecords = dataRecords; } - - displayRecords(): void { - const gridElement = document.getElementById('grid'); - if (gridElement) { - gridElement.innerHTML = ''; - const table = document.createElement('table'); - const thead = document.createElement('thead'); - const headerRow = document.createElement('tr'); - this.columnNames.forEach((column) => { - const th = document.createElement('th'); - th.textContent = column.name; - headerRow.appendChild(th); - }); - thead.appendChild(headerRow); - table.appendChild(thead); - - const tbody = document.createElement('tbody'); - this.dataRecords.forEach((row) => { - const tr = document.createElement('tr'); - this.columnNames.forEach((column) => { - const td = document.createElement('td'); - td.textContent = row[column.name]; - tr.appendChild(td); - }); - tbody.appendChild(tr); - }); - table.appendChild(tbody); - gridElement.appendChild(table); - - } + + //dispplay records in a grid in table format + displayRecords(): void { + const gridElement = document.getElementById('grid'); + if (gridElement) { + gridElement.innerHTML = ''; + const table = document.createElement('table'); + const thead = document.createElement('thead'); + const headerRow = document.createElement('tr'); + this.columnNames.forEach((column) => { + const th = document.createElement('th'); + th.textContent = column.name; + headerRow.appendChild(th); + }); + thead.appendChild(headerRow); + table.appendChild(thead); + + + const tbody = document.createElement('tbody'); + this.dataRecords.forEach((row) => { + const tr = document.createElement('tr'); + this.columnNames.forEach((column) => { + const td = document.createElement('td'); + td.textContent = row[column.name]; + tr.appendChild(td); + }); + tbody.appendChild(tr); + }); + table.appendChild(tbody); + gridElement.appendChild(table); } - -} - -const gridRatio = 0.45; -const rowHeight = 16; - -function debounce any>(func: F, waitFor: number) { + } + } + + + const gridRatio = 0.45; + const rowHeight = 16; + + function debounce any>(func: F, waitFor: number) { let timeout: number; - + + return (...args: Parameters): Promise> => { - clearTimeout(timeout); - - return new Promise((resolve) => { - timeout = setTimeout(() => { - resolve(func(...args)); - }, waitFor); - }); + clearTimeout(timeout); + + + return new Promise((resolve) => { + timeout = setTimeout(() => { + resolve(func(...args)); + }, waitFor); + }); }; -} - -$(document).ready(() => { + } + // Wait for the document to be ready + $(document).ready(() => { const windowHeight = Math.floor($(window).innerHeight() as number); const initialGridSize = Math.floor((windowHeight * gridRatio) / rowHeight) - 1; - const apidata = new ApiData(initialGridSize); - + const apidata = new ApiData(initialGridSize); + // Set up search button click handler $('#searchBtn').on('click', () => { - const from = parseInt($('#fromInput').val() as string); - const pageSize = apidata.pageSize; - const maxRange = apidata.totalItems - 1; - - if (!isNaN(from) && from >= 0 && from <= maxRange) { - let to = Math.min(from + pageSize - 1, maxRange); - let adjustedFrom = from; - if (adjustedFrom + pageSize > maxRange) { - adjustedFrom = Math.max(0, maxRange - pageSize); - to = maxRange; - } - apidata.searchRecords(adjustedFrom, to); - - - }else if (from < 0 || from > maxRange) { - alert('please enter values in the range (0-999999)'); - return; - }else if (isNaN(from)){ - alert('Please enter a numerical value ') - }else{ - console.error('error') - } - - $('#fromInput').val('') ; + const from = parseInt($('#fromInput').val() as string); + const pageSize = apidata.pageSize; + console.log(pageSize); + const maxRange = apidata.totalItems - 1; + if (!isNaN(from) && from >= 0 && from <= maxRange) { + let to = Math.min(from + pageSize , maxRange); + let adjustedFrom = from; + if (adjustedFrom + pageSize > maxRange) { + adjustedFrom = Math.max(0, maxRange - pageSize); + to = maxRange; + } + apidata.searchRecords(adjustedFrom); + }else if (from < 0 || from > maxRange) { + alert('please enter values in the range (0-999999)'); + return; + }else if (isNaN(from)){ + alert('Please enter a numerical value ') + }else{ + console.error('error') + } -}); - - + $('#fromInput').val('') ; + }); + // Initialize the grid apidata.initialize(); const overlay = $('
'); $('body').append(overlay); -}); - - + }); + + + From 92c2304619f863bf57a95794b1b326cc46caa76a Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 24 Aug 2023 16:16:20 +0200 Subject: [PATCH 11/90] firstpage functionality improved --- app.ts | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/app.ts b/app.ts index 321355d9..6e755428 100644 --- a/app.ts +++ b/app.ts @@ -8,6 +8,7 @@ interface GridData { } // Class to manage data fetching and grid operations class ApiData { + // Properties to manage data and settings pageSize: number; currentPage: number = 1; data: GridData[] = []; @@ -59,15 +60,13 @@ interface GridData { throw error; } } - //get records from API + //get records from API for fetch an search functionality async fetchAndProcessRecords(from: number = this.firstVal, to: number =this.lastVal) { try { $('#spinner').show() $('#grid').hide() const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); const res = JSON.parse(response); - - const processedData = res.map((record: any) => { const obj: GridData = {}; for (let j = 0; j < this.columnNames.length && j < record.length; j++) { @@ -76,8 +75,6 @@ interface GridData { obj[columnName] = columnValue; } return obj; - - }); $('#spinner').hide(); $('#grid').show() @@ -111,18 +108,18 @@ interface GridData { async searchRecords(searchValue: number) { try { const maxRange = this.totalItems - 1; // Maximum allowed Value - if (searchValue >= 0 && searchValue <= maxRange) { const from = searchValue; const to = Math.min(from + this.pageSize, maxRange); const processedData = await this.fetchAndProcessRecords(from, to); this.data = processedData; - this.currentPage = Math.floor(from / this.pageSize) + this.currentPage = Math.floor(from / this.pageSize) +1 this.firstVal = from; // Set firstVal to searched value this.lastVal = from + this.pageSize ; // Calculate lastVal based on pageSize this.displayRecords(); this.updatePageInfo(); - } else { + } + else { alert('Please enter values in the range (0-999999)'); } } catch (error) { @@ -139,8 +136,7 @@ interface GridData { gridElement.style.overflow = 'none'; } } - - + private async fetchData(url: string): Promise { try { $('#overlay').show(); @@ -171,6 +167,11 @@ interface GridData { this.lastVal = this.firstVal + this.pageSize - 1; this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchRecords(); + }else if (newFirstVal <= this.pageSize){ + this.firstVal = 0 + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); } } @@ -218,15 +219,15 @@ interface GridData { updatePageInfo() { const totalPages = Math.floor(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; + const maxRange = this.totalItems - 1; const from = this.firstVal; - this.lastVal = from + this.pageSize; - const to = this.lastVal; + let to = Math.min(from + this.pageSize , maxRange); $('#pageInfo').text(`${pageInfo}`); $('.records').text(`Showing records ${from} to ${to}`); } } - +// Class to manage the grid template and display records class GridTemplate { @@ -244,7 +245,7 @@ interface GridData { this.dataRecords = dataRecords; } - //dispplay records in a grid in table format + // Display records in a grid in table format displayRecords(): void { const gridElement = document.getElementById('grid'); if (gridElement) { @@ -258,9 +259,8 @@ interface GridData { headerRow.appendChild(th); }); thead.appendChild(headerRow); - table.appendChild(thead); - - + table.appendChild(thead); + // Create table body const tbody = document.createElement('tbody'); this.dataRecords.forEach((row) => { const tr = document.createElement('tr'); @@ -272,15 +272,16 @@ interface GridData { tbody.appendChild(tr); }); table.appendChild(tbody); + // Append the table to the grid element gridElement.appendChild(table); } } } - - const gridRatio = 0.45; + // Constants for grid calculation + const gridRatio = 0.45;// represents the ratio of the grid's height to the window's height. const rowHeight = 16; - + // Debounce utility function to limit function execution frequency function debounce any>(func: F, waitFor: number) { let timeout: number; @@ -298,6 +299,7 @@ interface GridData { } // Wait for the document to be ready $(document).ready(() => { + // Initialization and setup code const windowHeight = Math.floor($(window).innerHeight() as number); const initialGridSize = Math.floor((windowHeight * gridRatio) / rowHeight) - 1; const apidata = new ApiData(initialGridSize); From 0449b6f626281b1a514ff048a749bfdfacbd91cf Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 24 Aug 2023 16:19:01 +0200 Subject: [PATCH 12/90] firstpage functionality improved --- Styles.css | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Styles.css b/Styles.css index 3c86c274..dc027f0f 100644 --- a/Styles.css +++ b/Styles.css @@ -34,7 +34,6 @@ td { align-content: center; width: 350px; } - .grid-controls { display: flex; align-items: center; @@ -45,8 +44,6 @@ td { position: sticky; bottom: 0; } - - #pageInfo{ font-weight: 350; background-color: #333333; @@ -75,10 +72,7 @@ td { z-index: 1000; display: none; } -.highlighted-row{ - background-color:#464e53; - color: black; -} + From 18305eeceb9c3da1bfd3a71eff4e6aff8f3ac3fe Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 25 Aug 2023 09:21:23 +0200 Subject: [PATCH 13/90] mmm --- app.ts | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/app.ts b/app.ts index 6e755428..a4b6797a 100644 --- a/app.ts +++ b/app.ts @@ -8,7 +8,7 @@ interface GridData { } // Class to manage data fetching and grid operations class ApiData { - // Properties to manage data and settings + // Properties to manage data and settings pageSize: number; currentPage: number = 1; data: GridData[] = []; @@ -149,7 +149,7 @@ interface GridData { } catch (error) { throw error; } - } +} private setupControls() { @@ -179,28 +179,22 @@ interface GridData { private handleResize = () => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; - + if (newGridSize >= 0) { const oldPageSize = this.pageSize; - const totalPages = Math.floor(this.totalItems / oldPageSize); const newPageSize = newGridSize; - - const newPageNumber = Math.max(1, Math.floor(this.firstVal / oldPageSize) + 1); // Ensure newPageNumber is at least 1 - + const newPageNumber = Math.max(1, Math.floor(this.firstVal / oldPageSize) + 1); // +1 to ensure newPageNumber is at least 1 let newFirstValueIndex = this.firstVal; - // Adjust firstVal for the first page and the last page if ( newFirstValueIndex + newPageSize > this.totalItems) { newFirstValueIndex = Math.max(0, this.totalItems - newPageSize); }else if(newPageNumber === 1){ newFirstValueIndex= 0 } - // Update firstVal, lastVal, and page size this.pageSize = newPageSize; this.firstVal = newFirstValueIndex; this.lastVal = newFirstValueIndex + newPageSize - 1; - // Fetch records, update page info, and adjust grid height this.fetchRecords(); this.updatePageInfo(); @@ -214,8 +208,8 @@ interface GridData { gridTemplate.displayRecords(); this.updatePageInfo(); }; - - + + // Update the page information and records display based on the current state of the grid. updatePageInfo() { const totalPages = Math.floor(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; @@ -224,27 +218,18 @@ interface GridData { let to = Math.min(from + this.pageSize , maxRange); $('#pageInfo').text(`${pageInfo}`); $('.records').text(`Showing records ${from} to ${to}`); - } - + } } // Class to manage the grid template and display records class GridTemplate { - - private columnNames: ColumnName[] = []; private dataRecords: GridData[] = []; - - + // Initializes the column names and data records that will be used to display records in the grid. constructor(columnNames: ColumnName[], dataRecords: GridData[]) { this.columnNames = columnNames; this.dataRecords = dataRecords; } - - - setDataRecords(dataRecords: GridData[]): void { - this.dataRecords = dataRecords; - } - + // Display records in a grid in table format displayRecords(): void { const gridElement = document.getElementById('grid'); @@ -325,11 +310,12 @@ interface GridData { }else{ console.error('error') } - + //empty search input after searching $('#fromInput').val('') ; }); // Initialize the grid apidata.initialize(); + //overlay when the page is still getting ready const overlay = $('
'); $('body').append(overlay); }); From 07b2e55899217459144b3f55fd4e6ccb43adb598 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 25 Aug 2023 09:24:00 +0200 Subject: [PATCH 14/90] mmm --- app.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app.ts b/app.ts index a4b6797a..fe268f25 100644 --- a/app.ts +++ b/app.ts @@ -292,7 +292,6 @@ interface GridData { $('#searchBtn').on('click', () => { const from = parseInt($('#fromInput').val() as string); const pageSize = apidata.pageSize; - console.log(pageSize); const maxRange = apidata.totalItems - 1; if (!isNaN(from) && from >= 0 && from <= maxRange) { let to = Math.min(from + pageSize , maxRange); From 46fa399e8f9e100a66e2f9fb06e974f947832ad4 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 25 Aug 2023 14:05:58 +0200 Subject: [PATCH 15/90] final --- Styles.css | 102 ++++++++++++++++++++++++++--------------------------- app.ts | 24 ++++--------- 2 files changed, 57 insertions(+), 69 deletions(-) diff --git a/Styles.css b/Styles.css index dc027f0f..3e876c60 100644 --- a/Styles.css +++ b/Styles.css @@ -1,62 +1,65 @@ /* body with overflow hidden for no scrolling */ -body, html { - margin: 0; - padding: 0; - height: 100%; - overflow: hidden; - font-family: system-ui; - } - .grid-container{ - text-align: center; - } - #grid { - border: 1px solid #333333; - text-align: center; - background-color: #d4e1e7; - } - /* table */ - table{ - height: 100%; - border-collapse: collapse; - } -tr{ - border: 1px solid #333333; - } -thead{ - background-color: #333333e0; - font-weight: bold; - color: white; - } - +body, +html { + margin: 0; + padding: 0; + height: 100%; + overflow: hidden; + font-family: system-ui; +} +.grid-container { + text-align: center; +} +#grid { + border: 1px solid #333333; + text-align: center; + background-color: #d4e1e7; +} +/* table */ +table { + height: 100%; + border-collapse: collapse; +} +tr { + border: 1px solid #333333; +} +thead { + background-color: #333333; + font-weight: 450; + color: white; + padding: 2px; +} + td { - padding: 2px; - border: 1px solid #333333; - align-content: center; - width: 350px; + padding: 2px; + border: 1px solid #333333; + align-content: center; + width: 350px; } .grid-controls { - display: flex; - align-items: center; - justify-content: center; - padding: 10px; - color: white; - background-color: #333333; - position: sticky; - bottom: 0; - } -#pageInfo{ - font-weight: 350; - background-color: #333333; + display: flex; + align-items: center; + justify-content: center; + padding: 10px; + color: white; + background-color: #333333; + position: sticky; + bottom: 0; +} +#pageInfo , .records { + font-weight: 450; + background-color: #333333; } + /* button */ -.btn{ +.btn { cursor: pointer; padding: 5px 10px; margin: 0 5px; color: white; background-color: #5a5f61; } -#fromInput{ +#fromInput { padding: 5px 10px; margin: 0 5px; background-color: #232425; @@ -72,8 +75,3 @@ td { z-index: 1000; display: none; } - - - - - diff --git a/app.ts b/app.ts index fe268f25..c9caf39b 100644 --- a/app.ts +++ b/app.ts @@ -18,14 +18,10 @@ interface GridData { firstVal : number = 0; lastVal!: number; - - constructor(pageSize: number) { this.pageSize = pageSize; } - - // Initialize method to set up the grid async initialize() { try { @@ -113,7 +109,7 @@ interface GridData { const to = Math.min(from + this.pageSize, maxRange); const processedData = await this.fetchAndProcessRecords(from, to); this.data = processedData; - this.currentPage = Math.floor(from / this.pageSize) +1 + this.currentPage = Math.ceil(from / this.pageSize) +1 this.firstVal = from; // Set firstVal to searched value this.lastVal = from + this.pageSize ; // Calculate lastVal based on pageSize this.displayRecords(); @@ -181,16 +177,12 @@ interface GridData { const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; if (newGridSize >= 0) { - const oldPageSize = this.pageSize; const newPageSize = newGridSize; - const newPageNumber = Math.max(1, Math.floor(this.firstVal / oldPageSize) + 1); // +1 to ensure newPageNumber is at least 1 let newFirstValueIndex = this.firstVal; - // Adjust firstVal for the first page and the last page + // Adjust firstVal for the last page if ( newFirstValueIndex + newPageSize > this.totalItems) { - newFirstValueIndex = Math.max(0, this.totalItems - newPageSize); - }else if(newPageNumber === 1){ - newFirstValueIndex= 0 - } + newFirstValueIndex = Math.max( this.totalItems - newPageSize); + } // Update firstVal, lastVal, and page size this.pageSize = newPageSize; this.firstVal = newFirstValueIndex; @@ -264,17 +256,15 @@ interface GridData { } // Constants for grid calculation - const gridRatio = 0.45;// represents the ratio of the grid's height to the window's height. + const gridRatio = 9/20;// represents the ratio of the grid's height to the window's height. const rowHeight = 16; // Debounce utility function to limit function execution frequency function debounce any>(func: F, waitFor: number) { let timeout: number; - - + return (...args: Parameters): Promise> => { clearTimeout(timeout); - - + return new Promise((resolve) => { timeout = setTimeout(() => { resolve(func(...args)); From 120a175027ffc7241b5dde5efa083ace4b02344a Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 25 Aug 2023 14:06:33 +0200 Subject: [PATCH 16/90] final --- Styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Styles.css b/Styles.css index 3e876c60..11602d93 100644 --- a/Styles.css +++ b/Styles.css @@ -27,7 +27,7 @@ thead { background-color: #333333; font-weight: 450; color: white; - padding: 2px; + } td { From 0250464ad75046e317c6264cf583427068da48c5 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 28 Aug 2023 10:13:27 +0200 Subject: [PATCH 17/90] formatting added --- Styles.css | 5 +- app.ts | 605 ++++++++++++++++++++++++++--------------------------- 2 files changed, 304 insertions(+), 306 deletions(-) diff --git a/Styles.css b/Styles.css index 11602d93..b21dd165 100644 --- a/Styles.css +++ b/Styles.css @@ -1,4 +1,4 @@ -/* body with overflow hidden for no scrolling */ + body, html { margin: 0; @@ -46,7 +46,8 @@ td { position: sticky; bottom: 0; } -#pageInfo , .records { +#pageInfo , +.records { font-weight: 450; background-color: #333333; } diff --git a/app.ts b/app.ts index c9caf39b..8fe52776 100644 --- a/app.ts +++ b/app.ts @@ -1,313 +1,310 @@ // Interface to define the structure of grid data interface GridData { - [key: string]: any; - } - // Interface to define column names - interface ColumnName { - name: string; - } - // Class to manage data fetching and grid operations - class ApiData { - // Properties to manage data and settings - pageSize: number; - currentPage: number = 1; - data: GridData[] = []; - totalItems: number = 0; - columnNames: ColumnName[] = []; - maxGridHeight: number = 0; - firstVal : number = 0; - lastVal!: number; - - constructor(pageSize: number) { - this.pageSize = pageSize; - - } - // Initialize method to set up the grid - async initialize() { - try { - this.adjustGridHeight(); - await this.recordCount(); - await this.fetchColumns(); - await this.fetchRecords(); - this.setupControls(); - } catch (error) { - console.error('Error during initialization:', error); - } - } - // Method to fetch total record count from the server - async recordCount() { - try { - const response = await this.fetchData('http://localhost:2050/recordCount'); - this.totalItems = response; - } catch (error) { - console.error('Failed to fetch record count', error); - throw error; - } - } - //fectch column names - async fetchColumns() { - try { - const response = await this.fetchData('http://localhost:2050/columns'); - const res = JSON.parse(response); - this.columnNames = res.map((columnName: any) => ({ name: columnName })); - this.data = new Array(this.columnNames.length); - } catch (error) { - console.error('Failed to fetch columns', error); - throw error; - } - } - //get records from API for fetch an search functionality - async fetchAndProcessRecords(from: number = this.firstVal, to: number =this.lastVal) { - try { - $('#spinner').show() - $('#grid').hide() - const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); - const res = JSON.parse(response); - const processedData = res.map((record: any) => { - const obj: GridData = {}; - for (let j = 0; j < this.columnNames.length && j < record.length; j++) { - const columnName = this.columnNames[j].name; - const columnValue = record[j]; - obj[columnName] = columnValue; - } - return obj; - }); - $('#spinner').hide(); - $('#grid').show() - return processedData; - } catch (error) { - console.error('Failed to fetch records', error); - throw error; - } - } - - - async fetchRecords() { - const maxRange = this.totalItems - 1; - const from = this.firstVal; - let to = Math.min(from + this.pageSize , maxRange); - if (to >= maxRange) { - this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page - to = maxRange; - } - try { - const processedData = await this.fetchAndProcessRecords(from, to); - this.data = processedData; - this.displayRecords(); - this.updatePageInfo(); - } catch (error) { - alert('failed to fetch records '); - } - } - - - async searchRecords(searchValue: number) { - try { - const maxRange = this.totalItems - 1; // Maximum allowed Value - if (searchValue >= 0 && searchValue <= maxRange) { - const from = searchValue; - const to = Math.min(from + this.pageSize, maxRange); - const processedData = await this.fetchAndProcessRecords(from, to); - this.data = processedData; - this.currentPage = Math.ceil(from / this.pageSize) +1 - this.firstVal = from; // Set firstVal to searched value - this.lastVal = from + this.pageSize ; // Calculate lastVal based on pageSize - this.displayRecords(); - this.updatePageInfo(); - } - else { - alert('Please enter values in the range (0-999999)'); - } - } catch (error) { - alert('Failed to fetch records'); - } - } - adjustGridHeight() { - const gridElement = document.getElementById('grid'); - const pageCntrl = $('.grid-controls').innerHeight(); - const screenHeight = $(window).innerHeight(); - if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { - this.maxGridHeight = screenHeight - pageCntrl; - gridElement.style.height = `${this.maxGridHeight}px`; - gridElement.style.overflow = 'none'; - } - } - - private async fetchData(url: string): Promise { - try { - $('#overlay').show(); - const response = await $.ajax({ - url, - method: 'GET', - }); - $('#overlay').hide(); - return response; - } catch (error) { - throw error; - } + [key: string]: any; +} +// Interface to define column names +interface ColumnName { + name: string; } - - - private setupControls() { - $('#prevBtn').on('click', () => this.handlePageChange(-1)); - $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize, 350)); - } - - - - private handlePageChange(delta: number) { - const newFirstVal = this.firstVal + delta * this.pageSize; - if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { - this.firstVal = newFirstVal; - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - }else if (newFirstVal <= this.pageSize){ - this.firstVal = 0 - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - } - } - - - private handleResize = () => { - const newWindowHeight = Math.floor($(window).innerHeight() as number); - const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; - - if (newGridSize >= 0) { - const newPageSize = newGridSize; - let newFirstValueIndex = this.firstVal; - // Adjust firstVal for the last page - if ( newFirstValueIndex + newPageSize > this.totalItems) { - newFirstValueIndex = Math.max( this.totalItems - newPageSize); - } - // Update firstVal, lastVal, and page size - this.pageSize = newPageSize; - this.firstVal = newFirstValueIndex; - this.lastVal = newFirstValueIndex + newPageSize - 1; - // Fetch records, update page info, and adjust grid height - this.fetchRecords(); - this.updatePageInfo(); - this.adjustGridHeight(); - } - } - - - private displayRecords = () => { - const gridTemplate = new GridTemplate(this.columnNames, this.data); - gridTemplate.displayRecords(); - this.updatePageInfo(); - }; +// Class to manage data fetching and grid operations +class ApiData { + // Properties to manage data and settings + pageSize: number; + currentPage: number = 1; + data: GridData[] = []; + totalItems: number = 0; + columnNames: ColumnName[] = []; + maxGridHeight: number = 0; + firstVal: number = 0; + lastVal!: number; + + constructor(pageSize: number) { + this.pageSize = pageSize; + + } + // Initialize method to set up the grid + async initialize() { + try { + this.adjustGridHeight(); + await this.recordCount(); + await this.fetchColumns(); + await this.fetchRecords(); + this.setupControls(); + } catch (error) { + console.error('Error during initialization:', error); + } + } + // Method to fetch total record count from the server + async recordCount() { + try { + const response = await this.fetchData('http://localhost:2050/recordCount'); + this.totalItems = response; + } catch (error) { + console.error('Failed to fetch record count', error); + throw error; + } + } + //fectch column names + async fetchColumns() { + try { + const response = await this.fetchData('http://localhost:2050/columns'); + const res = JSON.parse(response); + this.columnNames = res.map((columnName: any) => ({ name: columnName })); + this.data = new Array(this.columnNames.length); + } catch (error) { + console.error('Failed to fetch columns', error); + throw error; + } + } + //get records from API for fetch an search functionality + async fetchAndProcessRecords(from: number = this.firstVal, to: number = this.lastVal) { + try { + $('#spinner').show() + $('#grid').hide() + const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); + const res = JSON.parse(response); + const processedData = res.map((record: any) => { + const obj: GridData = {}; + for (let j = 0; j < this.columnNames.length && j < record.length; j++) { + const columnName = this.columnNames[j].name; + const columnValue = record[j]; + obj[columnName] = columnValue; + } + return obj; + }); + $('#spinner').hide(); + $('#grid').show() + return processedData; + } catch (error) { + console.error('Failed to fetch records', error); + throw error; + } + } + + + async fetchRecords() { + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize, maxRange); + if (to >= maxRange) { + this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page + to = maxRange; + } + try { + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.displayRecords(); + this.updatePageInfo(); + } catch (error) { + alert('failed to fetch records '); + } + } + + + async searchRecords(searchValue: number) { + try { + const maxRange = this.totalItems - 1; // Maximum allowed Value + if (searchValue >= 0 && searchValue <= maxRange) { + const from = searchValue; + const to = Math.min(from + this.pageSize, maxRange); + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.currentPage = Math.ceil(from / this.pageSize) + 1 + this.firstVal = from; // Set firstVal to searched value + this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize + this.displayRecords(); + this.updatePageInfo(); + } + else { + alert('Please enter values in the range (0-999999)'); + } + } catch (error) { + alert('Failed to fetch records'); + } + } + adjustGridHeight() { + const gridElement = document.getElementById('grid'); + const pageCntrl = $('.grid-controls').innerHeight(); + const screenHeight = $(window).innerHeight(); + if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { + this.maxGridHeight = screenHeight - pageCntrl; + gridElement.style.height = `${this.maxGridHeight}px`; + gridElement.style.overflow = 'none'; + } + } + + private async fetchData(url: string): Promise { + try { + $('#overlay').show(); + const response = await $.ajax({ + url, + method: 'GET', + }); + $('#overlay').hide(); + return response; + } catch (error) { + throw error; + } + } + + + private setupControls() { + $('#prevBtn').on('click', () => this.handlePageChange(-1)); + $('#nextBtn').on('click', () => this.handlePageChange(1)); + $(window).on('resize', debounce(this.handleResize, 350)); + } - // Update the page information and records display based on the current state of the grid. - updatePageInfo() { - const totalPages = Math.floor(this.totalItems / this.pageSize); - const pageInfo = `Page ${this.currentPage} of ${totalPages}`; - const maxRange = this.totalItems - 1; - const from = this.firstVal; - let to = Math.min(from + this.pageSize , maxRange); - $('#pageInfo').text(`${pageInfo}`); - $('.records').text(`Showing records ${from} to ${to}`); - } + + + private handlePageChange(delta: number) { + const newFirstVal = this.firstVal + delta * this.pageSize; + if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { + this.firstVal = newFirstVal; + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + } else if (newFirstVal <= this.pageSize) { + this.firstVal = 0 + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + } + } + + + private handleResize = () => { + const newWindowHeight = Math.floor($(window).innerHeight() as number); + const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; + + if (newGridSize >= 0) { + const newPageSize = newGridSize; + let newFirstValueIndex = this.firstVal; + // Adjust firstVal for the last page + if (newFirstValueIndex + newPageSize > this.totalItems) { + newFirstValueIndex = Math.max(this.totalItems - newPageSize); + } + // Update firstVal, lastVal, and page size + this.pageSize = newPageSize; + this.firstVal = newFirstValueIndex; + this.lastVal = newFirstValueIndex + newPageSize - 1; + // Fetch records, update page info, and adjust grid height + this.fetchRecords(); + this.updatePageInfo(); + this.adjustGridHeight(); + } + } + + + private displayRecords = () => { + const gridTemplate = new GridTemplate(this.columnNames, this.data); + gridTemplate.displayRecords(); + this.updatePageInfo(); + }; + + // Update the page information and records display based on the current state of the grid. + updatePageInfo() { + const totalPages = Math.floor(this.totalItems / this.pageSize); + const pageInfo = `Page ${this.currentPage} of ${totalPages}`; + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize, maxRange); + $('#pageInfo').text(`${pageInfo}`); + $('.records').text(`Showing records ${from} to ${to}`); + } } // Class to manage the grid template and display records - class GridTemplate { - private columnNames: ColumnName[] = []; - private dataRecords: GridData[] = []; - // Initializes the column names and data records that will be used to display records in the grid. - constructor(columnNames: ColumnName[], dataRecords: GridData[]) { - this.columnNames = columnNames; - this.dataRecords = dataRecords; - } +class GridTemplate { + private columnNames: ColumnName[] = []; + private dataRecords: GridData[] = []; + // Initializes the column names and data records that will be used to display records in the grid. + constructor(columnNames: ColumnName[], dataRecords: GridData[]) { + this.columnNames = columnNames; + this.dataRecords = dataRecords; + } - // Display records in a grid in table format - displayRecords(): void { - const gridElement = document.getElementById('grid'); - if (gridElement) { - gridElement.innerHTML = ''; - const table = document.createElement('table'); - const thead = document.createElement('thead'); - const headerRow = document.createElement('tr'); - this.columnNames.forEach((column) => { - const th = document.createElement('th'); - th.textContent = column.name; - headerRow.appendChild(th); - }); - thead.appendChild(headerRow); - table.appendChild(thead); - // Create table body - const tbody = document.createElement('tbody'); - this.dataRecords.forEach((row) => { - const tr = document.createElement('tr'); - this.columnNames.forEach((column) => { - const td = document.createElement('td'); - td.textContent = row[column.name]; - tr.appendChild(td); - }); - tbody.appendChild(tr); - }); - table.appendChild(tbody); - // Append the table to the grid element - gridElement.appendChild(table); - } - } - } - - // Constants for grid calculation - const gridRatio = 9/20;// represents the ratio of the grid's height to the window's height. - const rowHeight = 16; - // Debounce utility function to limit function execution frequency - function debounce any>(func: F, waitFor: number) { - let timeout: number; + // Display records in a grid in table format + displayRecords(): void { + const gridElement = document.getElementById('grid'); + if (gridElement) { + gridElement.innerHTML = ''; + const table = document.createElement('table'); + const thead = document.createElement('thead'); + const headerRow = document.createElement('tr'); + this.columnNames.forEach((column) => { + const th = document.createElement('th'); + th.textContent = column.name; + headerRow.appendChild(th); + }); + thead.appendChild(headerRow); + table.appendChild(thead); + // Create table body + const tbody = document.createElement('tbody'); + this.dataRecords.forEach((row) => { + const tr = document.createElement('tr'); + this.columnNames.forEach((column) => { + const td = document.createElement('td'); + td.textContent = row[column.name]; + tr.appendChild(td); + }); + tbody.appendChild(tr); + }); + table.appendChild(tbody); + // Append the table to the grid element + gridElement.appendChild(table); + } + } +} - return (...args: Parameters): Promise> => { - clearTimeout(timeout); +// Constants for grid calculation +const gridRatio = 9 / 20;// represents the ratio of the grid's height to the window's height. +const rowHeight = 16; +// Debounce utility function to limit function execution frequency +function debounce any>(func: F, waitFor: number) { + let timeout: number; - return new Promise((resolve) => { - timeout = setTimeout(() => { - resolve(func(...args)); - }, waitFor); - }); - }; - } - // Wait for the document to be ready - $(document).ready(() => { - // Initialization and setup code - const windowHeight = Math.floor($(window).innerHeight() as number); - const initialGridSize = Math.floor((windowHeight * gridRatio) / rowHeight) - 1; - const apidata = new ApiData(initialGridSize); - // Set up search button click handler - $('#searchBtn').on('click', () => { - const from = parseInt($('#fromInput').val() as string); - const pageSize = apidata.pageSize; - const maxRange = apidata.totalItems - 1; - if (!isNaN(from) && from >= 0 && from <= maxRange) { - let to = Math.min(from + pageSize , maxRange); - let adjustedFrom = from; - if (adjustedFrom + pageSize > maxRange) { - adjustedFrom = Math.max(0, maxRange - pageSize); - to = maxRange; - } - apidata.searchRecords(adjustedFrom); - }else if (from < 0 || from > maxRange) { - alert('please enter values in the range (0-999999)'); - return; - }else if (isNaN(from)){ - alert('Please enter a numerical value ') - }else{ - console.error('error') - } - //empty search input after searching - $('#fromInput').val('') ; - }); - // Initialize the grid - apidata.initialize(); - //overlay when the page is still getting ready - const overlay = $('
'); - $('body').append(overlay); - }); - - - + return (...args: Parameters): Promise> => { + clearTimeout(timeout); + + return new Promise((resolve) => { + timeout = setTimeout(() => { + resolve(func(...args)); + }, waitFor); + }); + }; +} +// Wait for the document to be ready +$(document).ready(() => { + // Initialization and setup code + const windowHeight = Math.floor($(window).innerHeight() as number); + const initialGridSize = Math.floor((windowHeight * gridRatio) / rowHeight) - 1; + const apidata = new ApiData(initialGridSize); + // Set up search button click handler + $('#searchBtn').on('click', () => { + const from = parseInt($('#fromInput').val() as string); + const pageSize = apidata.pageSize; + const maxRange = apidata.totalItems - 1; + if (!isNaN(from) && from >= 0 && from <= maxRange) { + let to = Math.min(from + pageSize, maxRange); + let adjustedFrom = from; + if (adjustedFrom + pageSize > maxRange) { + adjustedFrom = Math.max(0, maxRange - pageSize); + to = maxRange; + } + apidata.searchRecords(adjustedFrom); + } else if (from < 0 || from > maxRange) { + alert('please enter values in the range (0-999999)'); + return; + } else if (isNaN(from)) { + alert('Please enter a numerical value ') + } else { + console.error('error') + } + //empty search input after searching + $('#fromInput').val(''); + }); + // Initialize the grid + apidata.initialize(); + //overlay when the page is still getting ready + const overlay = $('
'); + $('body').append(overlay); +}); From e79a514e5641965b7e8f5a4873f8141ac1d30e77 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 28 Aug 2023 10:27:00 +0200 Subject: [PATCH 18/90] formatting added --- app.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/app.ts b/app.ts index 8fe52776..4fea7e68 100644 --- a/app.ts +++ b/app.ts @@ -56,7 +56,7 @@ class ApiData { throw error; } } - //get records from API for fetch an search functionality + //get records from API for fetch and search functionality async fetchAndProcessRecords(from: number = this.firstVal, to: number = this.lastVal) { try { $('#spinner').show() @@ -80,8 +80,6 @@ class ApiData { throw error; } } - - async fetchRecords() { const maxRange = this.totalItems - 1; const from = this.firstVal; @@ -99,8 +97,6 @@ class ApiData { alert('failed to fetch records '); } } - - async searchRecords(searchValue: number) { try { const maxRange = this.totalItems - 1; // Maximum allowed Value @@ -132,7 +128,6 @@ class ApiData { gridElement.style.overflow = 'none'; } } - private async fetchData(url: string): Promise { try { $('#overlay').show(); @@ -146,16 +141,11 @@ class ApiData { throw error; } } - - private setupControls() { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); $(window).on('resize', debounce(this.handleResize, 350)); } - - - private handlePageChange(delta: number) { const newFirstVal = this.firstVal + delta * this.pageSize; if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { @@ -170,8 +160,6 @@ class ApiData { this.fetchRecords(); } } - - private handleResize = () => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; @@ -193,14 +181,11 @@ class ApiData { this.adjustGridHeight(); } } - - private displayRecords = () => { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); this.updatePageInfo(); }; - // Update the page information and records display based on the current state of the grid. updatePageInfo() { const totalPages = Math.floor(this.totalItems / this.pageSize); @@ -254,7 +239,6 @@ class GridTemplate { } } } - // Constants for grid calculation const gridRatio = 9 / 20;// represents the ratio of the grid's height to the window's height. const rowHeight = 16; From 8df21a6ddb1c746885548449ed316963b3fbc444 Mon Sep 17 00:00:00 2001 From: Ntobe99 <114492950+Ntobe99@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:20:54 +0200 Subject: [PATCH 19/90] Update app.ts --- app.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app.ts b/app.ts index 4fea7e68..8054c99c 100644 --- a/app.ts +++ b/app.ts @@ -206,7 +206,6 @@ class GridTemplate { this.columnNames = columnNames; this.dataRecords = dataRecords; } - // Display records in a grid in table format displayRecords(): void { const gridElement = document.getElementById('grid'); From b2c0a700e2ecad3319c53fd4fc2f112deb079712 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 28 Aug 2023 11:51:11 +0200 Subject: [PATCH 20/90] script tag moved to head tag --- app.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app.ts b/app.ts index 4fea7e68..8fe52776 100644 --- a/app.ts +++ b/app.ts @@ -56,7 +56,7 @@ class ApiData { throw error; } } - //get records from API for fetch and search functionality + //get records from API for fetch an search functionality async fetchAndProcessRecords(from: number = this.firstVal, to: number = this.lastVal) { try { $('#spinner').show() @@ -80,6 +80,8 @@ class ApiData { throw error; } } + + async fetchRecords() { const maxRange = this.totalItems - 1; const from = this.firstVal; @@ -97,6 +99,8 @@ class ApiData { alert('failed to fetch records '); } } + + async searchRecords(searchValue: number) { try { const maxRange = this.totalItems - 1; // Maximum allowed Value @@ -128,6 +132,7 @@ class ApiData { gridElement.style.overflow = 'none'; } } + private async fetchData(url: string): Promise { try { $('#overlay').show(); @@ -141,11 +146,16 @@ class ApiData { throw error; } } + + private setupControls() { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); $(window).on('resize', debounce(this.handleResize, 350)); } + + + private handlePageChange(delta: number) { const newFirstVal = this.firstVal + delta * this.pageSize; if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { @@ -160,6 +170,8 @@ class ApiData { this.fetchRecords(); } } + + private handleResize = () => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; @@ -181,11 +193,14 @@ class ApiData { this.adjustGridHeight(); } } + + private displayRecords = () => { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); this.updatePageInfo(); }; + // Update the page information and records display based on the current state of the grid. updatePageInfo() { const totalPages = Math.floor(this.totalItems / this.pageSize); @@ -239,6 +254,7 @@ class GridTemplate { } } } + // Constants for grid calculation const gridRatio = 9 / 20;// represents the ratio of the grid's height to the window's height. const rowHeight = 16; From 75c52e0d357d6a8621d25d811c6989020730e4c6 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 08:02:27 +0200 Subject: [PATCH 21/90] some changes --- app.ts | 113 ++++++++++++++++++++------------------------------- index.html | 10 +---- package.json | 46 ++++++++++----------- 3 files changed, 68 insertions(+), 101 deletions(-) diff --git a/app.ts b/app.ts index 7f846175..9155b300 100644 --- a/app.ts +++ b/app.ts @@ -1,26 +1,21 @@ -// Interface to define the structure of grid data -interface GridData { - [key: string]: any; -} -// Interface to define column names interface ColumnName { name: string; } -// Class to manage data fetching and grid operations -class ApiData { +interface GridData { + [key: string]: any; +}class ApiData { // Properties to manage data and settings pageSize: number; - currentPage: number = 1; + currentPage = 1; data: GridData[] = []; - totalItems: number = 0; + totalItems = 0; columnNames: ColumnName[] = []; - maxGridHeight: number = 0; - firstVal: number = 0; - lastVal!: number; + maxGridHeight = 0; + firstVal = 0; + lastVal: number | undefined; constructor(pageSize: number) { this.pageSize = pageSize; - } // Initialize method to set up the grid async initialize() { @@ -35,54 +30,51 @@ class ApiData { } } // Method to fetch total record count from the server - async recordCount() { + async recordCount(): Promise { try { const response = await this.fetchData('http://localhost:2050/recordCount'); this.totalItems = response; } catch (error) { - console.error('Failed to fetch record count', error); - throw error; + throw new Error('Failed to fetch record count'); } } //fectch column names - async fetchColumns() { + async fetchColumns() : Promise{ try { const response = await this.fetchData('http://localhost:2050/columns'); const res = JSON.parse(response); this.columnNames = res.map((columnName: any) => ({ name: columnName })); this.data = new Array(this.columnNames.length); } catch (error) { - console.error('Failed to fetch columns', error); - throw error; - } - } + throw new Error('Failed to fetch record count'); + }} //get records from API for fetch an search functionality - async fetchAndProcessRecords(from: number = this.firstVal, to: number = this.lastVal) { + async fetchAndProcessRecords(from: number = this.firstVal, to: number) : Promise { try { $('#spinner').show() - $('#grid').hide() + $('#grid').hide(); const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); const res = JSON.parse(response); const processedData = res.map((record: any) => { const obj: GridData = {}; - for (let j = 0; j < this.columnNames.length && j < record.length; j++) { - const columnName = this.columnNames[j].name; - const columnValue = record[j]; - obj[columnName] = columnValue; + let columnIndex = 0; + for (const column of this.columnNames) { + if (columnIndex < record.length) { + const columnName = column.name; + const columnValue = record[columnIndex]; + obj[columnName] = columnValue; + } + columnIndex++; } return obj; }); $('#spinner').hide(); - $('#grid').show() + $('#grid').show(); return processedData; } catch (error) { - console.error('Failed to fetch records', error); - throw error; - } - } - - - async fetchRecords() { + throw new Error('Failed to fetch records'); + }} + async fetchRecords(): Promise { const maxRange = this.totalItems - 1; const from = this.firstVal; let to = Math.min(from + this.pageSize, maxRange); @@ -99,9 +91,7 @@ class ApiData { alert('failed to fetch records '); } } - - - async searchRecords(searchValue: number) { + async searchRecords(searchValue: number): Promise { try { const maxRange = this.totalItems - 1; // Maximum allowed Value if (searchValue >= 0 && searchValue <= maxRange) { @@ -114,15 +104,13 @@ class ApiData { this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize this.displayRecords(); this.updatePageInfo(); - } - else { + } else { alert('Please enter values in the range (0-999999)'); } } catch (error) { - alert('Failed to fetch records'); - } - } - adjustGridHeight() { + throw new Error('Failed to search value'); + }} + adjustGridHeight(): void { const gridElement = document.getElementById('grid'); const pageCntrl = $('.grid-controls').innerHeight(); const screenHeight = $(window).innerHeight(); @@ -130,9 +118,7 @@ class ApiData { this.maxGridHeight = screenHeight - pageCntrl; gridElement.style.height = `${this.maxGridHeight}px`; gridElement.style.overflow = 'none'; - } - } - + }} private async fetchData(url: string): Promise { try { $('#overlay').show(); @@ -144,19 +130,13 @@ class ApiData { return response; } catch (error) { throw error; - } - } - - - private setupControls() { + }} + private setupControls(): void{ $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); $(window).on('resize', debounce(this.handleResize, 350)); } - - - - private handlePageChange(delta: number) { + private handlePageChange(delta: number): void { const newFirstVal = this.firstVal + delta * this.pageSize; if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { this.firstVal = newFirstVal; @@ -170,9 +150,7 @@ class ApiData { this.fetchRecords(); } } - - - private handleResize = () => { + private handleResize = (): void => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; @@ -193,16 +171,13 @@ class ApiData { this.adjustGridHeight(); } } - - - private displayRecords = () => { + private displayRecords = () : void => { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); this.updatePageInfo(); }; - // Update the page information and records display based on the current state of the grid. - updatePageInfo() { + updatePageInfo(): void { const totalPages = Math.floor(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; const maxRange = this.totalItems - 1; @@ -212,7 +187,6 @@ class ApiData { $('.records').text(`Showing records ${from} to ${to}`); } } -// Class to manage the grid template and display records class GridTemplate { private columnNames: ColumnName[] = []; private dataRecords: GridData[] = []; @@ -253,12 +227,8 @@ class GridTemplate { } } } - -// Constants for grid calculation -const gridRatio = 9 / 20;// represents the ratio of the grid's height to the window's height. -const rowHeight = 16; // Debounce utility function to limit function execution frequency -function debounce any>(func: F, waitFor: number) { +export function debounce any>(func: F, waitFor: number) { let timeout: number; return (...args: Parameters): Promise> => { @@ -271,6 +241,9 @@ function debounce any>(func: F, waitFor: number) { }); }; } +// Constants for grid calculation +const gridRatio = 9 / 20;// represents the ratio of the grid's height to the window's height. +const rowHeight = 16; // Wait for the document to be ready $(document).ready(() => { // Initialization and setup code diff --git a/index.html b/index.html index f8297ef6..e86d2572 100644 --- a/index.html +++ b/index.html @@ -4,9 +4,8 @@ JS Onboard Project - + -
@@ -16,7 +15,6 @@
-
@@ -25,11 +23,7 @@
-
- +
- - - diff --git a/package.json b/package.json index ff2e01bb..a4e866f9 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,25 @@ { - "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": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build":"tsc", - "start": "npm run build -- -w" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Ntobe99/onboard-javascript.git" - }, - "author": "", - "license": "ISC", - "bugs": { - "url": "https://github.com/Ntobe99/onboard-javascript/issues" - }, - "homepage": "https://github.com/Ntobe99/onboard-javascript#readme", - "dependencies": { - "@types/jquery": "^3.5.16", - "typescript": "^3.8.3" - } + "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": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsc", + "start": "npm run build -- -w" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Ntobe99/onboard-javascript.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/Ntobe99/onboard-javascript/issues" + }, + "homepage": "https://github.com/Ntobe99/onboard-javascript#readme", + "dependencies": { + "@types/jquery": "^3.5.16", + "typescript": "^3.8.3" + } } From 5c1408bc95ff3cae61f5dbfc4a0540e02eed50cb Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 08:14:48 +0200 Subject: [PATCH 22/90] update --- app.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app.ts b/app.ts index 9155b300..a66fc228 100644 --- a/app.ts +++ b/app.ts @@ -27,15 +27,14 @@ interface GridData { this.setupControls(); } catch (error) { console.error('Error during initialization:', error); - } - } + }} // Method to fetch total record count from the server async recordCount(): Promise { try { const response = await this.fetchData('http://localhost:2050/recordCount'); this.totalItems = response; } catch (error) { - throw new Error('Failed to fetch record count'); + throw new Error('Failed to fetch record count.'); } } //fectch column names @@ -46,7 +45,7 @@ interface GridData { this.columnNames = res.map((columnName: any) => ({ name: columnName })); this.data = new Array(this.columnNames.length); } catch (error) { - throw new Error('Failed to fetch record count'); + throw new Error('Failed to fetch columns.'); }} //get records from API for fetch an search functionality async fetchAndProcessRecords(from: number = this.firstVal, to: number) : Promise { @@ -88,7 +87,7 @@ interface GridData { this.displayRecords(); this.updatePageInfo(); } catch (error) { - alert('failed to fetch records '); + throw new Error('Failed to fetch records') } } async searchRecords(searchValue: number): Promise { @@ -109,7 +108,8 @@ interface GridData { } } catch (error) { throw new Error('Failed to search value'); - }} + } + } adjustGridHeight(): void { const gridElement = document.getElementById('grid'); const pageCntrl = $('.grid-controls').innerHeight(); @@ -118,7 +118,8 @@ interface GridData { this.maxGridHeight = screenHeight - pageCntrl; gridElement.style.height = `${this.maxGridHeight}px`; gridElement.style.overflow = 'none'; - }} + } + } private async fetchData(url: string): Promise { try { $('#overlay').show(); From ee7b2ca17e6362b561f7b77ee638e9b6080d0335 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 08:27:02 +0200 Subject: [PATCH 23/90] update --- app.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app.ts b/app.ts index a66fc228..fb229b65 100644 --- a/app.ts +++ b/app.ts @@ -16,7 +16,7 @@ interface GridData { constructor(pageSize: number) { this.pageSize = pageSize; - } + }; // Initialize method to set up the grid async initialize() { try { @@ -80,7 +80,7 @@ interface GridData { if (to >= maxRange) { this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page to = maxRange; - } + }; try { const processedData = await this.fetchAndProcessRecords(from, to); this.data = processedData; @@ -120,7 +120,7 @@ interface GridData { gridElement.style.overflow = 'none'; } } - private async fetchData(url: string): Promise { + private async fetchData(url: string):Promise { try { $('#overlay').show(); const response = await $.ajax({ From 07261b4af7015ae547482240d2b40573bb9c98db Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 08:31:25 +0200 Subject: [PATCH 24/90] update --- app.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app.ts b/app.ts index fb229b65..41e325b7 100644 --- a/app.ts +++ b/app.ts @@ -27,9 +27,10 @@ interface GridData { this.setupControls(); } catch (error) { console.error('Error during initialization:', error); - }} + } + } // Method to fetch total record count from the server - async recordCount(): Promise { + async recordCount(): Promise { try { const response = await this.fetchData('http://localhost:2050/recordCount'); this.totalItems = response; @@ -38,7 +39,7 @@ interface GridData { } } //fectch column names - async fetchColumns() : Promise{ + async fetchColumns(): Promise { try { const response = await this.fetchData('http://localhost:2050/columns'); const res = JSON.parse(response); @@ -46,9 +47,10 @@ interface GridData { this.data = new Array(this.columnNames.length); } catch (error) { throw new Error('Failed to fetch columns.'); - }} + } + } //get records from API for fetch an search functionality - async fetchAndProcessRecords(from: number = this.firstVal, to: number) : Promise { + async fetchAndProcessRecords(from: number = this.firstVal, to: number): Promise { try { $('#spinner').show() $('#grid').hide(); @@ -72,7 +74,8 @@ interface GridData { return processedData; } catch (error) { throw new Error('Failed to fetch records'); - }} + } + } async fetchRecords(): Promise { const maxRange = this.totalItems - 1; const from = this.firstVal; @@ -120,7 +123,7 @@ interface GridData { gridElement.style.overflow = 'none'; } } - private async fetchData(url: string):Promise { + private async fetchData(url: string): Promise { try { $('#overlay').show(); const response = await $.ajax({ @@ -131,8 +134,9 @@ interface GridData { return response; } catch (error) { throw error; - }} - private setupControls(): void{ + } + } + private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); $(window).on('resize', debounce(this.handleResize, 350)); @@ -151,7 +155,7 @@ interface GridData { this.fetchRecords(); } } - private handleResize = (): void => { + private handleResize = (): void => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; @@ -172,7 +176,7 @@ interface GridData { this.adjustGridHeight(); } } - private displayRecords = () : void => { + private displayRecords = (): void => { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); this.updatePageInfo(); From 5a64f0bc29bc2c3fc7425fcd4fa75629e82af11f Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 08:38:23 +0200 Subject: [PATCH 25/90] update --- app.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app.ts b/app.ts index 41e325b7..562fe004 100644 --- a/app.ts +++ b/app.ts @@ -76,6 +76,7 @@ interface GridData { throw new Error('Failed to fetch records'); } } + //fetch records from api async fetchRecords(): Promise { const maxRange = this.totalItems - 1; const from = this.firstVal; @@ -93,6 +94,7 @@ interface GridData { throw new Error('Failed to fetch records') } } + //funtion to search through records using fromID async searchRecords(searchValue: number): Promise { try { const maxRange = this.totalItems - 1; // Maximum allowed Value @@ -113,6 +115,7 @@ interface GridData { throw new Error('Failed to search value'); } } + //chnge grid height according to screen size adjustGridHeight(): void { const gridElement = document.getElementById('grid'); const pageCntrl = $('.grid-controls').innerHeight(); @@ -123,6 +126,7 @@ interface GridData { gridElement.style.overflow = 'none'; } } + // use Ajax for data fetching private async fetchData(url: string): Promise { try { $('#overlay').show(); From b92adc395117bc99920e7ca7abe55dc0ac7fd480 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 08:48:27 +0200 Subject: [PATCH 26/90] update --- app.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app.ts b/app.ts index 562fe004..b7a8880d 100644 --- a/app.ts +++ b/app.ts @@ -28,7 +28,7 @@ interface GridData { } catch (error) { console.error('Error during initialization:', error); } - } + }; // Method to fetch total record count from the server async recordCount(): Promise { try { @@ -37,7 +37,7 @@ interface GridData { } catch (error) { throw new Error('Failed to fetch record count.'); } - } + }; //fectch column names async fetchColumns(): Promise { try { @@ -48,7 +48,7 @@ interface GridData { } catch (error) { throw new Error('Failed to fetch columns.'); } - } + }; //get records from API for fetch an search functionality async fetchAndProcessRecords(from: number = this.firstVal, to: number): Promise { try { @@ -75,7 +75,7 @@ interface GridData { } catch (error) { throw new Error('Failed to fetch records'); } - } + }; //fetch records from api async fetchRecords(): Promise { const maxRange = this.totalItems - 1; @@ -93,7 +93,7 @@ interface GridData { } catch (error) { throw new Error('Failed to fetch records') } - } + }; //funtion to search through records using fromID async searchRecords(searchValue: number): Promise { try { @@ -114,7 +114,7 @@ interface GridData { } catch (error) { throw new Error('Failed to search value'); } - } + }; //chnge grid height according to screen size adjustGridHeight(): void { const gridElement = document.getElementById('grid'); @@ -125,7 +125,7 @@ interface GridData { gridElement.style.height = `${this.maxGridHeight}px`; gridElement.style.overflow = 'none'; } - } + }; // use Ajax for data fetching private async fetchData(url: string): Promise { try { @@ -139,12 +139,12 @@ interface GridData { } catch (error) { throw error; } - } + }; private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); $(window).on('resize', debounce(this.handleResize, 350)); - } + }; private handlePageChange(delta: number): void { const newFirstVal = this.firstVal + delta * this.pageSize; if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { @@ -158,7 +158,7 @@ interface GridData { this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchRecords(); } - } + }; private handleResize = (): void => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; @@ -179,7 +179,7 @@ interface GridData { this.updatePageInfo(); this.adjustGridHeight(); } - } + }; private displayRecords = (): void => { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); @@ -194,7 +194,7 @@ interface GridData { let to = Math.min(from + this.pageSize, maxRange); $('#pageInfo').text(`${pageInfo}`); $('.records').text(`Showing records ${from} to ${to}`); - } + }; } class GridTemplate { private columnNames: ColumnName[] = []; @@ -203,7 +203,7 @@ class GridTemplate { constructor(columnNames: ColumnName[], dataRecords: GridData[]) { this.columnNames = columnNames; this.dataRecords = dataRecords; - } + }; // Display records in a grid in table format displayRecords(): void { const gridElement = document.getElementById('grid'); @@ -234,10 +234,10 @@ class GridTemplate { // Append the table to the grid element gridElement.appendChild(table); } - } + }; } // Debounce utility function to limit function execution frequency -export function debounce any>(func: F, waitFor: number) { +function debounce any>(func: F, waitFor: number) { let timeout: number; return (...args: Parameters): Promise> => { @@ -249,7 +249,7 @@ export function debounce any>(func: F, waitFor: numb }, waitFor); }); }; -} +}; // Constants for grid calculation const gridRatio = 9 / 20;// represents the ratio of the grid's height to the window's height. const rowHeight = 16; @@ -279,7 +279,7 @@ $(document).ready(() => { alert('Please enter a numerical value ') } else { console.error('error') - } + }; //empty search input after searching $('#fromInput').val(''); }); From e06829bda7fdadf233a1a626ba31131ec62d2be6 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 09:31:47 +0200 Subject: [PATCH 27/90] update --- app.ts | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/app.ts b/app.ts index b7a8880d..39542a0b 100644 --- a/app.ts +++ b/app.ts @@ -1,9 +1,12 @@ +// Interface to define the structure of grid data interface ColumnName { name: string; } +// Interface to define column names interface GridData { [key: string]: any; -}class ApiData { +} +class ApiData { // Properties to manage data and settings pageSize: number; currentPage = 1; @@ -126,6 +129,16 @@ interface GridData { gridElement.style.overflow = 'none'; } }; + // Update the page information and records display based on the current state of the grid. + updatePageInfo(): void { + const totalPages = Math.floor(this.totalItems / this.pageSize); + const pageInfo = `Page ${this.currentPage} of ${totalPages}`; + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize, maxRange); + $('#pageInfo').text(`${pageInfo}`); + $('.records').text(`Showing records ${from} to ${to}`); + }; // use Ajax for data fetching private async fetchData(url: string): Promise { try { @@ -185,17 +198,8 @@ interface GridData { gridTemplate.displayRecords(); this.updatePageInfo(); }; - // Update the page information and records display based on the current state of the grid. - updatePageInfo(): void { - const totalPages = Math.floor(this.totalItems / this.pageSize); - const pageInfo = `Page ${this.currentPage} of ${totalPages}`; - const maxRange = this.totalItems - 1; - const from = this.firstVal; - let to = Math.min(from + this.pageSize, maxRange); - $('#pageInfo').text(`${pageInfo}`); - $('.records').text(`Showing records ${from} to ${to}`); - }; } +// Class to manage the grid template and display records class GridTemplate { private columnNames: ColumnName[] = []; private dataRecords: GridData[] = []; @@ -270,7 +274,7 @@ $(document).ready(() => { if (adjustedFrom + pageSize > maxRange) { adjustedFrom = Math.max(0, maxRange - pageSize); to = maxRange; - } + }; apidata.searchRecords(adjustedFrom); } else if (from < 0 || from > maxRange) { alert('please enter values in the range (0-999999)'); From b49d5a89989c8a45f2547c756479f3b0015ce471 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 12:25:30 +0200 Subject: [PATCH 28/90] no more any types --- app.ts | 10 +++++----- index.html | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app.ts b/app.ts index 39542a0b..99b133d6 100644 --- a/app.ts +++ b/app.ts @@ -21,7 +21,7 @@ class ApiData { this.pageSize = pageSize; }; // Initialize method to set up the grid - async initialize() { + async initialize(): Promise { try { this.adjustGridHeight(); await this.recordCount(); @@ -36,7 +36,7 @@ class ApiData { async recordCount(): Promise { try { const response = await this.fetchData('http://localhost:2050/recordCount'); - this.totalItems = response; + this.totalItems = typeof response === 'number' ? response : parseInt(response as string, 10); } catch (error) { throw new Error('Failed to fetch record count.'); } @@ -45,7 +45,7 @@ class ApiData { async fetchColumns(): Promise { try { const response = await this.fetchData('http://localhost:2050/columns'); - const res = JSON.parse(response); + const res = JSON.parse(response as string); this.columnNames = res.map((columnName: any) => ({ name: columnName })); this.data = new Array(this.columnNames.length); } catch (error) { @@ -58,7 +58,7 @@ class ApiData { $('#spinner').show() $('#grid').hide(); const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); - const res = JSON.parse(response); + const res = JSON.parse(response as string); const processedData = res.map((record: any) => { const obj: GridData = {}; let columnIndex = 0; @@ -140,7 +140,7 @@ class ApiData { $('.records').text(`Showing records ${from} to ${to}`); }; // use Ajax for data fetching - private async fetchData(url: string): Promise { + private async fetchData(url: string): Promise { try { $('#overlay').show(); const response = await $.ajax({ diff --git a/index.html b/index.html index e86d2572..84b290ac 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ JS Onboard Project - + @@ -24,6 +24,6 @@
-
+
From 28f1ff8d4f54b9c9559f29fa6276572a05017ae0 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 13:45:09 +0200 Subject: [PATCH 29/90] no more any types --- app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.ts b/app.ts index 99b133d6..4435ad09 100644 --- a/app.ts +++ b/app.ts @@ -170,7 +170,7 @@ class ApiData { this.lastVal = this.firstVal + this.pageSize - 1; this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchRecords(); - } + }; }; private handleResize = (): void => { const newWindowHeight = Math.floor($(window).innerHeight() as number); From 2a2a633929c569ee4103666c7a0b9ebd91c3f0be Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 29 Aug 2023 16:30:50 +0200 Subject: [PATCH 30/90] some changes --- app.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app.ts b/app.ts index 4435ad09..647d2ea0 100644 --- a/app.ts +++ b/app.ts @@ -126,12 +126,11 @@ class ApiData { if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { this.maxGridHeight = screenHeight - pageCntrl; gridElement.style.height = `${this.maxGridHeight}px`; - gridElement.style.overflow = 'none'; } }; // Update the page information and records display based on the current state of the grid. updatePageInfo(): void { - const totalPages = Math.floor(this.totalItems / this.pageSize); + const totalPages = Math.ceil(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; const maxRange = this.totalItems - 1; const from = this.firstVal; @@ -175,7 +174,7 @@ class ApiData { private handleResize = (): void => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; - + // Check if the new grid size is non-negative if (newGridSize >= 0) { const newPageSize = newGridSize; let newFirstValueIndex = this.firstVal; From f70d83143b2d9cffdb160ab800a0db68e5828c17 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 30 Aug 2023 08:12:03 +0200 Subject: [PATCH 31/90] files separated --- ApiData.js | 338 ++++++++++++++++++++++++++++++++++++++++++++++++ ApiData.js.map | 1 + ApiData.ts | 193 +++++++++++++++++++++++++++ GridTemp.js | 49 +++++++ GridTemp.js.map | 1 + GridTemp.ts | 46 +++++++ app.ts | 233 --------------------------------- index.html | 2 + 8 files changed, 630 insertions(+), 233 deletions(-) create mode 100644 ApiData.js create mode 100644 ApiData.js.map create mode 100644 ApiData.ts create mode 100644 GridTemp.js create mode 100644 GridTemp.js.map create mode 100644 GridTemp.ts diff --git a/ApiData.js b/ApiData.js new file mode 100644 index 00000000..1c580049 --- /dev/null +++ b/ApiData.js @@ -0,0 +1,338 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +//class to fetch and manage data from the api +var ApiData = /** @class */ (function () { + function ApiData(pageSize) { + var _this = this; + this.currentPage = 1; + this.data = []; + this.totalItems = 0; + this.columnNames = []; + this.maxGridHeight = 0; + this.firstVal = 0; + this.handleResize = function () { + var newWindowHeight = Math.floor($(window).innerHeight()); + var newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; + // Check if the new grid size is non-negative + if (newGridSize >= 0) { + var newPageSize = newGridSize; + var newFirstValueIndex = _this.firstVal; + // Adjust firstVal for the last page + if (newFirstValueIndex + newPageSize > _this.totalItems) { + newFirstValueIndex = Math.max(_this.totalItems - newPageSize); + } + // Update firstVal, lastVal, and page size + _this.pageSize = newPageSize; + _this.firstVal = newFirstValueIndex; + _this.lastVal = newFirstValueIndex + newPageSize - 1; + // Fetch records, update page info, and adjust grid height + _this.fetchRecords(); + _this.updatePageInfo(); + _this.adjustGridHeight(); + } + }; + this.displayRecords = function () { + var gridTemplate = new GridTemplate(_this.columnNames, _this.data); + gridTemplate.displayRecords(); + _this.updatePageInfo(); + }; + this.pageSize = pageSize; + } + ; + // Initialize method to set up the grid + ApiData.prototype.initialize = function () { + return __awaiter(this, void 0, void 0, function () { + var error_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 4, , 5]); + this.adjustGridHeight(); + return [4 /*yield*/, this.recordCount()]; + case 1: + _a.sent(); + return [4 /*yield*/, this.fetchColumns()]; + case 2: + _a.sent(); + return [4 /*yield*/, this.fetchRecords()]; + case 3: + _a.sent(); + this.setupControls(); + return [3 /*break*/, 5]; + case 4: + error_1 = _a.sent(); + console.error('Error during initialization:', error_1); + return [3 /*break*/, 5]; + case 5: return [2 /*return*/]; + } + }); + }); + }; + ; + // Method to fetch total record count from the server + ApiData.prototype.recordCount = function () { + return __awaiter(this, void 0, void 0, function () { + var response, error_2; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 2, , 3]); + return [4 /*yield*/, this.fetchData('http://localhost:2050/recordCount')]; + case 1: + response = _a.sent(); + this.totalItems = typeof response === 'number' ? response : parseInt(response, 10); + return [3 /*break*/, 3]; + case 2: + error_2 = _a.sent(); + throw new Error('Failed to fetch record count.'); + case 3: return [2 /*return*/]; + } + }); + }); + }; + ; + //fectch column names + ApiData.prototype.fetchColumns = function () { + return __awaiter(this, void 0, void 0, function () { + var response, res, error_3; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 2, , 3]); + return [4 /*yield*/, this.fetchData('http://localhost:2050/columns')]; + case 1: + response = _a.sent(); + res = JSON.parse(response); + this.columnNames = res.map(function (columnName) { return ({ name: columnName }); }); + this.data = new Array(this.columnNames.length); + return [3 /*break*/, 3]; + case 2: + error_3 = _a.sent(); + throw new Error('Failed to fetch columns.'); + case 3: return [2 /*return*/]; + } + }); + }); + }; + ; + //get records from API for fetch an search functionality + ApiData.prototype.fetchAndProcessRecords = function (from, to) { + if (from === void 0) { from = this.firstVal; } + return __awaiter(this, void 0, void 0, function () { + var response, res, processedData, error_4; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 2, , 3]); + $('#spinner').show(); + $('#grid').hide(); + return [4 /*yield*/, this.fetchData("http://localhost:2050/records?from=" + from + "&to=" + to)]; + case 1: + response = _a.sent(); + res = JSON.parse(response); + processedData = res.map(function (record) { + var obj = {}; + var columnIndex = 0; + for (var _i = 0, _a = _this.columnNames; _i < _a.length; _i++) { + var column = _a[_i]; + if (columnIndex < record.length) { + var columnName = column.name; + var columnValue = record[columnIndex]; + obj[columnName] = columnValue; + } + columnIndex++; + } + return obj; + }); + $('#spinner').hide(); + $('#grid').show(); + return [2 /*return*/, processedData]; + case 2: + error_4 = _a.sent(); + throw new Error('Failed to fetch records'); + case 3: return [2 /*return*/]; + } + }); + }); + }; + ; + //fetch records from api + ApiData.prototype.fetchRecords = function () { + return __awaiter(this, void 0, void 0, function () { + var maxRange, from, to, processedData, error_5; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + maxRange = this.totalItems - 1; + from = this.firstVal; + to = Math.min(from + this.pageSize, maxRange); + if (to >= maxRange) { + this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page + to = maxRange; + } + ; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.fetchAndProcessRecords(from, to)]; + case 2: + processedData = _a.sent(); + this.data = processedData; + this.displayRecords(); + this.updatePageInfo(); + return [3 /*break*/, 4]; + case 3: + error_5 = _a.sent(); + throw new Error('Failed to fetch records'); + case 4: return [2 /*return*/]; + } + }); + }); + }; + ; + //funtion to search through records using fromID + ApiData.prototype.searchRecords = function (searchValue) { + return __awaiter(this, void 0, void 0, function () { + var maxRange, from, to, processedData, error_6; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 4, , 5]); + maxRange = this.totalItems - 1; + if (!(searchValue >= 0 && searchValue <= maxRange)) return [3 /*break*/, 2]; + from = searchValue; + to = Math.min(from + this.pageSize, maxRange); + return [4 /*yield*/, this.fetchAndProcessRecords(from, to)]; + case 1: + processedData = _a.sent(); + this.data = processedData; + this.currentPage = Math.ceil(from / this.pageSize) + 1; + this.firstVal = from; // Set firstVal to searched value + this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize + this.displayRecords(); + this.updatePageInfo(); + return [3 /*break*/, 3]; + case 2: + alert('Please enter values in the range (0-999999)'); + _a.label = 3; + case 3: return [3 /*break*/, 5]; + case 4: + error_6 = _a.sent(); + throw new Error('Failed to search value'); + case 5: return [2 /*return*/]; + } + }); + }); + }; + ; + //chnge grid height according to screen size + ApiData.prototype.adjustGridHeight = function () { + var gridElement = document.getElementById('grid'); + var pageCntrl = $('.grid-controls').innerHeight(); + var screenHeight = $(window).innerHeight(); + if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { + this.maxGridHeight = screenHeight - pageCntrl; + gridElement.style.height = this.maxGridHeight + "px"; + } + }; + ; + // Update the page information and records display based on the current state of the grid. + ApiData.prototype.updatePageInfo = function () { + var totalPages = Math.ceil(this.totalItems / this.pageSize); + var pageInfo = "Page " + this.currentPage + " of " + totalPages; + var maxRange = this.totalItems - 1; + var from = this.firstVal; + var to = Math.min(from + this.pageSize, maxRange); + $('#pageInfo').text("" + pageInfo); + $('.records').text("Showing records " + from + " to " + to); + }; + ; + // use Ajax for data fetching + ApiData.prototype.fetchData = function (url) { + return __awaiter(this, void 0, void 0, function () { + var response, error_7; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 2, , 3]); + $('#overlay').show(); + return [4 /*yield*/, $.ajax({ + url: url, + method: 'GET', + })]; + case 1: + response = _a.sent(); + $('#overlay').hide(); + return [2 /*return*/, response]; + case 2: + error_7 = _a.sent(); + throw error_7; + case 3: return [2 /*return*/]; + } + }); + }); + }; + ; + ApiData.prototype.setupControls = function () { + var _this = this; + $('#prevBtn').on('click', function () { return _this.handlePageChange(-1); }); + $('#nextBtn').on('click', function () { return _this.handlePageChange(1); }); + $(window).on('resize', debounce(this.handleResize, 350)); + }; + ; + ApiData.prototype.handlePageChange = function (delta) { + var newFirstVal = this.firstVal + delta * this.pageSize; + if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { + this.firstVal = newFirstVal; + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + } + else if (newFirstVal <= this.pageSize) { + this.firstVal = 0; + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + } + ; + }; + ; + return ApiData; +}()); +//# sourceMappingURL=ApiData.js.map \ No newline at end of file diff --git a/ApiData.js.map b/ApiData.js.map new file mode 100644 index 00000000..60858d50 --- /dev/null +++ b/ApiData.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ApiData.js","sourceRoot":"","sources":["ApiData.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/ApiData.ts b/ApiData.ts new file mode 100644 index 00000000..afacdc57 --- /dev/null +++ b/ApiData.ts @@ -0,0 +1,193 @@ +//class to fetch and manage data from the api +class ApiData { + // Properties to manage data and settings + pageSize: number; + currentPage = 1; + data: GridData[] = []; + totalItems = 0; + columnNames: ColumnName[] = []; + maxGridHeight = 0; + firstVal = 0; + lastVal: number | undefined; + + constructor(pageSize: number) { + this.pageSize = pageSize; + }; + // Initialize method to set up the grid + async initialize(): Promise { + try { + this.adjustGridHeight(); + await this.recordCount(); + await this.fetchColumns(); + await this.fetchRecords(); + this.setupControls(); + } catch (error) { + console.error('Error during initialization:', error); + } + }; + // Method to fetch total record count from the server + async recordCount(): Promise { + try { + const response = await this.fetchData('http://localhost:2050/recordCount'); + this.totalItems = typeof response === 'number' ? response : parseInt(response as string, 10); + } catch (error) { + throw new Error('Failed to fetch record count.'); + } + }; + //fectch column names + async fetchColumns(): Promise { + try { + const response = await this.fetchData('http://localhost:2050/columns'); + const res = JSON.parse(response as string); + this.columnNames = res.map((columnName: any) => ({ name: columnName })); + this.data = new Array(this.columnNames.length); + } catch (error) { + throw new Error('Failed to fetch columns.'); + } + }; + //get records from API for fetch an search functionality + async fetchAndProcessRecords(from: number = this.firstVal, to: number): Promise { + try { + $('#spinner').show() + $('#grid').hide(); + const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); + const res = JSON.parse(response as string); + const processedData = res.map((record: any) => { + const obj: GridData = {}; + let columnIndex = 0; + for (const column of this.columnNames) { + if (columnIndex < record.length) { + const columnName = column.name; + const columnValue = record[columnIndex]; + obj[columnName] = columnValue; + } + columnIndex++; + } + return obj; + }); + $('#spinner').hide(); + $('#grid').show(); + return processedData; + } catch (error) { + throw new Error('Failed to fetch records'); + } + }; + //fetch records from api + async fetchRecords(): Promise { + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize, maxRange); + if (to >= maxRange) { + this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page + to = maxRange; + }; + try { + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.displayRecords(); + this.updatePageInfo(); + } catch (error) { + throw new Error('Failed to fetch records') + } + }; + //funtion to search through records using fromID + async searchRecords(searchValue: number): Promise { + try { + const maxRange = this.totalItems - 1; // Maximum allowed Value + if (searchValue >= 0 && searchValue <= maxRange) { + const from = searchValue; + const to = Math.min(from + this.pageSize, maxRange); + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.currentPage = Math.ceil(from / this.pageSize) + 1 + this.firstVal = from; // Set firstVal to searched value + this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize + this.displayRecords(); + this.updatePageInfo(); + } else { + alert('Please enter values in the range (0-999999)'); + } + } catch (error) { + throw new Error('Failed to search value'); + } + }; + //chnge grid height according to screen size + adjustGridHeight(): void { + const gridElement = document.getElementById('grid'); + const pageCntrl = $('.grid-controls').innerHeight(); + const screenHeight = $(window).innerHeight(); + if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { + this.maxGridHeight = screenHeight - pageCntrl; + gridElement.style.height = `${this.maxGridHeight}px`; + } + }; + // Update the page information and records display based on the current state of the grid. + updatePageInfo(): void { + const totalPages = Math.ceil(this.totalItems / this.pageSize); + const pageInfo = `Page ${this.currentPage} of ${totalPages}`; + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize, maxRange); + $('#pageInfo').text(`${pageInfo}`); + $('.records').text(`Showing records ${from} to ${to}`); + }; + // use Ajax for data fetching + private async fetchData(url: string): Promise { + try { + $('#overlay').show(); + const response = await $.ajax({ + url, + method: 'GET', + }); + $('#overlay').hide(); + return response; + } catch (error) { + throw error; + } + }; + private setupControls(): void { + $('#prevBtn').on('click', () => this.handlePageChange(-1)); + $('#nextBtn').on('click', () => this.handlePageChange(1)); + $(window).on('resize', debounce(this.handleResize, 350)); + }; + private handlePageChange(delta: number): void { + const newFirstVal = this.firstVal + delta * this.pageSize; + if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { + this.firstVal = newFirstVal; + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + } else if (newFirstVal <= this.pageSize) { + this.firstVal = 0 + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + }; + }; + private handleResize = (): void => { + const newWindowHeight = Math.floor($(window).innerHeight() as number); + const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; + // Check if the new grid size is non-negative + if (newGridSize >= 0) { + const newPageSize = newGridSize; + let newFirstValueIndex = this.firstVal; + // Adjust firstVal for the last page + if (newFirstValueIndex + newPageSize > this.totalItems) { + newFirstValueIndex = Math.max(this.totalItems - newPageSize); + } + // Update firstVal, lastVal, and page size + this.pageSize = newPageSize; + this.firstVal = newFirstValueIndex; + this.lastVal = newFirstValueIndex + newPageSize - 1; + // Fetch records, update page info, and adjust grid height + this.fetchRecords(); + this.updatePageInfo(); + this.adjustGridHeight(); + } + }; + private displayRecords = (): void => { + const gridTemplate = new GridTemplate(this.columnNames, this.data); + gridTemplate.displayRecords(); + this.updatePageInfo(); + }; +} diff --git a/GridTemp.js b/GridTemp.js new file mode 100644 index 00000000..b9654221 --- /dev/null +++ b/GridTemp.js @@ -0,0 +1,49 @@ +"use strict"; +// Class to manage the grid template and display records +var GridTemplate = /** @class */ (function () { + // Initializes the column names and data records that will be used to display records in the grid. + function GridTemplate(columnNames, dataRecords) { + this.columnNames = []; + this.dataRecords = []; + this.columnNames = columnNames; + this.dataRecords = dataRecords; + } + ; + // Display records in a grid in table format + GridTemplate.prototype.displayRecords = function () { + var gridElement = document.getElementById('grid'); + if (gridElement) { + gridElement.innerHTML = ''; + var table = document.createElement('table'); + var thead = document.createElement('thead'); + var headerRow = document.createElement('tr'); + for (var _i = 0, _a = this.columnNames; _i < _a.length; _i++) { + var column = _a[_i]; + var th = document.createElement('th'); + th.textContent = column.name; + headerRow.appendChild(th); + } + thead.appendChild(headerRow); + table.appendChild(thead); + // Create table body + var tbody = document.createElement('tbody'); + for (var _b = 0, _c = this.dataRecords; _b < _c.length; _b++) { + var row = _c[_b]; + var tr = document.createElement('tr'); + for (var _d = 0, _e = this.columnNames; _d < _e.length; _d++) { + var column = _e[_d]; + var td = document.createElement('td'); + td.textContent = row[column.name]; + tr.appendChild(td); + } + tbody.appendChild(tr); + } + table.appendChild(tbody); + // Append the table to the grid element + gridElement.appendChild(table); + } + }; + return GridTemplate; +}()); +; +//# sourceMappingURL=GridTemp.js.map \ No newline at end of file diff --git a/GridTemp.js.map b/GridTemp.js.map new file mode 100644 index 00000000..06da827b --- /dev/null +++ b/GridTemp.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GridTemp.js","sourceRoot":"","sources":["GridTemp.ts"],"names":[],"mappings":";AAAA,wDAAwD;AACxD;IAGC,kGAAkG;IAClG,sBAAY,WAAyB,EAAE,WAAuB;QAHtD,gBAAW,GAAiB,EAAE,CAAC;QAC/B,gBAAW,GAAe,EAAE,CAAC;QAGpC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IAChC,CAAC;IAAA,CAAC;IACF,6CAA6C;IAC7C,qCAAc,GAAd;QACC,IAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,WAAW,EAAE;YAChB,WAAW,CAAC,SAAS,GAAG,EAAE,CAAC;YAC3B,IAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAC/C,KAAqB,UAAgB,EAAhB,KAAA,IAAI,CAAC,WAAW,EAAhB,cAAgB,EAAhB,IAAgB,EAAE;gBAAlC,IAAM,MAAM,SAAA;gBAChB,IAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBACxC,EAAE,CAAC,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC;gBAC7B,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;aAC1B;YACD,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YAC7B,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACzB,oBAAoB;YACpB,IAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC9C,KAAkB,UAAgB,EAAhB,KAAA,IAAI,CAAC,WAAW,EAAhB,cAAgB,EAAhB,IAAgB,EAAE;gBAA/B,IAAM,GAAG,SAAA;gBACb,IAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBAExC,KAAqB,UAAgB,EAAhB,KAAA,IAAI,CAAC,WAAW,EAAhB,cAAgB,EAAhB,IAAgB,EAAE;oBAAlC,IAAM,MAAM,SAAA;oBAChB,IAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;oBACxC,EAAE,CAAC,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBAClC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;iBACnB;gBAED,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;aACtB;YAED,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAEzB,uCAAuC;YACvC,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;SAC/B;IAEF,CAAC;IACF,mBAAC;AAAD,CAAC,AA5CD,IA4CC;AAAA,CAAC"} \ No newline at end of file diff --git a/GridTemp.ts b/GridTemp.ts new file mode 100644 index 00000000..c2ee69fa --- /dev/null +++ b/GridTemp.ts @@ -0,0 +1,46 @@ +// Class to manage the grid template and display records +class GridTemplate { + private columnNames: ColumnName[] = []; + private dataRecords: GridData[] = []; + // Initializes the column names and data records that will be used to display records in the grid. + constructor(columnNames: ColumnName[], dataRecords: GridData[]) { + this.columnNames = columnNames; + this.dataRecords = dataRecords; + }; + // Display records in a grid in table format + displayRecords(): void { + const gridElement = document.getElementById('grid'); + if (gridElement) { + gridElement.innerHTML = ''; + const table = document.createElement('table'); + const thead = document.createElement('thead'); + const headerRow = document.createElement('tr'); + for (const column of this.columnNames) { + const th = document.createElement('th'); + th.textContent = column.name; + headerRow.appendChild(th); + } + thead.appendChild(headerRow); + table.appendChild(thead); + // Create table body + const tbody = document.createElement('tbody'); + for (const row of this.dataRecords) { + const tr = document.createElement('tr'); + + for (const column of this.columnNames) { + const td = document.createElement('td'); + td.textContent = row[column.name]; + tr.appendChild(td); + } + + tbody.appendChild(tr); + } + + table.appendChild(tbody); + + // Append the table to the grid element + gridElement.appendChild(table); + } + + } +}; diff --git a/app.ts b/app.ts index 647d2ea0..32473641 100644 --- a/app.ts +++ b/app.ts @@ -6,239 +6,6 @@ interface ColumnName { interface GridData { [key: string]: any; } -class ApiData { - // Properties to manage data and settings - pageSize: number; - currentPage = 1; - data: GridData[] = []; - totalItems = 0; - columnNames: ColumnName[] = []; - maxGridHeight = 0; - firstVal = 0; - lastVal: number | undefined; - - constructor(pageSize: number) { - this.pageSize = pageSize; - }; - // Initialize method to set up the grid - async initialize(): Promise { - try { - this.adjustGridHeight(); - await this.recordCount(); - await this.fetchColumns(); - await this.fetchRecords(); - this.setupControls(); - } catch (error) { - console.error('Error during initialization:', error); - } - }; - // Method to fetch total record count from the server - async recordCount(): Promise { - try { - const response = await this.fetchData('http://localhost:2050/recordCount'); - this.totalItems = typeof response === 'number' ? response : parseInt(response as string, 10); - } catch (error) { - throw new Error('Failed to fetch record count.'); - } - }; - //fectch column names - async fetchColumns(): Promise { - try { - const response = await this.fetchData('http://localhost:2050/columns'); - const res = JSON.parse(response as string); - this.columnNames = res.map((columnName: any) => ({ name: columnName })); - this.data = new Array(this.columnNames.length); - } catch (error) { - throw new Error('Failed to fetch columns.'); - } - }; - //get records from API for fetch an search functionality - async fetchAndProcessRecords(from: number = this.firstVal, to: number): Promise { - try { - $('#spinner').show() - $('#grid').hide(); - const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); - const res = JSON.parse(response as string); - const processedData = res.map((record: any) => { - const obj: GridData = {}; - let columnIndex = 0; - for (const column of this.columnNames) { - if (columnIndex < record.length) { - const columnName = column.name; - const columnValue = record[columnIndex]; - obj[columnName] = columnValue; - } - columnIndex++; - } - return obj; - }); - $('#spinner').hide(); - $('#grid').show(); - return processedData; - } catch (error) { - throw new Error('Failed to fetch records'); - } - }; - //fetch records from api - async fetchRecords(): Promise { - const maxRange = this.totalItems - 1; - const from = this.firstVal; - let to = Math.min(from + this.pageSize, maxRange); - if (to >= maxRange) { - this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page - to = maxRange; - }; - try { - const processedData = await this.fetchAndProcessRecords(from, to); - this.data = processedData; - this.displayRecords(); - this.updatePageInfo(); - } catch (error) { - throw new Error('Failed to fetch records') - } - }; - //funtion to search through records using fromID - async searchRecords(searchValue: number): Promise { - try { - const maxRange = this.totalItems - 1; // Maximum allowed Value - if (searchValue >= 0 && searchValue <= maxRange) { - const from = searchValue; - const to = Math.min(from + this.pageSize, maxRange); - const processedData = await this.fetchAndProcessRecords(from, to); - this.data = processedData; - this.currentPage = Math.ceil(from / this.pageSize) + 1 - this.firstVal = from; // Set firstVal to searched value - this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize - this.displayRecords(); - this.updatePageInfo(); - } else { - alert('Please enter values in the range (0-999999)'); - } - } catch (error) { - throw new Error('Failed to search value'); - } - }; - //chnge grid height according to screen size - adjustGridHeight(): void { - const gridElement = document.getElementById('grid'); - const pageCntrl = $('.grid-controls').innerHeight(); - const screenHeight = $(window).innerHeight(); - if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { - this.maxGridHeight = screenHeight - pageCntrl; - gridElement.style.height = `${this.maxGridHeight}px`; - } - }; - // Update the page information and records display based on the current state of the grid. - updatePageInfo(): void { - const totalPages = Math.ceil(this.totalItems / this.pageSize); - const pageInfo = `Page ${this.currentPage} of ${totalPages}`; - const maxRange = this.totalItems - 1; - const from = this.firstVal; - let to = Math.min(from + this.pageSize, maxRange); - $('#pageInfo').text(`${pageInfo}`); - $('.records').text(`Showing records ${from} to ${to}`); - }; - // use Ajax for data fetching - private async fetchData(url: string): Promise { - try { - $('#overlay').show(); - const response = await $.ajax({ - url, - method: 'GET', - }); - $('#overlay').hide(); - return response; - } catch (error) { - throw error; - } - }; - private setupControls(): void { - $('#prevBtn').on('click', () => this.handlePageChange(-1)); - $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize, 350)); - }; - private handlePageChange(delta: number): void { - const newFirstVal = this.firstVal + delta * this.pageSize; - if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { - this.firstVal = newFirstVal; - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - } else if (newFirstVal <= this.pageSize) { - this.firstVal = 0 - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - }; - }; - private handleResize = (): void => { - const newWindowHeight = Math.floor($(window).innerHeight() as number); - const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; - // Check if the new grid size is non-negative - if (newGridSize >= 0) { - const newPageSize = newGridSize; - let newFirstValueIndex = this.firstVal; - // Adjust firstVal for the last page - if (newFirstValueIndex + newPageSize > this.totalItems) { - newFirstValueIndex = Math.max(this.totalItems - newPageSize); - } - // Update firstVal, lastVal, and page size - this.pageSize = newPageSize; - this.firstVal = newFirstValueIndex; - this.lastVal = newFirstValueIndex + newPageSize - 1; - // Fetch records, update page info, and adjust grid height - this.fetchRecords(); - this.updatePageInfo(); - this.adjustGridHeight(); - } - }; - private displayRecords = (): void => { - const gridTemplate = new GridTemplate(this.columnNames, this.data); - gridTemplate.displayRecords(); - this.updatePageInfo(); - }; -} -// Class to manage the grid template and display records -class GridTemplate { - private columnNames: ColumnName[] = []; - private dataRecords: GridData[] = []; - // Initializes the column names and data records that will be used to display records in the grid. - constructor(columnNames: ColumnName[], dataRecords: GridData[]) { - this.columnNames = columnNames; - this.dataRecords = dataRecords; - }; - // Display records in a grid in table format - displayRecords(): void { - const gridElement = document.getElementById('grid'); - if (gridElement) { - gridElement.innerHTML = ''; - const table = document.createElement('table'); - const thead = document.createElement('thead'); - const headerRow = document.createElement('tr'); - this.columnNames.forEach((column) => { - const th = document.createElement('th'); - th.textContent = column.name; - headerRow.appendChild(th); - }); - thead.appendChild(headerRow); - table.appendChild(thead); - // Create table body - const tbody = document.createElement('tbody'); - this.dataRecords.forEach((row) => { - const tr = document.createElement('tr'); - this.columnNames.forEach((column) => { - const td = document.createElement('td'); - td.textContent = row[column.name]; - tr.appendChild(td); - }); - tbody.appendChild(tr); - }); - table.appendChild(tbody); - // Append the table to the grid element - gridElement.appendChild(table); - } - }; -} // Debounce utility function to limit function execution frequency function debounce any>(func: F, waitFor: number) { let timeout: number; diff --git a/index.html b/index.html index 84b290ac..a8a41a20 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,8 @@ + + From 1a2ee0d6c256b296712879903b32032d63a802d2 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 30 Aug 2023 08:34:31 +0200 Subject: [PATCH 32/90] checking --- ApiData.js | 338 ------------------------------------------------ ApiData.js.map | 1 - ApiData.ts | 193 --------------------------- GridTemp.js | 49 ------- GridTemp.js.map | 1 - GridTemp.ts | 46 ------- app.ts | 233 +++++++++++++++++++++++++++++++++ index.html | 2 - 8 files changed, 233 insertions(+), 630 deletions(-) delete mode 100644 ApiData.js delete mode 100644 ApiData.js.map delete mode 100644 ApiData.ts delete mode 100644 GridTemp.js delete mode 100644 GridTemp.js.map delete mode 100644 GridTemp.ts diff --git a/ApiData.js b/ApiData.js deleted file mode 100644 index 1c580049..00000000 --- a/ApiData.js +++ /dev/null @@ -1,338 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -//class to fetch and manage data from the api -var ApiData = /** @class */ (function () { - function ApiData(pageSize) { - var _this = this; - this.currentPage = 1; - this.data = []; - this.totalItems = 0; - this.columnNames = []; - this.maxGridHeight = 0; - this.firstVal = 0; - this.handleResize = function () { - var newWindowHeight = Math.floor($(window).innerHeight()); - var newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; - // Check if the new grid size is non-negative - if (newGridSize >= 0) { - var newPageSize = newGridSize; - var newFirstValueIndex = _this.firstVal; - // Adjust firstVal for the last page - if (newFirstValueIndex + newPageSize > _this.totalItems) { - newFirstValueIndex = Math.max(_this.totalItems - newPageSize); - } - // Update firstVal, lastVal, and page size - _this.pageSize = newPageSize; - _this.firstVal = newFirstValueIndex; - _this.lastVal = newFirstValueIndex + newPageSize - 1; - // Fetch records, update page info, and adjust grid height - _this.fetchRecords(); - _this.updatePageInfo(); - _this.adjustGridHeight(); - } - }; - this.displayRecords = function () { - var gridTemplate = new GridTemplate(_this.columnNames, _this.data); - gridTemplate.displayRecords(); - _this.updatePageInfo(); - }; - this.pageSize = pageSize; - } - ; - // Initialize method to set up the grid - ApiData.prototype.initialize = function () { - return __awaiter(this, void 0, void 0, function () { - var error_1; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - _a.trys.push([0, 4, , 5]); - this.adjustGridHeight(); - return [4 /*yield*/, this.recordCount()]; - case 1: - _a.sent(); - return [4 /*yield*/, this.fetchColumns()]; - case 2: - _a.sent(); - return [4 /*yield*/, this.fetchRecords()]; - case 3: - _a.sent(); - this.setupControls(); - return [3 /*break*/, 5]; - case 4: - error_1 = _a.sent(); - console.error('Error during initialization:', error_1); - return [3 /*break*/, 5]; - case 5: return [2 /*return*/]; - } - }); - }); - }; - ; - // Method to fetch total record count from the server - ApiData.prototype.recordCount = function () { - return __awaiter(this, void 0, void 0, function () { - var response, error_2; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - _a.trys.push([0, 2, , 3]); - return [4 /*yield*/, this.fetchData('http://localhost:2050/recordCount')]; - case 1: - response = _a.sent(); - this.totalItems = typeof response === 'number' ? response : parseInt(response, 10); - return [3 /*break*/, 3]; - case 2: - error_2 = _a.sent(); - throw new Error('Failed to fetch record count.'); - case 3: return [2 /*return*/]; - } - }); - }); - }; - ; - //fectch column names - ApiData.prototype.fetchColumns = function () { - return __awaiter(this, void 0, void 0, function () { - var response, res, error_3; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - _a.trys.push([0, 2, , 3]); - return [4 /*yield*/, this.fetchData('http://localhost:2050/columns')]; - case 1: - response = _a.sent(); - res = JSON.parse(response); - this.columnNames = res.map(function (columnName) { return ({ name: columnName }); }); - this.data = new Array(this.columnNames.length); - return [3 /*break*/, 3]; - case 2: - error_3 = _a.sent(); - throw new Error('Failed to fetch columns.'); - case 3: return [2 /*return*/]; - } - }); - }); - }; - ; - //get records from API for fetch an search functionality - ApiData.prototype.fetchAndProcessRecords = function (from, to) { - if (from === void 0) { from = this.firstVal; } - return __awaiter(this, void 0, void 0, function () { - var response, res, processedData, error_4; - var _this = this; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - _a.trys.push([0, 2, , 3]); - $('#spinner').show(); - $('#grid').hide(); - return [4 /*yield*/, this.fetchData("http://localhost:2050/records?from=" + from + "&to=" + to)]; - case 1: - response = _a.sent(); - res = JSON.parse(response); - processedData = res.map(function (record) { - var obj = {}; - var columnIndex = 0; - for (var _i = 0, _a = _this.columnNames; _i < _a.length; _i++) { - var column = _a[_i]; - if (columnIndex < record.length) { - var columnName = column.name; - var columnValue = record[columnIndex]; - obj[columnName] = columnValue; - } - columnIndex++; - } - return obj; - }); - $('#spinner').hide(); - $('#grid').show(); - return [2 /*return*/, processedData]; - case 2: - error_4 = _a.sent(); - throw new Error('Failed to fetch records'); - case 3: return [2 /*return*/]; - } - }); - }); - }; - ; - //fetch records from api - ApiData.prototype.fetchRecords = function () { - return __awaiter(this, void 0, void 0, function () { - var maxRange, from, to, processedData, error_5; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - maxRange = this.totalItems - 1; - from = this.firstVal; - to = Math.min(from + this.pageSize, maxRange); - if (to >= maxRange) { - this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page - to = maxRange; - } - ; - _a.label = 1; - case 1: - _a.trys.push([1, 3, , 4]); - return [4 /*yield*/, this.fetchAndProcessRecords(from, to)]; - case 2: - processedData = _a.sent(); - this.data = processedData; - this.displayRecords(); - this.updatePageInfo(); - return [3 /*break*/, 4]; - case 3: - error_5 = _a.sent(); - throw new Error('Failed to fetch records'); - case 4: return [2 /*return*/]; - } - }); - }); - }; - ; - //funtion to search through records using fromID - ApiData.prototype.searchRecords = function (searchValue) { - return __awaiter(this, void 0, void 0, function () { - var maxRange, from, to, processedData, error_6; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - _a.trys.push([0, 4, , 5]); - maxRange = this.totalItems - 1; - if (!(searchValue >= 0 && searchValue <= maxRange)) return [3 /*break*/, 2]; - from = searchValue; - to = Math.min(from + this.pageSize, maxRange); - return [4 /*yield*/, this.fetchAndProcessRecords(from, to)]; - case 1: - processedData = _a.sent(); - this.data = processedData; - this.currentPage = Math.ceil(from / this.pageSize) + 1; - this.firstVal = from; // Set firstVal to searched value - this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize - this.displayRecords(); - this.updatePageInfo(); - return [3 /*break*/, 3]; - case 2: - alert('Please enter values in the range (0-999999)'); - _a.label = 3; - case 3: return [3 /*break*/, 5]; - case 4: - error_6 = _a.sent(); - throw new Error('Failed to search value'); - case 5: return [2 /*return*/]; - } - }); - }); - }; - ; - //chnge grid height according to screen size - ApiData.prototype.adjustGridHeight = function () { - var gridElement = document.getElementById('grid'); - var pageCntrl = $('.grid-controls').innerHeight(); - var screenHeight = $(window).innerHeight(); - if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { - this.maxGridHeight = screenHeight - pageCntrl; - gridElement.style.height = this.maxGridHeight + "px"; - } - }; - ; - // Update the page information and records display based on the current state of the grid. - ApiData.prototype.updatePageInfo = function () { - var totalPages = Math.ceil(this.totalItems / this.pageSize); - var pageInfo = "Page " + this.currentPage + " of " + totalPages; - var maxRange = this.totalItems - 1; - var from = this.firstVal; - var to = Math.min(from + this.pageSize, maxRange); - $('#pageInfo').text("" + pageInfo); - $('.records').text("Showing records " + from + " to " + to); - }; - ; - // use Ajax for data fetching - ApiData.prototype.fetchData = function (url) { - return __awaiter(this, void 0, void 0, function () { - var response, error_7; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - _a.trys.push([0, 2, , 3]); - $('#overlay').show(); - return [4 /*yield*/, $.ajax({ - url: url, - method: 'GET', - })]; - case 1: - response = _a.sent(); - $('#overlay').hide(); - return [2 /*return*/, response]; - case 2: - error_7 = _a.sent(); - throw error_7; - case 3: return [2 /*return*/]; - } - }); - }); - }; - ; - ApiData.prototype.setupControls = function () { - var _this = this; - $('#prevBtn').on('click', function () { return _this.handlePageChange(-1); }); - $('#nextBtn').on('click', function () { return _this.handlePageChange(1); }); - $(window).on('resize', debounce(this.handleResize, 350)); - }; - ; - ApiData.prototype.handlePageChange = function (delta) { - var newFirstVal = this.firstVal + delta * this.pageSize; - if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { - this.firstVal = newFirstVal; - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - } - else if (newFirstVal <= this.pageSize) { - this.firstVal = 0; - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - } - ; - }; - ; - return ApiData; -}()); -//# sourceMappingURL=ApiData.js.map \ No newline at end of file diff --git a/ApiData.js.map b/ApiData.js.map deleted file mode 100644 index 60858d50..00000000 --- a/ApiData.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ApiData.js","sourceRoot":"","sources":["ApiData.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/ApiData.ts b/ApiData.ts deleted file mode 100644 index afacdc57..00000000 --- a/ApiData.ts +++ /dev/null @@ -1,193 +0,0 @@ -//class to fetch and manage data from the api -class ApiData { - // Properties to manage data and settings - pageSize: number; - currentPage = 1; - data: GridData[] = []; - totalItems = 0; - columnNames: ColumnName[] = []; - maxGridHeight = 0; - firstVal = 0; - lastVal: number | undefined; - - constructor(pageSize: number) { - this.pageSize = pageSize; - }; - // Initialize method to set up the grid - async initialize(): Promise { - try { - this.adjustGridHeight(); - await this.recordCount(); - await this.fetchColumns(); - await this.fetchRecords(); - this.setupControls(); - } catch (error) { - console.error('Error during initialization:', error); - } - }; - // Method to fetch total record count from the server - async recordCount(): Promise { - try { - const response = await this.fetchData('http://localhost:2050/recordCount'); - this.totalItems = typeof response === 'number' ? response : parseInt(response as string, 10); - } catch (error) { - throw new Error('Failed to fetch record count.'); - } - }; - //fectch column names - async fetchColumns(): Promise { - try { - const response = await this.fetchData('http://localhost:2050/columns'); - const res = JSON.parse(response as string); - this.columnNames = res.map((columnName: any) => ({ name: columnName })); - this.data = new Array(this.columnNames.length); - } catch (error) { - throw new Error('Failed to fetch columns.'); - } - }; - //get records from API for fetch an search functionality - async fetchAndProcessRecords(from: number = this.firstVal, to: number): Promise { - try { - $('#spinner').show() - $('#grid').hide(); - const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); - const res = JSON.parse(response as string); - const processedData = res.map((record: any) => { - const obj: GridData = {}; - let columnIndex = 0; - for (const column of this.columnNames) { - if (columnIndex < record.length) { - const columnName = column.name; - const columnValue = record[columnIndex]; - obj[columnName] = columnValue; - } - columnIndex++; - } - return obj; - }); - $('#spinner').hide(); - $('#grid').show(); - return processedData; - } catch (error) { - throw new Error('Failed to fetch records'); - } - }; - //fetch records from api - async fetchRecords(): Promise { - const maxRange = this.totalItems - 1; - const from = this.firstVal; - let to = Math.min(from + this.pageSize, maxRange); - if (to >= maxRange) { - this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page - to = maxRange; - }; - try { - const processedData = await this.fetchAndProcessRecords(from, to); - this.data = processedData; - this.displayRecords(); - this.updatePageInfo(); - } catch (error) { - throw new Error('Failed to fetch records') - } - }; - //funtion to search through records using fromID - async searchRecords(searchValue: number): Promise { - try { - const maxRange = this.totalItems - 1; // Maximum allowed Value - if (searchValue >= 0 && searchValue <= maxRange) { - const from = searchValue; - const to = Math.min(from + this.pageSize, maxRange); - const processedData = await this.fetchAndProcessRecords(from, to); - this.data = processedData; - this.currentPage = Math.ceil(from / this.pageSize) + 1 - this.firstVal = from; // Set firstVal to searched value - this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize - this.displayRecords(); - this.updatePageInfo(); - } else { - alert('Please enter values in the range (0-999999)'); - } - } catch (error) { - throw new Error('Failed to search value'); - } - }; - //chnge grid height according to screen size - adjustGridHeight(): void { - const gridElement = document.getElementById('grid'); - const pageCntrl = $('.grid-controls').innerHeight(); - const screenHeight = $(window).innerHeight(); - if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { - this.maxGridHeight = screenHeight - pageCntrl; - gridElement.style.height = `${this.maxGridHeight}px`; - } - }; - // Update the page information and records display based on the current state of the grid. - updatePageInfo(): void { - const totalPages = Math.ceil(this.totalItems / this.pageSize); - const pageInfo = `Page ${this.currentPage} of ${totalPages}`; - const maxRange = this.totalItems - 1; - const from = this.firstVal; - let to = Math.min(from + this.pageSize, maxRange); - $('#pageInfo').text(`${pageInfo}`); - $('.records').text(`Showing records ${from} to ${to}`); - }; - // use Ajax for data fetching - private async fetchData(url: string): Promise { - try { - $('#overlay').show(); - const response = await $.ajax({ - url, - method: 'GET', - }); - $('#overlay').hide(); - return response; - } catch (error) { - throw error; - } - }; - private setupControls(): void { - $('#prevBtn').on('click', () => this.handlePageChange(-1)); - $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize, 350)); - }; - private handlePageChange(delta: number): void { - const newFirstVal = this.firstVal + delta * this.pageSize; - if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { - this.firstVal = newFirstVal; - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - } else if (newFirstVal <= this.pageSize) { - this.firstVal = 0 - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - }; - }; - private handleResize = (): void => { - const newWindowHeight = Math.floor($(window).innerHeight() as number); - const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; - // Check if the new grid size is non-negative - if (newGridSize >= 0) { - const newPageSize = newGridSize; - let newFirstValueIndex = this.firstVal; - // Adjust firstVal for the last page - if (newFirstValueIndex + newPageSize > this.totalItems) { - newFirstValueIndex = Math.max(this.totalItems - newPageSize); - } - // Update firstVal, lastVal, and page size - this.pageSize = newPageSize; - this.firstVal = newFirstValueIndex; - this.lastVal = newFirstValueIndex + newPageSize - 1; - // Fetch records, update page info, and adjust grid height - this.fetchRecords(); - this.updatePageInfo(); - this.adjustGridHeight(); - } - }; - private displayRecords = (): void => { - const gridTemplate = new GridTemplate(this.columnNames, this.data); - gridTemplate.displayRecords(); - this.updatePageInfo(); - }; -} diff --git a/GridTemp.js b/GridTemp.js deleted file mode 100644 index b9654221..00000000 --- a/GridTemp.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -// Class to manage the grid template and display records -var GridTemplate = /** @class */ (function () { - // Initializes the column names and data records that will be used to display records in the grid. - function GridTemplate(columnNames, dataRecords) { - this.columnNames = []; - this.dataRecords = []; - this.columnNames = columnNames; - this.dataRecords = dataRecords; - } - ; - // Display records in a grid in table format - GridTemplate.prototype.displayRecords = function () { - var gridElement = document.getElementById('grid'); - if (gridElement) { - gridElement.innerHTML = ''; - var table = document.createElement('table'); - var thead = document.createElement('thead'); - var headerRow = document.createElement('tr'); - for (var _i = 0, _a = this.columnNames; _i < _a.length; _i++) { - var column = _a[_i]; - var th = document.createElement('th'); - th.textContent = column.name; - headerRow.appendChild(th); - } - thead.appendChild(headerRow); - table.appendChild(thead); - // Create table body - var tbody = document.createElement('tbody'); - for (var _b = 0, _c = this.dataRecords; _b < _c.length; _b++) { - var row = _c[_b]; - var tr = document.createElement('tr'); - for (var _d = 0, _e = this.columnNames; _d < _e.length; _d++) { - var column = _e[_d]; - var td = document.createElement('td'); - td.textContent = row[column.name]; - tr.appendChild(td); - } - tbody.appendChild(tr); - } - table.appendChild(tbody); - // Append the table to the grid element - gridElement.appendChild(table); - } - }; - return GridTemplate; -}()); -; -//# sourceMappingURL=GridTemp.js.map \ No newline at end of file diff --git a/GridTemp.js.map b/GridTemp.js.map deleted file mode 100644 index 06da827b..00000000 --- a/GridTemp.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"GridTemp.js","sourceRoot":"","sources":["GridTemp.ts"],"names":[],"mappings":";AAAA,wDAAwD;AACxD;IAGC,kGAAkG;IAClG,sBAAY,WAAyB,EAAE,WAAuB;QAHtD,gBAAW,GAAiB,EAAE,CAAC;QAC/B,gBAAW,GAAe,EAAE,CAAC;QAGpC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IAChC,CAAC;IAAA,CAAC;IACF,6CAA6C;IAC7C,qCAAc,GAAd;QACC,IAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,WAAW,EAAE;YAChB,WAAW,CAAC,SAAS,GAAG,EAAE,CAAC;YAC3B,IAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAC/C,KAAqB,UAAgB,EAAhB,KAAA,IAAI,CAAC,WAAW,EAAhB,cAAgB,EAAhB,IAAgB,EAAE;gBAAlC,IAAM,MAAM,SAAA;gBAChB,IAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBACxC,EAAE,CAAC,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC;gBAC7B,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;aAC1B;YACD,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YAC7B,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACzB,oBAAoB;YACpB,IAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC9C,KAAkB,UAAgB,EAAhB,KAAA,IAAI,CAAC,WAAW,EAAhB,cAAgB,EAAhB,IAAgB,EAAE;gBAA/B,IAAM,GAAG,SAAA;gBACb,IAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBAExC,KAAqB,UAAgB,EAAhB,KAAA,IAAI,CAAC,WAAW,EAAhB,cAAgB,EAAhB,IAAgB,EAAE;oBAAlC,IAAM,MAAM,SAAA;oBAChB,IAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;oBACxC,EAAE,CAAC,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBAClC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;iBACnB;gBAED,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;aACtB;YAED,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAEzB,uCAAuC;YACvC,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;SAC/B;IAEF,CAAC;IACF,mBAAC;AAAD,CAAC,AA5CD,IA4CC;AAAA,CAAC"} \ No newline at end of file diff --git a/GridTemp.ts b/GridTemp.ts deleted file mode 100644 index c2ee69fa..00000000 --- a/GridTemp.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Class to manage the grid template and display records -class GridTemplate { - private columnNames: ColumnName[] = []; - private dataRecords: GridData[] = []; - // Initializes the column names and data records that will be used to display records in the grid. - constructor(columnNames: ColumnName[], dataRecords: GridData[]) { - this.columnNames = columnNames; - this.dataRecords = dataRecords; - }; - // Display records in a grid in table format - displayRecords(): void { - const gridElement = document.getElementById('grid'); - if (gridElement) { - gridElement.innerHTML = ''; - const table = document.createElement('table'); - const thead = document.createElement('thead'); - const headerRow = document.createElement('tr'); - for (const column of this.columnNames) { - const th = document.createElement('th'); - th.textContent = column.name; - headerRow.appendChild(th); - } - thead.appendChild(headerRow); - table.appendChild(thead); - // Create table body - const tbody = document.createElement('tbody'); - for (const row of this.dataRecords) { - const tr = document.createElement('tr'); - - for (const column of this.columnNames) { - const td = document.createElement('td'); - td.textContent = row[column.name]; - tr.appendChild(td); - } - - tbody.appendChild(tr); - } - - table.appendChild(tbody); - - // Append the table to the grid element - gridElement.appendChild(table); - } - - } -}; diff --git a/app.ts b/app.ts index 32473641..647d2ea0 100644 --- a/app.ts +++ b/app.ts @@ -6,6 +6,239 @@ interface ColumnName { interface GridData { [key: string]: any; } +class ApiData { + // Properties to manage data and settings + pageSize: number; + currentPage = 1; + data: GridData[] = []; + totalItems = 0; + columnNames: ColumnName[] = []; + maxGridHeight = 0; + firstVal = 0; + lastVal: number | undefined; + + constructor(pageSize: number) { + this.pageSize = pageSize; + }; + // Initialize method to set up the grid + async initialize(): Promise { + try { + this.adjustGridHeight(); + await this.recordCount(); + await this.fetchColumns(); + await this.fetchRecords(); + this.setupControls(); + } catch (error) { + console.error('Error during initialization:', error); + } + }; + // Method to fetch total record count from the server + async recordCount(): Promise { + try { + const response = await this.fetchData('http://localhost:2050/recordCount'); + this.totalItems = typeof response === 'number' ? response : parseInt(response as string, 10); + } catch (error) { + throw new Error('Failed to fetch record count.'); + } + }; + //fectch column names + async fetchColumns(): Promise { + try { + const response = await this.fetchData('http://localhost:2050/columns'); + const res = JSON.parse(response as string); + this.columnNames = res.map((columnName: any) => ({ name: columnName })); + this.data = new Array(this.columnNames.length); + } catch (error) { + throw new Error('Failed to fetch columns.'); + } + }; + //get records from API for fetch an search functionality + async fetchAndProcessRecords(from: number = this.firstVal, to: number): Promise { + try { + $('#spinner').show() + $('#grid').hide(); + const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); + const res = JSON.parse(response as string); + const processedData = res.map((record: any) => { + const obj: GridData = {}; + let columnIndex = 0; + for (const column of this.columnNames) { + if (columnIndex < record.length) { + const columnName = column.name; + const columnValue = record[columnIndex]; + obj[columnName] = columnValue; + } + columnIndex++; + } + return obj; + }); + $('#spinner').hide(); + $('#grid').show(); + return processedData; + } catch (error) { + throw new Error('Failed to fetch records'); + } + }; + //fetch records from api + async fetchRecords(): Promise { + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize, maxRange); + if (to >= maxRange) { + this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page + to = maxRange; + }; + try { + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.displayRecords(); + this.updatePageInfo(); + } catch (error) { + throw new Error('Failed to fetch records') + } + }; + //funtion to search through records using fromID + async searchRecords(searchValue: number): Promise { + try { + const maxRange = this.totalItems - 1; // Maximum allowed Value + if (searchValue >= 0 && searchValue <= maxRange) { + const from = searchValue; + const to = Math.min(from + this.pageSize, maxRange); + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.currentPage = Math.ceil(from / this.pageSize) + 1 + this.firstVal = from; // Set firstVal to searched value + this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize + this.displayRecords(); + this.updatePageInfo(); + } else { + alert('Please enter values in the range (0-999999)'); + } + } catch (error) { + throw new Error('Failed to search value'); + } + }; + //chnge grid height according to screen size + adjustGridHeight(): void { + const gridElement = document.getElementById('grid'); + const pageCntrl = $('.grid-controls').innerHeight(); + const screenHeight = $(window).innerHeight(); + if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { + this.maxGridHeight = screenHeight - pageCntrl; + gridElement.style.height = `${this.maxGridHeight}px`; + } + }; + // Update the page information and records display based on the current state of the grid. + updatePageInfo(): void { + const totalPages = Math.ceil(this.totalItems / this.pageSize); + const pageInfo = `Page ${this.currentPage} of ${totalPages}`; + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize, maxRange); + $('#pageInfo').text(`${pageInfo}`); + $('.records').text(`Showing records ${from} to ${to}`); + }; + // use Ajax for data fetching + private async fetchData(url: string): Promise { + try { + $('#overlay').show(); + const response = await $.ajax({ + url, + method: 'GET', + }); + $('#overlay').hide(); + return response; + } catch (error) { + throw error; + } + }; + private setupControls(): void { + $('#prevBtn').on('click', () => this.handlePageChange(-1)); + $('#nextBtn').on('click', () => this.handlePageChange(1)); + $(window).on('resize', debounce(this.handleResize, 350)); + }; + private handlePageChange(delta: number): void { + const newFirstVal = this.firstVal + delta * this.pageSize; + if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { + this.firstVal = newFirstVal; + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + } else if (newFirstVal <= this.pageSize) { + this.firstVal = 0 + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + }; + }; + private handleResize = (): void => { + const newWindowHeight = Math.floor($(window).innerHeight() as number); + const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; + // Check if the new grid size is non-negative + if (newGridSize >= 0) { + const newPageSize = newGridSize; + let newFirstValueIndex = this.firstVal; + // Adjust firstVal for the last page + if (newFirstValueIndex + newPageSize > this.totalItems) { + newFirstValueIndex = Math.max(this.totalItems - newPageSize); + } + // Update firstVal, lastVal, and page size + this.pageSize = newPageSize; + this.firstVal = newFirstValueIndex; + this.lastVal = newFirstValueIndex + newPageSize - 1; + // Fetch records, update page info, and adjust grid height + this.fetchRecords(); + this.updatePageInfo(); + this.adjustGridHeight(); + } + }; + private displayRecords = (): void => { + const gridTemplate = new GridTemplate(this.columnNames, this.data); + gridTemplate.displayRecords(); + this.updatePageInfo(); + }; +} +// Class to manage the grid template and display records +class GridTemplate { + private columnNames: ColumnName[] = []; + private dataRecords: GridData[] = []; + // Initializes the column names and data records that will be used to display records in the grid. + constructor(columnNames: ColumnName[], dataRecords: GridData[]) { + this.columnNames = columnNames; + this.dataRecords = dataRecords; + }; + // Display records in a grid in table format + displayRecords(): void { + const gridElement = document.getElementById('grid'); + if (gridElement) { + gridElement.innerHTML = ''; + const table = document.createElement('table'); + const thead = document.createElement('thead'); + const headerRow = document.createElement('tr'); + this.columnNames.forEach((column) => { + const th = document.createElement('th'); + th.textContent = column.name; + headerRow.appendChild(th); + }); + thead.appendChild(headerRow); + table.appendChild(thead); + // Create table body + const tbody = document.createElement('tbody'); + this.dataRecords.forEach((row) => { + const tr = document.createElement('tr'); + this.columnNames.forEach((column) => { + const td = document.createElement('td'); + td.textContent = row[column.name]; + tr.appendChild(td); + }); + tbody.appendChild(tr); + }); + table.appendChild(tbody); + // Append the table to the grid element + gridElement.appendChild(table); + } + }; +} // Debounce utility function to limit function execution frequency function debounce any>(func: F, waitFor: number) { let timeout: number; diff --git a/index.html b/index.html index a8a41a20..84b290ac 100644 --- a/index.html +++ b/index.html @@ -5,8 +5,6 @@ - - From 784d208dac77ccc2c1954cdcc21b1ad526066f24 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 30 Aug 2023 08:47:56 +0200 Subject: [PATCH 33/90] file separated into smaller parts --- .gitignore | 4 + ApiData.ts | 192 +++++++++++++++++++++++++++++++++++++++++++ GridTemp.ts | 41 +++++++++ app.ts | 233 +--------------------------------------------------- index.html | 2 + 5 files changed, 240 insertions(+), 232 deletions(-) create mode 100644 ApiData.ts create mode 100644 GridTemp.ts diff --git a/.gitignore b/.gitignore index 0872c36c..99a97374 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ /node_modules app.js app.js.map +ApiData.js +ApiData.js.map +GridTemp.js +GridTemp.js.map diff --git a/ApiData.ts b/ApiData.ts new file mode 100644 index 00000000..ea771d88 --- /dev/null +++ b/ApiData.ts @@ -0,0 +1,192 @@ +class ApiData { + // Properties to manage data and settings + pageSize: number; + currentPage = 1; + data: GridData[] = []; + totalItems = 0; + columnNames: ColumnName[] = []; + maxGridHeight = 0; + firstVal = 0; + lastVal: number | undefined; + + constructor(pageSize: number) { + this.pageSize = pageSize; + }; + // Initialize method to set up the grid + async initialize(): Promise { + try { + this.adjustGridHeight(); + await this.recordCount(); + await this.fetchColumns(); + await this.fetchRecords(); + this.setupControls(); + } catch (error) { + console.error('Error during initialization:', error); + } + }; + // Method to fetch total record count from the server + async recordCount(): Promise { + try { + const response = await this.fetchData('http://localhost:2050/recordCount'); + this.totalItems = typeof response === 'number' ? response : parseInt(response as string, 10); + } catch (error) { + throw new Error('Failed to fetch record count.'); + } + }; + //fectch column names + async fetchColumns(): Promise { + try { + const response = await this.fetchData('http://localhost:2050/columns'); + const res = JSON.parse(response as string); + this.columnNames = res.map((columnName: any) => ({ name: columnName })); + this.data = new Array(this.columnNames.length); + } catch (error) { + throw new Error('Failed to fetch columns.'); + } + }; + //get records from API for fetch an search functionality + async fetchAndProcessRecords(from: number = this.firstVal, to: number): Promise { + try { + $('#spinner').show() + $('#grid').hide(); + const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); + const res = JSON.parse(response as string); + const processedData = res.map((record: any) => { + const obj: GridData = {}; + let columnIndex = 0; + for (const column of this.columnNames) { + if (columnIndex < record.length) { + const columnName = column.name; + const columnValue = record[columnIndex]; + obj[columnName] = columnValue; + } + columnIndex++; + } + return obj; + }); + $('#spinner').hide(); + $('#grid').show(); + return processedData; + } catch (error) { + throw new Error('Failed to fetch records'); + } + }; + //fetch records from api + async fetchRecords(): Promise { + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize, maxRange); + if (to >= maxRange) { + this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page + to = maxRange; + }; + try { + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.displayRecords(); + this.updatePageInfo(); + } catch (error) { + throw new Error('Failed to fetch records') + } + }; + //funtion to search through records using fromID + async searchRecords(searchValue: number): Promise { + try { + const maxRange = this.totalItems - 1; // Maximum allowed Value + if (searchValue >= 0 && searchValue <= maxRange) { + const from = searchValue; + const to = Math.min(from + this.pageSize, maxRange); + const processedData = await this.fetchAndProcessRecords(from, to); + this.data = processedData; + this.currentPage = Math.ceil(from / this.pageSize) + 1 + this.firstVal = from; // Set firstVal to searched value + this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize + this.displayRecords(); + this.updatePageInfo(); + } else { + alert('Please enter values in the range (0-999999)'); + } + } catch (error) { + throw new Error('Failed to search value'); + } + }; + //chnge grid height according to screen size + adjustGridHeight(): void { + const gridElement = document.getElementById('grid'); + const pageCntrl = $('.grid-controls').innerHeight(); + const screenHeight = $(window).innerHeight(); + if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { + this.maxGridHeight = screenHeight - pageCntrl; + gridElement.style.height = `${this.maxGridHeight}px`; + } + }; + // Update the page information and records display based on the current state of the grid. + updatePageInfo(): void { + const totalPages = Math.ceil(this.totalItems / this.pageSize); + const pageInfo = `Page ${this.currentPage} of ${totalPages}`; + const maxRange = this.totalItems - 1; + const from = this.firstVal; + let to = Math.min(from + this.pageSize, maxRange); + $('#pageInfo').text(`${pageInfo}`); + $('.records').text(`Showing records ${from} to ${to}`); + }; + // use Ajax for data fetching + private async fetchData(url: string): Promise { + try { + $('#overlay').show(); + const response = await $.ajax({ + url, + method: 'GET', + }); + $('#overlay').hide(); + return response; + } catch (error) { + throw error; + } + }; + private setupControls(): void { + $('#prevBtn').on('click', () => this.handlePageChange(-1)); + $('#nextBtn').on('click', () => this.handlePageChange(1)); + $(window).on('resize', debounce(this.handleResize, 350)); + }; + private handlePageChange(delta: number): void { + const newFirstVal = this.firstVal + delta * this.pageSize; + if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { + this.firstVal = newFirstVal; + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + } else if (newFirstVal <= this.pageSize) { + this.firstVal = 0 + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + }; + }; + private handleResize = (): void => { + const newWindowHeight = Math.floor($(window).innerHeight() as number); + const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; + // Check if the new grid size is non-negative + if (newGridSize >= 0) { + const newPageSize = newGridSize; + let newFirstValueIndex = this.firstVal; + // Adjust firstVal for the last page + if (newFirstValueIndex + newPageSize > this.totalItems) { + newFirstValueIndex = Math.max(this.totalItems - newPageSize); + } + // Update firstVal, lastVal, and page size + this.pageSize = newPageSize; + this.firstVal = newFirstValueIndex; + this.lastVal = newFirstValueIndex + newPageSize - 1; + // Fetch records, update page info, and adjust grid height + this.fetchRecords(); + this.updatePageInfo(); + this.adjustGridHeight(); + } + }; + private displayRecords = (): void => { + const gridTemplate = new GridTemplate(this.columnNames, this.data); + gridTemplate.displayRecords(); + this.updatePageInfo(); + }; +} diff --git a/GridTemp.ts b/GridTemp.ts new file mode 100644 index 00000000..4a69932f --- /dev/null +++ b/GridTemp.ts @@ -0,0 +1,41 @@ +// Class to manage the grid template and display records +class GridTemplate { + private columnNames: ColumnName[] = []; + private dataRecords: GridData[] = []; + // Initializes the column names and data records that will be used to display records in the grid. + constructor(columnNames: ColumnName[], dataRecords: GridData[]) { + this.columnNames = columnNames; + this.dataRecords = dataRecords; + }; + // Display records in a grid in table format + displayRecords(): void { + const gridElement = document.getElementById('grid'); + if (gridElement) { + gridElement.innerHTML = ''; + const table = document.createElement('table'); + const thead = document.createElement('thead'); + const headerRow = document.createElement('tr'); + for (const column of this.columnNames) { + const th = document.createElement('th'); + th.textContent = column.name; + headerRow.appendChild(th); + } + thead.appendChild(headerRow); + table.appendChild(thead); + // Create table body + const tbody = document.createElement('tbody'); + for (const row of this.dataRecords) { + const tr = document.createElement('tr'); + for (const column of this.columnNames) { + const td = document.createElement('td'); + td.textContent = row[column.name]; + tr.appendChild(td); + } + tbody.appendChild(tr); + } + table.appendChild(tbody); + // Append the table to the grid element + gridElement.appendChild(table); + } + }; +} diff --git a/app.ts b/app.ts index 647d2ea0..fb0979d4 100644 --- a/app.ts +++ b/app.ts @@ -6,239 +6,8 @@ interface ColumnName { interface GridData { [key: string]: any; } -class ApiData { - // Properties to manage data and settings - pageSize: number; - currentPage = 1; - data: GridData[] = []; - totalItems = 0; - columnNames: ColumnName[] = []; - maxGridHeight = 0; - firstVal = 0; - lastVal: number | undefined; - constructor(pageSize: number) { - this.pageSize = pageSize; - }; - // Initialize method to set up the grid - async initialize(): Promise { - try { - this.adjustGridHeight(); - await this.recordCount(); - await this.fetchColumns(); - await this.fetchRecords(); - this.setupControls(); - } catch (error) { - console.error('Error during initialization:', error); - } - }; - // Method to fetch total record count from the server - async recordCount(): Promise { - try { - const response = await this.fetchData('http://localhost:2050/recordCount'); - this.totalItems = typeof response === 'number' ? response : parseInt(response as string, 10); - } catch (error) { - throw new Error('Failed to fetch record count.'); - } - }; - //fectch column names - async fetchColumns(): Promise { - try { - const response = await this.fetchData('http://localhost:2050/columns'); - const res = JSON.parse(response as string); - this.columnNames = res.map((columnName: any) => ({ name: columnName })); - this.data = new Array(this.columnNames.length); - } catch (error) { - throw new Error('Failed to fetch columns.'); - } - }; - //get records from API for fetch an search functionality - async fetchAndProcessRecords(from: number = this.firstVal, to: number): Promise { - try { - $('#spinner').show() - $('#grid').hide(); - const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); - const res = JSON.parse(response as string); - const processedData = res.map((record: any) => { - const obj: GridData = {}; - let columnIndex = 0; - for (const column of this.columnNames) { - if (columnIndex < record.length) { - const columnName = column.name; - const columnValue = record[columnIndex]; - obj[columnName] = columnValue; - } - columnIndex++; - } - return obj; - }); - $('#spinner').hide(); - $('#grid').show(); - return processedData; - } catch (error) { - throw new Error('Failed to fetch records'); - } - }; - //fetch records from api - async fetchRecords(): Promise { - const maxRange = this.totalItems - 1; - const from = this.firstVal; - let to = Math.min(from + this.pageSize, maxRange); - if (to >= maxRange) { - this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page - to = maxRange; - }; - try { - const processedData = await this.fetchAndProcessRecords(from, to); - this.data = processedData; - this.displayRecords(); - this.updatePageInfo(); - } catch (error) { - throw new Error('Failed to fetch records') - } - }; - //funtion to search through records using fromID - async searchRecords(searchValue: number): Promise { - try { - const maxRange = this.totalItems - 1; // Maximum allowed Value - if (searchValue >= 0 && searchValue <= maxRange) { - const from = searchValue; - const to = Math.min(from + this.pageSize, maxRange); - const processedData = await this.fetchAndProcessRecords(from, to); - this.data = processedData; - this.currentPage = Math.ceil(from / this.pageSize) + 1 - this.firstVal = from; // Set firstVal to searched value - this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize - this.displayRecords(); - this.updatePageInfo(); - } else { - alert('Please enter values in the range (0-999999)'); - } - } catch (error) { - throw new Error('Failed to search value'); - } - }; - //chnge grid height according to screen size - adjustGridHeight(): void { - const gridElement = document.getElementById('grid'); - const pageCntrl = $('.grid-controls').innerHeight(); - const screenHeight = $(window).innerHeight(); - if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { - this.maxGridHeight = screenHeight - pageCntrl; - gridElement.style.height = `${this.maxGridHeight}px`; - } - }; - // Update the page information and records display based on the current state of the grid. - updatePageInfo(): void { - const totalPages = Math.ceil(this.totalItems / this.pageSize); - const pageInfo = `Page ${this.currentPage} of ${totalPages}`; - const maxRange = this.totalItems - 1; - const from = this.firstVal; - let to = Math.min(from + this.pageSize, maxRange); - $('#pageInfo').text(`${pageInfo}`); - $('.records').text(`Showing records ${from} to ${to}`); - }; - // use Ajax for data fetching - private async fetchData(url: string): Promise { - try { - $('#overlay').show(); - const response = await $.ajax({ - url, - method: 'GET', - }); - $('#overlay').hide(); - return response; - } catch (error) { - throw error; - } - }; - private setupControls(): void { - $('#prevBtn').on('click', () => this.handlePageChange(-1)); - $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize, 350)); - }; - private handlePageChange(delta: number): void { - const newFirstVal = this.firstVal + delta * this.pageSize; - if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { - this.firstVal = newFirstVal; - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - } else if (newFirstVal <= this.pageSize) { - this.firstVal = 0 - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - }; - }; - private handleResize = (): void => { - const newWindowHeight = Math.floor($(window).innerHeight() as number); - const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; - // Check if the new grid size is non-negative - if (newGridSize >= 0) { - const newPageSize = newGridSize; - let newFirstValueIndex = this.firstVal; - // Adjust firstVal for the last page - if (newFirstValueIndex + newPageSize > this.totalItems) { - newFirstValueIndex = Math.max(this.totalItems - newPageSize); - } - // Update firstVal, lastVal, and page size - this.pageSize = newPageSize; - this.firstVal = newFirstValueIndex; - this.lastVal = newFirstValueIndex + newPageSize - 1; - // Fetch records, update page info, and adjust grid height - this.fetchRecords(); - this.updatePageInfo(); - this.adjustGridHeight(); - } - }; - private displayRecords = (): void => { - const gridTemplate = new GridTemplate(this.columnNames, this.data); - gridTemplate.displayRecords(); - this.updatePageInfo(); - }; -} -// Class to manage the grid template and display records -class GridTemplate { - private columnNames: ColumnName[] = []; - private dataRecords: GridData[] = []; - // Initializes the column names and data records that will be used to display records in the grid. - constructor(columnNames: ColumnName[], dataRecords: GridData[]) { - this.columnNames = columnNames; - this.dataRecords = dataRecords; - }; - // Display records in a grid in table format - displayRecords(): void { - const gridElement = document.getElementById('grid'); - if (gridElement) { - gridElement.innerHTML = ''; - const table = document.createElement('table'); - const thead = document.createElement('thead'); - const headerRow = document.createElement('tr'); - this.columnNames.forEach((column) => { - const th = document.createElement('th'); - th.textContent = column.name; - headerRow.appendChild(th); - }); - thead.appendChild(headerRow); - table.appendChild(thead); - // Create table body - const tbody = document.createElement('tbody'); - this.dataRecords.forEach((row) => { - const tr = document.createElement('tr'); - this.columnNames.forEach((column) => { - const td = document.createElement('td'); - td.textContent = row[column.name]; - tr.appendChild(td); - }); - tbody.appendChild(tr); - }); - table.appendChild(tbody); - // Append the table to the grid element - gridElement.appendChild(table); - } - }; -} + // Debounce utility function to limit function execution frequency function debounce any>(func: F, waitFor: number) { let timeout: number; diff --git a/index.html b/index.html index 84b290ac..a8a41a20 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,8 @@ + + From 7b77515f2f60fadb1586d9f7ddf1e8b4a01fcc14 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 30 Aug 2023 08:48:55 +0200 Subject: [PATCH 34/90] file separated into smaller parts --- app.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/app.ts b/app.ts index fb0979d4..32473641 100644 --- a/app.ts +++ b/app.ts @@ -6,8 +6,6 @@ interface ColumnName { interface GridData { [key: string]: any; } - - // Debounce utility function to limit function execution frequency function debounce any>(func: F, waitFor: number) { let timeout: number; From d7367c07da61b0f003e45387940a4a1f52808295 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 30 Aug 2023 10:13:27 +0200 Subject: [PATCH 35/90] file separated into smaller parts --- app.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app.ts b/app.ts index 32473641..7b952ab7 100644 --- a/app.ts +++ b/app.ts @@ -27,7 +27,7 @@ const rowHeight = 16; $(document).ready(() => { // Initialization and setup code const windowHeight = Math.floor($(window).innerHeight() as number); - const initialGridSize = Math.floor((windowHeight * gridRatio) / rowHeight) - 1; + const initialGridSize = Math.floor((windowHeight * gridRatio) / rowHeight); const apidata = new ApiData(initialGridSize); // Set up search button click handler $('#searchBtn').on('click', () => { @@ -38,7 +38,6 @@ $(document).ready(() => { let to = Math.min(from + pageSize, maxRange); let adjustedFrom = from; if (adjustedFrom + pageSize > maxRange) { - adjustedFrom = Math.max(0, maxRange - pageSize); to = maxRange; }; apidata.searchRecords(adjustedFrom); @@ -46,9 +45,9 @@ $(document).ready(() => { alert('please enter values in the range (0-999999)'); return; } else if (isNaN(from)) { - alert('Please enter a numerical value ') + alert('Please enter a numerical value '); } else { - console.error('error') + console.error('error'); }; //empty search input after searching $('#fromInput').val(''); From d15a3555a07d08ef86f4a57fdd5135440039c955 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 31 Aug 2023 10:25:21 +0200 Subject: [PATCH 36/90] first and last page more dynamic --- ApiData.ts | 27 ++++++++++++++++----------- Styles.css | 2 +- app.ts | 5 +++-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index ea771d88..59327767 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -151,18 +151,23 @@ class ApiData { }; private handlePageChange(delta: number): void { const newFirstVal = this.firstVal + delta * this.pageSize; - if (newFirstVal >= 0 && newFirstVal <= this.totalItems - 1) { - this.firstVal = newFirstVal; - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - } else if (newFirstVal <= this.pageSize) { - this.firstVal = 0 - this.lastVal = this.firstVal + this.pageSize - 1; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); + + if (delta > 0 && newFirstVal > this.totalItems - 1) { + // If moving forward and newFirstVal exceeds total items, go to the first page + this.firstVal = 0; + } else if (delta < 0 && newFirstVal < 0) { + // If moving backward and newFirstVal goes below 0, go to the last page + this.firstVal = Math.max(0, this.totalItems - this.pageSize); + }else { + this.firstVal = Math.max(0, Math.min(newFirstVal, this.totalItems - 1)); }; - }; + + this.lastVal = this.firstVal + this.pageSize - 1; + this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.fetchRecords(); + this.updatePageInfo(); + } + private handleResize = (): void => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; diff --git a/Styles.css b/Styles.css index b21dd165..3c9997ad 100644 --- a/Styles.css +++ b/Styles.css @@ -72,7 +72,7 @@ td { left: 0; width: 100%; height: 100%; - background-color: rgba(231, 238, 238, 0.753); + background-color: rgba(231, 238, 238, 0.699); z-index: 1000; display: none; } diff --git a/app.ts b/app.ts index 7b952ab7..379c620a 100644 --- a/app.ts +++ b/app.ts @@ -38,6 +38,7 @@ $(document).ready(() => { let to = Math.min(from + pageSize, maxRange); let adjustedFrom = from; if (adjustedFrom + pageSize > maxRange) { + adjustedFrom = Math.max(0, maxRange - pageSize); to = maxRange; }; apidata.searchRecords(adjustedFrom); @@ -45,9 +46,9 @@ $(document).ready(() => { alert('please enter values in the range (0-999999)'); return; } else if (isNaN(from)) { - alert('Please enter a numerical value '); + alert('Please enter a numerical value ') } else { - console.error('error'); + console.error('error') }; //empty search input after searching $('#fromInput').val(''); From 0b1bb066a079e7b15b40267d8e049f269726c718 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 31 Aug 2023 16:41:01 +0200 Subject: [PATCH 37/90] first and last page more dynamic --- ApiData.ts | 38 ++++++++++-------- Styles.css | 116 ++++++++++++++++++++++++++++------------------------- index.html | 34 ++++++++-------- 3 files changed, 101 insertions(+), 87 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 59327767..c88be19b 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -77,7 +77,8 @@ class ApiData { const from = this.firstVal; let to = Math.min(from + this.pageSize, maxRange); if (to >= maxRange) { - this.currentPage = Math.floor(maxRange / this.pageSize) + 1; // Set currentPage to the last page + // Set currentPage to the last page + this.currentPage = Math.floor(maxRange / this.pageSize) + 1; to = maxRange; }; try { @@ -87,20 +88,23 @@ class ApiData { this.updatePageInfo(); } catch (error) { throw new Error('Failed to fetch records') - } + }; }; //funtion to search through records using fromID async searchRecords(searchValue: number): Promise { try { - const maxRange = this.totalItems - 1; // Maximum allowed Value + // Maximum allowed Value + const maxRange = this.totalItems - 1; if (searchValue >= 0 && searchValue <= maxRange) { const from = searchValue; const to = Math.min(from + this.pageSize, maxRange); const processedData = await this.fetchAndProcessRecords(from, to); this.data = processedData; this.currentPage = Math.ceil(from / this.pageSize) + 1 - this.firstVal = from; // Set firstVal to searched value - this.lastVal = from + this.pageSize; // Calculate lastVal based on pageSize + // Set firstVal to searched value + this.firstVal = from; + // Calculate lastVal based on pageSize + this.lastVal = from + this.pageSize; this.displayRecords(); this.updatePageInfo(); } else { @@ -108,9 +112,9 @@ class ApiData { } } catch (error) { throw new Error('Failed to search value'); - } + }; }; - //chnge grid height according to screen size + //change grid height according to screen size adjustGridHeight(): void { const gridElement = document.getElementById('grid'); const pageCntrl = $('.grid-controls').innerHeight(); @@ -118,7 +122,7 @@ class ApiData { if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { this.maxGridHeight = screenHeight - pageCntrl; gridElement.style.height = `${this.maxGridHeight}px`; - } + }; }; // Update the page information and records display based on the current state of the grid. updatePageInfo(): void { @@ -142,32 +146,31 @@ class ApiData { return response; } catch (error) { throw error; - } - }; + }; + } private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); $(window).on('resize', debounce(this.handleResize, 350)); }; + //self explanatory private handlePageChange(delta: number): void { const newFirstVal = this.firstVal + delta * this.pageSize; - + // If moving forward and newFirstVal exceeds total items, go to the first page if (delta > 0 && newFirstVal > this.totalItems - 1) { - // If moving forward and newFirstVal exceeds total items, go to the first page this.firstVal = 0; } else if (delta < 0 && newFirstVal < 0) { // If moving backward and newFirstVal goes below 0, go to the last page this.firstVal = Math.max(0, this.totalItems - this.pageSize); - }else { + } else { this.firstVal = Math.max(0, Math.min(newFirstVal, this.totalItems - 1)); }; - this.lastVal = this.firstVal + this.pageSize - 1; this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchRecords(); this.updatePageInfo(); } - + //for resizing the page private handleResize = (): void => { const newWindowHeight = Math.floor($(window).innerHeight() as number); const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; @@ -187,8 +190,9 @@ class ApiData { this.fetchRecords(); this.updatePageInfo(); this.adjustGridHeight(); - } - }; + }; + } + private displayRecords = (): void => { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); diff --git a/Styles.css b/Styles.css index 3c9997ad..85b8c058 100644 --- a/Styles.css +++ b/Styles.css @@ -1,78 +1,86 @@ - body, html { - margin: 0; - padding: 0; - height: 100%; - overflow: hidden; - font-family: system-ui; + margin: 0; + padding: 0; + height: 100%; + overflow: hidden; + font-family: system-ui; } -.grid-container { - text-align: center; + +#overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(231, 238, 238, 0.815); + z-index: 900; + display: none; +} + +.spinner-container { + text-align: center; } + #grid { - border: 1px solid #333333; - text-align: center; - background-color: #d4e1e7; + border: 1px solid #333333; + text-align: center; + background-color: #d4e1e7; } + /* table */ table { - height: 100%; - border-collapse: collapse; + height: 100%; + border-collapse: collapse; } + tr { - border: 1px solid #333333; + border: 1px solid #333333; } + thead { - background-color: #333333; - font-weight: 450; - color: white; - + background-color: #333333; + font-weight: 450; + color: white; + } td { - padding: 2px; - border: 1px solid #333333; - align-content: center; - width: 350px; + padding: 2px; + border: 1px solid #333333; + align-content: center; + width: 350px; } + .grid-controls { - display: flex; - align-items: center; - justify-content: center; - padding: 10px; - color: white; - background-color: #333333; - position: sticky; - bottom: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 10px; + color: white; + background-color: #333333; + position: sticky; + bottom: 0; } -#pageInfo , + +#pageInfo, .records { - font-weight: 450; - background-color: #333333; + font-weight: 450; + background-color: #333333; } -/* button */ -.btn { - cursor: pointer; - padding: 5px 10px; - margin: 0 5px; - color: white; - background-color: #5a5f61; -} #fromInput { - padding: 5px 10px; - margin: 0 5px; - background-color: #232425; - color: white; + padding: 5px 10px; + margin: 0 5px; + background-color: #232425; + color: white; } -#overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(231, 238, 238, 0.699); - z-index: 1000; - display: none; + +/* button */ +.btn { + cursor: pointer; + padding: 5px 10px; + margin: 0 5px; + color: white; + background-color: #5a5f61; } diff --git a/index.html b/index.html index a8a41a20..0a052a21 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,6 @@ + JS Onboard Project @@ -8,24 +9,25 @@ + - -
- -
+ +
+ +
+
+
+
+ + + +
+ +
-
-
- - - -
- - -
-
+
+ From 631882623f13ab5a352ca5a7aa73445816a876f1 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 4 Sep 2023 08:30:10 +0200 Subject: [PATCH 38/90] unnecessary semicolons removed --- ApiData.ts | 18 +++++++++--------- Styles.css | 2 +- app.ts | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index c88be19b..ac3ffb76 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -23,7 +23,7 @@ class ApiData { } catch (error) { console.error('Error during initialization:', error); } - }; + } // Method to fetch total record count from the server async recordCount(): Promise { try { @@ -32,7 +32,7 @@ class ApiData { } catch (error) { throw new Error('Failed to fetch record count.'); } - }; + } //fectch column names async fetchColumns(): Promise { try { @@ -70,7 +70,7 @@ class ApiData { } catch (error) { throw new Error('Failed to fetch records'); } - }; + } //fetch records from api async fetchRecords(): Promise { const maxRange = this.totalItems - 1; @@ -89,7 +89,7 @@ class ApiData { } catch (error) { throw new Error('Failed to fetch records') }; - }; + } //funtion to search through records using fromID async searchRecords(searchValue: number): Promise { try { @@ -113,7 +113,7 @@ class ApiData { } catch (error) { throw new Error('Failed to search value'); }; - }; + } //change grid height according to screen size adjustGridHeight(): void { const gridElement = document.getElementById('grid'); @@ -123,7 +123,7 @@ class ApiData { this.maxGridHeight = screenHeight - pageCntrl; gridElement.style.height = `${this.maxGridHeight}px`; }; - }; + } // Update the page information and records display based on the current state of the grid. updatePageInfo(): void { const totalPages = Math.ceil(this.totalItems / this.pageSize); @@ -133,7 +133,7 @@ class ApiData { let to = Math.min(from + this.pageSize, maxRange); $('#pageInfo').text(`${pageInfo}`); $('.records').text(`Showing records ${from} to ${to}`); - }; + } // use Ajax for data fetching private async fetchData(url: string): Promise { try { @@ -152,7 +152,7 @@ class ApiData { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); $(window).on('resize', debounce(this.handleResize, 350)); - }; + } //self explanatory private handlePageChange(delta: number): void { const newFirstVal = this.firstVal + delta * this.pageSize; @@ -197,5 +197,5 @@ class ApiData { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); this.updatePageInfo(); - }; + } } diff --git a/Styles.css b/Styles.css index 85b8c058..6005ce39 100644 --- a/Styles.css +++ b/Styles.css @@ -25,7 +25,7 @@ html { #grid { border: 1px solid #333333; text-align: center; - background-color: #d4e1e7; + background-color: #f2f6f7; } /* table */ diff --git a/app.ts b/app.ts index 379c620a..f3fb8021 100644 --- a/app.ts +++ b/app.ts @@ -40,7 +40,7 @@ $(document).ready(() => { if (adjustedFrom + pageSize > maxRange) { adjustedFrom = Math.max(0, maxRange - pageSize); to = maxRange; - }; + } apidata.searchRecords(adjustedFrom); } else if (from < 0 || from > maxRange) { alert('please enter values in the range (0-999999)'); From 6b095c5ecd854dc5751cef0a71873868856d62ba Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 4 Sep 2023 15:29:03 +0200 Subject: [PATCH 39/90] updates --- ApiData.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index ac3ffb76..7f6ff93e 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -45,7 +45,7 @@ class ApiData { } }; //get records from API for fetch an search functionality - async fetchAndProcessRecords(from: number = this.firstVal, to: number): Promise { + async fetchAndProcessRecords(from: number, to: number): Promise { try { $('#spinner').show() $('#grid').hide(); @@ -115,7 +115,7 @@ class ApiData { }; } //change grid height according to screen size - adjustGridHeight(): void { + private adjustGridHeight(): void { const gridElement = document.getElementById('grid'); const pageCntrl = $('.grid-controls').innerHeight(); const screenHeight = $(window).innerHeight(); @@ -125,7 +125,7 @@ class ApiData { }; } // Update the page information and records display based on the current state of the grid. - updatePageInfo(): void { + private updatePageInfo(): void { const totalPages = Math.ceil(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; const maxRange = this.totalItems - 1; From 3af4520002d5c5998c9650b13060780785ed6745 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 5 Sep 2023 10:39:28 +0200 Subject: [PATCH 40/90] some of the requsted changes --- ApiData.ts | 237 ++++++++++++++++++++++++++++------------------------ GridTemp.ts | 5 +- Styles.css | 1 - app.ts | 42 +++++----- index.html | 2 +- 5 files changed, 154 insertions(+), 133 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 7f6ff93e..a30a4e6d 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -1,119 +1,139 @@ +// Interface to define the structure of grid data +interface ColumnName { + name: string; +} +// Interface to define column names +interface GridData { + [key: string]: any; +} class ApiData { // Properties to manage data and settings pageSize: number; - currentPage = 1; + currentPage: number = 1; data: GridData[] = []; - totalItems = 0; + totalItems: number = 0; columnNames: ColumnName[] = []; - maxGridHeight = 0; - firstVal = 0; - lastVal: number | undefined; + maxGridHeight: number = 0; + firstVal: number = 0; + lastVal: number = -1; constructor(pageSize: number) { this.pageSize = pageSize; - }; - // Initialize method to set up the grid + } + + /** Initialize method to set up the grid */ async initialize(): Promise { - try { - this.adjustGridHeight(); - await this.recordCount(); - await this.fetchColumns(); - await this.fetchRecords(); - this.setupControls(); - } catch (error) { - console.error('Error during initialization:', error); - } + Promise.resolve() + .then(() => this.adjustGridHeight()) + .then(() => this.recordCount()) + .then(() => this.fetchColumns()) + .then(() => this.fetchRecords()) + .then(() => this.setupControls()) } - // Method to fetch total record count from the server + + /**Method to fetch total record count from the server */ async recordCount(): Promise { - try { - const response = await this.fetchData('http://localhost:2050/recordCount'); - this.totalItems = typeof response === 'number' ? response : parseInt(response as string, 10); - } catch (error) { - throw new Error('Failed to fetch record count.'); - } + return this.fetchData('http://localhost:2050/recordCount') + .then((response: any) => { + const totalItems = response; + this.totalItems = totalItems; + }) + .catch(() => { + throw ('Failed to fetch record count.'); + }); } - //fectch column names + + /**fectch column names*/ async fetchColumns(): Promise { - try { - const response = await this.fetchData('http://localhost:2050/columns'); - const res = JSON.parse(response as string); - this.columnNames = res.map((columnName: any) => ({ name: columnName })); - this.data = new Array(this.columnNames.length); - } catch (error) { - throw new Error('Failed to fetch columns.'); - } - }; - //get records from API for fetch an search functionality + return this.fetchData('http://localhost:2050/columns') + .then((response: any) => { + const res = JSON.parse(response); + this.columnNames = res.map((columnName: any) => ({ name: columnName })); + this.data = new Array(this.columnNames.length); + }) + .catch(() => { + throw ('Failed to fetch columns.'); + }); + } + + /**get records from API for fetch an search functionality*/ async fetchAndProcessRecords(from: number, to: number): Promise { - try { - $('#spinner').show() - $('#grid').hide(); - const response = await this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`); - const res = JSON.parse(response as string); - const processedData = res.map((record: any) => { - const obj: GridData = {}; - let columnIndex = 0; - for (const column of this.columnNames) { - if (columnIndex < record.length) { - const columnName = column.name; - const columnValue = record[columnIndex]; - obj[columnName] = columnValue; + $('#spinner').show(); + $('#grid').hide(); + + return this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`) + .then((response: any) => { + const res = JSON.parse(response); + const processedData = res.map((record: any) => { + const obj: GridData = {}; + for (let j = 0; j < this.columnNames.length && j < record.length; j++) { + obj[this.columnNames[j].name] = record[j]; } - columnIndex++; - } - return obj; + return obj; + }); + $('#spinner').hide(); + $('#grid').show(); + return processedData; + }) + .catch(() => { + throw ('Failed to fetch records'); }); - $('#spinner').hide(); - $('#grid').show(); - return processedData; - } catch (error) { - throw new Error('Failed to fetch records'); - } } - //fetch records from api + + /**fetch records from api*/ async fetchRecords(): Promise { const maxRange = this.totalItems - 1; const from = this.firstVal; let to = Math.min(from + this.pageSize, maxRange); + if (to >= maxRange) { // Set currentPage to the last page this.currentPage = Math.floor(maxRange / this.pageSize) + 1; to = maxRange; - }; - try { - const processedData = await this.fetchAndProcessRecords(from, to); - this.data = processedData; - this.displayRecords(); - this.updatePageInfo(); - } catch (error) { - throw new Error('Failed to fetch records') - }; - } - //funtion to search through records using fromID - async searchRecords(searchValue: number): Promise { - try { - // Maximum allowed Value - const maxRange = this.totalItems - 1; - if (searchValue >= 0 && searchValue <= maxRange) { - const from = searchValue; - const to = Math.min(from + this.pageSize, maxRange); - const processedData = await this.fetchAndProcessRecords(from, to); + } + + return this.fetchAndProcessRecords(from, to) + .then((processedData) => { this.data = processedData; - this.currentPage = Math.ceil(from / this.pageSize) + 1 - // Set firstVal to searched value - this.firstVal = from; - // Calculate lastVal based on pageSize - this.lastVal = from + this.pageSize; this.displayRecords(); this.updatePageInfo(); - } else { - alert('Please enter values in the range (0-999999)'); - } - } catch (error) { - throw new Error('Failed to search value'); - }; + }) + .catch(() => { + throw ('Failed to fetch records'); + }); } + + + /**funtion to search through records using fromID*/ + async searchRecords(searchValue: number): Promise { + // Maximum allowed Value + const maxRange = this.totalItems - 1; + + if (searchValue >= 0 && searchValue <= maxRange) { + const from = searchValue; + const to = Math.min(from + this.pageSize, maxRange); + + return this.fetchAndProcessRecords(from, to) + .then((processedData) => { + this.data = processedData; + this.currentPage = Math.ceil(from / this.pageSize) + 1; + // Set firstVal to searched value + this.firstVal = from; + // Calculate lastVal based on pageSize + this.lastVal = from + this.pageSize; + this.displayRecords(); + this.updatePageInfo(); + }) + .catch(() => { + throw ('Failed to search value'); + }); + } else { + alert('Please enter values in the range (0-999999)'); + return Promise.resolve(); + } + } + + //change grid height according to screen size private adjustGridHeight(): void { const gridElement = document.getElementById('grid'); @@ -124,6 +144,7 @@ class ApiData { gridElement.style.height = `${this.maxGridHeight}px`; }; } + // Update the page information and records display based on the current state of the grid. private updatePageInfo(): void { const totalPages = Math.ceil(this.totalItems / this.pageSize); @@ -134,6 +155,7 @@ class ApiData { $('#pageInfo').text(`${pageInfo}`); $('.records').text(`Showing records ${from} to ${to}`); } + // use Ajax for data fetching private async fetchData(url: string): Promise { try { @@ -148,51 +170,50 @@ class ApiData { throw error; }; } + private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize, 350)); + $(window).on('resize', debounce(this.handleResize, 100)); } - //self explanatory + private handlePageChange(delta: number): void { - const newFirstVal = this.firstVal + delta * this.pageSize; - // If moving forward and newFirstVal exceeds total items, go to the first page - if (delta > 0 && newFirstVal > this.totalItems - 1) { + + if (delta > 0 && this.firstVal + delta * this.pageSize > this.totalItems - 1) { this.firstVal = 0; - } else if (delta < 0 && newFirstVal < 0) { - // If moving backward and newFirstVal goes below 0, go to the last page + } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { this.firstVal = Math.max(0, this.totalItems - this.pageSize); } else { - this.firstVal = Math.max(0, Math.min(newFirstVal, this.totalItems - 1)); - }; + this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.totalItems - 1)); + } + this.lastVal = this.firstVal + this.pageSize - 1; this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchRecords(); this.updatePageInfo(); } - //for resizing the page + private handleResize = (): void => { - const newWindowHeight = Math.floor($(window).innerHeight() as number); - const newGridSize = Math.floor((newWindowHeight * gridRatio) / rowHeight) - 1; + const newGridSize = Math.floor((Math.floor($(window).innerHeight() as number) * GRID_RATIO) / ROW_HEIGHT) - 1; + // Check if the new grid size is non-negative if (newGridSize >= 0) { - const newPageSize = newGridSize; - let newFirstValueIndex = this.firstVal; // Adjust firstVal for the last page - if (newFirstValueIndex + newPageSize > this.totalItems) { - newFirstValueIndex = Math.max(this.totalItems - newPageSize); + if (this.firstVal + newGridSize > this.totalItems) { + this.firstVal = Math.max(this.totalItems - newGridSize); } - // Update firstVal, lastVal, and page size - this.pageSize = newPageSize; - this.firstVal = newFirstValueIndex; - this.lastVal = newFirstValueIndex + newPageSize - 1; + + this.pageSize = newGridSize; + this.lastVal = this.firstVal + newGridSize - 1; + // Fetch records, update page info, and adjust grid height this.fetchRecords(); this.updatePageInfo(); this.adjustGridHeight(); - }; + } } + private displayRecords = (): void => { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); diff --git a/GridTemp.ts b/GridTemp.ts index 4a69932f..3ceeb168 100644 --- a/GridTemp.ts +++ b/GridTemp.ts @@ -6,9 +6,10 @@ class GridTemplate { constructor(columnNames: ColumnName[], dataRecords: GridData[]) { this.columnNames = columnNames; this.dataRecords = dataRecords; - }; + } // Display records in a grid in table format displayRecords(): void { + const gridElement = document.getElementById('grid'); if (gridElement) { gridElement.innerHTML = ''; @@ -37,5 +38,5 @@ class GridTemplate { // Append the table to the grid element gridElement.appendChild(table); } - }; + } } diff --git a/Styles.css b/Styles.css index 6005ce39..82507cd0 100644 --- a/Styles.css +++ b/Styles.css @@ -42,7 +42,6 @@ thead { background-color: #333333; font-weight: 450; color: white; - } td { diff --git a/app.ts b/app.ts index f3fb8021..6527dc0a 100644 --- a/app.ts +++ b/app.ts @@ -1,12 +1,5 @@ -// Interface to define the structure of grid data -interface ColumnName { - name: string; -} -// Interface to define column names -interface GridData { - [key: string]: any; -} -// Debounce utility function to limit function execution frequency + +/** Debounce utility function to limit function execution frequency*/ function debounce any>(func: F, waitFor: number) { let timeout: number; @@ -19,19 +12,22 @@ function debounce any>(func: F, waitFor: number) { }, waitFor); }); }; -}; -// Constants for grid calculation -const gridRatio = 9 / 20;// represents the ratio of the grid's height to the window's height. -const rowHeight = 16; -// Wait for the document to be ready +} +/** Constants for grid calculation + * GRID_RATIO represents the ratio of the grid's height to the window's height. +*/ +const GRID_RATIO = 9 / 20; +const ROW_HEIGHT = 16; +/** Wait for the document to be ready*/ $(document).ready(() => { // Initialization and setup code - const windowHeight = Math.floor($(window).innerHeight() as number); - const initialGridSize = Math.floor((windowHeight * gridRatio) / rowHeight); + const windowHeight = Math.floor(($(window).innerHeight() )); + const initialGridSize = Math.floor((windowHeight * GRID_RATIO) / ROW_HEIGHT); const apidata = new ApiData(initialGridSize); // Set up search button click handler $('#searchBtn').on('click', () => { - const from = parseInt($('#fromInput').val() as string); + + const from = parseInt(($('#fromInput').val())); const pageSize = apidata.pageSize; const maxRange = apidata.totalItems - 1; if (!isNaN(from) && from >= 0 && from <= maxRange) { @@ -46,15 +42,19 @@ $(document).ready(() => { alert('please enter values in the range (0-999999)'); return; } else if (isNaN(from)) { - alert('Please enter a numerical value ') + alert('Please enter a numerical value '); } else { - console.error('error') - }; + console.error('error'); + } //empty search input after searching $('#fromInput').val(''); }); // Initialize the grid - apidata.initialize(); + apidata.initialize() + .catch((error) => { + console.error('Error during initialization:', error); + alert(error); + }); //overlay when the page is still getting ready const overlay = $('
'); $('body').append(overlay); diff --git a/index.html b/index.html index 0a052a21..25ee6280 100644 --- a/index.html +++ b/index.html @@ -5,9 +5,9 @@ JS Onboard Project - + From 9722222b5a2ad4aee92de680494564abbbf9d31a Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 5 Sep 2023 14:11:26 +0200 Subject: [PATCH 41/90] some of the requsted changes --- ApiData.ts | 6 ++++-- GridTemp.ts | 3 ++- app.ts | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index a30a4e6d..75345de2 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -1,11 +1,13 @@ -// Interface to define the structure of grid data +/**Interface to define the structure of grid data*/ interface ColumnName { name: string; } -// Interface to define column names + +/**Interface to define column names*/ interface GridData { [key: string]: any; } + class ApiData { // Properties to manage data and settings pageSize: number; diff --git a/GridTemp.ts b/GridTemp.ts index 3ceeb168..72d0b139 100644 --- a/GridTemp.ts +++ b/GridTemp.ts @@ -1,8 +1,9 @@ -// Class to manage the grid template and display records +/** Class to manage the grid template and display records*/ class GridTemplate { private columnNames: ColumnName[] = []; private dataRecords: GridData[] = []; // Initializes the column names and data records that will be used to display records in the grid. + constructor(columnNames: ColumnName[], dataRecords: GridData[]) { this.columnNames = columnNames; this.dataRecords = dataRecords; diff --git a/app.ts b/app.ts index 6527dc0a..7ec05bdb 100644 --- a/app.ts +++ b/app.ts @@ -13,11 +13,13 @@ function debounce any>(func: F, waitFor: number) { }); }; } + /** Constants for grid calculation * GRID_RATIO represents the ratio of the grid's height to the window's height. */ const GRID_RATIO = 9 / 20; const ROW_HEIGHT = 16; + /** Wait for the document to be ready*/ $(document).ready(() => { // Initialization and setup code @@ -26,7 +28,7 @@ $(document).ready(() => { const apidata = new ApiData(initialGridSize); // Set up search button click handler $('#searchBtn').on('click', () => { - + const from = parseInt(($('#fromInput').val())); const pageSize = apidata.pageSize; const maxRange = apidata.totalItems - 1; From 5a6339bdf6fa3f8f69a35303120f1769ab2c8a8d Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 5 Sep 2023 16:10:04 +0200 Subject: [PATCH 42/90] some of the requested changes --- ApiData.ts | 6 +++--- GridTemp.ts | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 75345de2..c2a177c2 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -7,7 +7,7 @@ interface ColumnName { interface GridData { [key: string]: any; } - +/** class to manage data and */ class ApiData { // Properties to manage data and settings pageSize: number; @@ -136,7 +136,7 @@ class ApiData { } - //change grid height according to screen size + /**change grid height according to screen size*/ private adjustGridHeight(): void { const gridElement = document.getElementById('grid'); const pageCntrl = $('.grid-controls').innerHeight(); @@ -147,7 +147,7 @@ class ApiData { }; } - // Update the page information and records display based on the current state of the grid. + /** Update the page information and records display based on the current state of the grid.*/ private updatePageInfo(): void { const totalPages = Math.ceil(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; diff --git a/GridTemp.ts b/GridTemp.ts index 72d0b139..99eed795 100644 --- a/GridTemp.ts +++ b/GridTemp.ts @@ -2,13 +2,14 @@ class GridTemplate { private columnNames: ColumnName[] = []; private dataRecords: GridData[] = []; - // Initializes the column names and data records that will be used to display records in the grid. + /** Initializes the column names and data records that will be used to display records in the grid. */ constructor(columnNames: ColumnName[], dataRecords: GridData[]) { this.columnNames = columnNames; this.dataRecords = dataRecords; } - // Display records in a grid in table format + + /*Display records in a grid in table format*/ displayRecords(): void { const gridElement = document.getElementById('grid'); From c81a30884a2b1b12c56267760f6861d2effa2852 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 5 Sep 2023 16:58:27 +0200 Subject: [PATCH 43/90] requested changes --- ApiData.ts | 4 ++-- GridTemp.ts | 4 ++-- app.ts | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index c2a177c2..640fc42f 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -147,7 +147,7 @@ class ApiData { }; } - /** Update the page information and records display based on the current state of the grid.*/ + /** Update the page information and records display based on the current state of the grid.*/ private updatePageInfo(): void { const totalPages = Math.ceil(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; @@ -158,7 +158,7 @@ class ApiData { $('.records').text(`Showing records ${from} to ${to}`); } - // use Ajax for data fetching + /** use Ajax for data fetching*/ private async fetchData(url: string): Promise { try { $('#overlay').show(); diff --git a/GridTemp.ts b/GridTemp.ts index 99eed795..7f80e238 100644 --- a/GridTemp.ts +++ b/GridTemp.ts @@ -2,14 +2,14 @@ class GridTemplate { private columnNames: ColumnName[] = []; private dataRecords: GridData[] = []; - + /** Initializes the column names and data records that will be used to display records in the grid. */ constructor(columnNames: ColumnName[], dataRecords: GridData[]) { this.columnNames = columnNames; this.dataRecords = dataRecords; } - /*Display records in a grid in table format*/ + /*Display records in a grid in table format*/ displayRecords(): void { const gridElement = document.getElementById('grid'); diff --git a/app.ts b/app.ts index 7ec05bdb..aa7e882a 100644 --- a/app.ts +++ b/app.ts @@ -17,13 +17,13 @@ function debounce any>(func: F, waitFor: number) { /** Constants for grid calculation * GRID_RATIO represents the ratio of the grid's height to the window's height. */ -const GRID_RATIO = 9 / 20; +const GRID_RATIO = 9 / 20; const ROW_HEIGHT = 16; /** Wait for the document to be ready*/ $(document).ready(() => { // Initialization and setup code - const windowHeight = Math.floor(($(window).innerHeight() )); + const windowHeight = Math.floor(($(window).innerHeight())); const initialGridSize = Math.floor((windowHeight * GRID_RATIO) / ROW_HEIGHT); const apidata = new ApiData(initialGridSize); // Set up search button click handler @@ -53,10 +53,10 @@ $(document).ready(() => { }); // Initialize the grid apidata.initialize() - .catch((error) => { - console.error('Error during initialization:', error); - alert(error); - }); + .catch((error) => { + console.error('Error during initialization:', error); + alert(error); + }); //overlay when the page is still getting ready const overlay = $('
'); $('body').append(overlay); From 60d758acec48d022e0650713a5f1945a84e39956 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 6 Sep 2023 09:07:08 +0200 Subject: [PATCH 44/90] requested changes --- ApiData.ts | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 640fc42f..7490ae85 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -7,6 +7,7 @@ interface ColumnName { interface GridData { [key: string]: any; } + /** class to manage data and */ class ApiData { // Properties to manage data and settings @@ -34,7 +35,7 @@ class ApiData { } /**Method to fetch total record count from the server */ - async recordCount(): Promise { + recordCount(): Promise { return this.fetchData('http://localhost:2050/recordCount') .then((response: any) => { const totalItems = response; @@ -46,7 +47,7 @@ class ApiData { } /**fectch column names*/ - async fetchColumns(): Promise { + fetchColumns(): Promise { return this.fetchData('http://localhost:2050/columns') .then((response: any) => { const res = JSON.parse(response); @@ -59,7 +60,7 @@ class ApiData { } /**get records from API for fetch an search functionality*/ - async fetchAndProcessRecords(from: number, to: number): Promise { + fetchAndProcessRecords(from: number, to: number): Promise { $('#spinner').show(); $('#grid').hide(); @@ -83,7 +84,7 @@ class ApiData { } /**fetch records from api*/ - async fetchRecords(): Promise { + fetchRecords(): Promise { const maxRange = this.totalItems - 1; const from = this.firstVal; let to = Math.min(from + this.pageSize, maxRange); @@ -107,7 +108,7 @@ class ApiData { /**funtion to search through records using fromID*/ - async searchRecords(searchValue: number): Promise { + searchRecords(searchValue: number): Promise { // Maximum allowed Value const maxRange = this.totalItems - 1; @@ -160,19 +161,14 @@ class ApiData { /** use Ajax for data fetching*/ private async fetchData(url: string): Promise { - try { - $('#overlay').show(); - const response = await $.ajax({ - url, - method: 'GET', - }); - $('#overlay').hide(); - return response; - } catch (error) { - throw error; - }; - } - + $('#overlay').show(); + const response = await $.ajax({ + url, + method: 'GET', + }); + $('#overlay').hide(); + return response; + } private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); @@ -215,7 +211,6 @@ class ApiData { } } - private displayRecords = (): void => { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); From 79b2f6a83712458b19eb88cc917afb6f653e249a Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 6 Sep 2023 09:11:16 +0200 Subject: [PATCH 45/90] requested changes --- ApiData.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 7490ae85..47409ca2 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -136,6 +136,16 @@ class ApiData { } } + /** use Ajax for data fetching*/ + private async fetchData(url: string): Promise { + $('#overlay').show(); + const response = await $.ajax({ + url, + method: 'GET', + }); + $('#overlay').hide(); + return response; + } /**change grid height according to screen size*/ private adjustGridHeight(): void { @@ -159,16 +169,7 @@ class ApiData { $('.records').text(`Showing records ${from} to ${to}`); } - /** use Ajax for data fetching*/ - private async fetchData(url: string): Promise { - $('#overlay').show(); - const response = await $.ajax({ - url, - method: 'GET', - }); - $('#overlay').hide(); - return response; - } + private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); From 2d119637cd822b62ac0281aa709b7a11c68a0eed Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 6 Sep 2023 10:12:49 +0200 Subject: [PATCH 46/90] updates --- .gitignore | 2 ++ ApiData.ts | 10 +--------- Interfaces.ts | 9 +++++++++ index.html | 1 + 4 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 Interfaces.ts diff --git a/.gitignore b/.gitignore index 99a97374..970fe8e5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ ApiData.js ApiData.js.map GridTemp.js GridTemp.js.map +Interfaces.js +Interfaces.js.map diff --git a/ApiData.ts b/ApiData.ts index 47409ca2..4ad59162 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -1,12 +1,4 @@ -/**Interface to define the structure of grid data*/ -interface ColumnName { - name: string; -} -/**Interface to define column names*/ -interface GridData { - [key: string]: any; -} /** class to manage data and */ class ApiData { @@ -186,7 +178,7 @@ class ApiData { this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.totalItems - 1)); } - this.lastVal = this.firstVal + this.pageSize - 1; + this.lastVal = this.firstVal + this.pageSize ; this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchRecords(); this.updatePageInfo(); diff --git a/Interfaces.ts b/Interfaces.ts new file mode 100644 index 00000000..e741ad57 --- /dev/null +++ b/Interfaces.ts @@ -0,0 +1,9 @@ +/**Interface to define the structure of grid data*/ +interface ColumnName { + name: string; +} + +/**Interface to define column names*/ +interface GridData { + [key: string]: any; +} diff --git a/index.html b/index.html index 25ee6280..cefee271 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,7 @@ JS Onboard Project + From 3f81c01c749d3d1aa8347772b29a74bfeedb93fd Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 6 Sep 2023 10:31:31 +0200 Subject: [PATCH 47/90] updates --- ApiData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index 4ad59162..e0470126 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -185,7 +185,7 @@ class ApiData { } private handleResize = (): void => { - const newGridSize = Math.floor((Math.floor($(window).innerHeight() as number) * GRID_RATIO) / ROW_HEIGHT) - 1; + const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; // Check if the new grid size is non-negative if (newGridSize >= 0) { From 7a1f77150902a5c7665b1484d62df0dfc03fcc8b Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 7 Sep 2023 08:21:45 +0200 Subject: [PATCH 48/90] new updates --- ApiData.ts | 8 +++----- app.ts | 5 ++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index e0470126..ecf13029 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -1,6 +1,4 @@ - - -/** class to manage data and */ +/** class to manage data and settings on the table */ class ApiData { // Properties to manage data and settings pageSize: number; @@ -115,7 +113,7 @@ class ApiData { // Set firstVal to searched value this.firstVal = from; // Calculate lastVal based on pageSize - this.lastVal = from + this.pageSize; + this.lastVal = from + this.pageSize - 1; this.displayRecords(); this.updatePageInfo(); }) @@ -185,7 +183,7 @@ class ApiData { } private handleResize = (): void => { - const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; + const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT); // Check if the new grid size is non-negative if (newGridSize >= 0) { diff --git a/app.ts b/app.ts index aa7e882a..bb9612d9 100644 --- a/app.ts +++ b/app.ts @@ -1,4 +1,3 @@ - /** Debounce utility function to limit function execution frequency*/ function debounce any>(func: F, waitFor: number) { let timeout: number; @@ -17,14 +16,14 @@ function debounce any>(func: F, waitFor: number) { /** Constants for grid calculation * GRID_RATIO represents the ratio of the grid's height to the window's height. */ -const GRID_RATIO = 9 / 20; +const GRID_RATIO = 9/20 ; const ROW_HEIGHT = 16; /** Wait for the document to be ready*/ $(document).ready(() => { // Initialization and setup code const windowHeight = Math.floor(($(window).innerHeight())); - const initialGridSize = Math.floor((windowHeight * GRID_RATIO) / ROW_HEIGHT); + const initialGridSize = Math.floor((windowHeight * GRID_RATIO) / ROW_HEIGHT) ; const apidata = new ApiData(initialGridSize); // Set up search button click handler $('#searchBtn').on('click', () => { From 20a47903b57bf52e37a54bc6a79df95eda755b2b Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 7 Sep 2023 08:29:19 +0200 Subject: [PATCH 49/90] new updates --- ApiData.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index ecf13029..f944a305 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -96,7 +96,6 @@ class ApiData { }); } - /**funtion to search through records using fromID*/ searchRecords(searchValue: number): Promise { // Maximum allowed Value @@ -159,7 +158,6 @@ class ApiData { $('.records').text(`Showing records ${from} to ${to}`); } - private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); From 105295d89bad8314a91236bab70ef2539f2eb84d Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 7 Sep 2023 15:27:32 +0200 Subject: [PATCH 50/90] new updates --- ApiData.ts | 6 +++++- app.ts | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index f944a305..59b4f6d4 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -1,4 +1,6 @@ -/** class to manage data and settings on the table */ + + +/** class to manage data and */ class ApiData { // Properties to manage data and settings pageSize: number; @@ -96,6 +98,7 @@ class ApiData { }); } + /**funtion to search through records using fromID*/ searchRecords(searchValue: number): Promise { // Maximum allowed Value @@ -158,6 +161,7 @@ class ApiData { $('.records').text(`Showing records ${from} to ${to}`); } + private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); diff --git a/app.ts b/app.ts index bb9612d9..6c445bd2 100644 --- a/app.ts +++ b/app.ts @@ -22,8 +22,7 @@ const ROW_HEIGHT = 16; /** Wait for the document to be ready*/ $(document).ready(() => { // Initialization and setup code - const windowHeight = Math.floor(($(window).innerHeight())); - const initialGridSize = Math.floor((windowHeight * GRID_RATIO) / ROW_HEIGHT) ; + const initialGridSize = Math.floor((($(window).innerHeight()) * GRID_RATIO) / ROW_HEIGHT) ; const apidata = new ApiData(initialGridSize); // Set up search button click handler $('#searchBtn').on('click', () => { From 42bca521714179d892221ccdf707a372caf72f6f Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 7 Sep 2023 16:04:03 +0200 Subject: [PATCH 51/90] some updates --- ApiData.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 59b4f6d4..30b2ab07 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -1,6 +1,4 @@ - - -/** class to manage data and */ +/** class to manage data and setting on the grid */ class ApiData { // Properties to manage data and settings pageSize: number; @@ -98,7 +96,6 @@ class ApiData { }); } - /**funtion to search through records using fromID*/ searchRecords(searchValue: number): Promise { // Maximum allowed Value From 57f6a668ac04aea4d97a5226f5a85a8662d80f52 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 8 Sep 2023 13:23:46 +0200 Subject: [PATCH 52/90] some updates --- ApiData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 30b2ab07..fddddf6c 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -1,4 +1,4 @@ -/** class to manage data and setting on the grid */ +/** class to manage data and settings on the grid */ class ApiData { // Properties to manage data and settings pageSize: number; @@ -49,7 +49,7 @@ class ApiData { }); } - /**get records from API for fetch an search functionality*/ + /**get records from API for fetch and search functionality*/ fetchAndProcessRecords(from: number, to: number): Promise { $('#spinner').show(); $('#grid').hide(); From f0e6652877a6bd54a345a3d1ee94a4bf5efd533b Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 8 Sep 2023 16:37:14 +0200 Subject: [PATCH 53/90] some of the requested updates --- ApiData.ts | 70 +++++++++++++++++++++++++++++++++++---------------- GridTemp.ts | 4 +-- Interfaces.ts | 4 +-- app.ts | 37 ++++++--------------------- index.html | 2 +- 5 files changed, 60 insertions(+), 57 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index fddddf6c..455d1267 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -1,3 +1,24 @@ +/** Debounce utility function to limit function execution frequency */ +function debounce any>(func: F, waitFor: number) { + let timeout: number; + + return (...args: Parameters): Promise> => { + clearTimeout(timeout); + + return new Promise((resolve) => { + timeout = setTimeout(() => { + resolve(func(...args)); + }, waitFor); + }); + }; +} + +/** Constants for grid calculation + * GRID_RATIO represents the ratio of the grid's height to the window's height. +*/ +const GRID_RATIO = 9/20; +const ROW_HEIGHT = 16; + /** class to manage data and settings on the grid */ class ApiData { // Properties to manage data and settings @@ -15,28 +36,27 @@ class ApiData { } /** Initialize method to set up the grid */ - async initialize(): Promise { - Promise.resolve() - .then(() => this.adjustGridHeight()) - .then(() => this.recordCount()) + initialize(): Promise { + this.adjustGridHeight(); + return this.recordCount() + .then(() => this.fetchColumns()) .then(() => this.fetchColumns()) .then(() => this.fetchRecords()) .then(() => this.setupControls()) } - - /**Method to fetch total record count from the server */ + /** Fetch total record count from the server */ recordCount(): Promise { return this.fetchData('http://localhost:2050/recordCount') .then((response: any) => { const totalItems = response; - this.totalItems = totalItems; + this.totalItems = totalItems ; }) .catch(() => { throw ('Failed to fetch record count.'); }); } - /**fectch column names*/ + /** fectch column names */ fetchColumns(): Promise { return this.fetchData('http://localhost:2050/columns') .then((response: any) => { @@ -49,7 +69,7 @@ class ApiData { }); } - /**get records from API for fetch and search functionality*/ + /** get records from API for fetch and search functionality */ fetchAndProcessRecords(from: number, to: number): Promise { $('#spinner').show(); $('#grid').hide(); @@ -73,7 +93,7 @@ class ApiData { }); } - /**fetch records from api*/ + /** fetch records from api */ fetchRecords(): Promise { const maxRange = this.totalItems - 1; const from = this.firstVal; @@ -86,7 +106,7 @@ class ApiData { } return this.fetchAndProcessRecords(from, to) - .then((processedData) => { + .then(processedData => { this.data = processedData; this.displayRecords(); this.updatePageInfo(); @@ -96,7 +116,7 @@ class ApiData { }); } - /**funtion to search through records using fromID*/ + /** search through records using fromID */ searchRecords(searchValue: number): Promise { // Maximum allowed Value const maxRange = this.totalItems - 1; @@ -120,7 +140,7 @@ class ApiData { throw ('Failed to search value'); }); } else { - alert('Please enter values in the range (0-999999)'); + alert(`Please enter values in the range (0-${this.totalItems-1})`); return Promise.resolve(); } } @@ -166,38 +186,44 @@ class ApiData { } private handlePageChange(delta: number): void { + let prevBtn = $('#prevBtn'); - if (delta > 0 && this.firstVal + delta * this.pageSize > this.totalItems - 1) { + // from last page go to first page + if (delta > 0 && this.firstVal + delta * this.pageSize > this.totalItems - 1){ + this.firstVal = 0; + prevBtn.attr("disabled", null); + }else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { this.firstVal = 0; - } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { - this.firstVal = Math.max(0, this.totalItems - this.pageSize); + prevBtn.attr("disabled","disabled"); } else { this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.totalItems - 1)); + prevBtn.attr("disabled", null ); } - this.lastVal = this.firstVal + this.pageSize ; + this.lastVal = this.firstVal + this.pageSize; this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchRecords(); this.updatePageInfo(); } private handleResize = (): void => { - const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT); + const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; // Check if the new grid size is non-negative if (newGridSize >= 0) { // Adjust firstVal for the last page - if (this.firstVal + newGridSize > this.totalItems) { - this.firstVal = Math.max(this.totalItems - newGridSize); + if (this.firstVal + newGridSize > this.totalItems - 1) { + this.firstVal = Math.min(this.totalItems - newGridSize); } this.pageSize = newGridSize; this.lastVal = this.firstVal + newGridSize - 1; - // Fetch records, update page info, and adjust grid height + // adjust grid height,Fetch records,and update page info + this.adjustGridHeight(); this.fetchRecords(); this.updatePageInfo(); - this.adjustGridHeight(); + } } diff --git a/GridTemp.ts b/GridTemp.ts index 7f80e238..70555595 100644 --- a/GridTemp.ts +++ b/GridTemp.ts @@ -1,4 +1,4 @@ -/** Class to manage the grid template and display records*/ +/** Class to manage the grid template and display records */ class GridTemplate { private columnNames: ColumnName[] = []; private dataRecords: GridData[] = []; @@ -9,7 +9,7 @@ class GridTemplate { this.dataRecords = dataRecords; } - /*Display records in a grid in table format*/ + /** Display records in a grid in table format */ displayRecords(): void { const gridElement = document.getElementById('grid'); diff --git a/Interfaces.ts b/Interfaces.ts index e741ad57..6c600670 100644 --- a/Interfaces.ts +++ b/Interfaces.ts @@ -1,9 +1,9 @@ -/**Interface to define the structure of grid data*/ +/** Interface to define column names */ interface ColumnName { name: string; } -/**Interface to define column names*/ +/** Interface to define the structure of grid data */ interface GridData { [key: string]: any; } diff --git a/app.ts b/app.ts index 6c445bd2..9cac8792 100644 --- a/app.ts +++ b/app.ts @@ -1,45 +1,22 @@ -/** Debounce utility function to limit function execution frequency*/ -function debounce any>(func: F, waitFor: number) { - let timeout: number; - return (...args: Parameters): Promise> => { - clearTimeout(timeout); - - return new Promise((resolve) => { - timeout = setTimeout(() => { - resolve(func(...args)); - }, waitFor); - }); - }; -} - -/** Constants for grid calculation - * GRID_RATIO represents the ratio of the grid's height to the window's height. -*/ -const GRID_RATIO = 9/20 ; -const ROW_HEIGHT = 16; - -/** Wait for the document to be ready*/ +/** Wait for the document to be ready*/ $(document).ready(() => { // Initialization and setup code const initialGridSize = Math.floor((($(window).innerHeight()) * GRID_RATIO) / ROW_HEIGHT) ; const apidata = new ApiData(initialGridSize); // Set up search button click handler $('#searchBtn').on('click', () => { - - const from = parseInt(($('#fromInput').val())); + let from = parseInt(($('#fromInput').val())); const pageSize = apidata.pageSize; const maxRange = apidata.totalItems - 1; + if (!isNaN(from) && from >= 0 && from <= maxRange) { - let to = Math.min(from + pageSize, maxRange); - let adjustedFrom = from; - if (adjustedFrom + pageSize > maxRange) { - adjustedFrom = Math.max(0, maxRange - pageSize); - to = maxRange; + if (from + pageSize > maxRange) { + from = Math.max(0, maxRange - pageSize); } - apidata.searchRecords(adjustedFrom); + apidata.searchRecords(from); } else if (from < 0 || from > maxRange) { - alert('please enter values in the range (0-999999)'); + alert(`please enter values in the range (0-${maxRange})`); return; } else if (isNaN(from)) { alert('Please enter a numerical value '); diff --git a/index.html b/index.html index cefee271..c037ab5f 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ JS Onboard Project - + From e31e167e3a08ac00f57c8610ff78c0ecd457360b Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 11 Sep 2023 10:27:06 +0200 Subject: [PATCH 54/90] some updates to logic --- ApiData.ts | 55 ++++++++++++++++++++++++++++++------------------------ app.ts | 5 +++-- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 455d1267..b9faa21f 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -16,7 +16,7 @@ function debounce any>(func: F, waitFor: number) { /** Constants for grid calculation * GRID_RATIO represents the ratio of the grid's height to the window's height. */ -const GRID_RATIO = 9/20; +const GRID_RATIO = 9 / 20; const ROW_HEIGHT = 16; /** class to manage data and settings on the grid */ @@ -38,7 +38,7 @@ class ApiData { /** Initialize method to set up the grid */ initialize(): Promise { this.adjustGridHeight(); - return this.recordCount() + return this.recordCount() .then(() => this.fetchColumns()) .then(() => this.fetchColumns()) .then(() => this.fetchRecords()) @@ -49,7 +49,7 @@ class ApiData { return this.fetchData('http://localhost:2050/recordCount') .then((response: any) => { const totalItems = response; - this.totalItems = totalItems ; + this.totalItems = totalItems; }) .catch(() => { throw ('Failed to fetch record count.'); @@ -96,13 +96,17 @@ class ApiData { /** fetch records from api */ fetchRecords(): Promise { const maxRange = this.totalItems - 1; - const from = this.firstVal; + let from = this.firstVal; let to = Math.min(from + this.pageSize, maxRange); if (to >= maxRange) { - // Set currentPage to the last page - this.currentPage = Math.floor(maxRange / this.pageSize) + 1; + const lastPage = Math.floor(maxRange / this.pageSize) + 1; + + this.currentPage = lastPage; + to = maxRange; + from = (lastPage - 1) * this.pageSize + 1; + this.firstVal = from; } return this.fetchAndProcessRecords(from, to) @@ -140,23 +144,23 @@ class ApiData { throw ('Failed to search value'); }); } else { - alert(`Please enter values in the range (0-${this.totalItems-1})`); + alert(`Please enter values in the range (0-${this.totalItems - 1})`); return Promise.resolve(); } } - /** use Ajax for data fetching*/ + /** use Ajax for data fetching */ private async fetchData(url: string): Promise { $('#overlay').show(); const response = await $.ajax({ - url, - method: 'GET', + url, + method: 'GET', }); $('#overlay').hide(); return response; } - /**change grid height according to screen size*/ + /** Change grid height according to screen size */ private adjustGridHeight(): void { const gridElement = document.getElementById('grid'); const pageCntrl = $('.grid-controls').innerHeight(); @@ -167,7 +171,7 @@ class ApiData { }; } - /** Update the page information and records display based on the current state of the grid.*/ + /** Update the page information and records display based on the current state of the grid. */ private updatePageInfo(): void { const totalPages = Math.ceil(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; @@ -178,7 +182,7 @@ class ApiData { $('.records').text(`Showing records ${from} to ${to}`); } - + private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); @@ -187,20 +191,23 @@ class ApiData { private handlePageChange(delta: number): void { let prevBtn = $('#prevBtn'); + let nextBtn = $('#nextBtn'); - // from last page go to first page - if (delta > 0 && this.firstVal + delta * this.pageSize > this.totalItems - 1){ - this.firstVal = 0; - prevBtn.attr("disabled", null); - }else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { + if (delta > 0 && this.firstVal + delta * this.pageSize > this.totalItems - 1) { + this.firstVal = this.lastVal - this.pageSize; + prevBtn.attr("disabled", null); + nextBtn.attr("disabled", "disabled"); + } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { this.firstVal = 0; - prevBtn.attr("disabled","disabled"); + prevBtn.attr("disabled", "disabled"); + nextBtn.attr("disabled", null); } else { this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.totalItems - 1)); - prevBtn.attr("disabled", null ); + prevBtn.attr("disabled", null); + nextBtn.attr("disabled", null); } - this.lastVal = this.firstVal + this.pageSize; + this.lastVal = this.firstVal + delta * this.pageSize; this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchRecords(); this.updatePageInfo(); @@ -213,17 +220,17 @@ class ApiData { if (newGridSize >= 0) { // Adjust firstVal for the last page if (this.firstVal + newGridSize > this.totalItems - 1) { - this.firstVal = Math.min(this.totalItems - newGridSize); + this.firstVal = Math.min((this.totalItems - 1) - newGridSize); } this.pageSize = newGridSize; - this.lastVal = this.firstVal + newGridSize - 1; + this.lastVal = this.firstVal + newGridSize; // adjust grid height,Fetch records,and update page info this.adjustGridHeight(); this.fetchRecords(); this.updatePageInfo(); - + } } diff --git a/app.ts b/app.ts index 9cac8792..3d3b51b9 100644 --- a/app.ts +++ b/app.ts @@ -1,5 +1,5 @@ -/** Wait for the document to be ready*/ +/** Wait for the document to be ready */ $(document).ready(() => { // Initialization and setup code const initialGridSize = Math.floor((($(window).innerHeight()) * GRID_RATIO) / ROW_HEIGHT) ; @@ -19,9 +19,10 @@ $(document).ready(() => { alert(`please enter values in the range (0-${maxRange})`); return; } else if (isNaN(from)) { - alert('Please enter a numerical value '); + alert('Please enter a numerical value.'); } else { console.error('error'); + alert('An error has occured') } //empty search input after searching $('#fromInput').val(''); From 432722c544dd225c8bb37ba50aaee43a49c8a1ca Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 11 Sep 2023 16:10:23 +0200 Subject: [PATCH 55/90] some updates to logic --- ApiData.ts | 34 ++++++++++++++++++++++++---------- app.ts | 1 - index.html | 2 +- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index b9faa21f..ef3328be 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -14,14 +14,14 @@ function debounce any>(func: F, waitFor: number) { } /** Constants for grid calculation - * GRID_RATIO represents the ratio of the grid's height to the window's height. +* GRID_RATIO represents the ratio of the grid's height to the window's height. */ const GRID_RATIO = 9 / 20; const ROW_HEIGHT = 16; -/** class to manage data and settings on the grid */ +/** manage data and settings on the grid */ class ApiData { - // Properties to manage data and settings + pageSize: number; currentPage: number = 1; data: GridData[] = []; @@ -42,7 +42,7 @@ class ApiData { .then(() => this.fetchColumns()) .then(() => this.fetchColumns()) .then(() => this.fetchRecords()) - .then(() => this.setupControls()) + .then(() => this.setupControls()); } /** Fetch total record count from the server */ recordCount(): Promise { @@ -194,7 +194,7 @@ class ApiData { let nextBtn = $('#nextBtn'); if (delta > 0 && this.firstVal + delta * this.pageSize > this.totalItems - 1) { - this.firstVal = this.lastVal - this.pageSize; + this.firstVal = this.lastVal - delta * this.pageSize; prevBtn.attr("disabled", null); nextBtn.attr("disabled", "disabled"); } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { @@ -209,10 +209,18 @@ class ApiData { this.lastVal = this.firstVal + delta * this.pageSize; this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords(); - this.updatePageInfo(); + + this.fetchRecords() + .then(() => { + this.updatePageInfo(); + }) + .catch((error) => { + console.error("Error fetching records while changing page :", error); + alert('Error occured while changing page!'); + }); } + private handleResize = (): void => { const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; @@ -226,10 +234,16 @@ class ApiData { this.pageSize = newGridSize; this.lastVal = this.firstVal + newGridSize; - // adjust grid height,Fetch records,and update page info this.adjustGridHeight(); - this.fetchRecords(); - this.updatePageInfo(); + + this.fetchRecords() + .then(() => { + this.updatePageInfo(); + }) + .catch((error) => { + console.error("Error fetching records while resizing:", error); + alert('Error occured while resizing!'); + }); } } diff --git a/app.ts b/app.ts index 3d3b51b9..02e1f8e4 100644 --- a/app.ts +++ b/app.ts @@ -1,4 +1,3 @@ - /** Wait for the document to be ready */ $(document).ready(() => { // Initialization and setup code diff --git a/index.html b/index.html index c037ab5f..9a8a6f21 100644 --- a/index.html +++ b/index.html @@ -6,8 +6,8 @@ - + From 24af382c31e6ab5a776d75f3da4e171c937d7c0d Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 11 Sep 2023 16:12:02 +0200 Subject: [PATCH 56/90] some updates to logic --- ApiData.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index ef3328be..9a4ed71c 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -244,7 +244,6 @@ class ApiData { console.error("Error fetching records while resizing:", error); alert('Error occured while resizing!'); }); - } } From 9311f7113597cd5b83be7781d3d3aa166361cac5 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 12 Sep 2023 14:49:06 +0200 Subject: [PATCH 57/90] most updates requested done --- ApiData.ts | 56 ++++++++++++++++++++++++++++++++--------------------- GridTemp.ts | 2 +- app.ts | 22 ++------------------- index.html | 2 +- 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 9a4ed71c..3d41d188 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -30,16 +30,18 @@ class ApiData { maxGridHeight: number = 0; firstVal: number = 0; lastVal: number = -1; + maxRange: number; constructor(pageSize: number) { this.pageSize = pageSize; + this.maxRange = 0; + } /** Initialize method to set up the grid */ initialize(): Promise { this.adjustGridHeight(); return this.recordCount() - .then(() => this.fetchColumns()) .then(() => this.fetchColumns()) .then(() => this.fetchRecords()) .then(() => this.setupControls()); @@ -95,18 +97,15 @@ class ApiData { /** fetch records from api */ fetchRecords(): Promise { - const maxRange = this.totalItems - 1; + this.maxRange = this.totalItems - 1; let from = this.firstVal; - let to = Math.min(from + this.pageSize, maxRange); - - if (to >= maxRange) { - const lastPage = Math.floor(maxRange / this.pageSize) + 1; + let to = Math.min(from + this.pageSize, this.maxRange); + if (to >= this.maxRange) { + const lastPage = Math.floor(this.maxRange / this.pageSize) + 1; this.currentPage = lastPage; - - to = maxRange; - from = (lastPage - 1) * this.pageSize + 1; - this.firstVal = from; + from = Math.min(this.maxRange - this.pageSize); + to = this.maxRange; } return this.fetchAndProcessRecords(from, to) @@ -123,13 +122,17 @@ class ApiData { /** search through records using fromID */ searchRecords(searchValue: number): Promise { // Maximum allowed Value - const maxRange = this.totalItems - 1; + const maxRange = this.maxRange if (searchValue >= 0 && searchValue <= maxRange) { - const from = searchValue; - const to = Math.min(from + this.pageSize, maxRange); + let from = searchValue; + const pageSize = this.pageSize; + + if (from + pageSize > maxRange) { + from = Math.max(0, maxRange - pageSize); + } - return this.fetchAndProcessRecords(from, to) + return this.fetchAndProcessRecords(from, from + pageSize) .then((processedData) => { this.data = processedData; this.currentPage = Math.ceil(from / this.pageSize) + 1; @@ -139,12 +142,14 @@ class ApiData { this.lastVal = from + this.pageSize - 1; this.displayRecords(); this.updatePageInfo(); + //empty search input after searching + $('#fromInput').val(''); }) .catch(() => { throw ('Failed to search value'); }); } else { - alert(`Please enter values in the range (0-${this.totalItems - 1})`); + alert(`Error while searching , please enter values in the range (0-${maxRange})`); return Promise.resolve(); } } @@ -175,9 +180,8 @@ class ApiData { private updatePageInfo(): void { const totalPages = Math.ceil(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; - const maxRange = this.totalItems - 1; const from = this.firstVal; - let to = Math.min(from + this.pageSize, maxRange); + let to = Math.min(from + this.pageSize, this.maxRange); $('#pageInfo').text(`${pageInfo}`); $('.records').text(`Showing records ${from} to ${to}`); } @@ -186,25 +190,31 @@ class ApiData { private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize, 100)); + $(window).on('resize', debounce(this.handleResize, 250)); } private handlePageChange(delta: number): void { let prevBtn = $('#prevBtn'); let nextBtn = $('#nextBtn'); - if (delta > 0 && this.firstVal + delta * this.pageSize > this.totalItems - 1) { + // Check if delta is positive and the next page exceeds the MaxRange. + if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange) { + this.firstVal = this.lastVal - delta * this.pageSize; prevBtn.attr("disabled", null); nextBtn.attr("disabled", "disabled"); } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { + + // If delta is negative then reset firstVal to 0 and disabled prev button this.firstVal = 0; prevBtn.attr("disabled", "disabled"); nextBtn.attr("disabled", null); } else { - this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.totalItems - 1)); + + this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.maxRange)); prevBtn.attr("disabled", null); nextBtn.attr("disabled", null); + } this.lastVal = this.firstVal + delta * this.pageSize; @@ -227,8 +237,9 @@ class ApiData { // Check if the new grid size is non-negative if (newGridSize >= 0) { // Adjust firstVal for the last page - if (this.firstVal + newGridSize > this.totalItems - 1) { - this.firstVal = Math.min((this.totalItems - 1) - newGridSize); + if (this.firstVal + newGridSize > this.maxRange) { + this.firstVal = Math.min(this.maxRange - newGridSize); + } this.pageSize = newGridSize; @@ -251,5 +262,6 @@ class ApiData { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); this.updatePageInfo(); + } } diff --git a/GridTemp.ts b/GridTemp.ts index 70555595..e1f3af49 100644 --- a/GridTemp.ts +++ b/GridTemp.ts @@ -1,4 +1,4 @@ -/** Class to manage the grid template and display records */ +/** manage the grid template and display records */ class GridTemplate { private columnNames: ColumnName[] = []; private dataRecords: GridData[] = []; diff --git a/app.ts b/app.ts index 02e1f8e4..ae8ba58d 100644 --- a/app.ts +++ b/app.ts @@ -1,30 +1,12 @@ /** Wait for the document to be ready */ $(document).ready(() => { // Initialization and setup code - const initialGridSize = Math.floor((($(window).innerHeight()) * GRID_RATIO) / ROW_HEIGHT) ; + const initialGridSize = Math.floor((($(window).innerHeight()) * GRID_RATIO) / ROW_HEIGHT) - 1; const apidata = new ApiData(initialGridSize); // Set up search button click handler $('#searchBtn').on('click', () => { let from = parseInt(($('#fromInput').val())); - const pageSize = apidata.pageSize; - const maxRange = apidata.totalItems - 1; - - if (!isNaN(from) && from >= 0 && from <= maxRange) { - if (from + pageSize > maxRange) { - from = Math.max(0, maxRange - pageSize); - } - apidata.searchRecords(from); - } else if (from < 0 || from > maxRange) { - alert(`please enter values in the range (0-${maxRange})`); - return; - } else if (isNaN(from)) { - alert('Please enter a numerical value.'); - } else { - console.error('error'); - alert('An error has occured') - } - //empty search input after searching - $('#fromInput').val(''); + apidata.searchRecords(from); }); // Initialize the grid apidata.initialize() diff --git a/index.html b/index.html index 9a8a6f21..990b3779 100644 --- a/index.html +++ b/index.html @@ -25,7 +25,7 @@
- +
From 73c6b0bb05076df4d045d0bc8e592f6635cfab02 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 12 Sep 2023 14:54:10 +0200 Subject: [PATCH 58/90] most updates requested done --- ApiData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index 3d41d188..8e8c864f 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -200,7 +200,7 @@ class ApiData { // Check if delta is positive and the next page exceeds the MaxRange. if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange) { - this.firstVal = this.lastVal - delta * this.pageSize; + this.firstVal = this.lastVal - this.pageSize; prevBtn.attr("disabled", null); nextBtn.attr("disabled", "disabled"); } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { From a0b243f51a39ccc1caf305418d5b0be1d98e0d3a Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 12 Sep 2023 15:08:59 +0200 Subject: [PATCH 59/90] most updates requested done --- ApiData.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 8e8c864f..2132cbd5 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -186,7 +186,6 @@ class ApiData { $('.records').text(`Showing records ${from} to ${to}`); } - private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); @@ -197,7 +196,7 @@ class ApiData { let prevBtn = $('#prevBtn'); let nextBtn = $('#nextBtn'); - // Check if delta is positive and the next page exceeds the MaxRange. + // Check if delta is positive and disable the next page if firstval + pageSize exceeds the MaxRange. if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange) { this.firstVal = this.lastVal - this.pageSize; @@ -205,7 +204,7 @@ class ApiData { nextBtn.attr("disabled", "disabled"); } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { - // If delta is negative then reset firstVal to 0 and disabled prev button + // If delta is negative then reset firstVal to 0 and disable prev button this.firstVal = 0; prevBtn.attr("disabled", "disabled"); nextBtn.attr("disabled", null); @@ -230,7 +229,6 @@ class ApiData { }); } - private handleResize = (): void => { const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; @@ -262,6 +260,5 @@ class ApiData { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); this.updatePageInfo(); - } } From 2d5786bb92c231f885e30cbb12b94593f45aa2c4 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 12 Sep 2023 16:26:03 +0200 Subject: [PATCH 60/90] most updates requested done --- ApiData.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 2132cbd5..028b70aa 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -53,8 +53,9 @@ class ApiData { const totalItems = response; this.totalItems = totalItems; }) - .catch(() => { - throw ('Failed to fetch record count.'); + .catch(error => { + console.error('Failed to fetch record count:', error); + alert('Failed to fetch record count.'); }); } @@ -114,8 +115,9 @@ class ApiData { this.displayRecords(); this.updatePageInfo(); }) - .catch(() => { - throw ('Failed to fetch records'); + .catch((error) => { + console.error ('Failed to fetch records:',error); + alert('Error occured while fetching records!'); }); } @@ -223,7 +225,7 @@ class ApiData { .then(() => { this.updatePageInfo(); }) - .catch((error) => { + .catch(error => { console.error("Error fetching records while changing page :", error); alert('Error occured while changing page!'); }); @@ -249,7 +251,7 @@ class ApiData { .then(() => { this.updatePageInfo(); }) - .catch((error) => { + .catch(error => { console.error("Error fetching records while resizing:", error); alert('Error occured while resizing!'); }); From c73e79754791bca514a7c665229eb2621ff9ea76 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 13 Sep 2023 08:22:01 +0200 Subject: [PATCH 61/90] most updates requested done --- ApiData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index 028b70aa..0767be08 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -191,7 +191,7 @@ class ApiData { private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize, 250)); + $(window).on('resize', debounce(this.handleResize, 100)); } private handlePageChange(delta: number): void { From 6b5b5b463c47daa6aff4af3d60f9a6f057c9e521 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 13 Sep 2023 14:57:23 +0200 Subject: [PATCH 62/90] new updates with changes requested --- ApiData.ts | 41 ++++++++++++++++++++--------------------- app.ts | 5 ++++- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 0767be08..10a1ea95 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -35,7 +35,6 @@ class ApiData { constructor(pageSize: number) { this.pageSize = pageSize; this.maxRange = 0; - } /** Initialize method to set up the grid */ @@ -43,28 +42,30 @@ class ApiData { this.adjustGridHeight(); return this.recordCount() .then(() => this.fetchColumns()) - .then(() => this.fetchRecords()) + .then(() => this.fetchAndDisplayRecords()) .then(() => this.setupControls()); } + /** Fetch total record count from the server */ recordCount(): Promise { return this.fetchData('http://localhost:2050/recordCount') - .then((response: any) => { - const totalItems = response; + .then((response: number | string) => { + const totalItems = response; this.totalItems = totalItems; }) .catch(error => { console.error('Failed to fetch record count:', error); alert('Failed to fetch record count.'); + throw new Error('Failed to fetch record count.'); }); } - /** fectch column names */ + fetchColumns(): Promise { return this.fetchData('http://localhost:2050/columns') - .then((response: any) => { - const res = JSON.parse(response); - this.columnNames = res.map((columnName: any) => ({ name: columnName })); + .then((response:number|string) => { + const res = JSON.parse((response)); + this.columnNames = res.map((columnName: string) => ({ name: columnName })); this.data = new Array(this.columnNames.length); }) .catch(() => { @@ -78,9 +79,9 @@ class ApiData { $('#grid').hide(); return this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`) - .then((response: any) => { - const res = JSON.parse(response); - const processedData = res.map((record: any) => { + .then((response:number|string) => { + const res = JSON.parse((response)); + const processedData = res.map((record: string) => { const obj: GridData = {}; for (let j = 0; j < this.columnNames.length && j < record.length; j++) { obj[this.columnNames[j].name] = record[j]; @@ -96,8 +97,8 @@ class ApiData { }); } - /** fetch records from api */ - fetchRecords(): Promise { + /** Fetch records from the API, process them, display them, and update page info. */ + fetchAndDisplayRecords(): Promise { this.maxRange = this.totalItems - 1; let from = this.firstVal; let to = Math.min(from + this.pageSize, this.maxRange); @@ -105,7 +106,7 @@ class ApiData { if (to >= this.maxRange) { const lastPage = Math.floor(this.maxRange / this.pageSize) + 1; this.currentPage = lastPage; - from = Math.min(this.maxRange - this.pageSize); + from = this.maxRange - this.pageSize; to = this.maxRange; } @@ -115,7 +116,7 @@ class ApiData { this.displayRecords(); this.updatePageInfo(); }) - .catch((error) => { + .catch(error => { console.error ('Failed to fetch records:',error); alert('Error occured while fetching records!'); }); @@ -135,7 +136,7 @@ class ApiData { } return this.fetchAndProcessRecords(from, from + pageSize) - .then((processedData) => { + .then(processedData => { this.data = processedData; this.currentPage = Math.ceil(from / this.pageSize) + 1; // Set firstVal to searched value @@ -144,7 +145,7 @@ class ApiData { this.lastVal = from + this.pageSize - 1; this.displayRecords(); this.updatePageInfo(); - //empty search input after searching + // empty search input after searching $('#fromInput').val(''); }) .catch(() => { @@ -215,13 +216,12 @@ class ApiData { this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.maxRange)); prevBtn.attr("disabled", null); nextBtn.attr("disabled", null); - } this.lastVal = this.firstVal + delta * this.pageSize; this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; - this.fetchRecords() + this.fetchAndDisplayRecords() .then(() => { this.updatePageInfo(); }) @@ -239,7 +239,6 @@ class ApiData { // Adjust firstVal for the last page if (this.firstVal + newGridSize > this.maxRange) { this.firstVal = Math.min(this.maxRange - newGridSize); - } this.pageSize = newGridSize; @@ -247,7 +246,7 @@ class ApiData { this.adjustGridHeight(); - this.fetchRecords() + this.fetchAndDisplayRecords() .then(() => { this.updatePageInfo(); }) diff --git a/app.ts b/app.ts index ae8ba58d..15d14cda 100644 --- a/app.ts +++ b/app.ts @@ -3,18 +3,21 @@ $(document).ready(() => { // Initialization and setup code const initialGridSize = Math.floor((($(window).innerHeight()) * GRID_RATIO) / ROW_HEIGHT) - 1; const apidata = new ApiData(initialGridSize); + // Set up search button click handler $('#searchBtn').on('click', () => { let from = parseInt(($('#fromInput').val())); apidata.searchRecords(from); }); + // Initialize the grid apidata.initialize() .catch((error) => { console.error('Error during initialization:', error); alert(error); }); - //overlay when the page is still getting ready + + // overlay when the page is still getting ready const overlay = $('
'); $('body').append(overlay); }); From 9214692a804f0e8c4b2ee383fcfce739b7542bfa Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 13 Sep 2023 15:31:28 +0200 Subject: [PATCH 63/90] most changes done, to-do add more comments and functionality --- ApiData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 10a1ea95..3922c26a 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -231,7 +231,7 @@ class ApiData { }); } - private handleResize = (): void => { + private handleResize(): void { const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; // Check if the new grid size is non-negative @@ -257,7 +257,7 @@ class ApiData { } } - private displayRecords = (): void => { + private displayRecords():void { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); this.updatePageInfo(); From 7c5ee28550fb63bbea38ce4657929a1980bd3952 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 13 Sep 2023 16:43:53 +0200 Subject: [PATCH 64/90] most changes done, to-do add more comments and functionality. --- ApiData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 3922c26a..f9fdd919 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -231,7 +231,7 @@ class ApiData { }); } - private handleResize(): void { + private handleResize(){ const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; // Check if the new grid size is non-negative @@ -257,7 +257,7 @@ class ApiData { } } - private displayRecords():void { + private displayRecords(): void { const gridTemplate = new GridTemplate(this.columnNames, this.data); gridTemplate.displayRecords(); this.updatePageInfo(); From 603fc3dd15f3070d6730e110494a97a9898e042b Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 14 Sep 2023 08:03:13 +0200 Subject: [PATCH 65/90] most changes done, to-do add more comments and functionality. --- ApiData.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index f9fdd919..fdfa8e7c 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -63,7 +63,7 @@ class ApiData { fetchColumns(): Promise { return this.fetchData('http://localhost:2050/columns') - .then((response:number|string) => { + .then((response: number | string) => { const res = JSON.parse((response)); this.columnNames = res.map((columnName: string) => ({ name: columnName })); this.data = new Array(this.columnNames.length); @@ -79,7 +79,7 @@ class ApiData { $('#grid').hide(); return this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`) - .then((response:number|string) => { + .then((response: number | string) => { const res = JSON.parse((response)); const processedData = res.map((record: string) => { const obj: GridData = {}; @@ -117,7 +117,7 @@ class ApiData { this.updatePageInfo(); }) .catch(error => { - console.error ('Failed to fetch records:',error); + console.error('Failed to fetch records:', error); alert('Error occured while fetching records!'); }); } @@ -231,7 +231,7 @@ class ApiData { }); } - private handleResize(){ + private handleResize() { const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; // Check if the new grid size is non-negative From 04cb27af0a8f3115be06095dd5a95c3508a7c31b Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 14 Sep 2023 09:55:32 +0200 Subject: [PATCH 66/90] most requested changes done --- ApiData.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index fdfa8e7c..c94abe67 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -46,7 +46,7 @@ class ApiData { .then(() => this.setupControls()); } - /** Fetch total record count from the server */ + /** Fetch total record count from the server,fetches data from an API and populates class properties */ recordCount(): Promise { return this.fetchData('http://localhost:2050/recordCount') .then((response: number | string) => { @@ -60,12 +60,13 @@ class ApiData { }); } - + /** fetchColumns that returns a Promise */ fetchColumns(): Promise { return this.fetchData('http://localhost:2050/columns') .then((response: number | string) => { const res = JSON.parse((response)); this.columnNames = res.map((columnName: string) => ({ name: columnName })); + // Initialize the 'data' property as an empty array of GridData objects this.data = new Array(this.columnNames.length); }) .catch(() => { @@ -97,7 +98,7 @@ class ApiData { }); } - /** Fetch records from the API, process them, display them, and update page info. */ + /** Fetches records using fetchAndProcessRecords(), processes them, displays them, and updates page information. */ fetchAndDisplayRecords(): Promise { this.maxRange = this.totalItems - 1; let from = this.firstVal; @@ -192,14 +193,15 @@ class ApiData { private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize, 100)); + $(window).on('resize', debounce(this.handleResize.bind(this), 100)); } + /** Handles page navigation by updating the firstVal, lastVal, current page, and enabling/disabling previous and next buttons as needed. */ private handlePageChange(delta: number): void { let prevBtn = $('#prevBtn'); let nextBtn = $('#nextBtn'); - // Check if delta is positive and disable the next page if firstval + pageSize exceeds the MaxRange. + // Check if delta(change in page number) is positive and disable the next page if firstval + pageSize exceeds the MaxRange. if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange) { this.firstVal = this.lastVal - this.pageSize; @@ -231,7 +233,7 @@ class ApiData { }); } - private handleResize() { + private handleResize(): void { const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; // Check if the new grid size is non-negative From a43ea0875ad7a716aa6b5476b6142cfcbd655c40 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 14 Sep 2023 09:57:59 +0200 Subject: [PATCH 67/90] most requested changes done --- ApiData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index c94abe67..0589e509 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -177,7 +177,7 @@ class ApiData { if (gridElement && pageCntrl !== undefined && screenHeight !== undefined) { this.maxGridHeight = screenHeight - pageCntrl; gridElement.style.height = `${this.maxGridHeight}px`; - }; + } } /** Update the page information and records display based on the current state of the grid. */ From 7bf7ba502b6219a5880d8b68b17165888c3da99f Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 14 Sep 2023 10:28:57 +0200 Subject: [PATCH 68/90] most requested changes done --- ApiData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 0589e509..b29a004a 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -60,7 +60,7 @@ class ApiData { }); } - /** fetchColumns that returns a Promise */ + /** Use the fetchData() func to make an HTTP request to the API endpoint and process the data returned*/ fetchColumns(): Promise { return this.fetchData('http://localhost:2050/columns') .then((response: number | string) => { @@ -74,7 +74,7 @@ class ApiData { }); } - /** get records from API for fetch and search functionality */ + /** Get records from API for fetch and search functionality */ fetchAndProcessRecords(from: number, to: number): Promise { $('#spinner').show(); $('#grid').hide(); From c5174fc81ebdef64638222c4d97ac3fffd8bc606 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 14 Sep 2023 11:39:50 +0200 Subject: [PATCH 69/90] most requested changes done --- ApiData.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index b29a004a..b6395f93 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -203,18 +203,15 @@ class ApiData { // Check if delta(change in page number) is positive and disable the next page if firstval + pageSize exceeds the MaxRange. if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange) { - this.firstVal = this.lastVal - this.pageSize; prevBtn.attr("disabled", null); nextBtn.attr("disabled", "disabled"); } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { - // If delta is negative then reset firstVal to 0 and disable prev button this.firstVal = 0; prevBtn.attr("disabled", "disabled"); nextBtn.attr("disabled", null); } else { - this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.maxRange)); prevBtn.attr("disabled", null); nextBtn.attr("disabled", null); From 4eb4f8243ede93e1634c83cb25e3cf483ecba79c Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 14 Sep 2023 12:39:15 +0200 Subject: [PATCH 70/90] most requested changes done --- ApiData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index b6395f93..8e58664d 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -60,7 +60,7 @@ class ApiData { }); } - /** Use the fetchData() func to make an HTTP request to the API endpoint and process the data returned*/ + /** Use the fetchData() func to make an HTTP request to the API endpoint and process the data */ fetchColumns(): Promise { return this.fetchData('http://localhost:2050/columns') .then((response: number | string) => { @@ -237,7 +237,7 @@ class ApiData { if (newGridSize >= 0) { // Adjust firstVal for the last page if (this.firstVal + newGridSize > this.maxRange) { - this.firstVal = Math.min(this.maxRange - newGridSize); + this.firstVal = this.maxRange - newGridSize; } this.pageSize = newGridSize; From 5528fd67d3eb679d469c2be6a8d86f682afddfab Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 14 Sep 2023 12:50:43 +0200 Subject: [PATCH 71/90] most requested changes done --- ApiData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index 8e58664d..58965807 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -56,7 +56,7 @@ class ApiData { .catch(error => { console.error('Failed to fetch record count:', error); alert('Failed to fetch record count.'); - throw new Error('Failed to fetch record count.'); + throw error; }); } From 7c7ea1a58b7e621a809b60b1b47529e027a114b9 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 15 Sep 2023 10:54:08 +0200 Subject: [PATCH 72/90] requested changes done --- ApiData.ts | 2 +- index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 58965807..0d1acc2f 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -193,7 +193,7 @@ class ApiData { private setupControls(): void { $('#prevBtn').on('click', () => this.handlePageChange(-1)); $('#nextBtn').on('click', () => this.handlePageChange(1)); - $(window).on('resize', debounce(this.handleResize.bind(this), 100)); + $(window).on('resize', debounce(() => { this.handleResize(); }, 100)); } /** Handles page navigation by updating the firstVal, lastVal, current page, and enabling/disabling previous and next buttons as needed. */ diff --git a/index.html b/index.html index 990b3779..17eea436 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ JS Onboard Project - + From 1df95c0371b2c8b3c1a664bd5c2c1deee9a6fe96 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 18 Sep 2023 09:05:18 +0200 Subject: [PATCH 73/90] some changes done --- ApiData.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 0d1acc2f..643ef31a 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -93,8 +93,8 @@ class ApiData { $('#grid').show(); return processedData; }) - .catch(() => { - throw ('Failed to fetch records'); + .catch(error => { + throw new Error('Failed to fetch records: '+ error); }); } @@ -115,7 +115,7 @@ class ApiData { .then(processedData => { this.data = processedData; this.displayRecords(); - this.updatePageInfo(); + }) .catch(error => { console.error('Failed to fetch records:', error); @@ -126,7 +126,7 @@ class ApiData { /** search through records using fromID */ searchRecords(searchValue: number): Promise { // Maximum allowed Value - const maxRange = this.maxRange + const maxRange = this.maxRange; if (searchValue >= 0 && searchValue <= maxRange) { let from = searchValue; @@ -145,7 +145,6 @@ class ApiData { // Calculate lastVal based on pageSize this.lastVal = from + this.pageSize - 1; this.displayRecords(); - this.updatePageInfo(); // empty search input after searching $('#fromInput').val(''); }) @@ -221,9 +220,7 @@ class ApiData { this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchAndDisplayRecords() - .then(() => { - this.updatePageInfo(); - }) + .catch(error => { console.error("Error fetching records while changing page :", error); alert('Error occured while changing page!'); From 95183315045f702a1ddd06abd7fce2c3e354564c Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 19 Sep 2023 08:08:41 +0200 Subject: [PATCH 74/90] some changes done --- ApiData.ts | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 643ef31a..4feb8da7 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -48,9 +48,9 @@ class ApiData { /** Fetch total record count from the server,fetches data from an API and populates class properties */ recordCount(): Promise { - return this.fetchData('http://localhost:2050/recordCount') - .then((response: number | string) => { - const totalItems = response; + return this.fetchNumData('http://localhost:2050/recordCount') + .then((response: number) => { + const totalItems = response; this.totalItems = totalItems; }) .catch(error => { @@ -62,15 +62,16 @@ class ApiData { /** Use the fetchData() func to make an HTTP request to the API endpoint and process the data */ fetchColumns(): Promise { - return this.fetchData('http://localhost:2050/columns') - .then((response: number | string) => { - const res = JSON.parse((response)); + return this.fetchStrData('http://localhost:2050/columns') + .then((response: string) => { + const res = JSON.parse(response); this.columnNames = res.map((columnName: string) => ({ name: columnName })); // Initialize the 'data' property as an empty array of GridData objects this.data = new Array(this.columnNames.length); }) - .catch(() => { - throw ('Failed to fetch columns.'); + .catch(error => { + console.error('Failed to fetch columns:' + error); + throw ('Failed to fetch columns:' + error); }); } @@ -79,9 +80,9 @@ class ApiData { $('#spinner').show(); $('#grid').hide(); - return this.fetchData(`http://localhost:2050/records?from=${from}&to=${to}`) - .then((response: number | string) => { - const res = JSON.parse((response)); + return this.fetchStrData(`http://localhost:2050/records?from=${from}&to=${to}`) + .then((response: string) => { + const res = JSON.parse(response); const processedData = res.map((record: string) => { const obj: GridData = {}; for (let j = 0; j < this.columnNames.length && j < record.length; j++) { @@ -94,7 +95,8 @@ class ApiData { return processedData; }) .catch(error => { - throw new Error('Failed to fetch records: '+ error); + console.error('Failed to fetch records: ', error); + throw new Error('Failed to fetch records: ' + error); }); } @@ -115,7 +117,7 @@ class ApiData { .then(processedData => { this.data = processedData; this.displayRecords(); - + }) .catch(error => { console.error('Failed to fetch records:', error); @@ -158,7 +160,7 @@ class ApiData { } /** use Ajax for data fetching */ - private async fetchData(url: string): Promise { + private async fetchStrData(url: string): Promise { $('#overlay').show(); const response = await $.ajax({ url, @@ -168,6 +170,14 @@ class ApiData { return response; } + private async fetchNumData(url: string): Promise { + const response = await $.ajax({ + url, + method: 'GET', + }); + return response; + } + /** Change grid height according to screen size */ private adjustGridHeight(): void { const gridElement = document.getElementById('grid'); @@ -220,7 +230,6 @@ class ApiData { this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; this.fetchAndDisplayRecords() - .catch(error => { console.error("Error fetching records while changing page :", error); alert('Error occured while changing page!'); From 8093c8a08c5bfe2d38e55280fae3afe3dbda5525 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 19 Sep 2023 12:04:36 +0200 Subject: [PATCH 75/90] some changes done --- ApiData.ts | 9 ++++----- GridTemp.ts | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 4feb8da7..eb1b46ac 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -50,7 +50,7 @@ class ApiData { recordCount(): Promise { return this.fetchNumData('http://localhost:2050/recordCount') .then((response: number) => { - const totalItems = response; + const totalItems =response; this.totalItems = totalItems; }) .catch(error => { @@ -63,7 +63,7 @@ class ApiData { /** Use the fetchData() func to make an HTTP request to the API endpoint and process the data */ fetchColumns(): Promise { return this.fetchStrData('http://localhost:2050/columns') - .then((response: string) => { + .then((response:string) => { const res = JSON.parse(response); this.columnNames = res.map((columnName: string) => ({ name: columnName })); // Initialize the 'data' property as an empty array of GridData objects @@ -81,7 +81,7 @@ class ApiData { $('#grid').hide(); return this.fetchStrData(`http://localhost:2050/records?from=${from}&to=${to}`) - .then((response: string) => { + .then((response:string) => { const res = JSON.parse(response); const processedData = res.map((record: string) => { const obj: GridData = {}; @@ -117,7 +117,6 @@ class ApiData { .then(processedData => { this.data = processedData; this.displayRecords(); - }) .catch(error => { console.error('Failed to fetch records:', error); @@ -177,7 +176,7 @@ class ApiData { }); return response; } - + /** Change grid height according to screen size */ private adjustGridHeight(): void { const gridElement = document.getElementById('grid'); diff --git a/GridTemp.ts b/GridTemp.ts index e1f3af49..bd8799ce 100644 --- a/GridTemp.ts +++ b/GridTemp.ts @@ -1,7 +1,7 @@ /** manage the grid template and display records */ class GridTemplate { - private columnNames: ColumnName[] = []; - private dataRecords: GridData[] = []; + private columnNames: ColumnName[]; + private dataRecords: GridData[]; /** Initializes the column names and data records that will be used to display records in the grid. */ constructor(columnNames: ColumnName[], dataRecords: GridData[]) { From dc20c08383ebee79cbb5d8ec8bbf1a87d577cda5 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 21 Sep 2023 10:17:30 +0200 Subject: [PATCH 76/90] changes to search and a few --- ApiData.ts | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index eb1b46ac..4bb8dd40 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -50,8 +50,7 @@ class ApiData { recordCount(): Promise { return this.fetchNumData('http://localhost:2050/recordCount') .then((response: number) => { - const totalItems =response; - this.totalItems = totalItems; + this.totalItems = response; }) .catch(error => { console.error('Failed to fetch record count:', error); @@ -63,7 +62,7 @@ class ApiData { /** Use the fetchData() func to make an HTTP request to the API endpoint and process the data */ fetchColumns(): Promise { return this.fetchStrData('http://localhost:2050/columns') - .then((response:string) => { + .then((response: string) => { const res = JSON.parse(response); this.columnNames = res.map((columnName: string) => ({ name: columnName })); // Initialize the 'data' property as an empty array of GridData objects @@ -81,7 +80,7 @@ class ApiData { $('#grid').hide(); return this.fetchStrData(`http://localhost:2050/records?from=${from}&to=${to}`) - .then((response:string) => { + .then((response: string) => { const res = JSON.parse(response); const processedData = res.map((record: string) => { const obj: GridData = {}; @@ -130,21 +129,17 @@ class ApiData { const maxRange = this.maxRange; if (searchValue >= 0 && searchValue <= maxRange) { - let from = searchValue; + this.firstVal = searchValue; const pageSize = this.pageSize; - if (from + pageSize > maxRange) { - from = Math.max(0, maxRange - pageSize); + if (searchValue + pageSize > maxRange) { + searchValue = Math.max(0, maxRange - pageSize); } - return this.fetchAndProcessRecords(from, from + pageSize) + return this.fetchAndProcessRecords(searchValue, searchValue + pageSize) .then(processedData => { this.data = processedData; - this.currentPage = Math.ceil(from / this.pageSize) + 1; - // Set firstVal to searched value - this.firstVal = from; - // Calculate lastVal based on pageSize - this.lastVal = from + this.pageSize - 1; + this.currentPage = Math.ceil(searchValue / this.pageSize) + 1; this.displayRecords(); // empty search input after searching $('#fromInput').val(''); @@ -176,7 +171,7 @@ class ApiData { }); return response; } - + /** Change grid height according to screen size */ private adjustGridHeight(): void { const gridElement = document.getElementById('grid'); From 6fd89543486a20353b37b125ddda2cfcad203325 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 21 Sep 2023 12:16:28 +0200 Subject: [PATCH 77/90] changes to search and a few changes --- ApiData.ts | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 4bb8dd40..9b95f3cd 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -130,25 +130,12 @@ class ApiData { if (searchValue >= 0 && searchValue <= maxRange) { this.firstVal = searchValue; - const pageSize = this.pageSize; - - if (searchValue + pageSize > maxRange) { - searchValue = Math.max(0, maxRange - pageSize); - } - - return this.fetchAndProcessRecords(searchValue, searchValue + pageSize) - .then(processedData => { - this.data = processedData; - this.currentPage = Math.ceil(searchValue / this.pageSize) + 1; - this.displayRecords(); - // empty search input after searching - $('#fromInput').val(''); - }) - .catch(() => { - throw ('Failed to search value'); - }); + this.currentPage = Math.ceil(searchValue / this.pageSize) + 1; + // empty search input after searching + $('#fromInput').val(''); + return this.fetchAndDisplayRecords(); } else { - alert(`Error while searching , please enter values in the range (0-${maxRange})`); + alert(`Error while searching, please enter values in the range (0-${maxRange})`); return Promise.resolve(); } } From 5ffd990f2d113b6e040124361b4ac17351de206f Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Thu, 21 Sep 2023 12:29:08 +0200 Subject: [PATCH 78/90] removed unnecessary maxrange var --- ApiData.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 9b95f3cd..75085c95 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -125,17 +125,14 @@ class ApiData { /** search through records using fromID */ searchRecords(searchValue: number): Promise { - // Maximum allowed Value - const maxRange = this.maxRange; - - if (searchValue >= 0 && searchValue <= maxRange) { + if (searchValue >= 0 && searchValue <= this.maxRange) { this.firstVal = searchValue; this.currentPage = Math.ceil(searchValue / this.pageSize) + 1; // empty search input after searching $('#fromInput').val(''); return this.fetchAndDisplayRecords(); } else { - alert(`Error while searching, please enter values in the range (0-${maxRange})`); + alert(`Error while searching, please enter values in the range (0-${this.maxRange})`); return Promise.resolve(); } } From 8e9d50a5702a6014b78ba9b368da1a0e3cc26599 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 22 Sep 2023 14:22:50 +0200 Subject: [PATCH 79/90] some changes to logic --- .gitignore | 4 ++-- ApiData.ts | 8 ++++---- GridTemp.ts => GridTemplate.ts | 0 Styles.css | 18 ++++++++++++++++-- index.html | 2 +- 5 files changed, 23 insertions(+), 9 deletions(-) rename GridTemp.ts => GridTemplate.ts (100%) diff --git a/.gitignore b/.gitignore index 970fe8e5..e94d4e01 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ app.js app.js.map ApiData.js ApiData.js.map -GridTemp.js -GridTemp.js.map +GridTemplate.js +GridTemplate.js.map Interfaces.js Interfaces.js.map diff --git a/ApiData.ts b/ApiData.ts index 75085c95..f0f7a8ed 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -127,7 +127,7 @@ class ApiData { searchRecords(searchValue: number): Promise { if (searchValue >= 0 && searchValue <= this.maxRange) { this.firstVal = searchValue; - this.currentPage = Math.ceil(searchValue / this.pageSize) + 1; + this.currentPage = Math.ceil(this.firstVal / this.pageSize) + 1; // empty search input after searching $('#fromInput').val(''); return this.fetchAndDisplayRecords(); @@ -189,11 +189,11 @@ class ApiData { let nextBtn = $('#nextBtn'); // Check if delta(change in page number) is positive and disable the next page if firstval + pageSize exceeds the MaxRange. - if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange) { - this.firstVal = this.lastVal - this.pageSize; + if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange ) { + this.firstVal = this.maxRange - this.pageSize; prevBtn.attr("disabled", null); nextBtn.attr("disabled", "disabled"); - } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { + } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0 ) { // If delta is negative then reset firstVal to 0 and disable prev button this.firstVal = 0; prevBtn.attr("disabled", "disabled"); diff --git a/GridTemp.ts b/GridTemplate.ts similarity index 100% rename from GridTemp.ts rename to GridTemplate.ts diff --git a/Styles.css b/Styles.css index 82507cd0..b99d351f 100644 --- a/Styles.css +++ b/Styles.css @@ -38,17 +38,17 @@ tr { border: 1px solid #333333; } -thead { +th { background-color: #333333; font-weight: 450; color: white; } td { - padding: 2px; border: 1px solid #333333; align-content: center; width: 350px; + } .grid-controls { @@ -83,3 +83,17 @@ td { color: white; background-color: #5a5f61; } + +@media screen and (max-width: 8000px) { + td { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + td:hover { + overflow: visible; + white-space: normal; + text-overflow: unset; + } +} diff --git a/index.html b/index.html index 17eea436..f2d4e134 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,7 @@ - + From f2b7bf34fb6e86ceb04ac3676fe7858a00b01de8 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 22 Sep 2023 14:44:50 +0200 Subject: [PATCH 80/90] some changes to logic --- Styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Styles.css b/Styles.css index b99d351f..4d0fea23 100644 --- a/Styles.css +++ b/Styles.css @@ -84,7 +84,7 @@ td { background-color: #5a5f61; } -@media screen and (max-width: 8000px) { +@media screen and (max-width: 800px) { td { white-space: nowrap; overflow: hidden; From 138f01c2dde72f54a2986895db91d47e744caf17 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 26 Sep 2023 10:13:04 +0200 Subject: [PATCH 81/90] accounted for zero based indexing --- ApiData.ts | 17 ++++++++++------- Styles.css | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index f0f7a8ed..bb086f04 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -106,7 +106,7 @@ class ApiData { let to = Math.min(from + this.pageSize, this.maxRange); if (to >= this.maxRange) { - const lastPage = Math.floor(this.maxRange / this.pageSize) + 1; + const lastPage = Math.ceil(this.maxRange / (this.pageSize + 1)) + 1; this.currentPage = lastPage; from = this.maxRange - this.pageSize; to = this.maxRange; @@ -127,7 +127,10 @@ class ApiData { searchRecords(searchValue: number): Promise { if (searchValue >= 0 && searchValue <= this.maxRange) { this.firstVal = searchValue; - this.currentPage = Math.ceil(this.firstVal / this.pageSize) + 1; + if (searchValue + this.pageSize > this.maxRange) { + this.firstVal = Math.max(0, this.maxRange - this.pageSize); + } + this.currentPage = Math.ceil(this.firstVal / (this.pageSize + 1)) + 1; // empty search input after searching $('#fromInput').val(''); return this.fetchAndDisplayRecords(); @@ -169,7 +172,7 @@ class ApiData { /** Update the page information and records display based on the current state of the grid. */ private updatePageInfo(): void { - const totalPages = Math.ceil(this.totalItems / this.pageSize); + const totalPages = Math.ceil(this.totalItems /(this.pageSize + 1)); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; const from = this.firstVal; let to = Math.min(from + this.pageSize, this.maxRange); @@ -189,23 +192,23 @@ class ApiData { let nextBtn = $('#nextBtn'); // Check if delta(change in page number) is positive and disable the next page if firstval + pageSize exceeds the MaxRange. - if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange ) { + if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange) { this.firstVal = this.maxRange - this.pageSize; prevBtn.attr("disabled", null); nextBtn.attr("disabled", "disabled"); - } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0 ) { + } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { // If delta is negative then reset firstVal to 0 and disable prev button this.firstVal = 0; prevBtn.attr("disabled", "disabled"); nextBtn.attr("disabled", null); } else { - this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.maxRange)); + this.firstVal = Math.max(0, Math.min(this.firstVal + delta * (this.pageSize + 1), this.maxRange)); prevBtn.attr("disabled", null); nextBtn.attr("disabled", null); } this.lastVal = this.firstVal + delta * this.pageSize; - this.currentPage = Math.floor(this.firstVal / this.pageSize) + 1; + this.currentPage = Math.ceil(this.firstVal / (this.pageSize + 1)) + 1; this.fetchAndDisplayRecords() .catch(error => { diff --git a/Styles.css b/Styles.css index 4d0fea23..00bdfab2 100644 --- a/Styles.css +++ b/Styles.css @@ -84,7 +84,7 @@ td { background-color: #5a5f61; } -@media screen and (max-width: 800px) { +@media screen and (max-width: 850px) { td { white-space: nowrap; overflow: hidden; From e94ac2e1ddd99fb50515a0e05c868daa4500ff87 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Wed, 27 Sep 2023 15:09:02 +0200 Subject: [PATCH 82/90] 27/09 --- ApiData.ts | 12 ++++++------ Styles.css | 1 - app.ts | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index bb086f04..3cbee200 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -106,7 +106,7 @@ class ApiData { let to = Math.min(from + this.pageSize, this.maxRange); if (to >= this.maxRange) { - const lastPage = Math.ceil(this.maxRange / (this.pageSize + 1)) + 1; + const lastPage = Math.ceil(this.maxRange / this.pageSize) + 1; this.currentPage = lastPage; from = this.maxRange - this.pageSize; to = this.maxRange; @@ -130,7 +130,7 @@ class ApiData { if (searchValue + this.pageSize > this.maxRange) { this.firstVal = Math.max(0, this.maxRange - this.pageSize); } - this.currentPage = Math.ceil(this.firstVal / (this.pageSize + 1)) + 1; + this.currentPage = Math.ceil(this.firstVal / this.pageSize) + 1; // empty search input after searching $('#fromInput').val(''); return this.fetchAndDisplayRecords(); @@ -172,7 +172,7 @@ class ApiData { /** Update the page information and records display based on the current state of the grid. */ private updatePageInfo(): void { - const totalPages = Math.ceil(this.totalItems /(this.pageSize + 1)); + const totalPages = Math.ceil(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; const from = this.firstVal; let to = Math.min(from + this.pageSize, this.maxRange); @@ -207,8 +207,8 @@ class ApiData { nextBtn.attr("disabled", null); } - this.lastVal = this.firstVal + delta * this.pageSize; - this.currentPage = Math.ceil(this.firstVal / (this.pageSize + 1)) + 1; + this.lastVal = this.firstVal + this.pageSize; + this.currentPage = Math.ceil(this.firstVal / this.pageSize) + 1; this.fetchAndDisplayRecords() .catch(error => { @@ -218,7 +218,7 @@ class ApiData { } private handleResize(): void { - const newGridSize = Math.floor((Math.floor(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; + const newGridSize = Math.ceil((Math.ceil(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; // Check if the new grid size is non-negative if (newGridSize >= 0) { diff --git a/Styles.css b/Styles.css index 00bdfab2..8ad3c3d6 100644 --- a/Styles.css +++ b/Styles.css @@ -48,7 +48,6 @@ td { border: 1px solid #333333; align-content: center; width: 350px; - } .grid-controls { diff --git a/app.ts b/app.ts index 15d14cda..05aca4cc 100644 --- a/app.ts +++ b/app.ts @@ -1,7 +1,7 @@ /** Wait for the document to be ready */ $(document).ready(() => { // Initialization and setup code - const initialGridSize = Math.floor((($(window).innerHeight()) * GRID_RATIO) / ROW_HEIGHT) - 1; + const initialGridSize = Math.ceil(Math.ceil((($(window).innerHeight()))* GRID_RATIO)/ ROW_HEIGHT) - 1; const apidata = new ApiData(initialGridSize); // Set up search button click handler From cd397acdd41052a7ca936118fb73dbffbff80b32 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 29 Sep 2023 09:47:27 +0200 Subject: [PATCH 83/90] logic updated --- ApiData.ts | 20 ++++++++++---------- app.ts | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 3cbee200..fd15457d 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -103,12 +103,12 @@ class ApiData { fetchAndDisplayRecords(): Promise { this.maxRange = this.totalItems - 1; let from = this.firstVal; - let to = Math.min(from + this.pageSize, this.maxRange); + let to = Math.min(from + this.pageSize - 1, this.maxRange); if (to >= this.maxRange) { - const lastPage = Math.ceil(this.maxRange / this.pageSize) + 1; + const lastPage = Math.ceil(this.firstVal / this.pageSize ) + 1; this.currentPage = lastPage; - from = this.maxRange - this.pageSize; + from = this.maxRange - this.pageSize + 1; to = this.maxRange; } @@ -128,9 +128,9 @@ class ApiData { if (searchValue >= 0 && searchValue <= this.maxRange) { this.firstVal = searchValue; if (searchValue + this.pageSize > this.maxRange) { - this.firstVal = Math.max(0, this.maxRange - this.pageSize); + this.firstVal = Math.max(0, this.maxRange - this.pageSize + 1); } - this.currentPage = Math.ceil(this.firstVal / this.pageSize) + 1; + this.currentPage = Math.ceil(this.firstVal / this.pageSize ) + 1; // empty search input after searching $('#fromInput').val(''); return this.fetchAndDisplayRecords(); @@ -172,10 +172,10 @@ class ApiData { /** Update the page information and records display based on the current state of the grid. */ private updatePageInfo(): void { - const totalPages = Math.ceil(this.totalItems / this.pageSize); + const totalPages = Math.ceil(this.totalItems / this.pageSize ); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; const from = this.firstVal; - let to = Math.min(from + this.pageSize, this.maxRange); + let to = Math.min(from + this.pageSize - 1, this.maxRange); $('#pageInfo').text(`${pageInfo}`); $('.records').text(`Showing records ${from} to ${to}`); } @@ -193,7 +193,7 @@ class ApiData { // Check if delta(change in page number) is positive and disable the next page if firstval + pageSize exceeds the MaxRange. if (delta > 0 && this.firstVal + delta * this.pageSize > this.maxRange) { - this.firstVal = this.maxRange - this.pageSize; + this.firstVal = this.maxRange - this.pageSize + 1; prevBtn.attr("disabled", null); nextBtn.attr("disabled", "disabled"); } else if (delta < 0 && this.firstVal + delta * this.pageSize < 0) { @@ -202,7 +202,7 @@ class ApiData { prevBtn.attr("disabled", "disabled"); nextBtn.attr("disabled", null); } else { - this.firstVal = Math.max(0, Math.min(this.firstVal + delta * (this.pageSize + 1), this.maxRange)); + this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize , this.maxRange)); prevBtn.attr("disabled", null); nextBtn.attr("disabled", null); } @@ -218,7 +218,7 @@ class ApiData { } private handleResize(): void { - const newGridSize = Math.ceil((Math.ceil(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; + const newGridSize = (Math.ceil((Math.ceil(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT)) - 1; // Check if the new grid size is non-negative if (newGridSize >= 0) { diff --git a/app.ts b/app.ts index 05aca4cc..533e55c0 100644 --- a/app.ts +++ b/app.ts @@ -1,7 +1,7 @@ /** Wait for the document to be ready */ $(document).ready(() => { // Initialization and setup code - const initialGridSize = Math.ceil(Math.ceil((($(window).innerHeight()))* GRID_RATIO)/ ROW_HEIGHT) - 1; + const initialGridSize = (Math.ceil(Math.ceil((($(window).innerHeight()))* GRID_RATIO)/ ROW_HEIGHT)) - 1; const apidata = new ApiData(initialGridSize); // Set up search button click handler From e2d638d6beebd635bc01ee27d7b2c0e9e363cf14 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 29 Sep 2023 09:53:11 +0200 Subject: [PATCH 84/90] logic updated --- ApiData.ts | 2 +- app.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index fd15457d..95da97a4 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -218,7 +218,7 @@ class ApiData { } private handleResize(): void { - const newGridSize = (Math.ceil((Math.ceil(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT)) - 1; + const newGridSize = Math.ceil((Math.ceil(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT)- 1; // Check if the new grid size is non-negative if (newGridSize >= 0) { diff --git a/app.ts b/app.ts index 533e55c0..42edcb00 100644 --- a/app.ts +++ b/app.ts @@ -1,7 +1,7 @@ /** Wait for the document to be ready */ $(document).ready(() => { // Initialization and setup code - const initialGridSize = (Math.ceil(Math.ceil((($(window).innerHeight()))* GRID_RATIO)/ ROW_HEIGHT)) - 1; + const initialGridSize = Math.ceil(Math.ceil((($(window).innerHeight()))* GRID_RATIO)/ ROW_HEIGHT) - 1; const apidata = new ApiData(initialGridSize); // Set up search button click handler From 27e027ab96d65c491e15eae962fcc440289aba69 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 29 Sep 2023 11:12:22 +0200 Subject: [PATCH 85/90] style updated for width resize --- ApiData.ts | 2 +- Styles.css | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 95da97a4..5c55172e 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -224,7 +224,7 @@ class ApiData { if (newGridSize >= 0) { // Adjust firstVal for the last page if (this.firstVal + newGridSize > this.maxRange) { - this.firstVal = this.maxRange - newGridSize; + this.firstVal = this.maxRange - newGridSize + 1; } this.pageSize = newGridSize; diff --git a/Styles.css b/Styles.css index 8ad3c3d6..d4ee8bb1 100644 --- a/Styles.css +++ b/Styles.css @@ -1,5 +1,6 @@ +html, body, -html { +#grid { margin: 0; padding: 0; height: 100%; @@ -26,12 +27,16 @@ html { border: 1px solid #333333; text-align: center; background-color: #f2f6f7; + display: grid; + grid-template-rows: 1fr; } /* table */ table { + width: 100%; height: 100%; border-collapse: collapse; + table-layout: fixed; } tr { @@ -47,7 +52,7 @@ th { td { border: 1px solid #333333; align-content: center; - width: 350px; + width: 1%; } .grid-controls { @@ -83,7 +88,7 @@ td { background-color: #5a5f61; } -@media screen and (max-width: 850px) { +@media screen and (max-width: 1300px) { td { white-space: nowrap; overflow: hidden; @@ -95,4 +100,8 @@ td { white-space: normal; text-overflow: unset; } + + #pageInfo { + display: none; + } } From 126a1f071194754826f570d9706a61ced4064b0b Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Fri, 29 Sep 2023 12:13:36 +0200 Subject: [PATCH 86/90] resize logic updated --- ApiData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index 5c55172e..22ba31d9 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -228,7 +228,7 @@ class ApiData { } this.pageSize = newGridSize; - this.lastVal = this.firstVal + newGridSize; + this.lastVal = this.firstVal + newGridSize - 1; this.adjustGridHeight(); From 03274ebedfd29907fbeb4496f61c3d86ceaf5ad7 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 2 Oct 2023 08:14:51 +0200 Subject: [PATCH 87/90] changes requested done --- ApiData.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ApiData.ts b/ApiData.ts index 22ba31d9..bcbaaa3e 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -51,6 +51,7 @@ class ApiData { return this.fetchNumData('http://localhost:2050/recordCount') .then((response: number) => { this.totalItems = response; + this.maxRange = this.totalItems - 1; }) .catch(error => { console.error('Failed to fetch record count:', error); @@ -101,12 +102,11 @@ class ApiData { /** Fetches records using fetchAndProcessRecords(), processes them, displays them, and updates page information. */ fetchAndDisplayRecords(): Promise { - this.maxRange = this.totalItems - 1; let from = this.firstVal; let to = Math.min(from + this.pageSize - 1, this.maxRange); if (to >= this.maxRange) { - const lastPage = Math.ceil(this.firstVal / this.pageSize ) + 1; + const lastPage = Math.ceil(this.firstVal / this.pageSize) + 1; this.currentPage = lastPage; from = this.maxRange - this.pageSize + 1; to = this.maxRange; @@ -130,7 +130,7 @@ class ApiData { if (searchValue + this.pageSize > this.maxRange) { this.firstVal = Math.max(0, this.maxRange - this.pageSize + 1); } - this.currentPage = Math.ceil(this.firstVal / this.pageSize ) + 1; + this.currentPage = Math.ceil(this.firstVal / this.pageSize) + 1; // empty search input after searching $('#fromInput').val(''); return this.fetchAndDisplayRecords(); @@ -172,7 +172,7 @@ class ApiData { /** Update the page information and records display based on the current state of the grid. */ private updatePageInfo(): void { - const totalPages = Math.ceil(this.totalItems / this.pageSize ); + const totalPages = Math.ceil(this.totalItems / this.pageSize); const pageInfo = `Page ${this.currentPage} of ${totalPages}`; const from = this.firstVal; let to = Math.min(from + this.pageSize - 1, this.maxRange); @@ -202,12 +202,12 @@ class ApiData { prevBtn.attr("disabled", "disabled"); nextBtn.attr("disabled", null); } else { - this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize , this.maxRange)); + this.firstVal = Math.max(0, Math.min(this.firstVal + delta * this.pageSize, this.maxRange)); prevBtn.attr("disabled", null); nextBtn.attr("disabled", null); } - this.lastVal = this.firstVal + this.pageSize; + // this.lastVal = this.firstVal + this.pageSize; this.currentPage = Math.ceil(this.firstVal / this.pageSize) + 1; this.fetchAndDisplayRecords() @@ -218,7 +218,7 @@ class ApiData { } private handleResize(): void { - const newGridSize = Math.ceil((Math.ceil(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT)- 1; + const newGridSize = Math.ceil((Math.ceil(($(window).innerHeight())) * GRID_RATIO) / ROW_HEIGHT) - 1; // Check if the new grid size is non-negative if (newGridSize >= 0) { From 194d261f849bb2c5b1c54ac3002180ae0e6dafe0 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 2 Oct 2023 09:39:26 +0200 Subject: [PATCH 88/90] changes requested done --- ApiData.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index bcbaaa3e..ce60ecb0 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -207,7 +207,6 @@ class ApiData { nextBtn.attr("disabled", null); } - // this.lastVal = this.firstVal + this.pageSize; this.currentPage = Math.ceil(this.firstVal / this.pageSize) + 1; this.fetchAndDisplayRecords() From 4d6153690c75e1ad361ae87246067f2b38727cb6 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Mon, 2 Oct 2023 16:01:31 +0200 Subject: [PATCH 89/90] changes requested done --- ApiData.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index ce60ecb0..d93232f7 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -55,7 +55,6 @@ class ApiData { }) .catch(error => { console.error('Failed to fetch record count:', error); - alert('Failed to fetch record count.'); throw error; }); } From 350c90b6519b2a6712bb93a1ffe81d5a7d5a3df8 Mon Sep 17 00:00:00 2001 From: Ntobe99 Date: Tue, 3 Oct 2023 08:33:09 +0200 Subject: [PATCH 90/90] changes requested done for real this time --- ApiData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ApiData.ts b/ApiData.ts index d93232f7..09e4356a 100644 --- a/ApiData.ts +++ b/ApiData.ts @@ -82,7 +82,7 @@ class ApiData { return this.fetchStrData(`http://localhost:2050/records?from=${from}&to=${to}`) .then((response: string) => { const res = JSON.parse(response); - const processedData = res.map((record: string) => { + const processedData = res.map((record: string[]) => { const obj: GridData = {}; for (let j = 0; j < this.columnNames.length && j < record.length; j++) { obj[this.columnNames[j].name] = record[j];