@nsxbet/admin-sdk 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/CHECKLIST.md +40 -10
  2. package/README.md +337 -36
  3. package/dist/auth/client/gateway-token.d.ts +19 -0
  4. package/dist/auth/client/gateway-token.js +89 -0
  5. package/dist/auth/client/in-memory.d.ts +5 -1
  6. package/dist/auth/client/in-memory.js +75 -38
  7. package/dist/auth/client/index.d.ts +0 -1
  8. package/dist/auth/client/interface.d.ts +6 -3
  9. package/dist/auth/client/keycloak.d.ts +0 -1
  10. package/dist/auth/client/keycloak.js +6 -3
  11. package/dist/auth/components/UserSelector.d.ts +0 -1
  12. package/dist/auth/components/UserSelector.js +89 -7
  13. package/dist/auth/components/index.d.ts +0 -1
  14. package/dist/auth/index.d.ts +0 -1
  15. package/dist/components/AuthProvider.d.ts +0 -1
  16. package/dist/components/Timestamp.d.ts +7 -0
  17. package/dist/components/Timestamp.js +50 -0
  18. package/dist/hooks/useAuth.d.ts +0 -1
  19. package/dist/hooks/useAuth.js +1 -1
  20. package/dist/hooks/useFetch.d.ts +0 -1
  21. package/dist/hooks/useI18n.d.ts +0 -1
  22. package/dist/hooks/usePlatformAPI.d.ts +0 -1
  23. package/dist/hooks/useTelemetry.d.ts +0 -1
  24. package/dist/hooks/useTimestamp.d.ts +8 -0
  25. package/dist/hooks/useTimestamp.js +122 -0
  26. package/dist/i18n/config.d.ts +20 -2
  27. package/dist/i18n/config.js +48 -0
  28. package/dist/i18n/index.d.ts +2 -3
  29. package/dist/i18n/index.js +1 -1
  30. package/dist/i18n/locales/en-US.json +95 -18
  31. package/dist/i18n/locales/es.json +95 -18
  32. package/dist/i18n/locales/pt-BR.json +95 -18
  33. package/dist/i18n/locales/ro.json +95 -18
  34. package/dist/index.d.ts +11 -7
  35. package/dist/index.js +5 -1
  36. package/dist/registry/AdminShellRegistry.d.ts +1 -2
  37. package/dist/registry/cache/cached-catalog.d.ts +11 -0
  38. package/dist/registry/cache/cached-catalog.js +42 -0
  39. package/dist/registry/cache/catalog-cache.d.ts +10 -0
  40. package/dist/registry/cache/catalog-cache.js +58 -0
  41. package/dist/registry/cache/index.d.ts +5 -0
  42. package/dist/registry/cache/index.js +3 -0
  43. package/dist/registry/cache/types.d.ts +20 -0
  44. package/dist/registry/cache/types.js +3 -0
  45. package/dist/registry/client/http.d.ts +0 -1
  46. package/dist/registry/client/http.js +13 -0
  47. package/dist/registry/client/in-memory.d.ts +0 -1
  48. package/dist/registry/client/in-memory.js +117 -12
  49. package/dist/registry/client/index.d.ts +0 -1
  50. package/dist/registry/client/interface.d.ts +21 -6
  51. package/dist/registry/index.d.ts +5 -2
  52. package/dist/registry/index.js +4 -0
  53. package/dist/registry/types/index.d.ts +2 -3
  54. package/dist/registry/types/manifest.d.ts +20 -24
  55. package/dist/registry/types/manifest.js +17 -18
  56. package/dist/registry/types/module.d.ts +43 -14
  57. package/dist/registry/useRegistryPolling.d.ts +15 -0
  58. package/dist/registry/useRegistryPolling.js +66 -0
  59. package/dist/router/DynamicModule.d.ts +6 -22
  60. package/dist/router/DynamicModule.js +25 -48
  61. package/dist/router/ModuleErrorBoundary.d.ts +39 -0
  62. package/dist/router/ModuleErrorBoundary.js +101 -0
  63. package/dist/router/index.d.ts +1 -1
  64. package/dist/router/url-allowlist.d.ts +22 -0
  65. package/dist/router/url-allowlist.js +65 -0
  66. package/dist/shell/AdminShell.d.ts +0 -1
  67. package/dist/shell/AdminShell.js +178 -43
  68. package/dist/shell/BackofficeShell.d.ts +0 -1
  69. package/dist/shell/BackofficeShell.js +59 -25
  70. package/dist/shell/components/CommandPalette.d.ts +0 -1
  71. package/dist/shell/components/CommandPalette.js +26 -50
  72. package/dist/shell/components/DevtoolsPanel.d.ts +11 -0
  73. package/dist/shell/components/DevtoolsPanel.js +145 -0
  74. package/dist/shell/components/HomePage.d.ts +0 -1
  75. package/dist/shell/components/HomePage.js +9 -4
  76. package/dist/shell/components/LeftNav.d.ts +0 -1
  77. package/dist/shell/components/LeftNav.js +91 -93
  78. package/dist/shell/components/MainContent.d.ts +3 -2
  79. package/dist/shell/components/MainContent.js +8 -23
  80. package/dist/shell/components/ModuleOverview.d.ts +0 -1
  81. package/dist/shell/components/ModuleOverview.js +4 -20
  82. package/dist/shell/components/ProfilePage.d.ts +0 -1
  83. package/dist/shell/components/ProfilePage.js +1 -1
  84. package/dist/shell/components/RegistryPage.d.ts +0 -1
  85. package/dist/shell/components/RegistryPage.js +154 -64
  86. package/dist/shell/components/RegistryStatusBanner.d.ts +6 -0
  87. package/dist/shell/components/RegistryStatusBanner.js +31 -0
  88. package/dist/shell/components/RegistryUnavailable.d.ts +4 -0
  89. package/dist/shell/components/RegistryUnavailable.js +7 -0
  90. package/dist/shell/components/SettingsPage.d.ts +0 -1
  91. package/dist/shell/components/StackedPanel.d.ts +15 -0
  92. package/dist/shell/components/StackedPanel.js +45 -0
  93. package/dist/shell/components/TopBar.d.ts +4 -2
  94. package/dist/shell/components/TopBar.js +9 -3
  95. package/dist/shell/components/UpdateBanner.d.ts +5 -0
  96. package/dist/shell/components/UpdateBanner.js +8 -0
  97. package/dist/shell/components/index.d.ts +4 -1
  98. package/dist/shell/components/index.js +2 -0
  99. package/dist/shell/components/theme-provider.d.ts +0 -1
  100. package/dist/shell/components/theme-provider.js +8 -5
  101. package/dist/shell/hooks/useCspViolations.d.ts +12 -0
  102. package/dist/shell/hooks/useCspViolations.js +34 -0
  103. package/dist/shell/index.d.ts +1 -2
  104. package/dist/shell/polling-config.d.ts +10 -0
  105. package/dist/shell/polling-config.js +26 -0
  106. package/dist/shell/search/fuzzy.d.ts +0 -1
  107. package/dist/shell/search/index.d.ts +0 -1
  108. package/dist/shell/telemetry.d.ts +0 -1
  109. package/dist/shell/types.d.ts +34 -18
  110. package/dist/tailwind/index.d.ts +0 -1
  111. package/dist/types/keycloak.d.ts +0 -1
  112. package/dist/types/platform.d.ts +12 -1
  113. package/dist/vite/AdminShellSharedDeps.d.ts +64 -0
  114. package/dist/vite/AdminShellSharedDeps.js +215 -0
  115. package/dist/vite/config.d.ts +10 -2
  116. package/dist/vite/config.js +13 -10
  117. package/dist/vite/i18n-plugin.d.ts +13 -0
  118. package/dist/vite/i18n-plugin.js +81 -0
  119. package/dist/vite/index.d.ts +2 -1
  120. package/dist/vite/index.js +2 -0
  121. package/dist/vite/plugins.d.ts +0 -1
  122. package/package.json +6 -2
  123. package/dist/auth/client/in-memory.d.ts.map +0 -1
  124. package/dist/auth/client/index.d.ts.map +0 -1
  125. package/dist/auth/client/interface.d.ts.map +0 -1
  126. package/dist/auth/client/keycloak.d.ts.map +0 -1
  127. package/dist/auth/components/UserSelector.d.ts.map +0 -1
  128. package/dist/auth/components/index.d.ts.map +0 -1
  129. package/dist/auth/index.d.ts.map +0 -1
  130. package/dist/components/AuthProvider.d.ts.map +0 -1
  131. package/dist/hooks/useAuth.d.ts.map +0 -1
  132. package/dist/hooks/useFetch.d.ts.map +0 -1
  133. package/dist/hooks/useI18n.d.ts.map +0 -1
  134. package/dist/hooks/usePlatformAPI.d.ts.map +0 -1
  135. package/dist/hooks/useTelemetry.d.ts.map +0 -1
  136. package/dist/i18n/config.d.ts.map +0 -1
  137. package/dist/i18n/index.d.ts.map +0 -1
  138. package/dist/index.d.ts.map +0 -1
  139. package/dist/registry/AdminShellRegistry.d.ts.map +0 -1
  140. package/dist/registry/client/http.d.ts.map +0 -1
  141. package/dist/registry/client/in-memory.d.ts.map +0 -1
  142. package/dist/registry/client/index.d.ts.map +0 -1
  143. package/dist/registry/client/interface.d.ts.map +0 -1
  144. package/dist/registry/index.d.ts.map +0 -1
  145. package/dist/registry/types/index.d.ts.map +0 -1
  146. package/dist/registry/types/manifest.d.ts.map +0 -1
  147. package/dist/registry/types/module.d.ts.map +0 -1
  148. package/dist/router/DynamicModule.d.ts.map +0 -1
  149. package/dist/router/index.d.ts.map +0 -1
  150. package/dist/shell/AdminShell.d.ts.map +0 -1
  151. package/dist/shell/BackofficeShell.d.ts.map +0 -1
  152. package/dist/shell/components/CommandPalette.d.ts.map +0 -1
  153. package/dist/shell/components/HomePage.d.ts.map +0 -1
  154. package/dist/shell/components/LeftNav.d.ts.map +0 -1
  155. package/dist/shell/components/MainContent.d.ts.map +0 -1
  156. package/dist/shell/components/ModuleOverview.d.ts.map +0 -1
  157. package/dist/shell/components/ProfilePage.d.ts.map +0 -1
  158. package/dist/shell/components/RegistryPage.d.ts.map +0 -1
  159. package/dist/shell/components/SettingsPage.d.ts.map +0 -1
  160. package/dist/shell/components/TopBar.d.ts.map +0 -1
  161. package/dist/shell/components/index.d.ts.map +0 -1
  162. package/dist/shell/components/theme-provider.d.ts.map +0 -1
  163. package/dist/shell/index.d.ts.map +0 -1
  164. package/dist/shell/search/fuzzy.d.ts.map +0 -1
  165. package/dist/shell/search/index.d.ts.map +0 -1
  166. package/dist/shell/telemetry.d.ts.map +0 -1
  167. package/dist/shell/types.d.ts.map +0 -1
  168. package/dist/tailwind/index.d.ts.map +0 -1
  169. package/dist/types/keycloak.d.ts.map +0 -1
  170. package/dist/types/platform.d.ts.map +0 -1
  171. package/dist/vite/config.d.ts.map +0 -1
  172. package/dist/vite/index.d.ts.map +0 -1
  173. package/dist/vite/plugins.d.ts.map +0 -1
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState, useEffect, useMemo, useCallback } from "react";
3
- import { BrowserRouter, Routes, Route, useNavigate } from "react-router-dom";
2
+ import { useState, useEffect, useMemo, useCallback, useRef } from "react";
3
+ import { BrowserRouter, Routes, Route, useNavigate, useLocation } from "react-router-dom";
4
4
  import { I18nextProvider } from "react-i18next";
