Skip to content

Commit 35f6271

Browse files
committed
feat: implement user menu settings pages in configuration and UI
1 parent f332404 commit 35f6271

File tree

5 files changed

+103
-28
lines changed

5 files changed

+103
-28
lines changed

adminforth/modules/codeInjector.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -323,22 +323,16 @@ class CodeInjector implements ICodeInjector {
323323
}
324324
},`})
325325
}}
326-
// const registerSettingPages = ( settingPage ) => {
327-
// if (!settingPage) {
328-
// return;
329-
// }
330-
// console.log('🪲⚙️ registerSettingPages', settingPage);
331-
// routes += `{
332-
// path: '/settings',
333-
// name: 'Settings',
334-
// component: () => import('@/views/SettingsView.vue'),
335-
// meta: { title: 'Settings'}
336-
// },\n`
337-
// }
326+
const registerSettingPages = ( settingPage ) => {
327+
if (!settingPage) {
328+
return;
329+
}
330+
console.log('🪲⚙️ registerSettingPages', settingPage);
331+
}
338332

339333
registerCustomPages(this.adminforth.config);
340334
collectAssetsFromMenu(this.adminforth.config.menu);
341-
// registerSettingPages(this.adminforth.config.auth.userMenuSettingsPages);
335+
registerSettingPages(this.adminforth.config.auth.userMenuSettingsPages);
342336
const spaDir = this.getSpaDir();
343337

344338
if (process.env.HEAVY_DEBUG) {
@@ -484,6 +478,12 @@ class CodeInjector implements ICodeInjector {
484478
});
485479
}
486480

481+
if (this.adminforth.config.auth.userMenuSettingsPages) {
482+
for (const settingPage of this.adminforth.config.auth.userMenuSettingsPages) {
483+
checkInjections([{ file: settingPage.component, meta: settingPage.pageLabel }]);
484+
}
485+
}
486+
487487

488488
customResourceComponents.forEach((filePath) => {
489489
const componentName = getComponentNameFromPath(filePath);

adminforth/modules/restApi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
375375
announcementBadge,
376376
globalInjections: this.adminforth.config.customization.globalInjections,
377377
userFullnameField: this.adminforth.config.auth.userFullNameField,
378+
settingPages: this.adminforth.config.auth.userMenuSettingsPages,
378379
}
379380

380381
// translate menu labels

adminforth/spa/src/afcl/VerticalTabs.vue

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,4 @@
4343
activeTab.value = tabs.value[0];
4444
}
4545
});
46-
47-
48-
4946
</script>

adminforth/spa/src/views/SettingsView.vue

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<template>
22
<div>
33
<nav
4-
v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady && defaultLayout"
5-
class="fixed h-14 top-0 z-20 w-full border-b shadow-sm bg-lightNavbar shadow-headerShadow dark:bg-darkNavbar dark:border-darkSidebarDevider"
4+
v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady"
5+
class="h-14 top-0 z-20 w-full border-b shadow-sm bg-lightNavbar shadow-headerShadow dark:bg-darkNavbar dark:border-darkSidebarDevider"
66
>
77
<div class="af-header px-3 lg:px-5 lg:pl-3 flex items-center justify-between h-full w-full" >
88
<div class="flex items-center justify-start rtl:justify-end">
@@ -69,31 +69,102 @@
6969
</div>
7070
</nav>
7171
</div>
72+
<div class="m-4 h-full w-full">
73+
74+
<div v-if="!coreStore?.config?.settingPages || coreStore?.config?.settingPages.length === 0">
75+
<p>No setting pages configured or still loading...</p>
76+
</div>
77+
<VerticalTabs v-else>
78+
<template v-for="(c,i) in coreStore?.config?.settingPages" :key="`tab:${settingPageSlotName(c,i)}`" v-slot:['tab:'+settingPageSlotName(c,i)]>
79+
<div class="flex items-center justify-center whitespace-nowrap px-4 mx-4">
80+
{{ c.pageLabel }}
81+
</div>
82+
</template>
83+
84+
<template v-for="(c,i) in coreStore?.config?.settingPages" :key="`${settingPageSlotName(c,i)}-content`" v-slot:[settingPageSlotName(c,i)]>
85+
<component
86+
:is="getCustomComponent({file: c.component || ''})"
87+
:resource="coreStore.resource"
88+
:adminUser="coreStore.adminUser"
89+
/>
90+
</template>
91+
</VerticalTabs>
92+
</div>
7293
</template>
7394

