@mongoosejs/studio 0.0.107 → 0.0.109
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/getDocuments.js +5 -1
- package/frontend/public/app.js +45 -10
- package/frontend/public/tw.css +8 -8
- package/frontend/src/chat/chat-message-script/chat-message-script.html +18 -5
- package/frontend/src/chat/chat-message-script/chat-message-script.js +16 -0
- package/frontend/src/dashboard/dashboard.html +5 -1
- package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.html +16 -2
- package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.js +18 -1
- package/frontend/src/dashboard-result/dashboard-result.html +3 -3
- package/frontend/src/models/models.css +7 -1
- package/frontend/src/models/models.html +31 -24
- package/frontend/src/models/models.js +4 -2
- package/package.json +1 -1
|
@@ -52,11 +52,15 @@ module.exports = ({ db }) => async function getDocuments(params) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const hasSort = typeof sort === 'object' && sort != null && Object.keys(sort).length > 0;
|
|
55
|
+
const sortObj = hasSort ? { ...sort } : {};
|
|
56
|
+
if (!sortObj.hasOwnProperty('_id')) {
|
|
57
|
+
sortObj._id = -1;
|
|
58
|
+
}
|
|
55
59
|
const cursor = await Model.
|
|
56
60
|
find(filter == null ? {} : filter).
|
|
57
61
|
limit(limit).
|
|
58
62
|
skip(skip).
|
|
59
|
-
sort(
|
|
63
|
+
sort(sortObj).
|
|
60
64
|
cursor();
|
|
61
65
|
const docs = [];
|
|
62
66
|
for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
|
package/frontend/public/app.js
CHANGED
|
@@ -335,6 +335,7 @@ module.exports = app => app.component('chat-message-script', {
|
|
|
335
335
|
activeTab: 'code',
|
|
336
336
|
showDetailModal: false,
|
|
337
337
|
showCreateDashboardModal: false,
|
|
338
|
+
showDropdown: false,
|
|
338
339
|
newDashboardTitle: '',
|
|
339
340
|
dashboardCode: '',
|
|
340
341
|
createError: null,
|
|
@@ -377,6 +378,15 @@ module.exports = app => app.component('chat-message-script', {
|
|
|
377
378
|
});
|
|
378
379
|
});
|
|
379
380
|
},
|
|
381
|
+
toggleDropdown() {
|
|
382
|
+
this.showDropdown = !this.showDropdown;
|
|
383
|
+
},
|
|
384
|
+
handleBodyClick(event) {
|
|
385
|
+
const dropdown = this.$refs.dropdown;
|
|
386
|
+
if (dropdown && typeof dropdown.contains === 'function' && !dropdown.contains(event.target)) {
|
|
387
|
+
this.showDropdown = false;
|
|
388
|
+
}
|
|
389
|
+
},
|
|
380
390
|
async createDashboardFromScript() {
|
|
381
391
|
this.dashboardCode = this.dashboardEditor.getValue();
|
|
382
392
|
const { dashboard } = await api.Dashboard.createDashboard({
|
|
@@ -415,9 +425,15 @@ module.exports = app => app.component('chat-message-script', {
|
|
|
415
425
|
},
|
|
416
426
|
mounted() {
|
|
417
427
|
Prism.highlightElement(this.$refs.code);
|
|
428
|
+
this.$nextTick(() => {
|
|
429
|
+
document.body.addEventListener('click', this.handleBodyClick);
|
|
430
|
+
});
|
|
418
431
|
if (this.message.executionResult?.output) {
|
|
419
432
|
this.activeTab = 'output';
|
|
420
433
|
}
|
|
434
|
+
},
|
|
435
|
+
unmounted() {
|
|
436
|
+
document.body.removeEventListener('click', this.handleBodyClick);
|
|
421
437
|
}
|
|
422
438
|
});
|
|
423
439
|
|
|
@@ -838,9 +854,26 @@ const template = __webpack_require__(/*! ./dashboard-chart.html */ "./frontend/s
|
|
|
838
854
|
module.exports = app => app.component('dashboard-chart', {
|
|
839
855
|
template: template,
|
|
840
856
|
props: ['value', 'responsive'],
|
|
857
|
+
data: () => ({
|
|
858
|
+
chart: null
|
|
859
|
+
}),
|
|
841
860
|
mounted() {
|
|
842
861
|
const ctx = this.$refs.chart.getContext('2d');
|
|
843
|
-
|
|
862
|
+
this.chart = new Chart(ctx, this.value.$chart);
|
|
863
|
+
},
|
|
864
|
+
methods: {
|
|
865
|
+
exportPNG() {
|
|
866
|
+
if (this.chart == null) {
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
const dataUrl = this.chart.toBase64Image();
|
|
870
|
+
const anchor = document.createElement('a');
|
|
871
|
+
anchor.href = dataUrl;
|
|
872
|
+
anchor.download = 'chart.png';
|
|
873
|
+
document.body.appendChild(anchor);
|
|
874
|
+
anchor.click();
|
|
875
|
+
document.body.removeChild(anchor);
|
|
876
|
+
}
|
|
844
877
|
},
|
|
845
878
|
computed: {
|
|
846
879
|
header() {
|
|
@@ -2394,7 +2427,8 @@ module.exports = app => app.component('models', {
|
|
|
2394
2427
|
query: {},
|
|
2395
2428
|
scrollHeight: 0,
|
|
2396
2429
|
interval: null,
|
|
2397
|
-
outputType: 'table' // json, table
|
|
2430
|
+
outputType: 'table', // json, table
|
|
2431
|
+
hideSidebar: null
|
|
2398
2432
|
}),
|
|
2399
2433
|
created() {
|
|
2400
2434
|
this.currentModel = this.model;
|
|
@@ -2593,13 +2627,14 @@ module.exports = app => app.component('models', {
|
|
|
2593
2627
|
this.selectedPaths = [...this.schemaPaths];
|
|
2594
2628
|
},
|
|
2595
2629
|
async loadMoreDocuments() {
|
|
2596
|
-
const { docs } = await api.Model.getDocuments({
|
|
2630
|
+
const { docs, numDocs } = await api.Model.getDocuments({
|
|
2597
2631
|
model: this.currentModel,
|
|
2598
2632
|
filter: this.filter,
|
|
2599
2633
|
sort: this.sortBy,
|
|
2600
2634
|
limit
|
|
2601
2635
|
});
|
|
2602
2636
|
this.documents = docs;
|
|
2637
|
+
this.numDocuments = numDocs;
|
|
2603
2638
|
if (docs.length < limit) {
|
|
2604
2639
|
this.loadedAllDocs = true;
|
|
2605
2640
|
}
|
|
@@ -4029,7 +4064,7 @@ module.exports = "<button v-bind=\"attrsToBind\" :disabled=\"isDisabled\" @click
|
|
|
4029
4064
|
/***/ ((module) => {
|
|
4030
4065
|
|
|
4031
4066
|
"use strict";
|
|
4032
|
-
module.exports = "<div class=\"relative border rounded bg-gray-100 text-black text-sm overflow-hidden\">\n <div class=\"flex border-b pt-[1px] text-xs font-medium bg-gray-200\">\n <button\n class=\"px-3 py-1 border-r border-gray-300 hover:bg-green-300\"\n :class=\"{'bg-gray-300': activeTab === 'code', 'bg-green-300': activeTab === 'code'}\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-3 py-1 hover:bg-green-300\"\n :class=\"{'bg-green-300': activeTab === 'output'}\"\n @click=\"activeTab = 'output'\">\n Output\n </button>\n <div class=\"ml-auto mr-1 flex\">\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-500 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click=\"copyOutput\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-blue-500 text-white border-none rounded cursor-pointer hover:bg-blue-600 transition-colors flex items-center\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5\" />\n </svg>\n </button>\n <button\n class=\"px-2 py-1
|
|
4067
|
+
module.exports = "<div class=\"relative border rounded bg-gray-100 text-black text-sm overflow-hidden\">\n <div class=\"flex border-b pt-[1px] text-xs font-medium bg-gray-200\">\n <button\n class=\"px-3 py-1 border-r border-gray-300 hover:bg-green-300\"\n :class=\"{'bg-gray-300': activeTab === 'code', 'bg-green-300': activeTab === 'code'}\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-3 py-1 hover:bg-green-300\"\n :class=\"{'bg-green-300': activeTab === 'output'}\"\n @click=\"activeTab = 'output'\">\n Output\n </button>\n <div class=\"ml-auto mr-1 flex\">\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-500 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click=\"copyOutput\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-blue-500 text-white border-none rounded cursor-pointer hover:bg-blue-600 transition-colors flex items-center\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5\" />\n </svg>\n </button>\n <async-button\n class=\"px-2 py-1 text-xs bg-green-500 text-white border-none rounded cursor-pointer hover:bg-green-600 transition-colors disabled:bg-gray-400\"\n @click=\"executeScript(message, script)\">\n Execute\n </async-button>\n <div class=\"relative ml-1\" ref=\"dropdown\">\n <button\n @click.stop=\"toggleDropdown\"\n class=\"px-1 py-1 text-xs hover:bg-gray-300 rounded flex items-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n <div\n v-if=\"showDropdown\"\n class=\"absolute right-0 z-10 mt-1 w-64 origin-top-right rounded-md bg-white 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-gray-700 hover:bg-gray-100\"\n @click=\"openCreateDashboardModal(); showDropdown = false\">\n Create Dashboard\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <pre class=\"p-3 whitespace-pre-wrap max-h-[50vh] max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] overflow-y-auto\" v-show=\"activeTab === 'code'\"><code v-text=\"script\" ref=\"code\" :class=\"'language-' + language\"></code></pre>\n\n <div class=\"p-3 whitespace-pre-wrap max-h-[50vh] overflow-y-auto bg-white border-t max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] relative\" v-show=\"activeTab === 'output'\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" />\n <pre v-else>{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n\n <modal ref=\"outputModal\" v-if=\"showDetailModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\">×</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" :responsive=\"true\" />\n <pre v-else class=\"whitespace-pre-wrap\">{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n </template>\n </modal>\n <modal v-if=\"showCreateDashboardModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showCreateDashboardModal = false\">×</div>\n <div>\n <div class=\"mt-4 text-gray-900 font-semibold\">Create Dashboard</div>\n <div class=\"mt-4\">\n <label class=\"block text-sm font-medium leading-6 text-gray-900\">Title</label>\n <div class=\"mt-2\">\n <div class=\"w-full flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-teal-600\">\n <input type=\"text\" v-model=\"newDashboardTitle\" class=\"outline-none block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6\" placeholder=\"My Dashboard\">\n </div>\n </div>\n </div>\n <div class=\"my-4\">\n <label class=\"block text-sm font-medium leading-6 text-gray-900\">Code</label>\n <div class=\"border border-gray-200\">\n <textarea class=\"p-2 h-[300px] w-full\" ref=\"dashboardCodeEditor\"></textarea>\n </div>\n </div>\n <async-button\n @click=\"createDashboardFromScript\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Submit\n </async-button>\n <div v-if=\"createErrors.length > 0\" class=\"rounded-md bg-red-50 p-4 mt-1\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{createError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
4033
4068
|
|
|
4034
4069
|
/***/ }),
|
|
4035
4070
|
|
|
@@ -4117,7 +4152,7 @@ module.exports = "<div>\n <div class=\"mb-2\">\n <textarea class=\"border bo
|
|
|
4117
4152
|
/***/ ((module) => {
|
|
4118
4153
|
|
|
4119
4154
|
"use strict";
|
|
4120
|
-
module.exports = "<div :class=\"responsive ? 'h-full' : ''\">\n <div v-if=\"header\" class=\"border-b border-gray-100 px-2 pb-2 text-xl font-bold\"
|
|
4155
|
+
module.exports = "<div :class=\"responsive ? 'h-full' : ''\">\n <div v-if=\"header\" class=\"border-b border-gray-100 px-2 pb-2 flex items-center\">\n <div class=\"text-xl font-bold\">{{header}}</div>\n <button\n class=\"ml-auto px-2 py-1 text-xs bg-ultramarine-600 text-white border-none rounded cursor-pointer hover:bg-ultramarine-500 transition-colors\"\n @click=\"exportPNG\"\n title=\"Export PNG\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"height: 1.5em;\" viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M280-280h400v-80H280v80Zm200-120 160-160-56-56-64 62v-166h-80v166l-64-62-56 56 160 160Zm0 320q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z\"/></svg>\n </button>\n </div>\n <div v-else class=\"border-b border-gray-100 px-2 pb-2 text-right\">\n <button\n class=\"mt-1 px-2 py-1 text-xs bg-ultramarine-600 text-white border-none rounded cursor-pointer hover:bg-ultramarine-500 transition-colors\"\n @click=\"exportPNG\"\n title=\"Export PNG\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"height: 1.5em;\" viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M280-280h400v-80H280v80Zm200-120 160-160-56-56-64 62v-166h-80v166l-64-62-56 56 160 160Zm0 320q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z\"/></svg>\n </button>\n </div>\n <div class=\"text-xl\" :class=\"responsive ? 'h-full' : ''\">\n <canvas ref=\"chart\"></canvas>\n </div>\n</div>\n";
|
|
4121
4156
|
|
|
4122
4157
|
/***/ }),
|
|
4123
4158
|
|
|
@@ -4150,7 +4185,7 @@ module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b
|
|
|
4150
4185
|
/***/ ((module) => {
|
|
4151
4186
|
|
|
4152
4187
|
"use strict";
|
|
4153
|
-
module.exports = "<div>\n <div v-if=\"Array.isArray(result)\">\n <div v-for=\"el in result\">\n <component\n class=\"bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl\"\n :is=\"getComponentForValue(
|
|
4188
|
+
module.exports = "<div>\n <div v-if=\"Array.isArray(result)\">\n <div v-for=\"el in result\" :key=\"el._id || el.finishedEvaluatingAt\">\n <component\n class=\"bg-white 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-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl\"\n :is=\"getComponentForValue(result)\"\n :value=\"result\">\n </component>\n </div>\n <div class=\"text-right text-sm text-gray-700 mt-1\" v-if=\"finishedEvaluatingAt\">\n Last Evaluated: {{ format.isoToLongDateTime(finishedEvaluatingAt) }}\n </div>\n</div>\n";
|
|
4154
4189
|
|
|
4155
4190
|
/***/ }),
|
|
4156
4191
|
|
|
@@ -4172,7 +4207,7 @@ module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b
|
|
|
4172
4207
|
/***/ ((module) => {
|
|
4173
4208
|
|
|
4174
4209
|
"use strict";
|
|
4175
|
-
module.exports = "<div class=\"dashboard px-1\">\n <div v-if=\"status === 'loading'\" class=\"max-w-5xl mx-auto text-center\">\n <img src=\"images/loader.gif\" class=\"inline mt-10\">\n </div>\n <div v-if=\"dashboard && status !== 'loading'\" class=\"max-w-5xl mx-auto\">\n <div class=\"flex items-center w-full\" v-if=\"!showEditor\">\n <h2 class=\"mt-4 mb-4 text-gray-900 font-semibold text-xl grow shrink\">{{title}}</h2>\n <div class=\"flex gap-2\">\n <button\n @click=\"showEditor = true\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"flex items-center 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-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\">\n <img src=\"images/edit.svg\" class=\"inline h-[1.25em] mr-1\" /> Edit\n </button>\n\n <async-button\n @click=\"evaluateDashboard\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"flex items-center rounded-md bg-ultramarine-600 px-4 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-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\"\n >\n <svg class=\"inline h-[1.25em] mr-1\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"m670-140 160-100-160-100v200ZM240-600h480v-80H240v80ZM720-40q-83 0-141.5-58.5T520-240q0-83 58.5-141.5T720-440q83 0 141.5 58.5T920-240q0 83-58.5 141.5T720-40ZM120-80v-680q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v267q-19-9-39-15t-41-9v-243H200v562h243q5 31 15.5 59T486-86l-6 6-60-60-60 60-60-60-60 60-60-60-60 60Zm120-200h203q3-21 9-41t15-39H240v80Zm0-160h284q38-37 88.5-58.5T720-520H240v80Zm-40 242v-562 562Z\"/></svg>\n Evaluate\n </async-button>\n </div>\n </div>\n <div v-if=\"!showEditor\" class=\"mt-4 mb-4\">\n <div v-if=\"dashboardResults.length === 0\">\n <div class=\"flex flex-col items-center justify-center py-8\">\n <p class=\"text-gray-700 text-base mb-4\">This dashboard hasn't been evaluated yet.</p>\n <async-button\n @click=\"evaluateDashboard\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"rounded-md bg-ultramarine-600 px-4 py-2 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-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\"\n >\n Evaluate Dashboard\n </async-button>\n </div>\n </div>\n <div v-else>\n <dashboard-result
|
|
4210
|
+
module.exports = "<div class=\"dashboard px-1\">\n <div v-if=\"status === 'loading'\" class=\"max-w-5xl mx-auto text-center\">\n <img src=\"images/loader.gif\" class=\"inline mt-10\">\n </div>\n <div v-if=\"dashboard && status !== 'loading'\" class=\"max-w-5xl mx-auto\">\n <div class=\"flex items-center w-full\" v-if=\"!showEditor\">\n <h2 class=\"mt-4 mb-4 text-gray-900 font-semibold text-xl grow shrink\">{{title}}</h2>\n <div class=\"flex gap-2\">\n <button\n @click=\"showEditor = true\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"flex items-center 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-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\">\n <img src=\"images/edit.svg\" class=\"inline h-[1.25em] mr-1\" /> Edit\n </button>\n\n <async-button\n @click=\"evaluateDashboard\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"flex items-center rounded-md bg-ultramarine-600 px-4 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-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\"\n >\n <svg class=\"inline h-[1.25em] mr-1\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"m670-140 160-100-160-100v200ZM240-600h480v-80H240v80ZM720-40q-83 0-141.5-58.5T520-240q0-83 58.5-141.5T720-440q83 0 141.5 58.5T920-240q0 83-58.5 141.5T720-40ZM120-80v-680q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v267q-19-9-39-15t-41-9v-243H200v562h243q5 31 15.5 59T486-86l-6 6-60-60-60 60-60-60-60 60-60-60-60 60Zm120-200h203q3-21 9-41t15-39H240v80Zm0-160h284q38-37 88.5-58.5T720-520H240v80Zm-40 242v-562 562Z\"/></svg>\n Evaluate\n </async-button>\n </div>\n </div>\n <div v-if=\"!showEditor\" class=\"mt-4 mb-4\">\n <div v-if=\"dashboardResults.length === 0\">\n <div class=\"flex flex-col items-center justify-center py-8\">\n <p class=\"text-gray-700 text-base mb-4\">This dashboard hasn't been evaluated yet.</p>\n <async-button\n @click=\"evaluateDashboard\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"rounded-md bg-ultramarine-600 px-4 py-2 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-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\"\n >\n Evaluate Dashboard\n </async-button>\n </div>\n </div>\n <div v-else>\n <dashboard-result\n :key=\"dashboardResult.finishedEvaluatingAt\"\n :result=\"dashboardResult.result\"\n :finishedEvaluatingAt=\"dashboardResult.finishedEvaluatingAt\">\n </dashboard-result>\n </div>\n </div>\n <div v-if=\"showEditor\" class=\"mt-4\">\n <edit-dashboard\n :dashboardId=\"dashboard._id\"\n :code=\"code\"\n :currentDescription=\"description\"\n :currentTitle=\"title\"\n @close=\"showEditor=false;\"\n @update=\"updateCode\"></edit-dashboard>\n </div>\n <div v-if=\"errorMessage\" class=\"rounded-md bg-red-50 p-4 mt-4\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\" data-slot=\"icon\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">{{errorMessage}}</h3>\n </div>\n </div>\n </div>\n\n </div>\n <div v-if=\"!dashboard && status !== 'loading'\">\n No dashboard with the given id could be found.\n </div>\n</div>\n";
|
|
4176
4211
|
|
|
4177
4212
|
/***/ }),
|
|
4178
4213
|
|
|
@@ -4557,7 +4592,7 @@ module.exports = "<transition name=\"modal\">\n <div class=\"modal-mask\">\n
|
|
|
4557
4592
|
/***/ ((module) => {
|
|
4558
4593
|
|
|
4559
4594
|
"use strict";
|
|
4560
|
-
module.exports = ".models {\n position: relative;\n display: flex;\n flex-direction: row;\n min-height: calc(100% - 56px);\n}\n\n.models button.gray {\n color: black;\n background-color: #eee;\n}\n\n.models .model-selector {\n background-color: #eee;\n flex-grow: 0;\n padding: 15px;\n padding-top: 0px;\n}\n\n.models h1 {\n margin-top: 0px;\n}\n\n.models .documents {\n flex-grow: 1;\n overflow: scroll;\n max-height: calc(100vh - 56px);\n}\n\n.models .documents table {\n /* max-width: -moz-fit-content;\n max-width: fit-content; */\n width: 100%;\n table-layout: auto;\n font-size: small;\n padding: 0;\n margin-right: 1em;\n white-space: nowrap;\n z-index: -1;\n border-collapse: collapse;\n line-height: 1.5em;\n}\n\n.models .documents table th {\n position: sticky;\n top: 42px;\n background-color: white;\n z-index: 1;\n}\n\n.models .documents table th:after {\n content: \"\";\n position: absolute;\n left: 0;\n width: 100%;\n bottom: -1px;\n border-bottom: thin solid rgba(0, 0, 0, 0.12);\n}\n\n.models .documents table tr {\n color: black;\n border-spacing: 0px 0px;\n background-color: white;\n cursor: pointer;\n}\n\n.models .documents table tr:nth-child(even) {\n background-color: #f5f5f5;\n}\n\n.models .documents table tr:hover {\n background-color: #a7b9ff;\n}\n\n.models .documents table th,\ntd {\n border-bottom: thin solid rgba(0, 0, 0, 0.12);\n text-align: left;\n padding: 0 16px;\n height: 48px;\n}\n\n.models textarea {\n font-size: 1.2em;\n}\n\n.models .path-type {\n color: rgba(0, 0, 0, 0.36);\n font-size: 0.8em;\n}\n\n.models .documents-menu {\n position: fixed;\n background-color: white;\n z-index: 1;\n padding: 4px;\n display: flex;\n width: calc(100vw - 12rem);\n}\n\n.models .documents-menu .search-input {\n flex-grow: 1;\n align-items: center;\n}\n\n.models .search-input input {\n padding: 0.25em 0.5em;\n font-size: 1.1em;\n border: 1px solid #ddd;\n border-radius: 3px;\n width: calc(100% - 1em);\n}\n\n.models .sort-arrow {\n padding-left: 10px;\n padding-right: 10px;\n}\n\n.models .loader {\n width: 100%;\n text-align: center;\n}\n\n.models .loader img {\n height: 4em;\n}\n\n.models .documents .buttons {\n display: inline-flex;\n justify-content: space-around;\n align-items: center;\n}\n";
|
|
4595
|
+
module.exports = ".models {\n position: relative;\n display: flex;\n flex-direction: row;\n min-height: calc(100% - 56px);\n}\n\n.models button.gray {\n color: black;\n background-color: #eee;\n}\n\n.models .model-selector {\n background-color: #eee;\n flex-grow: 0;\n padding: 15px;\n padding-top: 0px;\n}\n\n.models h1 {\n margin-top: 0px;\n}\n\n.models .documents {\n flex-grow: 1;\n overflow: scroll;\n max-height: calc(100vh - 56px);\n}\n\n.models .documents table {\n /* max-width: -moz-fit-content;\n max-width: fit-content; */\n width: 100%;\n table-layout: auto;\n font-size: small;\n padding: 0;\n margin-right: 1em;\n white-space: nowrap;\n z-index: -1;\n border-collapse: collapse;\n line-height: 1.5em;\n}\n\n.models .documents table th {\n position: sticky;\n top: 42px;\n background-color: white;\n z-index: 1;\n}\n\n.models .documents table th:after {\n content: \"\";\n position: absolute;\n left: 0;\n width: 100%;\n bottom: -1px;\n border-bottom: thin solid rgba(0, 0, 0, 0.12);\n}\n\n.models .documents table tr {\n color: black;\n border-spacing: 0px 0px;\n background-color: white;\n cursor: pointer;\n}\n\n.models .documents table tr:nth-child(even) {\n background-color: #f5f5f5;\n}\n\n.models .documents table tr:hover {\n background-color: #a7b9ff;\n}\n\n.models .documents table th,\ntd {\n border-bottom: thin solid rgba(0, 0, 0, 0.12);\n text-align: left;\n padding: 0 16px;\n height: 48px;\n}\n\n.models textarea {\n font-size: 1.2em;\n}\n\n.models .path-type {\n color: rgba(0, 0, 0, 0.36);\n font-size: 0.8em;\n}\n\n.models .documents-menu {\n position: fixed;\n background-color: white;\n z-index: 1;\n padding: 4px;\n display: flex;\n width: 100vw;\n}\n\n@media (min-width: 1024px) {\n .models .documents-menu {\n width: calc(100vw - 12rem);\n }\n}\n\n.models .documents-menu .search-input {\n flex-grow: 1;\n align-items: center;\n}\n\n.models .search-input input {\n padding: 0.25em 0.5em;\n font-size: 1.1em;\n border: 1px solid #ddd;\n border-radius: 3px;\n width: calc(100% - 1em);\n}\n\n.models .sort-arrow {\n padding-left: 10px;\n padding-right: 10px;\n}\n\n.models .loader {\n width: 100%;\n text-align: center;\n}\n\n.models .loader img {\n height: 4em;\n}\n\n.models .documents .buttons {\n display: inline-flex;\n justify-content: space-around;\n align-items: center;\n}\n";
|
|
4561
4596
|
|
|
4562
4597
|
/***/ }),
|
|
4563
4598
|
|
|
@@ -4568,7 +4603,7 @@ module.exports = ".models {\n position: relative;\n display: flex;\n flex-dir
|
|
|
4568
4603
|
/***/ ((module) => {
|
|
4569
4604
|
|
|
4570
4605
|
"use strict";
|
|
4571
|
-
module.exports = "<div class=\"models\">\n <div>\n <div class=\"flex grow flex-col gap-y-5 overflow-auto border-r border-gray-200 bg-white px-2 h-[calc(100vh-55px)] w-48\">\n <div class=\"flex font-bold font-xl mt-4 pl-2\">\n Models\n </div>\n <nav class=\"flex flex-1 flex-col\">\n <ul role=\"list\" class=\"flex flex-1 flex-col gap-y-7\">\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 font-semibold text-gray-700\"\n :class=\"model === currentModel ? 'bg-ultramarine-100 font-bold' : 'hover:bg-ultramarine-100'\">\n {{model}}\n </router-link>\n </li>\n </ul>\n </li>\n </ul>\n </nav>\n </div>\n\n </div>\n <div class=\"documents\" ref=\"documentsList\">\n <div class=\"relative h-[42px]\">\n <div class=\"documents-menu\">\n <div class=\"flex flex-row items-center w-full gap-2\">\n <form @submit.prevent=\"search\" class=\"flex-grow m-0\">\n <input ref=\"searchInput\" class=\"w-full font-mono rounded-md p-1 border border-gray-300 outline-gray-300 text-lg focus:ring-1 focus:ring-ultramarine-200 focus:ring-offset-0 focus:outline-none\" type=\"text\" placeholder=\"Filter\" v-model=\"searchText\" @click=\"initFilter\" />\n </form>\n <div>\n <span v-if=\"status === 'loading'\">Loading ...</span>\n <span v-if=\"status === 'loaded'\">{{numDocuments === 1 ? numDocuments+ ' document' : numDocuments + ' documents'}}</span>\n </div>\n <button\n @click=\"shouldShowExportModal = true\"\n type=\"button\"\n v-show=\"!selectMultiple\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\">\n Export\n </button>\n <button\n @click=\"stagingSelect\"\n type=\"button\"\n :class=\"{ 'bg-ultramarine-500 ring-inset ring-2 ring-gray-300 hover:bg-ultramarine-600': selectMultiple }\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\"\n >\n 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 <button\n @click=\"openIndexModal\"\n type=\"button\"\n v-show=\"!selectMultiple\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\">\n Indexes\n </button>\n <button\n @click=\"shouldShowCreateModal = true;\"\n type=\"button\"\n v-show=\"!selectMultiple\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\">\n Create\n </button>\n <button\n @click=\"openFieldSelection\"\n type=\"button\"\n v-show=\"!selectMultiple\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\">\n Fields\n </button>\n <span class=\"isolate inline-flex rounded-md shadow-sm\">\n <button\n @click=\"outputType = '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=\"outputType = 'json'\"\n type=\"button\"\n class=\"relative -ml-px inline-flex items-center rounded-none rounded-r-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 === 'json' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/json.svg\">\n </button>\n </span>\n </div>\n </div>\n </div>\n <div class=\"documents-container relative\">\n <table v-if=\"outputType === 'table'\">\n <thead>\n <th v-for=\"path in filteredPaths\" @click=\"clickFilter(path.path)\" class=\"cursor-pointer\">\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)\" :key=\"document._id\">\n <td v-for=\"schemaPath in filteredPaths\" :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-if=\"outputType === 'json'\">\n <div v-for=\"document in documents\" @click=\"handleDocumentClick(document)\" :key=\"document._id\" :class=\"{ 'bg-blue-200': selectedDocuments.some(x => x._id.toString() === document._id.toString()) }\">\n <list-json :value=\"filterDocument(document)\">\n </list-json>\n </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 :filter=\"filter\"\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\">{{ index.name }}</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=\"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";
|
|
4606
|
+
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\" :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-4 font-bold text-lg\">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\">\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 font-semibold text-gray-700\"\n :class=\"model === currentModel ? 'bg-ultramarine-100 font-bold' : 'hover:bg-ultramarine-100'\">\n {{model}}\n </router-link>\n </li>\n </ul>\n </li>\n </ul>\n </nav>\n </aside>\n <div class=\"documents\" ref=\"documentsList\">\n <div class=\"relative h-[42px]\">\n <div class=\"documents-menu\">\n <div class=\"flex flex-row items-center w-full gap-2\">\n <form @submit.prevent=\"search\" class=\"flex-grow m-0\">\n <input ref=\"searchInput\" class=\"w-full font-mono rounded-md p-1 border border-gray-300 outline-gray-300 text-lg focus:ring-1 focus:ring-ultramarine-200 focus:ring-offset-0 focus:outline-none\" type=\"text\" placeholder=\"Filter\" v-model=\"searchText\" @click=\"initFilter\" />\n </form>\n <div>\n <span v-if=\"status === 'loading'\">Loading ...</span>\n <span v-if=\"status === 'loaded'\">{{numDocuments === 1 ? numDocuments+ ' document' : numDocuments + ' documents'}}</span>\n </div>\n <button\n @click=\"shouldShowExportModal = true\"\n type=\"button\"\n v-show=\"!selectMultiple\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\">\n Export\n </button>\n <button\n @click=\"stagingSelect\"\n type=\"button\"\n :class=\"{ 'bg-ultramarine-500 ring-inset ring-2 ring-gray-300 hover:bg-ultramarine-600': selectMultiple }\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\"\n >\n 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 <button\n @click=\"openIndexModal\"\n type=\"button\"\n v-show=\"!selectMultiple\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\">\n Indexes\n </button>\n <button\n @click=\"shouldShowCreateModal = true;\"\n type=\"button\"\n v-show=\"!selectMultiple\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\">\n Create\n </button>\n <button\n @click=\"openFieldSelection\"\n type=\"button\"\n v-show=\"!selectMultiple\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 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-ultramarine-600\">\n Fields\n </button>\n <span class=\"isolate inline-flex rounded-md shadow-sm\">\n <button\n @click=\"outputType = '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=\"outputType = 'json'\"\n type=\"button\"\n class=\"relative -ml-px inline-flex items-center rounded-none rounded-r-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 === 'json' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/json.svg\">\n </button>\n </span>\n </div>\n </div>\n </div>\n <div class=\"documents-container relative\">\n <table v-if=\"outputType === 'table'\">\n <thead>\n <th v-for=\"path in filteredPaths\" @click=\"clickFilter(path.path)\" class=\"cursor-pointer\">\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)\" :key=\"document._id\">\n <td v-for=\"schemaPath in filteredPaths\" :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-if=\"outputType === 'json'\">\n <div v-for=\"document in documents\" @click=\"handleDocumentClick(document)\" :key=\"document._id\" :class=\"{ 'bg-blue-200': selectedDocuments.some(x => x._id.toString() === document._id.toString()) }\">\n <list-json :value=\"filterDocument(document)\">\n </list-json>\n </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 :filter=\"filter\"\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\">{{ index.name }}</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=\"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";
|
|
4572
4607
|
|
|
4573
4608
|
/***/ }),
|
|
4574
4609
|
|
|
@@ -14686,7 +14721,7 @@ var bson = /*#__PURE__*/Object.freeze({
|
|
|
14686
14721
|
/***/ ((module) => {
|
|
14687
14722
|
|
|
14688
14723
|
"use strict";
|
|
14689
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.0.
|
|
14724
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.0.109","description":"A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.","homepage":"https://studio.mongoosejs.io/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"dependencies":{"archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"0.0.26","marked":"15.0.12","node-inspect-extracted":"3.x","tailwindcss":"3.4.0","vanillatoasts":"^1.6.0","vue":"3.x","webpack":"5.x"},"peerDependencies":{"bson":"^5.5.1 || 6.x","express":"4.x","mongoose":"7.x || 8.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongoose":"8.x"},"scripts":{"lint":"eslint .","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js"}}');
|
|
14690
14725
|
|
|
14691
14726
|
/***/ })
|
|
14692
14727
|
|
package/frontend/public/tw.css
CHANGED
|
@@ -882,10 +882,6 @@ video {
|
|
|
882
882
|
height: 42px;
|
|
883
883
|
}
|
|
884
884
|
|
|
885
|
-
.h-\[calc\(100vh-55px\)\] {
|
|
886
|
-
height: calc(100vh - 55px);
|
|
887
|
-
}
|
|
888
|
-
|
|
889
885
|
.h-full {
|
|
890
886
|
height: 100%;
|
|
891
887
|
}
|
|
@@ -902,6 +898,10 @@ video {
|
|
|
902
898
|
width: 0px !important;
|
|
903
899
|
}
|
|
904
900
|
|
|
901
|
+
.\!w-48 {
|
|
902
|
+
width: 12rem !important;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
905
|
.\!w-64 {
|
|
906
906
|
width: 16rem !important;
|
|
907
907
|
}
|
|
@@ -1120,10 +1120,6 @@ video {
|
|
|
1120
1120
|
column-gap: 1.5rem;
|
|
1121
1121
|
}
|
|
1122
1122
|
|
|
1123
|
-
.gap-y-5 {
|
|
1124
|
-
row-gap: 1.25rem;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
1123
|
.gap-y-7 {
|
|
1128
1124
|
row-gap: 1.75rem;
|
|
1129
1125
|
}
|
|
@@ -2356,6 +2352,10 @@ video {
|
|
|
2356
2352
|
margin-right: -2rem;
|
|
2357
2353
|
}
|
|
2358
2354
|
|
|
2355
|
+
.lg\:w-48 {
|
|
2356
|
+
width: 12rem;
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
2359
|
.lg\:w-64 {
|
|
2360
2360
|
width: 16rem;
|
|
2361
2361
|
}
|
|
@@ -29,16 +29,29 @@
|
|
|
29
29
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5" />
|
|
30
30
|
</svg>
|
|
31
31
|
</button>
|
|
32
|
-
<button
|
|
33
|
-
class="px-2 py-1 mr-1 text-xs bg-ultramarine-500 text-white border-none rounded cursor-pointer hover:bg-ultramarine-600 transition-colors flex items-center"
|
|
34
|
-
@click="openCreateDashboardModal">
|
|
35
|
-
Create Dashboard
|
|
36
|
-
</button>
|
|
37
32
|
<async-button
|
|
38
33
|
class="px-2 py-1 text-xs bg-green-500 text-white border-none rounded cursor-pointer hover:bg-green-600 transition-colors disabled:bg-gray-400"
|
|
39
34
|
@click="executeScript(message, script)">
|
|
40
35
|
Execute
|
|
41
36
|
</async-button>
|
|
37
|
+
<div class="relative ml-1" ref="dropdown">
|
|
38
|
+
<button
|
|
39
|
+
@click.stop="toggleDropdown"
|
|
40
|
+
class="px-1 py-1 text-xs hover:bg-gray-300 rounded flex items-center">
|
|
41
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
|
42
|
+
<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" />
|
|
43
|
+
</svg>
|
|
44
|
+
</button>
|
|
45
|
+
<div
|
|
46
|
+
v-if="showDropdown"
|
|
47
|
+
class="absolute right-0 z-10 mt-1 w-64 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5">
|
|
48
|
+
<button
|
|
49
|
+
class="block w-full text-left px-4 py-2 text-xs text-gray-700 hover:bg-gray-100"
|
|
50
|
+
@click="openCreateDashboardModal(); showDropdown = false">
|
|
51
|
+
Create Dashboard
|
|
52
|
+
</button>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
42
55
|
</div>
|
|
43
56
|
</div>
|
|
44
57
|
|
|
@@ -12,6 +12,7 @@ module.exports = app => app.component('chat-message-script', {
|
|
|
12
12
|
activeTab: 'code',
|
|
13
13
|
showDetailModal: false,
|
|
14
14
|
showCreateDashboardModal: false,
|
|
15
|
+
showDropdown: false,
|
|
15
16
|
newDashboardTitle: '',
|
|
16
17
|
dashboardCode: '',
|
|
17
18
|
createError: null,
|
|
@@ -54,6 +55,15 @@ module.exports = app => app.component('chat-message-script', {
|
|
|
54
55
|
});
|
|
55
56
|
});
|
|
56
57
|
},
|
|
58
|
+
toggleDropdown() {
|
|
59
|
+
this.showDropdown = !this.showDropdown;
|
|
60
|
+
},
|
|
61
|
+
handleBodyClick(event) {
|
|
62
|
+
const dropdown = this.$refs.dropdown;
|
|
63
|
+
if (dropdown && typeof dropdown.contains === 'function' && !dropdown.contains(event.target)) {
|
|
64
|
+
this.showDropdown = false;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
57
67
|
async createDashboardFromScript() {
|
|
58
68
|
this.dashboardCode = this.dashboardEditor.getValue();
|
|
59
69
|
const { dashboard } = await api.Dashboard.createDashboard({
|
|
@@ -92,8 +102,14 @@ module.exports = app => app.component('chat-message-script', {
|
|
|
92
102
|
},
|
|
93
103
|
mounted() {
|
|
94
104
|
Prism.highlightElement(this.$refs.code);
|
|
105
|
+
this.$nextTick(() => {
|
|
106
|
+
document.body.addEventListener('click', this.handleBodyClick);
|
|
107
|
+
});
|
|
95
108
|
if (this.message.executionResult?.output) {
|
|
96
109
|
this.activeTab = 'output';
|
|
97
110
|
}
|
|
111
|
+
},
|
|
112
|
+
unmounted() {
|
|
113
|
+
document.body.removeEventListener('click', this.handleBodyClick);
|
|
98
114
|
}
|
|
99
115
|
});
|
|
@@ -40,7 +40,11 @@
|
|
|
40
40
|
</div>
|
|
41
41
|
</div>
|
|
42
42
|
<div v-else>
|
|
43
|
-
<dashboard-result
|
|
43
|
+
<dashboard-result
|
|
44
|
+
:key="dashboardResult.finishedEvaluatingAt"
|
|
45
|
+
:result="dashboardResult.result"
|
|
46
|
+
:finishedEvaluatingAt="dashboardResult.finishedEvaluatingAt">
|
|
47
|
+
</dashboard-result>
|
|
44
48
|
</div>
|
|
45
49
|
</div>
|
|
46
50
|
<div v-if="showEditor" class="mt-4">
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
<div :class="responsive ? 'h-full' : ''">
|
|
2
|
-
<div v-if="header" class="border-b border-gray-100 px-2 pb-2
|
|
3
|
-
{{header}}
|
|
2
|
+
<div v-if="header" class="border-b border-gray-100 px-2 pb-2 flex items-center">
|
|
3
|
+
<div class="text-xl font-bold">{{header}}</div>
|
|
4
|
+
<button
|
|
5
|
+
class="ml-auto px-2 py-1 text-xs bg-ultramarine-600 text-white border-none rounded cursor-pointer hover:bg-ultramarine-500 transition-colors"
|
|
6
|
+
@click="exportPNG"
|
|
7
|
+
title="Export PNG">
|
|
8
|
+
<svg xmlns="http://www.w3.org/2000/svg" style="height: 1.5em;" viewBox="0 -960 960 960" fill="currentColor"><path d="M280-280h400v-80H280v80Zm200-120 160-160-56-56-64 62v-166h-80v166l-64-62-56 56 160 160Zm0 320q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
|
9
|
+
</button>
|
|
10
|
+
</div>
|
|
11
|
+
<div v-else class="border-b border-gray-100 px-2 pb-2 text-right">
|
|
12
|
+
<button
|
|
13
|
+
class="mt-1 px-2 py-1 text-xs bg-ultramarine-600 text-white border-none rounded cursor-pointer hover:bg-ultramarine-500 transition-colors"
|
|
14
|
+
@click="exportPNG"
|
|
15
|
+
title="Export PNG">
|
|
16
|
+
<svg xmlns="http://www.w3.org/2000/svg" style="height: 1.5em;" viewBox="0 -960 960 960" fill="currentColor"><path d="M280-280h400v-80H280v80Zm200-120 160-160-56-56-64 62v-166h-80v166l-64-62-56 56 160 160Zm0 320q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
|
17
|
+
</button>
|
|
4
18
|
</div>
|
|
5
19
|
<div class="text-xl" :class="responsive ? 'h-full' : ''">
|
|
6
20
|
<canvas ref="chart"></canvas>
|
|
@@ -5,9 +5,26 @@ const template = require('./dashboard-chart.html');
|
|
|
5
5
|
module.exports = app => app.component('dashboard-chart', {
|
|
6
6
|
template: template,
|
|
7
7
|
props: ['value', 'responsive'],
|
|
8
|
+
data: () => ({
|
|
9
|
+
chart: null
|
|
10
|
+
}),
|
|
8
11
|
mounted() {
|
|
9
12
|
const ctx = this.$refs.chart.getContext('2d');
|
|
10
|
-
|
|
13
|
+
this.chart = new Chart(ctx, this.value.$chart);
|
|
14
|
+
},
|
|
15
|
+
methods: {
|
|
16
|
+
exportPNG() {
|
|
17
|
+
if (this.chart == null) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const dataUrl = this.chart.toBase64Image();
|
|
21
|
+
const anchor = document.createElement('a');
|
|
22
|
+
anchor.href = dataUrl;
|
|
23
|
+
anchor.download = 'chart.png';
|
|
24
|
+
document.body.appendChild(anchor);
|
|
25
|
+
anchor.click();
|
|
26
|
+
document.body.removeChild(anchor);
|
|
27
|
+
}
|
|
11
28
|
},
|
|
12
29
|
computed: {
|
|
13
30
|
header() {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<div>
|
|
2
2
|
<div v-if="Array.isArray(result)">
|
|
3
|
-
<div v-for="el in result">
|
|
3
|
+
<div v-for="el in result" :key="el._id || el.finishedEvaluatingAt">
|
|
4
4
|
<component
|
|
5
5
|
class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl"
|
|
6
|
-
:is="getComponentForValue(
|
|
7
|
-
:value="
|
|
6
|
+
:is="getComponentForValue(el)"
|
|
7
|
+
:value="el">
|
|
8
8
|
</component>
|
|
9
9
|
</div>
|
|
10
10
|
</div>
|
|
@@ -95,7 +95,13 @@ td {
|
|
|
95
95
|
z-index: 1;
|
|
96
96
|
padding: 4px;
|
|
97
97
|
display: flex;
|
|
98
|
-
width:
|
|
98
|
+
width: 100vw;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@media (min-width: 1024px) {
|
|
102
|
+
.models .documents-menu {
|
|
103
|
+
width: calc(100vw - 12rem);
|
|
104
|
+
}
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
.models .documents-menu .search-input {
|
|
@@ -1,28 +1,35 @@
|
|
|
1
|
-
<div class="models">
|
|
2
|
-
<div>
|
|
3
|
-
<
|
|
4
|
-
<div class="flex font-bold font-xl mt-4 pl-2">
|
|
5
|
-
Models
|
|
6
|
-
</div>
|
|
7
|
-
<nav class="flex flex-1 flex-col">
|
|
8
|
-
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
|
9
|
-
<li>
|
|
10
|
-
<ul role="list">
|
|
11
|
-
<li v-for="model in models">
|
|
12
|
-
<router-link
|
|
13
|
-
:to="'/model/' + model"
|
|
14
|
-
class="block truncate rounded-md py-2 pr-2 pl-2 text-sm font-semibold text-gray-700"
|
|
15
|
-
:class="model === currentModel ? 'bg-ultramarine-100 font-bold' : 'hover:bg-ultramarine-100'">
|
|
16
|
-
{{model}}
|
|
17
|
-
</router-link>
|
|
18
|
-
</li>
|
|
19
|
-
</ul>
|
|
20
|
-
</li>
|
|
21
|
-
</ul>
|
|
22
|
-
</nav>
|
|
23
|
-
</div>
|
|
24
|
-
|
|
1
|
+
<div class="models flex" style="height: calc(100vh - 55px); height: calc(100dvh - 55px)">
|
|
2
|
+
<div class="fixed top-[65px] cursor-pointer bg-gray-100 rounded-r-md z-10" @click="hideSidebar = false">
|
|
3
|
+
<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>
|
|
25
4
|
</div>
|
|
5
|
+
<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" :class="hideSidebar === true ? '!w-0' : hideSidebar === false ? '!w-48' : ''">
|
|
6
|
+
<div class="flex items-center border-b border-gray-100 w-48 overflow-x-hidden">
|
|
7
|
+
<div class="p-4 font-bold text-lg">Models</div>
|
|
8
|
+
<button
|
|
9
|
+
@click="hideSidebar = true"
|
|
10
|
+
class="ml-auto mr-2 p-2 rounded hover:bg-gray-200 focus:outline-none"
|
|
11
|
+
aria-label="Close sidebar"
|
|
12
|
+
>
|
|
13
|
+
<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>
|
|
14
|
+
</button>
|
|
15
|
+
</div>
|
|
16
|
+
<nav class="flex flex-1 flex-col">
|
|
17
|
+
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
|
18
|
+
<li>
|
|
19
|
+
<ul role="list">
|
|
20
|
+
<li v-for="model in models">
|
|
21
|
+
<router-link
|
|
22
|
+
:to="'/model/' + model"
|
|
23
|
+
class="block truncate rounded-md py-2 pr-2 pl-2 text-sm font-semibold text-gray-700"
|
|
24
|
+
:class="model === currentModel ? 'bg-ultramarine-100 font-bold' : 'hover:bg-ultramarine-100'">
|
|
25
|
+
{{model}}
|
|
26
|
+
</router-link>
|
|
27
|
+
</li>
|
|
28
|
+
</ul>
|
|
29
|
+
</li>
|
|
30
|
+
</ul>
|
|
31
|
+
</nav>
|
|
32
|
+
</aside>
|
|
26
33
|
<div class="documents" ref="documentsList">
|
|
27
34
|
<div class="relative h-[42px]">
|
|
28
35
|
<div class="documents-menu">
|
|
@@ -52,7 +52,8 @@ module.exports = app => app.component('models', {
|
|
|
52
52
|
query: {},
|
|
53
53
|
scrollHeight: 0,
|
|
54
54
|
interval: null,
|
|
55
|
-
outputType: 'table' // json, table
|
|
55
|
+
outputType: 'table', // json, table
|
|
56
|
+
hideSidebar: null
|
|
56
57
|
}),
|
|
57
58
|
created() {
|
|
58
59
|
this.currentModel = this.model;
|
|
@@ -251,13 +252,14 @@ module.exports = app => app.component('models', {
|
|
|
251
252
|
this.selectedPaths = [...this.schemaPaths];
|
|
252
253
|
},
|
|
253
254
|
async loadMoreDocuments() {
|
|
254
|
-
const { docs } = await api.Model.getDocuments({
|
|
255
|
+
const { docs, numDocs } = await api.Model.getDocuments({
|
|
255
256
|
model: this.currentModel,
|
|
256
257
|
filter: this.filter,
|
|
257
258
|
sort: this.sortBy,
|
|
258
259
|
limit
|
|
259
260
|
});
|
|
260
261
|
this.documents = docs;
|
|
262
|
+
this.numDocuments = numDocs;
|
|
261
263
|
if (docs.length < limit) {
|
|
262
264
|
this.loadedAllDocs = true;
|
|
263
265
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mongoosejs/studio",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.109",
|
|
4
4
|
"description": "A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.",
|
|
5
5
|
"homepage": "https://studio.mongoosejs.io/",
|
|
6
6
|
"repository": {
|