@mongoosejs/studio 0.3.6 → 0.3.7
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/Task/getTasksOverTime.js +66 -0
- package/backend/actions/Task/index.js +1 -0
- package/frontend/public/app.js +541 -88
- package/frontend/public/tw.css +14 -12
- package/frontend/src/api.js +6 -0
- package/frontend/src/dashboard-result/dashboard-primitive/dashboard-primitive.html +2 -2
- package/frontend/src/dashboard-result/dashboard-primitive/dashboard-primitive.js +13 -1
- package/frontend/src/dashboard-result/dashboard-result.html +1 -1
- package/frontend/src/dashboard-result/dashboard-table/dashboard-table.html +21 -1
- package/frontend/src/dashboard-result/dashboard-table/dashboard-table.js +52 -0
- package/frontend/src/detail-date/detail-date.html +1 -0
- package/frontend/src/detail-date/detail-date.js +123 -0
- package/frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.html +26 -0
- package/frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.js +41 -0
- package/frontend/src/document-details/document-property/document-property.html +13 -5
- package/frontend/src/document-details/document-property/document-property.js +14 -1
- package/frontend/src/tasks/tasks.html +34 -20
- package/frontend/src/tasks/tasks.js +158 -5
- package/local.js +2 -1
- package/package.json +1 -1
package/frontend/public/app.js
CHANGED
|
@@ -814,6 +814,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
814
814
|
getTaskOverview: function getTaskOverview(params) {
|
|
815
815
|
return client.post('', { action: 'Task.getTaskOverview', ...params }).then(res => res.data);
|
|
816
816
|
},
|
|
817
|
+
getTasksOverTime: function getTasksOverTime(params) {
|
|
818
|
+
return client.post('', { action: 'Task.getTasksOverTime', ...params }).then(res => res.data);
|
|
819
|
+
},
|
|
817
820
|
rescheduleTask: function rescheduleTask(params) {
|
|
818
821
|
return client.post('', { action: 'Task.rescheduleTask', ...params }).then(res => res.data);
|
|
819
822
|
},
|
|
@@ -1154,6 +1157,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
1154
1157
|
getTaskOverview: function getTaskOverview(params) {
|
|
1155
1158
|
return client.post('/Task/getTaskOverview', params).then(res => res.data);
|
|
1156
1159
|
},
|
|
1160
|
+
getTasksOverTime: function getTasksOverTime(params) {
|
|
1161
|
+
return client.post('/Task/getTasksOverTime', params).then(res => res.data);
|
|
1162
|
+
},
|
|
1157
1163
|
rescheduleTask: function rescheduleTask(params) {
|
|
1158
1164
|
return client.post('/Task/rescheduleTask', params).then(res => res.data);
|
|
1159
1165
|
},
|
|
@@ -2442,16 +2448,28 @@ module.exports = app => app.component('dashboard-primitive', {
|
|
|
2442
2448
|
props: ['value'],
|
|
2443
2449
|
computed: {
|
|
2444
2450
|
header() {
|
|
2445
|
-
if (this.value != null && this.value.$primitive
|
|
2451
|
+
if (this.value != null && this.value.$primitive?.header) {
|
|
2446
2452
|
return this.value.$primitive.header;
|
|
2447
2453
|
}
|
|
2448
2454
|
return null;
|
|
2449
2455
|
},
|
|
2450
2456
|
displayValue() {
|
|
2451
2457
|
if (this.value != null && this.value.$primitive) {
|
|
2458
|
+
if (this.value.$primitive.value === null) {
|
|
2459
|
+
return 'null';
|
|
2460
|
+
}
|
|
2452
2461
|
return this.value.$primitive.value;
|
|
2453
2462
|
}
|
|
2463
|
+
if (this.value === null) {
|
|
2464
|
+
return 'null';
|
|
2465
|
+
}
|
|
2454
2466
|
return this.value;
|
|
2467
|
+
},
|
|
2468
|
+
displayClass() {
|
|
2469
|
+
if (this.value == null) {
|
|
2470
|
+
return 'text-content-tertiary';
|
|
2471
|
+
}
|
|
2472
|
+
return null;
|
|
2455
2473
|
}
|
|
2456
2474
|
}
|
|
2457
2475
|
});
|
|
@@ -2520,6 +2538,7 @@ module.exports = app => app.component('dashboard-result', {
|
|
|
2520
2538
|
(module, __unused_webpack_exports, __webpack_require__) {
|
|
2521
2539
|
|
|
2522
2540
|
"use strict";
|
|
2541
|
+
/* global Blob, URL, document */
|
|
2523
2542
|
|
|
2524
2543
|
|
|
2525
2544
|
const template = __webpack_require__(/*! ./dashboard-table.html */ "./frontend/src/dashboard-result/dashboard-table/dashboard-table.html");
|
|
@@ -2527,6 +2546,11 @@ const template = __webpack_require__(/*! ./dashboard-table.html */ "./frontend/s
|
|
|
2527
2546
|
module.exports = app => app.component('dashboard-table', {
|
|
2528
2547
|
template,
|
|
2529
2548
|
props: ['value'],
|
|
2549
|
+
data() {
|
|
2550
|
+
return {
|
|
2551
|
+
showDropdown: false
|
|
2552
|
+
};
|
|
2553
|
+
},
|
|
2530
2554
|
computed: {
|
|
2531
2555
|
columns() {
|
|
2532
2556
|
return Array.isArray(this.value?.$table?.columns) ? this.value.$table.columns : [];
|
|
@@ -2542,6 +2566,46 @@ module.exports = app => app.component('dashboard-table', {
|
|
|
2542
2566
|
}
|
|
2543
2567
|
},
|
|
2544
2568
|
methods: {
|
|
2569
|
+
toggleDropdown() {
|
|
2570
|
+
this.showDropdown = !this.showDropdown;
|
|
2571
|
+
},
|
|
2572
|
+
handleBodyClick(event) {
|
|
2573
|
+
const dropdownRefs = this.$refs.dropdown;
|
|
2574
|
+
const dropdowns = Array.isArray(dropdownRefs) ? dropdownRefs : [dropdownRefs];
|
|
2575
|
+
const hasClickInsideDropdown = dropdowns
|
|
2576
|
+
.filter(dropdown => dropdown && typeof dropdown.contains === 'function')
|
|
2577
|
+
.some(dropdown => dropdown.contains(event.target));
|
|
2578
|
+
|
|
2579
|
+
if (!hasClickInsideDropdown) {
|
|
2580
|
+
this.showDropdown = false;
|
|
2581
|
+
}
|
|
2582
|
+
},
|
|
2583
|
+
neutralizeCsvCell(cell) {
|
|
2584
|
+
const value = this.displayValue(cell);
|
|
2585
|
+
return /^\s*[=+\-@]/.test(value) ? `'${value}` : value;
|
|
2586
|
+
},
|
|
2587
|
+
escapeCsvCell(cell) {
|
|
2588
|
+
const escapedCell = this.neutralizeCsvCell(cell).replaceAll('"', '""');
|
|
2589
|
+
return `"${escapedCell}"`;
|
|
2590
|
+
},
|
|
2591
|
+
downloadCsv() {
|
|
2592
|
+
const header = this.columns.map(this.escapeCsvCell).join(',');
|
|
2593
|
+
const rows = this.rows
|
|
2594
|
+
.map(row => row.map(this.escapeCsvCell).join(','))
|
|
2595
|
+
.join('\n');
|
|
2596
|
+
|
|
2597
|
+
const csv = [header, rows].filter(v => v.length > 0).join('\n');
|
|
2598
|
+
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
|
2599
|
+
const url = URL.createObjectURL(blob);
|
|
2600
|
+
const anchor = document.createElement('a');
|
|
2601
|
+
anchor.href = url;
|
|
2602
|
+
anchor.download = 'table.csv';
|
|
2603
|
+
document.body.appendChild(anchor);
|
|
2604
|
+
anchor.click();
|
|
2605
|
+
document.body.removeChild(anchor);
|
|
2606
|
+
URL.revokeObjectURL(url);
|
|
2607
|
+
this.$toast.success('CSV downloaded!');
|
|
2608
|
+
},
|
|
2545
2609
|
displayValue(cell) {
|
|
2546
2610
|
if (cell == null) {
|
|
2547
2611
|
return '';
|
|
@@ -2555,6 +2619,12 @@ module.exports = app => app.component('dashboard-table', {
|
|
|
2555
2619
|
}
|
|
2556
2620
|
return String(cell);
|
|
2557
2621
|
}
|
|
2622
|
+
},
|
|
2623
|
+
mounted() {
|
|
2624
|
+
document.body.addEventListener('click', this.handleBodyClick);
|
|
2625
|
+
},
|
|
2626
|
+
unmounted() {
|
|
2627
|
+
document.body.removeEventListener('click', this.handleBodyClick);
|
|
2558
2628
|
}
|
|
2559
2629
|
});
|
|
2560
2630
|
|
|
@@ -2940,6 +3010,140 @@ module.exports = app => app.component('detail-array', {
|
|
|
2940
3010
|
}
|
|
2941
3011
|
});
|
|
2942
3012
|
|
|
3013
|
+
/***/ },
|
|
3014
|
+
|
|
3015
|
+
/***/ "./frontend/src/detail-date/detail-date.js"
|
|
3016
|
+
/*!*************************************************!*\
|
|
3017
|
+
!*** ./frontend/src/detail-date/detail-date.js ***!
|
|
3018
|
+
\*************************************************/
|
|
3019
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
3020
|
+
|
|
3021
|
+
"use strict";
|
|
3022
|
+
|
|
3023
|
+
|
|
3024
|
+
const template = __webpack_require__(/*! ./detail-date.html */ "./frontend/src/detail-date/detail-date.html");
|
|
3025
|
+
|
|
3026
|
+
module.exports = app => app.component('detail-date', {
|
|
3027
|
+
template,
|
|
3028
|
+
props: ['value', 'viewMode'],
|
|
3029
|
+
emits: ['updated'],
|
|
3030
|
+
watch: {
|
|
3031
|
+
displayValue: {
|
|
3032
|
+
immediate: true,
|
|
3033
|
+
handler(val) {
|
|
3034
|
+
this.$emit('updated', val);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
},
|
|
3038
|
+
computed: {
|
|
3039
|
+
format() {
|
|
3040
|
+
if (this.viewMode != null && typeof this.viewMode === 'object') {
|
|
3041
|
+
return this.viewMode.format;
|
|
3042
|
+
}
|
|
3043
|
+
return this.viewMode;
|
|
3044
|
+
},
|
|
3045
|
+
timezone() {
|
|
3046
|
+
if (this.viewMode != null && typeof this.viewMode === 'object') {
|
|
3047
|
+
return this.viewMode.timezone || '';
|
|
3048
|
+
}
|
|
3049
|
+
return '';
|
|
3050
|
+
},
|
|
3051
|
+
parsedDate() {
|
|
3052
|
+
if (this.value == null) {
|
|
3053
|
+
return null;
|
|
3054
|
+
}
|
|
3055
|
+
const date = new Date(this.value);
|
|
3056
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
3057
|
+
},
|
|
3058
|
+
displayValue() {
|
|
3059
|
+
if (this.value == null) {
|
|
3060
|
+
return String(this.value);
|
|
3061
|
+
}
|
|
3062
|
+
if (!this.parsedDate) {
|
|
3063
|
+
return 'Invalid Date';
|
|
3064
|
+
}
|
|
3065
|
+
return this.formatDateForDisplay(this.parsedDate);
|
|
3066
|
+
}
|
|
3067
|
+
},
|
|
3068
|
+
methods: {
|
|
3069
|
+
formatDateForDisplay(date) {
|
|
3070
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
3071
|
+
return 'Invalid Date';
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
if (this.format === 'utc_iso') {
|
|
3075
|
+
return date.toISOString();
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
if (this.format === 'local_browser') {
|
|
3079
|
+
return date.toLocaleString();
|
|
3080
|
+
}
|
|
3081
|
+
|
|
3082
|
+
if (this.format === 'unix_ms') {
|
|
3083
|
+
return String(date.getTime());
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3086
|
+
if (this.format === 'unix_seconds') {
|
|
3087
|
+
return String(Math.floor(date.getTime() / 1000));
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
if (this.format === 'duration_relative') {
|
|
3091
|
+
return this.formatRelativeDuration(date);
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3094
|
+
if (this.format === 'custom_tz') {
|
|
3095
|
+
return this.formatCustomTimezone(date);
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3098
|
+
return date.toISOString();
|
|
3099
|
+
},
|
|
3100
|
+
formatRelativeDuration(date) {
|
|
3101
|
+
const diffMs = date.getTime() - Date.now();
|
|
3102
|
+
const absMs = Math.abs(diffMs);
|
|
3103
|
+
const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' });
|
|
3104
|
+
const units = [
|
|
3105
|
+
{ unit: 'year', ms: 365 * 24 * 60 * 60 * 1000 },
|
|
3106
|
+
{ unit: 'month', ms: 30 * 24 * 60 * 60 * 1000 },
|
|
3107
|
+
{ unit: 'day', ms: 24 * 60 * 60 * 1000 },
|
|
3108
|
+
{ unit: 'hour', ms: 60 * 60 * 1000 },
|
|
3109
|
+
{ unit: 'minute', ms: 60 * 1000 },
|
|
3110
|
+
{ unit: 'second', ms: 1000 }
|
|
3111
|
+
];
|
|
3112
|
+
|
|
3113
|
+
for (const { unit, ms } of units) {
|
|
3114
|
+
if (absMs >= ms || unit === 'second') {
|
|
3115
|
+
const value = Math.round(diffMs / ms);
|
|
3116
|
+
return rtf.format(value, unit);
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
return 'now';
|
|
3121
|
+
},
|
|
3122
|
+
formatCustomTimezone(date) {
|
|
3123
|
+
const tz = (this.timezone || '').trim();
|
|
3124
|
+
if (!tz) {
|
|
3125
|
+
return `${date.toISOString()} (enter an IANA timezone)`;
|
|
3126
|
+
}
|
|
3127
|
+
|
|
3128
|
+
try {
|
|
3129
|
+
return new Intl.DateTimeFormat(undefined, {
|
|
3130
|
+
timeZone: tz,
|
|
3131
|
+
year: 'numeric',
|
|
3132
|
+
month: '2-digit',
|
|
3133
|
+
day: '2-digit',
|
|
3134
|
+
hour: '2-digit',
|
|
3135
|
+
minute: '2-digit',
|
|
3136
|
+
second: '2-digit',
|
|
3137
|
+
timeZoneName: 'short'
|
|
3138
|
+
}).format(date);
|
|
3139
|
+
} catch (err) {
|
|
3140
|
+
return `Invalid timezone: ${tz}`;
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
});
|
|
3145
|
+
|
|
3146
|
+
|
|
2943
3147
|
/***/ },
|
|
2944
3148
|
|
|
2945
3149
|
/***/ "./frontend/src/detail-default/detail-default.js"
|
|
@@ -4064,6 +4268,58 @@ module.exports = app => app.component('detail-default', {
|
|
|
4064
4268
|
});
|
|
4065
4269
|
|
|
4066
4270
|
|
|
4271
|
+
/***/ },
|
|
4272
|
+
|
|
4273
|
+
/***/ "./frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.js"
|
|
4274
|
+
/*!**************************************************************************************!*\
|
|
4275
|
+
!*** ./frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.js ***!
|
|
4276
|
+
\**************************************************************************************/
|
|
4277
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
4278
|
+
|
|
4279
|
+
"use strict";
|
|
4280
|
+
|
|
4281
|
+
|
|
4282
|
+
const template = __webpack_require__(/*! ./date-view-mode-picker.html */ "./frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.html");
|
|
4283
|
+
|
|
4284
|
+
module.exports = app => app.component('date-view-mode-picker', {
|
|
4285
|
+
template,
|
|
4286
|
+
props: ['viewMode', 'path'],
|
|
4287
|
+
emits: ['update:viewMode'],
|
|
4288
|
+
computed: {
|
|
4289
|
+
format() {
|
|
4290
|
+
if (this.viewMode != null && typeof this.viewMode === 'object') {
|
|
4291
|
+
return this.viewMode.format;
|
|
4292
|
+
}
|
|
4293
|
+
return this.viewMode;
|
|
4294
|
+
},
|
|
4295
|
+
timezone() {
|
|
4296
|
+
if (this.viewMode != null && typeof this.viewMode === 'object') {
|
|
4297
|
+
return this.viewMode.timezone || '';
|
|
4298
|
+
}
|
|
4299
|
+
return '';
|
|
4300
|
+
},
|
|
4301
|
+
timezoneDatalistId() {
|
|
4302
|
+
return `timezone-options-${String(this.path?.path || '').replace(/[^a-zA-Z0-9_-]/g, '-')}`;
|
|
4303
|
+
},
|
|
4304
|
+
timezones() {
|
|
4305
|
+
return Intl.supportedValuesOf('timeZone');
|
|
4306
|
+
}
|
|
4307
|
+
},
|
|
4308
|
+
methods: {
|
|
4309
|
+
onFormatChange(newFormat) {
|
|
4310
|
+
if (newFormat === 'custom_tz') {
|
|
4311
|
+
this.$emit('update:viewMode', { format: 'custom_tz', timezone: this.timezone });
|
|
4312
|
+
} else {
|
|
4313
|
+
this.$emit('update:viewMode', newFormat);
|
|
4314
|
+
}
|
|
4315
|
+
},
|
|
4316
|
+
onTimezoneChange(newTimezone) {
|
|
4317
|
+
this.$emit('update:viewMode', { format: 'custom_tz', timezone: newTimezone });
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
});
|
|
4321
|
+
|
|
4322
|
+
|
|
4067
4323
|
/***/ },
|
|
4068
4324
|
|
|
4069
4325
|
/***/ "./frontend/src/document-details/document-details.js"
|
|
@@ -4528,11 +4784,15 @@ const appendCSS = __webpack_require__(/*! ../../appendCSS */ "./frontend/src/app
|
|
|
4528
4784
|
|
|
4529
4785
|
appendCSS(__webpack_require__(/*! ./document-property.css */ "./frontend/src/document-details/document-property/document-property.css"));
|
|
4530
4786
|
|
|
4787
|
+
const UNSET = Symbol('unset');
|
|
4788
|
+
|
|
4531
4789
|
module.exports = app => app.component('document-property', {
|
|
4532
4790
|
template,
|
|
4533
4791
|
data: function() {
|
|
4534
4792
|
return {
|
|
4535
4793
|
dateType: 'picker', // picker, iso
|
|
4794
|
+
dateViewMode: 'utc_iso',
|
|
4795
|
+
renderedValue: UNSET,
|
|
4536
4796
|
isCollapsed: false, // Start uncollapsed by default
|
|
4537
4797
|
isValueExpanded: false, // Track if the value is expanded
|
|
4538
4798
|
detailViewMode: 'text',
|
|
@@ -4549,8 +4809,14 @@ module.exports = app => app.component('document-property', {
|
|
|
4549
4809
|
},
|
|
4550
4810
|
props: ['path', 'document', 'schemaPaths', 'editting', 'changes', 'invalid', 'highlight'],
|
|
4551
4811
|
computed: {
|
|
4812
|
+
isDatePath() {
|
|
4813
|
+
return this.path?.instance === 'Date';
|
|
4814
|
+
},
|
|
4815
|
+
rawValue() {
|
|
4816
|
+
return this.getValueForPath(this.path.path);
|
|
4817
|
+
},
|
|
4552
4818
|
valueAsString() {
|
|
4553
|
-
const value = this.
|
|
4819
|
+
const value = this.renderedValue !== UNSET ? this.renderedValue : this.rawValue;
|
|
4554
4820
|
if (value == null) {
|
|
4555
4821
|
return String(value);
|
|
4556
4822
|
}
|
|
@@ -4682,6 +4948,9 @@ module.exports = app => app.component('document-property', {
|
|
|
4682
4948
|
if (schemaPath.instance === 'Array') {
|
|
4683
4949
|
return 'detail-array';
|
|
4684
4950
|
}
|
|
4951
|
+
if (schemaPath.instance === 'Date') {
|
|
4952
|
+
return 'detail-date';
|
|
4953
|
+
}
|
|
4685
4954
|
return 'detail-default';
|
|
4686
4955
|
},
|
|
4687
4956
|
getEditComponentForPath(path) {
|
|
@@ -9751,6 +10020,39 @@ const template = __webpack_require__(/*! ./tasks.html */ "./frontend/src/tasks/t
|
|
|
9751
10020
|
const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
|
|
9752
10021
|
const { DATE_FILTERS, getDateRangeForRange } = __webpack_require__(/*! ../_util/dateRange */ "./frontend/src/_util/dateRange.js");
|
|
9753
10022
|
|
|
10023
|
+
/** Returns the bucket size in ms for the given date range. */
|
|
10024
|
+
function getBucketSizeMs(range) {
|
|
10025
|
+
switch (range) {
|
|
10026
|
+
case 'last_hour': return 5 * 60 * 1000; // 5 minutes
|
|
10027
|
+
case 'today':
|
|
10028
|
+
case 'yesterday': return 60 * 60 * 1000; // 1 hour
|
|
10029
|
+
case 'thisWeek':
|
|
10030
|
+
case 'lastWeek': return 24 * 60 * 60 * 1000; // 1 day
|
|
10031
|
+
case 'thisMonth':
|
|
10032
|
+
case 'lastMonth': return 24 * 60 * 60 * 1000; // 1 day
|
|
10033
|
+
default: return 5 * 60 * 1000;
|
|
10034
|
+
}
|
|
10035
|
+
}
|
|
10036
|
+
|
|
10037
|
+
/** Formats a bucket timestamp for the x-axis label based on the date range. */
|
|
10038
|
+
function formatBucketLabel(timestamp, range) {
|
|
10039
|
+
const date = new Date(timestamp);
|
|
10040
|
+
switch (range) {
|
|
10041
|
+
case 'last_hour':
|
|
10042
|
+
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
10043
|
+
case 'today':
|
|
10044
|
+
case 'yesterday':
|
|
10045
|
+
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
10046
|
+
case 'thisWeek':
|
|
10047
|
+
case 'lastWeek':
|
|
10048
|
+
case 'thisMonth':
|
|
10049
|
+
case 'lastMonth':
|
|
10050
|
+
return date.toLocaleDateString([], { month: 'short', day: 'numeric' });
|
|
10051
|
+
default:
|
|
10052
|
+
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
10053
|
+
}
|
|
10054
|
+
}
|
|
10055
|
+
|
|
9754
10056
|
module.exports = app => app.component('tasks', {
|
|
9755
10057
|
data: () => ({
|
|
9756
10058
|
status: 'init',
|
|
@@ -9778,10 +10080,22 @@ module.exports = app => app.component('tasks', {
|
|
|
9778
10080
|
scheduledAt: '',
|
|
9779
10081
|
parameters: '',
|
|
9780
10082
|
repeatInterval: ''
|
|
9781
|
-
}
|
|
10083
|
+
},
|
|
10084
|
+
// Chart over time
|
|
10085
|
+
overTimeChart: null,
|
|
10086
|
+
overTimeBuckets: [],
|
|
10087
|
+
// Toggled with v-if on the canvas so Chart.js is torn down and remounted on
|
|
10088
|
+
// filter changes. Updating Chart.js in place during a big Vue re-render was
|
|
10089
|
+
// freezing the page (dropdowns unresponsive, chart stale).
|
|
10090
|
+
showOverTimeChart: true
|
|
9782
10091
|
}),
|
|
9783
10092
|
methods: {
|
|
9784
10093
|
async getTasks() {
|
|
10094
|
+
// Hide chart canvas + teardown Chart.js immediately on filter changes
|
|
10095
|
+
// (see showOverTimeChart + v-if on the canvas in tasks.html).
|
|
10096
|
+
this.showOverTimeChart = false;
|
|
10097
|
+
this.destroyOverTimeChart();
|
|
10098
|
+
|
|
9785
10099
|
const params = {};
|
|
9786
10100
|
if (this.selectedStatus == 'all') {
|
|
9787
10101
|
params.status = null;
|
|
@@ -9800,9 +10114,105 @@ module.exports = app => app.component('tasks', {
|
|
|
9800
10114
|
params.name = this.searchQuery.trim();
|
|
9801
10115
|
}
|
|
9802
10116
|
|
|
9803
|
-
const
|
|
9804
|
-
|
|
9805
|
-
|
|
10117
|
+
const [overviewResult, overTimeResult] = await Promise.all([
|
|
10118
|
+
api.Task.getTaskOverview(params),
|
|
10119
|
+
api.Task.getTasksOverTime({
|
|
10120
|
+
start: params.start,
|
|
10121
|
+
end: params.end,
|
|
10122
|
+
bucketSizeMs: getBucketSizeMs(this.selectedRange)
|
|
10123
|
+
})
|
|
10124
|
+
]);
|
|
10125
|
+
|
|
10126
|
+
this.statusCounts = overviewResult.statusCounts || this.statusCounts;
|
|
10127
|
+
this.tasksByName = overviewResult.tasksByName || [];
|
|
10128
|
+
this.overTimeBuckets = overTimeResult || [];
|
|
10129
|
+
if (this.overTimeBuckets.length === 0) {
|
|
10130
|
+
this.showOverTimeChart = false;
|
|
10131
|
+
this.destroyOverTimeChart();
|
|
10132
|
+
} else {
|
|
10133
|
+
this.showOverTimeChart = true;
|
|
10134
|
+
await this.$nextTick();
|
|
10135
|
+
this.renderOverTimeChart();
|
|
10136
|
+
}
|
|
10137
|
+
},
|
|
10138
|
+
|
|
10139
|
+
/** Build or update the stacked bar chart showing tasks over time. */
|
|
10140
|
+
renderOverTimeChart() {
|
|
10141
|
+
const Chart = typeof window !== 'undefined' && window.Chart;
|
|
10142
|
+
if (!Chart) {
|
|
10143
|
+
throw new Error('Chart.js not found');
|
|
10144
|
+
}
|
|
10145
|
+
const canvas = this.$refs.overTimeChart;
|
|
10146
|
+
if (!canvas || typeof canvas.getContext !== 'function') return;
|
|
10147
|
+
|
|
10148
|
+
const buckets = this.overTimeBuckets;
|
|
10149
|
+
const labels = buckets.map(b => formatBucketLabel(b.timestamp, this.selectedRange));
|
|
10150
|
+
const succeeded = buckets.map(b => b.succeeded || 0);
|
|
10151
|
+
const failed = buckets.map(b => b.failed || 0);
|
|
10152
|
+
const cancelled = buckets.map(b => b.cancelled || 0);
|
|
10153
|
+
|
|
10154
|
+
const isDark = typeof document !== 'undefined' && document.documentElement.classList.contains('dark');
|
|
10155
|
+
const tickColor = isDark ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.6)';
|
|
10156
|
+
const gridColor = isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)';
|
|
10157
|
+
|
|
10158
|
+
const chartData = {
|
|
10159
|
+
labels,
|
|
10160
|
+
datasets: [
|
|
10161
|
+
{ label: 'Succeeded', data: succeeded, backgroundColor: '#22c55e', stack: 'tasks' },
|
|
10162
|
+
{ label: 'Failed', data: failed, backgroundColor: '#ef4444', stack: 'tasks' },
|
|
10163
|
+
{ label: 'Cancelled', data: cancelled, backgroundColor: '#6b7280', stack: 'tasks' }
|
|
10164
|
+
]
|
|
10165
|
+
};
|
|
10166
|
+
|
|
10167
|
+
if (this.overTimeChart) {
|
|
10168
|
+
try {
|
|
10169
|
+
this.overTimeChart.data.labels = labels;
|
|
10170
|
+
this.overTimeChart.data.datasets[0].data = succeeded;
|
|
10171
|
+
this.overTimeChart.data.datasets[1].data = failed;
|
|
10172
|
+
this.overTimeChart.data.datasets[2].data = cancelled;
|
|
10173
|
+
this.overTimeChart.update('none');
|
|
10174
|
+
} finally {
|
|
10175
|
+
this.destroyOverTimeChart();
|
|
10176
|
+
}
|
|
10177
|
+
}
|
|
10178
|
+
|
|
10179
|
+
this.overTimeChart = new Chart(canvas, {
|
|
10180
|
+
type: 'bar',
|
|
10181
|
+
data: chartData,
|
|
10182
|
+
options: {
|
|
10183
|
+
responsive: true,
|
|
10184
|
+
maintainAspectRatio: false,
|
|
10185
|
+
animation: false,
|
|
10186
|
+
scales: {
|
|
10187
|
+
x: {
|
|
10188
|
+
stacked: true,
|
|
10189
|
+
ticks: { color: tickColor, maxRotation: 45, minRotation: 0 },
|
|
10190
|
+
grid: { color: gridColor }
|
|
10191
|
+
},
|
|
10192
|
+
y: {
|
|
10193
|
+
stacked: true,
|
|
10194
|
+
beginAtZero: true,
|
|
10195
|
+
ticks: { color: tickColor, precision: 0 },
|
|
10196
|
+
grid: { color: gridColor }
|
|
10197
|
+
}
|
|
10198
|
+
},
|
|
10199
|
+
plugins: {
|
|
10200
|
+
legend: {
|
|
10201
|
+
display: true,
|
|
10202
|
+
position: 'top',
|
|
10203
|
+
labels: { color: tickColor }
|
|
10204
|
+
},
|
|
10205
|
+
tooltip: { mode: 'index', intersect: false }
|
|
10206
|
+
}
|
|
10207
|
+
}
|
|
10208
|
+
});
|
|
10209
|
+
},
|
|
10210
|
+
|
|
10211
|
+
destroyOverTimeChart() {
|
|
10212
|
+
if (this.overTimeChart) {
|
|
10213
|
+
this.overTimeChart.destroy();
|
|
10214
|
+
this.overTimeChart = null;
|
|
10215
|
+
}
|
|
9806
10216
|
},
|
|
9807
10217
|
openTaskGroupDetails(group) {
|
|
9808
10218
|
const query = { dateRange: this.selectedRange || 'last_hour' };
|
|
@@ -9978,11 +10388,23 @@ module.exports = app => app.component('tasks', {
|
|
|
9978
10388
|
}
|
|
9979
10389
|
},
|
|
9980
10390
|
mounted: async function() {
|
|
10391
|
+
// Load initial data while showing the loader state.
|
|
9981
10392
|
await this.updateDateRange();
|
|
9982
|
-
|
|
10393
|
+
|
|
10394
|
+
// Once data is loaded, switch to the main view.
|
|
9983
10395
|
this.status = 'loaded';
|
|
10396
|
+
await this.$nextTick();
|
|
10397
|
+
|
|
10398
|
+
// Ensure the chart renders now that the canvas exists in the DOM.
|
|
10399
|
+
if (this.showOverTimeChart && this.overTimeBuckets.length > 0) {
|
|
10400
|
+
this.renderOverTimeChart();
|
|
10401
|
+
}
|
|
10402
|
+
|
|
9984
10403
|
this.setDefaultCreateTaskValues();
|
|
9985
10404
|
},
|
|
10405
|
+
beforeUnmount() {
|
|
10406
|
+
this.destroyOverTimeChart();
|
|
10407
|
+
},
|
|
9986
10408
|
template: template
|
|
9987
10409
|
});
|
|
9988
10410
|
|
|
@@ -10285,9 +10707,15 @@ var map = {
|
|
|
10285
10707
|
"./detail-array/detail-array": "./frontend/src/detail-array/detail-array.js",
|
|
10286
10708
|
"./detail-array/detail-array.html": "./frontend/src/detail-array/detail-array.html",
|
|
10287
10709
|
"./detail-array/detail-array.js": "./frontend/src/detail-array/detail-array.js",
|
|
10710
|
+
"./detail-date/detail-date": "./frontend/src/detail-date/detail-date.js",
|
|
10711
|
+
"./detail-date/detail-date.html": "./frontend/src/detail-date/detail-date.html",
|
|
10712
|
+
"./detail-date/detail-date.js": "./frontend/src/detail-date/detail-date.js",
|
|
10288
10713
|
"./detail-default/detail-default": "./frontend/src/detail-default/detail-default.js",
|
|
10289
10714
|
"./detail-default/detail-default.html": "./frontend/src/detail-default/detail-default.html",
|
|
10290
10715
|
"./detail-default/detail-default.js": "./frontend/src/detail-default/detail-default.js",
|
|
10716
|
+
"./document-details/date-view-mode-picker/date-view-mode-picker": "./frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.js",
|
|
10717
|
+
"./document-details/date-view-mode-picker/date-view-mode-picker.html": "./frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.html",
|
|
10718
|
+
"./document-details/date-view-mode-picker/date-view-mode-picker.js": "./frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.js",
|
|
10291
10719
|
"./document-details/document-details": "./frontend/src/document-details/document-details.js",
|
|
10292
10720
|
"./document-details/document-details.css": "./frontend/src/document-details/document-details.css",
|
|
10293
10721
|
"./document-details/document-details.html": "./frontend/src/document-details/document-details.html",
|
|
@@ -10506,7 +10934,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
10506
10934
|
/* harmony export */ });
|
|
10507
10935
|
/* harmony import */ var _vue_shared__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
|
|
10508
10936
|
/**
|
|
10509
|
-
* @vue/reactivity v3.5.
|
|
10937
|
+
* @vue/reactivity v3.5.32
|
|
10510
10938
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
10511
10939
|
* @license MIT
|
|
10512
10940
|
**/
|
|
@@ -12653,7 +13081,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
12653
13081
|
/* harmony import */ var _vue_reactivity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/reactivity */ "./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js");
|
|
12654
13082
|
/* harmony import */ var _vue_shared__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
|
|
12655
13083
|
/**
|
|
12656
|
-
* @vue/runtime-core v3.5.
|
|
13084
|
+
* @vue/runtime-core v3.5.32
|
|
12657
13085
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
12658
13086
|
* @license MIT
|
|
12659
13087
|
**/
|
|
@@ -13600,6 +14028,7 @@ function createPathGetter(ctx, path) {
|
|
|
13600
14028
|
};
|
|
13601
14029
|
}
|
|
13602
14030
|
|
|
14031
|
+
const pendingMounts = /* @__PURE__ */ new WeakMap();
|
|
13603
14032
|
const TeleportEndKey = /* @__PURE__ */ Symbol("_vte");
|
|
13604
14033
|
const isTeleport = (type) => type.__isTeleport;
|
|
13605
14034
|
const isTeleportDisabled = (props) => props && (props.disabled || props.disabled === "");
|
|
@@ -13641,91 +14070,86 @@ const TeleportImpl = {
|
|
|
13641
14070
|
o: { insert, querySelector, createText, createComment }
|
|
13642
14071
|
} = internals;
|
|
13643
14072
|
const disabled = isTeleportDisabled(n2.props);
|
|
13644
|
-
let {
|
|
14073
|
+
let { dynamicChildren } = n2;
|
|
13645
14074
|
if ( true && isHmrUpdating) {
|
|
13646
14075
|
optimized = false;
|
|
13647
14076
|
dynamicChildren = null;
|
|
13648
14077
|
}
|
|
14078
|
+
const mount = (vnode, container2, anchor2) => {
|
|
14079
|
+
if (vnode.shapeFlag & 16) {
|
|
14080
|
+
mountChildren(
|
|
14081
|
+
vnode.children,
|
|
14082
|
+
container2,
|
|
14083
|
+
anchor2,
|
|
14084
|
+
parentComponent,
|
|
14085
|
+
parentSuspense,
|
|
14086
|
+
namespace,
|
|
14087
|
+
slotScopeIds,
|
|
14088
|
+
optimized
|
|
14089
|
+
);
|
|
14090
|
+
}
|
|
14091
|
+
};
|
|
14092
|
+
const mountToTarget = (vnode = n2) => {
|
|
14093
|
+
const disabled2 = isTeleportDisabled(vnode.props);
|
|
14094
|
+
const target = vnode.target = resolveTarget(vnode.props, querySelector);
|
|
14095
|
+
const targetAnchor = prepareAnchor(target, vnode, createText, insert);
|
|
14096
|
+
if (target) {
|
|
14097
|
+
if (namespace !== "svg" && isTargetSVG(target)) {
|
|
14098
|
+
namespace = "svg";
|
|
14099
|
+
} else if (namespace !== "mathml" && isTargetMathML(target)) {
|
|
14100
|
+
namespace = "mathml";
|
|
14101
|
+
}
|
|
14102
|
+
if (parentComponent && parentComponent.isCE) {
|
|
14103
|
+
(parentComponent.ce._teleportTargets || (parentComponent.ce._teleportTargets = /* @__PURE__ */ new Set())).add(target);
|
|
14104
|
+
}
|
|
14105
|
+
if (!disabled2) {
|
|
14106
|
+
mount(vnode, target, targetAnchor);
|
|
14107
|
+
updateCssVars(vnode, false);
|
|
14108
|
+
}
|
|
14109
|
+
} else if ( true && !disabled2) {
|
|
14110
|
+
warn$1("Invalid Teleport target on mount:", target, `(${typeof target})`);
|
|
14111
|
+
}
|
|
14112
|
+
};
|
|
14113
|
+
const queuePendingMount = (vnode) => {
|
|
14114
|
+
const mountJob = () => {
|
|
14115
|
+
if (pendingMounts.get(vnode) !== mountJob) return;
|
|
14116
|
+
pendingMounts.delete(vnode);
|
|
14117
|
+
if (isTeleportDisabled(vnode.props)) {
|
|
14118
|
+
mount(vnode, container, vnode.anchor);
|
|
14119
|
+
updateCssVars(vnode, true);
|
|
14120
|
+
}
|
|
14121
|
+
mountToTarget(vnode);
|
|
14122
|
+
};
|
|
14123
|
+
pendingMounts.set(vnode, mountJob);
|
|
14124
|
+
queuePostRenderEffect(mountJob, parentSuspense);
|
|
14125
|
+
};
|
|
13649
14126
|
if (n1 == null) {
|
|
13650
14127
|
const placeholder = n2.el = true ? createComment("teleport start") : 0;
|
|
13651
14128
|
const mainAnchor = n2.anchor = true ? createComment("teleport end") : 0;
|
|
13652
14129
|
insert(placeholder, container, anchor);
|
|
13653
14130
|
insert(mainAnchor, container, anchor);
|
|
13654
|
-
|
|
13655
|
-
|
|
13656
|
-
|
|
13657
|
-
|
|
13658
|
-
container2,
|
|
13659
|
-
anchor2,
|
|
13660
|
-
parentComponent,
|
|
13661
|
-
parentSuspense,
|
|
13662
|
-
namespace,
|
|
13663
|
-
slotScopeIds,
|
|
13664
|
-
optimized
|
|
13665
|
-
);
|
|
13666
|
-
}
|
|
13667
|
-
};
|
|
13668
|
-
const mountToTarget = () => {
|
|
13669
|
-
const target = n2.target = resolveTarget(n2.props, querySelector);
|
|
13670
|
-
const targetAnchor = prepareAnchor(target, n2, createText, insert);
|
|
13671
|
-
if (target) {
|
|
13672
|
-
if (namespace !== "svg" && isTargetSVG(target)) {
|
|
13673
|
-
namespace = "svg";
|
|
13674
|
-
} else if (namespace !== "mathml" && isTargetMathML(target)) {
|
|
13675
|
-
namespace = "mathml";
|
|
13676
|
-
}
|
|
13677
|
-
if (parentComponent && parentComponent.isCE) {
|
|
13678
|
-
(parentComponent.ce._teleportTargets || (parentComponent.ce._teleportTargets = /* @__PURE__ */ new Set())).add(target);
|
|
13679
|
-
}
|
|
13680
|
-
if (!disabled) {
|
|
13681
|
-
mount(target, targetAnchor);
|
|
13682
|
-
updateCssVars(n2, false);
|
|
13683
|
-
}
|
|
13684
|
-
} else if ( true && !disabled) {
|
|
13685
|
-
warn$1(
|
|
13686
|
-
"Invalid Teleport target on mount:",
|
|
13687
|
-
target,
|
|
13688
|
-
`(${typeof target})`
|
|
13689
|
-
);
|
|
13690
|
-
}
|
|
13691
|
-
};
|
|
14131
|
+
if (isTeleportDeferred(n2.props) || parentSuspense && parentSuspense.pendingBranch) {
|
|
14132
|
+
queuePendingMount(n2);
|
|
14133
|
+
return;
|
|
14134
|
+
}
|
|
13692
14135
|
if (disabled) {
|
|
13693
|
-
mount(container, mainAnchor);
|
|
14136
|
+
mount(n2, container, mainAnchor);
|
|
13694
14137
|
updateCssVars(n2, true);
|
|
13695
14138
|
}
|
|
13696
|
-
|
|
13697
|
-
n2.el.__isMounted = false;
|
|
13698
|
-
queuePostRenderEffect(() => {
|
|
13699
|
-
if (n2.el.__isMounted !== false) return;
|
|
13700
|
-
mountToTarget();
|
|
13701
|
-
delete n2.el.__isMounted;
|
|
13702
|
-
}, parentSuspense);
|
|
13703
|
-
} else {
|
|
13704
|
-
mountToTarget();
|
|
13705
|
-
}
|
|
14139
|
+
mountToTarget();
|
|
13706
14140
|
} else {
|
|
13707
14141
|
n2.el = n1.el;
|
|
13708
|
-
n2.targetStart = n1.targetStart;
|
|
13709
14142
|
const mainAnchor = n2.anchor = n1.anchor;
|
|
13710
|
-
const
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
n1,
|
|
13716
|
-
n2,
|
|
13717
|
-
container,
|
|
13718
|
-
anchor,
|
|
13719
|
-
parentComponent,
|
|
13720
|
-
parentSuspense,
|
|
13721
|
-
namespace,
|
|
13722
|
-
slotScopeIds,
|
|
13723
|
-
optimized,
|
|
13724
|
-
internals
|
|
13725
|
-
);
|
|
13726
|
-
}, parentSuspense);
|
|
14143
|
+
const pendingMount = pendingMounts.get(n1);
|
|
14144
|
+
if (pendingMount) {
|
|
14145
|
+
pendingMount.flags |= 8;
|
|
14146
|
+
pendingMounts.delete(n1);
|
|
14147
|
+
queuePendingMount(n2);
|
|
13727
14148
|
return;
|
|
13728
14149
|
}
|
|
14150
|
+
n2.targetStart = n1.targetStart;
|
|
14151
|
+
const target = n2.target = n1.target;
|
|
14152
|
+
const targetAnchor = n2.targetAnchor = n1.targetAnchor;
|
|
13729
14153
|
const wasDisabled = isTeleportDisabled(n1.props);
|
|
13730
14154
|
const currentContainer = wasDisabled ? container : target;
|
|
13731
14155
|
const currentAnchor = wasDisabled ? mainAnchor : targetAnchor;
|
|
@@ -13816,13 +14240,19 @@ const TeleportImpl = {
|
|
|
13816
14240
|
target,
|
|
13817
14241
|
props
|
|
13818
14242
|
} = vnode;
|
|
14243
|
+
let shouldRemove = doRemove || !isTeleportDisabled(props);
|
|
14244
|
+
const pendingMount = pendingMounts.get(vnode);
|
|
14245
|
+
if (pendingMount) {
|
|
14246
|
+
pendingMount.flags |= 8;
|
|
14247
|
+
pendingMounts.delete(vnode);
|
|
14248
|
+
shouldRemove = false;
|
|
14249
|
+
}
|
|
13819
14250
|
if (target) {
|
|
13820
14251
|
hostRemove(targetStart);
|
|
13821
14252
|
hostRemove(targetAnchor);
|
|
13822
14253
|
}
|
|
13823
14254
|
doRemove && hostRemove(anchor);
|
|
13824
14255
|
if (shapeFlag & 16) {
|
|
13825
|
-
const shouldRemove = doRemove || !isTeleportDisabled(props);
|
|
13826
14256
|
for (let i = 0; i < children.length; i++) {
|
|
13827
14257
|
const child = children[i];
|
|
13828
14258
|
unmount(
|
|
@@ -20031,6 +20461,7 @@ function createSuspenseBoundary(vnode, parentSuspense, parentComponent, containe
|
|
|
20031
20461
|
if (instance.isUnmounted || suspense.isUnmounted || suspense.pendingId !== instance.suspenseId) {
|
|
20032
20462
|
return;
|
|
20033
20463
|
}
|
|
20464
|
+
unsetCurrentInstance();
|
|
20034
20465
|
instance.asyncResolved = true;
|
|
20035
20466
|
const { vnode: vnode2 } = instance;
|
|
20036
20467
|
if (true) {
|
|
@@ -21237,7 +21668,7 @@ function isMemoSame(cached, memo) {
|
|
|
21237
21668
|
return true;
|
|
21238
21669
|
}
|
|
21239
21670
|
|
|
21240
|
-
const version = "3.5.
|
|
21671
|
+
const version = "3.5.32";
|
|
21241
21672
|
const warn = true ? warn$1 : 0;
|
|
21242
21673
|
const ErrorTypeStrings = ErrorTypeStrings$1 ;
|
|
21243
21674
|
const devtools = true ? devtools$1 : 0;
|
|
@@ -21448,7 +21879,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
21448
21879
|
/* harmony import */ var _vue_runtime_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/runtime-core */ "./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js");
|
|
21449
21880
|
/* harmony import */ var _vue_runtime_core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
|
|
21450
21881
|
/**
|
|
21451
|
-
* @vue/runtime-dom v3.5.
|
|
21882
|
+
* @vue/runtime-dom v3.5.32
|
|
21452
21883
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
21453
21884
|
* @license MIT
|
|
21454
21885
|
**/
|
|
@@ -23520,7 +23951,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
23520
23951
|
/* harmony export */ toTypeString: () => (/* binding */ toTypeString)
|
|
23521
23952
|
/* harmony export */ });
|
|
23522
23953
|
/**
|
|
23523
|
-
* @vue/shared v3.5.
|
|
23954
|
+
* @vue/shared v3.5.32
|
|
23524
23955
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
23525
23956
|
* @license MIT
|
|
23526
23957
|
**/
|
|
@@ -49188,7 +49619,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
49188
49619
|
/* 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");
|
|
49189
49620
|
/* 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");
|
|
49190
49621
|
/**
|
|
49191
|
-
* vue v3.5.
|
|
49622
|
+
* vue v3.5.32
|
|
49192
49623
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
|
49193
49624
|
* @license MIT
|
|
49194
49625
|
**/
|
|
@@ -50474,7 +50905,7 @@ module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b
|
|
|
50474
50905
|
(module) {
|
|
50475
50906
|
|
|
50476
50907
|
"use strict";
|
|
50477
|
-
module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b border-gray-100 px-2 pb-2 text-xl font-bold\">\n {{header}}\n </div>\n <div class=\"text-xl p-2\">\n {{displayValue}}\n </div>\n</div
|
|
50908
|
+
module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b border-gray-100 px-2 pb-2 text-xl font-bold\">\n {{header}}\n </div>\n <div class=\"text-xl p-2\" :class=\"displayClass\">\n {{displayValue}}\n </div>\n</div>\n";
|
|
50478
50909
|
|
|
50479
50910
|
/***/ },
|
|
50480
50911
|
|
|
@@ -50485,7 +50916,7 @@ module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b
|
|
|
50485
50916
|
(module) {
|
|
50486
50917
|
|
|
50487
50918
|
"use strict";
|
|
50488
|
-
module.exports = "<div>\n <div v-if=\"Array.isArray(result)\">\n <div v-for=\"el in result\"
|
|
50919
|
+
module.exports = "<div>\n <div v-if=\"Array.isArray(result)\">\n <div v-for=\"(el, index) in result\">\n <component\n class=\"bg-surface shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl\"\n :is=\"getComponentForValue(el)\"\n :value=\"el\">\n </component>\n </div>\n </div>\n <div v-else>\n <component\n class=\"bg-surface shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl\"\n :is=\"getComponentForValue(result)\"\n :value=\"result\"\n :fullscreen=\"fullscreen\"\n @fullscreen=\"$emit('fullscreen')\">\n </component>\n </div>\n <div class=\"text-right text-sm text-content-secondary mt-1\" v-if=\"finishedEvaluatingAt && !fullscreen\">\n Last Evaluated: {{ format.isoToLongDateTime(finishedEvaluatingAt) }}\n </div>\n</div>\n";
|
|
50489
50920
|
|
|
50490
50921
|
/***/ },
|
|
50491
50922
|
|
|
@@ -50496,7 +50927,7 @@ module.exports = "<div>\n <div v-if=\"Array.isArray(result)\">\n <div v-for=
|
|
|
50496
50927
|
(module) {
|
|
50497
50928
|
|
|
50498
50929
|
"use strict";
|
|
50499
|
-
module.exports = "<div class=\"overflow-x-auto\">\n <table class=\"min-w-full border-separate border-spacing-0\">\n <thead v-if=\"hasColumns\" class=\"bg-slate-50\">\n <tr>\n <th\n v-for=\"(column, index) in columns\"\n :key=\"'column-' + index\"\n class=\"bg-slate-50 p-3 text-left text-sm font-semibold text-content border-b border-edge\"\n >\n {{ column }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(row, rowIndex) in rows\" :key=\"'row-' + rowIndex\" class=\"bg-surface hover:bg-slate-50\">\n <td\n v-for=\"(cell, columnIndex) in row\"\n :key=\"'cell-' + rowIndex + '-' + columnIndex\"\n class=\"p-3 text-sm text-content border-b border-edge\"\n >\n {{ displayValue(cell) }}\n </td>\n </tr>\n <tr v-if=\"!hasRows\">\n <td\n :colspan=\"Math.max(columns.length, 1)\"\n class=\"p-3 text-sm text-content-tertiary border-b border-edge\"\n >\n No rows\n </td>\n </tr>\n </tbody>\n </table>\n</div>\n";
|
|
50930
|
+
module.exports = "<div class=\"overflow-x-auto\">\n <table class=\"min-w-full border-separate border-spacing-0\">\n <thead v-if=\"hasColumns\" class=\"bg-slate-50\">\n <tr>\n <th\n v-for=\"(column, index) in columns\"\n :key=\"'column-' + index\"\n class=\"bg-slate-50 p-3 text-left text-sm font-semibold text-content border-b border-edge\"\n >\n <div v-if=\"index === columns.length - 1\" class=\"relative flex items-center gap-2 w-full\" ref=\"dropdown\">\n <span class=\"min-w-0 flex-1\">{{ column }}</span>\n <button\n @click.stop=\"toggleDropdown\"\n class=\"ml-auto rounded p-1 text-content-secondary hover:bg-muted hover:text-content\"\n aria-label=\"Table actions\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n <div\n v-if=\"showDropdown\"\n class=\"absolute right-0 top-full z-10 mt-2 w-56 origin-top-right rounded-md bg-surface py-1 shadow-lg ring-1 ring-black/5\">\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"downloadCsv(); showDropdown = false\">\n Download as CSV\n </button>\n </div>\n </div>\n <template v-else>{{ column }}</template>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(row, rowIndex) in rows\" :key=\"'row-' + rowIndex\" class=\"bg-surface hover:bg-slate-50\">\n <td\n v-for=\"(cell, columnIndex) in row\"\n :key=\"'cell-' + rowIndex + '-' + columnIndex\"\n class=\"p-3 text-sm text-content border-b border-edge\"\n >\n {{ displayValue(cell) }}\n </td>\n </tr>\n <tr v-if=\"!hasRows\">\n <td\n :colspan=\"Math.max(columns.length, 1)\"\n class=\"p-3 text-sm text-content-tertiary border-b border-edge\"\n >\n No rows\n </td>\n </tr>\n </tbody>\n </table>\n</div>\n";
|
|
50500
50931
|
|
|
50501
50932
|
/***/ },
|
|
50502
50933
|
|
|
@@ -50555,6 +50986,17 @@ module.exports = "<div class=\"w-full\">\n <div v-if=\"!arrayValue || arrayValu
|
|
|
50555
50986
|
|
|
50556
50987
|
/***/ },
|
|
50557
50988
|
|
|
50989
|
+
/***/ "./frontend/src/detail-date/detail-date.html"
|
|
50990
|
+
/*!***************************************************!*\
|
|
50991
|
+
!*** ./frontend/src/detail-date/detail-date.html ***!
|
|
50992
|
+
\***************************************************/
|
|
50993
|
+
(module) {
|
|
50994
|
+
|
|
50995
|
+
"use strict";
|
|
50996
|
+
module.exports = "<pre class=\"w-full whitespace-pre-wrap break-words font-mono text-sm text-content-secondary m-0\">{{displayValue}}</pre>\n";
|
|
50997
|
+
|
|
50998
|
+
/***/ },
|
|
50999
|
+
|
|
50558
51000
|
/***/ "./frontend/src/detail-default/detail-default.html"
|
|
50559
51001
|
/*!*********************************************************!*\
|
|
50560
51002
|
!*** ./frontend/src/detail-default/detail-default.html ***!
|
|
@@ -50566,6 +51008,17 @@ module.exports = "<div class=\"w-full\">\n <pre v-if=\"!isGeoJsonGeometry || !m
|
|
|
50566
51008
|
|
|
50567
51009
|
/***/ },
|
|
50568
51010
|
|
|
51011
|
+
/***/ "./frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.html"
|
|
51012
|
+
/*!****************************************************************************************!*\
|
|
51013
|
+
!*** ./frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.html ***!
|
|
51014
|
+
\****************************************************************************************/
|
|
51015
|
+
(module) {
|
|
51016
|
+
|
|
51017
|
+
"use strict";
|
|
51018
|
+
module.exports = "<div class=\"flex items-center gap-2\" @click.stop>\n <select\n :value=\"format\"\n @input=\"onFormatChange($event.target.value)\"\n class=\"text-xs border border-edge rounded-md bg-surface px-2 py-1\"\n >\n <option value=\"utc_iso\">UTC (ISO)</option>\n <option value=\"local_browser\">Local (Browser)</option>\n <option value=\"unix_ms\">Unix (ms)</option>\n <option value=\"unix_seconds\">Unix (seconds)</option>\n <option value=\"duration_relative\">Duration relative to now</option>\n <option value=\"custom_tz\">Custom TZ...</option>\n </select>\n <input\n v-if=\"format === 'custom_tz'\"\n :value=\"timezone\"\n @input=\"onTimezoneChange($event.target.value.trim())\"\n :list=\"timezoneDatalistId\"\n type=\"text\"\n placeholder=\"America/New_York\"\n class=\"text-xs border border-edge rounded-md bg-surface px-2 py-1 w-40\"\n >\n <datalist v-if=\"format === 'custom_tz'\" :id=\"timezoneDatalistId\">\n <option v-for=\"tz in timezones\" :key=\"tz\" :value=\"tz\"></option>\n </datalist>\n</div>\n";
|
|
51019
|
+
|
|
51020
|
+
/***/ },
|
|
51021
|
+
|
|
50569
51022
|
/***/ "./frontend/src/document-details/document-details.css"
|
|
50570
51023
|
/*!************************************************************!*\
|
|
50571
51024
|
!*** ./frontend/src/document-details/document-details.css ***!
|
|
@@ -50606,7 +51059,7 @@ module.exports = ".document-details {\n width: 100%;\n }\n \n .document-de
|
|
|
50606
51059
|
(module) {
|
|
50607
51060
|
|
|
50608
51061
|
"use strict";
|
|
50609
|
-
module.exports = "<div class=\"border border-edge bg-surface rounded-lg mb-2\" style=\"overflow: visible;\">\n <!-- Collapsible Header -->\n <div\n @click=\"toggleCollapse\"\n class=\"p-1 cursor-pointer flex items-center justify-between border-b border-edge transition-colors duration-200 ease-in-out\"\n :class=\"{ 'bg-amber-100 hover:bg-amber-200': highlight, 'bg-slate-100 hover:bg-muted': !highlight }\"\n style=\"overflow: visible; position: relative;\"\n >\n <div class=\"flex items-center\" >\n <svg\n :class=\"isCollapsed ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-content\">{{path.path}}</span>\n <span class=\"ml-2 text-sm text-content-tertiary\">({{(path.instance || 'unknown').toLowerCase()}})</span>\n <div v-if=\"isGeoJsonGeometry\" class=\"ml-3 inline-flex items-center gap-2\">\n <div class=\"inline-flex items-center rounded-full bg-gray-200 p-0.5 text-xs font-semibold\">\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"detailViewMode === 'text' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'\"\n :style=\"detailViewMode === 'text' ? 'color: white !important; background-color: #2563eb !important;' : ''\"\n @click.stop=\"setDetailViewMode('text')\">\n Text\n </button>\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"detailViewMode === 'map' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'\"\n :style=\"detailViewMode === 'map' ? 'color: white !important; background-color: #2563eb !important;' : ''\"\n @click.stop=\"setDetailViewMode('map')\">\n Map\n </button>\n </div>\n <!-- Info icon with tooltip -->\n <div v-if=\"editting\" class=\"relative inline-block\" style=\"z-index: 10002;\" @mouseenter=\"showTooltip = true\" @mouseleave=\"showTooltip = false\" @click.stop>\n <svg\n ref=\"infoIcon\"\n class=\"w-6 h-6 text-gray-400 hover:text-gray-600 cursor-help\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n <div\n v-show=\"showTooltip\"\n ref=\"tooltip\"\n class=\"absolute left-full top-0 ml-2 w-64 p-3 text-white text-xs rounded-lg shadow-xl\"\n style=\"z-index: 99999; pointer-events: none; white-space: normal; position: fixed; background-color: #111827;\"\n :style=\"getTooltipStyle()\"\n >\n <div class=\"font-semibold mb-2\">Map Controls:</div>\n <div v-if=\"isGeoJsonPoint\" class=\"space-y-1\">\n <div>• Drag pin to move location</div>\n </div>\n <div v-else-if=\"isGeoJsonPolygon\" class=\"space-y-1\">\n <div>• Drag vertices to reshape polygon</div>\n <div v-if=\"isMultiPolygon\">• Right-click edge to add new vertex</div>\n <div>• Right-click vertex to delete</div>\n </div>\n <div class=\"absolute top-2 -left-1 w-0 h-0 border-t-4 border-b-4 border-r-4 border-transparent border-r-gray-900\"></div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"flex items-center gap-1 text-sm text-gray-600 hover:text-gray-800 px-2 py-1 rounded-md border border-transparent hover:border-edge-strong bg-surface\"\n @click.stop.prevent=\"copyPropertyValue\"\n title=\"Copy value\"\n aria-label=\"Copy property value\"\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=\"M8 7h8m-8 4h8m-8 4h5m-7-9a2 2 0 012-2h7a2 2 0 012 2v10a2 2 0 01-2 2H8l-4-4V7a2 2 0 012-2z\" />\n </svg>\n {{copyButtonLabel}}\n </button>\n <router-link\n v-if=\"path.ref && getValueForPath(path.path)\"\n :to=\"`/model/${path.ref}/document/${getValueForPath(path.path)}`\"\n class=\"bg-primary hover:bg-primary-hover text-primary-text px-2 py-1 text-sm rounded-md\"\n @click.stop\n >View Document\n </router-link>\n </div>\n </div>\n\n <!-- Collapsible Content -->\n <div v-if=\"!isCollapsed\" class=\"p-2\">\n <!-- Date Type Selector (when editing dates) -->\n <div v-if=\"editting && path.instance === 'Date'\" class=\"mb-3 flex gap-1.5\">\n <div\n @click=\"dateType = 'picker'\"\n :class=\"dateType === 'picker' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'picker' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n Date Picker\n </div>\n </div>\n <div\n @click=\"dateType = 'iso'\"\n :class=\"dateType === 'iso' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'iso' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n ISO String\n </div>\n </div>\n </div>\n\n <!-- Field Content -->\n <div v-if=\"editting && path.path !== '_id'\">\n <!-- Use detail-default with map editing for GeoJSON geometries -->\n <component\n v-if=\"isGeoJsonGeometry\"\n :is=\"getComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :view-mode=\"detailViewMode\"\n :on-change=\"handleInputChange\"\n >\n </component>\n <!-- Use standard edit components for other types -->\n <component\n v-else\n :is=\"getEditComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :format=\"dateType\"\n v-bind=\"getEditComponentProps(path)\"\n @input=\"handleInputChange($event)\"\n @error=\"invalid[path.path] = $event;\"\n >\n </component>\n </div>\n <div v-else>\n <!-- Show truncated or full value based on needsTruncation and isValueExpanded -->\n <!-- Special handling for truncated arrays -->\n <div v-if=\"isArray && shouldShowTruncated\" class=\"w-full\">\n <div class=\"mt-2\">\n <div\n v-for=\"(item, index) in truncatedArrayItems\"\n :key=\"index\"\n class=\"mb-1.5 py-2.5 px-3 pl-4 bg-transparent border-l-[3px] border-l-blue-500 rounded-none transition-all duration-200 cursor-pointer relative hover:bg-slate-50 hover:border-l-blue-600\">\n <div class=\"absolute -left-2 top-1/2 -translate-y-1/2 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-[10px] font-semibold font-mono z-10 hover:bg-blue-600\">{{ index }}</div>\n <div v-if=\"arrayUtils.isObjectItem(item)\" class=\"flex flex-col gap-1 mt-1 px-2\">\n <div\n v-for=\"key in arrayUtils.getItemKeys(item)\"\n :key=\"key\"\n class=\"flex items-start gap-2 text-xs font-mono\">\n <span class=\"font-semibold text-gray-600 flex-shrink-0 min-w-[80px]\">{{ key }}:</span>\n <span class=\"text-gray-800 break-words whitespace-pre-wrap flex-1\">{{ arrayUtils.formatItemValue(item, key) }}</span>\n </div>\n </div>\n <div v-else class=\"text-xs py-1.5 px-2 font-mono text-gray-800 break-words whitespace-pre-wrap mt-1\">{{ arrayUtils.formatValue(item) }}</div>\n </div>\n <div class=\"mb-1.5 py-2.5 px-3 pl-4 bg-transparent border-none border-l-[3px] border-l-blue-500 rounded-none transition-all duration-200 cursor-pointer relative opacity-70 hover:opacity-100\">\n <div class=\"text-xs py-1.5 px-2 font-mono text-content-tertiary italic break-words whitespace-pre-wrap mt-1\">\n ... and {{ remainingArrayCount }} more item{{ remainingArrayCount !== 1 ? 's' : '' }}\n </div>\n </div>\n </div>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\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=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show all {{ arrayValue.length }} items\n </button>\n </div>\n <!-- Non-array truncated view -->\n <div v-else-if=\"shouldShowTruncated && !isArray\" class=\"relative\">\n <div class=\"text-content-secondary whitespace-pre-wrap break-words font-mono text-sm\">{{truncatedString}}</div>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\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=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show more ({{valueAsString.length}} characters)\n </button>\n </div>\n <!-- Expanded view -->\n <div v-else-if=\"needsTruncation && isValueExpanded\" class=\"relative\">\n <component\n :is=\"getComponentForPath(path)\"\n :value=\"getValueForPath(path.path)\"\n :view-mode=\"detailViewMode\"></component>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\n >\n <svg class=\"w-4 h-4 rotate-180\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show less\n </button>\n </div>\n <!-- Full view (no truncation needed) -->\n <div v-else>\n <component\n :is=\"getComponentForPath(path)\"\n :value=\"getValueForPath(path.path)\"\n :view-mode=\"detailViewMode\"></component>\n </div>\n </div>\n </div>\n</div>\n";
|
|
51062
|
+
module.exports = "<div class=\"border border-edge bg-surface rounded-lg mb-2\" style=\"overflow: visible;\">\n <!-- Collapsible Header -->\n <div\n @click=\"toggleCollapse\"\n class=\"p-1 cursor-pointer flex items-center justify-between border-b border-edge transition-colors duration-200 ease-in-out\"\n :class=\"{ 'bg-amber-100 hover:bg-amber-200': highlight, 'bg-slate-100 hover:bg-muted': !highlight }\"\n style=\"overflow: visible; position: relative;\"\n >\n <div class=\"flex items-center\" >\n <svg\n :class=\"isCollapsed ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-content\">{{path.path}}</span>\n <span class=\"ml-2 text-sm text-content-tertiary\">({{(path.instance || 'unknown').toLowerCase()}})</span>\n <div v-if=\"isGeoJsonGeometry\" class=\"ml-3 inline-flex items-center gap-2\">\n <div class=\"inline-flex items-center rounded-full bg-gray-200 p-0.5 text-xs font-semibold\">\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"detailViewMode === 'text' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'\"\n :style=\"detailViewMode === 'text' ? 'color: white !important; background-color: #2563eb !important;' : ''\"\n @click.stop=\"setDetailViewMode('text')\">\n Text\n </button>\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"detailViewMode === 'map' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'\"\n :style=\"detailViewMode === 'map' ? 'color: white !important; background-color: #2563eb !important;' : ''\"\n @click.stop=\"setDetailViewMode('map')\">\n Map\n </button>\n </div>\n <!-- Info icon with tooltip -->\n <div v-if=\"editting\" class=\"relative inline-block\" style=\"z-index: 10002;\" @mouseenter=\"showTooltip = true\" @mouseleave=\"showTooltip = false\" @click.stop>\n <svg\n ref=\"infoIcon\"\n class=\"w-6 h-6 text-gray-400 hover:text-gray-600 cursor-help\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n <div\n v-show=\"showTooltip\"\n ref=\"tooltip\"\n class=\"absolute left-full top-0 ml-2 w-64 p-3 text-white text-xs rounded-lg shadow-xl\"\n style=\"z-index: 99999; pointer-events: none; white-space: normal; position: fixed; background-color: #111827;\"\n :style=\"getTooltipStyle()\"\n >\n <div class=\"font-semibold mb-2\">Map Controls:</div>\n <div v-if=\"isGeoJsonPoint\" class=\"space-y-1\">\n <div>• Drag pin to move location</div>\n </div>\n <div v-else-if=\"isGeoJsonPolygon\" class=\"space-y-1\">\n <div>• Drag vertices to reshape polygon</div>\n <div v-if=\"isMultiPolygon\">• Right-click edge to add new vertex</div>\n <div>• Right-click vertex to delete</div>\n </div>\n <div class=\"absolute top-2 -left-1 w-0 h-0 border-t-4 border-b-4 border-r-4 border-transparent border-r-gray-900\"></div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"flex items-center gap-2\">\n <date-view-mode-picker\n v-if=\"isDatePath\"\n :viewMode=\"dateViewMode\"\n :path=\"path\"\n @update:viewMode=\"dateViewMode = $event\"\n ></date-view-mode-picker>\n <button\n type=\"button\"\n class=\"flex items-center gap-1 text-sm text-gray-600 hover:text-gray-800 px-2 py-1 rounded-md border border-transparent hover:border-edge-strong bg-surface\"\n @click.stop.prevent=\"copyPropertyValue\"\n title=\"Copy value\"\n aria-label=\"Copy property value\"\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=\"M8 7h8m-8 4h8m-8 4h5m-7-9a2 2 0 012-2h7a2 2 0 012 2v10a2 2 0 01-2 2H8l-4-4V7a2 2 0 012-2z\" />\n </svg>\n {{copyButtonLabel}}\n </button>\n <router-link\n v-if=\"path.ref && getValueForPath(path.path)\"\n :to=\"`/model/${path.ref}/document/${getValueForPath(path.path)}`\"\n class=\"bg-primary hover:bg-primary-hover text-primary-text px-2 py-1 text-sm rounded-md\"\n @click.stop\n >View Document\n </router-link>\n </div>\n </div>\n\n <!-- Collapsible Content -->\n <div v-if=\"!isCollapsed\" class=\"p-2\">\n <!-- Date Type Selector (when editing dates) -->\n <div v-if=\"editting && path.instance === 'Date'\" class=\"mb-3 flex gap-1.5\">\n <div\n @click=\"dateType = 'picker'\"\n :class=\"dateType === 'picker' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'picker' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n Date Picker\n </div>\n </div>\n <div\n @click=\"dateType = 'iso'\"\n :class=\"dateType === 'iso' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'iso' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n ISO String\n </div>\n </div>\n </div>\n\n <!-- Field Content -->\n <div v-if=\"editting && path.path !== '_id'\">\n <!-- Use detail-default with map editing for GeoJSON geometries -->\n <component\n v-if=\"isGeoJsonGeometry\"\n :is=\"getComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :viewMode=\"detailViewMode\"\n :on-change=\"handleInputChange\"\n >\n </component>\n <!-- Use standard edit components for other types -->\n <component\n v-else\n :is=\"getEditComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :format=\"dateType\"\n v-bind=\"getEditComponentProps(path)\"\n @input=\"handleInputChange($event)\"\n @error=\"invalid[path.path] = $event;\"\n >\n </component>\n </div>\n <div v-else>\n <!-- Show truncated or full value based on needsTruncation and isValueExpanded -->\n <!-- Special handling for truncated arrays -->\n <div v-if=\"isArray && shouldShowTruncated\" class=\"w-full\">\n <div class=\"mt-2\">\n <div\n v-for=\"(item, index) in truncatedArrayItems\"\n :key=\"index\"\n class=\"mb-1.5 py-2.5 px-3 pl-4 bg-transparent border-l-[3px] border-l-blue-500 rounded-none transition-all duration-200 cursor-pointer relative hover:bg-slate-50 hover:border-l-blue-600\">\n <div class=\"absolute -left-2 top-1/2 -translate-y-1/2 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-[10px] font-semibold font-mono z-10 hover:bg-blue-600\">{{ index }}</div>\n <div v-if=\"arrayUtils.isObjectItem(item)\" class=\"flex flex-col gap-1 mt-1 px-2\">\n <div\n v-for=\"key in arrayUtils.getItemKeys(item)\"\n :key=\"key\"\n class=\"flex items-start gap-2 text-xs font-mono\">\n <span class=\"font-semibold text-gray-600 flex-shrink-0 min-w-[80px]\">{{ key }}:</span>\n <span class=\"text-gray-800 break-words whitespace-pre-wrap flex-1\">{{ arrayUtils.formatItemValue(item, key) }}</span>\n </div>\n </div>\n <div v-else class=\"text-xs py-1.5 px-2 font-mono text-gray-800 break-words whitespace-pre-wrap mt-1\">{{ arrayUtils.formatValue(item) }}</div>\n </div>\n <div class=\"mb-1.5 py-2.5 px-3 pl-4 bg-transparent border-none border-l-[3px] border-l-blue-500 rounded-none transition-all duration-200 cursor-pointer relative opacity-70 hover:opacity-100\">\n <div class=\"text-xs py-1.5 px-2 font-mono text-content-tertiary italic break-words whitespace-pre-wrap mt-1\">\n ... and {{ remainingArrayCount }} more item{{ remainingArrayCount !== 1 ? 's' : '' }}\n </div>\n </div>\n </div>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\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=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show all {{ arrayValue.length }} items\n </button>\n </div>\n <!-- Non-array truncated view -->\n <div v-else-if=\"shouldShowTruncated && !isArray\" class=\"relative\">\n <div class=\"text-content-secondary whitespace-pre-wrap break-words font-mono text-sm\">{{truncatedString}}</div>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\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=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show more ({{valueAsString.length}} characters)\n </button>\n </div>\n <!-- Expanded view -->\n <div v-else-if=\"needsTruncation && isValueExpanded\" class=\"relative\">\n <component\n :is=\"getComponentForPath(path)\"\n :value=\"rawValue\"\n :viewMode=\"isDatePath ? dateViewMode : detailViewMode\"\n @updated=\"renderedValue = $event\"></component>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\n >\n <svg class=\"w-4 h-4 rotate-180\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show less\n </button>\n </div>\n <!-- Full view (no truncation needed) -->\n <div v-else>\n <component\n :is=\"getComponentForPath(path)\"\n :value=\"rawValue\"\n :viewMode=\"isDatePath ? dateViewMode : detailViewMode\"\n @updated=\"renderedValue = $event\"></component>\n </div>\n </div>\n </div>\n</div>\n";
|
|
50610
51063
|
|
|
50611
51064
|
/***/ },
|
|
50612
51065
|
|
|
@@ -51068,7 +51521,7 @@ module.exports = "";
|
|
|
51068
51521
|
(module) {
|
|
51069
51522
|
|
|
51070
51523
|
"use strict";
|
|
51071
|
-
module.exports = "<div class=\"p-4 space-y-6\">\n <div>\n <h1 class=\"text-2xl font-bold text-content-secondary 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-surface p-4 rounded-lg shadow\" v-if=\"status == 'loaded'\">\n <div class=\"mb-4\">\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Filter by Date:</label>\n <select v-model=\"selectedRange\" @change=\"updateDateRange\" class=\"border-edge-strong 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-content-secondary mb-1\">Filter by Status:</label>\n <select v-model=\"selectedStatus\" @change=\"getTasks\" class=\"border-edge-strong 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-content-secondary mb-1\">Search by Task Name:</label>\n <input \n v-model=\"searchQuery\" \n type=\"text\" \n @input=\"onSearchInput\"\n class=\"border-edge-strong 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-content-secondary 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-primary text-primary-text hover:bg-primary-hover 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-edge hover:border-edge-strong'\"\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-content-secondary 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-primary transition-colors\">{{ group.name }}</div>\n <svg class=\"w-4 h-4 text-gray-400 group-hover:text-primary 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-content-tertiary group-hover:text-content-secondary transition-colors\">Total: {{ group.totalCount }} tasks</div>\n <div class=\"text-xs text-primary 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-content-tertiary\">\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-page border border-edge rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-edge-strong\"\n >\n <div class=\"text-xs text-gray-600 font-medium\">Cancelled</div>\n <div class=\"text-lg font-bold text-content-secondary\">{{ 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-content-secondary mb-4\">Create New Task</h3>\n \n <div>\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Task Name:</label>\n <input \n v-model=\"newTask.name\" \n type=\"text\" \n class=\"w-full border border-edge-strong rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"Enter task name\"\n >\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Scheduled Time:</label>\n <input \n v-model=\"newTask.scheduledAt\" \n type=\"datetime-local\" \n class=\"w-full border border-edge-strong rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Parameters (JSON):</label>\n <ace-editor\n v-model=\"newTask.parameters\"\n mode=\"json\"\n :line-numbers=\"true\"\n class=\"min-h-[120px]\"\n />\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-content-secondary 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-edge-strong rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"0 for no repetition\"\n >\n <p class=\"text-xs text-content-tertiary 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-primary text-primary-text px-4 py-2 rounded-md hover:bg-primary-hover\"\n >\n Create Task\n </button>\n <button \n @click=\"closeCreateTaskModal\" \n class=\"flex-1 bg-gray-300 text-content-secondary 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";
|
|
51524
|
+
module.exports = "<div class=\"p-4 space-y-6\">\n <div>\n <h1 class=\"text-2xl font-bold text-content-secondary 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-surface p-4 rounded-lg shadow\" v-if=\"status == 'loaded'\">\n <div class=\"mb-4\">\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Filter by Date:</label>\n <select v-model=\"selectedRange\" @change=\"updateDateRange\" class=\"border-edge-strong 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-content-secondary mb-1\">Filter by Status:</label>\n <select v-model=\"selectedStatus\" @change=\"getTasks\" class=\"border-edge-strong 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-content-secondary mb-1\">Search by Task Name:</label>\n <input \n v-model=\"searchQuery\" \n type=\"text\" \n @input=\"onSearchInput\"\n class=\"border-edge-strong 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-content-secondary 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-primary text-primary-text hover:bg-primary-hover 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-edge hover:border-edge-strong'\"\n >\n <div class=\"text-sm\">Cancelled</div>\n <div class=\"text-2xl font-bold\">{{cancelledCount}}</div>\n </button>\n </div>\n\n <!-- Tasks Over Time Chart -->\n <!--\n Canvas is gated by showOverTimeChart (v-if) so Chart.js is destroyed\n and the DOM node is removed before each refresh. In-place Chart.js\n updates during Vue re-renders from filter changes could freeze the UI\n (dropdowns stuck, chart not updating). See tasks.js getTasks().\n -->\n <div class=\"mt-6\">\n <h2 class=\"text-lg font-semibold text-content-secondary mb-3\">Tasks Over Time</h2>\n <div class=\"bg-page border border-edge rounded-lg p-4\" style=\"height: 260px;\">\n <canvas v-if=\"showOverTimeChart && overTimeBuckets.length > 0\" ref=\"overTimeChart\" style=\"width:100%;height:100%;\"></canvas>\n <div v-else class=\"flex items-center justify-center h-full text-content-tertiary text-sm\">\n No task activity in the selected window\n </div>\n </div>\n </div>\n \n <!-- Grouped Task List -->\n <div class=\"mt-6\">\n <h2 class=\"text-lg font-semibold text-content-secondary mb-4\">Tasks by Name</h2>\n <div class=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n <div v-for=\"group in tasksByName\" :key=\"group.name\" class=\"border border-edge rounded-lg p-4 group hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200\">\n <div class=\"flex items-start justify-between mb-3\">\n <div class=\"flex-1 cursor-pointer min-w-0 mr-2\" @click=\"openTaskGroupDetails(group)\">\n <div class=\"flex items-center gap-1\">\n <div class=\"font-medium text-sm group-hover:text-primary transition-colors truncate\">{{ group.name }}</div>\n <svg class=\"w-3 h-3 text-gray-400 group-hover:text-primary transition-colors flex-shrink-0\" 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-xs text-content-tertiary\">Total: {{ group.totalCount }} tasks</div>\n </div>\n <div class=\"text-xs text-content-tertiary flex-shrink-0\">\n {{ group.lastRun ? new Date(group.lastRun).toLocaleString() : 'Never' }}\n </div>\n </div>\n \n <!-- Status Counts -->\n <div class=\"grid grid-cols-2 gap-1.5\">\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-base 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-base 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-base font-bold text-red-700\">{{ group.statusCounts.failed || 0 }}</div>\n </button>\n <button \n @click.stop=\"openTaskGroupDetailsWithFilter(group, 'cancelled')\"\n class=\"bg-page border border-edge rounded-md p-2 text-center shadow-sm hover:shadow-md transform hover:-translate-y-1 transition-all duration-200 cursor-pointer hover:border-edge-strong\"\n >\n <div class=\"text-xs text-gray-600 font-medium\">Cancelled</div>\n <div class=\"text-base font-bold text-content-secondary\">{{ group.statusCounts.cancelled || 0 }}</div>\n </button>\n </div>\n </div>\n </div>\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-content-secondary mb-4\">Create New Task</h3>\n \n <div>\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Task Name:</label>\n <input \n v-model=\"newTask.name\" \n type=\"text\" \n class=\"w-full border border-edge-strong rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"Enter task name\"\n >\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Scheduled Time:</label>\n <input \n v-model=\"newTask.scheduledAt\" \n type=\"datetime-local\" \n class=\"w-full border border-edge-strong rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-content-secondary mb-1\">Parameters (JSON):</label>\n <ace-editor\n v-model=\"newTask.parameters\"\n mode=\"json\"\n :line-numbers=\"true\"\n class=\"min-h-[120px]\"\n />\n </div>\n \n <div>\n <label class=\"block text-sm font-medium text-content-secondary 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-edge-strong rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"0 for no repetition\"\n >\n <p class=\"text-xs text-content-tertiary 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-primary text-primary-text px-4 py-2 rounded-md hover:bg-primary-hover\"\n >\n Create Task\n </button>\n <button \n @click=\"closeCreateTaskModal\" \n class=\"flex-1 bg-gray-300 text-content-secondary 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";
|
|
51072
51525
|
|
|
51073
51526
|
/***/ },
|
|
51074
51527
|
|
|
@@ -62382,7 +62835,7 @@ var src_default = VueToastificationPlugin;
|
|
|
62382
62835
|
(module) {
|
|
62383
62836
|
|
|
62384
62837
|
"use strict";
|
|
62385
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.3.
|
|
62838
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.3.7","description":"A Mongoose-native MongoDB UI with schema-aware autocomplete, AI-assisted queries, and dashboards that understand your models - not just your data.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.0","marked":"15.0.12","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","time-commando":"1.0.1","xss":"^1.0.15"},"peerDependencies":{"mongoose":"7.x || 8.x || ^9.0.0"},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","seed":"node seed/index.js","start":"node ./local.js","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js"}}');
|
|
62386
62839
|
|
|
62387
62840
|
/***/ }
|
|
62388
62841
|
|