5
5
  import { TopBar } from "./components/TopBar";
6
6
  import { LeftNav } from "./components/LeftNav";
7
7
  import { MainContent } from "./components/MainContent";
8
8
  import { CommandPalette } from "./components/CommandPalette";
9
+ import { UpdateBanner } from "./components/UpdateBanner";
9
10
  import { ThemeProvider } from "./components/theme-provider";
10
11
  import { ModuleOverview } from "./components/ModuleOverview";
11
12
  import { ProfilePage } from "./components/ProfilePage";
@@ -13,13 +14,33 @@ import { SettingsPage } from "./components/SettingsPage";
13
14
  import { RegistryPage } from "./components/RegistryPage";
14
15
  import { HomePage } from "./components/HomePage";
15
16
  import { AuthProvider, useAuthContext } from "../components/AuthProvider";
16
- import { createInMemoryAuthClient } from "../auth/client/in-memory";
17
+ import { createInMemoryAuthClient, createMockUsersFromRoles, } from "../auth/client/in-memory";
17
18
  import { createKeycloakAuthClient } from "../auth/client/keycloak";
18
19
  import { createInMemoryRegistryClient } from "../registry/client/in-memory";
20
+ import { createCachedCatalog } from "../registry/cache/cached-catalog";
21
+ import { DevtoolsPanel } from "./components/DevtoolsPanel";
22
+ import { RegistryStatusBanner } from "./components/RegistryStatusBanner";
23
+ import { RegistryUnavailable } from "./components/RegistryUnavailable";
24
+ import { useRegistryPolling } from "../registry/useRegistryPolling";
19
25
  import { DynamicModule } from "../router/DynamicModule";
