@mongoosejs/studio 0.0.18 → 0.0.19
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/frontend/public/app.js +27 -51
- package/frontend/public/tw.css +115 -17
- package/frontend/src/document/document.html +14 -0
- package/frontend/src/document/document.js +13 -3
- package/frontend/src/models/models.css +0 -4
- package/frontend/src/models/models.html +27 -9
- package/frontend/src/models/models.js +11 -45
- package/package.json +1 -1
package/frontend/public/app.js
CHANGED
|
@@ -245,12 +245,13 @@ module.exports = app => app.component('document', {
|
|
|
245
245
|
status: 'init',
|
|
246
246
|
document: null,
|
|
247
247
|
changes: {},
|
|
248
|
-
editting: false
|
|
248
|
+
editting: false,
|
|
249
|
+
virtuals: []
|
|
249
250
|
}),
|
|
250
251
|
async mounted() {
|
|
251
252
|
const { doc, schemaPaths } = await api.Model.getDocument({ model: this.model, documentId: this.documentId });
|
|
252
253
|
this.document = doc;
|
|
253
|
-
this.schemaPaths = Object.keys(schemaPaths).sort((k1, k2) => {
|
|
254
|
+
this.schemaPaths = await Object.keys(schemaPaths).sort((k1, k2) => {
|
|
254
255
|
if (k1 === '_id' && k2 !== '_id') {
|
|
255
256
|
return -1;
|
|
256
257
|
}
|
|
@@ -259,7 +260,7 @@ module.exports = app => app.component('document', {
|
|
|
259
260
|
}
|
|
260
261
|
return 0;
|
|
261
262
|
}).map(key => schemaPaths[key]);
|
|
262
|
-
|
|
263
|
+
this.getVirtuals();
|
|
263
264
|
this.status = 'loaded';
|
|
264
265
|
},
|
|
265
266
|
methods: {
|
|
@@ -287,6 +288,15 @@ module.exports = app => app.component('document', {
|
|
|
287
288
|
getEditValueForPath({ path }) {
|
|
288
289
|
return path in this.changes ? this.changes[path] : mpath.get(path, this.document);
|
|
289
290
|
},
|
|
291
|
+
getVirtuals() {
|
|
292
|
+
const exists = this.schemaPaths.map(x => x.path);
|
|
293
|
+
const docKeys = Object.keys(this.document);
|
|
294
|
+
for (let i = 0; i < docKeys.length; i++) {
|
|
295
|
+
if (!exists.includes(docKeys[i])) {
|
|
296
|
+
this.virtuals.push({ name: docKeys[i], value: this.document[docKeys[i]] });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
},
|
|
290
300
|
cancelEdit() {
|
|
291
301
|
this.changes = {};
|
|
292
302
|
this.editting = false;
|
|
@@ -847,31 +857,18 @@ module.exports = app => app.component('models', {
|
|
|
847
857
|
this.sortDocs(num, path);
|
|
848
858
|
}
|
|
849
859
|
|
|
860
|
+
|
|
850
861
|
if (this.currentModel != null) {
|
|
851
862
|
await this.getDocuments();
|
|
852
863
|
}
|
|
853
|
-
this.
|
|
864
|
+
if (this.$route.query?.fields) {
|
|
865
|
+
const filter = this.$route.query.fields.split(',');
|
|
866
|
+
this.filteredPaths = this.filteredPaths.filter(x => filter.includes(x.path))
|
|
867
|
+
}
|
|
854
868
|
|
|
855
869
|
this.status = 'loaded';
|
|
856
870
|
},
|
|
857
871
|
methods: {
|
|
858
|
-
applyQueryParams() {
|
|
859
|
-
const hashUrl = window.location.hash.replace(/^#/, '');
|
|
860
|
-
if (hashUrl.indexOf('?') !== -1) {
|
|
861
|
-
const searchParams = new URLSearchParams(
|
|
862
|
-
hashUrl.slice(hashUrl.indexOf('?') + 1)
|
|
863
|
-
);
|
|
864
|
-
if (searchParams.has('fields')) {
|
|
865
|
-
const filter = searchParams.get('fields').split(',');
|
|
866
|
-
this.filteredPaths = this.filteredPaths.filter(x => filter.includes(x.path))
|
|
867
|
-
}
|
|
868
|
-
if (searchParams.has('search')) {
|
|
869
|
-
this.searchText = searchParams.get('search');
|
|
870
|
-
this.filter = eval(`(${this.searchText})`);
|
|
871
|
-
this.filter = EJSON.stringify(this.filter);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
},
|
|
875
872
|
async onScroll() {
|
|
876
873
|
if (this.status === 'loading' || this.loadedAllDocs) {
|
|
877
874
|
return;
|
|
@@ -915,22 +912,11 @@ module.exports = app => app.component('models', {
|
|
|
915
912
|
this.filter = eval(`(${this.searchText})`);
|
|
916
913
|
this.filter = EJSON.stringify(this.filter);
|
|
917
914
|
this.query.search = this.searchText;
|
|
915
|
+
this.$router.push({ query: this.query });
|
|
918
916
|
} else {
|
|
919
917
|
this.filter = {};
|
|
920
918
|
delete this.query.search;
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const hashUrl = window.location.hash.replace(/^#/, '');
|
|
924
|
-
if (hashUrl.indexOf('?') === -1) {
|
|
925
|
-
window.history.pushState({}, '', window.location.pathname + '#' + hashUrl + '?search=' + this.query.search);
|
|
926
|
-
} else {
|
|
927
|
-
const searchParams = new URLSearchParams(
|
|
928
|
-
hashUrl.indexOf('?') === -1 ? '' : hashUrl.slice(hashUrl.indexOf('?') + 1)
|
|
929
|
-
);
|
|
930
|
-
const hashUrlWithoutSearchParams = hashUrl.slice(0, hashUrl.indexOf('?'));
|
|
931
|
-
|
|
932
|
-
searchParams.set('search', this.query.search);
|
|
933
|
-
window.history.pushState({}, '', window.location.pathname + '#' + hashUrlWithoutSearchParams + '?' + searchParams);
|
|
919
|
+
this.$router.push({ query: this.query });
|
|
934
920
|
}
|
|
935
921
|
await this.loadMoreDocuments();
|
|
936
922
|
},
|
|
@@ -997,23 +983,13 @@ module.exports = app => app.component('models', {
|
|
|
997
983
|
this.filteredPaths = [...this.selectedPaths];
|
|
998
984
|
this.shouldShowFieldModal = false;
|
|
999
985
|
const selectedParams = this.filteredPaths.map(x => x.path).join(',');
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
if (hashUrl.indexOf('?') === -1) {
|
|
1003
|
-
window.history.pushState({}, '', window.location.pathname + '#' + hashUrl + '?fields=' + selectedParams);
|
|
1004
|
-
} else {
|
|
1005
|
-
const searchParams = new URLSearchParams(
|
|
1006
|
-
hashUrl.indexOf('?') === -1 ? '' : hashUrl.slice(hashUrl.indexOf('?') + 1)
|
|
1007
|
-
);
|
|
1008
|
-
const hashUrlWithoutSearchParams = hashUrl.slice(0, hashUrl.indexOf('?'));
|
|
1009
|
-
|
|
1010
|
-
searchParams.set('fields', selectedParams);
|
|
1011
|
-
window.history.pushState({}, '', window.location.pathname + '#' + hashUrlWithoutSearchParams + '?' + searchParams);
|
|
1012
|
-
}
|
|
1013
|
-
|
|
986
|
+
this.query.fields = selectedParams;
|
|
987
|
+
this.$router.push({ query: this.query });
|
|
1014
988
|
},
|
|
1015
989
|
resetDocuments() {
|
|
1016
990
|
this.selectedPaths = [...this.filteredPaths];
|
|
991
|
+
this.query.fields = {};
|
|
992
|
+
this.$router.push({ query: this.query });
|
|
1017
993
|
this.shouldShowFieldModal = false;
|
|
1018
994
|
},
|
|
1019
995
|
deselectAll() {
|
|
@@ -1780,7 +1756,7 @@ module.exports = ".document {\n max-width: 1200px;\n margin-left: auto;\n mar
|
|
|
1780
1756
|
/***/ ((module) => {
|
|
1781
1757
|
|
|
1782
1758
|
"use strict";
|
|
1783
|
-
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-puerto-rico-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-puerto-rico-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-puerto-rico-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"save\"\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=\"save\"\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 <div v-for=\"path in schemaPaths\" class=\"value\">\n <div class=\"path-key\">\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown').toLowerCase()}})\n </span>\n </div>\n <div v-if=\"editting && path.path !== '_id'\">\n <component\n :is=\"getEditComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n @input=\"changes[path.path] = $event;\"\n >\n </component>\n </div>\n <div v-else>\n <component :is=\"getComponentForPath(path)\" :value=\"getValueForPath(path.path)\"></component>\n </div>\n </div>\n </div>\n</div>\n";
|
|
1759
|
+
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-puerto-rico-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-puerto-rico-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-puerto-rico-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"save\"\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=\"save\"\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 <div v-for=\"path in schemaPaths\" class=\"value\">\n <div class=\"path-key\">\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown').toLowerCase()}})\n </span>\n </div>\n <div v-if=\"editting && path.path !== '_id'\">\n <component\n :is=\"getEditComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n @input=\"changes[path.path] = $event;\"\n >\n </component>\n </div>\n <div v-else>\n <component :is=\"getComponentForPath(path)\" :value=\"getValueForPath(path.path)\"></component>\n </div>\n </div>\n <div v-for=\"path in virtuals\" class=\"mb-2\">\n <div class=\"p-1 mb-1 bg-slate-100\">\n {{path.name}}\n <span class=\"path-type\">\n (virtual)\n </span>\n </div>\n <div v-if=\"path.value == null\" class=\"text-sky-800\">\n {{'' + path.value}}\n </div>\n <div v-else>\n {{path.value}}\n </div>\n </div>\n </div>\n</div>\n";
|
|
1784
1760
|
|
|
1785
1761
|
/***/ }),
|
|
1786
1762
|
|
|
@@ -2000,7 +1976,7 @@ module.exports = "<transition name=\"modal\">\n <div class=\"modal-mask\">\n
|
|
|
2000
1976
|
/***/ ((module) => {
|
|
2001
1977
|
|
|
2002
1978
|
"use strict";
|
|
2003
|
-
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 .documents-container {\n margin-top: 60px;\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: 0px;\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,.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: #55A3D4;\n}\n\n.models .documents table th, td {\n border-bottom: thin solid rgba(0,0,0,.12);\n text-align: left;\n padding: 0 16px;\n height: 48px;\n}\n\n.models textarea {\n width: 100%;\n height: 600px;\n font-size: 1.2em;\n}\n\n.models .path-type {\n color: rgba(0,0,0,.36);\n font-size: 0.8em;\n}\n\n.models .documents-menu {\n display: flex;\n margin: 0.25em;\n position: fixed;\n width: calc(100vw - 220px);\n}\n\n.models .documents-menu .search-input {\n flex-grow: 1;\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: baseline;\n}
|
|
1979
|
+
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 .documents-container {\n margin-top: 60px;\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: 0px;\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,.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: #55A3D4;\n}\n\n.models .documents table th, td {\n border-bottom: thin solid rgba(0,0,0,.12);\n text-align: left;\n padding: 0 16px;\n height: 48px;\n}\n\n.models textarea {\n width: 100%;\n height: 600px;\n font-size: 1.2em;\n}\n\n.models .path-type {\n color: rgba(0,0,0,.36);\n font-size: 0.8em;\n}\n\n.models .documents-menu {\n display: flex;\n margin: 0.25em;\n position: fixed;\n width: calc(100vw - 220px);\n}\n\n.models .documents-menu .search-input {\n flex-grow: 1;\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: baseline;\n}";
|
|
2004
1980
|
|
|
2005
1981
|
/***/ }),
|
|
2006
1982
|
|
|
@@ -2011,7 +1987,7 @@ module.exports = ".models {\n position: relative;\n display: flex;\n flex-dir
|
|
|
2011
1987
|
/***/ ((module) => {
|
|
2012
1988
|
|
|
2013
1989
|
"use strict";
|
|
2014
|
-
module.exports = "<div class=\"models\">\n <div class=\"
|
|
1990
|
+
module.exports = "<div class=\"models\">\n <div>\n <div class=\"flex grow flex-col gap-y-5 overflow-y-auto border-r border-gray-200 bg-white px-2 h-[calc(100vh-55px)]\">\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 rounded-md py-2 pr-2 pl-2 text-sm font-semibold text-gray-700\"\n :class=\"model === currentModel ? 'bg-puerto-rico-100 font-bold' : 'hover:bg-puerto-rico-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>\n <div class=\"documents-menu\">\n <div class=\"search-input\">\n <form @submit.prevent=\"search\">\n <input class=\"search-text\" type=\"text\" placeholder=\"Filter or text\" v-model=\"searchText\" />\n </form>\n </div>\n <div class=\"buttons\">\n <div class=\"mr-2\">\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 class=\"mr-2 rounded bg-puerto-rico-600 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-puerto-rico-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-puerto-rico-600\">\n Export\n </button>\n <button\n @click=\"shouldShowFieldModal = true\"\n type=\"button\"\n class=\"rounded bg-puerto-rico-600 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-puerto-rico-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-puerto-rico-600\">\n Fields\n </button>\n </div>\n </div>\n </div>\n <div class=\"documents-container\">\n <table>\n <thead>\n <th v-for=\"path in filteredPaths\">\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=\"$router.push('/model/' + currentModel + '/document/' + document._id)\" :key=\"document._id\">\n <td v-for=\"schemaPath in filteredPaths\">\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=\"status === 'loading'\" class=\"loader\">\n <img src=\"images/loader.gif\">\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=\"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\" style=\"margin-bottom: 0.5em\">\n <input type=\"checkbox\" :id=\"'path.path'+index\" @change=\"addOrRemove(path)\" :value=\"path.path\" :checked=\"isSelected(path.path)\" />\n <label :for=\"'path' + index\">{{path.path}}</label>\n </div>\n <div style=\"margin-top: 1em\">\n <button type=\"submit\" @click=\"filterDocuments()\" style=\"color: black;margin-right: 0.5em\">Filter Selection</button>\n <button type=\"submit\" @click=\"deselectAll()\" class=\"gray\" style=\"margin-right: 0.5em\">Deselect All</button>\n <button type=\"submit\" @click=\"resetDocuments()\" class=\"gray\">Cancel</button>\n \n </div>\n </template>\n </modal>\n </div>\n</div>";
|
|
2015
1991
|
|
|
2016
1992
|
/***/ }),
|
|
2017
1993
|
|
package/frontend/public/tw.css
CHANGED
|
@@ -578,14 +578,66 @@ video {
|
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
580
|
|
|
581
|
+
.mb-1 {
|
|
582
|
+
margin-bottom: 0.25rem;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.mb-2 {
|
|
586
|
+
margin-bottom: 0.5rem;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.mr-2 {
|
|
590
|
+
margin-right: 0.5rem;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.mt-4 {
|
|
594
|
+
margin-top: 1rem;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.block {
|
|
598
|
+
display: block;
|
|
599
|
+
}
|
|
600
|
+
|
|
581
601
|
.inline {
|
|
582
602
|
display: inline;
|
|
583
603
|
}
|
|
584
604
|
|
|
605
|
+
.flex {
|
|
606
|
+
display: flex;
|
|
607
|
+
}
|
|
608
|
+
|
|
585
609
|
.table {
|
|
586
610
|
display: table;
|
|
587
611
|
}
|
|
588
612
|
|
|
613
|
+
.h-\[calc\(100vh-55px\)\] {
|
|
614
|
+
height: calc(100vh - 55px);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.flex-1 {
|
|
618
|
+
flex: 1 1 0%;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.grow {
|
|
622
|
+
flex-grow: 1;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.flex-col {
|
|
626
|
+
flex-direction: column;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.gap-y-5 {
|
|
630
|
+
row-gap: 1.25rem;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.gap-y-7 {
|
|
634
|
+
row-gap: 1.75rem;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.overflow-y-auto {
|
|
638
|
+
overflow-y: auto;
|
|
639
|
+
}
|
|
640
|
+
|
|
589
641
|
.rounded {
|
|
590
642
|
border-radius: 0.25rem;
|
|
591
643
|
}
|
|
@@ -594,19 +646,28 @@ video {
|
|
|
594
646
|
border-radius: 0.375rem;
|
|
595
647
|
}
|
|
596
648
|
|
|
649
|
+
.border-r {
|
|
650
|
+
border-right-width: 1px;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.border-gray-200 {
|
|
654
|
+
--tw-border-opacity: 1;
|
|
655
|
+
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
|
656
|
+
}
|
|
657
|
+
|
|
597
658
|
.bg-green-600 {
|
|
598
659
|
--tw-bg-opacity: 1;
|
|
599
660
|
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
|
|
600
661
|
}
|
|
601
662
|
|
|
602
|
-
.bg-puerto-rico-
|
|
663
|
+
.bg-puerto-rico-100 {
|
|
603
664
|
--tw-bg-opacity: 1;
|
|
604
|
-
background-color: rgb(
|
|
665
|
+
background-color: rgb(198 255 243 / var(--tw-bg-opacity));
|
|
605
666
|
}
|
|
606
667
|
|
|
607
|
-
.bg-
|
|
668
|
+
.bg-puerto-rico-600 {
|
|
608
669
|
--tw-bg-opacity: 1;
|
|
609
|
-
background-color: rgb(
|
|
670
|
+
background-color: rgb(0 164 145 / var(--tw-bg-opacity));
|
|
610
671
|
}
|
|
611
672
|
|
|
612
673
|
.bg-red-600 {
|
|
@@ -614,40 +675,81 @@ video {
|
|
|
614
675
|
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
|
|
615
676
|
}
|
|
616
677
|
|
|
678
|
+
.bg-slate-100 {
|
|
679
|
+
--tw-bg-opacity: 1;
|
|
680
|
+
background-color: rgb(241 245 249 / var(--tw-bg-opacity));
|
|
681
|
+
}
|
|
682
|
+
|
|
617
683
|
.bg-slate-600 {
|
|
618
684
|
--tw-bg-opacity: 1;
|
|
619
685
|
background-color: rgb(71 85 105 / var(--tw-bg-opacity));
|
|
620
686
|
}
|
|
621
687
|
|
|
688
|
+
.bg-white {
|
|
689
|
+
--tw-bg-opacity: 1;
|
|
690
|
+
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.p-1 {
|
|
694
|
+
padding: 0.25rem;
|
|
695
|
+
}
|
|
696
|
+
|
|
622
697
|
.px-2 {
|
|
623
698
|
padding-left: 0.5rem;
|
|
624
699
|
padding-right: 0.5rem;
|
|
625
700
|
}
|
|
626
701
|
|
|
627
|
-
.py-1 {
|
|
628
|
-
padding-top: 0.25rem;
|
|
629
|
-
padding-bottom: 0.25rem;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
702
|
.px-2\.5 {
|
|
633
703
|
padding-left: 0.625rem;
|
|
634
704
|
padding-right: 0.625rem;
|
|
635
705
|
}
|
|
636
706
|
|
|
707
|
+
.py-1 {
|
|
708
|
+
padding-top: 0.25rem;
|
|
709
|
+
padding-bottom: 0.25rem;
|
|
710
|
+
}
|
|
711
|
+
|
|
637
712
|
.py-1\.5 {
|
|
638
713
|
padding-top: 0.375rem;
|
|
639
714
|
padding-bottom: 0.375rem;
|
|
640
715
|
}
|
|
641
716
|
|
|
717
|
+
.py-2 {
|
|
718
|
+
padding-top: 0.5rem;
|
|
719
|
+
padding-bottom: 0.5rem;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.pl-2 {
|
|
723
|
+
padding-left: 0.5rem;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.pr-2 {
|
|
727
|
+
padding-right: 0.5rem;
|
|
728
|
+
}
|
|
729
|
+
|
|
642
730
|
.text-sm {
|
|
643
731
|
font-size: 0.875rem;
|
|
644
732
|
line-height: 1.25rem;
|
|
645
733
|
}
|
|
646
734
|
|
|
735
|
+
.font-bold {
|
|
736
|
+
font-weight: 700;
|
|
737
|
+
}
|
|
738
|
+
|
|
647
739
|
.font-semibold {
|
|
648
740
|
font-weight: 600;
|
|
649
741
|
}
|
|
650
742
|
|
|
743
|
+
.text-gray-700 {
|
|
744
|
+
--tw-text-opacity: 1;
|
|
745
|
+
color: rgb(55 65 81 / var(--tw-text-opacity));
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.text-sky-800 {
|
|
749
|
+
--tw-text-opacity: 1;
|
|
750
|
+
color: rgb(7 89 133 / var(--tw-text-opacity));
|
|
751
|
+
}
|
|
752
|
+
|
|
651
753
|
.text-white {
|
|
652
754
|
--tw-text-opacity: 1;
|
|
653
755
|
color: rgb(255 255 255 / var(--tw-text-opacity));
|
|
@@ -676,14 +778,14 @@ video {
|
|
|
676
778
|
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
|
|
677
779
|
}
|
|
678
780
|
|
|
679
|
-
.hover\:bg-puerto-rico-
|
|
781
|
+
.hover\:bg-puerto-rico-100:hover {
|
|
680
782
|
--tw-bg-opacity: 1;
|
|
681
|
-
background-color: rgb(
|
|
783
|
+
background-color: rgb(198 255 243 / var(--tw-bg-opacity));
|
|
682
784
|
}
|
|
683
785
|
|
|
684
|
-
.hover\:bg-
|
|
786
|
+
.hover\:bg-puerto-rico-500:hover {
|
|
685
787
|
--tw-bg-opacity: 1;
|
|
686
|
-
background-color: rgb(
|
|
788
|
+
background-color: rgb(0 204 176 / var(--tw-bg-opacity));
|
|
687
789
|
}
|
|
688
790
|
|
|
689
791
|
.hover\:bg-red-500:hover {
|
|
@@ -716,10 +818,6 @@ video {
|
|
|
716
818
|
outline-color: #00a491;
|
|
717
819
|
}
|
|
718
820
|
|
|
719
|
-
.focus-visible\:outline-indigo-600:focus-visible {
|
|
720
|
-
outline-color: #4f46e5;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
821
|
.focus-visible\:outline-red-600:focus-visible {
|
|
724
822
|
outline-color: #dc2626;
|
|
725
823
|
}
|
|
@@ -56,5 +56,19 @@
|
|
|
56
56
|
<component :is="getComponentForPath(path)" :value="getValueForPath(path.path)"></component>
|
|
57
57
|
</div>
|
|
58
58
|
</div>
|
|
59
|
+
<div v-for="path in virtuals" class="mb-2">
|
|
60
|
+
<div class="p-1 mb-1 bg-slate-100">
|
|
61
|
+
{{path.name}}
|
|
62
|
+
<span class="path-type">
|
|
63
|
+
(virtual)
|
|
64
|
+
</span>
|
|
65
|
+
</div>
|
|
66
|
+
<div v-if="path.value == null" class="text-sky-800">
|
|
67
|
+
{{'' + path.value}}
|
|
68
|
+
</div>
|
|
69
|
+
<div v-else>
|
|
70
|
+
{{path.value}}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
59
73
|
</div>
|
|
60
74
|
</div>
|
|
@@ -17,12 +17,13 @@ module.exports = app => app.component('document', {
|
|
|
17
17
|
status: 'init',
|
|
18
18
|
document: null,
|
|
19
19
|
changes: {},
|
|
20
|
-
editting: false
|
|
20
|
+
editting: false,
|
|
21
|
+
virtuals: []
|
|
21
22
|
}),
|
|
22
23
|
async mounted() {
|
|
23
24
|
const { doc, schemaPaths } = await api.Model.getDocument({ model: this.model, documentId: this.documentId });
|
|
24
25
|
this.document = doc;
|
|
25
|
-
this.schemaPaths = Object.keys(schemaPaths).sort((k1, k2) => {
|
|
26
|
+
this.schemaPaths = await Object.keys(schemaPaths).sort((k1, k2) => {
|
|
26
27
|
if (k1 === '_id' && k2 !== '_id') {
|
|
27
28
|
return -1;
|
|
28
29
|
}
|
|
@@ -31,7 +32,7 @@ module.exports = app => app.component('document', {
|
|
|
31
32
|
}
|
|
32
33
|
return 0;
|
|
33
34
|
}).map(key => schemaPaths[key]);
|
|
34
|
-
|
|
35
|
+
this.getVirtuals();
|
|
35
36
|
this.status = 'loaded';
|
|
36
37
|
},
|
|
37
38
|
methods: {
|
|
@@ -59,6 +60,15 @@ module.exports = app => app.component('document', {
|
|
|
59
60
|
getEditValueForPath({ path }) {
|
|
60
61
|
return path in this.changes ? this.changes[path] : mpath.get(path, this.document);
|
|
61
62
|
},
|
|
63
|
+
getVirtuals() {
|
|
64
|
+
const exists = this.schemaPaths.map(x => x.path);
|
|
65
|
+
const docKeys = Object.keys(this.document);
|
|
66
|
+
for (let i = 0; i < docKeys.length; i++) {
|
|
67
|
+
if (!exists.includes(docKeys[i])) {
|
|
68
|
+
this.virtuals.push({ name: docKeys[i], value: this.document[docKeys[i]] });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
62
72
|
cancelEdit() {
|
|
63
73
|
this.changes = {};
|
|
64
74
|
this.editting = false;
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
<div class="models">
|
|
2
|
-
<div
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<div class="flex grow flex-col gap-y-5 overflow-y-auto border-r border-gray-200 bg-white px-2 h-[calc(100vh-55px)]">
|
|
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 rounded-md py-2 pr-2 pl-2 text-sm font-semibold text-gray-700"
|
|
15
|
+
:class="model === currentModel ? 'bg-puerto-rico-100 font-bold' : 'hover:bg-puerto-rico-100'">
|
|
16
|
+
{{model}}
|
|
17
|
+
</router-link>
|
|
18
|
+
</li>
|
|
19
|
+
</ul>
|
|
20
|
+
</li>
|
|
21
|
+
</ul>
|
|
22
|
+
</nav>
|
|
8
23
|
</div>
|
|
24
|
+
|
|
9
25
|
</div>
|
|
10
26
|
<div class="documents" ref="documentsList">
|
|
11
27
|
<div>
|
|
@@ -13,15 +29,17 @@
|
|
|
13
29
|
<div class="search-input">
|
|
14
30
|
<form @submit.prevent="search">
|
|
15
31
|
<input class="search-text" type="text" placeholder="Filter or text" v-model="searchText" />
|
|
16
|
-
<div>Number of Documents: {{numDocuments}}</div>
|
|
17
32
|
</form>
|
|
18
|
-
|
|
19
33
|
</div>
|
|
20
34
|
<div class="buttons">
|
|
35
|
+
<div class="mr-2">
|
|
36
|
+
<span v-if="status === 'loading'">Loading ...</span>
|
|
37
|
+
<span v-if="status === 'loaded'">{{numDocuments === 1 ? numDocuments+ ' document' : numDocuments + ' documents'}}</span>
|
|
38
|
+
</div>
|
|
21
39
|
<button
|
|
22
40
|
@click="shouldShowExportModal = true"
|
|
23
41
|
type="button"
|
|
24
|
-
class="rounded bg-puerto-rico-600 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-puerto-rico-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-puerto-rico-600">
|
|
42
|
+
class="mr-2 rounded bg-puerto-rico-600 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-puerto-rico-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-puerto-rico-600">
|
|
25
43
|
Export
|
|
26
44
|
</button>
|
|
27
45
|
<button
|
|
@@ -68,31 +68,18 @@ module.exports = app => app.component('models', {
|
|
|
68
68
|
this.sortDocs(num, path);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
|
|
71
72
|
if (this.currentModel != null) {
|
|
72
73
|
await this.getDocuments();
|
|
73
74
|
}
|
|
74
|
-
this.
|
|
75
|
+
if (this.$route.query?.fields) {
|
|
76
|
+
const filter = this.$route.query.fields.split(',');
|
|
77
|
+
this.filteredPaths = this.filteredPaths.filter(x => filter.includes(x.path))
|
|
78
|
+
}
|
|
75
79
|
|
|
76
80
|
this.status = 'loaded';
|
|
77
81
|
},
|
|
78
82
|
methods: {
|
|
79
|
-
applyQueryParams() {
|
|
80
|
-
const hashUrl = window.location.hash.replace(/^#/, '');
|
|
81
|
-
if (hashUrl.indexOf('?') !== -1) {
|
|
82
|
-
const searchParams = new URLSearchParams(
|
|
83
|
-
hashUrl.slice(hashUrl.indexOf('?') + 1)
|
|
84
|
-
);
|
|
85
|
-
if (searchParams.has('fields')) {
|
|
86
|
-
const filter = searchParams.get('fields').split(',');
|
|
87
|
-
this.filteredPaths = this.filteredPaths.filter(x => filter.includes(x.path))
|
|
88
|
-
}
|
|
89
|
-
if (searchParams.has('search')) {
|
|
90
|
-
this.searchText = searchParams.get('search');
|
|
91
|
-
this.filter = eval(`(${this.searchText})`);
|
|
92
|
-
this.filter = EJSON.stringify(this.filter);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
83
|
async onScroll() {
|
|
97
84
|
if (this.status === 'loading' || this.loadedAllDocs) {
|
|
98
85
|
return;
|
|
@@ -136,22 +123,11 @@ module.exports = app => app.component('models', {
|
|
|
136
123
|
this.filter = eval(`(${this.searchText})`);
|
|
137
124
|
this.filter = EJSON.stringify(this.filter);
|
|
138
125
|
this.query.search = this.searchText;
|
|
126
|
+
this.$router.push({ query: this.query });
|
|
139
127
|
} else {
|
|
140
128
|
this.filter = {};
|
|
141
129
|
delete this.query.search;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const hashUrl = window.location.hash.replace(/^#/, '');
|
|
145
|
-
if (hashUrl.indexOf('?') === -1) {
|
|
146
|
-
window.history.pushState({}, '', window.location.pathname + '#' + hashUrl + '?search=' + this.query.search);
|
|
147
|
-
} else {
|
|
148
|
-
const searchParams = new URLSearchParams(
|
|
149
|
-
hashUrl.indexOf('?') === -1 ? '' : hashUrl.slice(hashUrl.indexOf('?') + 1)
|
|
150
|
-
);
|
|
151
|
-
const hashUrlWithoutSearchParams = hashUrl.slice(0, hashUrl.indexOf('?'));
|
|
152
|
-
|
|
153
|
-
searchParams.set('search', this.query.search);
|
|
154
|
-
window.history.pushState({}, '', window.location.pathname + '#' + hashUrlWithoutSearchParams + '?' + searchParams);
|
|
130
|
+
this.$router.push({ query: this.query });
|
|
155
131
|
}
|
|
156
132
|
await this.loadMoreDocuments();
|
|
157
133
|
},
|
|
@@ -218,23 +194,13 @@ module.exports = app => app.component('models', {
|
|
|
218
194
|
this.filteredPaths = [...this.selectedPaths];
|
|
219
195
|
this.shouldShowFieldModal = false;
|
|
220
196
|
const selectedParams = this.filteredPaths.map(x => x.path).join(',');
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (hashUrl.indexOf('?') === -1) {
|
|
224
|
-
window.history.pushState({}, '', window.location.pathname + '#' + hashUrl + '?fields=' + selectedParams);
|
|
225
|
-
} else {
|
|
226
|
-
const searchParams = new URLSearchParams(
|
|
227
|
-
hashUrl.indexOf('?') === -1 ? '' : hashUrl.slice(hashUrl.indexOf('?') + 1)
|
|
228
|
-
);
|
|
229
|
-
const hashUrlWithoutSearchParams = hashUrl.slice(0, hashUrl.indexOf('?'));
|
|
230
|
-
|
|
231
|
-
searchParams.set('fields', selectedParams);
|
|
232
|
-
window.history.pushState({}, '', window.location.pathname + '#' + hashUrlWithoutSearchParams + '?' + searchParams);
|
|
233
|
-
}
|
|
234
|
-
|
|
197
|
+
this.query.fields = selectedParams;
|
|
198
|
+
this.$router.push({ query: this.query });
|
|
235
199
|
},
|
|
236
200
|
resetDocuments() {
|
|
237
201
|
this.selectedPaths = [...this.filteredPaths];
|
|
202
|
+
this.query.fields = {};
|
|
203
|
+
this.$router.push({ query: this.query });
|
|
238
204
|
this.shouldShowFieldModal = false;
|
|
239
205
|
},
|
|
240
206
|
deselectAll() {
|