@digitaldefiance/express-suite-react-components 2.9.37 → 2.9.38

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 (257) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +4 -5
  3. package/src/auth/Private.tsx +17 -0
  4. package/src/auth/PrivateRoute.tsx +28 -0
  5. package/src/auth/UnAuth.tsx +16 -0
  6. package/src/auth/UnAuthRoute.tsx +30 -0
  7. package/src/auth/{index.d.ts → index.ts} +1 -2
  8. package/src/components/ApiAccess.tsx +174 -0
  9. package/src/components/BackupCodeLoginForm.tsx +488 -0
  10. package/src/components/BackupCodesForm.tsx +286 -0
  11. package/src/components/ChangePasswordForm.tsx +272 -0
  12. package/src/components/ConfirmationDialog.tsx +48 -0
  13. package/src/components/CurrencyCodeSelector.tsx +60 -0
  14. package/src/components/CurrencyInput.tsx +80 -0
  15. package/src/components/DashboardPage.tsx +24 -0
  16. package/src/components/DropdownMenu.tsx +92 -0
  17. package/src/components/ExpirationSecondsSelector.tsx +60 -0
  18. package/src/components/Flag.tsx +52 -0
  19. package/src/components/ForgotPasswordForm.tsx +173 -0
  20. package/src/components/LoginForm.tsx +455 -0
  21. package/src/components/LogoutPage.tsx +21 -0
  22. package/src/components/RegisterForm.tsx +602 -0
  23. package/src/components/ResetPasswordForm.tsx +246 -0
  24. package/src/components/SideMenu.tsx +46 -0
  25. package/src/components/SideMenuListItem.tsx +74 -0
  26. package/src/components/TopMenu.tsx +145 -0
  27. package/src/components/TranslatedTitle.tsx +29 -0
  28. package/src/components/UserLanguageSelector.tsx +45 -0
  29. package/src/components/UserMenu.tsx +15 -0
  30. package/src/components/UserSettingsForm.tsx +505 -0
  31. package/src/components/VerifyEmailPage.tsx +184 -0
  32. package/src/components/{index.d.ts → index.ts} +1 -1
  33. package/src/contexts/AuthProvider.spec.tsx +1195 -0
  34. package/src/contexts/AuthProvider.tsx +924 -0
  35. package/src/contexts/I18nProvider.tsx +114 -0
  36. package/src/contexts/MenuContext.tsx +398 -0
  37. package/src/contexts/SuiteConfigProvider.tsx +93 -0
  38. package/src/contexts/ThemeProvider.tsx +67 -0
  39. package/src/contexts/{index.d.ts → index.ts} +0 -1
  40. package/src/hooks/{index.d.ts → index.ts} +0 -1
  41. package/src/hooks/useBackupCodes.ts +105 -0
  42. package/src/hooks/useEmailVerification.ts +49 -0
  43. package/src/hooks/useExpiringValue.ts +78 -0
  44. package/src/hooks/useLocalStorage.ts +18 -0
  45. package/src/hooks/useUserSettings.ts +269 -0
  46. package/src/{index.d.ts → index.ts} +1 -1
  47. package/src/interfaces/IAppConfig.ts +5 -0
  48. package/src/interfaces/IMenuConfig.ts +11 -0
  49. package/src/interfaces/IMenuOption.ts +55 -0
  50. package/src/interfaces/index.ts +3 -0
  51. package/src/services/__mocks__/authService.ts +14 -0
  52. package/src/services/api.ts +13 -0
  53. package/src/services/authService.ts +500 -0
  54. package/src/services/authenticatedApi.ts +17 -0
  55. package/src/services/index.ts +3 -0
  56. package/src/types/MenuType.ts +15 -0
  57. package/src/types/expirationSeconds.ts +18 -0
  58. package/src/types/index.ts +1 -0
  59. package/src/types/translation.ts +20 -0
  60. package/src/wrappers/BackupCodeLoginWrapper.tsx +34 -0
  61. package/src/wrappers/BackupCodesWrapper.tsx +28 -0
  62. package/src/wrappers/ChangePasswordFormWrapper.tsx +34 -0
  63. package/src/wrappers/LoginFormWrapper.tsx +59 -0
  64. package/src/wrappers/LogoutPageWrapper.tsx +30 -0
  65. package/src/wrappers/RegisterFormWrapper.tsx +61 -0
  66. package/src/wrappers/UserSettingsFormWrapper.tsx +39 -0
  67. package/src/wrappers/VerifyEmailPageWrapper.tsx +27 -0
  68. package/src/wrappers/{index.d.ts → index.tsx} +8 -1
  69. package/src/auth/Private.d.ts +0 -6
  70. package/src/auth/Private.d.ts.map +0 -1
  71. package/src/auth/Private.js +0 -14
  72. package/src/auth/PrivateRoute.d.ts +0 -8
  73. package/src/auth/PrivateRoute.d.ts.map +0 -1
  74. package/src/auth/PrivateRoute.js +0 -23
  75. package/src/auth/UnAuth.d.ts +0 -6
  76. package/src/auth/UnAuth.d.ts.map +0 -1
  77. package/src/auth/UnAuth.js +0 -14
  78. package/src/auth/UnAuthRoute.d.ts +0 -8
  79. package/src/auth/UnAuthRoute.d.ts.map +0 -1
  80. package/src/auth/UnAuthRoute.js +0 -22
  81. package/src/auth/index.d.ts.map +0 -1
  82. package/src/auth/index.js +0 -10
  83. package/src/components/ApiAccess.d.ts +0 -16
  84. package/src/components/ApiAccess.d.ts.map +0 -1
  85. package/src/components/ApiAccess.js +0 -77
  86. package/src/components/BackupCodeLoginForm.d.ts +0 -43
  87. package/src/components/BackupCodeLoginForm.d.ts.map +0 -1
  88. package/src/components/BackupCodeLoginForm.js +0 -139
  89. package/src/components/BackupCodesForm.d.ts +0 -26
  90. package/src/components/BackupCodesForm.d.ts.map +0 -1
  91. package/src/components/BackupCodesForm.js +0 -120
  92. package/src/components/ChangePasswordForm.d.ts +0 -26
  93. package/src/components/ChangePasswordForm.d.ts.map +0 -1
  94. package/src/components/ChangePasswordForm.js +0 -78
  95. package/src/components/ConfirmationDialog.d.ts +0 -13
  96. package/src/components/ConfirmationDialog.d.ts.map +0 -1
  97. package/src/components/ConfirmationDialog.js +0 -10
  98. package/src/components/CurrencyCodeSelector.d.ts +0 -9
  99. package/src/components/CurrencyCodeSelector.d.ts.map +0 -1
  100. package/src/components/CurrencyCodeSelector.js +0 -31
  101. package/src/components/CurrencyInput.d.ts +0 -13
  102. package/src/components/CurrencyInput.d.ts.map +0 -1
  103. package/src/components/CurrencyInput.js +0 -22
  104. package/src/components/DashboardPage.d.ts +0 -8
  105. package/src/components/DashboardPage.d.ts.map +0 -1
  106. package/src/components/DashboardPage.js +0 -10
  107. package/src/components/DropdownMenu.d.ts +0 -9
  108. package/src/components/DropdownMenu.d.ts.map +0 -1
  109. package/src/components/DropdownMenu.js +0 -56
  110. package/src/components/ExpirationSecondsSelector.d.ts +0 -13
  111. package/src/components/ExpirationSecondsSelector.d.ts.map +0 -1
  112. package/src/components/ExpirationSecondsSelector.js +0 -32
  113. package/src/components/Flag.d.ts +0 -20
  114. package/src/components/Flag.d.ts.map +0 -1
  115. package/src/components/Flag.js +0 -43
  116. package/src/components/ForgotPasswordForm.d.ts +0 -18
  117. package/src/components/ForgotPasswordForm.d.ts.map +0 -1
  118. package/src/components/ForgotPasswordForm.js +0 -61
  119. package/src/components/LoginForm.d.ts +0 -44
  120. package/src/components/LoginForm.d.ts.map +0 -1
  121. package/src/components/LoginForm.js +0 -122
  122. package/src/components/LogoutPage.d.ts +0 -8
  123. package/src/components/LogoutPage.d.ts.map +0 -1
  124. package/src/components/LogoutPage.js +0 -16
  125. package/src/components/RegisterForm.d.ts +0 -56
  126. package/src/components/RegisterForm.d.ts.map +0 -1
  127. package/src/components/RegisterForm.js +0 -140
  128. package/src/components/ResetPasswordForm.d.ts +0 -23
  129. package/src/components/ResetPasswordForm.d.ts.map +0 -1
  130. package/src/components/ResetPasswordForm.js +0 -78
  131. package/src/components/SideMenu.d.ts +0 -8
  132. package/src/components/SideMenu.d.ts.map +0 -1
  133. package/src/components/SideMenu.js +0 -25
  134. package/src/components/SideMenuListItem.d.ts +0 -13
  135. package/src/components/SideMenuListItem.d.ts.map +0 -1
  136. package/src/components/SideMenuListItem.js +0 -44
  137. package/src/components/TopMenu.d.ts +0 -24
  138. package/src/components/TopMenu.d.ts.map +0 -1
  139. package/src/components/TopMenu.js +0 -35
  140. package/src/components/TranslatedTitle.d.ts +0 -7
  141. package/src/components/TranslatedTitle.d.ts.map +0 -1
  142. package/src/components/TranslatedTitle.js +0 -15
  143. package/src/components/UserLanguageSelector.d.ts +0 -4
  144. package/src/components/UserLanguageSelector.d.ts.map +0 -1
  145. package/src/components/UserLanguageSelector.js +0 -31
  146. package/src/components/UserMenu.d.ts +0 -4
  147. package/src/components/UserMenu.d.ts.map +0 -1
  148. package/src/components/UserMenu.js +0 -12
  149. package/src/components/UserSettingsForm.d.ts +0 -57
  150. package/src/components/UserSettingsForm.d.ts.map +0 -1
  151. package/src/components/UserSettingsForm.js +0 -126
  152. package/src/components/VerifyEmailPage.d.ts +0 -23
  153. package/src/components/VerifyEmailPage.d.ts.map +0 -1
  154. package/src/components/VerifyEmailPage.js +0 -70
  155. package/src/components/index.d.ts.map +0 -1
  156. package/src/components/index.js +0 -28
  157. package/src/contexts/AuthProvider.d.ts +0 -152
  158. package/src/contexts/AuthProvider.d.ts.map +0 -1
  159. package/src/contexts/AuthProvider.js +0 -502
  160. package/src/contexts/I18nProvider.d.ts +0 -16
  161. package/src/contexts/I18nProvider.d.ts.map +0 -1
  162. package/src/contexts/I18nProvider.js +0 -46
  163. package/src/contexts/MenuContext.d.ts +0 -20
  164. package/src/contexts/MenuContext.d.ts.map +0 -1
  165. package/src/contexts/MenuContext.js +0 -273
  166. package/src/contexts/SuiteConfigProvider.d.ts +0 -44
  167. package/src/contexts/SuiteConfigProvider.d.ts.map +0 -1
  168. package/src/contexts/SuiteConfigProvider.js +0 -43
  169. package/src/contexts/ThemeProvider.d.ts +0 -15
  170. package/src/contexts/ThemeProvider.d.ts.map +0 -1
  171. package/src/contexts/ThemeProvider.js +0 -36
  172. package/src/contexts/index.d.ts.map +0 -1
  173. package/src/contexts/index.js +0 -8
  174. package/src/hooks/index.d.ts.map +0 -1
  175. package/src/hooks/index.js +0 -8
  176. package/src/hooks/useBackupCodes.d.ts +0 -15
  177. package/src/hooks/useBackupCodes.d.ts.map +0 -1
  178. package/src/hooks/useBackupCodes.js +0 -74
  179. package/src/hooks/useEmailVerification.d.ts +0 -10
  180. package/src/hooks/useEmailVerification.d.ts.map +0 -1
  181. package/src/hooks/useEmailVerification.js +0 -40
  182. package/src/hooks/useExpiringValue.d.ts +0 -14
  183. package/src/hooks/useExpiringValue.d.ts.map +0 -1
  184. package/src/hooks/useExpiringValue.js +0 -53
  185. package/src/hooks/useLocalStorage.d.ts +0 -2
  186. package/src/hooks/useLocalStorage.d.ts.map +0 -1
  187. package/src/hooks/useLocalStorage.js +0 -15
  188. package/src/hooks/useUserSettings.d.ts +0 -48
  189. package/src/hooks/useUserSettings.d.ts.map +0 -1
  190. package/src/hooks/useUserSettings.js +0 -169
  191. package/src/index.d.ts.map +0 -1
  192. package/src/index.js +0 -12
  193. package/src/interfaces/IAppConfig.d.ts +0 -6
  194. package/src/interfaces/IAppConfig.d.ts.map +0 -1
  195. package/src/interfaces/IAppConfig.js +0 -2
  196. package/src/interfaces/IMenuConfig.d.ts +0 -11
  197. package/src/interfaces/IMenuConfig.d.ts.map +0 -1
  198. package/src/interfaces/IMenuConfig.js +0 -2
  199. package/src/interfaces/IMenuOption.d.ts +0 -58
  200. package/src/interfaces/IMenuOption.d.ts.map +0 -1
  201. package/src/interfaces/IMenuOption.js +0 -2
  202. package/src/interfaces/index.d.ts +0 -4
  203. package/src/interfaces/index.d.ts.map +0 -1
  204. package/src/interfaces/index.js +0 -6
  205. package/src/services/__mocks__/authService.d.ts +0 -21
  206. package/src/services/__mocks__/authService.d.ts.map +0 -1
  207. package/src/services/__mocks__/authService.js +0 -15
  208. package/src/services/api.d.ts +0 -3
  209. package/src/services/api.d.ts.map +0 -1
  210. package/src/services/api.js +0 -14
  211. package/src/services/authService.d.ts +0 -72
  212. package/src/services/authService.d.ts.map +0 -1
  213. package/src/services/authService.js +0 -335
  214. package/src/services/authenticatedApi.d.ts +0 -3
  215. package/src/services/authenticatedApi.d.ts.map +0 -1
  216. package/src/services/authenticatedApi.js +0 -18
  217. package/src/services/index.d.ts +0 -4
  218. package/src/services/index.d.ts.map +0 -1
  219. package/src/services/index.js +0 -6
  220. package/src/types/MenuType.d.ts +0 -11
  221. package/src/types/MenuType.d.ts.map +0 -1
  222. package/src/types/MenuType.js +0 -12
  223. package/src/types/expirationSeconds.d.ts +0 -3
  224. package/src/types/expirationSeconds.d.ts.map +0 -1
  225. package/src/types/expirationSeconds.js +0 -17
  226. package/src/types/index.d.ts +0 -2
  227. package/src/types/index.d.ts.map +0 -1
  228. package/src/types/index.js +0 -4
  229. package/src/types/translation.d.ts +0 -10
  230. package/src/types/translation.d.ts.map +0 -1
  231. package/src/types/translation.js +0 -9
  232. package/src/wrappers/BackupCodeLoginWrapper.d.ts +0 -8
  233. package/src/wrappers/BackupCodeLoginWrapper.d.ts.map +0 -1
  234. package/src/wrappers/BackupCodeLoginWrapper.js +0 -20
  235. package/src/wrappers/BackupCodesWrapper.d.ts +0 -7
  236. package/src/wrappers/BackupCodesWrapper.d.ts.map +0 -1
  237. package/src/wrappers/BackupCodesWrapper.js +0 -17
  238. package/src/wrappers/ChangePasswordFormWrapper.d.ts +0 -8
  239. package/src/wrappers/ChangePasswordFormWrapper.d.ts.map +0 -1
  240. package/src/wrappers/ChangePasswordFormWrapper.js +0 -21
  241. package/src/wrappers/LoginFormWrapper.d.ts +0 -9
  242. package/src/wrappers/LoginFormWrapper.d.ts.map +0 -1
  243. package/src/wrappers/LoginFormWrapper.js +0 -43
  244. package/src/wrappers/LogoutPageWrapper.d.ts +0 -9
  245. package/src/wrappers/LogoutPageWrapper.d.ts.map +0 -1
  246. package/src/wrappers/LogoutPageWrapper.js +0 -21
  247. package/src/wrappers/RegisterFormWrapper.d.ts +0 -9
  248. package/src/wrappers/RegisterFormWrapper.d.ts.map +0 -1
  249. package/src/wrappers/RegisterFormWrapper.js +0 -31
  250. package/src/wrappers/UserSettingsFormWrapper.d.ts +0 -8
  251. package/src/wrappers/UserSettingsFormWrapper.d.ts.map +0 -1
  252. package/src/wrappers/UserSettingsFormWrapper.js +0 -24
  253. package/src/wrappers/VerifyEmailPageWrapper.d.ts +0 -8
  254. package/src/wrappers/VerifyEmailPageWrapper.d.ts.map +0 -1
  255. package/src/wrappers/VerifyEmailPageWrapper.js +0 -20
  256. package/src/wrappers/index.d.ts.map +0 -1
  257. package/src/wrappers/index.js +0 -20
