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

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 (311) hide show
  1. package/package.json +5 -4
  2. package/src/auth/AdminRoute.d.ts +8 -0
  3. package/src/auth/AdminRoute.d.ts.map +1 -0
  4. package/src/auth/AdminRoute.js +28 -0
  5. package/src/auth/Private.d.ts +6 -0
  6. package/src/auth/Private.d.ts.map +1 -0
  7. package/src/auth/Private.js +14 -0
  8. package/src/auth/PrivateRoute.d.ts +8 -0
  9. package/src/auth/PrivateRoute.d.ts.map +1 -0
  10. package/src/auth/PrivateRoute.js +23 -0
  11. package/src/auth/UnAuth.d.ts +6 -0
  12. package/src/auth/UnAuth.d.ts.map +1 -0
  13. package/src/auth/UnAuth.js +14 -0
  14. package/src/auth/UnAuthRoute.d.ts +8 -0
  15. package/src/auth/UnAuthRoute.d.ts.map +1 -0
  16. package/src/auth/UnAuthRoute.js +22 -0
  17. package/src/auth/{index.ts → index.d.ts} +3 -2
  18. package/src/auth/index.d.ts.map +1 -0
  19. package/src/auth/index.js +12 -0
  20. package/src/components/ApiAccess.d.ts +16 -0
  21. package/src/components/ApiAccess.d.ts.map +1 -0
  22. package/src/components/ApiAccess.js +77 -0
  23. package/src/components/BackupCodeLoginForm.d.ts +43 -0
  24. package/src/components/BackupCodeLoginForm.d.ts.map +1 -0
  25. package/src/components/BackupCodeLoginForm.js +139 -0
  26. package/src/components/BackupCodesForm.d.ts +26 -0
  27. package/src/components/BackupCodesForm.d.ts.map +1 -0
  28. package/src/components/BackupCodesForm.js +120 -0
  29. package/src/components/CategoryMenu.d.ts +17 -0
  30. package/src/components/CategoryMenu.d.ts.map +1 -0
  31. package/src/components/CategoryMenu.js +65 -0
  32. package/src/components/ChangePasswordForm.d.ts +26 -0
  33. package/src/components/ChangePasswordForm.d.ts.map +1 -0
  34. package/src/components/ChangePasswordForm.js +78 -0
  35. package/src/components/ConfirmationDialog.d.ts +13 -0
  36. package/src/components/ConfirmationDialog.d.ts.map +1 -0
  37. package/src/components/ConfirmationDialog.js +10 -0
  38. package/src/components/CurrencyCodeSelector.d.ts +9 -0
  39. package/src/components/CurrencyCodeSelector.d.ts.map +1 -0
  40. package/src/components/CurrencyCodeSelector.js +31 -0
  41. package/src/components/CurrencyInput.d.ts +13 -0
  42. package/src/components/CurrencyInput.d.ts.map +1 -0
  43. package/src/components/CurrencyInput.js +22 -0
  44. package/src/components/DashboardPage.d.ts +8 -0
  45. package/src/components/DashboardPage.d.ts.map +1 -0
  46. package/src/components/DashboardPage.js +10 -0
  47. package/src/components/DropdownMenu.d.ts +13 -0
  48. package/src/components/DropdownMenu.d.ts.map +1 -0
  49. package/src/components/DropdownMenu.js +63 -0
  50. package/src/components/ExpirationSecondsSelector.d.ts +13 -0
  51. package/src/components/ExpirationSecondsSelector.d.ts.map +1 -0
  52. package/src/components/ExpirationSecondsSelector.js +32 -0
  53. package/src/components/FakeEmailAdminPanel.d.ts +23 -0
  54. package/src/components/FakeEmailAdminPanel.d.ts.map +1 -0
  55. package/src/components/FakeEmailAdminPanel.js +147 -0
  56. package/src/components/Flag.d.ts +20 -0
  57. package/src/components/Flag.d.ts.map +1 -0
  58. package/src/components/Flag.js +43 -0
  59. package/src/components/ForgotPasswordForm.d.ts +18 -0
  60. package/src/components/ForgotPasswordForm.d.ts.map +1 -0
  61. package/src/components/ForgotPasswordForm.js +61 -0
  62. package/src/components/LoginForm.d.ts +44 -0
  63. package/src/components/LoginForm.d.ts.map +1 -0
  64. package/src/components/LoginForm.js +123 -0
  65. package/src/components/LogoutPage.d.ts +8 -0
  66. package/src/components/LogoutPage.d.ts.map +1 -0
  67. package/src/components/LogoutPage.js +16 -0
  68. package/src/components/RegisterForm.d.ts +100 -0
  69. package/src/components/RegisterForm.d.ts.map +1 -0
  70. package/src/components/RegisterForm.js +306 -0
  71. package/src/components/ResetPasswordForm.d.ts +23 -0
  72. package/src/components/ResetPasswordForm.d.ts.map +1 -0
  73. package/src/components/ResetPasswordForm.js +78 -0
  74. package/src/components/SideMenu.d.ts +8 -0
  75. package/src/components/SideMenu.d.ts.map +1 -0
  76. package/src/components/SideMenu.js +25 -0
  77. package/src/components/SideMenuListItem.d.ts +13 -0
  78. package/src/components/SideMenuListItem.d.ts.map +1 -0
  79. package/src/components/SideMenuListItem.js +44 -0
  80. package/src/components/T.d.ts +7 -0
  81. package/src/components/T.d.ts.map +1 -0
  82. package/src/components/T.js +13 -0
  83. package/src/components/TDiv.d.ts +9 -0
  84. package/src/components/TDiv.d.ts.map +1 -0
  85. package/src/components/TDiv.js +14 -0
  86. package/src/components/TImgAlt.d.ts +10 -0
  87. package/src/components/TImgAlt.d.ts.map +1 -0
  88. package/src/components/TImgAlt.js +14 -0
  89. package/src/components/TopMenu.d.ts +50 -0
  90. package/src/components/TopMenu.d.ts.map +1 -0
  91. package/src/components/TopMenu.js +79 -0
  92. package/src/components/TotpSetupForm.d.ts +24 -0
  93. package/src/components/TotpSetupForm.d.ts.map +1 -0
  94. package/src/components/TotpSetupForm.js +82 -0
  95. package/src/components/TotpVerificationForm.d.ts +23 -0
  96. package/src/components/TotpVerificationForm.d.ts.map +1 -0
  97. package/src/components/TotpVerificationForm.js +66 -0
  98. package/src/components/TranslatedTitle.d.ts +8 -0
  99. package/src/components/TranslatedTitle.d.ts.map +1 -0
  100. package/src/components/TranslatedTitle.js +15 -0
  101. package/src/components/UserLanguageSelector.d.ts +4 -0
  102. package/src/components/UserLanguageSelector.d.ts.map +1 -0
  103. package/src/components/UserLanguageSelector.js +31 -0
  104. package/src/components/UserMenu.d.ts +4 -0
  105. package/src/components/UserMenu.d.ts.map +1 -0
  106. package/src/components/UserMenu.js +12 -0
  107. package/src/components/UserSettingsForm.d.ts +113 -0
  108. package/src/components/UserSettingsForm.d.ts.map +1 -0
  109. package/src/components/UserSettingsForm.js +269 -0
  110. package/src/components/VerifyEmailPage.d.ts +24 -0
  111. package/src/components/VerifyEmailPage.d.ts.map +1 -0
  112. package/src/components/VerifyEmailPage.js +81 -0
  113. package/src/components/{index.ts → index.d.ts} +1 -1
  114. package/src/components/index.d.ts.map +1 -0
  115. package/src/components/index.js +35 -0
  116. package/src/contexts/AuthProvider.d.ts +186 -0
  117. package/src/contexts/AuthProvider.d.ts.map +1 -0
  118. package/src/contexts/AuthProvider.js +515 -0
  119. package/src/contexts/I18nProvider.d.ts +27 -0
  120. package/src/contexts/I18nProvider.d.ts.map +1 -0
  121. package/src/contexts/I18nProvider.js +60 -0
  122. package/src/contexts/MenuContext.d.ts +28 -0
  123. package/src/contexts/MenuContext.d.ts.map +1 -0
  124. package/src/contexts/MenuContext.js +292 -0
  125. package/src/contexts/SuiteConfigProvider.d.ts +44 -0
  126. package/src/contexts/SuiteConfigProvider.d.ts.map +1 -0
  127. package/src/contexts/SuiteConfigProvider.js +43 -0
  128. package/src/contexts/ThemeProvider.d.ts +15 -0
  129. package/src/contexts/ThemeProvider.d.ts.map +1 -0
  130. package/src/contexts/ThemeProvider.js +36 -0
  131. package/src/contexts/{index.ts → index.d.ts} +1 -0
  132. package/src/contexts/index.d.ts.map +1 -0
  133. package/src/contexts/index.js +8 -0
  134. package/src/hooks/{index.ts → index.d.ts} +1 -0
  135. package/src/hooks/index.d.ts.map +1 -0
  136. package/src/hooks/index.js +9 -0
  137. package/src/hooks/useAuthenticatedApi.d.ts +34 -0
  138. package/src/hooks/useAuthenticatedApi.d.ts.map +1 -0
  139. package/src/hooks/useAuthenticatedApi.js +46 -0
  140. package/src/hooks/useBackupCodes.d.ts +15 -0
  141. package/src/hooks/useBackupCodes.d.ts.map +1 -0
  142. package/src/hooks/useBackupCodes.js +74 -0
  143. package/src/hooks/useEmailVerification.d.ts +10 -0
  144. package/src/hooks/useEmailVerification.d.ts.map +1 -0
  145. package/src/hooks/useEmailVerification.js +40 -0
  146. package/src/hooks/useExpiringValue.d.ts +14 -0
  147. package/src/hooks/useExpiringValue.d.ts.map +1 -0
  148. package/src/hooks/useExpiringValue.js +53 -0
  149. package/src/hooks/useLocalStorage.d.ts +2 -0
  150. package/src/hooks/useLocalStorage.d.ts.map +1 -0
  151. package/src/hooks/useLocalStorage.js +15 -0
  152. package/src/hooks/useUserSettings.d.ts +48 -0
  153. package/src/hooks/useUserSettings.d.ts.map +1 -0
  154. package/src/hooks/useUserSettings.js +171 -0
  155. package/src/{index.ts → index.d.ts} +1 -1
  156. package/src/index.d.ts.map +1 -0
  157. package/src/index.js +12 -0
  158. package/src/interfaces/IAppConfig.d.ts +8 -0
  159. package/src/interfaces/IAppConfig.d.ts.map +1 -0
  160. package/src/interfaces/IAppConfig.js +2 -0
  161. package/src/interfaces/ICategoryConfig.d.ts +41 -0
  162. package/src/interfaces/ICategoryConfig.d.ts.map +1 -0
  163. package/src/interfaces/ICategoryConfig.js +2 -0
  164. package/src/interfaces/IMenuConfig.d.ts +28 -0
  165. package/src/interfaces/IMenuConfig.d.ts.map +1 -0
  166. package/src/interfaces/IMenuConfig.js +2 -0
  167. package/src/interfaces/IMenuOption.d.ts +82 -0
  168. package/src/interfaces/IMenuOption.d.ts.map +1 -0
  169. package/src/interfaces/IMenuOption.js +2 -0
  170. package/src/interfaces/{index.ts → index.d.ts} +1 -0
  171. package/src/interfaces/index.d.ts.map +1 -0
  172. package/src/interfaces/index.js +7 -0
  173. package/src/services/__mocks__/authService.d.ts +21 -0
  174. package/src/services/__mocks__/authService.d.ts.map +1 -0
  175. package/src/services/__mocks__/authService.js +15 -0
  176. package/src/services/api.d.ts +3 -0
  177. package/src/services/api.d.ts.map +1 -0
  178. package/src/services/api.js +14 -0
  179. package/src/services/authService.d.ts +126 -0
  180. package/src/services/authService.d.ts.map +1 -0
  181. package/src/services/authService.js +471 -0
  182. package/src/services/authenticatedApi.d.ts +3 -0
  183. package/src/services/authenticatedApi.d.ts.map +1 -0
  184. package/src/services/authenticatedApi.js +18 -0
  185. package/src/services/index.d.ts +4 -0
  186. package/src/services/index.d.ts.map +1 -0
  187. package/src/services/index.js +6 -0
  188. package/src/types/MenuType.d.ts +11 -0
  189. package/src/types/MenuType.d.ts.map +1 -0
  190. package/src/types/MenuType.js +12 -0
  191. package/src/types/expirationSeconds.d.ts +3 -0
  192. package/src/types/expirationSeconds.d.ts.map +1 -0
  193. package/src/types/expirationSeconds.js +17 -0
  194. package/src/types/index.d.ts +2 -0
  195. package/src/types/index.d.ts.map +1 -0
  196. package/src/types/index.js +4 -0
  197. package/src/types/translation.d.ts +10 -0
  198. package/src/types/translation.d.ts.map +1 -0
  199. package/src/types/translation.js +9 -0
  200. package/src/wrappers/BackupCodeLoginWrapper.d.ts +8 -0
  201. package/src/wrappers/BackupCodeLoginWrapper.d.ts.map +1 -0
  202. package/src/wrappers/BackupCodeLoginWrapper.js +20 -0
  203. package/src/wrappers/BackupCodesWrapper.d.ts +7 -0
  204. package/src/wrappers/BackupCodesWrapper.d.ts.map +1 -0
  205. package/src/wrappers/BackupCodesWrapper.js +17 -0
  206. package/src/wrappers/ChangePasswordFormWrapper.d.ts +8 -0
  207. package/src/wrappers/ChangePasswordFormWrapper.d.ts.map +1 -0
  208. package/src/wrappers/ChangePasswordFormWrapper.js +21 -0
  209. package/src/wrappers/ForgotPasswordFormWrapper.d.ts +8 -0
  210. package/src/wrappers/ForgotPasswordFormWrapper.d.ts.map +1 -0
  211. package/src/wrappers/ForgotPasswordFormWrapper.js +29 -0
  212. package/src/wrappers/LoginFormWrapper.d.ts +21 -0
  213. package/src/wrappers/LoginFormWrapper.d.ts.map +1 -0
  214. package/src/wrappers/LoginFormWrapper.js +72 -0
  215. package/src/wrappers/LogoutPageWrapper.d.ts +9 -0
  216. package/src/wrappers/LogoutPageWrapper.d.ts.map +1 -0
  217. package/src/wrappers/LogoutPageWrapper.js +21 -0
  218. package/src/wrappers/RegisterFormWrapper.d.ts +20 -0
  219. package/src/wrappers/RegisterFormWrapper.d.ts.map +1 -0
  220. package/src/wrappers/RegisterFormWrapper.js +51 -0
  221. package/src/wrappers/ResetPasswordFormWrapper.d.ts +9 -0
  222. package/src/wrappers/ResetPasswordFormWrapper.d.ts.map +1 -0
  223. package/src/wrappers/ResetPasswordFormWrapper.js +34 -0
  224. package/src/wrappers/UserSettingsFormWrapper.d.ts +29 -0
  225. package/src/wrappers/UserSettingsFormWrapper.d.ts.map +1 -0
  226. package/src/wrappers/UserSettingsFormWrapper.js +95 -0
  227. package/src/wrappers/VerifyEmailPageWrapper.d.ts +8 -0
  228. package/src/wrappers/VerifyEmailPageWrapper.d.ts.map +1 -0
  229. package/src/wrappers/VerifyEmailPageWrapper.js +21 -0
  230. package/src/wrappers/{index.tsx → index.d.ts} +1 -10
  231. package/src/wrappers/index.d.ts.map +1 -0
  232. package/src/wrappers/index.js +24 -0
  233. package/LICENSE +0 -21
  234. package/src/auth/AdminRoute.tsx +0 -54
  235. package/src/auth/Private.tsx +0 -17
  236. package/src/auth/PrivateRoute.tsx +0 -28
  237. package/src/auth/UnAuth.tsx +0 -16
  238. package/src/auth/UnAuthRoute.tsx +0 -30
  239. package/src/components/ApiAccess.tsx +0 -175
  240. package/src/components/BackupCodeLoginForm.tsx +0 -489
  241. package/src/components/BackupCodesForm.tsx +0 -287
  242. package/src/components/CategoryMenu.tsx +0 -155
  243. package/src/components/ChangePasswordForm.tsx +0 -273
  244. package/src/components/ConfirmationDialog.tsx +0 -48
  245. package/src/components/CurrencyCodeSelector.tsx +0 -60
  246. package/src/components/CurrencyInput.tsx +0 -80
  247. package/src/components/DashboardPage.tsx +0 -24
  248. package/src/components/DropdownMenu.tsx +0 -105
  249. package/src/components/ExpirationSecondsSelector.tsx +0 -60
  250. package/src/components/FakeEmailAdminPanel.tsx +0 -374
  251. package/src/components/Flag.tsx +0 -52
  252. package/src/components/ForgotPasswordForm.tsx +0 -174
  253. package/src/components/LoginForm.tsx +0 -468
  254. package/src/components/LogoutPage.tsx +0 -21
  255. package/src/components/RegisterForm.tsx +0 -1169
  256. package/src/components/ResetPasswordForm.tsx +0 -247
  257. package/src/components/SideMenu.tsx +0 -46
  258. package/src/components/SideMenuListItem.tsx +0 -75
  259. package/src/components/T.tsx +0 -24
  260. package/src/components/TDiv.tsx +0 -26
  261. package/src/components/TImgAlt.tsx +0 -28
  262. package/src/components/TopMenu.tsx +0 -252
  263. package/src/components/TotpSetupForm.tsx +0 -188
  264. package/src/components/TotpVerificationForm.tsx +0 -148
  265. package/src/components/TranslatedTitle.tsx +0 -31
  266. package/src/components/UserLanguageSelector.tsx +0 -45
  267. package/src/components/UserMenu.tsx +0 -15
  268. package/src/components/UserSettingsForm.tsx +0 -904
  269. package/src/components/VerifyEmailPage.tsx +0 -222
  270. package/src/components/__tests__/RegisterForm.totp.spec.tsx +0 -377
  271. package/src/components/__tests__/TotpSetupForm.spec.tsx +0 -258
  272. package/src/components/__tests__/TotpVerificationForm.spec.tsx +0 -259
  273. package/src/components/__tests__/UserSettingsForm.totp.spec.tsx +0 -396
  274. package/src/contexts/AuthProvider.spec.tsx +0 -1284
  275. package/src/contexts/AuthProvider.tsx +0 -977
  276. package/src/contexts/I18nProvider.tsx +0 -164
  277. package/src/contexts/MenuContext.tsx +0 -426
  278. package/src/contexts/SuiteConfigProvider.tsx +0 -93
  279. package/src/contexts/ThemeProvider.tsx +0 -67
  280. package/src/hooks/useAuthenticatedApi.tsx +0 -67
  281. package/src/hooks/useBackupCodes.ts +0 -105
  282. package/src/hooks/useEmailVerification.ts +0 -54
  283. package/src/hooks/useExpiringValue.ts +0 -78
  284. package/src/hooks/useLocalStorage.ts +0 -18
  285. package/src/hooks/useUserSettings.ts +0 -271
  286. package/src/interfaces/IAppConfig.ts +0 -7
  287. package/src/interfaces/ICategoryConfig.ts +0 -41
  288. package/src/interfaces/IMenuConfig.ts +0 -28
  289. package/src/interfaces/IMenuOption.ts +0 -79
  290. package/src/services/__mocks__/authService.ts +0 -14
  291. package/src/services/__tests__/authService.totp.property.spec.ts +0 -360
  292. package/src/services/__tests__/authService.totp.spec.ts +0 -439
  293. package/src/services/api.ts +0 -13
  294. package/src/services/authService.ts +0 -692
  295. package/src/services/authenticatedApi.ts +0 -17
  296. package/src/services/index.ts +0 -3
  297. package/src/types/MenuType.ts +0 -15
  298. package/src/types/expirationSeconds.ts +0 -18
  299. package/src/types/index.ts +0 -1
  300. package/src/types/translation.ts +0 -20
  301. package/src/wrappers/BackupCodeLoginWrapper.tsx +0 -34
  302. package/src/wrappers/BackupCodesWrapper.tsx +0 -28
  303. package/src/wrappers/ChangePasswordFormWrapper.tsx +0 -34
  304. package/src/wrappers/ForgotPasswordFormWrapper.tsx +0 -45
  305. package/src/wrappers/LoginFormWrapper.tsx +0 -108
  306. package/src/wrappers/LogoutPageWrapper.tsx +0 -30
  307. package/src/wrappers/RegisterFormWrapper.tsx +0 -109
  308. package/src/wrappers/ResetPasswordFormWrapper.tsx +0 -57
  309. package/src/wrappers/UserSettingsFormWrapper.tsx +0 -140
  310. package/src/wrappers/VerifyEmailPageWrapper.tsx +0 -27
  311. package/src/wrappers/__tests__/LoginFormWrapper.totp.spec.tsx +0 -339
