@mongoosejs/studio 0.2.12 → 0.2.13
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/Model/getEstimatedDocumentCounts.js +38 -0
- package/backend/actions/Model/index.js +1 -0
- package/backend/actions/Model/streamDocumentChanges.js +8 -7
- package/backend/actions/Task/getTasks.js +9 -6
- package/backend/authorize.js +1 -0
- package/backend/index.js +11 -3
- package/eslint.config.js +2 -1
- package/express.js +1 -0
- package/frontend/public/app.js +535 -61
- package/frontend/public/tw.css +181 -12
- package/frontend/src/api.js +6 -0
- package/frontend/src/chat/chat.html +13 -7
- package/frontend/src/document/document.html +26 -0
- package/frontend/src/document/document.js +27 -1
- package/frontend/src/models/models.html +8 -2
- package/frontend/src/models/models.js +34 -0
- package/frontend/src/navbar/navbar.js +1 -1
- package/frontend/src/routes.js +20 -4
- package/frontend/src/task-by-name/task-by-name.html +15 -0
- package/frontend/src/task-by-name/task-by-name.js +78 -0
- package/frontend/src/task-single/task-single.html +157 -0
- package/frontend/src/task-single/task-single.js +116 -0
- package/frontend/src/tasks/task-details/task-details.html +101 -50
- package/frontend/src/tasks/task-details/task-details.js +161 -10
- package/frontend/src/tasks/tasks.html +1 -13
- package/frontend/src/tasks/tasks.js +9 -25
- package/package.json +2 -1
package/frontend/public/app.js
CHANGED
|
@@ -479,6 +479,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
479
479
|
yield { document: doc };
|
|
480
480
|
}
|
|
481
481
|
},
|
|
482
|
+
getEstimatedDocumentCounts: function getEstimatedDocumentCounts() {
|
|
483
|
+
return client.post('', { action: 'Model.getEstimatedDocumentCounts' }).then(res => res.data);
|
|
484
|
+
},
|
|
482
485
|
streamDocumentChanges: async function* streamDocumentChanges(params, options = {}) {
|
|
483
486
|
const pollIntervalMs = 5000;
|
|
484
487
|
while (!options.signal?.aborted) {
|
|
@@ -729,6 +732,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
729
732
|
}
|
|
730
733
|
}
|
|
731
734
|
},
|
|
735
|
+
getEstimatedDocumentCounts: function getEstimatedDocumentCounts() {
|
|
736
|
+
return client.post('/Model/getEstimatedDocumentCounts', {}).then(res => res.data);
|
|
737
|
+
},
|
|
732
738
|
streamDocumentChanges: async function* streamDocumentChanges(params, options = {}) {
|
|
733
739
|
const accessToken = window.localStorage.getItem('_mongooseStudioAccessToken') || null;
|
|
734
740
|
const url = window.MONGOOSE_STUDIO_CONFIG.baseURL + '/Model/streamDocumentChanges?' + new URLSearchParams(params).toString();
|
|
@@ -4502,11 +4508,17 @@ module.exports = app => app.component('document', {
|
|
|
4502
4508
|
}),
|
|
4503
4509
|
async mounted() {
|
|
4504
4510
|
window.pageState = this;
|
|
4511
|
+
if (typeof window !== 'undefined' && window.addEventListener) {
|
|
4512
|
+
window.addEventListener('keydown', this.handleSaveShortcut);
|
|
4513
|
+
}
|
|
4505
4514
|
// Store query parameters from the route (preserved from models page)
|
|
4506
4515
|
this.previousQuery = Object.assign({}, this.$route.query);
|
|
4507
4516
|
await this.refreshDocument({ force: true, source: 'initial' });
|
|
4508
4517
|
},
|
|
4509
|
-
|
|
4518
|
+
beforeUnmount() {
|
|
4519
|
+
if (typeof window !== 'undefined' && window.removeEventListener) {
|
|
4520
|
+
window.removeEventListener('keydown', this.handleSaveShortcut);
|
|
4521
|
+
}
|
|
4510
4522
|
this.stopAutoRefresh();
|
|
4511
4523
|
},
|
|
4512
4524
|
computed: {
|
|
@@ -4536,6 +4548,13 @@ module.exports = app => app.component('document', {
|
|
|
4536
4548
|
canEdit() {
|
|
4537
4549
|
return this.canManipulate && this.viewMode === 'fields';
|
|
4538
4550
|
},
|
|
4551
|
+
keyboardShortcuts() {
|
|
4552
|
+
const shortcuts = [];
|
|
4553
|
+
if (this.editting && this.canManipulate) {
|
|
4554
|
+
shortcuts.push({ command: 'Ctrl + S', description: 'Save document' });
|
|
4555
|
+
}
|
|
4556
|
+
return shortcuts;
|
|
4557
|
+
},
|
|
4539
4558
|
isLambda() {
|
|
4540
4559
|
return !!window?.MONGOOSE_STUDIO_CONFIG?.isLambda;
|
|
4541
4560
|
}
|
|
@@ -4548,6 +4567,19 @@ module.exports = app => app.component('document', {
|
|
|
4548
4567
|
}
|
|
4549
4568
|
},
|
|
4550
4569
|
methods: {
|
|
4570
|
+
handleSaveShortcut(event) {
|
|
4571
|
+
const key = typeof event?.key === 'string' ? event.key.toLowerCase() : '';
|
|
4572
|
+
const isSaveShortcut = (event.ctrlKey || event.metaKey) && key === 's';
|
|
4573
|
+
if (!isSaveShortcut) {
|
|
4574
|
+
return;
|
|
4575
|
+
}
|
|
4576
|
+
if (!this.editting || !this.canManipulate) {
|
|
4577
|
+
return;
|
|
4578
|
+
}
|
|
4579
|
+
|
|
4580
|
+
event.preventDefault();
|
|
4581
|
+
this.shouldShowConfirmModal = true;
|
|
4582
|
+
},
|
|
4551
4583
|
cancelEdit() {
|
|
4552
4584
|
this.changes = {};
|
|
4553
4585
|
this.editting = false;
|
|
@@ -6578,6 +6610,7 @@ module.exports = app => app.component('models', {
|
|
|
6578
6610
|
data: () => ({
|
|
6579
6611
|
models: [],
|
|
6580
6612
|
currentModel: null,
|
|
6613
|
+
modelDocumentCounts: {},
|
|
6581
6614
|
documents: [],
|
|
6582
6615
|
schemaPaths: [],
|
|
6583
6616
|
filteredPaths: [],
|
|
@@ -6642,6 +6675,7 @@ module.exports = app => app.component('models', {
|
|
|
6642
6675
|
document.addEventListener('click', this.onOutsideActionsMenuClick, true);
|
|
6643
6676
|
const { models, readyState } = await api.Model.listModels();
|
|
6644
6677
|
this.models = models;
|
|
6678
|
+
await this.loadModelCounts();
|
|
6645
6679
|
if (this.currentModel == null && this.models.length > 0) {
|
|
6646
6680
|
this.currentModel = this.models[0];
|
|
6647
6681
|
}
|
|
@@ -7147,6 +7181,25 @@ module.exports = app => app.component('models', {
|
|
|
7147
7181
|
|
|
7148
7182
|
return value.toLocaleString();
|
|
7149
7183
|
},
|
|
7184
|
+
formatCompactCount(value) {
|
|
7185
|
+
if (typeof value !== 'number') {
|
|
7186
|
+
return '—';
|
|
7187
|
+
}
|
|
7188
|
+
if (value < 1000) {
|
|
7189
|
+
return `${value}`;
|
|
7190
|
+
}
|
|
7191
|
+
const formatValue = (number, suffix) => {
|
|
7192
|
+
const rounded = (Math.round(number * 10) / 10).toFixed(1).replace(/\.0$/, '');
|
|
7193
|
+
return `${rounded}${suffix}`;
|
|
7194
|
+
};
|
|
7195
|
+
if (value < 1000000) {
|
|
7196
|
+
return formatValue(value / 1000, 'k');
|
|
7197
|
+
}
|
|
7198
|
+
if (value < 1000000000) {
|
|
7199
|
+
return formatValue(value / 1000000, 'M');
|
|
7200
|
+
}
|
|
7201
|
+
return formatValue(value / 1000000000, 'B');
|
|
7202
|
+
},
|
|
7150
7203
|
checkIndexLocation(indexName) {
|
|
7151
7204
|
if (this.schemaIndexes.find(x => x.name == indexName) && this.mongoDBIndexes.find(x => x.name == indexName)) {
|
|
7152
7205
|
return 'text-gray-500';
|
|
@@ -7400,6 +7453,19 @@ module.exports = app => app.component('models', {
|
|
|
7400
7453
|
} else {
|
|
7401
7454
|
this.selectMultiple = true;
|
|
7402
7455
|
}
|
|
7456
|
+
},
|
|
7457
|
+
async loadModelCounts() {
|
|
7458
|
+
if (!Array.isArray(this.models) || this.models.length === 0) {
|
|
7459
|
+
return;
|
|
7460
|
+
}
|
|
7461
|
+
try {
|
|
7462
|
+
const { counts } = await api.Model.getEstimatedDocumentCounts();
|
|
7463
|
+
if (counts && typeof counts === 'object') {
|
|
7464
|
+
this.modelDocumentCounts = counts;
|
|
7465
|
+
}
|
|
7466
|
+
} catch (err) {
|
|
7467
|
+
console.error('Failed to load model document counts', err);
|
|
7468
|
+
}
|
|
7403
7469
|
}
|
|
7404
7470
|
}
|
|
7405
7471
|
});
|
|
@@ -7693,7 +7759,7 @@ module.exports = app => app.component('navbar', {
|
|
|
7693
7759
|
return ['chat index', 'chat'].includes(this.$route.name);
|
|
7694
7760
|
},
|
|
7695
7761
|
taskView() {
|
|
7696
|
-
return ['tasks'].includes(this.$route.name);
|
|
7762
|
+
return ['tasks', 'taskByName', 'taskSingle'].includes(this.$route.name);
|
|
7697
7763
|
},
|
|
7698
7764
|
routeName() {
|
|
7699
7765
|
return this.$route.name;
|
|
@@ -7770,14 +7836,14 @@ module.exports = app => app.component('navbar', {
|
|
|
7770
7836
|
|
|
7771
7837
|
// Role-based access control configuration
|
|
7772
7838
|
const roleAccess = {
|
|
7773
|
-
owner: ['root', 'model', 'document', 'dashboards', 'dashboard', 'team', 'chat'],
|
|
7774
|
-
admin: ['root', 'model', 'document', 'dashboards', 'dashboard', 'team', 'chat'],
|
|
7775
|
-
member: ['root', 'model', 'document', 'dashboards', 'dashboard', 'chat'],
|
|
7839
|
+
owner: ['root', 'model', 'document', 'dashboards', 'dashboard', 'team', 'chat', 'tasks', 'taskByName', 'taskSingle'],
|
|
7840
|
+
admin: ['root', 'model', 'document', 'dashboards', 'dashboard', 'team', 'chat', 'tasks', 'taskByName', 'taskSingle'],
|
|
7841
|
+
member: ['root', 'model', 'document', 'dashboards', 'dashboard', 'chat', 'tasks', 'taskByName', 'taskSingle'],
|
|
7776
7842
|
readonly: ['root', 'model', 'document', 'chat'],
|
|
7777
7843
|
dashboards: ['dashboards', 'dashboard']
|
|
7778
7844
|
};
|
|
7779
7845
|
|
|
7780
|
-
const allowedRoutesForLocalDev = ['document', 'root', 'chat', 'model'];
|
|
7846
|
+
const allowedRoutesForLocalDev = ['document', 'root', 'chat', 'model', 'tasks', 'taskByName', 'taskSingle'];
|
|
7781
7847
|
|
|
7782
7848
|
// Helper function to check if a role has access to a route
|
|
7783
7849
|
function hasAccess(roles, routeName) {
|
|
@@ -7844,6 +7910,22 @@ module.exports = {
|
|
|
7844
7910
|
authorized: true
|
|
7845
7911
|
}
|
|
7846
7912
|
},
|
|
7913
|
+
{
|
|
7914
|
+
path: '/tasks/:name',
|
|
7915
|
+
name: 'taskByName',
|
|
7916
|
+
component: 'task-by-name',
|
|
7917
|
+
meta: {
|
|
7918
|
+
authorized: true
|
|
7919
|
+
}
|
|
7920
|
+
},
|
|
7921
|
+
{
|
|
7922
|
+
path: '/tasks/:name/:id',
|
|
7923
|
+
name: 'taskSingle',
|
|
7924
|
+
component: 'task-single',
|
|
7925
|
+
meta: {
|
|
7926
|
+
authorized: true
|
|
7927
|
+
}
|
|
7928
|
+
},
|
|
7847
7929
|
{
|
|
7848
7930
|
path: '/chat',
|
|
7849
7931
|
name: 'chat index',
|
|
@@ -7903,6 +7985,222 @@ module.exports = app => app.component('splash', {
|
|
|
7903
7985
|
});
|
|
7904
7986
|
|
|
7905
7987
|
|
|
7988
|
+
/***/ },
|
|
7989
|
+
|
|
7990
|
+
/***/ "./frontend/src/task-by-name/task-by-name.js"
|
|
7991
|
+
/*!***************************************************!*\
|
|
7992
|
+
!*** ./frontend/src/task-by-name/task-by-name.js ***!
|
|
7993
|
+
\***************************************************/
|
|
7994
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
7995
|
+
|
|
7996
|
+
"use strict";
|
|
7997
|
+
|
|
7998
|
+
|
|
7999
|
+
// Page: all tasks with a given name. Reuses task-details to render the list (many tasks).
|
|
8000
|
+
const template = __webpack_require__(/*! ./task-by-name.html */ "./frontend/src/task-by-name/task-by-name.html");
|
|
8001
|
+
const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
|
|
8002
|
+
|
|
8003
|
+
function buildTaskGroup(name, tasks) {
|
|
8004
|
+
const statusCounts = { pending: 0, succeeded: 0, failed: 0, cancelled: 0, in_progress: 0, unknown: 0 };
|
|
8005
|
+
let lastRun = null;
|
|
8006
|
+
tasks.forEach(task => {
|
|
8007
|
+
const status = task.status || 'unknown';
|
|
8008
|
+
if (Object.prototype.hasOwnProperty.call(statusCounts, status)) {
|
|
8009
|
+
statusCounts[status]++;
|
|
8010
|
+
} else {
|
|
8011
|
+
statusCounts.unknown++;
|
|
8012
|
+
}
|
|
8013
|
+
const taskTime = new Date(task.scheduledAt || task.createdAt || 0);
|
|
8014
|
+
if (!lastRun || taskTime > lastRun) lastRun = taskTime;
|
|
8015
|
+
});
|
|
8016
|
+
return {
|
|
8017
|
+
name,
|
|
8018
|
+
tasks,
|
|
8019
|
+
statusCounts,
|
|
8020
|
+
totalCount: tasks.length,
|
|
8021
|
+
lastRun
|
|
8022
|
+
};
|
|
8023
|
+
}
|
|
8024
|
+
|
|
8025
|
+
module.exports = app => app.component('task-by-name', {
|
|
8026
|
+
template,
|
|
8027
|
+
data: () => ({
|
|
8028
|
+
status: 'init',
|
|
8029
|
+
taskGroup: null,
|
|
8030
|
+
errorMessage: ''
|
|
8031
|
+
}),
|
|
8032
|
+
computed: {
|
|
8033
|
+
taskName() {
|
|
8034
|
+
return this.$route.params.name || '';
|
|
8035
|
+
}
|
|
8036
|
+
},
|
|
8037
|
+
watch: {
|
|
8038
|
+
taskName: {
|
|
8039
|
+
immediate: true,
|
|
8040
|
+
handler() {
|
|
8041
|
+
this.loadTasks();
|
|
8042
|
+
}
|
|
8043
|
+
}
|
|
8044
|
+
},
|
|
8045
|
+
methods: {
|
|
8046
|
+
async loadTasks() {
|
|
8047
|
+
if (!this.taskName) return;
|
|
8048
|
+
this.status = 'init';
|
|
8049
|
+
this.taskGroup = null;
|
|
8050
|
+
this.errorMessage = '';
|
|
8051
|
+
const start = new Date();
|
|
8052
|
+
start.setHours(start.getHours() - 1);
|
|
8053
|
+
const end = new Date();
|
|
8054
|
+
try {
|
|
8055
|
+
const { tasks } = await api.Task.getTasks({
|
|
8056
|
+
name: this.taskName,
|
|
8057
|
+
start,
|
|
8058
|
+
end
|
|
8059
|
+
});
|
|
8060
|
+
this.taskGroup = buildTaskGroup(this.taskName, tasks);
|
|
8061
|
+
this.status = 'loaded';
|
|
8062
|
+
} catch (err) {
|
|
8063
|
+
this.status = 'error';
|
|
8064
|
+
this.errorMessage = err?.response?.data?.message || err.message || 'Failed to load tasks';
|
|
8065
|
+
}
|
|
8066
|
+
},
|
|
8067
|
+
async onTaskCreated() {
|
|
8068
|
+
await this.loadTasks();
|
|
8069
|
+
},
|
|
8070
|
+
async onTaskCancelled() {
|
|
8071
|
+
await this.loadTasks();
|
|
8072
|
+
}
|
|
8073
|
+
}
|
|
8074
|
+
});
|
|
8075
|
+
|
|
8076
|
+
|
|
8077
|
+
/***/ },
|
|
8078
|
+
|
|
8079
|
+
/***/ "./frontend/src/task-single/task-single.js"
|
|
8080
|
+
/*!*************************************************!*\
|
|
8081
|
+
!*** ./frontend/src/task-single/task-single.js ***!
|
|
8082
|
+
\*************************************************/
|
|
8083
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
8084
|
+
|
|
8085
|
+
"use strict";
|
|
8086
|
+
|
|
8087
|
+
|
|
8088
|
+
// Page: one task by id. Dedicated single-task detail UI (not the list).
|
|
8089
|
+
const template = __webpack_require__(/*! ./task-single.html */ "./frontend/src/task-single/task-single.html");
|
|
8090
|
+
const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
|
|
8091
|
+
|
|
8092
|
+
module.exports = app => app.component('task-single', {
|
|
8093
|
+
template,
|
|
8094
|
+
data: () => ({
|
|
8095
|
+
status: 'init',
|
|
8096
|
+
task: null,
|
|
8097
|
+
errorMessage: '',
|
|
8098
|
+
showRescheduleModal: false,
|
|
8099
|
+
showRunModal: false,
|
|
8100
|
+
showCancelModal: false,
|
|
8101
|
+
selectedTask: null,
|
|
8102
|
+
newScheduledTime: ''
|
|
8103
|
+
}),
|
|
8104
|
+
computed: {
|
|
8105
|
+
taskId() {
|
|
8106
|
+
return this.$route.params.id || '';
|
|
8107
|
+
},
|
|
8108
|
+
taskByNamePath() {
|
|
8109
|
+
const name = this.task?.name ?? this.$route.params.name ?? '';
|
|
8110
|
+
const path = `/tasks/${encodeURIComponent(name || '')}`;
|
|
8111
|
+
const query = this.$route.query?.status ? { status: this.$route.query.status } : {};
|
|
8112
|
+
return { path, query };
|
|
8113
|
+
},
|
|
8114
|
+
},
|
|
8115
|
+
watch: {
|
|
8116
|
+
'$route.params': {
|
|
8117
|
+
deep: true,
|
|
8118
|
+
handler() {
|
|
8119
|
+
this.loadTask();
|
|
8120
|
+
}
|
|
8121
|
+
}
|
|
8122
|
+
},
|
|
8123
|
+
methods: {
|
|
8124
|
+
getStatusColor(status) {
|
|
8125
|
+
if (status === 'succeeded') return 'bg-green-100 text-green-800';
|
|
8126
|
+
if (status === 'pending') return 'bg-yellow-100 text-yellow-800';
|
|
8127
|
+
if (status === 'cancelled') return 'bg-gray-100 text-gray-800';
|
|
8128
|
+
if (status === 'failed') return 'bg-red-100 text-red-800';
|
|
8129
|
+
if (status === 'in_progress') return 'bg-blue-100 text-blue-800';
|
|
8130
|
+
return 'bg-slate-100 text-slate-800';
|
|
8131
|
+
},
|
|
8132
|
+
formatDate(dateString) {
|
|
8133
|
+
if (!dateString) return 'N/A';
|
|
8134
|
+
return new Date(dateString).toLocaleString();
|
|
8135
|
+
},
|
|
8136
|
+
async loadTask() {
|
|
8137
|
+
if (!this.taskId) return;
|
|
8138
|
+
this.status = 'init';
|
|
8139
|
+
this.task = null;
|
|
8140
|
+
this.errorMessage = '';
|
|
8141
|
+
try {
|
|
8142
|
+
const { doc } = await api.Model.getDocument({ model: 'Task', documentId: this.taskId });
|
|
8143
|
+
this.task = doc;
|
|
8144
|
+
this.status = 'loaded';
|
|
8145
|
+
} catch (err) {
|
|
8146
|
+
const status = err?.response?.status;
|
|
8147
|
+
const notFound = status === 404 || err?.response?.data?.name === 'DocumentNotFoundError';
|
|
8148
|
+
this.status = notFound ? 'notfound' : 'error';
|
|
8149
|
+
this.errorMessage = notFound ? '' : (err?.response?.data?.message || err.message || 'Failed to load task');
|
|
8150
|
+
}
|
|
8151
|
+
},
|
|
8152
|
+
showRescheduleConfirmation(task) {
|
|
8153
|
+
this.selectedTask = task;
|
|
8154
|
+
const defaultTime = new Date();
|
|
8155
|
+
defaultTime.setHours(defaultTime.getHours() + 1);
|
|
8156
|
+
this.newScheduledTime = defaultTime.toISOString().slice(0, 16);
|
|
8157
|
+
this.showRescheduleModal = true;
|
|
8158
|
+
},
|
|
8159
|
+
showRunConfirmation(task) {
|
|
8160
|
+
this.selectedTask = task;
|
|
8161
|
+
this.showRunModal = true;
|
|
8162
|
+
},
|
|
8163
|
+
showCancelConfirmation(task) {
|
|
8164
|
+
this.selectedTask = task;
|
|
8165
|
+
this.showCancelModal = true;
|
|
8166
|
+
},
|
|
8167
|
+
async confirmRescheduleTask() {
|
|
8168
|
+
if (!this.newScheduledTime) return;
|
|
8169
|
+
await api.Task.rescheduleTask({ taskId: this.selectedTask.id, scheduledAt: this.newScheduledTime });
|
|
8170
|
+
this.$toast.success({ title: 'Task Rescheduled', text: `Task ${this.selectedTask.id} has been rescheduled`, });
|
|
8171
|
+
this.showRescheduleModal = false;
|
|
8172
|
+
this.selectedTask = null;
|
|
8173
|
+
this.newScheduledTime = '';
|
|
8174
|
+
await this.loadTask();
|
|
8175
|
+
},
|
|
8176
|
+
async confirmRunTask() {
|
|
8177
|
+
await api.Task.runTask({ taskId: this.selectedTask.id });
|
|
8178
|
+
this.$toast.success({ title: 'Task Started', text: `Task ${this.selectedTask.id} is now running`, type: 'success' });
|
|
8179
|
+
this.showRunModal = false;
|
|
8180
|
+
this.selectedTask = null;
|
|
8181
|
+
await this.loadTask();
|
|
8182
|
+
},
|
|
8183
|
+
goBack() {
|
|
8184
|
+
if (window.history.length > 1) {
|
|
8185
|
+
window.history.back();
|
|
8186
|
+
} else {
|
|
8187
|
+
this.$router.push(this.taskByNamePath);
|
|
8188
|
+
}
|
|
8189
|
+
},
|
|
8190
|
+
async confirmCancelTask() {
|
|
8191
|
+
await api.Task.cancelTask({ taskId: this.selectedTask.id });
|
|
8192
|
+
this.$toast.success({ title: 'Task Cancelled', text: `Task ${this.selectedTask.id} has been cancelled` });
|
|
8193
|
+
this.showCancelModal = false;
|
|
8194
|
+
this.selectedTask = null;
|
|
8195
|
+
this.goBack();
|
|
8196
|
+
}
|
|
8197
|
+
},
|
|
8198
|
+
mounted() {
|
|
8199
|
+
this.loadTask();
|
|
8200
|
+
}
|
|
8201
|
+
});
|
|
8202
|
+
|
|
8203
|
+
|
|
7906
8204
|
/***/ },
|
|
7907
8205
|
|
|
7908
8206
|
/***/ "./frontend/src/tasks/task-details/task-details.js"
|
|
@@ -7917,16 +8215,30 @@ module.exports = app => app.component('splash', {
|
|
|
7917
8215
|
const template = __webpack_require__(/*! ./task-details.html */ "./frontend/src/tasks/task-details/task-details.html");
|
|
7918
8216
|
const api = __webpack_require__(/*! ../../api */ "./frontend/src/api.js");
|
|
7919
8217
|
|
|
8218
|
+
const STATUS_ORDER = ['pending', 'succeeded', 'failed', 'cancelled'];
|
|
8219
|
+
const PIE_COLORS = ['#eab308', '#22c55e', '#ef4444', '#6b7280'];
|
|
8220
|
+
const PIE_HOVER = ['#ca8a04', '#16a34a', '#dc2626', '#4b5563'];
|
|
8221
|
+
|
|
7920
8222
|
module.exports = app => app.component('task-details', {
|
|
7921
|
-
props:
|
|
8223
|
+
props: {
|
|
8224
|
+
taskGroup: { type: Object, required: true },
|
|
8225
|
+
backTo: { type: Object, default: null }
|
|
8226
|
+
},
|
|
7922
8227
|
data: () => ({
|
|
8228
|
+
currentFilter: null,
|
|
7923
8229
|
showRescheduleModal: false,
|
|
7924
8230
|
showRunModal: false,
|
|
7925
8231
|
showCancelModal: false,
|
|
7926
8232
|
selectedTask: null,
|
|
7927
|
-
newScheduledTime: ''
|
|
8233
|
+
newScheduledTime: '',
|
|
8234
|
+
statusView: 'summary',
|
|
8235
|
+
statusChart: null
|
|
7928
8236
|
}),
|
|
7929
8237
|
computed: {
|
|
8238
|
+
backLabel() {
|
|
8239
|
+
if (this.backTo?.path?.startsWith('/tasks/') || this.backTo?.name === 'taskByName') return `Back to ${this.taskGroup?.name || 'tasks'}`;
|
|
8240
|
+
return 'Back to Task Groups';
|
|
8241
|
+
},
|
|
7930
8242
|
sortedTasks() {
|
|
7931
8243
|
let tasks = this.taskGroup.tasks;
|
|
7932
8244
|
|
|
@@ -7940,9 +8252,123 @@ module.exports = app => app.component('task-details', {
|
|
|
7940
8252
|
const dateB = new Date(b.scheduledAt || b.createdAt || 0);
|
|
7941
8253
|
return dateB - dateA; // Most recent first
|
|
7942
8254
|
});
|
|
8255
|
+
},
|
|
8256
|
+
pieChartData() {
|
|
8257
|
+
const counts = this.taskGroup?.statusCounts || {};
|
|
8258
|
+
return {
|
|
8259
|
+
labels: ['Pending', 'Succeeded', 'Failed', 'Cancelled'],
|
|
8260
|
+
datasets: [{
|
|
8261
|
+
data: STATUS_ORDER.map(s => counts[s] || 0),
|
|
8262
|
+
backgroundColor: PIE_COLORS,
|
|
8263
|
+
hoverBackgroundColor: PIE_HOVER,
|
|
8264
|
+
borderWidth: 2,
|
|
8265
|
+
borderColor: '#fff'
|
|
8266
|
+
}]
|
|
8267
|
+
};
|
|
8268
|
+
},
|
|
8269
|
+
statusOrderForDisplay() {
|
|
8270
|
+
return STATUS_ORDER;
|
|
7943
8271
|
}
|
|
7944
8272
|
},
|
|
8273
|
+
watch: {
|
|
8274
|
+
'$route.query.status': {
|
|
8275
|
+
handler(status) {
|
|
8276
|
+
this.currentFilter = status || null;
|
|
8277
|
+
},
|
|
8278
|
+
immediate: true
|
|
8279
|
+
},
|
|
8280
|
+
statusView(val) {
|
|
8281
|
+
if (val !== 'chart') this.destroyStatusChart();
|
|
8282
|
+
else {
|
|
8283
|
+
this.$nextTick(() => {
|
|
8284
|
+
requestAnimationFrame(() => this.ensureStatusChart());
|
|
8285
|
+
});
|
|
8286
|
+
}
|
|
8287
|
+
},
|
|
8288
|
+
taskGroup: {
|
|
8289
|
+
deep: true,
|
|
8290
|
+
handler() {
|
|
8291
|
+
this.$nextTick(() => {
|
|
8292
|
+
requestAnimationFrame(() => this.ensureStatusChart());
|
|
8293
|
+
});
|
|
8294
|
+
}
|
|
8295
|
+
},
|
|
8296
|
+
},
|
|
7945
8297
|
methods: {
|
|
8298
|
+
destroyStatusChart() {
|
|
8299
|
+
if (this.statusChart) {
|
|
8300
|
+
try {
|
|
8301
|
+
this.statusChart.destroy();
|
|
8302
|
+
} catch (_) {
|
|
8303
|
+
// ignore Chart.js teardown errors
|
|
8304
|
+
}
|
|
8305
|
+
this.statusChart = null;
|
|
8306
|
+
}
|
|
8307
|
+
},
|
|
8308
|
+
isChartCanvasReady(canvas) {
|
|
8309
|
+
return canvas && typeof canvas.getContext === 'function' && canvas.isConnected && canvas.offsetParent != null;
|
|
8310
|
+
},
|
|
8311
|
+
ensureStatusChart() {
|
|
8312
|
+
if (this.statusView !== 'chart' || !this.taskGroup || this.taskGroup.totalCount === 0) {
|
|
8313
|
+
this.destroyStatusChart();
|
|
8314
|
+
return;
|
|
8315
|
+
}
|
|
8316
|
+
const canvas = this.$refs.statusPieChart;
|
|
8317
|
+
if (!canvas || !this.isChartCanvasReady(canvas)) return;
|
|
8318
|
+
const Chart = typeof window !== 'undefined' && window.Chart;
|
|
8319
|
+
if (!Chart) return;
|
|
8320
|
+
const data = this.pieChartData;
|
|
8321
|
+
if (this.statusChart) {
|
|
8322
|
+
try {
|
|
8323
|
+
this.statusChart.data.labels = data.labels;
|
|
8324
|
+
this.statusChart.data.datasets[0].data = data.datasets[0].data;
|
|
8325
|
+
this.statusChart.update('none');
|
|
8326
|
+
} catch (_) {
|
|
8327
|
+
this.destroyStatusChart();
|
|
8328
|
+
}
|
|
8329
|
+
return;
|
|
8330
|
+
}
|
|
8331
|
+
try {
|
|
8332
|
+
this.statusChart = new Chart(canvas, {
|
|
8333
|
+
type: 'doughnut',
|
|
8334
|
+
data,
|
|
8335
|
+
options: {
|
|
8336
|
+
responsive: false,
|
|
8337
|
+
maintainAspectRatio: false,
|
|
8338
|
+
animation: false,
|
|
8339
|
+
layout: {
|
|
8340
|
+
padding: 8
|
|
8341
|
+
},
|
|
8342
|
+
onClick: (_evt, elements) => {
|
|
8343
|
+
if (elements && elements.length > 0) {
|
|
8344
|
+
const status = STATUS_ORDER[elements[0].index];
|
|
8345
|
+
this.$nextTick(() => this.filterByStatus(status));
|
|
8346
|
+
}
|
|
8347
|
+
},
|
|
8348
|
+
plugins: {
|
|
8349
|
+
legend: {
|
|
8350
|
+
display: true,
|
|
8351
|
+
position: 'bottom'
|
|
8352
|
+
}
|
|
8353
|
+
}
|
|
8354
|
+
}
|
|
8355
|
+
});
|
|
8356
|
+
} catch (_) {
|
|
8357
|
+
this.statusChart = null;
|
|
8358
|
+
}
|
|
8359
|
+
},
|
|
8360
|
+
statusLabel(status) {
|
|
8361
|
+
return status.charAt(0).toUpperCase() + status.slice(1).replace('_', ' ');
|
|
8362
|
+
},
|
|
8363
|
+
getStatusPillClass(status) {
|
|
8364
|
+
const classes = {
|
|
8365
|
+
pending: 'bg-yellow-200 text-yellow-900 ring-2 ring-yellow-500',
|
|
8366
|
+
succeeded: 'bg-green-200 text-green-900 ring-2 ring-green-500',
|
|
8367
|
+
failed: 'bg-red-200 text-red-900 ring-2 ring-red-500',
|
|
8368
|
+
cancelled: 'bg-gray-200 text-gray-900 ring-2 ring-gray-500'
|
|
8369
|
+
};
|
|
8370
|
+
return classes[status] || 'bg-slate-200 text-slate-900 ring-2 ring-slate-500';
|
|
8371
|
+
},
|
|
7946
8372
|
getStatusColor(status) {
|
|
7947
8373
|
if (status === 'succeeded') {
|
|
7948
8374
|
return 'bg-green-100 text-green-800';
|
|
@@ -7979,15 +8405,35 @@ module.exports = app => app.component('task-details', {
|
|
|
7979
8405
|
this.$emit('task-cancelled');
|
|
7980
8406
|
},
|
|
7981
8407
|
filterByStatus(status) {
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
8408
|
+
const next = this.currentFilter === status ? null : status;
|
|
8409
|
+
this.currentFilter = next;
|
|
8410
|
+
const query = { ...this.$route.query };
|
|
8411
|
+
if (next) query.status = next;
|
|
8412
|
+
else delete query.status;
|
|
8413
|
+
this.$router.replace({ path: this.$route.path, query });
|
|
8414
|
+
},
|
|
8415
|
+
clearFilter() {
|
|
8416
|
+
this.currentFilter = null;
|
|
8417
|
+
const query = { ...this.$route.query };
|
|
8418
|
+
delete query.status;
|
|
8419
|
+
this.$router.replace({ path: this.$route.path, query });
|
|
8420
|
+
},
|
|
8421
|
+
goBack() {
|
|
8422
|
+
if (this.backTo) {
|
|
8423
|
+
if (window.history.length > 1) {
|
|
8424
|
+
window.history.back();
|
|
8425
|
+
} else {
|
|
8426
|
+
this.$router.push(this.backTo);
|
|
8427
|
+
}
|
|
7985
8428
|
} else {
|
|
7986
|
-
this.$emit('
|
|
8429
|
+
this.$emit('back');
|
|
7987
8430
|
}
|
|
7988
8431
|
},
|
|
7989
|
-
|
|
7990
|
-
|
|
8432
|
+
taskDetailRoute(task) {
|
|
8433
|
+
const id = String(task.id || task._id);
|
|
8434
|
+
const path = `/tasks/${encodeURIComponent(this.taskGroup.name || '')}/${id}`;
|
|
8435
|
+
const query = this.currentFilter ? { status: this.currentFilter } : {};
|
|
8436
|
+
return { path, query };
|
|
7991
8437
|
},
|
|
7992
8438
|
showRescheduleConfirmation(task) {
|
|
7993
8439
|
this.selectedTask = task;
|
|
@@ -8087,11 +8533,14 @@ module.exports = app => app.component('task-details', {
|
|
|
8087
8533
|
|
|
8088
8534
|
},
|
|
8089
8535
|
mounted() {
|
|
8090
|
-
// Check if the task group was already filtered when passed from parent
|
|
8091
8536
|
if (this.taskGroup.filteredStatus && !this.currentFilter) {
|
|
8092
|
-
this
|
|
8537
|
+
this.currentFilter = this.taskGroup.filteredStatus;
|
|
8538
|
+
this.$router.replace({ path: this.$route.path, query: { ...this.$route.query, status: this.taskGroup.filteredStatus } });
|
|
8093
8539
|
}
|
|
8094
8540
|
},
|
|
8541
|
+
beforeUnmount() {
|
|
8542
|
+
this.destroyStatusChart();
|
|
8543
|
+
},
|
|
8095
8544
|
template: template
|
|
8096
8545
|
});
|
|
8097
8546
|
|
|
@@ -8115,11 +8564,12 @@ module.exports = app => app.component('tasks', {
|
|
|
8115
8564
|
status: 'init',
|
|
8116
8565
|
tasks: [],
|
|
8117
8566
|
groupedTasks: {},
|
|
8118
|
-
selectedRange: '
|
|
8567
|
+
selectedRange: 'last_hour',
|
|
8119
8568
|
start: null,
|
|
8120
8569
|
end: null,
|
|
8121
8570
|
dateFilters: [
|
|
8122
8571
|
{ value: 'all', label: 'All Time' },
|
|
8572
|
+
{ value: 'last_hour', label: 'Last Hour' },
|
|
8123
8573
|
{ value: 'today', label: 'Today' },
|
|
8124
8574
|
{ value: 'yesterday', label: 'Yesterday' },
|
|
8125
8575
|
{ value: 'thisWeek', label: 'This Week' },
|
|
@@ -8138,10 +8588,6 @@ module.exports = app => app.component('tasks', {
|
|
|
8138
8588
|
],
|
|
8139
8589
|
searchQuery: '',
|
|
8140
8590
|
searchTimeout: null,
|
|
8141
|
-
// Task details view state
|
|
8142
|
-
showTaskDetails: false,
|
|
8143
|
-
selectedTaskGroup: null,
|
|
8144
|
-
taskDetailsFilter: null,
|
|
8145
8591
|
// Create task modal state
|
|
8146
8592
|
showCreateTaskModal: false,
|
|
8147
8593
|
newTask: {
|
|
@@ -8177,28 +8623,10 @@ module.exports = app => app.component('tasks', {
|
|
|
8177
8623
|
this.groupedTasks = groupedTasks;
|
|
8178
8624
|
},
|
|
8179
8625
|
openTaskGroupDetails(group) {
|
|
8180
|
-
this.
|
|
8181
|
-
this.showTaskDetails = true;
|
|
8626
|
+
this.$router.push({ path: `/tasks/${encodeURIComponent(group.name || '')}` });
|
|
8182
8627
|
},
|
|
8183
8628
|
openTaskGroupDetailsWithFilter(group, status) {
|
|
8184
|
-
|
|
8185
|
-
const filteredGroup = {
|
|
8186
|
-
...group,
|
|
8187
|
-
tasks: group.tasks.filter(task => task.status === status),
|
|
8188
|
-
filteredStatus: status
|
|
8189
|
-
};
|
|
8190
|
-
this.selectedTaskGroup = filteredGroup;
|
|
8191
|
-
this.taskDetailsFilter = status;
|
|
8192
|
-
this.showTaskDetails = true;
|
|
8193
|
-
},
|
|
8194
|
-
async onTaskCancelled() {
|
|
8195
|
-
// Refresh the task data when a task is cancelled
|
|
8196
|
-
await this.getTasks();
|
|
8197
|
-
},
|
|
8198
|
-
hideTaskDetails() {
|
|
8199
|
-
this.showTaskDetails = false;
|
|
8200
|
-
this.selectedTaskGroup = null;
|
|
8201
|
-
this.taskDetailsFilter = null;
|
|
8629
|
+
this.$router.push({ path: `/tasks/${encodeURIComponent(group.name || '')}`, query: status ? { status } : {} });
|
|
8202
8630
|
},
|
|
8203
8631
|
async onTaskCreated() {
|
|
8204
8632
|
// Refresh the task data when a new task is created
|
|
@@ -8362,6 +8790,11 @@ module.exports = app => app.component('tasks', {
|
|
|
8362
8790
|
let start, end;
|
|
8363
8791
|
|
|
8364
8792
|
switch (this.selectedRange) {
|
|
8793
|
+
case 'last_hour':
|
|
8794
|
+
start = new Date();
|
|
8795
|
+
start.setHours(start.getHours() - 1);
|
|
8796
|
+
end = new Date();
|
|
8797
|
+
break;
|
|
8365
8798
|
case 'today':
|
|
8366
8799
|
start = new Date();
|
|
8367
8800
|
start.setHours(0, 0, 0, 0);
|
|
@@ -8880,6 +9313,12 @@ var map = {
|
|
|
8880
9313
|
"./splash/splash": "./frontend/src/splash/splash.js",
|
|
8881
9314
|
"./splash/splash.html": "./frontend/src/splash/splash.html",
|
|
8882
9315
|
"./splash/splash.js": "./frontend/src/splash/splash.js",
|
|
9316
|
+
"./task-by-name/task-by-name": "./frontend/src/task-by-name/task-by-name.js",
|
|
9317
|
+
"./task-by-name/task-by-name.html": "./frontend/src/task-by-name/task-by-name.html",
|
|
9318
|
+
"./task-by-name/task-by-name.js": "./frontend/src/task-by-name/task-by-name.js",
|
|
9319
|
+
"./task-single/task-single": "./frontend/src/task-single/task-single.js",
|
|
9320
|
+
"./task-single/task-single.html": "./frontend/src/task-single/task-single.html",
|
|
9321
|
+
"./task-single/task-single.js": "./frontend/src/task-single/task-single.js",
|
|
8883
9322
|
"./tasks/task-details/task-details": "./frontend/src/tasks/task-details/task-details.js",
|
|
8884
9323
|
"./tasks/task-details/task-details.html": "./frontend/src/tasks/task-details/task-details.html",
|
|
8885
9324
|
"./tasks/task-details/task-details.js": "./frontend/src/tasks/task-details/task-details.js",
|
|
@@ -8983,7 +9422,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
8983
9422
|
/* harmony export */ });
|
|
8984
9423
|
/* harmony import */ var _vue_shared__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
|
|
8985
9424
|
/**
|
|
8986
|
-
* @vue/reactivity v3.5.
|
|
9425
|
+
* @vue/reactivity v3.5.29
|
|
8987
9426
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
8988
9427
|
* @license MIT
|
|
8989
9428
|
**/
|
|
@@ -11123,7 +11562,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
11123
11562
|
/* harmony import */ var _vue_reactivity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/reactivity */ "./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js");
|
|
11124
11563
|
/* harmony import */ var _vue_shared__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
|
|
11125
11564
|
/**
|
|
11126
|
-
* @vue/runtime-core v3.5.
|
|
11565
|
+
* @vue/runtime-core v3.5.29
|
|
11127
11566
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
11128
11567
|
* @license MIT
|
|
11129
11568
|
**/
|
|
@@ -12652,6 +13091,7 @@ function resolveTransitionHooks(vnode, props, state, instance, postClone) {
|
|
|
12652
13091
|
callHook(hook, [el]);
|
|
12653
13092
|
},
|
|
12654
13093
|
enter(el) {
|
|
13094
|
+
if (leavingVNodesCache[key] === vnode) return;
|
|
12655
13095
|
let hook = onEnter;
|
|
12656
13096
|
let afterHook = onAfterEnter;
|
|
12657
13097
|
let cancelHook = onEnterCancelled;
|
|
@@ -14752,13 +15192,24 @@ function withAsyncContext(getAwaitable) {
|
|
|
14752
15192
|
}
|
|
14753
15193
|
let awaitable = getAwaitable();
|
|
14754
15194
|
unsetCurrentInstance();
|
|
15195
|
+
const cleanup = () => {
|
|
15196
|
+
if (getCurrentInstance() !== ctx) ctx.scope.off();
|
|
15197
|
+
unsetCurrentInstance();
|
|
15198
|
+
};
|
|
14755
15199
|
if ((0,_vue_shared__WEBPACK_IMPORTED_MODULE_1__.isPromise)(awaitable)) {
|
|
14756
15200
|
awaitable = awaitable.catch((e) => {
|
|
14757
15201
|
setCurrentInstance(ctx);
|
|
15202
|
+
Promise.resolve().then(() => Promise.resolve().then(cleanup));
|
|
14758
15203
|
throw e;
|
|
14759
15204
|
});
|
|
14760
15205
|
}
|
|
14761
|
-
return [
|
|
15206
|
+
return [
|
|
15207
|
+
awaitable,
|
|
15208
|
+
() => {
|
|
15209
|
+
setCurrentInstance(ctx);
|
|
15210
|
+
Promise.resolve().then(cleanup);
|
|
15211
|
+
}
|
|
15212
|
+
];
|
|
14762
15213
|
}
|
|
14763
15214
|
|
|
14764
15215
|
function createDuplicateChecker() {
|
|
@@ -19643,7 +20094,7 @@ function isMemoSame(cached, memo) {
|
|
|
19643
20094
|
return true;
|
|
19644
20095
|
}
|
|
19645
20096
|
|
|
19646
|
-
const version = "3.5.
|
|
20097
|
+
const version = "3.5.29";
|
|
19647
20098
|
const warn = true ? warn$1 : 0;
|
|
19648
20099
|
const ErrorTypeStrings = ErrorTypeStrings$1 ;
|
|
19649
20100
|
const devtools = true ? devtools$1 : 0;
|
|
@@ -19854,7 +20305,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
19854
20305
|
/* harmony import */ var _vue_runtime_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/runtime-core */ "./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js");
|
|
19855
20306
|
/* harmony import */ var _vue_runtime_core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
|
|
19856
20307
|
/**
|
|
19857
|
-
* @vue/runtime-dom v3.5.
|
|
20308
|
+
* @vue/runtime-dom v3.5.29
|
|
19858
20309
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
19859
20310
|
* @license MIT
|
|
19860
20311
|
**/
|
|
@@ -21878,7 +22329,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
21878
22329
|
/* harmony export */ toTypeString: () => (/* binding */ toTypeString)
|
|
21879
22330
|
/* harmony export */ });
|
|
21880
22331
|
/**
|
|
21881
|
-
* @vue/shared v3.5.
|
|
22332
|
+
* @vue/shared v3.5.29
|
|
21882
22333
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
21883
22334
|
* @license MIT
|
|
21884
22335
|
**/
|
|
@@ -23125,7 +23576,7 @@ module.exports = {
|
|
|
23125
23576
|
"use strict";
|
|
23126
23577
|
|
|
23127
23578
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
23128
|
-
exports.toUTF8 = exports.getBigInt64LE = exports.getFloat64LE = exports.getInt32LE = exports.UUID = exports.Timestamp = exports.serialize = exports.ObjectId = exports.MinKey = exports.MaxKey = exports.Long = exports.Int32 = exports.EJSON = exports.Double = exports.deserialize = exports.Decimal128 = exports.DBRef = exports.Code = exports.calculateObjectSize = exports.BSONType = exports.BSONSymbol = exports.BSONRegExp = exports.BSONError = exports.BSON = exports.Binary = void 0;
|
|
23579
|
+
exports.toUTF8 = exports.getBigInt64LE = exports.getFloat64LE = exports.getInt32LE = exports.UUID = exports.Timestamp = exports.serialize = exports.ObjectId = exports.NumberUtils = exports.MinKey = exports.MaxKey = exports.Long = exports.Int32 = exports.EJSON = exports.Double = exports.deserialize = exports.Decimal128 = exports.DBRef = exports.Code = exports.calculateObjectSize = exports.BSONType = exports.BSONSymbol = exports.BSONRegExp = exports.BSONError = exports.BSON = exports.Binary = void 0;
|
|
23129
23580
|
exports.parseToElementsToArray = parseToElementsToArray;
|
|
23130
23581
|
exports.pluckBSONSerializeOptions = pluckBSONSerializeOptions;
|
|
23131
23582
|
exports.resolveBSONOptions = resolveBSONOptions;
|
|
@@ -23150,6 +23601,7 @@ Object.defineProperty(exports, "Int32", ({ enumerable: true, get: function () {
|
|
|
23150
23601
|
Object.defineProperty(exports, "Long", ({ enumerable: true, get: function () { return bson_2.Long; } }));
|
|
23151
23602
|
Object.defineProperty(exports, "MaxKey", ({ enumerable: true, get: function () { return bson_2.MaxKey; } }));
|
|
23152
23603
|
Object.defineProperty(exports, "MinKey", ({ enumerable: true, get: function () { return bson_2.MinKey; } }));
|
|
23604
|
+
Object.defineProperty(exports, "NumberUtils", ({ enumerable: true, get: function () { return bson_2.NumberUtils; } }));
|
|
23153
23605
|
Object.defineProperty(exports, "ObjectId", ({ enumerable: true, get: function () { return bson_2.ObjectId; } }));
|
|
23154
23606
|
Object.defineProperty(exports, "serialize", ({ enumerable: true, get: function () { return bson_2.serialize; } }));
|
|
23155
23607
|
Object.defineProperty(exports, "Timestamp", ({ enumerable: true, get: function () { return bson_2.Timestamp; } }));
|
|
@@ -23815,7 +24267,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
23815
24267
|
/* harmony import */ var _vue_runtime_dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/runtime-dom */ "./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js");
|
|
23816
24268
|
/* harmony import */ var _vue_runtime_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/runtime-dom */ "./node_modules/@vue/runtime-dom/dist/runtime-dom.esm-bundler.js");
|
|
23817
24269
|
/**
|
|
23818
|
-
* vue v3.5.
|
|
24270
|
+
* vue v3.5.29
|
|
23819
24271
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
23820
24272
|
* @license MIT
|
|
23821
24273
|
**/
|
|
@@ -24969,7 +25421,7 @@ module.exports = "<div class=\"relative flex items-start\" :class=\"{'justify-en
|
|
|
24969
25421
|
(module) {
|
|
24970
25422
|
|
|
24971
25423
|
"use strict";
|
|
24972
|
-
module.exports = "<div class=\"flex\" style=\"height: calc(100vh - 55px); height: calc(100dvh - 55px)\">\n <
|
|
25424
|
+
module.exports = "<div class=\"flex\" style=\"height: calc(100vh - 55px); height: calc(100dvh - 55px)\">\n <button\n type=\"button\"\n class=\"fixed top-[65px] left-0 cursor-pointer bg-gray-100 rounded-r-md z-10 p-1 lg:hidden\"\n @click=\"hideSidebar = false\"\n v-show=\"hideSidebar !== false\"\n aria-label=\"Open chat history\"\n title=\"Open chat history\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\" class=\"w-5 h-5\" fill=\"#5f6368\"><path d=\"M360-120v-720h80v720h-80Zm160-160v-400l200 200-200 200Z\"/></svg>\n </button>\n <button\n class=\"fixed top-[65px] right-4 z-10 p-2 rounded-md shadow bg-white\"\n :class=\"hasWorkspace ? 'text-gray-700 hover:bg-gray-100' : 'text-gray-300 cursor-not-allowed bg-gray-50'\"\n @click=\"toggleShareThread\"\n :disabled=\"!hasWorkspace || !chatThreadId || sharingThread\"\n aria-label=\"Share thread with workspace\"\n title=\"Share thread with workspace\"\n >\n <svg v-if=\"hasWorkspace\" xmlns=\"http://www.w3.org/2000/svg\" class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7a2.48 2.48 0 0 0 0-1.39l7.02-4.11a2.5 2.5 0 1 0-.87-1.37L8.04 9.94a2.5 2.5 0 1 0 0 4.12l7.12 4.16a2.5 2.5 0 1 0 .84-1.34l-7.05-4.12c-.04-.02-.08-.05-.11-.07a2.48 2.48 0 0 0 0-1.39c.03-.02.07-.04.11-.07l7.11-4.16c.52.47 1.2.76 1.94.76a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0-1.94.94L7.97 8.43a2.5 2.5 0 1 0 0 7.14l9.09 5.3c.52-.47 1.2-.76 1.94-.76a2.5 2.5 0 1 0 0-5z\"/></svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M12 1a5 5 0 00-5 5v3H6a2 2 0 00-2 2v9a2 2 0 002 2h12a2 2 0 002-2v-9a2 2 0 00-2-2h-1V6a5 5 0 00-5-5zm-3 8V6a3 3 0 016 0v3H9zm9 2v9H6v-9h12z\"/></svg>\n </button>\n <!-- Sidebar: Chat Threads -->\n <aside\n class=\"bg-white border-r overflow-y-auto overflow-x-hidden h-full transition-transform duration-300 ease-in-out z-20 w-64 fixed lg:relative\"\n :class=\"hideSidebar === false ? 'translate-x-0' : hideSidebar === true ? '-translate-x-full lg:hidden' : '-translate-x-full lg:translate-x-0'\"\n style=\"z-index: 10000\">\n <div class=\"flex items-center border-b border-gray-100 w-64 overflow-x-hidden\">\n <div class=\"p-1 ml-2 font-bold\">Chat Threads</div>\n <button\n @click=\"hideSidebar = true\"\n class=\"ml-auto mr-2 p-2 rounded hover:bg-gray-200 focus:outline-none lg:hidden\"\n aria-label=\"Close sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"currentColor\"><path d=\"M660-320v-320L500-480l160 160ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm120-80v-560H200v560h120Zm80 0h360v-560H400v560Zm-80 0H200h120Z\"/></svg>\n </button>\n </div>\n <div class=\"p-4 w-64\">\n <async-button\n @click=\"createNewThread\"\n class=\"w-full bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700\"\n >\n Create New Thread\n </async-button>\n </div>\n <div v-if=\"status === 'loaded' && chatThreads.length === 0\" class=\"p-4 text-sm text-gray-700\">\n No threads yet\n </div>\n <ul v-if=\"status === 'loaded'\" class=\"w-full\">\n <li\n v-for=\"thread in chatThreads\"\n :key=\"thread._id\"\n @click=\"selectThread(thread._id)\"\n class=\"w-full p-2 hover:bg-ultramarine-100 cursor-pointer text-sm text-gray-700\"\n :class=\"{ 'bg-ultramarine-200 text-gray-900': thread._id === chatThreadId }\"\n >\n {{ thread.title || 'Untitled Thread' }}\n </li>\n </ul>\n </aside>\n\n <!-- Main Chat Area -->\n <main class=\"flex-1 flex flex-col bg-slate-50\">\n <div class=\"flex-1 overflow-y-auto p-6 space-y-4\" ref=\"messagesContainer\">\n <div v-if=\"chatMessages?.length === 0\">\n <div class=\"flex items-center w-full h-full justify-center py-3 mb-4\">\n <p class=\"mx-4 font-bold text-gray-900\">\n Ask Mongoose Studio to analyze your data or generate a script\n </p>\n </div>\n </div>\n <ul role=\"list\" class=\"space-y-4\" v-else>\n <li v-for=\"message in chatMessages\" :key=\"message._id\">\n <chat-message :message=\"message\" :target-dashboard-id=\"currentThread?.dashboardId\"></chat-message>\n </li>\n </ul>\n </div>\n\n\n <!-- Input Area -->\n <div class=\"border-t p-4\">\n <form @submit.prevent=\"sendMessage\" :disabled=\"sendingMessage\" class=\"flex gap-2 items-end justify-end\">\n <textarea\n v-model=\"newMessage\"\n :placeholder=\"sendingMessage ? 'Sending...' : 'Ask about your data, generate a query, or build a chart…'\"\n class=\"flex-1 border rounded px-4 py-2 resize-none overflow-y-auto shadow-sm\"\n :disabled=\"sendingMessage\"\n rows=\"2\"\n ref=\"messageInput\"\n @input=\"adjustTextareaHeight\"\n @keydown.enter.exact.prevent=\"handleEnter\"\n ></textarea>\n <button class=\"bg-blue-600 text-white px-4 h-[42px] rounded disabled:bg-gray-600\" :disabled=\"sendingMessage\">\n <svg v-if=\"sendingMessage\" style=\"height: 1em\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <g>\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" opacity=\"0.3\" />\n <path d=\"M12 2a10 10 0 0 1 10 10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 12 12\" to=\"360 12 12\" dur=\"1s\" repeatCount=\"indefinite\" />\n </path>\n </g>\n </svg>\n <span v-else>Send</span>\n </button>\n </form>\n </div>\n </main>\n</div>\n";
|
|
24973
25425
|
|
|
24974
25426
|
/***/ },
|
|
24975
25427
|
|
|
@@ -25255,7 +25707,7 @@ module.exports = ".document .document-menu {\n display: flex;\n position: stic
|
|
|
25255
25707
|
(module) {
|
|
25256
25708
|
|
|
25257
25709
|
"use strict";
|
|
25258
|
-
module.exports = "<div class=\"document px-1 pt-4 pb-16 md:px-0 bg-slate-50 w-full\">\n <div class=\"max-w-7xl mx-auto\">\n <div class=\"flex gap-4 items-center sticky top-0 z-50 bg-white p-4 border-b border-gray-200 shadow-sm\">\n <div class=\"font-bold overflow-hidden text-ellipsis\">{{model}}: {{documentId}}</div>\n <div class=\"flex grow\">\n <button\n @click=\"viewMode = 'fields'\"\n :class=\"viewMode === 'fields'\n ? 'bg-blue-600 text-white z-10'\n : 'bg-gray-200 text-gray-700 hover:bg-gray-300'\"\n class=\"px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-gray-300 border-r-0 rounded-l-lg rounded-r-none\"\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=\"M4 6h16M4 10h16M4 14h16M4 18h16\"></path>\n </svg>\n Fields\n </button>\n <button\n @click=\"viewMode = 'json'\"\n :class=\"viewMode === 'json'\n ? 'bg-blue-600 text-white z-10'\n : 'bg-gray-200 text-gray-700 hover:bg-gray-300'\"\n class=\"px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-gray-300 rounded-r-lg rounded-l-none\"\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=\"M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4\"></path>\n </svg>\n JSON\n </button>\n </div>\n\n <div class=\"hidden md:flex items-center gap-3 text-sm text-slate-600\">\n <div class=\"text-right leading-tight\">\n <div class=\"text-xs uppercase tracking-wide text-slate-400 flex items-center gap-2 justify-end\">\n <span>Loaded at</span>\n <span class=\"inline-flex items-center gap-1 text-[10px] font-semibold\" :class=\"autoRefreshEnabled ? 'text-forest-green-600' : 'text-slate-400'\">\n <span class=\"inline-block h-1.5 w-1.5 rounded-full\" :class=\"autoRefreshEnabled ? 'bg-forest-green-500' : 'bg-slate-300'\"></span>\n </span>\n </div>\n <div class=\"font-medium text-slate-700\">{{lastUpdatedLabel}}</div>\n </div>\n </div>\n\n <div class=\"gap-2 hidden md:flex items-center\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n :disabled=\"!canEdit\"\n :class=\"{'cursor-not-allowed opacity-50': !canEdit}\"\n type=\"button\"\n class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n × Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n\n <!-- 3-dot menu -->\n <div class=\"relative\">\n <button\n @click=\"desktopMenuOpen = !desktopMenuOpen\"\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md bg-gray-200 px-2.5 py-1.5 text-sm font-medium text-gray-700 hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\"\n aria-expanded=\"desktopMenuOpen\"\n aria-label=\"More options\"\n >\n <svg class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"12\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"19\" r=\"2\"></circle>\n </svg>\n </button>\n <div\n v-show=\"desktopMenuOpen\"\n @click.away=\"desktopMenuOpen = false\"\n class=\"origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-50\"\n >\n <div class=\"py-1 flex flex-col\">\n <button\n @click=\"refreshDocument({ force: true, source: 'manual' }); desktopMenuOpen = false\"\n :disabled=\"isRefreshing\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', isRefreshing ? 'cursor-not-allowed opacity-50' : 'hover:bg-slate-100']\"\n type=\"button\"\n >\n {{isRefreshing ? 'Refreshing...' : 'Refresh'}}\n </button>\n <button\n @click=\"toggleAutoRefresh(); desktopMenuOpen = false\"\n :disabled=\"isLambda\"\n type=\"button\"\n :class=\"['flex items-center px-4 py-2 text-sm', isLambda ? 'text-gray-400 cursor-not-allowed' : 'text-gray-700 hover:bg-slate-100']\"\n :title=\"isLambda ? 'Auto-refresh only available on Express deployments' : ''\"\n >\n {{autoRefreshEnabled ? 'Disable Auto-Refresh' : 'Enable Auto-Refresh'}}\n </button>\n <button\n @click=\"addField(); desktopMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-green-100\"\n >\n Add Field\n </button>\n <button\n @click=\"copyDocument(); desktopMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-blue-100\"\n >\n Copy Document\n </button>\n <button\n @click=\"openScriptDrawer()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-indigo-100\"\n >\n Run Script\n </button>\n <button\n @click=\"shouldShowDeleteModal=true; desktopMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-red-100']\"\n type=\"button\"\n >\n Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true; desktopMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-pink-100']\"\n type=\"button\"\n >\n Clone\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"md:hidden flex items-center\">\n <div class=\"relative\">\n <button\n @click=\"mobileMenuOpen = !mobileMenuOpen\"\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md bg-gray-200 px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\"\n aria-expanded=\"mobileMenuOpen\"\n aria-label=\"Open menu\"\n >\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\"\n d=\"M4 6h16M4 12h16M4 18h16\"></path>\n </svg>\n </button>\n <div\n v-show=\"mobileMenuOpen\"\n @click.away=\"mobileMenuOpen = false\"\n class=\"origin-top-right absolute right-0 mt-2 w-52 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-50\"\n >\n <div class=\"py-1 flex flex-col\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true; mobileMenuOpen = false\"\n :disabled=\"!canEdit\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canEdit ? 'cursor-not-allowed opacity-50' : 'hover:bg-ultramarine-100']\"\n type=\"button\"\n >\n Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false; mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-slate-100\"\n >\n Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-green-100']\"\n @click=\"shouldShowConfirmModal=true; mobileMenuOpen = false\"\n type=\"button\"\n >\n Save\n </button>\n <button\n @click=\"refreshDocument({ force: true, source: 'manual' }); mobileMenuOpen = false\"\n :disabled=\"isRefreshing\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', isRefreshing ? 'cursor-not-allowed opacity-50' : 'hover:bg-slate-100']\"\n type=\"button\"\n >\n {{isRefreshing ? 'Refreshing...' : 'Refresh'}}\n </button>\n <button\n @click=\"toggleAutoRefresh(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-slate-100\"\n >\n Auto-Refresh {{autoRefreshEnabled ? 'ON' : 'OFF'}}\n </button>\n <button\n @click=\"addField(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-green-100\"\n >\n Add Field\n </button>\n <button\n @click=\"copyDocument(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-blue-100\"\n >\n Copy Document\n </button>\n <button\n @click=\"openScriptDrawer()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-indigo-100\"\n >\n Run Script\n </button>\n <button\n @click=\"shouldShowDeleteModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-red-100']\"\n type=\"button\"\n >\n Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-pink-100']\"\n type=\"button\"\n >\n Clone\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :virtualPaths=\"virtualPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"\n :viewMode=\"viewMode\"\n :model=\"model\"\n @add-field=\"addField\"\n @view-mode-change=\"updateViewMode\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">×</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteModal = false;\">×</div>\n <confirm-delete @close=\"shouldShowDeleteModal = false;\" @remove=\"remove\" :value=\"document\"></confirm-delete>\n </template>\n </modal>\n <modal v-if=\"shouldShowCloneModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCloneModal = false;\">×</div>\n <clone-document :currentModel=\"model\" :doc=\"document\" :schemaPaths=\"schemaPaths\" @close=\"showClonedDocument\"></clone-document>\n </template>\n </modal>\n </div>\n <execute-script\n :visible=\"scriptDrawerOpen\"\n :model=\"model\"\n :document-id=\"documentId\"\n :editting=\"editting\"\n @close=\"closeScriptDrawer\"\n @refresh=\"handleScriptRefresh\"\n ></execute-script>\n </div>\n</div>\n";
|
|
25710
|
+
module.exports = "<div class=\"document px-1 pt-4 pb-16 md:px-0 bg-slate-50 w-full\">\n <div class=\"max-w-7xl mx-auto\">\n <div class=\"flex gap-4 items-center sticky top-0 z-50 bg-white p-4 border-b border-gray-200 shadow-sm\">\n <div class=\"font-bold overflow-hidden text-ellipsis\">{{model}}: {{documentId}}</div>\n <div class=\"flex grow\">\n <button\n @click=\"viewMode = 'fields'\"\n :class=\"viewMode === 'fields'\n ? 'bg-blue-600 text-white z-10'\n : 'bg-gray-200 text-gray-700 hover:bg-gray-300'\"\n class=\"px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-gray-300 border-r-0 rounded-l-lg rounded-r-none\"\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=\"M4 6h16M4 10h16M4 14h16M4 18h16\"></path>\n </svg>\n Fields\n </button>\n <button\n @click=\"viewMode = 'json'\"\n :class=\"viewMode === 'json'\n ? 'bg-blue-600 text-white z-10'\n : 'bg-gray-200 text-gray-700 hover:bg-gray-300'\"\n class=\"px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-gray-300 rounded-r-lg rounded-l-none\"\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=\"M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4\"></path>\n </svg>\n JSON\n </button>\n </div>\n\n <div class=\"hidden md:flex items-center gap-3 text-sm text-slate-600\">\n <div class=\"text-right leading-tight\">\n <div class=\"text-xs uppercase tracking-wide text-slate-400 flex items-center gap-2 justify-end\">\n <span>Loaded at</span>\n <span class=\"inline-flex items-center gap-1 text-[10px] font-semibold\" :class=\"autoRefreshEnabled ? 'text-forest-green-600' : 'text-slate-400'\">\n <span class=\"inline-block h-1.5 w-1.5 rounded-full\" :class=\"autoRefreshEnabled ? 'bg-forest-green-500' : 'bg-slate-300'\"></span>\n </span>\n </div>\n <div class=\"font-medium text-slate-700\">{{lastUpdatedLabel}}</div>\n </div>\n </div>\n\n <div class=\"gap-2 hidden md:flex items-center\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n :disabled=\"!canEdit\"\n :class=\"{'cursor-not-allowed opacity-50': !canEdit}\"\n type=\"button\"\n class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n × Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n\n <!-- 3-dot menu -->\n <div class=\"relative\">\n <button\n @click=\"desktopMenuOpen = !desktopMenuOpen\"\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md bg-gray-200 px-2.5 py-1.5 text-sm font-medium text-gray-700 hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\"\n aria-expanded=\"desktopMenuOpen\"\n aria-label=\"More options\"\n >\n <svg class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"12\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"19\" r=\"2\"></circle>\n </svg>\n </button>\n <div\n v-show=\"desktopMenuOpen\"\n @click.away=\"desktopMenuOpen = false\"\n class=\"origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-50\"\n >\n <div class=\"py-1 flex flex-col\">\n <button\n @click=\"refreshDocument({ force: true, source: 'manual' }); desktopMenuOpen = false\"\n :disabled=\"isRefreshing\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', isRefreshing ? 'cursor-not-allowed opacity-50' : 'hover:bg-slate-100']\"\n type=\"button\"\n >\n {{isRefreshing ? 'Refreshing...' : 'Refresh'}}\n </button>\n <button\n @click=\"toggleAutoRefresh(); desktopMenuOpen = false\"\n :disabled=\"isLambda\"\n type=\"button\"\n :class=\"['flex items-center px-4 py-2 text-sm', isLambda ? 'text-gray-400 cursor-not-allowed' : 'text-gray-700 hover:bg-slate-100']\"\n :title=\"isLambda ? 'Auto-refresh only available on Express deployments' : ''\"\n >\n {{autoRefreshEnabled ? 'Disable Auto-Refresh' : 'Enable Auto-Refresh'}}\n </button>\n <button\n @click=\"addField(); desktopMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-green-100\"\n >\n Add Field\n </button>\n <button\n @click=\"copyDocument(); desktopMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-blue-100\"\n >\n Copy Document\n </button>\n <button\n @click=\"openScriptDrawer()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-indigo-100\"\n >\n Run Script\n </button>\n <button\n @click=\"shouldShowDeleteModal=true; desktopMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-red-100']\"\n type=\"button\"\n >\n Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true; desktopMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-pink-100']\"\n type=\"button\"\n >\n Clone\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"md:hidden flex items-center\">\n <div class=\"relative\">\n <button\n @click=\"mobileMenuOpen = !mobileMenuOpen\"\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md bg-gray-200 px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\"\n aria-expanded=\"mobileMenuOpen\"\n aria-label=\"Open menu\"\n >\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\"\n d=\"M4 6h16M4 12h16M4 18h16\"></path>\n </svg>\n </button>\n <div\n v-show=\"mobileMenuOpen\"\n @click.away=\"mobileMenuOpen = false\"\n class=\"origin-top-right absolute right-0 mt-2 w-52 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-50\"\n >\n <div class=\"py-1 flex flex-col\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true; mobileMenuOpen = false\"\n :disabled=\"!canEdit\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canEdit ? 'cursor-not-allowed opacity-50' : 'hover:bg-ultramarine-100']\"\n type=\"button\"\n >\n Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false; mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-slate-100\"\n >\n Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-green-100']\"\n @click=\"shouldShowConfirmModal=true; mobileMenuOpen = false\"\n type=\"button\"\n >\n Save\n </button>\n <button\n @click=\"refreshDocument({ force: true, source: 'manual' }); mobileMenuOpen = false\"\n :disabled=\"isRefreshing\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', isRefreshing ? 'cursor-not-allowed opacity-50' : 'hover:bg-slate-100']\"\n type=\"button\"\n >\n {{isRefreshing ? 'Refreshing...' : 'Refresh'}}\n </button>\n <button\n @click=\"toggleAutoRefresh(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-slate-100\"\n >\n Auto-Refresh {{autoRefreshEnabled ? 'ON' : 'OFF'}}\n </button>\n <button\n @click=\"addField(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-green-100\"\n >\n Add Field\n </button>\n <button\n @click=\"copyDocument(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-blue-100\"\n >\n Copy Document\n </button>\n <button\n @click=\"openScriptDrawer()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-indigo-100\"\n >\n Run Script\n </button>\n <button\n @click=\"shouldShowDeleteModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-red-100']\"\n type=\"button\"\n >\n Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-pink-100']\"\n type=\"button\"\n >\n Clone\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :virtualPaths=\"virtualPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"\n :viewMode=\"viewMode\"\n :model=\"model\"\n @add-field=\"addField\"\n @view-mode-change=\"updateViewMode\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">×</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteModal = false;\">×</div>\n <confirm-delete @close=\"shouldShowDeleteModal = false;\" @remove=\"remove\" :value=\"document\"></confirm-delete>\n </template>\n </modal>\n <modal v-if=\"shouldShowCloneModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCloneModal = false;\">×</div>\n <clone-document :currentModel=\"model\" :doc=\"document\" :schemaPaths=\"schemaPaths\" @close=\"showClonedDocument\"></clone-document>\n </template>\n </modal>\n </div>\n <execute-script\n :visible=\"scriptDrawerOpen\"\n :model=\"model\"\n :document-id=\"documentId\"\n :editting=\"editting\"\n @close=\"closeScriptDrawer\"\n @refresh=\"handleScriptRefresh\"\n ></execute-script>\n <div\n v-if=\"keyboardShortcuts.length > 0\"\n class=\"fixed bottom-4 left-4 z-40 group\"\n >\n <button\n type=\"button\"\n aria-label=\"Keyboard shortcuts\"\n title=\"Keyboard shortcuts\"\n class=\"rounded-full border border-slate-300 bg-white p-2 text-slate-700 shadow-sm transition-colors hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-ultramarine-500\"\n >\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">\n <rect x=\"2.5\" y=\"6.5\" width=\"19\" height=\"11\" rx=\"2\" stroke-width=\"1.5\"></rect>\n <path d=\"M6 10.5h1.5M9 10.5h1.5M12 10.5h1.5M15 10.5h1.5M18 10.5h.01M6 13.5h1.5M9 13.5h6M16.5 13.5h1.5\" stroke-width=\"1.5\" stroke-linecap=\"round\"></path>\n </svg>\n </button>\n <div class=\"pointer-events-none absolute left-0 bottom-full mb-2 min-w-[220px] translate-y-1 rounded-lg bg-slate-900 px-3 py-2.5 text-slate-200 opacity-0 shadow-xl transition-all duration-150 group-hover:pointer-events-auto group-hover:translate-y-0 group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:translate-y-0 group-focus-within:opacity-100\">\n <div\n v-for=\"shortcut in keyboardShortcuts\"\n :key=\"shortcut.command + shortcut.description\"\n class=\"flex items-center justify-between gap-3 text-xs\"\n >\n <span class=\"font-mono font-semibold text-slate-50\">{{shortcut.command}}</span>\n <span class=\"text-slate-300\">{{shortcut.description}}</span>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
|
|
25259
25711
|
|
|
25260
25712
|
/***/ },
|
|
25261
25713
|
|
|
@@ -25563,7 +26015,7 @@ module.exports = ".models {\n position: relative;\n display: flex;\n flex-dir
|
|
|
25563
26015
|
(module) {
|
|
25564
26016
|
|
|
25565
26017
|
"use strict";
|
|
25566
|
-
module.exports = "<div class=\"models flex\" style=\"height: calc(100vh - 55px); height: calc(100dvh - 55px)\">\n <div class=\"fixed top-[65px] cursor-pointer bg-gray-100 rounded-r-md z-10\" @click=\"hideSidebar = false\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"#5f6368\"><path d=\"M360-120v-720h80v720h-80Zm160-160v-400l200 200-200 200Z\"/></svg>\n </div>\n <aside class=\"bg-white border-r overflow-y-auto overflow-x-hidden h-full transition-all duration-300 ease-in-out z-20 w-0 lg:w-48 fixed lg:relative shrink-0\" :class=\"hideSidebar === true ? '!w-0' : hideSidebar === false ? '!w-48' : ''\">\n <div class=\"flex items-center border-b border-gray-100 w-48 overflow-x-hidden\">\n <div class=\"p-1 ml-2 font-bold\">Models</div>\n <button\n @click=\"hideSidebar = true\"\n class=\"ml-auto mr-2 p-2 rounded hover:bg-gray-200 focus:outline-none\"\n aria-label=\"Close sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"currentColor\"><path d=\"M660-320v-320L500-480l160 160ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm120-80v-560H200v560h120Zm80 0h360v-560H400v560Zm-80 0H200h120Z\"/></svg>\n </button>\n </div>\n <nav class=\"flex flex-1 flex-col\">\n <ul role=\"list\" class=\"flex flex-1 flex-col gap-y-7 p-1\">\n <li>\n <ul role=\"list\">\n <li v-for=\"model in models\">\n <router-link\n :to=\"'/model/' + model\"\n class=\"block truncate rounded-md py-2 pr-2 pl-2 text-sm text-gray-700\"\n :class=\"model === currentModel ? 'bg-ultramarine-100 font-bold text-gray-900' : 'hover:bg-ultramarine-50'\">\n {{model}}\n </router-link>\n </li>\n </ul>\n </li>\n </ul>\n <div v-if=\"models.length === 0 && status === 'loaded'\" class=\"p-2 bg-red-100\">\n No models found\n </div>\n </nav>\n </aside>\n <div class=\"documents bg-slate-50\" ref=\"documentsList\">\n <div class=\"relative h-[42px]\" style=\"z-index: 1000\">\n <div class=\"documents-menu bg-slate-50\">\n <div class=\"flex flex-row items-center w-full gap-2\">\n <document-search\n ref=\"documentSearch\"\n :value=\"searchText\"\n :schema-paths=\"schemaPaths\"\n @search=\"search\"\n >\n </document-search>\n <div>\n <span v-if=\"numDocuments == null\">Loading ...</span>\n <span v-else-if=\"typeof numDocuments === 'number'\">{{documents.length}}/{{numDocuments === 1 ? numDocuments + ' document' : numDocuments + ' documents'}}</span>\n </div>\n <button\n @click=\"stagingSelect\"\n type=\"button\"\n :class=\"{ 'bg-gray-500 ring-inset ring-2 ring-gray-300 hover:bg-gray-600': selectMultiple, 'bg-ultramarine-600 hover:bg-ultramarine-500' : !selectMultiple }\"\n class=\"rounded px-2 py-2 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n >\n {{ selectMultiple ? 'Cancel' : 'Select' }}\n </button>\n <button\n v-show=\"selectMultiple\"\n @click=\"shouldShowUpdateMultipleModal=true;\"\n type=\"button\"\n class=\"rounded bg-green-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\"\n >\n Update\n </button>\n <button\n @click=\"shouldShowDeleteMultipleModal=true;\"\n type=\"button\"\n v-show=\"selectMultiple\"\n class=\"rounded bg-red-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500\"\n >\n Delete\n </button>\n <div class=\"relative\" v-show=\"!selectMultiple\" ref=\"actionsMenuContainer\" @keyup.esc.prevent=\"closeActionsMenu\">\n <button\n @click=\"toggleActionsMenu\"\n type=\"button\"\n aria-label=\"More actions\"\n class=\"rounded bg-white px-2 py-2 text-sm font-semibold text-gray-700 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-5 h-5\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Zm0 6a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Zm0 6a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z\" />\n </svg>\n </button>\n <div\n v-if=\"showActionsMenu\"\n class=\"absolute right-0 mt-2 w-48 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-20\"\n >\n <div class=\"py-1\">\n <button\n @click=\"shouldShowExportModal = true; showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Export\n </button>\n <button\n @click=\"shouldShowCreateModal = true; showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Create\n </button>\n <button\n @click=\"openFieldSelection(); showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Projection\n </button>\n <button\n @click=\"openIndexModal\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Indexes\n </button>\n <button\n @click=\"openCollectionInfo\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Collection Info\n </button>\n <button\n @click=\"findOldestDocument\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Find oldest document\n </button>\n </div>\n </div>\n </div>\n <span class=\"isolate inline-flex rounded-md shadow-sm\">\n <button\n @click=\"setOutputType('table')\"\n type=\"button\"\n class=\"relative inline-flex items-center rounded-none rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'table' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/table.svg\">\n </button>\n <button\n @click=\"setOutputType('json')\"\n type=\"button\"\n class=\"relative -ml-px inline-flex items-center px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'json' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/json.svg\">\n </button>\n <button\n @click=\"setOutputType('map')\"\n :disabled=\"geoJsonFields.length === 0\"\n type=\"button\"\n :title=\"geoJsonFields.length > 0 ? 'Map view' : 'No GeoJSON fields detected'\"\n class=\"relative -ml-px inline-flex items-center rounded-none rounded-r-md px-2 py-2 ring-1 ring-inset ring-gray-300 focus:z-10\"\n :class=\"[\n geoJsonFields.length === 0 ? 'text-gray-300 cursor-not-allowed bg-gray-100' : 'text-gray-400 hover:bg-gray-50',\n outputType === 'map' ? 'bg-gray-200' : (geoJsonFields.length > 0 ? 'bg-white' : '')\n ]\">\n <svg class=\"h-5 w-5\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 6.75V15m6-6v8.25m.503 3.498 4.875-2.437c.381-.19.622-.58.622-1.006V4.82c0-.836-.88-1.38-1.628-1.006l-3.869 1.934c-.317.159-.69.159-1.006 0L9.503 3.252a1.125 1.125 0 0 0-1.006 0L3.622 5.689C3.24 5.88 3 6.27 3 6.695V19.18c0 .836.88 1.38 1.628 1.006l3.869-1.934c.317-.159.69-.159 1.006 0l4.994 2.497c.317.158.69.158 1.006 0Z\" />\n </svg>\n </button>\n </span>\n </div>\n </div>\n </div>\n <div class=\"documents-container relative\">\n <div v-if=\"error\">\n <div class=\"bg-red-100 border border-red-400 text-red-700 px-4 py-3 relative m-4 rounded-md\" role=\"alert\">\n <span class=\"block font-bold\">Error</span>\n <span class=\"block\">{{ error }}</span>\n </div>\n </div>\n <table v-else-if=\"outputType === 'table'\">\n <thead class=\"bg-slate-50\">\n <th v-for=\"path in filteredPaths\" @click=\"addPathFilter(path.path)\" class=\"cursor-pointer p-3\">\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown')}})\n </span>\n <span class=\"sort-arrow\" @click=\"sortDocs(1, path.path)\">{{sortBy[path.path] == 1 ? 'X' : '↑'}}</span>\n <span class=\"sort-arrow\" @click=\"sortDocs(-1, path.path)\">{{sortBy[path.path] == -1 ? 'X' : '↓'}}</span>\n </th>\n </thead>\n <tbody>\n <tr v-for=\"document in documents\" @click=\"handleDocumentClick(document, $event)\" :key=\"document._id\" class=\"bg-white hover:bg-slate-50\">\n <td v-for=\"schemaPath in filteredPaths\" class=\"p-3 cursor-pointer\" :class=\"{ 'bg-blue-200': selectedDocuments.some(x => x._id.toString() === document._id.toString()) }\">\n <component\n :is=\"getComponentForPath(schemaPath)\"\n :value=\"getValueForPath(document, schemaPath.path)\"\n :allude=\"getReferenceModel(schemaPath)\">\n </component>\n </td>\n </tr>\n </tbody>\n </table>\n <div v-else-if=\"outputType === 'json'\" class=\"flex flex-col space-y-2 p-1 mt-1\">\n <div\n v-for=\"document in documents\"\n :key=\"document._id\"\n @click=\"handleDocumentContainerClick(document, $event)\"\n :class=\"[\n 'group relative transition-colors rounded-md border border-slate-100',\n selectedDocuments.some(x => x._id.toString() === document._id.toString()) ? 'bg-blue-200' : 'hover:shadow-sm hover:border-slate-300 bg-white'\n ]\"\n >\n <button\n type=\"button\"\n class=\"absolute top-2 right-2 z-10 inline-flex items-center rounded bg-ultramarine-600 px-2 py-1 text-xs font-semibold text-white shadow-sm transition-opacity duration-150 opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n @click.stop=\"openDocument(document)\"\n >\n Open this Document\n </button>\n <list-json :value=\"filterDocument(document)\" :references=\"referenceMap\">\n </list-json>\n </div>\n </div>\n <div v-else-if=\"outputType === 'map'\" class=\"flex flex-col h-full\">\n <div class=\"p-2 bg-white border-b flex items-center gap-2\">\n <label class=\"text-sm font-medium text-gray-700\">GeoJSON Field:</label>\n <select\n :value=\"selectedGeoField\"\n @change=\"setSelectedGeoField($event.target.value)\"\n class=\"rounded-md border border-gray-300 py-1 px-2 text-sm focus:border-ultramarine-500 focus:ring-ultramarine-500\"\n >\n <option v-for=\"field in geoJsonFields\" :key=\"field.path\" :value=\"field.path\">\n {{ field.label }}\n </option>\n </select>\n <async-button\n @click=\"loadMoreDocuments\"\n :disabled=\"loadedAllDocs\"\n type=\"button\"\n class=\"rounded px-2 py-1 text-xs font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n :class=\"loadedAllDocs ? 'bg-gray-400 cursor-not-allowed' : 'bg-ultramarine-600 hover:bg-ultramarine-500'\"\n >\n Load more\n </async-button>\n </div>\n <div class=\"flex-1 min-h-[400px]\" ref=\"modelsMap\"></div>\n </div>\n <div v-if=\"status === 'loading'\" class=\"loader\">\n <img src=\"images/loader.gif\">\n </div>\n </div>\n </div>\n <modal v-if=\"shouldShowExportModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowExportModal = false\">×</div>\n <export-query-results\n :schemaPaths=\"schemaPaths\"\n :search-text=\"searchText\"\n :currentModel=\"currentModel\"\n @done=\"shouldShowExportModal = false\">\n </export-query-results>\n </template>\n </modal>\n <modal v-if=\"shouldShowIndexModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowIndexModal = false\">×</div>\n <div class=\"text-xl font-bold mb-2\">Indexes</div>\n <div v-for=\"index in mongoDBIndexes\" class=\"w-full flex items-center\">\n <div class=\"grow shrink text-left flex justify-between items-center\" v-if=\"index.name != '_id_'\">\n <div>\n <div class=\"font-bold flex items-center gap-2\">\n <div>{{ index.name }}</div>\n <div v-if=\"isTTLIndex(index)\" class=\"rounded-full bg-ultramarine-100 px-2 py-0.5 text-xs font-semibold text-ultramarine-700\">\n TTL: {{ formatTTL(index.expireAfterSeconds) }}\n </div>\n </div>\n <div class=\"text-sm font-mono\">{{ JSON.stringify(index.key) }}</div>\n </div>\n <div>\n <async-button\n type=\"button\"\n @click=\"dropIndex(index.name)\"\n class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600 disabled:bg-gray-400 disabled:cursor-not-allowed\">\n Drop\n </async-button>\n </div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCollectionInfoModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCollectionInfoModal = false\">×</div>\n <div class=\"text-xl font-bold mb-2\">Collection Info</div>\n <div v-if=\"!collectionInfo\" class=\"text-gray-600\">Loading collection details...</div>\n <div v-else class=\"space-y-3\">\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Documents</div>\n <div class=\"text-gray-900\">{{ formatNumber(collectionInfo.documentCount) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Indexes</div>\n <div class=\"text-gray-900\">{{ formatNumber(collectionInfo.indexCount) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Total Index Size</div>\n <div class=\"text-gray-900\">{{ formatCollectionSize(collectionInfo.totalIndexSize) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Total Storage Size</div>\n <div class=\"text-gray-900\">{{ formatCollectionSize(collectionInfo.size) }}</div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Collation</div>\n <div class=\"text-gray-900\">{{ collectionInfo.hasCollation ? 'Yes' : 'No' }}</div>\n </div>\n <div v-if=\"collectionInfo.hasCollation\" class=\"rounded bg-gray-100 p-3 text-sm text-gray-800 overflow-x-auto\">\n <pre class=\"whitespace-pre-wrap\">{{ JSON.stringify(collectionInfo.collation, null, 2) }}</pre>\n </div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Capped</div>\n <div class=\"text-gray-900\">{{ collectionInfo.capped ? 'Yes' : 'No' }}</div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowFieldModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowFieldModal = false; selectedPaths = [...filteredPaths];\">×</div>\n <div v-for=\"(path, index) in schemaPaths\" :key=\"index\" class=\"w-5 flex items-center\">\n <input class=\"mt-0 h-4 w-4 rounded border-gray-300 text-sky-600 focus:ring-sky-600 accent-sky-600\" type=\"checkbox\" :id=\"'path.path'+index\" @change=\"addOrRemove(path)\" :value=\"path.path\" :checked=\"isSelected(path.path)\" />\n <div class=\"ml-2 text-gray-700 grow shrink text-left\">\n <label :for=\"'path.path' + index\">{{path.path}}</label>\n </div>\n </div>\n <div class=\"mt-4 flex gap-2\">\n <button type=\"button\" @click=\"filterDocuments()\" class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">Filter Selection</button>\n <button type=\"button\" @click=\"selectAll()\" class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">Select All</button>\n <button type=\"button\" @click=\"deselectAll()\" class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">Deselect All</button>\n <button type=\"button\" @click=\"resetDocuments()\" class=\"rounded-md bg-gray-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\" >Cancel</button>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCreateModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCreateModal = false;\">×</div>\n <create-document :currentModel=\"currentModel\" :paths=\"schemaPaths\" @close=\"closeCreationModal\"></create-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowUpdateMultipleModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowUpdateMultipleModal = false;\">×</div>\n <update-document :currentModel=\"currentModel\" :document=\"selectedDocuments\" :multiple=\"true\" @update=\"updateDocuments\" @close=\"shouldShowUpdateMultipleModal=false;\"></update-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteMultipleModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteMultipleModal = false;\">×</div>\n <h2>Are you sure you want to delete {{selectedDocuments.length}} documents?</h2>\n <div>\n <list-json :value=\"selectedDocuments\"></list-json>\n </div>\n <div class=\"flex gap-4\">\n <async-button @click=\"deleteDocuments\" class=\"rounded bg-red-500 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n Confirm\n </async-button>\n <button @click=\"shouldShowDeleteMultipleModal = false;\" class=\"rounded bg-gray-400 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500\">\n Cancel\n </button>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
26018
|
+
module.exports = "<div class=\"models flex\" style=\"height: calc(100vh - 55px); height: calc(100dvh - 55px)\">\n <div class=\"fixed top-[65px] cursor-pointer bg-gray-100 rounded-r-md z-10\" @click=\"hideSidebar = false\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"#5f6368\"><path d=\"M360-120v-720h80v720h-80Zm160-160v-400l200 200-200 200Z\"/></svg>\n </div>\n <aside class=\"bg-white border-r overflow-y-auto overflow-x-hidden h-full transition-all duration-300 ease-in-out z-20 w-0 lg:w-48 fixed lg:relative shrink-0\" :class=\"hideSidebar === true ? '!w-0' : hideSidebar === false ? '!w-48' : ''\">\n <div class=\"flex items-center border-b border-gray-100 w-48 overflow-x-hidden\">\n <div class=\"p-1 ml-2 font-bold\">Models</div>\n <button\n @click=\"hideSidebar = true\"\n class=\"ml-auto mr-2 p-2 rounded hover:bg-gray-200 focus:outline-none\"\n aria-label=\"Close sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"currentColor\"><path d=\"M660-320v-320L500-480l160 160ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm120-80v-560H200v560h120Zm80 0h360v-560H400v560Zm-80 0H200h120Z\"/></svg>\n </button>\n </div>\n <nav class=\"flex flex-1 flex-col\">\n <ul role=\"list\" class=\"flex flex-1 flex-col gap-y-7 p-1\">\n <li>\n <ul role=\"list\">\n <li v-for=\"model in models\">\n <router-link\n :to=\"'/model/' + model\"\n class=\"flex items-center gap-2 rounded-md py-2 pr-2 pl-2 text-sm text-gray-700\"\n :class=\"model === currentModel ? 'bg-ultramarine-100 font-bold text-gray-900' : 'hover:bg-ultramarine-50'\">\n <span class=\"truncate\">{{model}}</span>\n <span\n v-if=\"modelDocumentCounts && modelDocumentCounts[model] !== undefined && model !== currentModel\"\n class=\"ml-auto text-xs text-gray-500 bg-gray-100 rounded-md px-1 py-[2px]\"\n >\n {{formatCompactCount(modelDocumentCounts[model])}}\n </span>\n </router-link>\n </li>\n </ul>\n </li>\n </ul>\n <div v-if=\"models.length === 0 && status === 'loaded'\" class=\"p-2 bg-red-100\">\n No models found\n </div>\n </nav>\n </aside>\n <div class=\"documents bg-slate-50\" ref=\"documentsList\">\n <div class=\"relative h-[42px]\" style=\"z-index: 1000\">\n <div class=\"documents-menu bg-slate-50\">\n <div class=\"flex flex-row items-center w-full gap-2\">\n <document-search\n ref=\"documentSearch\"\n :value=\"searchText\"\n :schema-paths=\"schemaPaths\"\n @search=\"search\"\n >\n </document-search>\n <div>\n <span v-if=\"numDocuments == null\">Loading ...</span>\n <span v-else-if=\"typeof numDocuments === 'number'\">{{documents.length}}/{{numDocuments === 1 ? numDocuments + ' document' : numDocuments + ' documents'}}</span>\n </div>\n <button\n @click=\"stagingSelect\"\n type=\"button\"\n :class=\"{ 'bg-gray-500 ring-inset ring-2 ring-gray-300 hover:bg-gray-600': selectMultiple, 'bg-ultramarine-600 hover:bg-ultramarine-500' : !selectMultiple }\"\n class=\"rounded px-2 py-2 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n >\n {{ selectMultiple ? 'Cancel' : 'Select' }}\n </button>\n <button\n v-show=\"selectMultiple\"\n @click=\"shouldShowUpdateMultipleModal=true;\"\n type=\"button\"\n class=\"rounded bg-green-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\"\n >\n Update\n </button>\n <button\n @click=\"shouldShowDeleteMultipleModal=true;\"\n type=\"button\"\n v-show=\"selectMultiple\"\n class=\"rounded bg-red-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500\"\n >\n Delete\n </button>\n <div class=\"relative\" v-show=\"!selectMultiple\" ref=\"actionsMenuContainer\" @keyup.esc.prevent=\"closeActionsMenu\">\n <button\n @click=\"toggleActionsMenu\"\n type=\"button\"\n aria-label=\"More actions\"\n class=\"rounded bg-white px-2 py-2 text-sm font-semibold text-gray-700 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-5 h-5\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Zm0 6a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Zm0 6a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z\" />\n </svg>\n </button>\n <div\n v-if=\"showActionsMenu\"\n class=\"absolute right-0 mt-2 w-48 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-20\"\n >\n <div class=\"py-1\">\n <button\n @click=\"shouldShowExportModal = true; showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Export\n </button>\n <button\n @click=\"shouldShowCreateModal = true; showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Create\n </button>\n <button\n @click=\"openFieldSelection(); showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Projection\n </button>\n <button\n @click=\"openIndexModal\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Indexes\n </button>\n <button\n @click=\"openCollectionInfo\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Collection Info\n </button>\n <button\n @click=\"findOldestDocument\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Find oldest document\n </button>\n </div>\n </div>\n </div>\n <span class=\"isolate inline-flex rounded-md shadow-sm\">\n <button\n @click=\"setOutputType('table')\"\n type=\"button\"\n class=\"relative inline-flex items-center rounded-none rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'table' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/table.svg\">\n </button>\n <button\n @click=\"setOutputType('json')\"\n type=\"button\"\n class=\"relative -ml-px inline-flex items-center px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'json' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/json.svg\">\n </button>\n <button\n @click=\"setOutputType('map')\"\n :disabled=\"geoJsonFields.length === 0\"\n type=\"button\"\n :title=\"geoJsonFields.length > 0 ? 'Map view' : 'No GeoJSON fields detected'\"\n class=\"relative -ml-px inline-flex items-center rounded-none rounded-r-md px-2 py-2 ring-1 ring-inset ring-gray-300 focus:z-10\"\n :class=\"[\n geoJsonFields.length === 0 ? 'text-gray-300 cursor-not-allowed bg-gray-100' : 'text-gray-400 hover:bg-gray-50',\n outputType === 'map' ? 'bg-gray-200' : (geoJsonFields.length > 0 ? 'bg-white' : '')\n ]\">\n <svg class=\"h-5 w-5\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 6.75V15m6-6v8.25m.503 3.498 4.875-2.437c.381-.19.622-.58.622-1.006V4.82c0-.836-.88-1.38-1.628-1.006l-3.869 1.934c-.317.159-.69.159-1.006 0L9.503 3.252a1.125 1.125 0 0 0-1.006 0L3.622 5.689C3.24 5.88 3 6.27 3 6.695V19.18c0 .836.88 1.38 1.628 1.006l3.869-1.934c.317-.159.69-.159 1.006 0l4.994 2.497c.317.158.69.158 1.006 0Z\" />\n </svg>\n </button>\n </span>\n </div>\n </div>\n </div>\n <div class=\"documents-container relative\">\n <div v-if=\"error\">\n <div class=\"bg-red-100 border border-red-400 text-red-700 px-4 py-3 relative m-4 rounded-md\" role=\"alert\">\n <span class=\"block font-bold\">Error</span>\n <span class=\"block\">{{ error }}</span>\n </div>\n </div>\n <table v-else-if=\"outputType === 'table'\">\n <thead class=\"bg-slate-50\">\n <th v-for=\"path in filteredPaths\" @click=\"addPathFilter(path.path)\" class=\"cursor-pointer p-3\">\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown')}})\n </span>\n <span class=\"sort-arrow\" @click=\"sortDocs(1, path.path)\">{{sortBy[path.path] == 1 ? 'X' : '↑'}}</span>\n <span class=\"sort-arrow\" @click=\"sortDocs(-1, path.path)\">{{sortBy[path.path] == -1 ? 'X' : '↓'}}</span>\n </th>\n </thead>\n <tbody>\n <tr v-for=\"document in documents\" @click=\"handleDocumentClick(document, $event)\" :key=\"document._id\" class=\"bg-white hover:bg-slate-50\">\n <td v-for=\"schemaPath in filteredPaths\" class=\"p-3 cursor-pointer\" :class=\"{ 'bg-blue-200': selectedDocuments.some(x => x._id.toString() === document._id.toString()) }\">\n <component\n :is=\"getComponentForPath(schemaPath)\"\n :value=\"getValueForPath(document, schemaPath.path)\"\n :allude=\"getReferenceModel(schemaPath)\">\n </component>\n </td>\n </tr>\n </tbody>\n </table>\n <div v-else-if=\"outputType === 'json'\" class=\"flex flex-col space-y-2 p-1 mt-1\">\n <div\n v-for=\"document in documents\"\n :key=\"document._id\"\n @click=\"handleDocumentContainerClick(document, $event)\"\n :class=\"[\n 'group relative transition-colors rounded-md border border-slate-100',\n selectedDocuments.some(x => x._id.toString() === document._id.toString()) ? 'bg-blue-200' : 'hover:shadow-sm hover:border-slate-300 bg-white'\n ]\"\n >\n <button\n type=\"button\"\n class=\"absolute top-2 right-2 z-10 inline-flex items-center rounded bg-ultramarine-600 px-2 py-1 text-xs font-semibold text-white shadow-sm transition-opacity duration-150 opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n @click.stop=\"openDocument(document)\"\n >\n Open this Document\n </button>\n <list-json :value=\"filterDocument(document)\" :references=\"referenceMap\">\n </list-json>\n </div>\n </div>\n <div v-else-if=\"outputType === 'map'\" class=\"flex flex-col h-full\">\n <div class=\"p-2 bg-white border-b flex items-center gap-2\">\n <label class=\"text-sm font-medium text-gray-700\">GeoJSON Field:</label>\n <select\n :value=\"selectedGeoField\"\n @change=\"setSelectedGeoField($event.target.value)\"\n class=\"rounded-md border border-gray-300 py-1 px-2 text-sm focus:border-ultramarine-500 focus:ring-ultramarine-500\"\n >\n <option v-for=\"field in geoJsonFields\" :key=\"field.path\" :value=\"field.path\">\n {{ field.label }}\n </option>\n </select>\n <async-button\n @click=\"loadMoreDocuments\"\n :disabled=\"loadedAllDocs\"\n type=\"button\"\n class=\"rounded px-2 py-1 text-xs font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n :class=\"loadedAllDocs ? 'bg-gray-400 cursor-not-allowed' : 'bg-ultramarine-600 hover:bg-ultramarine-500'\"\n >\n Load more\n </async-button>\n </div>\n <div class=\"flex-1 min-h-[400px]\" ref=\"modelsMap\"></div>\n </div>\n <div v-if=\"status === 'loading'\" class=\"loader\">\n <img src=\"images/loader.gif\">\n </div>\n </div>\n </div>\n <modal v-if=\"shouldShowExportModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowExportModal = false\">×</div>\n <export-query-results\n :schemaPaths=\"schemaPaths\"\n :search-text=\"searchText\"\n :currentModel=\"currentModel\"\n @done=\"shouldShowExportModal = false\">\n </export-query-results>\n </template>\n </modal>\n <modal v-if=\"shouldShowIndexModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowIndexModal = false\">×</div>\n <div class=\"text-xl font-bold mb-2\">Indexes</div>\n <div v-for=\"index in mongoDBIndexes\" class=\"w-full flex items-center\">\n <div class=\"grow shrink text-left flex justify-between items-center\" v-if=\"index.name != '_id_'\">\n <div>\n <div class=\"font-bold flex items-center gap-2\">\n <div>{{ index.name }}</div>\n <div v-if=\"isTTLIndex(index)\" class=\"rounded-full bg-ultramarine-100 px-2 py-0.5 text-xs font-semibold text-ultramarine-700\">\n TTL: {{ formatTTL(index.expireAfterSeconds) }}\n </div>\n </div>\n <div class=\"text-sm font-mono\">{{ JSON.stringify(index.key) }}</div>\n </div>\n <div>\n <async-button\n type=\"button\"\n @click=\"dropIndex(index.name)\"\n class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600 disabled:bg-gray-400 disabled:cursor-not-allowed\">\n Drop\n </async-button>\n </div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCollectionInfoModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCollectionInfoModal = false\">×</div>\n <div class=\"text-xl font-bold mb-2\">Collection Info</div>\n <div v-if=\"!collectionInfo\" class=\"text-gray-600\">Loading collection details...</div>\n <div v-else class=\"space-y-3\">\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Documents</div>\n <div class=\"text-gray-900\">{{ formatNumber(collectionInfo.documentCount) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Indexes</div>\n <div class=\"text-gray-900\">{{ formatNumber(collectionInfo.indexCount) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Total Index Size</div>\n <div class=\"text-gray-900\">{{ formatCollectionSize(collectionInfo.totalIndexSize) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Total Storage Size</div>\n <div class=\"text-gray-900\">{{ formatCollectionSize(collectionInfo.size) }}</div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Collation</div>\n <div class=\"text-gray-900\">{{ collectionInfo.hasCollation ? 'Yes' : 'No' }}</div>\n </div>\n <div v-if=\"collectionInfo.hasCollation\" class=\"rounded bg-gray-100 p-3 text-sm text-gray-800 overflow-x-auto\">\n <pre class=\"whitespace-pre-wrap\">{{ JSON.stringify(collectionInfo.collation, null, 2) }}</pre>\n </div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Capped</div>\n <div class=\"text-gray-900\">{{ collectionInfo.capped ? 'Yes' : 'No' }}</div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowFieldModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowFieldModal = false; selectedPaths = [...filteredPaths];\">×</div>\n <div v-for=\"(path, index) in schemaPaths\" :key=\"index\" class=\"w-5 flex items-center\">\n <input class=\"mt-0 h-4 w-4 rounded border-gray-300 text-sky-600 focus:ring-sky-600 accent-sky-600\" type=\"checkbox\" :id=\"'path.path'+index\" @change=\"addOrRemove(path)\" :value=\"path.path\" :checked=\"isSelected(path.path)\" />\n <div class=\"ml-2 text-gray-700 grow shrink text-left\">\n <label :for=\"'path.path' + index\">{{path.path}}</label>\n </div>\n </div>\n <div class=\"mt-4 flex gap-2\">\n <button type=\"button\" @click=\"filterDocuments()\" class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">Filter Selection</button>\n <button type=\"button\" @click=\"selectAll()\" class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">Select All</button>\n <button type=\"button\" @click=\"deselectAll()\" class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">Deselect All</button>\n <button type=\"button\" @click=\"resetDocuments()\" class=\"rounded-md bg-gray-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\" >Cancel</button>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCreateModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCreateModal = false;\">×</div>\n <create-document :currentModel=\"currentModel\" :paths=\"schemaPaths\" @close=\"closeCreationModal\"></create-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowUpdateMultipleModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowUpdateMultipleModal = false;\">×</div>\n <update-document :currentModel=\"currentModel\" :document=\"selectedDocuments\" :multiple=\"true\" @update=\"updateDocuments\" @close=\"shouldShowUpdateMultipleModal=false;\"></update-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteMultipleModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteMultipleModal = false;\">×</div>\n <h2>Are you sure you want to delete {{selectedDocuments.length}} documents?</h2>\n <div>\n <list-json :value=\"selectedDocuments\"></list-json>\n </div>\n <div class=\"flex gap-4\">\n <async-button @click=\"deleteDocuments\" class=\"rounded bg-red-500 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n Confirm\n </async-button>\n <button @click=\"shouldShowDeleteMultipleModal = false;\" class=\"rounded bg-gray-400 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500\">\n Cancel\n </button>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
25567
26019
|
|
|
25568
26020
|
/***/ },
|
|
25569
26021
|
|
|
@@ -25600,6 +26052,28 @@ module.exports = "<div class=\"w-full h-full flex items-center justify-center\">
|
|
|
25600
26052
|
|
|
25601
26053
|
/***/ },
|
|
25602
26054
|
|
|
26055
|
+
/***/ "./frontend/src/task-by-name/task-by-name.html"
|
|
26056
|
+
/*!*****************************************************!*\
|
|
26057
|
+
!*** ./frontend/src/task-by-name/task-by-name.html ***!
|
|
26058
|
+
\*****************************************************/
|
|
26059
|
+
(module) {
|
|
26060
|
+
|
|
26061
|
+
"use strict";
|
|
26062
|
+
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 <task-details\n v-else-if=\"taskGroup\"\n :task-group=\"taskGroup\"\n :back-to=\"{ name: 'tasks' }\"\n @task-created=\"onTaskCreated\"\n @task-cancelled=\"onTaskCancelled\"\n ></task-details>\n</div>\n";
|
|
26063
|
+
|
|
26064
|
+
/***/ },
|
|
26065
|
+
|
|
26066
|
+
/***/ "./frontend/src/task-single/task-single.html"
|
|
26067
|
+
/*!***************************************************!*\
|
|
26068
|
+
!*** ./frontend/src/task-single/task-single.html ***!
|
|
26069
|
+
\***************************************************/
|
|
26070
|
+
(module) {
|
|
26071
|
+
|
|
26072
|
+
"use strict";
|
|
26073
|
+
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 <div v-else-if=\"status === 'notfound'\" class=\"text-gray-600\">\n Task not found.\n </div>\n <div v-else-if=\"task\" class=\"max-w-4xl\">\n <button @click=\"goBack\" class=\"text-gray-500 hover:text-gray-700 mb-4 flex items-center gap-1\">\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.name }}\n </button>\n <h1 class=\"text-2xl font-bold text-gray-700 mb-1\">{{ task.name }}</h1>\n <p class=\"text-gray-500 mb-6\">Task details</p>\n\n <div class=\"bg-white rounded-lg shadow p-6 md:p-8\">\n <div class=\"flex items-center gap-3 mb-6\">\n <span class=\"text-sm font-medium text-gray-900\">ID: {{ task.id }}</span>\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-6 mb-6\">\n <div>\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Scheduled At</label>\n <div class=\"text-sm text-gray-900\">{{ formatDate(task.scheduledAt) }}</div>\n </div>\n <div v-if=\"task?.startedAt\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Started At</label>\n <div class=\"text-sm text-gray-900\">{{ formatDate(task.startedAt) }}</div>\n </div>\n <div v-if=\"task?.completedAt\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Completed At</label>\n <div class=\"text-sm text-gray-900\">{{ formatDate(task.completedAt) }}</div>\n </div>\n </div>\n\n <div v-if=\"task?.params\" class=\"mb-6\">\n <label class=\"block text-sm font-medium text-gray-700 mb-2\">Params</label>\n <div class=\"bg-gray-50 rounded-md p-4\">\n <list-json :value=\"task.params\"></list-json>\n </div>\n </div>\n\n <div v-if=\"task?.result\" class=\"mb-6\">\n <label class=\"block text-sm font-medium text-gray-700 mb-2\">Result</label>\n <div class=\"bg-gray-50 rounded-md p-4\">\n <list-json :value=\"task.result\"></list-json>\n </div>\n </div>\n\n <div v-if=\"task?.error\" class=\"mb-6\">\n <label class=\"block text-sm font-medium text-gray-700 mb-2\">Error</label>\n <div class=\"bg-gray-50 rounded-md p-4\">\n <list-json :value=\"task.error\"></list-json>\n </div>\n </div>\n\n <div class=\"flex flex-wrap gap-3 pt-4 border-t border-gray-200\">\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 disabled:opacity-50 disabled:cursor-not-allowed\"\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 disabled:opacity-50 disabled:cursor-not-allowed\"\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\"\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 Task\n </button>\n </div>\n </div>\n </div>\n\n <!-- Reschedule 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 <h3 class=\"text-lg font-medium text-gray-900 mb-4\">Reschedule Task</h3>\n <p class=\"text-sm text-gray-600 mb-2\">Reschedule task <strong>{{ selectedTask?.id }}</strong>?</p>\n <p class=\"text-sm text-gray-500 mb-4\">This will reset the task's status and schedule it to run again.</p>\n <label for=\"newScheduledTime\" class=\"block text-sm font-medium text-gray-700 mb-2\">New Scheduled Time</label>\n <input\n id=\"newScheduledTime\"\n v-model=\"newScheduledTime\"\n type=\"datetime-local\"\n class=\"w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 mb-4\"\n />\n <div class=\"flex gap-3\">\n <button @click=\"confirmRescheduleTask\" class=\"flex-1 bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 font-medium\">Reschedule</button>\n <button @click=\"showRescheduleModal = false\" class=\"flex-1 bg-gray-300 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-400 font-medium\">Cancel</button>\n </div>\n </div>\n </template>\n </modal>\n\n <!-- Run 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 <h3 class=\"text-lg font-medium text-gray-900 mb-4\">Run Task Now</h3>\n <p class=\"text-sm text-gray-600 mb-2\">Run task <strong>{{ selectedTask?.id }}</strong> immediately?</p>\n <p class=\"text-sm text-gray-500 mb-4\">This will execute the task right away, bypassing its scheduled time.</p>\n <div class=\"flex gap-3\">\n <button @click=\"confirmRunTask\" class=\"flex-1 bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 font-medium\">Run Now</button>\n <button @click=\"showRunModal = false\" class=\"flex-1 bg-gray-300 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-400 font-medium\">Cancel</button>\n </div>\n </div>\n </template>\n </modal>\n\n <!-- Cancel Task 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 <h3 class=\"text-lg font-medium text-gray-900 mb-4\">Cancel Task</h3>\n <p class=\"text-sm text-gray-600 mb-2\">Cancel task <strong>{{ selectedTask?.id }}</strong>?</p>\n <p class=\"text-sm text-gray-500 mb-4\">This will permanently cancel the task and it cannot be undone.</p>\n <div class=\"flex gap-3\">\n <button @click=\"confirmCancelTask\" class=\"flex-1 bg-red-600 text-white px-4 py-2 rounded-md hover:bg-red-700 font-medium\">Cancel Task</button>\n <button @click=\"showCancelModal = false\" class=\"flex-1 bg-gray-300 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-400 font-medium\">Keep Task</button>\n </div>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
26074
|
+
|
|
26075
|
+
/***/ },
|
|
26076
|
+
|
|
25603
26077
|
/***/ "./frontend/src/tasks/task-details/task-details.html"
|
|
25604
26078
|
/*!***********************************************************!*\
|
|
25605
26079
|
!*** ./frontend/src/tasks/task-details/task-details.html ***!
|
|
@@ -25607,7 +26081,7 @@ module.exports = "<div class=\"w-full h-full flex items-center justify-center\">
|
|
|
25607
26081
|
(module) {
|
|
25608
26082
|
|
|
25609
26083
|
"use strict";
|
|
25610
|
-
module.exports = "<div class=\"p-4 space-y-6\">\n <div class=\"flex items-center justify-between\">\n <div>\n <button @click=\"$emit('back')\" class=\"text-gray-500 hover:text-gray-700 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 Back to Task Groups\n </button>\n <h1 class=\"text-2xl font-bold text-gray-700\">{{ taskGroup.name }}</h1>\n <p class=\"text-gray-500\">Total: {{ taskGroup.totalCount }} tasks</p>\n </div>\n\n </div>\n\n <!-- Status Summary -->\n <div 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-gray-50 border border-gray-200 rounded-md p-3 text-center hover:bg-gray-100 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-gray-700\">{{ taskGroup.statusCounts.cancelled || 0 }}</div>\n </button>\n </div>\n\n <!-- Task List -->\n <div class=\"bg-white rounded-lg shadow\">\n <div class=\"px-6 py-4 border-b border-gray-200 flex items-center justify-between\">\n <h2 class=\"text-lg font-semibold text-gray-700\">\n Individual Tasks\n <span v-if=\"currentFilter\" class=\"text-sm font-normal text-gray-500 ml-2\">\n (Filtered by {{ currentFilter }})\n </span>\n </h2>\n <button \n v-if=\"currentFilter\"\n @click=\"clearFilter\"\n class=\"text-sm text-ultramarine-600 hover:text-ultramarine-700 font-medium\"\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-gray-900\">Task ID: {{ task.id }}</span>\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-gray-700 mb-1\">Scheduled At</label>\n <div class=\"text-sm text-gray-900\">{{ formatDate(task.scheduledAt) }}</div>\n </div>\n <div v-if=\"task.startedAt\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Started At</label>\n <div class=\"text-sm text-gray-900\">{{ formatDate(task.startedAt) }}</div>\n </div>\n <div v-if=\"task.completedAt\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Completed At</label>\n <div class=\"text-sm text-gray-900\">{{ formatDate(task.completedAt) }}</div>\n </div>\n <div v-if=\"task.error\">\n <label class=\"block text-sm font-medium text-gray-700 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-gray-700 mb-2\">Parameters</label>\n <div class=\"bg-gray-50 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-gray-900\">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-gray-500 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-gray-700 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-gray-300 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-gray-700 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-gray-900\">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-gray-500 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-gray-700 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-gray-900\">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-gray-500 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-gray-700 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";
|
|
26084
|
+
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-gray-500 hover:text-gray-700 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-gray-700\">{{ taskGroup.name }}</h1>\n <p class=\"text-gray-500\">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-gray-700\">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-ultramarine-600 text-white border-ultramarine-600' : 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'\"\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-ultramarine-600 text-white border-ultramarine-600' : 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'\"\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-gray-50 border border-gray-200 rounded-md p-3 text-center hover:bg-gray-100 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-gray-700\">{{ 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-white border border-gray-200 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-gray-500 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-gray-100 text-gray-500 hover:bg-gray-200'\"\n @click=\"filterByStatus(status)\"\n >\n {{ statusLabel(status) }}\n </button>\n </div>\n </div>\n </div>\n\n <!-- Task List -->\n <div class=\"bg-white rounded-lg shadow\">\n <div class=\"px-6 py-6 border-b border-gray-200 flex items-center justify-between bg-gray-50\">\n <h2 class=\"text-xl font-bold text-gray-900\">\n Individual Tasks\n <span v-if=\"currentFilter\" class=\"ml-3 text-base font-semibold text-ultramarine-700\">\n (Filtered by {{ currentFilter }})\n </span>\n </h2>\n <button \n v-if=\"currentFilter\"\n @click=\"clearFilter\"\n class=\"text-sm font-semibold text-ultramarine-600 hover:text-ultramarine-700\"\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-gray-900\">Task ID: {{ task.id }}</span>\n <router-link\n v-if=\"backTo\"\n :to=\"taskDetailRoute(task)\"\n class=\"text-sm text-ultramarine-600 hover:text-ultramarine-700 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-gray-700 mb-1\">Scheduled At</label>\n <div class=\"text-sm text-gray-900\">{{ formatDate(task.scheduledAt) }}</div>\n </div>\n <div v-if=\"task.startedAt\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Started At</label>\n <div class=\"text-sm text-gray-900\">{{ formatDate(task.startedAt) }}</div>\n </div>\n <div v-if=\"task.completedAt\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Completed At</label>\n <div class=\"text-sm text-gray-900\">{{ formatDate(task.completedAt) }}</div>\n </div>\n <div v-if=\"task.error\">\n <label class=\"block text-sm font-medium text-gray-700 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-gray-700 mb-2\">Parameters</label>\n <div class=\"bg-gray-50 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-gray-900\">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-gray-500 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-gray-700 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-gray-300 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-gray-700 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-gray-900\">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-gray-500 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-gray-700 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-gray-900\">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-gray-500 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-gray-700 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";
|
|
25611
26085
|
|
|
25612
26086
|
/***/ },
|
|
25613
26087
|
|
|
@@ -25629,7 +26103,7 @@ module.exports = "";
|
|
|
25629
26103
|
(module) {
|
|
25630
26104
|
|
|
25631
26105
|
"use strict";
|
|
25632
|
-
module.exports = "<div class=\"p-4 space-y-6\">\n <!-- Task Details View -->\n <task-details \n v-if=\"showTaskDetails && selectedTaskGroup\"\n :task-group=\"selectedTaskGroup\"\n :current-filter=\"taskDetailsFilter\"\n @back=\"hideTaskDetails\"\n @task-created=\"onTaskCreated\"\n @task-cancelled=\"onTaskCancelled\"\n @update:current-filter=\"taskDetailsFilter = $event\"\n ></task-details>\n\n <!-- Main Tasks View -->\n <div v-else>\n <h1 class=\"text-2xl font-bold text-gray-700 mb-4\">Task Overview</h1>\n <div v-if=\"status == 'init'\">\n <img src=\"images/loader.gif\" />\n </div>\n <!-- Task List -->\n <div class=\"bg-white p-4 rounded-lg shadow\" v-if=\"status == 'loaded'\">\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\">\n <option v-for=\"option in dateFilters\" :key=\"option.value\" :value=\"option.value\">\n {{ option.label }}\n </option>\n </select>\n </div>\n <div class=\"mb-4\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Filter by Status:</label>\n <select v-model=\"selectedStatus\" @change=\"getTasks\" class=\"border-gray-300 rounded-md shadow-sm w-full p-2\">\n <option v-for=\"option in statusFilters\" :key=\"option.value\" :value=\"option.value\">\n {{ option.label }}\n </option>\n </select>\n </div>\n <div class=\"mb-4\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Search by Task Name:</label>\n <input \n v-model=\"searchQuery\" \n type=\"text\" \n @input=\"onSearchInput\"\n class=\"border-gray-300 rounded-md shadow-sm w-full p-2\"\n placeholder=\"Enter task name to search...\"\n >\n </div>\n <div class=\"mb-4\">\n <button\n @click=\"resetFilters\"\n class=\"w-full bg-gray-200 text-gray-700 hover:bg-gray-300 font-medium py-2 px-4 rounded-md transition\"\n >\n Reset Filters\n </button>\n </div>\n <div class=\"mb-6\">\n <button\n @click=\"openCreateTaskModal\"\n class=\"w-full bg-ultramarine-600 text-white hover:bg-ultramarine-700 font-medium py-2 px-4 rounded-md transition\"\n >\n Create New Task\n </button>\n </div>\n <!-- Summary Section -->\n <div class=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <button \n @click=\"setStatusFilter('pending')\"\n :class=\"getStatusColor('pending') + ' p-4 rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 cursor-pointer border-2 border-yellow-200 hover:border-yellow-300'\"\n >\n <div class=\"text-sm\">Scheduled</div>\n <div class=\"text-2xl font-bold\">{{pendingCount}}</div>\n </button>\n <button \n @click=\"setStatusFilter('succeeded')\"\n :class=\"getStatusColor('succeeded') + ' p-4 rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 cursor-pointer border-2 border-green-200 hover:border-green-300'\"\n >\n <div class=\"text-sm\">Completed</div>\n <div class=\"text-2xl font-bold\">{{succeededCount}}</div>\n </button>\n <button \n @click=\"setStatusFilter('failed')\"\n :class=\"getStatusColor('failed') + ' p-4 rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 cursor-pointer border-2 border-red-200 hover:border-red-300'\"\n >\n <div class=\"text-sm\">Failed</div>\n <div class=\"text-2xl font-bold\">{{failedCount}}</div>\n </button>\n <button \n @click=\"setStatusFilter('cancelled')\"\n :class=\"getStatusColor('cancelled') + ' p-4 rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 cursor-pointer border-2 border-gray-200 hover:border-gray-300'\"\n >\n <div class=\"text-sm\">Cancelled</div>\n <div class=\"text-2xl font-bold\">{{cancelledCount}}</div>\n </button>\n </div>\n \n <!-- Grouped Task List -->\n <div class=\"mt-6\">\n <h2 class=\"text-lg font-semibold text-gray-700 mb-4\">Tasks by Name</h2>\n <ul class=\"divide-y divide-gray-200\">\n <li v-for=\"group in tasksByName\" :key=\"group.name\" class=\"p-4 group hover:border hover:rounded-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200\">\n <div class=\"flex items-center justify-between mb-3 \">\n <div class=\"flex-1 cursor-pointer\" @click=\"openTaskGroupDetails(group)\">\n <div class=\"flex items-center gap-2\">\n <div class=\"font-medium text-lg group-hover:text-ultramarine-600 transition-colors\">{{ group.name }}</div>\n <svg class=\"w-4 h-4 text-gray-400 group-hover:text-ultramarine-600 transition-colors\" 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 </div>\n <div class=\"text-sm text-gray-500 group-hover:text-gray-700 transition-colors\">Total: {{ group.totalCount }} tasks</div>\n <div class=\"text-xs text-ultramarine-600 opacity-0 group-hover:opacity-100 transition-opacity mt-1\">\n Click to view details\n </div>\n </div>\n <div class=\"text-sm text-gray-500\">\n Last run: {{ group.lastRun ? new Date(group.lastRun).toLocaleString() : 'Never' }}\n </div>\n </div>\n \n <!-- Status Counts -->\n <div class=\"grid grid-cols-2 sm:grid-cols-4 gap-2\">\n <button \n @click.stop=\"openTaskGroupDetailsWithFilter(group, 'pending')\"\n class=\"bg-yellow-50 border border-yellow-200 rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-yellow-300\"\n >\n <div class=\"text-xs text-yellow-600 font-medium\">Pending</div>\n <div class=\"text-lg font-bold text-yellow-700\">{{ group.statusCounts.pending || 0 }}</div>\n </button>\n <button \n @click.stop=\"openTaskGroupDetailsWithFilter(group, 'succeeded')\"\n class=\"bg-green-50 border border-green-200 rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-green-300\"\n >\n <div class=\"text-xs text-green-600 font-medium\">Succeeded</div>\n <div class=\"text-lg font-bold text-green-700\">{{ group.statusCounts.succeeded || 0 }}</div>\n </button>\n <button \n @click.stop=\"openTaskGroupDetailsWithFilter(group, 'failed')\"\n class=\"bg-red-50 border border-red-200 rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-red-300\"\n >\n <div class=\"text-xs text-red-600 font-medium\">Failed</div>\n <div class=\"text-lg font-bold text-red-700\">{{ group.statusCounts.failed || 0 }}</div>\n </button>\n <button \n @click.stop=\"openTaskGroupDetailsWithFilter(group, 'cancelled')\"\n class=\"bg-gray-50 border border-gray-200 rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-gray-300\"\n >\n <div class=\"text-xs text-gray-600 font-medium\">Cancelled</div>\n <div class=\"text-lg font-bold text-gray-700\">{{ group.statusCounts.cancelled || 0 }}</div>\n </button>\n </div>\n </li>\n </ul>\n </div>\n </div>\n </div>\n\n <!-- Create Task Modal -->\n <modal v-if=\"showCreateTaskModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"closeCreateTaskModal\" role=\"button\" aria-label=\"Close modal\">×</div>\n <div class=\"space-y-4\">\n <h3 class=\"text-lg font-semibold text-gray-700 mb-4\">Create New Task</h3>\n \n <div>\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Task Name:</label>\n <input \n v-model=\"newTask.name\" \n type=\"text\" \n class=\"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ultramarine-500\"\n placeholder=\"Enter task name\"\n >\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Scheduled Time:</label>\n <input \n v-model=\"newTask.scheduledAt\" \n type=\"datetime-local\" \n class=\"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ultramarine-500\"\n >\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Parameters (JSON):</label>\n <textarea \n ref=\"parametersEditor\"\n class=\"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ultramarine-500\"\n placeholder='{\"key\": \"value\"}'\n ></textarea>\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Repeat Interval (ms):</label>\n <input \n v-model=\"newTask.repeatInterval\" \n type=\"number\" \n min=\"0\"\n step=\"1000\"\n class=\"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ultramarine-500\"\n placeholder=\"0 for no repetition\"\n >\n <p class=\"text-xs text-gray-500 mt-1\">Enter 0 or leave empty for no repetition. Use 1000 for 1 second, 60000 for 1 minute, etc.</p>\n </div>\n \n <div class=\"flex gap-2 pt-4\">\n <button \n @click=\"createTask\" \n class=\"flex-1 bg-ultramarine-600 text-white px-4 py-2 rounded-md hover:bg-ultramarine-700\"\n >\n Create Task\n </button>\n <button \n @click=\"closeCreateTaskModal\" \n class=\"flex-1 bg-gray-300 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-400\"\n >\n Cancel\n </button>\n </div>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
26106
|
+
module.exports = "<div class=\"p-4 space-y-6\">\n <div>\n <h1 class=\"text-2xl font-bold text-gray-700 mb-4\">Task Overview</h1>\n <div v-if=\"status == 'init'\">\n <img src=\"images/loader.gif\" />\n </div>\n <!-- Task List -->\n <div class=\"bg-white p-4 rounded-lg shadow\" v-if=\"status == 'loaded'\">\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\">\n <option v-for=\"option in dateFilters\" :key=\"option.value\" :value=\"option.value\">\n {{ option.label }}\n </option>\n </select>\n </div>\n <div class=\"mb-4\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Filter by Status:</label>\n <select v-model=\"selectedStatus\" @change=\"getTasks\" class=\"border-gray-300 rounded-md shadow-sm w-full p-2\">\n <option v-for=\"option in statusFilters\" :key=\"option.value\" :value=\"option.value\">\n {{ option.label }}\n </option>\n </select>\n </div>\n <div class=\"mb-4\">\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Search by Task Name:</label>\n <input \n v-model=\"searchQuery\" \n type=\"text\" \n @input=\"onSearchInput\"\n class=\"border-gray-300 rounded-md shadow-sm w-full p-2\"\n placeholder=\"Enter task name to search...\"\n >\n </div>\n <div class=\"mb-4\">\n <button\n @click=\"resetFilters\"\n class=\"w-full bg-gray-200 text-gray-700 hover:bg-gray-300 font-medium py-2 px-4 rounded-md transition\"\n >\n Reset Filters\n </button>\n </div>\n <div class=\"mb-6\">\n <button\n @click=\"openCreateTaskModal\"\n class=\"w-full bg-ultramarine-600 text-white hover:bg-ultramarine-700 font-medium py-2 px-4 rounded-md transition\"\n >\n Create New Task\n </button>\n </div>\n <!-- Summary Section -->\n <div class=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <button \n @click=\"setStatusFilter('pending')\"\n :class=\"getStatusColor('pending') + ' p-4 rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 cursor-pointer border-2 border-yellow-200 hover:border-yellow-300'\"\n >\n <div class=\"text-sm\">Scheduled</div>\n <div class=\"text-2xl font-bold\">{{pendingCount}}</div>\n </button>\n <button \n @click=\"setStatusFilter('succeeded')\"\n :class=\"getStatusColor('succeeded') + ' p-4 rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 cursor-pointer border-2 border-green-200 hover:border-green-300'\"\n >\n <div class=\"text-sm\">Completed</div>\n <div class=\"text-2xl font-bold\">{{succeededCount}}</div>\n </button>\n <button \n @click=\"setStatusFilter('failed')\"\n :class=\"getStatusColor('failed') + ' p-4 rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 cursor-pointer border-2 border-red-200 hover:border-red-300'\"\n >\n <div class=\"text-sm\">Failed</div>\n <div class=\"text-2xl font-bold\">{{failedCount}}</div>\n </button>\n <button \n @click=\"setStatusFilter('cancelled')\"\n :class=\"getStatusColor('cancelled') + ' p-4 rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 cursor-pointer border-2 border-gray-200 hover:border-gray-300'\"\n >\n <div class=\"text-sm\">Cancelled</div>\n <div class=\"text-2xl font-bold\">{{cancelledCount}}</div>\n </button>\n </div>\n \n <!-- Grouped Task List -->\n <div class=\"mt-6\">\n <h2 class=\"text-lg font-semibold text-gray-700 mb-4\">Tasks by Name</h2>\n <ul class=\"divide-y divide-gray-200\">\n <li v-for=\"group in tasksByName\" :key=\"group.name\" class=\"p-4 group hover:border hover:rounded-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200\">\n <div class=\"flex items-center justify-between mb-3 \">\n <div class=\"flex-1 cursor-pointer\" @click=\"openTaskGroupDetails(group)\">\n <div class=\"flex items-center gap-2\">\n <div class=\"font-medium text-lg group-hover:text-ultramarine-600 transition-colors\">{{ group.name }}</div>\n <svg class=\"w-4 h-4 text-gray-400 group-hover:text-ultramarine-600 transition-colors\" 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 </div>\n <div class=\"text-sm text-gray-500 group-hover:text-gray-700 transition-colors\">Total: {{ group.totalCount }} tasks</div>\n <div class=\"text-xs text-ultramarine-600 opacity-0 group-hover:opacity-100 transition-opacity mt-1\">\n Click to view details\n </div>\n </div>\n <div class=\"text-sm text-gray-500\">\n Last run: {{ group.lastRun ? new Date(group.lastRun).toLocaleString() : 'Never' }}\n </div>\n </div>\n \n <!-- Status Counts -->\n <div class=\"grid grid-cols-2 sm:grid-cols-4 gap-2\">\n <button \n @click.stop=\"openTaskGroupDetailsWithFilter(group, 'pending')\"\n class=\"bg-yellow-50 border border-yellow-200 rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-yellow-300\"\n >\n <div class=\"text-xs text-yellow-600 font-medium\">Pending</div>\n <div class=\"text-lg font-bold text-yellow-700\">{{ group.statusCounts.pending || 0 }}</div>\n </button>\n <button \n @click.stop=\"openTaskGroupDetailsWithFilter(group, 'succeeded')\"\n class=\"bg-green-50 border border-green-200 rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-green-300\"\n >\n <div class=\"text-xs text-green-600 font-medium\">Succeeded</div>\n <div class=\"text-lg font-bold text-green-700\">{{ group.statusCounts.succeeded || 0 }}</div>\n </button>\n <button \n @click.stop=\"openTaskGroupDetailsWithFilter(group, 'failed')\"\n class=\"bg-red-50 border border-red-200 rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-red-300\"\n >\n <div class=\"text-xs text-red-600 font-medium\">Failed</div>\n <div class=\"text-lg font-bold text-red-700\">{{ group.statusCounts.failed || 0 }}</div>\n </button>\n <button \n @click.stop=\"openTaskGroupDetailsWithFilter(group, 'cancelled')\"\n class=\"bg-gray-50 border border-gray-200 rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-gray-300\"\n >\n <div class=\"text-xs text-gray-600 font-medium\">Cancelled</div>\n <div class=\"text-lg font-bold text-gray-700\">{{ group.statusCounts.cancelled || 0 }}</div>\n </button>\n </div>\n </li>\n </ul>\n </div>\n </div>\n </div>\n\n <!-- Create Task Modal -->\n <modal v-if=\"showCreateTaskModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"closeCreateTaskModal\" role=\"button\" aria-label=\"Close modal\">×</div>\n <div class=\"space-y-4\">\n <h3 class=\"text-lg font-semibold text-gray-700 mb-4\">Create New Task</h3>\n \n <div>\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Task Name:</label>\n <input \n v-model=\"newTask.name\" \n type=\"text\" \n class=\"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ultramarine-500\"\n placeholder=\"Enter task name\"\n >\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Scheduled Time:</label>\n <input \n v-model=\"newTask.scheduledAt\" \n type=\"datetime-local\" \n class=\"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ultramarine-500\"\n >\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Parameters (JSON):</label>\n <textarea \n ref=\"parametersEditor\"\n class=\"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ultramarine-500\"\n placeholder='{\"key\": \"value\"}'\n ></textarea>\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-gray-700 mb-1\">Repeat Interval (ms):</label>\n <input \n v-model=\"newTask.repeatInterval\" \n type=\"number\" \n min=\"0\"\n step=\"1000\"\n class=\"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ultramarine-500\"\n placeholder=\"0 for no repetition\"\n >\n <p class=\"text-xs text-gray-500 mt-1\">Enter 0 or leave empty for no repetition. Use 1000 for 1 second, 60000 for 1 minute, etc.</p>\n </div>\n \n <div class=\"flex gap-2 pt-4\">\n <button \n @click=\"createTask\" \n class=\"flex-1 bg-ultramarine-600 text-white px-4 py-2 rounded-md hover:bg-ultramarine-700\"\n >\n Create Task\n </button>\n <button \n @click=\"closeCreateTaskModal\" \n class=\"flex-1 bg-gray-300 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-400\"\n >\n Cancel\n </button>\n </div>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
25633
26107
|
|
|
25634
26108
|
/***/ },
|
|
25635
26109
|
|
|
@@ -36943,7 +37417,7 @@ var src_default = VueToastificationPlugin;
|
|
|
36943
37417
|
(module) {
|
|
36944
37418
|
|
|
36945
37419
|
"use strict";
|
|
36946
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.2.
|
|
37420
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.2.13","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","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","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"}}');
|
|
36947
37421
|
|
|
36948
37422
|
/***/ }
|
|
36949
37423
|
|
|
@@ -36959,12 +37433,6 @@ module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version"
|
|
|
36959
37433
|
/******/ if (cachedModule !== undefined) {
|
|
36960
37434
|
/******/ return cachedModule.exports;
|
|
36961
37435
|
/******/ }
|
|
36962
|
-
/******/ // Check if module exists (development only)
|
|
36963
|
-
/******/ if (__webpack_modules__[moduleId] === undefined) {
|
|
36964
|
-
/******/ var e = new Error("Cannot find module '" + moduleId + "'");
|
|
36965
|
-
/******/ e.code = 'MODULE_NOT_FOUND';
|
|
36966
|
-
/******/ throw e;
|
|
36967
|
-
/******/ }
|
|
36968
37436
|
/******/ // Create a new module (and put it into the cache)
|
|
36969
37437
|
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
36970
37438
|
/******/ // no module.id needed
|
|
@@ -36973,6 +37441,12 @@ module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version"
|
|
|
36973
37441
|
/******/ };
|
|
36974
37442
|
/******/
|
|
36975
37443
|
/******/ // Execute the module function
|
|
37444
|
+
/******/ if (!(moduleId in __webpack_modules__)) {
|
|
37445
|
+
/******/ delete __webpack_module_cache__[moduleId];
|
|
37446
|
+
/******/ var e = new Error("Cannot find module '" + moduleId + "'");
|
|
37447
|
+
/******/ e.code = 'MODULE_NOT_FOUND';
|
|
37448
|
+
/******/ throw e;
|
|
37449
|
+
/******/ }
|
|
36976
37450
|
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
36977
37451
|
/******/
|
|
36978
37452
|
/******/ // Return the exports of the module
|