@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.
Files changed (35) hide show
  1. package/edge/components/auth/register.vue +51 -0
  2. package/edge/components/cms/block.vue +363 -42
  3. package/edge/components/cms/blockEditor.vue +50 -3
  4. package/edge/components/cms/codeEditor.vue +39 -2
  5. package/edge/components/cms/htmlContent.vue +10 -2
  6. package/edge/components/cms/init_blocks/footer.html +111 -19
  7. package/edge/components/cms/init_blocks/image.html +8 -0
  8. package/edge/components/cms/init_blocks/post_content.html +3 -2
  9. package/edge/components/cms/init_blocks/post_title_header.html +8 -6
  10. package/edge/components/cms/init_blocks/posts_list.html +6 -5
  11. package/edge/components/cms/mediaCard.vue +13 -2
  12. package/edge/components/cms/mediaManager.vue +35 -5
  13. package/edge/components/cms/menu.vue +384 -61
  14. package/edge/components/cms/optionsSelect.vue +20 -3
  15. package/edge/components/cms/page.vue +160 -18
  16. package/edge/components/cms/site.vue +548 -374
  17. package/edge/components/cms/siteSettingsForm.vue +623 -0
  18. package/edge/components/cms/themeDefaultMenu.vue +258 -22
  19. package/edge/components/cms/themeEditor.vue +95 -11
  20. package/edge/components/editor.vue +1 -0
  21. package/edge/components/formSubtypes/myOrgs.vue +112 -1
  22. package/edge/components/imagePicker.vue +126 -0
  23. package/edge/components/myAccount.vue +1 -0
  24. package/edge/components/myProfile.vue +345 -61
  25. package/edge/components/orgSwitcher.vue +1 -1
  26. package/edge/components/organizationMembers.vue +620 -235
  27. package/edge/components/shad/html.vue +6 -0
  28. package/edge/components/shad/number.vue +2 -2
  29. package/edge/components/sideBar.vue +7 -4
  30. package/edge/components/sideBarContent.vue +1 -1
  31. package/edge/components/userMenu.vue +50 -14
  32. package/edge/composables/global.ts +4 -1
  33. package/edge/composables/siteSettingsTemplate.js +79 -0
  34. package/edge/composables/structuredDataTemplates.js +36 -0
  35. 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="mt-5" />
87
+ <NumberFieldDecrement class="" />
88
88
  <FormControl>
89
89
  <NumberFieldInput />
90
90
  </FormControl>
91
- <NumberFieldIncrement class="mt-5" />
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(s)',
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
- } catch (error) {
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
- } else {
101
+ }
102
+ else {
101
103
  localStorage.removeItem(DEV_OVERRIDE_KEY)
102
104
  }
103
- } catch (error) {
105
+ }
106
+ catch (error) {
104
107
  console.warn('dev override write failed', error)
105
108
  }
106
109
  }
@@ -17,7 +17,7 @@ const props = defineProps({
17
17
  },
18
18
  organizationTitle: {
19
19
  type: String,
20
- default: 'Organization(s)',
20
+ default: 'Organization',
21
21
  },
22
22
  singleOrg: {
23
23
  type: Boolean,
@@ -3,7 +3,7 @@ import { cn } from '@/lib/utils'
3
3
  const props = defineProps({
4
4
  title: {
5
5
  type: String,
6
- default: 'Organization(s)',
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
- <template v-for="org in edgeGlobal.edgeState.organizations">
73
- <DropdownMenuItem
74
- v-if="!props.singleOrg"
75
- :key="org.docId"
76
- class="cursor-pointer"
77
- :class="{ 'bg-accent': org.docId === edgeGlobal.edgeState.currentOrganization }"
78
- @click="edgeGlobal.setOrganization(org.docId, edgeFirebase)"
79
- >
80
- {{ org.name }}
81
- <Check v-if="org.docId === edgeGlobal.edgeState.currentOrganization" class="h-3 w-3 mr-2 ml-auto" />
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
- const newQuery = { field: queryKey, operator, value: meta[key].queryItems[queryKey] }
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/create-edge-app",
3
- "version": "1.1.27",
3
+ "version": "1.1.29",
4
4
  "description": "Create Edge Starter App",
5
5
  "bin": {
6
6
  "create-edge-app": "./bin/cli.js"