@mongoosejs/studio 0.0.31 → 0.0.33
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/Dashboard/getDashboard.js +2 -20
- package/backend/actions/Dashboard/updateDashboard.js +26 -19
- package/backend/db/dashboardSchema.js +27 -1
- package/frontend/public/app.js +105 -17
- package/frontend/src/dashboard/dashboard.html +5 -1
- package/frontend/src/dashboard/dashboard.js +4 -3
- package/frontend/src/dashboard/edit-dashboard/edit-dashboard.js +5 -2
- package/frontend/src/dashboard-result/dashboard-result.js +0 -1
- package/frontend/src/dashboards/dashboards.html +2 -2
- package/frontend/src/document/document.html +2 -1
- package/frontend/src/document-details/document-details.js +4 -1
- package/frontend/src/edit-array/edit-array.html +3 -1
- package/frontend/src/edit-array/edit-array.js +12 -3
- package/frontend/src/edit-subdocument/edit-subdocument.html +6 -0
- package/frontend/src/edit-subdocument/edit-subdocument.js +50 -0
- package/frontend/src/index.js +1 -0
- package/frontend/src/routes.js +1 -1
- package/package.json +1 -1
|
@@ -19,23 +19,9 @@ module.exports = ({ db }) => async function getDashboard(params) {
|
|
|
19
19
|
|
|
20
20
|
const dashboard = await Dashboard.findOne({ _id: dashboardId });
|
|
21
21
|
if (evaluate) {
|
|
22
|
-
const context = vm.createContext({ db });
|
|
23
22
|
let result = null;
|
|
24
23
|
try {
|
|
25
|
-
result = await
|
|
26
|
-
if (result.$document?.model) {
|
|
27
|
-
let schemaPaths = {};
|
|
28
|
-
const Model = Dashboard.db.model(result.$document?.model);
|
|
29
|
-
for (const path of Object.keys(Model.schema.paths)) {
|
|
30
|
-
schemaPaths[path] = {
|
|
31
|
-
instance: Model.schema.paths[path].instance,
|
|
32
|
-
path,
|
|
33
|
-
ref: Model.schema.paths[path].options?.ref,
|
|
34
|
-
required: Model.schema.paths[path].options?.required
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
result.$document.schemaPaths = schemaPaths;
|
|
38
|
-
}
|
|
24
|
+
result = await dashboard.evaluate();
|
|
39
25
|
} catch (error) {
|
|
40
26
|
return { dashboard, error: { message: error.message } };
|
|
41
27
|
}
|
|
@@ -44,8 +30,4 @@ module.exports = ({ db }) => async function getDashboard(params) {
|
|
|
44
30
|
}
|
|
45
31
|
|
|
46
32
|
return { dashboard };
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const formatFunction = code => `(async function() {
|
|
50
|
-
${code}
|
|
51
|
-
})();`
|
|
33
|
+
};
|
|
@@ -3,23 +3,30 @@
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
|
|
5
5
|
const UpdateDashboardParams = new Archetype({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
dashboardId: {
|
|
7
|
+
$type: 'string',
|
|
8
|
+
$required: true
|
|
9
|
+
},
|
|
10
|
+
code: {
|
|
11
|
+
$type: 'string',
|
|
12
|
+
$required: true
|
|
13
|
+
}
|
|
14
|
+
}).compile('UpdateDashboardParams');
|
|
15
|
+
|
|
16
|
+
module.exports = ({ db }) => async function updateDashboard(params) {
|
|
17
|
+
const { dashboardId, code } = new UpdateDashboardParams(params);
|
|
18
|
+
|
|
19
|
+
const Dashboard = db.models[`__Studio_Dashboard`];
|
|
20
|
+
|
|
21
|
+
const doc = await Dashboard.
|
|
22
|
+
findByIdAndUpdate(dashboardId, { code }, { sanitizeFilter: true, returnDocument: 'after', overwriteImmutable: true });
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
let result = null;
|
|
25
|
+
try {
|
|
26
|
+
result = await doc.evaluate();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return { doc, error: { message: error.message } };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { doc, result };
|
|
32
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const mongoose = require('mongoose');
|
|
4
|
+
const vm = require('vm');
|
|
4
5
|
|
|
5
6
|
const dashboardSchema = new mongoose.Schema({
|
|
6
7
|
title: {
|
|
@@ -16,4 +17,29 @@ const dashboardSchema = new mongoose.Schema({
|
|
|
16
17
|
}
|
|
17
18
|
});
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
dashboardSchema.methods.evaluate = async function evaluate() {
|
|
21
|
+
const context = vm.createContext({ db: this.constructor.db });
|
|
22
|
+
let result = null;
|
|
23
|
+
result = await vm.runInContext(formatFunction(this.code), context);
|
|
24
|
+
if (result.$document?.model) {
|
|
25
|
+
let schemaPaths = {};
|
|
26
|
+
const Model = this.constructor.db.model(result.$document?.model);
|
|
27
|
+
for (const path of Object.keys(Model.schema.paths)) {
|
|
28
|
+
schemaPaths[path] = {
|
|
29
|
+
instance: Model.schema.paths[path].instance,
|
|
30
|
+
path,
|
|
31
|
+
ref: Model.schema.paths[path].options?.ref,
|
|
32
|
+
required: Model.schema.paths[path].options?.required
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
result.$document.schemaPaths = schemaPaths;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
module.exports = dashboardSchema;
|
|
42
|
+
|
|
43
|
+
const formatFunction = code => `(async function() {
|
|
44
|
+
${code}
|
|
45
|
+
})();`
|
package/frontend/public/app.js
CHANGED
|
@@ -34,10 +34,10 @@ if (false) {} else {
|
|
|
34
34
|
return client.post('/Dashboard/createDashboard', params).then(res => res.data);
|
|
35
35
|
},
|
|
36
36
|
getDashboard: function getDashboard(params) {
|
|
37
|
-
return client.
|
|
37
|
+
return client.put('/Dashboard/getDashboard', params).then(res => res.data);
|
|
38
38
|
},
|
|
39
39
|
getDashboards: function getDashboards(params) {
|
|
40
|
-
return client.
|
|
40
|
+
return client.put('/Dashboard/getDashboards', params).then(res => res.data);
|
|
41
41
|
},
|
|
42
42
|
updateDashboard: function updateDashboard(params) {
|
|
43
43
|
return client.post('/Dashboard/updateDashboard', params).then(res => res.data);
|
|
@@ -429,7 +429,6 @@ module.exports = app => app.component('dashboard-result', {
|
|
|
429
429
|
},
|
|
430
430
|
methods: {
|
|
431
431
|
getComponentForValue(value) {
|
|
432
|
-
console.log('X', value);
|
|
433
432
|
if (typeof value !== 'object' || value == null) {
|
|
434
433
|
return 'dashboard-primitive';
|
|
435
434
|
}
|
|
@@ -463,6 +462,7 @@ const template = __webpack_require__(/*! ./dashboard.html */ "./frontend/src/das
|
|
|
463
462
|
|
|
464
463
|
module.exports = app => app.component('dashboard', {
|
|
465
464
|
template: template,
|
|
465
|
+
props: ['dashboardId'],
|
|
466
466
|
data: function() {
|
|
467
467
|
return {
|
|
468
468
|
status: 'loading',
|
|
@@ -477,12 +477,12 @@ module.exports = app => app.component('dashboard', {
|
|
|
477
477
|
this.showEditor = !this.showEditor;
|
|
478
478
|
},
|
|
479
479
|
async updateCode(update) {
|
|
480
|
-
this.code = update;
|
|
480
|
+
this.code = update.doc.code;
|
|
481
|
+
this.result = update.result;
|
|
481
482
|
}
|
|
482
483
|
},
|
|
483
484
|
mounted: async function() {
|
|
484
|
-
const
|
|
485
|
-
const { dashboard, result } = await api.Dashboard.getDashboard({ params: { dashboardId: dashboardId, evaluate: true } });
|
|
485
|
+
const { dashboard, result } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
|
|
486
486
|
if (!dashboard) {
|
|
487
487
|
return;
|
|
488
488
|
}
|
|
@@ -522,8 +522,11 @@ module.exports = app => app.component('edit-dashboard', {
|
|
|
522
522
|
this.$emit('close')
|
|
523
523
|
},
|
|
524
524
|
async updateCode() {
|
|
525
|
-
const { doc } = await api.Dashboard.updateDashboard({
|
|
526
|
-
|
|
525
|
+
const { doc, result } = await api.Dashboard.updateDashboard({
|
|
526
|
+
dashboardId: this.dashboardId,
|
|
527
|
+
code: this.editor.getValue()
|
|
528
|
+
});
|
|
529
|
+
this.$emit('update', { doc, result });
|
|
527
530
|
this.editor.setValue(doc.code);
|
|
528
531
|
this.closeEditor();
|
|
529
532
|
}
|
|
@@ -662,7 +665,7 @@ appendCSS(__webpack_require__(/*! ./document-details.css */ "./frontend/src/docu
|
|
|
662
665
|
|
|
663
666
|
module.exports = app => app.component('document-details', {
|
|
664
667
|
template,
|
|
665
|
-
props: ['document', 'schemaPaths', 'editting', 'changes'],
|
|
668
|
+
props: ['document', 'schemaPaths', 'editting', 'changes', 'invalid'],
|
|
666
669
|
methods: {
|
|
667
670
|
getComponentForPath(schemaPath) {
|
|
668
671
|
if (schemaPath.instance === 'Array') {
|
|
@@ -680,6 +683,9 @@ module.exports = app => app.component('document-details', {
|
|
|
680
683
|
if (path.instance === 'Array') {
|
|
681
684
|
return 'edit-array';
|
|
682
685
|
}
|
|
686
|
+
if (path.instance === 'Embedded') {
|
|
687
|
+
return 'edit-subdocument';
|
|
688
|
+
}
|
|
683
689
|
return 'edit-default';
|
|
684
690
|
},
|
|
685
691
|
getValueForPath(path) {
|
|
@@ -860,19 +866,28 @@ appendCSS(__webpack_require__(/*! ./edit-array.css */ "./frontend/src/edit-array
|
|
|
860
866
|
module.exports = app => app.component('edit-array', {
|
|
861
867
|
template: template,
|
|
862
868
|
props: ['value'],
|
|
863
|
-
data: () => ({ currentValue: null }),
|
|
869
|
+
data: () => ({ currentValue: null, status: 'init' }),
|
|
864
870
|
mounted() {
|
|
865
|
-
this.currentValue =
|
|
871
|
+
this.currentValue = this.value == null
|
|
872
|
+
? '' + this.value
|
|
873
|
+
: JSON.stringify(this.value, null, ' ').trim();
|
|
866
874
|
this.$refs.arrayEditor.value = this.currentValue;
|
|
867
875
|
this.editor = CodeMirror.fromTextArea(this.$refs.arrayEditor, {
|
|
868
876
|
mode: 'javascript',
|
|
869
877
|
lineNumbers: true
|
|
870
878
|
});
|
|
879
|
+
this.editor.on('change', ev => {
|
|
880
|
+
this.currentValue = this.editor.getValue();
|
|
881
|
+
});
|
|
882
|
+
this.status = 'loaded';
|
|
871
883
|
},
|
|
872
884
|
watch: {
|
|
873
885
|
currentValue() {
|
|
886
|
+
if (this.status === 'init') {
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
874
889
|
try {
|
|
875
|
-
this.$emit('input', eval(this.currentValue));
|
|
890
|
+
this.$emit('input', eval(`(${this.currentValue})`));
|
|
876
891
|
} catch (err) {
|
|
877
892
|
this.$emit('error', err);
|
|
878
893
|
}
|
|
@@ -998,6 +1013,67 @@ module.exports = app => app.component('edit-number', {
|
|
|
998
1013
|
}
|
|
999
1014
|
});
|
|
1000
1015
|
|
|
1016
|
+
/***/ }),
|
|
1017
|
+
|
|
1018
|
+
/***/ "./frontend/src/edit-subdocument/edit-subdocument.js":
|
|
1019
|
+
/*!***********************************************************!*\
|
|
1020
|
+
!*** ./frontend/src/edit-subdocument/edit-subdocument.js ***!
|
|
1021
|
+
\***********************************************************/
|
|
1022
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
1023
|
+
|
|
1024
|
+
"use strict";
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
const template = __webpack_require__(/*! ./edit-subdocument.html */ "./frontend/src/edit-subdocument/edit-subdocument.html");
|
|
1028
|
+
|
|
1029
|
+
const { BSON, EJSON } = __webpack_require__(/*! bson */ "./node_modules/bson/lib/bson.cjs");
|
|
1030
|
+
|
|
1031
|
+
const ObjectId = new Proxy(BSON.ObjectId, {
|
|
1032
|
+
apply (target, thisArg, argumentsList) {
|
|
1033
|
+
return new target(...argumentsList);
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
module.exports = app => app.component('edit-subdocument', {
|
|
1038
|
+
template: template,
|
|
1039
|
+
props: ['value'],
|
|
1040
|
+
data: () => ({ currentValue: null, status: 'init' }),
|
|
1041
|
+
mounted() {
|
|
1042
|
+
this.currentValue = this.value == null
|
|
1043
|
+
? '' + this.value
|
|
1044
|
+
: JSON.stringify(this.value, null, ' ').trim();
|
|
1045
|
+
this.$refs.editor.value = this.currentValue;
|
|
1046
|
+
this.editor = CodeMirror.fromTextArea(this.$refs.editor, {
|
|
1047
|
+
mode: 'javascript',
|
|
1048
|
+
lineNumbers: true
|
|
1049
|
+
});
|
|
1050
|
+
this.editor.on('change', ev => {
|
|
1051
|
+
this.currentValue = this.editor.getValue();
|
|
1052
|
+
});
|
|
1053
|
+
this.status = 'loaded';
|
|
1054
|
+
},
|
|
1055
|
+
watch: {
|
|
1056
|
+
currentValue() {
|
|
1057
|
+
if (this.status === 'init') {
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
this.$emit('input', eval(`(${this.currentValue})`));
|
|
1062
|
+
} catch (err) {
|
|
1063
|
+
console.log('Error', err);
|
|
1064
|
+
this.$emit('error', err);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
},
|
|
1068
|
+
beforeDestroy() {
|
|
1069
|
+
if (this.editor) {
|
|
1070
|
+
this.editor.toTextArea();
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
emits: ['input', 'error']
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
|
|
1001
1077
|
/***/ }),
|
|
1002
1078
|
|
|
1003
1079
|
/***/ "./frontend/src/export-query-results/export-query-results.js":
|
|
@@ -1703,7 +1779,7 @@ module.exports = [
|
|
|
1703
1779
|
component: 'dashboards'
|
|
1704
1780
|
},
|
|
1705
1781
|
{
|
|
1706
|
-
path: '/dashboard',
|
|
1782
|
+
path: '/dashboard/:dashboardId',
|
|
1707
1783
|
name: 'dashboard',
|
|
1708
1784
|
component: 'dashboard'
|
|
1709
1785
|
}
|
|
@@ -2428,7 +2504,7 @@ module.exports = "<div>\n <div v-if=\"Array.isArray(result)\">\n <div v-for=
|
|
|
2428
2504
|
/***/ ((module) => {
|
|
2429
2505
|
|
|
2430
2506
|
"use strict";
|
|
2431
|
-
module.exports = "<div class=\"dashboard px-1\">\n <div v-if=\"dashboard\" class=\"max-w-5xl mx-auto\">\n <div class=\"flex items-center w-full\">\n <h2 class=\"mt-4 mb-4 text-gray-900 font-semibold text-xl grow shrink\">{{dashboard.title}}</h2>\n <div>\n <button\n v-if=\"!showEditor\"\n @click=\"showEditor = true\"\n type=\"button\"\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 <img src=\"images/edit.svg\" class=\"inline h-[1em]\" /> Edit\n </button>\n </div>\n </div>\n <div v-if=\"!showEditor\" class=\"mt-4 mb-4\">\n <dashboard-result :result=\"result\"></dashboard-result>\n </div>\n <div v-if=\"showEditor\">\n <edit-dashboard
|
|
2507
|
+
module.exports = "<div class=\"dashboard px-1\">\n <div v-if=\"dashboard\" class=\"max-w-5xl mx-auto\">\n <div class=\"flex items-center w-full\">\n <h2 class=\"mt-4 mb-4 text-gray-900 font-semibold text-xl grow shrink\">{{dashboard.title}}</h2>\n <div>\n <button\n v-if=\"!showEditor\"\n @click=\"showEditor = true\"\n type=\"button\"\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 <img src=\"images/edit.svg\" class=\"inline h-[1em]\" /> Edit\n </button>\n </div>\n </div>\n <div v-if=\"!showEditor\" class=\"mt-4 mb-4\">\n <dashboard-result :result=\"result\"></dashboard-result>\n </div>\n <div v-if=\"showEditor\">\n <edit-dashboard\n :dashboardId=\"dashboard._id\"\n :code=\"code\"\n @close=\"showEditor=false;\"\n @update=\"updateCode\"></edit-dashboard>\n </div>\n \n </div>\n <div v-if=\"!dashboard && status === 'loaded'\">\n No dashboard with the given id could be found.\n </div>\n</div>";
|
|
2432
2508
|
|
|
2433
2509
|
/***/ }),
|
|
2434
2510
|
|
|
@@ -2450,7 +2526,7 @@ module.exports = "<div>\n <textarea ref=\"codeEditor\">{{code}}</textarea>\n
|
|
|
2450
2526
|
/***/ ((module) => {
|
|
2451
2527
|
|
|
2452
2528
|
"use strict";
|
|
2453
|
-
module.exports = "<div class=\"dashboards max-w-5xl mx-auto mt-8\">\n <div v-if=\"status === 'loaded' && dashboards.length === 0\">\n <div class=\"text-center\">\n <h3 class=\"mt-2 text-sm font-semibold text-gray-900\">No dashboards yet</h3>\n <p class=\"mt-1 text-sm text-gray-500\">Get started by creating a new dashboard.</p>\n <div class=\"mt-6\">\n <button type=\"button\" class=\"inline-flex items-center rounded-md bg-teal-600 px-3 py-2 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 <svg class=\"-ml-0.5 mr-1.5 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z\" />\n </svg>\n New Dashboard\n </button>\n </div>\n </div>\n </div>\n\n\n <div class=\"px-4 sm:px-6 lg:px-8\">\n <div class=\"sm:flex sm:items-center\">\n <div class=\"sm:flex-auto\">\n <h1 class=\"text-base font-semibold leading-6 text-gray-900\">Dashboards</h1>\n </div>\n <div class=\"mt-4 sm:ml-16 sm:mt-0 sm:flex-none\">\n <button\n type=\"button\"\n @click=\"showCreateDashboardModal = true\"\n class=\"block rounded-md bg-teal-600 px-3 py-2 text-center 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\">Create New Dashboard</button>\n </div>\n </div>\n <div class=\"mt-8 flow-root\">\n <div class=\"-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8\">\n <div class=\"inline-block min-w-full py-2 align-middle\">\n <table class=\"min-w-full divide-y divide-gray-300\">\n <thead>\n <tr>\n <th scope=\"col\" class=\"py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg:pl-8\">Title</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900 w-[50%]\">Description</th>\n <th scope=\"col\" class=\"relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8\">\n </th>\n <th scope=\"col\" class=\"relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8\">\n </th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-200 bg-white\">\n <tr v-for=\"dashboard in dashboards\">\n <td class=\"whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 lg:pl-8\">{{dashboard.title}}</td>\n <td class=\"whitespace-nowrap px-3 py-4 text-sm text-gray-500 truncate w-[50%]\">{{dashboard.description}}</td>\n <td class=\"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8\">\n <router-link\n :to=\"'dashboard
|
|
2529
|
+
module.exports = "<div class=\"dashboards max-w-5xl mx-auto mt-8\">\n <div v-if=\"status === 'loaded' && dashboards.length === 0\">\n <div class=\"text-center\">\n <h3 class=\"mt-2 text-sm font-semibold text-gray-900\">No dashboards yet</h3>\n <p class=\"mt-1 text-sm text-gray-500\">Get started by creating a new dashboard.</p>\n <div class=\"mt-6\">\n <button type=\"button\" class=\"inline-flex items-center rounded-md bg-teal-600 px-3 py-2 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 <svg class=\"-ml-0.5 mr-1.5 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z\" />\n </svg>\n New Dashboard\n </button>\n </div>\n </div>\n </div>\n\n\n <div class=\"px-4 sm:px-6 lg:px-8\">\n <div class=\"sm:flex sm:items-center\">\n <div class=\"sm:flex-auto\">\n <h1 class=\"text-base font-semibold leading-6 text-gray-900\">Dashboards</h1>\n </div>\n <div class=\"mt-4 sm:ml-16 sm:mt-0 sm:flex-none\">\n <button\n type=\"button\"\n @click=\"showCreateDashboardModal = true\"\n class=\"block rounded-md bg-teal-600 px-3 py-2 text-center 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\">Create New Dashboard</button>\n </div>\n </div>\n <div class=\"mt-8 flow-root\">\n <div class=\"-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8\">\n <div class=\"inline-block min-w-full py-2 align-middle\">\n <table class=\"min-w-full divide-y divide-gray-300\">\n <thead>\n <tr>\n <th scope=\"col\" class=\"py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg:pl-8\">Title</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900 w-[50%]\">Description</th>\n <th scope=\"col\" class=\"relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8\">\n </th>\n <th scope=\"col\" class=\"relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8\">\n </th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-200 bg-white\">\n <tr v-for=\"dashboard in dashboards\">\n <td class=\"whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 lg:pl-8\">{{dashboard.title}}</td>\n <td class=\"whitespace-nowrap px-3 py-4 text-sm text-gray-500 truncate w-[50%]\">{{dashboard.description}}</td>\n <td class=\"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8\">\n <router-link\n :to=\"'/dashboard/' + dashboard._id + '?edit=true'\"\n class=\"text-teal-600 hover:text-teal-900\">\n Edit\n </router-link>\n </td>\n <td class=\"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8\">\n <router-link\n :to=\"'/dashboard/' + dashboard._id\"\n class=\"text-teal-600 hover:text-teal-900\">\n View\n </router-link>\n </td>\n </tr>\n \n <!-- More people... -->\n </tbody>\n </table>\n </div>\n </div>\n </div>\n </div>\n\n <modal v-if=\"showCreateDashboardModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showCreateDashboardModal = false;\">×</div>\n \n <create-dashboard></create-dashboard>\n </template>\n </modal>\n</div>";
|
|
2454
2530
|
|
|
2455
2531
|
/***/ }),
|
|
2456
2532
|
|
|
@@ -2527,7 +2603,7 @@ module.exports = ".document {\n max-width: 1200px;\n margin-left: auto;\n mar
|
|
|
2527
2603
|
/***/ ((module) => {
|
|
2528
2604
|
|
|
2529
2605
|
"use strict";
|
|
2530
|
-
module.exports = "<div class=\"document\">\n <div class=\"document-menu\">\n <div class=\"left\">\n <button @click=\"$router.push('/model/' + this.model)\">\n ‹ Back\n </button>\n </div>\n\n <div class=\"right\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n type=\"button\"\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 <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n × Cancel\n </button>\n <button\n v-if=\"editting\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n <button\n @click=\"remove\"\n type=\"button\"\n class=\"rounded-md bg-red-600 px-2.5 py-1.5 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-600\">\n <img src=\"images/delete.svg\" class=\"inline\" /> Delete\n </button>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">×</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n </div>\n</div>\n";
|
|
2606
|
+
module.exports = "<div class=\"document\">\n <div class=\"document-menu\">\n <div class=\"left\">\n <button @click=\"$router.push('/model/' + this.model)\">\n ‹ Back\n </button>\n </div>\n\n <div class=\"right\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n type=\"button\"\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 <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n × Cancel\n </button>\n <button\n v-if=\"editting\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n <button\n @click=\"remove\"\n type=\"button\"\n class=\"rounded-md bg-red-600 px-2.5 py-1.5 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-600\">\n <img src=\"images/delete.svg\" class=\"inline\" /> Delete\n </button>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">×</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n </div>\n</div>\n";
|
|
2531
2607
|
|
|
2532
2608
|
/***/ }),
|
|
2533
2609
|
|
|
@@ -2549,7 +2625,7 @@ module.exports = ".edit-array button {\n margin-top: 0.5em;\n}";
|
|
|
2549
2625
|
/***/ ((module) => {
|
|
2550
2626
|
|
|
2551
2627
|
"use strict";
|
|
2552
|
-
module.exports = "<div class=\"edit-array\">\n <textarea
|
|
2628
|
+
module.exports = "<div class=\"edit-array\">\n <textarea\n ref=\"arrayEditor\"\n class=\"w-full border border-gray-300 p-1 h-[300px]\"></textarea>\n</div>";
|
|
2553
2629
|
|
|
2554
2630
|
/***/ }),
|
|
2555
2631
|
|
|
@@ -2586,6 +2662,17 @@ module.exports = "<div>\n <input type=\"number\" :value=\"value\" @input=\"$emi
|
|
|
2586
2662
|
|
|
2587
2663
|
/***/ }),
|
|
2588
2664
|
|
|
2665
|
+
/***/ "./frontend/src/edit-subdocument/edit-subdocument.html":
|
|
2666
|
+
/*!*************************************************************!*\
|
|
2667
|
+
!*** ./frontend/src/edit-subdocument/edit-subdocument.html ***!
|
|
2668
|
+
\*************************************************************/
|
|
2669
|
+
/***/ ((module) => {
|
|
2670
|
+
|
|
2671
|
+
"use strict";
|
|
2672
|
+
module.exports = "<div class=\"edit-subdocument\">\n <textarea\n ref=\"editor\"\n v-model=\"currentValue\"\n class=\"w-full border border-gray-300 p-1 h-[300px]\"></textarea>\n</div>";
|
|
2673
|
+
|
|
2674
|
+
/***/ }),
|
|
2675
|
+
|
|
2589
2676
|
/***/ "./frontend/src/export-query-results/export-query-results.css":
|
|
2590
2677
|
/*!********************************************************************!*\
|
|
2591
2678
|
!*** ./frontend/src/export-query-results/export-query-results.css ***!
|
|
@@ -10478,6 +10565,7 @@ __webpack_require__(/*! ./edit-array/edit-array */ "./frontend/src/edit-array/ed
|
|
|
10478
10565
|
__webpack_require__(/*! ./edit-default/edit-default */ "./frontend/src/edit-default/edit-default.js")(app);
|
|
10479
10566
|
__webpack_require__(/*! ./edit-number/edit-number */ "./frontend/src/edit-number/edit-number.js")(app);
|
|
10480
10567
|
__webpack_require__(/*! ./edit-date/edit-date */ "./frontend/src/edit-date/edit-date.js")(app);
|
|
10568
|
+
__webpack_require__(/*! ./edit-subdocument/edit-subdocument */ "./frontend/src/edit-subdocument/edit-subdocument.js")(app);
|
|
10481
10569
|
__webpack_require__(/*! ./export-query-results/export-query-results */ "./frontend/src/export-query-results/export-query-results.js")(app);
|
|
10482
10570
|
__webpack_require__(/*! ./list-array/list-array */ "./frontend/src/list-array/list-array.js")(app);
|
|
10483
10571
|
__webpack_require__(/*! ./list-default/list-default */ "./frontend/src/list-default/list-default.js")(app);
|
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
<dashboard-result :result="result"></dashboard-result>
|
|
17
17
|
</div>
|
|
18
18
|
<div v-if="showEditor">
|
|
19
|
-
<edit-dashboard
|
|
19
|
+
<edit-dashboard
|
|
20
|
+
:dashboardId="dashboard._id"
|
|
21
|
+
:code="code"
|
|
22
|
+
@close="showEditor=false;"
|
|
23
|
+
@update="updateCode"></edit-dashboard>
|
|
20
24
|
</div>
|
|
21
25
|
|
|
22
26
|
</div>
|
|
@@ -5,6 +5,7 @@ const template = require('./dashboard.html');
|
|
|
5
5
|
|
|
6
6
|
module.exports = app => app.component('dashboard', {
|
|
7
7
|
template: template,
|
|
8
|
+
props: ['dashboardId'],
|
|
8
9
|
data: function() {
|
|
9
10
|
return {
|
|
10
11
|
status: 'loading',
|
|
@@ -19,12 +20,12 @@ module.exports = app => app.component('dashboard', {
|
|
|
19
20
|
this.showEditor = !this.showEditor;
|
|
20
21
|
},
|
|
21
22
|
async updateCode(update) {
|
|
22
|
-
this.code = update;
|
|
23
|
+
this.code = update.doc.code;
|
|
24
|
+
this.result = update.result;
|
|
23
25
|
}
|
|
24
26
|
},
|
|
25
27
|
mounted: async function() {
|
|
26
|
-
const
|
|
27
|
-
const { dashboard, result } = await api.Dashboard.getDashboard({ dashboardId: dashboardId, evaluate: true });
|
|
28
|
+
const { dashboard, result } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
|
|
28
29
|
if (!dashboard) {
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
@@ -17,8 +17,11 @@ module.exports = app => app.component('edit-dashboard', {
|
|
|
17
17
|
this.$emit('close')
|
|
18
18
|
},
|
|
19
19
|
async updateCode() {
|
|
20
|
-
const { doc } = await api.Dashboard.updateDashboard({
|
|
21
|
-
|
|
20
|
+
const { doc, result } = await api.Dashboard.updateDashboard({
|
|
21
|
+
dashboardId: this.dashboardId,
|
|
22
|
+
code: this.editor.getValue()
|
|
23
|
+
});
|
|
24
|
+
this.$emit('update', { doc, result });
|
|
22
25
|
this.editor.setValue(doc.code);
|
|
23
26
|
this.closeEditor();
|
|
24
27
|
}
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 truncate w-[50%]">{{dashboard.description}}</td>
|
|
48
48
|
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
|
|
49
49
|
<router-link
|
|
50
|
-
:to="'dashboard
|
|
50
|
+
:to="'/dashboard/' + dashboard._id + '?edit=true'"
|
|
51
51
|
class="text-teal-600 hover:text-teal-900">
|
|
52
52
|
Edit
|
|
53
53
|
</router-link>
|
|
54
54
|
</td>
|
|
55
55
|
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
|
|
56
56
|
<router-link
|
|
57
|
-
:to="'dashboard
|
|
57
|
+
:to="'/dashboard/' + dashboard._id"
|
|
58
58
|
class="text-teal-600 hover:text-teal-900">
|
|
59
59
|
View
|
|
60
60
|
</router-link>
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
:document="document"
|
|
42
42
|
:schemaPaths="schemaPaths"
|
|
43
43
|
:editting="editting"
|
|
44
|
-
:changes="changes"
|
|
44
|
+
:changes="changes"
|
|
45
|
+
:invalid="invalid"></document-details>
|
|
45
46
|
<modal v-if="shouldShowConfirmModal">
|
|
46
47
|
<template v-slot:body>
|
|
47
48
|
<div class="modal-exit" @click="shouldShowConfirmModal = false;">×</div>
|
|
@@ -9,7 +9,7 @@ appendCSS(require('./document-details.css'));
|
|
|
9
9
|
|
|
10
10
|
module.exports = app => app.component('document-details', {
|
|
11
11
|
template,
|
|
12
|
-
props: ['document', 'schemaPaths', 'editting', 'changes'],
|
|
12
|
+
props: ['document', 'schemaPaths', 'editting', 'changes', 'invalid'],
|
|
13
13
|
methods: {
|
|
14
14
|
getComponentForPath(schemaPath) {
|
|
15
15
|
if (schemaPath.instance === 'Array') {
|
|
@@ -27,6 +27,9 @@ module.exports = app => app.component('document-details', {
|
|
|
27
27
|
if (path.instance === 'Array') {
|
|
28
28
|
return 'edit-array';
|
|
29
29
|
}
|
|
30
|
+
if (path.instance === 'Embedded') {
|
|
31
|
+
return 'edit-subdocument';
|
|
32
|
+
}
|
|
30
33
|
return 'edit-default';
|
|
31
34
|
},
|
|
32
35
|
getValueForPath(path) {
|
|
@@ -16,19 +16,28 @@ appendCSS(require('./edit-array.css'));
|
|
|
16
16
|
module.exports = app => app.component('edit-array', {
|
|
17
17
|
template: template,
|
|
18
18
|
props: ['value'],
|
|
19
|
-
data: () => ({ currentValue: null }),
|
|
19
|
+
data: () => ({ currentValue: null, status: 'init' }),
|
|
20
20
|
mounted() {
|
|
21
|
-
this.currentValue =
|
|
21
|
+
this.currentValue = this.value == null
|
|
22
|
+
? '' + this.value
|
|
23
|
+
: JSON.stringify(this.value, null, ' ').trim();
|
|
22
24
|
this.$refs.arrayEditor.value = this.currentValue;
|
|
23
25
|
this.editor = CodeMirror.fromTextArea(this.$refs.arrayEditor, {
|
|
24
26
|
mode: 'javascript',
|
|
25
27
|
lineNumbers: true
|
|
26
28
|
});
|
|
29
|
+
this.editor.on('change', ev => {
|
|
30
|
+
this.currentValue = this.editor.getValue();
|
|
31
|
+
});
|
|
32
|
+
this.status = 'loaded';
|
|
27
33
|
},
|
|
28
34
|
watch: {
|
|
29
35
|
currentValue() {
|
|
36
|
+
if (this.status === 'init') {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
30
39
|
try {
|
|
31
|
-
this.$emit('input', eval(this.currentValue));
|
|
40
|
+
this.$emit('input', eval(`(${this.currentValue})`));
|
|
32
41
|
} catch (err) {
|
|
33
42
|
this.$emit('error', err);
|
|
34
43
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const template = require('./edit-subdocument.html');
|
|
4
|
+
|
|
5
|
+
const { BSON, EJSON } = require('bson');
|
|
6
|
+
|
|
7
|
+
const ObjectId = new Proxy(BSON.ObjectId, {
|
|
8
|
+
apply (target, thisArg, argumentsList) {
|
|
9
|
+
return new target(...argumentsList);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
module.exports = app => app.component('edit-subdocument', {
|
|
14
|
+
template: template,
|
|
15
|
+
props: ['value'],
|
|
16
|
+
data: () => ({ currentValue: null, status: 'init' }),
|
|
17
|
+
mounted() {
|
|
18
|
+
this.currentValue = this.value == null
|
|
19
|
+
? '' + this.value
|
|
20
|
+
: JSON.stringify(this.value, null, ' ').trim();
|
|
21
|
+
this.$refs.editor.value = this.currentValue;
|
|
22
|
+
this.editor = CodeMirror.fromTextArea(this.$refs.editor, {
|
|
23
|
+
mode: 'javascript',
|
|
24
|
+
lineNumbers: true
|
|
25
|
+
});
|
|
26
|
+
this.editor.on('change', ev => {
|
|
27
|
+
this.currentValue = this.editor.getValue();
|
|
28
|
+
});
|
|
29
|
+
this.status = 'loaded';
|
|
30
|
+
},
|
|
31
|
+
watch: {
|
|
32
|
+
currentValue() {
|
|
33
|
+
if (this.status === 'init') {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
this.$emit('input', eval(`(${this.currentValue})`));
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.log('Error', err);
|
|
40
|
+
this.$emit('error', err);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
beforeDestroy() {
|
|
45
|
+
if (this.editor) {
|
|
46
|
+
this.editor.toTextArea();
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
emits: ['input', 'error']
|
|
50
|
+
});
|
package/frontend/src/index.js
CHANGED
|
@@ -30,6 +30,7 @@ require('./edit-array/edit-array')(app);
|
|
|
30
30
|
require('./edit-default/edit-default')(app);
|
|
31
31
|
require('./edit-number/edit-number')(app);
|
|
32
32
|
require('./edit-date/edit-date')(app);
|
|
33
|
+
require('./edit-subdocument/edit-subdocument')(app);
|
|
33
34
|
require('./export-query-results/export-query-results')(app);
|
|
34
35
|
require('./list-array/list-array')(app);
|
|
35
36
|
require('./list-default/list-default')(app);
|
package/frontend/src/routes.js
CHANGED