standalone scheduler modal

3clyp50 committed Dec 26, 2025 at 13:52 UTC a6ea619568ad88624e496c4119b386dd0d0daf84
15 files changed +505 -490
webui/components/modals/scheduler/scheduler-modal-store.js new
+35
@@ -0,0 +1,35 @@
1 +import { createStore } from "/js/AlpineStore.js";
2 +import { store as schedulerStore } from "/components/modals/scheduler/scheduler-store.js";
3 +
4 +const model = {
5 + closePromise: null,
6 +
7 + async open(options = {}) {
8 + // Open modal
9 + this.closePromise = window.openModal("modals/scheduler/scheduler-modal.html");
10 +
11 + // Setup cleanup on modal close
12 + if (this.closePromise?.then) {
13 + this.closePromise.then(() => this.cleanup());
14 + }
15 +
16 + // Notify scheduler store that modal opened
17 + schedulerStore.onTabActivated?.();
18 +
19 + // If taskId provided, show detail after load
20 + if (options.taskId) {
21 + setTimeout(async () => {
22 + await schedulerStore.fetchTasks({ manual: true });
23 + schedulerStore.showTaskDetail(options.taskId);
24 + }, 200);
25 + }
26 + },
27 +
28 + cleanup() {
29 + schedulerStore.onModalClosed?.();
30 + this.closePromise = null;
31 + },
32 +};
33 +
34 +export const store = createStore("schedulerModal", model);
35 +
webui/components/modals/scheduler/scheduler-modal.html new
+27
@@ -0,0 +1,27 @@
1 +<html>
2 +<head>
3 + <title>Task Scheduler</title>
4 + <script type="module">
5 + import { store } from "/components/modals/scheduler/scheduler-modal-store.js";
6 + </script>
7 +</head>
8 +<body>
9 + <div x-data>
10 + <template x-if="$store.schedulerModal">
11 + <div class="scheduler-modal-content">
12 + <x-component path="modals/scheduler/scheduler-settings.html"></x-component>
13 + </div>
14 + </template>
15 + </div>
16 +
17 + <style>
18 + .scheduler-modal-content {
19 + display: flex;
20 + flex-direction: column;
21 + height: 100%;
22 + min-height: 400px;
23 + }
24 + </style>
25 +</body>
26 +</html>
27 +
webui/components/modals/scheduler/scheduler-settings.html new
+23
@@ -0,0 +1,23 @@
1 +<html>
2 +<head>
3 + <script type="module">
4 + import { store } from "/components/modals/scheduler/scheduler-store.js";
5 + </script>
6 +</head>
7 +<body>
8 +<div x-data>
9 + <template x-if="$store.schedulerStore">
10 + <div id="scheduler-settings-root">
11 + <div id="section-task-scheduler" class="section">
12 + <div class="section-title">Task Scheduler</div>
13 + <div class="section-description">Manage scheduled tasks and automated processes for Agent Zero.</div>
14 +
15 + <x-component path="modals/scheduler/scheduler-task-editor.html"></x-component>
16 + <x-component path="modals/scheduler/scheduler-task-list.html"></x-component>
17 + </div>
18 + </div>
19 + </template>
20 +</div>
21 +</body>
22 +</html>
23 +
webui/components/modals/scheduler/scheduler-store.js renamed
+1 -1
@@ -900,7 +900,7 @@ const schedulerStoreModel = {
900 }
901
902 this.selectedTaskForDetail = snapshot;
903 - const closePromise = window.openModal("settings/scheduler/scheduler-task-detail.html");
903 + const closePromise = window.openModal("modals/scheduler/scheduler-task-detail.html");
904 if (closePromise && typeof closePromise.then === "function") {
905 closePromise.then(() => {
906 if (this.selectedTaskForDetail?.uuid === snapshot.uuid) {
webui/components/modals/scheduler/scheduler-task-detail.html renamed
+2 -1
@@ -2,7 +2,7 @@
2 <head>
3 <title>Task Details</title>
4 <script type="module">
5 - import { store } from "/components/settings/scheduler/scheduler-store.js";
5 + import { store } from "/components/modals/scheduler/scheduler-store.js";
6 </script>
7 </head>
8 <body>
@@ -146,3 +146,4 @@
146 </div>
147 </body>
148 </html>
149 +
webui/components/modals/scheduler/scheduler-task-editor.html renamed
+2 -1
@@ -1,7 +1,7 @@
1 <html>
2 <head>
3 <script type="module">
4 - import { store } from "/components/settings/scheduler/scheduler-store.js";
4 + import { store } from "/components/modals/scheduler/scheduler-store.js";
5 </script>
6 </head>
7 <body>
@@ -437,3 +437,4 @@
437 </div>
438 </body>
439 </html>
440 +
webui/components/modals/scheduler/scheduler-task-list.html renamed
+2 -1
@@ -1,7 +1,7 @@
1 <html>
2 <head>
3 <script type="module">
4 - import { store } from "/components/settings/scheduler/scheduler-store.js";
4 + import { store } from "/components/modals/scheduler/scheduler-store.js";
5 </script>
6 </head>
7 <body>
@@ -133,3 +133,4 @@
133 </div>
134 </body>
135 </html>
136 +
webui/components/settings/scheduler/scheduler-settings.html deleted
-33
@@ -1,33 +0,0 @@
1 -<html>
2 -<head>
3 - <script type="module">
4 - import { store } from "/components/settings/scheduler/scheduler-store.js";
5 - </script>
6 -</head>
7 -<body>
8 -<div x-data>
9 - <template x-if="$store.schedulerStore">
10 - <div id="scheduler-settings-root">
11 - <nav>
12 - <ul>
13 - <li>
14 - <a href="#section-task-scheduler">
15 - <img src="/public/task_scheduler.svg" alt="Task Scheduler">
16 - <span>Task Scheduler</span>
17 - </a>
18 - </li>
19 - </ul>
20 - </nav>
21 -
22 - <div id="section-task-scheduler" class="section">
23 - <div class="section-title">Task Scheduler</div>
24 - <div class="section-description">Manage scheduled tasks and automated processes for Agent Zero.</div>
25 -
26 - <x-component path="settings/scheduler/scheduler-task-editor.html"></x-component>
27 - <x-component path="settings/scheduler/scheduler-task-list.html"></x-component>
28 - </div>
29 - </div>
30 - </template>
31 -</div>
32 -</body>
33 -</html>
webui/components/settings/settings-store.js
-36
@@ -1,7 +1,6 @@
1 import { createStore } from "/js/AlpineStore.js";
2 import * as API from "/js/api.js";
3 import { store as notificationStore } from "/components/notifications/notification-store.js";
4 -import { store as schedulerStore } from "/components/settings/scheduler/scheduler-store.js";
4
5 // Constants
6 const VIEW_MODE_STORAGE_KEY = "settingsActiveTab";
@@ -65,12 +64,6 @@ const model = {
64 },
65
66 cleanup() {
68 - // Notify scheduler if it was active
69 - if (this._activeTab === "scheduler") {
70 - schedulerStore.onTabDeactivated?.();
71 - }
72 - schedulerStore.onModalClosed?.();
73 -
67 this.settings = null;
68 this.additional = null;
69 this.error = null;
@@ -83,14 +76,6 @@ const model = {
76 try {
77 localStorage.setItem(VIEW_MODE_STORAGE_KEY, current);
78 } catch {}
86 -
87 - // Scheduler lifecycle
88 - if (previous === "scheduler" && current !== "scheduler") {
89 - schedulerStore.onTabDeactivated?.();
90 - }
91 - if (current === "scheduler" && previous !== "scheduler") {
92 - schedulerStore.onTabActivated?.();
93 - }
79 },
80
81 switchTab(tabName) {
@@ -183,27 +168,6 @@ const model = {
168 }
169 await window.openModal("settings/settings.html");
170 },
186 -
187 - // Scheduler integration: open Settings modal, switch to scheduler tab, show task detail
188 - async openSchedulerTaskDetail(taskId) {
189 - // Set tab to scheduler before opening
190 - this._activeTab = "scheduler";
191 -
192 - // Open the modal (do NOT await - openModal resolves on close)
193 - window.openModal("settings/settings.html");
194 -
195 - // Ensure scheduler tasks are loaded, then show the detail modal
196 - setTimeout(async () => {
197 - try {
198 - if (typeof schedulerStore.fetchTasks === "function") {
199 - await schedulerStore.fetchTasks({ manual: true });
200 - }
201 - schedulerStore.showTaskDetail?.(taskId);
202 - } catch (error) {
203 - console.warn("[settings-store] openSchedulerTaskDetail failed", error);
204 - }
205 - }, 200);
206 - },
171 };
172
173 const store = createStore("settingsStore", model);
webui/components/settings/settings.html
+1 -12
@@ -46,10 +46,6 @@
46 :class="{'active': $store.settingsStore.activeTab === 'developer'}"
47 @click="$store.settingsStore.switchTab('developer')"
48 title="Developer">Developer</div>
49 - <div class="settings-tab"
50 - :class="{'active': $store.settingsStore.activeTab === 'scheduler'}"
51 - @click="$store.settingsStore.switchTab('scheduler')"
52 - title="Task Scheduler">Task Scheduler</div>
49 <div class="settings-tab"
50 :class="{'active': $store.settingsStore.activeTab === 'backup'}"
51 @click="$store.settingsStore.switchTab('backup')"
@@ -58,7 +54,7 @@
54 </div>
55
56 <!-- Settings sections for agent, external, developer, mcp, backup tabs -->
61 - <div id="settings-sections" x-show="$store.settingsStore.activeTab !== 'scheduler'">
57 + <div id="settings-sections">
58 <template x-if="$store.settingsStore.activeTab === 'agent'">
59 <x-component path="settings/agent/agent-settings.html"></x-component>
60 </template>
@@ -75,13 +71,6 @@
71 <x-component path="settings/backup/backup-settings.html"></x-component>
72 </template>
73 </div>
78 -
79 - <!-- Task Scheduler Tab Content -->
80 - <template x-if="$store.settingsStore.activeTab === 'scheduler'">
81 - <div id="scheduler-tab-content">
82 - <x-component path="settings/scheduler/scheduler-settings.html"></x-component>
83 - </div>
84 - </template>
74 </div>
75
76 <!-- Footer -->
webui/components/sidebar/tasks/tasks-store.js
+4 -5
@@ -1,7 +1,6 @@
1 import { createStore } from "/js/AlpineStore.js";
2 import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";
3 -import { store as schedulerStore } from "/components/settings/scheduler/scheduler-store.js";
4 -import { store as settingsStore } from "/components/settings/settings-store.js";
3 +import { store as schedulerStore } from "/components/modals/scheduler/scheduler-store.js";
4
5 // Tasks sidebar store: tasks list and selected task id
6 const model = {
@@ -52,9 +51,9 @@ const model = {
51 },
52
53 openDetail(taskId) {
55 - // Use the new settings modal store to open scheduler task detail
56 - if (settingsStore?.openSchedulerTaskDetail) {
57 - settingsStore.openSchedulerTaskDetail(taskId);
54 + // Open lightweight task detail popup directly
55 + if (schedulerStore?.showTaskDetail) {
56 + schedulerStore.showTaskDetail(taskId);
57 }
58 },
59
webui/components/sidebar/top-section/quick-actions.html
+8 -1
@@ -4,8 +4,10 @@
4 <script type="module">
5 import { store } from "/components/sidebar/chats/chats-store.js";
6 import { store as settingsStore } from "/components/settings/settings-store.js";
7 + import { store as schedulerModalStore } from "/components/modals/scheduler/scheduler-modal-store.js";
8 // Expose for Alpine template access
9 globalThis.openSettingsModal = () => settingsStore.open();
10 + globalThis.openSchedulerModal = () => schedulerModalStore.open();
11 </script>
12 </head>
13
@@ -26,6 +28,10 @@
28 d="m50.301 66.5c-9 0-16.398-7.3008-16.398-16.398 0-9.1016 7.3008-16.398 16.398-16.398 9.1016 0 16.398 7.3008 16.398 16.398 0 9.0977-7.3984 16.398-16.398 16.398zm0-25.5c-5 0-9.1016 4.1016-9.1016 9.1016s4.1016 9.1016 9.1016 9.1016 9.1016-4.1016 9.1016-9.1016c-0.003906-5-4.1016-9.1016-9.1016-9.1016z">
29 </path>
30 </svg>Settings</button>
31 + <button class="config-button" id="scheduler" @click="openSchedulerModal()">
32 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
33 + <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z"/>
34 + </svg>Scheduler</button>
35 <button class="config-button" id="memory-dash"
36 @click="openModal('settings/memory/memory-dashboard.html');">Memory</button>
37 <button class="config-button" id="dashboard" @click="deselectChat()">Dashboard</button>
@@ -61,7 +67,8 @@
67 opacity: 0.5;
68 }
69
64 - #settings {
70 + #settings,
71 + #scheduler {
72 display: flex;
73 align-items: center;
74 }
webui/css/scheduler.css new
+399
@@ -0,0 +1,399 @@
1 +.scheduler-header {
2 + display: flex;
3 + justify-content: space-between;
4 + align-items: center;
5 + margin-bottom: 20px;
6 +}
7 +
8 +.scheduler-header h2 {
9 + margin: 0;
10 + font-size: 1.2rem;
11 + font-weight: 500;
12 +}
13 +
14 +.scheduler-filters {
15 + display: flex;
16 + gap: 20px;
17 + margin-bottom: 20px;
18 + flex-wrap: wrap;
19 +}
20 +
21 +.scheduler-filter-group {
22 + display: flex;
23 + align-items: center;
24 + gap: 8px;
25 +}
26 +
27 +.scheduler-filter-label {
28 + font-weight: 500;
29 + color: var(--color-text-secondary);
30 +}
31 +
32 +.scheduler-filter-select {
33 + padding: 6px 8px;
34 + border-radius: 4px;
35 + border: 1px solid var(--color-border);
36 + background-color: var(--color-bg-secondary);
37 + color: var(--color-text);
38 +}
39 +
40 +.scheduler-task-list {
41 + width: 100%;
42 + border-collapse: collapse;
43 + margin: 1rem 0;
44 + font-size: 0.9rem;
45 + table-layout: fixed;
46 +}
47 +
48 +.scheduler-task-list th,
49 +.scheduler-task-list td {
50 + padding: 0.75rem;
51 + text-align: left;
52 + border-bottom: 1px solid var(--color-border);
53 + vertical-align: middle;
54 + overflow: hidden;
55 + text-overflow: ellipsis;
56 + white-space: nowrap;
57 +}
58 +
59 +.scheduler-task-list th {
60 + background-color: var(--color-bg-secondary);
61 + font-weight: 500;
62 + cursor: pointer;
63 + user-select: none;
64 +}
65 +
66 +.scheduler-task-list th:hover {
67 + background-color: var(--color-bg-tertiary);
68 +}
69 +
70 +.scheduler-task-actions {
71 + display: flex;
72 + gap: 8px;
73 +}
74 +
75 +.scheduler-task-action {
76 + padding: 4px;
77 + background: none;
78 + border: none;
79 + color: var(--color-text-secondary);
80 + cursor: pointer;
81 + border-radius: 4px;
82 +}
83 +
84 +.scheduler-task-action:hover {
85 + background-color: var(--color-bg-tertiary);
86 + color: var(--color-text);
87 +}
88 +
89 +.scheduler-status-badge {
90 + display: inline-block;
91 + padding: 4px 8px;
92 + border-radius: 4px;
93 + font-size: 12px;
94 + font-weight: 500;
95 + text-transform: capitalize;
96 + white-space: nowrap;
97 +}
98 +
99 +.scheduler-status-idle {
100 + background-color: rgba(0, 100, 0, 0.2);
101 + color: #2a9d8f; /* Dark green that works with both light and dark themes */
102 + border: 1px solid rgba(42, 157, 143, 0.3);
103 +}
104 +
105 +.scheduler-status-running {
106 + background-color: rgba(0, 60, 120, 0.2);
107 + color: #4361ee; /* Dark blue that works with both light and dark themes */
108 + border: 1px solid rgba(67, 97, 238, 0.3);
109 +}
110 +
111 +.scheduler-status-disabled {
112 + background-color: rgba(70, 70, 70, 0.2);
113 + color: #6c757d; /* Dark grey that works with both light and dark themes */
114 + border: 1px solid rgba(108, 117, 125, 0.3);
115 +}
116 +
117 +.scheduler-status-error {
118 + background-color: rgba(120, 0, 0, 0.2);
119 + color: #e63946; /* Dark red that works with both light and dark themes */
120 + border: 1px solid rgba(230, 57, 70, 0.3);
121 +}
122 +
123 +/* Light mode adjustments */
124 +.light-mode .scheduler-status-idle {
125 + background-color: rgba(42, 157, 143, 0.1);
126 + color: #1a6f65; /* Darker green for light mode */
127 +}
128 +
129 +.light-mode .scheduler-status-running {
130 + background-color: rgba(67, 97, 238, 0.1);
131 + color: #2540b3; /* Darker blue for light mode */
132 +}
133 +
134 +.light-mode .scheduler-status-disabled {
135 + background-color: rgba(108, 117, 125, 0.1);
136 + color: #495057; /* Darker grey for light mode */
137 +}
138 +
139 +.light-mode .scheduler-status-error {
140 + background-color: rgba(230, 57, 70, 0.1);
141 + color: #c5283d; /* Darker red for light mode */
142 +}
143 +
144 +.scheduler-empty {
145 + text-align: center;
146 + padding: 40px 0;
147 + color: var(--color-text-secondary);
148 +}
149 +
150 +.scheduler-empty-icon {
151 + font-size: 32px;
152 + margin-bottom: 10px;
153 +}
154 +
155 +.scheduler-empty-text {
156 + margin-bottom: 20px;
157 +}
158 +
159 +.scheduler-loading {
160 + text-align: center;
161 + padding: 40px 0;
162 + color: var(--color-text-secondary);
163 +}
164 +
165 +.scheduler-task-details {
166 + padding: 16px;
167 + background-color: var(--color-bg-secondary);
168 + border-radius: 4px;
169 +}
170 +
171 +.scheduler-details-grid {
172 + display: grid;
173 + grid-template-columns: 120px 1fr;
174 + gap: 8px 16px;
175 + margin-bottom: 16px;
176 +}
177 +
178 +.scheduler-details-label {
179 + font-weight: 500;
180 + color: var(--color-text-secondary);
181 + display: flex;
182 + align-items: center;
183 +}
184 +
185 +.scheduler-details-value {
186 + color: var(--color-text);
187 + word-break: break-word;
188 +}
189 +
190 +.scheduler-details-actions {
191 + display: flex;
192 + justify-content: flex-end;
193 +}
194 +
195 +.scheduler-form {
196 + background-color: var(--color-bg-secondary);
197 + border-radius: 4px;
198 + padding: 20px;
199 + margin-bottom: 20px;
200 +}
201 +
202 +.scheduler-form-title {
203 + font-size: 1.2rem;
204 + font-weight: 500;
205 + margin-bottom: 20px;
206 + padding-bottom: 10px;
207 + border-bottom: 1px solid var(--color-border);
208 +}
209 +
210 +.scheduler-form-grid {
211 + display: grid;
212 + grid-template-columns: 1fr 1fr;
213 + gap: 16px;
214 + margin-bottom: 20px;
215 +}
216 +
217 +.scheduler-form-field {
218 + display: flex;
219 + flex-direction: column;
220 + gap: 6px;
221 +}
222 +
223 +.full-width {
224 + grid-column: 1 / -1;
225 +}
226 +
227 +.scheduler-form-label {
228 + font-weight: 500;
229 +}
230 +
231 +.scheduler-form-help {
232 + font-size: 12px;
233 + color: var(--color-text-secondary);
234 +}
235 +
236 +.scheduler-form-actions {
237 + display: flex;
238 + justify-content: flex-end;
239 + gap: 12px;
240 +}
241 +
242 +.scheduler-schedule-builder {
243 + display: grid;
244 + grid-template-columns: repeat(5, 1fr);
245 + gap: 12px;
246 +}
247 +
248 +.scheduler-schedule-field {
249 + display: flex;
250 + flex-direction: column;
251 + gap: 4px;
252 + max-width: 70px; /* Limit width of schedule fields */
253 +}
254 +
255 +.scheduler-schedule-field input {
256 + width: 100%;
257 + min-width: 0; /* Allow shrinking below content size */
258 + font-size: 0.9rem; /* Slightly reduce font size for better fit */
259 +}
260 +
261 +.scheduler-schedule-label {
262 + font-size: 12px;
263 + color: var(--color-text-secondary);
264 +}
265 +
266 +.input-group {
267 + display: flex;
268 + gap: 8px;
269 +}
270 +
271 +/* Sort indicators */
272 +.scheduler-sort-indicator {
273 + display: inline-block;
274 + margin-left: 4px;
275 + transition: transform 0.2s ease;
276 +}
277 +
278 +.scheduler-sort-desc {
279 + transform: rotate(180deg);
280 +}
281 +
282 +/* Responsive adjustments */
283 +@media (max-width: 768px) {
284 + .scheduler-form-grid {
285 + grid-template-columns: 1fr;
286 + }
287 +
288 + .scheduler-schedule-builder {
289 + grid-template-columns: 1fr 1fr;
290 + }
291 +
292 + .scheduler-filters {
293 + flex-direction: column;
294 + gap: 12px;
295 + }
296 +
297 + .scheduler-task-actions {
298 + flex-wrap: wrap;
299 + }
300 +}
301 +
302 +@media (max-width: 480px) {
303 + nav ul li a {
304 + flex-direction: row;
305 + justify-content: flex-start;
306 + gap: 1rem;
307 + padding: 0.75rem 1rem;
308 + }
309 +
310 + nav ul li a img {
311 + margin-bottom: 0;
312 + width: 30px;
313 + height: 30px;
314 + }
315 +}
316 +
317 +/* Add row hover effect for task list rows matching left panel hover */
318 +.scheduler-task-list tbody tr {
319 + cursor: pointer;
320 + transition: background-color 0.2s ease;
321 +}
322 +
323 +.scheduler-task-list tbody tr:hover {
324 + background-color: rgba(255, 255, 255, 0.03);
325 +}
326 +
327 +.light-mode .scheduler-task-list tbody tr:hover {
328 + background-color: rgba(0, 0, 0, 0.02);
329 +}
330 +
331 +.scheduler-task-list th {
332 + background-color: var(--color-bg-secondary);
333 + font-weight: 500;
334 + cursor: pointer;
335 + user-select: none;
336 +}
337 +
338 +.scheduler-task-list th:hover {
339 + background-color: var(--color-bg-tertiary);
340 +}
341 +
342 +/* Task detail view styling */
343 +.scheduler-detail-view {
344 + background-color: var(--color-bg-secondary);
345 + border-radius: 4px;
346 + padding: 20px;
347 + margin-bottom: 20px;
348 + animation: fadeIn 0.3s ease;
349 +}
350 +
351 +.scheduler-detail-header {
352 + display: flex;
353 + justify-content: flex-start;
354 + align-items: center;
355 + margin-bottom: 20px;
356 + padding-bottom: 10px;
357 + border-bottom: 1px solid var(--color-border);
358 + flex-wrap: wrap;
359 + gap: 10px;
360 +}
361 +
362 +.scheduler-detail-header .scheduler-detail-title {
363 + font-size: 1.4rem;
364 + font-weight: 500;
365 + margin: 0;
366 + margin-right: auto;
367 +}
368 +
369 +.scheduler-detail-header .scheduler-status-badge {
370 + margin-right: 10px;
371 +}
372 +
373 +.scheduler-detail-content {
374 + margin-bottom: 20px;
375 +}
376 +
377 +/* Task Scheduler Styles */
378 +
379 +.scheduler-no-schedule {
380 + color: var(--color-text-secondary);
381 + opacity: 0.7;
382 + font-style: italic;
383 +}
384 +
385 +.task-container-vertical {
386 + display: flex;
387 + flex-direction: column;
388 + width: 100%;
389 + gap: 6px;
390 +}
391 +
392 +/* Smaller status badge for task list */
393 +.scheduler-status-badge-small {
394 + font-size: 10px;
395 + padding: 2px 6px;
396 + margin-right: 5px;
397 + min-width: 40px;
398 + text-align: center;
399 +}
webui/index.css
-399
@@ -1166,405 +1166,6 @@ nav ul li a img {
1166 color: var(--color-text-secondary);
1167 }
1168
1169 -.scheduler-header {
1170 - display: flex;
1171 - justify-content: space-between;
1172 - align-items: center;
1173 - margin-bottom: 20px;
1174 -}
1175 -
1176 -.scheduler-header h2 {
1177 - margin: 0;
1178 - font-size: 1.2rem;
1179 - font-weight: 500;
1180 -}
1181 -
1182 -.scheduler-filters {
1183 - display: flex;
1184 - gap: 20px;
1185 - margin-bottom: 20px;
1186 - flex-wrap: wrap;
1187 -}
1188 -
1189 -.scheduler-filter-group {
1190 - display: flex;
1191 - align-items: center;
1192 - gap: 8px;
1193 -}
1194 -
1195 -.scheduler-filter-label {
1196 - font-weight: 500;
1197 - color: var(--color-text-secondary);
1198 -}
1199 -
1200 -.scheduler-filter-select {
1201 - padding: 6px 8px;
1202 - border-radius: 4px;
1203 - border: 1px solid var(--color-border);
1204 - background-color: var(--color-bg-secondary);
1205 - color: var(--color-text);
1206 -}
1207 -
1208 -.scheduler-task-list {
1209 - width: 100%;
1210 - border-collapse: collapse;
1211 - margin: 1rem 0;
1212 - font-size: 0.9rem;
1213 - table-layout: fixed;
1214 -}
1215 -
1216 -.scheduler-task-list th,
1217 -.scheduler-task-list td {
1218 - padding: 0.75rem;
1219 - text-align: left;
1220 - border-bottom: 1px solid var(--color-border);
1221 - vertical-align: middle;
1222 - overflow: hidden;
1223 - text-overflow: ellipsis;
1224 - white-space: nowrap;
1225 -}
1226 -
1227 -.scheduler-task-list th {
1228 - background-color: var(--color-bg-secondary);
1229 - font-weight: 500;
1230 - cursor: pointer;
1231 - user-select: none;
1232 -}
1233 -
1234 -.scheduler-task-list th:hover {
1235 - background-color: var(--color-bg-tertiary);
1236 -}
1237 -
1238 -.scheduler-task-actions {
1239 - display: flex;
1240 - gap: 8px;
1241 -}
1242 -
1243 -.scheduler-task-action {
1244 - padding: 4px;
1245 - background: none;
1246 - border: none;
1247 - color: var(--color-text-secondary);
1248 - cursor: pointer;
1249 - border-radius: 4px;
1250 -}
1251 -
1252 -.scheduler-task-action:hover {
1253 - background-color: var(--color-bg-tertiary);
1254 - color: var(--color-text);
1255 -}
1256 -
1257 -.scheduler-status-badge {
1258 - display: inline-block;
1259 - padding: 4px 8px;
1260 - border-radius: 4px;
1261 - font-size: 12px;
1262 - font-weight: 500;
1263 - text-transform: capitalize;
1264 - white-space: nowrap;
1265 -}
1266 -
1267 -.scheduler-status-idle {
1268 - background-color: rgba(0, 100, 0, 0.2);
1269 - color: #2a9d8f; /* Dark green that works with both light and dark themes */
1270 - border: 1px solid rgba(42, 157, 143, 0.3);
1271 -}
1272 -
1273 -.scheduler-status-running {
1274 - background-color: rgba(0, 60, 120, 0.2);
1275 - color: #4361ee; /* Dark blue that works with both light and dark themes */
1276 - border: 1px solid rgba(67, 97, 238, 0.3);
1277 -}
1278 -
1279 -.scheduler-status-disabled {
1280 - background-color: rgba(70, 70, 70, 0.2);
1281 - color: #6c757d; /* Dark grey that works with both light and dark themes */
1282 - border: 1px solid rgba(108, 117, 125, 0.3);
1283 -}
1284 -
1285 -.scheduler-status-error {
1286 - background-color: rgba(120, 0, 0, 0.2);
1287 - color: #e63946; /* Dark red that works with both light and dark themes */
1288 - border: 1px solid rgba(230, 57, 70, 0.3);
1289 -}
1290 -
1291 -/* Light mode adjustments */
1292 -.light-mode .scheduler-status-idle {
1293 - background-color: rgba(42, 157, 143, 0.1);
1294 - color: #1a6f65; /* Darker green for light mode */
1295 -}
1296 -
1297 -.light-mode .scheduler-status-running {
1298 - background-color: rgba(67, 97, 238, 0.1);
1299 - color: #2540b3; /* Darker blue for light mode */
1300 -}
1301 -
1302 -.light-mode .scheduler-status-disabled {
1303 - background-color: rgba(108, 117, 125, 0.1);
1304 - color: #495057; /* Darker grey for light mode */
1305 -}
1306 -
1307 -.light-mode .scheduler-status-error {
1308 - background-color: rgba(230, 57, 70, 0.1);
1309 - color: #c5283d; /* Darker red for light mode */
1310 -}
1311 -
1312 -.scheduler-empty {
1313 - text-align: center;
1314 - padding: 40px 0;
1315 - color: var(--color-text-secondary);
1316 -}
1317 -
1318 -.scheduler-empty-icon {
1319 - font-size: 32px;
1320 - margin-bottom: 10px;
1321 -}
1322 -
1323 -.scheduler-empty-text {
1324 - margin-bottom: 20px;
1325 -}
1326 -
1327 -.scheduler-loading {
1328 - text-align: center;
1329 - padding: 40px 0;
1330 - color: var(--color-text-secondary);
1331 -}
1332 -
1333 -.scheduler-task-details {
1334 - padding: 16px;
1335 - background-color: var(--color-bg-secondary);
1336 - border-radius: 4px;
1337 -}
1338 -
1339 -.scheduler-details-grid {
1340 - display: grid;
1341 - grid-template-columns: 120px 1fr;
1342 - gap: 8px 16px;
1343 - margin-bottom: 16px;
1344 -}
1345 -
1346 -.scheduler-details-label {
1347 - font-weight: 500;
1348 - color: var(--color-text-secondary);
1349 - display: flex;
1350 - align-items: center;
1351 -}
1352 -
1353 -.scheduler-details-value {
1354 - color: var(--color-text);
1355 - word-break: break-word;
1356 -}
1357 -
1358 -.scheduler-details-actions {
1359 - display: flex;
1360 - justify-content: flex-end;
1361 -}
1362 -
1363 -.scheduler-form {
1364 - background-color: var(--color-bg-secondary);
1365 - border-radius: 4px;
1366 - padding: 20px;
1367 - margin-bottom: 20px;
1368 -}
1369 -
1370 -.scheduler-form-title {
1371 - font-size: 1.2rem;
1372 - font-weight: 500;
1373 - margin-bottom: 20px;
1374 - padding-bottom: 10px;
1375 - border-bottom: 1px solid var(--color-border);
1376 -}
1377 -
1378 -.scheduler-form-grid {
1379 - display: grid;
1380 - grid-template-columns: 1fr 1fr;
1381 - gap: 16px;
1382 - margin-bottom: 20px;
1383 -}
1384 -
1385 -.scheduler-form-field {
1386 - display: flex;
1387 - flex-direction: column;
1388 - gap: 6px;
1389 -}
1390 -
1391 -.full-width {
1392 - grid-column: 1 / -1;
1393 -}
1394 -
1395 -.scheduler-form-label {
1396 - font-weight: 500;
1397 -}
1398 -
1399 -.scheduler-form-help {
1400 - font-size: 12px;
1401 - color: var(--color-text-secondary);
1402 -}
1403 -
1404 -.scheduler-form-actions {
1405 - display: flex;
1406 - justify-content: flex-end;
1407 - gap: 12px;
1408 -}
1409 -
1410 -.scheduler-schedule-builder {
1411 - display: grid;
1412 - grid-template-columns: repeat(5, 1fr);
1413 - gap: 12px;
1414 -}
1415 -
1416 -.scheduler-schedule-field {
1417 - display: flex;
1418 - flex-direction: column;
1419 - gap: 4px;
1420 - max-width: 70px; /* Limit width of schedule fields */
1421 -}
1422 -
1423 -.scheduler-schedule-field input {
1424 - width: 100%;
1425 - min-width: 0; /* Allow shrinking below content size */
1426 - font-size: 0.9rem; /* Slightly reduce font size for better fit */
1427 -}
1428 -
1429 -.scheduler-schedule-label {
1430 - font-size: 12px;
1431 - color: var(--color-text-secondary);
1432 -}
1433 -
1434 -.input-group {
1435 - display: flex;
1436 - gap: 8px;
1437 -}
1438 -
1439 -/* Sort indicators */
1440 -.scheduler-sort-indicator {
1441 - display: inline-block;
1442 - margin-left: 4px;
1443 - transition: transform 0.2s ease;
1444 -}
1445 -
1446 -.scheduler-sort-desc {
1447 - transform: rotate(180deg);
1448 -}
1449 -
1450 -/* Responsive adjustments */
1451 -@media (max-width: 768px) {
1452 - .scheduler-form-grid {
1453 - grid-template-columns: 1fr;
1454 - }
1455 -
1456 - .scheduler-schedule-builder {
1457 - grid-template-columns: 1fr 1fr;
1458 - }
1459 -
1460 - .scheduler-filters {
1461 - flex-direction: column;
1462 - gap: 12px;
1463 - }
1464 -
1465 - .scheduler-task-actions {
1466 - flex-wrap: wrap;
1467 - }
1468 -}
1469 -
1470 -@media (max-width: 480px) {
1471 - nav ul li a {
1472 - flex-direction: row;
1473 - justify-content: flex-start;
1474 - gap: 1rem;
1475 - padding: 0.75rem 1rem;
1476 - }
1477 -
1478 - nav ul li a img {
1479 - margin-bottom: 0;
1480 - width: 30px;
1481 - height: 30px;
1482 - }
1483 -}
1484 -
1485 -/* Add row hover effect for task list rows matching left panel hover */
1486 -.scheduler-task-list tbody tr {
1487 - cursor: pointer;
1488 - transition: background-color 0.2s ease;
1489 -}
1490 -
1491 -.scheduler-task-list tbody tr:hover {
1492 - background-color: rgba(255, 255, 255, 0.03);
1493 -}
1494 -
1495 -.light-mode .scheduler-task-list tbody tr:hover {
1496 - background-color: rgba(0, 0, 0, 0.02);
1497 -}
1498 -
1499 -.scheduler-task-list th {
1500 - background-color: var(--color-bg-secondary);
1501 - font-weight: 500;
1502 - cursor: pointer;
1503 - user-select: none;
1504 -}
1505 -
1506 -.scheduler-task-list th:hover {
1507 - background-color: var(--color-bg-tertiary);
1508 -}
1509 -
1510 -/* Task detail view styling */
1511 -.scheduler-detail-view {
1512 - background-color: var(--color-bg-secondary);
1513 - border-radius: 4px;
1514 - padding: 20px;
1515 - margin-bottom: 20px;
1516 - animation: fadeIn 0.3s ease;
1517 -}
1518 -
1519 -.scheduler-detail-header {
1520 - display: flex;
1521 - justify-content: flex-start;
1522 - align-items: center;
1523 - margin-bottom: 20px;
1524 - padding-bottom: 10px;
1525 - border-bottom: 1px solid var(--color-border);
1526 - flex-wrap: wrap;
1527 - gap: 10px;
1528 -}
1529 -
1530 -.scheduler-detail-header .scheduler-detail-title {
1531 - font-size: 1.4rem;
1532 - font-weight: 500;
1533 - margin: 0;
1534 - margin-right: auto;
1535 -}
1536 -
1537 -.scheduler-detail-header .scheduler-status-badge {
1538 - margin-right: 10px;
1539 -}
1540 -
1541 -.scheduler-detail-content {
1542 - margin-bottom: 20px;
1543 -}
1544 -
1545 -/* Task Scheduler Styles */
1546 -
1547 -.scheduler-no-schedule {
1548 - color: var(--color-text-secondary);
1549 - opacity: 0.7;
1550 - font-style: italic;
1551 -}
1552 -
1553 -.task-container-vertical {
1554 - display: flex;
1555 - flex-direction: column;
1556 - width: 100%;
1557 - gap: 6px;
1558 -}
1559 -
1560 -/* Smaller status badge for task list */
1561 -.scheduler-status-badge-small {
1562 - font-size: 10px;
1563 - padding: 2px 6px;
1564 - margin-right: 5px;
1565 - min-width: 40px;
1566 - text-align: center;
1567 -}
1169 .icon {
1170 vertical-align: middle;
1171 }
webui/index.html
+1
@@ -14,6 +14,7 @@
14 <link rel="stylesheet" href="css/modals.css">
15 <link rel="stylesheet" href="css/speech.css">
16 <link rel="stylesheet" href="css/scheduler-datepicker.css">
17 + <link rel="stylesheet" href="css/scheduler.css">
18 <link rel="stylesheet" href="css/notification.css">
19 <link rel="stylesheet" href="css/buttons.css">
20