@rapidmx/web-client 0.10.0 → 0.11.0

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 (310) hide show
  1. package/README.md +183 -2
  2. package/apps/admin/_layout.tsx +8 -2
  3. package/apps/admin/index.tsx +7 -1
  4. package/apps/admin/ingest-queue/index.tsx +2 -1
  5. package/apps/admin/mailboxes/[uid].tsx +23 -10
  6. package/apps/admin/quarantine/index.tsx +2 -1
  7. package/apps/admin/signing-certificates/index.tsx +343 -0
  8. package/apps/escrow/_layout.tsx +8 -2
  9. package/apps/shared/appearance/AppearanceForm.tsx +392 -0
  10. package/apps/shared/appearance/AppearanceHead.tsx +49 -0
  11. package/apps/shared/appearance/AppearanceProvider.tsx +470 -0
  12. package/apps/shared/appearance/ColorField.tsx +107 -0
  13. package/apps/shared/appearance/appearanceCache.ts +106 -0
  14. package/apps/shared/appearance/appearanceContext.ts +77 -0
  15. package/apps/shared/appearance/bootScript.ts +35 -0
  16. package/apps/shared/appearance/color.ts +169 -0
  17. package/apps/shared/appearance/photo.ts +88 -0
  18. package/apps/shared/appearance/resolvedTheme.ts +67 -0
  19. package/apps/shared/appearance/theme.ts +355 -0
  20. package/apps/shared/components/admin/layout/AdminShell.tsx +22 -10
  21. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +107 -60
  22. package/apps/shared/components/admin/settings/BrandingForm.tsx +24 -0
  23. package/apps/shared/components/admin/settings/PluginsManager.tsx +228 -25
  24. package/apps/shared/components/admin/setup/SetupWizard.tsx +2 -1
  25. package/apps/shared/components/calendar/CalendarListSidebar.tsx +4 -5
  26. package/apps/shared/components/contacts/ContactForm.tsx +11 -5
  27. package/apps/shared/components/contacts/ContactsSidebar.tsx +5 -3
  28. package/apps/shared/components/contacts/ContactsToolbar.tsx +25 -52
  29. package/apps/shared/components/contacts/layout/ContactsShell.tsx +10 -5
  30. package/apps/shared/components/escrow/layout/EscrowShell.tsx +13 -6
  31. package/apps/shared/components/layout/AppShell.tsx +98 -32
  32. package/apps/shared/components/layout/BrandingChrome.tsx +200 -13
  33. package/apps/shared/components/layout/RailIcon.tsx +100 -0
  34. package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -0
  35. package/apps/shared/components/layout/ThemeSwitch.tsx +84 -0
  36. package/apps/shared/components/layout/UserMenu.tsx +75 -6
  37. package/apps/shared/components/mail/ConversationList.tsx +4 -2
  38. package/apps/shared/components/mail/ConversationThreadPane.tsx +487 -475
  39. package/apps/shared/components/mail/LazyReadingPane.tsx +14 -16
  40. package/apps/shared/components/mail/MailSelectionBar.tsx +2 -10
  41. package/apps/shared/components/mail/MessageDetailPane.tsx +366 -287
  42. package/apps/shared/components/mail/OutboxBadge.tsx +44 -0
  43. package/apps/shared/components/mail/OutboxRowStatus.tsx +31 -0
  44. package/apps/shared/components/mail/compose/ComposeContext.tsx +35 -2
  45. package/apps/shared/components/mail/compose/ComposeWindow.tsx +239 -437
  46. package/apps/shared/components/mail/compose/encryptionRequirement.ts +86 -0
  47. package/apps/shared/components/mail/layout/MailShell.tsx +39 -25
  48. package/apps/shared/components/mail/reading/EncryptedBody.tsx +65 -0
  49. package/apps/shared/components/mail/reading/EncryptedPreview.tsx +35 -0
  50. package/apps/shared/components/mail/reading/MessageBody.tsx +240 -0
  51. package/apps/shared/components/mail/reading/MessageCard.tsx +174 -0
  52. package/apps/shared/components/mail/reading/bodyContent.ts +100 -0
  53. package/apps/shared/components/mail/reading/bodyHtml.ts +298 -0
  54. package/apps/shared/components/mail/reading/color.ts +245 -0
  55. package/apps/shared/components/mail/reading/frameControl.ts +142 -0
  56. package/apps/shared/components/mail/reading/frameDocument.ts +123 -0
  57. package/apps/shared/components/mail/reading/safeDocument.ts +24 -0
  58. package/apps/shared/components/mail/reading/themeAdaptation.ts +221 -0
  59. package/apps/shared/components/mail/reading/themeSurface.ts +90 -0
  60. package/apps/shared/components/mail/reading/viewOriginal.ts +38 -0
  61. package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -0
  62. package/apps/shared/components/settings/layout/SettingsShell.tsx +1 -0
  63. package/apps/shared/components/sharing/PrincipalPicker.tsx +145 -0
  64. package/apps/shared/components/tasks/TasksSidebar.tsx +5 -3
  65. package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -5
  66. package/apps/shared/mail/folderCounts.ts +9 -3
  67. package/apps/shared/mail/folderTree.ts +143 -0
  68. package/apps/shared/mail/outbox/UnlockBridge.tsx +23 -0
  69. package/apps/shared/mail/outbox/composeBridge.ts +69 -0
  70. package/apps/shared/mail/outbox/outboxState.ts +88 -0
  71. package/apps/shared/mail/outbox/pendingSends.ts +146 -0
  72. package/apps/shared/mail/outbox/sendDecision.ts +157 -0
  73. package/apps/shared/mail/outbox/sendJob.ts +427 -0
  74. package/apps/shared/mail/outbox/sendOutcomes.ts +146 -0
  75. package/apps/shared/mail/outbox/sendState.ts +35 -0
  76. package/apps/shared/mail/outbox/useOutboxStatus.ts +61 -0
  77. package/apps/shared/mail/useMailConnection.ts +81 -28
  78. package/apps/shared/mail/useMailLiveUpdates.ts +55 -10
  79. package/apps/shared/mail/useNewMailNotifications.ts +47 -32
  80. package/apps/shared/navigation/AppRouter.tsx +2 -2
  81. package/apps/shared/notifications/NotificationCenter.tsx +215 -0
  82. package/apps/shared/notifications/NotificationHistoryDialog.tsx +90 -0
  83. package/apps/shared/notifications/apiErrors.ts +98 -0
  84. package/apps/shared/notifications/headerOffset.ts +35 -0
  85. package/apps/shared/notifications/pushStatus.ts +56 -0
  86. package/apps/shared/notifications/store.ts +549 -0
  87. package/apps/shared/notifications/systemErrors.ts +63 -0
  88. package/apps/shared/notifications/useNotifications.ts +41 -0
  89. package/apps/shared/signing/enrollmentStorage.ts +33 -0
  90. package/apps/shared/signing/enrollmentTracker.ts +385 -0
  91. package/apps/shared/signing/enrollmentView.ts +251 -0
  92. package/apps/shared/signing/signingInfo.ts +58 -0
  93. package/apps/shared/signing/useNow.ts +19 -0
  94. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +90 -0
  95. package/apps/shared/styles/app.css +80 -0
  96. package/apps/www/_layout.tsx +8 -2
  97. package/apps/www/_routes.ts +1 -0
  98. package/apps/www/calendar/index.tsx +9 -14
  99. package/apps/www/contacts/[uid].tsx +5 -8
  100. package/apps/www/contacts/index.tsx +587 -584
  101. package/apps/www/index.tsx +38 -27
  102. package/apps/www/messages/[uid].tsx +3 -1
  103. package/apps/www/settings/appearance/index.tsx +21 -0
  104. package/apps/www/settings/auto-reply/index.tsx +3 -7
  105. package/apps/www/settings/encryption/index.tsx +151 -116
  106. package/apps/www/settings/filters/[uid].tsx +3 -1
  107. package/apps/www/settings/filters/new/index.tsx +3 -1
  108. package/apps/www/settings/privacy/index.tsx +3 -8
  109. package/apps/www/settings/read-receipts/index.tsx +3 -7
  110. package/apps/www/settings/sharing/index.tsx +44 -65
  111. package/apps/www/settings/signatures/[uid].tsx +3 -1
  112. package/apps/www/settings/signatures/new/index.tsx +3 -2
  113. package/apps/www/tasks/index.tsx +11 -16
  114. package/dist/apps/admin/_layout.d.ts +5 -1
  115. package/dist/apps/admin/_layout.js +3 -2
  116. package/dist/apps/admin/index.js +3 -2
  117. package/dist/apps/admin/ingest-queue/index.js +2 -1
  118. package/dist/apps/admin/mailboxes/[uid].js +9 -6
  119. package/dist/apps/admin/quarantine/index.js +2 -1
  120. package/dist/apps/admin/signing-certificates/index.d.ts +3 -0
  121. package/dist/apps/admin/signing-certificates/index.js +161 -0
  122. package/dist/apps/escrow/_layout.d.ts +5 -1
  123. package/dist/apps/escrow/_layout.js +3 -2
  124. package/dist/apps/shared/appearance/AppearanceForm.d.ts +7 -0
  125. package/dist/apps/shared/appearance/AppearanceForm.js +131 -0
  126. package/dist/apps/shared/appearance/AppearanceHead.d.ts +15 -0
  127. package/dist/apps/shared/appearance/AppearanceHead.js +31 -0
  128. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +32 -0
  129. package/dist/apps/shared/appearance/AppearanceProvider.js +390 -0
  130. package/dist/apps/shared/appearance/ColorField.d.ts +22 -0
  131. package/dist/apps/shared/appearance/ColorField.js +40 -0
  132. package/dist/apps/shared/appearance/appearanceCache.d.ts +45 -0
  133. package/dist/apps/shared/appearance/appearanceCache.js +80 -0
  134. package/dist/apps/shared/appearance/appearanceContext.d.ts +45 -0
  135. package/dist/apps/shared/appearance/appearanceContext.js +29 -0
  136. package/dist/apps/shared/appearance/bootScript.d.ts +16 -0
  137. package/dist/apps/shared/appearance/bootScript.js +33 -0
  138. package/dist/apps/shared/appearance/color.d.ts +65 -0
  139. package/dist/apps/shared/appearance/color.js +132 -0
  140. package/dist/apps/shared/appearance/photo.d.ts +17 -0
  141. package/dist/apps/shared/appearance/photo.js +79 -0
  142. package/dist/apps/shared/appearance/resolvedTheme.d.ts +21 -0
  143. package/dist/apps/shared/appearance/resolvedTheme.js +55 -0
  144. package/dist/apps/shared/appearance/theme.d.ts +111 -0
  145. package/dist/apps/shared/appearance/theme.js +251 -0
  146. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  147. package/dist/apps/shared/components/admin/layout/AdminShell.js +16 -6
  148. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.d.ts +10 -5
  149. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +49 -34
  150. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +2 -0
  151. package/dist/apps/shared/components/admin/settings/BrandingForm.js +7 -1
  152. package/dist/apps/shared/components/admin/settings/PluginsManager.js +106 -20
  153. package/dist/apps/shared/components/admin/setup/SetupWizard.js +2 -1
  154. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +8 -8
  155. package/dist/apps/shared/components/contacts/ContactForm.js +1 -1
  156. package/dist/apps/shared/components/contacts/ContactsSidebar.js +4 -2
  157. package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +3 -1
  158. package/dist/apps/shared/components/contacts/ContactsToolbar.js +24 -9
  159. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +4 -1
  160. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -4
  161. package/dist/apps/shared/components/layout/AppShell.d.ts +6 -1
  162. package/dist/apps/shared/components/layout/AppShell.js +59 -13
  163. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +69 -10
  164. package/dist/apps/shared/components/layout/BrandingChrome.js +140 -12
  165. package/dist/apps/shared/components/layout/RailIcon.d.ts +21 -0
  166. package/dist/apps/shared/components/layout/RailIcon.js +85 -0
  167. package/dist/apps/shared/components/layout/ResponsiveToolbar.d.ts +55 -0
  168. package/dist/apps/shared/components/layout/ResponsiveToolbar.js +199 -0
  169. package/dist/apps/shared/components/layout/ThemeSwitch.d.ts +9 -0
  170. package/dist/apps/shared/components/layout/ThemeSwitch.js +54 -0
  171. package/dist/apps/shared/components/layout/UserMenu.d.ts +7 -1
  172. package/dist/apps/shared/components/layout/UserMenu.js +41 -7
  173. package/dist/apps/shared/components/mail/ConversationList.js +3 -2
  174. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +2 -1
  175. package/dist/apps/shared/components/mail/ConversationThreadPane.js +42 -20
  176. package/dist/apps/shared/components/mail/LazyReadingPane.js +9 -8
  177. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +3 -4
  178. package/dist/apps/shared/components/mail/MailSelectionBar.js +2 -3
  179. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +18 -7
  180. package/dist/apps/shared/components/mail/MessageDetailPane.js +86 -41
  181. package/dist/apps/shared/components/mail/OutboxBadge.d.ts +17 -0
  182. package/dist/apps/shared/components/mail/OutboxBadge.js +18 -0
  183. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +10 -0
  184. package/dist/apps/shared/components/mail/OutboxRowStatus.js +19 -0
  185. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +8 -0
  186. package/dist/apps/shared/components/mail/compose/ComposeContext.js +28 -3
  187. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +2 -1
  188. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +229 -328
  189. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +49 -0
  190. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +35 -0
  191. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +2 -0
  192. package/dist/apps/shared/components/mail/layout/MailShell.js +20 -14
  193. package/dist/apps/shared/components/mail/reading/EncryptedBody.d.ts +21 -0
  194. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +22 -0
  195. package/dist/apps/shared/components/mail/reading/EncryptedPreview.d.ts +18 -0
  196. package/dist/apps/shared/components/mail/reading/EncryptedPreview.js +22 -0
  197. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +32 -0
  198. package/dist/apps/shared/components/mail/reading/MessageBody.js +136 -0
  199. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +62 -0
  200. package/dist/apps/shared/components/mail/reading/MessageCard.js +58 -0
  201. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +38 -0
  202. package/dist/apps/shared/components/mail/reading/bodyContent.js +84 -0
  203. package/dist/apps/shared/components/mail/reading/bodyHtml.d.ts +47 -0
  204. package/dist/apps/shared/components/mail/reading/bodyHtml.js +267 -0
  205. package/dist/apps/shared/components/mail/reading/color.d.ts +63 -0
  206. package/dist/apps/shared/components/mail/reading/color.js +201 -0
  207. package/dist/apps/shared/components/mail/reading/frameControl.d.ts +33 -0
  208. package/dist/apps/shared/components/mail/reading/frameControl.js +120 -0
  209. package/dist/apps/shared/components/mail/reading/frameDocument.d.ts +52 -0
  210. package/dist/apps/shared/components/mail/reading/frameDocument.js +98 -0
  211. package/dist/apps/shared/components/mail/reading/safeDocument.d.ts +3 -0
  212. package/dist/apps/shared/components/mail/reading/safeDocument.js +12 -0
  213. package/dist/apps/shared/components/mail/reading/themeAdaptation.d.ts +56 -0
  214. package/dist/apps/shared/components/mail/reading/themeAdaptation.js +144 -0
  215. package/dist/apps/shared/components/mail/reading/themeSurface.d.ts +17 -0
  216. package/dist/apps/shared/components/mail/reading/themeSurface.js +83 -0
  217. package/dist/apps/shared/components/mail/reading/viewOriginal.d.ts +9 -0
  218. package/dist/apps/shared/components/mail/reading/viewOriginal.js +32 -0
  219. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +32 -0
  220. package/dist/apps/shared/components/settings/SigningCertificateCard.js +98 -0
  221. package/dist/apps/shared/components/settings/layout/SettingsShell.js +1 -0
  222. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +26 -0
  223. package/dist/apps/shared/components/sharing/PrincipalPicker.js +77 -0
  224. package/dist/apps/shared/components/tasks/TasksSidebar.js +4 -2
  225. package/dist/apps/shared/components/tasks/layout/TasksShell.js +3 -1
  226. package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
  227. package/dist/apps/shared/mail/folderCounts.js +9 -3
  228. package/dist/apps/shared/mail/folderTree.d.ts +55 -0
  229. package/dist/apps/shared/mail/folderTree.js +116 -0
  230. package/dist/apps/shared/mail/outbox/UnlockBridge.d.ts +5 -0
  231. package/dist/apps/shared/mail/outbox/UnlockBridge.js +18 -0
  232. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +34 -0
  233. package/dist/apps/shared/mail/outbox/composeBridge.js +32 -0
  234. package/dist/apps/shared/mail/outbox/outboxState.d.ts +41 -0
  235. package/dist/apps/shared/mail/outbox/outboxState.js +42 -0
  236. package/dist/apps/shared/mail/outbox/pendingSends.d.ts +39 -0
  237. package/dist/apps/shared/mail/outbox/pendingSends.js +109 -0
  238. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +80 -0
  239. package/dist/apps/shared/mail/outbox/sendDecision.js +94 -0
  240. package/dist/apps/shared/mail/outbox/sendJob.d.ts +68 -0
  241. package/dist/apps/shared/mail/outbox/sendJob.js +319 -0
  242. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +3 -0
  243. package/dist/apps/shared/mail/outbox/sendOutcomes.js +141 -0
  244. package/dist/apps/shared/mail/outbox/sendState.d.ts +25 -0
  245. package/dist/apps/shared/mail/outbox/sendState.js +8 -0
  246. package/dist/apps/shared/mail/outbox/useOutboxStatus.d.ts +11 -0
  247. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +49 -0
  248. package/dist/apps/shared/mail/useMailConnection.d.ts +9 -8
  249. package/dist/apps/shared/mail/useMailConnection.js +64 -22
  250. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +17 -4
  251. package/dist/apps/shared/mail/useMailLiveUpdates.js +36 -10
  252. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +4 -10
  253. package/dist/apps/shared/mail/useNewMailNotifications.js +43 -23
  254. package/dist/apps/shared/navigation/AppRouter.js +2 -2
  255. package/dist/apps/shared/notifications/NotificationCenter.d.ts +19 -0
  256. package/dist/apps/shared/notifications/NotificationCenter.js +90 -0
  257. package/dist/apps/shared/notifications/NotificationHistoryDialog.d.ts +10 -0
  258. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +36 -0
  259. package/dist/apps/shared/notifications/apiErrors.d.ts +19 -0
  260. package/dist/apps/shared/notifications/apiErrors.js +86 -0
  261. package/dist/apps/shared/notifications/headerOffset.d.ts +8 -0
  262. package/dist/apps/shared/notifications/headerOffset.js +33 -0
  263. package/dist/apps/shared/notifications/pushStatus.d.ts +9 -0
  264. package/dist/apps/shared/notifications/pushStatus.js +54 -0
  265. package/dist/apps/shared/notifications/store.d.ts +136 -0
  266. package/dist/apps/shared/notifications/store.js +416 -0
  267. package/dist/apps/shared/notifications/systemErrors.d.ts +9 -0
  268. package/dist/apps/shared/notifications/systemErrors.js +58 -0
  269. package/dist/apps/shared/notifications/useNotifications.d.ts +17 -0
  270. package/dist/apps/shared/notifications/useNotifications.js +18 -0
  271. package/dist/apps/shared/signing/enrollmentStorage.d.ts +10 -0
  272. package/dist/apps/shared/signing/enrollmentStorage.js +33 -0
  273. package/dist/apps/shared/signing/enrollmentTracker.d.ts +73 -0
  274. package/dist/apps/shared/signing/enrollmentTracker.js +307 -0
  275. package/dist/apps/shared/signing/enrollmentView.d.ts +57 -0
  276. package/dist/apps/shared/signing/enrollmentView.js +214 -0
  277. package/dist/apps/shared/signing/signingInfo.d.ts +11 -0
  278. package/dist/apps/shared/signing/signingInfo.js +48 -0
  279. package/dist/apps/shared/signing/useNow.d.ts +2 -0
  280. package/dist/apps/shared/signing/useNow.js +18 -0
  281. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +16 -0
  282. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +87 -0
  283. package/dist/apps/shared/styles/app.css +80 -0
  284. package/dist/apps/www/_layout.d.ts +5 -1
  285. package/dist/apps/www/_layout.js +3 -2
  286. package/dist/apps/www/_routes.js +1 -0
  287. package/dist/apps/www/calendar/index.js +3 -4
  288. package/dist/apps/www/contacts/[uid].js +5 -7
  289. package/dist/apps/www/contacts/index.js +14 -21
  290. package/dist/apps/www/index.js +37 -19
  291. package/dist/apps/www/messages/[uid].js +3 -1
  292. package/dist/apps/www/settings/appearance/index.d.ts +4 -0
  293. package/dist/apps/www/settings/appearance/index.js +13 -0
  294. package/dist/apps/www/settings/auto-reply/index.js +3 -6
  295. package/dist/apps/www/settings/encryption/index.js +103 -95
  296. package/dist/apps/www/settings/filters/[uid].js +3 -1
  297. package/dist/apps/www/settings/filters/new/index.js +3 -1
  298. package/dist/apps/www/settings/privacy/index.js +5 -8
  299. package/dist/apps/www/settings/read-receipts/index.js +3 -6
  300. package/dist/apps/www/settings/sharing/index.js +19 -33
  301. package/dist/apps/www/settings/signatures/[uid].js +3 -1
  302. package/dist/apps/www/settings/signatures/new/index.js +3 -2
  303. package/dist/apps/www/tasks/index.js +8 -14
  304. package/package.json +2 -2
  305. package/apps/shared/components/mail/NewMailToasts.tsx +0 -141
  306. package/apps/shared/components/mail/compose/SendFailureAlert.tsx +0 -48
  307. package/dist/apps/shared/components/mail/NewMailToasts.d.ts +0 -19
  308. package/dist/apps/shared/components/mail/NewMailToasts.js +0 -55
  309. package/dist/apps/shared/components/mail/compose/SendFailureAlert.d.ts +0 -14
  310. package/dist/apps/shared/components/mail/compose/SendFailureAlert.js +0 -11