@@ -1,374 +0,0 @@
1
- /**
2
- * FakeEmailAdminPanel — dev-only panel for inspecting captured emails.
3
- *
4
- * Renders only in development/test environments where FakeEmailService is
5
- * active. Calls the AdminEmailRouter endpoints mounted at `baseUrl/api/admin/emails`.
6
- *
7
- * Self-contained: wraps itself with AuthenticatedApiProvider internally.
8
- * Usage (inside SuiteConfigProvider):
9
- * <FakeEmailAdminPanel />
10
- */
11
- import {
12
- SuiteCoreComponentId,
13
- SuiteCoreStringKey,
14
- SuiteCoreStringKeyValue,
15
- } from '@digitaldefiance/suite-core-lib';
16
- import DeleteIcon from '@mui/icons-material/Delete';
17
- import ExpandLessIcon from '@mui/icons-material/ExpandLess';
18
- import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
19
- import RefreshIcon from '@mui/icons-material/Refresh';
20
- import {
21
- Alert,
22
- Box,
23
- Button,
24
- Chip,
25
- CircularProgress,
26
- Collapse,
27
- Divider,
28
- IconButton,
29
- List,
30
- ListItemButton,
31
- ListItemText,
32
- Paper,
33
- Typography,
34
- } from '@mui/material';
35
- import { FC, useCallback, useEffect, useRef, useState } from 'react';
36
- import { useI18n } from '../contexts';
37
- import { ConfirmationDialog } from './ConfirmationDialog';
38
- import {
39
- AuthenticatedApiProvider,
40
- useAuthenticatedApi,
41
- } from '../hooks/useAuthenticatedApi';
42
-
43
- /** Shape of a single captured email as returned by AdminEmailRouter */
44
- export interface CapturedEmailInfo {
45
- to: string;
46
- subject: string;
47
- text: string;
48
- html: string;
49
- timestamp: string;
50
- }
51
-
52
- /** Shape of a recipient summary as returned by GET /api/admin/emails */
53
- export interface RecipientSummary {
54
- address: string;
55
- count: number;
56
- }
57
-
58
- /**
59
- * Renders email HTML content in a sandboxed iframe so that links are
60
- * clickable and open in a new tab. Falls back to plain text when no
61
- * HTML body is available.
62
- */
63
- const EmailBodyIframe: FC<{ html: string; text: string }> = ({
64
- html,
65
- text,
66
- }) => {
67
- const iframeRef = useRef<HTMLIFrameElement>(null);
68
-
69
- useEffect(() => {
70
- const iframe = iframeRef.current;
71
- if (!iframe) return;
72
- const doc = iframe.contentDocument ?? iframe.contentWindow?.document;
73
- if (!doc) return;
74
-
75
- if (html) {
76
- // Inject a <base target="_blank"> so every link opens in a new tab,
77
- // then write the original HTML body.
78
- doc.open();
79
- doc.write(
80
- `<!DOCTYPE html><html><head><base target="_blank"><style>body{font-family:sans-serif;font-size:14px;margin:8px;}</style></head><body>${html}</body></html>`
81
- );
82
- doc.close();
83
- } else {
84
- doc.open();
85
- doc.write(
86
- `<!DOCTYPE html><html><head><style>body{font-family:sans-serif;font-size:14px;margin:8px;white-space:pre-wrap;}</style></head><body>${text.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</body></html>`
87
- );
88
- doc.close();
89
- }
90
-
91
- // Auto-resize iframe height to fit content
92
- const resize = () => {
93
- if (doc.body) {
94
- iframe.style.height = `${doc.body.scrollHeight + 16}px`;
95
- }
96
- };
97
- // Resize after content loads (images, etc.)
98
- iframe.addEventListener('load', resize);
99
- resize();
100
- // Small delay to catch late-rendering content
101
- const timer = setTimeout(resize, 200);
102
- return () => {
103
- iframe.removeEventListener('load', resize);
104
- clearTimeout(timer);
105
- };
106
- }, [html, text]);
107
-
108
- return (
109
- <iframe
110
- ref={iframeRef}
111
- sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin"
112
- style={{
113
- width: '100%',
114
- minHeight: 80,
115
- border: '1px solid #e0e0e0',
116
- borderRadius: 4,
117
- background: '#fff',
118
- }}
119
- title="Email body"
120
- />
121
- );
122
- };
123
-
124
- const FakeEmailAdminPanelContent: FC = () => {
125
- const api = useAuthenticatedApi();
126
- const { tComponent } = useI18n();
127
- const t = (key: SuiteCoreStringKeyValue) =>
128
- tComponent<SuiteCoreStringKeyValue>(SuiteCoreComponentId, key);
129
-
130
- const [recipients, setRecipients] = useState<RecipientSummary[]>([]);
131
- const [expanded, setExpanded] = useState<string | null>(null);
132
- /** Tracks which individual email is expanded to show its body (address:idx) */
133
- const [expandedEmail, setExpandedEmail] = useState<string | null>(null);
134
- const [emailsByAddress, setEmailsByAddress] = useState<
135
- Record<string, CapturedEmailInfo[]>
136
- >({});
137
- const [loading, setLoading] = useState(false);
138
- const [error, setError] = useState<string | null>(null);
139
- const [clearDialogOpen, setClearDialogOpen] = useState(false);
140
-
141
- const loadRecipients = useCallback(async () => {
142
- setLoading(true);
143
- setError(null);
144
- try {
145
- const res = await api.get<{ recipients: RecipientSummary[] }>(
146
- '/admin/emails'
147
- );
148
- setRecipients(res.data.recipients);
149
- } catch (err) {
150
- setError(err instanceof Error ? err.message : String(err));
151
- } finally {
152
- setLoading(false);
153
- }
154
- }, [api]);
155
-
156
- useEffect(() => {
157
- void loadRecipients();
158
- }, [loadRecipients]);
159
-
160
- const toggleExpand = useCallback(
161
- async (address: string) => {
162
- if (expanded === address) {
163
- setExpanded(null);
164
- return;
165
- }
166
- setExpanded(address);
167
- if (!emailsByAddress[address]) {
168
- try {
169
- const res = await api.get<CapturedEmailInfo[]>(
170
- `/admin/emails/${encodeURIComponent(address)}`
171
- );
172
- setEmailsByAddress((prev) => ({ ...prev, [address]: res.data }));
173
- } catch (err) {
174
- setError(err instanceof Error ? err.message : String(err));
175
- }
176
- }
177
- },
178
- [api, expanded, emailsByAddress]
179
- );
180
-
181
- const handleClearAll = useCallback(async () => {
182
- setClearDialogOpen(false);
183
- try {
184
- await api.delete('/admin/emails');
185
- setRecipients([]);
186
- setEmailsByAddress({});
187
- setExpanded(null);
188
- } catch (err) {
189
- setError(err instanceof Error ? err.message : String(err));
190
- }
191
- }, [api]);
192
-
193
- return (
194
- <Paper variant="outlined" sx={{ p: 2 }}>
195
- {/* Header */}
196
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
197
- <Typography variant="subtitle1" fontWeight="bold" sx={{ flexGrow: 1 }}>
198
- {t(SuiteCoreStringKey.FakeEmail_Admin_Title)}
199
- </Typography>
200
- <Chip label="DEV" size="small" color="warning" />
201
- <IconButton
202
- size="small"
203
- onClick={() => void loadRecipients()}
204
- title={t(SuiteCoreStringKey.FakeEmail_Admin_Refresh)}
205
- >
206
- <RefreshIcon fontSize="small" />
207
- </IconButton>
208
- <IconButton
209
- size="small"
210
- color="error"
211
- onClick={() => setClearDialogOpen(true)}
212
- title={t(SuiteCoreStringKey.FakeEmail_Admin_ClearAll)}
213
- disabled={recipients.length === 0}
214
- >
215
- <DeleteIcon fontSize="small" />
216
- </IconButton>
217
- </Box>
218
-
219
- <Divider sx={{ mb: 1 }} />
220
-
221
- {/* Error */}
222
- {error && (
223
- <Alert severity="error" sx={{ mb: 1 }} onClose={() => setError(null)}>
224
- {error}
225
- </Alert>
226
- )}
227
-
228
- {/* Loading */}
229
- {loading && (
230
- <Box sx={{ display: 'flex', justifyContent: 'center', py: 2 }}>
231
- <CircularProgress size={24} />
232
- </Box>
233
- )}
234
-
235
- {/* Empty state */}
236
- {!loading && recipients.length === 0 && (
237
- <Typography variant="body2" color="text.secondary">
238
- {t(SuiteCoreStringKey.FakeEmail_Admin_NoRecipients)}
239
- </Typography>
240
- )}
241
-
242
- {/* Recipient list */}
243
- {!loading && recipients.length > 0 && (
244
- <List dense disablePadding>
245
- {recipients.map((r) => (
246
- <Box key={r.address}>
247
- <ListItemButton
248
- onClick={() => void toggleExpand(r.address)}
249
- sx={{ px: 0, py: 0.5 }}
250
- >
251
- <ListItemText
252
- primary={r.address}
253
- secondary={`${r.count} ${t(SuiteCoreStringKey.FakeEmail_Admin_Recipients)}`}
254
- />
255
- {expanded === r.address ? (
256
- <ExpandLessIcon fontSize="small" />
257
- ) : (
258
- <ExpandMoreIcon fontSize="small" />
259
- )}
260
- </ListItemButton>
261
-
262
- <Collapse in={expanded === r.address} unmountOnExit>
263
- {emailsByAddress[r.address]?.length === 0 ? (
264
- <Typography
265
- variant="body2"
266
- color="text.secondary"
267
- sx={{ pl: 2, py: 0.5 }}
268
- >
269
- {t(SuiteCoreStringKey.FakeEmail_Admin_NoEmails)}
270
- </Typography>
271
- ) : (
272
- <List dense disablePadding sx={{ pl: 2 }}>
273
- {(emailsByAddress[r.address] ?? []).map((email, idx) => {
274
- const emailKey = `${r.address}:${idx}`;
275
- const isEmailExpanded = expandedEmail === emailKey;
276
- return (
277
- <Box key={idx} sx={{ py: 0.25 }}>
278
- <ListItemButton
279
- disableGutters
280
- onClick={() =>
281
- setExpandedEmail(isEmailExpanded ? null : emailKey)
282
- }
283
- sx={{ py: 0.25, px: 0.5, borderRadius: 1 }}
284
- >
285
- <ListItemText
286
- primary={email.subject || '(no subject)'}
287
- secondary={`${t(SuiteCoreStringKey.FakeEmail_Admin_Timestamp)}: ${new Date(email.timestamp).toLocaleString()}`}
288
- primaryTypographyProps={{ variant: 'body2' }}
289
- secondaryTypographyProps={{ variant: 'caption' }}
290
- />
291
- {isEmailExpanded ? (
292
- <ExpandLessIcon fontSize="small" />
293
- ) : (
294
- <ExpandMoreIcon fontSize="small" />
295
- )}
296
- </ListItemButton>
297
- <Collapse in={isEmailExpanded} unmountOnExit>
298
- <Box sx={{ pl: 1, pr: 1, pb: 1 }}>
299
- <EmailBodyIframe
300
- html={email.html}
301
- text={email.text}
302
- />
303
- </Box>
304
- </Collapse>
305
- </Box>
306
- );
307
- })}
308
- </List>
309
- )}
310
- </Collapse>
311
- <Divider />
312
- </Box>
313
- ))}
314
- </List>
315
- )}
316
-
317
- {/* Clear confirmation */}
318
- <ConfirmationDialog
319
- open={clearDialogOpen}
320
- title={t(SuiteCoreStringKey.FakeEmail_Admin_ClearAll)}
321
- message={t(SuiteCoreStringKey.FakeEmail_Admin_ClearConfirm)}
322
- onConfirm={() => void handleClearAll()}
323
- onCancel={() => setClearDialogOpen(false)}
324
- />
325
- </Paper>
326
- );
327
- };
328
-
329
- export const FakeEmailAdminPanel: FC = () => (
330
- <AuthenticatedApiProvider>
331
- <FakeEmailAdminPanelContent />
332
- </AuthenticatedApiProvider>
333
- );
334
-
335
- export default FakeEmailAdminPanel;
336
-
337
- // Buttons component for use outside the panel (e.g. in a toolbar)
338
- export interface FakeEmailAdminButtonsProps {
339
- onRefresh: () => void;
340
- onClear: () => void;
341
- disabled?: boolean;
342
- }
343
-
344
- export const FakeEmailAdminButtons: FC<FakeEmailAdminButtonsProps> = ({
345
- onRefresh,
346
- onClear,
347
- disabled = false,
348
- }) => {
349
- const { tComponent } = useI18n();
350
- const t = (key: SuiteCoreStringKeyValue) =>
351
- tComponent<SuiteCoreStringKeyValue>(SuiteCoreComponentId, key);
352
-
353
- return (
354
- <Box sx={{ display: 'flex', gap: 1 }}>
355
- <Button
356
- size="small"
357
- startIcon={<RefreshIcon />}
358
- onClick={onRefresh}
359
- disabled={disabled}
360
- >
361
- {t(SuiteCoreStringKey.FakeEmail_Admin_Refresh)}
362
- </Button>
363
- <Button
364
- size="small"
365
- color="error"
366
- startIcon={<DeleteIcon />}
367
- onClick={onClear}
368
- disabled={disabled}
369
- >
370
- {t(SuiteCoreStringKey.FakeEmail_Admin_ClearAll)}
371
- </Button>
372
- </Box>
373
- );
374
- };
@@ -1,52 +0,0 @@
1
- import { getFlagCode } from '@digitaldefiance/suite-core-lib';
2
- import { Box, SxProps, Theme } from '@mui/material';
3
- import { FC } from 'react';
4
-
5
- export interface FlagProps {
6
- language: string;
7
- sx?: SxProps<Theme>;
8
- }
9
-
10
- /**
11
- * A simple component to display a flag icon for a given language.
12
- *
13
- * Props:
14
- * language: The language to display a flag for, as a StringLanguages enum value.
15
- * sx: Optional styles to apply to the component.
16
- *
17
- * Returns a Box component with an SVG flag icon from flagcdn.com as a ::before pseudo-element.
18
- * The flag is sized to 1.5rem by default, but can be overridden by passing a custom sx prop.
19
- * The component also includes an aria-label for accessibility, set to `Flag for <language>`.
20
- */
21
- export const Flag: FC<FlagProps> = ({ language, sx }) => {
22
- const flagContent = getFlagCode(language);
23
- if (!flagContent) {
24
- return null;
25
- }
26
- return (
27
- <Box
28
- component="span"
29
- aria-label={`Flag for ${language}`}
30
- sx={{
31
- fontSize: '1.5rem',
32
- lineHeight: 1,
33
- verticalAlign: 'middle',
34
- '&::before': {
35
- content: `" "`,
36
- display: 'inline-block',
37
- width: '1em',
38
- height: '1em',
39
- backgroundImage: `url(https://flagcdn.com/${flagContent.toLowerCase()}.svg)`,
40
- backgroundSize: 'contain',
41
- backgroundRepeat: 'no-repeat',
42
- backgroundPosition: 'center',
43
- border: '1px solid rgba(0, 0, 0, 0.1)',
44
- borderRadius: '2px',
45
- },
46
- ...sx,
47
- }}
48
- />
49
- );
50
- };
51
-
52
- export default Flag;
@@ -1,174 +0,0 @@
1
- import {
2
- SuiteCoreComponentId,
3
- SuiteCoreStringKey,
4
- SuiteCoreStringKeyValue,
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 ForgotPasswordFormValues {
20
- email: string;
21
- }
22
-
23
- export interface ForgotPasswordFormProps {
24
- onSubmit: (values: ForgotPasswordFormValues) => Promise<void>;
25
- emailValidation?: Yup.StringSchema;
26
- labels?: {
27
- title?: string;
28
- email?: string;
29
- sendResetLink?: string;
30
- successMessage?: string;
31
- };
32
- }
33
-
34
- export const ForgotPasswordForm: FC<ForgotPasswordFormProps> = ({
35
- onSubmit,
36
- emailValidation,
37
- labels = {},
38
- }) => {
39
- const { tComponent } = useI18n();
40
- const [success, setSuccess] = useState(false);
41
- const [apiError, setApiError] = useState<string>('');
42
-
43
- const validation = {
44
- email:
45
- emailValidation ||
46
- Yup.string()
47
- .email(
48
- tComponent<SuiteCoreStringKeyValue>(
49
- SuiteCoreComponentId,
50
- SuiteCoreStringKey.Validation_InvalidEmail
51
- )
52
- )
53
- .required(
54
- tComponent<SuiteCoreStringKeyValue>(
55
- SuiteCoreComponentId,
56
- SuiteCoreStringKey.Validation_Required
57
- )
58
- ),
59
- };
60
-
61
- const translatedLabels = {
62
- title:
63
- labels.title ||
64
- tComponent<SuiteCoreStringKeyValue>(
65
- SuiteCoreComponentId,
66
- SuiteCoreStringKey.ForgotPassword_Title
67
- ),
68
- email:
69
- labels.email ||
70
- tComponent<SuiteCoreStringKeyValue>(
71
- SuiteCoreComponentId,
72
- SuiteCoreStringKey.Common_Email
73
- ),
74
- sendResetLink:
75
- labels.sendResetLink ||
76
- tComponent<SuiteCoreStringKeyValue>(
77
- SuiteCoreComponentId,
78
- SuiteCoreStringKey.ForgotPassword_SendResetLink
79
- ),
80
- successMessage:
81
- labels.successMessage ||
82
- tComponent<SuiteCoreStringKeyValue>(
83
- SuiteCoreComponentId,
84
- SuiteCoreStringKey.PasswordReset_Success
85
- ),
86
- };
87
-
88
- const formik = useFormik<ForgotPasswordFormValues>({
89
- initialValues: {
90
- email: '',
91
- },
92
- validationSchema: Yup.object({
93
- email: validation.email,
94
- }),
95
- onSubmit: async (values) => {
96
- try {
97
- await onSubmit(values);
98
- setSuccess(true);
99
- setApiError('');
100
- } catch (error: unknown) {
101
- const err = error as { response?: { data?: { message?: string } } };
102
- setApiError(
103
- err.response?.data?.message ||
104
- tComponent<SuiteCoreStringKeyValue>(
105
- SuiteCoreComponentId,
106
- SuiteCoreStringKey.ForgotPassword_Error
107
- )
108
- );
109
- setSuccess(false);
110
- }
111
- },
112
- });
113
-
114
- return (
115
- <Container maxWidth="sm">
116
- <Box
117
- sx={{
118
- mt: 8,
119
- display: 'flex',
120
- flexDirection: 'column',
121
- alignItems: 'center',
122
- }}
123
- >
124
- <Typography variant="h4" component="h1" gutterBottom>
125
- {translatedLabels.title}
126
- </Typography>
127
-
128
- <Box
129
- component="form"
130
- onSubmit={formik.handleSubmit}
131
- sx={{ mt: 1, width: '100%' }}
132
- >
133
- <TextField
134
- fullWidth
135
- id="email"
136
- name="email"
137
- label={translatedLabels.email}
138
- value={formik.values.email}
139
- onChange={formik.handleChange}
140
- onBlur={formik.handleBlur}
141
- error={Boolean(formik.touched.email && formik.errors.email)}
142
- helperText={formik.touched.email && formik.errors.email}
143
- margin="normal"
144
- />
145
-
146
- {apiError && (
147
- <Alert severity="error" sx={{ mt: 2, mb: 2 }}>
148
- {apiError}
149
- </Alert>
150
- )}
151
-
152
- {success && (
153
- <Alert severity="success" sx={{ mt: 2, mb: 2 }}>
154
- {translatedLabels.successMessage}
155
- </Alert>
156
- )}
157
-
158
- <Button
159
- type="submit"
160
- fullWidth
161
- variant="contained"
162
- color="primary"
163
- sx={{ mt: 3, mb: 2 }}
164
- disabled={formik.isSubmitting}
165
- >
166
- {translatedLabels.sendResetLink}
167
- </Button>
168
- </Box>
169
- </Box>
170
- </Container>
171
- );
172
- };
173
-
174
- export default ForgotPasswordForm;