github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/static/js/dashboard.js (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 'use strict'; 18 19 let localPanels = [], dbData, dbName, dbDescr, dbId, panelIndex, flagDBSaved = true, allResultsDisplayed = 0; 20 let timeRange = "Last 1 Hr"; 21 let dbRefresh =""; 22 let panelContainer; 23 let panelContainerWidthGlobal; 24 let curFocus; 25 26 $(document).ready(function () { 27 getListIndices(); 28 var isActive = $('#app-side-nav').hasClass('active'); 29 if (isActive) { 30 $('#new-dashboard').css("transform", "translate(87px)") 31 $('#new-dashboard').css("width", "calc(100% - 97px)") 32 } 33 else { 34 $('#new-dashboard').css("transform", "translate(170px)") 35 $('#new-dashboard').css("width", "calc(100% - 180px)") 36 } 37 38 panelContainer = document.getElementById('panel-container'); 39 panelContainerWidthGlobal = isActive? panelContainer.offsetWidth-97: panelContainer.offsetWidth-215; 40 41 $('.panelEditor-container').hide(); 42 $('.dbSet-container').hide(); 43 if (Cookies.get('theme')) { 44 theme = Cookies.get('theme'); 45 $('body').attr('data-theme', theme); 46 } 47 $('.theme-btn').on('click', themePickerHandler); 48 setupEventHandlers(); 49 dbId = getDashboardId(); 50 51 $("#add-panel-btn").click(() => addPanel()); 52 $(".all-dashboards").click(function () { 53 window.location.href = "../dashboards-home.html"; 54 }) 55 56 displayDashboardName(); 57 58 $("#theme-btn").click(() => displayPanels()); 59 60 getDashboardData(); 61 62 setTimePicker(); 63 64 $(`.dbSet-textareaContainer .copy`).tooltip({ 65 delay: { show: 0, hide: 300 }, 66 trigger: 'hover' 67 }); 68 }) 69 $(document).mouseup(function (e) { 70 var popWindows = $("#panel-dropdown-modal"); 71 let panelHead = $(".panel-header"); 72 let j1 = !popWindows.is(e.target); 73 let j2 = !panelHead.is(e.target); 74 let j3 = !$(curFocus + " .dropdown-style").hasClass("hidden"); 75 if ( 76 !popWindows.is(e.target) && 77 popWindows.has(e.target).length === 0 && 78 !panelHead.is(e.target) && 79 panelHead.has(e.target).length === 0 && 80 !$(curFocus + " .dropdown-style").hasClass("hidden") 81 ) { 82 $(curFocus + " .dropdown-btn").toggleClass("active"); 83 $(curFocus + " .dropdown-style").toggleClass("hidden"); 84 } 85 }); 86 window.addEventListener('resize', function (event) { 87 if ($('.panelEditor-container').css('display') === 'none'){ 88 panelContainerWidthGlobal = panelContainer.offsetWidth-97; 89 recalculatePanelWidths(); 90 displayPanels(); 91 resetPanelLocationsHorizontally(); 92 } 93 }); 94 $(`.dbSet-textareaContainer .copy`).click(function() { 95 $(this).tooltip('dispose'); 96 $(this).attr('title', 'Copied!').tooltip('show'); 97 navigator.clipboard.writeText($(`.dbSet-jsonModelData`).val()) 98 .then(() => { 99 setTimeout(() => { 100 $(this).tooltip('dispose'); 101 $(this).attr('title', 'Copy').tooltip({ 102 delay: { show: 0, hide: 300 }, 103 trigger: 'hover', 104 }); 105 }, 1000); 106 }) 107 }); 108 109 function recalculatePanelWidths(){ 110 localPanels.map(localPanel => { 111 localPanel.gridpos.w = localPanel.gridpos.wPercent * panelContainerWidthGlobal; 112 }) 113 } 114 115 $('#save-db-btn').on("click", updateDashboard); 116 $('.refresh-btn').on("click", refreshDashboardHandler); 117 $('.settings-btn').on('click', handleDbSettings); 118 $('#dbSet-save').on('click', saveDbSetting); 119 $('#dbSet-discard').on('click', discardDbSetting); 120 $('.dbSet-goToDB').on('click', discardDbSetting); 121 $('.panView-goToDB').on("click", goToDashboardFromView) 122 $('.refresh-range-item').on('click', refreshRangeItemHandler); 123 124 125 function updateDashboard() { 126 timeRange = $('#date-picker-btn').text().trim().replace(/\s+/g, ' '); 127 resetPanelTimeRanges(); 128 flagDBSaved = true; 129 let tempPanels = JSON.parse(JSON.stringify(localPanels)); 130 for (let i = 0; i < tempPanels.length; i++) 131 delete tempPanels[i].queryRes; 132 return fetch('/api/dashboards/update', 133 { 134 method: 'POST', 135 body: JSON.stringify({ 136 "id": dbId, 137 "name": dbName, 138 "details": { 139 "name": dbName, 140 "description": dbDescr, 141 "timeRange": timeRange, 142 "panels": tempPanels, 143 "refresh": dbRefresh, 144 }, 145 }) 146 } 147 ) 148 .then(res => { 149 if (res.status === 409) { 150 showToast('Dashboard name already exists'); 151 throw new Error('Dashboard name already exists'); 152 } 153 if (res.status == 200) { 154 displayDashboardName(); 155 showToast('Dashboard Updated Successfully'); 156 return true; 157 } 158 return res.json(); 159 }) 160 .catch(error => { 161 console.error(error); 162 return false; 163 }); 164 } 165 166 function refreshDashboardHandler() { 167 if ($('#viewPanel-container').css('display') !== 'none') { 168 displayPanelView(panelIndex); 169 } 170 else { 171 for (let i = 0; i < localPanels.length; i++) { 172 localPanels[i].queryRes = undefined; 173 } 174 displayPanels(); 175 } 176 } 177 178 function handlePanelView() { 179 $(".panel-view-li").unbind("click"); 180 $(".panel-view-li").on("click", function () { 181 panelIndex = $(this).closest(".panel").attr("panel-index"); 182 pauseRefreshInterval(); 183 viewPanelInit(); 184 displayPanelView(panelIndex); 185 }) 186 } 187 188 function viewPanelInit() { 189 $('#app-container').show(); 190 $('.panView-goToDB').css('display', 'block'); 191 $('#viewPanel-container').show(); 192 $('#panel-container').hide(); 193 $('.panelEditor-container').hide(); 194 $('#add-panel-btn').hide(); 195 $('#viewPanel-container').css('display', 'flex'); 196 $('#viewPanel-container').css('height', '100%'); 197 setTimePicker(); 198 } 199 200 function goToDashboardFromView() { 201 $('#viewPanel-container').hide(); 202 $('#panel-container').show(); 203 $('.panView-goToDB').css('display', 'none'); 204 $('#add-panel-btn').show(); 205 $('#viewPanel-container .panel .panel-info-corner').empty(); 206 updateTimeRangeForPanels(); 207 displayPanels(); 208 if(dbRefresh){ 209 startRefreshInterval(dbRefresh) 210 } 211 } 212 213 function handlePanelEdit() { 214 $(".panel-edit-li").unbind("click"); 215 $(".panel-edit-li").on("click", function () { 216 panelIndex = $(this).closest(".panel").attr("panel-index"); 217 218 if ($('#viewPanel-container').css('display') !== 'none') { 219 editPanelInit(-1); 220 } else { 221 editPanelInit(); 222 } 223 $('.panelEditor-container').show(); 224 $('#app-container').hide(); 225 $('.panelDisplay #panelLogResultsGrid').empty(); 226 $('.panelDisplay .big-number-display-container').hide(); 227 $('.panelDisplay #empty-response').hide(); 228 }) 229 } 230 function handlePanelRemove(panelId) { 231 $(`#panel${panelId} .panel-remove-li`).unbind("click"); 232 $(`#panel${panelId} .panel-remove-li`).on("click", function () { 233 showPrompt(panelId); 234 }); 235 236 function showPrompt(panelId) { 237 $('.popupOverlay, .popupContent').addClass('active'); 238 $('#delete-btn-panel').on("click", function () { 239 deletePanel(panelId); 240 $('.popupOverlay, .popupContent').removeClass('active'); 241 }); 242 $('#cancel-btn-panel, .popupOverlay').on("click", function () { 243 $('.popupOverlay, .popupContent').removeClass('active'); 244 }); 245 } 246 247 function deletePanel(panelId) { 248 flagDBSaved = false; 249 const panel = $(`#panel${panelId}`); 250 let panelIndex = panel.attr("panel-index"); 251 252 localPanels = localPanels.filter(function (el) { 253 return el.panelIndex != panelIndex; 254 }); 255 panel.remove(); 256 257 resetPanelIndices(); 258 resetPanelLocationsHorizontally(); 259 resetPanelLocationsVertically(); 260 resetPanelContainerHeight(); 261 displayPanels(); 262 } 263 } 264 265 function handleDescriptionTooltip(panelId,description,searchText) { 266 const panelInfoCorner = $(`#panel${panelId} .panel-info-corner`); 267 const panelDescIcon = $(`#panel${panelId} .panel-info-corner #panel-desc-info`); 268 panelInfoCorner.show(); 269 let tooltipText = ''; 270 271 // Check if description is provided 272 if (description) { 273 tooltipText += `Description: ${description}`; 274 } 275 276 // Check if both description and searchText are provided, add line break if needed 277 if (description && searchText) { 278 tooltipText += '\n'; 279 } 280 281 // Check if searchText is provided 282 if (searchText) { 283 tooltipText += `Query: ${searchText}`; 284 } 285 286 panelDescIcon.attr('title', tooltipText); 287 288 panelDescIcon.tooltip({ 289 delay: { show: 0, hide: 300 }, 290 trigger: 'hover'}); 291 panelInfoCorner.hover(function () {panelDescIcon.tooltip('show');}, 292 function () {panelDescIcon.tooltip('hide');}); 293 } 294 295 function resetPanelLocationsHorizontally() { 296 let temp = []; 297 for (let i = 0; i < localPanels.length; i++) { 298 let x = localPanels[i].gridpos.x; 299 temp.push([x, i]); 300 } 301 temp.sort((a, b) => a[0] - b[0]); 302 let indices = []; 303 for (let i = 0; i < temp.length; i++) 304 indices.push(temp[i][1]) 305 306 for (let i = 0; i < indices.length; i++) { 307 let hRight = localPanels[indices[i]].gridpos.h; 308 let wRight = localPanels[indices[i]].gridpos.wPercent * panelContainerWidthGlobal; 309 let xRight = localPanels[indices[i]].gridpos.x; 310 let yRight = localPanels[indices[i]].gridpos.y; 311 312 let xmax = 0; 313 for (let j = 0; j < i; j++) { 314 let hLeft = localPanels[indices[j]].gridpos.h; 315 let wLeft = localPanels[indices[j]].gridpos.w; 316 let xLeft = localPanels[indices[j]].gridpos.x; 317 let yLeft = localPanels[indices[j]].gridpos.y; 318 319 if ((yLeft >= yRight && yLeft <= yRight + hRight) || (yLeft + hLeft >= yRight && yLeft + hLeft <= yRight + hRight) || (yLeft <= yRight && yLeft + hLeft >= yRight + hRight)) { 320 xmax = Math.max(xmax, xLeft + wLeft); 321 } 322 } 323 324 if ((xmax + wRight) < ($('#panel-container')[0].offsetWidth + $('#panel-container')[0].offsetLeft - 20)) { 325 localPanels[indices[i]].gridpos.x = xmax + 10; 326 } 327 } 328 } 329 330 function resetPanelLocationsVertically() { 331 let temp = []; 332 333 for (let i = 0; i < localPanels.length; i++) { 334 let y = localPanels[i].gridpos.y; 335 temp.push([y, i]); 336 } 337 temp.sort((a, b) => a[0] - b[0]); 338 let indices = []; 339 for (let i = 0; i < temp.length; i++) 340 indices.push(temp[i][1]) 341 342 for (let i = 0; i < indices.length; i++) { 343 let hDown = localPanels[indices[i]].gridpos.h; 344 let wDown = localPanels[indices[i]].gridpos.w; 345 let xDown = localPanels[indices[i]].gridpos.x; 346 let yDown = localPanels[indices[i]].gridpos.y; 347 348 let ymax = 10; 349 for (let j = 0; j < i; j++) { 350 let hTop = localPanels[indices[j]].gridpos.h; 351 let wTop = localPanels[indices[j]].gridpos.w; 352 let xTop = localPanels[indices[j]].gridpos.x; 353 let yTop = localPanels[indices[j]].gridpos.y; 354 355 if ((xTop >= xDown && xTop <= xDown + wDown) || (xTop + wTop >= xDown && xTop + wTop <= xDown + wDown) || (xTop <= xDown && xTop + wTop >= xDown + wDown)) { 356 ymax = Math.max(ymax, yTop + hTop); 357 } 358 } 359 360 localPanels[indices[i]].gridpos.y = ymax + 10; 361 } 362 } 363 364 function handlePanelDuplicate() { 365 $(".panel-dupl-li").unbind("click"); 366 $(".panel-dupl-li").on("click", function () { 367 flagDBSaved = false; 368 let duplicatedPanelIndex = $(this).closest(".panel").attr("panel-index"); 369 addPanel(JSON.parse(JSON.stringify(localPanels[duplicatedPanelIndex]))); 370 renderDuplicatePanel(duplicatedPanelIndex); 371 }) 372 } 373 374 function renderDuplicatePanel(duplicatedPanelIndex) { 375 let boundaryY = localPanels[duplicatedPanelIndex].gridpos.y + localPanels[duplicatedPanelIndex].gridpos.h; 376 for (let i = 0; i < localPanels.length; i++) { 377 if (localPanels[i].panelIndex == localPanels.length - 1) continue; // this is the newly created duplicate panel. 378 if (localPanels[i].gridpos.y >= boundaryY) // if any panel starts after the ending Y-cordinate of the duplicated panel, then it should be shifted downwards 379 localPanels[i].gridpos.y += localPanels[duplicatedPanelIndex].gridpos.h + 20; 380 } 381 resetPanelLocationsVertically(); 382 resetPanelLocationsHorizontally(); 383 resetPanelContainerHeight(); 384 displayPanelsWithoutRefreshing(); 385 let localPanel = localPanels[localPanels.length - 1]; 386 let panelId = localPanels[localPanels.length - 1].panelId; 387 // only render the duplicated panel 388 $(`#panel${localPanels[localPanels.length - 1].panelId} .panel-header p`).html(localPanels[duplicatedPanelIndex].name + "Copy"); 389 390 if (localPanel.chartType == 'Data Table' || localPanel.chartType == 'loglines') { 391 let panEl = $(`#panel${panelId} .panel-body`) 392 let responseDiv = `<div id="panelLogResultsGrid" class="panelLogResultsGrid ag-theme-mycustomtheme"></div> 393 <div id="empty-response"></div>` 394 panEl.append(responseDiv) 395 $("#panelLogResultsGrid").show(); 396 397 if (localPanel.queryRes) 398 runPanelLogsQuery(localPanel.queryData, panelId,localPanel, localPanel.queryRes); 399 else 400 runPanelLogsQuery(localPanel.queryData, panelId,localPanel); 401 } else if (localPanel.chartType == 'Line Chart') { 402 let panEl = $(`#panel${panelId} .panel-body`) 403 let responseDiv = `<div id="empty-response"></div></div><div id="corner-popup"></div>` 404 panEl.append(responseDiv) 405 if (localPanel.queryRes) 406 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel, localPanel.queryRes) 407 else 408 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel) 409 } else if (localPanel.chartType == 'number') { 410 let panEl = $(`#panel${panelId} .panel-body`) 411 let responseDiv = `<div class="big-number-display-container"></div> 412 <div id="empty-response"></div><div id="corner-popup"></div>` 413 panEl.append(responseDiv) 414 if(localPanel.queryType ==='metrics') { 415 if (localPanel.queryRes) 416 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel, localPanel.queryRes) 417 else 418 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel) 419 }else{ 420 if (localPanel.queryRes) 421 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex, localPanel.queryRes); 422 else 423 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex); 424 } 425 } else if (localPanel.chartType == 'Pie Chart' || localPanel.chartType == 'Bar Chart') { 426 // generic for both bar and pie chartTypes. 427 let panEl = $(`#panel${panelId} .panel-body`) 428 let responseDiv = `<div id="empty-response"></div><div id="corner-popup"></div>` 429 panEl.append(responseDiv) 430 if (localPanel.queryRes) 431 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex, localPanel.queryRes); 432 else 433 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex); 434 } 435 } 436 437 function resetPanelIndices() { 438 for (let i = 0; i < localPanels.length; i++) { 439 localPanels[i].panelIndex = i; 440 } 441 } 442 443 function displayDashboardName() { 444 $.ajax({ 445 method: "get", 446 url: "api/dashboards/" + dbId, 447 headers: { 448 'Content-Type': 'application/json; charset=utf-8', 449 'Accept': '*/*' 450 }, 451 dataType: 'json', 452 crossDomain: true, 453 }).then(function (res) { 454 $(".name-dashboard").text(res.name); 455 }) 456 } 457 458 async function getDashboardData() { 459 await fetch(`/api/dashboards/${dbId}`) 460 .then(res => { 461 return res.json(); 462 }) 463 .then(data => { 464 dbData = data; 465 }) 466 dbName = dbData.name; 467 dbDescr = dbData.description; 468 dbRefresh = dbData.refresh; 469 if (dbData.panels != undefined) { 470 localPanels = JSON.parse(JSON.stringify(dbData.panels)); 471 } else localPanels = []; 472 if (localPanels != undefined) { 473 updateTimeRangeForPanels(); 474 recalculatePanelWidths(); 475 resetPanelLocationsHorizontally(); 476 setRefreshItemHandler(); 477 refreshDashboardHandler(); 478 } 479 } 480 481 function updateTimeRangeForPanels() { 482 localPanels.forEach(panel => { 483 delete panel.queryRes; 484 if(panel.queryData) { 485 if(panel.chartType === "Line Chart" || panel.queryType === "metrics") { 486 datePickerHandler(panel.queryData.start, panel.queryData.end, panel.queryData.start) 487 panel.queryData.start = filterStartDate.toString(); 488 panel.queryData.end = filterEndDate.toString(); 489 } else { 490 datePickerHandler(panel.queryData.startEpoch, panel.queryData.endEpoch, panel.queryData.startEpoch) 491 panel.queryData.startEpoch = filterStartDate 492 panel.queryData.endEpoch = filterEndDate 493 } 494 $('.inner-range .db-range-item').removeClass('active'); 495 $('.inner-range #' + filterStartDate).addClass('active'); 496 } 497 }) 498 } 499 500 function updateTimeRangeForPanel(panelIndex) { 501 delete localPanels[panelIndex].queryRes; 502 if(localPanels[panelIndex].queryData) { 503 if(localPanels[panelIndex].chartType === "Line Chart" && localPanels[panelIndex].queryType === "metrics") { 504 localPanels[panelIndex].queryData.start = filterStartDate.toString(); 505 localPanels[panelIndex].queryData.end = filterEndDate.toString(); 506 } else { 507 localPanels[panelIndex].queryData.startEpoch = filterStartDate 508 localPanels[panelIndex].queryData.endEpoch = filterEndDate 509 } 510 } 511 } 512 513 514 function displayPanels() { 515 allResultsDisplayed = localPanels.length; 516 $('#panel-container .panel').remove(); 517 let panelContainerMinHeight = 0; 518 $('body').css('cursor', 'progress'); 519 localPanels.map((localPanel) => { 520 let idpanel = localPanel.panelId; 521 let panel = $("<div>").append(panelLayout).addClass("panel").attr("id", `panel${idpanel}`).attr("panel-index", localPanel.panelIndex); 522 $("#panel-container").append(panel); 523 handleDrag(idpanel); 524 handleResize(idpanel); 525 $("#panel" + idpanel + " .panel-header").click(function () { 526 curFocus = "#panel" + idpanel; 527 $("#panel" + idpanel + " .dropdown-btn").toggleClass("active") 528 $("#panel" + idpanel + " .dropdown-style").toggleClass("hidden"); 529 }) 530 $("#panel" + idpanel + " .dropdown-btn").click(function (e) { 531 e.stopPropagation(); 532 curFocus = "#panel" + idpanel; 533 $("#panel" + idpanel + " .dropdown-btn").toggleClass("active"); 534 $("#panel" + idpanel + " .dropdown-style").toggleClass("hidden"); 535 }); 536 $(`#panel${idpanel} .panel-header p`).html(localPanel.name); 537 538 if (localPanel.description || (localPanel.queryData && localPanel.queryData.searchText)) { 539 handleDescriptionTooltip(idpanel, localPanel.description, localPanel.queryData ? localPanel.queryData.searchText : ''); 540 } else { 541 $(`#panel${idpanel} .panel-info-corner`).hide(); 542 } 543 544 let panelElement = document.getElementById(`panel${idpanel}`); 545 panelElement.style.position = "absolute"; 546 panelElement.style.height = localPanel.gridpos.h + "px"; 547 panelElement.style.width = localPanel.gridpos.w + "px"; 548 panelElement.style.top = localPanel.gridpos.y + "px"; 549 panelElement.style.left = localPanel.gridpos.x + "px"; 550 551 let val = localPanel.gridpos.y + localPanel.gridpos.h; 552 if (val > panelContainerMinHeight) panelContainerMinHeight = val; 553 554 handlePanelRemove(idpanel) 555 556 if (localPanel.chartType == 'Data Table'||localPanel.chartType == 'loglines') { 557 let panEl = $(`#panel${idpanel} .panel-body`) 558 let responseDiv = `<div id="panelLogResultsGrid" class="panelLogResultsGrid ag-theme-mycustomtheme"></div> 559 <div id="empty-response"></div></div><div id="corner-popup"></div> 560 <div id="panel-loading"></div>` 561 panEl.append(responseDiv) 562 563 $("#panelLogResultsGrid").show(); 564 if (localPanel.queryRes) 565 runPanelLogsQuery(localPanel.queryData, idpanel,localPanel, localPanel.queryRes); 566 else 567 runPanelLogsQuery(localPanel.queryData, idpanel,localPanel); 568 } else if (localPanel.chartType == 'Line Chart') { 569 let panEl = $(`#panel${idpanel} .panel-body`) 570 let responseDiv = `<div id="empty-response"></div></div><div id="corner-popup"></div> 571 <div id="panel-loading"></div>` 572 panEl.append(responseDiv) 573 if (localPanel.queryRes){ 574 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel, localPanel.queryRes) 575 } 576 else { 577 //remove startEpoch from from localPanel.queryData 578 delete localPanel.queryData.startEpoch 579 delete localPanel.queryData.endEpoch 580 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel) 581 } 582 } else if (localPanel.chartType == 'number') { 583 let panEl = $(`#panel${idpanel} .panel-body`) 584 let responseDiv = `<div class="big-number-display-container"></div> 585 <div id="empty-response"></div><div id="corner-popup"></div> 586 <div id="panel-loading"></div>` 587 panEl.append(responseDiv) 588 589 $('.big-number-display-container').show(); 590 if (localPanel.queryType === "metrics"){ 591 592 if (localPanel.queryRes){ 593 delete localPanel.queryData.startEpoch 594 delete localPanel.queryData.endEpoch 595 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel, localPanel.queryRes) 596 } 597 else { 598 //remove startEpoch from from localPanel.queryData 599 delete localPanel.queryData.startEpoch 600 delete localPanel.queryData.endEpoch 601 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel) 602 } 603 }else { 604 if (localPanel.queryRes) 605 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex, localPanel.queryRes); 606 else 607 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex); 608 } 609 } else if (localPanel.chartType == 'Bar Chart' || localPanel.chartType == 'Pie Chart') { 610 // generic for both bar and pie chartTypes. 611 let panEl = $(`#panel${idpanel} .panel-body`) 612 let responseDiv = `<div id="empty-response"></div><div id="corner-popup"></div> 613 <div id="panel-loading"></div>` 614 panEl.append(responseDiv) 615 if (localPanel.queryRes) 616 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex, localPanel.queryRes); 617 else 618 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex); 619 } else 620 allResultsDisplayed--; 621 }) 622 if(allResultsDisplayed === 0) { 623 $('body').css('cursor', 'default'); 624 } 625 handlePanelView(); 626 handlePanelEdit(); 627 handlePanelDuplicate(); 628 resetPanelContainerHeight(); 629 } 630 631 function displayPanelView(panelIndex) { 632 let localPanel = localPanels[panelIndex]; 633 let panelId = localPanel.panelId; 634 $(`#panel-container #panel${panelId}`).remove(); 635 $(`#viewPanel-container`).empty(); 636 637 let panel = $("<div>").append(panelLayout).addClass("panel").attr("id", `panel${panelId}`).attr("panel-index", localPanel.panelIndex); 638 $("#viewPanel-container").append(panel); 639 $("#panel" + panelId + " .panel-header").click(function () { 640 $("#panel" + panelId + " .dropdown-btn").toggleClass("active") 641 $("#panel" + panelId + " .dropdown-style").toggleClass("hidden"); 642 }) 643 $("#" + `panel${panelId}` + " .dropdown-btn").click(function (e) { 644 e.stopPropagation(); 645 $("#" + `panel${panelId}` + " .dropdown-btn").toggleClass("active") 646 $("#" + `panel${panelId}` + " .dropdown-style").toggleClass("hidden"); 647 }); 648 $(`#panel${panelId} .panel-header p`).html(localPanel.name); 649 650 let panelElement = document.getElementById(`panel${panelId}`); 651 panelElement.style.position = "absolute"; 652 653 panelElement.style.height = "100%"; 654 panelElement.style.width = "100%"; 655 656 handlePanelRemove(localPanel.panelId); 657 if (localPanel.description||localPanel.queryData?.searchText) { 658 handleDescriptionTooltip(localPanel.panelId,localPanel.description,localPanel.queryData.searchText); 659 } else { 660 $(`#panel${panelId} .panel-info-corner`).hide(); 661 } 662 663 if (localPanel.chartType == 'Data Table'| localPanel.chartType == 'loglines') { 664 let panEl = $(`#panel${panelId} .panel-body`) 665 let responseDiv = `<div id="panelLogResultsGrid" class="panelLogResultsGrid ag-theme-mycustomtheme"></div> 666 <div id="empty-response"></div>` 667 panEl.append(responseDiv) 668 $("#panelLogResultsGrid").show(); 669 670 if (localPanel.queryRes) 671 runPanelLogsQuery(localPanel.queryData, panelId,localPanel, localPanel.queryRes); 672 else 673 runPanelLogsQuery(localPanel.queryData, panelId,localPanel); 674 } else if (localPanel.chartType == 'Line Chart') { 675 let panEl = $(`#panel${panelId} .panel-body`) 676 let responseDiv = `<div id="empty-response"></div></div><div id="corner-popup"></div>` 677 panEl.append(responseDiv) 678 if (localPanel.queryRes) 679 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel, localPanel.queryRes) 680 else 681 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel) 682 } else if (localPanel.chartType == 'number') { 683 let panEl = $(`#panel${panelId} .panel-body`) 684 let responseDiv = `<div class="big-number-display-container"></div> 685 <div id="empty-response"></div><div id="corner-popup"></div>` 686 panEl.append(responseDiv) 687 688 if (localPanel.queryRes) 689 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex, localPanel.queryRes); 690 else 691 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex); 692 } else if (localPanel.chartType == 'Pie Chart' || localPanel.chartType == 'Bar Chart') { 693 // generic for both bar and pie chartTypes. 694 let panEl = $(`#panel${panelId} .panel-body`) 695 let responseDiv = `<div id="empty-response"></div><div id="corner-popup"></div>` 696 panEl.append(responseDiv) 697 if (localPanel.queryRes) 698 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex, localPanel.queryRes); 699 else 700 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex); 701 } 702 703 handlePanelView(); 704 handlePanelEdit(); 705 } 706 707 function displayPanel(panelIndex) { 708 let localPanel = localPanels[panelIndex]; 709 let panelId = localPanel.panelId; 710 $(`#panel-container #panel${panelId}`).remove(); 711 $(`#viewPanel-container`).empty(); 712 713 let panel = $("<div>").append(panelLayout).addClass("panel").attr("id", `panel${panelId}`).attr("panel-index", localPanel.panelIndex); 714 $("#panel-container").append(panel); 715 handleDrag(panelId); 716 handleResize(panelId); 717 $("#panel" + panelId + " .panel-header").click(function () { 718 $("#panel" + panelId + " .dropdown-btn").toggleClass("active") 719 $("#panel" + panelId + " .dropdown-style").toggleClass("hidden"); 720 }) 721 $("#" + `panel${panelId}` + " .dropdown-btn").click(function (e) { 722 e.stopPropagation(); 723 $("#" + `panel${panelId}` + " .dropdown-btn").toggleClass("active") 724 $("#" + `panel${panelId}` + " .dropdown-style").toggleClass("hidden"); 725 }); 726 $(`#panel${panelId} .panel-header p`).html(localPanel.name); 727 if (localPanel.description||localPanel.queryData.searchText) { 728 handleDescriptionTooltip(panelId,localPanel.description,localPanel.queryData.searchText) 729 } else { 730 $(`#panel${panelId} .panel-info-corner`).hide(); 731 } 732 733 734 let panelElement = document.getElementById(`panel${panelId}`); 735 panelElement.style.position = "absolute"; 736 panelElement.style.height = localPanel.gridpos.h + "px"; 737 panelElement.style.width = (localPanel.gridpos.wPercent * 100) + "%"; 738 panelElement.style.top = localPanel.gridpos.y + "px"; 739 panelElement.style.left = localPanel.gridpos.x + "px"; 740 handlePanelRemove(localPanel.panelId) 741 742 if (localPanel.chartType == 'Data Table'|| localPanel.chartType =='loglines') { 743 let panEl = $(`#panel${panelId} .panel-body`) 744 let responseDiv = `<div id="panelLogResultsGrid" class="panelLogResultsGrid ag-theme-mycustomtheme"></div> 745 <div id="empty-response"></div>` 746 panEl.append(responseDiv) 747 $("#panelLogResultsGrid").show(); 748 749 if (localPanel.queryRes) 750 runPanelLogsQuery(localPanel.queryData, panelId,localPanel, localPanel.queryRes); 751 else 752 runPanelLogsQuery(localPanel.queryData, panelId,localPanel); 753 } else if (localPanel.chartType == 'Line Chart') { 754 let panEl = $(`#panel${panelId} .panel-body`) 755 let responseDiv = `<div id="empty-response"></div></div><div id="corner-popup"></div>` 756 panEl.append(responseDiv) 757 if (localPanel.queryRes) 758 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel, localPanel.queryRes) 759 else 760 runMetricsQuery(localPanel.queryData, localPanel.panelId, localPanel) 761 } else if (localPanel.chartType == 'number') { 762 let panEl = $(`#panel${panelId} .panel-body`) 763 let responseDiv = `<div class="big-number-display-container"></div> 764 <div id="empty-response"></div><div id="corner-popup"></div>` 765 panEl.append(responseDiv) 766 767 if (localPanel.queryRes) 768 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex, localPanel.queryRes); 769 else 770 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex); 771 } else if (localPanel.chartType == 'Pie Chart' || localPanel.chartType == 'Bar Chart') { 772 // generic for both bar and pie chartTypes. 773 let panEl = $(`#panel${panelId} .panel-body`) 774 let responseDiv = `<div id="empty-response"></div><div id="corner-popup"></div>` 775 panEl.append(responseDiv) 776 if (localPanel.queryRes) 777 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex, localPanel.queryRes); 778 else 779 runPanelAggsQuery(localPanel.queryData, localPanel.panelId, localPanel.chartType, localPanel.dataType, localPanel.panelIndex); 780 } 781 782 handlePanelView(); 783 handlePanelEdit(); 784 handlePanelDuplicate(); 785 resetPanelContainerHeight(); 786 } 787 788 function displayPanelsWithoutRefreshing() { 789 localPanels.map((localPanel) => { 790 let panelElement = document.getElementById(`panel${localPanel.panelId}`); 791 panelElement.style.position = "absolute"; 792 panelElement.style.height = localPanel.gridpos.h + "px"; 793 panelElement.style.width = localPanel.gridpos.w + "px"; 794 panelElement.style.top = localPanel.gridpos.y + "px"; 795 panelElement.style.left = localPanel.gridpos.x + "px"; 796 }) 797 } 798 799 function showToast(msg) { 800 let toast = 801 `<div class="div-toast" id="save-db-modal"> 802 ${msg} 803 <button type="button" aria-label="Close" class="toast-close">✖</button> 804 <div>` 805 $('body').prepend(toast); 806 $('.toast-close').on('click', removeToast) 807 setTimeout(removeToast, 1000); 808 } 809 810 function removeToast() { 811 $('.div-toast').remove(); 812 } 813 814 function getDashboardId() { 815 let queryString = decodeURIComponent(window.location.search); //parsing 816 queryString = queryString.substring(1).split("="); 817 let uniq = queryString[1]; 818 return uniq; 819 } 820 821 function handleResize(panelId) { 822 $(`#panel${panelId}`).resizable( 823 { containment: "parent" } 824 ); 825 $(`#panel${panelId}`).on("resizestop", function (event, ui) { 826 flagDBSaved = false; 827 panelIndex = $(this).attr("panel-index"); 828 localPanels[panelIndex].gridpos.w = ui.size.width; 829 localPanels[panelIndex].gridpos.wPercent = ui.size.width / panelContainerWidthGlobal; 830 localPanels[panelIndex].gridpos.h = ui.size.height; 831 displayPanel(panelIndex); 832 resetPanelLocationsHorizontally(); 833 resetPanelLocationsVertically(); 834 resetPanelContainerHeight(); 835 displayPanelsWithoutRefreshing(); 836 }) 837 }; 838 839 function resizePanelFontSize(panelIndex, panelId) { 840 if (panelIndex !== -1) { 841 let bigNumText = $(`#panel${panelId} .big-number`); 842 let unit = $(`#panel${panelId} .unit`); 843 let panelHeight = parseFloat((localPanels[panelIndex].gridpos.h)); 844 845 let numFontSize = panelHeight / 2; 846 let panelWidth = parseFloat((localPanels[panelIndex].gridpos.w)); 847 848 if (numFontSize > 170) 849 numFontSize = 170; 850 $(bigNumText).css('font-size', `${numFontSize}px`); 851 852 panelWidth = parseFloat((localPanels[panelIndex].gridpos.w)); 853 854 if (bigNumText.width() + $(`#panel${panelId} .unit`).width() >= panelWidth) { 855 numFontSize -= (bigNumText.width() + $(`#panel${panelId} .unit`).width() - panelWidth) / 3.5; 856 } 857 if (numFontSize < 140 && numFontSize > 50){ 858 $('.unit').css('bottom','18px') 859 } 860 861 if (numFontSize < 50) 862 numFontSize = 50; 863 $(bigNumText).css('font-size', `${numFontSize}px`); 864 let unitSize = numFontSize > 10 ? numFontSize - 40 : 12; 865 if (unitSize < 25) { 866 unitSize = 25; 867 $(unit).css('bottom', `10px`); 868 $(unit).css('margin-left', `8px`); 869 870 } 871 if (unitSize > 85) 872 unitSize = 85; 873 $(unit).css('font-size', `${unitSize}px`); 874 } else { 875 $('.big-number-display-container .big-number').css('font-size', `180px`); 876 } 877 } 878 879 function handleDrag(panelId) { 880 $(`#panel${panelId}`).draggable({ 881 start: function (event, ui) { 882 $(this).removeClass('temp'); 883 }, 884 obstacle: ".temp", 885 preventCollision: true, 886 containment: "parent" 887 }); 888 889 $(`#panel${panelId}`).on("dragstop", function (event, ui) { 890 flagDBSaved = false; 891 $(this).addClass('temp') 892 panelIndex = $(this).attr("panel-index"); 893 894 if ((ui.position.left + $(this).width()) < ($('#panel-container')[0].offsetWidth + $('#panel-container')[0].offsetLeft)) { 895 localPanels[panelIndex].gridpos.x = ui.position.left; 896 localPanels[panelIndex].gridpos.y = ui.position.top; 897 if (checkForVertical(ui.position)) { 898 resetPanelLocationsVertically(); 899 resetPanelLocationsHorizontally(); 900 } else { 901 resetPanelLocationsHorizontally(); 902 resetPanelLocationsVertically(); 903 } 904 } 905 906 resetPanelContainerHeight(); 907 displayPanelsWithoutRefreshing(); 908 }) 909 }; 910 911 function checkForVertical(uiPos) { 912 for (let i = 0; i < localPanels.length; i++) { 913 let x = localPanels[i].gridpos.x; 914 let y = localPanels[i].gridpos.y; 915 let w = localPanels[i].gridpos.w; 916 let h = localPanels[i].gridpos.h; 917 918 if (uiPos.left == x && uiPos.top == y) continue; 919 920 if (uiPos.left >= x && uiPos.left <= x + w && uiPos.top >= y && uiPos.top <= y + h) { 921 let distRightBound = x + w - uiPos.left; 922 let distBottomBound = y + h - uiPos.top; 923 return distBottomBound < distRightBound; 924 } 925 } 926 } 927 928 var panelLayout = 929 '<div class="panel-header">' + 930 '<p>Panel Title</p>' + 931 '<span class="dropdown-btn" id="panel-options-btn"></span>' + 932 '<ul class="dropdown-style hidden" id="panel-dropdown-modal">' + 933 '<li data-value="view" class="panel-view-li"><span class="view"></span>View</li>' + 934 '<li data-value="edit" class="panel-edit-li"><span class="edit"></span>Edit</li>' + 935 '<li data-value="duplicate" class="panel-dupl-li"><span class="duplicate"></span>Duplicate</li>' + 936 '<li data-value="remove" class="panel-remove-li"><span class="remove"></span>Remove</li>' + 937 '</ul>' + 938 '</div>' + 939 `<div class="panel-body"> 940 <div class="panEdit-panel"></div> 941 </div> 942 <div class="panel-info-corner"><i class="fa fa-info" aria-hidden="true" id="panel-desc-info"></i></div> 943 `; 944 945 function checkForAddigInTopRow() { 946 let temp = []; 947 948 for (let i = 0; i < localPanels.length; i++) { 949 let y = localPanels[i].gridpos.y; 950 temp.push([y, i]); 951 } 952 temp.sort((a, b) => a[0] - b[0]); 953 let indices = []; 954 for (let i = 0; i < temp.length; i++) 955 indices.push(temp[i][1]) 956 957 let topmostY = 10000; 958 let rightBoundary = 0; 959 if (indices.length == 0) topmostY = 0; 960 for (let i = 0; i < indices.length; i++) { 961 let hPanel = localPanels[indices[i]].gridpos.h; 962 let wPanel = localPanels[indices[i]].gridpos.w; 963 let xPanel = localPanels[indices[i]].gridpos.x; 964 let yPanel = localPanels[indices[i]].gridpos.y; 965 966 if (yPanel <= topmostY) { 967 topmostY = yPanel; 968 rightBoundary = Math.max(rightBoundary, xPanel + wPanel); 969 } 970 else break; 971 } 972 973 974 let panelContainerWidth = $('#panel-container').width(); 975 if (rightBoundary <= panelContainerWidth * 0.49 + 10) return [true, rightBoundary, topmostY]; 976 else return [false, null, null]; 977 } 978 979 function addPanel(panelToDuplicate) { 980 flagDBSaved = false; 981 panelIndex = localPanels.length; 982 let idpanel = uuidv4(); 983 let panel = $("<div>").append(panelLayout).addClass("panel temp").attr("id", `panel${idpanel}`).attr("panel-index", panelIndex); 984 $("#panel-container").append(panel); 985 $(`#panel${idpanel} .panel-header p`).html(`panel${panelIndex}`); 986 $("#panel" + idpanel + " .panel-header").click(function () { 987 $("#panel" + idpanel + " .dropdown-btn").toggleClass("active") 988 $("#panel" + idpanel + " .dropdown-style").toggleClass("hidden"); 989 }) 990 $("#panel" + idpanel + " .dropdown-btn").click(function (e) { 991 e.stopPropagation(); 992 $("#panel" + idpanel + " .dropdown-btn").toggleClass("active") 993 $("#panel" + idpanel + " .dropdown-style").toggleClass("hidden"); 994 }); 995 $(`#panel${idpanel} .panel-info-corner`).hide(); 996 let marginTop = 0; 997 998 localPanels.map((localPanel) => { 999 let val = localPanel.gridpos.y + localPanel.gridpos.h; 1000 if (val > marginTop) marginTop = val; 1001 }) 1002 1003 let panelElement = document.getElementById(`panel${idpanel}`); 1004 let panelHeight = panelToDuplicate ? panelToDuplicate.gridpos.h : panelElement.offsetHeight; 1005 let panelWidth = panelToDuplicate ? panelToDuplicate.gridpos.w : panelElement.offsetWidth; 1006 let panelTop = panelToDuplicate ? panelToDuplicate.gridpos.y + panelToDuplicate.gridpos.h + 20 : marginTop + 20; 1007 let panelLeft = panelToDuplicate ? panelToDuplicate.gridpos.x : panelElement.offsetLeft; 1008 let panelWidthPercentage = panelWidth / panelContainerWidthGlobal; 1009 1010 if (panelToDuplicate == undefined) { // means a new panel is being added 1011 let [shouldAddInTopRow, rightBoundary, topmostY] = checkForAddigInTopRow(); 1012 if (shouldAddInTopRow) { 1013 panelLeft = rightBoundary == 0 ? rightBoundary : rightBoundary + 20; 1014 panelTop = topmostY == 0 ? topmostY + 10 : topmostY; 1015 } 1016 } 1017 1018 panelElement.style.position = "absolute" 1019 panelElement.style.top = panelTop + "px" 1020 panelElement.style.left = panelLeft + "px" 1021 1022 if (panelToDuplicate) { 1023 panelToDuplicate.panelId = idpanel; 1024 panelToDuplicate.name += "Copy"; 1025 panelToDuplicate.panelIndex = panelIndex; 1026 panelToDuplicate.gridpos.x = panelLeft; 1027 panelToDuplicate.gridpos.y = panelTop; 1028 panelToDuplicate.gridpos.h = panelHeight; 1029 panelToDuplicate.gridpos.w = panelWidth; 1030 if (panelToDuplicate.description){ 1031 handleDescriptionTooltip(panelToDuplicate.panelId,panelToDuplicate.description) 1032 } 1033 } 1034 1035 panelToDuplicate 1036 ? 1037 localPanels.push(JSON.parse(JSON.stringify(panelToDuplicate))) 1038 : 1039 localPanels.push({ 1040 "name": `panel${panelIndex}`, 1041 "panelIndex": panelIndex, 1042 "panelId": idpanel, 1043 "description": "", 1044 "chartType": "", 1045 "unit": "", 1046 "dataType": "", 1047 "gridpos": { 1048 "h": panelHeight, 1049 "w": panelWidth, 1050 "x": panelLeft, 1051 "y": panelTop, 1052 "wPercent": panelWidthPercentage, 1053 }, 1054 "queryType": "", 1055 }); 1056 if (!panelToDuplicate) { 1057 editPanelInit(panelIndex); 1058 $('.panelEditor-container').show(); 1059 $('#app-container').hide(); 1060 $('.panelDisplay #panelLogResultsGrid').empty(); 1061 $('.panelDisplay .big-number-display-container').hide(); 1062 $('.panelDisplay #empty-response').hide(); 1063 } 1064 resetPanelContainerHeight(); 1065 1066 handlePanelView(); 1067 handlePanelEdit(); 1068 handlePanelRemove(idpanel); 1069 handlePanelDuplicate(); 1070 handleDrag(idpanel); 1071 handleResize(idpanel); 1072 $(`#panel${idpanel}`).get(0).scrollIntoView({ behavior: 'smooth' }); 1073 1074 } 1075 1076 function resetPanelContainerHeight() { 1077 let panelContainerMinHeight = 0; 1078 localPanels.map((localPanel, index) => { 1079 let val = localPanel.gridpos.y + localPanel.gridpos.h; 1080 if (val > panelContainerMinHeight) panelContainerMinHeight = val; 1081 }) 1082 let panelContainer = document.getElementById('panel-container'); 1083 panelContainer.style.minHeight = panelContainerMinHeight + 50 + "px"; 1084 } 1085 1086 window.onbeforeunload = function () { 1087 if (!flagDBSaved) { 1088 return "Unsaved panel changes will be lost if you leave the page, are you sure?"; 1089 } 1090 else return; 1091 }; 1092 1093 1094 // DASHBOARD SETTINGS PAGE 1095 let editPanelFlag = false; 1096 function handleDbSettings() { 1097 if ($('.panelEditor-container').css('display') !== 'none') { 1098 $('.panelEditor-container').hide(); 1099 editPanelFlag =true; 1100 } else { 1101 $('#app-container').hide(); 1102 } 1103 $('.dbSet-container').show(); 1104 1105 $('.dbSet-name').html(dbName) 1106 $('.dbSet-dbName').val(dbName) 1107 $('.dbSet-dbDescr').val(dbDescr) 1108 $('.dbSet-jsonModelData').val(JSON.stringify(JSON.unflatten({ 1109 description: dbDescr, 1110 name: dbName, 1111 timeRange: timeRange, 1112 panels: localPanels, 1113 refresh: dbRefresh, 1114 }), null, 2)) 1115 $('.dbSet-dbName').on("change keyup paste", function () { 1116 dbName = $('.dbSet-dbName').val() 1117 $('.dbSet-name').html(dbName) 1118 }) 1119 $('.dbSet-dbDescr').on("change keyup paste", function () { 1120 dbDescr = $('.dbSet-dbDescr').val() 1121 $('.dbSet-dbDescr').html(dbDescr) 1122 $('.dbSet-jsonModelData').val(JSON.stringify(JSON.unflatten({ 1123 description: dbDescr, 1124 name: dbName, 1125 timeRange: timeRange, 1126 panels: localPanels, 1127 refresh: dbRefresh, 1128 }), null, 2)) 1129 }) 1130 1131 //get dashboard data from database 1132 $.ajax({ 1133 method: "get", 1134 url: "api/dashboards/" + dbId, 1135 headers: { 1136 'Content-Type': 'application/json; charset=utf-8', 1137 'Accept': '*/*' 1138 }, 1139 dataType: 'json', 1140 crossDomain: true, 1141 }).then(function (res) { 1142 console.log(JSON.stringify(res)) 1143 $(".dbSet-dbName").val(res.name); 1144 $(".dbSet-dbDescr").val(res.description); 1145 $('.dbSet-jsonModelData').val(JSON.stringify(JSON.unflatten(res), null, 2)) 1146 }) 1147 1148 showGeneralDbSettings(); 1149 addDbSettingsEventListeners(); 1150 } 1151 1152 function showGeneralDbSettings() { 1153 $('.dbSet-general').addClass('selected') 1154 $('.dbSet-generalHTML').removeClass('hide'); 1155 1156 $('.dbSet-jsonModel').removeClass('selected'); 1157 $('.dbSet-jsonModelHTML').addClass('hide') 1158 } 1159 1160 function showJsonModelDbSettings() { 1161 $('.dbSet-general').removeClass('selected') 1162 $('.dbSet-generalHTML').addClass('hide'); 1163 1164 $('.dbSet-jsonModel').addClass('selected'); 1165 $('.dbSet-jsonModelHTML').removeClass('hide') 1166 } 1167 1168 function addDbSettingsEventListeners() { 1169 $('.dbSet-general').on('click', showGeneralDbSettings); 1170 $('.dbSet-jsonModel').on('click', showJsonModelDbSettings) 1171 } 1172 1173 function saveDbSetting() { 1174 let trimmedDbName = $('.dbSet-dbName').val().trim(); 1175 let trimmedDbDescription = $(".dbSet-dbDescr").val().trim(); 1176 if (!trimmedDbName) { 1177 // Show error message using error-tip and popupOverlay 1178 $('.error-tip').addClass('active'); 1179 $('.popupOverlay, .popupContent').addClass('active'); 1180 $('#error-message').text('Dashboard name cannot be empty.'); 1181 return; 1182 } 1183 1184 1185 dbName = trimmedDbName; 1186 dbDescr = trimmedDbDescription; 1187 1188 1189 updateDashboard() 1190 .then(updateSuccessful => { 1191 if (updateSuccessful) { 1192 $('#app-container').show(); 1193 $('.dbSet-container').hide(); 1194 } 1195 }) 1196 } 1197 1198 $('#error-ok-btn').click(function () { 1199 $('.popupOverlay, .popupContent').removeClass('active'); 1200 $('.error-tip').removeClass('active'); 1201 }); 1202 1203 function discardDbSetting() { 1204 if(editPanelFlag){ 1205 $('.panelEditor-container').show(); 1206 editPanelFlag=false; 1207 }else{ 1208 $('#app-container').show(); 1209 } 1210 $('.dbSet-dbName').val(""); 1211 $('.dbSet-dbDescr').val(""); 1212 $('.dbSet-jsonModelData').val(""); 1213 $('.dbSet-container').hide(); 1214 dbName = dbData.name; 1215 dbDescr = dbData.description; 1216 } 1217 1218 function setRefreshItemHandler(){ 1219 $(".refresh-range-item").removeClass("active"); 1220 if(dbRefresh){ 1221 $(`.refresh-range-item:contains('${dbRefresh}')`).addClass("active"); 1222 $('.refresh-container #refresh-picker-btn span').text(dbRefresh); 1223 startRefreshInterval(dbRefresh) 1224 }else{ 1225 $('.refresh-container #refresh-picker-btn span').text(""); 1226 $(`.refresh-range-item:contains('Off')`).addClass("active"); 1227 } 1228 } 1229 1230 function refreshRangeItemHandler(evt){ 1231 $.each($(".refresh-range-item.active"), function () { 1232 $(this).removeClass('active'); 1233 }); 1234 $(evt.currentTarget).addClass('active'); 1235 let refreshInterval = $(evt.currentTarget).attr('id'); 1236 if(refreshInterval==="0"){ 1237 dbRefresh = ""; 1238 $('.refresh-container #refresh-picker-btn span').html(""); 1239 }else{ 1240 dbRefresh = refreshInterval; 1241 $('.refresh-container #refresh-picker-btn span').html(refreshInterval); 1242 } 1243 startRefreshInterval(refreshInterval) 1244 } 1245 1246 let intervalId; 1247 1248 function startRefreshInterval(refreshInterval) { 1249 let parsedRefreshInterval = parseInterval(refreshInterval); 1250 clearInterval(intervalId); 1251 if (parsedRefreshInterval > 0) { 1252 intervalId = setInterval(function () { 1253 refreshDashboardHandler(); 1254 }, parsedRefreshInterval); 1255 1256 }else{ 1257 pauseRefreshInterval(); 1258 } 1259 } 1260 1261 1262 function pauseRefreshInterval() { 1263 clearInterval(intervalId); 1264 return 0; 1265 } 1266 1267 function parseInterval(interval) { 1268 if(interval==="0"){ 1269 pauseRefreshInterval(); 1270 return; 1271 } 1272 const regex = /(\d+)([smhd])/; 1273 const match = interval.match(regex); 1274 const value = parseInt(match[1]); 1275 const unit = match[2]; 1276 1277 switch (unit) { 1278 case 'm': 1279 return value * 60 * 1000; 1280 case 'h': 1281 return value * 60 * 60 * 1000; 1282 case 'd': 1283 return value * 24 * 60 * 60 * 1000; 1284 default: 1285 throw new Error("Invalid interval unit"); 1286 } 1287 }