@rapidmx/web-client 0.9.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 (443) hide show
  1. package/README.md +373 -88
  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/auth/accountUrl.ts +9 -0
  21. package/apps/shared/auth/adminAccess.ts +99 -0
  22. package/apps/shared/components/admin/layout/AdminShell.tsx +22 -10
  23. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +107 -60
  24. package/apps/shared/components/admin/settings/BrandingForm.tsx +24 -0
  25. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +2 -1
  26. package/apps/shared/components/admin/settings/PluginsManager.tsx +228 -25
  27. package/apps/shared/components/admin/setup/SetupWizard.tsx +2 -1
  28. package/apps/shared/components/calendar/CalendarListSidebar.tsx +4 -5
  29. package/apps/shared/components/calendar/layout/CalendarShell.tsx +8 -2
  30. package/apps/shared/components/contacts/ContactForm.tsx +11 -5
  31. package/apps/shared/components/contacts/ContactsSidebar.tsx +5 -3
  32. package/apps/shared/components/contacts/ContactsToolbar.tsx +98 -115
  33. package/apps/shared/components/contacts/layout/ContactsShell.tsx +25 -8
  34. package/apps/shared/components/escrow/layout/EscrowShell.tsx +13 -6
  35. package/apps/shared/components/layout/AppShell.tsx +477 -288
  36. package/apps/shared/components/layout/BrandingChrome.tsx +200 -13
  37. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -389
  38. package/apps/shared/components/layout/MailboxProvisioning.tsx +168 -164
  39. package/apps/shared/components/layout/RailIcon.tsx +100 -0
  40. package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -0
  41. package/apps/shared/components/layout/ThemeSwitch.tsx +84 -0
  42. package/apps/shared/components/layout/UserMenu.tsx +372 -185
  43. package/apps/shared/components/mail/ConversationList.tsx +271 -262
  44. package/apps/shared/components/mail/ConversationThreadPane.tsx +147 -94
  45. package/apps/shared/components/mail/LazyReadingPane.tsx +113 -0
  46. package/apps/shared/components/mail/MailSelectionBar.tsx +227 -222
  47. package/apps/shared/components/mail/MessageDetailPane.tsx +1537 -1387
  48. package/apps/shared/components/mail/OutboxBadge.tsx +44 -0
  49. package/apps/shared/components/mail/OutboxRowStatus.tsx +31 -0
  50. package/apps/shared/components/mail/compose/ComposeContext.tsx +312 -160
  51. package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -418
  52. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1644 -1736
  53. package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +111 -0
  54. package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -118
  55. package/apps/shared/components/mail/compose/composePerf.ts +46 -0
  56. package/apps/shared/components/mail/compose/encryptionRequirement.ts +86 -0
  57. package/apps/shared/components/mail/compose/quotedBody.ts +161 -100
  58. package/apps/shared/components/mail/layout/MailShell.tsx +490 -472
  59. package/apps/shared/components/mail/reading/EncryptedBody.tsx +65 -0
  60. package/apps/shared/components/mail/reading/EncryptedPreview.tsx +35 -0
  61. package/apps/shared/components/mail/reading/MessageBody.tsx +240 -0
  62. package/apps/shared/components/mail/reading/MessageCard.tsx +174 -0
  63. package/apps/shared/components/mail/reading/bodyContent.ts +100 -0
  64. package/apps/shared/components/mail/reading/bodyHtml.ts +298 -0
  65. package/apps/shared/components/mail/reading/color.ts +245 -0
  66. package/apps/shared/components/mail/reading/frameControl.ts +142 -0
  67. package/apps/shared/components/mail/reading/frameDocument.ts +123 -0
  68. package/apps/shared/components/mail/reading/safeDocument.ts +24 -0
  69. package/apps/shared/components/mail/reading/themeAdaptation.ts +221 -0
  70. package/apps/shared/components/mail/reading/themeSurface.ts +90 -0
  71. package/apps/shared/components/mail/reading/viewOriginal.ts +38 -0
  72. package/apps/shared/components/mail/unreadStyle.tsx +75 -0
  73. package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -0
  74. package/apps/shared/components/settings/layout/SettingsShell.tsx +253 -240
  75. package/apps/shared/components/sharing/PrincipalPicker.tsx +145 -0
  76. package/apps/shared/components/tasks/TasksSidebar.tsx +5 -3
  77. package/apps/shared/components/tasks/layout/TasksShell.tsx +24 -8
  78. package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -0
  79. package/apps/shared/keyboard/ShortcutProvider.tsx +62 -0
  80. package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -0
  81. package/apps/shared/keyboard/dispatch.ts +124 -0
  82. package/apps/shared/keyboard/format.ts +89 -0
  83. package/apps/shared/keyboard/keymap.ts +114 -0
  84. package/apps/shared/keyboard/match.ts +44 -0
  85. package/apps/shared/keyboard/parse.ts +136 -0
  86. package/apps/shared/keyboard/platform.ts +34 -0
  87. package/apps/shared/keyboard/registry.ts +65 -0
  88. package/apps/shared/keyboard/targets.ts +79 -0
  89. package/apps/shared/keyboard/useShortcut.ts +50 -0
  90. package/apps/shared/keyboard/useShortcutProps.ts +17 -0
  91. package/apps/shared/mail/folderCounts.ts +308 -0
  92. package/apps/shared/mail/folderTree.ts +143 -0
  93. package/apps/shared/mail/listSnapshots.ts +87 -0
  94. package/apps/shared/mail/messageReadState.ts +85 -0
  95. package/apps/shared/mail/newMailNotifications.ts +183 -0
  96. package/apps/shared/mail/outbox/UnlockBridge.tsx +23 -0
  97. package/apps/shared/mail/outbox/composeBridge.ts +69 -0
  98. package/apps/shared/mail/outbox/outboxState.ts +88 -0
  99. package/apps/shared/mail/outbox/pendingSends.ts +146 -0
  100. package/apps/shared/mail/outbox/sendDecision.ts +157 -0
  101. package/apps/shared/mail/outbox/sendJob.ts +427 -0
  102. package/apps/shared/mail/outbox/sendOutcomes.ts +146 -0
  103. package/apps/shared/mail/outbox/sendState.ts +35 -0
  104. package/apps/shared/mail/outbox/useOutboxStatus.ts +61 -0
  105. package/apps/shared/mail/useMailConnection.ts +203 -0
  106. package/apps/shared/mail/useMailLiveUpdates.ts +277 -224
  107. package/apps/shared/mail/useMarkMessageRead.ts +47 -0
  108. package/apps/shared/mail/useNewMailNotifications.ts +178 -0
  109. package/apps/shared/mail/useUnreadTitle.ts +42 -0
  110. package/apps/shared/navigation/AppRouter.tsx +300 -0
  111. package/apps/shared/navigation/appHrefs.ts +23 -0
  112. package/apps/shared/navigation/frameContext.tsx +35 -0
  113. package/apps/shared/navigation/idle.ts +45 -0
  114. package/apps/shared/navigation/routerContext.tsx +83 -0
  115. package/apps/shared/navigation/routes.ts +72 -0
  116. package/apps/shared/notifications/NotificationCenter.tsx +215 -0
  117. package/apps/shared/notifications/NotificationHistoryDialog.tsx +90 -0
  118. package/apps/shared/notifications/apiErrors.ts +98 -0
  119. package/apps/shared/notifications/headerOffset.ts +35 -0
  120. package/apps/shared/notifications/pushStatus.ts +56 -0
  121. package/apps/shared/notifications/store.ts +549 -0
  122. package/apps/shared/notifications/systemErrors.ts +63 -0
  123. package/apps/shared/notifications/useNotifications.ts +41 -0
  124. package/apps/shared/search/LocalIndexLifecycle.tsx +114 -98
  125. package/apps/shared/signing/enrollmentStorage.ts +33 -0
  126. package/apps/shared/signing/enrollmentTracker.ts +385 -0
  127. package/apps/shared/signing/enrollmentView.ts +251 -0
  128. package/apps/shared/signing/signingInfo.ts +58 -0
  129. package/apps/shared/signing/useNow.ts +19 -0
  130. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +90 -0
  131. package/apps/shared/styles/app.css +108 -10
  132. package/apps/www/_layout.tsx +8 -2
  133. package/apps/www/_routedPage.tsx +24 -0
  134. package/apps/www/_routes.ts +36 -0
  135. package/apps/www/calendar/index.tsx +64 -33
  136. package/apps/www/contacts/[uid].tsx +109 -107
  137. package/apps/www/contacts/index.tsx +62 -42
  138. package/apps/www/index.tsx +2291 -1917
  139. package/apps/www/messages/[uid].tsx +108 -101
  140. package/apps/www/settings/appearance/index.tsx +21 -0
  141. package/apps/www/settings/auto-reply/index.tsx +7 -8
  142. package/apps/www/settings/encryption/index.tsx +1287 -1249
  143. package/apps/www/settings/filters/[uid].tsx +7 -2
  144. package/apps/www/settings/filters/index.tsx +102 -99
  145. package/apps/www/settings/filters/new/index.tsx +140 -133
  146. package/apps/www/settings/labels/index.tsx +204 -201
  147. package/apps/www/settings/privacy/index.tsx +7 -9
  148. package/apps/www/settings/read-receipts/index.tsx +7 -8
  149. package/apps/www/settings/sharing/index.tsx +256 -274
  150. package/apps/www/settings/signatures/[uid].tsx +172 -167
  151. package/apps/www/settings/signatures/index.tsx +88 -85
  152. package/apps/www/settings/signatures/new/index.tsx +135 -129
  153. package/apps/www/tasks/index.tsx +29 -18
  154. package/dist/apps/admin/_layout.d.ts +5 -1
  155. package/dist/apps/admin/_layout.js +3 -2
  156. package/dist/apps/admin/index.js +3 -2
  157. package/dist/apps/admin/ingest-queue/index.js +2 -1
  158. package/dist/apps/admin/mailboxes/[uid].js +9 -6
  159. package/dist/apps/admin/quarantine/index.js +2 -1
  160. package/dist/apps/admin/signing-certificates/index.d.ts +3 -0
  161. package/dist/apps/admin/signing-certificates/index.js +161 -0
  162. package/dist/apps/escrow/_layout.d.ts +5 -1
  163. package/dist/apps/escrow/_layout.js +3 -2
  164. package/dist/apps/shared/appearance/AppearanceForm.d.ts +7 -0
  165. package/dist/apps/shared/appearance/AppearanceForm.js +131 -0
  166. package/dist/apps/shared/appearance/AppearanceHead.d.ts +15 -0
  167. package/dist/apps/shared/appearance/AppearanceHead.js +31 -0
  168. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +32 -0
  169. package/dist/apps/shared/appearance/AppearanceProvider.js +390 -0
  170. package/dist/apps/shared/appearance/ColorField.d.ts +22 -0
  171. package/dist/apps/shared/appearance/ColorField.js +40 -0
  172. package/dist/apps/shared/appearance/appearanceCache.d.ts +45 -0
  173. package/dist/apps/shared/appearance/appearanceCache.js +80 -0
  174. package/dist/apps/shared/appearance/appearanceContext.d.ts +45 -0
  175. package/dist/apps/shared/appearance/appearanceContext.js +29 -0
  176. package/dist/apps/shared/appearance/bootScript.d.ts +16 -0
  177. package/dist/apps/shared/appearance/bootScript.js +33 -0
  178. package/dist/apps/shared/appearance/color.d.ts +65 -0
  179. package/dist/apps/shared/appearance/color.js +132 -0
  180. package/dist/apps/shared/appearance/photo.d.ts +17 -0
  181. package/dist/apps/shared/appearance/photo.js +79 -0
  182. package/dist/apps/shared/appearance/resolvedTheme.d.ts +21 -0
  183. package/dist/apps/shared/appearance/resolvedTheme.js +55 -0
  184. package/dist/apps/shared/appearance/theme.d.ts +111 -0
  185. package/dist/apps/shared/appearance/theme.js +251 -0
  186. package/dist/apps/shared/auth/accountUrl.d.ts +2 -0
  187. package/dist/apps/shared/auth/accountUrl.js +8 -0
  188. package/dist/apps/shared/auth/adminAccess.d.ts +30 -0
  189. package/dist/apps/shared/auth/adminAccess.js +89 -0
  190. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  191. package/dist/apps/shared/components/admin/layout/AdminShell.js +16 -6
  192. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.d.ts +10 -5
  193. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +49 -34
  194. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +2 -0
  195. package/dist/apps/shared/components/admin/settings/BrandingForm.js +7 -1
  196. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +1 -1
  197. package/dist/apps/shared/components/admin/settings/PluginsManager.js +106 -20
  198. package/dist/apps/shared/components/admin/setup/SetupWizard.js +2 -1
  199. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +8 -8
  200. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  201. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +8 -4
  202. package/dist/apps/shared/components/contacts/ContactForm.js +1 -1
  203. package/dist/apps/shared/components/contacts/ContactsSidebar.js +4 -2
  204. package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +6 -2
  205. package/dist/apps/shared/components/contacts/ContactsToolbar.js +28 -10
  206. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  207. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +19 -6
  208. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -4
  209. package/dist/apps/shared/components/layout/AppShell.d.ts +34 -4
  210. package/dist/apps/shared/components/layout/AppShell.js +122 -17
  211. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +69 -10
  212. package/dist/apps/shared/components/layout/BrandingChrome.js +140 -12
  213. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +10 -9
  214. package/dist/apps/shared/components/layout/MailboxProvisioning.js +3 -1
  215. package/dist/apps/shared/components/layout/RailIcon.d.ts +21 -0
  216. package/dist/apps/shared/components/layout/RailIcon.js +85 -0
  217. package/dist/apps/shared/components/layout/ResponsiveToolbar.d.ts +55 -0
  218. package/dist/apps/shared/components/layout/ResponsiveToolbar.js +199 -0
  219. package/dist/apps/shared/components/layout/ThemeSwitch.d.ts +9 -0
  220. package/dist/apps/shared/components/layout/ThemeSwitch.js +54 -0
  221. package/dist/apps/shared/components/layout/UserMenu.d.ts +25 -3
  222. package/dist/apps/shared/components/layout/UserMenu.js +88 -7
  223. package/dist/apps/shared/components/mail/ConversationList.js +5 -11
  224. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +6 -2
  225. package/dist/apps/shared/components/mail/ConversationThreadPane.js +87 -40
  226. package/dist/apps/shared/components/mail/LazyReadingPane.d.ts +9 -0
  227. package/dist/apps/shared/components/mail/LazyReadingPane.js +83 -0
  228. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +6 -4
  229. package/dist/apps/shared/components/mail/MailSelectionBar.js +11 -4
  230. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +25 -8
  231. package/dist/apps/shared/components/mail/MessageDetailPane.js +167 -73
  232. package/dist/apps/shared/components/mail/OutboxBadge.d.ts +17 -0
  233. package/dist/apps/shared/components/mail/OutboxBadge.js +18 -0
  234. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +10 -0
  235. package/dist/apps/shared/components/mail/OutboxRowStatus.js +19 -0
  236. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +38 -0
  237. package/dist/apps/shared/components/mail/compose/ComposeContext.js +97 -6
  238. package/dist/apps/shared/components/mail/compose/ComposeToolbar.js +4 -3
  239. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +3 -2
  240. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +316 -339
  241. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.d.ts +19 -0
  242. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +33 -0
  243. package/dist/apps/shared/components/mail/compose/RichTextEditor.d.ts +9 -1
  244. package/dist/apps/shared/components/mail/compose/RichTextEditor.js +20 -2
  245. package/dist/apps/shared/components/mail/compose/composePerf.d.ts +16 -0
  246. package/dist/apps/shared/components/mail/compose/composePerf.js +41 -0
  247. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +49 -0
  248. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +35 -0
  249. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +13 -0
  250. package/dist/apps/shared/components/mail/compose/quotedBody.js +67 -11
  251. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +14 -4
  252. package/dist/apps/shared/components/mail/layout/MailShell.js +90 -104
  253. package/dist/apps/shared/components/mail/reading/EncryptedBody.d.ts +21 -0
  254. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +22 -0
  255. package/dist/apps/shared/components/mail/reading/EncryptedPreview.d.ts +18 -0
  256. package/dist/apps/shared/components/mail/reading/EncryptedPreview.js +22 -0
  257. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +32 -0
  258. package/dist/apps/shared/components/mail/reading/MessageBody.js +136 -0
  259. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +62 -0
  260. package/dist/apps/shared/components/mail/reading/MessageCard.js +58 -0
  261. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +38 -0
  262. package/dist/apps/shared/components/mail/reading/bodyContent.js +84 -0
  263. package/dist/apps/shared/components/mail/reading/bodyHtml.d.ts +47 -0
  264. package/dist/apps/shared/components/mail/reading/bodyHtml.js +267 -0
  265. package/dist/apps/shared/components/mail/reading/color.d.ts +63 -0
  266. package/dist/apps/shared/components/mail/reading/color.js +201 -0
  267. package/dist/apps/shared/components/mail/reading/frameControl.d.ts +33 -0
  268. package/dist/apps/shared/components/mail/reading/frameControl.js +120 -0
  269. package/dist/apps/shared/components/mail/reading/frameDocument.d.ts +52 -0
  270. package/dist/apps/shared/components/mail/reading/frameDocument.js +98 -0
  271. package/dist/apps/shared/components/mail/reading/safeDocument.d.ts +3 -0
  272. package/dist/apps/shared/components/mail/reading/safeDocument.js +12 -0
  273. package/dist/apps/shared/components/mail/reading/themeAdaptation.d.ts +56 -0
  274. package/dist/apps/shared/components/mail/reading/themeAdaptation.js +144 -0
  275. package/dist/apps/shared/components/mail/reading/themeSurface.d.ts +17 -0
  276. package/dist/apps/shared/components/mail/reading/themeSurface.js +83 -0
  277. package/dist/apps/shared/components/mail/reading/viewOriginal.d.ts +9 -0
  278. package/dist/apps/shared/components/mail/reading/viewOriginal.js +32 -0
  279. package/dist/apps/shared/components/mail/unreadStyle.d.ts +45 -0
  280. package/dist/apps/shared/components/mail/unreadStyle.js +61 -0
  281. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +32 -0
  282. package/dist/apps/shared/components/settings/SigningCertificateCard.js +98 -0
  283. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  284. package/dist/apps/shared/components/settings/layout/SettingsShell.js +16 -5
  285. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +26 -0
  286. package/dist/apps/shared/components/sharing/PrincipalPicker.js +77 -0
  287. package/dist/apps/shared/components/tasks/TasksSidebar.js +4 -2
  288. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  289. package/dist/apps/shared/components/tasks/layout/TasksShell.js +18 -6
  290. package/dist/apps/shared/keyboard/GlobalShortcuts.d.ts +14 -0
  291. package/dist/apps/shared/keyboard/GlobalShortcuts.js +37 -0
  292. package/dist/apps/shared/keyboard/ShortcutProvider.d.ts +20 -0
  293. package/dist/apps/shared/keyboard/ShortcutProvider.js +52 -0
  294. package/dist/apps/shared/keyboard/ShortcutsDialog.d.ts +14 -0
  295. package/dist/apps/shared/keyboard/ShortcutsDialog.js +42 -0
  296. package/dist/apps/shared/keyboard/dispatch.d.ts +16 -0
  297. package/dist/apps/shared/keyboard/dispatch.js +109 -0
  298. package/dist/apps/shared/keyboard/format.d.ts +12 -0
  299. package/dist/apps/shared/keyboard/format.js +74 -0
  300. package/dist/apps/shared/keyboard/keymap.d.ts +294 -0
  301. package/dist/apps/shared/keyboard/keymap.js +84 -0
  302. package/dist/apps/shared/keyboard/match.d.ts +20 -0
  303. package/dist/apps/shared/keyboard/match.js +31 -0
  304. package/dist/apps/shared/keyboard/parse.d.ts +31 -0
  305. package/dist/apps/shared/keyboard/parse.js +107 -0
  306. package/dist/apps/shared/keyboard/platform.d.ts +15 -0
  307. package/dist/apps/shared/keyboard/platform.js +22 -0
  308. package/dist/apps/shared/keyboard/registry.d.ts +39 -0
  309. package/dist/apps/shared/keyboard/registry.js +33 -0
  310. package/dist/apps/shared/keyboard/targets.d.ts +14 -0
  311. package/dist/apps/shared/keyboard/targets.js +67 -0
  312. package/dist/apps/shared/keyboard/useShortcut.d.ts +19 -0
  313. package/dist/apps/shared/keyboard/useShortcut.js +35 -0
  314. package/dist/apps/shared/keyboard/useShortcutProps.d.ts +10 -0
  315. package/dist/apps/shared/keyboard/useShortcutProps.js +15 -0
  316. package/dist/apps/shared/mail/folderCounts.d.ts +78 -0
  317. package/dist/apps/shared/mail/folderCounts.js +218 -0
  318. package/dist/apps/shared/mail/folderTree.d.ts +55 -0
  319. package/dist/apps/shared/mail/folderTree.js +116 -0
  320. package/dist/apps/shared/mail/listSnapshots.d.ts +46 -0
  321. package/dist/apps/shared/mail/listSnapshots.js +43 -0
  322. package/dist/apps/shared/mail/messageReadState.d.ts +32 -0
  323. package/dist/apps/shared/mail/messageReadState.js +63 -0
  324. package/dist/apps/shared/mail/newMailNotifications.d.ts +62 -0
  325. package/dist/apps/shared/mail/newMailNotifications.js +138 -0
  326. package/dist/apps/shared/mail/outbox/UnlockBridge.d.ts +5 -0
  327. package/dist/apps/shared/mail/outbox/UnlockBridge.js +18 -0
  328. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +34 -0
  329. package/dist/apps/shared/mail/outbox/composeBridge.js +32 -0
  330. package/dist/apps/shared/mail/outbox/outboxState.d.ts +41 -0
  331. package/dist/apps/shared/mail/outbox/outboxState.js +42 -0
  332. package/dist/apps/shared/mail/outbox/pendingSends.d.ts +39 -0
  333. package/dist/apps/shared/mail/outbox/pendingSends.js +109 -0
  334. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +80 -0
  335. package/dist/apps/shared/mail/outbox/sendDecision.js +94 -0
  336. package/dist/apps/shared/mail/outbox/sendJob.d.ts +68 -0
  337. package/dist/apps/shared/mail/outbox/sendJob.js +319 -0
  338. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +3 -0
  339. package/dist/apps/shared/mail/outbox/sendOutcomes.js +141 -0
  340. package/dist/apps/shared/mail/outbox/sendState.d.ts +25 -0
  341. package/dist/apps/shared/mail/outbox/sendState.js +8 -0
  342. package/dist/apps/shared/mail/outbox/useOutboxStatus.d.ts +11 -0
  343. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +49 -0
  344. package/dist/apps/shared/mail/useMailConnection.d.ts +56 -0
  345. package/dist/apps/shared/mail/useMailConnection.js +138 -0
  346. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +29 -9
  347. package/dist/apps/shared/mail/useMailLiveUpdates.js +60 -32
  348. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +12 -0
  349. package/dist/apps/shared/mail/useMarkMessageRead.js +44 -0
  350. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +35 -0
  351. package/dist/apps/shared/mail/useNewMailNotifications.js +130 -0
  352. package/dist/apps/shared/mail/useUnreadTitle.d.ts +18 -0
  353. package/dist/apps/shared/mail/useUnreadTitle.js +31 -0
  354. package/dist/apps/shared/navigation/AppRouter.d.ts +52 -0
  355. package/dist/apps/shared/navigation/AppRouter.js +242 -0
  356. package/dist/apps/shared/navigation/appHrefs.d.ts +14 -0
  357. package/dist/apps/shared/navigation/appHrefs.js +20 -0
  358. package/dist/apps/shared/navigation/frameContext.d.ts +19 -0
  359. package/dist/apps/shared/navigation/frameContext.js +24 -0
  360. package/dist/apps/shared/navigation/idle.d.ts +14 -0
  361. package/dist/apps/shared/navigation/idle.js +43 -0
  362. package/dist/apps/shared/navigation/routerContext.d.ts +37 -0
  363. package/dist/apps/shared/navigation/routerContext.js +56 -0
  364. package/dist/apps/shared/navigation/routes.d.ts +32 -0
  365. package/dist/apps/shared/navigation/routes.js +37 -0
  366. package/dist/apps/shared/notifications/NotificationCenter.d.ts +19 -0
  367. package/dist/apps/shared/notifications/NotificationCenter.js +90 -0
  368. package/dist/apps/shared/notifications/NotificationHistoryDialog.d.ts +10 -0
  369. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +36 -0
  370. package/dist/apps/shared/notifications/apiErrors.d.ts +19 -0
  371. package/dist/apps/shared/notifications/apiErrors.js +86 -0
  372. package/dist/apps/shared/notifications/headerOffset.d.ts +8 -0
  373. package/dist/apps/shared/notifications/headerOffset.js +33 -0
  374. package/dist/apps/shared/notifications/pushStatus.d.ts +9 -0
  375. package/dist/apps/shared/notifications/pushStatus.js +54 -0
  376. package/dist/apps/shared/notifications/store.d.ts +136 -0
  377. package/dist/apps/shared/notifications/store.js +416 -0
  378. package/dist/apps/shared/notifications/systemErrors.d.ts +9 -0
  379. package/dist/apps/shared/notifications/systemErrors.js +58 -0
  380. package/dist/apps/shared/notifications/useNotifications.d.ts +17 -0
  381. package/dist/apps/shared/notifications/useNotifications.js +18 -0
  382. package/dist/apps/shared/search/LocalIndexLifecycle.js +17 -3
  383. package/dist/apps/shared/signing/enrollmentStorage.d.ts +10 -0
  384. package/dist/apps/shared/signing/enrollmentStorage.js +33 -0
  385. package/dist/apps/shared/signing/enrollmentTracker.d.ts +73 -0
  386. package/dist/apps/shared/signing/enrollmentTracker.js +307 -0
  387. package/dist/apps/shared/signing/enrollmentView.d.ts +57 -0
  388. package/dist/apps/shared/signing/enrollmentView.js +214 -0
  389. package/dist/apps/shared/signing/signingInfo.d.ts +11 -0
  390. package/dist/apps/shared/signing/signingInfo.js +48 -0
  391. package/dist/apps/shared/signing/useNow.d.ts +2 -0
  392. package/dist/apps/shared/signing/useNow.js +18 -0
  393. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +16 -0
  394. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +87 -0
  395. package/dist/apps/shared/styles/app.css +108 -10
  396. package/dist/apps/www/_layout.d.ts +5 -1
  397. package/dist/apps/www/_layout.js +3 -2
  398. package/dist/apps/www/_routedPage.d.ts +12 -0
  399. package/dist/apps/www/_routedPage.js +19 -0
  400. package/dist/apps/www/_routes.d.ts +11 -0
  401. package/dist/apps/www/_routes.js +30 -0
  402. package/dist/apps/www/calendar/index.d.ts +2 -2
  403. package/dist/apps/www/calendar/index.js +41 -11
  404. package/dist/apps/www/contacts/[uid].d.ts +3 -9
  405. package/dist/apps/www/contacts/[uid].js +11 -9
  406. package/dist/apps/www/contacts/index.d.ts +2 -2
  407. package/dist/apps/www/contacts/index.js +30 -24
  408. package/dist/apps/www/index.d.ts +2 -2
  409. package/dist/apps/www/index.js +332 -48
  410. package/dist/apps/www/messages/[uid].d.ts +3 -7
  411. package/dist/apps/www/messages/[uid].js +10 -5
  412. package/dist/apps/www/settings/appearance/index.d.ts +4 -0
  413. package/dist/apps/www/settings/appearance/index.js +13 -0
  414. package/dist/apps/www/settings/auto-reply/index.d.ts +2 -2
  415. package/dist/apps/www/settings/auto-reply/index.js +6 -7
  416. package/dist/apps/www/settings/encryption/index.d.ts +2 -2
  417. package/dist/apps/www/settings/encryption/index.js +106 -96
  418. package/dist/apps/www/settings/filters/[uid].d.ts +2 -2
  419. package/dist/apps/www/settings/filters/[uid].js +6 -2
  420. package/dist/apps/www/settings/filters/index.d.ts +2 -2
  421. package/dist/apps/www/settings/filters/index.js +3 -1
  422. package/dist/apps/www/settings/filters/new/index.d.ts +2 -2
  423. package/dist/apps/www/settings/filters/new/index.js +9 -3
  424. package/dist/apps/www/settings/labels/index.d.ts +2 -2
  425. package/dist/apps/www/settings/labels/index.js +3 -1
  426. package/dist/apps/www/settings/privacy/index.d.ts +2 -2
  427. package/dist/apps/www/settings/privacy/index.js +8 -9
  428. package/dist/apps/www/settings/read-receipts/index.d.ts +2 -2
  429. package/dist/apps/www/settings/read-receipts/index.js +6 -7
  430. package/dist/apps/www/settings/sharing/index.d.ts +2 -2
  431. package/dist/apps/www/settings/sharing/index.js +22 -34
  432. package/dist/apps/www/settings/signatures/[uid].d.ts +2 -2
  433. package/dist/apps/www/settings/signatures/[uid].js +6 -2
  434. package/dist/apps/www/settings/signatures/index.d.ts +2 -2
  435. package/dist/apps/www/settings/signatures/index.js +3 -1
  436. package/dist/apps/www/settings/signatures/new/index.d.ts +2 -2
  437. package/dist/apps/www/settings/signatures/new/index.js +9 -4
  438. package/dist/apps/www/tasks/index.d.ts +2 -2
  439. package/dist/apps/www/tasks/index.js +22 -16
  440. package/package.json +2 -2
  441. package/apps/shared/components/mail/compose/SendFailureAlert.tsx +0 -48
  442. package/dist/apps/shared/components/mail/compose/SendFailureAlert.d.ts +0 -14
  443. package/dist/apps/shared/components/mail/compose/SendFailureAlert.js +0 -11
