@edgedev/create-edge-app 1.1.26 → 1.1.28
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/cms/block.vue +334 -26
- package/edge/components/cms/blockEditor.vue +50 -3
- package/edge/components/cms/codeEditor.vue +15 -0
- 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 +16 -2
- package/edge/components/cms/menu.vue +253 -42
- package/edge/components/cms/page.vue +151 -18
- package/edge/components/cms/site.vue +537 -215
- package/edge/components/cms/siteSettingsForm.vue +616 -0
- package/edge/components/cms/themeDefaultMenu.vue +258 -22
- package/edge/components/cms/themeEditor.vue +99 -12
- package/edge/components/editor.vue +1 -0
- package/edge/components/formSubtypes/myOrgs.vue +112 -1
- package/edge/components/orgSwitcher.vue +1 -1
- package/edge/components/organizationMembers.vue +171 -21
- package/edge/components/shad/html.vue +6 -0
- package/edge/components/sideBar.vue +7 -4
- package/edge/components/sideBarContent.vue +1 -1
- package/edge/components/userMenu.vue +50 -14
- package/edge/composables/siteSettingsTemplate.js +79 -0
- package/edge/composables/structuredDataTemplates.js +36 -0
- package/package.json +1 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script setup lang="js">
|
|
2
2
|
import { useVModel } from '@vueuse/core'
|
|
3
|
-
import { File, FileCheck, FileCog, FileDown, FileMinus2, FilePen, FilePlus2, FileUp, FileWarning, FileX, Folder, FolderMinus, FolderOpen, FolderPen, FolderPlus } from 'lucide-vue-next'
|
|
3
|
+
import { File, FileCheck, FileCog, FileDown, FileMinus2, FilePen, FilePlus2, FileUp, FileWarning, FileX, Folder, FolderMinus, FolderOpen, FolderPen, FolderPlus, Link } from 'lucide-vue-next'
|
|
4
4
|
import { toTypedSchema } from '@vee-validate/zod'
|
|
5
5
|
import * as z from 'zod'
|
|
6
|
+
import { useStructuredDataTemplates } from '@/edge/composables/structuredDataTemplates'
|
|
6
7
|
|
|
7
8
|
const props = defineProps({
|
|
8
9
|
prevModelValue: {
|
|
@@ -50,6 +51,15 @@ const router = useRouter()
|
|
|
50
51
|
const modelValue = useVModel(props, 'modelValue', emit)
|
|
51
52
|
const route = useRoute()
|
|
52
53
|
const edgeFirebase = inject('edgeFirebase')
|
|
54
|
+
const { buildPageStructuredData } = useStructuredDataTemplates()
|
|
55
|
+
|
|
56
|
+
const isExternalLinkEntry = entry => entry?.item && typeof entry.item === 'object' && entry.item.type === 'external'
|
|
57
|
+
const isPageEntry = entry => typeof entry?.item === 'string'
|
|
58
|
+
const isRenameDisabled = entry => isPageEntry(entry) && !!entry?.disableRename
|
|
59
|
+
const isDeleteDisabled = entry => isPageEntry(entry) && !!entry?.disableDelete
|
|
60
|
+
const isLinkUrlSpecial = url => /^tel:|^mailto:/i.test(String(url || '').trim())
|
|
61
|
+
const linkTarget = url => (isLinkUrlSpecial(url) ? null : '_blank')
|
|
62
|
+
const linkRel = url => (isLinkUrlSpecial(url) ? null : 'noopener noreferrer')
|
|
53
63
|
|
|
54
64
|
const normalizeForCompare = (value) => {
|
|
55
65
|
if (Array.isArray(value))
|
|
@@ -142,17 +152,26 @@ const state = reactive({
|
|
|
142
152
|
newPageName: '',
|
|
143
153
|
indexPath: '',
|
|
144
154
|
addMenu: false,
|
|
155
|
+
addLinkDialog: false,
|
|
145
156
|
deletePage: {},
|
|
146
157
|
renameItem: {},
|
|
147
158
|
renameFolderOrPageDialog: false,
|
|
148
159
|
deletePageDialog: false,
|
|
149
160
|
pageSettings: false,
|
|
150
161
|
pageData: {},
|
|
162
|
+
linkDialogMode: 'add',
|
|
163
|
+
linkName: '',
|
|
164
|
+
linkUrl: '',
|
|
165
|
+
linkTargetMenu: '',
|
|
166
|
+
linkTargetIndex: -1,
|
|
151
167
|
newDocs: {
|
|
152
168
|
pages: {
|
|
153
169
|
name: { value: '' },
|
|
154
170
|
content: { value: [] },
|
|
155
171
|
blockIds: { value: [] },
|
|
172
|
+
metaTitle: { value: '' },
|
|
173
|
+
metaDescription: { value: '' },
|
|
174
|
+
structuredData: { value: buildPageStructuredData() },
|
|
156
175
|
tags: { value: [] },
|
|
157
176
|
allowedThemes: { value: [] },
|
|
158
177
|
},
|
|
@@ -182,6 +201,8 @@ const templateTagItems = computed(() => {
|
|
|
182
201
|
return [{ name: 'Quick Picks', title: 'Quick Picks' }, ...tagList]
|
|
183
202
|
})
|
|
184
203
|
|
|
204
|
+
const BLANK_TEMPLATE_ID = 'blank'
|
|
205
|
+
|
|
185
206
|
const resetAddPageDialogState = () => {
|
|
186
207
|
state.newPageName = ''
|
|
187
208
|
state.templateFilter = 'quick-picks'
|
|
@@ -194,6 +215,21 @@ watch(() => state.addPageDialog, (open) => {
|
|
|
194
215
|
resetAddPageDialogState()
|
|
195
216
|
})
|
|
196
217
|
|
|
218
|
+
const resetLinkDialogState = () => {
|
|
219
|
+
state.linkDialogMode = 'add'
|
|
220
|
+
state.linkName = ''
|
|
221
|
+
state.linkUrl = ''
|
|
222
|
+
state.linkTargetMenu = ''
|
|
223
|
+
state.linkTargetIndex = -1
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
watch(() => state.addLinkDialog, (open) => {
|
|
227
|
+
if (!open)
|
|
228
|
+
resetLinkDialogState()
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
const TEMPLATE_COLLECTION_PATH = computed(() => `${edgeGlobal.edgeState.organizationDocPath}/sites/templates/pages`)
|
|
232
|
+
|
|
197
233
|
onMounted(async () => {
|
|
198
234
|
if (!edgeGlobal.edgeState.organizationDocPath)
|
|
199
235
|
return
|
|
@@ -202,8 +238,6 @@ onMounted(async () => {
|
|
|
202
238
|
await edgeFirebase.startSnapshot(path)
|
|
203
239
|
})
|
|
204
240
|
|
|
205
|
-
const TEMPLATE_COLLECTION_PATH = computed(() => `${edgeGlobal.edgeState.organizationDocPath}/sites/templates/pages`)
|
|
206
|
-
|
|
207
241
|
const templatePagesCollection = computed(() => {
|
|
208
242
|
return edgeFirebase.data?.[TEMPLATE_COLLECTION_PATH.value] || {}
|
|
209
243
|
})
|
|
@@ -265,8 +299,6 @@ watch(filteredTemplates, (templates) => {
|
|
|
265
299
|
state.selectedTemplateId = BLANK_TEMPLATE_ID
|
|
266
300
|
})
|
|
267
301
|
|
|
268
|
-
const BLANK_TEMPLATE_ID = 'blank'
|
|
269
|
-
|
|
270
302
|
const blankTemplateTile = {
|
|
271
303
|
docId: BLANK_TEMPLATE_ID,
|
|
272
304
|
name: 'Blank Page',
|
|
@@ -314,6 +346,8 @@ const templatePreviewBlocks = (template) => {
|
|
|
314
346
|
}
|
|
315
347
|
|
|
316
348
|
const renameFolderOrPageShow = (item) => {
|
|
349
|
+
if (isRenameDisabled(item))
|
|
350
|
+
return
|
|
317
351
|
// Work on a copy so edits in the dialog do not mutate the live menu entry.
|
|
318
352
|
state.renameItem = edgeGlobal.dupObject(item || {})
|
|
319
353
|
state.renameItem.previousName = item?.name
|
|
@@ -328,6 +362,8 @@ const addPageShow = (menuName, isMenu = false) => {
|
|
|
328
362
|
}
|
|
329
363
|
|
|
330
364
|
const deletePageShow = (page) => {
|
|
365
|
+
if (isDeleteDisabled(page))
|
|
366
|
+
return
|
|
331
367
|
state.deletePage = page
|
|
332
368
|
state.deletePageDialog = true
|
|
333
369
|
}
|
|
@@ -343,6 +379,9 @@ const collectRootLevelSlugs = (excludeName = '') => {
|
|
|
343
379
|
if (entry.name && entry.name !== excludeName)
|
|
344
380
|
slugs.add(entry.name)
|
|
345
381
|
}
|
|
382
|
+
else if (isExternalLinkEntry(entry)) {
|
|
383
|
+
continue
|
|
384
|
+
}
|
|
346
385
|
// Top-level folder at "/<folder>/*"
|
|
347
386
|
else if (entry && typeof entry.item === 'object') {
|
|
348
387
|
const key = Object.keys(entry.item)[0]
|
|
@@ -362,6 +401,9 @@ const collectRootLevelSlugs = (excludeName = '') => {
|
|
|
362
401
|
if (entry.name && entry.name !== excludeName)
|
|
363
402
|
slugs.add(entry.name)
|
|
364
403
|
}
|
|
404
|
+
else if (isExternalLinkEntry(entry)) {
|
|
405
|
+
continue
|
|
406
|
+
}
|
|
365
407
|
// Top-level folder at "/<folder>/*"
|
|
366
408
|
else if (entry && typeof entry.item === 'object') {
|
|
367
409
|
const key = Object.keys(entry.item)[0]
|
|
@@ -380,6 +422,9 @@ const collectRootLevelSlugs = (excludeName = '') => {
|
|
|
380
422
|
if (entry.name && entry.name !== excludeName)
|
|
381
423
|
slugs.add(entry.name)
|
|
382
424
|
}
|
|
425
|
+
else if (isExternalLinkEntry(entry)) {
|
|
426
|
+
continue
|
|
427
|
+
}
|
|
383
428
|
// Top-level folder at "/<folder>/*"
|
|
384
429
|
else if (entry && typeof entry.item === 'object') {
|
|
385
430
|
const key = Object.keys(entry.item)[0]
|
|
@@ -471,6 +516,8 @@ const hydrateSyncedBlocksFromSite = (blocks = []) => {
|
|
|
471
516
|
|
|
472
517
|
const buildPagePayloadFromTemplate = (templateDoc, slug) => {
|
|
473
518
|
const timestamp = Date.now()
|
|
519
|
+
const templateStructuredData = typeof templateDoc?.structuredData === 'string' ? templateDoc.structuredData.trim() : ''
|
|
520
|
+
const structuredData = templateDoc ? (templateStructuredData || buildPageStructuredData()) : buildPageStructuredData()
|
|
474
521
|
const basePayload = {
|
|
475
522
|
name: slug,
|
|
476
523
|
content: [],
|
|
@@ -478,7 +525,7 @@ const buildPagePayloadFromTemplate = (templateDoc, slug) => {
|
|
|
478
525
|
blockIds: [],
|
|
479
526
|
metaTitle: '',
|
|
480
527
|
metaDescription: '',
|
|
481
|
-
structuredData
|
|
528
|
+
structuredData,
|
|
482
529
|
doc_created_at: timestamp,
|
|
483
530
|
last_updated: timestamp,
|
|
484
531
|
}
|
|
@@ -492,10 +539,35 @@ const buildPagePayloadFromTemplate = (templateDoc, slug) => {
|
|
|
492
539
|
copy.content = Array.isArray(copy.content) ? hydrateSyncedBlocksFromSite(copy.content) : []
|
|
493
540
|
copy.postContent = Array.isArray(copy.postContent) ? hydrateSyncedBlocksFromSite(copy.postContent) : []
|
|
494
541
|
copy.blockIds = deriveBlockIds(copy)
|
|
542
|
+
if (!String(copy.structuredData || '').trim())
|
|
543
|
+
copy.structuredData = structuredData
|
|
495
544
|
return { ...basePayload, ...copy }
|
|
496
545
|
}
|
|
497
546
|
|
|
498
547
|
const renameFolderOrPageAction = async () => {
|
|
548
|
+
if (isRenameDisabled(state.renameItem)) {
|
|
549
|
+
state.renameFolderOrPageDialog = false
|
|
550
|
+
state.renameItem = {}
|
|
551
|
+
return
|
|
552
|
+
}
|
|
553
|
+
if (isExternalLinkEntry(state.renameItem)) {
|
|
554
|
+
const nextName = state.renameItem.name?.trim() || ''
|
|
555
|
+
if (!nextName) {
|
|
556
|
+
state.renameFolderOrPageDialog = false
|
|
557
|
+
state.renameItem = {}
|
|
558
|
+
return
|
|
559
|
+
}
|
|
560
|
+
const menuName = state.renameItem.menuName
|
|
561
|
+
const index = state.renameItem.index
|
|
562
|
+
if (menuName && Number.isInteger(index) && Array.isArray(modelValue.value[menuName])) {
|
|
563
|
+
const target = modelValue.value[menuName][index]
|
|
564
|
+
if (target)
|
|
565
|
+
target.name = nextName
|
|
566
|
+
}
|
|
567
|
+
state.renameFolderOrPageDialog = false
|
|
568
|
+
state.renameItem = {}
|
|
569
|
+
return
|
|
570
|
+
}
|
|
499
571
|
const newSlug = slugGenerator(state.renameItem.name, state.renameItem.previousName || '')
|
|
500
572
|
|
|
501
573
|
if (state.renameItem.name === state.renameItem.previousName) {
|
|
@@ -582,7 +654,70 @@ const addPageAction = async () => {
|
|
|
582
654
|
|
|
583
655
|
state.addPageDialog = false
|
|
584
656
|
}
|
|
657
|
+
|
|
658
|
+
const addLinkShow = (menuName) => {
|
|
659
|
+
state.linkDialogMode = 'add'
|
|
660
|
+
state.linkTargetMenu = menuName
|
|
661
|
+
state.addLinkDialog = true
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const editLinkShow = (menuName, index, entry) => {
|
|
665
|
+
state.linkDialogMode = 'edit'
|
|
666
|
+
state.linkTargetMenu = menuName
|
|
667
|
+
state.linkTargetIndex = index
|
|
668
|
+
state.linkName = entry?.name || ''
|
|
669
|
+
state.linkUrl = entry?.item?.url || ''
|
|
670
|
+
state.addLinkDialog = true
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const linkDialogSchema = toTypedSchema(z.object({
|
|
674
|
+
name: z.string({
|
|
675
|
+
required_error: 'Name is required',
|
|
676
|
+
}).min(1, { message: 'Name is required' }),
|
|
677
|
+
url: z.string({
|
|
678
|
+
required_error: 'URL is required',
|
|
679
|
+
}).min(1, { message: 'URL is required' }),
|
|
680
|
+
}))
|
|
681
|
+
|
|
682
|
+
const submitLinkDialog = () => {
|
|
683
|
+
const name = state.linkName?.trim() || ''
|
|
684
|
+
const url = state.linkUrl?.trim() || ''
|
|
685
|
+
if (!name || !url)
|
|
686
|
+
return
|
|
687
|
+
const payload = { name, item: { type: 'external', url } }
|
|
688
|
+
if (!Array.isArray(modelValue.value[state.linkTargetMenu]))
|
|
689
|
+
modelValue.value[state.linkTargetMenu] = []
|
|
690
|
+
if (state.linkDialogMode === 'edit') {
|
|
691
|
+
const target = modelValue.value[state.linkTargetMenu]?.[state.linkTargetIndex]
|
|
692
|
+
if (target) {
|
|
693
|
+
target.name = name
|
|
694
|
+
target.item = { type: 'external', url }
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
modelValue.value[state.linkTargetMenu].push(payload)
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
modelValue.value[state.linkTargetMenu].push(payload)
|
|
702
|
+
}
|
|
703
|
+
state.addLinkDialog = false
|
|
704
|
+
}
|
|
585
705
|
const deletePageAction = async () => {
|
|
706
|
+
if (isDeleteDisabled(state.deletePage)) {
|
|
707
|
+
state.deletePageDialog = false
|
|
708
|
+
state.deletePage = {}
|
|
709
|
+
return
|
|
710
|
+
}
|
|
711
|
+
if (isExternalLinkEntry(state.deletePage)) {
|
|
712
|
+
const menuName = state.deletePage.menuName
|
|
713
|
+
const index = state.deletePage.index
|
|
714
|
+
if (menuName && Number.isInteger(index) && Array.isArray(modelValue.value[menuName])) {
|
|
715
|
+
modelValue.value[menuName].splice(index, 1)
|
|
716
|
+
}
|
|
717
|
+
state.deletePageDialog = false
|
|
718
|
+
state.deletePage = {}
|
|
719
|
+
return
|
|
720
|
+
}
|
|
586
721
|
if (state.deletePage.item === '') {
|
|
587
722
|
// deleting a folder
|
|
588
723
|
delete modelValue.value[state.deletePage.name]
|
|
@@ -701,11 +836,11 @@ const theme = computed(() => {
|
|
|
701
836
|
|
|
702
837
|
<template>
|
|
703
838
|
<SidebarMenuItem v-for="({ menu, name: menuName }) in orderedMenus" :key="menuName">
|
|
704
|
-
<SidebarMenuButton class="!px-0 hover:!bg-transparent">
|
|
839
|
+
<SidebarMenuButton class="group !px-0 hover:!bg-transparent">
|
|
705
840
|
<FolderOpen
|
|
706
|
-
class="mr-2"
|
|
841
|
+
class="mr-2 group-hover:text-black"
|
|
707
842
|
/>
|
|
708
|
-
<span v-if="!props.isTemplateSite">{{ menuName === 'Site Root' ? 'Site Menu' : menuName }}</span>
|
|
843
|
+
<span v-if="!props.isTemplateSite" class="hover:text-black !text-black">{{ menuName === 'Site Root' ? 'Site Menu' : menuName }}</span>
|
|
709
844
|
<SidebarGroupAction class="absolute right-2 top-0 hover:!bg-transparent">
|
|
710
845
|
<DropdownMenu>
|
|
711
846
|
<DropdownMenuTrigger as-child>
|
|
@@ -725,6 +860,10 @@ const theme = computed(() => {
|
|
|
725
860
|
<FilePlus2 />
|
|
726
861
|
<span>New Page</span>
|
|
727
862
|
</DropdownMenuItem>
|
|
863
|
+
<DropdownMenuItem v-if="!props.isTemplateSite" @click="addLinkShow(menuName)">
|
|
864
|
+
<Link />
|
|
865
|
+
<span>New Link</span>
|
|
866
|
+
</DropdownMenuItem>
|
|
728
867
|
<DropdownMenuItem v-if="!props.prevMenu && !props.isTemplateSite" @click="addPageShow(menuName, true)">
|
|
729
868
|
<FolderPlus />
|
|
730
869
|
<span>New Folder</span>
|
|
@@ -754,7 +893,7 @@ const theme = computed(() => {
|
|
|
754
893
|
<template #item="{ element, index }">
|
|
755
894
|
<div class="handle list-group-item">
|
|
756
895
|
<edge-cms-menu
|
|
757
|
-
v-if="typeof element.item === 'object'"
|
|
896
|
+
v-if="typeof element.item === 'object' && !isExternalLinkEntry(element)"
|
|
758
897
|
v-model="modelValue[menuName][index].item"
|
|
759
898
|
:prev-menu="menuName"
|
|
760
899
|
:prev-model-value="modelValue"
|
|
@@ -764,13 +903,33 @@ const theme = computed(() => {
|
|
|
764
903
|
:is-template-site="props.isTemplateSite"
|
|
765
904
|
/>
|
|
766
905
|
<SidebarMenuSubItem v-else class="relative">
|
|
767
|
-
<SidebarMenuSubButton
|
|
768
|
-
|
|
906
|
+
<SidebarMenuSubButton
|
|
907
|
+
:class="{ 'text-gray-400': element.item === '' }"
|
|
908
|
+
as-child
|
|
909
|
+
:is-active="!isExternalLinkEntry(element) && element.item === props.page"
|
|
910
|
+
>
|
|
911
|
+
<NuxtLink
|
|
912
|
+
v-if="!isExternalLinkEntry(element)"
|
|
913
|
+
:disabled="element.item === ''"
|
|
914
|
+
:class="{ '!text-red-500': element.name === 'Deleting...' }"
|
|
915
|
+
class="text-xs"
|
|
916
|
+
:to="`${pageRouteBase}/${element.item}`"
|
|
917
|
+
>
|
|
769
918
|
<Loader2 v-if="element.item === '' || element.name === 'Deleting...'" :class="{ '!text-red-500': element.name === 'Deleting...' }" class="w-4 h-4 animate-spin" />
|
|
770
919
|
<FileWarning v-else-if="isPublishedPageDiff(element.item) && !props.isTemplateSite" class="!text-yellow-600" />
|
|
771
920
|
<FileCheck v-else class="text-xs !text-green-700 font-normal" />
|
|
772
921
|
<span>{{ element.name }}</span>
|
|
773
922
|
</NuxtLink>
|
|
923
|
+
<a
|
|
924
|
+
v-else
|
|
925
|
+
:href="element.item?.url || '#'"
|
|
926
|
+
class="text-xs inline-flex items-center gap-2"
|
|
927
|
+
:target="linkTarget(element.item?.url)"
|
|
928
|
+
:rel="linkRel(element.item?.url)"
|
|
929
|
+
>
|
|
930
|
+
<Link class="w-4 h-4 text-muted-foreground" />
|
|
931
|
+
<span>{{ element.name }}</span>
|
|
932
|
+
</a>
|
|
774
933
|
</SidebarMenuSubButton>
|
|
775
934
|
<div class="absolute right-0 -top-0.5">
|
|
776
935
|
<DropdownMenu>
|
|
@@ -787,34 +946,51 @@ const theme = computed(() => {
|
|
|
787
946
|
<File class="w-5 h-5" /> {{ ROOT_MENUS.includes(menuName) ? '' : menuName }}/{{ element.name }}
|
|
788
947
|
</DropdownMenuLabel>
|
|
789
948
|
<DropdownMenuSeparator />
|
|
790
|
-
<
|
|
791
|
-
<
|
|
792
|
-
|
|
793
|
-
<
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
<
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
<
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
<
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
<
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
<
|
|
816
|
-
|
|
817
|
-
|
|
949
|
+
<template v-if="!isExternalLinkEntry(element)">
|
|
950
|
+
<DropdownMenuItem :disabled="edgeGlobal.edgeState.cmsPageWithUnsavedChanges === element.item" @click="showPageSettings(element)">
|
|
951
|
+
<FileCog />
|
|
952
|
+
<div class="flex flex-col">
|
|
953
|
+
<span>Settings</span>
|
|
954
|
+
<span v-if="edgeGlobal.edgeState.cmsPageWithUnsavedChanges === element.item" class="text-xs text-red-500">(Unsaved Changes)</span>
|
|
955
|
+
</div>
|
|
956
|
+
</DropdownMenuItem>
|
|
957
|
+
<DropdownMenuItem v-if="!props.isTemplateSite && isPublishedPageDiff(element.item)" @click="publishPage(element.item)">
|
|
958
|
+
<FileUp />
|
|
959
|
+
Publish
|
|
960
|
+
</DropdownMenuItem>
|
|
961
|
+
<DropdownMenuItem :disabled="isRenameDisabled(element)" @click="renameFolderOrPageShow({ ...element, menuName, index })">
|
|
962
|
+
<FilePen />
|
|
963
|
+
<span>Rename</span>
|
|
964
|
+
</DropdownMenuItem>
|
|
965
|
+
<DropdownMenuSeparator />
|
|
966
|
+
<DropdownMenuItem v-if="!props.isTemplateSite && isPublishedPageDiff(element.item) && isPublished(element.item)" @click="discardPageChanges(element.item)">
|
|
967
|
+
<FileX />
|
|
968
|
+
Discard Changes
|
|
969
|
+
</DropdownMenuItem>
|
|
970
|
+
<DropdownMenuItem v-if="!props.isTemplateSite && isPublished(element.item)" @click="unPublishPage(element.item)">
|
|
971
|
+
<FileDown />
|
|
972
|
+
Unpublish
|
|
973
|
+
</DropdownMenuItem>
|
|
974
|
+
<DropdownMenuItem class="text-destructive" :disabled="isDeleteDisabled(element)" @click="deletePageShow({ ...element, menuName, index })">
|
|
975
|
+
<FileMinus2 />
|
|
976
|
+
<span>Delete</span>
|
|
977
|
+
</DropdownMenuItem>
|
|
978
|
+
</template>
|
|
979
|
+
<template v-else>
|
|
980
|
+
<DropdownMenuItem @click="editLinkShow(menuName, index, element)">
|
|
981
|
+
<Link />
|
|
982
|
+
<span>Edit Link</span>
|
|
983
|
+
</DropdownMenuItem>
|
|
984
|
+
<DropdownMenuItem @click="renameFolderOrPageShow({ ...element, menuName, index })">
|
|
985
|
+
<FilePen />
|
|
986
|
+
<span>Rename</span>
|
|
987
|
+
</DropdownMenuItem>
|
|
988
|
+
<DropdownMenuSeparator />
|
|
989
|
+
<DropdownMenuItem class="text-destructive" @click="deletePageShow({ ...element, menuName, index })">
|
|
990
|
+
<FileMinus2 />
|
|
991
|
+
<span>Delete</span>
|
|
992
|
+
</DropdownMenuItem>
|
|
993
|
+
</template>
|
|
818
994
|
</DropdownMenuContent>
|
|
819
995
|
</DropdownMenu>
|
|
820
996
|
</div>
|
|
@@ -831,6 +1007,7 @@ const theme = computed(() => {
|
|
|
831
1007
|
<DialogHeader>
|
|
832
1008
|
<DialogTitle class="text-left">
|
|
833
1009
|
<span v-if="state.deletePage.item === ''">Delete Folder "{{ state.deletePage.name }}"</span>
|
|
1010
|
+
<span v-else-if="isExternalLinkEntry(state.deletePage)">Delete Link "{{ state.deletePage.name }}"</span>
|
|
834
1011
|
<span v-else>Delete Page "{{ state.deletePage.name }}"</span>
|
|
835
1012
|
</DialogTitle>
|
|
836
1013
|
<DialogDescription />
|
|
@@ -847,7 +1024,9 @@ const theme = computed(() => {
|
|
|
847
1024
|
<edge-shad-button
|
|
848
1025
|
variant="destructive" class="text-white w-full" @click="deletePageAction()"
|
|
849
1026
|
>
|
|
850
|
-
Delete
|
|
1027
|
+
<span v-if="state.deletePage.item === ''">Delete Folder</span>
|
|
1028
|
+
<span v-else-if="isExternalLinkEntry(state.deletePage)">Delete Link</span>
|
|
1029
|
+
<span v-else>Delete Page</span>
|
|
851
1030
|
</edge-shad-button>
|
|
852
1031
|
</DialogFooter>
|
|
853
1032
|
</DialogContent>
|
|
@@ -962,6 +1141,32 @@ const theme = computed(() => {
|
|
|
962
1141
|
</edge-shad-form>
|
|
963
1142
|
</DialogContent>
|
|
964
1143
|
</edge-shad-dialog>
|
|
1144
|
+
<edge-shad-dialog v-model="state.addLinkDialog">
|
|
1145
|
+
<DialogContent class="pt-10">
|
|
1146
|
+
<edge-shad-form :schema="linkDialogSchema" @submit="submitLinkDialog">
|
|
1147
|
+
<DialogHeader>
|
|
1148
|
+
<DialogTitle class="text-left">
|
|
1149
|
+
<span v-if="state.linkDialogMode === 'edit'">Edit Link</span>
|
|
1150
|
+
<span v-else>Add link to "{{ state.linkTargetMenu }}"</span>
|
|
1151
|
+
</DialogTitle>
|
|
1152
|
+
<DialogDescription />
|
|
1153
|
+
</DialogHeader>
|
|
1154
|
+
<div class="space-y-4">
|
|
1155
|
+
<edge-shad-input v-model="state.linkName" name="name" label="Label" placeholder="Link label" />
|
|
1156
|
+
<edge-shad-input v-model="state.linkUrl" name="url" label="URL" placeholder="https://example.com or tel:123-456-7890" />
|
|
1157
|
+
</div>
|
|
1158
|
+
<DialogFooter class="pt-2 flex justify-between">
|
|
1159
|
+
<edge-shad-button type="button" variant="destructive" @click="state.addLinkDialog = false">
|
|
1160
|
+
Cancel
|
|
1161
|
+
</edge-shad-button>
|
|
1162
|
+
<edge-shad-button type="submit" class="text-white bg-slate-800 hover:bg-slate-400 w-full">
|
|
1163
|
+
<span v-if="state.linkDialogMode === 'edit'">Update Link</span>
|
|
1164
|
+
<span v-else>Add Link</span>
|
|
1165
|
+
</edge-shad-button>
|
|
1166
|
+
</DialogFooter>
|
|
1167
|
+
</edge-shad-form>
|
|
1168
|
+
</DialogContent>
|
|
1169
|
+
</edge-shad-dialog>
|
|
965
1170
|
<edge-shad-dialog
|
|
966
1171
|
v-model="state.renameFolderOrPageDialog"
|
|
967
1172
|
>
|
|
@@ -975,6 +1180,12 @@ const theme = computed(() => {
|
|
|
975
1180
|
<DialogDescription />
|
|
976
1181
|
</DialogHeader>
|
|
977
1182
|
<edge-shad-input v-model="state.renameItem.name" name="name" placeholder="New Name" />
|
|
1183
|
+
<p
|
|
1184
|
+
v-if="state.renameItem.item !== ''"
|
|
1185
|
+
class="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs font-medium text-red-700"
|
|
1186
|
+
>
|
|
1187
|
+
Renaming a page changes its URL. If search engines already indexed the old URL, rankings may drop temporarily.
|
|
1188
|
+
</p>
|
|
978
1189
|
<DialogFooter class="pt-2 flex justify-between">
|
|
979
1190
|
<edge-shad-button variant="destructive" @click="state.renameFolderOrPageDialog = false">
|
|
980
1191
|
Cancel
|
|
@@ -1024,7 +1235,7 @@ const theme = computed(() => {
|
|
|
1024
1235
|
:allow-additions="true"
|
|
1025
1236
|
/>
|
|
1026
1237
|
<edge-shad-select-tags
|
|
1027
|
-
v-if="props.themeOptions.length"
|
|
1238
|
+
v-if="props.isTemplateSite && props.themeOptions.length"
|
|
1028
1239
|
:model-value="Array.isArray(slotProps.workingDoc.allowedThemes) ? slotProps.workingDoc.allowedThemes : []"
|
|
1029
1240
|
name="allowedThemes"
|
|
1030
1241
|
label="Allowed Themes"
|