7495
<script setup lang="ts">
75-
76-
import { useCoreStore } from '@/stores/core';
77-
import { computed, ref } from 'vue';
96+
import { computed, ref, onMounted, watch } from 'vue';
7897
import { useRouter } from 'vue-router';
98+
import { useCoreStore } from '@/stores/core';
99+
import { useUserStore } from '@/stores/user';
100+
import { getCustomComponent } from '@/utils';
101+
import { Dropdown } from 'flowbite';
102+
import { IconMoonSolid, IconSunSolid } from '@iconify-prerendered/vue-flowbite';
103+
import adminforth from '@/adminforth';
104+
import { VerticalTabs } from '@/afcl'
105+
79106
const coreStore = useCoreStore();
107+
const userStore = useUserStore();
80108
const router = useRouter();
81109
82-
const loggedIn = computed(() => !!coreStore?.adminUser);
110+
const loggedIn = computed(() => { return !!coreStore?.adminUser });
83111
const routerIsReady = ref(false);
84112
const loginRedirectCheckIsReady = ref(false);
113+
const sideBarOpen = ref(false);
114+
const theme = ref('light');
115+
const dropdownUserButton = ref<HTMLElement | null>(null);
85116
86117
async function initRouter() {
87118
await router.isReady();
88119
routerIsReady.value = true;
89120
}
90121
91-
async function loadMenu() {
92-
await initRouter();
93-
if (!route.meta.customLayout) {
94-
// for custom layouts we don't need to fetch menu
95-
await coreStore.fetchMenuAndResource();
122+
function toggleTheme() {
123+
theme.value = theme.value === 'light' ? 'dark' : 'light';
124+
coreStore.toggleTheme();
125+
}
126+
127+
function settingPageSlotName(c: { slug?: string; pageLabel?: string }, idx: number) {
128+
const base = (c.slug && c.slug.trim()) || (c.pageLabel && c.pageLabel.trim()) || `tab-${idx}`;
129+
return base
130+
.toString()
131+
.toLowerCase()
132+
.replace(/\s+/g, '-')
133+
.replace(/[^a-z0-9-_]/g, '-') || `tab-${idx}`;
134+
}
135+
136+
137+
async function logout() {
138+
userStore.unauthorize();
139+
await userStore.logout();
140+
router.push({ name: 'login' })
141+
}
142+
143+
watch(dropdownUserButton, (el) => {
144+
if (el) {
145+
const dd = new Dropdown(
146+
document.querySelector('#dropdown-user') as HTMLElement,
147+
document.querySelector('[data-dropdown-toggle="dropdown-user"]') as HTMLElement,
148+
);
149+
adminforth.closeUserMenuDropdown = () => dd.hide();
96150
}
151+
});
152+
153+
onMounted(async () => {
154+
await loadMenu();
155+
await loadPublicConfig();
97156
loginRedirectCheckIsReady.value = true;
157+
console.log('Setting pages:', coreStore?.config?.settingPages);
158+
console.log('Full config:', coreStore?.config);
159+
});
160+
161+
async function loadPublicConfig() {
162+
await coreStore.getPublicConfig();
163+
}
164+
165+
async function loadMenu() {
166+
await initRouter();
167+
await coreStore.fetchMenuAndResource();
98168
}
169+
99170
</script>

adminforth/types/Common.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,13 @@ export interface AdminForthConfigForFrontend {
10931093
customHeadItems?: {
10941094
tagName: string;
10951095
attributes: Record<string, string | boolean>;
1096-
}[],
1096+
}[],
1097+
settingPages?:{
1098+
icon?: string,
1099+
pageLabel: string,
1100+
slug?: string,
1101+
component?: string,
1102+
}[],
10971103
}
10981104

10991105
export interface GetBaseConfigResponse {

0 commit comments

Comments
 (0)