@@ -1,129 +1,135 @@
1
- ///////////////////////////////////////////////////////////////////////////////
2
- // Copyright (C) 2026 Jean-Philippe Steinmetz
3
- // SPDX-License-Identifier: MPL-2.0
4
- ///////////////////////////////////////////////////////////////////////////////
5
- import React, { FormEvent, useState } from "react";
6
- import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
- import { createMailSignature, listMailSignatures } from "@rapidmx/react-shared/mail/mailSignaturesApi.js";
8
- import { clearPreviousDefaults } from "../signatureDefaults.js";
9
- import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../../shared/components/settings/layout/SettingsShell.js";
10
- import RichTextEditor from "../../../../shared/components/mail/compose/RichTextEditor.js";
11
- import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
12
- import Button from "@rapidmx/react-shared/components/buttons/Button.js";
13
- import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
14
-
15
- const INPUT_CLASS =
16
- "w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
17
-
18
- export type NewSignaturePageProps = Omit<SettingsShellProps, "active">;
19
-
20
- export default function NewSignaturePage(props: NewSignaturePageProps) {
21
- return (
22
- <SettingsShell {...props} active="signatures">
23
- <NewSignatureForm />
24
- </SettingsShell>
25
- );
26
- }
27
-
28
- /** Signatures aren't backed by a draft/attachment record the way Compose's own inline images are (see
29
- * `ComposeWindow.handleUploadImage()`) — inline images in a signature are out of scope for this phase
30
- * (`.claude/NOTES.md`'s Phase 12 entry), so this is a real, deliberate no-op, not a stub left unfinished. */
31
- async function handleUploadImage(): Promise<string | null> {
32
- return null;
33
- }
34
-
35
- function NewSignatureForm() {
36
- const { mailboxUid } = useSettingsShell();
37
- const [name, setName] = useState("");
38
- const [contentHtml, setContentHtml] = useState("");
39
- const [isDefaultForNewMessages, setIsDefaultForNewMessages] = useState(false);
40
- const [isDefaultForReplyForward, setIsDefaultForReplyForward] = useState(false);
41
- const [error, setError] = useState<string | null>(null);
42
- const [saving, setSaving] = useState(false);
43
-
44
- async function handleSubmit(e: FormEvent) {
45
- e.preventDefault();
46
- setError(null);
47
-
48
- if (!name.trim()) {
49
- setError("A name is required.");
50
- return;
51
- }
52
-
53
- setSaving(true);
54
- try {
55
- if (isDefaultForNewMessages || isDefaultForReplyForward) {
56
- const existing = await listMailSignatures(mailboxUid!, { limit: 100 });
57
- await clearPreviousDefaults(existing, undefined, isDefaultForNewMessages, isDefaultForReplyForward);
58
- }
59
- const created = await createMailSignature({
60
- mailboxUid: mailboxUid!,
61
- name: name.trim(),
62
- contentHtml,
63
- isDefaultForNewMessages,
64
- isDefaultForReplyForward,
65
- });
66
- window.location.href = `/settings/signatures/${encodeURIComponent(created.uid)}?mailboxUid=${encodeURIComponent(mailboxUid!)}`;
67
- } catch (err) {
68
- setError(err instanceof ApiRequestError ? err.message : "Could not create the signature.");
69
- } finally {
70
- setSaving(false);
71
- }
72
- }
73
-
74
- return (
75
- <div className="flex-1 min-w-0 overflow-y-auto p-6">
76
- <div className="max-w-3xl">
77
- <h1 className="text-lg font-bold tracking-tight mb-5">New signature</h1>
78
-
79
- {error && <Alert>{error}</Alert>}
80
-
81
- <form onSubmit={handleSubmit} className="flex flex-col gap-5">
82
- <div className="bg-surface border border-border rounded-md p-6 flex flex-col gap-4">
83
- <FormField label="Name" htmlFor="name">
84
- <input
85
- id="name"
86
- type="text"
87
- className={INPUT_CLASS}
88
- value={name}
89
- onChange={(e) => setName(e.target.value)}
90
- placeholder="Work signature"
91
- />
92
- </FormField>
93
- <label className="flex items-center gap-2 text-sm">
94
- <input
95
- type="checkbox"
96
- checked={isDefaultForNewMessages}
97
- onChange={(e) => setIsDefaultForNewMessages(e.target.checked)}
98
- />
99
- Use for new messages
100
- </label>
101
- <label className="flex items-center gap-2 text-sm">
102
- <input
103
- type="checkbox"
104
- checked={isDefaultForReplyForward}
105
- onChange={(e) => setIsDefaultForReplyForward(e.target.checked)}
106
- />
107
- Use for replies and forwards
108
- </label>
109
- </div>
110
-
111
- <div className="h-64">
112
- <RichTextEditor value={contentHtml} onChange={setContentHtml} fill onUploadImage={handleUploadImage} />
113
- </div>
114
-
115
- <div className="flex gap-3">
116
- <Button type="submit" loading={saving} disabled={saving} className="!w-auto">
117
- Create signature
118
- </Button>
119
- <a href={`/settings/signatures?mailboxUid=${encodeURIComponent(mailboxUid!)}`}>
120
- <Button type="button" variant="secondary" className="!w-auto">
121
- Cancel
122
- </Button>
123
- </a>
124
- </div>
125
- </form>
126
- </div>
127
- </div>
128
- );
129
- }
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { routedPage } from "../../../_routedPage.js";
6
+ import { useNavigate } from "../../../../shared/navigation/AppRouter.js";
7
+ import React, { FormEvent, useState } from "react";
8
+ import { createMailSignature, listMailSignatures } from "@rapidmx/react-shared/mail/mailSignaturesApi.js";
9
+ import { clearPreviousDefaults } from "../signatureDefaults.js";
10
+ import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../../shared/components/settings/layout/SettingsShell.js";
11
+ import RichTextEditor from "../../../../shared/components/mail/compose/RichTextEditor.js";
12
+ import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
13
+ import Button from "@rapidmx/react-shared/components/buttons/Button.js";
14
+ import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
15
+ import { notifyApiError } from "../../../../shared/notifications/apiErrors.js";
16
+
17
+ const INPUT_CLASS =
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";
19
+
20
+ export type NewSignaturePageProps = Omit<SettingsShellProps, "active">;
21
+
22
+ function NewSignaturePage(props: NewSignaturePageProps) {
23
+ return (
24
+ <SettingsShell {...props} active="signatures">
25
+ <NewSignatureForm />
26
+ </SettingsShell>
27
+ );
28
+ }
29
+
30
+ /** Signatures aren't backed by a draft/attachment record the way Compose's own inline images are (see
31
+ * `ComposeWindow.handleUploadImage()`) — inline images in a signature are out of scope for this phase
32
+ * (`.claude/NOTES.md`'s Phase 12 entry), so this is a real, deliberate no-op, not a stub left unfinished. */
33
+ async function handleUploadImage(): Promise<string | null> {
34
+ return null;
35
+ }
36
+
37
+ function NewSignatureForm() {
38
+ const navigate = useNavigate();
39
+ const { mailboxUid } = useSettingsShell();
40
+ const [name, setName] = useState("");
41
+ const [contentHtml, setContentHtml] = useState("");
42
+ const [isDefaultForNewMessages, setIsDefaultForNewMessages] = useState(false);
43
+ const [isDefaultForReplyForward, setIsDefaultForReplyForward] = useState(false);
44
+ const [error, setError] = useState<string | null>(null);
45
+ const [saving, setSaving] = useState(false);
46
+
47
+ async function handleSubmit(e: FormEvent) {
48
+ e.preventDefault();
49
+ setError(null);
50
+
51
+ if (!name.trim()) {
52
+ setError("A name is required.");
53
+ return;
54
+ }
55
+
56
+ setSaving(true);
57
+ try {
58
+ if (isDefaultForNewMessages || isDefaultForReplyForward) {
59
+ const existing = await listMailSignatures(mailboxUid!, { limit: 100 });
60
+ await clearPreviousDefaults(existing, undefined, isDefaultForNewMessages, isDefaultForReplyForward);
61
+ }
62
+ const created = await createMailSignature({
63
+ mailboxUid: mailboxUid!,
64
+ name: name.trim(),
65
+ contentHtml,
66
+ isDefaultForNewMessages,
67
+ isDefaultForReplyForward,
68
+ });
69
+ navigate(`/settings/signatures/${encodeURIComponent(created.uid)}?mailboxUid=${encodeURIComponent(mailboxUid!)}`);
70
+ } catch (err) {
71
+ // A pop-up: `error` above is only the form's own validation.
72
+ notifyApiError(err, "Couldn't create the signature");
73
+ } finally {
74
+ setSaving(false);
75
+ }
76
+ }
77
+
78
+ return (
79
+ <div className="flex-1 min-w-0 overflow-y-auto p-6">
80
+ <div className="max-w-3xl">
81
+ <h1 className="text-lg font-bold tracking-tight mb-5">New signature</h1>
82
+
83
+ {error && <Alert>{error}</Alert>}
84
+
85
+ <form onSubmit={handleSubmit} className="flex flex-col gap-5">
86
+ <div className="bg-surface border border-border rounded-md p-6 flex flex-col gap-4">
87
+ <FormField label="Name" htmlFor="name">
88
+ <input
89
+ id="name"
90
+ type="text"
91
+ className={INPUT_CLASS}
92
+ value={name}
93
+ onChange={(e) => setName(e.target.value)}
94
+ placeholder="Work signature"
95
+ />
96
+ </FormField>
97
+ <label className="flex items-center gap-2 text-sm">
98
+ <input
99
+ type="checkbox"
100
+ checked={isDefaultForNewMessages}
101
+ onChange={(e) => setIsDefaultForNewMessages(e.target.checked)}
102
+ />
103
+ Use for new messages
104
+ </label>
105
+ <label className="flex items-center gap-2 text-sm">
106
+ <input
107
+ type="checkbox"
108
+ checked={isDefaultForReplyForward}
109
+ onChange={(e) => setIsDefaultForReplyForward(e.target.checked)}
110
+ />
111
+ Use for replies and forwards
112
+ </label>
113
+ </div>
114
+
115
+ <div className="h-64">
116
+ <RichTextEditor value={contentHtml} onChange={setContentHtml} fill onUploadImage={handleUploadImage} />
117
+ </div>
118
+
119
+ <div className="flex gap-3">
120
+ <Button type="submit" loading={saving} disabled={saving} className="!w-auto">
121
+ Create signature
122
+ </Button>
123
+ <a href={`/settings/signatures?mailboxUid=${encodeURIComponent(mailboxUid!)}`}>
124
+ <Button type="button" variant="secondary" className="!w-auto">
125
+ Cancel
126
+ </Button>
127
+ </a>
128
+ </div>
129
+ </form>
130
+ </div>
131
+ </div>
132
+ );
133
+ }
134
+
135
+ export default routedPage("/settings/signatures/new", NewSignaturePage);
@@ -2,7 +2,8 @@
2
2
  // Copyright (C) 2026 Jean-Philippe Steinmetz