20
26
  import { SidebarProvider, SidebarInset } from "@nsxbet/admin-ui";
21
27
  import { initTelemetry, track, trackError } from "./telemetry";
22
28
  import { initI18n, saveLocale, isSupportedLocale, i18n } from "../i18n";
29
+ import { resolvePollingInterval } from "./polling-config";
30
+ const TIMEZONE_STORAGE_KEY = "admin-timezone-mode";
31
+ function getStoredTimezoneMode() {
32
+ if (typeof window === "undefined")
33
+ return "local";
34
+ const stored = localStorage.getItem(TIMEZONE_STORAGE_KEY);
35
+ if (stored === "utc" || stored === "local")
36
+ return stored;
37
+ return "local";
38
+ }
39
+ function saveTimezoneMode(mode) {
40
+ if (typeof window !== "undefined") {
41
+ localStorage.setItem(TIMEZONE_STORAGE_KEY, mode);
42
+ }
43
+ }
23
44
  /**
24
45
  * Convert AdminModuleManifest to Module for internal use
25
46
  */
@@ -27,9 +48,7 @@ function manifestToModule(manifest, baseUrl) {
27
48
  return {
28
49
  id: manifest.id,
29
50
  title: manifest.title,
30
- titleKey: manifest.titleKey,
31
- description: manifest.description || "",
32
- descriptionKey: manifest.descriptionKey,
51
+ description: manifest.description,
33
52
  category: manifest.category || "Modules",
34
53
  routeBase: manifest.routeBase,
35
54
  baseUrl: baseUrl || "",
@@ -42,16 +61,23 @@ function manifestToModule(manifest, baseUrl) {
42
61
  team: manifest.owners?.team || "Platform",
43
62
  supportChannel: manifest.owners?.supportChannel || "",
44
63
  },
45
- status: manifest.status || "active",
46
- navOrder: manifest.navOrder,
64
+ status: "active",
65
+ navigationOrder: manifest.navigationOrder,
47
66
  icon: manifest.icon,
67
+ navigation: manifest.navigation ? {
68
+ style: manifest.navigation.style,
69
+ sections: manifest.navigation.sections?.map(s => ({
70
+ id: s.id,
71
+ label: s.label,
72
+ })),
73
+ } : undefined,
48
74
  commands: manifest.commands?.map((cmd) => ({
49
75
  id: cmd.id,
50
76
  title: cmd.title,
51
- titleKey: cmd.titleKey,
52
77
  route: cmd.route,
53
78
  icon: cmd.icon,
54
79
  keywords: cmd.keywords,
80
+ section: cmd.section,
55
81
  })),
56
82
  };
57
83
  }
