@edgedev/create-edge-app 1.1.25 → 1.1.26

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.
Files changed (111) hide show
  1. package/README.md +55 -20
  2. package/{agent.md → agents.md} +2 -0
  3. package/bin/cli.js +6 -6
  4. package/edge/components/auth/login.vue +384 -0
  5. package/edge/components/auth/register.vue +396 -0
  6. package/edge/components/auth.vue +108 -0
  7. package/edge/components/autoFileUpload.vue +215 -0
  8. package/edge/components/billing.vue +8 -0
  9. package/edge/components/buttonDivider.vue +14 -0
  10. package/edge/components/chip.vue +34 -0
  11. package/edge/components/clipboardButton.vue +42 -0
  12. package/edge/components/cms/block.vue +529 -0
  13. package/edge/components/cms/blockApi.vue +212 -0
  14. package/edge/components/cms/blockEditor.vue +725 -0
  15. package/edge/components/cms/blockInput.vue +66 -0
  16. package/edge/components/cms/blockPicker.vue +486 -0
  17. package/edge/components/cms/blockRender.vue +78 -0
  18. package/edge/components/cms/blockSheetContent.vue +28 -0
  19. package/edge/components/cms/codeEditor.vue +466 -0
  20. package/edge/components/cms/fontUpload.vue +327 -0
  21. package/edge/components/cms/htmlContent.vue +807 -0
  22. package/edge/components/cms/init_blocks/api_with_subarrays.html +17 -0
  23. package/edge/components/cms/init_blocks/array_with_collection.html +7 -0
  24. package/edge/components/cms/init_blocks/array_with_objects.html +7 -0
  25. package/edge/components/cms/init_blocks/carousel.html +103 -0
  26. package/edge/components/cms/init_blocks/contact_us.html +69 -0
  27. package/edge/components/cms/init_blocks/content_with_left_image.html +27 -0
  28. package/edge/components/cms/init_blocks/footer.html +24 -0
  29. package/edge/components/cms/init_blocks/header_divider.html +7 -0
  30. package/edge/components/cms/init_blocks/hero.html +35 -0
  31. package/edge/components/cms/init_blocks/hero_carousel.html +52 -0
  32. package/edge/components/cms/init_blocks/newsletter.html +117 -0
  33. package/edge/components/cms/init_blocks/post_content.html +7 -0
  34. package/edge/components/cms/init_blocks/post_title_header.html +21 -0
  35. package/edge/components/cms/init_blocks/posts_list.html +20 -0
  36. package/edge/components/cms/init_blocks/properties_showcase.html +100 -0
  37. package/edge/components/cms/init_blocks/property_carousel.html +59 -0
  38. package/edge/components/cms/init_blocks/property_detail.html +112 -0
  39. package/edge/components/cms/init_blocks/property_detail_header.html +34 -0
  40. package/edge/components/cms/init_blocks/property_results.html +137 -0
  41. package/edge/components/cms/init_blocks/property_search.html +75 -0
  42. package/edge/components/cms/init_blocks/simple_array.html +7 -0
  43. package/edge/components/cms/mediaCard.vue +116 -0
  44. package/edge/components/cms/mediaManager.vue +386 -0
  45. package/edge/components/cms/menu.vue +1103 -0
  46. package/edge/components/cms/optionsSelect.vue +107 -0
  47. package/edge/components/cms/page.vue +1785 -0
  48. package/edge/components/cms/posts.vue +1083 -0
  49. package/edge/components/cms/site.vue +1298 -0
  50. package/edge/components/cms/themeDefaultMenu.vue +548 -0
  51. package/edge/components/cms/themeEditor.vue +426 -0
  52. package/edge/components/dashboard.vue +776 -0
  53. package/edge/components/editor.vue +671 -0
  54. package/edge/components/fileTree.vue +72 -0
  55. package/edge/components/files.vue +89 -0
  56. package/edge/components/formSubtypes/myOrgs.vue +214 -0
  57. package/edge/components/formSubtypes/users.vue +336 -0
  58. package/edge/components/functionChips.vue +57 -0
  59. package/edge/components/gError.vue +98 -0
  60. package/edge/components/gHelper.vue +67 -0
  61. package/edge/components/gInput.vue +1331 -0
  62. package/edge/components/loggingIn.vue +41 -0
  63. package/edge/components/menu.vue +137 -0
  64. package/edge/components/menuContent.vue +132 -0
  65. package/edge/components/myAccount.vue +317 -0
  66. package/edge/components/myOrganizations.vue +75 -0
  67. package/edge/components/myProfile.vue +122 -0
  68. package/edge/components/orgSwitcher.vue +25 -0
  69. package/edge/components/organizationMembers.vue +522 -0
  70. package/edge/components/organizationSettings.vue +271 -0
  71. package/edge/components/shad/breadcrumbs.vue +35 -0
  72. package/edge/components/shad/button.vue +43 -0
  73. package/edge/components/shad/checkbox.vue +73 -0
  74. package/edge/components/shad/combobox.vue +238 -0
  75. package/edge/components/shad/datepicker.vue +184 -0
  76. package/edge/components/shad/dialog.vue +32 -0
  77. package/edge/components/shad/dropdownMenu.vue +54 -0
  78. package/edge/components/shad/dropdownMenuItem.vue +21 -0
  79. package/edge/components/shad/form.vue +59 -0
  80. package/edge/components/shad/html.vue +877 -0
  81. package/edge/components/shad/input.vue +139 -0
  82. package/edge/components/shad/number.vue +109 -0
  83. package/edge/components/shad/select.vue +151 -0
  84. package/edge/components/shad/selectTags.vue +278 -0
  85. package/edge/components/shad/switch.vue +67 -0
  86. package/edge/components/shad/tags.vue +137 -0
  87. package/edge/components/shad/textarea.vue +102 -0
  88. package/edge/components/shad/typeMoney.vue +167 -0
  89. package/edge/components/sideBar.vue +288 -0
  90. package/edge/components/sideBarContent.vue +268 -0
  91. package/edge/components/sidebarProvider.vue +33 -0
  92. package/edge/components/tooltip.vue +16 -0
  93. package/edge/components/userMenu.vue +148 -0
  94. package/edge/components/v/alert.vue +59 -0
  95. package/edge/components/v/alertTitle.vue +18 -0
  96. package/edge/components/v/card.vue +53 -0
  97. package/edge/components/v/cardActions.vue +18 -0
  98. package/edge/components/v/cardText.vue +18 -0
  99. package/edge/components/v/cardTitle.vue +20 -0
  100. package/edge/components/v/col.vue +56 -0
  101. package/edge/components/v/list.vue +46 -0
  102. package/edge/components/v/listItem.vue +26 -0
  103. package/edge/components/v/listItemTitle.vue +18 -0
  104. package/edge/components/v/row.vue +42 -0
  105. package/edge/components/v/toolbar.vue +24 -0
  106. package/edge/composables/global.ts +519 -0
  107. package/edge-pull.sh +2 -0
  108. package/edge-push.sh +1 -0
  109. package/edge-status.sh +14 -0
  110. package/package.json +1 -1
  111. package/edge-components-install.sh +0 -1
