diff --git a/install/cws-ui/deployments.ftl b/install/cws-ui/deployments.ftl index fd524049..cc5d861a 100644 --- a/install/cws-ui/deployments.ftl +++ b/install/cws-ui/deployments.ftl @@ -64,13 +64,24 @@ "name": "${x.name}", "version": "${x.version}", "suspended": "${x.suspended?c}", - "id": "${x.id}" + "id": "${x.id}", + "numWorkers": 0 }; procDefArray.push(procDef); - // REFRESH THE TEXTUAL STATS SUMMARY - function refreshStatUI(name, statsCounts) { + // Update statistics for a process definition + function refreshStatUI(key, stats) { + var table = $('#process-table').DataTable(); + var row = table.row('#' + key); + if (row.length) { + var data = row.data(); + data.stats = stats; + row.data(data).draw(); + } + } + + function refreshTotalStatUI(name, statsCounts) { var statTotal = statsCounts.pending + statsCounts.disabled + @@ -195,12 +206,12 @@ // Update the tooltips document.querySelectorAll('.progress-bar[data-bs-toggle="tooltip"]').forEach(el => { const tooltipInstance = bootstrap.Tooltip.getInstance(el); - if (tooltipInstance){ + if (tooltipInstance) { if (tooltipInstance._config) { tooltipInstance._config.title = el.dataset.bsTitle; } + tooltipInstance.update(); } - tooltipInstance.update(); }); @@ -363,7 +374,7 @@ } }); - refreshStatUI('cws-reserved-total', statsTotalVal); + refreshTotalStatUI('cws-reserved-total', statsTotalVal); refreshing = false; }, @@ -376,6 +387,38 @@ }); } + // Helper function to calculate percentages for progress bars + function calculatePercentages(stats, total) { + var pcts = {}; + var minPct = 1.5; + + // Calculate initial percentages + pcts.pending = ((stats.pending || 0) / total) * 100; + pcts.disabled = ((stats.disabled || 0) / total) * 100; + pcts.active = ((stats.active || 0) / total) * 100; + pcts.completed = ((stats.completed || 0) / total) * 100; + pcts.error = ((stats.error || 0) / total) * 100; + pcts.fts = ((stats.fts || 0) / total) * 100; + pcts.incident = ((stats.incident || 0) / total) * 100; + + // Adjust small non-zero values to minimum percentage + Object.keys(pcts).forEach(function(key) { + if (pcts[key] > 0 && pcts[key] < minPct) { + pcts[key] = minPct; + } + }); + + // Normalize percentages to total 100% + var totalPct = Object.values(pcts).reduce(function(a, b) { return a + b; }, 0); + if (totalPct > 0) { + Object.keys(pcts).forEach(function(key) { + pcts[key] = (pcts[key] / totalPct) * 100; + }); + } + + return pcts; + } + //DOCUMENT.READY STARTS HERE $(document).ready(function () { // DISPLAY MESSAGE AT TOP OF PAGE (IF THERE IS ONE) @@ -445,7 +488,8 @@ if (type !== 'display') { return data.name; } else { - var html = `
` + data.name + `
`; + var style = data.suspended === "true" ? ' style="font-style: italic;"' : ''; + var html = '
' + data.name + '
'; return html; } } @@ -453,29 +497,31 @@ //KEY COLUMN { data: "key", - render: function (data, type) { + render: function (data, type, row) { if (type !== 'display') { return data; } else { if (data === null || data === undefined || data === "null") { return "ERROR"; } else { - return data; + var style = row.suspended === "true" ? ' style="font-style: italic;"' : ''; + return '' + data + ''; } } } }, //VERSION COLUMN { - data: "version", + data: {version: "version", suspended: "suspended"}, render: function (data, type) { if (type !== 'display') { - return data; + return data.version; } else { - if (data === null || data === undefined || data === "null") { + if (data.version === null || data.version === undefined || data.version === "null") { return "ERROR"; } else { - return data; + var style = data.suspended === "true" ? ' style="font-style: italic;"' : ''; + return '' + data.version + ''; } } }, @@ -483,14 +529,16 @@ }, //WORKERS BUTTON COLUMN { - data: "key", - type: "string", + data: {key: "key", numWorkers: "numWorkers"}, + type: "string", render: function (data, type) { if (type !== 'display') { return ""; } else { - var html = ``; + var btnClass = data.numWorkers === 0 ? "btn-danger" : "btn-outline-dark"; + var btnText = data.numWorkers === 0 ? "enable" : "view"; + var html = ``; return html; } } @@ -519,45 +567,56 @@ }, //INSTANCE STATISTICS COLUMN { - data: "key", + data: {key: "key", stats: "stats"}, type: "string", - render: function (data, type) { + render: function (data, type, row) { if (type !== 'display') { return ""; - } else { - var html = `
` - + `
` - + `
` - + `` - + `
` - + `
` - + `` - + `
` - + `
` - + `` - + `
` - + `
` - + `` - + `
` - + `
` - + `` - + `
` - + `
` - + `` - + `
` - + `
` - + `` - + `
` - + `
`; - return html; } + + var stats = data.stats || {}; + var total = (stats.pending || 0) + (stats.disabled || 0) + (stats.active || 0) + + (stats.completed || 0) + (stats.error || 0) + (stats.fts || 0) + (stats.incident || 0); + + if (total === 0) { + return '
No stats for this process
'; + } + + var pcts = calculatePercentages(stats, total); + + var instanceText = []; + if (stats.pending) instanceText.push('pending: ' + stats.pending); + if (stats.disabled) instanceText.push('disabled: ' + stats.disabled); + if (stats.active) instanceText.push('running: ' + stats.active); + if (stats.completed) instanceText.push('completed: ' + stats.completed); + if (stats.error) instanceText.push('failed: ' + stats.error); + if (stats.fts) instanceText.push('failed-start: ' + stats.fts); + if (stats.incident) instanceText.push('incidents: ' + stats.incident); + + var html = '
' + instanceText.join('  ') + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
'; + return html; } } ], @@ -592,8 +651,8 @@ $('').appendTo(".above-table-buttons"); $('
').appendTo(".above-table-buttons"); - //HANDLES MODAL POPUP FOR WORKER BUTTON - $(".worker-view-btn").on("click", function () { + //HANDLES MODAL POPUP FOR WORKER BUTTON - using event delegation for dynamic elements + $("#process-table").on("click", ".worker-view-btn", function() { dataProcKey = $(this).attr("data-proc-key"); listWorkersInModal(dataProcKey); }); @@ -757,23 +816,15 @@ //suspend procDef given procDefId function suspendProcDef(procDefId, procDefKey) { console.log("Suspending procDefId: " + procDefId); - var result; //make a post request to suspend the procDef $.ajax({ url: "/${base}/rest/deployments/suspend/" + encodeURIComponent(procDefId), - type: "POST", + type: "POST", success: function (data) { - //change the glyphicon to play & make green - $("#suspend-" + procDefKey).attr("src", "/${base}/images/play.svg"); - $("#suspend-" + procDefKey).css("color", "green"); - $("#btn-suspend-" + procDefKey).attr("onclick", "resumeProcDef('" + procDefId + "', '" + procDefKey + "')"); - // $("#status-txt-" + procDefKey).html("Suspended"); - $("#" + procDefKey).addClass("disabled"); - $("#pv-" + procDefKey).removeClass("btn-danger").addClass("btn-outline-dark").text("view"); - const table = $("#process-table").DataTable(); - table.cell("#" + procDefKey, 5).data("
Suspended
").draw(false); - + const rowData = table.row("#" + procDefKey).data(); + rowData.suspended = "true"; + table.row("#" + procDefKey).data(rowData).draw(); }, error: function (data) { console.log("error suspending"); @@ -785,41 +836,20 @@ //resume procDef given procDefId function resumeProcDef(procDefId, procDefKey) { console.log("Resuming procDefId: " + procDefId); - var result; - //make a post request to suspend the procDef $.ajax({ url: "/${base}/rest/deployments/activate/" + encodeURIComponent(procDefId), type: "POST", success: function (data) { console.log("successfully activated"); - //change the glyphicon to pause & make color #d9534f - $("#suspend-" + procDefKey).attr("src", "/${base}/images/pin_pause.svg"); - $("#suspend-" + procDefKey).css("color", "#d9534f"); - $("#btn-suspend-" + procDefKey).attr("onclick", "suspendProcDef('" + procDefId + "', '" + procDefKey + "')"); const table = $("#process-table").DataTable(); - table.cell("#" + procDefKey, 5).data("
Active
").draw(false); - $.get("/${base}/rest/processes/getProcDefWorkerCount", function(data) { - var rows = JSON.parse(data); - var hasWorker = false; - for (i in rows) { - if (rows[i].pdk === procDefKey && rows[i].workers > 0) { - hasWorker = true; - break; - } - } - if (hasWorker) { - $("#pv-" + procDefKey).removeClass("disabled btn-danger").addClass("btn-outline-dark").text("view"); - } else { - $("#pv-" + procDefKey).removeClass("disabled btn-outline-dark").addClass("btn-danger").text("enable"); - } - $("#" + procDefKey).removeClass("disabled"); - }); + const rowData = table.row("#" + procDefKey).data(); + rowData.suspended = "false"; + table.row("#" + procDefKey).data(rowData).draw(); }, error: function (data) { console.log("error activating"); } - }) - + }); } @@ -1184,10 +1214,6 @@ }); } - $(".worker-view-btn").click(function () { - dataProcKey = $(this).attr("data-proc-key"); - listWorkersInModal(dataProcKey); - }); // // CLICK ACTION FOR @@ -1244,18 +1270,12 @@ function adjustWorkersButton() { $.get("/${base}/rest/processes/getProcDefWorkerCount", function (data) { - var rows = JSON.parse(data) + var table = $("#process-table").DataTable(); + var rows = JSON.parse(data); for (i in rows) { - const table = $("#process-table").DataTable(); - const rowData = table.row("#" + rows[i].pdk).data(); - - if (rowData && rowData.suspended === "true") { - $("#pv-" + rows[i].pdk).removeClass("btn-danger").addClass("btn-outline-dark").text("view"); - } else if (rows[i].workers == 0) { - $("#pv-" + rows[i].pdk).removeClass("btn-default").addClass("btn-danger").text("enable").removeClass("btn-outline-dark"); - } else { - $("#pv-" + rows[i].pdk).removeClass("btn-danger").addClass("btn-outline-dark").text("view").addClass("btn-outline-dark"); - } + var rowData = table.row("#" + rows[i].pdk).data(); + rowData.numWorkers = rows[i].workers; + table.row("#" + rows[i].pdk).data(rowData).draw(); } }); }