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;