@mongoosejs/studio 0.3.0 → 0.3.2
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/DEVGUIDE.md +8 -0
- package/backend/actions/ChatThread/createChatMessage.js +2 -0
- package/backend/actions/ChatThread/streamChatMessage.js +2 -0
- package/backend/actions/Dashboard/getDashboard.js +15 -11
- 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 +318 -148
- 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/chat/chat.js +2 -0
- package/frontend/src/dashboard/dashboard.js +13 -2
- 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 +9 -3
- package/frontend/src/navbar/navbar.js +3 -2
- 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/local.js +38 -0
- package/package.json +4 -1
- package/seed/connect.js +23 -0
- package/seed/index.js +101 -0
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;
|
|
@@ -1723,6 +1822,8 @@ module.exports = {
|
|
|
1723
1822
|
}
|
|
1724
1823
|
},
|
|
1725
1824
|
async mounted() {
|
|
1825
|
+
window.pageState = this;
|
|
1826
|
+
|
|
1726
1827
|
this.chatThreadId = this.threadId;
|
|
1727
1828
|
const { chatThreads } = await api.ChatThread.listChatThreads();
|
|
1728
1829
|
this.chatThreads = chatThreads;
|
|
@@ -2387,12 +2488,63 @@ module.exports = app => app.component('dashboard-result', {
|
|
|
2387
2488
|
if (value.$grid) {
|
|
2388
2489
|
return 'dashboard-grid';
|
|
2389
2490
|
}
|
|
2491
|
+
if (value.$table) {
|
|
2492
|
+
return 'dashboard-table';
|
|
2493
|
+
}
|
|
2390
2494
|
return 'dashboard-object';
|
|
2391
2495
|
}
|
|
2392
2496
|
}
|
|
2393
2497
|
});
|
|
2394
2498
|
|
|
2395
2499
|
|
|
2500
|
+
/***/ },
|
|
2501
|
+
|
|
2502
|
+
/***/ "./frontend/src/dashboard-result/dashboard-table/dashboard-table.js"
|
|
2503
|
+
/*!**************************************************************************!*\
|
|
2504
|
+
!*** ./frontend/src/dashboard-result/dashboard-table/dashboard-table.js ***!
|
|
2505
|
+
\**************************************************************************/
|
|
2506
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
2507
|
+
|
|
2508
|
+
"use strict";
|
|
2509
|
+
|
|
2510
|
+
|
|
2511
|
+
const template = __webpack_require__(/*! ./dashboard-table.html */ "./frontend/src/dashboard-result/dashboard-table/dashboard-table.html");
|
|
2512
|
+
|
|
2513
|
+
module.exports = app => app.component('dashboard-table', {
|
|
2514
|
+
template,
|
|
2515
|
+
props: ['value'],
|
|
2516
|
+
computed: {
|
|
2517
|
+
columns() {
|
|
2518
|
+
return Array.isArray(this.value?.$table?.columns) ? this.value.$table.columns : [];
|
|
2519
|
+
},
|
|
2520
|
+
rows() {
|
|
2521
|
+
return Array.isArray(this.value?.$table?.rows) ? this.value.$table.rows : [];
|
|
2522
|
+
},
|
|
2523
|
+
hasColumns() {
|
|
2524
|
+
return this.columns.length > 0;
|
|
2525
|
+
},
|
|
2526
|
+
hasRows() {
|
|
2527
|
+
return this.rows.length > 0;
|
|
2528
|
+
}
|
|
2529
|
+
},
|
|
2530
|
+
methods: {
|
|
2531
|
+
displayValue(cell) {
|
|
2532
|
+
if (cell == null) {
|
|
2533
|
+
return '';
|
|
2534
|
+
}
|
|
2535
|
+
if (typeof cell === 'object') {
|
|
2536
|
+
try {
|
|
2537
|
+
return JSON.stringify(cell);
|
|
2538
|
+
} catch (err) {
|
|
2539
|
+
return String(cell);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
return String(cell);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
});
|
|
2546
|
+
|
|
2547
|
+
|
|
2396
2548
|
/***/ },
|
|
2397
2549
|
|
|
2398
2550
|
/***/ "./frontend/src/dashboard-result/dashboard-text/dashboard-text.js"
|
|
@@ -2451,6 +2603,13 @@ module.exports = {
|
|
|
2451
2603
|
this.showEditor = !this.showEditor;
|
|
2452
2604
|
},
|
|
2453
2605
|
async updateCode(update) {
|
|
2606
|
+
if (!update?.doc) {
|
|
2607
|
+
const message = update?.error?.message || 'Dashboard update failed';
|
|
2608
|
+
console.error(update?.error || new Error(message));
|
|
2609
|
+
this.$toast.error(message);
|
|
2610
|
+
return;
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2454
2613
|
this.code = update.doc.code;
|
|
2455
2614
|
this.title = update.doc.title;
|
|
2456
2615
|
this.description = update.doc.description;
|
|
@@ -2475,7 +2634,9 @@ module.exports = {
|
|
|
2475
2634
|
this.dashboardResults.unshift({ error: { message: error.message || 'Evaluation failed' }, finishedEvaluatingAt: new Date() });
|
|
2476
2635
|
}
|
|
2477
2636
|
} catch (err) {
|
|
2478
|
-
|
|
2637
|
+
const message = err?.response?.data?.message || err?.message || 'Dashboard evaluation failed';
|
|
2638
|
+
console.error(err || new Error(message));
|
|
2639
|
+
this.$toast.error(message);
|
|
2479
2640
|
} finally {
|
|
2480
2641
|
this.status = 'loaded';
|
|
2481
2642
|
}
|
|
@@ -2571,7 +2732,9 @@ module.exports = {
|
|
|
2571
2732
|
return this.dashboardResults.length > 0 ? this.dashboardResults[0] : null;
|
|
2572
2733
|
}
|
|
2573
2734
|
},
|
|
2574
|
-
mounted: async function() {
|
|
2735
|
+
mounted: async function () {
|
|
2736
|
+
window.pageState = this;
|
|
2737
|
+
|
|
2575
2738
|
document.addEventListener('click', this.handleDocumentClick);
|
|
2576
2739
|
this.showEditor = this.$route.query.edit;
|
|
2577
2740
|
await this.loadInitial();
|
|
@@ -2599,7 +2762,7 @@ const template = __webpack_require__(/*! ./edit-dashboard.html */ "./frontend/sr
|
|
|
2599
2762
|
module.exports = app => app.component('edit-dashboard', {
|
|
2600
2763
|
template: template,
|
|
2601
2764
|
props: ['dashboardId', 'code', 'currentDescription', 'currentTitle'],
|
|
2602
|
-
emits: ['close'],
|
|
2765
|
+
emits: ['close', 'update'],
|
|
2603
2766
|
data: function() {
|
|
2604
2767
|
return {
|
|
2605
2768
|
status: 'loaded',
|
|
@@ -2620,7 +2783,7 @@ module.exports = app => app.component('edit-dashboard', {
|
|
|
2620
2783
|
async updateCode() {
|
|
2621
2784
|
this.status = 'loading';
|
|
2622
2785
|
try {
|
|
2623
|
-
const codeToSave = this.$refs.codeEditor ? this.$refs.codeEditor.getValue() : this.editCode;
|
|
2786
|
+
const codeToSave = this.$refs.codeEditor?.getValue ? this.$refs.codeEditor.getValue() : this.editCode;
|
|
2624
2787
|
const { doc } = await api.Dashboard.updateDashboard({
|
|
2625
2788
|
dashboardId: this.dashboardId,
|
|
2626
2789
|
code: codeToSave,
|
|
@@ -2636,7 +2799,8 @@ module.exports = app => app.component('edit-dashboard', {
|
|
|
2636
2799
|
this.$toast.success('Dashboard updated!');
|
|
2637
2800
|
this.closeEditor();
|
|
2638
2801
|
} catch (err) {
|
|
2639
|
-
|
|
2802
|
+
const message = err?.response?.data?.message || err?.message || 'Dashboard update failed';
|
|
2803
|
+
this.$emit('update', { error: { message } });
|
|
2640
2804
|
} finally {
|
|
2641
2805
|
this.status = 'loaded';
|
|
2642
2806
|
}
|
|
@@ -6946,6 +7110,7 @@ module.exports = app => app.component('models', {
|
|
|
6946
7110
|
}),
|
|
6947
7111
|
created() {
|
|
6948
7112
|
this.currentModel = this.model;
|
|
7113
|
+
this.setSearchTextFromRoute();
|
|
6949
7114
|
this.loadOutputPreference();
|
|
6950
7115
|
this.loadSelectedGeoField();
|
|
6951
7116
|
this.loadRecentlyViewedModels();
|
|
@@ -6959,6 +7124,7 @@ module.exports = app => app.component('models', {
|
|
|
6959
7124
|
this.destroyMap();
|
|
6960
7125
|
},
|
|
6961
7126
|
async mounted() {
|
|
7127
|
+
window.pageState = this;
|
|
6962
7128
|
this.onScroll = () => this.checkIfScrolledToBottom();
|
|
6963
7129
|
document.addEventListener('scroll', this.onScroll, true);
|
|
6964
7130
|
this.onPopState = () => this.initSearchFromUrl();
|
|
@@ -6982,6 +7148,7 @@ module.exports = app => app.component('models', {
|
|
|
6982
7148
|
}
|
|
6983
7149
|
};
|
|
6984
7150
|
document.addEventListener('keydown', this.onCtrlP, true);
|
|
7151
|
+
this.query = Object.assign({}, this.$route.query);
|
|
6985
7152
|
const { models, readyState } = await api.Model.listModels();
|
|
6986
7153
|
this.models = models;
|
|
6987
7154
|
await this.loadModelCounts();
|
|
@@ -7364,14 +7531,17 @@ module.exports = app => app.component('models', {
|
|
|
7364
7531
|
|
|
7365
7532
|
return params;
|
|
7366
7533
|
},
|
|
7367
|
-
|
|
7368
|
-
this.status = 'loading';
|
|
7369
|
-
this.query = Object.assign({}, this.$route.query); // important that this is here before the if statements
|
|
7534
|
+
setSearchTextFromRoute() {
|
|
7370
7535
|
if (this.$route.query?.search) {
|
|
7371
7536
|
this.searchText = this.$route.query.search;
|
|
7372
7537
|
} else {
|
|
7373
7538
|
this.searchText = '';
|
|
7374
7539
|
}
|
|
7540
|
+
},
|
|
7541
|
+
async initSearchFromUrl() {
|
|
7542
|
+
this.status = 'loading';
|
|
7543
|
+
this.query = Object.assign({}, this.$route.query); // important that this is here before the if statements
|
|
7544
|
+
this.setSearchTextFromRoute();
|
|
7375
7545
|
if (this.$route.query?.sort) {
|
|
7376
7546
|
const sort = eval(`(${this.$route.query.sort})`);
|
|
7377
7547
|
const path = Object.keys(sort)[0];
|
|
@@ -8129,7 +8299,8 @@ module.exports = app => app.component('navbar', {
|
|
|
8129
8299
|
showFlyout: false,
|
|
8130
8300
|
darkMode: typeof localStorage !== 'undefined' && localStorage.getItem('studio-theme') === 'dark'
|
|
8131
8301
|
}),
|
|
8132
|
-
mounted: function() {
|
|
8302
|
+
mounted: function () {
|
|
8303
|
+
window.navbar = this;
|
|
8133
8304
|
const mobileMenuMask = document.querySelector('#mobile-menu-mask');
|
|
8134
8305
|
const mobileMenu = document.querySelector('#mobile-menu');
|
|
8135
8306
|
const openBtn = document.querySelector('#open-mobile-menu');
|
|
@@ -8183,7 +8354,7 @@ module.exports = app => app.component('navbar', {
|
|
|
8183
8354
|
} else {
|
|
8184
8355
|
return 'https://www.npmjs.com/package/@mongoosejs/task';
|
|
8185
8356
|
}
|
|
8186
|
-
|
|
8357
|
+
|
|
8187
8358
|
}
|
|
8188
8359
|
},
|
|
8189
8360
|
methods: {
|
|
@@ -8413,6 +8584,7 @@ module.exports = app => app.component('splash', {
|
|
|
8413
8584
|
// Page: all tasks with a given name. Reuses task-details to render the list (many tasks).
|
|
8414
8585
|
const template = __webpack_require__(/*! ./task-by-name.html */ "./frontend/src/task-by-name/task-by-name.html");
|
|
8415
8586
|
const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
|
|
8587
|
+
const { DATE_FILTERS, DATE_FILTER_VALUES, getDateRangeForRange } = __webpack_require__(/*! ../_util/dateRange */ "./frontend/src/_util/dateRange.js");
|
|
8416
8588
|
|
|
8417
8589
|
function buildTaskGroup(name, tasks) {
|
|
8418
8590
|
const statusCounts = { pending: 0, succeeded: 0, failed: 0, cancelled: 0, in_progress: 0, unknown: 0 };
|
|
@@ -8436,44 +8608,118 @@ function buildTaskGroup(name, tasks) {
|
|
|
8436
8608
|
};
|
|
8437
8609
|
}
|
|
8438
8610
|
|
|
8611
|
+
const PAGE_SIZE_OPTIONS = [25, 50, 100, 200];
|
|
8612
|
+
|
|
8439
8613
|
module.exports = app => app.component('task-by-name', {
|
|
8440
8614
|
template,
|
|
8441
8615
|
data: () => ({
|
|
8442
8616
|
status: 'init',
|
|
8443
8617
|
taskGroup: null,
|
|
8444
|
-
errorMessage: ''
|
|
8618
|
+
errorMessage: '',
|
|
8619
|
+
selectedRange: 'last_hour',
|
|
8620
|
+
start: null,
|
|
8621
|
+
end: null,
|
|
8622
|
+
dateFilters: DATE_FILTERS,
|
|
8623
|
+
page: 1,
|
|
8624
|
+
pageSize: 50,
|
|
8625
|
+
numDocs: 0,
|
|
8626
|
+
pageSizeOptions: PAGE_SIZE_OPTIONS,
|
|
8627
|
+
_loadId: 0,
|
|
8628
|
+
_lastQueryFilters: null
|
|
8445
8629
|
}),
|
|
8446
8630
|
computed: {
|
|
8447
8631
|
taskName() {
|
|
8448
8632
|
return this.$route.params.name || '';
|
|
8449
8633
|
}
|
|
8450
8634
|
},
|
|
8635
|
+
created() {
|
|
8636
|
+
const fromQuery = this.$route.query.dateRange;
|
|
8637
|
+
this.selectedRange = (fromQuery && DATE_FILTER_VALUES.includes(fromQuery)) ? fromQuery : 'last_hour';
|
|
8638
|
+
if (!this.$route.query.dateRange) {
|
|
8639
|
+
this.$router.replace({
|
|
8640
|
+
path: this.$route.path,
|
|
8641
|
+
query: { ...this.$route.query, dateRange: this.selectedRange }
|
|
8642
|
+
});
|
|
8643
|
+
}
|
|
8644
|
+
},
|
|
8451
8645
|
watch: {
|
|
8452
8646
|
taskName: {
|
|
8453
8647
|
immediate: true,
|
|
8454
8648
|
handler() {
|
|
8649
|
+
this.page = 1;
|
|
8455
8650
|
this.loadTasks();
|
|
8456
8651
|
}
|
|
8652
|
+
},
|
|
8653
|
+
'$route.query': {
|
|
8654
|
+
handler(query) {
|
|
8655
|
+
const dateRange = query.dateRange;
|
|
8656
|
+
if (dateRange && DATE_FILTER_VALUES.includes(dateRange) && this.selectedRange !== dateRange) {
|
|
8657
|
+
this.selectedRange = dateRange;
|
|
8658
|
+
}
|
|
8659
|
+
const effectiveDateRange = (dateRange && DATE_FILTER_VALUES.includes(dateRange)) ? dateRange : (this.selectedRange || 'last_hour');
|
|
8660
|
+
const effectiveStatus = query.status ?? '';
|
|
8661
|
+
const key = `${effectiveDateRange}|${effectiveStatus}`;
|
|
8662
|
+
if (this._lastQueryFilters === key) return;
|
|
8663
|
+
this.page = 1;
|
|
8664
|
+
this.loadTasks();
|
|
8665
|
+
},
|
|
8666
|
+
deep: true
|
|
8457
8667
|
}
|
|
8458
8668
|
},
|
|
8459
8669
|
methods: {
|
|
8670
|
+
updateDateRange() {
|
|
8671
|
+
this.page = 1;
|
|
8672
|
+
this.$router.replace({
|
|
8673
|
+
path: this.$route.path,
|
|
8674
|
+
query: { ...this.$route.query, dateRange: this.selectedRange }
|
|
8675
|
+
});
|
|
8676
|
+
},
|
|
8677
|
+
goToPage(page) {
|
|
8678
|
+
const maxPage = Math.max(1, Math.ceil(this.numDocs / this.pageSize));
|
|
8679
|
+
const next = Math.max(1, Math.min(page, maxPage));
|
|
8680
|
+
if (next === this.page) return;
|
|
8681
|
+
this.page = next;
|
|
8682
|
+
this.loadTasks();
|
|
8683
|
+
},
|
|
8684
|
+
onPageSizeChange() {
|
|
8685
|
+
this.page = 1;
|
|
8686
|
+
this.loadTasks();
|
|
8687
|
+
},
|
|
8460
8688
|
async loadTasks() {
|
|
8461
8689
|
if (!this.taskName) return;
|
|
8690
|
+
const loadId = ++this._loadId;
|
|
8462
8691
|
this.status = 'init';
|
|
8463
8692
|
this.taskGroup = null;
|
|
8464
8693
|
this.errorMessage = '';
|
|
8465
|
-
const
|
|
8466
|
-
|
|
8467
|
-
|
|
8694
|
+
const dateRangeFromQuery = this.$route.query.dateRange;
|
|
8695
|
+
const dateRange = (dateRangeFromQuery && DATE_FILTER_VALUES.includes(dateRangeFromQuery))
|
|
8696
|
+
? dateRangeFromQuery
|
|
8697
|
+
: (this.selectedRange || 'last_hour');
|
|
8698
|
+
this.selectedRange = dateRange;
|
|
8699
|
+
const { start, end } = getDateRangeForRange(dateRange);
|
|
8700
|
+
this.start = start;
|
|
8701
|
+
this.end = end;
|
|
8702
|
+
const skip = (this.page - 1) * this.pageSize;
|
|
8703
|
+
const params = {
|
|
8704
|
+
name: this.taskName,
|
|
8705
|
+
start: start instanceof Date ? start.toISOString() : start,
|
|
8706
|
+
end: end instanceof Date ? end.toISOString() : end,
|
|
8707
|
+
skip,
|
|
8708
|
+
limit: this.pageSize
|
|
8709
|
+
};
|
|
8710
|
+
const statusFromQuery = this.$route.query.status;
|
|
8711
|
+
if (statusFromQuery) params.status = statusFromQuery;
|
|
8712
|
+
this._lastQueryFilters = `${dateRange}|${statusFromQuery ?? ''}`;
|
|
8468
8713
|
try {
|
|
8469
|
-
const { tasks } = await api.Task.getTasks(
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
end
|
|
8473
|
-
});
|
|
8714
|
+
const { tasks, numDocs, statusCounts } = await api.Task.getTasks(params);
|
|
8715
|
+
if (loadId !== this._loadId) return;
|
|
8716
|
+
this.numDocs = numDocs ?? tasks.length;
|
|
8474
8717
|
this.taskGroup = buildTaskGroup(this.taskName, tasks);
|
|
8718
|
+
this.taskGroup.totalCount = this.numDocs;
|
|
8719
|
+
if (statusCounts) this.taskGroup.statusCounts = statusCounts;
|
|
8475
8720
|
this.status = 'loaded';
|
|
8476
8721
|
} catch (err) {
|
|
8722
|
+
if (loadId !== this._loadId) return;
|
|
8477
8723
|
this.status = 'error';
|
|
8478
8724
|
this.errorMessage = err?.response?.data?.message || err.message || 'Failed to load tasks';
|
|
8479
8725
|
}
|
|
@@ -8636,7 +8882,8 @@ const PIE_HOVER = ['#ca8a04', '#16a34a', '#dc2626', '#4b5563'];
|
|
|
8636
8882
|
module.exports = app => app.component('task-details', {
|
|
8637
8883
|
props: {
|
|
8638
8884
|
taskGroup: { type: Object, required: true },
|
|
8639
|
-
backTo: { type: Object, default: null }
|
|
8885
|
+
backTo: { type: Object, default: null },
|
|
8886
|
+
showBackButton: { type: Boolean, default: true }
|
|
8640
8887
|
},
|
|
8641
8888
|
data: () => ({
|
|
8642
8889
|
currentFilter: null,
|
|
@@ -8977,25 +9224,17 @@ module.exports = app => app.component('task-details', {
|
|
|
8977
9224
|
|
|
8978
9225
|
const template = __webpack_require__(/*! ./tasks.html */ "./frontend/src/tasks/tasks.html");
|
|
8979
9226
|
const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
|
|
9227
|
+
const { DATE_FILTERS, getDateRangeForRange } = __webpack_require__(/*! ../_util/dateRange */ "./frontend/src/_util/dateRange.js");
|
|
8980
9228
|
|
|
8981
9229
|
module.exports = app => app.component('tasks', {
|
|
8982
9230
|
data: () => ({
|
|
8983
9231
|
status: 'init',
|
|
8984
|
-
|
|
8985
|
-
|
|
9232
|
+
statusCounts: { pending: 0, succeeded: 0, failed: 0, cancelled: 0 },
|
|
9233
|
+
tasksByName: [],
|
|
8986
9234
|
selectedRange: 'last_hour',
|
|
8987
9235
|
start: null,
|
|
8988
9236
|
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
|
-
],
|
|
9237
|
+
dateFilters: DATE_FILTERS,
|
|
8999
9238
|
selectedStatus: 'all',
|
|
9000
9239
|
statusFilters: [
|
|
9001
9240
|
{ label: 'All', value: 'all' },
|
|
@@ -9025,26 +9264,31 @@ module.exports = app => app.component('tasks', {
|
|
|
9025
9264
|
params.status = this.selectedStatus;
|
|
9026
9265
|
}
|
|
9027
9266
|
|
|
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;
|
|
9267
|
+
if (this.start != null && this.end != null) {
|
|
9268
|
+
params.start = this.start instanceof Date ? this.start.toISOString() : this.start;
|
|
9269
|
+
params.end = this.end instanceof Date ? this.end.toISOString() : this.end;
|
|
9270
|
+
} else if (this.start != null) {
|
|
9271
|
+
params.start = this.start instanceof Date ? this.start.toISOString() : this.start;
|
|
9033
9272
|
}
|
|
9034
9273
|
|
|
9035
9274
|
if (this.searchQuery.trim()) {
|
|
9036
9275
|
params.name = this.searchQuery.trim();
|
|
9037
9276
|
}
|
|
9038
9277
|
|
|
9039
|
-
const {
|
|
9040
|
-
this.
|
|
9041
|
-
this.
|
|
9278
|
+
const { statusCounts, tasksByName } = await api.Task.getTaskOverview(params);
|
|
9279
|
+
this.statusCounts = statusCounts || this.statusCounts;
|
|
9280
|
+
this.tasksByName = tasksByName || [];
|
|
9042
9281
|
},
|
|
9043
9282
|
openTaskGroupDetails(group) {
|
|
9044
|
-
|
|
9283
|
+
const query = { dateRange: this.selectedRange || 'last_hour' };
|
|
9284
|
+
if (this.selectedStatus && this.selectedStatus !== 'all') query.status = this.selectedStatus;
|
|
9285
|
+
this.$router.push({ path: `/tasks/${encodeURIComponent(group.name || '')}`, query });
|
|
9045
9286
|
},
|
|
9046
9287
|
openTaskGroupDetailsWithFilter(group, status) {
|
|
9047
|
-
|
|
9288
|
+
const query = { dateRange: this.selectedRange || 'last_hour' };
|
|
9289
|
+
if (status) query.status = status;
|
|
9290
|
+
else if (this.selectedStatus && this.selectedStatus !== 'all') query.status = this.selectedStatus;
|
|
9291
|
+
this.$router.push({ path: `/tasks/${encodeURIComponent(group.name || '')}`, query });
|
|
9048
9292
|
},
|
|
9049
9293
|
async onTaskCreated() {
|
|
9050
9294
|
// Refresh the task data when a new task is created
|
|
@@ -9188,114 +9432,24 @@ module.exports = app => app.component('tasks', {
|
|
|
9188
9432
|
}, 300);
|
|
9189
9433
|
},
|
|
9190
9434
|
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
|
-
|
|
9435
|
+
const { start, end } = getDateRangeForRange(this.selectedRange);
|
|
9239
9436
|
this.start = start;
|
|
9240
9437
|
this.end = end;
|
|
9241
|
-
|
|
9242
9438
|
await this.getTasks();
|
|
9243
9439
|
}
|
|
9244
9440
|
},
|
|
9245
9441
|
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
|
-
});
|
|
9442
|
+
pendingCount() {
|
|
9443
|
+
return this.statusCounts.pending || 0;
|
|
9287
9444
|
},
|
|
9288
9445
|
succeededCount() {
|
|
9289
|
-
return this.
|
|
9446
|
+
return this.statusCounts.succeeded || 0;
|
|
9290
9447
|
},
|
|
9291
9448
|
failedCount() {
|
|
9292
|
-
return this.
|
|
9449
|
+
return this.statusCounts.failed || 0;
|
|
9293
9450
|
},
|
|
9294
9451
|
cancelledCount() {
|
|
9295
|
-
return this.
|
|
9296
|
-
},
|
|
9297
|
-
pendingCount() {
|
|
9298
|
-
return this.groupedTasks.pending ? this.groupedTasks.pending.length : 0;
|
|
9452
|
+
return this.statusCounts.cancelled || 0;
|
|
9299
9453
|
}
|
|
9300
9454
|
},
|
|
9301
9455
|
mounted: async function() {
|
|
@@ -9527,6 +9681,8 @@ var map = {
|
|
|
9527
9681
|
"./": "./frontend/src/index.js",
|
|
9528
9682
|
"./_util/baseComponent": "./frontend/src/_util/baseComponent.js",
|
|
9529
9683
|
"./_util/baseComponent.js": "./frontend/src/_util/baseComponent.js",
|
|
9684
|
+
"./_util/dateRange": "./frontend/src/_util/dateRange.js",
|
|
9685
|
+
"./_util/dateRange.js": "./frontend/src/_util/dateRange.js",
|
|
9530
9686
|
"./_util/deepEqual": "./frontend/src/_util/deepEqual.js",
|
|
9531
9687
|
"./_util/deepEqual.js": "./frontend/src/_util/deepEqual.js",
|
|
9532
9688
|
"./_util/document-search-autocomplete": "./frontend/src/_util/document-search-autocomplete.js",
|
|
@@ -9586,6 +9742,9 @@ var map = {
|
|
|
9586
9742
|
"./dashboard-result/dashboard-result": "./frontend/src/dashboard-result/dashboard-result.js",
|
|
9587
9743
|
"./dashboard-result/dashboard-result.html": "./frontend/src/dashboard-result/dashboard-result.html",
|
|
9588
9744
|
"./dashboard-result/dashboard-result.js": "./frontend/src/dashboard-result/dashboard-result.js",
|
|
9745
|
+
"./dashboard-result/dashboard-table/dashboard-table": "./frontend/src/dashboard-result/dashboard-table/dashboard-table.js",
|
|
9746
|
+
"./dashboard-result/dashboard-table/dashboard-table.html": "./frontend/src/dashboard-result/dashboard-table/dashboard-table.html",
|
|
9747
|
+
"./dashboard-result/dashboard-table/dashboard-table.js": "./frontend/src/dashboard-result/dashboard-table/dashboard-table.js",
|
|
9589
9748
|
"./dashboard-result/dashboard-text/dashboard-text": "./frontend/src/dashboard-result/dashboard-text/dashboard-text.js",
|
|
9590
9749
|
"./dashboard-result/dashboard-text/dashboard-text.html": "./frontend/src/dashboard-result/dashboard-text/dashboard-text.html",
|
|
9591
9750
|
"./dashboard-result/dashboard-text/dashboard-text.js": "./frontend/src/dashboard-result/dashboard-text/dashboard-text.js",
|
|
@@ -49468,7 +49627,7 @@ module.exports = "<button v-bind=\"attrsToBind\" :disabled=\"isDisabled\" @click
|
|
|
49468
49627
|
(module) {
|
|
49469
49628
|
|
|
49470
49629
|
"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";
|
|
49630
|
+
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
49631
|
|
|
49473
49632
|
/***/ },
|
|
49474
49633
|
|
|
@@ -49626,6 +49785,17 @@ module.exports = "<div>\n <div v-if=\"Array.isArray(result)\">\n <div v-for=
|
|
|
49626
49785
|
|
|
49627
49786
|
/***/ },
|
|
49628
49787
|
|
|
49788
|
+
/***/ "./frontend/src/dashboard-result/dashboard-table/dashboard-table.html"
|
|
49789
|
+
/*!****************************************************************************!*\
|
|
49790
|
+
!*** ./frontend/src/dashboard-result/dashboard-table/dashboard-table.html ***!
|
|
49791
|
+
\****************************************************************************/
|
|
49792
|
+
(module) {
|
|
49793
|
+
|
|
49794
|
+
"use strict";
|
|
49795
|
+
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";
|
|
49796
|
+
|
|
49797
|
+
/***/ },
|
|
49798
|
+
|
|
49629
49799
|
/***/ "./frontend/src/dashboard-result/dashboard-text/dashboard-text.html"
|
|
49630
49800
|
/*!**************************************************************************!*\
|
|
49631
49801
|
!*** ./frontend/src/dashboard-result/dashboard-text/dashboard-text.html ***!
|
|
@@ -50139,7 +50309,7 @@ module.exports = "<div class=\"w-full h-full flex items-center justify-center\">
|
|
|
50139
50309
|
(module) {
|
|
50140
50310
|
|
|
50141
50311
|
"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 <
|
|
50312
|
+
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
50313
|
|
|
50144
50314
|
/***/ },
|
|
50145
50315
|
|
|
@@ -50161,7 +50331,7 @@ module.exports = "<div class=\"p-4 space-y-6\">\n <div v-if=\"status === 'init'
|
|
|
50161
50331
|
(module) {
|
|
50162
50332
|
|
|
50163
50333
|
"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";
|
|
50334
|
+
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
50335
|
|
|
50166
50336
|
/***/ },
|
|
50167
50337
|
|
|
@@ -61497,7 +61667,7 @@ var src_default = VueToastificationPlugin;
|
|
|
61497
61667
|
(module) {
|
|
61498
61668
|
|
|
61499
61669
|
"use strict";
|
|
61500
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.3.
|
|
61670
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.3.2","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 .","seed":"node seed/index.js","start":"node ./local.js","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
61671
|
|
|
61502
61672
|
/***/ }
|
|
61503
61673
|
|