task detail ux/quality of life
Alessandro committed
Dec 22, 2025 at 08:46 UTC
d3a02482766a3950fc698562a3b5ad539f8c897f
6 files changed
+127
-110
webui/components/settings/scheduler/scheduler-store.js
+13
@@ -915,6 +915,19 @@ const schedulerStoreModel = {
915
window.closeModal();
916
},
917
918
+ editFromDetail() {
919
+ const taskId = this.selectedTaskForDetail?.uuid;
920
+ if (!taskId) return;
921
+ this.closeTaskDetail();
922
+ this.startEditTask(taskId);
923
+ },
924
+
925
+ async deleteFromDetail() {
926
+ const taskId = this.selectedTaskForDetail?.uuid;
927
+ if (!taskId) return;
928
+ await this.deleteTask(taskId);
929
+ },
930
+
931
async startCreateTask() {
932
this.isCreating = true;
933
this.isEditing = false;
webui/components/settings/scheduler/scheduler-task-detail.html
+25
-6
@@ -11,12 +11,31 @@
11
<div class="scheduler-detail-view"
12
x-show="$store.schedulerStore.selectedTaskForDetail">
13
<div class="scheduler-detail-header">
14
- <h2 class="scheduler-detail-title"
15
- x-text="$store.schedulerStore.selectedTaskForDetail ? $store.schedulerStore.selectedTaskForDetail.name : ''"></h2>
16
- <div class="scheduler-status-badge"
17
- :class="$store.schedulerStore.selectedTaskForDetail ? $store.schedulerStore.getStateBadgeClass($store.schedulerStore.selectedTaskForDetail.state) : ''"
18
- x-text="$store.schedulerStore.selectedTaskForDetail ? $store.schedulerStore.selectedTaskForDetail.state : ''"></div>
19
- <button class="btn btn-cancel" @click="$store.schedulerStore.closeTaskDetail()">Close</button>
14
+ <div class="scheduler-detail-header-left">
15
+ <h2 class="scheduler-detail-title"
16
+ x-text="$store.schedulerStore.selectedTaskForDetail ? $store.schedulerStore.selectedTaskForDetail.name : ''"></h2>
17
+ <div class="scheduler-status-badge"
18
+ :class="$store.schedulerStore.selectedTaskForDetail ? $store.schedulerStore.getStateBadgeClass($store.schedulerStore.selectedTaskForDetail.state) : ''"
19
+ x-text="$store.schedulerStore.selectedTaskForDetail ? $store.schedulerStore.selectedTaskForDetail.state : ''"></div>
20
+ </div>
21
+ <div class="scheduler-detail-header-actions">
22
+ <button class="btn btn-action-header"
23
+ @click="$store.schedulerStore.runTask($store.schedulerStore.selectedTaskForDetail.uuid)"
24
+ title="Run Task">
25
+ <span class="material-symbols-outlined">play_arrow</span>
26
+ </button>
27
+ <button class="btn btn-action-header"
28
+ @click="$store.schedulerStore.editFromDetail()"
29
+ title="Edit Task">
30
+ <span class="material-symbols-outlined">edit</span>
31
+ </button>
32
+ <button class="btn btn-action-header danger"
33
+ @click="$store.schedulerStore.deleteFromDetail()"
34
+ title="Delete Task">
35
+ <span class="material-symbols-outlined">delete</span>
36
+ </button>
37
+ <button class="btn btn-cancel" @click="$store.schedulerStore.closeTaskDetail()">Close</button>
38
+ </div>
39
</div>
40
41
<div class="scheduler-detail-content">
webui/components/settings/scheduler/scheduler-task-list.html
+10
-21
@@ -53,57 +53,46 @@
53
x-effect="$el.style.display = (!$store.schedulerStore.isLoading && $store.schedulerStore.filteredTasks.length > 0) ? '' : 'none'">
54
<thead>
55
<tr>
56
- <th @click="$store.schedulerStore.changeSort('name')">
56
+ <th class="col-name" @click="$store.schedulerStore.changeSort('name')">
57
Name
58
<span class="scheduler-sort-indicator"
59
x-show="$store.schedulerStore.sortField === 'name'"
60
:class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
61
</th>
62
- <th @click="$store.schedulerStore.changeSort('state')">
62
+ <th class="col-state" @click="$store.schedulerStore.changeSort('state')">
63
State
64
<span class="scheduler-sort-indicator"
65
x-show="$store.schedulerStore.sortField === 'state'"
66
:class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
67
</th>
68
- <th>Type</th>
69
- <th>Project</th>
70
- <th>Schedule</th>
71
- <th @click="$store.schedulerStore.changeSort('last_run')">
68
+ <th class="col-project">Project</th>
69
+ <th class="col-lastrun" @click="$store.schedulerStore.changeSort('last_run')">
70
Last Run
71
<span class="scheduler-sort-indicator"
72
x-show="$store.schedulerStore.sortField === 'last_run'"
73
:class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
74
</th>
77
- <th>Actions</th>
75
+ <th class="col-actions">Actions</th>
76
</tr>
77
</thead>
78
<tbody>
79
<template x-for="task in $store.schedulerStore.filteredTasks" :key="task.uuid">
80
<tr @click="$store.schedulerStore.showTaskDetail(task.uuid)">
83
- <td>
81
+ <td class="col-name">
82
<span x-text="task.name"></span>
83
</td>
86
- <td>
84
+ <td class="col-state">
85
<span class="scheduler-status-badge"
86
:class="$store.schedulerStore.getStateBadgeClass(task.state)"
87
x-text="task.state"></span>
88
</td>
91
- <td x-text="task.type"></td>
92
- <td>
89
+ <td class="col-project">
90
<span class="project-color-ball"
91
:style="$store.schedulerStore.extractTaskProject(task)?.color ? { backgroundColor: $store.schedulerStore.extractTaskProject(task).color } : { border: '1px solid var(--color-border)' }"></span>
92
<span x-text="$store.schedulerStore.formatTaskProject(task)"></span>
93
</td>
97
- <td>
98
- <span x-show="task.type === 'scheduled'"
99
- x-text="$store.schedulerStore.formatSchedule(task)"></span>
100
- <span x-show="task.type === 'adhoc'"
101
- class="scheduler-no-schedule">—</span>
102
- <span x-show="task.type === 'planned'"
103
- x-html="$store.schedulerStore.formatPlan(task).replace(/\n/g, '<br>')"></span>
104
- </td>
105
- <td x-text="$store.schedulerStore.formatDate(task.last_run)"></td>
106
- <td @click.stop>
94
+ <td class="col-lastrun" x-text="$store.schedulerStore.formatDate(task.last_run)"></td>
95
+ <td class="col-actions" @click.stop>
96
<div class="scheduler-task-actions">
97
<button class="scheduler-task-action"
98
@click="$store.schedulerStore.runTask(task.uuid)" title="Run Task">
webui/css/modals.css
-1
@@ -124,7 +124,6 @@ some classes like modal-header are shared between the old and the new system */
124
.modal-content {
125
padding: 0.5rem 1.5rem 0 1.5rem;
126
overflow-y: auto;
127
- overflow-x: hidden;
127
height: calc(90vh);
128
flex-grow: 1;
129
background-clip: border-box;
webui/css/settings.css
+79
-50
@@ -382,7 +382,7 @@ nav ul li a img {
382
border-spacing: 0;
383
white-space: nowrap;
384
padding-bottom: 8px;
385
- table-layout: auto;
385
+ table-layout: fixed;
386
}
387
388
.scheduler-task-list th,
@@ -393,41 +393,51 @@ nav ul li a img {
393
border-bottom: 1px solid var(--color-border);
394
}
395
396
-/* Ensure columns have proper min-width */
397
-.scheduler-task-list th:nth-child(1),
398
-.scheduler-task-list td:nth-child(1) {
399
- min-width: 150px;
400
- max-width: 200px;
396
+/* Column styles */
397
+.scheduler-task-list .col-name {
398
+ width: 30%;
399
overflow: hidden;
400
text-overflow: ellipsis;
401
}
402
405
-.scheduler-task-list th:nth-child(2),
406
-.scheduler-task-list td:nth-child(2) {
407
- min-width: 100px;
403
+.scheduler-task-list .col-state {
404
+ width: 12%;
405
}
406
410
-.scheduler-task-list th:nth-child(3),
411
-.scheduler-task-list td:nth-child(3) {
412
- min-width: 100px;
407
+.scheduler-task-list .col-project {
408
+ width: 20%;
409
}
410
415
-.scheduler-task-list th:nth-child(4),
416
-.scheduler-task-list td:nth-child(4) {
417
- min-width: 150px;
411
+.scheduler-task-list .col-lastrun {
412
+ width: 20%;
413
}
414
420
-.scheduler-task-list th:nth-child(5),
421
-.scheduler-task-list td:nth-child(5) {
422
- min-width: 180px;
415
+.scheduler-task-list .col-actions {
416
+ width: 18%;
417
+ text-align: right;
418
}
419
425
-.scheduler-task-list th:nth-child(6),
426
-.scheduler-task-list td:nth-child(6) {
427
- min-width: 160px;
428
- white-space: nowrap;
420
+/* Mobile: hide less important columns */
421
+@media (max-width: 768px) {
422
+ .scheduler-task-list .col-project,
423
+ .scheduler-task-list .col-lastrun {
424
+ display: none;
425
+ }
426
+
427
+ .scheduler-task-list .col-name {
428
+ width: 50%;
429
+ }
430
+
431
+ .scheduler-task-list .col-state {
432
+ width: 25%;
433
+ }
434
+
435
+ .scheduler-task-list .col-actions {
436
+ width: 25%;
437
+ }
438
}
439
440
+
441
/* Task actions container */
442
.scheduler-task-actions {
443
display: flex;
@@ -551,17 +561,6 @@ nav ul li a img {
561
background-color: var(--color-secondary);
562
}
563
554
-/* Adjust media queries to handle small screens */
555
-@media (max-width: 768px) {
556
- .scheduler-task-list {
557
- min-width: 700px;
558
- }
559
-
560
- .scheduler-detail-view {
561
- min-width: 650px;
562
- }
563
-}
564
-
564
/* Scrollbar styling for better visibility */
565
.section::-webkit-scrollbar {
566
height: 10px; /* Taller scrollbar for better usability */
@@ -579,14 +578,6 @@ nav ul li a img {
578
579
/* Mobile styles for scheduler sections */
580
@media (max-width: 1280px) {
582
- .scheduler-container {
583
- min-width: max-content; /* Allow expansion based on content */
584
- }
585
-
586
- .scheduler-task-list {
587
- min-width: max-content; /* Expand to fit content if needed */
588
- }
589
-
581
/* Scrollbar styling for mobile view */
582
.section::-webkit-scrollbar {
583
height: 10px;
@@ -634,15 +625,53 @@ nav ul li a img {
625
/* Scheduler detail header styling */
626
.scheduler-detail-header {
627
display: flex;
637
- justify-content: flex-start;
628
+ justify-content: space-between;
629
align-items: center;
630
flex-wrap: wrap;
631
gap: 10px;
632
width: 100%;
633
}
634
644
-.scheduler-detail-header .scheduler-detail-title {
645
- margin-right: auto;
635
+.scheduler-detail-header-left {
636
+ display: flex;
637
+ align-items: center;
638
+ gap: 10px;
639
+ flex-wrap: wrap;
640
+}
641
+
642
+.scheduler-detail-header-left .scheduler-detail-title {
643
+ margin: 0;
644
+}
645
+
646
+.scheduler-detail-header-actions {
647
+ display: flex;
648
+ align-items: center;
649
+ gap: 0.5rem;
650
+}
651
+
652
+.scheduler-detail-header .btn-action-header {
653
+ padding: 0.4rem 0.6rem;
654
+ display: flex;
655
+ align-items: center;
656
+ background: var(--color-bg-secondary);
657
+ border: 1px solid var(--color-border);
658
+ color: var(--color-text);
659
+ border-radius: var(--border-radius);
660
+ cursor: pointer;
661
+}
662
+
663
+.scheduler-detail-header .btn-action-header:hover {
664
+ border-color: var(--color-primary);
665
+ color: var(--color-primary);
666
+}
667
+
668
+.scheduler-detail-header .btn-action-header.danger:hover {
669
+ border-color: var(--color-accent);
670
+ color: var(--color-accent);
671
+}
672
+
673
+.scheduler-detail-header .btn-action-header .material-symbols-outlined {
674
+ font-size: 18px;
675
}
676
677
/* Responsive adjustments for headers */
@@ -658,14 +687,14 @@ nav ul li a img {
687
}
688
689
.scheduler-detail-header {
661
- flex-direction: row; /* Keep in row even on mobile */
662
- align-items: center;
663
- flex-wrap: wrap;
664
- gap: 0.5rem;
690
+ flex-direction: column;
691
+ align-items: flex-start;
692
+ gap: 0.75rem;
693
}
694
667
- .scheduler-detail-header .btn {
668
- margin-left: auto; /* Push to right edge */
695
+ .scheduler-detail-header-actions {
696
+ width: 100%;
697
+ justify-content: flex-end;
698
}
699
700
.scheduler-form-field {
webui/index.css
-32
@@ -1210,7 +1210,6 @@ nav ul li a img {
1210
border-collapse: collapse;
1211
margin: 1rem 0;
1212
font-size: 0.9rem;
1213
- overflow-x: auto;
1213
table-layout: fixed;
1214
}
1215
@@ -1225,37 +1224,6 @@ nav ul li a img {
1224
white-space: nowrap;
1225
}
1226
1228
-.scheduler-task-list th:nth-child(1), /* Name */
1229
-.scheduler-task-list td:nth-child(1) {
1230
- width: 25%;
1231
-}
1232
-
1233
-.scheduler-task-list th:nth-child(2), /* State */
1234
-.scheduler-task-list td:nth-child(2) {
1235
- width: 10%;
1236
-}
1237
-
1238
-.scheduler-task-list th:nth-child(3), /* Type */
1239
-.scheduler-task-list td:nth-child(3) {
1240
- width: 10%;
1241
-}
1242
-
1243
-.scheduler-task-list th:nth-child(4), /* Schedule */
1244
-.scheduler-task-list td:nth-child(4) {
1245
- width: 20%;
1246
-}
1247
-
1248
-.scheduler-task-list th:nth-child(5), /* Last Run */
1249
-.scheduler-task-list td:nth-child(5) {
1250
- width: 20%;
1251
-}
1252
-
1253
-.scheduler-task-list th:nth-child(6), /* Actions */
1254
-.scheduler-task-list td:nth-child(6) {
1255
- width: 15%;
1256
- text-align: right;
1257
-}
1258
-
1227
.scheduler-task-list th {
1228
background-color: var(--color-bg-secondary);
1229
font-weight: 500;