@digitaldefiance/express-suite-react-components 5.0.0 → 5.0.2

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 (312) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +4 -0
  3. package/package.json +3 -4
  4. package/src/auth/AdminRoute.tsx +54 -0
  5. package/src/auth/Private.tsx +17 -0
  6. package/src/auth/PrivateRoute.tsx +28 -0
  7. package/src/auth/UnAuth.tsx +16 -0
  8. package/src/auth/UnAuthRoute.tsx +30 -0
  9. package/src/auth/{index.d.ts → index.ts} +2 -3
  10. package/src/components/ApiAccess.tsx +175 -0
  11. package/src/components/BackupCodeLoginForm.tsx +489 -0
  12. package/src/components/BackupCodesForm.tsx +287 -0
  13. package/src/components/CategoryMenu.tsx +155 -0
  14. package/src/components/ChangePasswordForm.tsx +273 -0
  15. package/src/components/ConfirmationDialog.tsx +48 -0
  16. package/src/components/CurrencyCodeSelector.tsx +60 -0
  17. package/src/components/CurrencyInput.tsx +80 -0
  18. package/src/components/DashboardPage.tsx +24 -0
  19. package/src/components/DropdownMenu.tsx +105 -0
  20. package/src/components/ExpirationSecondsSelector.tsx +60 -0
  21. package/src/components/FakeEmailAdminPanel.tsx +374 -0
  22. package/src/components/Flag.tsx +52 -0
  23. package/src/components/ForgotPasswordForm.tsx +174 -0
  24. package/src/components/LoginForm.tsx +468 -0
  25. package/src/components/LogoutPage.tsx +21 -0
  26. package/src/components/RegisterForm.tsx +1169 -0
  27. package/src/components/ResetPasswordForm.tsx +247 -0
  28. package/src/components/SideMenu.tsx +46 -0
  29. package/src/components/SideMenuListItem.tsx +75 -0
  30. package/src/components/T.tsx +24 -0
  31. package/src/components/TDiv.tsx +26 -0
  32. package/src/components/TImgAlt.tsx +28 -0
  33. package/src/components/TopMenu.tsx +252 -0
  34. package/src/components/TotpSetupForm.tsx +188 -0
  35. package/src/components/TotpVerificationForm.tsx +148 -0
  36. package/src/components/TranslatedTitle.tsx +31 -0
  37. package/src/components/UserLanguageSelector.tsx +45 -0
  38. package/src/components/UserMenu.tsx +15 -0
  39. package/src/components/UserSettingsForm.tsx +904 -0
  40. package/src/components/VerifyEmailPage.tsx +222 -0
  41. package/src/components/__tests__/RegisterForm.totp.spec.tsx +377 -0
  42. package/src/components/__tests__/TotpSetupForm.spec.tsx +258 -0
  43. package/src/components/__tests__/TotpVerificationForm.spec.tsx +259 -0
  44. package/src/components/__tests__/UserSettingsForm.totp.spec.tsx +396 -0
  45. package/src/components/{index.d.ts → index.ts} +1 -1
  46. package/src/contexts/AuthProvider.spec.tsx +1284 -0
  47. package/src/contexts/AuthProvider.tsx +977 -0
  48. package/src/contexts/I18nProvider.tsx +164 -0
  49. package/src/contexts/MenuContext.tsx +426 -0
  50. package/src/contexts/SuiteConfigProvider.tsx +93 -0
  51. package/src/contexts/ThemeProvider.tsx +67 -0
  52. package/src/contexts/{index.d.ts → index.ts} +0 -1
  53. package/src/hooks/{index.d.ts → index.ts} +0 -1
  54. package/src/hooks/useAuthenticatedApi.tsx +67 -0
  55. package/src/hooks/useBackupCodes.ts +105 -0
  56. package/src/hooks/useEmailVerification.ts +54 -0
  57. package/src/hooks/useExpiringValue.ts +78 -0
  58. package/src/hooks/useLocalStorage.ts +18 -0
  59. package/src/hooks/useUserSettings.ts +271 -0
  60. package/src/{index.d.ts → index.ts} +1 -1
  61. package/src/interfaces/IAppConfig.ts +7 -0
  62. package/src/interfaces/ICategoryConfig.ts +41 -0
  63. package/src/interfaces/IMenuConfig.ts +28 -0
  64. package/src/interfaces/IMenuOption.ts +79 -0
  65. package/src/interfaces/{index.d.ts → index.ts} +0 -1
  66. package/src/services/__mocks__/authService.ts +14 -0
  67. package/src/services/__tests__/authService.totp.property.spec.ts +360 -0
  68. package/src/services/__tests__/authService.totp.spec.ts +439 -0
  69. package/src/services/api.ts +13 -0
  70. package/src/services/authService.ts +692 -0
  71. package/src/services/authenticatedApi.ts +17 -0
  72. package/src/services/index.ts +3 -0
  73. package/src/types/MenuType.ts +15 -0
  74. package/src/types/expirationSeconds.ts +18 -0
  75. package/src/types/index.ts +1 -0
  76. package/src/types/translation.ts +20 -0
  77. package/src/wrappers/BackupCodeLoginWrapper.tsx +34 -0
  78. package/src/wrappers/BackupCodesWrapper.tsx +28 -0
  79. package/src/wrappers/ChangePasswordFormWrapper.tsx +34 -0
  80. package/src/wrappers/ForgotPasswordFormWrapper.tsx +45 -0
  81. package/src/wrappers/LoginFormWrapper.tsx +108 -0
  82. package/src/wrappers/LogoutPageWrapper.tsx +30 -0
  83. package/src/wrappers/RegisterFormWrapper.tsx +109 -0
  84. package/src/wrappers/ResetPasswordFormWrapper.tsx +57 -0
  85. package/src/wrappers/UserSettingsFormWrapper.tsx +140 -0
  86. package/src/wrappers/VerifyEmailPageWrapper.tsx +27 -0
  87. package/src/wrappers/__tests__/LoginFormWrapper.totp.spec.tsx +339 -0
  88. package/src/wrappers/{index.d.ts → index.tsx} +10 -1
  89. package/src/auth/AdminRoute.d.ts +0 -8
  90. package/src/auth/AdminRoute.d.ts.map +0 -1
  91. package/src/auth/AdminRoute.js +0 -28
  92. package/src/auth/Private.d.ts +0 -6
  93. package/src/auth/Private.d.ts.map +0 -1
  94. package/src/auth/Private.js +0 -14
  95. package/src/auth/PrivateRoute.d.ts +0 -8
  96. package/src/auth/PrivateRoute.d.ts.map +0 -1
  97. package/src/auth/PrivateRoute.js +0 -23
  98. package/src/auth/UnAuth.d.ts +0 -6
  99. package/src/auth/UnAuth.d.ts.map +0 -1
  100. package/src/auth/UnAuth.js +0 -14
  101. package/src/auth/UnAuthRoute.d.ts +0 -8
  102. package/src/auth/UnAuthRoute.d.ts.map +0 -1
  103. package/src/auth/UnAuthRoute.js +0 -22
  104. package/src/auth/index.d.ts.map +0 -1
  105. package/src/auth/index.js +0 -12
  106. package/src/components/ApiAccess.d.ts +0 -16
  107. package/src/components/ApiAccess.d.ts.map +0 -1
  108. package/src/components/ApiAccess.js +0 -77
  109. package/src/components/BackupCodeLoginForm.d.ts +0 -43
  110. package/src/components/BackupCodeLoginForm.d.ts.map +0 -1
  111. package/src/components/BackupCodeLoginForm.js +0 -139
  112. package/src/components/BackupCodesForm.d.ts +0 -26
  113. package/src/components/BackupCodesForm.d.ts.map +0 -1
  114. package/src/components/BackupCodesForm.js +0 -120
  115. package/src/components/CategoryMenu.d.ts +0 -17
  116. package/src/components/CategoryMenu.d.ts.map +0 -1
  117. package/src/components/CategoryMenu.js +0 -65
  118. package/src/components/ChangePasswordForm.d.ts +0 -26
  119. package/src/components/ChangePasswordForm.d.ts.map +0 -1
  120. package/src/components/ChangePasswordForm.js +0 -78
  121. package/src/components/ConfirmationDialog.d.ts +0 -13
  122. package/src/components/ConfirmationDialog.d.ts.map +0 -1
  123. package/src/components/ConfirmationDialog.js +0 -10
  124. package/src/components/CurrencyCodeSelector.d.ts +0 -9
  125. package/src/components/CurrencyCodeSelector.d.ts.map +0 -1
  126. package/src/components/CurrencyCodeSelector.js +0 -31
  127. package/src/components/CurrencyInput.d.ts +0 -13
  128. package/src/components/CurrencyInput.d.ts.map +0 -1
  129. package/src/components/CurrencyInput.js +0 -22
  130. package/src/components/DashboardPage.d.ts +0 -8
  131. package/src/components/DashboardPage.d.ts.map +0 -1
  132. package/src/components/DashboardPage.js +0 -10
  133. package/src/components/DropdownMenu.d.ts +0 -13
  134. package/src/components/DropdownMenu.d.ts.map +0 -1
  135. package/src/components/DropdownMenu.js +0 -63
  136. package/src/components/ExpirationSecondsSelector.d.ts +0 -13
  137. package/src/components/ExpirationSecondsSelector.d.ts.map +0 -1
  138. package/src/components/ExpirationSecondsSelector.js +0 -32
  139. package/src/components/FakeEmailAdminPanel.d.ts +0 -23
  140. package/src/components/FakeEmailAdminPanel.d.ts.map +0 -1
  141. package/src/components/FakeEmailAdminPanel.js +0 -147
  142. package/src/components/Flag.d.ts +0 -20
  143. package/src/components/Flag.d.ts.map +0 -1
  144. package/src/components/Flag.js +0 -43
  145. package/src/components/ForgotPasswordForm.d.ts +0 -18
  146. package/src/components/ForgotPasswordForm.d.ts.map +0 -1
  147. package/src/components/ForgotPasswordForm.js +0 -61
  148. package/src/components/LoginForm.d.ts +0 -44
  149. package/src/components/LoginForm.d.ts.map +0 -1
  150. package/src/components/LoginForm.js +0 -123
  151. package/src/components/LogoutPage.d.ts +0 -8
  152. package/src/components/LogoutPage.d.ts.map +0 -1
  153. package/src/components/LogoutPage.js +0 -16
  154. package/src/components/RegisterForm.d.ts +0 -100
  155. package/src/components/RegisterForm.d.ts.map +0 -1
  156. package/src/components/RegisterForm.js +0 -306
  157. package/src/components/ResetPasswordForm.d.ts +0 -23
  158. package/src/components/ResetPasswordForm.d.ts.map +0 -1
  159. package/src/components/ResetPasswordForm.js +0 -78
  160. package/src/components/SideMenu.d.ts +0 -8
  161. package/src/components/SideMenu.d.ts.map +0 -1
  162. package/src/components/SideMenu.js +0 -25
  163. package/src/components/SideMenuListItem.d.ts +0 -13
  164. package/src/components/SideMenuListItem.d.ts.map +0 -1
  165. package/src/components/SideMenuListItem.js +0 -44
  166. package/src/components/T.d.ts +0 -7
  167. package/src/components/T.d.ts.map +0 -1
  168. package/src/components/T.js +0 -13
  169. package/src/components/TDiv.d.ts +0 -9
  170. package/src/components/TDiv.d.ts.map +0 -1
  171. package/src/components/TDiv.js +0 -14
  172. package/src/components/TImgAlt.d.ts +0 -10
  173. package/src/components/TImgAlt.d.ts.map +0 -1
  174. package/src/components/TImgAlt.js +0 -14
  175. package/src/components/TopMenu.d.ts +0 -50
  176. package/src/components/TopMenu.d.ts.map +0 -1
  177. package/src/components/TopMenu.js +0 -79
  178. package/src/components/TotpSetupForm.d.ts +0 -24
  179. package/src/components/TotpSetupForm.d.ts.map +0 -1
  180. package/src/components/TotpSetupForm.js +0 -82
  181. package/src/components/TotpVerificationForm.d.ts +0 -23
  182. package/src/components/TotpVerificationForm.d.ts.map +0 -1
  183. package/src/components/TotpVerificationForm.js +0 -66
  184. package/src/components/TranslatedTitle.d.ts +0 -8
  185. package/src/components/TranslatedTitle.d.ts.map +0 -1
  186. package/src/components/TranslatedTitle.js +0 -15
  187. package/src/components/UserLanguageSelector.d.ts +0 -4
  188. package/src/components/UserLanguageSelector.d.ts.map +0 -1
  189. package/src/components/UserLanguageSelector.js +0 -31
  190. package/src/components/UserMenu.d.ts +0 -4
  191. package/src/components/UserMenu.d.ts.map +0 -1
  192. package/src/components/UserMenu.js +0 -12
  193. package/src/components/UserSettingsForm.d.ts +0 -113
  194. package/src/components/UserSettingsForm.d.ts.map +0 -1
  195. package/src/components/UserSettingsForm.js +0 -269
  196. package/src/components/VerifyEmailPage.d.ts +0 -24
  197. package/src/components/VerifyEmailPage.d.ts.map +0 -1
  198. package/src/components/VerifyEmailPage.js +0 -81
  199. package/src/components/index.d.ts.map +0 -1
  200. package/src/components/index.js +0 -35
  201. package/src/contexts/AuthProvider.d.ts +0 -186
  202. package/src/contexts/AuthProvider.d.ts.map +0 -1
  203. package/src/contexts/AuthProvider.js +0 -515
  204. package/src/contexts/I18nProvider.d.ts +0 -27
  205. package/src/contexts/I18nProvider.d.ts.map +0 -1
  206. package/src/contexts/I18nProvider.js +0 -60
  207. package/src/contexts/MenuContext.d.ts +0 -20
  208. package/src/contexts/MenuContext.d.ts.map +0 -1
  209. package/src/contexts/MenuContext.js +0 -275
  210. package/src/contexts/SuiteConfigProvider.d.ts +0 -44
  211. package/src/contexts/SuiteConfigProvider.d.ts.map +0 -1
  212. package/src/contexts/SuiteConfigProvider.js +0 -43
  213. package/src/contexts/ThemeProvider.d.ts +0 -15
  214. package/src/contexts/ThemeProvider.d.ts.map +0 -1
  215. package/src/contexts/ThemeProvider.js +0 -36
  216. package/src/contexts/index.d.ts.map +0 -1
  217. package/src/contexts/index.js +0 -8
  218. package/src/hooks/index.d.ts.map +0 -1
  219. package/src/hooks/index.js +0 -9
  220. package/src/hooks/useAuthenticatedApi.d.ts +0 -34
  221. package/src/hooks/useAuthenticatedApi.d.ts.map +0 -1
  222. package/src/hooks/useAuthenticatedApi.js +0 -46
  223. package/src/hooks/useBackupCodes.d.ts +0 -15
  224. package/src/hooks/useBackupCodes.d.ts.map +0 -1
  225. package/src/hooks/useBackupCodes.js +0 -74
  226. package/src/hooks/useEmailVerification.d.ts +0 -10
  227. package/src/hooks/useEmailVerification.d.ts.map +0 -1
  228. package/src/hooks/useEmailVerification.js +0 -40
  229. package/src/hooks/useExpiringValue.d.ts +0 -14
  230. package/src/hooks/useExpiringValue.d.ts.map +0 -1
  231. package/src/hooks/useExpiringValue.js +0 -53
  232. package/src/hooks/useLocalStorage.d.ts +0 -2
  233. package/src/hooks/useLocalStorage.d.ts.map +0 -1
  234. package/src/hooks/useLocalStorage.js +0 -15
  235. package/src/hooks/useUserSettings.d.ts +0 -48
  236. package/src/hooks/useUserSettings.d.ts.map +0 -1
  237. package/src/hooks/useUserSettings.js +0 -171
  238. package/src/index.d.ts.map +0 -1
  239. package/src/index.js +0 -12
  240. package/src/interfaces/IAppConfig.d.ts +0 -8
  241. package/src/interfaces/IAppConfig.d.ts.map +0 -1
  242. package/src/interfaces/IAppConfig.js +0 -2
  243. package/src/interfaces/ICategoryConfig.d.ts +0 -41
  244. package/src/interfaces/ICategoryConfig.d.ts.map +0 -1
  245. package/src/interfaces/ICategoryConfig.js +0 -2
  246. package/src/interfaces/IMenuConfig.d.ts +0 -28
  247. package/src/interfaces/IMenuConfig.d.ts.map +0 -1
  248. package/src/interfaces/IMenuConfig.js +0 -2
  249. package/src/interfaces/IMenuOption.d.ts +0 -82
  250. package/src/interfaces/IMenuOption.d.ts.map +0 -1
  251. package/src/interfaces/IMenuOption.js +0 -2
  252. package/src/interfaces/index.d.ts.map +0 -1
  253. package/src/interfaces/index.js +0 -7
  254. package/src/services/__mocks__/authService.d.ts +0 -21
  255. package/src/services/__mocks__/authService.d.ts.map +0 -1
  256. package/src/services/__mocks__/authService.js +0 -15
  257. package/src/services/api.d.ts +0 -3
  258. package/src/services/api.d.ts.map +0 -1
  259. package/src/services/api.js +0 -14
  260. package/src/services/authService.d.ts +0 -126
  261. package/src/services/authService.d.ts.map +0 -1
  262. package/src/services/authService.js +0 -471
  263. package/src/services/authenticatedApi.d.ts +0 -3
  264. package/src/services/authenticatedApi.d.ts.map +0 -1
  265. package/src/services/authenticatedApi.js +0 -18
  266. package/src/services/index.d.ts +0 -4
  267. package/src/services/index.d.ts.map +0 -1
  268. package/src/services/index.js +0 -6
  269. package/src/types/MenuType.d.ts +0 -11
  270. package/src/types/MenuType.d.ts.map +0 -1
  271. package/src/types/MenuType.js +0 -12
  272. package/src/types/expirationSeconds.d.ts +0 -3
  273. package/src/types/expirationSeconds.d.ts.map +0 -1
  274. package/src/types/expirationSeconds.js +0 -17
  275. package/src/types/index.d.ts +0 -2
  276. package/src/types/index.d.ts.map +0 -1
  277. package/src/types/index.js +0 -4
  278. package/src/types/translation.d.ts +0 -10
  279. package/src/types/translation.d.ts.map +0 -1
  280. package/src/types/translation.js +0 -9
  281. package/src/wrappers/BackupCodeLoginWrapper.d.ts +0 -8
  282. package/src/wrappers/BackupCodeLoginWrapper.d.ts.map +0 -1
  283. package/src/wrappers/BackupCodeLoginWrapper.js +0 -20
  284. package/src/wrappers/BackupCodesWrapper.d.ts +0 -7
  285. package/src/wrappers/BackupCodesWrapper.d.ts.map +0 -1
  286. package/src/wrappers/BackupCodesWrapper.js +0 -17
  287. package/src/wrappers/ChangePasswordFormWrapper.d.ts +0 -8
  288. package/src/wrappers/ChangePasswordFormWrapper.d.ts.map +0 -1
  289. package/src/wrappers/ChangePasswordFormWrapper.js +0 -21
  290. package/src/wrappers/ForgotPasswordFormWrapper.d.ts +0 -8
  291. package/src/wrappers/ForgotPasswordFormWrapper.d.ts.map +0 -1
  292. package/src/wrappers/ForgotPasswordFormWrapper.js +0 -29
  293. package/src/wrappers/LoginFormWrapper.d.ts +0 -21
  294. package/src/wrappers/LoginFormWrapper.d.ts.map +0 -1
  295. package/src/wrappers/LoginFormWrapper.js +0 -72
  296. package/src/wrappers/LogoutPageWrapper.d.ts +0 -9
  297. package/src/wrappers/LogoutPageWrapper.d.ts.map +0 -1
  298. package/src/wrappers/LogoutPageWrapper.js +0 -21
  299. package/src/wrappers/RegisterFormWrapper.d.ts +0 -20
  300. package/src/wrappers/RegisterFormWrapper.d.ts.map +0 -1
  301. package/src/wrappers/RegisterFormWrapper.js +0 -51
  302. package/src/wrappers/ResetPasswordFormWrapper.d.ts +0 -9
  303. package/src/wrappers/ResetPasswordFormWrapper.d.ts.map +0 -1
  304. package/src/wrappers/ResetPasswordFormWrapper.js +0 -34
  305. package/src/wrappers/UserSettingsFormWrapper.d.ts +0 -29
  306. package/src/wrappers/UserSettingsFormWrapper.d.ts.map +0 -1
  307. package/src/wrappers/UserSettingsFormWrapper.js +0 -95
  308. package/src/wrappers/VerifyEmailPageWrapper.d.ts +0 -8
  309. package/src/wrappers/VerifyEmailPageWrapper.d.ts.map +0 -1
  310. package/src/wrappers/VerifyEmailPageWrapper.js +0 -21
  311. package/src/wrappers/index.d.ts.map +0 -1
  312. package/src/wrappers/index.js +0 -24