@@ -62,9 +88,7 @@ function catalogModuleToModule(m) {
62
88
  return {
63
89
  id: m.id,
64
90
  title: m.title,
65
- titleKey: m.titleKey,
66
91
  description: m.description,
67
- descriptionKey: m.descriptionKey,
68
92
  category: m.category,
69
93
  routeBase: m.routeBase,
70
94
  baseUrl: m.baseUrl,
@@ -72,20 +96,36 @@ function catalogModuleToModule(m) {
72
96
  permissions: m.permissions,
73
97
  owners: m.owners,
74
98
  status: m.status,
75
- navOrder: m.navOrder,
99
+ navigationOrder: m.navigationOrder,
76
100
  icon: m.icon,
101
+ navigation: m.navigation ? {
102
+ style: m.navigation.style,
103
+ sections: m.navigation.sections?.map(s => ({
104
+ id: s.id,
105
+ label: s.title,
106
+ })),
107
+ } : undefined,
77
108
  commands: m.commands?.map(cmd => ({
78
- ...cmd,
79
- titleKey: cmd.titleKey,
109
+ id: cmd.id,
110
+ title: cmd.title,
111
+ route: cmd.route,
112
+ icon: cmd.icon,
113
+ keywords: cmd.keywords,
114
+ section: cmd.section,
80
115
  })),
81
116
  };
82
117
  }
83
118
  /**
84
119
  * Inner shell component that has access to React Router hooks
85
120
  */
86
- function ShellContent({ modules, children, environment, locale, onLocaleChange, onSearchClick, catalog, commandPaletteOpen, onCommandPaletteChange, apiUrl, registryClient, isStandaloneMode, }) {
121
+ function ShellContent({ modules, children, environment, locale, onLocaleChange, onSearchClick, catalog, commandPaletteOpen, onCommandPaletteChange, apiUrl, registryClient, isStandaloneMode, cacheStatus, onRetry, hasUpdates, onDismissUpdate, localeCallbacksRef, timezoneMode, onTimezoneToggle, timezoneCallbacksRef, }) {
87
122
  const navigate = useNavigate();
123
+ const location = useLocation();
88
124
  const auth = useAuthContext();
125
+ const [moduleBreadcrumbs, setModuleBreadcrumbs] = useState(null);
126
+ useEffect(() => {
127
+ setModuleBreadcrumbs(null);
128
+ }, [location.pathname]);
89
129
  // Set up the platform API for modules to use
90
130
  useEffect(() => {
91
131
  const platformAPI = {
@@ -94,10 +134,9 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
94
134
  auth: {
95
135
  getAccessToken: auth.getAccessToken,
96
136
  hasPermission: auth.hasPermission,
97
- getUser: () => auth.user || { id: "", email: "", displayName: "" },
137
+ getUser: () => auth.user || { id: "", email: "", displayName: "", roles: [] },
98
138
  logout: () => {
99
139
  auth.logout();
100
- // Navigate to root after logout (for Keycloak, this will redirect)
101
140
  navigate("/");
102
141
  },
103
142
  },
@@ -105,17 +144,29 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
105
144
  navigate: (path) => {
106
145
  navigate(path);
107
146
  },
108
- setBreadcrumbs: (_items) => {
109
- // Breadcrumbs are managed by MainContent based on route
147
+ setBreadcrumbs: (items) => {
148
+ setModuleBreadcrumbs(items.length > 0 ? items : null);
110
149
  },
111
150
  },
112
151
  i18n: {
113
152
  locale: locale,
114
153
  setLocale: onLocaleChange,
115
154
  onLocaleChange: (callback) => {
116
- // Simple implementation - just call callback immediately with current locale
155
+ localeCallbacksRef.current.add(callback);
117
156
  callback(locale);
118
- return () => { }; // Return unsubscribe function
157
+ return () => { localeCallbacksRef.current.delete(callback); };
158
+ },
159
+ },
160
+ timestamp: {
161
+ mode: timezoneMode,
162
+ setMode: (mode) => {
163
+ saveTimezoneMode(mode);
164
+ timezoneCallbacksRef.current.forEach(cb => cb(mode));
165
+ },
166
+ onModeChange: (callback) => {
167
+ timezoneCallbacksRef.current.add(callback);
168
+ callback(timezoneMode);
169
+ return () => { timezoneCallbacksRef.current.delete(callback); };
119
170
  },
120
171
  },
121
172
  telemetry: {
@@ -123,7 +174,6 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
123
174
  trackError: trackError,
124
175
  },
125
176
  fetch: async (input, init) => {
126
- // Add auth token to requests
127
177
  const token = await auth.getAccessToken();
128
178
  const headers = new Headers(init?.headers);
129
179
  headers.set("Authorization", `Bearer ${token}`);
@@ -134,7 +184,7 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
134
184
  return () => {
135
185
  delete window.__ADMIN_PLATFORM_API__;
136
186
  };
137
- }, [environment, locale, navigate, onLocaleChange, auth]);
187
+ }, [environment, locale, navigate, onLocaleChange, auth, timezoneMode]);
138
188
  // Load initial sidebar state from localStorage
139
189
  const getInitialSidebarState = () => {
140
190
  try {
@@ -160,11 +210,15 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
160
210
  // Ignore
161
211
  }
162
212
  };
163
- return (_jsxs(_Fragment, { children: [_jsxs(SidebarProvider, { open: sidebarOpen, onOpenChange: handleSidebarChange, children: [_jsx(LeftNav, { modules: modules }), _jsxs(SidebarInset, { className: "flex flex-col", children: [_jsx(TopBar, { onSearchClick: onSearchClick, environment: environment, locale: locale, onLocaleChange: onLocaleChange }), _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(MainContent, { modules: modules, children: _jsx(HomePage, {}) }) }), _jsx(Route, { path: "/_profile", element: _jsx(MainContent, { modules: modules, children: _jsx(ProfilePage, {}) }) }), _jsx(Route, { path: "/_settings", element: _jsx(MainContent, { modules: modules, children: _jsx(SettingsPage, {}) }) }), _jsx(Route, { path: "/_registry", element: _jsx(MainContent, { modules: modules, children: _jsx(RegistryPage, { apiUrl: apiUrl, registryClient: registryClient }) }) }), _jsx(Route, { path: "/_modules/*", element: _jsx(MainContent, { modules: modules, children: _jsx(ModuleOverview, { modules: modules }) }) }), isStandaloneMode
213
+ return (_jsxs(_Fragment, { children: [hasUpdates && (_jsx(UpdateBanner, { onReload: () => window.location.reload(), onDismiss: onDismissUpdate })), _jsxs(SidebarProvider, { open: sidebarOpen, onOpenChange: handleSidebarChange, children: [_jsx(LeftNav, { modules: modules }), _jsxs(SidebarInset, { className: "flex flex-col", children: [_jsx(TopBar, { onSearchClick: onSearchClick, environment: environment, locale: locale, onLocaleChange: onLocaleChange, timezoneMode: timezoneMode, onTimezoneToggle: onTimezoneToggle }), cacheStatus.state !== "fresh" && cacheStatus.state !== "unavailable" && (_jsx("div", { className: "px-4 pt-2", children: _jsx(RegistryStatusBanner, { status: cacheStatus, onRetry: onRetry }) })), _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(HomePage, {}) }) }), _jsx(Route, { path: "/_profile", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(ProfilePage, {}) }) }), _jsx(Route, { path: "/_settings", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(SettingsPage, {}) }) }), _jsx(Route, { path: "/_registry", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(RegistryPage, { apiUrl: apiUrl, registryClient: registryClient }) }) }), _jsx(Route, { path: "/_modules/*", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(ModuleOverview, { modules: modules }) }) }), isStandaloneMode
164
214
  ? // Standalone mode: render children (module is imported directly)
165
- modules.map((module) => (_jsx(Route, { path: `${module.routeBase}/*`, element: _jsx(MainContent, { modules: modules, children: children }) }, module.id)))
215
+ modules.map((module) => (_jsx(Route, { path: `${module.routeBase}/*`, element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: children }) }, module.id)))
166
216
  : // Shell mode: load modules dynamically via React.lazy
167
- modules.map((module) => (_jsx(Route, { path: `${module.routeBase}/*`, element: _jsx(MainContent, { modules: modules, children: module.baseUrl ? (_jsx(DynamicModule, { baseUrl: module.baseUrl })) : (_jsxs("div", { className: "text-muted-foreground", children: ["Module ", module.id, " has no baseUrl configured"] })) }) }, module.id))), _jsx(Route, { path: "*", element: _jsx(MainContent, { modules: modules, children: isStandaloneMode ? children : null }) })] })] })] }), _jsx(CommandPalette, { open: commandPaletteOpen, onOpenChange: onCommandPaletteChange, catalog: catalog })] }));
217
+ modules.map((module) => (_jsx(Route, { path: `${module.routeBase}/*`, element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: module.baseUrl ? (_jsx(DynamicModule, { baseUrl: module.baseUrl, moduleInfo: {
218
+ id: module.id,
219
+ title: module.title,
220
+ owners: module.owners,
221
+ } })) : (_jsxs("div", { className: "text-muted-foreground", children: ["Module ", module.id, " has no baseUrl configured"] })) }) }, module.id))), _jsx(Route, { path: "*", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: isStandaloneMode ? children : null }) })] })] })] }), _jsx(CommandPalette, { open: commandPaletteOpen, onOpenChange: onCommandPaletteChange, catalog: catalog }), _jsx(DevtoolsPanel, { environment: environment, modules: modules, catalogVersion: catalog.version, catalogGeneratedAt: catalog.generatedAt, registryMode: registryClient ? "api" : "in-memory", cacheStatus: cacheStatus })] }));
168
222
  }
