robotframework-dashboard 2.0.0__tar.gz → 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {robotframework_dashboard-2.0.0/robotframework_dashboard.egg-info → robotframework_dashboard-2.1.0}/PKG-INFO +1 -1
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/arguments.py +3 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/css/components.css +62 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/database.py +54 -1
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/admin_page/admin_api.js +3 -0
- robotframework_dashboard-2.1.0/robotframework_dashboard/js/customsections.js +224 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/eventlisteners.js +100 -5
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/filter.js +120 -3
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/keyword.js +15 -3
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/suite.js +15 -2
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/test.js +28 -6
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/layout.js +33 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/localstorage.js +4 -2
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/variables/globals.js +7 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/variables/information.js +9 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/variables/settings.js +3 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/server.py +28 -6
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/templates/admin.html +11 -1
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/templates/dashboard.html +74 -1
- robotframework_dashboard-2.1.0/robotframework_dashboard/version.py +1 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0/robotframework_dashboard.egg-info}/PKG-INFO +1 -1
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard.egg-info/SOURCES.txt +1 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/setup.py +1 -1
- robotframework_dashboard-2.0.0/robotframework_dashboard/version.py +0 -1
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/LICENSE +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/MANIFEST.in +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/README.md +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/pyproject.toml +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/__init__.py +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/abstractdb.py +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/css/base.css +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/css/colors.css +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/css/dark.css +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dashboard.py +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/bootstrap.css +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/bootstrap.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/chart.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/chartjs-adapter-date-fns.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/chartjs-chart-boxplot.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/chartjs-chart-matrix.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/chartjs-plugin-datalabels.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/datatables.css +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/datatables.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/gridstack.css +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/gridstack.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies/pako.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/dependencies.py +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/admin_page/admin_common.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/admin_page/admin_eventlisteners.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/admin_page/admin_information.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/admin_page/admin_main.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/admin_page/admin_theme.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/common.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/database.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/all.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/chart_factory.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/compare.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/config_helpers.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/overview.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/run.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_creation/tables.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/donut.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/duration.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/duration_deviation.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/failed.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/flaky.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/graph_config.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/heatmap.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/helpers.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/messages.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/statistics.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/stats.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/time_consuming.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/graph_data/tooltip_helpers.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/information.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/log.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/main.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/menu.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/statwidgets.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/theme.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/variables/chartconfig.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/variables/data.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/variables/graphmetadata.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/variables/graphs.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/variables/statwidgetdefs.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/js/variables/svg.js +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/licenses/THIRD_PARTY_LICENSES.txt +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/main.py +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/processors.py +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/queries.py +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard/robotdashboard.py +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard.egg-info/dependency_links.txt +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard.egg-info/entry_points.txt +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard.egg-info/not-zip-safe +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard.egg-info/requires.txt +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/robotframework_dashboard.egg-info/top_level.txt +0 -0
- {robotframework_dashboard-2.0.0 → robotframework_dashboard-2.1.0}/setup.cfg +0 -0
|
@@ -267,6 +267,9 @@ class ArgumentParser:
|
|
|
267
267
|
" • '-r run_start=2024-07-30 15:27:20.184407' -> remove specified run\n"
|
|
268
268
|
" • '-r alias=some_alias,tag=prod'\n"
|
|
269
269
|
" • '-r limit=10' -> keep only the 10 most recent runs\n"
|
|
270
|
+
" • '-r age=10d' -> remove runs older than 10 days\n"
|
|
271
|
+
" • '-r age=-10d' -> remove runs younger than 10 days\n"
|
|
272
|
+
" • (y)ear/(d)ay/(h)our/(m)inute/(s)econd supported\n"
|
|
270
273
|
),
|
|
271
274
|
action="append",
|
|
272
275
|
nargs="*",
|
|
@@ -193,6 +193,68 @@
|
|
|
193
193
|
min-width: 60px;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
/* Add custom section horizontal tile — shown in customize-view mode (unified grid only) */
|
|
197
|
+
.add-section-tile {
|
|
198
|
+
cursor: pointer;
|
|
199
|
+
display: flex;
|
|
200
|
+
align-items: center;
|
|
201
|
+
justify-content: center;
|
|
202
|
+
border: 2px dashed var(--bs-border-color, rgba(128,128,128,0.4));
|
|
203
|
+
border-radius: 8px;
|
|
204
|
+
transition: border-color 0.15s, background-color 0.15s;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.add-section-tile:hover {
|
|
208
|
+
border-color: var(--color-text);
|
|
209
|
+
background-color: rgba(128,128,128,0.08);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.add-section-tile-inner {
|
|
213
|
+
display: flex;
|
|
214
|
+
flex-direction: row;
|
|
215
|
+
align-items: center;
|
|
216
|
+
gap: 0.5rem;
|
|
217
|
+
user-select: none;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.add-section-plus {
|
|
221
|
+
font-size: 1.5rem;
|
|
222
|
+
font-weight: 300;
|
|
223
|
+
line-height: 1;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.add-section-tile-label {
|
|
227
|
+
font-size: 0.85rem;
|
|
228
|
+
font-weight: 500;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* Custom section divider bar rendered inside the unified GridStack */
|
|
232
|
+
.custom-section-divider {
|
|
233
|
+
display: flex;
|
|
234
|
+
align-items: center;
|
|
235
|
+
padding: 0 1rem;
|
|
236
|
+
border-radius: 6px;
|
|
237
|
+
height: 100%;
|
|
238
|
+
width: 100%;
|
|
239
|
+
position: relative;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.custom-section-title {
|
|
243
|
+
flex: 1;
|
|
244
|
+
font-size: 1rem;
|
|
245
|
+
font-weight: 600;
|
|
246
|
+
text-align: center;
|
|
247
|
+
user-select: none;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Delete button on custom section dividers — pinned to top-right */
|
|
251
|
+
.delete-custom-section {
|
|
252
|
+
position: absolute;
|
|
253
|
+
top: 5px;
|
|
254
|
+
right: 5px;
|
|
255
|
+
z-index: 1;
|
|
256
|
+
}
|
|
257
|
+
|
|
196
258
|
/* Delete button on custom stat widgets — pinned to top-right, out of flex flow */
|
|
197
259
|
.delete-custom-stat-widget {
|
|
198
260
|
position: absolute;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import sqlite3
|
|
2
|
+
import re
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from .queries import *
|
|
4
5
|
from .abstractdb import AbstractDatabaseProcessor
|
|
5
6
|
from time import time
|
|
6
|
-
from datetime import datetime, timezone
|
|
7
|
+
from datetime import datetime, timezone, timedelta
|
|
7
8
|
from typing import Union
|
|
8
9
|
|
|
9
10
|
# Explicit adapter for datetime -> ISO string, replacing the deprecated default
|
|
@@ -399,6 +400,8 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
399
400
|
console += self._remove_by_tag(run, run_starts, run_tags)
|
|
400
401
|
elif "limit=" in run:
|
|
401
402
|
console += self._remove_by_limit(run, run_starts)
|
|
403
|
+
elif "age=" in run:
|
|
404
|
+
console += self._remove_by_age(run, run_starts)
|
|
402
405
|
else:
|
|
403
406
|
print(
|
|
404
407
|
f" ERROR: incorrect usage of the remove_run feature ({run}), check out robotdashboard --help for instructions"
|
|
@@ -490,6 +493,37 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
490
493
|
console += f" Removed run from the database: index={index}, run_start={run_starts[index]}\n"
|
|
491
494
|
return console
|
|
492
495
|
|
|
496
|
+
def _remove_by_age(self, run_query: str, run_starts: list):
|
|
497
|
+
console = ""
|
|
498
|
+
try:
|
|
499
|
+
clean_query = run_query.replace("age=", "")
|
|
500
|
+
mod, delta = self.parse_time_range(clean_query)
|
|
501
|
+
except ValueError as e:
|
|
502
|
+
return f" ERROR: {e}"
|
|
503
|
+
cutoff = datetime.now(timezone.utc)-delta
|
|
504
|
+
targets = []
|
|
505
|
+
for r in run_starts:
|
|
506
|
+
try:
|
|
507
|
+
run_dt = datetime.fromisoformat(r)
|
|
508
|
+
if run_dt.tzinfo is None:
|
|
509
|
+
run_dt = run_dt.replace(tzinfo=timezone.utc)
|
|
510
|
+
if mod == "+":
|
|
511
|
+
if run_dt < cutoff:
|
|
512
|
+
targets.append(r)
|
|
513
|
+
elif mod == "-":
|
|
514
|
+
if run_dt > cutoff:
|
|
515
|
+
targets.append(r)
|
|
516
|
+
except ValueError as e:
|
|
517
|
+
print(f" WARNING: Skipping invalid timestamp: '{r}' ({e})")
|
|
518
|
+
if not targets:
|
|
519
|
+
console += f" WARNING: no runs were removed as no runs were within range {clean_query}"
|
|
520
|
+
return console
|
|
521
|
+
for run_to_remove in targets:
|
|
522
|
+
self._remove_run(run_to_remove)
|
|
523
|
+
print(f" Removed run from the database: run_start={run_to_remove}")
|
|
524
|
+
console += f" Removed run from the database: run_start={run_to_remove}\n"
|
|
525
|
+
return console
|
|
526
|
+
|
|
493
527
|
def _remove_run(self, run_start: str):
|
|
494
528
|
"""Helper function to remove the data from all tables"""
|
|
495
529
|
self.connection.cursor().execute(DELETE_FROM_RUNS.format(run_start=run_start))
|
|
@@ -510,6 +544,25 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
510
544
|
print(f" Vacuumed the database in {round(end - start, 2)} seconds")
|
|
511
545
|
return console
|
|
512
546
|
|
|
547
|
+
def parse_time_range(self, range_str: str):
|
|
548
|
+
# Regex groups : [modifier] [value] [unit]
|
|
549
|
+
# e.g., +10d, -4h, 1y
|
|
550
|
+
match = re.match(r"([+-])?(\d+)([smhdy])", range_str)
|
|
551
|
+
if not match:
|
|
552
|
+
raise ValueError("Invalid format. Use e.g., 10d, +5h, -1y")
|
|
553
|
+
modifier, value, unit = match.groups()
|
|
554
|
+
value = int(value)
|
|
555
|
+
units = {
|
|
556
|
+
's': 'seconds',
|
|
557
|
+
'm': 'minutes',
|
|
558
|
+
'h': 'hours',
|
|
559
|
+
'd': 'days',
|
|
560
|
+
'y': 'days'
|
|
561
|
+
}
|
|
562
|
+
# assume year is 365 days
|
|
563
|
+
delta_kwargs = {units[unit]: value * (365 if unit == 'y' else 1)}
|
|
564
|
+
return modifier or '+', timedelta(**delta_kwargs)
|
|
565
|
+
|
|
513
566
|
def update_output_path(self, log_path: str):
|
|
514
567
|
"""Function to update the output_path using the log path that the server has used"""
|
|
515
568
|
console = ""
|
|
@@ -116,6 +116,8 @@ function remove_outputs() {
|
|
|
116
116
|
const removeTags = document.getElementById("removeTags").value.split(",")
|
|
117
117
|
const removeLimit = document.getElementById("removeLimit").value
|
|
118
118
|
if (removeLimit != "") { data["limit"] = removeLimit }
|
|
119
|
+
const removeAge = document.getElementById("removeAge").value
|
|
120
|
+
if (removeAge != "") { data["age"] = removeAge }
|
|
119
121
|
for (const removeTag of removeTags) {
|
|
120
122
|
if (removeTag == "") { continue }
|
|
121
123
|
tags.push(removeTag)
|
|
@@ -126,6 +128,7 @@ function remove_outputs() {
|
|
|
126
128
|
document.getElementById("removeAliases").value = ""
|
|
127
129
|
document.getElementById("removeTags").value = ""
|
|
128
130
|
document.getElementById("removeLimit").value = ""
|
|
131
|
+
document.getElementById("removeAge").value = ""
|
|
129
132
|
const body = JSON.stringify(data)
|
|
130
133
|
send_request("DELETE", "/remove-outputs", body, "removeSpinner")
|
|
131
134
|
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { settings } from './variables/settings.js';
|
|
2
|
+
import { set_local_storage_item } from './localstorage.js';
|
|
3
|
+
import { STAT_WIDGET_COLORS, STAT_WIDGET_BG_COLORS } from './variables/statwidgetdefs.js';
|
|
4
|
+
|
|
5
|
+
// Generates a short random ID (safe across all modern browsers)
|
|
6
|
+
function generate_section_id() {
|
|
7
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
8
|
+
return crypto.randomUUID().replace(/-/g, '').slice(0, 12);
|
|
9
|
+
}
|
|
10
|
+
return Math.random().toString(36).slice(2, 14);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Applies (or updates) the bg class on the grid-stack-item-content of a section item el
|
|
14
|
+
function apply_section_bg_class(itemEl, bgColor) {
|
|
15
|
+
const content = itemEl.querySelector('.grid-stack-item-content');
|
|
16
|
+
if (!content) return;
|
|
17
|
+
content.classList.remove('blue-bg', 'green-bg', 'red-bg', 'yellow-bg');
|
|
18
|
+
if (bgColor) content.classList.add(bgColor);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Builds the inner HTML for a custom section divider bar
|
|
22
|
+
function build_section_divider_html(section, editMode) {
|
|
23
|
+
const deleteBtn = editMode
|
|
24
|
+
? `<button type="button" class="btn-close btn-close-sm delete-custom-section" aria-label="Remove section" data-section-id="${section.id}"></button>`
|
|
25
|
+
: '';
|
|
26
|
+
return `<div class="custom-section-divider ${section.textColor || 'white-text'}">
|
|
27
|
+
<span class="custom-section-title">${section.title}</span>
|
|
28
|
+
${deleteBtn}
|
|
29
|
+
</div>`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Adds all custom section dividers (for unified grid) to the provided GridStack
|
|
33
|
+
function render_custom_sections(gridStack, editMode) {
|
|
34
|
+
const savedLayout = settings.layouts?.['gridUnified']
|
|
35
|
+
? JSON.parse(settings.layouts['gridUnified'])
|
|
36
|
+
: null;
|
|
37
|
+
|
|
38
|
+
const sections = settings.customSections || [];
|
|
39
|
+
for (const section of sections) {
|
|
40
|
+
const saved = savedLayout?.find(l => l.id === `customSection-${section.id}`);
|
|
41
|
+
|
|
42
|
+
const item = document.createElement('div');
|
|
43
|
+
item.classList.add('grid-stack-item');
|
|
44
|
+
item.setAttribute('gs-w', saved ? saved.w : 12);
|
|
45
|
+
item.setAttribute('gs-h', saved ? saved.h : 1);
|
|
46
|
+
item.setAttribute('gs-min-w', 12);
|
|
47
|
+
item.setAttribute('gs-max-w', 12);
|
|
48
|
+
item.setAttribute('gs-min-h', 1);
|
|
49
|
+
item.setAttribute('gs-max-h', 3);
|
|
50
|
+
if (saved) {
|
|
51
|
+
item.setAttribute('gs-x', saved.x);
|
|
52
|
+
item.setAttribute('gs-y', saved.y);
|
|
53
|
+
}
|
|
54
|
+
item.setAttribute('data-gs-id', `customSection-${section.id}`);
|
|
55
|
+
item.innerHTML = `<div class="grid-stack-item-content">${build_section_divider_html(section, editMode)}</div>`;
|
|
56
|
+
gridStack.makeWidget(item);
|
|
57
|
+
apply_section_bg_class(item, section.bgColor);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Adds a special "Add custom section" horizontal tile to the unified GridStack for edit mode
|
|
62
|
+
function render_add_section_tile(gridStack) {
|
|
63
|
+
const tileId = 'addSectionTile-unified';
|
|
64
|
+
if (gridStack.el.querySelector(`[data-gs-id="${tileId}"]`)) return;
|
|
65
|
+
|
|
66
|
+
const item = document.createElement('div');
|
|
67
|
+
item.classList.add('grid-stack-item');
|
|
68
|
+
item.setAttribute('gs-w', 12);
|
|
69
|
+
item.setAttribute('gs-h', 1);
|
|
70
|
+
item.setAttribute('gs-min-w', 12);
|
|
71
|
+
item.setAttribute('gs-max-w', 12);
|
|
72
|
+
item.setAttribute('gs-min-h', 1);
|
|
73
|
+
item.setAttribute('gs-max-h', 1);
|
|
74
|
+
item.setAttribute('gs-no-resize', 'true');
|
|
75
|
+
item.setAttribute('data-gs-id', tileId);
|
|
76
|
+
item.innerHTML = `<div class="grid-stack-item-content add-section-tile">
|
|
77
|
+
<div class="add-section-tile-inner">
|
|
78
|
+
<div class="add-section-plus">+</div>
|
|
79
|
+
<div class="add-section-tile-label">Add custom section</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>`;
|
|
82
|
+
gridStack.makeWidget(item);
|
|
83
|
+
item.querySelector('.add-section-tile').addEventListener('click', () => {
|
|
84
|
+
open_add_custom_section_modal();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Saves a new custom section divider to localStorage and returns it
|
|
89
|
+
function add_custom_section(title, bgColor, textColor) {
|
|
90
|
+
const id = generate_section_id();
|
|
91
|
+
const section = { id, title, bgColor, textColor };
|
|
92
|
+
const list = settings.customSections ? [...settings.customSections] : [];
|
|
93
|
+
list.push(section);
|
|
94
|
+
set_local_storage_item('customSections', list);
|
|
95
|
+
return section;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Removes a custom section divider from localStorage by its id
|
|
99
|
+
function remove_custom_section(id) {
|
|
100
|
+
const list = (settings.customSections || []).filter(s => s.id !== id);
|
|
101
|
+
set_local_storage_item('customSections', list);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Populates the background color picker in the Add Custom Section modal
|
|
105
|
+
function populate_section_bg_colors() {
|
|
106
|
+
const picker = document.getElementById('addCustomSectionBgColorPicker');
|
|
107
|
+
if (!picker) return;
|
|
108
|
+
picker.innerHTML = '';
|
|
109
|
+
for (const c of STAT_WIDGET_BG_COLORS) {
|
|
110
|
+
const btn = document.createElement('button');
|
|
111
|
+
btn.type = 'button';
|
|
112
|
+
btn.className = 'btn btn-outline-light btn-sm stat-color-btn';
|
|
113
|
+
btn.dataset.color = c.value;
|
|
114
|
+
btn.textContent = c.label;
|
|
115
|
+
if (c.value === '') btn.classList.add('active');
|
|
116
|
+
btn.addEventListener('click', () => {
|
|
117
|
+
picker.querySelectorAll('.stat-color-btn').forEach(b => b.classList.remove('active'));
|
|
118
|
+
btn.classList.add('active');
|
|
119
|
+
});
|
|
120
|
+
picker.appendChild(btn);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Populates the text color picker in the Add Custom Section modal
|
|
125
|
+
function populate_section_text_colors() {
|
|
126
|
+
const picker = document.getElementById('addCustomSectionTextColorPicker');
|
|
127
|
+
if (!picker) return;
|
|
128
|
+
picker.innerHTML = '';
|
|
129
|
+
for (const c of STAT_WIDGET_COLORS) {
|
|
130
|
+
const btn = document.createElement('button');
|
|
131
|
+
btn.type = 'button';
|
|
132
|
+
btn.className = 'btn btn-outline-light btn-sm stat-color-btn';
|
|
133
|
+
btn.dataset.color = c.value;
|
|
134
|
+
btn.textContent = c.label;
|
|
135
|
+
if (c.value === 'white-text') btn.classList.add('active');
|
|
136
|
+
btn.addEventListener('click', () => {
|
|
137
|
+
picker.querySelectorAll('.stat-color-btn').forEach(b => b.classList.remove('active'));
|
|
138
|
+
btn.classList.add('active');
|
|
139
|
+
});
|
|
140
|
+
picker.appendChild(btn);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Wires all events inside the Add Custom Section modal (call once after DOM ready)
|
|
145
|
+
function setup_add_custom_section_modal() {
|
|
146
|
+
populate_section_bg_colors();
|
|
147
|
+
populate_section_text_colors();
|
|
148
|
+
|
|
149
|
+
document.getElementById('addCustomSectionConfirm')?.addEventListener('click', () => {
|
|
150
|
+
const titleInput = document.getElementById('addCustomSectionTitle');
|
|
151
|
+
const title = titleInput?.value.trim() || 'Section';
|
|
152
|
+
const bgColorBtn = document.querySelector('#addCustomSectionBgColorPicker .stat-color-btn.active');
|
|
153
|
+
const bgColor = bgColorBtn?.dataset.color || '';
|
|
154
|
+
const textColorBtn = document.querySelector('#addCustomSectionTextColorPicker .stat-color-btn.active');
|
|
155
|
+
const textColor = textColorBtn?.dataset.color || 'white-text';
|
|
156
|
+
|
|
157
|
+
const section = add_custom_section(title, bgColor, textColor);
|
|
158
|
+
|
|
159
|
+
const grid = window['gridUnified'];
|
|
160
|
+
if (grid) {
|
|
161
|
+
const item = document.createElement('div');
|
|
162
|
+
item.classList.add('grid-stack-item');
|
|
163
|
+
item.setAttribute('gs-w', 12);
|
|
164
|
+
item.setAttribute('gs-h', 1);
|
|
165
|
+
item.setAttribute('gs-min-w', 12);
|
|
166
|
+
item.setAttribute('gs-max-w', 12);
|
|
167
|
+
item.setAttribute('gs-min-h', 1);
|
|
168
|
+
item.setAttribute('gs-max-h', 3);
|
|
169
|
+
item.setAttribute('data-gs-id', `customSection-${section.id}`);
|
|
170
|
+
item.innerHTML = `<div class="grid-stack-item-content">${build_section_divider_html(section, true)}</div>`;
|
|
171
|
+
grid.makeWidget(item);
|
|
172
|
+
apply_section_bg_class(item, section.bgColor);
|
|
173
|
+
const deleteBtn = item.querySelector(`.delete-custom-section[data-section-id="${section.id}"]`);
|
|
174
|
+
if (deleteBtn) {
|
|
175
|
+
deleteBtn.addEventListener('click', () => handle_delete_section(section.id, grid));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
document.dispatchEvent(new CustomEvent("layout-user-action"));
|
|
180
|
+
|
|
181
|
+
if (titleInput) titleInput.value = '';
|
|
182
|
+
bootstrap.Modal.getInstance(document.getElementById('addCustomSectionModal'))?.hide();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Reset color pickers each time the modal opens
|
|
186
|
+
document.getElementById('addCustomSectionModal')?.addEventListener('show.bs.modal', () => {
|
|
187
|
+
populate_section_bg_colors();
|
|
188
|
+
populate_section_text_colors();
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Wires delete buttons on all currently rendered custom section dividers
|
|
193
|
+
function wire_delete_section_buttons(gridStack) {
|
|
194
|
+
const sections = settings.customSections || [];
|
|
195
|
+
for (const section of sections) {
|
|
196
|
+
const btn = document.querySelector(`.delete-custom-section[data-section-id="${section.id}"]`);
|
|
197
|
+
if (btn) {
|
|
198
|
+
btn.addEventListener('click', () => handle_delete_section(section.id, gridStack));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function handle_delete_section(id, gridStack) {
|
|
204
|
+
const el = gridStack?.el?.querySelector(`[data-gs-id="customSection-${id}"]`);
|
|
205
|
+
if (el && gridStack) {
|
|
206
|
+
gridStack.removeWidget(el);
|
|
207
|
+
}
|
|
208
|
+
remove_custom_section(id);
|
|
209
|
+
document.dispatchEvent(new CustomEvent("layout-user-action"));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Opens the Add Custom Section modal
|
|
213
|
+
function open_add_custom_section_modal() {
|
|
214
|
+
const modal = document.getElementById('addCustomSectionModal');
|
|
215
|
+
if (!modal) return;
|
|
216
|
+
bootstrap.Modal.getOrCreateInstance(modal).show();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export {
|
|
220
|
+
render_custom_sections,
|
|
221
|
+
render_add_section_tile,
|
|
222
|
+
setup_add_custom_section_modal,
|
|
223
|
+
wire_delete_section_buttons,
|
|
224
|
+
};
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
setup_lowest_highest_dates,
|
|
27
27
|
clear_all_filters,
|
|
28
28
|
setup_project_versions_in_select_filter_buttons,
|
|
29
|
+
setup_suite_path_navigator,
|
|
29
30
|
setup_custom_filters_in_select_filter_buttons,
|
|
30
31
|
update_overview_version_select_list,
|
|
31
32
|
setup_metadata_filter,
|
|
@@ -251,6 +252,7 @@ function setup_filter_modal() {
|
|
|
251
252
|
setup_runs_in_select_filter_buttons();
|
|
252
253
|
setup_runtags_in_select_filter_buttons();
|
|
253
254
|
setup_project_versions_in_select_filter_buttons();
|
|
255
|
+
setup_suite_path_navigator("All");
|
|
254
256
|
setup_custom_filters_in_select_filter_buttons();
|
|
255
257
|
// snapshot the default/initial filter state so profile checkboxes can reflect changes
|
|
256
258
|
capture_default_filters();
|
|
@@ -822,9 +824,16 @@ function setup_sections_filters() {
|
|
|
822
824
|
update_overview_filter_visibility();
|
|
823
825
|
});
|
|
824
826
|
document.getElementById("suiteSelectSuites").addEventListener("change", () => {
|
|
825
|
-
|
|
827
|
+
const mostGraphIds = settings.switch.sectionFiltersApplySuite
|
|
828
|
+
? ["suiteMostFailedGraph", "suiteMostTimeConsumingGraph"]
|
|
829
|
+
: [];
|
|
830
|
+
update_graphs_with_loading(["suiteStatisticsGraph", "suiteDurationGraph", ...mostGraphIds], () => {
|
|
826
831
|
update_suite_duration_graph();
|
|
827
832
|
update_suite_statistics_graph();
|
|
833
|
+
if (settings.switch.sectionFiltersApplySuite) {
|
|
834
|
+
update_suite_most_failed_graph();
|
|
835
|
+
update_suite_most_time_consuming_graph();
|
|
836
|
+
}
|
|
828
837
|
});
|
|
829
838
|
});
|
|
830
839
|
update_switch_local_storage("switch.suitePathsSuiteSection", settings.switch.suitePathsSuiteSection, true);
|
|
@@ -842,20 +851,44 @@ function setup_sections_filters() {
|
|
|
842
851
|
}
|
|
843
852
|
);
|
|
844
853
|
});
|
|
854
|
+
update_switch_local_storage("switch.sectionFiltersApplySuite", settings.switch.sectionFiltersApplySuite, true);
|
|
855
|
+
document.getElementById("switchSectionFiltersApplySuite").addEventListener("change", () => {
|
|
856
|
+
settings.switch.sectionFiltersApplySuite = !settings.switch.sectionFiltersApplySuite;
|
|
857
|
+
update_switch_local_storage("switch.sectionFiltersApplySuite", settings.switch.sectionFiltersApplySuite);
|
|
858
|
+
update_graphs_with_loading(
|
|
859
|
+
["suiteMostFailedGraph", "suiteMostTimeConsumingGraph"],
|
|
860
|
+
() => {
|
|
861
|
+
update_suite_most_failed_graph();
|
|
862
|
+
update_suite_most_time_consuming_graph();
|
|
863
|
+
}
|
|
864
|
+
);
|
|
865
|
+
});
|
|
845
866
|
document.getElementById("resetSuiteFolder").addEventListener("click", () => {
|
|
846
867
|
update_graphs_with_loading(["suiteFolderDonutGraph", "suiteFolderFailDonutGraph", "suiteStatisticsGraph", "suiteDurationGraph"], () => {
|
|
847
868
|
update_suite_folder_donut_graph("");
|
|
848
869
|
});
|
|
849
870
|
});
|
|
850
871
|
document.getElementById("suiteSelectTests").addEventListener("change", () => {
|
|
872
|
+
const mostGraphIds = settings.switch.sectionFiltersApplyTest
|
|
873
|
+
? ["testMessagesGraph", "testMostFlakyGraph", "testRecentMostFlakyGraph",
|
|
874
|
+
"testMostFailedGraph", "testRecentMostFailedGraph", "testMostTimeConsumingGraph"]
|
|
875
|
+
: [];
|
|
851
876
|
update_graphs_with_loading(
|
|
852
|
-
["testStatisticsGraph", "testDurationGraph", "testDurationDeviationGraph"],
|
|
877
|
+
["testStatisticsGraph", "testDurationGraph", "testDurationDeviationGraph", ...mostGraphIds],
|
|
853
878
|
() => {
|
|
854
879
|
setup_testtags_in_select();
|
|
855
880
|
setup_tests_in_select();
|
|
856
881
|
update_test_statistics_graph();
|
|
857
882
|
update_test_duration_graph();
|
|
858
883
|
update_test_duration_deviation_graph();
|
|
884
|
+
if (settings.switch.sectionFiltersApplyTest) {
|
|
885
|
+
update_test_messages_graph();
|
|
886
|
+
update_test_most_flaky_graph();
|
|
887
|
+
update_test_recent_most_flaky_graph();
|
|
888
|
+
update_test_most_failed_graph();
|
|
889
|
+
update_test_recent_most_failed_graph();
|
|
890
|
+
update_test_most_time_consuming_graph();
|
|
891
|
+
}
|
|
859
892
|
}
|
|
860
893
|
);
|
|
861
894
|
});
|
|
@@ -881,31 +914,75 @@ function setup_sections_filters() {
|
|
|
881
914
|
}
|
|
882
915
|
);
|
|
883
916
|
});
|
|
917
|
+
update_switch_local_storage("switch.sectionFiltersApplyTest", settings.switch.sectionFiltersApplyTest, true);
|
|
918
|
+
document.getElementById("switchSectionFiltersApplyTest").addEventListener("change", () => {
|
|
919
|
+
settings.switch.sectionFiltersApplyTest = !settings.switch.sectionFiltersApplyTest;
|
|
920
|
+
update_switch_local_storage("switch.sectionFiltersApplyTest", settings.switch.sectionFiltersApplyTest);
|
|
921
|
+
update_graphs_with_loading(
|
|
922
|
+
["testMessagesGraph", "testMostFlakyGraph", "testRecentMostFlakyGraph",
|
|
923
|
+
"testMostFailedGraph", "testRecentMostFailedGraph", "testMostTimeConsumingGraph"],
|
|
924
|
+
() => {
|
|
925
|
+
update_test_messages_graph();
|
|
926
|
+
update_test_most_flaky_graph();
|
|
927
|
+
update_test_recent_most_flaky_graph();
|
|
928
|
+
update_test_most_failed_graph();
|
|
929
|
+
update_test_recent_most_failed_graph();
|
|
930
|
+
update_test_most_time_consuming_graph();
|
|
931
|
+
}
|
|
932
|
+
);
|
|
933
|
+
});
|
|
884
934
|
document.getElementById("testTagsSelect").addEventListener("change", () => {
|
|
935
|
+
const mostGraphIds = settings.switch.sectionFiltersApplyTest
|
|
936
|
+
? ["testMessagesGraph", "testMostFlakyGraph", "testRecentMostFlakyGraph",
|
|
937
|
+
"testMostFailedGraph", "testRecentMostFailedGraph", "testMostTimeConsumingGraph"]
|
|
938
|
+
: [];
|
|
885
939
|
update_graphs_with_loading(
|
|
886
|
-
["testStatisticsGraph", "testDurationGraph", "testDurationDeviationGraph"],
|
|
940
|
+
["testStatisticsGraph", "testDurationGraph", "testDurationDeviationGraph", ...mostGraphIds],
|
|
887
941
|
() => {
|
|
888
942
|
setup_tests_in_select();
|
|
889
943
|
update_test_statistics_graph();
|
|
890
944
|
update_test_duration_graph();
|
|
891
945
|
update_test_duration_deviation_graph();
|
|
946
|
+
if (settings.switch.sectionFiltersApplyTest) {
|
|
947
|
+
update_test_messages_graph();
|
|
948
|
+
update_test_most_flaky_graph();
|
|
949
|
+
update_test_recent_most_flaky_graph();
|
|
950
|
+
update_test_most_failed_graph();
|
|
951
|
+
update_test_recent_most_failed_graph();
|
|
952
|
+
update_test_most_time_consuming_graph();
|
|
953
|
+
}
|
|
892
954
|
}
|
|
893
955
|
);
|
|
894
956
|
});
|
|
895
957
|
document.getElementById("testSelect").addEventListener("change", () => {
|
|
958
|
+
const mostGraphIds = settings.switch.sectionFiltersApplyTest
|
|
959
|
+
? ["testMessagesGraph", "testMostFlakyGraph", "testRecentMostFlakyGraph",
|
|
960
|
+
"testMostFailedGraph", "testRecentMostFailedGraph", "testMostTimeConsumingGraph"]
|
|
961
|
+
: [];
|
|
896
962
|
update_graphs_with_loading(
|
|
897
|
-
["testStatisticsGraph", "testDurationGraph", "testDurationDeviationGraph"],
|
|
963
|
+
["testStatisticsGraph", "testDurationGraph", "testDurationDeviationGraph", ...mostGraphIds],
|
|
898
964
|
() => {
|
|
899
965
|
update_test_statistics_graph();
|
|
900
966
|
update_test_duration_graph();
|
|
901
967
|
update_test_duration_deviation_graph();
|
|
968
|
+
if (settings.switch.sectionFiltersApplyTest) {
|
|
969
|
+
update_test_messages_graph();
|
|
970
|
+
update_test_most_flaky_graph();
|
|
971
|
+
update_test_recent_most_flaky_graph();
|
|
972
|
+
update_test_most_failed_graph();
|
|
973
|
+
update_test_recent_most_failed_graph();
|
|
974
|
+
update_test_most_time_consuming_graph();
|
|
975
|
+
}
|
|
902
976
|
}
|
|
903
977
|
);
|
|
904
978
|
});
|
|
905
979
|
document.getElementById("keywordSelect").addEventListener("change", () => {
|
|
980
|
+
const mostGraphIds = settings.switch.sectionFiltersApplyKeyword
|
|
981
|
+
? ["keywordMostFailedGraph", "keywordMostTimeConsumingGraph", "keywordMostUsedGraph"]
|
|
982
|
+
: [];
|
|
906
983
|
update_graphs_with_loading(
|
|
907
984
|
["keywordStatisticsGraph", "keywordTimesRunGraph", "keywordTotalDurationGraph",
|
|
908
|
-
"keywordAverageDurationGraph", "keywordMinDurationGraph", "keywordMaxDurationGraph"],
|
|
985
|
+
"keywordAverageDurationGraph", "keywordMinDurationGraph", "keywordMaxDurationGraph", ...mostGraphIds],
|
|
909
986
|
() => {
|
|
910
987
|
update_keyword_statistics_graph();
|
|
911
988
|
update_keyword_times_run_graph();
|
|
@@ -913,6 +990,24 @@ function setup_sections_filters() {
|
|
|
913
990
|
update_keyword_average_duration_graph();
|
|
914
991
|
update_keyword_min_duration_graph();
|
|
915
992
|
update_keyword_max_duration_graph();
|
|
993
|
+
if (settings.switch.sectionFiltersApplyKeyword) {
|
|
994
|
+
update_keyword_most_failed_graph();
|
|
995
|
+
update_keyword_most_time_consuming_graph();
|
|
996
|
+
update_keyword_most_used_graph();
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
);
|
|
1000
|
+
});
|
|
1001
|
+
update_switch_local_storage("switch.sectionFiltersApplyKeyword", settings.switch.sectionFiltersApplyKeyword, true);
|
|
1002
|
+
document.getElementById("switchSectionFiltersApplyKeyword").addEventListener("change", () => {
|
|
1003
|
+
settings.switch.sectionFiltersApplyKeyword = !settings.switch.sectionFiltersApplyKeyword;
|
|
1004
|
+
update_switch_local_storage("switch.sectionFiltersApplyKeyword", settings.switch.sectionFiltersApplyKeyword);
|
|
1005
|
+
update_graphs_with_loading(
|
|
1006
|
+
["keywordMostFailedGraph", "keywordMostTimeConsumingGraph", "keywordMostUsedGraph"],
|
|
1007
|
+
() => {
|
|
1008
|
+
update_keyword_most_failed_graph();
|
|
1009
|
+
update_keyword_most_time_consuming_graph();
|
|
1010
|
+
update_keyword_most_used_graph();
|
|
916
1011
|
}
|
|
917
1012
|
);
|
|
918
1013
|
});
|