@mongoosejs/studio 0.0.128 → 0.0.129
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/addField.js +54 -0
- package/backend/actions/Model/getDocument.js +3 -1
- package/backend/actions/Model/getDocuments.js +14 -5
- package/backend/actions/Model/getDocumentsStream.js +14 -5
- package/backend/actions/Model/index.js +1 -0
- package/backend/actions/Model/updateDocument.js +23 -6
- package/backend/actions/Model/updateDocuments.js +23 -5
- package/frontend/public/app.js +613 -45
- package/frontend/public/tw.css +219 -2
- package/frontend/src/api.js +6 -0
- package/frontend/src/document/document.css +8 -0
- package/frontend/src/document/document.html +102 -8
- package/frontend/src/document/document.js +34 -1
- package/frontend/src/document-details/document-details.css +98 -0
- package/frontend/src/document-details/document-details.html +231 -19
- package/frontend/src/document-details/document-details.js +328 -3
- package/frontend/src/document-details/document-property/document-property.css +15 -0
- package/frontend/src/document-details/document-property/document-property.html +75 -31
- package/frontend/src/document-details/document-property/document-property.js +43 -2
- package/frontend/src/edit-boolean/edit-boolean.html +47 -0
- package/frontend/src/edit-boolean/edit-boolean.js +38 -0
- package/frontend/src/models/models.js +79 -30
- package/frontend/src/mothership.js +4 -0
- package/frontend/src/navbar/navbar.html +1 -1
- package/frontend/src/team/team.html +63 -4
- package/frontend/src/team/team.js +47 -1
- package/package.json +1 -1
|
@@ -20,4 +20,102 @@
|
|
|
20
20
|
.document-details .date-position {
|
|
21
21
|
float: right;
|
|
22
22
|
margin-top: -7px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Add Field Modal Styles */
|
|
26
|
+
.add-field-modal {
|
|
27
|
+
max-width: 500px;
|
|
28
|
+
width: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.add-field-modal .modal-exit {
|
|
32
|
+
position: absolute;
|
|
33
|
+
top: 15px;
|
|
34
|
+
right: 20px;
|
|
35
|
+
font-size: 24px;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
color: #6b7280;
|
|
38
|
+
z-index: 10;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.add-field-modal .modal-exit:hover {
|
|
42
|
+
color: #374151;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.add-field-modal form {
|
|
46
|
+
max-height: 70vh;
|
|
47
|
+
overflow-y: auto;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.add-field-modal input[type="text"],
|
|
51
|
+
.add-field-modal input[type="email"],
|
|
52
|
+
.add-field-modal input[type="password"],
|
|
53
|
+
.add-field-modal select,
|
|
54
|
+
.add-field-modal textarea {
|
|
55
|
+
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.add-field-modal input:focus,
|
|
59
|
+
.add-field-modal select:focus,
|
|
60
|
+
.add-field-modal textarea:focus {
|
|
61
|
+
border-color: #3b82f6;
|
|
62
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.add-field-modal .border-red-500 {
|
|
66
|
+
border-color: #ef4444 !important;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.add-field-modal .border-red-500:focus {
|
|
70
|
+
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1) !important;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* CodeMirror styling in modal */
|
|
74
|
+
.add-field-modal .CodeMirror {
|
|
75
|
+
border: 1px solid #d1d5db;
|
|
76
|
+
border-radius: 0.375rem;
|
|
77
|
+
font-size: 14px;
|
|
78
|
+
height: auto;
|
|
79
|
+
min-height: 100px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.add-field-modal .CodeMirror:focus-within {
|
|
83
|
+
border-color: #3b82f6;
|
|
84
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.add-field-modal .CodeMirror.CodeMirror-focused {
|
|
88
|
+
border-color: #3b82f6;
|
|
89
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* JSON View Styles */
|
|
93
|
+
.json-view {
|
|
94
|
+
width: 100%;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.json-view pre {
|
|
98
|
+
margin: 0;
|
|
99
|
+
max-height: 70vh;
|
|
100
|
+
overflow: auto;
|
|
101
|
+
line-height: 1.5;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.json-view pre::-webkit-scrollbar {
|
|
105
|
+
width: 8px;
|
|
106
|
+
height: 8px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.json-view pre::-webkit-scrollbar-track {
|
|
110
|
+
background: #f1f1f1;
|
|
111
|
+
border-radius: 4px;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.json-view pre::-webkit-scrollbar-thumb {
|
|
115
|
+
background: #888;
|
|
116
|
+
border-radius: 4px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.json-view pre::-webkit-scrollbar-thumb:hover {
|
|
120
|
+
background: #555;
|
|
23
121
|
}
|
|
@@ -1,25 +1,237 @@
|
|
|
1
1
|
<div class="document-details">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
<!-- View Toggle and Search/Filter Bar -->
|
|
3
|
+
<div class="mb-4 mt-4">
|
|
4
|
+
|
|
5
|
+
<!-- Search and Filter Bar (only show in fields view) -->
|
|
6
|
+
<div v-if="viewMode === 'fields'" class="flex md:gap-3">
|
|
7
|
+
<!-- Search Bar -->
|
|
8
|
+
<div class="relative flex-1">
|
|
9
|
+
<input
|
|
10
|
+
ref="searchInput"
|
|
11
|
+
v-model="searchQuery"
|
|
12
|
+
type="text"
|
|
13
|
+
placeholder="Search fields..."
|
|
14
|
+
class="w-full px-4 py-2 pl-10 pr-4 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
15
|
+
/>
|
|
16
|
+
<div class="absolute inset-y-0 left-0 flex items-center pl-3">
|
|
17
|
+
<svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
18
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
|
19
|
+
</svg>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<!-- Data Type Filter -->
|
|
24
|
+
<div class="relative">
|
|
25
|
+
<select
|
|
26
|
+
v-model="selectedType"
|
|
27
|
+
class="hidden md:block px-4 py-2 pr-8 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white min-w-[140px] appearance-none"
|
|
28
|
+
>
|
|
29
|
+
<option value="">All Types</option>
|
|
30
|
+
<option v-for="type in availableTypes" :key="type" :value="type">
|
|
31
|
+
{{type}}
|
|
32
|
+
</option>
|
|
33
|
+
</select>
|
|
34
|
+
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
|
35
|
+
<svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
36
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
|
37
|
+
</svg>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<!-- Add Field Button -->
|
|
42
|
+
<button
|
|
43
|
+
@click="openAddFieldModal"
|
|
44
|
+
class="hidden md:block px-4 py-2 text-sm font-medium text-white bg-green-600 hover:bg-green-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 flex items-center gap-2"
|
|
45
|
+
>
|
|
46
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
47
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
|
48
|
+
</svg>
|
|
49
|
+
Add Field
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
10
52
|
</div>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
53
|
+
|
|
54
|
+
<!-- Fields View -->
|
|
55
|
+
<div v-if="viewMode === 'fields'">
|
|
56
|
+
<!-- Schema Paths -->
|
|
57
|
+
<div v-for="path in filteredSchemaPaths" :key="path" class="value">
|
|
58
|
+
<document-property
|
|
59
|
+
:path="path"
|
|
60
|
+
:document="document"
|
|
61
|
+
:schemaPaths="schemaPaths"
|
|
62
|
+
:editting="editting"
|
|
63
|
+
:changes="changes"
|
|
64
|
+
:invalid="invalid"></document-property>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<!-- Virtual Fields -->
|
|
68
|
+
<div v-for="path in filteredVirtuals" class="border border-gray-200 rounded-lg mb-2">
|
|
69
|
+
<!-- Virtual Field Header (Always Visible) -->
|
|
70
|
+
<div
|
|
71
|
+
@click="toggleVirtualField(path.name)"
|
|
72
|
+
class="p-3 bg-slate-50 hover:bg-slate-100 cursor-pointer flex items-center justify-between border-b border-gray-200"
|
|
73
|
+
>
|
|
74
|
+
<div class="flex items-center">
|
|
75
|
+
<svg
|
|
76
|
+
:class="isVirtualFieldCollapsed(path.name) ? 'rotate-0' : 'rotate-90'"
|
|
77
|
+
class="w-4 h-4 text-gray-500 mr-2 transition-transform duration-200"
|
|
78
|
+
fill="none"
|
|
79
|
+
stroke="currentColor"
|
|
80
|
+
viewBox="0 0 24 24"
|
|
81
|
+
>
|
|
82
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
83
|
+
</svg>
|
|
84
|
+
<span class="font-medium text-gray-900">{{path.name}}</span>
|
|
85
|
+
<span v-if="path.isVirtual" class="ml-2 text-sm text-purple-600">(virtual - {{getVirtualFieldType(path)}})</span>
|
|
86
|
+
<span v-else class="ml-2 text-sm text-blue-600">(user-added - {{getVirtualFieldType(path)}})</span>
|
|
87
|
+
</div>
|
|
17
88
|
</div>
|
|
18
|
-
|
|
19
|
-
|
|
89
|
+
|
|
90
|
+
<!-- Virtual Field Content (Collapsible) -->
|
|
91
|
+
<div v-if="!isVirtualFieldCollapsed(path.name)" class="p-3">
|
|
92
|
+
<div v-if="path.value == null" class="text-sky-800">
|
|
93
|
+
{{'' + path.value}}
|
|
94
|
+
</div>
|
|
95
|
+
<div v-else>
|
|
96
|
+
{{path.value}}
|
|
97
|
+
</div>
|
|
20
98
|
</div>
|
|
21
|
-
|
|
22
|
-
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<!-- No Results Message -->
|
|
102
|
+
<div v-if="searchQuery && filteredSchemaPaths.length === 0 && filteredVirtuals.length === 0" class="text-center py-8 text-gray-500">
|
|
103
|
+
<svg class="w-12 h-12 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
104
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.29-1.009-5.824-2.709M15 6.291A7.962 7.962 0 0012 5c-2.34 0-4.29 1.009-5.824 2.709"></path>
|
|
105
|
+
</svg>
|
|
106
|
+
<p>No fields found matching "{{searchQuery}}"</p>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<!-- JSON View -->
|
|
111
|
+
<div v-if="viewMode === 'json'" class="json-view">
|
|
112
|
+
<div class="border border-gray-300 rounded-lg bg-gray-50 p-4 overflow-auto">
|
|
113
|
+
<pre class="text-sm font-mono text-gray-800 whitespace-pre">{{formattedJson}}</pre>
|
|
23
114
|
</div>
|
|
24
115
|
</div>
|
|
25
|
-
|
|
116
|
+
|
|
117
|
+
<!-- Add Field Modal -->
|
|
118
|
+
<modal v-if="showAddFieldModal">
|
|
119
|
+
<template v-slot:body>
|
|
120
|
+
<div class="modal-exit" @click="closeAddFieldModal">×</div>
|
|
121
|
+
<div class="add-field-modal">
|
|
122
|
+
<div class="mb-6">
|
|
123
|
+
<h2 class="text-xl font-semibold text-gray-900 mb-2">Add New Field</h2>
|
|
124
|
+
<p class="text-sm text-gray-600">Create a new field for this document</p>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<form @submit.prevent="handleAddFieldSubmit" class="space-y-4">
|
|
128
|
+
<!-- Field Name -->
|
|
129
|
+
<div>
|
|
130
|
+
<label for="fieldName" class="block text-sm font-medium text-gray-700 mb-1">
|
|
131
|
+
Field Name *
|
|
132
|
+
</label>
|
|
133
|
+
<input
|
|
134
|
+
id="fieldName"
|
|
135
|
+
v-model="fieldData.name"
|
|
136
|
+
type="text"
|
|
137
|
+
required
|
|
138
|
+
placeholder="Enter field name..."
|
|
139
|
+
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
140
|
+
:class="{ 'border-red-500': fieldErrors.name }"
|
|
141
|
+
/>
|
|
142
|
+
<p v-if="fieldErrors.name" class="mt-1 text-sm text-red-600">{{fieldErrors.name}}</p>
|
|
143
|
+
<p v-if="fieldData.name && getTransformedFieldName() !== fieldData.name.trim()" class="mt-1 text-sm text-blue-600">
|
|
144
|
+
Field name will be: <code class="bg-blue-50 px-1 py-0.5 rounded text-xs">{{getTransformedFieldName()}}</code>
|
|
145
|
+
</p>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<!-- Field Type -->
|
|
149
|
+
<div>
|
|
150
|
+
<label for="fieldType" class="block text-sm font-medium text-gray-700 mb-1">
|
|
151
|
+
Field Type *
|
|
152
|
+
</label>
|
|
153
|
+
<select
|
|
154
|
+
id="fieldType"
|
|
155
|
+
v-model="fieldData.type"
|
|
156
|
+
required
|
|
157
|
+
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
158
|
+
:class="{ 'border-red-500': fieldErrors.type }"
|
|
159
|
+
>
|
|
160
|
+
<option value="">Select field type...</option>
|
|
161
|
+
<option v-for="type in allFieldTypes" :key="type" :value="type">
|
|
162
|
+
{{type}}
|
|
163
|
+
</option>
|
|
164
|
+
</select>
|
|
165
|
+
<p v-if="fieldErrors.type" class="mt-1 text-sm text-red-600">{{fieldErrors.type}}</p>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<!-- Field Value -->
|
|
169
|
+
<div>
|
|
170
|
+
<label for="fieldValue" class="block text-sm font-medium text-gray-700 mb-1">
|
|
171
|
+
Initial Value
|
|
172
|
+
</label>
|
|
173
|
+
|
|
174
|
+
<!-- Date picker for Date type -->
|
|
175
|
+
<input
|
|
176
|
+
v-if="shouldUseDatePicker"
|
|
177
|
+
v-model="fieldData.value"
|
|
178
|
+
type="date"
|
|
179
|
+
id="fieldValue"
|
|
180
|
+
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
181
|
+
:class="{ 'border-red-500': fieldErrors.value }"
|
|
182
|
+
/>
|
|
183
|
+
|
|
184
|
+
<!-- Simple input for basic types -->
|
|
185
|
+
<input
|
|
186
|
+
v-else-if="!shouldUseCodeMirror"
|
|
187
|
+
v-model="fieldData.value"
|
|
188
|
+
type="text"
|
|
189
|
+
id="fieldValue"
|
|
190
|
+
placeholder="Enter initial value (optional)..."
|
|
191
|
+
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
192
|
+
:class="{ 'border-red-500': fieldErrors.value }"
|
|
193
|
+
/>
|
|
194
|
+
|
|
195
|
+
<!-- CodeMirror textarea for complex types -->
|
|
196
|
+
<textarea
|
|
197
|
+
v-else
|
|
198
|
+
ref="fieldValueEditor"
|
|
199
|
+
id="fieldValue"
|
|
200
|
+
rows="3"
|
|
201
|
+
placeholder="Enter initial value (optional)..."
|
|
202
|
+
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
203
|
+
:class="{ 'border-red-500': fieldErrors.value }"
|
|
204
|
+
></textarea>
|
|
205
|
+
|
|
206
|
+
<p v-if="fieldErrors.value" class="mt-1 text-sm text-red-600">{{fieldErrors.value}}</p>
|
|
207
|
+
<p class="mt-1 text-xs text-gray-500">
|
|
208
|
+
<span v-if="shouldUseDatePicker">Select a date or leave empty for null/undefined values.</span>
|
|
209
|
+
<span v-else-if="shouldUseCodeMirror">Leave empty for null/undefined values. Use valid JSON format.</span>
|
|
210
|
+
<span v-else>Leave empty for null/undefined values.</span>
|
|
211
|
+
</p>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
<!-- Action Buttons -->
|
|
216
|
+
<div class="flex justify-end gap-3 pt-4 border-t border-gray-200">
|
|
217
|
+
<button
|
|
218
|
+
type="button"
|
|
219
|
+
@click="closeAddFieldModal"
|
|
220
|
+
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
|
221
|
+
>
|
|
222
|
+
Cancel
|
|
223
|
+
</button>
|
|
224
|
+
<button
|
|
225
|
+
type="submit"
|
|
226
|
+
:disabled="isSubmittingField"
|
|
227
|
+
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
228
|
+
>
|
|
229
|
+
<span v-if="isSubmittingField">Adding...</span>
|
|
230
|
+
<span v-else>Add Field</span>
|
|
231
|
+
</button>
|
|
232
|
+
</div>
|
|
233
|
+
</form>
|
|
234
|
+
</div>
|
|
235
|
+
</template>
|
|
236
|
+
</modal>
|
|
237
|
+
</div>
|