169
223
  export function AdminShell({ modules: manifests = [], children, keycloak, authClient: providedAuthClient, registryClient, apiUrl, inMemoryRegistry = true, environment = "local", }) {
170
224
  const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
@@ -174,13 +228,29 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
174
228
  return i18n.language || "pt-BR";
175
229
  });
176
230
  const [apiModules, setApiModules] = useState([]);
231
+ const [initialCatalogVersion, setInitialCatalogVersion] = useState("");
232
+ const [catalogGeneratedAt, setCatalogGeneratedAt] = useState("");
177
233
  const [isLoading, setIsLoading] = useState(!!registryClient);
234
+ const [cacheStatus, setCacheStatus] = useState({ state: 'fresh' });
235
+ const [cachedCatalogOps, setCachedCatalogOps] = useState(null);
236
+ const localeCallbacksRef = useRef(new Set());
237
+ const timezoneCallbacksRef = useRef(new Set());
238
+ const [timezoneMode, setTimezoneMode] = useState(getStoredTimezoneMode);
239
+ const handleTimezoneToggle = useCallback(() => {
240
+ setTimezoneMode((prev) => {
241
+ const next = prev === "utc" ? "local" : "utc";
242
+ saveTimezoneMode(next);
243
+ timezoneCallbacksRef.current.forEach(cb => cb(next));
244
+ return next;
245
+ });
246
+ }, []);
178
247
  // Handle locale change - update both state and i18next
179
248
  const handleLocaleChange = useCallback((newLocale) => {
180
249
  if (isSupportedLocale(newLocale)) {
181
250
  i18n.changeLanguage(newLocale);
182
251
  saveLocale(newLocale);
183
252
  setLocale(newLocale);
253
+ localeCallbacksRef.current.forEach(cb => cb(newLocale));
184
254
  }
185
255
  }, []);
186
256
  // Sync locale state with i18next language changes
@@ -200,17 +270,50 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
200
270
  if (providedAuthClient) {
201
271
  return providedAuthClient;
202
272
  }
273
+ const isProd = import.meta.env.PROD === true;
274
+ const mockAuthExplicit = import.meta.env.VITE_MOCK_AUTH === 'true';
275
+ const noAuthConfig = !keycloak;
276
+ // Production guard: require auth config unless explicitly opted in
277
+ if (typeof window !== 'undefined' &&
278
+ isProd &&
279
+ noAuthConfig &&
280
+ !mockAuthExplicit) {
281
+ throw new Error('[AdminShell] Authentication configuration is required in production. ' +
282
+ 'Provide authClient or keycloak prop, or set VITE_MOCK_AUTH=true to explicitly opt in to mock auth.');
283
+ }
203
284
  // Check if we should use mock auth
204
285
  const useMockAuth = typeof window !== 'undefined' &&
205
- (import.meta.env.VITE_MOCK_AUTH === 'true' || !keycloak);
286
+ (mockAuthExplicit || noAuthConfig);
206
287
  if (useMockAuth) {
207
- // Create default mock users with wildcard permissions for shell development
208
- return createInMemoryAuthClient({
209
- users: [
210
- { id: 'admin-user', email: 'admin@example.com', displayName: 'Admin User', roles: ['*'] },
211
- { id: 'viewer-user', email: 'viewer@example.com', displayName: 'Viewer User', roles: [] },
288
+ if (isProd && mockAuthExplicit) {
289
+ console.warn('[AdminShell] Mock auth is active in production build (VITE_MOCK_AUTH=true). ' +
290
+ 'Use real authentication (Keycloak) for production deployments.');
291
+ }
292
+ // Default mock users with explicit roles for tasks and users modules
293
+ const defaultMockUsers = createMockUsersFromRoles({
294
+ admin: [
295
+ 'admin.tasks.view',
296
+ 'admin.tasks.edit',
297
+ 'admin.tasks.delete',
298
+ 'admin.users.view',
299
+ 'admin.users.edit',
300
+ 'admin.users.delete',
301
+ 'admin.platform.view',
302
+ 'admin.platform.edit',
303
+ 'admin.platform.delete',
304
+ ],
305
+ editor: [
306
+ 'admin.tasks.view',
307
+ 'admin.tasks.edit',
308
+ 'admin.users.view',
309
+ 'admin.users.edit',
310
+ 'admin.platform.view',
311
+ 'admin.platform.edit',
212
312
  ],
313
+ viewer: ['admin.tasks.view', 'admin.users.view', 'admin.platform.view'],
314
+ noAccess: [],
213
315
  });
316
+ return createInMemoryAuthClient({ users: defaultMockUsers, gatewayUrl: import.meta.env.VITE_ADMIN_GATEWAY_URL });
214
317
  }
215
318
  // Use Keycloak
216
319
  return createKeycloakAuthClient({
@@ -239,27 +342,30 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
239
342
  });
240
343
  }
241
344
  }, [registryClient, inMemoryRegistry, manifests]);
242
- // Fetch modules from registry client (modules load via React.lazy)
345
+ // Fetch modules from registry client with LKG caching (modules load via React.lazy)
243
346
  useEffect(() => {
244
347
  if (!registryClient) {
245
348
  setIsLoading(false);
246
349
  return;
247
350
  }
248
351
  let mounted = true;
352
+ const cached = createCachedCatalog(registryClient.catalog);
353
+ setCachedCatalogOps(cached);
249
354
  async function fetchModules() {
250
355
  try {
251
- // Fetch catalog
252
- const catalog = await registryClient.catalog.get();
356
+ const catalog = await cached.get();
253
357
  if (!mounted)
254
358
  return;
255
- // Convert catalog modules to Module type for UI
256
359
  const modules = catalog.modules.map(catalogModuleToModule);
257
360
  setApiModules(modules);
361
+ setCacheStatus(cached.getStatus());
362
+ setInitialCatalogVersion(catalog.version);
363
+ setCatalogGeneratedAt(catalog.generatedAt);
258
364
  setIsLoading(false);
259
365
  }
260
- catch (error) {
261
- console.error("[Shell] Failed to fetch modules from API:", error);
366
+ catch {
262
367
  if (mounted) {
368
+ setCacheStatus(cached.getStatus());
263
369
  setIsLoading(false);
264
370
  }
265
371
  }
@@ -275,14 +381,26 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
275
381
  }, [manifests]);
276
382
  // Use API modules if registry client is provided, otherwise use manifest modules
277
383
  const modules = registryClient ? apiModules : manifestModules;
278
- // Create catalog for command palette
384
+ // Resolve polling interval (0 = disabled)
385
+ const pollingInterval = useMemo(() => {
386
+ if (!registryClient || !initialCatalogVersion)
387
+ return 0;
388
+ return resolvePollingInterval(environment);
389
+ }, [registryClient, initialCatalogVersion, environment]);
390
+ // Poll for catalog version changes
391
+ const { hasUpdates, dismiss: dismissUpdate } = useRegistryPolling({
392
+ registryClient: registryClient,
393
+ initialVersion: initialCatalogVersion,
394
+ interval: pollingInterval,
395
+ });
396
+ // Create catalog for command palette and devtools
279
397
  const catalog = useMemo(() => {
280
398
  return {
281
- version: "1.0.0",
282
- generatedAt: new Date().toISOString(),
399
+ version: initialCatalogVersion || "1.0.0",
400
+ generatedAt: catalogGeneratedAt || new Date().toISOString(),
283
401
  modules: modules,
284
402
  };
285
- }, [modules]);
403
+ }, [modules, initialCatalogVersion, catalogGeneratedAt]);
286
404
  // Keyboard shortcut for command palette
287
405
  useEffect(() => {
288
406
  const handleKeyDown = (e) => {
@@ -297,9 +415,26 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
297
415
  const handleSearchClick = useCallback(() => {
298
416
  setCommandPaletteOpen(true);
299
417
  }, []);
418
+ const handleRetry = useCallback(async () => {
419
+ if (!cachedCatalogOps)
420
+ return;
421
+ try {
422
+ const catalog = await cachedCatalogOps.retry();
423
+ const modules = catalog.modules.map(catalogModuleToModule);
424
+ setApiModules(modules);
425
+ setCacheStatus(cachedCatalogOps.getStatus());
426
+ }
427
+ catch {
428
+ setCacheStatus(cachedCatalogOps.getStatus());
429
+ }
430
+ }, [cachedCatalogOps]);
300
431
  // Show loading state while fetching from API
301
432
  if (isLoading) {
302
433
  return (_jsx("div", { className: "flex h-screen items-center justify-center", children: _jsx("div", { className: "text-muted-foreground", children: "Loading modules..." }) }));
303
434
  }
304
- return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(AuthProvider, { authClient: authClient, children: _jsx(ShellContent, { modules: modules, environment: environment, locale: locale, onLocaleChange: handleLocaleChange, onSearchClick: handleSearchClick, catalog: catalog, commandPaletteOpen: commandPaletteOpen, onCommandPaletteChange: setCommandPaletteOpen, apiUrl: apiUrl, registryClient: registryClient, isStandaloneMode: isStandaloneMode, children: children }) }) }) }) }));
435
+ // Registry completely unavailable (no cache, no API)
436
+ if (cacheStatus.state === "unavailable") {
437
+ return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(RegistryUnavailable, { onRetry: handleRetry }) }) }) }));
438
+ }
439
+ return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(AuthProvider, { authClient: authClient, children: _jsx(ShellContent, { modules: modules, environment: environment, locale: locale, onLocaleChange: handleLocaleChange, onSearchClick: handleSearchClick, catalog: catalog, commandPaletteOpen: commandPaletteOpen, onCommandPaletteChange: setCommandPaletteOpen, apiUrl: apiUrl, registryClient: registryClient, isStandaloneMode: isStandaloneMode, cacheStatus: cacheStatus, onRetry: handleRetry, hasUpdates: hasUpdates, onDismissUpdate: dismissUpdate, localeCallbacksRef: localeCallbacksRef, timezoneMode: timezoneMode, onTimezoneToggle: handleTimezoneToggle, timezoneCallbacksRef: timezoneCallbacksRef, children: children }) }) }) }) }));
305
440
  }
@@ -35,4 +35,3 @@ export interface AdminShellProps {
35
35
  environment?: string;
36
36
  }
37
37
  export declare function AdminShell({ modules: manifests, children, keycloak, authClient: providedAuthClient, registryClient, apiUrl, inMemoryRegistry, environment, }: AdminShellProps): import("react/jsx-runtime").JSX.Element;
38
- //# sourceMappingURL=BackofficeShell.d.ts.map
@@ -12,10 +12,12 @@ import { ProfilePage } from "./components/ProfilePage";
12
12
  import { SettingsPage } from "./components/SettingsPage";
13
13
  import { RegistryPage } from "./components/RegistryPage";
14
14
  import { HomePage } from "./components/HomePage";
15
+ import { DevtoolsPanel } from "./components/DevtoolsPanel";
15
16
  import { AuthProvider, useAuthContext } from "../components/AuthProvider";
16
17
  import { createInMemoryAuthClient } from "../auth/client/in-memory";