@@ -0,0 +1,114 @@
1
+ import {
2
+ GlobalActiveContext,
3
+ I18nEngine,
4
+ IActiveContext,
5
+ LanguageRegistry,
6
+ } from '@digitaldefiance/i18n-lib';
7
+ import {
8
+ createContext,
9
+ FC,
10
+ ReactNode,
11
+ useCallback,
12
+ useContext,
13
+ useState,
14
+ } from 'react';
15
+
16
+ export interface I18nProviderProps {
17
+ children: ReactNode;
18
+ i18nEngine: I18nEngine;
19
+ onLanguageChange?: (language: string) => Promise<void>;
20
+ }
21
+
22
+ export interface I18nContextType {
23
+ t: (
24
+ key: string,
25
+ vars?: Record<string, string | number>,
26
+ language?: string
27
+ ) => string;
28
+ tComponent: <TStringKey extends string>(
29
+ componentId: string,
30
+ stringKey: TStringKey,
31
+ vars?: Record<string, string | number>,
32
+ language?: string
33
+ ) => string;
34
+ changeLanguage: (language: string) => void;
35
+ currentLanguage: string;
36
+ }
37
+
38
+ const I18nContext = createContext<I18nContextType | undefined>(undefined);
39
+
40
+ export const I18nProvider: FC<I18nProviderProps> = ({
41
+ children,
42
+ i18nEngine,
43
+ onLanguageChange,
44
+ }) => {
45
+ const context = GlobalActiveContext.getInstance<
46
+ string,
47
+ IActiveContext<string>
48
+ >();
49
+ const [currentLanguage, setCurrentLanguage] = useState<string>(
50
+ context.userLanguage
51
+ );
52
+
53
+ const changeLanguage = useCallback(
54
+ async (language: string) => {
55
+ const languageDetails = LanguageRegistry.getLanguageByCode(language);
56
+ if (language && languageDetails) {
57
+ context.userLanguage = language;
58
+ i18nEngine.setLanguage(language);
59
+ localStorage.setItem('language', languageDetails.name);
60
+ localStorage.setItem('languageCode', language);
61
+ setCurrentLanguage(language);
62
+ if (onLanguageChange) {
63
+ await onLanguageChange(language);
64
+ }
65
+ }
66
+ },
67
+ [onLanguageChange, i18nEngine, context]
68
+ );
69
+
70
+ const t = useCallback(
71
+ (
72
+ key: string,
73
+ vars?: Record<string, string | number>,
74
+ language?: string
75
+ ) => {
76
+ return i18nEngine.t(key, vars, language ?? currentLanguage);
77
+ },
78
+ [i18nEngine, currentLanguage]
79
+ );
80
+
81
+ const tComponent = useCallback(
82
+ <TStringKey extends string>(
83
+ componentId: string,
84
+ stringKey: TStringKey,
85
+ vars?: Record<string, string | number>,
86
+ language?: string
87
+ ): string => {
88
+ return i18nEngine.translate(
89
+ componentId,
90
+ stringKey,
91
+ vars,
92
+ language ?? currentLanguage
93
+ );
94
+ },
95
+ [currentLanguage, i18nEngine]
96
+ );
97
+
98
+ const value = {
99
+ t,
100
+ tComponent,
101
+ changeLanguage,
102
+ currentLanguage,
103
+ };
104
+
105
+ return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
106
+ };
107
+
108
+ export const useI18n = () => {
109
+ const context = useContext(I18nContext);
110
+ if (context === undefined) {
111
+ throw new Error('useI18n must be used within an I18nProvider');
112
+ }
113
+ return context;
114
+ };
@@ -0,0 +1,398 @@
1
+ // src/app/menuContext.tsx
2
+ import {
3
+ IRoleDTO,
4
+ SuiteCoreComponentId,
5
+ SuiteCoreStringKey,
6
+ } from '@digitaldefiance/suite-core-lib';
7
+ import {
8
+ AccountCircle,
9
+ Autorenew as AutorenewIcon,
10
+ Brightness4,
11
+ Brightness7,
12
+ Dashboard as DashboardIcon,
13
+ Key as KeyIcon,
14
+ LockOpen as LockOpenIcon,
15
+ LockReset as LockResetIcon,
16
+ Login as LoginIcon,
17
+ ExitToApp as LogoutIcon,
18
+ PersonAdd as PersonAddIcon,
19
+ Settings,
20
+ } from '@mui/icons-material';
21
+ import {
22
+ FC,
23
+ ReactNode,
24
+ createContext,
25
+ useCallback,
26
+ useContext,
27
+ useEffect,
28
+ useMemo,
29
+ useRef,
30
+ useState,
31
+ } from 'react';
32
+ import { useUserSettings } from '../hooks';
33
+ import { IMenuConfig } from '../interfaces/IMenuConfig';
34
+ import { IMenuOption } from '../interfaces/IMenuOption';
35
+ import { createAuthenticatedApiClient } from '../services';
36
+ import { MenuType, MenuTypes } from '../types/MenuType';
37
+ import { useAuth } from './AuthProvider';
38
+ import { useI18n } from './I18nProvider';
39
+ import { useSuiteConfig } from './SuiteConfigProvider';
40
+ import { useTheme } from './ThemeProvider';
41
+
42
+ interface MenuProviderProps {
43
+ children: ReactNode;
44
+ menuConfigs?: IMenuConfig[];
45
+ enableBackupCodes?: boolean;
46
+ }
47
+
48
+ interface MenuContextType {
49
+ menuOptions: IMenuOption[];
50
+ getMenuOptions: (
51
+ menuType: MenuType,
52
+ includeDividers: boolean
53
+ ) => IMenuOption[];
54
+ registerMenuOption: (option: IMenuOption) => () => void;
55
+ registerMenuOptions: (options: IMenuOption[]) => () => void;
56
+ getTopMenus: () => Array<IMenuConfig>;
57
+ }
58
+
59
+ const MenuContext = createContext<MenuContextType | undefined>(undefined);
60
+
61
+ export const MenuProvider: FC<MenuProviderProps> = ({
62
+ children,
63
+ menuConfigs = [],
64
+ enableBackupCodes = true,
65
+ }) => {
66
+ const {
67
+ userData: user,
68
+ isAuthenticated,
69
+ mnemonic,
70
+ clearMnemonic,
71
+ wallet,
72
+ clearWallet,
73
+ } = useAuth();
74
+ const { mode: colorMode } = useTheme();
75
+ const registeredMenuOptions = useRef(new Set<() => void>());
76
+ const [registeredOptions, setRegisteredOptions] = useState<
77
+ Map<string, IMenuOption>
78
+ >(new Map<string, IMenuOption>());
79
+ const { tComponent } = useI18n();
80
+ const { baseUrl } = useSuiteConfig();
81
+ const authenticatedApi = useMemo(
82
+ () => createAuthenticatedApiClient(baseUrl),
83
+ [baseUrl]
84
+ );
85
+ const { toggleColorMode } = useUserSettings({
86
+ authenticatedApi,
87
+ isAuthenticated,
88
+ });
89
+
90
+ const registerMenuOption = useCallback((option: IMenuOption) => {
91
+ const unregister = () => {
92
+ setRegisteredOptions((prev) => {
93
+ const newMap = new Map(prev);
94
+ newMap.delete(option.id);
95
+ return newMap;
96
+ });
97
+ registeredMenuOptions.current.delete(unregister);
98
+ };
99
+
100
+ setRegisteredOptions((prev) => {
101
+ const newMap = new Map(prev);
102
+ newMap.set(option.id, option);
103
+ return newMap;
104
+ });
105
+ registeredMenuOptions.current.add(unregister);
106
+
107
+ return unregister;
108
+ }, []);
109
+
110
+ const registerMenuOptions = useCallback(
111
+ (options: IMenuOption[]) => {
112
+ const unregisterFunctions = options.map(registerMenuOption);
113
+ return () => unregisterFunctions.forEach((f) => f());
114
+ },
115
+ [registerMenuOption]
116
+ );
117
+
118
+ const menuOptions = useMemo(() => {
119
+ const isUserRestricted = () => {
120
+ return user?.roles?.some((role: IRoleDTO) => role.child) ?? false;
121
+ };
122
+ let index = 0;
123
+ const baseOptions: IMenuOption[] = [
124
+ {
125
+ id: 'dashboard',
126
+ label: tComponent<SuiteCoreStringKey>(
127
+ SuiteCoreComponentId,
128
+ SuiteCoreStringKey.Common_Dashboard
129
+ ),
130
+ icon: <DashboardIcon />,
131
+ link: '/dashboard',
132
+ requiresAuth: true,
133
+ includeOnMenus: [MenuTypes.SideMenu],
134
+ index: index++,
135
+ },
136
+ {
137
+ id: 'user-divider',
138
+ label: '',
139
+ divider: true,
140
+ includeOnMenus: [MenuTypes.SideMenu],
141
+ index: index++,
142
+ requiresAuth: false,
143
+ },
144
+ {
145
+ id: 'logout',
146
+ label: tComponent<SuiteCoreStringKey>(
147
+ SuiteCoreComponentId,
148
+ SuiteCoreStringKey.LogoutButton
149
+ ),
150
+ icon: <LogoutIcon />,
151
+ link: '/logout',
152
+ requiresAuth: true,
153
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
154
+ index: index++,
155
+ },
156
+ {
157
+ id: 'login',
158
+ label: tComponent<SuiteCoreStringKey>(
159
+ SuiteCoreComponentId,
160
+ SuiteCoreStringKey.Login_LoginButton
161
+ ),
162
+ icon: <LoginIcon />,
163
+ link: '/login',
164
+ requiresAuth: false,
165
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
166
+ index: index++,
167
+ },
168
+ {
169
+ id: 'register',
170
+ label: tComponent<SuiteCoreStringKey>(
171
+ SuiteCoreComponentId,
172
+ SuiteCoreStringKey.RegisterButton
173
+ ),
174
+ icon: <PersonAddIcon />,
175
+ link: '/register',
176
+ requiresAuth: false,
177
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
178
+ index: index++,
179
+ },
180
+ {
181
+ id: 'forgot-password',
182
+ label: tComponent<SuiteCoreStringKey>(
183
+ SuiteCoreComponentId,
184
+ SuiteCoreStringKey.ForgotPassword_Title
185
+ ),
186
+ icon: <LockOpenIcon />,
187
+ link: '/forgot-password',
188
+ requiresAuth: false,
189
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
190
+ index: index++,
191
+ },
192
+ {
193
+ id: 'change-password',
194
+ label: tComponent<SuiteCoreStringKey>(
195
+ SuiteCoreComponentId,
196
+ SuiteCoreStringKey.Common_ChangePassword
197
+ ),
198
+ icon: <LockResetIcon />,
199
+ link: '/change-password',
200
+ requiresAuth: true,
201
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
202
+ index: index++,
203
+ },
204
+ ...(enableBackupCodes
205
+ ? [
206
+ {
207
+ id: 'backup-code',
208
+ label: tComponent<SuiteCoreStringKey>(
209
+ SuiteCoreComponentId,
210
+ SuiteCoreStringKey.BackupCodeRecovery_Title
211
+ ),
212
+ icon: <KeyIcon />,
213
+ link: '/backup-code',
214
+ requiresAuth: false,
215
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
216
+ index: index++,
217
+ },
218
+ {
219
+ id: 'backup-codes',
220
+ label: tComponent<SuiteCoreStringKey>(
221
+ SuiteCoreComponentId,
222
+ SuiteCoreStringKey.BackupCodeRecovery_GenerateNewCodes
223
+ ),
224
+ icon: <AutorenewIcon />,
225
+ link: '/backup-codes',
226
+ requiresAuth: true,
227
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
228
+ index: index++,
229
+ },
230
+ ]
231
+ : []),
232
+ {
233
+ id: 'divider',
234
+ label: '',
235
+ divider: true,
236
+ includeOnMenus: [MenuTypes.SideMenu],
237
+ index: index++,
238
+ requiresAuth: false,
239
+ },
240
+ {
241
+ id: 'clear-mnemonic',
242
+ label: tComponent<SuiteCoreStringKey>(
243
+ SuiteCoreComponentId,
244
+ SuiteCoreStringKey.Common_ClearMnemonic
245
+ ),
246
+ action: clearMnemonic,
247
+ icon: <KeyIcon />,
248
+ requiresAuth: true,
249
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
250
+ index: index++,
251
+ filter: () => !!mnemonic,
252
+ },
253
+ {
254
+ id: 'clear-wallet',
255
+ label: tComponent<SuiteCoreStringKey>(
256
+ SuiteCoreComponentId,
257
+ SuiteCoreStringKey.Common_ClearWallet
258
+ ),
259
+ action: clearWallet,
260
+ icon: <KeyIcon />,
261
+ requiresAuth: true,
262
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
263
+ index: index++,
264
+ filter: () => !!wallet,
265
+ },
266
+ ...(isUserRestricted() ? [] : []),
267
+ {
268
+ id: 'color-divider',
269
+ label: '',
270
+ divider: true,
271
+ includeOnMenus: [MenuTypes.SideMenu],
272
+ index: index++,
273
+ requiresAuth: undefined,
274
+ },
275
+ {
276
+ id: 'theme-toggle',
277
+ label:
278
+ colorMode === 'dark'
279
+ ? tComponent<SuiteCoreStringKey>(
280
+ SuiteCoreComponentId,
281
+ SuiteCoreStringKey.Common_ThemeToggle_Light
282
+ )
283
+ : tComponent<SuiteCoreStringKey>(
284
+ SuiteCoreComponentId,
285
+ SuiteCoreStringKey.Common_ThemeToggle_Dark
286
+ ),
287
+ icon: colorMode === 'dark' ? <Brightness7 /> : <Brightness4 />,
288
+ includeOnMenus: [MenuTypes.SideMenu],
289
+ index: index++,
290
+ requiresAuth: undefined,
291
+ action: toggleColorMode,
292
+ },
293
+ {
294
+ id: 'user-settings',
295
+ label: tComponent<SuiteCoreStringKey>(
296
+ SuiteCoreComponentId,
297
+ SuiteCoreStringKey.Settings_Title
298
+ ),
299
+ icon: <Settings />,
300
+ link: '/user-settings',
301
+ requiresAuth: true,
302
+ includeOnMenus: [MenuTypes.UserMenu, MenuTypes.SideMenu],
303
+ index: index++,
304
+ },
305
+ ];
306
+
307
+ const allOptions = [...baseOptions, ...registeredOptions.values()];
308
+ return allOptions.sort((a, b) => a.index - b.index);
309
+ }, [
310
+ tComponent,
311
+ registeredOptions,
312
+ user?.roles,
313
+ colorMode,
314
+ toggleColorMode,
315
+ clearMnemonic,
316
+ clearWallet,
317
+ mnemonic,
318
+ wallet,
319
+ enableBackupCodes,
320
+ ]);
321
+
322
+ const getMenuOptions = useCallback(
323
+ (menuType: MenuType, includeDividers: boolean) => {
324
+ const MenuFilter = (o: IMenuOption) => {
325
+ // Apply the custom filter first
326
+ let customFilterPasses = true;
327
+ if (o.filter !== undefined) {
328
+ customFilterPasses = o.filter(o);
329
+ }
330
+ if (!customFilterPasses) return false;
331
+
332
+ if (o.divider === true && !includeDividers) return false;
333
+
334
+ return (
335
+ o.includeOnMenus.includes(menuType) &&
336
+ (o.requiresAuth === undefined || o.requiresAuth === isAuthenticated)
337
+ );
338
+ };
339
+
340
+ return menuOptions.filter(MenuFilter);
341
+ },
342
+ [isAuthenticated, menuOptions]
343
+ );
344
+
345
+ useEffect(() => {
346
+ if (menuConfigs.length > 0) {
347
+ return registerMenuOptions(
348
+ menuConfigs.flatMap((config) => config.options)
349
+ );
350
+ }
351
+ return undefined;
352
+ }, [menuConfigs, registerMenuOptions]);
353
+
354
+ const getTopMenus = useCallback(() => {
355
+ const menus: Array<IMenuConfig & { isUserMenu?: boolean }> = [
356
+ ...menuConfigs.map((config) => ({ ...config, isUserMenu: false })),
357
+ {
358
+ menuType: MenuTypes.UserMenu,
359
+ menuIcon: <AccountCircle />,
360
+ priority: 0,
361
+ options: [],
362
+ isUserMenu: true,
363
+ },
364
+ ];
365
+ return menus.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
366
+ }, [menuConfigs]);
367
+
368
+ const contextValue = useMemo(() => {
369
+ return {
370
+ menuOptions: menuOptions,
371
+ getMenuOptions: getMenuOptions,
372
+ registerMenuOption: registerMenuOption,
373
+ registerMenuOptions: registerMenuOptions,
374
+ getTopMenus: getTopMenus,
375
+ };
376
+ }, [
377
+ menuOptions,
378
+ getMenuOptions,
379
+ registerMenuOption,
380
+ registerMenuOptions,
381
+ getTopMenus,
382
+ ]);
383
+
384
+ const memoizedChildren = useMemo(() => children, [children]);
385
+ return (
386
+ <MenuContext.Provider value={contextValue}>
387
+ {memoizedChildren}
388
+ </MenuContext.Provider>
389
+ );
390
+ };
391
+
392
+ export const useMenu = (): MenuContextType => {
393
+ const context = useContext(MenuContext);
394
+ if (context === undefined) {
395
+ throw new Error('useMenu must be used within a MenuProvider');
396
+ }
397
+ return context;
398
+ };
@@ -0,0 +1,93 @@
1
+ import { createContext, ReactNode, useContext } from 'react';
2
+
3
+ export interface SuiteConfigRoutes {
4
+ dashboard?: string;
5
+ login?: string;
6
+ register?: string;
7
+ verifyEmail?: string;
8
+ forgotPassword?: string;
9
+ resetPassword?: string;
10
+ settings?: string;
11
+ }
12
+
13
+ export interface SuiteConfigContextData {
14
+ /**
15
+ * Base URL for API calls
16
+ */
17
+ baseUrl: string;
18
+
19
+ /**
20
+ * Application routes for navigation
21
+ */
22
+ routes: SuiteConfigRoutes;
23
+
24
+ /**
25
+ * Available languages for the application
26
+ */
27
+ languages: Array<{ code: string; label: string }>;
28
+
29
+ /**
30
+ * Available timezones
31
+ */
32
+ timezones?: string[];
33
+ }
34
+
35
+ const defaultRoutes: SuiteConfigRoutes = {
36
+ dashboard: '/dashboard',
37
+ login: '/login',
38
+ register: '/register',
39
+ verifyEmail: '/verify-email',
40
+ forgotPassword: '/forgot-password',
41
+ resetPassword: '/reset-password',
42
+ settings: '/settings',
43
+ };
44
+
45
+ const defaultLanguages = [
46
+ { code: 'en-US', label: 'English (US)' },
47
+ { code: 'en-GB', label: 'English (UK)' },
48
+ { code: 'es-ES', label: 'Español' },
49
+ { code: 'fr-FR', label: 'Français' },
50
+ { code: 'de-DE', label: 'Deutsch' },
51
+ { code: 'ja', label: '日本語' },
52
+ { code: 'zh-CN', label: '中文 (简体)' },
53
+ { code: 'uk', label: 'Українська' },
54
+ ];
55
+
56
+ const SuiteConfigContext = createContext<SuiteConfigContextData | undefined>(undefined);
57
+
58
+ export interface SuiteConfigProviderProps {
59
+ children: ReactNode;
60
+ baseUrl: string;
61
+ routes?: Partial<SuiteConfigRoutes>;
62
+ languages?: Array<{ code: string; label: string }>;
63
+ timezones?: string[];
64
+ }
65
+
66
+ export const SuiteConfigProvider = ({
67
+ children,
68
+ baseUrl,
69
+ routes = {},
70
+ languages = defaultLanguages,
71
+ timezones,
72
+ }: SuiteConfigProviderProps) => {
73
+ const value: SuiteConfigContextData = {
74
+ baseUrl,
75
+ routes: { ...defaultRoutes, ...routes },
76
+ languages,
77
+ timezones,
78
+ };
79
+
80
+ return (
81
+ <SuiteConfigContext.Provider value={value}>
82
+ {children}
83
+ </SuiteConfigContext.Provider>
84
+ );
85
+ };
86
+
87
+ export const useSuiteConfig = (): SuiteConfigContextData => {
88
+ const context = useContext(SuiteConfigContext);
89
+ if (!context) {
90
+ throw new Error('useSuiteConfig must be used within a SuiteConfigProvider');
91
+ }
92
+ return context;
93
+ };
@@ -0,0 +1,67 @@
1
+ import { Brightness4, Brightness7 } from '@mui/icons-material';
2
+ import {
3
+ IconButton,
4
+ ThemeProvider as MuiThemeProvider,
5
+ PaletteMode,
6
+ createTheme,
7
+ Theme,
8
+ } from '@mui/material';
9
+ import { FC, ReactNode, createContext, useContext, useMemo, useState } from 'react';
10
+
11
+ export interface ThemeContextType {
12
+ toggleColorMode: () => void;
13
+ setColorMode: (mode: PaletteMode) => void;
14
+ mode: PaletteMode;
15
+ }
16
+
17
+ const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
18
+
19
+ export const useTheme = () => {
20
+ const context = useContext(ThemeContext);
21
+ if (!context) {
22
+ throw new Error('useTheme must be used within an AppThemeProvider');
23
+ }
24
+ return context;
25
+ };
26
+
27
+ export interface AppThemeProviderProps {
28
+ children: ReactNode;
29
+ customTheme?: (mode: PaletteMode) => Theme;
30
+ }
31
+
32
+ export const AppThemeProvider: FC<AppThemeProviderProps> = ({ children, customTheme }) => {
33
+ const [mode, setMode] = useState<PaletteMode>('light');
34
+
35
+ const colorMode = useMemo(
36
+ () => ({
37
+ toggleColorMode: () => {
38
+ setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
39
+ },
40
+ setColorMode: (newMode: PaletteMode) => {
41
+ setMode(newMode);
42
+ },
43
+ mode,
44
+ }),
45
+ [mode]
46
+ );
47
+
48
+ const theme = useMemo(
49
+ () => (customTheme ? customTheme(mode) : createTheme({ palette: { mode } })),
50
+ [mode, customTheme]
51
+ );
52
+
53
+ return (
54
+ <ThemeContext.Provider value={colorMode}>
55
+ <MuiThemeProvider theme={theme}>{children}</MuiThemeProvider>
56
+ </ThemeContext.Provider>
57
+ );
58
+ };
59
+
60
+ export const ThemeToggleButton: FC = () => {
61
+ const { mode, toggleColorMode } = useTheme();
62
+ return (
63
+ <IconButton onClick={toggleColorMode} color="inherit">
64
+ {mode === 'dark' ? <Brightness7 /> : <Brightness4 />}
65
+ </IconButton>
66
+ );
67
+ };
@@ -3,4 +3,3 @@ export * from './MenuContext';
3
3
  export * from './I18nProvider';
4
4
  export * from './ThemeProvider';
5
5
  export * from './SuiteConfigProvider';
6
- //# sourceMappingURL=index.d.ts.map
@@ -3,4 +3,3 @@ export * from './useLocalStorage';
3
3
  export * from './useBackupCodes';
4
4
  export * from './useUserSettings';
5
5
  export * from './useEmailVerification';
6
- //# sourceMappingURL=index.d.ts.map