@@ -0,0 +1,529 @@
1
+ <script setup>
2
+ import { useVModel } from '@vueuse/core'
3
+ import { ImagePlus, Plus } from 'lucide-vue-next'
4
+ const props = defineProps({
5
+ modelValue: {
6
+ type: Object,
7
+ required: true,
8
+ },
9
+ blockId: {
10
+ type: String,
11
+ required: true,
12
+ },
13
+ editMode: {
14
+ type: Boolean,
15
+ default: true,
16
+ },
17
+ theme: {
18
+ type: Object,
19
+ default: null,
20
+ },
21
+ siteId: {
22
+ type: String,
23
+ default: '',
24
+ },
25
+ viewportMode: {
26
+ type: String,
27
+ default: 'auto',
28
+ },
29
+ })
30
+ const emit = defineEmits(['update:modelValue', 'delete'])
31
+ const edgeFirebase = inject('edgeFirebase')
32
+ function extractFieldsInOrder(template) {
33
+ if (!template || typeof template !== 'string')
34
+ return []
35
+ const fields = []
36
+ const seen = new Set()
37
+ const TAG_RE = /\{\{\{#[^\s]+\s+(\{[\s\S]*?\})\}\}\}/g
38
+ let m = TAG_RE.exec(template)
39
+ while (m) {
40
+ const cfg = m[1]
41
+ const fm = cfg.match(/"field"\s*:\s*"([^"]+)"/)
42
+ if (fm && !seen.has(fm[1])) {
43
+ fields.push(fm[1])
44
+ seen.add(fm[1])
45
+ }
46
+ m = TAG_RE.exec(template)
47
+ }
48
+ return fields
49
+ }
50
+
51
+ const modelValue = useVModel(props, 'modelValue', emit)
52
+
53
+ const state = reactive({
54
+ open: false,
55
+ draft: {},
56
+ delete: false,
57
+ meta: {},
58
+ arrayItems: {},
59
+ reload: false,
60
+ metaUpdate: {},
61
+ loading: true,
62
+ afterLoad: false,
63
+ imageOpen: false,
64
+ })
65
+
66
+ const ensureQueryItemsDefaults = (meta) => {
67
+ Object.keys(meta || {}).forEach((key) => {
68
+ const cfg = meta[key]
69
+ if (!cfg?.queryOptions || cfg.queryOptions.length === 0)
70
+ return
71
+
72
+ if (!cfg.queryItems)
73
+ cfg.queryItems = {}
74
+
75
+ for (const option of cfg.queryOptions) {
76
+ const hasField = Object.prototype.hasOwnProperty.call(cfg.queryItems, option.field)
77
+ if (!hasField) {
78
+ cfg.queryItems[option.field] = (cfg.collection?.path === 'posts' && option.field === 'tags') ? [] : null
79
+ }
80
+ else if (cfg.queryItems[option.field] === '') {
81
+ // Normalize empty strings from older saves so "(none)" stays unset
82
+ cfg.queryItems[option.field] = null
83
+ }
84
+ }
85
+ })
86
+ }
87
+
88
+ const sanitizeQueryItems = (meta) => {
89
+ const cleaned = JSON.parse(JSON.stringify(meta || {}))
90
+ for (const key of Object.keys(cleaned)) {
91
+ const cfg = cleaned[key]
92
+ if (!cfg?.queryItems || typeof cfg.queryItems !== 'object')
93
+ continue
94
+
95
+ for (const field of Object.keys(cfg.queryItems)) {
96
+ const value = cfg.queryItems[field]
97
+ const isEmptyArray = Array.isArray(value) && value.length === 0
98
+ if (value === null || value === '' || isEmptyArray) {
99
+ delete cfg.queryItems[field]
100
+ }
101
+ }
102
+
103
+ if (cfg.queryItems && Object.keys(cfg.queryItems).length === 0)
104
+ delete cfg.queryItems
105
+ }
106
+ return cleaned
107
+ }
108
+
109
+ const resetArrayItems = (field) => {
110
+ if (!state.arrayItems?.[field]) {
111
+ state.arrayItems[field] = {}
112
+ }
113
+ for (const schemaItem of modelValue.value.meta[field].schema) {
114
+ if (schemaItem.type === 'text') {
115
+ state.arrayItems[field][schemaItem.field] = ''
116
+ }
117
+ else if (schemaItem.type === 'number') {
118
+ state.arrayItems[field][schemaItem.field] = 0
119
+ }
120
+ else if (schemaItem.type === 'richtext') {
121
+ state.arrayItems[field][schemaItem.field] = ''
122
+ }
123
+ else if (schemaItem.type === 'textarea') {
124
+ state.arrayItems[field][schemaItem.field] = ''
125
+ }
126
+ else if (schemaItem.type === 'image') {
127
+ state.arrayItems[field][schemaItem.field] = ''
128
+ }
129
+ }
130
+ }
131
+
132
+ const openEditor = async () => {
133
+ if (!props.editMode)
134
+ return
135
+ for (const key of Object.keys(modelValue.value?.meta || {})) {
136
+ if (modelValue.value.meta[key]?.type === 'array' && modelValue.value.meta[key]?.schema) {
137
+ if (!modelValue.value.meta[key]?.api) {
138
+ resetArrayItems(key)
139
+ }
140
+ }
141
+ }
142
+ state.draft = JSON.parse(JSON.stringify(modelValue.value?.values || {}))
143
+ state.meta = JSON.parse(JSON.stringify(modelValue.value?.meta || {}))
144
+ ensureQueryItemsDefaults(state.meta)
145
+ const blockData = edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/blocks`]?.[modelValue.value.blockId]
146
+ state.metaUpdate = edgeGlobal.dupObject(modelValue.value?.meta) || {}
147
+ if (blockData?.meta) {
148
+ for (const key of Object.keys(blockData.meta)) {
149
+ if (!(key in state.metaUpdate)) {
150
+ state.metaUpdate[key] = blockData.meta[key]
151
+ }
152
+ }
153
+ }
154
+ if (blockData?.values) {
155
+ for (const key of Object.keys(blockData.values)) {
156
+ if (!(key in state.draft)) {
157
+ state.draft[key] = blockData.values[key]
158
+ }
159
+ }
160
+ }
161
+ modelValue.value.blockUpdatedAt = new Date().toISOString()
162
+ state.open = true
163
+ state.afterLoad = true
164
+ }
165
+
166
+ const save = () => {
167
+ const updated = {
168
+ ...modelValue.value,
169
+ values: JSON.parse(JSON.stringify(state.draft)),
170
+ meta: sanitizeQueryItems(state.meta),
171
+ }
172
+ modelValue.value = updated
173
+ state.open = false
174
+ }
175
+ const orderedMeta = computed(() => {
176
+ const metaObj = state.metaUpdate || {}
177
+ const tpl = modelValue.value?.content || ''
178
+ const orderedFields = extractFieldsInOrder(tpl)
179
+
180
+ const out = []
181
+ const picked = new Set()
182
+
183
+ for (const f of orderedFields) {
184
+ if (f in metaObj) {
185
+ out.push({ field: f, meta: metaObj[f] })
186
+ picked.add(f)
187
+ }
188
+ }
189
+
190
+ for (const f of Object.keys(metaObj)) {
191
+ if (!picked.has(f)) {
192
+ out.push({ field: f, meta: metaObj[f] })
193
+ }
194
+ }
195
+
196
+ return out
197
+ })
198
+
199
+ const genTitleFromField = (field) => {
200
+ if (field?.title)
201
+ return field.title
202
+ if (field?.meta?.title)
203
+ return field.meta.title
204
+ // Insert space before a capital only if it's followed by a lowercase
205
+ return field.field
206
+ // Insert space before a capital only if it's followed by a lowercase
207
+ .replace('_', ' ')
208
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
209
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
210
+ .replace(/^./, str => str.toUpperCase())
211
+ }
212
+ const addToArray = async (field) => {
213
+ state.reload = true
214
+ state.draft[field].push(JSON.parse(JSON.stringify(state.arrayItems[field])))
215
+ resetArrayItems(field)
216
+ await nextTick()
217
+ state.reload = false
218
+ }
219
+
220
+ const loadingRender = (content) => {
221
+ if (state.loading) {
222
+ content = content.replaceAll('{{loading}}', '')
223
+ content = content.replaceAll('{{loaded}}', 'hidden')
224
+ }
225
+ else {
226
+ content = content.replaceAll('{{loading}}', 'hidden')
227
+ content = content.replaceAll('{{loaded}}', '')
228
+ }
229
+ return content
230
+ }
231
+
232
+ const postsList = computed(() => {
233
+ const postsCollectionPath = `${edgeGlobal.edgeState.organizationDocPath}/sites/${props.siteId}/posts`
234
+ return Object.values(edgeFirebase.data[postsCollectionPath] || {}).sort((a, b) => {
235
+ if (a.publishDate && b.publishDate) {
236
+ return new Date(b.publishDate).getTime() - new Date(a.publishDate).getTime()
237
+ }
238
+ return 0
239
+ })
240
+ })
241
+ const getTagsFromPosts = computed(() => {
242
+ const tagMap = new Map()
243
+ postsList.value.forEach((post) => {
244
+ if (Array.isArray(post.tags)) {
245
+ post.tags.forEach((tag) => {
246
+ if (tag && typeof tag === 'string' && !tagMap.has(tag)) {
247
+ tagMap.set(tag, { name: tag, title: tag })
248
+ }
249
+ })
250
+ }
251
+ })
252
+ return Array.from(tagMap.values()).sort((a, b) => a.title.localeCompare(b.title))
253
+ })
254
+ </script>
255
+
256
+ <template>
257
+ <div>
258
+ <div
259
+ :class="{ 'cursor-pointer': props.editMode }"
260
+ class="relative group "
261
+ @click="openEditor"
262
+ >
263
+ <!-- Content -->
264
+ <edge-cms-block-api :site-id="props.siteId" :theme="props.theme" :content="modelValue?.content" :values="modelValue?.values" :meta="modelValue?.meta" :viewport-mode="props.viewportMode" @pending="state.loading = $event" />
265
+ <edge-cms-block-render
266
+ v-if="state.loading"
267
+ :content="loadingRender(modelValue?.content)"
268
+ :values="modelValue?.values"
269
+ :meta="modelValue?.meta"
270
+ :theme="props.theme"
271
+ :viewport-mode="props.viewportMode"
272
+ />
273
+ <!-- Darken overlay on hover -->
274
+ <div v-if="props.editMode" class="pointer-events-none absolute inset-0 bg-black/50 opacity-0 transition-opacity duration-200 group-hover:opacity-100 z-10" />
275
+
276
+ <!-- Hover controls -->
277
+ <div v-if="props.editMode" class="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-20">
278
+ <!-- Delete button top right -->
279
+ <div class="absolute top-2 right-2">
280
+ <edge-shad-button
281
+ variant="destructive"
282
+ size="icon"
283
+ @click.stop.prevent="state.delete = true"
284
+ >
285
+ <Trash class="h-4 w-4" />
286
+ </edge-shad-button>
287
+ </div>
288
+
289
+ <!-- Edit button centered -->
290
+ <div class="flex items-center justify-center h-full">
291
+ <!-- <edge-shad-button class="text-xl py-6 px-8" @click.stop.prevent="openEditor">
292
+ <Pencil class="w-4 h-4 mr-1" />
293
+ Edit
294
+ </edge-shad-button> -->
295
+ </div>
296
+ </div>
297
+ </div>
298
+ <edge-shad-dialog v-model="state.delete">
299
+ <DialogContent class="max-w-md">
300
+ <DialogHeader>
301
+ <DialogTitle>Delete Block</DialogTitle>
302
+ </DialogHeader>
303
+ <DialogDescription>
304
+ Are you sure you want to delete this block?
305
+ </DialogDescription>
306
+ <DialogFooter class="pt-2 flex justify-between">
307
+ <edge-shad-button class="text-white bg-slate-800 hover:bg-slate-400" @click="state.delete = false">
308
+ Cancel
309
+ </edge-shad-button>
310
+ <edge-shad-button variant="destructive" class="text-white w-full" @click="emit('delete', props.blockId); state.delete = false">
311
+ Delete
312
+ </edge-shad-button>
313
+ </DialogFooter>
314
+ </DialogContent>
315
+ </edge-shad-dialog>
316
+
317
+ <Sheet v-model:open="state.open">
318
+ <edge-cms-block-sheet-content v-if="state.afterLoad" class="w-full md:w-1/2 max-w-none sm:max-w-none max-w-2xl">
319
+ <SheetHeader>
320
+ <SheetTitle>Edit Block</SheetTitle>
321
+ <SheetDescription v-if="modelValue.synced" class="text-sm text-red-500">
322
+ This is a synced block. Changes made here will be reflected across all instances of this block on your site.
323
+ </SheetDescription>
324
+ </SheetHeader>
325
+
326
+ <edge-shad-form>
327
+ <div v-if="orderedMeta.length === 0">
328
+ <Alert variant="info" class="mt-4 mb-4">
329
+ <AlertTitle>No editable fields found</AlertTitle>
330
+ <AlertDescription class="text-sm">
331
+ This block does not have any editable fields defined.
332
+ </AlertDescription>
333
+ </Alert>
334
+ </div>
335
+ <div :class="modelValue.synced ? 'h-[calc(100vh-160px)]' : 'h-[calc(100vh-130px)]'" class="p-6 space-y-4 overflow-y-auto">
336
+ <template v-for="entry in orderedMeta" :key="entry.field">
337
+ <div v-if="entry.meta.type === 'array'">
338
+ <div v-if="!entry.meta?.api && !entry.meta?.collection">
339
+ <div v-if="entry.meta?.schema">
340
+ <Card v-if="!state.reload" class="mb-4 bg-white shadow-sm border border-gray-200 p-4">
341
+ <CardHeader class="p-0 mb-2">
342
+ <div class="relative flex items-center bg-secondary p-2 justify-between sticky top-0 z-10 bg-primary rounded">
343
+ <span class="text-lg font-semibold whitespace-nowrap pr-1"> {{ genTitleFromField(entry) }}</span>
344
+ <div class="flex w-full items-center">
345
+ <div class="w-full border-t border-gray-300 dark:border-white/15" aria-hidden="true" />
346
+ <edge-shad-button variant="text" class="hover:text-primary/50 text-xs h-[26px] text-primary" @click="state.editMode = !state.editMode">
347
+ <Popover>
348
+ <PopoverTrigger as-child>
349
+ <edge-shad-button
350
+ variant="text"
351
+ type="submit"
352
+ class="bg-secondary hover:text-primary/50 text-xs h-[26px] text-primary"
353
+ >
354
+ <Plus class="w-4 h-4" />
355
+ </edge-shad-button>
356
+ </PopoverTrigger>
357
+ <PopoverContent class="!w-80 mr-20">
358
+ <Card class="border-none shadow-none p-4">
359
+ <template v-for="schemaItem in entry.meta.schema" :key="schemaItem.field">
360
+ <edge-cms-block-input
361
+ v-model="state.arrayItems[entry.field][schemaItem.field]"
362
+ :type="schemaItem.type"
363
+ :field="schemaItem.field"
364
+ :schema="schemaItem"
365
+ :label="genTitleFromField(schemaItem)"
366
+ />
367
+ </template>
368
+ <CardFooter class="mt-2 flex justify-end">
369
+ <edge-shad-button
370
+ class="bg-secondary hover:text-white text-xs h-[26px] text-primary"
371
+ @click="addToArray(entry.field)"
372
+ >
373
+ Add Entry
374
+ </edge-shad-button>
375
+ </CardFooter>
376
+ </Card>
377
+ </PopoverContent>
378
+ </Popover>
379
+ </edge-shad-button>
380
+ </div>
381
+ </div>
382
+ </CardHeader>
383
+ <draggable
384
+ v-if="state.draft?.[entry.field] && state.draft[entry.field].length > 0"
385
+ v-model="state.draft[entry.field]"
386
+ handle=".handle"
387
+ item-key="index"
388
+ >
389
+ <template #item="{ element, index }">
390
+ <div :key="index" class="">
391
+ <div class="flex gap-2 w-full items-center w-full border-1 border-dotted py-1 mb-1">
392
+ <div class="text-left px-2">
393
+ <Grip class="handle pointer" />
394
+ </div>
395
+ <div class="px-2 py-2 w-[98%] flex gap-1">
396
+ <template v-for="schemaItem in entry.meta.schema" :key="schemaItem.field">
397
+ <Popover>
398
+ <PopoverTrigger as-child>
399
+ <Alert class="w-[200px] text-xs py-1 px-2 cursor-pointer hover:bg-primary hover:text-white">
400
+ <AlertTitle> {{ genTitleFromField(schemaItem) }}</AlertTitle>
401
+ <AlertDescription class="text-sm truncate max-w-[200px]">
402
+ {{ element[schemaItem.field] }}
403
+ </AlertDescription>
404
+ </Alert>
405
+ </PopoverTrigger>
406
+ <PopoverContent class="!w-80 mr-20">
407
+ <Card class="border-none shadow-none p-4">
408
+ <edge-cms-block-input
409
+ v-model="element[schemaItem.field]"
410
+ :type="schemaItem.type"
411
+ :schema="schemaItem"
412
+ :field="`${schemaItem.field}-${index}-entry`"
413
+ :label="genTitleFromField(schemaItem)"
414
+ />
415
+ </Card>
416
+ </PopoverContent>
417
+ </Popover>
418
+ </template>
419
+ </div>
420
+ <div class="pr-2">
421
+ <edge-shad-button
422
+ variant="destructive"
423
+ size="icon"
424
+ @click="state.draft[entry.field].splice(index, 1)"
425
+ >
426
+ <Trash class="h-4 w-4" />
427
+ </edge-shad-button>
428
+ </div>
429
+ </div>
430
+ </div>
431
+ </template>
432
+ </draggable>
433
+ </Card>
434
+ </div>
435
+ <edge-cms-block-input
436
+ v-else
437
+ v-model="state.draft[entry.field]"
438
+ :type="entry.meta.type"
439
+ :field="entry.field"
440
+ :label="genTitleFromField(entry)"
441
+ />
442
+ </div>
443
+ <div v-else>
444
+ <template v-if="entry.meta?.queryOptions">
445
+ <div v-for="option in entry.meta.queryOptions" :key="option.field" class="mb-2">
446
+ <edge-shad-select-tags
447
+ v-if="entry.meta?.collection?.path === 'posts' && option.field === 'tags'"
448
+ v-model="state.meta[entry.field].queryItems[option.field]"
449
+ :items="getTagsFromPosts"
450
+ :label="`${genTitleFromField(option)}`"
451
+ :name="option.field"
452
+ :placeholder="`Select ${genTitleFromField(option)}`"
453
+ />
454
+ <edge-cms-options-select
455
+ v-else-if="entry.meta?.collection?.path !== 'post'"
456
+ v-model="state.meta[entry.field].queryItems[option.field]"
457
+ :option="option"
458
+ :label="genTitleFromField(option)"
459
+ />
460
+ </div>
461
+ </template>
462
+ <edge-shad-number v-if="entry.meta?.collection?.path !== 'post'" v-model="state.meta[entry.field].limit" name="limit" label="Limit" />
463
+ </div>
464
+ </div>
465
+ <div v-else-if="entry.meta?.type === 'image'" class="w-full">
466
+ <div class="relative bg-muted py-2 rounded-md">
467
+ <div class="bg-black/80 absolute left-0 top-0 w-full h-full opacity-0 hover:opacity-100 transition-opacity flex items-center justify-center z-10 cursor-pointer">
468
+ <Dialog v-model:open="state.imageOpen">
469
+ <DialogTrigger as-child>
470
+ <edge-shad-button variant="outline" class="bg-white text-black hover:bg-gray-200">
471
+ <ImagePlus class="h-5 w-5 mr-2" />
472
+ Select Image
473
+ </edge-shad-button>
474
+ </DialogTrigger>
475
+ <DialogContent class="w-full max-w-[1200px] max-h-[80vh] overflow-y-auto">
476
+ <DialogHeader>
477
+ <DialogTitle>Select Image</DialogTitle>
478
+ <DialogDescription />
479
+ </DialogHeader>
480
+ <edge-cms-media-manager
481
+ v-if="entry.meta?.tags && entry.meta.tags.length > 0"
482
+ :site="props.siteId"
483
+ :select-mode="true"
484
+ :default-tags="entry.meta.tags"
485
+ @select="(url) => { state.draft[entry.field] = url; state.imageOpen = false; }"
486
+ />
487
+ <edge-cms-media-manager
488
+ v-else
489
+ :site="props.siteId"
490
+ :select-mode="true"
491
+ @select="(url) => { state.draft[entry.field] = url; state.imageOpen = false; }"
492
+ />
493
+ </DialogContent>
494
+ </Dialog>
495
+ </div>
496
+ <img v-if="state.draft[entry.field]" :src="state.draft[entry.field]" class="mb-2 max-h-40 mx-auto object-contain">
497
+ </div>
498
+ </div>
499
+ <div v-else-if="entry.meta?.option">
500
+ <edge-cms-options-select
501
+ v-model="state.draft[entry.field]"
502
+ :option="entry.meta.option"
503
+ :label="genTitleFromField(entry)"
504
+ />
505
+ </div>
506
+ <div v-else>
507
+ <edge-cms-block-input
508
+ v-model="state.draft[entry.field]"
509
+ :type="entry.meta.type"
510
+ :field="entry.field"
511
+ :label="genTitleFromField(entry)"
512
+ />
513
+ </div>
514
+ </template>
515
+ </div>
516
+
517
+ <SheetFooter class="pt-2 flex justify-between">
518
+ <edge-shad-button variant="destructive" class="text-white" @click="state.open = false">
519
+ Cancel
520
+ </edge-shad-button>
521
+ <edge-shad-button class=" bg-slate-800 hover:bg-slate-400 w-full" @click="save">
522
+ Save changes
523
+ </edge-shad-button>
524
+ </SheetFooter>
525
+ </edge-shad-form>
526
+ </edge-cms-block-sheet-content>
527
+ </Sheet>
528
+ </div>
529
+ </template>