17
18
  import { createKeycloakAuthClient } from "../auth/client/keycloak";
18
19
  import { createInMemoryRegistryClient } from "../registry/client/in-memory";
20
+ import { createCachedCatalog } from "../registry/cache/cached-catalog";
19
21
  import { DynamicModule } from "../router/DynamicModule";
20
22
  import { SidebarProvider, SidebarInset } from "@nsxbet/admin-ui";
21
23
  import { initTelemetry, track, trackError } from "./telemetry";
@@ -27,9 +29,7 @@ function manifestToModule(manifest, baseUrl) {
27
29
  return {
28
30
  id: manifest.id,
29
31
  title: manifest.title,
30
- titleKey: manifest.titleKey,
31
- description: manifest.description || "",
32
- descriptionKey: manifest.descriptionKey,
32
+ description: manifest.description,
33
33
  category: manifest.category || "Modules",
34
34
  routeBase: manifest.routeBase,
35
35
  baseUrl: baseUrl || "",
@@ -42,16 +42,23 @@ function manifestToModule(manifest, baseUrl) {
42
42
  team: manifest.owners?.team || "Platform",
43
43
  supportChannel: manifest.owners?.supportChannel || "",
44
44
  },
45
- status: manifest.status || "active",
46
- navOrder: manifest.navOrder,
45
+ status: "active",
46
+ navigationOrder: manifest.navigationOrder,
47
47
  icon: manifest.icon,
48
+ navigation: manifest.navigation ? {
49
+ style: manifest.navigation.style,
50
+ sections: manifest.navigation.sections?.map(s => ({
51
+ id: s.id,
52
+ label: s.label,
53
+ })),
54
+ } : undefined,
48
55
  commands: manifest.commands?.map((cmd) => ({
49
56
  id: cmd.id,
50
57
  title: cmd.title,
51
- titleKey: cmd.titleKey,
52
58
  route: cmd.route,
53
59
  icon: cmd.icon,
54
60
  keywords: cmd.keywords,
61
+ section: cmd.section,
55
62
  })),
56
63
  };
57
64
  }
@@ -62,9 +69,7 @@ function catalogModuleToModule(m) {
62
69
  return {
63
70
  id: m.id,
64
71
  title: m.title,
65
- titleKey: m.titleKey,
66
72
  description: m.description,
67
- descriptionKey: m.descriptionKey,
68
73
  category: m.category,
69
74
  routeBase: m.routeBase,
70
75
  baseUrl: m.baseUrl,
@@ -72,18 +77,29 @@ function catalogModuleToModule(m) {
72
77
  permissions: m.permissions,
73
78
  owners: m.owners,
74
79
  status: m.status,
75
- navOrder: m.navOrder,
80
+ navigationOrder: m.navigationOrder,
76
81
  icon: m.icon,
82
+ navigation: m.navigation ? {
83
+ style: m.navigation.style,
84
+ sections: m.navigation.sections?.map(s => ({
85
+ id: s.id,
86
+ label: s.title,
87
+ })),
88
+ } : undefined,
77
89
  commands: m.commands?.map(cmd => ({
78
- ...cmd,
79
- titleKey: cmd.titleKey,
90
+ id: cmd.id,
91
+ title: cmd.title,
92
+ route: cmd.route,
93
+ icon: cmd.icon,
94
+ keywords: cmd.keywords,
95
+ section: cmd.section,
80
96
  })),
81
97
  };
82
98
  }
83
99
  /**
84
100
  * Inner shell component that has access to React Router hooks
85
101
  */
86
- function ShellContent({ modules, children, environment, locale, onLocaleChange, onSearchClick, catalog, commandPaletteOpen, onCommandPaletteChange, apiUrl, registryClient, isStandaloneMode, }) {
102
+ function ShellContent({ modules, children, environment, locale, onLocaleChange, onSearchClick, catalog, commandPaletteOpen, onCommandPaletteChange, apiUrl, registryClient, isStandaloneMode, cacheStatus, }) {
87
103
  const navigate = useNavigate();
88
104
  const auth = useAuthContext();
89
105
  // Set up the platform API for modules to use
@@ -94,7 +110,7 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
94
110
  auth: {
95
111
  getAccessToken: auth.getAccessToken,
96
112
  hasPermission: auth.hasPermission,
97
- getUser: () => auth.user || { id: "", email: "", displayName: "" },
113
+ getUser: () => auth.user || { id: "", email: "", displayName: "", roles: [] },
98
114
  logout: () => {
99
115
  auth.logout();
100
116
  // Navigate to root after logout (for Keycloak, this will redirect)
@@ -113,9 +129,16 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
113
129
  locale: locale,
114
130
  setLocale: onLocaleChange,
115
131
  onLocaleChange: (callback) => {
116
- // Simple implementation - just call callback immediately with current locale
117
132
  callback(locale);
118
- return () => { }; // Return unsubscribe function
133
+ return () => { };
134
+ },
135
+ },
136
+ timestamp: {
137
+ mode: "local",
138
+ setMode: () => { },
139
+ onModeChange: (callback) => {
140
+ callback("local");
141
+ return () => { };
119
142
  },
120
143
  },
121
144
  telemetry: {
@@ -164,7 +187,11 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
164
187
  ? // Standalone mode: render children (module is imported directly)
165
188
  modules.map((module) => (_jsx(Route, { path: `${module.routeBase}/*`, element: _jsx(MainContent, { modules: modules, children: children }) }, module.id)))
166
189
  : // Shell mode: load modules dynamically via React.lazy
167
- modules.map((module) => (_jsx(Route, { path: `${module.routeBase}/*`, element: _jsx(MainContent, { modules: modules, children: module.baseUrl ? (_jsx(DynamicModule, { baseUrl: module.baseUrl })) : (_jsxs("div", { className: "text-muted-foreground", children: ["Module ", module.id, " has no baseUrl configured"] })) }) }, module.id))), _jsx(Route, { path: "*", element: _jsx(MainContent, { modules: modules, children: isStandaloneMode ? children : null }) })] })] })] }), _jsx(CommandPalette, { open: commandPaletteOpen, onOpenChange: onCommandPaletteChange, catalog: catalog })] }));
190
+ modules.map((module) => (_jsx(Route, { path: `${module.routeBase}/*`, element: _jsx(MainContent, { modules: modules, children: module.baseUrl ? (_jsx(DynamicModule, { baseUrl: module.baseUrl, moduleInfo: {
191
+ id: module.id,
192
+ title: module.title,
193
+ owners: module.owners,
194
+ } })) : (_jsxs("div", { className: "text-muted-foreground", children: ["Module ", module.id, " has no baseUrl configured"] })) }) }, module.id))), _jsx(Route, { path: "*", element: _jsx(MainContent, { modules: modules, children: isStandaloneMode ? children : null }) })] })] })] }), _jsx(CommandPalette, { open: commandPaletteOpen, onOpenChange: onCommandPaletteChange, catalog: catalog }), _jsx(DevtoolsPanel, { environment: environment, modules: modules, catalogVersion: catalog.version, catalogGeneratedAt: catalog.generatedAt, registryMode: registryClient ? "api" : "in-memory", cacheStatus: cacheStatus })] }));
168
195
  }
169
196
  export function AdminShell({ modules: manifests = [], children, keycloak, authClient: providedAuthClient, registryClient, apiUrl, inMemoryRegistry = true, environment = "local", }) {
170
197
  const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
@@ -175,6 +202,9 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
175
202
  });
176
203
  const [apiModules, setApiModules] = useState([]);
177
204
  const [isLoading, setIsLoading] = useState(!!registryClient);
205
+ const [cacheStatus, setCacheStatus] = useState({ state: 'fresh' });
206
+ const [catalogGeneratedAt, setCatalogGeneratedAt] = useState("");
207
+ const [catalogVersion, setCatalogVersion] = useState("");
178
208
  // Handle locale change - update both state and i18next
179
209
  const handleLocaleChange = useCallback((newLocale) => {
180
210
  if (isSupportedLocale(newLocale)) {
@@ -210,6 +240,7 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
210
240
  { id: 'admin-user', email: 'admin@example.com', displayName: 'Admin User', roles: ['*'] },
211
241
  { id: 'viewer-user', email: 'viewer@example.com', displayName: 'Viewer User', roles: [] },
212
242
  ],
243
+ gatewayUrl: import.meta.env.VITE_ADMIN_GATEWAY_URL,
213
244
  });
214
245
  }
215
246
  // Use Keycloak
@@ -239,27 +270,30 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
239
270
  });
240
271
  }
241
272
  }, [registryClient, inMemoryRegistry, manifests]);
242
- // Fetch modules from registry client (modules load via React.lazy)
273
+ // Fetch modules from registry client with LKG caching (modules load via React.lazy)
243
274
  useEffect(() => {
244
275
  if (!registryClient) {
245
276
  setIsLoading(false);
246
277
  return;
247
278
  }
248
279
  let mounted = true;
280
+ const cached = createCachedCatalog(registryClient.catalog);
249
281
  async function fetchModules() {
250
282
  try {
251
- // Fetch catalog
252
- const catalog = await registryClient.catalog.get();
283
+ const catalog = await cached.get();
253
284
  if (!mounted)
254
285
  return;
255
- // Convert catalog modules to Module type for UI
256
286
  const modules = catalog.modules.map(catalogModuleToModule);
257
287
  setApiModules(modules);
288
+ setCacheStatus(cached.getStatus());
289
+ setCatalogVersion(catalog.version);
290
+ setCatalogGeneratedAt(catalog.generatedAt);
258
291
  setIsLoading(false);
259
292
  }
260
293
  catch (error) {
261
294
  console.error("[Shell] Failed to fetch modules from API:", error);
262
295
  if (mounted) {
296
+ setCacheStatus(cached.getStatus());
263
297
  setIsLoading(false);
264
298
  }
265
299
  }
@@ -275,14 +309,14 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
275
309
  }, [manifests]);
276
310
  // Use API modules if registry client is provided, otherwise use manifest modules
277
311
  const modules = registryClient ? apiModules : manifestModules;
278
- // Create catalog for command palette
312
+ // Create catalog for command palette and devtools
279
313
  const catalog = useMemo(() => {
280
314
  return {
281
- version: "1.0.0",
282
- generatedAt: new Date().toISOString(),
315
+ version: catalogVersion || "1.0.0",
316
+ generatedAt: catalogGeneratedAt || new Date().toISOString(),
283
317
  modules: modules,
284
318
  };
285
- }, [modules]);
319
+ }, [modules, catalogVersion, catalogGeneratedAt]);
286
320
  // Keyboard shortcut for command palette
287
321
  useEffect(() => {
288
322
  const handleKeyDown = (e) => {
@@ -301,5 +335,5 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
301
335
  if (isLoading) {
302
336
  return (_jsx("div", { className: "flex h-screen items-center justify-center", children: _jsx("div", { className: "text-muted-foreground", children: "Loading modules..." }) }));
303
337
  }
304
- return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(AuthProvider, { authClient: authClient, children: _jsx(ShellContent, { modules: modules, environment: environment, locale: locale, onLocaleChange: handleLocaleChange, onSearchClick: handleSearchClick, catalog: catalog, commandPaletteOpen: commandPaletteOpen, onCommandPaletteChange: setCommandPaletteOpen, apiUrl: apiUrl, registryClient: registryClient, isStandaloneMode: isStandaloneMode, children: children }) }) }) }) }));
338
+ return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(AuthProvider, { authClient: authClient, children: _jsx(ShellContent, { modules: modules, environment: environment, locale: locale, onLocaleChange: handleLocaleChange, onSearchClick: handleSearchClick, catalog: catalog, commandPaletteOpen: commandPaletteOpen, onCommandPaletteChange: setCommandPaletteOpen, apiUrl: apiUrl, registryClient: registryClient, isStandaloneMode: isStandaloneMode, cacheStatus: cacheStatus, children: children }) }) }) }) }));
305
339
  }
@@ -5,4 +5,3 @@ export interface CommandPaletteProps {
5
5
  catalog: Catalog | null;
6
6
  }
7
7
  export declare function CommandPalette({ open, onOpenChange, catalog, }: CommandPaletteProps): import("react/jsx-runtime").JSX.Element;
8
- //# sourceMappingURL=CommandPalette.d.ts.map