import { computed, defineComponent, onMounted, ref, type Ref } from 'vue'; import { BCMSJwtRoleName, type BCMSTemplate, type BCMSTemplateOrganizer, type BCMSUserPolicyCRUD, type BCMSUserPolicyTemplate, } from '@becomes/cms-sdk/types'; import type { BCMSNavItemMergeEvent, BCMSNavItemType } from '../../../types'; import BCMSIcon from '../../icon'; import BCMSNavItem from './item'; import { useRoute, useRouter } from 'vue-router'; import BCMSLogo from './logo'; import { useTranslation } from '../../../translations'; interface OrganizerExtended extends BCMSTemplateOrganizer { templates: BCMSTemplate[]; } const component = defineComponent({ setup() { const translations = computed(() => { return useTranslation(); }); const throwable = window.bcms.util.throwable; const store = window.bcms.vue.store; const stringUtil = window.bcms.util.string; const router = useRouter(); const route = useRoute(); const isMobileNavOpen = ref(false); const user = computed(() => store.getters.user_me); const administration: Ref<{ data: BCMSNavItemType[]; show: boolean; extended: boolean; }> = computed(() => { if (user.value) { const path = route.path; let templatePath = '/dashboard/t'; if (path.startsWith('/dashboard/t') && route.name === 'TemplateId') { templatePath = path; } let groupPath = '/dashboard/g'; if (path.startsWith('/dashboard/g')) { groupPath = path; } let widgetPath = '/dashboard/w'; if (path.startsWith('/dashboard/w')) { // const parts = path.split('/'); // if (parts.length === 4) { // widgetPath = // widgetPath.split('/').slice(0, 3).join('/') + `/${parts[3]}`; // } widgetPath = path; } let mediaPath = '/dashboard/media'; if (path.startsWith('/dashboard/media')) { mediaPath = path; } const settingsPath = '/dashboard/settings'; let keyManagerPath = '/dashboard/key-manager'; if (path.startsWith(keyManagerPath)) { keyManagerPath = path; } const isAdmin = user.value.roles[0].name === BCMSJwtRoleName.ADMIN; const data: BCMSNavItemType[] = [ { type: 'child', name: translations.value.layout.nav.administration.template.label, onClick: (event) => { logic.onNavItemClick(templatePath, event); }, href: templatePath, icon: '/administration/template', visible: isAdmin, selected: logic.isSelected('template', path), }, { type: 'child', name: translations.value.layout.nav.administration.group.label, href: groupPath, onClick: (event) => { logic.onNavItemClick(groupPath, event); }, icon: '/administration/group', visible: isAdmin, selected: logic.isSelected('group', path), }, { type: 'child', name: translations.value.layout.nav.administration.widget.label, href: widgetPath, onClick: (event) => { logic.onNavItemClick(widgetPath, event); }, icon: '/administration/widget', visible: isAdmin, selected: logic.isSelected('widget', path), }, { type: 'child', name: translations.value.layout.nav.administration.media.label, href: mediaPath, onClick: (event) => { logic.onNavItemClick(mediaPath, event); }, icon: '/administration/media', visible: isAdmin || user.value.customPool.policy.media.get, selected: logic.isSelected('media', path), }, { type: 'child', name: translations.value.layout.nav.administration.settings.label, href: settingsPath, onClick: (event) => { logic.onNavItemClick(settingsPath, event); }, icon: '/cog', visible: true, selected: logic.isSelected('settings', path), }, { type: 'child', name: translations.value.layout.nav.administration.keyManager.label, href: keyManagerPath, onClick: (event) => { logic.onNavItemClick(keyManagerPath, event); }, icon: '/administration/key', visible: isAdmin, selected: logic.isSelected('keyManager', path), }, ]; return { show: true, extended: !!data.find((e) => e.selected) || store.getters.entryLite_items.length === 0, data, }; } return { data: [], show: false, extended: false, }; }); const templates: Ref<{ root?: BCMSNavItemType; show: boolean; extended: boolean; }> = computed(() => { if (user.value) { const organizers = store.getters.templateOrganizer_items; const tmps = store.getters.template_items; const isAdmin = user.value.roles[0].name === BCMSJwtRoleName.ADMIN; const policy = user.value.customPool.policy.templates; const items: BCMSNavItemType[] = logic.aggregateOrganizersAndTemplates({ organizers, templates: tmps, isAdmin, policy, }); return { show: tmps.length > 0, extended: templates.value ? templates.value.extended : tmps.length > 0, root: { id: 'root', draggableType: 'organizer', type: 'parent', name: translations.value.layout.nav.entry.label, visible: true, selected: templates.value ? templates.value.extended : tmps.length > 0, children: items, }, }; } return { root: undefined, show: false, extended: true, }; }); const pluginsList = ref([]); const plugins: Ref<{ data: BCMSNavItemType[]; show: boolean; extended: boolean; }> = computed(() => { if (user.value) { const isAdmin = user.value.roles[0].name === BCMSJwtRoleName.ADMIN; const policy = user.value.customPool.policy.plugins; const data: BCMSNavItemType[] = pluginsList.value .filter( (e) => isAdmin || (policy && !!policy.find((p) => p.allowed && p.name === e)) ) .map((e) => { const path = `/dashboard/plugin/${e}`; const navItem: BCMSNavItemType = { type: 'child', name: stringUtil.toPretty(e), href: path, onClick: (event) => { if (event) { event.preventDefault(); } isMobileNavOpen.value = false; router.push(path); }, icon: '/wind', visible: true, selected: route.path.startsWith(path), ignoreSelected: true, }; return navItem; }) .sort((a, b) => (b.name < a.name ? 1 : -1)); return { show: pluginsList.value.length > 0, extended: !!data.find((e) => e.selected), data, }; } return { data: [], show: false, extended: true, }; }); const logic = { isSelected( target: | 'template' | 'group' | 'widget' | 'media' | 'entry' | 'settings' | 'keyManager', path: string ): boolean { switch (target) { case 'template': { if (path === '/dashboard/t') { return true; } else { const parts = path.split('/'); if (parts.length === 4 && parts[2] === 't') { return true; } } } break; case 'group': { if (path === '/dashboard/g') { return true; } else { const parts = path.split('/'); if (parts.length === 4 && parts[2] === 'g') { return true; } } } break; case 'widget': { if (path === '/dashboard/w') { return true; } else { const parts = path.split('/'); if (parts.length === 4 && parts[2] === 'w') { return true; } } } break; case 'media': { if (path === '/dashboard/media') { return true; } else { const parts = path.split('/'); if (parts.length === 4 && parts[2] === 'media') { return true; } } } break; case 'settings': { if (path === '/dashboard/settings') { return true; } } break; case 'keyManager': { if (path === '/dashboard/key-manager') { return true; } else { const parts = path.split('/'); if (parts.length === 4 && parts[2] === 'key-manager') { return true; } } } break; case 'entry': { // TODO } break; } return false; }, toggleMobileNav() { isMobileNavOpen.value = !isMobileNavOpen.value; if (isMobileNavOpen.value) { document.body.style.overflowY = 'hidden'; } else { document.body.style.overflowY = 'auto'; } }, onNavItemClick(path: string, event?: Event) { if (event) { event.preventDefault(); isMobileNavOpen.value = false; document.body.style.overflowY = 'auto'; if ( (event as MouseEventInit).metaKey || (event as MouseEventInit).ctrlKey ) { const routeData = router.resolve({ path: path }); window.open(routeData.href, '_blank'); } else { router.push(path); } } }, handleMerge(event: BCMSNavItemMergeEvent): void { const rootItem = templates.value.root as BCMSNavItemType; const result = logic.findNavItem([rootItem], event.targetId); if (result) { const targetItem = result.item; const srcItem = event.src; if ( targetItem.parentId !== 'root' && srcItem.parentId !== 'root' && targetItem.parentId === srcItem.parentId ) { return; } if ( targetItem.draggableType === 'template' && srcItem.draggableType === 'template' ) { if (targetItem.parentId !== 'root') { throwable(async () => { const organizer = store.getters.templateOrganizer_findOne( (e) => e._id === targetItem.parentId ); if (organizer) { organizer.templateIds.push(srcItem.id as string); await window.bcms.sdk.templateOrganizer.update({ _id: organizer._id, templateIds: organizer.templateIds, }); } }); } else { window.bcms.modal.templateOrganizer.create.show({ async onDone(data) { await throwable(async () => { await window.bcms.sdk.templateOrganizer.create({ label: data.name, templateIds: [ srcItem.id as string, targetItem.id as string, ], }); }); }, }); } } else if (targetItem.id === 'root' && srcItem.parentId !== 'root') { const organizer = store.getters.templateOrganizer_findOne( (e) => e._id === srcItem.parentId ); if (organizer) { throwable(async () => { organizer.templateIds = organizer.templateIds.filter( (e) => e !== srcItem.id ); if (organizer.templateIds.length === 0) { await window.bcms.sdk.templateOrganizer.deleteById( organizer._id ); } else { await window.bcms.sdk.templateOrganizer.update({ _id: organizer._id, templateIds: organizer.templateIds.filter( (e) => e !== srcItem.id ), }); } }); } } else if ( targetItem.draggableType === 'organizer' && srcItem.draggableType === 'template' ) { const organizer = store.getters.templateOrganizer_findOne( (e) => e._id === targetItem.id ); if (organizer) { throwable(async () => { if (srcItem.parentId !== 'root') { const srcOrganizer = store.getters.templateOrganizer_findOne( (e) => e._id === srcItem.parentId ); if (srcOrganizer) { srcOrganizer.templateIds = srcOrganizer.templateIds.filter( (e) => e !== srcItem.id ); if (srcOrganizer.templateIds.length === 0) { await window.bcms.sdk.templateOrganizer.deleteById( srcOrganizer._id ); } else { await window.bcms.sdk.templateOrganizer.update({ _id: srcOrganizer._id, templateIds: srcOrganizer.templateIds, }); } } } organizer.templateIds.push(srcItem.id as string); await window.bcms.sdk.templateOrganizer.update({ _id: organizer._id, templateIds: organizer.templateIds, }); }); } } } }, findNavItem( items: BCMSNavItemType[], id: string, index?: string[] ): { item: BCMSNavItemType; index: string[] } | null { if (!index) { index = []; } for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.id === id) { index.push(`${i}`); return { index, item, }; } else if (item.children) { index.push('children'); const result = logic.findNavItem(item.children, id, index); if (result) { return result; } index.pop(); } } return null; }, aggregateOrganizersAndTemplates(data: { organizers: BCMSTemplateOrganizer[]; templates: BCMSTemplate[]; isAdmin: boolean; policy: BCMSUserPolicyTemplate[]; }): BCMSNavItemType[] { const items: BCMSNavItemType[] = []; const extendedOrgs: OrganizerExtended[] = []; const foundTemplateIds: string[] = []; for (let i = 0; i < data.organizers.length; i++) { const organizer = data.organizers[i]; if (organizer.templateIds.length > 0) { const extendedOrg: OrganizerExtended = { ...organizer, templates: [], }; for (let j = 0; j < organizer.templateIds.length; j++) { const templateId = organizer.templateIds[j]; const template = data.templates.find((e) => e._id === templateId); if (template) { foundTemplateIds.push(template._id); extendedOrg.templates.push(template); } } extendedOrg.templates.sort((a, b) => (a.name < b.name ? -1 : 1)); extendedOrgs.push(extendedOrg); } } if (foundTemplateIds.length !== data.templates.length) { for (let i = 0; i < data.templates.length; i++) { const template = data.templates[i]; let path: string; if (template.singleEntry) { path = `/dashboard/t/${template.cid}/e/single`; } else { path = `/dashboard/t/${template.cid}/e`; } const tPolicy = data.policy.find((p) => p._id === template._id); if ( !foundTemplateIds.includes(template._id) && (data.isAdmin || (tPolicy && tPolicy.get)) ) { items.push({ id: template._id, parentId: 'root', type: 'child', draggableType: 'template', name: template.label, href: path, onClick: (event) => { logic.onNavItemClick(path, event); }, visible: true, selected: route.path.startsWith(path), ignoreSelected: true, }); } } } items.sort((a, b) => (a.name < b.name ? -1 : 1)); const organizeredItems = logic.aggregateExtendedOrganizers({ organizers: extendedOrgs, forParent: '', isAdmin: data.isAdmin, policy: data.policy, }); return [...organizeredItems, ...items]; }, aggregateExtendedOrganizers(data: { organizers: OrganizerExtended[]; forParent: string; isAdmin: boolean; policy: Array; }): BCMSNavItemType[] { const items: BCMSNavItemType[] = []; for (let i = 0; i < data.organizers.length; i++) { const organizer = data.organizers[i]; if ( organizer.parentId === data.forParent || (!organizer.parentId && !data.forParent) ) { const item: BCMSNavItemType = { id: organizer._id, draggableType: 'organizer', parentId: organizer.parentId ? organizer.parentId : 'root', type: 'parent', name: organizer.label, visible: true, selected: false, children: organizer.templates .filter((template) => { const tPolicy = data.policy.find( (e) => e._id === template._id ); return data.isAdmin || (tPolicy && tPolicy.get); }) .map((template) => { let path: string; if (template.singleEntry) { path = `/dashboard/t/${template.cid}/e/single`; } else { path = `/dashboard/t/${template.cid}/e`; } return { id: template._id, parentId: organizer._id, type: 'child', draggableType: 'template', name: template.label, href: path, onClick: (event) => { logic.onNavItemClick(path, event); }, visible: true, selected: route.path.startsWith(path), ignoreSelected: true, }; }), }; (item.children as BCMSNavItemType[]).sort((a, b) => b.name < a.name ? 1 : -1 ); const children = logic.aggregateExtendedOrganizers({ organizers: data.organizers, forParent: organizer._id, isAdmin: data.isAdmin, policy: data.policy, }); if (children.length > 0) { (item.children as BCMSNavItemType[]) = [ ...children, ...(item.children as BCMSNavItemType[]), ]; } items.push(item); } } return items; }, }; async function signOut() { await window.bcms.util.throwable( async () => { await window.bcms.sdk.user.logout(); }, async () => { window.location.href = 'https://cloud.thebcms.com/dashboard'; } ); } onMounted(async () => { if (await window.bcms.sdk.isLoggedIn()) { await throwable(async () => { await window.bcms.sdk.templateOrganizer.getAll(); return window.bcms.sdk.user.get(); }); await throwable(async () => { return await window.bcms.sdk.template.getAll(); }); await throwable( async () => { await window.bcms.sdk.template.getAll(); return await window.bcms.sdk.plugin.getAll(); }, async (result) => { pluginsList.value = result.map((e) => e.name); } ); } }); return () => ( ); }, }); export default component;