WIP: frontend module federation

This commit is contained in:
Antoine Nguyen
2025-02-14 13:39:08 +01:00
parent 6cef7553c8
commit 604a91caa9
9 changed files with 695 additions and 399 deletions
+1
View File
@@ -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>
+55 -44
View File
@@ -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()
}
+1 -1
View File
@@ -26,7 +26,7 @@ const routes = [
{
path: '',
name: 'Dashboard',
component: () => import('@/views/Dashboard.vue'),
component: () => import('@/views/DashboardView.vue'),
meta: {
requiresAuth: true,
},
+1 -1
View File
@@ -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(() => {
+12
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff