@mongoosejs/studio 0.0.128 → 0.0.130

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.
@@ -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
- <div v-for="path in schemaPaths" class="value">
3
- <document-property
4
- :path="path"
5
- :document="document"
6
- :schemaPaths="schemaPaths"
7
- :editting="editting"
8
- :changes="changes"
9
- :invalid="invalid"></document-property>
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
- <div v-for="path in virtuals" class="mb-2">
12
- <div class="p-1 mb-1 bg-slate-100">
13
- {{path.name}}
14
- <span class="path-type">
15
- (virtual)
16
- </span>
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
- <div v-if="path.value == null" class="text-sky-800">
19
- {{'' + path.value}}
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
- <div v-else>
22
- {{path.value}}
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
- </div>
116
+
117
+ <!-- Add Field Modal -->
118
+ <modal v-if="showAddFieldModal">
119
+ <template v-slot:body>
120
+ <div class="modal-exit" @click="closeAddFieldModal">&times;</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>