@edgedev/create-edge-app 1.2.30 → 1.2.32
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/.env
CHANGED
package/.env.dev
CHANGED
|
@@ -103,6 +103,7 @@ const schemas = {
|
|
|
103
103
|
message: 'At least one domain is required',
|
|
104
104
|
path: ['domains', 0],
|
|
105
105
|
}),
|
|
106
|
+
forwardApex: z.boolean().optional(),
|
|
106
107
|
contactEmail: z.string().optional(),
|
|
107
108
|
contactPhone: z.string().optional(),
|
|
108
109
|
theme: z.string({
|
|
@@ -783,6 +784,7 @@ const isSiteDiff = computed(() => {
|
|
|
783
784
|
brandLogoLight: publishedSite.brandLogoLight,
|
|
784
785
|
favicon: publishedSite.favicon,
|
|
785
786
|
menuPosition: publishedSite.menuPosition,
|
|
787
|
+
forwardApex: publishedSite.forwardApex,
|
|
786
788
|
contactEmail: publishedSite.contactEmail,
|
|
787
789
|
contactPhone: publishedSite.contactPhone,
|
|
788
790
|
metaTitle: publishedSite.metaTitle,
|
|
@@ -810,6 +812,7 @@ const isSiteDiff = computed(() => {
|
|
|
810
812
|
brandLogoLight: siteData.value.brandLogoLight,
|
|
811
813
|
favicon: siteData.value.favicon,
|
|
812
814
|
menuPosition: siteData.value.menuPosition,
|
|
815
|
+
forwardApex: siteData.value.forwardApex,
|
|
813
816
|
contactEmail: siteData.value.contactEmail,
|
|
814
817
|
contactPhone: siteData.value.contactPhone,
|
|
815
818
|
metaTitle: siteData.value.metaTitle,
|
|
@@ -851,6 +854,7 @@ const discardSiteSettings = async () => {
|
|
|
851
854
|
brandLogoLight: publishedSite.brandLogoLight || '',
|
|
852
855
|
favicon: publishedSite.favicon || '',
|
|
853
856
|
menuPosition: publishedSite.menuPosition || '',
|
|
857
|
+
forwardApex: publishedSite.forwardApex === false ? false : true,
|
|
854
858
|
contactEmail: publishedSite.contactEmail || '',
|
|
855
859
|
contactPhone: publishedSite.contactPhone || '',
|
|
856
860
|
metaTitle: publishedSite.metaTitle || '',
|
|
@@ -1699,6 +1703,7 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1699
1703
|
:enable-media-picker="true"
|
|
1700
1704
|
:site-id="props.site"
|
|
1701
1705
|
:domain-error="domainError"
|
|
1706
|
+
:settings-open="state.siteSettings"
|
|
1702
1707
|
/>
|
|
1703
1708
|
</div>
|
|
1704
1709
|
<SheetFooter class="pt-2 flex justify-between">
|
|
@@ -42,6 +42,10 @@ const props = defineProps({
|
|
|
42
42
|
type: String,
|
|
43
43
|
default: '',
|
|
44
44
|
},
|
|
45
|
+
settingsOpen: {
|
|
46
|
+
type: Boolean,
|
|
47
|
+
default: true,
|
|
48
|
+
},
|
|
45
49
|
})
|
|
46
50
|
|
|
47
51
|
const edgeFirebase = inject('edgeFirebase')
|
|
@@ -101,41 +105,266 @@ const menuPositionOptions = [
|
|
|
101
105
|
|
|
102
106
|
const domainError = computed(() => String(props.domainError || '').trim())
|
|
103
107
|
const serverPagesProject = ref('')
|
|
108
|
+
const domainRegistry = ref({})
|
|
109
|
+
const loadingDomainRegistry = ref(false)
|
|
110
|
+
const hasLoadedDomainRegistry = ref(false)
|
|
104
111
|
const pagesProject = computed(() => String(serverPagesProject.value || '').trim())
|
|
105
112
|
const pagesDomain = computed(() => (pagesProject.value ? `${pagesProject.value}.pages.dev` : '(CLOUDFLARE_PAGES_PROJECT).pages.dev'))
|
|
113
|
+
const forwardApexEnabled = computed({
|
|
114
|
+
get: () => props.settings?.forwardApex !== false,
|
|
115
|
+
set: (value) => {
|
|
116
|
+
props.settings.forwardApex = !!value
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const normalizeDomain = (value) => {
|
|
121
|
+
if (!value)
|
|
122
|
+
return ''
|
|
123
|
+
let normalized = String(value).trim().toLowerCase()
|
|
124
|
+
if (!normalized)
|
|
125
|
+
return ''
|
|
126
|
+
if (normalized.includes('://')) {
|
|
127
|
+
try {
|
|
128
|
+
normalized = new URL(normalized).host
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
normalized = normalized.split('://').pop() || normalized
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
normalized = normalized.split('/')[0] || ''
|
|
135
|
+
if (normalized.includes(':') && !normalized.startsWith('[')) {
|
|
136
|
+
normalized = normalized.split(':')[0] || ''
|
|
137
|
+
}
|
|
138
|
+
return normalized.replace(/\.+$/g, '')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const isIpv4Address = (value) => {
|
|
142
|
+
const parts = String(value || '').split('.')
|
|
143
|
+
if (parts.length !== 4)
|
|
144
|
+
return false
|
|
145
|
+
return parts.every((part) => {
|
|
146
|
+
if (!/^\d{1,3}$/.test(part))
|
|
147
|
+
return false
|
|
148
|
+
const num = Number(part)
|
|
149
|
+
return num >= 0 && num <= 255
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const isIpv6Address = (value) => {
|
|
154
|
+
const normalized = String(value || '').toLowerCase()
|
|
155
|
+
if (!normalized.includes(':'))
|
|
156
|
+
return false
|
|
157
|
+
return /^[0-9a-f:]+$/.test(normalized)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const isIpAddress = value => isIpv4Address(value) || isIpv6Address(value)
|
|
161
|
+
|
|
162
|
+
const shouldDisplayDomainDnsRecords = (domain) => {
|
|
163
|
+
if (!domain)
|
|
164
|
+
return false
|
|
165
|
+
if (domain.includes('localhost'))
|
|
166
|
+
return false
|
|
167
|
+
if (isIpAddress(domain))
|
|
168
|
+
return false
|
|
169
|
+
if (domain.endsWith('.dev'))
|
|
170
|
+
return false
|
|
171
|
+
return true
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const getCloudflareApexDomain = (domain) => {
|
|
175
|
+
if (!domain)
|
|
176
|
+
return ''
|
|
177
|
+
if (domain.startsWith('www.'))
|
|
178
|
+
return domain.slice(4)
|
|
179
|
+
return domain
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const getCloudflarePagesDomain = (domain) => {
|
|
183
|
+
if (!domain)
|
|
184
|
+
return ''
|
|
185
|
+
if (domain.startsWith('www.'))
|
|
186
|
+
return domain
|
|
187
|
+
return `www.${domain}`
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const normalizedDomains = computed(() => {
|
|
191
|
+
const values = Array.isArray(props.settings?.domains) ? props.settings.domains : []
|
|
192
|
+
return Array.from(new Set(values.map(normalizeDomain).filter(Boolean)))
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const dnsEligibleDomains = computed(() => normalizedDomains.value.filter(shouldDisplayDomainDnsRecords))
|
|
196
|
+
|
|
197
|
+
const organizationId = computed(() => String(edgeGlobal?.edgeState?.currentOrganization || '').trim())
|
|
198
|
+
const shouldShowDomainRegistryLoading = computed(() => {
|
|
199
|
+
if (!normalizedDomains.value.length)
|
|
200
|
+
return false
|
|
201
|
+
return loadingDomainRegistry.value || !hasLoadedDomainRegistry.value
|
|
202
|
+
})
|
|
203
|
+
const DOMAIN_REGISTRY_POLL_MS = 2500
|
|
204
|
+
let domainRegistryPollTimer = null
|
|
205
|
+
|
|
206
|
+
const buildFallbackDomainEntry = (domain) => {
|
|
207
|
+
const apexDomain = getCloudflareApexDomain(domain)
|
|
208
|
+
const wwwDomain = getCloudflarePagesDomain(apexDomain)
|
|
209
|
+
const target = pagesDomain.value
|
|
210
|
+
const hasTarget = !!String(target || '').trim()
|
|
211
|
+
return {
|
|
212
|
+
domain,
|
|
213
|
+
apexDomain,
|
|
214
|
+
wwwDomain,
|
|
215
|
+
apexAttempted: false,
|
|
216
|
+
apexAdded: false,
|
|
217
|
+
apexError: '',
|
|
218
|
+
dnsGuidance: 'Add the www CNAME record. Apex is unavailable; forward apex to www.',
|
|
219
|
+
dnsRecords: {
|
|
220
|
+
target,
|
|
221
|
+
www: {
|
|
222
|
+
type: 'CNAME',
|
|
223
|
+
name: 'www',
|
|
224
|
+
host: wwwDomain,
|
|
225
|
+
value: target,
|
|
226
|
+
enabled: hasTarget,
|
|
227
|
+
},
|
|
228
|
+
apex: {
|
|
229
|
+
type: 'CNAME',
|
|
230
|
+
name: '@',
|
|
231
|
+
host: apexDomain,
|
|
232
|
+
value: target,
|
|
233
|
+
enabled: false,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const domainDnsEntries = computed(() => {
|
|
240
|
+
return dnsEligibleDomains.value.map((domain) => {
|
|
241
|
+
const fallback = buildFallbackDomainEntry(domain)
|
|
242
|
+
const value = domainRegistry.value?.[domain] || {}
|
|
243
|
+
const dnsRecords = {
|
|
244
|
+
...fallback.dnsRecords,
|
|
245
|
+
...(value.dnsRecords || {}),
|
|
246
|
+
www: {
|
|
247
|
+
...fallback.dnsRecords.www,
|
|
248
|
+
...(value?.dnsRecords?.www || {}),
|
|
249
|
+
},
|
|
250
|
+
apex: {
|
|
251
|
+
...fallback.dnsRecords.apex,
|
|
252
|
+
...(value?.dnsRecords?.apex || {}),
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
|
+
const apexAdded = value?.apexAdded === true
|
|
256
|
+
return {
|
|
257
|
+
...fallback,
|
|
258
|
+
...value,
|
|
259
|
+
dnsRecords,
|
|
260
|
+
apexAdded,
|
|
261
|
+
apexAttempted: value?.apexAttempted === true,
|
|
262
|
+
apexError: String(value?.apexError || '').trim(),
|
|
263
|
+
dnsGuidance: String(value?.dnsGuidance || '').trim()
|
|
264
|
+
|| (apexAdded ? '' : fallback.dnsGuidance),
|
|
265
|
+
}
|
|
266
|
+
})
|
|
267
|
+
})
|
|
106
268
|
|
|
107
|
-
|
|
269
|
+
const fetchDomainRegistry = async (options = {}) => {
|
|
270
|
+
const { background = false } = options
|
|
108
271
|
if (!edgeFirebase?.runFunction)
|
|
109
272
|
return
|
|
273
|
+
const domains = normalizedDomains.value
|
|
274
|
+
if (!background)
|
|
275
|
+
loadingDomainRegistry.value = true
|
|
110
276
|
try {
|
|
111
|
-
const response = await edgeFirebase.runFunction('cms-getCloudflarePagesProject', {
|
|
277
|
+
const response = await edgeFirebase.runFunction('cms-getCloudflarePagesProject', {
|
|
278
|
+
orgId: organizationId.value,
|
|
279
|
+
siteId: props.siteId || '',
|
|
280
|
+
domains,
|
|
281
|
+
})
|
|
112
282
|
serverPagesProject.value = String(response?.data?.project || '').trim()
|
|
283
|
+
const nextRegistry = response?.data?.domainRegistry
|
|
284
|
+
if (domains.length && nextRegistry && typeof nextRegistry === 'object')
|
|
285
|
+
domainRegistry.value = nextRegistry
|
|
286
|
+
else
|
|
287
|
+
domainRegistry.value = {}
|
|
288
|
+
hasLoadedDomainRegistry.value = true
|
|
113
289
|
}
|
|
114
290
|
catch {
|
|
115
291
|
serverPagesProject.value = ''
|
|
292
|
+
domainRegistry.value = {}
|
|
293
|
+
hasLoadedDomainRegistry.value = true
|
|
116
294
|
}
|
|
295
|
+
finally {
|
|
296
|
+
if (!background)
|
|
297
|
+
loadingDomainRegistry.value = false
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const stopDomainRegistryPolling = () => {
|
|
302
|
+
if (domainRegistryPollTimer) {
|
|
303
|
+
clearInterval(domainRegistryPollTimer)
|
|
304
|
+
domainRegistryPollTimer = null
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const startDomainRegistryPolling = () => {
|
|
309
|
+
stopDomainRegistryPolling()
|
|
310
|
+
if (!props.settingsOpen)
|
|
311
|
+
return
|
|
312
|
+
if (!normalizedDomains.value.length)
|
|
313
|
+
return
|
|
314
|
+
domainRegistryPollTimer = setInterval(() => {
|
|
315
|
+
fetchDomainRegistry({ background: true }).catch(() => {})
|
|
316
|
+
}, DOMAIN_REGISTRY_POLL_MS)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
watch(() => `${props.siteId}:${organizationId.value}:${normalizedDomains.value.join('|')}`, async () => {
|
|
320
|
+
hasLoadedDomainRegistry.value = false
|
|
321
|
+
await fetchDomainRegistry()
|
|
322
|
+
startDomainRegistryPolling()
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
watch(() => props.settingsOpen, async (open) => {
|
|
326
|
+
if (open) {
|
|
327
|
+
hasLoadedDomainRegistry.value = false
|
|
328
|
+
await fetchDomainRegistry()
|
|
329
|
+
startDomainRegistryPolling()
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
stopDomainRegistryPolling()
|
|
333
|
+
}
|
|
334
|
+
}, { immediate: true })
|
|
335
|
+
|
|
336
|
+
onBeforeUnmount(() => {
|
|
337
|
+
stopDomainRegistryPolling()
|
|
117
338
|
})
|
|
339
|
+
|
|
340
|
+
watch(() => props.settings?.forwardApex, (value) => {
|
|
341
|
+
if (value === undefined)
|
|
342
|
+
props.settings.forwardApex = true
|
|
343
|
+
}, { immediate: true })
|
|
118
344
|
</script>
|
|
119
345
|
|
|
120
346
|
<template>
|
|
121
347
|
<Tabs class="w-full" default-value="general">
|
|
122
|
-
<TabsList class="w-full
|
|
123
|
-
<TabsTrigger value="general" class="text-
|
|
348
|
+
<TabsList class="w-full mt-3 bg-secondary rounded-sm grid grid-cols-2 md:grid-cols-4 xl:grid-cols-7 gap-1">
|
|
349
|
+
<TabsTrigger value="general" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
124
350
|
General
|
|
125
351
|
</TabsTrigger>
|
|
126
|
-
<TabsTrigger value="
|
|
352
|
+
<TabsTrigger value="domains" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
353
|
+
Domains
|
|
354
|
+
</TabsTrigger>
|
|
355
|
+
<TabsTrigger value="appearance" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
127
356
|
Appearance
|
|
128
357
|
</TabsTrigger>
|
|
129
|
-
<TabsTrigger value="branding" class="text-
|
|
358
|
+
<TabsTrigger value="branding" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
130
359
|
Branding
|
|
131
360
|
</TabsTrigger>
|
|
132
|
-
<TabsTrigger value="seo" class="text-
|
|
361
|
+
<TabsTrigger value="seo" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
133
362
|
SEO
|
|
134
363
|
</TabsTrigger>
|
|
135
|
-
<TabsTrigger value="tracking" class="text-
|
|
364
|
+
<TabsTrigger value="tracking" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
136
365
|
Tracking Pixels
|
|
137
366
|
</TabsTrigger>
|
|
138
|
-
<TabsTrigger value="social" class="text-
|
|
367
|
+
<TabsTrigger value="social" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
139
368
|
Social Media
|
|
140
369
|
</TabsTrigger>
|
|
141
370
|
</TabsList>
|
|
@@ -147,46 +376,6 @@ onMounted(async () => {
|
|
|
147
376
|
placeholder="Enter name"
|
|
148
377
|
class="w-full"
|
|
149
378
|
/>
|
|
150
|
-
<edge-shad-tags
|
|
151
|
-
v-model="props.settings.domains"
|
|
152
|
-
name="domains"
|
|
153
|
-
label="Domains"
|
|
154
|
-
placeholder="Add or remove domains"
|
|
155
|
-
class="w-full"
|
|
156
|
-
/>
|
|
157
|
-
<Alert v-if="domainError" variant="destructive">
|
|
158
|
-
<CircleAlert class="h-4 w-4" />
|
|
159
|
-
<AlertTitle>Domain error</AlertTitle>
|
|
160
|
-
<AlertDescription class="text-sm">
|
|
161
|
-
{{ domainError }}
|
|
162
|
-
</AlertDescription>
|
|
163
|
-
</Alert>
|
|
164
|
-
<div class="rounded-lg border border-border/60 bg-muted/40 p-4 space-y-3">
|
|
165
|
-
<div class="flex items-center justify-between">
|
|
166
|
-
<div class="text-sm font-semibold text-foreground">
|
|
167
|
-
Domain DNS records
|
|
168
|
-
</div>
|
|
169
|
-
<div class="text-xs text-muted-foreground">
|
|
170
|
-
Target: <span class="font-mono">{{ pagesDomain }}</span>
|
|
171
|
-
</div>
|
|
172
|
-
</div>
|
|
173
|
-
<p class="text-sm text-muted-foreground">
|
|
174
|
-
Add these records at your DNS provider.
|
|
175
|
-
</p>
|
|
176
|
-
<div class="space-y-2 text-sm">
|
|
177
|
-
<div class="grid grid-cols-[70px_1fr] gap-3">
|
|
178
|
-
<div class="text-muted-foreground">CNAME</div>
|
|
179
|
-
<div class="font-mono">www → {{ pagesDomain }}</div>
|
|
180
|
-
</div>
|
|
181
|
-
<div class="grid grid-cols-[70px_1fr] gap-3">
|
|
182
|
-
<div class="text-muted-foreground">CNAME</div>
|
|
183
|
-
<div class="font-mono">@ → {{ pagesDomain }}</div>
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
<p class="text-xs text-muted-foreground">
|
|
187
|
-
Then forward the root/apex (TLD) domain to <span class="font-mono">www</span> with a 301 redirect.
|
|
188
|
-
</p>
|
|
189
|
-
</div>
|
|
190
379
|
<edge-shad-input
|
|
191
380
|
v-model="props.settings.contactEmail"
|
|
192
381
|
name="contactEmail"
|
|
@@ -219,6 +408,99 @@ onMounted(async () => {
|
|
|
219
408
|
No organization users available for this site.
|
|
220
409
|
</p>
|
|
221
410
|
</TabsContent>
|
|
411
|
+
<TabsContent value="domains" class="pt-4 space-y-4">
|
|
412
|
+
<edge-shad-tags
|
|
413
|
+
v-model="props.settings.domains"
|
|
414
|
+
name="domains"
|
|
415
|
+
label="Domains"
|
|
416
|
+
placeholder="Add or remove domains"
|
|
417
|
+
class="w-full"
|
|
418
|
+
/>
|
|
419
|
+
<div class="rounded-lg border border-border/60 bg-muted/40 p-4 space-y-3">
|
|
420
|
+
<edge-shad-switch
|
|
421
|
+
v-model="forwardApexEnabled"
|
|
422
|
+
name="forwardApex"
|
|
423
|
+
label="Forward Apex (non-www) domains to www"
|
|
424
|
+
class="w-full"
|
|
425
|
+
>
|
|
426
|
+
Use a single canonical host (www) to avoid duplicate URLs and consolidate SEO signals.
|
|
427
|
+
</edge-shad-switch>
|
|
428
|
+
<p class="text-xs text-amber-700 dark:text-amber-300">
|
|
429
|
+
Warning: if your DNS provider already redirects www to non-www, this can create a redirect loop.
|
|
430
|
+
</p>
|
|
431
|
+
</div>
|
|
432
|
+
<Alert v-if="domainError" variant="destructive">
|
|
433
|
+
<CircleAlert class="h-4 w-4" />
|
|
434
|
+
<AlertTitle>Domain error</AlertTitle>
|
|
435
|
+
<AlertDescription class="text-sm">
|
|
436
|
+
{{ domainError }}
|
|
437
|
+
</AlertDescription>
|
|
438
|
+
</Alert>
|
|
439
|
+
<div class="rounded-lg border border-border/60 bg-muted/40 p-4 space-y-3">
|
|
440
|
+
<div class="flex items-center justify-between">
|
|
441
|
+
<div class="text-sm font-semibold text-foreground">
|
|
442
|
+
Domain DNS records
|
|
443
|
+
</div>
|
|
444
|
+
<div class="text-xs text-muted-foreground">
|
|
445
|
+
Target: <span class="font-mono">{{ pagesDomain }}</span>
|
|
446
|
+
</div>
|
|
447
|
+
</div>
|
|
448
|
+
<p class="text-sm text-muted-foreground">
|
|
449
|
+
Records are listed for each domain.
|
|
450
|
+
</p>
|
|
451
|
+
<p v-if="shouldShowDomainRegistryLoading" class="text-xs text-muted-foreground">
|
|
452
|
+
Waiting for latest domain sync results...
|
|
453
|
+
</p>
|
|
454
|
+
<div v-if="!domainDnsEntries.length" class="text-sm text-muted-foreground italic">
|
|
455
|
+
Add at least one valid domain to see DNS records.
|
|
456
|
+
</div>
|
|
457
|
+
<div
|
|
458
|
+
v-for="entry in domainDnsEntries"
|
|
459
|
+
:key="entry.domain"
|
|
460
|
+
class="rounded-md border border-border/50 bg-background/70 p-3 space-y-3"
|
|
461
|
+
>
|
|
462
|
+
<div class="flex items-center justify-between gap-3">
|
|
463
|
+
<div class="text-sm font-semibold text-foreground font-mono">
|
|
464
|
+
{{ entry.domain }}
|
|
465
|
+
</div>
|
|
466
|
+
<div class="text-xs">
|
|
467
|
+
<span
|
|
468
|
+
v-if="entry.apexAdded"
|
|
469
|
+
class="rounded bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 px-2 py-1"
|
|
470
|
+
>
|
|
471
|
+
Apex added
|
|
472
|
+
</span>
|
|
473
|
+
<span
|
|
474
|
+
v-else
|
|
475
|
+
class="rounded bg-amber-500/15 text-amber-700 dark:text-amber-300 px-2 py-1"
|
|
476
|
+
>
|
|
477
|
+
Apex not added
|
|
478
|
+
</span>
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
<div class="space-y-2 text-sm">
|
|
482
|
+
<div class="grid grid-cols-[70px_1fr] gap-3">
|
|
483
|
+
<div class="text-muted-foreground">CNAME</div>
|
|
484
|
+
<div class="font-mono">
|
|
485
|
+
{{ entry?.dnsRecords?.www?.name || 'www' }} → {{ entry?.dnsRecords?.www?.value || pagesDomain }}
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
<div class="grid grid-cols-[70px_1fr] gap-3">
|
|
489
|
+
<div class="text-muted-foreground">CNAME</div>
|
|
490
|
+
<div class="font-mono">
|
|
491
|
+
{{ entry?.dnsRecords?.apex?.name || '@' }} → {{ entry?.dnsRecords?.apex?.value || pagesDomain }}
|
|
492
|
+
</div>
|
|
493
|
+
</div>
|
|
494
|
+
</div>
|
|
495
|
+
<p v-if="entry.apexError" class="text-xs text-amber-700 dark:text-amber-300">
|
|
496
|
+
{{ entry.apexError }}
|
|
497
|
+
</p>
|
|
498
|
+
<p v-if="!entry.apexAdded && entry.dnsGuidance" class="text-xs text-muted-foreground">
|
|
499
|
+
{{ entry.dnsGuidance }}
|
|
500
|
+
</p>
|
|
501
|
+
</div>
|
|
502
|
+
</div>
|
|
503
|
+
</TabsContent>
|
|
222
504
|
<TabsContent value="appearance" class="pt-4 space-y-4">
|
|
223
505
|
<edge-shad-select-tags
|
|
224
506
|
v-if="props.showThemeFields && props.isAdmin"
|
|
@@ -87,6 +87,24 @@ const filteredMetaFields = computed(() => {
|
|
|
87
87
|
return (props.metaFields || []).filter(field => !skipFields.has(field.field))
|
|
88
88
|
})
|
|
89
89
|
|
|
90
|
+
const profilePhotoField = computed(() => {
|
|
91
|
+
return (props.metaFields || []).find(field => field?.field === 'profilephoto') || {}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const profilePhotoSite = computed(() => {
|
|
95
|
+
const baseSite = String(profilePhotoField.value?.site || 'system-images').trim() || 'system-images'
|
|
96
|
+
const uid = edgeFirebase?.user?.uid
|
|
97
|
+
if (!uid)
|
|
98
|
+
return baseSite
|
|
99
|
+
return `${baseSite}-${uid}`
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const profilePhotoTags = computed(() => {
|
|
103
|
+
if (Array.isArray(profilePhotoField.value?.tags) && profilePhotoField.value.tags.length)
|
|
104
|
+
return profilePhotoField.value.tags
|
|
105
|
+
return ['Headshots']
|
|
106
|
+
})
|
|
107
|
+
|
|
90
108
|
const initializeDefaults = (meta) => {
|
|
91
109
|
defaultFields.forEach((field) => {
|
|
92
110
|
if (!(field.field in meta))
|
|
@@ -249,8 +267,8 @@ const menuIcon = computed(() => {
|
|
|
249
267
|
v-model="state.meta.profilephoto"
|
|
250
268
|
label="Profile Photo"
|
|
251
269
|
dialog-title="Select Profile Photo"
|
|
252
|
-
:site="
|
|
253
|
-
:default-tags="
|
|
270
|
+
:site="profilePhotoSite"
|
|
271
|
+
:default-tags="profilePhotoTags"
|
|
254
272
|
height-class="h-full min-h-[180px]"
|
|
255
273
|
:include-cms-all="false"
|
|
256
274
|
/>
|
|
@@ -294,7 +312,7 @@ const menuIcon = computed(() => {
|
|
|
294
312
|
:model-value="getByPath(state.meta, field.field, '')"
|
|
295
313
|
:label="field?.label || 'Photo'"
|
|
296
314
|
:dialog-title="field?.dialogTitle || 'Select Image'"
|
|
297
|
-
:site="field?.site || '
|
|
315
|
+
:site="field?.site || 'system-images'"
|
|
298
316
|
:include-cms-all="false"
|
|
299
317
|
:default-tags="field?.tags || []"
|
|
300
318
|
:height-class="field?.heightClass || 'h-[160px]'"
|
|
@@ -15,6 +15,7 @@ export const useSiteSettingsTemplate = () => {
|
|
|
15
15
|
favicon: '',
|
|
16
16
|
menuPosition: 'right',
|
|
17
17
|
domains: [],
|
|
18
|
+
forwardApex: true,
|
|
18
19
|
contactEmail: '',
|
|
19
20
|
contactPhone: '',
|
|
20
21
|
metaTitle: '',
|
|
@@ -49,6 +50,7 @@ export const useSiteSettingsTemplate = () => {
|
|
|
49
50
|
favicon: { bindings: { 'field-type': 'text', 'label': 'Favicon' }, cols: '12', value: defaults.favicon },
|
|
50
51
|
menuPosition: { bindings: { 'field-type': 'select', 'label': 'Menu Position', 'items': ['left', 'center', 'right'] }, cols: '12', value: defaults.menuPosition },
|
|
51
52
|
domains: { bindings: { 'field-type': 'tags', 'label': 'Domains', 'helper': 'Add or remove domains' }, cols: '12', value: defaults.domains },
|
|
53
|
+
forwardApex: { bindings: { 'field-type': 'boolean', 'label': 'Forward Apex (non-www) domains to www' }, cols: '12', value: defaults.forwardApex },
|
|
52
54
|
contactEmail: { bindings: { 'field-type': 'text', 'label': 'Contact Email' }, cols: '12', value: defaults.contactEmail },
|
|
53
55
|
contactPhone: { bindings: { 'field-type': 'text', 'label': 'Contact Phone' }, cols: '12', value: defaults.contactPhone },
|
|
54
56
|
metaTitle: { bindings: { 'field-type': 'text', 'label': 'Meta Title' }, cols: '12', value: defaults.metaTitle },
|