@mongoosejs/studio 0.3.0 → 0.3.1
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.
- package/backend/actions/ChatThread/createChatMessage.js +2 -0
- package/backend/actions/ChatThread/streamChatMessage.js +2 -0
- package/backend/actions/Dashboard/updateDashboard.js +2 -2
- package/backend/actions/Task/getTaskOverview.js +102 -0
- package/backend/actions/Task/getTasks.js +85 -45
- package/backend/actions/Task/index.js +1 -0
- package/frontend/public/app.js +309 -145
- package/frontend/public/tw.css +82 -0
- package/frontend/src/_util/dateRange.js +82 -0
- package/frontend/src/ace-editor/ace-editor.js +6 -0
- package/frontend/src/api.js +6 -0
- package/frontend/src/chat/chat-message-script/chat-message-script.html +11 -16
- package/frontend/src/chat/chat-message-script/chat-message-script.js +0 -6
- package/frontend/src/dashboard/dashboard.js +10 -1
- package/frontend/src/dashboard/edit-dashboard/edit-dashboard.js +4 -3
- package/frontend/src/dashboard-result/dashboard-result.js +3 -0
- package/frontend/src/dashboard-result/dashboard-table/dashboard-table.html +34 -0
- package/frontend/src/dashboard-result/dashboard-table/dashboard-table.js +37 -0
- package/frontend/src/models/models.js +8 -3
- package/frontend/src/task-by-name/task-by-name.html +77 -7
- package/frontend/src/task-by-name/task-by-name.js +84 -9
- package/frontend/src/tasks/task-details/task-details.html +8 -8
- package/frontend/src/tasks/task-details/task-details.js +2 -1
- package/frontend/src/tasks/tasks.js +25 -118
- package/package.json +2 -1
package/frontend/public/app.js
CHANGED
|
@@ -29,6 +29,99 @@ module.exports = {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
/***/ },
|
|
33
|
+
|
|
34
|
+
/***/ "./frontend/src/_util/dateRange.js"
|
|
35
|
+
/*!*****************************************!*\
|
|
36
|
+
!*** ./frontend/src/_util/dateRange.js ***!
|
|
37
|
+
\*****************************************/
|
|
38
|
+
(module) {
|
|
39
|
+
|
|
40
|
+
"use strict";
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
const DATE_FILTERS = [
|
|
44
|
+
{ value: 'last_hour', label: 'Last Hour' },
|
|
45
|
+
{ value: 'today', label: 'Today' },
|
|
46
|
+
{ value: 'yesterday', label: 'Yesterday' },
|
|
47
|
+
{ value: 'thisWeek', label: 'This Week' },
|
|
48
|
+
{ value: 'lastWeek', label: 'Last Week' },
|
|
49
|
+
{ value: 'thisMonth', label: 'This Month' },
|
|
50
|
+
{ value: 'lastMonth', label: 'Last Month' }
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const DATE_FILTER_VALUES = DATE_FILTERS.map(f => f.value);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Returns { start, end } Date objects for a given range key (e.g. 'last_hour', 'today').
|
|
57
|
+
* Month ranges use UTC boundaries.
|
|
58
|
+
* @param {string} selectedRange - One of DATE_FILTER_VALUES
|
|
59
|
+
* @returns {{ start: Date, end: Date }}
|
|
60
|
+
*/
|
|
61
|
+
function getDateRangeForRange(selectedRange) {
|
|
62
|
+
const now = new Date();
|
|
63
|
+
let start, end;
|
|
64
|
+
switch (selectedRange) {
|
|
65
|
+
case 'last_hour':
|
|
66
|
+
start = new Date();
|
|
67
|
+
start.setHours(start.getHours() - 1);
|
|
68
|
+
end = new Date();
|
|
69
|
+
break;
|
|
70
|
+
case 'today':
|
|
71
|
+
start = new Date();
|
|
72
|
+
start.setHours(0, 0, 0, 0);
|
|
73
|
+
end = new Date();
|
|
74
|
+
end.setHours(23, 59, 59, 999);
|
|
75
|
+
break;
|
|
76
|
+
case 'yesterday':
|
|
77
|
+
start = new Date(now);
|
|
78
|
+
start.setDate(start.getDate() - 1);
|
|
79
|
+
start.setHours(0, 0, 0, 0);
|
|
80
|
+
end = new Date(start);
|
|
81
|
+
end.setHours(23, 59, 59, 999);
|
|
82
|
+
break;
|
|
83
|
+
case 'thisWeek':
|
|
84
|
+
start = new Date(now.getTime() - (7 * 86400000));
|
|
85
|
+
start.setHours(0, 0, 0, 0);
|
|
86
|
+
end = new Date();
|
|
87
|
+
end.setHours(23, 59, 59, 999);
|
|
88
|
+
break;
|
|
89
|
+
case 'lastWeek':
|
|
90
|
+
start = new Date(now.getTime() - (14 * 86400000));
|
|
91
|
+
start.setHours(0, 0, 0, 0);
|
|
92
|
+
end = new Date(now.getTime() - (7 * 86400000));
|
|
93
|
+
end.setHours(23, 59, 59, 999);
|
|
94
|
+
break;
|
|
95
|
+
case 'thisMonth': {
|
|
96
|
+
const y = now.getUTCFullYear();
|
|
97
|
+
const m = now.getUTCMonth();
|
|
98
|
+
start = new Date(Date.UTC(y, m, 1, 0, 0, 0, 0));
|
|
99
|
+
end = new Date(Date.UTC(y, m + 1, 0, 23, 59, 59, 999));
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case 'lastMonth': {
|
|
103
|
+
const y = now.getUTCFullYear();
|
|
104
|
+
const m = now.getUTCMonth();
|
|
105
|
+
start = new Date(Date.UTC(y, m - 1, 1, 0, 0, 0, 0));
|
|
106
|
+
end = new Date(Date.UTC(y, m, 0, 23, 59, 59, 999));
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
start = new Date();
|
|
111
|
+
start.setHours(start.getHours() - 1);
|
|
112
|
+
end = new Date();
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
return { start, end };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = {
|
|
119
|
+
DATE_FILTERS,
|
|
120
|
+
DATE_FILTER_VALUES,
|
|
121
|
+
getDateRangeForRange
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
|
|
32
125
|
/***/ },
|
|
33
126
|
|
|
34
127
|
/***/ "./frontend/src/_util/deepEqual.js"
|
|
@@ -424,6 +517,12 @@ module.exports = app => app.component('ace-editor', {
|
|
|
424
517
|
}
|
|
425
518
|
},
|
|
426
519
|
methods: {
|
|
520
|
+
getValue() {
|
|
521
|
+
if (this.editor) {
|
|
522
|
+
return this.editor.getValue();
|
|
523
|
+
}
|
|
524
|
+
return this.modelValue !== '' ? this.modelValue : this.value;
|
|
525
|
+
},
|
|
427
526
|
setValue(val) {
|
|
428
527
|
if (this.editor) {
|
|
429
528
|
this.editor.setValue(val ?? '', -1);
|
|
@@ -709,6 +808,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
709
808
|
getTasks: function getTasks(params) {
|
|
710
809
|
return client.post('', { action: 'Task.getTasks', ...params }).then(res => res.data);
|
|
711
810
|
},
|
|
811
|
+
getTaskOverview: function getTaskOverview(params) {
|
|
812
|
+
return client.post('', { action: 'Task.getTaskOverview', ...params }).then(res => res.data);
|
|
813
|
+
},
|
|
712
814
|
rescheduleTask: function rescheduleTask(params) {
|
|
713
815
|
return client.post('', { action: 'Task.rescheduleTask', ...params }).then(res => res.data);
|
|
714
816
|
},
|
|
@@ -1043,6 +1145,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
1043
1145
|
getTasks: function getTasks(params) {
|
|
1044
1146
|
return client.post('/Task/getTasks', params).then(res => res.data);
|
|
1045
1147
|
},
|
|
1148
|
+
getTaskOverview: function getTaskOverview(params) {
|
|
1149
|
+
return client.post('/Task/getTaskOverview', params).then(res => res.data);
|
|
1150
|
+
},
|
|
1046
1151
|
rescheduleTask: function rescheduleTask(params) {
|
|
1047
1152
|
return client.post('/Task/rescheduleTask', params).then(res => res.data);
|
|
1048
1153
|
},
|
|
@@ -1252,15 +1357,9 @@ module.exports = app => app.component('chat-message-script', {
|
|
|
1252
1357
|
},
|
|
1253
1358
|
canOverwriteDashboard() {
|
|
1254
1359
|
return !!this.targetDashboardId;
|
|
1255
|
-
},
|
|
1256
|
-
isChartOutput() {
|
|
1257
|
-
return !!this.message.executionResult?.output?.$chart;
|
|
1258
1360
|
}
|
|
1259
1361
|
},
|
|
1260
1362
|
methods: {
|
|
1261
|
-
exportChartPNG() {
|
|
1262
|
-
this.$refs.chartOutput?.exportPNG?.();
|
|
1263
|
-
},
|
|
1264
1363
|
async executeScript() {
|
|
1265
1364
|
const scriptToRun = this.isEditing ? this.editedScript : this.script;
|
|
1266
1365
|
this.editedScript = scriptToRun;
|
|
@@ -2387,12 +2486,63 @@ module.exports = app => app.component('dashboard-result', {
|
|
|
2387
2486
|
if (value.$grid) {
|
|
2388
2487
|
return 'dashboard-grid';
|
|
2389
2488
|
}
|
|
2489
|
+
if (value.$table) {
|
|
2490
|
+
return 'dashboard-table';
|
|
2491
|
+
}
|
|
2390
2492
|
return 'dashboard-object';
|
|
2391
2493
|
}
|
|
2392
2494
|
}
|
|
2393
2495
|
});
|
|
2394
2496
|
|
|
2395
2497
|
|
|
2498
|
+
/***/ },
|
|
2499
|
+
|
|
2500
|
+
/***/ "./frontend/src/dashboard-result/dashboard-table/dashboard-table.js"
|
|
2501
|
+
/*!**************************************************************************!*\
|
|
2502
|
+
!*** ./frontend/src/dashboard-result/dashboard-table/dashboard-table.js ***!
|
|
2503
|
+
\**************************************************************************/
|
|
2504
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
2505
|
+
|
|
2506
|
+
"use strict";
|
|
2507
|
+
|
|
2508
|
+
|
|
2509
|
+
const template = __webpack_require__(/*! ./dashboard-table.html */ "./frontend/src/dashboard-result/dashboard-table/dashboard-table.html");
|
|
2510
|
+
|
|
2511
|
+
module.exports = app => app.component('dashboard-table', {
|
|
2512
|
+
template,
|
|
2513
|
+
props: ['value'],
|
|
2514
|
+
computed: {
|
|
2515
|
+
columns() {
|
|
2516
|
+
return Array.isArray(this.value?.$table?.columns) ? this.value.$table.columns : [];
|
|
2517
|
+
},
|
|
2518
|
+
rows() {
|
|
2519
|
+
return Array.isArray(this.value?.$table?.rows) ? this.value.$table.rows : [];
|
|
2520
|
+
},
|
|
2521
|
+
hasColumns() {
|
|
2522
|
+
return this.columns.length > 0;
|
|
2523
|
+
},
|
|
2524
|
+
hasRows() {
|
|
2525
|
+
return this.rows.length > 0;
|
|
2526
|
+
}
|
|
2527
|
+
},
|
|
2528
|
+
methods: {
|
|
2529
|
+
displayValue(cell) {
|
|
2530
|
+
if (cell == null) {
|
|
2531
|
+
return '';
|
|
2532
|
+
}
|
|
2533
|
+
if (typeof cell === 'object') {
|
|
2534
|
+
try {
|
|
2535
|
+
return JSON.stringify(cell);
|
|
2536
|
+
} catch (err) {
|
|
2537
|
+
return String(cell);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
return String(cell);
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
});
|
|
2544
|
+
|
|
2545
|
+
|
|
2396
2546
|
/***/ },
|
|
2397
2547
|
|
|
2398
2548
|
/***/ "./frontend/src/dashboard-result/dashboard-text/dashboard-text.js"
|
|
@@ -2451,6 +2601,13 @@ module.exports = {
|
|
|
2451
2601
|
this.showEditor = !this.showEditor;
|
|
2452
2602
|
},
|
|
2453
2603
|
async updateCode(update) {
|
|
2604
|
+
if (!update?.doc) {
|
|
2605
|
+
const message = update?.error?.message || 'Dashboard update failed';
|
|
2606
|
+
console.error(update?.error || new Error(message));
|
|
2607
|
+
this.$toast.error(message);
|
|
2608
|
+
return;
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2454
2611
|
this.code = update.doc.code;
|
|
2455
2612
|
this.title = update.doc.title;
|
|
2456
2613
|
this.description = update.doc.description;
|
|
@@ -2475,7 +2632,9 @@ module.exports = {
|
|
|
2475
2632
|
this.dashboardResults.unshift({ error: { message: error.message || 'Evaluation failed' }, finishedEvaluatingAt: new Date() });
|
|
2476
2633
|
}
|
|
2477
2634
|
} catch (err) {
|
|
2478
|
-
|
|
2635
|
+
const message = err?.response?.data?.message || err?.message || 'Dashboard evaluation failed';
|
|
2636
|
+
console.error(err || new Error(message));
|
|
2637
|
+
this.$toast.error(message);
|
|
2479
2638
|
} finally {
|
|
2480
2639
|
this.status = 'loaded';
|
|
2481
2640
|
}
|
|
@@ -2599,7 +2758,7 @@ const template = __webpack_require__(/*! ./edit-dashboard.html */ "./frontend/sr
|
|
|
2599
2758
|
module.exports = app => app.component('edit-dashboard', {
|
|
2600
2759
|
template: template,
|
|
2601
2760
|
props: ['dashboardId', 'code', 'currentDescription', 'currentTitle'],
|
|
2602
|
-
emits: ['close'],
|
|
2761
|
+
emits: ['close', 'update'],
|
|
2603
2762
|
data: function() {
|
|
2604
2763
|
return {
|
|
2605
2764
|
status: 'loaded',
|
|
@@ -2620,7 +2779,7 @@ module.exports = app => app.component('edit-dashboard', {
|
|
|
2620
2779
|
async updateCode() {
|
|
2621
2780
|
this.status = 'loading';
|
|
2622
2781
|
try {
|
|
2623
|
-
const codeToSave = this.$refs.codeEditor ? this.$refs.codeEditor.getValue() : this.editCode;
|
|
2782
|
+
const codeToSave = this.$refs.codeEditor?.getValue ? this.$refs.codeEditor.getValue() : this.editCode;
|
|
2624
2783
|
const { doc } = await api.Dashboard.updateDashboard({
|
|
2625
2784
|
dashboardId: this.dashboardId,
|
|
2626
2785
|
code: codeToSave,
|
|
@@ -2636,7 +2795,8 @@ module.exports = app => app.component('edit-dashboard', {
|
|
|
2636
2795
|
this.$toast.success('Dashboard updated!');
|
|
2637
2796
|
this.closeEditor();
|
|
2638
2797
|
} catch (err) {
|
|
2639
|
-
|
|
2798
|
+
const message = err?.response?.data?.message || err?.message || 'Dashboard update failed';
|
|
2799
|
+
this.$emit('update', { error: { message } });
|
|
2640
2800
|
} finally {
|
|
2641
2801
|
this.status = 'loaded';
|
|
2642
2802
|
}
|
|
@@ -6946,6 +7106,7 @@ module.exports = app => app.component('models', {
|
|
|
6946
7106
|
}),
|
|
6947
7107
|
created() {
|
|
6948
7108
|
this.currentModel = this.model;
|
|
7109
|
+
this.setSearchTextFromRoute();
|
|
6949
7110
|
this.loadOutputPreference();
|
|
6950
7111
|
this.loadSelectedGeoField();
|
|
6951
7112
|
this.loadRecentlyViewedModels();
|
|
@@ -6982,6 +7143,7 @@ module.exports = app => app.component('models', {
|
|
|
6982
7143
|
}
|
|
6983
7144
|
};
|
|
6984
7145
|
document.addEventListener('keydown', this.onCtrlP, true);
|
|
7146
|
+
this.query = Object.assign({}, this.$route.query);
|
|
6985
7147
|
const { models, readyState } = await api.Model.listModels();
|
|
6986
7148
|
this.models = models;
|
|
6987
7149
|
await this.loadModelCounts();
|
|
@@ -7364,14 +7526,17 @@ module.exports = app => app.component('models', {
|
|
|
7364
7526
|
|
|
7365
7527
|
return params;
|
|
7366
7528
|
},
|
|
7367
|
-
|
|
7368
|
-
this.status = 'loading';
|
|
7369
|
-
this.query = Object.assign({}, this.$route.query); // important that this is here before the if statements
|
|
7529
|
+
setSearchTextFromRoute() {
|
|
7370
7530
|
if (this.$route.query?.search) {
|
|
7371
7531
|
this.searchText = this.$route.query.search;
|
|
7372
7532
|
} else {
|
|
7373
7533
|
this.searchText = '';
|
|
7374
7534
|
}
|
|
7535
|
+
},
|
|
7536
|
+
async initSearchFromUrl() {
|
|
7537
|
+
this.status = 'loading';
|
|
7538
|
+
this.query = Object.assign({}, this.$route.query); // important that this is here before the if statements
|
|
7539
|
+
this.setSearchTextFromRoute();
|
|
7375
7540
|
if (this.$route.query?.sort) {
|
|
7376
7541
|
const sort = eval(`(${this.$route.query.sort})`);
|
|
7377
7542
|
const path = Object.keys(sort)[0];
|
|
@@ -8413,6 +8578,7 @@ module.exports = app => app.component('splash', {
|
|
|
8413
8578
|
// Page: all tasks with a given name. Reuses task-details to render the list (many tasks).
|
|
8414
8579
|
const template = __webpack_require__(/*! ./task-by-name.html */ "./frontend/src/task-by-name/task-by-name.html");
|
|
8415
8580
|
const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
|
|
8581
|
+
const { DATE_FILTERS, DATE_FILTER_VALUES, getDateRangeForRange } = __webpack_require__(/*! ../_util/dateRange */ "./frontend/src/_util/dateRange.js");
|
|
8416
8582
|
|
|
8417
8583
|
function buildTaskGroup(name, tasks) {
|
|
8418
8584
|
const statusCounts = { pending: 0, succeeded: 0, failed: 0, cancelled: 0, in_progress: 0, unknown: 0 };
|
|
@@ -8436,44 +8602,118 @@ function buildTaskGroup(name, tasks) {
|
|
|
8436
8602
|
};
|
|
8437
8603
|
}
|
|
8438
8604
|
|
|
8605
|
+
const PAGE_SIZE_OPTIONS = [25, 50, 100, 200];
|
|
8606
|
+
|
|
8439
8607
|
module.exports = app => app.component('task-by-name', {
|
|
8440
8608
|
template,
|
|
8441
8609
|
data: () => ({
|
|
8442
8610
|
status: 'init',
|
|
8443
8611
|
taskGroup: null,
|
|
8444
|
-
errorMessage: ''
|
|
8612
|
+
errorMessage: '',
|
|
8613
|
+
selectedRange: 'last_hour',
|
|
8614
|
+
start: null,
|
|
8615
|
+
end: null,
|
|
8616
|
+
dateFilters: DATE_FILTERS,
|
|
8617
|
+
page: 1,
|
|
8618
|
+
pageSize: 50,
|
|
8619
|
+
numDocs: 0,
|
|
8620
|
+
pageSizeOptions: PAGE_SIZE_OPTIONS,
|
|
8621
|
+
_loadId: 0,
|
|
8622
|
+
_lastQueryFilters: null
|
|
8445
8623
|
}),
|
|
8446
8624
|
computed: {
|
|
8447
8625
|
taskName() {
|
|
8448
8626
|
return this.$route.params.name || '';
|
|
8449
8627
|
}
|
|
8450
8628
|
},
|
|
8629
|
+
created() {
|
|
8630
|
+
const fromQuery = this.$route.query.dateRange;
|
|
8631
|
+
this.selectedRange = (fromQuery && DATE_FILTER_VALUES.includes(fromQuery)) ? fromQuery : 'last_hour';
|
|
8632
|
+
if (!this.$route.query.dateRange) {
|
|
8633
|
+
this.$router.replace({
|
|
8634
|
+
path: this.$route.path,
|
|
8635
|
+
query: { ...this.$route.query, dateRange: this.selectedRange }
|
|
8636
|
+
});
|
|
8637
|
+
}
|
|
8638
|
+
},
|
|
8451
8639
|
watch: {
|
|
8452
8640
|
taskName: {
|
|
8453
8641
|
immediate: true,
|
|
8454
8642
|
handler() {
|
|
8643
|
+
this.page = 1;
|
|
8455
8644
|
this.loadTasks();
|
|
8456
8645
|
}
|
|
8646
|
+
},
|
|
8647
|
+
'$route.query': {
|
|
8648
|
+
handler(query) {
|
|
8649
|
+
const dateRange = query.dateRange;
|
|
8650
|
+
if (dateRange && DATE_FILTER_VALUES.includes(dateRange) && this.selectedRange !== dateRange) {
|
|
8651
|
+
this.selectedRange = dateRange;
|
|
8652
|
+
}
|
|
8653
|
+
const effectiveDateRange = (dateRange && DATE_FILTER_VALUES.includes(dateRange)) ? dateRange : (this.selectedRange || 'last_hour');
|
|
8654
|
+
const effectiveStatus = query.status ?? '';
|
|
8655
|
+
const key = `${effectiveDateRange}|${effectiveStatus}`;
|
|
8656
|
+
if (this._lastQueryFilters === key) return;
|
|
8657
|
+
this.page = 1;
|
|
8658
|
+
this.loadTasks();
|
|
8659
|
+
},
|
|
8660
|
+
deep: true
|
|
8457
8661
|
}
|
|
8458
8662
|
},
|
|
8459
8663
|
methods: {
|
|
8664
|
+
updateDateRange() {
|
|
8665
|
+
this.page = 1;
|
|
8666
|
+
this.$router.replace({
|
|
8667
|
+
path: this.$route.path,
|
|
8668
|
+
query: { ...this.$route.query, dateRange: this.selectedRange }
|
|
8669
|
+
});
|
|
8670
|
+
},
|
|
8671
|
+
goToPage(page) {
|
|
8672
|
+
const maxPage = Math.max(1, Math.ceil(this.numDocs / this.pageSize));
|
|
8673
|
+
const next = Math.max(1, Math.min(page, maxPage));
|
|
8674
|
+
if (next === this.page) return;
|
|
8675
|
+
this.page = next;
|
|
8676
|
+
this.loadTasks();
|
|
8677
|
+
},
|
|
8678
|
+
onPageSizeChange() {
|
|
8679
|
+
this.page = 1;
|
|
8680
|
+
this.loadTasks();
|
|
8681
|
+
},
|
|
8460
8682
|
async loadTasks() {
|
|
8461
8683
|
if (!this.taskName) return;
|
|
8684
|
+
const loadId = ++this._loadId;
|
|
8462
8685
|
this.status = 'init';
|
|
8463
8686
|
this.taskGroup = null;
|
|
8464
8687
|
this.errorMessage = '';
|
|
8465
|
-
const
|
|
8466
|
-
|
|
8467
|
-
|
|
8688
|
+
const dateRangeFromQuery = this.$route.query.dateRange;
|
|
8689
|
+
const dateRange = (dateRangeFromQuery && DATE_FILTER_VALUES.includes(dateRangeFromQuery))
|
|
8690
|
+
? dateRangeFromQuery
|
|
8691
|
+
: (this.selectedRange || 'last_hour');
|
|
8692
|
+
this.selectedRange = dateRange;
|
|
8693
|
+
const { start, end } = getDateRangeForRange(dateRange);
|
|
8694
|
+
this.start = start;
|
|
8695
|
+
this.end = end;
|
|
8696
|
+
const skip = (this.page - 1) * this.pageSize;
|
|
8697
|
+
const params = {
|
|
8698
|
+
name: this.taskName,
|
|
8699
|
+
start: start instanceof Date ? start.toISOString() : start,
|
|
8700
|
+
end: end instanceof Date ? end.toISOString() : end,
|
|
8701
|
+
skip,
|
|
8702
|
+
limit: this.pageSize
|
|
8703
|
+
};
|
|
8704
|
+
const statusFromQuery = this.$route.query.status;
|
|
8705
|
+
if (statusFromQuery) params.status = statusFromQuery;
|
|
8706
|
+
this._lastQueryFilters = `${dateRange}|${statusFromQuery ?? ''}`;
|
|
8468
8707
|
try {
|
|
8469
|
-
const { tasks } = await api.Task.getTasks(
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
end
|
|
8473
|
-
});
|
|
8708
|
+
const { tasks, numDocs, statusCounts } = await api.Task.getTasks(params);
|
|
8709
|
+
if (loadId !== this._loadId) return;
|
|
8710
|
+
this.numDocs = numDocs ?? tasks.length;
|
|
8474
8711
|
this.taskGroup = buildTaskGroup(this.taskName, tasks);
|
|
8712
|
+
this.taskGroup.totalCount = this.numDocs;
|
|
8713
|
+
if (statusCounts) this.taskGroup.statusCounts = statusCounts;
|
|
8475
8714
|
this.status = 'loaded';
|
|
8476
8715
|
} catch (err) {
|
|
8716
|
+
if (loadId !== this._loadId) return;
|
|
8477
8717
|
this.status = 'error';
|
|
8478
8718
|
this.errorMessage = err?.response?.data?.message || err.message || 'Failed to load tasks';
|
|
8479
8719
|
}
|
|
@@ -8636,7 +8876,8 @@ const PIE_HOVER = ['#ca8a04', '#16a34a', '#dc2626', '#4b5563'];
|
|
|
8636
8876
|
module.exports = app => app.component('task-details', {
|
|
8637
8877
|
props: {
|
|
8638
8878
|
taskGroup: { type: Object, required: true },
|
|
8639
|
-
backTo: { type: Object, default: null }
|
|
8879
|
+
backTo: { type: Object, default: null },
|
|
8880
|
+
showBackButton: { type: Boolean, default: true }
|
|
8640
8881
|
},
|
|
8641
8882
|
data: () => ({
|
|
8642
8883
|
currentFilter: null,
|
|
@@ -8977,25 +9218,17 @@ module.exports = app => app.component('task-details', {
|
|
|
8977
9218
|
|
|
8978
9219
|
const template = __webpack_require__(/*! ./tasks.html */ "./frontend/src/tasks/tasks.html");
|
|
8979
9220
|
const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
|
|
9221
|
+
const { DATE_FILTERS, getDateRangeForRange } = __webpack_require__(/*! ../_util/dateRange */ "./frontend/src/_util/dateRange.js");
|
|
8980
9222
|
|
|
8981
9223
|
module.exports = app => app.component('tasks', {
|
|
8982
9224
|
data: () => ({
|
|
8983
9225
|
status: 'init',
|
|
8984
|
-
|
|
8985
|
-
|
|
9226
|
+
statusCounts: { pending: 0, succeeded: 0, failed: 0, cancelled: 0 },
|
|
9227
|
+
tasksByName: [],
|
|
8986
9228
|
selectedRange: 'last_hour',
|
|
8987
9229
|
start: null,
|
|
8988
9230
|
end: null,
|
|
8989
|
-
dateFilters:
|
|
8990
|
-
{ value: 'all', label: 'All Time' },
|
|
8991
|
-
{ value: 'last_hour', label: 'Last Hour' },
|
|
8992
|
-
{ value: 'today', label: 'Today' },
|
|
8993
|
-
{ value: 'yesterday', label: 'Yesterday' },
|
|
8994
|
-
{ value: 'thisWeek', label: 'This Week' },
|
|
8995
|
-
{ value: 'lastWeek', label: 'Last Week' },
|
|
8996
|
-
{ value: 'thisMonth', label: 'This Month' },
|
|
8997
|
-
{ value: 'lastMonth', label: 'Last Month' }
|
|
8998
|
-
],
|
|
9231
|
+
dateFilters: DATE_FILTERS,
|
|
8999
9232
|
selectedStatus: 'all',
|
|
9000
9233
|
statusFilters: [
|
|
9001
9234
|
{ label: 'All', value: 'all' },
|
|
@@ -9025,26 +9258,31 @@ module.exports = app => app.component('tasks', {
|
|
|
9025
9258
|
params.status = this.selectedStatus;
|
|
9026
9259
|
}
|
|
9027
9260
|
|
|
9028
|
-
if (this.start && this.end) {
|
|
9029
|
-
params.start = this.start;
|
|
9030
|
-
params.end = this.end;
|
|
9031
|
-
} else if (this.start) {
|
|
9032
|
-
params.start = this.start;
|
|
9261
|
+
if (this.start != null && this.end != null) {
|
|
9262
|
+
params.start = this.start instanceof Date ? this.start.toISOString() : this.start;
|
|
9263
|
+
params.end = this.end instanceof Date ? this.end.toISOString() : this.end;
|
|
9264
|
+
} else if (this.start != null) {
|
|
9265
|
+
params.start = this.start instanceof Date ? this.start.toISOString() : this.start;
|
|
9033
9266
|
}
|
|
9034
9267
|
|
|
9035
9268
|
if (this.searchQuery.trim()) {
|
|
9036
9269
|
params.name = this.searchQuery.trim();
|
|
9037
9270
|
}
|
|
9038
9271
|
|
|
9039
|
-
const {
|
|
9040
|
-
this.
|
|
9041
|
-
this.
|
|
9272
|
+
const { statusCounts, tasksByName } = await api.Task.getTaskOverview(params);
|
|
9273
|
+
this.statusCounts = statusCounts || this.statusCounts;
|
|
9274
|
+
this.tasksByName = tasksByName || [];
|
|
9042
9275
|
},
|
|
9043
9276
|
openTaskGroupDetails(group) {
|
|
9044
|
-
|
|
9277
|
+
const query = { dateRange: this.selectedRange || 'last_hour' };
|
|
9278
|
+
if (this.selectedStatus && this.selectedStatus !== 'all') query.status = this.selectedStatus;
|
|
9279
|
+
this.$router.push({ path: `/tasks/${encodeURIComponent(group.name || '')}`, query });
|
|
9045
9280
|
},
|
|
9046
9281
|
openTaskGroupDetailsWithFilter(group, status) {
|
|
9047
|
-
|
|
9282
|
+
const query = { dateRange: this.selectedRange || 'last_hour' };
|
|
9283
|
+
if (status) query.status = status;
|
|
9284
|
+
else if (this.selectedStatus && this.selectedStatus !== 'all') query.status = this.selectedStatus;
|
|
9285
|
+
this.$router.push({ path: `/tasks/${encodeURIComponent(group.name || '')}`, query });
|
|
9048
9286
|
},
|
|
9049
9287
|
async onTaskCreated() {
|
|
9050
9288
|
// Refresh the task data when a new task is created
|
|
@@ -9188,114 +9426,24 @@ module.exports = app => app.component('tasks', {
|
|
|
9188
9426
|
}, 300);
|
|
9189
9427
|
},
|
|
9190
9428
|
async updateDateRange() {
|
|
9191
|
-
const
|
|
9192
|
-
let start, end;
|
|
9193
|
-
|
|
9194
|
-
switch (this.selectedRange) {
|
|
9195
|
-
case 'last_hour':
|
|
9196
|
-
start = new Date();
|
|
9197
|
-
start.setHours(start.getHours() - 1);
|
|
9198
|
-
end = new Date();
|
|
9199
|
-
break;
|
|
9200
|
-
case 'today':
|
|
9201
|
-
start = new Date();
|
|
9202
|
-
start.setHours(0, 0, 0, 0);
|
|
9203
|
-
end = new Date();
|
|
9204
|
-
end.setHours(23, 59, 59, 999);
|
|
9205
|
-
break;
|
|
9206
|
-
case 'yesterday':
|
|
9207
|
-
start = new Date();
|
|
9208
|
-
start.setDate(start.getDate() - 1);
|
|
9209
|
-
start.setHours(0, 0, 0, 0);
|
|
9210
|
-
end = new Date();
|
|
9211
|
-
break;
|
|
9212
|
-
case 'thisWeek':
|
|
9213
|
-
start = new Date(now.getTime() - (7 * 86400000));
|
|
9214
|
-
start.setHours(0, 0, 0, 0);
|
|
9215
|
-
end = new Date();
|
|
9216
|
-
end.setHours(23, 59, 59, 999);
|
|
9217
|
-
break;
|
|
9218
|
-
case 'lastWeek':
|
|
9219
|
-
start = new Date(now.getTime() - (14 * 86400000));
|
|
9220
|
-
start.setHours(0, 0, 0, 0);
|
|
9221
|
-
end = new Date(now.getTime() - (7 * 86400000));
|
|
9222
|
-
end.setHours(23, 59, 59, 999);
|
|
9223
|
-
break;
|
|
9224
|
-
case 'thisMonth':
|
|
9225
|
-
start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
9226
|
-
end = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
|
|
9227
|
-
break;
|
|
9228
|
-
case 'lastMonth':
|
|
9229
|
-
start = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
|
9230
|
-
end = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999);
|
|
9231
|
-
break;
|
|
9232
|
-
case 'all':
|
|
9233
|
-
default:
|
|
9234
|
-
this.start = null;
|
|
9235
|
-
this.end = null;
|
|
9236
|
-
break;
|
|
9237
|
-
}
|
|
9238
|
-
|
|
9429
|
+
const { start, end } = getDateRangeForRange(this.selectedRange);
|
|
9239
9430
|
this.start = start;
|
|
9240
9431
|
this.end = end;
|
|
9241
|
-
|
|
9242
9432
|
await this.getTasks();
|
|
9243
9433
|
}
|
|
9244
9434
|
},
|
|
9245
9435
|
computed: {
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
// Process tasks from groupedTasks to create name-based groups
|
|
9250
|
-
Object.entries(this.groupedTasks).forEach(([status, tasks]) => {
|
|
9251
|
-
tasks.forEach(task => {
|
|
9252
|
-
if (!groups[task.name]) {
|
|
9253
|
-
groups[task.name] = {
|
|
9254
|
-
name: task.name,
|
|
9255
|
-
tasks: [],
|
|
9256
|
-
statusCounts: {
|
|
9257
|
-
pending: 0,
|
|
9258
|
-
succeeded: 0,
|
|
9259
|
-
failed: 0,
|
|
9260
|
-
cancelled: 0
|
|
9261
|
-
},
|
|
9262
|
-
totalCount: 0,
|
|
9263
|
-
lastRun: null
|
|
9264
|
-
};
|
|
9265
|
-
}
|
|
9266
|
-
|
|
9267
|
-
groups[task.name].tasks.push(task);
|
|
9268
|
-
groups[task.name].totalCount++;
|
|
9269
|
-
|
|
9270
|
-
// Count status using the status from groupedTasks
|
|
9271
|
-
if (groups[task.name].statusCounts.hasOwnProperty(status)) {
|
|
9272
|
-
groups[task.name].statusCounts[status]++;
|
|
9273
|
-
}
|
|
9274
|
-
|
|
9275
|
-
// Track last run time
|
|
9276
|
-
const taskTime = new Date(task.scheduledAt || task.createdAt || 0);
|
|
9277
|
-
if (!groups[task.name].lastRun || taskTime > new Date(groups[task.name].lastRun)) {
|
|
9278
|
-
groups[task.name].lastRun = taskTime;
|
|
9279
|
-
}
|
|
9280
|
-
});
|
|
9281
|
-
});
|
|
9282
|
-
|
|
9283
|
-
// Convert to array and sort alphabetically by name
|
|
9284
|
-
return Object.values(groups).sort((a, b) => {
|
|
9285
|
-
return a.name.localeCompare(b.name);
|
|
9286
|
-
});
|
|
9436
|
+
pendingCount() {
|
|
9437
|
+
return this.statusCounts.pending || 0;
|
|
9287
9438
|
},
|
|
9288
9439
|
succeededCount() {
|
|
9289
|
-
return this.
|
|
9440
|
+
return this.statusCounts.succeeded || 0;
|
|
9290
9441
|
},
|
|
9291
9442
|
failedCount() {
|
|
9292
|
-
return this.
|
|
9443
|
+
return this.statusCounts.failed || 0;
|
|
9293
9444
|
},
|
|
9294
9445
|
cancelledCount() {
|
|
9295
|
-
return this.
|
|
9296
|
-
},
|
|
9297
|
-
pendingCount() {
|
|
9298
|
-
return this.groupedTasks.pending ? this.groupedTasks.pending.length : 0;
|
|
9446
|
+
return this.statusCounts.cancelled || 0;
|
|
9299
9447
|
}
|
|
9300
9448
|
},
|
|
9301
9449
|
mounted: async function() {
|
|
@@ -9527,6 +9675,8 @@ var map = {
|
|
|
9527
9675
|
"./": "./frontend/src/index.js",
|
|
9528
9676
|
"./_util/baseComponent": "./frontend/src/_util/baseComponent.js",
|
|
9529
9677
|
"./_util/baseComponent.js": "./frontend/src/_util/baseComponent.js",
|
|
9678
|
+
"./_util/dateRange": "./frontend/src/_util/dateRange.js",
|
|
9679
|
+
"./_util/dateRange.js": "./frontend/src/_util/dateRange.js",
|
|
9530
9680
|
"./_util/deepEqual": "./frontend/src/_util/deepEqual.js",
|
|
9531
9681
|
"./_util/deepEqual.js": "./frontend/src/_util/deepEqual.js",
|
|
9532
9682
|
"./_util/document-search-autocomplete": "./frontend/src/_util/document-search-autocomplete.js",
|
|
@@ -9586,6 +9736,9 @@ var map = {
|
|
|
9586
9736
|
"./dashboard-result/dashboard-result": "./frontend/src/dashboard-result/dashboard-result.js",
|
|
9587
9737
|
"./dashboard-result/dashboard-result.html": "./frontend/src/dashboard-result/dashboard-result.html",
|
|
9588
9738
|
"./dashboard-result/dashboard-result.js": "./frontend/src/dashboard-result/dashboard-result.js",
|
|
9739
|
+
"./dashboard-result/dashboard-table/dashboard-table": "./frontend/src/dashboard-result/dashboard-table/dashboard-table.js",
|
|
9740
|
+
"./dashboard-result/dashboard-table/dashboard-table.html": "./frontend/src/dashboard-result/dashboard-table/dashboard-table.html",
|
|
9741
|
+
"./dashboard-result/dashboard-table/dashboard-table.js": "./frontend/src/dashboard-result/dashboard-table/dashboard-table.js",
|
|
9589
9742
|
"./dashboard-result/dashboard-text/dashboard-text": "./frontend/src/dashboard-result/dashboard-text/dashboard-text.js",
|
|
9590
9743
|
"./dashboard-result/dashboard-text/dashboard-text.html": "./frontend/src/dashboard-result/dashboard-text/dashboard-text.html",
|
|
9591
9744
|
"./dashboard-result/dashboard-text/dashboard-text.js": "./frontend/src/dashboard-result/dashboard-text/dashboard-text.js",
|
|
@@ -49468,7 +49621,7 @@ module.exports = "<button v-bind=\"attrsToBind\" :disabled=\"isDisabled\" @click
|
|
|
49468
49621
|
(module) {
|
|
49469
49622
|
|
|
49470
49623
|
"use strict";
|
|
49471
|
-
module.exports = "<div class=\"chat-message-script relative border rounded bg-surface my-1 text-content text-sm overflow-hidden\">\n <div class=\"flex border-b py-1 pl-1 text-xs font-medium bg-surface\">\n <button\n class=\"px-4 py-1 border-r border-edge-strong text-content-secondary font-semibold transition-colors duration-200 focus:outline-none\"\n :class=\"[\n 'rounded-l-md',\n activeTab === 'code'\n ? 'bg-gray-700 text-white shadow'\n : 'bg-muted hover:bg-muted text-gray-600'\n ]\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-4 py-1 text-content-secondary font-semibold transition-colors duration-200 focus:outline-none\"\n :class=\"[\n 'rounded-r-md',\n activeTab === 'output'\n ? 'bg-gray-700 text-white shadow'\n : 'bg-muted hover:bg-muted text-gray-600'\n ]\"\n @click=\"activeTab = 'output'\">\n Output\n </button>\n <div class=\"ml-auto mr-1 flex items-center\">\n <button\n v-if=\"activeTab === 'output' && isChartOutput\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-700 text-white border-none rounded cursor-pointer hover:bg-primary-hover transition-colors flex items-center\"\n @click=\"exportChartPNG\"\n title=\"Export PNG\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"height: 1.2em;\" viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M280-280h400v-80H280v80Zm200-120 160-160-56-56-64 62v-166h-80v166l-64-62-56 56 160 160Zm0 320q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z\"/></svg>\n </button>\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-700 text-white border-none rounded cursor-pointer hover:bg-primary-hover transition-colors flex items-center\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"height: 1.2em;\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'code' && !isEditing\"\n class=\"px-2 py-1 mr-1 text-xs bg-page0 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click.stop=\"startEditing\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM4 13.5V16h2.5l7.086-7.086-2.828-2.828L4 13.5z\" />\n </svg>\n </button>\n <async-button\n v-if=\"!isEditing\"\n class=\"px-2 py-1 text-xs bg-primary hover:bg-primary-hover text-primary-text border-none rounded cursor-pointer transition-colors disabled:bg-gray-400\"\n @click=\"executeScript\">\n Run\n </async-button>\n <div class=\"relative ml-1\" ref=\"dropdown\">\n <button\n @click.stop=\"toggleDropdown\"\n class=\"px-1 py-1 text-xs hover:bg-gray-300 rounded flex items-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n <div\n v-if=\"showDropdown\"\n class=\"absolute right-0 z-10 mt-1 w-64 origin-top-right rounded-md bg-surface py-1 shadow-lg ring-1 ring-black/5\">\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"openCreateDashboardModal(); showDropdown = false\">\n Create Dashboard\n </button>\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"copyOutput(); showDropdown = false\">\n Copy Output As Text\n </button>\n <button\n v-if=\"canOverwriteDashboard\"\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"openOverwriteDashboardConfirmation(); showDropdown = false\">\n Overwrite Dashboard\n </button>\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"$emit('copyMessage'); showDropdown = false\">\n Copy Full Message\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"p-0 max-h-[50vh] max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] overflow-y-auto\" v-show=\"activeTab === 'code'\">\n <div v-if=\"isEditing\" class=\"flex flex-col space-y-2 chat-script-editor-wrap\">\n <div class=\"border border-edge\">\n <ace-editor\n v-model=\"editedScript\"\n mode=\"javascript\"\n :line-numbers=\"true\"\n class=\"w-full h-[45vh] min-h-[200px]\"\n />\n </div>\n <div class=\"flex justify-end gap-2 pb-2\">\n <button\n class=\"px-2 py-1 text-xs bg-gray-300 text-gray-800 border-none rounded cursor-pointer hover:bg-gray-400 transition-colors\"\n @click.stop=\"cancelEditing\">\n Cancel\n </button>\n <async-button\n class=\"px-2 py-1 text-xs bg-blue-600 text-white border-none rounded cursor-pointer hover:bg-blue-700 transition-colors disabled:bg-gray-400\"\n @click=\"executeScript\">\n Execute\n </async-button>\n </div>\n </div>\n <pre v-else class=\"whitespace-pre-wrap !my-0 bg-muted\"><code v-text=\"script\" ref=\"code\" :class=\"'language-' + language\"></code></pre>\n </div>\n\n <div class=\"p-3 whitespace-pre-wrap max-h-[50vh] overflow-y-auto bg-surface border-t max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] relative\" v-show=\"activeTab === 'output'\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" ref=\"chartOutput\" />\n <dashboard-map v-else-if=\"message.executionResult?.output?.$featureCollection\" :value=\"message.executionResult?.output\" />\n <pre v-else>{{ message.executionResult?.output || 'No output' }}</pre>\n\n <div v-if=\"message.executionResult?.logs?.length\" class=\"mt-3 pt-3 border-t border-edge\">\n <div class=\"text-xs font-semibold text-gray-600 uppercase tracking-wide\">Console</div>\n <pre class=\"mt-1 bg-muted text-content p-3 rounded whitespace-pre-wrap overflow-x-auto max-h-[280px]\">{{ message.executionResult.logs }}</pre>\n </div>\n </div>\n\n <modal ref=\"outputModal\" v-if=\"showDetailModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\">×</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" :responsive=\"true\" />\n <dashboard-map\n v-else-if=\"message.executionResult?.output?.$featureCollection\"\n :value=\"message.executionResult?.output\"\n height=\"80vh\" />\n <pre v-else class=\"whitespace-pre-wrap\">{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n </template>\n </modal>\n <modal v-if=\"showCreateDashboardModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showCreateDashboardModal = false\">×</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">Create Dashboard</div>\n <div class=\"mt-4\">\n <label class=\"block text-sm font-medium leading-6 text-content\">Title</label>\n <div class=\"mt-2\">\n <div class=\"w-full flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-teal-600\">\n <input type=\"text\" v-model=\"newDashboardTitle\" class=\"outline-none block flex-1 border-0 bg-transparent py-1.5 pl-1 text-content placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6\" placeholder=\"My Dashboard\">\n </div>\n </div>\n </div>\n <div class=\"my-4\">\n <label class=\"block text-sm font-medium leading-6 text-content\">Code</label>\n <div class=\"border border-edge\">\n <ace-editor\n v-model=\"dashboardCode\"\n mode=\"javascript\"\n :line-numbers=\"true\"\n class=\"h-[300px] w-full\"\n />\n </div>\n </div>\n <async-button\n @click=\"createDashboardFromScript\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Submit\n </async-button>\n <div v-if=\"createError\" class=\"rounded-md bg-red-50 p-4 mt-1\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{createError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"showOverwriteDashboardConfirmationModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showOverwriteDashboardConfirmationModal = false\">×</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">Overwrite Dashboard</div>\n <p class=\"mt-2 text-sm text-content-secondary\">\n This will replace the linked dashboard's code with the script below.\n </p>\n <p class=\"mt-1 text-xs text-gray-600 break-all\" v-if=\"targetDashboardId\">\n Dashboard ID: {{ targetDashboardId }}\n </p>\n <div class=\"my-4 border border-edge bg-page rounded\">\n <pre class=\"p-2 h-[300px] overflow-auto whitespace-pre-wrap text-xs\">{{ overwriteDashboardCode }}</pre>\n </div>\n <div class=\"flex items-center gap-2\">\n <async-button\n @click=\"confirmOverwriteDashboard\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Confirm Overwrite\n </async-button>\n <button\n class=\"px-2.5 py-1.5 rounded-md text-sm font-semibold text-content-secondary bg-gray-200 hover:bg-gray-300\"\n @click=\"showOverwriteDashboardConfirmationModal = false\">\n Cancel\n </button>\n </div>\n <div v-if=\"overwriteError\" class=\"rounded-md bg-red-50 p-4 mt-3\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{overwriteError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
49624
|
+
module.exports = "<div class=\"chat-message-script relative border rounded bg-surface my-1 text-content text-sm overflow-hidden\">\n <div class=\"flex border-b py-1 pl-1 text-xs font-medium bg-surface\">\n <button\n class=\"px-4 py-1 border-r border-edge-strong text-content-secondary font-semibold transition-colors duration-200 focus:outline-none\"\n :class=\"[\n 'rounded-l-md',\n activeTab === 'code'\n ? 'bg-gray-700 text-white shadow'\n : 'bg-muted hover:bg-muted text-gray-600'\n ]\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-4 py-1 text-content-secondary font-semibold transition-colors duration-200 focus:outline-none\"\n :class=\"[\n 'rounded-r-md',\n activeTab === 'output'\n ? 'bg-gray-700 text-white shadow'\n : 'bg-muted hover:bg-muted text-gray-600'\n ]\"\n @click=\"activeTab = 'output'\">\n Output\n </button>\n <div class=\"ml-auto mr-1 flex items-center\">\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-700 text-white border-none rounded cursor-pointer hover:bg-primary-hover transition-colors flex items-center\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"height: 1.2em;\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'code' && !isEditing\"\n class=\"px-2 py-1 mr-1 text-xs bg-page0 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click.stop=\"startEditing\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM4 13.5V16h2.5l7.086-7.086-2.828-2.828L4 13.5z\" />\n </svg>\n </button>\n <async-button\n v-if=\"!isEditing\"\n class=\"px-2 py-1 text-xs bg-primary hover:bg-primary-hover text-primary-text border-none rounded cursor-pointer transition-colors disabled:bg-gray-400\"\n @click=\"executeScript\">\n Run\n </async-button>\n <div class=\"relative ml-1\" ref=\"dropdown\">\n <button\n @click.stop=\"toggleDropdown\"\n class=\"px-1 py-1 text-xs hover:bg-gray-300 rounded flex items-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n <div\n v-if=\"showDropdown\"\n class=\"absolute right-0 z-10 mt-1 w-64 origin-top-right rounded-md bg-surface py-1 shadow-lg ring-1 ring-black/5\">\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"openCreateDashboardModal(); showDropdown = false\">\n Create Dashboard\n </button>\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"copyOutput(); showDropdown = false\">\n Copy Output As Text\n </button>\n <button\n v-if=\"canOverwriteDashboard\"\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"openOverwriteDashboardConfirmation(); showDropdown = false\">\n Overwrite Dashboard\n </button>\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"$emit('copyMessage'); showDropdown = false\">\n Copy Full Message\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"p-0 max-h-[50vh] max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] overflow-y-auto\" v-show=\"activeTab === 'code'\">\n <div v-if=\"isEditing\" class=\"flex flex-col space-y-2 chat-script-editor-wrap\">\n <div class=\"border border-edge\">\n <ace-editor\n v-model=\"editedScript\"\n mode=\"javascript\"\n :line-numbers=\"true\"\n class=\"w-full h-[45vh] min-h-[200px]\"\n />\n </div>\n <div class=\"flex justify-end gap-2 pb-2\">\n <button\n class=\"px-2 py-1 text-xs bg-gray-300 text-gray-800 border-none rounded cursor-pointer hover:bg-gray-400 transition-colors\"\n @click.stop=\"cancelEditing\">\n Cancel\n </button>\n <async-button\n class=\"px-2 py-1 text-xs bg-blue-600 text-white border-none rounded cursor-pointer hover:bg-blue-700 transition-colors disabled:bg-gray-400\"\n @click=\"executeScript\">\n Execute\n </async-button>\n </div>\n </div>\n <pre v-else class=\"whitespace-pre-wrap !my-0 bg-muted\"><code v-text=\"script\" ref=\"code\" :class=\"'language-' + language\"></code></pre>\n </div>\n\n <div class=\"p-3 whitespace-pre-wrap max-h-[50vh] overflow-y-auto bg-surface border-t max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] relative\" v-show=\"activeTab === 'output'\">\n <dashboard-result\n v-if=\"message.executionResult?.output != null\"\n :result=\"message.executionResult.output\">\n </dashboard-result>\n <pre v-else>No output</pre>\n\n <div v-if=\"message.executionResult?.logs?.length\" class=\"mt-3 pt-3 border-t border-edge\">\n <div class=\"text-xs font-semibold text-gray-600 uppercase tracking-wide\">Console</div>\n <pre class=\"mt-1 bg-muted text-content p-3 rounded whitespace-pre-wrap overflow-x-auto max-h-[280px]\">{{ message.executionResult.logs }}</pre>\n </div>\n </div>\n\n <modal ref=\"outputModal\" v-if=\"showDetailModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\">×</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-result\n v-if=\"message.executionResult?.output != null\"\n :result=\"message.executionResult.output\"\n :fullscreen=\"true\">\n </dashboard-result>\n <pre v-else class=\"whitespace-pre-wrap\">No output</pre>\n </div>\n </template>\n </modal>\n <modal v-if=\"showCreateDashboardModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showCreateDashboardModal = false\">×</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">Create Dashboard</div>\n <div class=\"mt-4\">\n <label class=\"block text-sm font-medium leading-6 text-content\">Title</label>\n <div class=\"mt-2\">\n <div class=\"w-full flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-teal-600\">\n <input type=\"text\" v-model=\"newDashboardTitle\" class=\"outline-none block flex-1 border-0 bg-transparent py-1.5 pl-1 text-content placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6\" placeholder=\"My Dashboard\">\n </div>\n </div>\n </div>\n <div class=\"my-4\">\n <label class=\"block text-sm font-medium leading-6 text-content\">Code</label>\n <div class=\"border border-edge\">\n <ace-editor\n v-model=\"dashboardCode\"\n mode=\"javascript\"\n :line-numbers=\"true\"\n class=\"h-[300px] w-full\"\n />\n </div>\n </div>\n <async-button\n @click=\"createDashboardFromScript\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Submit\n </async-button>\n <div v-if=\"createError\" class=\"rounded-md bg-red-50 p-4 mt-1\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{createError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"showOverwriteDashboardConfirmationModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showOverwriteDashboardConfirmationModal = false\">×</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">Overwrite Dashboard</div>\n <p class=\"mt-2 text-sm text-content-secondary\">\n This will replace the linked dashboard's code with the script below.\n </p>\n <p class=\"mt-1 text-xs text-gray-600 break-all\" v-if=\"targetDashboardId\">\n Dashboard ID: {{ targetDashboardId }}\n </p>\n <div class=\"my-4 border border-edge bg-page rounded\">\n <pre class=\"p-2 h-[300px] overflow-auto whitespace-pre-wrap text-xs\">{{ overwriteDashboardCode }}</pre>\n </div>\n <div class=\"flex items-center gap-2\">\n <async-button\n @click=\"confirmOverwriteDashboard\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Confirm Overwrite\n </async-button>\n <button\n class=\"px-2.5 py-1.5 rounded-md text-sm font-semibold text-content-secondary bg-gray-200 hover:bg-gray-300\"\n @click=\"showOverwriteDashboardConfirmationModal = false\">\n Cancel\n </button>\n </div>\n <div v-if=\"overwriteError\" class=\"rounded-md bg-red-50 p-4 mt-3\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{overwriteError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
49472
49625
|
|
|
49473
49626
|
/***/ },
|
|
49474
49627
|
|
|
@@ -49626,6 +49779,17 @@ module.exports = "<div>\n <div v-if=\"Array.isArray(result)\">\n <div v-for=
|
|
|
49626
49779
|
|
|
49627
49780
|
/***/ },
|
|
49628
49781
|
|
|
49782
|
+
/***/ "./frontend/src/dashboard-result/dashboard-table/dashboard-table.html"
|
|
49783
|
+
/*!****************************************************************************!*\
|
|
49784
|
+
!*** ./frontend/src/dashboard-result/dashboard-table/dashboard-table.html ***!
|
|
49785
|
+
\****************************************************************************/
|
|
49786
|
+
(module) {
|
|
49787
|
+
|
|
49788
|
+
"use strict";
|
|
49789
|
+
module.exports = "<div class=\"overflow-x-auto\">\n <table class=\"min-w-full border-separate border-spacing-0\">\n <thead v-if=\"hasColumns\" class=\"bg-slate-50\">\n <tr>\n <th\n v-for=\"(column, index) in columns\"\n :key=\"'column-' + index\"\n class=\"bg-slate-50 p-3 text-left text-sm font-semibold text-content border-b border-edge\"\n >\n {{ column }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(row, rowIndex) in rows\" :key=\"'row-' + rowIndex\" class=\"bg-surface hover:bg-slate-50\">\n <td\n v-for=\"(cell, columnIndex) in row\"\n :key=\"'cell-' + rowIndex + '-' + columnIndex\"\n class=\"p-3 text-sm text-content border-b border-edge\"\n >\n {{ displayValue(cell) }}\n </td>\n </tr>\n <tr v-if=\"!hasRows\">\n <td\n :colspan=\"Math.max(columns.length, 1)\"\n class=\"p-3 text-sm text-content-tertiary border-b border-edge\"\n >\n No rows\n </td>\n </tr>\n </tbody>\n </table>\n</div>\n";
|
|
49790
|
+
|
|
49791
|
+
/***/ },
|
|
49792
|
+
|
|
49629
49793
|
/***/ "./frontend/src/dashboard-result/dashboard-text/dashboard-text.html"
|
|
49630
49794
|
/*!**************************************************************************!*\
|
|
49631
49795
|
!*** ./frontend/src/dashboard-result/dashboard-text/dashboard-text.html ***!
|
|
@@ -50139,7 +50303,7 @@ module.exports = "<div class=\"w-full h-full flex items-center justify-center\">
|
|
|
50139
50303
|
(module) {
|
|
50140
50304
|
|
|
50141
50305
|
"use strict";
|
|
50142
|
-
module.exports = "<div class=\"p-4 space-y-6\">\n <div v-if=\"status === 'init'\">\n <img src=\"images/loader.gif\" alt=\"Loading\" />\n </div>\n <div v-else-if=\"status === 'error'\" class=\"text-red-600\">\n {{ errorMessage }}\n </div>\n <
|
|
50306
|
+
module.exports = "<div class=\"p-4 space-y-6\">\n <div v-if=\"status === 'init'\">\n <img src=\"images/loader.gif\" alt=\"Loading\" />\n </div>\n <div v-else-if=\"status === 'error'\" class=\"text-red-600\">\n {{ errorMessage }}\n </div>\n <template v-else-if=\"taskGroup\">\n <div class=\"pb-24\">\n <router-link :to=\"{ name: 'tasks' }\" class=\"inline-flex items-center gap-1 text-gray-500 hover:text-gray-700 mb-4\">\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"></path>\n </svg>\n Back to Task Groups\n </router-link>\n <div class=\"mb-4\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Filter by Date:</label>\n <select v-model=\"selectedRange\" @change=\"updateDateRange\" class=\"border-gray-300 rounded-md shadow-sm w-full p-2 max-w-xs\">\n <option v-for=\"option in dateFilters\" :key=\"option.value\" :value=\"option.value\">\n {{ option.label }}\n </option>\n </select>\n </div>\n <task-details\n :task-group=\"taskGroup\"\n :back-to=\"{ name: 'tasks' }\"\n :show-back-button=\"false\"\n @task-created=\"onTaskCreated\"\n @task-cancelled=\"onTaskCancelled\"\n ></task-details>\n </div>\n <div\n v-if=\"numDocs > 0\"\n class=\"fixed bottom-0 left-0 right-0 z-10 px-4 py-4 bg-white border-t border-gray-200 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)]\"\n >\n <div class=\"flex flex-wrap items-center justify-between gap-4 max-w-6xl mx-auto\">\n <div class=\"flex items-center gap-6\">\n <p class=\"text-sm text-gray-600\">\n <span class=\"font-medium text-gray-900\">{{ Math.min((page - 1) * pageSize + 1, numDocs) }}–{{ Math.min(page * pageSize, numDocs) }}</span>\n <span class=\"mx-1\">of</span>\n <span class=\"font-medium text-gray-900\">{{ numDocs }}</span>\n <span class=\"ml-1 text-gray-500\">tasks</span>\n </p>\n <div class=\"flex items-center gap-2\">\n <label class=\"text-sm font-medium text-gray-700\">Per page</label>\n <select\n v-model.number=\"pageSize\"\n @change=\"onPageSizeChange\"\n class=\"border border-gray-300 rounded-md shadow-sm px-3 py-2 text-sm text-gray-700 bg-white hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-ultramarine-500 focus:border-ultramarine-500\"\n >\n <option v-for=\"n in pageSizeOptions\" :key=\"n\" :value=\"n\">{{ n }}</option>\n </select>\n </div>\n </div>\n <div class=\"flex items-center gap-1\">\n <button\n type=\"button\"\n :disabled=\"page <= 1\"\n @click=\"goToPage(page - 1)\"\n class=\"inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium rounded-md border transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white bg-white text-gray-700 border-gray-300 hover:bg-gray-50 hover:border-gray-400\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"></path>\n </svg>\n Previous\n </button>\n <span class=\"px-4 py-2 text-sm text-gray-600 min-w-[7rem] text-center\">\n Page <span class=\"font-semibold text-gray-900\">{{ page }}</span> of <span class=\"font-semibold text-gray-900\">{{ Math.max(1, Math.ceil(numDocs / pageSize)) }}</span>\n </span>\n <button\n type=\"button\"\n :disabled=\"page >= Math.ceil(numDocs / pageSize)\"\n @click=\"goToPage(page + 1)\"\n class=\"inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium rounded-md border transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white bg-white text-gray-700 border-gray-300 hover:bg-gray-50 hover:border-gray-400\"\n >\n Next\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n </button>\n </div>\n </div>\n </div>\n </template>\n</div>\n";
|
|
50143
50307
|
|
|
50144
50308
|
/***/ },
|
|
50145
50309
|
|
|
@@ -50161,7 +50325,7 @@ module.exports = "<div class=\"p-4 space-y-6\">\n <div v-if=\"status === 'init'
|
|
|
50161
50325
|
(module) {
|
|
50162
50326
|
|
|
50163
50327
|
"use strict";
|
|
50164
|
-
module.exports = "<div class=\"p-4 space-y-6\">\n <div class=\"flex items-center justify-between\">\n <div>\n <button @click=\"goBack\" class=\"text-content-tertiary hover:text-content-secondary mb-2\">\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"></path>\n </svg>\n {{ backLabel }}\n </button>\n <h1 class=\"text-2xl font-bold text-content-secondary\">{{ taskGroup.name }}</h1>\n <p class=\"text-content-tertiary\">Total: {{ taskGroup.totalCount }} tasks</p>\n </div>\n\n </div>\n\n <!-- Status Summary -->\n <div class=\"space-y-3\">\n <div class=\"flex items-center justify-between\">\n <span class=\"text-sm font-medium text-content-secondary\">Status</span>\n <div class=\"flex rounded-md shadow-sm\" role=\"group\">\n <button\n type=\"button\"\n @click=\"statusView = 'summary'\"\n class=\"px-3 py-1.5 text-sm font-medium rounded-l-md border transition-colors\"\n :class=\"statusView === 'summary' ? 'bg-primary text-primary-text border-primary' : 'bg-surface text-content-secondary border-edge-strong hover:bg-page'\"\n >\n Summary\n </button>\n <button\n type=\"button\"\n @click=\"statusView = 'chart'\"\n class=\"px-3 py-1.5 text-sm font-medium rounded-r-md border border-l-0 transition-colors\"\n :class=\"statusView === 'chart' ? 'bg-primary text-primary-text border-primary' : 'bg-surface text-content-secondary border-edge-strong hover:bg-page'\"\n >\n Chart\n </button>\n </div>\n </div>\n <!-- Summary view -->\n <div v-show=\"statusView === 'summary'\" class=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <button \n @click=\"filterByStatus('pending')\"\n class=\"bg-yellow-50 border border-yellow-200 rounded-md p-3 text-center hover:bg-yellow-100 transition-colors cursor-pointer\"\n :class=\"{ 'ring-2 ring-yellow-400': currentFilter === 'pending' }\"\n >\n <div class=\"text-xs text-yellow-600 font-medium\">Pending</div>\n <div class=\"text-lg font-bold text-yellow-700\">{{ taskGroup.statusCounts.pending || 0 }}</div>\n </button>\n <button \n @click=\"filterByStatus('succeeded')\"\n class=\"bg-green-50 border border-green-200 rounded-md p-3 text-center hover:bg-green-100 transition-colors cursor-pointer\"\n :class=\"{ 'ring-2 ring-green-400': currentFilter === 'succeeded' }\"\n >\n <div class=\"text-xs text-green-600 font-medium\">Succeeded</div>\n <div class=\"text-lg font-bold text-green-700\">{{ taskGroup.statusCounts.succeeded || 0 }}</div>\n </button>\n <button \n @click=\"filterByStatus('failed')\"\n class=\"bg-red-50 border border-red-200 rounded-md p-3 text-center hover:bg-red-100 transition-colors cursor-pointer\"\n :class=\"{ 'ring-2 ring-red-400': currentFilter === 'failed' }\"\n >\n <div class=\"text-xs text-red-600 font-medium\">Failed</div>\n <div class=\"text-lg font-bold text-red-700\">{{ taskGroup.statusCounts.failed || 0 }}</div>\n </button>\n <button \n @click=\"filterByStatus('cancelled')\"\n class=\"bg-page border border-edge rounded-md p-3 text-center hover:bg-muted transition-colors cursor-pointer\"\n :class=\"{ 'ring-2 ring-gray-400': currentFilter === 'cancelled' }\"\n >\n <div class=\"text-xs text-gray-600 font-medium\">Cancelled</div>\n <div class=\"text-lg font-bold text-content-secondary\">{{ taskGroup.statusCounts.cancelled || 0 }}</div>\n </button>\n </div>\n <!-- Chart view -->\n <div v-show=\"statusView === 'chart'\" class=\"flex flex-col items-center justify-center bg-surface border border-edge rounded-lg p-4 gap-3\" style=\"min-height: 280px;\">\n <div v-if=\"taskGroup.totalCount > 0\" class=\"w-[240px] h-[240px] shrink-0\">\n <canvas ref=\"statusPieChart\" width=\"240\" height=\"240\" class=\"block\"></canvas>\n </div>\n <p v-else class=\"text-content-tertiary text-sm py-8\">No tasks to display</p>\n <!-- Selection labels: show which segment is selected (click to filter) -->\n <div v-if=\"taskGroup.totalCount > 0\" class=\"flex flex-wrap justify-center gap-2\">\n <button\n v-for=\"status in statusOrderForDisplay\"\n :key=\"status\"\n type=\"button\"\n class=\"text-xs px-2 py-1 rounded-full font-medium transition-all cursor-pointer\"\n :class=\"currentFilter === status ? getStatusPillClass(status) : 'bg-muted text-content-tertiary hover:bg-muted'\"\n @click=\"filterByStatus(status)\"\n >\n {{ statusLabel(status) }}\n </button>\n </div>\n </div>\n </div>\n\n <!-- Task List -->\n <div class=\"bg-surface rounded-lg shadow\">\n <div class=\"px-6 py-6 border-b border-edge flex items-center justify-between bg-page\">\n <h2 class=\"text-xl font-bold text-content\">\n Individual Tasks\n <span v-if=\"currentFilter\" class=\"ml-3 text-base font-semibold text-primary\">\n (Filtered by {{ currentFilter }})\n </span>\n </h2>\n <button \n v-if=\"currentFilter\"\n @click=\"clearFilter\"\n class=\"text-sm font-semibold text-primary hover:text-primary\"\n >\n Show All\n </button>\n </div>\n <div class=\"divide-y divide-gray-200\">\n <div v-for=\"task in sortedTasks\" :key=\"task.id\" class=\"p-6\">\n <div class=\"flex items-start justify-between\">\n <div class=\"flex-1\">\n <div class=\"flex items-center gap-3 mb-2\">\n <span class=\"text-sm font-medium text-content\">Task ID: {{ task.id }}</span>\n <router-link\n v-if=\"backTo\"\n :to=\"taskDetailRoute(task)\"\n class=\"text-sm text-primary hover:text-primary font-medium\"\n >\n View details\n </router-link>\n <span\n class=\"text-xs px-2 py-1 rounded-full font-medium\"\n :class=\"getStatusColor(task.status)\"\n >\n {{ task.status }}\n </span>\n </div>\n \n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mb-4\">\n <div>\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Scheduled At</label>\n <div class=\"text-sm text-content\">{{ formatDate(task.scheduledAt) }}</div>\n </div>\n <div v-if=\"task.startedAt\">\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Started At</label>\n <div class=\"text-sm text-content\">{{ formatDate(task.startedAt) }}</div>\n </div>\n <div v-if=\"task.completedAt\">\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Completed At</label>\n <div class=\"text-sm text-content\">{{ formatDate(task.completedAt) }}</div>\n </div>\n <div v-if=\"task.error\">\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Error</label>\n <div class=\"text-sm text-red-600\">{{ task.error }}</div>\n </div>\n </div>\n\n <!-- Task Parameters -->\n <div v-if=\"task.parameters && Object.keys(task.parameters).length > 0\">\n <label class=\"block text-sm font-medium text-content-secondary mb-2\">Parameters</label>\n <div class=\"bg-page rounded-md p-3\">\n <pre class=\"text-sm text-gray-800 whitespace-pre-wrap\">{{ JSON.stringify(task.parameters, null, 2) }}</pre>\n </div>\n </div>\n </div>\n \n <div class=\"flex flex-col gap-3 ml-6\">\n <button \n @click=\"showRescheduleConfirmation(task)\"\n class=\"flex items-center justify-center gap-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:from-blue-600 hover:to-blue-700 transform hover:scale-105 transition-all duration-200 shadow-md hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none\"\n :disabled=\"task.status === 'in_progress'\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n Reschedule\n </button>\n <button \n @click=\"showRunConfirmation(task)\"\n class=\"flex items-center justify-center gap-2 bg-gradient-to-r from-green-500 to-green-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:from-green-600 hover:to-green-700 transform hover:scale-105 transition-all duration-200 shadow-md hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none\"\n :disabled=\"task.status === 'in_progress'\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 3l14 9-14 9V3z\"></path>\n </svg>\n Run Now\n </button>\n <button \n v-if=\"task.status === 'pending'\"\n @click=\"showCancelConfirmation(task)\"\n class=\"flex items-center justify-center gap-2 bg-gradient-to-r from-red-500 to-red-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:from-red-600 hover:to-red-700 transform hover:scale-105 transition-all duration-200 shadow-md hover:shadow-lg\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n </svg>\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Reschedule Confirmation Modal -->\n <modal v-if=\"showRescheduleModal\" containerClass=\"!max-w-md\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showRescheduleModal = false;\" role=\"button\" aria-label=\"Close modal\">×</div>\n <div class=\"p-6\">\n <div class=\"flex items-center mb-4\">\n <div class=\"flex-shrink-0\">\n <svg class=\"w-6 h-6 text-blue-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-lg font-medium text-content\">Reschedule Task</h3>\n </div>\n </div>\n <div class=\"mb-4\">\n <p class=\"text-sm text-gray-600\">\n Are you sure you want to reschedule task <strong>{{ selectedTask?.id }}</strong>?\n </p>\n <p class=\"text-sm text-content-tertiary mt-2\">\n This will reset the task's status and schedule it to run again.\n </p>\n \n <div class=\"mt-4\">\n <label for=\"newScheduledTime\" class=\"block text-sm font-medium text-content-secondary mb-2\">\n New Scheduled Time\n </label>\n <input\n id=\"newScheduledTime\"\n v-model=\"newScheduledTime\"\n type=\"datetime-local\"\n class=\"w-full px-3 py-2 border border-edge-strong rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500\"\n required\n />\n </div>\n </div>\n <div class=\"flex gap-3\">\n <button \n @click=\"confirmRescheduleTask\"\n class=\"flex-1 bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 font-medium\"\n >\n Reschedule\n </button>\n <button \n @click=\"showRescheduleModal = false\"\n class=\"flex-1 bg-gray-300 text-content-secondary px-4 py-2 rounded-md hover:bg-gray-400 font-medium\"\n >\n Cancel\n </button>\n </div>\n </div>\n </template>\n </modal>\n\n <!-- Run Task Confirmation Modal -->\n <modal v-if=\"showRunModal\" containerClass=\"!max-w-md\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showRunModal = false;\" role=\"button\" aria-label=\"Close modal\">×</div>\n <div class=\"p-6\">\n <div class=\"flex items-center mb-4\">\n <div class=\"flex-shrink-0\">\n <svg class=\"w-6 h-6 text-green-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-lg font-medium text-content\">Run Task Now</h3>\n </div>\n </div>\n <div class=\"mb-4\">\n <p class=\"text-sm text-gray-600\">\n Are you sure you want to run task <strong>{{ selectedTask?.id }}</strong> immediately?\n </p>\n <p class=\"text-sm text-content-tertiary mt-2\">\n This will execute the task right away, bypassing its scheduled time.\n </p>\n </div>\n <div class=\"flex gap-3\">\n <button \n @click=\"confirmRunTask\"\n class=\"flex-1 bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 font-medium\"\n >\n Run Now\n </button>\n <button \n @click=\"showRunModal = false\"\n class=\"flex-1 bg-gray-300 text-content-secondary px-4 py-2 rounded-md hover:bg-gray-400 font-medium\"\n >\n Cancel\n </button>\n </div>\n </div>\n </template>\n </modal>\n\n <!-- Cancel Task Confirmation Modal -->\n <modal v-if=\"showCancelModal\" containerClass=\"!max-w-md\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showCancelModal = false;\" role=\"button\" aria-label=\"Close modal\">×</div>\n <div class=\"p-6\">\n <div class=\"flex items-center mb-4\">\n <div class=\"flex-shrink-0\">\n <svg class=\"w-6 h-6 text-red-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-lg font-medium text-content\">Cancel Task</h3>\n </div>\n </div>\n <div class=\"mb-4\">\n <p class=\"text-sm text-gray-600\">\n Are you sure you want to cancel task <strong>{{ selectedTask?.id }}</strong>?\n </p>\n <p class=\"text-sm text-content-tertiary mt-2\">\n This will permanently cancel the task and it cannot be undone.\n </p>\n </div>\n <div class=\"flex gap-3\">\n <button \n @click=\"confirmCancelTask\"\n class=\"flex-1 bg-red-600 text-white px-4 py-2 rounded-md hover:bg-red-700 font-medium\"\n >\n Cancel Task\n </button>\n <button \n @click=\"showCancelModal = false\"\n class=\"flex-1 bg-gray-300 text-content-secondary px-4 py-2 rounded-md hover:bg-gray-400 font-medium\"\n >\n Keep Task\n </button>\n </div>\n </div>\n </template>\n </modal>\n</div>\n\n";
|
|
50328
|
+
module.exports = "<div class=\"p-4 space-y-6\">\n <div class=\"flex items-center justify-between\">\n <div>\n <button v-if=\"showBackButton\" @click=\"goBack\" class=\"text-content-tertiary hover:text-content-secondary mb-2\">\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"></path>\n </svg>\n {{ backLabel }}\n </button>\n <h1 class=\"text-2xl font-bold text-content-secondary\">{{ taskGroup.name }}</h1>\n <p class=\"text-content-tertiary\">Total: {{ taskGroup.totalCount }} tasks</p>\n </div>\n\n </div>\n\n <!-- Status Summary -->\n <div class=\"space-y-3\">\n <div class=\"flex items-center justify-between\">\n <span class=\"text-sm font-medium text-content-secondary\">Status</span>\n <div class=\"flex rounded-md shadow-sm\" role=\"group\">\n <button\n type=\"button\"\n @click=\"statusView = 'summary'\"\n class=\"px-3 py-1.5 text-sm font-medium rounded-l-md border transition-colors\"\n :class=\"statusView === 'summary' ? 'bg-primary text-primary-text border-primary' : 'bg-surface text-content-secondary border-edge-strong hover:bg-page'\"\n >\n Summary\n </button>\n <button\n type=\"button\"\n @click=\"statusView = 'chart'\"\n class=\"px-3 py-1.5 text-sm font-medium rounded-r-md border border-l-0 transition-colors\"\n :class=\"statusView === 'chart' ? 'bg-primary text-primary-text border-primary' : 'bg-surface text-content-secondary border-edge-strong hover:bg-page'\"\n >\n Chart\n </button>\n </div>\n </div>\n <!-- Summary view -->\n <div v-show=\"statusView === 'summary'\" class=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <button \n @click=\"filterByStatus('pending')\"\n class=\"bg-yellow-50 border border-yellow-200 rounded-md p-3 text-center hover:bg-yellow-100 transition-colors cursor-pointer\"\n :class=\"{ 'ring-2 ring-yellow-400': currentFilter === 'pending' }\"\n >\n <div class=\"text-xs text-yellow-600 font-medium\">Pending</div>\n <div class=\"text-lg font-bold text-yellow-700\">{{ taskGroup.statusCounts.pending || 0 }}</div>\n </button>\n <button \n @click=\"filterByStatus('succeeded')\"\n class=\"bg-green-50 border border-green-200 rounded-md p-3 text-center hover:bg-green-100 transition-colors cursor-pointer\"\n :class=\"{ 'ring-2 ring-green-400': currentFilter === 'succeeded' }\"\n >\n <div class=\"text-xs text-green-600 font-medium\">Succeeded</div>\n <div class=\"text-lg font-bold text-green-700\">{{ taskGroup.statusCounts.succeeded || 0 }}</div>\n </button>\n <button \n @click=\"filterByStatus('failed')\"\n class=\"bg-red-50 border border-red-200 rounded-md p-3 text-center hover:bg-red-100 transition-colors cursor-pointer\"\n :class=\"{ 'ring-2 ring-red-400': currentFilter === 'failed' }\"\n >\n <div class=\"text-xs text-red-600 font-medium\">Failed</div>\n <div class=\"text-lg font-bold text-red-700\">{{ taskGroup.statusCounts.failed || 0 }}</div>\n </button>\n <button \n @click=\"filterByStatus('cancelled')\"\n class=\"bg-page border border-edge rounded-md p-3 text-center hover:bg-muted transition-colors cursor-pointer\"\n :class=\"{ 'ring-2 ring-gray-400': currentFilter === 'cancelled' }\"\n >\n <div class=\"text-xs text-gray-600 font-medium\">Cancelled</div>\n <div class=\"text-lg font-bold text-content-secondary\">{{ taskGroup.statusCounts.cancelled || 0 }}</div>\n </button>\n </div>\n <!-- Chart view -->\n <div v-show=\"statusView === 'chart'\" class=\"flex flex-col items-center justify-center bg-surface border border-edge rounded-lg p-4 gap-3\" style=\"min-height: 280px;\">\n <div v-if=\"taskGroup.totalCount > 0\" class=\"w-[240px] h-[240px] shrink-0\">\n <canvas ref=\"statusPieChart\" width=\"240\" height=\"240\" class=\"block\"></canvas>\n </div>\n <p v-else class=\"text-content-tertiary text-sm py-8\">No tasks to display</p>\n <!-- Selection labels: show which segment is selected (click to filter) -->\n <div v-if=\"taskGroup.totalCount > 0\" class=\"flex flex-wrap justify-center gap-2\">\n <button\n v-for=\"status in statusOrderForDisplay\"\n :key=\"status\"\n type=\"button\"\n class=\"text-xs px-2 py-1 rounded-full font-medium transition-all cursor-pointer\"\n :class=\"currentFilter === status ? getStatusPillClass(status) : 'bg-muted text-content-tertiary hover:bg-muted'\"\n @click=\"filterByStatus(status)\"\n >\n {{ statusLabel(status) }}\n </button>\n </div>\n </div>\n </div>\n\n <!-- Task List -->\n <div class=\"bg-surface rounded-lg shadow\">\n <div class=\"px-6 py-6 border-b border-edge flex items-center justify-between bg-page\">\n <h2 class=\"text-xl font-bold text-content\">\n Individual Tasks\n <span v-if=\"currentFilter\" class=\"ml-3 text-base font-semibold text-primary\">\n (Filtered by {{ currentFilter }})\n </span>\n </h2>\n <button \n v-if=\"currentFilter\"\n @click=\"clearFilter\"\n class=\"text-sm font-semibold text-primary hover:text-primary\"\n >\n Show All\n </button>\n </div>\n <div class=\"divide-y divide-gray-200\">\n <div v-for=\"task in sortedTasks\" :key=\"task.id\" class=\"p-6\">\n <div class=\"flex items-start justify-between\">\n <div class=\"flex-1\">\n <div class=\"flex items-center gap-3 mb-2\">\n <span class=\"text-sm font-medium text-content\">Task ID: {{ task.id }}</span>\n <router-link\n v-if=\"backTo\"\n :to=\"taskDetailRoute(task)\"\n class=\"text-sm text-primary hover:text-primary font-medium\"\n >\n View details\n </router-link>\n <span\n class=\"text-xs px-2 py-1 rounded-full font-medium\"\n :class=\"getStatusColor(task.status)\"\n >\n {{ task.status }}\n </span>\n </div>\n \n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mb-4\">\n <div>\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Scheduled At</label>\n <div class=\"text-sm text-content\">{{ formatDate(task.scheduledAt) }}</div>\n </div>\n <div v-if=\"task.startedAt\">\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Started At</label>\n <div class=\"text-sm text-content\">{{ formatDate(task.startedAt) }}</div>\n </div>\n <div v-if=\"task.completedAt\">\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Completed At</label>\n <div class=\"text-sm text-content\">{{ formatDate(task.completedAt) }}</div>\n </div>\n <div v-if=\"task.error\">\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Error</label>\n <div class=\"text-sm text-red-600\">{{ task.error }}</div>\n </div>\n </div>\n\n <!-- Task Parameters -->\n <div v-if=\"task.parameters && Object.keys(task.parameters).length > 0\">\n <label class=\"block text-sm font-medium text-content-secondary mb-2\">Parameters</label>\n <div class=\"bg-page rounded-md p-3\">\n <pre class=\"text-sm text-gray-800 whitespace-pre-wrap\">{{ JSON.stringify(task.parameters, null, 2) }}</pre>\n </div>\n </div>\n </div>\n \n <div class=\"flex flex-col gap-3 ml-6\">\n <button \n @click=\"showRescheduleConfirmation(task)\"\n class=\"flex items-center justify-center gap-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:from-blue-600 hover:to-blue-700 transform hover:scale-105 transition-all duration-200 shadow-md hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none\"\n :disabled=\"task.status === 'in_progress'\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n Reschedule\n </button>\n <button \n @click=\"showRunConfirmation(task)\"\n class=\"flex items-center justify-center gap-2 bg-gradient-to-r from-green-500 to-green-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:from-green-600 hover:to-green-700 transform hover:scale-105 transition-all duration-200 shadow-md hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none\"\n :disabled=\"task.status === 'in_progress'\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 3l14 9-14 9V3z\"></path>\n </svg>\n Run Now\n </button>\n <button \n v-if=\"task.status === 'pending'\"\n @click=\"showCancelConfirmation(task)\"\n class=\"flex items-center justify-center gap-2 bg-gradient-to-r from-red-500 to-red-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:from-red-600 hover:to-red-700 transform hover:scale-105 transition-all duration-200 shadow-md hover:shadow-lg\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n </svg>\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Reschedule Confirmation Modal -->\n <modal v-if=\"showRescheduleModal\" containerClass=\"!max-w-md\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showRescheduleModal = false;\" role=\"button\" aria-label=\"Close modal\">×</div>\n <div class=\"p-6\">\n <div class=\"flex items-center mb-4\">\n <div class=\"flex-shrink-0\">\n <svg class=\"w-6 h-6 text-blue-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-lg font-medium text-content\">Reschedule Task</h3>\n </div>\n </div>\n <div class=\"mb-4\">\n <p class=\"text-sm text-gray-600\">\n Are you sure you want to reschedule task <strong>{{ selectedTask?.id }}</strong>?\n </p>\n <p class=\"text-sm text-content-tertiary mt-2\">\n This will reset the task's status and schedule it to run again.\n </p>\n \n <div class=\"mt-4\">\n <label for=\"newScheduledTime\" class=\"block text-sm font-medium text-content-secondary mb-2\">\n New Scheduled Time\n </label>\n <input\n id=\"newScheduledTime\"\n v-model=\"newScheduledTime\"\n type=\"datetime-local\"\n class=\"w-full px-3 py-2 border border-edge-strong rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500\"\n required\n />\n </div>\n </div>\n <div class=\"flex gap-3\">\n <button \n @click=\"confirmRescheduleTask\"\n class=\"flex-1 bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 font-medium\"\n >\n Reschedule\n </button>\n <button \n @click=\"showRescheduleModal = false\"\n class=\"flex-1 bg-gray-300 text-content-secondary px-4 py-2 rounded-md hover:bg-gray-400 font-medium\"\n >\n Cancel\n </button>\n </div>\n </div>\n </template>\n </modal>\n\n <!-- Run Task Confirmation Modal -->\n <modal v-if=\"showRunModal\" containerClass=\"!max-w-md\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showRunModal = false;\" role=\"button\" aria-label=\"Close modal\">×</div>\n <div class=\"p-6\">\n <div class=\"flex items-center mb-4\">\n <div class=\"flex-shrink-0\">\n <svg class=\"w-6 h-6 text-green-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-lg font-medium text-content\">Run Task Now</h3>\n </div>\n </div>\n <div class=\"mb-4\">\n <p class=\"text-sm text-gray-600\">\n Are you sure you want to run task <strong>{{ selectedTask?.id }}</strong> immediately?\n </p>\n <p class=\"text-sm text-content-tertiary mt-2\">\n This will execute the task right away, bypassing its scheduled time.\n </p>\n </div>\n <div class=\"flex gap-3\">\n <button \n @click=\"confirmRunTask\"\n class=\"flex-1 bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 font-medium\"\n >\n Run Now\n </button>\n <button \n @click=\"showRunModal = false\"\n class=\"flex-1 bg-gray-300 text-content-secondary px-4 py-2 rounded-md hover:bg-gray-400 font-medium\"\n >\n Cancel\n </button>\n </div>\n </div>\n </template>\n </modal>\n\n <!-- Cancel Task Confirmation Modal -->\n <modal v-if=\"showCancelModal\" containerClass=\"!max-w-md\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showCancelModal = false;\" role=\"button\" aria-label=\"Close modal\">×</div>\n <div class=\"p-6\">\n <div class=\"flex items-center mb-4\">\n <div class=\"flex-shrink-0\">\n <svg class=\"w-6 h-6 text-red-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-lg font-medium text-content\">Cancel Task</h3>\n </div>\n </div>\n <div class=\"mb-4\">\n <p class=\"text-sm text-gray-600\">\n Are you sure you want to cancel task <strong>{{ selectedTask?.id }}</strong>?\n </p>\n <p class=\"text-sm text-content-tertiary mt-2\">\n This will permanently cancel the task and it cannot be undone.\n </p>\n </div>\n <div class=\"flex gap-3\">\n <button \n @click=\"confirmCancelTask\"\n class=\"flex-1 bg-red-600 text-white px-4 py-2 rounded-md hover:bg-red-700 font-medium\"\n >\n Cancel Task\n </button>\n <button \n @click=\"showCancelModal = false\"\n class=\"flex-1 bg-gray-300 text-content-secondary px-4 py-2 rounded-md hover:bg-gray-400 font-medium\"\n >\n Keep Task\n </button>\n </div>\n </div>\n </template>\n </modal>\n</div>\n\n";
|
|
50165
50329
|
|
|
50166
50330
|
/***/ },
|
|
50167
50331
|
|
|
@@ -61497,7 +61661,7 @@ var src_default = VueToastificationPlugin;
|
|
|
61497
61661
|
(module) {
|
|
61498
61662
|
|
|
61499
61663
|
"use strict";
|
|
61500
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.3.
|
|
61664
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.3.1","description":"A Mongoose-native MongoDB UI with schema-aware autocomplete, AI-assisted queries, and dashboards that understand your models - not just your data.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.0","marked":"15.0.12","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","xss":"^1.0.15"},"peerDependencies":{"mongoose":"7.x || 8.x || ^9.0.0"},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js"}}');
|
|
61501
61665
|
|
|
61502
61666
|
/***/ }
|
|
61503
61667
|
|