@@ -3,21 +3,22 @@
3
3
  // SPDX-License-Identifier: MPL-2.0
4
4
  ///////////////////////////////////////////////////////////////////////////////
5
5
  import { routedPage } from "../../_routedPage.js";
6
- import React, { FormEvent, useEffect, useState } from "react";
6
+ import React, { useEffect, useState } from "react";
7
7
  import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
8
8
  import {
9
9
  MailboxAccessMember,
10
10
  MailboxAccessRole,
11
11
  listMailboxAccess,
12
- lookupMailboxOwnerByEmail,
13
12
  removeMailboxAccess,
14
13
  setMailboxAccess,
15
14
  } from "@rapidmx/react-shared/mail/mailboxAccessApi.js";
16
15
  import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../shared/components/settings/layout/SettingsShell.js";
16
+ import PrincipalPicker from "../../../shared/components/sharing/PrincipalPicker.js";
17
17
  import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
18
18
  import Button from "@rapidmx/react-shared/components/buttons/Button.js";
19
19
  import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
20
20
  import { SkeletonList } from "@rapidmx/react-shared/components/feedback/Skeleton.js";
21
+ import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
21
22
 
22
23
  const ROLE_LABELS: Record<MailboxAccessRole, string> = { viewer: "Can view", manager: "Can manage" };
