@edgedev/create-edge-app 1.1.28 → 1.2.29
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/agents.md +2 -0
- package/bin/cli.js +13 -5
- package/deploy-services.sh +237 -0
- package/deploy.sh +88 -1
- package/edge/components/auth/register.vue +51 -0
- package/edge/components/cms/block.vue +29 -16
- package/edge/components/cms/blockEditor.vue +748 -7
- package/edge/components/cms/codeEditor.vue +24 -2
- package/edge/components/cms/htmlContent.vue +10 -2
- package/edge/components/cms/mediaManager.vue +19 -3
- package/edge/components/cms/menu.vue +231 -34
- package/edge/components/cms/optionsSelect.vue +20 -3
- package/edge/components/cms/page.vue +9 -0
- package/edge/components/cms/site.vue +114 -5
- package/edge/components/cms/siteSettingsForm.vue +7 -0
- package/edge/components/cms/themeEditor.vue +9 -3
- package/edge/components/dashboard.vue +22 -3
- package/edge/components/imagePicker.vue +126 -0
- package/edge/components/myAccount.vue +1 -0
- package/edge/components/myProfile.vue +345 -61
- package/edge/components/organizationMembers.vue +569 -261
- package/edge/components/shad/combobox.vue +2 -2
- package/edge/components/shad/number.vue +2 -2
- package/edge/composables/global.ts +5 -2
- package/edge/composables/structuredDataTemplates.js +6 -6
- package/firebase_init.sh +63 -2
- package/package.json +1 -1
- package/services/.deploy.shared.env.example +12 -0
|
@@ -14,6 +14,11 @@ const props = defineProps({
|
|
|
14
14
|
required: false,
|
|
15
15
|
default: '',
|
|
16
16
|
},
|
|
17
|
+
disableAddSiteForNonAdmin: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
required: false,
|
|
20
|
+
default: false,
|
|
21
|
+
},
|
|
17
22
|
})
|
|
18
23
|
const edgeFirebase = inject('edgeFirebase')
|
|
19
24
|
const { createDefaults: createSiteSettingsDefaults, createNewDocSchema: createSiteSettingsNewDocSchema } = useSiteSettingsTemplate()
|
|
@@ -33,6 +38,22 @@ const normalizeForCompare = (value) => {
|
|
|
33
38
|
|
|
34
39
|
const stableSerialize = value => JSON.stringify(normalizeForCompare(value))
|
|
35
40
|
const areEqualNormalized = (a, b) => stableSerialize(a) === stableSerialize(b)
|
|
41
|
+
const isJsonInvalid = (value) => {
|
|
42
|
+
if (value === null || value === undefined)
|
|
43
|
+
return false
|
|
44
|
+
if (typeof value === 'object')
|
|
45
|
+
return false
|
|
46
|
+
const text = String(value).trim()
|
|
47
|
+
if (!text)
|
|
48
|
+
return false
|
|
49
|
+
try {
|
|
50
|
+
JSON.parse(text)
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
36
57
|
|
|
37
58
|
const isTemplateSite = computed(() => props.site === 'templates')
|
|
38
59
|
const router = useRouter()
|
|
@@ -121,6 +142,17 @@ const schemas = {
|
|
|
121
142
|
const isAdmin = computed(() => {
|
|
122
143
|
return edgeGlobal.isAdminGlobal(edgeFirebase).value
|
|
123
144
|
})
|
|
145
|
+
const currentOrgRoleName = computed(() => {
|
|
146
|
+
return String(edgeGlobal.getRoleName(edgeFirebase?.user?.roles || [], edgeGlobal.edgeState.currentOrganization) || '').toLowerCase()
|
|
147
|
+
})
|
|
148
|
+
const isOrgAdmin = computed(() => {
|
|
149
|
+
return currentOrgRoleName.value === 'admin'
|
|
150
|
+
})
|
|
151
|
+
const canCreateSite = computed(() => {
|
|
152
|
+
if (!props.disableAddSiteForNonAdmin)
|
|
153
|
+
return true
|
|
154
|
+
return isOrgAdmin.value
|
|
155
|
+
})
|
|
124
156
|
|
|
125
157
|
const siteData = computed(() => {
|
|
126
158
|
return edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/sites`]?.[props.site] || {}
|
|
@@ -339,12 +371,72 @@ const themeOptionsMap = computed(() => {
|
|
|
339
371
|
const orgUsers = computed(() => edgeFirebase.state?.users || {})
|
|
340
372
|
const userOptions = computed(() => {
|
|
341
373
|
return Object.entries(orgUsers.value || {})
|
|
374
|
+
.filter(([, user]) => Boolean(user?.userId))
|
|
342
375
|
.map(([id, user]) => ({
|
|
343
|
-
value: user?.userId
|
|
376
|
+
value: user?.userId,
|
|
344
377
|
label: user?.meta?.name || user?.userId || id,
|
|
345
378
|
}))
|
|
346
379
|
.sort((a, b) => a.label.localeCompare(b.label))
|
|
347
380
|
})
|
|
381
|
+
const authUid = computed(() => String(edgeFirebase?.user?.uid || '').trim())
|
|
382
|
+
const currentOrgUser = computed(() => {
|
|
383
|
+
if (!authUid.value)
|
|
384
|
+
return null
|
|
385
|
+
const users = Object.values(orgUsers.value || {})
|
|
386
|
+
return users.find((user) => {
|
|
387
|
+
const userId = String(user?.userId || '').trim()
|
|
388
|
+
const docId = String(user?.docId || '').trim()
|
|
389
|
+
const uid = String(user?.uid || '').trim()
|
|
390
|
+
return userId === authUid.value || docId === authUid.value || uid === authUid.value
|
|
391
|
+
}) || null
|
|
392
|
+
})
|
|
393
|
+
const currentOrgUserId = computed(() => {
|
|
394
|
+
return String(
|
|
395
|
+
currentOrgUser.value?.userId
|
|
396
|
+
|| currentOrgUser.value?.docId
|
|
397
|
+
|| authUid.value
|
|
398
|
+
|| '',
|
|
399
|
+
).trim()
|
|
400
|
+
})
|
|
401
|
+
const currentUserOption = computed(() => {
|
|
402
|
+
if (!currentOrgUserId.value)
|
|
403
|
+
return null
|
|
404
|
+
return {
|
|
405
|
+
value: currentOrgUserId.value,
|
|
406
|
+
label: currentOrgUser.value?.meta?.name || currentOrgUser.value?.meta?.email || currentOrgUserId.value,
|
|
407
|
+
}
|
|
408
|
+
})
|
|
409
|
+
const shouldForceCurrentUserForNewSite = computed(() => !isAdmin.value && props.site === 'new')
|
|
410
|
+
const aiUserOptions = computed(() => {
|
|
411
|
+
if (!shouldForceCurrentUserForNewSite.value)
|
|
412
|
+
return userOptions.value
|
|
413
|
+
return currentUserOption.value ? [currentUserOption.value] : []
|
|
414
|
+
})
|
|
415
|
+
const normalizeUserIds = items => (Array.isArray(items) ? items : [])
|
|
416
|
+
.map(item => String(item || '').trim())
|
|
417
|
+
.filter(Boolean)
|
|
418
|
+
const getSiteUsersModel = (workingDoc) => {
|
|
419
|
+
if (!workingDoc || typeof workingDoc !== 'object')
|
|
420
|
+
return []
|
|
421
|
+
const users = normalizeUserIds(workingDoc?.users)
|
|
422
|
+
if (!shouldForceCurrentUserForNewSite.value)
|
|
423
|
+
return users
|
|
424
|
+
if (!currentOrgUserId.value)
|
|
425
|
+
return users
|
|
426
|
+
if (users.length === 1 && users[0] === currentOrgUserId.value)
|
|
427
|
+
return users
|
|
428
|
+
workingDoc.users = [currentOrgUserId.value]
|
|
429
|
+
return workingDoc.users
|
|
430
|
+
}
|
|
431
|
+
const updateSiteUsersModel = (workingDoc, value) => {
|
|
432
|
+
if (!workingDoc || typeof workingDoc !== 'object')
|
|
433
|
+
return
|
|
434
|
+
if (shouldForceCurrentUserForNewSite.value) {
|
|
435
|
+
workingDoc.users = currentOrgUserId.value ? [currentOrgUserId.value] : []
|
|
436
|
+
return
|
|
437
|
+
}
|
|
438
|
+
workingDoc.users = normalizeUserIds(value)
|
|
439
|
+
}
|
|
348
440
|
|
|
349
441
|
const themeItemsForAllowed = (allowed, current) => {
|
|
350
442
|
const base = themeOptions.value
|
|
@@ -859,6 +951,9 @@ const isPublishedPageDiff = (pageId) => {
|
|
|
859
951
|
metaTitle: publishedPage.metaTitle,
|
|
860
952
|
metaDescription: publishedPage.metaDescription,
|
|
861
953
|
structuredData: publishedPage.structuredData,
|
|
954
|
+
postMetaTitle: publishedPage.postMetaTitle,
|
|
955
|
+
postMetaDescription: publishedPage.postMetaDescription,
|
|
956
|
+
postStructuredData: publishedPage.postStructuredData,
|
|
862
957
|
},
|
|
863
958
|
{
|
|
864
959
|
content: draftPage.content,
|
|
@@ -868,6 +963,9 @@ const isPublishedPageDiff = (pageId) => {
|
|
|
868
963
|
metaTitle: draftPage.metaTitle,
|
|
869
964
|
metaDescription: draftPage.metaDescription,
|
|
870
965
|
structuredData: draftPage.structuredData,
|
|
966
|
+
postMetaTitle: draftPage.postMetaTitle,
|
|
967
|
+
postMetaDescription: draftPage.postMetaDescription,
|
|
968
|
+
postStructuredData: draftPage.postStructuredData,
|
|
871
969
|
},
|
|
872
970
|
)
|
|
873
971
|
}
|
|
@@ -1069,6 +1167,9 @@ const isAnyPagesDiff = computed(() => {
|
|
|
1069
1167
|
metaTitle: pageData.metaTitle,
|
|
1070
1168
|
metaDescription: pageData.metaDescription,
|
|
1071
1169
|
structuredData: pageData.structuredData,
|
|
1170
|
+
postMetaTitle: pageData.postMetaTitle,
|
|
1171
|
+
postMetaDescription: pageData.postMetaDescription,
|
|
1172
|
+
postStructuredData: pageData.postStructuredData,
|
|
1072
1173
|
},
|
|
1073
1174
|
{
|
|
1074
1175
|
content: publishedPage.content,
|
|
@@ -1078,6 +1179,9 @@ const isAnyPagesDiff = computed(() => {
|
|
|
1078
1179
|
metaTitle: publishedPage.metaTitle,
|
|
1079
1180
|
metaDescription: publishedPage.metaDescription,
|
|
1080
1181
|
structuredData: publishedPage.structuredData,
|
|
1182
|
+
postMetaTitle: publishedPage.postMetaTitle,
|
|
1183
|
+
postMetaDescription: publishedPage.postMetaDescription,
|
|
1184
|
+
postStructuredData: publishedPage.postStructuredData,
|
|
1081
1185
|
},
|
|
1082
1186
|
)) {
|
|
1083
1187
|
return true
|
|
@@ -1104,7 +1208,7 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1104
1208
|
v-if="edgeGlobal.edgeState.organizationDocPath"
|
|
1105
1209
|
>
|
|
1106
1210
|
<edge-editor
|
|
1107
|
-
v-if="!props.page && props.site === 'new'"
|
|
1211
|
+
v-if="!props.page && props.site === 'new' && canCreateSite"
|
|
1108
1212
|
collection="sites"
|
|
1109
1213
|
:doc-id="props.site"
|
|
1110
1214
|
:schema="schemas.sites"
|
|
@@ -1186,7 +1290,8 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1186
1290
|
/>
|
|
1187
1291
|
<edge-shad-select-tags
|
|
1188
1292
|
v-if="Object.keys(orgUsers).length > 0"
|
|
1189
|
-
|
|
1293
|
+
:model-value="getSiteUsersModel(slotProps.workingDoc)"
|
|
1294
|
+
:disabled="shouldForceCurrentUserForNewSite || !edgeGlobal.isAdminGlobal(edgeFirebase).value"
|
|
1190
1295
|
:items="userOptions"
|
|
1191
1296
|
name="users"
|
|
1192
1297
|
label="Users"
|
|
@@ -1195,6 +1300,7 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1195
1300
|
placeholder="Select users"
|
|
1196
1301
|
class="w-full"
|
|
1197
1302
|
:multiple="true"
|
|
1303
|
+
@update:model-value="value => updateSiteUsersModel(slotProps.workingDoc, value)"
|
|
1198
1304
|
/>
|
|
1199
1305
|
<div class="rounded-lg border border-dashed border-slate-200 p-4 ">
|
|
1200
1306
|
<div class="flex items-start justify-between gap-3">
|
|
@@ -1219,7 +1325,7 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1219
1325
|
label="User Data for AI to use to build initial site"
|
|
1220
1326
|
placeholder="- select one -"
|
|
1221
1327
|
class="w-full"
|
|
1222
|
-
:items="
|
|
1328
|
+
:items="aiUserOptions"
|
|
1223
1329
|
item-title="label"
|
|
1224
1330
|
item-value="value"
|
|
1225
1331
|
@update:model-value="value => (slotProps.workingDoc.aiAgentUserId = value || '')"
|
|
@@ -1236,6 +1342,9 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1236
1342
|
</div>
|
|
1237
1343
|
</template>
|
|
1238
1344
|
</edge-editor>
|
|
1345
|
+
<div v-else-if="!props.page && props.site === 'new' && !canCreateSite" class="p-6 text-sm text-red-600">
|
|
1346
|
+
Only organization admins can create sites.
|
|
1347
|
+
</div>
|
|
1239
1348
|
<div v-else class="flex flex-col h-[calc(100vh-58px)] overflow-hidden">
|
|
1240
1349
|
<div class="grid grid-cols-[1fr_auto_1fr] items-center gap-3 px-4 py-2 border-b bg-secondary">
|
|
1241
1350
|
<div class="flex items-center gap-3">
|
|
@@ -1596,7 +1705,7 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1596
1705
|
<edge-shad-button variant="destructive" class="text-white" @click="state.siteSettings = false">
|
|
1597
1706
|
Cancel
|
|
1598
1707
|
</edge-shad-button>
|
|
1599
|
-
<edge-shad-button :disabled="slotProps.submitting" type="submit" class=" bg-slate-800 hover:bg-slate-400 w-full">
|
|
1708
|
+
<edge-shad-button :disabled="slotProps.submitting || isJsonInvalid(slotProps.workingDoc?.structuredData)" type="submit" class=" bg-slate-800 hover:bg-slate-400 w-full">
|
|
1600
1709
|
<Loader2 v-if="slotProps.submitting" class=" h-4 w-4 animate-spin" />
|
|
1601
1710
|
Update
|
|
1602
1711
|
</edge-shad-button>
|
|
@@ -534,11 +534,18 @@ onMounted(async () => {
|
|
|
534
534
|
label="Meta Description"
|
|
535
535
|
name="metaDescription"
|
|
536
536
|
/>
|
|
537
|
+
<div class="rounded-md border border-border/60 bg-muted/30 px-3 py-2 text-xs text-muted-foreground">
|
|
538
|
+
CMS tokens in double curly braces are replaced on the front end.
|
|
539
|
+
Example: <span v-pre class="font-semibold text-foreground">"{{cms-site}}"</span> for the site URL,
|
|
540
|
+
<span v-pre class="font-semibold text-foreground">"{{cms-url}}"</span> for the page URL, and
|
|
541
|
+
<span v-pre class="font-semibold text-foreground">"{{cms-logo}}"</span> for the logo URL. Keep the tokens intact.
|
|
542
|
+
</div>
|
|
537
543
|
<edge-cms-code-editor
|
|
538
544
|
v-model="props.settings.structuredData"
|
|
539
545
|
title="Structured Data (JSON-LD)"
|
|
540
546
|
language="json"
|
|
541
547
|
name="structuredData"
|
|
548
|
+
validate-json
|
|
542
549
|
height="300px"
|
|
543
550
|
class="mb-4 w-full"
|
|
544
551
|
/>
|
|
@@ -132,7 +132,10 @@ watch(headObject, (newHeadElements) => {
|
|
|
132
132
|
}, { immediate: true, deep: true })
|
|
133
133
|
|
|
134
134
|
const sites = computed(() => {
|
|
135
|
-
|
|
135
|
+
const sitesMap = edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/sites`] || {}
|
|
136
|
+
return Object.entries(sitesMap)
|
|
137
|
+
.map(([docId, data]) => ({ docId, ...(data || {}) }))
|
|
138
|
+
.filter(site => site.docId && site.docId !== 'templates')
|
|
136
139
|
})
|
|
137
140
|
|
|
138
141
|
const templatePages = computed(() => {
|
|
@@ -324,9 +327,12 @@ const templatePageOptions = computed(() => {
|
|
|
324
327
|
|
|
325
328
|
watch (sites, async (newSites) => {
|
|
326
329
|
state.loading = true
|
|
327
|
-
|
|
330
|
+
const selectedSite = String(edgeGlobal.edgeState.blockEditorSite || '').trim()
|
|
331
|
+
const hasSelectedSite = newSites.some(site => site.docId === selectedSite)
|
|
332
|
+
if ((!selectedSite || !hasSelectedSite) && newSites.length > 0)
|
|
328
333
|
edgeGlobal.edgeState.blockEditorSite = newSites[0].docId
|
|
329
|
-
|
|
334
|
+
else if (!newSites.length)
|
|
335
|
+
edgeGlobal.edgeState.blockEditorSite = ''
|
|
330
336
|
await nextTick()
|
|
331
337
|
state.loading = false
|
|
332
338
|
}, { immediate: true, deep: true })
|
|
@@ -170,6 +170,20 @@ const getByPath = (obj, path) => {
|
|
|
170
170
|
}, obj)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
const normalizeCollectionItems = (collectionMap = {}) => {
|
|
174
|
+
const items = Object.entries(collectionMap).map(([docId, data]) => {
|
|
175
|
+
const source = (data && typeof data === 'object') ? data : {}
|
|
176
|
+
return {
|
|
177
|
+
...source,
|
|
178
|
+
docId: String(source.docId || docId || ''),
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
if (props.collection === 'sites') {
|
|
182
|
+
return items.filter(item => item.docId !== 'templates')
|
|
183
|
+
}
|
|
184
|
+
return items
|
|
185
|
+
}
|
|
186
|
+
|
|
173
187
|
const snapShotQuery = computed(() => {
|
|
174
188
|
if (state.queryField && state.queryValue) {
|
|
175
189
|
// console.log('snapShotQuery', state.queryField, state.queryOperator, state.queryValue)
|
|
@@ -232,7 +246,10 @@ const allData = computed(() => {
|
|
|
232
246
|
if (!edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`]) {
|
|
233
247
|
return []
|
|
234
248
|
}
|
|
235
|
-
return
|
|
249
|
+
return normalizeCollectionItems(edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`])
|
|
250
|
+
}
|
|
251
|
+
if (props.collection === 'sites') {
|
|
252
|
+
return state.paginatedResults.filter(item => String(item?.docId || '') !== 'templates')
|
|
236
253
|
}
|
|
237
254
|
return state.paginatedResults
|
|
238
255
|
})
|
|
@@ -243,10 +260,12 @@ const filtered = computed(() => {
|
|
|
243
260
|
if (!edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`]) {
|
|
244
261
|
return []
|
|
245
262
|
}
|
|
246
|
-
allData =
|
|
263
|
+
allData = normalizeCollectionItems(edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`])
|
|
247
264
|
}
|
|
248
265
|
else {
|
|
249
|
-
allData =
|
|
266
|
+
allData = props.collection === 'sites'
|
|
267
|
+
? state.paginatedResults.filter(item => String(item?.docId || '') !== 'templates')
|
|
268
|
+
: state.paginatedResults
|
|
250
269
|
}
|
|
251
270
|
|
|
252
271
|
const qRaw = filterText.value
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useVModel } from '@vueuse/core'
|
|
3
|
+
import { ImagePlus, Trash2 } from 'lucide-vue-next'
|
|
4
|
+
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
modelValue: {
|
|
7
|
+
type: String,
|
|
8
|
+
default: '',
|
|
9
|
+
},
|
|
10
|
+
label: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: 'Photo',
|
|
13
|
+
},
|
|
14
|
+
dialogTitle: {
|
|
15
|
+
type: String,
|
|
16
|
+
default: 'Select Image',
|
|
17
|
+
},
|
|
18
|
+
site: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: 'clearwater-hub-images',
|
|
21
|
+
},
|
|
22
|
+
defaultTags: {
|
|
23
|
+
type: Array,
|
|
24
|
+
default: () => [],
|
|
25
|
+
},
|
|
26
|
+
includeCmsAll: {
|
|
27
|
+
type: Boolean,
|
|
28
|
+
default: true,
|
|
29
|
+
},
|
|
30
|
+
heightClass: {
|
|
31
|
+
type: String,
|
|
32
|
+
default: 'h-[160px]',
|
|
33
|
+
},
|
|
34
|
+
showRemove: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
default: true,
|
|
37
|
+
},
|
|
38
|
+
disabled: {
|
|
39
|
+
type: Boolean,
|
|
40
|
+
default: false,
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const emits = defineEmits(['update:modelValue'])
|
|
45
|
+
|
|
46
|
+
const state = reactive({
|
|
47
|
+
dialog: false,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const modelValue = useVModel(props, 'modelValue', emits, {
|
|
51
|
+
passive: false,
|
|
52
|
+
prop: 'modelValue',
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const openDialog = () => {
|
|
56
|
+
if (props.disabled)
|
|
57
|
+
return
|
|
58
|
+
state.dialog = true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const closeDialog = () => {
|
|
62
|
+
state.dialog = false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const selectImage = (url) => {
|
|
66
|
+
modelValue.value = url || ''
|
|
67
|
+
closeDialog()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const clearImage = () => {
|
|
71
|
+
if (props.disabled)
|
|
72
|
+
return
|
|
73
|
+
modelValue.value = ''
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<div class="rounded-lg border bg-background p-4 space-y-3">
|
|
79
|
+
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
80
|
+
<div class="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
81
|
+
{{ props.label }}
|
|
82
|
+
</div>
|
|
83
|
+
<div class="flex items-center gap-2">
|
|
84
|
+
<edge-shad-button size="sm" variant="outline" class="h-8 px-2" :disabled="props.disabled" @click="openDialog">
|
|
85
|
+
<ImagePlus class="h-4 w-4 mr-2" />
|
|
86
|
+
Select
|
|
87
|
+
</edge-shad-button>
|
|
88
|
+
<edge-shad-button
|
|
89
|
+
v-if="props.showRemove && modelValue"
|
|
90
|
+
size="icon"
|
|
91
|
+
variant="ghost"
|
|
92
|
+
class="h-8 w-8 text-destructive/80 hover:text-destructive"
|
|
93
|
+
:disabled="props.disabled"
|
|
94
|
+
@click="clearImage"
|
|
95
|
+
>
|
|
96
|
+
<Trash2 class="h-4 w-4" />
|
|
97
|
+
</edge-shad-button>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="rounded-lg flex items-center justify-center overflow-hidden border bg-muted/20" :class="props.heightClass">
|
|
101
|
+
<img
|
|
102
|
+
v-if="modelValue"
|
|
103
|
+
:src="modelValue"
|
|
104
|
+
alt=""
|
|
105
|
+
class="max-h-full max-w-full h-auto w-auto object-contain"
|
|
106
|
+
>
|
|
107
|
+
<span v-else class="text-sm text-muted-foreground italic">No photo selected</span>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<Dialog v-model:open="state.dialog">
|
|
112
|
+
<DialogContent class="w-full max-w-[1200px] max-h-[80vh] overflow-y-auto">
|
|
113
|
+
<DialogHeader>
|
|
114
|
+
<DialogTitle>{{ props.dialogTitle }}</DialogTitle>
|
|
115
|
+
<DialogDescription />
|
|
116
|
+
</DialogHeader>
|
|
117
|
+
<edge-cms-media-manager
|
|
118
|
+
:site="props.site"
|
|
119
|
+
:default-tags="props.defaultTags"
|
|
120
|
+
:include-cms-all="props.includeCmsAll"
|
|
121
|
+
:select-mode="true"
|
|
122
|
+
@select="selectImage"
|
|
123
|
+
/>
|
|
124
|
+
</DialogContent>
|
|
125
|
+
</Dialog>
|
|
126
|
+
</template>
|
|
@@ -34,6 +34,7 @@ const updateUser = async () => {
|
|
|
34
34
|
state.userError = { success: state.userError.success, message: state.userError.message.replace('Firebase: ', '').replace(' (auth/invalid-email)', '') }
|
|
35
35
|
if (state.userError.success) {
|
|
36
36
|
state.userError = { success: true, message: 'A verification link has been sent to your new email address. Please click the link to complete the email change process.' }
|
|
37
|
+
await edgeFirebase.setUserMeta({ email: state.username })
|
|
37
38
|
}
|
|
38
39
|
edgeGlobal.edgeState.changeTracker = {}
|
|
39
40
|
state.loaded = false
|