@mongoosejs/studio 0.1.16 → 0.1.17
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/updateDocument.js +3 -0
- package/backend/actions/Model/updateDocuments.js +3 -0
- package/frontend/public/app.js +684 -447
- package/frontend/public/tw.css +99 -19
- package/frontend/src/array-utils.js +66 -0
- package/frontend/src/dashboard/dashboard.js +1 -1
- package/frontend/src/detail-array/detail-array.html +25 -3
- package/frontend/src/detail-array/detail-array.js +22 -6
- package/frontend/src/document-details/document-details.html +61 -12
- package/frontend/src/document-details/document-details.js +28 -0
- package/frontend/src/document-details/document-property/document-property.html +41 -3
- package/frontend/src/document-details/document-property/document-property.js +47 -2
- package/frontend/src/edit-array/edit-array.html +5 -2
- package/frontend/src/edit-array/edit-array.js +79 -23
- package/frontend/src/index.js +2 -1
- package/frontend/src/list-array/list-array.html +1 -1
- package/frontend/src/list-array/list-array.js +0 -2
- package/frontend/src/models/models.html +6 -1
- package/frontend/src/models/models.js +32 -0
- package/package.json +1 -1
- package/frontend/src/edit-array/edit-array.css +0 -3
- package/frontend/src/list-array/list-array.css +0 -8
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
5
|
const mpath = require('mpath');
|
|
6
|
+
const { inspect } = require('node-inspect-extracted');
|
|
6
7
|
const template = require('./document-property.html');
|
|
7
8
|
|
|
8
9
|
const appendCSS = require('../../appendCSS');
|
|
@@ -38,10 +39,32 @@ module.exports = app => app.component('document-property', {
|
|
|
38
39
|
}
|
|
39
40
|
return String(value);
|
|
40
41
|
},
|
|
42
|
+
_arrayValueData() {
|
|
43
|
+
const value = this.getValueForPath(this.path.path);
|
|
44
|
+
return {
|
|
45
|
+
value: Array.isArray(value) ? value : [],
|
|
46
|
+
isArray: Array.isArray(value)
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
isArray() {
|
|
50
|
+
return this._arrayValueData.isArray;
|
|
51
|
+
},
|
|
52
|
+
arrayValue() {
|
|
53
|
+
return this._arrayValueData.value;
|
|
54
|
+
},
|
|
41
55
|
needsTruncation() {
|
|
42
|
-
//
|
|
56
|
+
// For arrays, check if it has more than 3 items (regardless of expansion state)
|
|
57
|
+
if (this.isArray) {
|
|
58
|
+
const arr = this.arrayValue;
|
|
59
|
+
return arr && arr.length > 3;
|
|
60
|
+
}
|
|
61
|
+
// For other types, truncate if value is longer than 200 characters
|
|
43
62
|
return this.valueAsString.length > 200;
|
|
44
63
|
},
|
|
64
|
+
shouldShowTruncated() {
|
|
65
|
+
// For other types, show truncated if needs truncation and not expanded
|
|
66
|
+
return this.needsTruncation && !this.isValueExpanded;
|
|
67
|
+
},
|
|
45
68
|
displayValue() {
|
|
46
69
|
if (!this.needsTruncation || this.isValueExpanded) {
|
|
47
70
|
return this.getValueForPath(this.path.path);
|
|
@@ -51,9 +74,24 @@ module.exports = app => app.component('document-property', {
|
|
|
51
74
|
},
|
|
52
75
|
truncatedString() {
|
|
53
76
|
if (this.needsTruncation && !this.isValueExpanded) {
|
|
54
|
-
|
|
77
|
+
// Arrays are handled in template, so this is for non-arrays
|
|
78
|
+
if (!this.isArray) {
|
|
79
|
+
return this.valueAsString.substring(0, 200) + '...';
|
|
80
|
+
}
|
|
55
81
|
}
|
|
56
82
|
return this.valueAsString;
|
|
83
|
+
},
|
|
84
|
+
truncatedArrayItems() {
|
|
85
|
+
if (this.isArray && this.needsTruncation && !this.isValueExpanded) {
|
|
86
|
+
return this.arrayValue.slice(0, 2);
|
|
87
|
+
}
|
|
88
|
+
return [];
|
|
89
|
+
},
|
|
90
|
+
remainingArrayCount() {
|
|
91
|
+
if (this.isArray && this.needsTruncation && !this.isValueExpanded) {
|
|
92
|
+
return this.arrayValue.length - 2;
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
57
95
|
}
|
|
58
96
|
},
|
|
59
97
|
methods: {
|
|
@@ -79,6 +117,9 @@ module.exports = app => app.component('document-property', {
|
|
|
79
117
|
if (path.instance === 'Embedded') {
|
|
80
118
|
return 'edit-subdocument';
|
|
81
119
|
}
|
|
120
|
+
if (path.instance === 'Mixed') {
|
|
121
|
+
return 'edit-subdocument';
|
|
122
|
+
}
|
|
82
123
|
if (path.instance === 'Boolean') {
|
|
83
124
|
return 'edit-boolean';
|
|
84
125
|
}
|
|
@@ -91,6 +132,10 @@ module.exports = app => app.component('document-property', {
|
|
|
91
132
|
props.enumValues = path.enum;
|
|
92
133
|
}
|
|
93
134
|
}
|
|
135
|
+
if (path.instance === 'Array') {
|
|
136
|
+
props.path = path;
|
|
137
|
+
props.schemaPaths = this.schemaPaths;
|
|
138
|
+
}
|
|
94
139
|
return props;
|
|
95
140
|
},
|
|
96
141
|
getValueForPath(path) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
<div class="
|
|
1
|
+
<div class="w-full">
|
|
2
|
+
<!-- CodeMirror editor for the entire array -->
|
|
2
3
|
<textarea
|
|
3
4
|
ref="arrayEditor"
|
|
4
|
-
class="w-full border border-gray-300 p-
|
|
5
|
+
class="w-full border border-gray-300 p-2 font-mono"
|
|
6
|
+
:style="{ minHeight: '300px' }">
|
|
7
|
+
</textarea>
|
|
5
8
|
</div>
|
|
@@ -10,43 +10,99 @@ const ObjectId = new Proxy(BSON.ObjectId, {
|
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
const appendCSS = require('../appendCSS');
|
|
14
|
-
appendCSS(require('./edit-array.css'));
|
|
15
13
|
|
|
16
14
|
module.exports = app => app.component('edit-array', {
|
|
17
15
|
template: template,
|
|
18
16
|
props: ['value'],
|
|
19
|
-
data
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.$refs.arrayEditor.value = this.currentValue;
|
|
25
|
-
this.editor = CodeMirror.fromTextArea(this.$refs.arrayEditor, {
|
|
26
|
-
mode: 'javascript',
|
|
27
|
-
lineNumbers: true
|
|
28
|
-
});
|
|
29
|
-
this.editor.on('change', ev => {
|
|
30
|
-
this.currentValue = this.editor.getValue();
|
|
31
|
-
});
|
|
17
|
+
data() {
|
|
18
|
+
return {
|
|
19
|
+
arrayValue: [],
|
|
20
|
+
arrayEditor: null
|
|
21
|
+
};
|
|
32
22
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
methods: {
|
|
24
|
+
initializeArray() {
|
|
25
|
+
if (this.value == null) {
|
|
26
|
+
this.arrayValue = [];
|
|
27
|
+
} else if (Array.isArray(this.value)) {
|
|
28
|
+
this.arrayValue = JSON.parse(JSON.stringify(this.value));
|
|
29
|
+
} else {
|
|
30
|
+
this.arrayValue = [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Update CodeMirror editor if it exists
|
|
34
|
+
this.$nextTick(() => {
|
|
35
|
+
if (this.arrayEditor) {
|
|
36
|
+
const arrayStr = JSON.stringify(this.arrayValue, null, 2);
|
|
37
|
+
this.arrayEditor.setValue(arrayStr);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
initializeArrayEditor() {
|
|
42
|
+
this.$nextTick(() => {
|
|
43
|
+
const textareaRef = this.$refs.arrayEditor;
|
|
44
|
+
const textarea = Array.isArray(textareaRef) ? textareaRef[0] : textareaRef;
|
|
45
|
+
if (textarea && !this.arrayEditor) {
|
|
46
|
+
const arrayStr = JSON.stringify(this.arrayValue, null, 2);
|
|
47
|
+
textarea.value = arrayStr;
|
|
48
|
+
this.arrayEditor = CodeMirror.fromTextArea(textarea, {
|
|
49
|
+
mode: 'javascript',
|
|
50
|
+
lineNumbers: true
|
|
51
|
+
});
|
|
52
|
+
this.arrayEditor.on('change', () => {
|
|
53
|
+
this.updateArrayFromEditor();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
updateArrayFromEditor() {
|
|
59
|
+
if (!this.arrayEditor) {
|
|
37
60
|
return;
|
|
38
61
|
}
|
|
39
62
|
try {
|
|
40
|
-
const
|
|
41
|
-
|
|
63
|
+
const value = this.arrayEditor.getValue();
|
|
64
|
+
if (value.trim() === '') {
|
|
65
|
+
this.arrayValue = [];
|
|
66
|
+
} else {
|
|
67
|
+
this.arrayValue = JSON.parse(value);
|
|
68
|
+
}
|
|
69
|
+
this.emitUpdate();
|
|
70
|
+
} catch (err) {
|
|
71
|
+
// Invalid JSON, don't update
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
emitUpdate() {
|
|
75
|
+
try {
|
|
76
|
+
this.$emit('input', this.arrayValue);
|
|
42
77
|
} catch (err) {
|
|
43
78
|
this.$emit('error', err);
|
|
44
79
|
}
|
|
45
80
|
}
|
|
46
81
|
},
|
|
82
|
+
mounted() {
|
|
83
|
+
this.initializeArray();
|
|
84
|
+
this.initializeArrayEditor();
|
|
85
|
+
},
|
|
47
86
|
beforeDestroy() {
|
|
48
|
-
if (this.
|
|
49
|
-
this.
|
|
87
|
+
if (this.arrayEditor) {
|
|
88
|
+
this.arrayEditor.toTextArea();
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
watch: {
|
|
92
|
+
value: {
|
|
93
|
+
handler(newValue, oldValue) {
|
|
94
|
+
// Initialize array when value prop changes
|
|
95
|
+
this.initializeArray();
|
|
96
|
+
// Update array editor if it exists
|
|
97
|
+
if (this.arrayEditor) {
|
|
98
|
+
this.$nextTick(() => {
|
|
99
|
+
const arrayStr = JSON.stringify(this.arrayValue, null, 2);
|
|
100
|
+
this.arrayEditor.setValue(arrayStr);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
deep: true,
|
|
105
|
+
immediate: true
|
|
50
106
|
}
|
|
51
107
|
},
|
|
52
108
|
emits: ['input', 'error']
|
package/frontend/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ console.log(`Mongoose Studio Version ${version}`);
|
|
|
9
9
|
|
|
10
10
|
const api = require('./api');
|
|
11
11
|
const format = require('./format');
|
|
12
|
+
const arrayUtils = require('./array-utils');
|
|
12
13
|
const mothership = require('./mothership');
|
|
13
14
|
const { routes } = require('./routes');
|
|
14
15
|
const vanillatoasts = require('vanillatoasts');
|
|
@@ -149,7 +150,7 @@ router.beforeEach((to, from, next) => {
|
|
|
149
150
|
}
|
|
150
151
|
});
|
|
151
152
|
|
|
152
|
-
app.config.globalProperties = { format };
|
|
153
|
+
app.config.globalProperties = { format, arrayUtils };
|
|
153
154
|
app.use(router);
|
|
154
155
|
|
|
155
156
|
app.mount('#content');
|
|
@@ -193,7 +193,12 @@
|
|
|
193
193
|
<div v-for="index in mongoDBIndexes" class="w-full flex items-center">
|
|
194
194
|
<div class="grow shrink text-left flex justify-between items-center" v-if="index.name != '_id_'">
|
|
195
195
|
<div>
|
|
196
|
-
<div class="font-bold
|
|
196
|
+
<div class="font-bold flex items-center gap-2">
|
|
197
|
+
<div>{{ index.name }}</div>
|
|
198
|
+
<div v-if="isTTLIndex(index)" class="rounded-full bg-ultramarine-100 px-2 py-0.5 text-xs font-semibold text-ultramarine-700">
|
|
199
|
+
TTL: {{ formatTTL(index.expireAfterSeconds) }}
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
197
202
|
<div class="text-sm font-mono">{{ JSON.stringify(index.key) }}</div>
|
|
198
203
|
</div>
|
|
199
204
|
<div>
|
|
@@ -237,6 +237,38 @@ module.exports = app => app.component('models', {
|
|
|
237
237
|
this.mongoDBIndexes = mongoDBIndexes;
|
|
238
238
|
this.schemaIndexes = schemaIndexes;
|
|
239
239
|
},
|
|
240
|
+
isTTLIndex(index) {
|
|
241
|
+
return index != null && index.expireAfterSeconds != null;
|
|
242
|
+
},
|
|
243
|
+
formatTTL(expireAfterSeconds) {
|
|
244
|
+
if (typeof expireAfterSeconds !== 'number') {
|
|
245
|
+
return '';
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
let remaining = expireAfterSeconds;
|
|
249
|
+
const days = Math.floor(remaining / (24 * 60 * 60));
|
|
250
|
+
remaining = remaining % (24 * 60 * 60);
|
|
251
|
+
const hours = Math.floor(remaining / (60 * 60));
|
|
252
|
+
remaining = remaining % (60 * 60);
|
|
253
|
+
const minutes = Math.floor(remaining / 60);
|
|
254
|
+
const seconds = remaining % 60;
|
|
255
|
+
|
|
256
|
+
const parts = [];
|
|
257
|
+
if (days > 0) {
|
|
258
|
+
parts.push(`${days} day${days === 1 ? '' : 's'}`);
|
|
259
|
+
}
|
|
260
|
+
if (hours > 0) {
|
|
261
|
+
parts.push(`${hours} hour${hours === 1 ? '' : 's'}`);
|
|
262
|
+
}
|
|
263
|
+
if (minutes > 0) {
|
|
264
|
+
parts.push(`${minutes} minute${minutes === 1 ? '' : 's'}`);
|
|
265
|
+
}
|
|
266
|
+
if (seconds > 0 || parts.length === 0) {
|
|
267
|
+
parts.push(`${seconds} second${seconds === 1 ? '' : 's'}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return parts.join(', ');
|
|
271
|
+
},
|
|
240
272
|
checkIndexLocation(indexName) {
|
|
241
273
|
if (this.schemaIndexes.find(x => x.name == indexName) && this.mongoDBIndexes.find(x => x.name == indexName)) {
|
|
242
274
|
return 'text-gray-500';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mongoosejs/studio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
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": {
|