Skip to content

Commit f332404

Browse files
committed
feat: add settings page and validation for user menu settings
1 parent 9461618 commit f332404

File tree

5 files changed

+143
-1
lines changed

5 files changed

+143
-1
lines changed

adminforth/modules/codeInjector.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,22 @@ 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+
// }
326338

327339
registerCustomPages(this.adminforth.config);
328340
collectAssetsFromMenu(this.adminforth.config.menu);
329-
341+
// registerSettingPages(this.adminforth.config.auth.userMenuSettingsPages);
330342
const spaDir = this.getSpaDir();
331343

332344
if (process.env.HEAVY_DEBUG) {

adminforth/modules/configValidator.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,18 @@ export default class ConfigValidator implements IConfigValidator {
999999
const similar = suggestIfTypo(newConfig.resources.map((res) => res.resourceId ), newConfig.auth.usersResourceId);
10001000
throw new Error(`Resource with id "${newConfig.auth.usersResourceId}" not found. ${similar ? `Did you mean "${similar}"?` : ''}`);
10011001
}
1002+
if (newConfig.auth.userMenuSettingsPages) {
1003+
for (const page of newConfig.auth.userMenuSettingsPages) {
1004+
if (!page.component.startsWith('@@')) {
1005+
errors.push(`Menu item component must start with @@ : ${JSON.stringify(page)}`);
1006+
}
1007+
1008+
const path = page.component.replace('@@', newConfig.customization.customComponentsDir);
1009+
if (!fs.existsSync(path)) {
1010+
errors.push(`Menu item component "${page.component.replace('@@', '')}" does not exist in "${newConfig.customization.customComponentsDir}"`);
1011+
}
1012+
}
1013+
}
10021014

10031015
// normalize beforeLoginConfirmation hooks
10041016
const blc = this.inputConfig.auth.beforeLoginConfirmation;

adminforth/spa/src/router/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ const router = createRouter({
6262
},
6363
]
6464
},
65+
{
66+
path: '/settings',
67+
name: 'settings',
68+
component: () => import('@/views/SettingsView.vue'),
69+
meta: {
70+
title: 'Settings',
71+
customLayout: true
72+
},
73+
},
6574
/* IMPORTANT:ADMINFORTH ROUTES */
6675
{ path: "/:pathMatch(.*)*", component: PageNotFound },
6776
]
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<template>
2+
<div>
3+
<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"
6+
>
7+
<div class="af-header px-3 lg:px-5 lg:pl-3 flex items-center justify-between h-full w-full" >
8+
<div class="flex items-center justify-start rtl:justify-end">
9+
<button @click="sideBarOpen = !sideBarOpen"
10+
type="button" class="inline-flex items-center p-2 text-sm rounded-lg sm:hidden hover:bg-lightSidebarItemHover focus:outline-none focus:ring-2 focus:ring-lightSidebarDevider dark:text-darkSidebarIcons dark:hover:bg-darkSidebarHover dark:focus:ring-lightSidebarDevider">
11+
<span class="sr-only">{{ $t('Open sidebar') }}</span>
12+
<svg class="w-6 h-6" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
13+
<path clip-rule="evenodd" fill-rule="evenodd" d="M2 4.75A.75.75 0 012.75 4h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 4.75zm0 10.5a.75.75 0 01.75-.75h7.5a.75.75 0 010 1.5h-7.5a.75.75 0 01-.75-.75zM2 10a.75.75 0 01.75-.75h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 10z"></path>
14+
</svg>
15+
</button>
16+
</div>
17+
<div class="flex items-center">
18+
<component
19+
v-if="coreStore?.adminUser"
20+
v-for="c in coreStore?.config?.globalInjections?.header || []"
21+
:is="getCustomComponent(c)"
22+
:meta="c.meta"
23+
:adminUser="coreStore.adminUser"
24+
/>
25+
26+
<div class="flex items-center ms-3 ">
27+
<span
28+
v-if="!coreStore.config?.singleTheme"
29+
@click="toggleTheme" class="cursor-pointer flex items-center gap-1 block px-4 py-2 text-sm text-black dark:text-darkSidebarTextHover dark:hover:text-darkSidebarTextActive" role="menuitem">
30+
<IconMoonSolid class="w-5 h-5 text-blue-300" v-if="coreStore.theme !== 'dark'" />
31+
<IconSunSolid class="w-5 h-5 text-yellow-300" v-else />
32+
</span>
33+
<div>
34+
<button
35+
ref="dropdownUserButton"
36+
type="button" class="flex text-sm bg- rounded-full focus:ring-4 focus:ring-lightSidebarDevider dark:focus:ring-darkSidebarDevider dark:bg-" aria-expanded="false" data-dropdown-toggle="dropdown-user">
37+
<span class="sr-only">{{ $t('Open user menu') }}</span>
38+
<svg class="w-8 h-8 text-lightNavbarIcons dark:text-darkNavbarIcons" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
39+
<path fill-rule="evenodd" d="M12 20a7.966 7.966 0 0 1-5.002-1.756l.002.001v-.683c0-1.794 1.492-3.25 3.333-3.25h3.334c1.84 0 3.333 1.456 3.333 3.25v.683A7.966 7.966 0 0 1 12 20ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10c0 5.5-4.44 9.963-9.932 10h-.138C6.438 21.962 2 17.5 2 12Zm10-5c-1.84 0-3.333 1.455-3.333 3.25S10.159 13.5 12 13.5c1.84 0 3.333-1.455 3.333-3.25S13.841 7 12 7Z" clip-rule="evenodd"/>
40+
</svg>
41+
</button>
42+
</div>
43+
44+
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:shadow-black dark:bg-darkSidebar dark:divide-darkSidebarDevider dark:shadow-black" id="dropdown-user">
45+
<div class="px-4 py-3" role="none">
46+
<p class="text-sm text-gray-900 dark:text-darkNavbarText" role="none" v-if="coreStore.userFullname">
47+
{{ coreStore.userFullname }}
48+
</p>
49+
<p class="text-sm font-medium text-gray-900 truncate dark:text-darkSidebarText" role="none">
50+
{{ coreStore.username }}
51+
</p>
52+
</div>
53+
54+
<ul class="py-1" role="none">
55+
<li v-for="c in coreStore?.config?.globalInjections?.userMenu || []" >
56+
<component
57+
:is="getCustomComponent(c)"
58+
:meta="c.meta"
59+
:adminUser="coreStore.adminUser"
60+
/>
61+
</li>
62+
<li>
63+
<button @click="logout" class="cursor-pointer flex items-center gap-1 block px-4 py-2 text-sm text-black hover:bg-html dark:text-darkSidebarTextHover dark:hover:bg-darkSidebarItemHover dark:hover:text-darkSidebarTextActive w-full" role="menuitem">{{ $t('Sign out') }}</button>
64+
</li>
65+
</ul>
66+
</div>
67+
</div>
68+
</div>
69+
</div>
70+
</nav>
71+
</div>
72+
</template>
73+
74+
<script setup lang="ts">
75+
76+
import { useCoreStore } from '@/stores/core';
77+
import { computed, ref } from 'vue';
78+
import { useRouter } from 'vue-router';
79+
const coreStore = useCoreStore();
80+
const router = useRouter();
81+
82+
const loggedIn = computed(() => !!coreStore?.adminUser);
83+
const routerIsReady = ref(false);
84+
const loginRedirectCheckIsReady = ref(false);
85+
86+
async function initRouter() {
87+
await router.isReady();
88+
routerIsReady.value = true;
89+
}
90+
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();
96+
}
97+
loginRedirectCheckIsReady.value = true;
98+
}
99+
</script>

adminforth/types/Back.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,16 @@ export interface AdminForthInputConfig {
10341034
* If you are using Cloudflare, set this to 'CF-Connecting-IP'. Case-insensitive.
10351035
*/
10361036
clientIpHeader?: string,
1037+
1038+
/**
1039+
* Add custom page to the settings page
1040+
*/
1041+
userMenuSettingsPages: {
1042+
icon?: string,
1043+
pageLabel: string,
1044+
slug?: string,
1045+
component: string
1046+
}[],
10371047
},
10381048

10391049
/**

0 commit comments

Comments
 (0)