@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,286 @@
1
+ import {
2
+ Constants,
3
+ SuiteCoreComponentId,
4
+ SuiteCoreStringKey,
5
+ } from '@digitaldefiance/suite-core-lib';
6
+ import {
7
+ Alert,
8
+ Box,
9
+ Button,
10
+ Container,
11
+ TextField,
12
+ Typography,
13
+ } from '@mui/material';
14
+ import { useFormik } from 'formik';
15
+ import { FC, useState } from 'react';
16
+ import * as Yup from 'yup';
17
+ import { useI18n } from '../contexts';
18
+
19
+ export interface BackupCodesFormValues {
20
+ password?: string;
21
+ mnemonic?: string;
22
+ }
23
+
24
+ export interface BackupCodesFormProps {
25
+ onSubmit: (values: BackupCodesFormValues) => Promise<{
26
+ message: string;
27
+ backupCodes: string[];
28
+ }>;
29
+ backupCodesRemaining?: number | null;
30
+ mnemonicValidation?: Yup.StringSchema;
31
+ passwordValidation?: Yup.StringSchema;
32
+ labels?: {
33
+ title?: string;
34
+ codesRemaining?: string;
35
+ mnemonic?: string;
36
+ password?: string;
37
+ generateButton?: string;
38
+ successTitle?: string;
39
+ };
40
+ }
41
+
42
+ export const BackupCodesForm: FC<BackupCodesFormProps> = ({
43
+ onSubmit,
44
+ backupCodesRemaining = null,
45
+ mnemonicValidation,
46
+ passwordValidation,
47
+ labels = {},
48
+ }) => {
49
+ const { tComponent } = useI18n();
50
+ const [apiError, setApiError] = useState<string | null>(null);
51
+ const [apiSuccess, setApiSuccess] = useState<string | null>(null);
52
+ const [backupCodes, setBackupCodes] = useState<string[] | null>(null);
53
+
54
+ const validation = {
55
+ mnemonic:
56
+ mnemonicValidation ||
57
+ Yup.string()
58
+ .trim()
59
+ .matches(
60
+ Constants.MnemonicRegex,
61
+ tComponent<SuiteCoreStringKey>(
62
+ SuiteCoreComponentId,
63
+ SuiteCoreStringKey.Validation_MnemonicRegex
64
+ )
65
+ )
66
+ .optional(),
67
+ password:
68
+ passwordValidation ||
69
+ Yup.string()
70
+ .trim()
71
+ .matches(
72
+ Constants.PasswordRegex,
73
+ tComponent<SuiteCoreStringKey>(
74
+ SuiteCoreComponentId,
75
+ SuiteCoreStringKey.Validation_PasswordRegexErrorTemplate
76
+ )
77
+ )
78
+ .optional(),
79
+ };
80
+
81
+ const translatedLabels = {
82
+ codesRemaining:
83
+ labels.codesRemaining ||
84
+ tComponent<SuiteCoreStringKey>(
85
+ SuiteCoreComponentId,
86
+ SuiteCoreStringKey.BackupCodeRecovery_CodesRemainingTemplate
87
+ ),
88
+ mnemonic:
89
+ labels.mnemonic ||
90
+ tComponent<SuiteCoreStringKey>(
91
+ SuiteCoreComponentId,
92
+ SuiteCoreStringKey.Common_Mnemonic
93
+ ),
94
+ password:
95
+ labels.password ||
96
+ tComponent<SuiteCoreStringKey>(
97
+ SuiteCoreComponentId,
98
+ SuiteCoreStringKey.Common_Password
99
+ ),
100
+ generateButton:
101
+ labels.generateButton ||
102
+ tComponent<SuiteCoreStringKey>(
103
+ SuiteCoreComponentId,
104
+ SuiteCoreStringKey.BackupCodeRecovery_GenerateNewCodes
105
+ ),
106
+ successTitle:
107
+ labels.successTitle ||
108
+ tComponent<SuiteCoreStringKey>(
109
+ SuiteCoreComponentId,
110
+ SuiteCoreStringKey.BackupCodeRecovery_YourNewCodes
111
+ ),
112
+ xorError: tComponent<SuiteCoreStringKey>(
113
+ SuiteCoreComponentId,
114
+ SuiteCoreStringKey.Validation_MnemonicOrPasswordRequired
115
+ ),
116
+ unexpectedError: tComponent<SuiteCoreStringKey>(
117
+ SuiteCoreComponentId,
118
+ SuiteCoreStringKey.Common_UnexpectedError
119
+ ),
120
+ };
121
+
122
+ const validationSchema = Yup.object({
123
+ mnemonic: validation.mnemonic,
124
+ password: validation.password,
125
+ })
126
+ .test(
127
+ 'xor-mnemonic-password-mnemonic',
128
+ translatedLabels.xorError,
129
+ function (value) {
130
+ const mnemonic = value?.mnemonic?.trim() ?? '';
131
+ const password = value?.password?.trim() ?? '';
132
+ const hasMnemonic = mnemonic.length > 0;
133
+ const hasPassword = password.length > 0;
134
+
135
+ if (!hasMnemonic && !hasPassword) {
136
+ return this.createError({
137
+ path: 'mnemonic',
138
+ message: translatedLabels.xorError,
139
+ });
140
+ }
141
+ if (hasMnemonic && hasPassword) {
142
+ return this.createError({
143
+ path: 'mnemonic',
144
+ message: translatedLabels.xorError,
145
+ });
146
+ }
147
+ return true;
148
+ }
149
+ )
150
+ .test(
151
+ 'xor-mnemonic-password-password',
152
+ translatedLabels.xorError,
153
+ function (value) {
154
+ const mnemonic = value?.mnemonic?.trim() ?? '';
155
+ const password = value?.password?.trim() ?? '';
156
+ const hasMnemonic = mnemonic.length > 0;
157
+ const hasPassword = password.length > 0;
158
+
159
+ if (!hasMnemonic && !hasPassword) {
160
+ return this.createError({
161
+ path: 'password',
162
+ message: translatedLabels.xorError,
163
+ });
164
+ }
165
+ if (hasMnemonic && hasPassword) {
166
+ return this.createError({
167
+ path: 'password',
168
+ message: translatedLabels.xorError,
169
+ });
170
+ }
171
+ return true;
172
+ }
173
+ );
174
+
175
+ const formik = useFormik<BackupCodesFormValues>({
176
+ initialValues: {
177
+ password: '',
178
+ mnemonic: '',
179
+ },
180
+ validationSchema,
181
+ onSubmit: async (values, { setSubmitting }) => {
182
+ try {
183
+ const result = await onSubmit(values);
184
+ if (result && result.backupCodes) {
185
+ setApiSuccess(translatedLabels.successTitle);
186
+ setBackupCodes(result.backupCodes);
187
+ }
188
+ if (result && result.message) {
189
+ setApiSuccess(result.message);
190
+ }
191
+ setApiError(null);
192
+ } catch (e: unknown) {
193
+ setApiSuccess(null);
194
+ const error = e as { response?: { data?: { message?: string } } };
195
+ setApiError(
196
+ error.response?.data?.message ?? translatedLabels.unexpectedError
197
+ );
198
+ } finally {
199
+ setSubmitting(false);
200
+ }
201
+ },
202
+ });
203
+
204
+ return (
205
+ <Container component="main" maxWidth="xs">
206
+ <Box>
207
+ <Typography component="h1" variant="h5">
208
+ {translatedLabels.codesRemaining.replace(
209
+ '{count}',
210
+ String(backupCodesRemaining ?? 0)
211
+ )}
212
+ </Typography>
213
+ </Box>
214
+ <Box
215
+ component="form"
216
+ onSubmit={formik.handleSubmit}
217
+ sx={{ mt: 1, width: '100%' }}
218
+ >
219
+ <TextField
220
+ margin="normal"
221
+ fullWidth
222
+ id="mnemonic"
223
+ label={translatedLabels.mnemonic}
224
+ type="password"
225
+ name="mnemonic"
226
+ autoComplete="mnemonic"
227
+ autoFocus
228
+ value={formik.values.mnemonic}
229
+ onChange={formik.handleChange}
230
+ onBlur={formik.handleBlur}
231
+ error={
232
+ (formik.touched.mnemonic || formik.submitCount > 0) &&
233
+ Boolean(formik.errors.mnemonic)
234
+ }
235
+ helperText={
236
+ (formik.touched.mnemonic || formik.submitCount > 0) &&
237
+ formik.errors.mnemonic
238
+ }
239
+ />
240
+ <TextField
241
+ margin="normal"
242
+ fullWidth
243
+ name="password"
244
+ label={translatedLabels.password}
245
+ type="password"
246
+ id="password"
247
+ value={formik.values.password}
248
+ onChange={formik.handleChange}
249
+ onBlur={formik.handleBlur}
250
+ error={
251
+ (formik.touched.password || formik.submitCount > 0) &&
252
+ Boolean(formik.errors.password)
253
+ }
254
+ helperText={
255
+ (formik.touched.password || formik.submitCount > 0) &&
256
+ formik.errors.password
257
+ }
258
+ />
259
+ <Button type="submit" fullWidth variant="contained" sx={{ mt: 2 }}>
260
+ {translatedLabels.generateButton}
261
+ </Button>
262
+ </Box>
263
+ {apiError && (
264
+ <Alert severity="error" sx={{ mt: 2, mb: 2 }}>
265
+ {apiError}
266
+ </Alert>
267
+ )}
268
+ {backupCodes && apiSuccess && (
269
+ <Box sx={{ mt: 2, mb: 2 }}>
270
+ <Typography component="h2" variant="h6">
271
+ {apiSuccess}
272
+ </Typography>
273
+ <ul>
274
+ {backupCodes.map((code, index) => (
275
+ <li key={index}>
276
+ <pre>{code}</pre>
277
+ </li>
278
+ ))}
279
+ </ul>
280
+ </Box>
281
+ )}
282
+ </Container>
283
+ );
284
+ };
285
+
286
+ export default BackupCodesForm;
@@ -0,0 +1,272 @@
1
+ import {
2
+ Constants,
3
+ SuiteCoreComponentId,
4
+ SuiteCoreStringKey,
5
+ } from '@digitaldefiance/suite-core-lib';
6
+ import {
7
+ Alert,
8
+ Box,
9
+ Button,
10
+ Container,
11
+ TextField,
12
+ Typography,
13
+ } from '@mui/material';
14
+ import { useFormik } from 'formik';
15
+ import { FC, useState } from 'react';
16
+ import * as Yup from 'yup';
17
+ import { useI18n } from '../contexts';
18
+
19
+ export interface ChangePasswordFormValues {
20
+ currentPassword: string;
21
+ newPassword: string;
22
+ confirmPassword: string;
23
+ }
24
+
25
+ export interface ChangePasswordFormProps {
26
+ onSubmit: (
27
+ values: ChangePasswordFormValues
28
+ ) => Promise<{ success?: boolean; error?: string }>;
29
+ titleText?: string;
30
+ currentPasswordLabel?: string;
31
+ newPasswordLabel?: string;
32
+ confirmPasswordLabel?: string;
33
+ submitButtonText?: string;
34
+ submittingButtonText?: string;
35
+ successMessage?: string;
36
+ currentPasswordValidation?: Yup.StringSchema;
37
+ newPasswordValidation?: Yup.StringSchema;
38
+ confirmPasswordValidation?: Yup.StringSchema;
39
+ }
40
+
41
+ export const ChangePasswordForm: FC<ChangePasswordFormProps> = ({
42
+ onSubmit,
43
+ titleText,
44
+ currentPasswordLabel,
45
+ newPasswordLabel,
46
+ confirmPasswordLabel,
47
+ submitButtonText,
48
+ submittingButtonText,
49
+ successMessage,
50
+ currentPasswordValidation,
51
+ newPasswordValidation,
52
+ confirmPasswordValidation,
53
+ }) => {
54
+ const { tComponent } = useI18n();
55
+ const [success, setSuccess] = useState(false);
56
+ const [apiError, setApiError] = useState<string>('');
57
+
58
+ const validation = {
59
+ currentPassword:
60
+ currentPasswordValidation ||
61
+ Yup.string().required(
62
+ tComponent<SuiteCoreStringKey>(
63
+ SuiteCoreComponentId,
64
+ SuiteCoreStringKey.Validation_Required
65
+ )
66
+ ),
67
+ newPassword:
68
+ newPasswordValidation ||
69
+ Yup.string()
70
+ .min(
71
+ Constants.PasswordMinLength,
72
+ tComponent<SuiteCoreStringKey>(
73
+ SuiteCoreComponentId,
74
+ SuiteCoreStringKey.Validation_PasswordMinLengthTemplate
75
+ )
76
+ )
77
+ .required(
78
+ tComponent<SuiteCoreStringKey>(
79
+ SuiteCoreComponentId,
80
+ SuiteCoreStringKey.Validation_Required
81
+ )
82
+ ),
83
+ confirmPassword:
84
+ confirmPasswordValidation ||
85
+ Yup.string()
86
+ .oneOf(
87
+ [Yup.ref('newPassword')],
88
+ tComponent<SuiteCoreStringKey>(
89
+ SuiteCoreComponentId,
90
+ SuiteCoreStringKey.Validation_PasswordMatch
91
+ )
92
+ )
93
+ .required(
94
+ tComponent<SuiteCoreStringKey>(
95
+ SuiteCoreComponentId,
96
+ SuiteCoreStringKey.Validation_Required
97
+ )
98
+ ),
99
+ };
100
+
101
+ const labels = {
102
+ title:
103
+ titleText ||
104
+ tComponent<SuiteCoreStringKey>(
105
+ SuiteCoreComponentId,
106
+ SuiteCoreStringKey.Common_ChangePassword
107
+ ),
108
+ currentPassword:
109
+ currentPasswordLabel ||
110
+ tComponent<SuiteCoreStringKey>(
111
+ SuiteCoreComponentId,
112
+ SuiteCoreStringKey.Common_CurrentPassword
113
+ ),
114
+ newPassword:
115
+ newPasswordLabel ||
116
+ tComponent<SuiteCoreStringKey>(
117
+ SuiteCoreComponentId,
118
+ SuiteCoreStringKey.Common_NewPassword
119
+ ),
120
+ confirmPassword:
121
+ confirmPasswordLabel ||
122
+ tComponent<SuiteCoreStringKey>(
123
+ SuiteCoreComponentId,
124
+ SuiteCoreStringKey.Common_ConfirmNewPassword
125
+ ),
126
+ submitButton:
127
+ submitButtonText ||
128
+ tComponent<SuiteCoreStringKey>(
129
+ SuiteCoreComponentId,
130
+ SuiteCoreStringKey.Common_ChangePassword
131
+ ),
132
+ submittingButton:
133
+ submittingButtonText ||
134
+ tComponent<SuiteCoreStringKey>(
135
+ SuiteCoreComponentId,
136
+ SuiteCoreStringKey.Common_ChangingPassword
137
+ ),
138
+ success:
139
+ successMessage ||
140
+ tComponent<SuiteCoreStringKey>(
141
+ SuiteCoreComponentId,
142
+ SuiteCoreStringKey.PasswordChange_Success
143
+ ),
144
+ };
145
+
146
+ const formik = useFormik<ChangePasswordFormValues>({
147
+ initialValues: {
148
+ currentPassword: '',
149
+ newPassword: '',
150
+ confirmPassword: '',
151
+ },
152
+ validationSchema: Yup.object({
153
+ currentPassword: validation.currentPassword,
154
+ newPassword: validation.newPassword,
155
+ confirmPassword: validation.confirmPassword,
156
+ }),
157
+ onSubmit: async (values, { resetForm }) => {
158
+ const result = await onSubmit(values);
159
+
160
+ if ('success' in result) {
161
+ setSuccess(true);
162
+ setApiError('');
163
+ resetForm();
164
+ } else if ('error' in result && result.error) {
165
+ setApiError(result.error);
166
+ setSuccess(false);
167
+ }
168
+ },
169
+ });
170
+
171
+ return (
172
+ <Container maxWidth="sm">
173
+ <Box
174
+ sx={{
175
+ mt: 8,
176
+ display: 'flex',
177
+ flexDirection: 'column',
178
+ alignItems: 'center',
179
+ }}
180
+ >
181
+ <Typography variant="h4" component="h1" gutterBottom>
182
+ {labels.title}
183
+ </Typography>
184
+
185
+ <Box
186
+ component="form"
187
+ onSubmit={formik.handleSubmit}
188
+ sx={{ mt: 1, width: '100%' }}
189
+ >
190
+ <TextField
191
+ fullWidth
192
+ id="currentPassword"
193
+ name="currentPassword"
194
+ label={labels.currentPassword}
195
+ type="password"
196
+ value={formik.values.currentPassword}
197
+ onChange={formik.handleChange}
198
+ onBlur={formik.handleBlur}
199
+ error={Boolean(
200
+ formik.touched.currentPassword && formik.errors.currentPassword
201
+ )}
202
+ helperText={
203
+ formik.touched.currentPassword && formik.errors.currentPassword
204
+ }
205
+ margin="normal"
206
+ />
207
+
208
+ <TextField
209
+ fullWidth
210
+ id="newPassword"
211
+ name="newPassword"
212
+ label={labels.newPassword}
213
+ type="password"
214
+ value={formik.values.newPassword}
215
+ onChange={formik.handleChange}
216
+ onBlur={formik.handleBlur}
217
+ error={Boolean(
218
+ formik.touched.newPassword && formik.errors.newPassword
219
+ )}
220
+ helperText={formik.touched.newPassword && formik.errors.newPassword}
221
+ margin="normal"
222
+ />
223
+
224
+ <TextField
225
+ fullWidth
226
+ id="confirmPassword"
227
+ name="confirmPassword"
228
+ label={labels.confirmPassword}
229
+ type="password"
230
+ value={formik.values.confirmPassword}
231
+ onChange={formik.handleChange}
232
+ onBlur={formik.handleBlur}
233
+ error={Boolean(
234
+ formik.touched.confirmPassword && formik.errors.confirmPassword
235
+ )}
236
+ helperText={
237
+ formik.touched.confirmPassword && formik.errors.confirmPassword
238
+ }
239
+ margin="normal"
240
+ />
241
+
242
+ {apiError && (
243
+ <Alert severity="error" sx={{ mt: 2, mb: 2 }}>
244
+ {apiError}
245
+ </Alert>
246
+ )}
247
+
248
+ {success && (
249
+ <Alert severity="success" sx={{ mt: 2, mb: 2 }}>
250
+ {labels.success}
251
+ </Alert>
252
+ )}
253
+
254
+ <Button
255
+ type="submit"
256
+ fullWidth
257
+ variant="contained"
258
+ color="primary"
259
+ sx={{ mt: 3, mb: 2 }}
260
+ disabled={formik.isSubmitting}
261
+ >
262
+ {formik.isSubmitting
263
+ ? labels.submittingButton
264
+ : labels.submitButton}
265
+ </Button>
266
+ </Box>
267
+ </Box>
268
+ </Container>
269
+ );
270
+ };
271
+
272
+ export default ChangePasswordForm;
@@ -0,0 +1,48 @@
1
+ import {
2
+ Button,
3
+ Dialog,
4
+ DialogActions,
5
+ DialogContent,
6
+ DialogContentText,
7
+ DialogTitle,
8
+ } from '@mui/material';
9
+ import React from 'react';
10
+
11
+ export interface ConfirmationDialogProps {
12
+ open: boolean;
13
+ title: string;
14
+ message: string;
15
+ confirmText?: string;
16
+ cancelText?: string;
17
+ onConfirm: () => void;
18
+ onCancel: () => void;
19
+ }
20
+
21
+ export const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
22
+ open,
23
+ title,
24
+ message,
25
+ confirmText = 'Confirm',
26
+ cancelText = 'Cancel',
27
+ onConfirm,
28
+ onCancel,
29
+ }) => {
30
+ return (
31
+ <Dialog open={open} onClose={onCancel}>
32
+ <DialogTitle>{title}</DialogTitle>
33
+ <DialogContent>
34
+ <DialogContentText>{message}</DialogContentText>
35
+ </DialogContent>
36
+ <DialogActions>
37
+ <Button onClick={onCancel} color="primary">
38
+ {cancelText}
39
+ </Button>
40
+ <Button onClick={onConfirm} color="primary">
41
+ {confirmText}
42
+ </Button>
43
+ </DialogActions>
44
+ </Dialog>
45
+ );
46
+ };
47
+
48
+ export default ConfirmationDialog;
@@ -0,0 +1,60 @@
1
+ import { CurrencyCode } from '@digitaldefiance/i18n-lib';
2
+ import { MenuItem, TextField } from '@mui/material';
3
+ import { Field, FieldProps } from 'formik';
4
+ import { ChangeEvent, FC } from 'react';
5
+
6
+ export interface CurrencyCodeSelectorProps {
7
+ name: string;
8
+ label: string;
9
+ onCurrencyChange?: (code: string) => void;
10
+ }
11
+
12
+ export const CurrencyCodeSelector: FC<CurrencyCodeSelectorProps> = ({
13
+ name,
14
+ label,
15
+ onCurrencyChange,
16
+ }) => {
17
+ return (
18
+ <Field name={name}>
19
+ {({ field, form }: FieldProps) => (
20
+ <TextField
21
+ select
22
+ fullWidth
23
+ label={label}
24
+ {...field}
25
+ onChange={(event: ChangeEvent<HTMLInputElement>) => {
26
+ const selectedCode = event.target.value;
27
+ form.setFieldValue(name, selectedCode);
28
+ onCurrencyChange?.(selectedCode);
29
+ }}
30
+ error={form.touched[name] && Boolean(form.errors[name])}
31
+ helperText={form.touched[name] && (form.errors[name] as string)}
32
+ sx={{
33
+ '& .MuiSelect-select': {
34
+ paddingRight: '32px',
35
+ },
36
+ '& .MuiOutlinedInput-root': {
37
+ '& fieldset': {
38
+ borderColor: 'rgba(0, 0, 0, 0.23)',
39
+ },
40
+ '&:hover fieldset': {
41
+ borderColor: 'rgba(0, 0, 0, 0.87)',
42
+ },
43
+ '&.Mui-focused fieldset': {
44
+ borderColor: 'primary.main',
45
+ },
46
+ },
47
+ }}
48
+ >
49
+ {CurrencyCode.getAll().map((code: string) => (
50
+ <MenuItem key={code} value={code}>
51
+ {code}
52
+ </MenuItem>
53
+ ))}
54
+ </TextField>
55
+ )}
56
+ </Field>
57
+ );
58
+ };
59
+
60
+ export default CurrencyCodeSelector;