@edgedev/create-edge-app 1.1.27 → 1.1.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/edge/components/auth/register.vue +51 -0
- package/edge/components/cms/block.vue +363 -42
- package/edge/components/cms/blockEditor.vue +50 -3
- package/edge/components/cms/codeEditor.vue +39 -2
- package/edge/components/cms/htmlContent.vue +10 -2
- package/edge/components/cms/init_blocks/footer.html +111 -19
- package/edge/components/cms/init_blocks/image.html +8 -0
- package/edge/components/cms/init_blocks/post_content.html +3 -2
- package/edge/components/cms/init_blocks/post_title_header.html +8 -6
- package/edge/components/cms/init_blocks/posts_list.html +6 -5
- package/edge/components/cms/mediaCard.vue +13 -2
- package/edge/components/cms/mediaManager.vue +35 -5
- package/edge/components/cms/menu.vue +384 -61
- package/edge/components/cms/optionsSelect.vue +20 -3
- package/edge/components/cms/page.vue +160 -18
- package/edge/components/cms/site.vue +548 -374
- package/edge/components/cms/siteSettingsForm.vue +623 -0
- package/edge/components/cms/themeDefaultMenu.vue +258 -22
- package/edge/components/cms/themeEditor.vue +95 -11
- package/edge/components/editor.vue +1 -0
- package/edge/components/formSubtypes/myOrgs.vue +112 -1
- package/edge/components/imagePicker.vue +126 -0
- package/edge/components/myAccount.vue +1 -0
- package/edge/components/myProfile.vue +345 -61
- package/edge/components/orgSwitcher.vue +1 -1
- package/edge/components/organizationMembers.vue +620 -235
- package/edge/components/shad/html.vue +6 -0
- package/edge/components/shad/number.vue +2 -2
- package/edge/components/sideBar.vue +7 -4
- package/edge/components/sideBarContent.vue +1 -1
- package/edge/components/userMenu.vue +50 -14
- package/edge/composables/global.ts +4 -1
- package/edge/composables/siteSettingsTemplate.js +79 -0
- package/edge/composables/structuredDataTemplates.js +36 -0
- package/package.json +1 -1
|
@@ -163,6 +163,10 @@ const modelValue = useVModel(props, 'modelValue', emits, {
|
|
|
163
163
|
})
|
|
164
164
|
|
|
165
165
|
const editor = ref(null)
|
|
166
|
+
const enterKeyHandler = (event) => {
|
|
167
|
+
if (event.key === 'Enter')
|
|
168
|
+
event.stopPropagation()
|
|
169
|
+
}
|
|
166
170
|
const imageState = reactive({
|
|
167
171
|
active: false,
|
|
168
172
|
size: 'medium',
|
|
@@ -279,6 +283,7 @@ onMounted(() => {
|
|
|
279
283
|
})
|
|
280
284
|
editor.value.on('selectionUpdate', updateImageState)
|
|
281
285
|
editor.value.on('transaction', updateImageState)
|
|
286
|
+
editor.value?.view?.dom?.addEventListener('keydown', enterKeyHandler)
|
|
282
287
|
updateImageState()
|
|
283
288
|
applyHeightClasses()
|
|
284
289
|
})
|
|
@@ -287,6 +292,7 @@ onBeforeUnmount(() => {
|
|
|
287
292
|
if (editor.value) {
|
|
288
293
|
editor.value.off('selectionUpdate', updateImageState)
|
|
289
294
|
editor.value.off('transaction', updateImageState)
|
|
295
|
+
editor.value?.view?.dom?.removeEventListener('keydown', enterKeyHandler)
|
|
290
296
|
editor.value.destroy()
|
|
291
297
|
}
|
|
292
298
|
})
|
|
@@ -84,11 +84,11 @@ const numericValue = computed({
|
|
|
84
84
|
:disabled="props.disabled"
|
|
85
85
|
>
|
|
86
86
|
<NumberFieldContent>
|
|
87
|
-
<NumberFieldDecrement class="
|
|
87
|
+
<NumberFieldDecrement class="" />
|
|
88
88
|
<FormControl>
|
|
89
89
|
<NumberFieldInput />
|
|
90
90
|
</FormControl>
|
|
91
|
-
<NumberFieldIncrement class="
|
|
91
|
+
<NumberFieldIncrement class="" />
|
|
92
92
|
</NumberFieldContent>
|
|
93
93
|
</NumberField>
|
|
94
94
|
|
|
@@ -16,7 +16,7 @@ const props = defineProps({
|
|
|
16
16
|
},
|
|
17
17
|
organizationTitle: {
|
|
18
18
|
type: String,
|
|
19
|
-
default: 'Organization
|
|
19
|
+
default: 'Organization',
|
|
20
20
|
},
|
|
21
21
|
menuItems: {
|
|
22
22
|
type: Array,
|
|
@@ -86,7 +86,8 @@ onMounted(() => {
|
|
|
86
86
|
try {
|
|
87
87
|
devOverride.value = localStorage.getItem(DEV_OVERRIDE_KEY) === '1'
|
|
88
88
|
edgeGlobal.edgeState.devOverride = devOverride.value
|
|
89
|
-
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
90
91
|
console.warn('dev override read failed', error)
|
|
91
92
|
}
|
|
92
93
|
})
|
|
@@ -97,10 +98,12 @@ const toggleDevOverride = () => {
|
|
|
97
98
|
try {
|
|
98
99
|
if (devOverride.value) {
|
|
99
100
|
localStorage.setItem(DEV_OVERRIDE_KEY, '1')
|
|
100
|
-
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
101
103
|
localStorage.removeItem(DEV_OVERRIDE_KEY)
|
|
102
104
|
}
|
|
103
|
-
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
104
107
|
console.warn('dev override write failed', error)
|
|
105
108
|
}
|
|
106
109
|
}
|
|
@@ -3,7 +3,7 @@ import { cn } from '@/lib/utils'
|
|
|
3
3
|
const props = defineProps({
|
|
4
4
|
title: {
|
|
5
5
|
type: String,
|
|
6
|
-
default: 'Organization
|
|
6
|
+
default: 'Organization',
|
|
7
7
|
},
|
|
8
8
|
buttonClass: {
|
|
9
9
|
type: String,
|
|
@@ -26,6 +26,20 @@ const isAdmin = computed(() => {
|
|
|
26
26
|
|
|
27
27
|
return orgRole && orgRole.role === 'admin'
|
|
28
28
|
})
|
|
29
|
+
const organizations = computed(() => edgeGlobal.edgeState.organizations || [])
|
|
30
|
+
const currentOrg = computed(() => organizations.value.find(org => org.docId === edgeGlobal.edgeState.currentOrganization))
|
|
31
|
+
const currentOrgName = computed(() => currentOrg.value?.name || 'Organization')
|
|
32
|
+
const hasMultipleOrgs = computed(() => organizations.value.length > 1)
|
|
33
|
+
const orgDialogOpen = ref(false)
|
|
34
|
+
const openOrgDialog = () => {
|
|
35
|
+
if (hasMultipleOrgs.value) {
|
|
36
|
+
orgDialogOpen.value = true
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const selectOrg = (orgId) => {
|
|
40
|
+
edgeGlobal.setOrganization(orgId, edgeFirebase)
|
|
41
|
+
orgDialogOpen.value = false
|
|
42
|
+
}
|
|
29
43
|
const route = useRoute()
|
|
30
44
|
const router = useRouter()
|
|
31
45
|
const goTo = (path) => {
|
|
@@ -69,19 +83,16 @@ const firstPart = computed(() => {
|
|
|
69
83
|
<DropdownMenuLabel v-if="!props.singleOrg" class="text-xs text-muted-foreground">
|
|
70
84
|
{{ props.title }}
|
|
71
85
|
</DropdownMenuLabel>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
<div v-else class="h-3 w-3 mr-2" />
|
|
83
|
-
</DropdownMenuItem>
|
|
84
|
-
</template>
|
|
86
|
+
<DropdownMenuItem
|
|
87
|
+
v-if="!props.singleOrg"
|
|
88
|
+
class="cursor-pointer"
|
|
89
|
+
:disabled="!hasMultipleOrgs"
|
|
90
|
+
@click="openOrgDialog"
|
|
91
|
+
>
|
|
92
|
+
<span class="truncate max-w-[180px]">{{ currentOrgName }}</span>
|
|
93
|
+
<span v-if="hasMultipleOrgs" class="ml-auto text-xs text-muted-foreground">Switch</span>
|
|
94
|
+
<ChevronsUpDown v-if="hasMultipleOrgs" class="h-4 w-4 ml-2" />
|
|
95
|
+
</DropdownMenuItem>
|
|
85
96
|
<template v-if="isAdmin">
|
|
86
97
|
<DropdownMenuSeparator />
|
|
87
98
|
<DropdownMenuLabel class="text-xs text-muted-foreground">
|
|
@@ -145,4 +156,29 @@ const firstPart = computed(() => {
|
|
|
145
156
|
</DropdownMenuItem>
|
|
146
157
|
</DropdownMenuContent>
|
|
147
158
|
</DropdownMenu>
|
|
159
|
+
|
|
160
|
+
<edge-shad-dialog v-model="orgDialogOpen">
|
|
161
|
+
<DialogContent class="w-full max-w-3xl max-h-[80vh] flex flex-col">
|
|
162
|
+
<DialogHeader>
|
|
163
|
+
<DialogTitle>Select Organization</DialogTitle>
|
|
164
|
+
<DialogDescription class="text-left">
|
|
165
|
+
Choose an organization to switch context.
|
|
166
|
+
</DialogDescription>
|
|
167
|
+
</DialogHeader>
|
|
168
|
+
<div class="mt-4 flex-1 overflow-y-auto">
|
|
169
|
+
<div class="space-y-2">
|
|
170
|
+
<edge-shad-button
|
|
171
|
+
v-for="org in organizations"
|
|
172
|
+
:key="org.docId"
|
|
173
|
+
variant="ghost"
|
|
174
|
+
class="w-full justify-between"
|
|
175
|
+
@click="selectOrg(org.docId)"
|
|
176
|
+
>
|
|
177
|
+
<span class="truncate text-left">{{ org.name }}</span>
|
|
178
|
+
<Check v-if="org.docId === edgeGlobal.edgeState.currentOrganization" class="h-4 w-4" />
|
|
179
|
+
</edge-shad-button>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</DialogContent>
|
|
183
|
+
</edge-shad-dialog>
|
|
148
184
|
</template>
|
|
@@ -451,7 +451,10 @@ const cmsCollectionData = async (edgeFirebase: any, value: any, meta: any, curre
|
|
|
451
451
|
const findIndex = currentQuery.findIndex((q: any) => q.field === queryKey)
|
|
452
452
|
const queryOption = meta[key]?.queryOptions?.find((o: any) => o.field === queryKey)
|
|
453
453
|
const operator = queryOption?.operator || '=='
|
|
454
|
-
|
|
454
|
+
let value = meta[key].queryItems[queryKey]
|
|
455
|
+
if (operator === 'array-contains-any' && !Array.isArray(value))
|
|
456
|
+
value = [value]
|
|
457
|
+
const newQuery = { field: queryKey, operator, value }
|
|
455
458
|
if (findIndex > -1) {
|
|
456
459
|
currentQuery[findIndex] = newQuery
|
|
457
460
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useStructuredDataTemplates } from '@/edge/composables/structuredDataTemplates'
|
|
2
|
+
|
|
3
|
+
export const useSiteSettingsTemplate = () => {
|
|
4
|
+
const { buildSiteStructuredData } = useStructuredDataTemplates()
|
|
5
|
+
const createDefaults = () => ({
|
|
6
|
+
name: '',
|
|
7
|
+
theme: '',
|
|
8
|
+
allowedThemes: [],
|
|
9
|
+
logo: '',
|
|
10
|
+
logoLight: '',
|
|
11
|
+
logoText: '',
|
|
12
|
+
logoType: 'image',
|
|
13
|
+
brandLogoDark: '',
|
|
14
|
+
brandLogoLight: '',
|
|
15
|
+
favicon: '',
|
|
16
|
+
menuPosition: 'right',
|
|
17
|
+
domains: [],
|
|
18
|
+
contactEmail: '',
|
|
19
|
+
contactPhone: '',
|
|
20
|
+
metaTitle: '',
|
|
21
|
+
metaDescription: '',
|
|
22
|
+
structuredData: buildSiteStructuredData(),
|
|
23
|
+
trackingFacebookPixel: '',
|
|
24
|
+
trackingGoogleAnalytics: '',
|
|
25
|
+
trackingAdroll: '',
|
|
26
|
+
socialFacebook: '',
|
|
27
|
+
socialInstagram: '',
|
|
28
|
+
socialTwitter: '',
|
|
29
|
+
socialLinkedIn: '',
|
|
30
|
+
socialYouTube: '',
|
|
31
|
+
socialTikTok: '',
|
|
32
|
+
users: [],
|
|
33
|
+
aiAgentUserId: '',
|
|
34
|
+
aiInstructions: '',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const createNewDocSchema = () => {
|
|
38
|
+
const defaults = createDefaults()
|
|
39
|
+
return {
|
|
40
|
+
name: { bindings: { 'field-type': 'text', 'label': 'Name' }, cols: '12', value: defaults.name },
|
|
41
|
+
theme: { bindings: { 'field-type': 'collection', 'label': 'Themes', 'collection-path': 'themes' }, cols: '12', value: defaults.theme },
|
|
42
|
+
allowedThemes: { bindings: { 'field-type': 'tags', 'label': 'Allowed Themes' }, cols: '12', value: defaults.allowedThemes },
|
|
43
|
+
logo: { bindings: { 'field-type': 'text', 'label': 'Dark logo' }, cols: '12', value: defaults.logo },
|
|
44
|
+
logoLight: { bindings: { 'field-type': 'text', 'label': 'Logo Light' }, cols: '12', value: defaults.logoLight },
|
|
45
|
+
logoText: { bindings: { 'field-type': 'text', 'label': 'Logo Text' }, cols: '12', value: defaults.logoText },
|
|
46
|
+
logoType: { bindings: { 'field-type': 'select', 'label': 'Logo Type', 'items': ['image', 'text'] }, cols: '12', value: defaults.logoType },
|
|
47
|
+
brandLogoDark: { bindings: { 'field-type': 'text', 'label': 'Brand Logo Dark' }, cols: '12', value: defaults.brandLogoDark },
|
|
48
|
+
brandLogoLight: { bindings: { 'field-type': 'text', 'label': 'Brand Logo Light' }, cols: '12', value: defaults.brandLogoLight },
|
|
49
|
+
favicon: { bindings: { 'field-type': 'text', 'label': 'Favicon' }, cols: '12', value: defaults.favicon },
|
|
50
|
+
menuPosition: { bindings: { 'field-type': 'select', 'label': 'Menu Position', 'items': ['left', 'center', 'right'] }, cols: '12', value: defaults.menuPosition },
|
|
51
|
+
domains: { bindings: { 'field-type': 'tags', 'label': 'Domains', 'helper': 'Add or remove domains' }, cols: '12', value: defaults.domains },
|
|
52
|
+
contactEmail: { bindings: { 'field-type': 'text', 'label': 'Contact Email' }, cols: '12', value: defaults.contactEmail },
|
|
53
|
+
contactPhone: { bindings: { 'field-type': 'text', 'label': 'Contact Phone' }, cols: '12', value: defaults.contactPhone },
|
|
54
|
+
metaTitle: { bindings: { 'field-type': 'text', 'label': 'Meta Title' }, cols: '12', value: defaults.metaTitle },
|
|
55
|
+
metaDescription: { bindings: { 'field-type': 'textarea', 'label': 'Meta Description' }, cols: '12', value: defaults.metaDescription },
|
|
56
|
+
structuredData: { bindings: { 'field-type': 'textarea', 'label': 'Structured Data (JSON-LD)' }, cols: '12', value: defaults.structuredData },
|
|
57
|
+
trackingFacebookPixel: { bindings: { 'field-type': 'text', 'label': 'Facebook Pixel ID' }, cols: '12', value: defaults.trackingFacebookPixel },
|
|
58
|
+
trackingGoogleAnalytics: { bindings: { 'field-type': 'text', 'label': 'Google Analytics ID' }, cols: '12', value: defaults.trackingGoogleAnalytics },
|
|
59
|
+
trackingAdroll: { bindings: { 'field-type': 'text', 'label': 'AdRoll ID' }, cols: '12', value: defaults.trackingAdroll },
|
|
60
|
+
socialFacebook: { bindings: { 'field-type': 'text', 'label': 'Facebook URL' }, cols: '12', value: defaults.socialFacebook },
|
|
61
|
+
socialInstagram: { bindings: { 'field-type': 'text', 'label': 'Instagram URL' }, cols: '12', value: defaults.socialInstagram },
|
|
62
|
+
socialTwitter: { bindings: { 'field-type': 'text', 'label': 'X (Twitter) URL' }, cols: '12', value: defaults.socialTwitter },
|
|
63
|
+
socialLinkedIn: { bindings: { 'field-type': 'text', 'label': 'LinkedIn URL' }, cols: '12', value: defaults.socialLinkedIn },
|
|
64
|
+
socialYouTube: { bindings: { 'field-type': 'text', 'label': 'YouTube URL' }, cols: '12', value: defaults.socialYouTube },
|
|
65
|
+
socialTikTok: { bindings: { 'field-type': 'text', 'label': 'TikTok URL' }, cols: '12', value: defaults.socialTikTok },
|
|
66
|
+
users: { bindings: { 'field-type': 'users', 'label': 'Users', 'hint': 'Choose users' }, cols: '12', value: defaults.users },
|
|
67
|
+
aiAgentUserId: { bindings: { 'field-type': 'select', 'label': 'Agent Data for AI to use to build initial site' }, cols: '12', value: defaults.aiAgentUserId },
|
|
68
|
+
aiInstructions: { bindings: { 'field-type': 'textarea', 'label': 'Additional AI Instructions' }, cols: '12', value: defaults.aiInstructions },
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const settingsKeys = Object.keys(createDefaults())
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
createDefaults,
|
|
76
|
+
createNewDocSchema,
|
|
77
|
+
settingsKeys,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const useStructuredDataTemplates = () => {
|
|
2
|
+
const buildSiteStructuredData = () => JSON.stringify({
|
|
3
|
+
'@context': 'https://schema.org',
|
|
4
|
+
'@type': 'WebSite',
|
|
5
|
+
'@id': '{{cms-site}}#website',
|
|
6
|
+
'name': '',
|
|
7
|
+
'url': '{{cms-site}}',
|
|
8
|
+
'description': '',
|
|
9
|
+
'publisher': {
|
|
10
|
+
'@type': 'Organization',
|
|
11
|
+
'name': '',
|
|
12
|
+
'logo': {
|
|
13
|
+
'@type': 'ImageObject',
|
|
14
|
+
'url': '{{cms-logo}}',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
'sameAs': [],
|
|
18
|
+
}, null, 2)
|
|
19
|
+
|
|
20
|
+
const buildPageStructuredData = () => JSON.stringify({
|
|
21
|
+
'@context': 'https://schema.org',
|
|
22
|
+
'@type': 'WebPage',
|
|
23
|
+
'@id': '{{cms-url}}#webpage',
|
|
24
|
+
'name': '',
|
|
25
|
+
'url': '{{cms-url}}',
|
|
26
|
+
'description': '',
|
|
27
|
+
'isPartOf': {
|
|
28
|
+
'@id': '{{cms-site}}#website',
|
|
29
|
+
},
|
|
30
|
+
}, null, 2)
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
buildSiteStructuredData,
|
|
34
|
+
buildPageStructuredData,
|
|
35
|
+
}
|
|
36
|
+
}
|