diff --git a/server/src/static/assets/js/filter.js b/server/src/static/assets/js/filter.js index e9c1816f..8225c46d 100644 --- a/server/src/static/assets/js/filter.js +++ b/server/src/static/assets/js/filter.js @@ -14,22 +14,21 @@ document.addEventListener('DOMContentLoaded', function() { }); }); -function sortTable(header, table) { - var SORTABLE_STATES = { - none: 0, - ascending: -1, - descending: 1, - ORDER: ['none', 'ascending', 'descending'], - }; - +/** + * Sorts the specified table by the specified ordering + * @param {HTMLTableCellElement} header + * @param {HTMLTableElement} table + * @param {String} newOrder + */ +function sortTable(header, table, newOrder) { // Get index of column based on position of header cell in // We assume there is only one row in the table head. var col = [].slice.call(table.tHead.rows[0].cells).indexOf(header); - - // Based on the current aria-sort value, get the next state. - var newOrder = SORTABLE_STATES.ORDER.indexOf(header.getAttribute('aria-sort')) + 1; - newOrder = newOrder > SORTABLE_STATES.ORDER.length - 1 ? 0 : newOrder; - newOrder = SORTABLE_STATES.ORDER[newOrder]; + const sortDirectionMap = { + none: 0, + ascending: -1, + descending: 1, + }; // Reset all header sorts. var headerSorts = table.querySelectorAll('[aria-sort]'); @@ -43,7 +42,7 @@ function sortTable(header, table) { // Get the direction of the sort and assume only one tbody. // For this example only assume one tbody. - var direction = SORTABLE_STATES[newOrder]; + var direction = sortDirectionMap[newOrder]; var body = table.tBodies[0]; // Convert the HTML element list to an array. @@ -56,29 +55,92 @@ function sortTable(header, table) { return a.getAttribute('data-index') - b.getAttribute('data-index'); }); } else { + sortOrderFunc = defaultSortOrder; + if (header.hasAttribute("use-outcome-sort")) { + sortOrderFunc = outcomeSortOrder; + } // Sort based on a cell contents newRows.sort(function (rowA, rowB) { - // Trim the cell contents. - var contentA = rowA.cells[col].textContent.trim(); - var contentB = rowB.cells[col].textContent.trim(); - - // Based on the direction, do the sort. - // - // This example only sorts based on alphabetical order, to sort based on - // number value a more specific implementation would be needed, to provide - // number parsing and comparison function between text strings and numbers. - return contentA < contentB ? direction : -direction; + return sortOrderFunc(rowA.cells[col], rowB.cells[col], direction); }); } // Append each row into the table, replacing the current elements. for (i = 0, ii = body.rows.length; i < ii; i += 1) { body.appendChild(newRows[i]); } + + // Change url parameters based on sort order for the agents table + if (table.id == "agentsTable") { + var pageURL = new URL(window.location.href); + pageURL.searchParams.set("tableId", table.id); + pageURL.searchParams.set("headerId", header.id); + pageURL.searchParams.set("sortOrder", newOrder); + window.history.replaceState(null, null, pageURL); + } +} + +/** + * Cycles through sort order states and applies the designated sort order to the + * specified table. + * @param {HTMLTableCellElement} header + * @param {HTMLTableElement} table + */ +function cycleTableSort(header, table) { + var SORTABLE_STATES = { + none: 0, + ascending: -1, + descending: 1, + ORDER: ['none', 'ascending', 'descending'], + }; + + // Based on the current aria-sort value, get the next state. + var newOrder = SORTABLE_STATES.ORDER.indexOf(header.getAttribute('aria-sort')) + 1; + newOrder = newOrder > SORTABLE_STATES.ORDER.length - 1 ? 0 : newOrder; + newOrder = SORTABLE_STATES.ORDER[newOrder]; + + sortTable(header, table, newOrder); +} + +/** + * Default sort order for generic table columns. Uses an alphebetical ordering. + * @param {HTMLTableCellElement} cellA + * @param {HTMLTableCellElement} cellB + * @param {Number} direction + */ +function defaultSortOrder(cellA, cellB, direction) { + // Trim the cell contents. + var contentA = cellA.textContent.trim(); + var contentB = cellB.textContent.trim(); + return contentA < contentB ? direction : -direction; +} + +/** + * Custom ordering function to sort the provisioning streak column in the agents table. + * Succeses and failures are grouped, then the streak number is always ordered + * in descending order. + * @param {HTMLTableCellElement} cellA + * @param {HTMLTableCellElement} cellB + * @param {Number} direction + */ +function outcomeSortOrder(cellA, cellB, direction) { + // Trim the provision streak number. + var streakA = cellA.getElementsByClassName("provision-streak")[0].textContent.trim(); + var streakB = cellB.getElementsByClassName("provision-streak")[0].textContent.trim(); + // Get success/failure icon of the row + var isFailureA = cellA.getElementsByClassName("p-icon--warning").length > 0; + var isFailureB = cellB.getElementsByClassName("p-icon--warning").length > 0; + // If both are failures or both are successes, we do a simple comparison + if (isFailureA == isFailureB) { + return parseInt(streakB) - parseInt(streakA); + } + + // If A is failure and B is success, A should come before B in descending order + return isFailureA ? -direction : direction; } function setupClickableHeader(table, header) { header.addEventListener('click', function () { - sortTable(header, table); + cycleTableSort(header, table); }); } @@ -102,9 +164,36 @@ function setupSortableTable(table) { } } +/** + * Gets URL parameters and uses them to sort the specified table. + * Currently only supports the agent table. + */ +function sortTableFromURL() { + // Get url parameters for table sort ordering if specified + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const tableId = urlParams.get("tableId"); + // Only url parameters for the agents table is supported + if (tableId != "agentsTable") { + return; + } + const headerId = urlParams.get("headerId"); + var sortOrder = urlParams.get("sortOrder"); + const validSortOrders = ["ascending", "descending", "none"]; + if (!validSortOrders.includes(sortOrder)) { + sortOrder = "none" + } + var table = document.getElementById(tableId); + var header = document.getElementById(headerId); + if (table == null || header == null) { + return; + } + sortTable(header, table, sortOrder); +} + // Make all tables on the page sortable. var tables = document.querySelectorAll('table'); - for (var i = 0, ii = tables.length; i < ii; i += 1) { setupSortableTable(tables[i]); } +sortTableFromURL() diff --git a/server/src/templates/agents.html b/server/src/templates/agents.html index d232a191..cb4faf50 100644 --- a/server/src/templates/agents.html +++ b/server/src/templates/agents.html @@ -17,14 +17,18 @@

- +
- - - - - + + + + @@ -38,14 +42,18 @@

This agent has failed the last {{ agent.provision_streak_count }} provision attempts - {{ agent.provision_streak_count }} + + {{ agent.provision_streak_count }} + {% elif agent.provision_streak_type == "pass" and agent.provision_streak_count > 0 %} This agent has passed the last {{ agent.provision_streak_count }} provision attempts - {{ agent.provision_streak_count }} + + {{ agent.provision_streak_count }} + {% endif %}

@@ -56,4 +64,4 @@

{% endfor %}

NameStateUpdated At + + The number of consecutively successful / failed provisioning attempts + Outcome + + NameStateUpdated At Job ID
{{ agent.name }}
-{% endblock %} \ No newline at end of file +{% endblock %}