mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
WIP: frontend module federation
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
"vuetify": "^3.6.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@module-federation/vite": "^1.1.3",
|
||||
"@vitejs/plugin-basic-ssl": "^1.1.0",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"eslint": "^9.6.0",
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<v-fab app icon flat location="top right" color="primary">
|
||||
{{ userInitials }}
|
||||
<v-menu activator="parent" location="bottom">
|
||||
<v-card min-width="300" max-width="350">
|
||||
<v-list>
|
||||
<div class="text-center">
|
||||
<v-avatar color="primary">
|
||||
<span class="text-h5">{{ userInitials }}</span>
|
||||
</v-avatar>
|
||||
<v-list-item :title="user.username" />
|
||||
</div>
|
||||
<v-divider></v-divider>
|
||||
<template v-if="remote">
|
||||
<v-list-item href="https://localhost:3000/account">
|
||||
<template #prepend>
|
||||
<v-icon icon="mdi-account-circle-outline"></v-icon>
|
||||
</template>
|
||||
<v-list-item-title>Account</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="logout">
|
||||
<template #prepend>
|
||||
<v-icon icon="mdi-logout"></v-icon>
|
||||
</template>
|
||||
<v-list-item-title>Logout</v-list-item-title>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</v-fab>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { getActivePinia } from 'pinia'
|
||||
|
||||
const props = defineProps({
|
||||
user: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
remote: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const userInitials = computed(() => {
|
||||
return props.user.first_name && props.user.last_name
|
||||
? `${props.user.first_name[0].toUpperCase()}${props.user.last_name[0].toUpperCase()}`
|
||||
: props.user.username.slice(0, 2).toUpperCase()
|
||||
})
|
||||
|
||||
async function logout() {
|
||||
getActivePinia()._s.forEach(async (store) => await store.$reset())
|
||||
}
|
||||
</script>
|
||||
@@ -62,50 +62,50 @@
|
||||
</template>
|
||||
</v-list>
|
||||
<template #append>
|
||||
<v-menu v-if="isAuthenticated" rounded="lg" offset-y top>
|
||||
<template #activator="{ props }">
|
||||
<div
|
||||
:class="[
|
||||
backgroundColor,
|
||||
'd-flex',
|
||||
'justify-center',
|
||||
'align-center',
|
||||
'pa-2',
|
||||
]"
|
||||
>
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
:class="[backgroundColor, 'text-white']"
|
||||
rounded
|
||||
density="compact"
|
||||
height="48"
|
||||
elevation="0"
|
||||
>
|
||||
<v-avatar size="40" color="primary">
|
||||
<span class="text-white headline">{{ userInitials }}</span>
|
||||
</v-avatar>
|
||||
<template v-if="!rail">
|
||||
<span class="mx-2">{{ displayName }}</span>
|
||||
<v-icon class="float-right">mdi-chevron-up</v-icon>
|
||||
</template>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="item in userMenuItems"
|
||||
:key="item.text"
|
||||
:to="item.to"
|
||||
link
|
||||
@click="item.click"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon :icon="item.icon"></v-icon>
|
||||
</template>
|
||||
<v-list-item-title>{{ item.text }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<!-- <v-menu v-if="isAuthenticated" rounded="lg" offset-y top>
|
||||
<template #activator="{ props }">
|
||||
<div
|
||||
:class="[
|
||||
backgroundColor,
|
||||
'd-flex',
|
||||
'justify-center',
|
||||
'align-center',
|
||||
'pa-2',
|
||||
]"
|
||||
>
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
:class="[backgroundColor, 'text-white']"
|
||||
rounded
|
||||
density="compact"
|
||||
height="48"
|
||||
elevation="0"
|
||||
>
|
||||
<v-avatar size="40" color="primary">
|
||||
<span class="text-white headline">{{ userInitials }}</span>
|
||||
</v-avatar>
|
||||
<template v-if="!rail">
|
||||
<span class="mx-2">{{ displayName }}</span>
|
||||
<v-icon class="float-right">mdi-chevron-up</v-icon>
|
||||
</template>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="item in userMenuItems"
|
||||
:key="item.text"
|
||||
:to="item.to"
|
||||
link
|
||||
@click="item.click"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon :icon="item.icon"></v-icon>
|
||||
</template>
|
||||
<v-list-item-title>{{ item.text }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu> -->
|
||||
</template>
|
||||
</v-navigation-drawer>
|
||||
</template>
|
||||
@@ -118,6 +118,14 @@ import { getActivePinia } from 'pinia'
|
||||
import { useGlobalStore, useAuthStore, useParametersStore } from '@/stores'
|
||||
import parametersApi from '@/api/parameters'
|
||||
|
||||
const props = defineProps({
|
||||
menuItems: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
const authStore = useAuthStore()
|
||||
const parametersStore = useParametersStore()
|
||||
@@ -155,6 +163,9 @@ const displayName = computed(() => {
|
||||
})
|
||||
|
||||
const menuItems = computed(() => {
|
||||
if (props.menuItems) {
|
||||
return props.menuItems
|
||||
}
|
||||
if (route.meta.layout === 'account') {
|
||||
return getUserSettingsMenuItems()
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const routes = [
|
||||
{
|
||||
path: '',
|
||||
name: 'Dashboard',
|
||||
component: () => import('@/views/Dashboard.vue'),
|
||||
component: () => import('@/views/DashboardView.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, inject, ref } from 'vue'
|
||||
import gettext from '@/plugins/gettext'
|
||||
import router from '@/router/index.js'
|
||||
import { UserManager } from 'oidc-client-ts'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="js">
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { useAuthStore } from '@/stores'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import basicSsl from '@vitejs/plugin-basic-ssl'
|
||||
|
||||
// Utilities
|
||||
import { defineConfig } from 'vite'
|
||||
import { federation } from '@module-federation/vite'
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
@@ -32,6 +33,17 @@ export default defineConfig({
|
||||
],
|
||||
},
|
||||
}),
|
||||
federation({
|
||||
name: 'coretools',
|
||||
filename: 'remoteEntry.js',
|
||||
exposes: {
|
||||
'./auth-store': './src/stores/auth.store.js',
|
||||
'./auth-callback': './src/views/login/LoginCallbackView.vue',
|
||||
'./navbar': './src/layouts/dashboard/NavBar.vue',
|
||||
'./user-menu': './src/components/shared/UserMenu.vue',
|
||||
},
|
||||
shared: ['vue', 'pinia', 'vue-router', 'vue3-gettext'],
|
||||
}),
|
||||
],
|
||||
base: process.env.NODE_ENV === 'production' ? '/new-admin/' : '/',
|
||||
define: { 'process.env': {}, __VUE_OPTIONS_API__: false },
|
||||
|
||||
+565
-352
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user