@mongoosejs/studio 0.0.2 → 0.0.4
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/deleteDocument.js +31 -0
- package/backend/actions/Model/getDocument.js +11 -1
- package/backend/actions/Model/getDocuments.js +26 -4
- package/backend/actions/Model/index.js +1 -0
- package/backend/helpers/removeSpecifiedPaths.js +9 -0
- package/frontend/public/images/delete.svg +25 -0
- package/frontend/public/images/edit.svg +5 -0
- package/frontend/public/images/logo.svg +160 -0
- package/frontend/public/images/save.svg +21 -0
- package/frontend/public/images/success.png +0 -0
- package/frontend/public/index.html +1 -0
- package/frontend/public/style.css +38 -1
- package/frontend/public/vanillatoasts/vanillatoasts.css +125 -0
- package/frontend/src/api.js +6 -0
- package/frontend/src/document/document.css +4 -0
- package/frontend/src/document/document.html +6 -3
- package/frontend/src/document/document.js +25 -1
- package/frontend/src/edit-date/edit-date.html +3 -0
- package/frontend/src/edit-date/edit-date.js +9 -0
- package/frontend/src/edit-number/edit-number.html +3 -0
- package/frontend/src/edit-number/edit-number.js +20 -0
- package/frontend/src/index.js +3 -0
- package/frontend/src/list-default/list-default.css +0 -0
- package/frontend/src/list-default/list-default.html +6 -2
- package/frontend/src/list-default/list-default.js +30 -1
- package/frontend/src/list-string/list-string.css +4 -0
- package/frontend/src/list-string/list-string.html +4 -0
- package/frontend/src/list-string/list-string.js +43 -0
- package/frontend/src/list-subdocument/list-subdocument.html +1 -1
- package/frontend/src/list-subdocument/list-subdocument.js +4 -1
- package/frontend/src/modal/modal.css +8 -0
- package/frontend/src/models/models.css +7 -1
- package/frontend/src/models/models.html +11 -2
- package/frontend/src/models/models.js +44 -21
- package/frontend/src/navbar/navbar.css +3 -3
- package/frontend/src/navbar/navbar.html +2 -2
- package/package.json +9 -7
- package/frontend/public/app.js +0 -4012
- package/test.js +0 -264
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const template = require('./edit-number.html');
|
|
4
|
+
|
|
5
|
+
module.exports = app => app.component('edit-number', {
|
|
6
|
+
template: template,
|
|
7
|
+
props: ['value'],
|
|
8
|
+
emits: ['input'],
|
|
9
|
+
computed: {
|
|
10
|
+
displayValue() {
|
|
11
|
+
if (this.value === null) {
|
|
12
|
+
return 'null';
|
|
13
|
+
}
|
|
14
|
+
if (this.value === undefined) {
|
|
15
|
+
return 'undefined';
|
|
16
|
+
}
|
|
17
|
+
return this.value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
});
|
package/frontend/src/index.js
CHANGED
|
@@ -9,9 +9,12 @@ require('./detail-array/detail-array')(app);
|
|
|
9
9
|
require('./detail-default/detail-default')(app);
|
|
10
10
|
require('./document/document')(app);
|
|
11
11
|
require('./edit-default/edit-default')(app);
|
|
12
|
+
require('./edit-number/edit-number')(app);
|
|
13
|
+
require('./edit-date/edit-date')(app);
|
|
12
14
|
require('./export-query-results/export-query-results')(app);
|
|
13
15
|
require('./list-array/list-array')(app);
|
|
14
16
|
require('./list-default/list-default')(app);
|
|
17
|
+
require('./list-string/list-string')(app);
|
|
15
18
|
require('./list-subdocument/list-subdocument')(app);
|
|
16
19
|
require('./modal/modal')(app);
|
|
17
20
|
require('./models/models')(app);
|
|
File without changes
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
<div>
|
|
2
|
-
{{
|
|
1
|
+
<div ref="itemData" class="tooltip">
|
|
2
|
+
{{displayValue}}
|
|
3
|
+
<div class="tooltiptext" style="display:flex; width: 100%; justify-content: space-around; align-items: center; min-width: 180px;">
|
|
4
|
+
<div class="tooltiptextchild" v-if="allude" @click.stop="goToDoc(value)">View Document</div>
|
|
5
|
+
<div class="tooltiptextchild" @click.stop="copyText(value)">copy 📋</div>
|
|
6
|
+
</div>
|
|
3
7
|
</div>
|
|
@@ -1,10 +1,36 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const template = require('./list-default.html');
|
|
4
|
+
const appendCSS = require('../appendCSS');
|
|
5
|
+
const vanillatoast = require('vanillatoasts');
|
|
6
|
+
|
|
7
|
+
appendCSS(require('./list-default.css'));
|
|
4
8
|
|
|
5
9
|
module.exports = app => app.component('list-default', {
|
|
6
10
|
template: template,
|
|
7
|
-
props: ['value'],
|
|
11
|
+
props: ['value', 'allude'],
|
|
12
|
+
methods: {
|
|
13
|
+
copyText(value) {
|
|
14
|
+
const storage = document.createElement('textarea');
|
|
15
|
+
storage.value = value;
|
|
16
|
+
const elem = this.$refs.itemData
|
|
17
|
+
elem.appendChild(storage);
|
|
18
|
+
storage.select();
|
|
19
|
+
storage.setSelectionRange(0, 99999);
|
|
20
|
+
document.execCommand('copy');
|
|
21
|
+
elem.removeChild(storage);
|
|
22
|
+
vanillatoast.create({
|
|
23
|
+
title: 'Text copied!',
|
|
24
|
+
type: 'success',
|
|
25
|
+
timeout: 3000,
|
|
26
|
+
icon: 'images/success.png',
|
|
27
|
+
positionClass: 'bottomRight'
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
goToDoc(id) {
|
|
31
|
+
this.$router.push({ path: `/model/${this.allude}/document/${id}`});
|
|
32
|
+
}
|
|
33
|
+
},
|
|
8
34
|
computed: {
|
|
9
35
|
displayValue() {
|
|
10
36
|
if (this.value === null) {
|
|
@@ -14,6 +40,9 @@ module.exports = app => app.component('list-default', {
|
|
|
14
40
|
return 'undefined';
|
|
15
41
|
}
|
|
16
42
|
return this.value;
|
|
43
|
+
},
|
|
44
|
+
hasReference() {
|
|
45
|
+
return this.allude;
|
|
17
46
|
}
|
|
18
47
|
}
|
|
19
48
|
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const template = require('./list-string.html');
|
|
4
|
+
const appendCSS = require('../appendCSS');
|
|
5
|
+
const vanillatoast = require('vanillatoasts');
|
|
6
|
+
appendCSS(require('./list-string.css'));
|
|
7
|
+
|
|
8
|
+
module.exports = app => app.component('list-string', {
|
|
9
|
+
template: template,
|
|
10
|
+
props: ['value'],
|
|
11
|
+
methods: {
|
|
12
|
+
copyText(value) {
|
|
13
|
+
const storage = document.createElement('textarea');
|
|
14
|
+
storage.value = value;
|
|
15
|
+
const elem = this.$refs.itemData;
|
|
16
|
+
elem.appendChild(storage);
|
|
17
|
+
storage.select();
|
|
18
|
+
storage.setSelectionRange(0, 99999);
|
|
19
|
+
document.execCommand('copy');
|
|
20
|
+
elem.removeChild(storage);
|
|
21
|
+
vanillatoast.create({
|
|
22
|
+
title: 'Text copied!',
|
|
23
|
+
type: 'success',
|
|
24
|
+
timeout: 3000,
|
|
25
|
+
icon: 'images/success.png',
|
|
26
|
+
positionClass: 'bottomRight'
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
computed: {
|
|
31
|
+
displayValue() {
|
|
32
|
+
if (!this.value) {
|
|
33
|
+
return this.value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (this.value.length < 50) {
|
|
37
|
+
return this.value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return this.value.slice(0, 47) + '...';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
|
|
28
28
|
transition: all 0.3s ease;
|
|
29
29
|
font-family: Helvetica, Arial, sans-serif;
|
|
30
|
+
position: relative;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
.modal-header {
|
|
@@ -72,4 +73,11 @@
|
|
|
72
73
|
.modal-leave-active .modal-container {
|
|
73
74
|
-webkit-transform: scale(1.1);
|
|
74
75
|
transform: scale(1.1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.modal-container .modal-exit {
|
|
79
|
+
position: absolute;
|
|
80
|
+
right: 0.25em;
|
|
81
|
+
top: 0.25em;
|
|
82
|
+
cursor: pointer;
|
|
75
83
|
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.models .documents .documents-container {
|
|
26
|
-
margin-top:
|
|
26
|
+
margin-top: 60px;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.models .documents table {
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
position: sticky;
|
|
45
45
|
top: 0px;
|
|
46
46
|
background-color: white;
|
|
47
|
+
z-index: 1;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
.models .documents table th:after {
|
|
@@ -105,4 +106,9 @@
|
|
|
105
106
|
border: 1px solid #ddd;
|
|
106
107
|
border-radius: 3px;
|
|
107
108
|
width: calc(100% - 1em);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.sort-arrow {
|
|
112
|
+
padding-left: 10px;
|
|
113
|
+
padding-right: 10px;
|
|
108
114
|
}
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
<div class="search-input">
|
|
14
14
|
<form @submit.prevent="search">
|
|
15
15
|
<input class="search-text" type="text" placeholder="Filter or text" v-model="searchText" />
|
|
16
|
+
<div>Number of Documents: {{numDocuments}}</div>
|
|
16
17
|
</form>
|
|
18
|
+
|
|
17
19
|
</div>
|
|
18
20
|
<div class="buttons">
|
|
19
21
|
<button @click="shouldShowExportModal = true">Export</button>
|
|
@@ -26,14 +28,20 @@
|
|
|
26
28
|
<th v-for="path in schemaPaths">
|
|
27
29
|
{{path.path}}
|
|
28
30
|
<span class="path-type">
|
|
29
|
-
({{path.instance
|
|
31
|
+
({{(path.instance || 'unknown')}})
|
|
30
32
|
</span>
|
|
33
|
+
<span class="sort-arrow" @click="sortDocs(1, path.path)">{{sortBy[path.path] == 1 ? 'X' : '↑'}}</span>
|
|
34
|
+
<span class="sort-arrow" @click="sortDocs(-1, path.path)">{{sortBy[path.path] == -1 ? 'X' : '↓'}}</span>
|
|
31
35
|
</th>
|
|
32
36
|
</thead>
|
|
33
37
|
<tbody>
|
|
34
38
|
<tr v-for="document in documents" @click="$router.push('/model/' + currentModel + '/document/' + document._id)" :key="document._id">
|
|
35
39
|
<td v-for="schemaPath in schemaPaths">
|
|
36
|
-
<component
|
|
40
|
+
<component
|
|
41
|
+
:is="getComponentForPath(schemaPath)"
|
|
42
|
+
:value="getValueForPath(document, schemaPath.path)"
|
|
43
|
+
:allude="getReferenceModel(schemaPath)">
|
|
44
|
+
</component>
|
|
37
45
|
</td>
|
|
38
46
|
</tr>
|
|
39
47
|
</tbody>
|
|
@@ -41,6 +49,7 @@
|
|
|
41
49
|
</div>
|
|
42
50
|
<modal v-if="shouldShowExportModal">
|
|
43
51
|
<template v-slot:body>
|
|
52
|
+
<div class="modal-exit" @click="shouldShowExportModal = false">×</div>
|
|
44
53
|
<export-query-results
|
|
45
54
|
:schemaPaths="schemaPaths"
|
|
46
55
|
:filter="filter"
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const api = require('../api');
|
|
4
4
|
const template = require('./models.html');
|
|
5
|
+
const EJSON = require('ejson');
|
|
6
|
+
const mpath = require('mpath');
|
|
5
7
|
|
|
6
8
|
const appendCSS = require('../appendCSS');
|
|
7
9
|
|
|
@@ -15,13 +17,15 @@ module.exports = app => app.component('models', {
|
|
|
15
17
|
currentModel: null,
|
|
16
18
|
documents: [],
|
|
17
19
|
schemaPaths: [],
|
|
20
|
+
numDocuments: 0,
|
|
18
21
|
status: 'init',
|
|
19
22
|
edittingDoc: null,
|
|
20
23
|
docEdits: null,
|
|
21
24
|
filter: null,
|
|
22
25
|
searchText: '',
|
|
23
26
|
shouldShowExportModal: false,
|
|
24
|
-
shouldExport: {}
|
|
27
|
+
shouldExport: {},
|
|
28
|
+
sortBy: {}
|
|
25
29
|
}),
|
|
26
30
|
created() {
|
|
27
31
|
this.currentModel = this.model;
|
|
@@ -31,42 +35,48 @@ module.exports = app => app.component('models', {
|
|
|
31
35
|
if (this.currentModel == null && this.models.length > 0) {
|
|
32
36
|
this.currentModel = this.models[0];
|
|
33
37
|
}
|
|
38
|
+
if (this.$route.query?.search) {
|
|
39
|
+
this.searchText = this.$route.query.search;
|
|
40
|
+
this.filter = eval(`(${this.$route.query.search})`);
|
|
41
|
+
this.filter = EJSON.stringify(this.filter);
|
|
42
|
+
}
|
|
34
43
|
|
|
35
44
|
if (this.currentModel != null) {
|
|
36
|
-
|
|
37
|
-
this.documents = docs;
|
|
38
|
-
this.schemaPaths = Object.keys(schemaPaths).sort((k1, k2) => {
|
|
39
|
-
if (k1 === '_id' && k2 !== '_id') {
|
|
40
|
-
return -1;
|
|
41
|
-
}
|
|
42
|
-
if (k1 !== '_id' && k2 === '_id') {
|
|
43
|
-
return 1;
|
|
44
|
-
}
|
|
45
|
-
return 0;
|
|
46
|
-
}).map(key => schemaPaths[key]);
|
|
47
|
-
|
|
48
|
-
this.shouldExport = {};
|
|
49
|
-
for (const { path } of this.schemaPaths) {
|
|
50
|
-
this.shouldExport[path] = true;
|
|
51
|
-
}
|
|
45
|
+
await this.getDocuments();
|
|
52
46
|
}
|
|
53
47
|
|
|
54
48
|
this.status = 'loaded';
|
|
55
49
|
},
|
|
56
50
|
methods: {
|
|
51
|
+
async sortDocs(num, path) {
|
|
52
|
+
let sorted = false;
|
|
53
|
+
if (this.sortBy[path] == num) {
|
|
54
|
+
sorted = true;
|
|
55
|
+
}
|
|
56
|
+
for (const key in this.sortBy) {
|
|
57
|
+
delete this.sortBy[key];
|
|
58
|
+
}
|
|
59
|
+
if (!sorted) {
|
|
60
|
+
this.sortBy[path] = num;
|
|
61
|
+
}
|
|
62
|
+
await this.getDocuments();
|
|
63
|
+
},
|
|
57
64
|
async search() {
|
|
58
|
-
if (this.searchText) {
|
|
65
|
+
if (this.searchText && Object.keys(this.searchText).length) {
|
|
59
66
|
this.filter = eval(`(${this.searchText})`);
|
|
67
|
+
this.filter = EJSON.stringify(this.filter);
|
|
68
|
+
this.$router.push({ path: this.$route.path, query: { search: this.searchText }})
|
|
60
69
|
} else {
|
|
61
70
|
this.filter = {};
|
|
71
|
+
this.$router.push({ path: this.$route.path });
|
|
62
72
|
}
|
|
63
|
-
|
|
64
73
|
await this.getDocuments();
|
|
65
74
|
},
|
|
66
75
|
async getDocuments() {
|
|
67
|
-
const { docs, schemaPaths } = await api.Model.getDocuments({
|
|
76
|
+
const { docs, schemaPaths, numDocs } = await api.Model.getDocuments({
|
|
68
77
|
model: this.currentModel,
|
|
69
|
-
filter: this.filter
|
|
78
|
+
filter: this.filter,
|
|
79
|
+
sort: this.sortBy
|
|
70
80
|
});
|
|
71
81
|
this.documents = docs;
|
|
72
82
|
this.schemaPaths = Object.keys(schemaPaths).sort((k1, k2) => {
|
|
@@ -78,6 +88,7 @@ module.exports = app => app.component('models', {
|
|
|
78
88
|
}
|
|
79
89
|
return 0;
|
|
80
90
|
}).map(key => schemaPaths[key]);
|
|
91
|
+
this.numDocuments = numDocs;
|
|
81
92
|
|
|
82
93
|
this.shouldExport = {};
|
|
83
94
|
for (const { path } of this.schemaPaths) {
|
|
@@ -88,8 +99,20 @@ module.exports = app => app.component('models', {
|
|
|
88
99
|
if (schemaPath.instance === 'Array') {
|
|
89
100
|
return 'list-array';
|
|
90
101
|
}
|
|
102
|
+
if (schemaPath.instance === 'String') {
|
|
103
|
+
return 'list-string';
|
|
104
|
+
}
|
|
105
|
+
if (schemaPath.instance == 'Embedded') {
|
|
106
|
+
return 'list-subdocument';
|
|
107
|
+
}
|
|
91
108
|
return 'list-default';
|
|
92
109
|
},
|
|
110
|
+
getReferenceModel(schemaPath) {
|
|
111
|
+
return schemaPath.ref;
|
|
112
|
+
},
|
|
113
|
+
getValueForPath(doc, path) {
|
|
114
|
+
return mpath.get(path, doc);
|
|
115
|
+
},
|
|
93
116
|
async saveDocEdits() {
|
|
94
117
|
const res = await api.Model.updateDocument({
|
|
95
118
|
model: this.currentModel,
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
.navbar .nav-left img {
|
|
23
|
-
height:
|
|
23
|
+
height: 32px;
|
|
24
24
|
vertical-align: middle;
|
|
25
25
|
margin-right: 0.5em;
|
|
26
|
-
margin-top:
|
|
26
|
+
margin-top: 8px;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.navbar .nav-right {
|
|
@@ -159,4 +159,4 @@
|
|
|
159
159
|
transition: all 0.3s ease;
|
|
160
160
|
z-index: 10000;
|
|
161
161
|
}
|
|
162
|
-
}
|
|
162
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<div class="navbar">
|
|
2
2
|
<div class="nav-left">
|
|
3
3
|
<router-link to="/">
|
|
4
|
-
<img src="images/
|
|
4
|
+
<img src="images/logo.svg" alt="Mongoose Studio Logo" />
|
|
5
5
|
</router-link>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="nav-right">
|
|
8
8
|
</div>
|
|
9
9
|
<div style="clear: both"></div>
|
|
10
|
-
</div>
|
|
10
|
+
</div>
|
package/package.json
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mongoosejs/studio",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"archetype": "0.13.0",
|
|
6
6
|
"csv-stringify": "6.3.0",
|
|
7
|
-
"
|
|
7
|
+
"ejson": "^2.2.3",
|
|
8
|
+
"extrovert": "0.0.20",
|
|
9
|
+
"vanillatoasts": "^1.6.0"
|
|
8
10
|
},
|
|
9
11
|
"peerDependencies": {
|
|
10
12
|
"express": "4.x",
|
|
11
|
-
"mongoose": "
|
|
13
|
+
"mongoose": "7.x"
|
|
12
14
|
},
|
|
13
15
|
"devDependencies": {
|
|
14
16
|
"axios": "1.2.2",
|
|
15
17
|
"express": "4.x",
|
|
16
18
|
"mocha": "10.2.0",
|
|
17
|
-
"mongoose": "
|
|
18
|
-
"
|
|
19
|
-
"
|
|
19
|
+
"mongoose": "7.x",
|
|
20
|
+
"vue": "3.x",
|
|
21
|
+
"webpack": "5.75.0"
|
|
20
22
|
}
|
|
21
|
-
}
|
|
23
|
+
}
|