@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,247 @@
1
+ import {
2
+ Constants,
3
+ SuiteCoreComponentId,
4
+ SuiteCoreStringKey,
5
+ SuiteCoreStringKeyValue,
6
+ } from '@digitaldefiance/suite-core-lib';
7
+ import {
8
+ Alert,
9
+ Box,
10
+ Button,
11
+ Container,
12
+ TextField,
13
+ Typography,
14
+ } from '@mui/material';
15
+ import { useFormik } from 'formik';
16
+ import { FC, useState } from 'react';
17
+ import * as Yup from 'yup';
18
+ import { useI18n } from '../contexts';
19
+
20
+ export interface ResetPasswordFormValues {
21
+ password: string;
22
+ confirmPassword: string;
23
+ }
24
+
25
+ export interface ResetPasswordFormProps {
26
+ token: string | null;
27
+ onSubmit: (token: string, password: string) => Promise<void>;
28
+ passwordValidation?: Yup.StringSchema;
29
+ confirmPasswordValidation?: Yup.StringSchema;
30
+ labels?: {
31
+ title?: string;
32
+ password?: string;
33
+ confirmPassword?: string;
34
+ resetButton?: string;
35
+ successMessage?: string;
36
+ invalidToken?: string;
37
+ };
38
+ }
39
+
40
+ export const ResetPasswordForm: FC<ResetPasswordFormProps> = ({
41
+ token,
42
+ onSubmit,
43
+ passwordValidation,
44
+ confirmPasswordValidation,
45
+ labels = {},
46
+ }) => {
47
+ const { tComponent } = useI18n();
48
+ const [success, setSuccess] = useState(false);
49
+ const [apiError, setApiError] = useState<string>('');
50
+
51
+ const validation = {
52
+ password:
53
+ passwordValidation ||
54
+ Yup.string()
55
+ .min(
56
+ Constants.PasswordMinLength,
57
+ tComponent<SuiteCoreStringKeyValue>(
58
+ SuiteCoreComponentId,
59
+ SuiteCoreStringKey.Validation_PasswordMinLengthTemplate
60
+ )
61
+ )
62
+ .required(
63
+ tComponent<SuiteCoreStringKeyValue>(
64
+ SuiteCoreComponentId,
65
+ SuiteCoreStringKey.Validation_Required
66
+ )
67
+ ),
68
+ confirmPassword:
69
+ confirmPasswordValidation ||
70
+ Yup.string()
71
+ .oneOf(
72
+ [Yup.ref('password')],
73
+ tComponent<SuiteCoreStringKeyValue>(
74
+ SuiteCoreComponentId,
75
+ SuiteCoreStringKey.Validation_PasswordMatch
76
+ )
77
+ )
78
+ .required(
79
+ tComponent<SuiteCoreStringKeyValue>(
80
+ SuiteCoreComponentId,
81
+ SuiteCoreStringKey.Validation_Required
82
+ )
83
+ ),
84
+ };
85
+
86
+ const translatedLabels = {
87
+ title:
88
+ labels.title ||
89
+ tComponent<SuiteCoreStringKeyValue>(
90
+ SuiteCoreComponentId,
91
+ SuiteCoreStringKey.PasswordReset_Title
92
+ ),
93
+ password:
94
+ labels.password ||
95
+ tComponent<SuiteCoreStringKeyValue>(
96
+ SuiteCoreComponentId,
97
+ SuiteCoreStringKey.Common_NewPassword
98
+ ),
99
+ confirmPassword:
100
+ labels.confirmPassword ||
101
+ tComponent<SuiteCoreStringKeyValue>(
102
+ SuiteCoreComponentId,
103
+ SuiteCoreStringKey.Common_ConfirmNewPassword
104
+ ),
105
+ resetButton:
106
+ labels.resetButton ||
107
+ tComponent<SuiteCoreStringKeyValue>(
108
+ SuiteCoreComponentId,
109
+ SuiteCoreStringKey.PasswordReset_Button
110
+ ),
111
+ successMessage:
112
+ labels.successMessage ||
113
+ tComponent<SuiteCoreStringKeyValue>(
114
+ SuiteCoreComponentId,
115
+ SuiteCoreStringKey.PasswordReset_Success
116
+ ),
117
+ invalidToken:
118
+ labels.invalidToken ||
119
+ tComponent<SuiteCoreStringKeyValue>(
120
+ SuiteCoreComponentId,
121
+ SuiteCoreStringKey.ForgotPassword_InvalidToken
122
+ ),
123
+ };
124
+
125
+ const formik = useFormik<ResetPasswordFormValues>({
126
+ initialValues: {
127
+ password: '',
128
+ confirmPassword: '',
129
+ },
130
+ validationSchema: Yup.object({
131
+ password: validation.password,
132
+ confirmPassword: validation.confirmPassword,
133
+ }),
134
+ onSubmit: async (values) => {
135
+ if (!token) {
136
+ setApiError(translatedLabels.invalidToken);
137
+ return;
138
+ }
139
+
140
+ try {
141
+ await onSubmit(token, values.password);
142
+ setSuccess(true);
143
+ setApiError('');
144
+ } catch (error: unknown) {
145
+ const err = error as { response?: { data?: { message?: string } } };
146
+ setApiError(
147
+ err.response?.data?.message ||
148
+ tComponent<SuiteCoreStringKeyValue>(
149
+ SuiteCoreComponentId,
150
+ SuiteCoreStringKey.Error_PasswordChange
151
+ )
152
+ );
153
+ setSuccess(false);
154
+ }
155
+ },
156
+ });
157
+
158
+ if (!token) {
159
+ return (
160
+ <Container maxWidth="sm">
161
+ <Alert severity="error" sx={{ mt: 4 }}>
162
+ {translatedLabels.invalidToken}
163
+ </Alert>
164
+ </Container>
165
+ );
166
+ }
167
+
168
+ return (
169
+ <Container maxWidth="sm">
170
+ <Box
171
+ sx={{
172
+ mt: 8,
173
+ display: 'flex',
174
+ flexDirection: 'column',
175
+ alignItems: 'center',
176
+ }}
177
+ >
178
+ <Typography variant="h4" component="h1" gutterBottom>
179
+ {translatedLabels.title}
180
+ </Typography>
181
+
182
+ <Box
183
+ component="form"
184
+ onSubmit={formik.handleSubmit}
185
+ sx={{ mt: 1, width: '100%' }}
186
+ >
187
+ <TextField
188
+ fullWidth
189
+ id="password"
190
+ name="password"
191
+ label={translatedLabels.password}
192
+ type="password"
193
+ value={formik.values.password}
194
+ onChange={formik.handleChange}
195
+ onBlur={formik.handleBlur}
196
+ error={Boolean(formik.touched.password && formik.errors.password)}
197
+ helperText={formik.touched.password && formik.errors.password}
198
+ margin="normal"
199
+ />
200
+
201
+ <TextField
202
+ fullWidth
203
+ id="confirmPassword"
204
+ name="confirmPassword"
205
+ label={translatedLabels.confirmPassword}
206
+ type="password"
207
+ value={formik.values.confirmPassword}
208
+ onChange={formik.handleChange}
209
+ onBlur={formik.handleBlur}
210
+ error={Boolean(
211
+ formik.touched.confirmPassword && formik.errors.confirmPassword
212
+ )}
213
+ helperText={
214
+ formik.touched.confirmPassword && formik.errors.confirmPassword
215
+ }
216
+ margin="normal"
217
+ />
218
+
219
+ {apiError && (
220
+ <Alert severity="error" sx={{ mt: 2, mb: 2 }}>
221
+ {apiError}
222
+ </Alert>
223
+ )}
224
+
225
+ {success && (
226
+ <Alert severity="success" sx={{ mt: 2, mb: 2 }}>
227
+ {translatedLabels.successMessage}
228
+ </Alert>
229
+ )}
230
+
231
+ <Button
232
+ type="submit"
233
+ fullWidth
234
+ variant="contained"
235
+ color="primary"
236
+ sx={{ mt: 3, mb: 2 }}
237
+ disabled={formik.isSubmitting}
238
+ >
239
+ {translatedLabels.resetButton}
240
+ </Button>
241
+ </Box>
242
+ </Box>
243
+ </Container>
244
+ );
245
+ };
246
+
247
+ export default ResetPasswordForm;
@@ -0,0 +1,46 @@
1
+ import { Drawer, List } from '@mui/material';
2
+ import { FC } from 'react';
3
+ import { useNavigate } from 'react-router';
4
+ import { useMenu } from '../contexts/MenuContext';
5
+ import { IMenuOption } from '../interfaces/IMenuOption';
6
+ import { MenuTypes } from '../types/MenuType';
7
+ import { SideMenuListItem } from './SideMenuListItem';
8
+
9
+ interface SideMenuProps {
10
+ isOpen: boolean;
11
+ onClose: () => void;
12
+ }
13
+
14
+ export const SideMenu: FC<SideMenuProps> = ({ isOpen, onClose }) => {
15
+ const { getMenuOptions } = useMenu();
16
+ const navigate = useNavigate();
17
+
18
+ const menuOptions = getMenuOptions(MenuTypes.SideMenu, true);
19
+
20
+ const handleNavigate = (
21
+ link: string | Partial<{ pathname: string; state?: unknown }>
22
+ ) => {
23
+ if (typeof link === 'string') {
24
+ navigate(link);
25
+ } else if (link.pathname) {
26
+ navigate(link.pathname, { state: link.state });
27
+ }
28
+ };
29
+
30
+ return (
31
+ <Drawer anchor="left" open={isOpen} onClose={onClose}>
32
+ <List>
33
+ {menuOptions.map((item: IMenuOption) => (
34
+ <SideMenuListItem
35
+ key={item.id}
36
+ menuItem={item}
37
+ onClose={onClose}
38
+ onNavigate={handleNavigate}
39
+ />
40
+ ))}
41
+ </List>
42
+ </Drawer>
43
+ );
44
+ };
45
+
46
+ export default SideMenu;
@@ -0,0 +1,75 @@
1
+ import {
2
+ Divider,
3
+ ListItemButton,
4
+ ListItemIcon,
5
+ ListItemText,
6
+ } from '@mui/material';
7
+ import { FC, useCallback } from 'react';
8
+ import { IMenuOption } from '../interfaces';
9
+
10
+ export interface SideMenuListItemProps {
11
+ menuItem: IMenuOption;
12
+ onClose: () => void;
13
+ onNavigate?: (
14
+ link: string | Partial<{ pathname: string; state?: unknown }>
15
+ ) => void;
16
+ }
17
+
18
+ export const SideMenuListItem: FC<SideMenuListItemProps> = ({
19
+ menuItem,
20
+ onClose,
21
+ onNavigate,
22
+ }) => {
23
+ const handleMenuItemClick = useCallback(
24
+ (option: IMenuOption) => (event: React.MouseEvent<HTMLElement>) => {
25
+ event.stopPropagation();
26
+ if (option.action) {
27
+ option.action();
28
+ } else if (option.link !== undefined && onNavigate) {
29
+ if (typeof option.link === 'string') {
30
+ onNavigate(option.link);
31
+ } else if (
32
+ typeof option.link === 'object' &&
33
+ 'pathname' in option.link &&
34
+ option.link.pathname
35
+ ) {
36
+ onNavigate({
37
+ pathname: option.link.pathname,
38
+ state: 'state' in option.link ? option.link.state : undefined,
39
+ });
40
+ }
41
+ }
42
+ onClose();
43
+ },
44
+ [onNavigate, onClose]
45
+ );
46
+
47
+ if (menuItem.divider) {
48
+ return <Divider key={menuItem.id} sx={{...menuItem.sideSx}} />;
49
+ } else if (menuItem.link) {
50
+ return (
51
+ <ListItemButton key={menuItem.id} onClick={handleMenuItemClick(menuItem)} sx={{...menuItem.sideSx}}>
52
+ {menuItem.icon && <ListItemIcon sx={{...menuItem.sideIconSx}}>{menuItem.icon}</ListItemIcon>}
53
+ <ListItemText primary={menuItem.label} sx={{...menuItem.sideTextSx}} />
54
+ </ListItemButton>
55
+ );
56
+ } else if (menuItem.action) {
57
+ const action = menuItem.action;
58
+ return (
59
+ <ListItemButton
60
+ key={menuItem.id}
61
+ onClick={async () => {
62
+ await action();
63
+ onClose();
64
+ }}
65
+ sx={{...menuItem.sideSx}}
66
+ >
67
+ {menuItem.icon && <ListItemIcon sx={{...menuItem.sideIconSx}}>{menuItem.icon}</ListItemIcon>}
68
+ <ListItemText primary={menuItem.label} sx={{...menuItem.sideTextSx}} />
69
+ </ListItemButton>
70
+ );
71
+ }
72
+ return null;
73
+ };
74
+
75
+ export default SideMenuListItem;
@@ -0,0 +1,24 @@
1
+ // src/app/components/T.tsx
2
+
3
+ import { useMemo } from 'react';
4
+ import { useI18n } from '../contexts';
5
+
6
+ interface FCParams<TEnum extends string> {
7
+ stringKey: TEnum;
8
+ vars?: Record<string, string | number>;
9
+ }
10
+
11
+ export const Tbranded = <TEnum extends string>({
12
+ stringKey,
13
+ vars,
14
+ }: FCParams<TEnum>): string | undefined => {
15
+ const { currentLanguage, tBranded } = useI18n();
16
+ const translated = useMemo(
17
+ () => tBranded<TEnum>(stringKey, vars, currentLanguage),
18
+ [tBranded, stringKey, vars, currentLanguage]
19
+ );
20
+
21
+ return translated;
22
+ };
23
+
24
+ export default Tbranded;
@@ -0,0 +1,26 @@
1
+ // src/app/components/TDiv.tsx
2
+
3
+ import { DetailedHTMLProps, HTMLAttributes, ReactElement, useMemo } from 'react';
4
+ import { useI18n } from '../contexts';
5
+
6
+ interface FCParams<TEnum extends string> {
7
+ stringKey: TEnum;
8
+ vars?: Record<string, string | number>;
9
+ divOptions?: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
10
+ }
11
+
12
+ export const TDivBranded = <TEnum extends string>({
13
+ stringKey,
14
+ vars,
15
+ divOptions,
16
+ }: FCParams<TEnum>): ReactElement => {
17
+ const { currentLanguage, tBranded } = useI18n();
18
+ const translated = useMemo(
19
+ () => tBranded<TEnum>(stringKey, vars, currentLanguage),
20
+ [tBranded, stringKey, vars, currentLanguage]
21
+ );
22
+
23
+ return <div {...divOptions}>{translated}</div>;
24
+ };
25
+
26
+ export default TDivBranded;
@@ -0,0 +1,28 @@
1
+ // src/app/components/TImgAlt.tsx
2
+
3
+ import { DetailedHTMLProps, ImgHTMLAttributes, ReactElement, useMemo } from 'react';
4
+ import { useI18n } from '../contexts';
5
+
6
+ interface FCParams<TEnum extends string> {
7
+ src: string;
8
+ stringKey: TEnum;
9
+ vars?: Record<string, string | number>;
10
+ imgOptions?: DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>;
11
+ }
12
+
13
+ export const TImgAlt = <TEnum extends string>({
14
+ src,
15
+ stringKey,
16
+ vars,
17
+ imgOptions,
18
+ }: FCParams<TEnum>): ReactElement => {
19
+ const { currentLanguage, tBranded } = useI18n();
20
+ const translated = useMemo(
21
+ () => tBranded<TEnum>(stringKey, vars, currentLanguage),
22
+ [tBranded, stringKey, vars, currentLanguage]
23
+ );
24
+
25
+ return <img src={src} {...imgOptions} alt={translated} />;
26
+ };
27
+
28
+ export default TImgAlt;
@@ -0,0 +1,252 @@
1
+ import {
2
+ IConstants,
3
+ SuiteCoreComponentId,
4
+ SuiteCoreStringKey,
5
+ SuiteCoreStringKeyValue,
6
+ } from '@digitaldefiance/suite-core-lib';
7
+ import MenuIcon from '@mui/icons-material/Menu';
8
+ import {
9
+ AppBar,
10
+ Box,
11
+ Button,
12
+ IconButton,
13
+ Toolbar,
14
+ Typography,
15
+ } from '@mui/material';
16
+ import React, {
17
+ ElementType,
18
+ FC,
19
+ ReactElement,
20
+ useContext,
21
+ useMemo,
22
+ useState,
23
+ } from 'react';
24
+ import { Link } from 'react-router';
25
+ import { AuthContext } from '../contexts/AuthProvider';
26
+ import { useI18n } from '../contexts/I18nProvider';
27
+ import { useMenu } from '../contexts/MenuContext';
28
+ import { ICategoryConfig } from '../interfaces/ICategoryConfig';
29
+ import { IMenuConfig } from '../interfaces/IMenuConfig';
30
+ import { MenuType } from '../types/MenuType';
31
+ import { CategoryMenu } from './CategoryMenu';
32
+ import { DropdownMenu } from './DropdownMenu';
33
+ import { SideMenu } from './SideMenu';
34
+ import { UserLanguageSelector } from './UserLanguageSelector';
35
+ import { UserMenu } from './UserMenu';
36
+
37
+ // Extend Window interface for APP_CONFIG
38
+ declare global {
39
+ interface Window {
40
+ APP_CONFIG?: {
41
+ hostname: string;
42
+ siteTitle: string;
43
+ server: string;
44
+ [key: string]: unknown;
45
+ };
46
+ }
47
+ }
48
+
49
+ export interface AdditionalDropdownMenu {
50
+ menuType: MenuType;
51
+ menuIcon: ReactElement;
52
+ priority?: number;
53
+ /** Optional callback invoked when the icon button is clicked. */
54
+ action?: () => void;
55
+ /** When true, the menu icon is hidden if there are no sub-options. Defaults to false. */
56
+ hideWhenEmpty?: boolean;
57
+ /**
58
+ * Optional category id. When the TopMenu is given matching `categories`,
59
+ * this menu is rendered inside that category's popover instead of inline.
60
+ */
61
+ category?: string;
62
+ /** Optional label used for tile captions and tooltips. */
63
+ label?: string;
64
+ }
65
+
66
+ export interface TopMenuProps {
67
+ Logo: React.ReactNode;
68
+ additionalMenus?: Array<AdditionalDropdownMenu>;
69
+ /**
70
+ * Optional category configurations. When provided, any menu (whether
71
+ * registered via `MenuContext` or passed in `additionalMenus`) whose
72
+ * `category` matches a category `id` is rendered as a tile inside that
73
+ * category's mega-menu popover. Menus without a matching category render
74
+ * inline in the AppBar exactly as before, so this is backwards compatible:
75
+ * omit `categories` (or use empty array) for the original layout.
76
+ */
77
+ categories?: ICategoryConfig[];
78
+ /** Custom action elements rendered in the toolbar after menu dropdowns (e.g. notification bell). Only shown when authenticated. */
79
+ actions?: React.ReactNode;
80
+ constants?: IConstants;
81
+ showTitle?: boolean;
82
+ }
83
+
84
+ export const TopMenu: FC<TopMenuProps> = ({
85
+ Logo,
86
+ additionalMenus,
87
+ categories,
88
+ actions,
89
+ constants,
90
+ showTitle,
91
+ }) => {
92
+ const { isAuthenticated } = useContext(AuthContext);
93
+ const { getTopMenus } = useMenu();
94
+ const [isSideMenuOpen, setIsSideMenuOpen] = useState(false);
95
+
96
+ const handleOpenSideMenu = () => setIsSideMenuOpen(true);
97
+ const handleCloseSideMenu = () => setIsSideMenuOpen(false);
98
+ const { tComponent } = useI18n();
99
+ // When constants are explicitly provided, pass Site as a template variable
100
+ // override. Otherwise let the i18n engine resolve {Site} from its registered
101
+ // constants (which the consuming app sets up via createI18nSetup).
102
+ const siteTitle = tComponent<SuiteCoreStringKeyValue>(
103
+ SuiteCoreComponentId,
104
+ SuiteCoreStringKey.Common_SiteTemplate,
105
+ constants ? { Site: constants.Site } : undefined
106
+ );
107
+
108
+ const allMenus = useMemo(() => {
109
+ const contextMenus = getTopMenus();
110
+ if (!additionalMenus || additionalMenus.length === 0) {
111
+ return contextMenus;
112
+ }
113
+ const additional = additionalMenus.map((m) => ({
114
+ ...m,
115
+ options: [],
116
+ isUserMenu: false,
117
+ }));
118
+ return [...contextMenus, ...additional].sort(
119
+ (a, b) => (b.priority ?? 0) - (a.priority ?? 0)
120
+ );
121
+ }, [getTopMenus, additionalMenus]);
122
+
123
+ // Split menus into those that belong to a configured category vs. those
124
+ // that should render inline in the AppBar (default, backwards-compatible
125
+ // behaviour). The user menu is always inline.
126
+ const { inlineMenus, menusByCategory, orderedCategories } = useMemo(() => {
127
+ const hasCategories = !!categories && categories.length > 0;
128
+ const categoryIds = new Set(
129
+ hasCategories ? (categories as ICategoryConfig[]).map((c) => c.id) : []
130
+ );
131
+ const byCategory = new Map<string, IMenuConfig[]>();
132
+ const inline: typeof allMenus = [];
133
+
134
+ for (const menu of allMenus) {
135
+ const cat = (menu as IMenuConfig).category;
136
+ if (!menu.isUserMenu && hasCategories && cat && categoryIds.has(cat)) {
137
+ const list = byCategory.get(cat) ?? [];
138
+ list.push(menu as IMenuConfig);
139
+ byCategory.set(cat, list);
140
+ } else {
141
+ inline.push(menu);
142
+ }
143
+ }
144
+
145
+ const ordered = hasCategories
146
+ ? [...(categories as ICategoryConfig[])].sort(
147
+ (a, b) => (b.priority ?? 0) - (a.priority ?? 0)
148
+ )
149
+ : [];
150
+
151
+ return {
152
+ inlineMenus: inline,
153
+ menusByCategory: byCategory,
154
+ orderedCategories: ordered,
155
+ };
156
+ }, [allMenus, categories]);
157
+
158
+ return (
159
+ <AppBar position="fixed" sx={{ top: 10 }}>
160
+ <Toolbar>
161
+ <IconButton
162
+ size="large"
163
+ edge="start"
164
+ color="inherit"
165
+ aria-label="menu"
166
+ sx={{ mr: 2 }}
167
+ onClick={handleOpenSideMenu}
168
+ >
169
+ <MenuIcon />
170
+ </IconButton>
171
+ <Box
172
+ sx={{
173
+ height: 40,
174
+ width: 'auto',
175
+ marginRight: 2,
176
+ display: 'flex',
177
+ alignItems: 'center',
178
+ }}
179
+ >
180
+ {Logo}
181
+ </Box>
182
+ <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
183
+ {showTitle !== false ? siteTitle : '\u00A0'}
184
+ </Typography>
185
+ <Box sx={{ display: 'flex', alignItems: 'center' }}>
186
+ {isAuthenticated ? (
187
+ <>
188
+ <Button
189
+ color="inherit"
190
+ component={Link as ElementType}
191
+ to="/dashboard"
192
+ >
193
+ {tComponent<SuiteCoreStringKeyValue>(
194
+ SuiteCoreComponentId,
195
+ SuiteCoreStringKey.Common_Dashboard
196
+ )}
197
+ </Button>
198
+ {inlineMenus.map((menu, index) =>
199
+ menu.isUserMenu ? (
200
+ <UserMenu key={`user-menu`} />
201
+ ) : (
202
+ <DropdownMenu
203
+ key={`menu-${index}`}
204
+ menuType={menu.menuType}
205
+ menuIcon={menu.menuIcon as ReactElement}
206
+ action={menu.action}
207
+ hideWhenEmpty={menu.hideWhenEmpty}
208
+ />
209
+ )
210
+ )}
211
+ {orderedCategories.map((category) => (
212
+ <CategoryMenu
213
+ key={`category-${category.id}`}
214
+ category={category}
215
+ menus={menusByCategory.get(category.id) ?? []}
216
+ />
217
+ ))}
218
+ </>
219
+ ) : (
220
+ <>
221
+ <Button
222
+ color="inherit"
223
+ component={Link as ElementType}
224
+ to="/login"
225
+ >
226
+ {tComponent<SuiteCoreStringKeyValue>(
227
+ SuiteCoreComponentId,
228
+ SuiteCoreStringKey.Login_LoginButton
229
+ )}
230
+ </Button>
231
+ <Button
232
+ color="inherit"
233
+ component={Link as ElementType}
234
+ to="/register"
235
+ >
236
+ {tComponent<SuiteCoreStringKeyValue>(
237
+ SuiteCoreComponentId,
238
+ SuiteCoreStringKey.RegisterButton
239
+ )}
240
+ </Button>
241
+ </>
242
+ )}
243
+ {isAuthenticated && actions}
244
+ <UserLanguageSelector />
245
+ </Box>
246
+ </Toolbar>
247
+ <SideMenu isOpen={isSideMenuOpen} onClose={handleCloseSideMenu} />
248
+ </AppBar>
249
+ );
250
+ };
251
+
252
+ export default TopMenu;