Skip to content

Commit 64e0550

Browse files
Merge branch 'admin-ui-issue-2250' of github-faisal:GluuFederation/flex into admin-ui-issue-2250
2 parents ee739e5 + 968208e commit 64e0550

File tree

35 files changed

+560
-103
lines changed

35 files changed

+560
-103
lines changed

admin-ui/app/routes/Apps/Gluu/GluuAppSidebar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ function GluuAppSidebar(): JSX.Element {
135135
[hasChildren],
136136
)
137137

138-
const memoizedFilteredMenus = useMemo((): PluginMenu[] => {
139-
const menus: PluginMenu[] = processMenus()
138+
const memoizedFilteredMenus = useMemo(async (): Promise<PluginMenu[]> => {
139+
const menus: PluginMenu[] = await processMenus()
140140

141141
if (!fetchedServersLength) {
142142
return []
@@ -150,7 +150,7 @@ function GluuAppSidebar(): JSX.Element {
150150

151151
const loadMenus = async () => {
152152
try {
153-
const filteredMenus = await filterMenuItems(memoizedFilteredMenus)
153+
const filteredMenus = await filterMenuItems(await memoizedFilteredMenus)
154154
setPluginMenus(filteredMenus)
155155
} finally {
156156
!loading && setLoading(true)

admin-ui/app/routes/index.tsx

Lines changed: 26 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
1-
import { useState, useEffect, lazy, Suspense } from 'react'
1+
import { useState, useEffect } from 'react'
22
import { Route, Routes, Navigate } from 'react-router-dom'
33
import { useSelector } from 'react-redux'
44

5-
import NavbarOnly from './Layouts/NavbarOnly'
6-
import SidebarDefault from './Layouts/SidebarDefault'
7-
import SidebarA from './Layouts/SidebarA'
8-
import SidebarWithNavbar from './Layouts/SidebarWithNavbar'
9-
105
// ----------- Layout Imports ---------------
11-
import { processRoutes } from 'Plugins/PluginMenuResolver'
6+
import { processRoutes, processRoutesSync } from 'Plugins/PluginMenuResolver'
127

138
import GluuSuspenseLoader from 'Routes/Apps/Gluu/GluuSuspenseLoader'
149

1510
import { uuidv4 } from 'Utils/Util'
1611
import ProtectedRoute from './Pages/ProtectRoutes'
17-
18-
const DashboardPage = lazy(() => import('./Dashboards/DashboardPage'))
19-
const ProfilePage = lazy(() => import('./Apps/Profile/ProfilePage'))
20-
const Gluu404Error = lazy(() => import('./Apps/Gluu/Gluu404Error'))
21-
const ByeBye = lazy(() => import('./Pages/ByeBye'))
22-
const GluuNavBar = lazy(() => import('./Apps/Gluu/GluuNavBar'))
23-
const DefaultSidebar = lazy(() => import('./../layout/components/DefaultSidebar'))
12+
import { LazyRoutes } from 'Utils/RouteLoader'
2413

2514
//------ Route Definitions --------
2615

2716
export const RoutedContent = () => {
2817
const [pluginMenus, setPluginMenus] = useState<Array<any>>([])
18+
2919
useEffect(() => {
30-
setPluginMenus(processRoutes())
20+
const loadPlugins = async () => {
21+
try {
22+
const routes = await processRoutes()
23+
setPluginMenus(routes)
24+
} catch (error) {
25+
console.error('Failed to load plugins:', error)
26+
// Fallback to sync loading
27+
setPluginMenus(processRoutesSync())
28+
}
29+
}
30+
31+
loadPlugins()
3132
}, [])
3233

3334
const { userinfo, config } = useSelector((state: any) => state.authReducer)
@@ -46,53 +47,35 @@ export const RoutedContent = () => {
4647
path="/home/dashboard"
4748
element={
4849
<ProtectedRoute>
49-
<Suspense fallback={<GluuSuspenseLoader />}>
50-
<DashboardPage />
51-
</Suspense>
50+
<LazyRoutes.DashboardPage />
5251
</ProtectedRoute>
5352
}
5453
/>
5554
<Route path="/" element={<Navigate to="/home/dashboard" />} />
5655

5756
{/* Layouts */}
58-
<Route path="/layouts/navbar" element={<NavbarOnly />} />
59-
<Route path="/layouts/sidebar" element={<SidebarDefault />} />
60-
<Route path="/layouts/sidebar-a" element={<SidebarA />} />
61-
<Route path="/layouts/sidebar-with-navbar" element={<SidebarWithNavbar />} />
57+
<Route path="/layouts/navbar" element={<LazyRoutes.NavbarOnly />} />
58+
<Route path="/layouts/sidebar" element={<LazyRoutes.SidebarDefault />} />
59+
<Route path="/layouts/sidebar-a" element={<LazyRoutes.SidebarA />} />
60+
<Route path="/layouts/sidebar-with-navbar" element={<LazyRoutes.SidebarWithNavbar />} />
6261

6362
{/* -------- Plugins ---------*/}
6463
{pluginMenus.map((item, key) => (
6564
<Route key={key} path={item.path} element={<item.component />} />
6665
))}
6766

6867
{/* Pages Routes */}
69-
<Route
70-
element={
71-
<Suspense fallback={<GluuSuspenseLoader />}>
72-
<ProfilePage />
73-
</Suspense>
74-
}
75-
path="/profile"
76-
/>
68+
<Route element={<LazyRoutes.ProfilePage />} path="/profile" />
7769

7870
<Route
7971
path="/logout"
8072
element={
8173
<ProtectedRoute>
82-
<Suspense fallback={<GluuSuspenseLoader />}>
83-
<ByeBye />
84-
</Suspense>
74+
<LazyRoutes.ByeBye />
8575
</ProtectedRoute>
8676
}
8777
/>
88-
<Route
89-
element={
90-
<Suspense fallback={<GluuSuspenseLoader />}>
91-
<Gluu404Error />
92-
</Suspense>
93-
}
94-
path="/error-404"
95-
/>
78+
<Route element={<LazyRoutes.Gluu404Error />} path="/error-404" />
9679

9780
{/* 404 */}
9881
<Route path="*" element={<Navigate to="/error-404" />} />
@@ -103,26 +86,12 @@ export const RoutedContent = () => {
10386
//------ Custom Layout Parts --------
10487
export const RoutedNavbars = () => (
10588
<Routes>
106-
<Route
107-
path="/*"
108-
element={
109-
<Suspense fallback={<GluuSuspenseLoader />}>
110-
<GluuNavBar />
111-
</Suspense>
112-
}
113-
/>
89+
<Route path="/*" element={<LazyRoutes.GluuNavBar />} />
11490
</Routes>
11591
)
11692

11793
export const RoutedSidebars = () => (
11894
<Routes>
119-
<Route
120-
path="/*"
121-
element={
122-
<Suspense fallback={<GluuSuspenseLoader />}>
123-
<DefaultSidebar />
124-
</Suspense>
125-
}
126-
/>
95+
<Route path="/*" element={<LazyRoutes.DefaultSidebar />} />
12796
</Routes>
12897
)

admin-ui/app/utils/RouteLoader.tsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import React, { Suspense, lazy, ComponentType } from 'react'
2+
import GluuSuspenseLoader from 'Routes/Apps/Gluu/GluuSuspenseLoader'
3+
4+
// Route loading utility
5+
export const createLazyRoute = (importFn: () => Promise<{ default: ComponentType<any> }>) => {
6+
const LazyComponent = lazy(importFn)
7+
8+
const Wrapper: any = (props: any) => (
9+
<Suspense fallback={<GluuSuspenseLoader />}>
10+
<LazyComponent {...props} />
11+
</Suspense>
12+
)
13+
// Allow preloading the chunk ahead of time
14+
Wrapper.preload = importFn
15+
return Wrapper
16+
}
17+
18+
// Pre-defined lazy routes for better code splitting
19+
export const LazyRoutes = {
20+
// Dashboard routes
21+
DashboardPage: createLazyRoute(() => import('../routes/Dashboards/DashboardPage')),
22+
23+
// App routes
24+
ProfilePage: createLazyRoute(() => import('../routes/Apps/Profile/ProfilePage')),
25+
Gluu404Error: createLazyRoute(() => import('../routes/Apps/Gluu/Gluu404Error')),
26+
ByeBye: createLazyRoute(() => import('../routes/Pages/ByeBye')),
27+
GluuNavBar: createLazyRoute(() => import('../routes/Apps/Gluu/GluuNavBar')),
28+
29+
// Layout routes
30+
NavbarOnly: createLazyRoute(() => import('../routes/Layouts/NavbarOnly')),
31+
SidebarDefault: createLazyRoute(() => import('../routes/Layouts/SidebarDefault')),
32+
SidebarA: createLazyRoute(() => import('../routes/Layouts/SidebarA')),
33+
SidebarWithNavbar: createLazyRoute(() => import('../routes/Layouts/SidebarWithNavbar')),
34+
35+
// Layout components
36+
DefaultSidebar: createLazyRoute(() => import('../layout/components/DefaultSidebar')),
37+
38+
// Activity routes
39+
ActivityPage: createLazyRoute(() => import('../routes/Activity/ActivityPage')),
40+
}
41+
42+
// Dynamic route loader for plugins
43+
export const loadPluginRoute = (pluginName: string, routePath?: string) => {
44+
const componentPath = routePath || `${pluginName.charAt(0).toUpperCase() + pluginName.slice(1)}`
45+
46+
return createLazyRoute(() =>
47+
import(
48+
/* webpackChunkName: "plugin-[request]" */
49+
/* webpackMode: "lazy" */
50+
/* webpackInclude: /^[^/]+\/components\/[^/]+$/ */
51+
`../../plugins/${pluginName}/components/${componentPath}`
52+
).catch(() =>
53+
import(
54+
/* webpackChunkName: "plugin-[request]" */
55+
/* webpackMode: "lazy" */
56+
/* webpackInclude: /^[^/]+\/components\/index(\.(t|j)sx?)?$/ */
57+
`../../plugins/${pluginName}/components/index`
58+
),
59+
)
60+
)
61+
}
62+
63+
// Route preloading utility
64+
export const preloadRoute = (routeName: keyof typeof LazyRoutes) => {
65+
const route = LazyRoutes[routeName]
66+
if (route && 'preload' in route) {
67+
;(route as any).preload()
68+
}
69+
}
70+
71+
// Batch preload multiple routes
72+
export const preloadRoutes = (routeNames: (keyof typeof LazyRoutes)[]) => {
73+
routeNames.forEach(preloadRoute)
74+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
2+
3+
module.exports = {
4+
// Enable bundle analyzer in production builds
5+
plugins: [
6+
new BundleAnalyzerPlugin({
7+
analyzerMode: process.env.ANALYZE ? 'server' : 'disabled',
8+
analyzerHost: 'localhost',
9+
analyzerPort: 8888,
10+
openAnalyzer: true,
11+
generateStatsFile: true,
12+
statsFilename: 'bundle-stats.json',
13+
statsOptions: {
14+
source: false,
15+
modules: false,
16+
chunks: true,
17+
chunkModules: true,
18+
chunkOrigins: true,
19+
reasons: true,
20+
usedExports: true,
21+
providedExports: true,
22+
optimizationBailout: true,
23+
errorDetails: true,
24+
publicPath: true,
25+
timings: true,
26+
builtAt: true,
27+
assets: true,
28+
entrypoints: true,
29+
chunkGroups: true,
30+
modulesSpace: 0,
31+
chunksSpace: 0,
32+
assetsSpace: 0,
33+
reasonsSpace: 0,
34+
providedExportsSpace: 0,
35+
usedExportsSpace: 0,
36+
optimizationBailoutSpace: 0,
37+
errorDetailsSpace: 0,
38+
timingsSpace: 0,
39+
builtAtSpace: 0,
40+
entrypointsSpace: 0,
41+
chunkGroupsSpace: 0,
42+
},
43+
}),
44+
],
45+
}

admin-ui/config/webpack.config.client.dev.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,68 @@ const webpackConfig: WebpackConfig & { devServer?: DevServerConfig } = {
3030
optimization: {
3131
moduleIds: 'named',
3232
minimizer: [`...`, new CssMinimizerPlugin()],
33+
splitChunks: {
34+
chunks: 'all',
35+
cacheGroups: {
36+
// Vendor libraries - split by framework/library type
37+
react: {
38+
test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom)[\\/]/,
39+
name: 'react-vendor',
40+
chunks: 'all',
41+
priority: 20,
42+
},
43+
mui: {
44+
test: /[\\/]node_modules[\\/](@mui|@emotion)[\\/]/,
45+
name: 'mui-vendor',
46+
chunks: 'all',
47+
priority: 19,
48+
},
49+
redux: {
50+
test: /[\\/]node_modules[\\/](@reduxjs|redux|redux-saga|redux-persist)[\\/]/,
51+
name: 'redux-vendor',
52+
chunks: 'all',
53+
priority: 18,
54+
},
55+
charts: {
56+
test: /[\\/]node_modules[\\/](recharts|react-ace|ace-builds)[\\/]/,
57+
name: 'charts-vendor',
58+
chunks: 'all',
59+
priority: 17,
60+
},
61+
utils: {
62+
test: /[\\/]node_modules[\\/](lodash|moment|dayjs|axios|formik|yup)[\\/]/,
63+
name: 'utils-vendor',
64+
chunks: 'all',
65+
priority: 16,
66+
},
67+
// Plugin chunks - each plugin gets its own chunk
68+
plugins: {
69+
test: /[\\/]plugins[\\/]/,
70+
name: (module: any) => {
71+
const context = (module && module.context) || ''
72+
const match = context && context.match(/[\\/]plugins[\\/]([^\\/]+)[\\/]/)
73+
return match && match[1] ? `plugin-${match[1]}` : 'plugin-common'
74+
},
75+
chunks: 'all',
76+
priority: 15,
77+
reuseExistingChunk: true,
78+
},
79+
// Common vendor libraries
80+
vendor: {
81+
test: /[\\/]node_modules[\\/]/,
82+
name: 'vendor',
83+
chunks: 'all',
84+
priority: 10,
85+
},
86+
// Common application code
87+
common: {
88+
name: 'common',
89+
minChunks: 2,
90+
chunks: 'all',
91+
priority: 5,
92+
},
93+
},
94+
},
3395
},
3496
devtool: 'source-map',
3597
target: 'web',

0 commit comments

Comments
 (0)