3
3
  // SPDX-License-Identifier: MPL-2.0
4
4
  ///////////////////////////////////////////////////////////////////////////////
5
- import React, { FormEvent, useEffect, useMemo, useState } from "react";
5
+ import { routedPage } from "../_routedPage.js";
6
+ import React, { FormEvent, useEffect, useMemo, useRef, useState } from "react";
6
7
  import { endOfWeek, isAfter, isBefore, isToday, parseISO, startOfDay } from "date-fns";
7
8
  import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
8
9
  import {
@@ -26,11 +27,15 @@ import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
26
27
  import { findWellKnownFolderUid } from "../../shared/mail/findWellKnownFolderUid.js";
27
28
  import { useWritableMailboxes } from "../../shared/components/mail/writableMailboxes.js";
28
29
  import { LIST_PAGE_SIZE, MAX_LIST_PAGES, listAllPages } from "../../shared/mail/listAllPages.js";
30
+ import { SHORTCUTS } from "../../shared/keyboard/keymap.js";
31
+ import { useShortcut } from "../../shared/keyboard/useShortcut.js";
32
+ import { useShortcutProps } from "../../shared/keyboard/useShortcutProps.js";
33
+ import { notifyApiError } from "../../shared/notifications/apiErrors.js";
29
34
 
30
35
  const INPUT_CLASS =
31
36
  "text-sm py-1.5 px-2 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
32
37
 
33
- export default function TasksPage(props: TasksShellProps) {
38
+ function TasksPage(props: TasksShellProps) {
34
39
  return (
35
40
  <TasksShell {...props}>
36
41
  <TasksContent />
@@ -103,6 +108,14 @@ function TasksContent() {
103
108
  // checked task (the toolbar's Delete).
104
109
  const [pendingDelete, setPendingDelete] = useState<{ type: "single"; task: Task } | { type: "bulk" } | null>(null);
105
110
 
111
+ // "New task" from the keyboard: a task is made in the quick-add form, so this moves the caret to its title field (which the Flagged view,
112
+ // a list of flagged mail rather than tasks, doesn't have). A dialog open - the delete confirmation - silences it.
113
+ const addTaskRef = useRef<HTMLInputElement | null>(null);
114
+ const canAddTask = view.type !== "flagged";
115
+ // Registered only while the form is rendered (`canAddTask`), so its field is there.
116
+ useShortcut(SHORTCUTS.tasks.create, () => addTaskRef.current!.focus(), { enabled: canAddTask });
117
+ const addTaskHint = useShortcutProps("Add a task", SHORTCUTS.tasks.create, canAddTask);
118
+
106
119
  function reload(): Promise<void> {
107
120
  if (!folderUid) {
108
121
  setTasks([]);
@@ -186,7 +199,6 @@ function TasksContent() {
186
199
  }
187
200
 
188
201
  async function handleToggle(task: Task) {
189
- setError(null);
190
202
  const nextCompleted = !task.completed;
191
203
  setTasks((prev) => prev.map((t) => (t.uid === task.uid ? { ...t, completed: nextCompleted } : t)));
192
204
  try {
@@ -194,17 +206,16 @@ function TasksContent() {
194
206
  setTasks((prev) => prev.map((t) => (t.uid === task.uid ? updated : t)));
195
207
  } catch (err) {
196
208
  setTasks((prev) => prev.map((t) => (t.uid === task.uid ? task : t)));
197
- setError(err instanceof ApiRequestError ? err.message : "Could not update this task.");
209
+ notifyApiError(err, "Couldn't update the task");
198
210
  }
199
211
  }
200
212
 
201
213
  async function handleDelete(task: Task) {
202
- setError(null);
203
214
  try {
204
215
  await deleteTask(task.uid, task.version);
205
216
  setTasks((prev) => prev.filter((t) => t.uid !== task.uid));
206
217
  } catch (err) {
207
- setError(err instanceof ApiRequestError ? err.message : "Could not delete this task.");
218
+ notifyApiError(err, "Couldn't delete the task");
208
219
  }
209
220
  }
210
221
 
@@ -246,46 +257,41 @@ function TasksContent() {
246
257
 
247
258
  const checkedTasks = viewFiltered.filter((t) => checkedUids.has(t.uid));
248
259
 
260
+ // The bulk handlers try every task and report each failure: the same failure repeated is one pop-up with a count.
249
261
  async function handleBulkComplete() {
250
- let bulkError: string | null = null;
251
262
  for (const task of checkedTasks) {
252
263
  try {
253
264
  await setTaskCompleted(task, true);
254
265
  } catch (err) {
255
- bulkError = err instanceof ApiRequestError ? err.message : "Could not update one or more tasks.";
266
+ notifyApiError(err, "Couldn't update some of the tasks");
256
267
  }
257
268
  }
258
269
  setCheckedUids(new Set());
259
270
  await reload();
260
- setError(bulkError);
261
271
  }
262
272
 
263
273
  async function handleBulkAddToMyDay() {
264
- let bulkError: string | null = null;
265
274
  for (const task of checkedTasks) {
266
275
  try {
267
276
  await setTaskMyDay(task, true);
268
277
  } catch (err) {
269
- bulkError = err instanceof ApiRequestError ? err.message : "Could not update one or more tasks.";
278
+ notifyApiError(err, "Couldn't update some of the tasks");
270
279
  }
271
280
  }
272
281
  setCheckedUids(new Set());
273
282
  await reload();
274
- setError(bulkError);
275
283
  }
276
284
 
277
285
  async function handleBulkDelete() {
278
- let bulkError: string | null = null;
279
286
  for (const task of checkedTasks) {
280
287
  try {
281
288
  await deleteTask(task.uid, task.version);
282
289
  } catch (err) {
283
- bulkError = err instanceof ApiRequestError ? err.message : "Could not delete one or more tasks.";
290
+ notifyApiError(err, "Couldn't delete some of the tasks");
284
291
  }
285
292
  }
286
293
  setCheckedUids(new Set());
287
294
  await reload();
288
- setError(bulkError);
289
295
  }
290
296
 
291
297
  function requestDelete(task: Task) {
@@ -317,7 +323,8 @@ function TasksContent() {
317
323
  const completedTasks = viewFiltered.filter((t) => t.completed);
318
324
 
319
325
  return (
320
- <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">
321
328
  <TasksSidebar mailboxUid={mailboxUid} tasks={tasks} userUid={userUid} active={view} onSelect={handleSelectView} />
322
329
  <div className="flex-1 min-w-0 flex flex-col overflow-y-auto">
323
330
  <TasksToolbar
@@ -342,11 +349,13 @@ function TasksContent() {
342
349
  <FlaggedEmailList loading={flaggedLoading} error={flaggedError} messages={flaggedMessages} />
343
350
  ) : (
344
351
  <>
345
- <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">
346
353
  <input
354
+ ref={addTaskRef}
347
355
  type="text"
348
356
  aria-label="Add a task"
349
- className={`${INPUT_CLASS} flex-1`}
357
+ {...addTaskHint}
358
+ className={`${INPUT_CLASS} min-w-40 basis-40 flex-1`}
350
359
  value={title}
351
360
  onChange={(e) => setTitle(e.target.value)}
352
361
  placeholder="Add a task"
@@ -636,3 +645,5 @@ function TaskTable({ tasks, checkedUids, onToggleChecked, onToggle, onDelete }:
636
645
  </div>
637
646
  );
638
647
  }
648
+
649
+ export default routedPage("/tasks", TasksPage);
@@ -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;