@@ -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
@@ -4,4 +4,3 @@ export * from './useBackupCodes';
4
4
  export * from './useUserSettings';
5
5
  export * from './useEmailVerification';
6
6
  export * from './useAuthenticatedApi';
7
- //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * useAuthenticatedApi — React hook + provider for a shared authenticated
3
+ * Axios instance.
4
+ *
5
+ * The provider creates a single AxiosInstance via createAuthenticatedApiClient
6
+ * using the baseUrl from SuiteConfigProvider. All component libraries consume
7
+ * the same instance through the useAuthenticatedApi() hook, eliminating
8
+ * duplicate axios instances and circular dependency issues.
9
+ *
10
+ * Usage:
11
+ * // In the app root (wrap inside SuiteConfigProvider):
12
+ * <AuthenticatedApiProvider>
13
+ * <App />
14
+ * </AuthenticatedApiProvider>
15
+ *
16
+ * // In any component library:
17
+ * const api = useAuthenticatedApi();
18
+ * const res = await api.get('/some-endpoint');
19
+ */
20
+
21
+ import { createContext, ReactNode, useContext, useMemo } from 'react';
22
+ import type { AxiosInstance } from 'axios';
23
+ import { createAuthenticatedApiClient } from '../services/authenticatedApi';
24
+ import { useSuiteConfig } from '../contexts/SuiteConfigProvider';
25
+
26
+ const AuthenticatedApiContext = createContext<AxiosInstance | undefined>(
27
+ undefined,
28
+ );
29
+
30
+ export interface AuthenticatedApiProviderProps {
31
+ children: ReactNode;
32
+ /** Optional token key in localStorage. Defaults to 'authToken'. */
33
+ tokenKey?: string;
34
+ }
35
+
36
+ export const AuthenticatedApiProvider = ({
37
+ children,
38
+ tokenKey,
39
+ }: AuthenticatedApiProviderProps) => {
40
+ const { baseUrl } = useSuiteConfig();
41
+
42
+ const api = useMemo(
43
+ () => createAuthenticatedApiClient(baseUrl, tokenKey),
44
+ [baseUrl, tokenKey],
45
+ );
46
+
47
+ return (
48
+ <AuthenticatedApiContext.Provider value={api}>
49
+ {children}
50
+ </AuthenticatedApiContext.Provider>
51
+ );
52
+ };
53
+
54
+ /**
55
+ * Returns the shared authenticated Axios instance.
56
+ * Must be used within an AuthenticatedApiProvider (which itself must be
57
+ * inside a SuiteConfigProvider).
58
+ */
59
+ export const useAuthenticatedApi = (): AxiosInstance => {
60
+ const context = useContext(AuthenticatedApiContext);
61
+ if (!context) {
62
+ throw new Error(
63
+ 'useAuthenticatedApi must be used within an AuthenticatedApiProvider',
64
+ );
65
+ }
66
+ return context;
67
+ };
@@ -0,0 +1,105 @@
1
+ import {
2
+ SuiteCoreStringKey,
3
+ TranslatableSuiteError,
4
+ } from '@digitaldefiance/suite-core-lib';
5
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import { useSuiteConfig } from '../contexts';
7
+ import { createAuthenticatedApiClient } from '../services';
8
+
9
+ export interface UseBackupCodesOptions {
10
+ initialCodeCount?: number | null;
11
+ }
12
+
13
+ export interface UseBackupCodesResult {
14
+ backupCodesRemaining: number | null;
15
+ isLoading: boolean;
16
+ error: Error | null;
17
+ generateBackupCodes: (
18
+ password?: string,
19
+ mnemonic?: string
20
+ ) => Promise<{
21
+ message: string;
22
+ backupCodes: string[];
23
+ }>;
24
+ refreshCodeCount: () => Promise<void>;
25
+ }
26
+
27
+ export const useBackupCodes = (
28
+ options: UseBackupCodesOptions = {}
29
+ ): UseBackupCodesResult => {
30
+ const { baseUrl } = useSuiteConfig();
31
+ const [backupCodesRemaining, setBackupCodesRemaining] = useState<
32
+ number | null
33
+ >(options.initialCodeCount ?? null);
34
+ const [isLoading, setIsLoading] = useState(false);
35
+ const [error, setError] = useState<Error | null>(null);
36
+ const requestedOnMountRef = useRef(false);
37
+ const api = useMemo(() => createAuthenticatedApiClient(baseUrl), [baseUrl]);
38
+
39
+ const refreshCodeCount = useCallback(async () => {
40
+ setIsLoading(true);
41
+ setError(null);
42
+ try {
43
+ const result = await api.get<{ codeCount: number }>('/user/backup-codes');
44
+ if (result?.data?.codeCount !== undefined) {
45
+ setBackupCodesRemaining(result.data.codeCount);
46
+ }
47
+ } catch (err) {
48
+ setError(
49
+ err instanceof Error
50
+ ? err
51
+ : new TranslatableSuiteError(
52
+ SuiteCoreStringKey.BackupCodes_FailedToFetch
53
+ )
54
+ );
55
+ setBackupCodesRemaining(0);
56
+ } finally {
57
+ setIsLoading(false);
58
+ }
59
+ }, [api]);
60
+
61
+ useEffect(() => {
62
+ if (requestedOnMountRef.current) return;
63
+ if (backupCodesRemaining !== null) return;
64
+
65
+ requestedOnMountRef.current = true;
66
+ refreshCodeCount();
67
+ }, [backupCodesRemaining, refreshCodeCount]);
68
+
69
+ const generateBackupCodes = async (password?: string, mnemonic?: string) => {
70
+ setIsLoading(true);
71
+ setError(null);
72
+ try {
73
+ const result = await api.post<{ message: string; backupCodes: string[] }>(
74
+ '/user/backup-codes',
75
+ {
76
+ ...(password ? { password } : {}),
77
+ ...(mnemonic ? { mnemonic } : {}),
78
+ }
79
+ );
80
+ return {
81
+ message: result.data.message,
82
+ backupCodes: result.data.backupCodes,
83
+ };
84
+ } catch (err) {
85
+ const error =
86
+ err instanceof Error
87
+ ? err
88
+ : new TranslatableSuiteError(
89
+ SuiteCoreStringKey.BackupCodes_FailedToGenerate
90
+ );
91
+ setError(error);
92
+ throw error;
93
+ } finally {
94
+ setIsLoading(false);
95
+ }
96
+ };
97
+
98
+ return {
99
+ backupCodesRemaining,
100
+ isLoading,
101
+ error,
102
+ generateBackupCodes,
103
+ refreshCodeCount,
104
+ };
105
+ };
@@ -0,0 +1,54 @@
1
+ import {
2
+ getSuiteCoreTranslation,
3
+ SuiteCoreStringKey,
4
+ } from '@digitaldefiance/suite-core-lib';
5
+ import { useCallback, useMemo, useState } from 'react';
6
+ import { useSuiteConfig } from '../contexts';
7
+ import { createAuthenticatedApiClient } from '../services';
8
+
9
+ export interface UseEmailVerificationResult {
10
+ isVerifying: boolean;
11
+ error: Error | null;
12
+ verifyEmail: (
13
+ token: string
14
+ ) => Promise<{ success: boolean; message?: string }>;
15
+ }
16
+
17
+ export const useEmailVerification = (): UseEmailVerificationResult => {
18
+ const { baseUrl } = useSuiteConfig();
19
+ const api = useMemo(() => createAuthenticatedApiClient(baseUrl), [baseUrl]);
20
+ const [isVerifying, setIsVerifying] = useState(false);
21
+ const [error, setError] = useState<Error | null>(null);
22
+
23
+ const verifyEmail = useCallback(
24
+ async (verificationToken: string) => {
25
+ setIsVerifying(true);
26
+ setError(null);
27
+ try {
28
+ const result = await api.post<{ message: string }>('/user/verify-email', {
29
+ token: verificationToken,
30
+ });
31
+ return { success: true, message: result.data.message };
32
+ } catch (err: unknown) {
33
+ const axiosError = err as {
34
+ response?: { data?: { message?: string } };
35
+ };
36
+ const errorMessage =
37
+ axiosError.response?.data?.message ||
38
+ getSuiteCoreTranslation(SuiteCoreStringKey.Error_VerificationFailed);
39
+ const error = new Error(errorMessage);
40
+ setError(error);
41
+ return { success: false, message: errorMessage };
42
+ } finally {
43
+ setIsVerifying(false);
44
+ }
45
+ },
46
+ [api]
47
+ );
48
+
49
+ return {
50
+ isVerifying,
51
+ error,
52
+ verifyEmail,
53
+ };
54
+ };
@@ -0,0 +1,78 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ export interface TimerInfo {
4
+ timeout: NodeJS.Timeout;
5
+ startTime: number;
6
+ durationMs: number;
7
+ }
8
+
9
+ export interface ExpiringValueReturn<T> {
10
+ value: T | undefined;
11
+ setValue: (newValue: T, durationSeconds?: number, saveToStorage?: boolean) => () => void;
12
+ clearValue: () => void;
13
+ getRemainingTime: () => number;
14
+ isActive: boolean;
15
+ }
16
+
17
+ export function useExpiringValue<T>(
18
+ defaultDurationSeconds: number,
19
+ localStorageKey?: string
20
+ ): ExpiringValueReturn<T> {
21
+ const [value, _setValue] = useState<T | undefined>(undefined);
22
+ const [timer, setTimer] = useState<TimerInfo | undefined>(undefined);
23
+ const [durationSeconds, _setDurationSeconds] = useState<number>(() => {
24
+ if (localStorageKey) {
25
+ const stored = localStorage.getItem(localStorageKey);
26
+ return stored ? parseInt(stored, 10) : defaultDurationSeconds;
27
+ }
28
+ return defaultDurationSeconds;
29
+ });
30
+
31
+ const getRemainingTime = useCallback((): number => {
32
+ if (!timer) return 0;
33
+ const elapsed = Date.now() - timer.startTime;
34
+ const remaining = Math.max(0, timer.durationMs - elapsed);
35
+ return Math.ceil(remaining / 1000);
36
+ }, [timer]);
37
+
38
+ const setValue = useCallback((newValue: T, customDurationSeconds?: number, saveToStorage?: boolean) => {
39
+ const finalDurationSeconds = customDurationSeconds ?? durationSeconds;
40
+
41
+ if (saveToStorage && customDurationSeconds !== undefined && localStorageKey) {
42
+ _setDurationSeconds(customDurationSeconds);
43
+ localStorage.setItem(localStorageKey, customDurationSeconds.toString());
44
+ }
45
+
46
+ _setValue(newValue);
47
+
48
+ if (timer) {
49
+ clearTimeout(timer.timeout);
50
+ }
51
+
52
+ const startTime = Date.now();
53
+ const durationMs = finalDurationSeconds * 1000;
54
+ const timeout = setTimeout(() => {
55
+ _setValue(undefined);
56
+ setTimer(undefined);
57
+ }, durationMs);
58
+
59
+ setTimer({ timeout, startTime, durationMs });
60
+
61
+ return () => {
62
+ clearTimeout(timeout);
63
+ setTimer(undefined);
64
+ };
65
+ }, [durationSeconds, timer, localStorageKey]);
66
+
67
+ const clearValue = useCallback(() => {
68
+ if (timer) {
69
+ clearTimeout(timer.timeout);
70
+ setTimer(undefined);
71
+ }
72
+ _setValue(undefined);
73
+ }, [timer]);
74
+
75
+ const isActive = Boolean(timer && value !== undefined);
76
+
77
+ return { value, setValue, clearValue, getRemainingTime, isActive };
78
+ }
@@ -0,0 +1,18 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { LocalStorageManager } from '@digitaldefiance/suite-core-lib';
3
+
4
+ export function useLocalStorage<T>(
5
+ key: string,
6
+ defaultValue: T
7
+ ): [T, (value: T) => void] {
8
+ const [storedValue, setStoredValue] = useState<T>(() => {
9
+ return LocalStorageManager.getValue(key, defaultValue);
10
+ });
11
+
12
+ const setValue = useCallback((value: T) => {
13
+ setStoredValue(value);
14
+ LocalStorageManager.setValue(key, value);
15
+ }, [key]);
16
+
17
+ return [storedValue, setValue];
18
+ }
@@ -0,0 +1,271 @@
1
+ import { EmailString } from '@digitaldefiance/ecies-lib';
2
+ import {
3
+ CurrencyCode,
4
+ DefaultCurrencyCode,
5
+ DefaultLanguageCode,
6
+ DefaultTimezone,
7
+ Timezone,
8
+ } from '@digitaldefiance/i18n-lib';
9
+ import {
10
+ dehydrateUserSettings,
11
+ getSuiteCoreTranslation,
12
+ IUserSettings,
13
+ IUserSettingsDTO,
14
+ SuiteCoreStringKey,
15
+ TranslatableSuiteError,
16
+ } from '@digitaldefiance/suite-core-lib';
17
+ import { useCallback, useEffect, useState } from 'react';
18
+ import { useAuth, useI18n, useTheme } from '../contexts';
19
+
20
+ const defaultUserSettings: Partial<IUserSettings> = {
21
+ darkMode: false,
22
+ currency: new CurrencyCode(DefaultCurrencyCode),
23
+ timezone: new Timezone(DefaultTimezone),
24
+ siteLanguage: DefaultLanguageCode,
25
+ directChallenge: false,
26
+ } as const;
27
+
28
+ export interface UseUserSettingsOptions {
29
+ authenticatedApi: {
30
+ post: <T = unknown>(
31
+ url: string,
32
+ data: Record<string, unknown>
33
+ ) => Promise<{ data: T }>;
34
+ };
35
+ isAuthenticated: boolean;
36
+ }
37
+
38
+ export interface UseUserSettingsResult {
39
+ currentLanguage: string;
40
+ changeLanguage: (languageCode: string) => Promise<void>;
41
+ userSettings: IUserSettings | undefined;
42
+ setUserSettingAndUpdateSettings: (
43
+ setting?: Partial<IUserSettings>
44
+ ) => Promise<void>;
45
+ toggleColorMode: () => Promise<void>;
46
+ }
47
+
48
+ /**
49
+ * Hook for managing user settings state and synchronization.
50
+ * Used by AuthProvider to handle user settings logic.
51
+ */
52
+ export const useUserSettings = ({
53
+ authenticatedApi,
54
+ isAuthenticated,
55
+ }: UseUserSettingsOptions): UseUserSettingsResult => {
56
+ const { setColorMode: themeSetPaletteMode, mode: currentThemeMode } =
57
+ useTheme();
58
+ const { currentLanguage, changeLanguage } = useI18n();
59
+ const [userSettings, setUserSettings] = useState<IUserSettings | undefined>(
60
+ undefined
61
+ );
62
+
63
+ const setUserSettingAndUpdateSettings = useCallback(
64
+ async (setting?: Partial<IUserSettings>) => {
65
+ if (setting) {
66
+ // Merge settings with defaults and existing settings
67
+ const merged = {
68
+ ...defaultUserSettings,
69
+ ...(userSettings ? userSettings : {}),
70
+ ...setting,
71
+ };
72
+
73
+ // TypeScript requires all IUserSettings fields, but during initialization
74
+ // some fields like email may not be set yet. We'll use Partial until
75
+ // all required fields are available.
76
+ setUserSettings(merged as IUserSettings);
77
+
78
+ if (setting.darkMode !== undefined) {
79
+ themeSetPaletteMode(setting.darkMode ? 'dark' : 'light');
80
+ }
81
+ if (
82
+ setting.siteLanguage !== undefined &&
83
+ setting.siteLanguage !== currentLanguage
84
+ ) {
85
+ await changeLanguage(setting.siteLanguage);
86
+ }
87
+
88
+ // Send to server if authenticated and we have complete settings (including email)
89
+ if (isAuthenticated && merged.email) {
90
+ const dehydratedSettings = dehydrateUserSettings(
91
+ merged as IUserSettings
92
+ );
93
+ await authenticatedApi.post(
94
+ '/user/settings',
95
+ dehydratedSettings as unknown as Record<string, unknown>
96
+ );
97
+ }
98
+ } else {
99
+ setUserSettings(undefined);
100
+ themeSetPaletteMode('light');
101
+ }
102
+ },
103
+ [
104
+ isAuthenticated,
105
+ userSettings,
106
+ currentLanguage,
107
+ changeLanguage,
108
+ themeSetPaletteMode,
109
+ authenticatedApi,
110
+ ]
111
+ );
112
+
113
+ const changeLanguageSetting = useCallback(
114
+ async (languageCode: string) => {
115
+ await setUserSettingAndUpdateSettings({ siteLanguage: languageCode });
116
+ },
117
+ [setUserSettingAndUpdateSettings]
118
+ );
119
+
120
+ const toggleColorMode = useCallback(async () => {
121
+ // When not authenticated, read from the current theme mode
122
+ // When authenticated, read from userSettings (synced with server)
123
+ const currentDarkMode = isAuthenticated
124
+ ? userSettings?.darkMode ?? false
125
+ : currentThemeMode === 'dark';
126
+ await setUserSettingAndUpdateSettings({ darkMode: !currentDarkMode });
127
+ }, [
128
+ isAuthenticated,
129
+ userSettings?.darkMode,
130
+ currentThemeMode,
131
+ setUserSettingAndUpdateSettings,
132
+ ]);
133
+
134
+ return {
135
+ changeLanguage: changeLanguageSetting,
136
+ currentLanguage,
137
+ userSettings,
138
+ setUserSettingAndUpdateSettings,
139
+ toggleColorMode,
140
+ };
141
+ };
142
+
143
+ // Public-facing hook for components
144
+ export interface UserSettingsValues extends IUserSettingsDTO {
145
+ [key: string]: string | boolean | undefined;
146
+ }
147
+
148
+ export interface UseUserSettingsPublicResult {
149
+ settings: UserSettingsValues | null;
150
+ isLoading: boolean;
151
+ error: Error | null;
152
+ updateSettings: (values: UserSettingsValues) => Promise<
153
+ | {
154
+ success: boolean;
155
+ message: string;
156
+ }
157
+ | {
158
+ error: string;
159
+ errorType?: string;
160
+ field?: string;
161
+ errors?: Array<{ path: string; msg: string }>;
162
+ }
163
+ >;
164
+ refreshSettings: () => Promise<void>;
165
+ }
166
+
167
+ /**
168
+ * Public hook for components to manage user settings.
169
+ * Provides a simpler API for fetching and updating settings.
170
+ */
171
+ export const useUserSettingsPublic = (): UseUserSettingsPublicResult => {
172
+ const { userData, setUserSetting, userSettings } = useAuth();
173
+ const [settings, setSettings] = useState<UserSettingsValues | null>(null);
174
+ const [isLoading, setIsLoading] = useState(true);
175
+ const [error, setError] = useState<Error | null>(null);
176
+
177
+ const refreshSettings = useCallback(async () => {
178
+ setIsLoading(true);
179
+ setError(null);
180
+ try {
181
+ if (userSettings) {
182
+ const dehydratedSettings = dehydrateUserSettings(userSettings);
183
+ setSettings(dehydratedSettings as UserSettingsValues);
184
+ } else if (userData) {
185
+ const fallback = {
186
+ email: userData.email || '',
187
+ timezone: userData.timezone || 'UTC',
188
+ siteLanguage: userData.siteLanguage || 'en-US',
189
+ currency: userData.currency || 'USD',
190
+ darkMode: userData.darkMode || false,
191
+ directChallenge: userData.directChallenge || false,
192
+ ...(userData.displayName !== undefined ? { displayName: userData.displayName } : {}),
193
+ };
194
+ setSettings(fallback);
195
+ }
196
+ } catch (err) {
197
+ setError(
198
+ err instanceof Error
199
+ ? err
200
+ : new TranslatableSuiteError(
201
+ SuiteCoreStringKey.Settings_RetrieveFailure
202
+ )
203
+ );
204
+ } finally {
205
+ setIsLoading(false);
206
+ }
207
+ }, [userSettings, userData]);
208
+
209
+ useEffect(() => {
210
+ refreshSettings();
211
+ }, [refreshSettings]);
212
+
213
+ const updateSettings = useCallback(
214
+ async (values: UserSettingsValues) => {
215
+ setIsLoading(true);
216
+ setError(null);
217
+ try {
218
+ await setUserSetting({
219
+ darkMode: values.darkMode,
220
+ currency: new CurrencyCode(values.currency),
221
+ timezone: new Timezone(values.timezone),
222
+ siteLanguage: values.siteLanguage,
223
+ email: new EmailString(values.email),
224
+ directChallenge: values.directChallenge,
225
+ ...(values.displayName !== undefined ? { displayName: values.displayName } : {}),
226
+ });
227
+
228
+ setSettings(values);
229
+
230
+ return {
231
+ success: true,
232
+ message: getSuiteCoreTranslation(
233
+ SuiteCoreStringKey.Settings_SaveSuccess
234
+ ),
235
+ };
236
+ } catch (err: unknown) {
237
+ const axiosError = err as {
238
+ response?: {
239
+ data?: {
240
+ message?: string;
241
+ errorType?: string;
242
+ field?: string;
243
+ errors?: Array<{ path: string; msg: string }>;
244
+ };
245
+ };
246
+ };
247
+ const errorMessage =
248
+ axiosError.response?.data?.message ||
249
+ getSuiteCoreTranslation(SuiteCoreStringKey.Settings_UpdateFailed);
250
+ setError(err instanceof Error ? err : new Error(errorMessage));
251
+ return {
252
+ error: errorMessage,
253
+ errorType: axiosError.response?.data?.errorType,
254
+ field: axiosError.response?.data?.field,
255
+ errors: axiosError.response?.data?.errors,
256
+ };
257
+ } finally {
258
+ setIsLoading(false);
259
+ }
260
+ },
261
+ [setUserSetting]
262
+ );
263
+
264
+ return {
265
+ settings,
266
+ isLoading,
267
+ error,
268
+ updateSettings,
269
+ refreshSettings,
270
+ };
271
+ };
@@ -1,3 +1,4 @@
1
+ // Export all components
1
2
  export * from './auth';
2
3
  export * from './components';
3
4
  export * from './contexts';
@@ -6,4 +7,3 @@ export * from './interfaces';
6
7
  export * from './services';
7
8
  export * from './types';
8
9
  export * from './wrappers';
9
- //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,7 @@
1
+ export interface IAppConfig {
2
+ hostname: string;
3
+ siteTitle: string;
4
+ server: string;
5
+ /** Whether TOTP two-factor authentication is available on this deployment. */
6
+ totpAvailable?: boolean;
7
+ }