@mongoosejs/studio 0.5.7 → 0.5.9
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/integrations/streamLLM.js +1 -1
- package/eslint.config.js +3 -1
- package/frontend/public/app.js +1454 -667
- package/frontend/public/tw.css +113 -0
- package/frontend/src/_util/calendar.js +155 -0
- package/frontend/src/_util/document-search-autocomplete.js +105 -23
- package/frontend/src/custom-date-picker/custom-date-picker.html +183 -0
- package/frontend/src/custom-date-picker/custom-date-picker.js +407 -0
- package/frontend/src/document/document.css +62 -0
- package/frontend/src/document/document.html +113 -2
- package/frontend/src/document/document.js +171 -9
- package/frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.html +1 -0
- package/frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.js +1 -1
- package/frontend/src/document-details/document-details.css +1 -63
- package/frontend/src/document-details/document-details.html +0 -122
- package/frontend/src/document-details/document-details.js +2 -138
- package/frontend/src/document-details/document-property/document-property.html +3 -2
- package/frontend/src/edit-date/edit-date.html +8 -6
- package/frontend/src/edit-date/edit-date.js +0 -20
- package/frontend/src/index.js +1 -1
- package/frontend/src/models/document-search/document-search.html +78 -22
- package/frontend/src/models/document-search/document-search.js +64 -2
- package/frontend/src/models/models.js +46 -10
- package/frontend/src/routes.js +2 -1
- package/package.json +1 -1
|
@@ -273,9 +273,120 @@
|
|
|
273
273
|
:changes="changes"
|
|
274
274
|
:invalid="invalid"
|
|
275
275
|
:viewMode="viewMode"
|
|
276
|
-
:model="model"
|
|
277
|
-
@add-field="addField"
|
|
278
276
|
@view-mode-change="updateViewMode"></document-details>
|
|
277
|
+
<modal v-if="showAddFieldModal">
|
|
278
|
+
<template v-slot:body>
|
|
279
|
+
<div class="modal-exit" @click="closeAddFieldModal">×</div>
|
|
280
|
+
<div class="add-field-modal">
|
|
281
|
+
<div class="mb-6">
|
|
282
|
+
<h2 class="text-xl font-semibold text-content mb-2">Add New Field</h2>
|
|
283
|
+
<p class="text-sm text-gray-600">Create a new field for this document</p>
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
<form @submit.prevent="handleAddFieldSubmit" class="space-y-4">
|
|
287
|
+
<div>
|
|
288
|
+
<label for="fieldName" class="block text-sm font-medium text-content-secondary mb-1">
|
|
289
|
+
Field Name *
|
|
290
|
+
</label>
|
|
291
|
+
<input
|
|
292
|
+
id="fieldName"
|
|
293
|
+
v-model="fieldData.name"
|
|
294
|
+
type="text"
|
|
295
|
+
required
|
|
296
|
+
placeholder="Enter field name..."
|
|
297
|
+
class="w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
298
|
+
:class="{ 'border-red-500': fieldErrors.name }"
|
|
299
|
+
/>
|
|
300
|
+
<p v-if="fieldErrors.name" class="mt-1 text-sm text-red-600">{{fieldErrors.name}}</p>
|
|
301
|
+
<p v-if="fieldData.name && getTransformedFieldName() !== fieldData.name.trim()" class="mt-1 text-sm text-blue-600">
|
|
302
|
+
Field name will be: <code class="bg-blue-50 px-1 py-0.5 rounded text-xs">{{getTransformedFieldName()}}</code>
|
|
303
|
+
</p>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<div>
|
|
307
|
+
<label for="fieldType" class="block text-sm font-medium text-content-secondary mb-1">
|
|
308
|
+
Field Type *
|
|
309
|
+
</label>
|
|
310
|
+
<select
|
|
311
|
+
id="fieldType"
|
|
312
|
+
v-model="fieldData.type"
|
|
313
|
+
required
|
|
314
|
+
class="w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
315
|
+
:class="{ 'border-red-500': fieldErrors.type }"
|
|
316
|
+
>
|
|
317
|
+
<option value="">Select field type...</option>
|
|
318
|
+
<option v-for="type in allFieldTypes" :key="type" :value="type">
|
|
319
|
+
{{type}}
|
|
320
|
+
</option>
|
|
321
|
+
</select>
|
|
322
|
+
<p v-if="fieldErrors.type" class="mt-1 text-sm text-red-600">{{fieldErrors.type}}</p>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<div>
|
|
326
|
+
<label for="fieldValue" class="block text-sm font-medium text-content-secondary mb-1">
|
|
327
|
+
Initial Value
|
|
328
|
+
</label>
|
|
329
|
+
|
|
330
|
+
<custom-date-picker
|
|
331
|
+
v-if="shouldUseDatePicker"
|
|
332
|
+
id="fieldValue"
|
|
333
|
+
mode="date"
|
|
334
|
+
:value="fieldData.value"
|
|
335
|
+
:class="{ 'border-red-500': fieldErrors.value, 'max-w-[400px]': true }"
|
|
336
|
+
:horizontal="true"
|
|
337
|
+
@input="fieldData.value = $event"
|
|
338
|
+
/>
|
|
339
|
+
|
|
340
|
+
<input
|
|
341
|
+
v-else-if="!shouldUseAce"
|
|
342
|
+
v-model="fieldData.value"
|
|
343
|
+
type="text"
|
|
344
|
+
id="fieldValue"
|
|
345
|
+
placeholder="Enter initial value (optional)..."
|
|
346
|
+
class="w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
347
|
+
:class="{ 'border-red-500': fieldErrors.value }"
|
|
348
|
+
/>
|
|
349
|
+
|
|
350
|
+
<ace-editor
|
|
351
|
+
v-else
|
|
352
|
+
id="fieldValue"
|
|
353
|
+
v-model="fieldData.value"
|
|
354
|
+
mode="json"
|
|
355
|
+
:line-numbers="true"
|
|
356
|
+
:min-lines="3"
|
|
357
|
+
class="add-field-ace-editor w-full min-h-[100px]"
|
|
358
|
+
:class="{ 'border-red-500': fieldErrors.value }"
|
|
359
|
+
/>
|
|
360
|
+
|
|
361
|
+
<p v-if="fieldErrors.value" class="mt-1 text-sm text-red-600">{{fieldErrors.value}}</p>
|
|
362
|
+
<p class="mt-1 text-xs text-content-tertiary">
|
|
363
|
+
<span v-if="shouldUseDatePicker">Select a date or leave empty for null/undefined values.</span>
|
|
364
|
+
<span v-else-if="shouldUseAce">Leave empty for null/undefined values. Use valid JSON format.</span>
|
|
365
|
+
<span v-else>Leave empty for null/undefined values.</span>
|
|
366
|
+
</p>
|
|
367
|
+
</div>
|
|
368
|
+
|
|
369
|
+
<div class="flex justify-end gap-3 pt-4 border-t border-edge">
|
|
370
|
+
<button
|
|
371
|
+
type="button"
|
|
372
|
+
@click="closeAddFieldModal"
|
|
373
|
+
class="px-4 py-2 text-sm font-medium text-content-secondary bg-surface border border-edge-strong rounded-md hover:bg-page focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
|
374
|
+
>
|
|
375
|
+
Cancel
|
|
376
|
+
</button>
|
|
377
|
+
<button
|
|
378
|
+
type="submit"
|
|
379
|
+
:disabled="isSubmittingField"
|
|
380
|
+
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"
|
|
381
|
+
>
|
|
382
|
+
<span v-if="isSubmittingField">Adding...</span>
|
|
383
|
+
<span v-else>Add Field</span>
|
|
384
|
+
</button>
|
|
385
|
+
</div>
|
|
386
|
+
</form>
|
|
387
|
+
</div>
|
|
388
|
+
</template>
|
|
389
|
+
</modal>
|
|
279
390
|
<modal v-if="shouldShowConfirmModal">
|
|
280
391
|
<template v-slot:body>
|
|
281
392
|
<div class="modal-exit" @click="shouldShowConfirmModal = false;">×</div>
|
|
@@ -27,7 +27,15 @@ module.exports = app => app.component('document', {
|
|
|
27
27
|
shouldShowDeleteModal: false,
|
|
28
28
|
shouldShowCloneModal: false,
|
|
29
29
|
shouldShowValidationModal: false,
|
|
30
|
+
showAddFieldModal: false,
|
|
30
31
|
validationResult: null,
|
|
32
|
+
fieldData: {
|
|
33
|
+
name: '',
|
|
34
|
+
type: '',
|
|
35
|
+
value: ''
|
|
36
|
+
},
|
|
37
|
+
fieldErrors: {},
|
|
38
|
+
isSubmittingField: false,
|
|
31
39
|
previousQuery: null,
|
|
32
40
|
lastUpdatedAt: null,
|
|
33
41
|
isRefreshing: false,
|
|
@@ -89,6 +97,33 @@ module.exports = app => app.component('document', {
|
|
|
89
97
|
}
|
|
90
98
|
return shortcuts;
|
|
91
99
|
},
|
|
100
|
+
allFieldTypes() {
|
|
101
|
+
const types = new Set(['String', 'Number', 'Boolean', 'Date', 'Array', 'Object']);
|
|
102
|
+
|
|
103
|
+
(this.schemaPaths || []).forEach(path => {
|
|
104
|
+
if (path.instance) {
|
|
105
|
+
types.add(path.instance);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
Object.keys(this.document || {}).forEach(key => {
|
|
110
|
+
if ((this.schemaPaths || []).some(path => path.path === key)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const fieldType = this.getFieldType(this.document[key]);
|
|
114
|
+
if (fieldType && fieldType !== 'unknown' && fieldType !== 'null') {
|
|
115
|
+
types.add(fieldType);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return Array.from(types).sort();
|
|
120
|
+
},
|
|
121
|
+
shouldUseAce() {
|
|
122
|
+
return ['Array', 'Object', 'Embedded'].includes(this.fieldData.type);
|
|
123
|
+
},
|
|
124
|
+
shouldUseDatePicker() {
|
|
125
|
+
return this.fieldData.type === 'Date';
|
|
126
|
+
},
|
|
92
127
|
isLambda() {
|
|
93
128
|
return !!window?.MONGOOSE_STUDIO_CONFIG?.isLambda;
|
|
94
129
|
}
|
|
@@ -285,16 +320,143 @@ module.exports = app => app.component('document', {
|
|
|
285
320
|
showClonedDocument(doc) {
|
|
286
321
|
this.$router.push({ path: `/model/${this.model}/document/${doc._id}` });
|
|
287
322
|
},
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
323
|
+
addField() {
|
|
324
|
+
this.showAddFieldModal = true;
|
|
325
|
+
},
|
|
326
|
+
closeAddFieldModal() {
|
|
327
|
+
this.showAddFieldModal = false;
|
|
328
|
+
this.resetFieldForm();
|
|
329
|
+
},
|
|
330
|
+
resetFieldForm() {
|
|
331
|
+
this.fieldData = {
|
|
332
|
+
name: '',
|
|
333
|
+
type: '',
|
|
334
|
+
value: ''
|
|
335
|
+
};
|
|
336
|
+
this.fieldErrors = {};
|
|
337
|
+
this.isSubmittingField = false;
|
|
338
|
+
},
|
|
339
|
+
getTransformedFieldName() {
|
|
340
|
+
const trimmedName = this.fieldData.name.trim();
|
|
341
|
+
return trimmedName
|
|
342
|
+
.replace(/[^a-zA-Z0-9_$]/g, '_')
|
|
343
|
+
.replace(/^[^a-zA-Z_$]+/, '');
|
|
344
|
+
},
|
|
345
|
+
validateFieldForm() {
|
|
346
|
+
this.fieldErrors = {};
|
|
347
|
+
|
|
348
|
+
const trimmedName = this.fieldData.name.trim();
|
|
349
|
+
if (!trimmedName) {
|
|
350
|
+
this.fieldErrors.name = 'Field name is required';
|
|
351
|
+
} else {
|
|
352
|
+
const transformedName = this.getTransformedFieldName();
|
|
353
|
+
if (!transformedName) {
|
|
354
|
+
this.fieldErrors.name = 'Field name contains only invalid characters';
|
|
355
|
+
} else if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(transformedName)) {
|
|
356
|
+
this.fieldErrors.name = 'Field name must start with a letter, underscore, or $ and contain only letters, numbers, underscores, and $';
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!this.fieldData.type) {
|
|
361
|
+
this.fieldErrors.type = 'Field type is required';
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (this.fieldData.value && this.fieldData.value.trim()) {
|
|
365
|
+
if (['Object', 'Array'].includes(this.fieldData.type)) {
|
|
366
|
+
try {
|
|
367
|
+
JSON.parse(this.fieldData.value);
|
|
368
|
+
} catch (err) {
|
|
369
|
+
this.fieldErrors.value = 'Invalid JSON format for object/array type';
|
|
370
|
+
}
|
|
371
|
+
} else if (this.fieldData.type === 'Number') {
|
|
372
|
+
if (isNaN(Number(this.fieldData.value))) {
|
|
373
|
+
this.fieldErrors.value = 'Invalid number format';
|
|
374
|
+
}
|
|
375
|
+
} else if (this.fieldData.type === 'Boolean') {
|
|
376
|
+
const lowerValue = this.fieldData.value.toLowerCase();
|
|
377
|
+
if (!['true', 'false', '1', '0', 'yes', 'no'].includes(lowerValue)) {
|
|
378
|
+
this.fieldErrors.value = 'Invalid boolean value (use true/false, 1/0, yes/no)';
|
|
379
|
+
}
|
|
380
|
+
} else if (this.fieldData.type === 'Date') {
|
|
381
|
+
const dateValue = new Date(this.fieldData.value);
|
|
382
|
+
if (isNaN(dateValue.getTime())) {
|
|
383
|
+
this.fieldErrors.value = 'Invalid date format';
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return Object.keys(this.fieldErrors).length === 0;
|
|
389
|
+
},
|
|
390
|
+
parseFieldValue(value, type) {
|
|
391
|
+
if (!value || !value.trim()) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
switch (type) {
|
|
396
|
+
case 'Number':
|
|
397
|
+
return Number(value);
|
|
398
|
+
case 'Boolean':
|
|
399
|
+
return ['true', '1', 'yes'].includes(value.toLowerCase());
|
|
400
|
+
case 'Date':
|
|
401
|
+
return new Date(value);
|
|
402
|
+
case 'Object':
|
|
403
|
+
case 'Array':
|
|
404
|
+
return JSON.parse(value);
|
|
405
|
+
default:
|
|
406
|
+
return value;
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
async handleAddFieldSubmit() {
|
|
410
|
+
if (!this.validateFieldForm()) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
this.isSubmittingField = true;
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
const fieldName = this.getTransformedFieldName();
|
|
418
|
+
const fieldValue = this.parseFieldValue(this.fieldData.value, this.fieldData.type);
|
|
419
|
+
|
|
420
|
+
const { doc } = await api.Model.addField({
|
|
421
|
+
model: this.model,
|
|
422
|
+
_id: this.document._id,
|
|
423
|
+
fieldName,
|
|
424
|
+
fieldValue
|
|
425
|
+
});
|
|
426
|
+
this.document = doc;
|
|
427
|
+
this.closeAddFieldModal();
|
|
296
428
|
|
|
297
|
-
|
|
429
|
+
this.$toast.success(`Field added! Field "${fieldName}" has been added to the document`);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
console.error('Error adding field:', error);
|
|
432
|
+
this.fieldErrors.value = 'Error adding field';
|
|
433
|
+
} finally {
|
|
434
|
+
this.isSubmittingField = false;
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
getFieldType(value) {
|
|
438
|
+
if (value === null || value === undefined) {
|
|
439
|
+
return 'null';
|
|
440
|
+
}
|
|
441
|
+
if (Array.isArray(value)) {
|
|
442
|
+
return 'Array';
|
|
443
|
+
}
|
|
444
|
+
if (value instanceof Date) {
|
|
445
|
+
return 'Date';
|
|
446
|
+
}
|
|
447
|
+
if (typeof value === 'object') {
|
|
448
|
+
return 'Object';
|
|
449
|
+
}
|
|
450
|
+
if (typeof value === 'number') {
|
|
451
|
+
return 'Number';
|
|
452
|
+
}
|
|
453
|
+
if (typeof value === 'boolean') {
|
|
454
|
+
return 'Boolean';
|
|
455
|
+
}
|
|
456
|
+
if (typeof value === 'string') {
|
|
457
|
+
return 'String';
|
|
458
|
+
}
|
|
459
|
+
return 'unknown';
|
|
298
460
|
},
|
|
299
461
|
updateViewMode(mode) {
|
|
300
462
|
this.viewMode = mode;
|
|
@@ -4,7 +4,7 @@ const template = require('./date-view-mode-picker.html');
|
|
|
4
4
|
|
|
5
5
|
module.exports = app => app.component('date-view-mode-picker', {
|
|
6
6
|
template,
|
|
7
|
-
props: ['viewMode', 'path'],
|
|
7
|
+
props: ['viewMode', 'path', 'editting'],
|
|
8
8
|
emits: ['update:viewMode'],
|
|
9
9
|
computed: {
|
|
10
10
|
format() {
|
|
@@ -22,68 +22,6 @@
|
|
|
22
22
|
margin-top: -7px;
|
|
23
23
|
}
|
|
24
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
|
-
/* Ace editor styling in modal */
|
|
74
|
-
.add-field-modal .add-field-ace-editor.ace_editor {
|
|
75
|
-
border: 1px solid #d1d5db;
|
|
76
|
-
border-radius: 0.375rem;
|
|
77
|
-
font-size: 14px;
|
|
78
|
-
min-height: 100px;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.add-field-modal .add-field-ace-editor:focus-within.ace_editor,
|
|
82
|
-
.add-field-modal .add-field-ace-editor.ace_focus {
|
|
83
|
-
border-color: #3b82f6;
|
|
84
|
-
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
25
|
/* JSON View Styles */
|
|
88
26
|
.json-view {
|
|
89
27
|
width: 100%;
|
|
@@ -113,4 +51,4 @@
|
|
|
113
51
|
|
|
114
52
|
.json-view pre::-webkit-scrollbar-thumb:hover {
|
|
115
53
|
background: #555;
|
|
116
|
-
}
|
|
54
|
+
}
|
|
@@ -172,126 +172,4 @@
|
|
|
172
172
|
<pre class="text-sm font-mono text-gray-800 whitespace-pre">{{formattedJson}}</pre>
|
|
173
173
|
</div>
|
|
174
174
|
</div>
|
|
175
|
-
|
|
176
|
-
<!-- Add Field Modal -->
|
|
177
|
-
<modal v-if="showAddFieldModal">
|
|
178
|
-
<template v-slot:body>
|
|
179
|
-
<div class="modal-exit" @click="closeAddFieldModal">×</div>
|
|
180
|
-
<div class="add-field-modal">
|
|
181
|
-
<div class="mb-6">
|
|
182
|
-
<h2 class="text-xl font-semibold text-content mb-2">Add New Field</h2>
|
|
183
|
-
<p class="text-sm text-gray-600">Create a new field for this document</p>
|
|
184
|
-
</div>
|
|
185
|
-
|
|
186
|
-
<form @submit.prevent="handleAddFieldSubmit" class="space-y-4">
|
|
187
|
-
<!-- Field Name -->
|
|
188
|
-
<div>
|
|
189
|
-
<label for="fieldName" class="block text-sm font-medium text-content-secondary mb-1">
|
|
190
|
-
Field Name *
|
|
191
|
-
</label>
|
|
192
|
-
<input
|
|
193
|
-
id="fieldName"
|
|
194
|
-
v-model="fieldData.name"
|
|
195
|
-
type="text"
|
|
196
|
-
required
|
|
197
|
-
placeholder="Enter field name..."
|
|
198
|
-
class="w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
199
|
-
:class="{ 'border-red-500': fieldErrors.name }"
|
|
200
|
-
/>
|
|
201
|
-
<p v-if="fieldErrors.name" class="mt-1 text-sm text-red-600">{{fieldErrors.name}}</p>
|
|
202
|
-
<p v-if="fieldData.name && getTransformedFieldName() !== fieldData.name.trim()" class="mt-1 text-sm text-blue-600">
|
|
203
|
-
Field name will be: <code class="bg-blue-50 px-1 py-0.5 rounded text-xs">{{getTransformedFieldName()}}</code>
|
|
204
|
-
</p>
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
<!-- Field Type -->
|
|
208
|
-
<div>
|
|
209
|
-
<label for="fieldType" class="block text-sm font-medium text-content-secondary mb-1">
|
|
210
|
-
Field Type *
|
|
211
|
-
</label>
|
|
212
|
-
<select
|
|
213
|
-
id="fieldType"
|
|
214
|
-
v-model="fieldData.type"
|
|
215
|
-
required
|
|
216
|
-
class="w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
217
|
-
:class="{ 'border-red-500': fieldErrors.type }"
|
|
218
|
-
>
|
|
219
|
-
<option value="">Select field type...</option>
|
|
220
|
-
<option v-for="type in allFieldTypes" :key="type" :value="type">
|
|
221
|
-
{{type}}
|
|
222
|
-
</option>
|
|
223
|
-
</select>
|
|
224
|
-
<p v-if="fieldErrors.type" class="mt-1 text-sm text-red-600">{{fieldErrors.type}}</p>
|
|
225
|
-
</div>
|
|
226
|
-
|
|
227
|
-
<!-- Field Value -->
|
|
228
|
-
<div>
|
|
229
|
-
<label for="fieldValue" class="block text-sm font-medium text-content-secondary mb-1">
|
|
230
|
-
Initial Value
|
|
231
|
-
</label>
|
|
232
|
-
|
|
233
|
-
<!-- Date picker for Date type -->
|
|
234
|
-
<input
|
|
235
|
-
v-if="shouldUseDatePicker"
|
|
236
|
-
v-model="fieldData.value"
|
|
237
|
-
type="date"
|
|
238
|
-
id="fieldValue"
|
|
239
|
-
class="w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
240
|
-
:class="{ 'border-red-500': fieldErrors.value }"
|
|
241
|
-
/>
|
|
242
|
-
|
|
243
|
-
<!-- Simple input for basic types -->
|
|
244
|
-
<input
|
|
245
|
-
v-else-if="!shouldUseAce"
|
|
246
|
-
v-model="fieldData.value"
|
|
247
|
-
type="text"
|
|
248
|
-
id="fieldValue"
|
|
249
|
-
placeholder="Enter initial value (optional)..."
|
|
250
|
-
class="w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
251
|
-
:class="{ 'border-red-500': fieldErrors.value }"
|
|
252
|
-
/>
|
|
253
|
-
|
|
254
|
-
<!-- Ace editor for complex types (Array/Object/Embedded) -->
|
|
255
|
-
<ace-editor
|
|
256
|
-
v-else
|
|
257
|
-
id="fieldValue"
|
|
258
|
-
v-model="fieldData.value"
|
|
259
|
-
mode="json"
|
|
260
|
-
:line-numbers="true"
|
|
261
|
-
:min-lines="3"
|
|
262
|
-
class="add-field-ace-editor w-full min-h-[100px]"
|
|
263
|
-
:class="{ 'border-red-500': fieldErrors.value }"
|
|
264
|
-
/>
|
|
265
|
-
|
|
266
|
-
<p v-if="fieldErrors.value" class="mt-1 text-sm text-red-600">{{fieldErrors.value}}</p>
|
|
267
|
-
<p class="mt-1 text-xs text-content-tertiary">
|
|
268
|
-
<span v-if="shouldUseDatePicker">Select a date or leave empty for null/undefined values.</span>
|
|
269
|
-
<span v-else-if="shouldUseAce">Leave empty for null/undefined values. Use valid JSON format.</span>
|
|
270
|
-
<span v-else>Leave empty for null/undefined values.</span>
|
|
271
|
-
</p>
|
|
272
|
-
</div>
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
<!-- Action Buttons -->
|
|
276
|
-
<div class="flex justify-end gap-3 pt-4 border-t border-edge">
|
|
277
|
-
<button
|
|
278
|
-
type="button"
|
|
279
|
-
@click="closeAddFieldModal"
|
|
280
|
-
class="px-4 py-2 text-sm font-medium text-content-secondary bg-surface border border-edge-strong rounded-md hover:bg-page focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
|
281
|
-
>
|
|
282
|
-
Cancel
|
|
283
|
-
</button>
|
|
284
|
-
<button
|
|
285
|
-
type="submit"
|
|
286
|
-
:disabled="isSubmittingField"
|
|
287
|
-
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"
|
|
288
|
-
>
|
|
289
|
-
<span v-if="isSubmittingField">Adding...</span>
|
|
290
|
-
<span v-else>Add Field</span>
|
|
291
|
-
</button>
|
|
292
|
-
</div>
|
|
293
|
-
</form>
|
|
294
|
-
</div>
|
|
295
|
-
</template>
|
|
296
|
-
</modal>
|
|
297
175
|
</div>
|