23
24
 
@@ -49,10 +50,8 @@ function SharingContent() {
49
50
  const [status, setStatus] = useState<Status>("loading");
50
51
  const [error, setError] = useState<string | null>(null);
51
52
 
52
- const [emailInput, setEmailInput] = useState("");
53
- const [newRole, setNewRole] = useState<MailboxAccessRole>("viewer");
54
- const [adding, setAdding] = useState(false);
55
- const [addError, setAddError] = useState<string | null>(null);
53
+ // An entry that is not a user uid, being replaced with the user it was meant for.
54
+ const [replacing, setReplacing] = useState<MailboxAccessMember | null>(null);
56
55
 
57
56
  const [pendingRoleChange, setPendingRoleChange] = useState<string | null>(null);
58
57
  const [removeTarget, setRemoveTarget] = useState<MailboxAccessMember | null>(null);
@@ -84,39 +83,27 @@ function SharingContent() {
84
83
 
85
84
  useEffect(reload, [mailboxUid]);
86
85
 
87
- async function handleAdd(e: FormEvent) {
88
- e.preventDefault();
89
- const email = emailInput.trim();
90
- if (!email) {
91
- return;
92
- }
93
- setAddError(null);
94
- setAdding(true);
95
- try {
96
- const found = await lookupMailboxOwnerByEmail(email);
97
- if (!found) {
98
- setAddError("No one found with that email on this platform.");
99
- return;
86
+ // Once the person is granted, the entry they replace (if any) is removed: the string it named never matched anyone.
87
+ async function handleGranted() {
88
+ const stale = replacing;
89
+ setReplacing(null);
90
+ if (stale) {
91
+ try {
92
+ await removeMailboxAccess(mailboxUid!, stale.userOrRoleId);
93
+ } catch (err) {
94
+ notifyApiError(err, "Couldn't remove the old entry");
100
95
  }
101
- await setMailboxAccess(mailboxUid!, found.userUid, newRole);
102
- setEmailInput("");
103
- setNewRole("viewer");
104
- reload();
105
- } catch (err) {
106
- setAddError(err instanceof ApiRequestError ? err.message : "Could not grant access.");
107
- } finally {
108
- setAdding(false);
109
96
  }
97
+ reload();
110
98
  }
111
99
 
112
100
  async function handleRoleChange(userOrRoleId: string, role: MailboxAccessRole) {
113
101
  setPendingRoleChange(userOrRoleId);
114
- setError(null);
115
102
  try {
116
103
  await setMailboxAccess(mailboxUid!, userOrRoleId, role);
117
104
  reload();
118
105
  } catch (err) {
119
- setError(err instanceof ApiRequestError ? err.message : "Could not update this member's role.");
106
+ notifyApiError(err, "Couldn't change this member's role");
120
107
  } finally {
121
108
  setPendingRoleChange(null);
122
109
  }
@@ -130,13 +117,12 @@ function SharingContent() {
130
117
  // Only reachable from the confirm modal's own "Remove" button, and `Modal` renders nothing at all
131
118
  // unless `removeTarget` is set, so `removeTarget` is always non-null here.
132
119
  setRemoving(true);
133
- setError(null);
134
120
  try {
135
121
  await removeMailboxAccess(mailboxUid!, removeTarget!.userOrRoleId);
136
122
  setRemoveTarget(null);
137
123
  reload();
138
124
  } catch (err) {
139
- setError(err instanceof ApiRequestError ? err.message : "Could not remove this member.");
125
+ notifyApiError(err, "Couldn't remove this member");
140
126
  } finally {
141
127
  setRemoving(false);
142
128
  }
@@ -164,8 +150,6 @@ function SharingContent() {
164
150
 
165
151
  {status === "ready" && (
166
152
  <>
167
- {error && <Alert>{error}</Alert>}
168
-
169
153
  <div>
170
154
  <h2 className="text-sm font-semibold mb-2">People with access</h2>
171
155
  {members.length === 0 ? (
@@ -177,8 +161,20 @@ function SharingContent() {
177
161
  key={member.userOrRoleId}
178
162
  className="flex items-center justify-between gap-3 text-sm py-1.5 px-3 bg-surface-alt rounded-sm"
179
163
  >
180
- <span className="truncate">{member.userOrRoleId}</span>
164
+ <span className="truncate">
165
+ {member.userOrRoleId}
166
+ {member.noEffect && (
167
+ <span className="block text-xs text-danger">
168
+ Not a user - this entry has no effect. Replace it with the person it was meant for, or remove it.
169
+ </span>
170
+ )}
171
+ </span>
181
172
  <span className="flex items-center gap-2 shrink-0">
173
+ {member.noEffect && (
174
+ <Button type="button" variant="text" className="!w-auto" onClick={() => setReplacing(member)}>
175
+ Replace with a user
176
+ </Button>
177
+ )}
182
178
  <select
183
179
  aria-label={`Role for ${member.userOrRoleId}`}
184
180
  className="text-sm border border-border rounded-sm py-1 px-2 bg-surface"
@@ -216,37 +212,20 @@ function SharingContent() {
216
212
  )}
217
213
  </div>
218
214
 
219
- <form onSubmit={handleAdd} className="flex flex-col gap-2">
220
- <h2 className="text-sm font-semibold">Add someone</h2>
221
- {addError && <Alert>{addError}</Alert>}
222
- <div className="flex gap-2">
223
- <input
224
- type="email"
225
- aria-label="Email address"
226
- placeholder="Email address"
227
- className="flex-1 text-sm border border-border rounded-sm py-1.5 px-2 bg-surface"
228
- value={emailInput}
229
- onChange={(e) => setEmailInput(e.target.value)}
230
- disabled={adding}
231
- />
232
- <select
233
- aria-label="Role to grant"
234
- className="text-sm border border-border rounded-sm py-1.5 px-2 bg-surface"
235
- value={newRole}
236
- onChange={(e) => setNewRole(e.target.value as MailboxAccessRole)}
237
- disabled={adding}
238
- >
239
- {(Object.keys(ROLE_LABELS) as MailboxAccessRole[]).map((role) => (
240
- <option key={role} value={role}>
241
- {ROLE_LABELS[role]}
242
- </option>
243
- ))}
244
- </select>
245
- <Button type="submit" loading={adding} disabled={adding} className="!w-auto">
246
- Add
247
- </Button>
248
- </div>
249
- </form>
215
+ <div className="flex flex-col gap-2">
216
+ <h2 className="text-sm font-semibold">{replacing ? `Replace ${replacing.userOrRoleId}` : "Add someone"}</h2>
217
+ {/* The person is looked up and shown - name and address - before anything is saved. */}
218
+ <PrincipalPicker
219
+ key={replacing?.userOrRoleId ?? "new"}
220
+ mailboxUid={mailboxUid!}
221
+ roleLabels={ROLE_LABELS}
222
+ initialPrincipal={replacing?.userOrRoleId}
223
+ defaultRole={replacing?.role === "manager" ? "manager" : "viewer"}
224
+ placeholder="Email address or username"
225
+ onGranted={handleGranted}
226
+ onCancel={replacing ? () => setReplacing(null) : undefined}
227
+ />
228
+ </div>
250
229
  </>
251
230
  )}
252
231
 
@@ -12,6 +12,7 @@ import RichTextEditor from "../../../shared/components/mail/compose/RichTextEdit
12
12
  import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
13
13
  import Button from "@rapidmx/react-shared/components/buttons/Button.js";
14
14
  import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
15
+ import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
15
16
 
16
17
  const INPUT_CLASS =
17
18
  "w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
@@ -91,7 +92,8 @@ function SignatureDetailContent({ uid }: { uid: string }) {
91
92
  setOriginal(updated);
92
93
  setSaved(true);
93
94
  } catch (err) {
94
- setError(err instanceof ApiRequestError ? err.message : "Could not save this signature.");
95
+ // A pop-up: `error` is only this form's own validation (and the load failure above).
96
+ notifyApiError(err, "Couldn't save the signature");
95
97
  } finally {
96
98
  setSaving(false);
97
99
  }
@@ -5,7 +5,6 @@
5
5
  import { routedPage } from "../../../_routedPage.js";
6
6
  import { useNavigate } from "../../../../shared/navigation/AppRouter.js";
7
7
  import React, { FormEvent, useState } from "react";
8
- import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
9
8
  import { createMailSignature, listMailSignatures } from "@rapidmx/react-shared/mail/mailSignaturesApi.js";
10
9
  import { clearPreviousDefaults } from "../signatureDefaults.js";
11
10
  import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../../shared/components/settings/layout/SettingsShell.js";
@@ -13,6 +12,7 @@ import RichTextEditor from "../../../../shared/components/mail/compose/RichTextE
13
12
  import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
14
13
  import Button from "@rapidmx/react-shared/components/buttons/Button.js";
15
14
  import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
15
+ import { notifyApiError } from "../../../../shared/notifications/apiErrors.js";
16
16
 
17
17
  const INPUT_CLASS =
18
18
  "w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
@@ -68,7 +68,8 @@ function NewSignatureForm() {
68
68
  });
69
69
  navigate(`/settings/signatures/${encodeURIComponent(created.uid)}?mailboxUid=${encodeURIComponent(mailboxUid!)}`);
70
70
  } catch (err) {
71
- setError(err instanceof ApiRequestError ? err.message : "Could not create the signature.");
71
+ // A pop-up: `error` above is only the form's own validation.
72
+ notifyApiError(err, "Couldn't create the signature");
72
73
  } finally {
73
74
  setSaving(false);
74
75
  }
@@ -30,6 +30,7 @@ import { LIST_PAGE_SIZE, MAX_LIST_PAGES, listAllPages } from "../../shared/mail/
30
30
  import { SHORTCUTS } from "../../shared/keyboard/keymap.js";
31
31
  import { useShortcut } from "../../shared/keyboard/useShortcut.js";
32
32
  import { useShortcutProps } from "../../shared/keyboard/useShortcutProps.js";
33
+ import { notifyApiError } from "../../shared/notifications/apiErrors.js";
33
34
 
34
35
  const INPUT_CLASS =
35
36
  "text-sm py-1.5 px-2 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
@@ -198,7 +199,6 @@ function TasksContent() {
198
199
  }
199
200
 
200
201
  async function handleToggle(task: Task) {
201
- setError(null);
202
202
  const nextCompleted = !task.completed;
203
203
  setTasks((prev) => prev.map((t) => (t.uid === task.uid ? { ...t, completed: nextCompleted } : t)));
204
204
  try {
@@ -206,17 +206,16 @@ function TasksContent() {
206
206
  setTasks((prev) => prev.map((t) => (t.uid === task.uid ? updated : t)));
207
207
  } catch (err) {
208
208
  setTasks((prev) => prev.map((t) => (t.uid === task.uid ? task : t)));
209
- setError(err instanceof ApiRequestError ? err.message : "Could not update this task.");
209
+ notifyApiError(err, "Couldn't update the task");
210
210
  }
211
211
  }
212
212
 
213
213
  async function handleDelete(task: Task) {
214
- setError(null);
215
214
  try {
216
215
  await deleteTask(task.uid, task.version);
217
216
  setTasks((prev) => prev.filter((t) => t.uid !== task.uid));
218
217
  } catch (err) {
219
- setError(err instanceof ApiRequestError ? err.message : "Could not delete this task.");
218
+ notifyApiError(err, "Couldn't delete the task");
220
219
  }
221
220
  }
222
221
 
@@ -258,46 +257,41 @@ function TasksContent() {
258
257
 
259
258
  const checkedTasks = viewFiltered.filter((t) => checkedUids.has(t.uid));
260
259
 
260
+ // The bulk handlers try every task and report each failure: the same failure repeated is one pop-up with a count.
261
261
  async function handleBulkComplete() {
262
- let bulkError: string | null = null;
263
262
  for (const task of checkedTasks) {
264
263
  try {
265
264
  await setTaskCompleted(task, true);
266
265
  } catch (err) {
267
- bulkError = err instanceof ApiRequestError ? err.message : "Could not update one or more tasks.";
266
+ notifyApiError(err, "Couldn't update some of the tasks");
268
267
  }
269
268
  }
270
269
  setCheckedUids(new Set());
271
270
  await reload();
272
- setError(bulkError);
273
271
  }
274
272
 
275
273
  async function handleBulkAddToMyDay() {
276
- let bulkError: string | null = null;
277
274
  for (const task of checkedTasks) {
278
275
  try {
279
276
  await setTaskMyDay(task, true);
280
277
  } catch (err) {
281
- bulkError = err instanceof ApiRequestError ? err.message : "Could not update one or more tasks.";
278
+ notifyApiError(err, "Couldn't update some of the tasks");
282
279
  }
283
280
  }
284
281
  setCheckedUids(new Set());
285
282
  await reload();
286
- setError(bulkError);
287
283
  }
288
284
 
289
285
  async function handleBulkDelete() {
290
- let bulkError: string | null = null;
291
286
  for (const task of checkedTasks) {
292
287
  try {
293
288
  await deleteTask(task.uid, task.version);
294
289
  } catch (err) {
295
- bulkError = err instanceof ApiRequestError ? err.message : "Could not delete one or more tasks.";
290
+ notifyApiError(err, "Couldn't delete some of the tasks");
296
291
  }
297
292
  }
298
293
  setCheckedUids(new Set());
299
294
  await reload();
300
- setError(bulkError);
301
295
  }
302
296
 
303
297
  function requestDelete(task: Task) {
@@ -329,7 +323,8 @@ function TasksContent() {
329
323
  const completedTasks = viewFiltered.filter((t) => t.completed);
330
324
 
331
325
  return (
332
- <div className="flex-1 flex min-h-0">
326
+ // Below `lg` the tasks menu is a drawer (its button sits above the list), so the list keeps the whole width.
327
+ <div className="flex-1 min-w-0 min-h-0 flex flex-col lg:flex-row">
333
328
  <TasksSidebar mailboxUid={mailboxUid} tasks={tasks} userUid={userUid} active={view} onSelect={handleSelectView} />
334
329
  <div className="flex-1 min-w-0 flex flex-col overflow-y-auto">
335
330
  <TasksToolbar
@@ -354,13 +349,13 @@ function TasksContent() {
354
349
  <FlaggedEmailList loading={flaggedLoading} error={flaggedError} messages={flaggedMessages} />
355
350
  ) : (
356
351
  <>
357
- <form onSubmit={handleCreate} className="flex items-center gap-2 bg-surface border border-border rounded-md p-2">
352
+ <form onSubmit={handleCreate} className="flex flex-wrap items-center gap-2 bg-surface border border-border rounded-md p-2">
358
353
  <input
359
354
  ref={addTaskRef}
360
355
  type="text"
361
356
  aria-label="Add a task"
362
357
  {...addTaskHint}
363
- className={`${INPUT_CLASS} flex-1`}
358
+ className={`${INPUT_CLASS} min-w-40 basis-40 flex-1`}
364
359
  value={title}
365
360
  onChange={(e) => setTitle(e.target.value)}
366
361
  placeholder="Add a task"
@@ -4,5 +4,9 @@ export interface LayoutProps {
4
4
  /** See `apps/www/_layout.tsx`'s `LayoutProps` doc comment - identical mechanism, supplied by
5
5
  * `AdminConsoleRoute`'s `fetchProps()` override. */
6
6
  branding?: Branding;
7
+ /** The signed-in user's stored appearance preferences (a page prop from the same `fetchProps()`, present only on the www pages and only when the user has any): rendered as a stylesheet in the `<head>` so the very first paint is themed - see `AppearanceHead`. */
8
+ appearance?: unknown;
9
+ /** The signed-in user (supplied by the framework), so a cached appearance from another account on this browser is never applied. */
10
+ userUid?: string;
7
11
  }
8
- export default function Layout({ children, branding }: PropsWithChildren<LayoutProps>): React.JSX.Element;
12
+ export default function Layout({ children, branding, appearance, userUid }: PropsWithChildren<LayoutProps>): React.JSX.Element;
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AppearanceHead, appearanceHtmlAttributes } from "../shared/appearance/AppearanceHead.js";
2
3
  import { CUSTOM_STYLESHEET_LINK_ID } from "@rapidmx/react-shared/branding/useBranding.js";
3
- export default function Layout({ children, branding }) {
4
+ export default function Layout({ children, branding, appearance, userUid }) {
4
5
  const title = branding?.title || branding?.companyName
5
6
  ? `${branding?.title || branding?.companyName}: Mail Admin Console`
6
7
  : "RapidMX: Mail Admin Console";
7
8
  const iconHref = branding?.iconUrl || branding?.logoUrl || "/images/logo.svg";
8
9
  const stylesheetHref = branding?.stylesheetUrl;
9
- return (_jsxs("html", { lang: "en", children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("title", { children: title }), _jsx("link", { rel: "icon", type: "image/svg+xml", href: iconHref }), _jsx("link", { rel: "alternate icon", href: "/favicon.ico" }), _jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }), _jsx("link", { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" }), stylesheetHref && _jsx("link", { rel: "stylesheet", href: stylesheetHref, id: CUSTOM_STYLESHEET_LINK_ID })] }), _jsx("body", { children: children })] }));
10
+ return (_jsxs("html", { lang: "en", ...appearanceHtmlAttributes(appearance, userUid), children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("title", { children: title }), _jsx("link", { rel: "icon", type: "image/svg+xml", href: iconHref }), _jsx("link", { rel: "alternate icon", href: "/favicon.ico" }), _jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }), _jsx("link", { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" }), stylesheetHref && _jsx("link", { rel: "stylesheet", href: stylesheetHref, id: CUSTOM_STYLESHEET_LINK_ID }), _jsx(AppearanceHead, { appearance: appearance })] }), _jsx("body", { children: children })] }));
10
11
  }
@@ -24,7 +24,8 @@ function MailboxesListContent() {
24
24
  useEffect(() => {
25
25
  setLoading(true);
26
26
  setError(null);
27
- listMailboxes({ page, limit: PAGE_SIZE })
27
+ // The administration scope: every mailbox as administrative metadata - the console never shows anybody's mail.
28
+ listMailboxes({ page, limit: PAGE_SIZE, scope: "admin" })
28
29
  .then(setMailboxes)
29
30
  .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load mailboxes."))
30
31
  .finally(() => setLoading(false));
@@ -44,5 +45,5 @@ function MailboxesListContent() {
44
45
  setConfirmingSetup(false);
45
46
  }
46
47
  }
47
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-between mb-5", children: [_jsx("h1", { className: "text-xl font-bold uppercase tracking-wide", children: "Mailboxes" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: reopening, onClick: () => setConfirmingSetup(true), children: "Run setup again" }), _jsx("a", { href: "/admin/mailboxes/new", children: _jsx(Button, { type: "button", className: "!w-auto", children: "+ New mailbox" }) })] })] }), error && _jsx(Alert, { children: error }), _jsxs(Modal, { open: confirmingSetup, onClose: () => setConfirmingSetup(false), title: "Run setup again?", children: [_jsx("p", { className: "text-sm mb-4", children: "Setup starts again from its first step, and the admin console sends administrators to it until it's finished. Your existing settings, domains and mailboxes are kept." }), _jsxs("div", { className: "flex gap-2 justify-end", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingSetup(false), children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: reopening, disabled: reopening, onClick: () => void handleRunSetup(), children: "Run setup" })] })] }), loading ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : (_jsx(MailboxTable, { mailboxes: mailboxes })), _jsxs("div", { className: "flex gap-3 items-center mt-4", children: [_jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: page === 0 || loading, onClick: () => setPage((p) => Math.max(0, p - 1)), children: "Previous" }), _jsxs("span", { className: "text-sm text-text-muted", children: ["Page ", page + 1] }), _jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: !hasNextPage || loading, onClick: () => setPage((p) => p + 1), children: "Next" })] })] }));
48
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-between mb-5", children: [_jsx("h1", { className: "text-xl font-bold uppercase tracking-wide", children: "Mailboxes" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: reopening, onClick: () => setConfirmingSetup(true), children: "Run setup again" }), _jsx("a", { href: "/admin/mailboxes/new", children: _jsx(Button, { type: "button", className: "!w-auto", children: "+ New mailbox" }) })] })] }), _jsx("p", { className: "text-sm text-text-muted mb-4", children: "Administrative details only (addresses, owners, quota). The console never shows a mailbox\u2019s mail: to see someone\u2019s account, impersonate them from their mailbox page." }), error && _jsx(Alert, { children: error }), _jsxs(Modal, { open: confirmingSetup, onClose: () => setConfirmingSetup(false), title: "Run setup again?", children: [_jsx("p", { className: "text-sm mb-4", children: "Setup starts again from its first step, and the admin console sends administrators to it until it's finished. Your existing settings, domains and mailboxes are kept." }), _jsxs("div", { className: "flex gap-2 justify-end", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingSetup(false), children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: reopening, disabled: reopening, onClick: () => void handleRunSetup(), children: "Run setup" })] })] }), loading ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : (_jsx(MailboxTable, { mailboxes: mailboxes })), _jsxs("div", { className: "flex gap-3 items-center mt-4", children: [_jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: page === 0 || loading, onClick: () => setPage((p) => Math.max(0, p - 1)), children: "Previous" }), _jsxs("span", { className: "text-sm text-text-muted", children: ["Page ", page + 1] }), _jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: !hasNextPage || loading, onClick: () => setPage((p) => p + 1), children: "Next" })] })] }));
48
49
  }
@@ -41,7 +41,8 @@ function IngestQueueContent() {
41
41
  let cancelled = false;
42
42
  setLoading(true);
43
43
  setError(null);
44
- listIngestQueue(mailboxUid, { page, limit: PAGE_SIZE })
44
+ // The administration scope: any mailbox's pending mail, recorded in the audit log.
45
+ listIngestQueue(mailboxUid, { page, limit: PAGE_SIZE, scope: "admin" })
45
46
  .then((data) => {
46
47
  if (!cancelled)
47
48
  setEntries(data);
@@ -23,9 +23,9 @@ function formatBytes(bytes) {
23
23
  return `${bytes} B`;
24
24
  }
25
25
  export default function MailboxDetailPage(props) {
26
- return (_jsx(AdminShell, { ...props, active: "mailboxes", children: _jsx(MailboxDetailContent, { uid: props.params.uid, impersonationBaseUrl: props.impersonationBaseUrl }) }));
26
+ return (_jsx(AdminShell, { ...props, active: "mailboxes", children: _jsx(MailboxDetailContent, { uid: props.params.uid, impersonationBaseUrl: props.impersonationBaseUrl, currentUserUid: props.userUid }) }));
27
27
  }
28
- function MailboxDetailContent({ uid, impersonationBaseUrl }) {
28
+ function MailboxDetailContent({ uid, impersonationBaseUrl, currentUserUid, }) {
29
29
  const [mailbox, setMailbox] = useState(null);
30
30
  const [loading, setLoading] = useState(true);
31
31
  const [error, setError] = useState(null);
@@ -43,7 +43,7 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
43
43
  setConfirmingAccess(false);
44
44
  setAccessError(null);
45
45
  }
46
- // Only ever invoked from the access-confirmation modal below, which only renders for a loaded mailbox that has
46
+ // Only ever invoked from the impersonation-confirmation modal below, which only renders for a loaded mailbox that has
47
47
  // an owner. A failure stays in the modal rather than replacing the whole page.
48
48
  async function handleAccessMailbox() {
49
49
  setImpersonating(true);
@@ -53,7 +53,7 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
53
53
  window.location.href = "/";
54
54
  }
55
55
  catch (err) {
56
- setAccessError(err instanceof ApiRequestError ? err.message : "Could not access this mailbox.");
56
+ setAccessError(err instanceof ApiRequestError ? err.message : "Could not impersonate this user.");
57
57
  setImpersonating(false);
58
58
  }
59
59
  }
@@ -82,7 +82,8 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
82
82
  useEffect(() => {
83
83
  setLoading(true);
84
84
  setError(null);
85
- getMailbox(uid)
85
+ // The administration scope: administrative metadata only - the console never shows a mailbox's mail or settings.
86
+ getMailbox(uid, { scope: "admin" })
86
87
  .then(setMailbox)
87
88
  .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this mailbox."))
88
89
  .finally(() => setLoading(false));
@@ -95,5 +96,7 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
95
96
  }
96
97
  const quarantineHref = `/admin/quarantine?mailboxUid=${encodeURIComponent(mailbox.uid)}`;
97
98
  const ingestQueueHref = `/admin/ingest-queue?mailboxUid=${encodeURIComponent(mailbox.uid)}`;
98
- return (_jsxs("div", { className: "max-w-3xl flex flex-col gap-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsx("a", { href: "/admin", className: "text-sm text-primary-dark hover:underline", children: "\u2190 All mailboxes" }), _jsx("h1", { className: "text-xl font-bold tracking-tight mt-1", children: mailbox.primarySmtpAddress })] }), _jsxs("div", { className: "flex gap-3 shrink-0", children: [mailbox.ownerUserUid && (_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingAccess(true), children: "Access this mailbox" })), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto !border-danger !text-danger hover:!border-danger hover:!text-danger", onClick: () => setConfirmingDelete(true), children: "Delete mailbox" })] })] }), _jsxs("div", { className: "bg-surface border border-border rounded-md p-6", children: [_jsxs("dl", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-3 text-sm", children: [_jsx("dt", { className: "text-text-muted", children: "Display name" }), _jsx("dd", { children: mailbox.displayName }), _jsx("dt", { className: "text-text-muted", children: "Owner" }), _jsx("dd", { children: mailbox.ownerUserUid ?? "None (shared mailbox)" }), _jsx("dt", { className: "text-text-muted", children: "Timezone" }), _jsx("dd", { children: mailbox.timezone }), _jsx("dt", { className: "text-text-muted", children: "Quota" }), _jsxs("dd", { children: [formatBytes(mailbox.usedBytes), " / ", formatBytes(mailbox.quotaBytes)] }), _jsx("dt", { className: "text-text-muted", children: "Alias addresses" }), _jsx("dd", { children: mailbox.aliasAddresses.length > 0 ? mailbox.aliasAddresses.join(", ") : "None" }), mailbox.isResource && (_jsxs(_Fragment, { children: [_jsx("dt", { className: "text-text-muted", children: "Resource type" }), _jsx("dd", { className: "capitalize", children: mailbox.resourceType ?? "room" })] })), _jsx("dt", { className: "text-text-muted", children: "Created" }), _jsx("dd", { children: new Date(mailbox.dateCreated).toLocaleString() })] }), _jsxs("div", { className: "flex gap-4 mt-5 pt-5 border-t border-border text-sm font-medium", children: [_jsx("a", { href: quarantineHref, className: "text-primary-dark hover:underline", children: "View quarantine" }), _jsx("a", { href: ingestQueueHref, className: "text-primary-dark hover:underline", children: "View ingest queue" })] })] }), _jsx(ShareAccessCard, { mailboxUid: mailbox.uid, ownerUserUid: mailbox.ownerUserUid }), _jsx(EscrowScopeCard, { mailbox: mailbox, onUpdate: setMailbox }), mailbox.isResource && _jsx(ResourceSettingsCard, { mailbox: mailbox, onUpdate: setMailbox }), _jsxs(Modal, { open: confirmingAccess, onClose: closeAccessModal, title: "Access this mailbox", children: [_jsxs("p", { className: "text-sm mb-3", children: ["You\u2019ll be signed in as ", _jsx("strong", { className: "break-all", children: mailbox.ownerUserUid }), ", the owner of ", _jsx("strong", { className: "break-all", children: mailbox.primarySmtpAddress }), ", and see everything they can - and act as them, until you stop impersonating."] }), accessError && _jsx(Alert, { children: accessError }), _jsxs("div", { className: "flex gap-3 justify-end mt-5", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: impersonating, onClick: closeAccessModal, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: impersonating, disabled: impersonating, onClick: handleAccessMailbox, children: "Access mailbox" })] })] }), _jsxs(Modal, { open: confirmingDelete, onClose: closeDeleteModal, title: "Delete mailbox", children: [_jsxs("p", { className: "text-sm mb-5", children: ["Are you sure you want to delete ", _jsx("strong", { children: mailbox.primarySmtpAddress }), "? This permanently deletes the mailbox and everything in it, and cannot be undone. It fails if this mailbox is a custodian on an active legal hold."] }), deleteError && _jsx(Alert, { children: deleteError }), _jsxs("div", { className: "flex gap-3 justify-end mt-5", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: deleting, onClick: closeDeleteModal, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger", loading: deleting, disabled: deleting, onClick: handleDelete, children: "Delete" })] })] })] }));
99
+ return (_jsxs("div", { className: "max-w-3xl flex flex-col gap-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsx("a", { href: "/admin", className: "text-sm text-primary-dark hover:underline", children: "\u2190 All mailboxes" }), _jsx("h1", { className: "text-xl font-bold tracking-tight mt-1", children: mailbox.primarySmtpAddress })] }), _jsxs("div", { className: "flex gap-3 shrink-0", children: [mailbox.ownerUserUid && (_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingAccess(true), children: "Impersonate this user" })), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto !border-danger !text-danger hover:!border-danger hover:!text-danger", onClick: () => setConfirmingDelete(true), children: "Delete mailbox" })] })] }), _jsxs("p", { className: "text-sm text-text-muted", children: ["The console shows administrative details only - never a mailbox\u2019s mail or settings.", mailbox.ownerUserUid
100
+ ? " To see this mailbox as its owner sees it, impersonate them: that is recorded, and you can stop at any time."
101
+ : " This shared mailbox has no owner: add yourself under Shared access to open it in the mail client."] }), _jsxs("div", { className: "bg-surface border border-border rounded-md p-6", children: [_jsxs("dl", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-3 text-sm", children: [_jsx("dt", { className: "text-text-muted", children: "Display name" }), _jsx("dd", { children: mailbox.displayName }), _jsx("dt", { className: "text-text-muted", children: "Owner" }), _jsx("dd", { children: mailbox.ownerUserUid ?? "None (shared mailbox)" }), _jsx("dt", { className: "text-text-muted", children: "Timezone" }), _jsx("dd", { children: mailbox.timezone }), _jsx("dt", { className: "text-text-muted", children: "Quota" }), _jsxs("dd", { children: [formatBytes(mailbox.usedBytes), " / ", formatBytes(mailbox.quotaBytes)] }), _jsx("dt", { className: "text-text-muted", children: "Alias addresses" }), _jsx("dd", { children: mailbox.aliasAddresses.length > 0 ? mailbox.aliasAddresses.join(", ") : "None" }), mailbox.isResource && (_jsxs(_Fragment, { children: [_jsx("dt", { className: "text-text-muted", children: "Resource type" }), _jsx("dd", { className: "capitalize", children: mailbox.resourceType ?? "room" })] })), _jsx("dt", { className: "text-text-muted", children: "Created" }), _jsx("dd", { children: new Date(mailbox.dateCreated).toLocaleString() })] }), _jsxs("div", { className: "flex gap-4 mt-5 pt-5 border-t border-border text-sm font-medium", children: [_jsx("a", { href: quarantineHref, className: "text-primary-dark hover:underline", children: "View quarantine" }), _jsx("a", { href: ingestQueueHref, className: "text-primary-dark hover:underline", children: "View ingest queue" })] })] }), _jsx(ShareAccessCard, { mailboxUid: mailbox.uid, ownerUserUid: mailbox.ownerUserUid, currentUserUid: currentUserUid }), _jsx(EscrowScopeCard, { mailbox: mailbox, onUpdate: setMailbox }), mailbox.isResource && _jsx(ResourceSettingsCard, { mailbox: mailbox, onUpdate: setMailbox }), _jsxs(Modal, { open: confirmingAccess, onClose: closeAccessModal, title: "Impersonate this user", children: [_jsxs("p", { className: "text-sm mb-3", children: ["You\u2019ll be signed in as ", _jsx("strong", { className: "break-all", children: mailbox.ownerUserUid }), ", the owner of ", _jsx("strong", { className: "break-all", children: mailbox.primarySmtpAddress }), ", and see everything they can - and act as them, until you stop impersonating. This is the only way an administrator sees another user\u2019s mail."] }), accessError && _jsx(Alert, { children: accessError }), _jsxs("div", { className: "flex gap-3 justify-end mt-5", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: impersonating, onClick: closeAccessModal, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: impersonating, disabled: impersonating, onClick: handleAccessMailbox, children: "Impersonate" })] })] }), _jsxs(Modal, { open: confirmingDelete, onClose: closeDeleteModal, title: "Delete mailbox", children: [_jsxs("p", { className: "text-sm mb-5", children: ["Are you sure you want to delete ", _jsx("strong", { children: mailbox.primarySmtpAddress }), "? This permanently deletes the mailbox and everything in it, and cannot be undone. It fails if this mailbox is a custodian on an active legal hold."] }), deleteError && _jsx(Alert, { children: deleteError }), _jsxs("div", { className: "flex gap-3 justify-end mt-5", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: deleting, onClick: closeDeleteModal, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger", loading: deleting, disabled: deleting, onClick: handleDelete, children: "Delete" })] })] })] }));
99
102
  }
@@ -34,7 +34,8 @@ function QuarantineContent({ userUid }) {
34
34
  function reload(uid) {
35
35
  setLoading(true);
36
36
  setError(null);
37
- listQuarantine(uid, { page, limit: PAGE_SIZE })
37
+ // The administration scope: any mailbox's held mail, recorded in the audit log.
38
+ listQuarantine(uid, { page, limit: PAGE_SIZE, scope: "admin" })
38
39
  .then(setEntries)
39
40
  .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load quarantine."))
40
41
  .finally(() => setLoading(false));
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function SigningCertificatesPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;