@rapidmx/web-client 0.20.0 → 0.22.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 (906) hide show
  1. package/README.md +415 -381
  2. package/apps/admin/_layout.tsx +2 -2
  3. package/apps/admin/audit-log/index.tsx +4 -4
  4. package/apps/admin/branding/index.tsx +39 -39
  5. package/apps/admin/data-requests/index.tsx +482 -482
  6. package/apps/admin/distribution-lists/[uid].tsx +3 -3
  7. package/apps/admin/distribution-lists/index.tsx +4 -4
  8. package/apps/admin/distribution-lists/new/index.tsx +5 -5
  9. package/apps/admin/domains/[uid].tsx +166 -166
  10. package/apps/admin/domains/index.tsx +4 -4
  11. package/apps/admin/domains/new/index.tsx +5 -5
  12. package/apps/admin/encryption-policy/index.tsx +1 -1
  13. package/apps/admin/escrow-scopes/[uid].tsx +350 -350
  14. package/apps/admin/escrow-scopes/index.tsx +4 -4
  15. package/apps/admin/escrow-scopes/new/index.tsx +5 -5
  16. package/apps/admin/index.tsx +129 -129
  17. package/apps/admin/ingest-queue/index.tsx +4 -4
  18. package/apps/admin/mailbox-policy/index.tsx +1 -1
  19. package/apps/admin/mailboxes/[uid].tsx +271 -271
  20. package/apps/admin/mailboxes/new/index.tsx +30 -30
  21. package/apps/admin/plugins/index.tsx +15 -15
  22. package/apps/admin/quarantine/index.tsx +5 -5
  23. package/apps/admin/retention-policy/index.tsx +39 -39
  24. package/apps/admin/signing-certificates/index.tsx +343 -343
  25. package/apps/admin/transport-rules/[uid].tsx +5 -5
  26. package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
  27. package/apps/admin/transport-rules/index.tsx +4 -4
  28. package/apps/admin/transport-rules/new/index.tsx +5 -5
  29. package/apps/escrow/_layout.tsx +1 -1
  30. package/apps/escrow/audit-log/index.tsx +196 -196
  31. package/apps/escrow/index.tsx +4 -4
  32. package/apps/escrow/matters/[uid].tsx +621 -621
  33. package/apps/escrow/matters/new/index.tsx +6 -6
  34. package/apps/shared/appearance/AppearanceForm.tsx +3 -3
  35. package/apps/shared/appearance/AppearanceHead.tsx +1 -1
  36. package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
  37. package/apps/shared/appearance/appearanceCache.ts +1 -1
  38. package/apps/shared/appearance/appearanceContext.ts +1 -1
  39. package/apps/shared/appearance/theme.ts +1 -1
  40. package/apps/shared/auth/adminAccess.ts +99 -99
  41. package/apps/shared/calendar/bookingPlugin.ts +1 -1
  42. package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
  43. package/apps/shared/calendar/calendarReminders.ts +1 -1
  44. package/apps/shared/calendar/useCalendarReminders.ts +1 -1
  45. package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
  46. package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
  47. package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
  48. package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
  49. package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
  50. package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
  51. package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
  52. package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
  53. package/apps/shared/components/admin/diagnostics/format.ts +1 -1
  54. package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
  55. package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
  56. package/apps/shared/components/admin/elevation.ts +1 -1
  57. package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
  58. package/apps/shared/components/admin/layout/AdminShell.tsx +384 -384
  59. package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +238 -238
  60. package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +154 -152
  61. package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +194 -194
  62. package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
  63. package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
  64. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
  65. package/apps/shared/components/admin/settings/BrandingForm.tsx +423 -423
  66. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
  67. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +119 -119
  68. package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
  69. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
  70. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +198 -198
  71. package/apps/shared/components/admin/settings/PluginsManager.tsx +2093 -2093
  72. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +182 -182
  73. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +288 -288
  74. package/apps/shared/components/admin/setup/SetupWizard.tsx +450 -446
  75. package/apps/shared/components/admin/signOut.ts +1 -1
  76. package/apps/shared/components/admin/usePagedList.tsx +129 -129
  77. package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
  78. package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
  79. package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
  80. package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
  81. package/apps/shared/components/calendar/EventDetails.tsx +16 -14
  82. package/apps/shared/components/calendar/EventEditor.tsx +54 -39
  83. package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
  84. package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
  85. package/apps/shared/components/calendar/EventModal.tsx +206 -206
  86. package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
  87. package/apps/shared/components/calendar/EventShell.tsx +1 -1
  88. package/apps/shared/components/calendar/FindATime.tsx +6 -4
  89. package/apps/shared/components/calendar/GuestInput.tsx +2 -2
  90. package/apps/shared/components/calendar/MonthView.tsx +185 -185
  91. package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
  92. package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
  93. package/apps/shared/components/calendar/RecurrenceEditor.tsx +227 -227
  94. package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
  95. package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
  96. package/apps/shared/components/calendar/SplitDayView.tsx +144 -144
  97. package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
  98. package/apps/shared/components/calendar/TimeGridView.tsx +246 -246
  99. package/apps/shared/components/calendar/allDay.ts +124 -124
  100. package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
  101. package/apps/shared/components/calendar/eventForm.ts +3 -3
  102. package/apps/shared/components/calendar/eventFormat.ts +3 -3
  103. package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
  104. package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
  105. package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
  106. package/apps/shared/components/contacts/ContactForm.tsx +393 -383
  107. package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
  108. package/apps/shared/components/contacts/ContactsToolbar.tsx +103 -103
  109. package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
  110. package/apps/shared/components/contacts/contactKeys.ts +3 -3
  111. package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
  112. package/apps/shared/components/escrow/layout/EscrowShell.tsx +155 -155
  113. package/apps/shared/components/forms/StringListField.tsx +1 -1
  114. package/apps/shared/components/layout/AppShell.tsx +465 -461
  115. package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
  116. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -398
  117. package/apps/shared/components/layout/MailboxProvisioning.tsx +170 -168
  118. package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
  119. package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -315
  120. package/apps/shared/components/layout/ThemeSwitch.tsx +84 -84
  121. package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
  122. package/apps/shared/components/layout/UserMenu.tsx +441 -439
  123. package/apps/shared/components/mail/ConversationList.tsx +334 -332
  124. package/apps/shared/components/mail/ConversationThreadPane.tsx +624 -613
  125. package/apps/shared/components/mail/InviteCard.tsx +11 -9
  126. package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
  127. package/apps/shared/components/mail/MailAddress.tsx +1 -1
  128. package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
  129. package/apps/shared/components/mail/MailSelectionBar.tsx +240 -240
  130. package/apps/shared/components/mail/MenuButton.tsx +404 -404
  131. package/apps/shared/components/mail/MessageDetailPane.tsx +1759 -1757
  132. package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
  133. package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
  134. package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
  135. package/apps/shared/components/mail/compose/ComposeContext.tsx +323 -312
  136. package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -422
  137. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1709 -1681
  138. package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
  139. package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
  140. package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
  141. package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
  142. package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -147
  143. package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
  144. package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -60
  145. package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
  146. package/apps/shared/components/mail/compose/quotedBody.ts +161 -161
  147. package/apps/shared/components/mail/compose/recipients.ts +1 -1
  148. package/apps/shared/components/mail/invite/InviteRowChip.tsx +2 -2
  149. package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +9 -7
  150. package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
  151. package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
  152. package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
  153. package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
  154. package/apps/shared/components/mail/labelMenu.tsx +9 -7
  155. package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
  156. package/apps/shared/components/mail/listPreferences.ts +2 -2
  157. package/apps/shared/components/mail/pinnedSigners.ts +45 -19
  158. package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
  159. package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
  160. package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
  161. package/apps/shared/components/mail/reading/MessageMoreMenu.tsx +258 -258
  162. package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +79 -79
  163. package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
  164. package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
  165. package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
  166. package/apps/shared/components/mail/reading/messageExport.ts +59 -59
  167. package/apps/shared/components/mail/reading/printMessage.ts +99 -99
  168. package/apps/shared/components/mail/reading/useMessageActions.ts +450 -443
  169. package/apps/shared/components/mail/unreadStyle.tsx +1 -1
  170. package/apps/shared/components/mail/verificationSeals.ts +129 -125
  171. package/apps/shared/components/mail/writableMailboxes.ts +14 -7
  172. package/apps/shared/components/rules/RuleBuilder.tsx +311 -311
  173. package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -344
  174. package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
  175. package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
  176. package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
  177. package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
  178. package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
  179. package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
  180. package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -51
  181. package/apps/shared/keyboard/ShortcutProvider.tsx +62 -62
  182. package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -84
  183. package/apps/shared/keyboard/dispatch.ts +124 -124
  184. package/apps/shared/keyboard/format.ts +89 -89
  185. package/apps/shared/keyboard/keymap.ts +114 -114
  186. package/apps/shared/keyboard/registry.ts +65 -65
  187. package/apps/shared/keyboard/targets.ts +79 -79
  188. package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
  189. package/apps/shared/mail/folderCounts.ts +8 -4
  190. package/apps/shared/mail/folderOfType.ts +51 -49
  191. package/apps/shared/mail/folderTree.ts +143 -143
  192. package/apps/shared/mail/listAllPages.ts +39 -39
  193. package/apps/shared/mail/listSnapshots.ts +2 -2
  194. package/apps/shared/mail/messageReadState.ts +6 -5
  195. package/apps/shared/mail/newMailNotifications.ts +171 -171
  196. package/apps/shared/mail/outbox/composeBridge.ts +2 -2
  197. package/apps/shared/mail/outbox/outboxState.ts +1 -1
  198. package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
  199. package/apps/shared/mail/outbox/sendDecision.ts +2 -2
  200. package/apps/shared/mail/outbox/sendJob.ts +450 -445
  201. package/apps/shared/mail/outbox/sendOutcomes.ts +160 -155
  202. package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
  203. package/apps/shared/mail/permanentDelete.ts +8 -7
  204. package/apps/shared/mail/primaryMailbox.ts +1 -1
  205. package/apps/shared/mail/reportNotices.ts +66 -66
  206. package/apps/shared/mail/senderBlocking.ts +157 -141
  207. package/apps/shared/mail/useMailConnection.ts +207 -205
  208. package/apps/shared/mail/useMailLiveUpdates.ts +277 -277
  209. package/apps/shared/mail/useMailboxUpdateAccess.ts +84 -60
  210. package/apps/shared/mail/useMarkMessageRead.ts +54 -47
  211. package/apps/shared/mail/useNewMailNotifications.ts +178 -178
  212. package/apps/shared/mail/usePermanentDelete.tsx +5 -3
  213. package/apps/shared/navigation/pageTitle.ts +1 -1
  214. package/apps/shared/notifications/NotificationCenter.tsx +1 -1
  215. package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
  216. package/apps/shared/notifications/apiErrors.ts +2 -2
  217. package/apps/shared/notifications/pushStatus.ts +1 -1
  218. package/apps/shared/notifications/store.ts +560 -560
  219. package/apps/shared/notifications/systemErrors.ts +1 -1
  220. package/apps/shared/search/LocalIndexLifecycle.tsx +120 -114
  221. package/apps/shared/search/crossMailboxSearch.ts +3 -3
  222. package/apps/shared/search/localIndexBuilder.ts +493 -481
  223. package/apps/shared/search/localIndexKey.ts +1 -1
  224. package/apps/shared/search/localIndexRpcClient.ts +150 -30
  225. package/apps/shared/search/localIndexTransport.ts +84 -0
  226. package/apps/shared/search/localIndexWorker.ts +1 -1
  227. package/apps/shared/search/searchTier2.ts +3 -3
  228. package/apps/shared/signing/enrollmentStorage.ts +33 -33
  229. package/apps/shared/signing/enrollmentTracker.ts +385 -385
  230. package/apps/shared/signing/enrollmentView.ts +251 -251
  231. package/apps/shared/signing/signingInfo.ts +1 -1
  232. package/apps/shared/signing/useNow.ts +19 -19
  233. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +92 -90
  234. package/apps/shared/styles/app.css +387 -396
  235. package/apps/www/_layout.tsx +2 -2
  236. package/apps/www/calendar/index.tsx +583 -581
  237. package/apps/www/contacts/[uid].tsx +114 -112
  238. package/apps/www/contacts/index.tsx +19 -17
  239. package/apps/www/index.tsx +3021 -3012
  240. package/apps/www/messages/[uid].tsx +141 -139
  241. package/apps/www/settings/auto-reply/index.tsx +138 -136
  242. package/apps/www/settings/blocked-senders/index.tsx +305 -303
  243. package/apps/www/settings/encryption/index.tsx +1296 -1290
  244. package/apps/www/settings/filters/[uid].tsx +184 -179
  245. package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
  246. package/apps/www/settings/filters/index.tsx +105 -105
  247. package/apps/www/settings/filters/new/index.tsx +167 -165
  248. package/apps/www/settings/labels/index.tsx +209 -207
  249. package/apps/www/settings/privacy/index.tsx +499 -495
  250. package/apps/www/settings/profile/index.tsx +256 -251
  251. package/apps/www/settings/read-receipts/index.tsx +155 -150
  252. package/apps/www/settings/sharing/index.tsx +259 -259
  253. package/apps/www/settings/signatures/[uid].tsx +175 -175
  254. package/apps/www/settings/signatures/index.tsx +91 -91
  255. package/apps/www/settings/signatures/new/index.tsx +138 -138
  256. package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
  257. package/apps/www/tasks/index.tsx +659 -654
  258. package/dist/apps/admin/_layout.d.ts +1 -1
  259. package/dist/apps/admin/_layout.js +1 -1
  260. package/dist/apps/admin/audit-log/index.d.ts +1 -1
  261. package/dist/apps/admin/audit-log/index.js +4 -4
  262. package/dist/apps/admin/branding/index.js +3 -3
  263. package/dist/apps/admin/data-requests/index.js +8 -8
  264. package/dist/apps/admin/distribution-lists/[uid].js +3 -3
  265. package/dist/apps/admin/distribution-lists/index.js +4 -4
  266. package/dist/apps/admin/distribution-lists/new/index.js +5 -5
  267. package/dist/apps/admin/domains/[uid].js +6 -6
  268. package/dist/apps/admin/domains/index.js +4 -4
  269. package/dist/apps/admin/domains/new/index.js +5 -5
  270. package/dist/apps/admin/encryption-policy/index.js +1 -1
  271. package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
  272. package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
  273. package/dist/apps/admin/escrow-scopes/index.js +4 -4
  274. package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
  275. package/dist/apps/admin/index.js +6 -6
  276. package/dist/apps/admin/ingest-queue/index.js +4 -4
  277. package/dist/apps/admin/mailbox-policy/index.js +1 -1
  278. package/dist/apps/admin/mailboxes/[uid].js +5 -5
  279. package/dist/apps/admin/quarantine/index.js +5 -5
  280. package/dist/apps/admin/retention-policy/index.js +3 -3
  281. package/dist/apps/admin/signing-certificates/index.js +5 -5
  282. package/dist/apps/admin/transport-rules/[uid].js +5 -5
  283. package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
  284. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
  285. package/dist/apps/admin/transport-rules/index.js +4 -4
  286. package/dist/apps/admin/transport-rules/new/index.js +5 -5
  287. package/dist/apps/escrow/_layout.d.ts +1 -1
  288. package/dist/apps/escrow/audit-log/index.d.ts +1 -1
  289. package/dist/apps/escrow/audit-log/index.js +4 -4
  290. package/dist/apps/escrow/index.js +4 -4
  291. package/dist/apps/escrow/matters/[uid].js +10 -10
  292. package/dist/apps/escrow/matters/new/index.js +6 -6
  293. package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
  294. package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
  295. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
  296. package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
  297. package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
  298. package/dist/apps/shared/appearance/appearanceCache.js +1 -1
  299. package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
  300. package/dist/apps/shared/appearance/theme.d.ts +1 -1
  301. package/dist/apps/shared/auth/adminAccess.js +1 -1
  302. package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
  303. package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
  304. package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
  305. package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
  306. package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
  307. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
  308. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
  309. package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
  310. package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
  311. package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
  312. package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
  313. package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
  314. package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
  315. package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
  316. package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
  317. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
  318. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
  319. package/dist/apps/shared/components/admin/elevation.js +1 -1
  320. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
  321. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  322. package/dist/apps/shared/components/admin/layout/AdminShell.js +8 -8
  323. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
  324. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
  325. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
  326. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
  327. package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
  328. package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
  329. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
  330. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
  331. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
  332. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
  333. package/dist/apps/shared/components/admin/settings/BrandingForm.js +8 -8
  334. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
  335. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
  336. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
  337. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
  338. package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
  339. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
  340. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
  341. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
  342. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
  343. package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
  344. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
  345. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
  346. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
  347. package/dist/apps/shared/components/admin/setup/SetupWizard.js +26 -22
  348. package/dist/apps/shared/components/admin/signOut.js +1 -1
  349. package/dist/apps/shared/components/admin/usePagedList.js +2 -2
  350. package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
  351. package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
  352. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
  353. package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
  354. package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
  355. package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
  356. package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
  357. package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
  358. package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
  359. package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
  360. package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
  361. package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
  362. package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
  363. package/dist/apps/shared/components/calendar/EventModal.js +3 -3
  364. package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
  365. package/dist/apps/shared/components/calendar/EventShell.js +1 -1
  366. package/dist/apps/shared/components/calendar/FindATime.js +6 -4
  367. package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
  368. package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
  369. package/dist/apps/shared/components/calendar/MonthView.js +1 -1
  370. package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
  371. package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
  372. package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
  373. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
  374. package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
  375. package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
  376. package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
  377. package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
  378. package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
  379. package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
  380. package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
  381. package/dist/apps/shared/components/calendar/TimeGridView.js +1 -1
  382. package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
  383. package/dist/apps/shared/components/calendar/allDay.js +1 -1
  384. package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
  385. package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
  386. package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
  387. package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
  388. package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
  389. package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
  390. package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
  391. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  392. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
  393. package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
  394. package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
  395. package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
  396. package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
  397. package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
  398. package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
  399. package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
  400. package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
  401. package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
  402. package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
  403. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  404. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
  405. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -7
  406. package/dist/apps/shared/components/forms/StringListField.js +1 -1
  407. package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
  408. package/dist/apps/shared/components/layout/AppShell.js +14 -10
  409. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
  410. package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
  411. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
  412. package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
  413. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
  414. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
  415. package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
  416. package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
  417. package/dist/apps/shared/components/layout/UserMenu.js +6 -4
  418. package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
  419. package/dist/apps/shared/components/mail/ConversationList.js +6 -4
  420. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
  421. package/dist/apps/shared/components/mail/ConversationThreadPane.js +13 -11
  422. package/dist/apps/shared/components/mail/InviteCard.js +10 -8
  423. package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
  424. package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
  425. package/dist/apps/shared/components/mail/MailAddress.js +1 -1
  426. package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
  427. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
  428. package/dist/apps/shared/components/mail/MenuButton.js +1 -1
  429. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
  430. package/dist/apps/shared/components/mail/MessageDetailPane.js +30 -28
  431. package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
  432. package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
  433. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
  434. package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
  435. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
  436. package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
  437. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +7 -1
  438. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +54 -31
  439. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
  440. package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
  441. package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
  442. package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
  443. package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
  444. package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
  445. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
  446. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
  447. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
  448. package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
  449. package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
  450. package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +1 -1
  451. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +1 -1
  452. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +9 -7
  453. package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
  454. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
  455. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
  456. package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
  457. package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
  458. package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
  459. package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
  460. package/dist/apps/shared/components/mail/labelMenu.js +9 -7
  461. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
  462. package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
  463. package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
  464. package/dist/apps/shared/components/mail/listPreferences.js +1 -1
  465. package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
  466. package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
  467. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
  468. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
  469. package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
  470. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
  471. package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
  472. package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
  473. package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
  474. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
  475. package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
  476. package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
  477. package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
  478. package/dist/apps/shared/components/mail/reading/printMessage.d.ts +2 -2
  479. package/dist/apps/shared/components/mail/reading/printMessage.js +11 -11
  480. package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
  481. package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
  482. package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
  483. package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
  484. package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
  485. package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
  486. package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
  487. package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
  488. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
  489. package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
  490. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  491. package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
  492. package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
  493. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
  494. package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
  495. package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
  496. package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
  497. package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
  498. package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
  499. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  500. package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
  501. package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
  502. package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
  503. package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
  504. package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
  505. package/dist/apps/shared/mail/folderCounts.js +6 -4
  506. package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
  507. package/dist/apps/shared/mail/folderOfType.js +4 -4
  508. package/dist/apps/shared/mail/folderTree.d.ts +1 -1
  509. package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
  510. package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
  511. package/dist/apps/shared/mail/messageReadState.js +5 -5
  512. package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
  513. package/dist/apps/shared/mail/newMailNotifications.js +1 -1
  514. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
  515. package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
  516. package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
  517. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
  518. package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
  519. package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
  520. package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
  521. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
  522. package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
  523. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
  524. package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
  525. package/dist/apps/shared/mail/permanentDelete.js +8 -8
  526. package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
  527. package/dist/apps/shared/mail/primaryMailbox.js +1 -1
  528. package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
  529. package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
  530. package/dist/apps/shared/mail/senderBlocking.js +19 -16
  531. package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
  532. package/dist/apps/shared/mail/useMailConnection.js +12 -10
  533. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
  534. package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
  535. package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
  536. package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
  537. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
  538. package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
  539. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
  540. package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
  541. package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
  542. package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
  543. package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
  544. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
  545. package/dist/apps/shared/notifications/apiErrors.js +2 -2
  546. package/dist/apps/shared/notifications/pushStatus.js +1 -1
  547. package/dist/apps/shared/notifications/systemErrors.js +1 -1
  548. package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
  549. package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
  550. package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
  551. package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
  552. package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
  553. package/dist/apps/shared/search/localIndexBuilder.js +15 -12
  554. package/dist/apps/shared/search/localIndexKey.js +1 -1
  555. package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
  556. package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
  557. package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
  558. package/dist/apps/shared/search/localIndexTransport.js +1 -0
  559. package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
  560. package/dist/apps/shared/search/searchTier2.d.ts +3 -3
  561. package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
  562. package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
  563. package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
  564. package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
  565. package/dist/apps/shared/signing/signingInfo.js +1 -1
  566. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
  567. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
  568. package/dist/apps/shared/styles/app.css +387 -396
  569. package/dist/apps/www/_layout.d.ts +1 -1
  570. package/dist/apps/www/_layout.js +1 -1
  571. package/dist/apps/www/calendar/index.d.ts +1 -1
  572. package/dist/apps/www/calendar/index.js +20 -18
  573. package/dist/apps/www/contacts/[uid].d.ts +1 -1
  574. package/dist/apps/www/contacts/[uid].js +8 -6
  575. package/dist/apps/www/contacts/index.d.ts +1 -1
  576. package/dist/apps/www/contacts/index.js +19 -17
  577. package/dist/apps/www/index.d.ts +1 -1
  578. package/dist/apps/www/index.js +54 -50
  579. package/dist/apps/www/messages/[uid].d.ts +1 -1
  580. package/dist/apps/www/messages/[uid].js +11 -9
  581. package/dist/apps/www/settings/appearance/index.d.ts +1 -1
  582. package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
  583. package/dist/apps/www/settings/auto-reply/index.js +6 -4
  584. package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
  585. package/dist/apps/www/settings/blocked-senders/index.js +6 -4
  586. package/dist/apps/www/settings/encryption/index.d.ts +1 -1
  587. package/dist/apps/www/settings/encryption/index.js +31 -29
  588. package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
  589. package/dist/apps/www/settings/filters/[uid].js +11 -9
  590. package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
  591. package/dist/apps/www/settings/filters/index.d.ts +1 -1
  592. package/dist/apps/www/settings/filters/index.js +4 -4
  593. package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
  594. package/dist/apps/www/settings/filters/new/index.js +10 -8
  595. package/dist/apps/www/settings/labels/index.d.ts +1 -1
  596. package/dist/apps/www/settings/labels/index.js +12 -10
  597. package/dist/apps/www/settings/privacy/index.d.ts +1 -1
  598. package/dist/apps/www/settings/privacy/index.js +18 -14
  599. package/dist/apps/www/settings/profile/index.d.ts +1 -1
  600. package/dist/apps/www/settings/profile/index.js +7 -5
  601. package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
  602. package/dist/apps/www/settings/read-receipts/index.js +5 -3
  603. package/dist/apps/www/settings/sharing/index.d.ts +1 -1
  604. package/dist/apps/www/settings/sharing/index.js +6 -6
  605. package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
  606. package/dist/apps/www/settings/signatures/[uid].js +5 -5
  607. package/dist/apps/www/settings/signatures/index.d.ts +1 -1
  608. package/dist/apps/www/settings/signatures/index.js +4 -4
  609. package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
  610. package/dist/apps/www/settings/signatures/new/index.js +4 -4
  611. package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
  612. package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
  613. package/dist/apps/www/tasks/index.d.ts +1 -1
  614. package/dist/apps/www/tasks/index.js +17 -15
  615. package/dist/lib/admin/auditLogApi.d.ts +32 -0
  616. package/dist/lib/admin/auditLogApi.js +27 -0
  617. package/dist/lib/admin/distributionListsApi.d.ts +47 -0
  618. package/dist/lib/admin/distributionListsApi.js +34 -0
  619. package/dist/lib/admin/domainsApi.d.ts +75 -0
  620. package/dist/lib/admin/domainsApi.js +44 -0
  621. package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
  622. package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
  623. package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
  624. package/dist/lib/admin/escrowAuditLogApi.js +31 -0
  625. package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
  626. package/dist/lib/admin/escrowScopesApi.js +50 -0
  627. package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
  628. package/dist/lib/admin/leftoverMailboxApi.js +67 -0
  629. package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
  630. package/dist/lib/admin/mailboxPolicyApi.js +21 -0
  631. package/dist/lib/admin/matterExportApi.d.ts +40 -0
  632. package/dist/lib/admin/matterExportApi.js +39 -0
  633. package/dist/lib/admin/matterSearchApi.d.ts +20 -0
  634. package/dist/lib/admin/matterSearchApi.js +27 -0
  635. package/dist/lib/admin/mattersApi.d.ts +56 -0
  636. package/dist/lib/admin/mattersApi.js +40 -0
  637. package/dist/lib/admin/pluginsApi.d.ts +265 -0
  638. package/dist/lib/admin/pluginsApi.js +102 -0
  639. package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
  640. package/dist/lib/admin/retentionPolicyApi.js +29 -0
  641. package/dist/lib/admin/setupApi.d.ts +23 -0
  642. package/dist/lib/admin/setupApi.js +26 -0
  643. package/dist/lib/admin/transportRulesApi.d.ts +66 -0
  644. package/dist/lib/admin/transportRulesApi.js +43 -0
  645. package/dist/lib/appearance/preferencesApi.d.ts +128 -0
  646. package/dist/lib/appearance/preferencesApi.js +257 -0
  647. package/dist/lib/auth/profileApi.d.ts +33 -0
  648. package/dist/lib/auth/profileApi.js +52 -0
  649. package/dist/lib/auth/session.d.ts +70 -0
  650. package/dist/lib/auth/session.js +210 -0
  651. package/dist/lib/branding/brandingApi.d.ts +47 -0
  652. package/dist/lib/branding/brandingApi.js +61 -0
  653. package/dist/lib/branding/useBranding.d.ts +23 -0
  654. package/dist/lib/branding/useBranding.js +65 -0
  655. package/dist/lib/calendar/calendarApi.d.ts +230 -0
  656. package/dist/lib/calendar/calendarApi.js +106 -0
  657. package/dist/lib/calendar/calendarColors.d.ts +18 -0
  658. package/dist/lib/calendar/calendarColors.js +39 -0
  659. package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
  660. package/dist/lib/calendar/calendarDragIds.js +92 -0
  661. package/dist/lib/calendar/calendarMutations.d.ts +68 -0
  662. package/dist/lib/calendar/calendarMutations.js +190 -0
  663. package/dist/lib/calendar/eventDescription.d.ts +43 -0
  664. package/dist/lib/calendar/eventDescription.js +195 -0
  665. package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
  666. package/dist/lib/calendar/freeBusyApi.js +119 -0
  667. package/dist/lib/calendar/inviteApi.d.ts +121 -0
  668. package/dist/lib/calendar/inviteApi.js +54 -0
  669. package/dist/lib/calendar/recurrence.d.ts +60 -0
  670. package/dist/lib/calendar/recurrence.js +225 -0
  671. package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
  672. package/dist/lib/components/avatar/ContactAvatar.js +26 -0
  673. package/dist/lib/components/buttons/Button.d.ts +9 -0
  674. package/dist/lib/components/buttons/Button.js +14 -0
  675. package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
  676. package/dist/lib/components/buttons/CopyButton.js +14 -0
  677. package/dist/lib/components/feedback/Alert.d.ts +4 -0
  678. package/dist/lib/components/feedback/Alert.js +4 -0
  679. package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
  680. package/dist/lib/components/feedback/Skeleton.js +14 -0
  681. package/dist/lib/components/forms/FormField.d.ts +8 -0
  682. package/dist/lib/components/forms/FormField.js +5 -0
  683. package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
  684. package/dist/lib/components/navigation/BottomTabBar.js +32 -0
  685. package/dist/lib/components/overlays/Drawer.d.ts +20 -0
  686. package/dist/lib/components/overlays/Drawer.js +27 -0
  687. package/dist/lib/components/overlays/Modal.d.ts +17 -0
  688. package/dist/lib/components/overlays/Modal.js +25 -0
  689. package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
  690. package/dist/lib/components/overlays/PopoverPortal.js +53 -0
  691. package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
  692. package/dist/lib/components/overlays/overlayStack.js +143 -0
  693. package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
  694. package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
  695. package/dist/lib/contacts/contactsApi.d.ts +174 -0
  696. package/dist/lib/contacts/contactsApi.js +139 -0
  697. package/dist/lib/contacts/vcard.d.ts +23 -0
  698. package/dist/lib/contacts/vcard.js +179 -0
  699. package/dist/lib/crypto/composeSecurity.d.ts +67 -0
  700. package/dist/lib/crypto/composeSecurity.js +93 -0
  701. package/dist/lib/crypto/encoding.d.ts +9 -0
  702. package/dist/lib/crypto/encoding.js +32 -0
  703. package/dist/lib/crypto/escrowKeys.d.ts +25 -0
  704. package/dist/lib/crypto/escrowKeys.js +59 -0
  705. package/dist/lib/crypto/idleTimeout.d.ts +16 -0
  706. package/dist/lib/crypto/idleTimeout.js +47 -0
  707. package/dist/lib/crypto/keySession.d.ts +159 -0
  708. package/dist/lib/crypto/keySession.js +367 -0
  709. package/dist/lib/crypto/keys.d.ts +50 -0
  710. package/dist/lib/crypto/keys.js +89 -0
  711. package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
  712. package/dist/lib/crypto/keyvaultApi.js +350 -0
  713. package/dist/lib/crypto/masterKey.d.ts +59 -0
  714. package/dist/lib/crypto/masterKey.js +104 -0
  715. package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
  716. package/dist/lib/crypto/masterKeyWraps.js +228 -0
  717. package/dist/lib/crypto/messageSecurity.d.ts +248 -0
  718. package/dist/lib/crypto/messageSecurity.js +367 -0
  719. package/dist/lib/crypto/mime.d.ts +143 -0
  720. package/dist/lib/crypto/mime.js +504 -0
  721. package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
  722. package/dist/lib/crypto/passkeyUnlock.js +79 -0
  723. package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
  724. package/dist/lib/crypto/passwordUnlock.js +73 -0
  725. package/dist/lib/crypto/recoveryCode.d.ts +28 -0
  726. package/dist/lib/crypto/recoveryCode.js +86 -0
  727. package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
  728. package/dist/lib/crypto/signingProviderApi.js +60 -0
  729. package/dist/lib/crypto/smime.d.ts +128 -0
  730. package/dist/lib/crypto/smime.js +402 -0
  731. package/dist/lib/crypto/smimeMessage.d.ts +132 -0
  732. package/dist/lib/crypto/smimeMessage.js +341 -0
  733. package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
  734. package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
  735. package/dist/lib/crypto/verificationSeal.d.ts +56 -0
  736. package/dist/lib/crypto/verificationSeal.js +220 -0
  737. package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
  738. package/dist/lib/mail/compose/composeQuoting.js +204 -0
  739. package/dist/lib/mail/compose/emojiData.d.ts +9 -0
  740. package/dist/lib/mail/compose/emojiData.js +18 -0
  741. package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
  742. package/dist/lib/mail/compose/giphyApi.js +20 -0
  743. package/dist/lib/mail/conversationsApi.d.ts +80 -0
  744. package/dist/lib/mail/conversationsApi.js +44 -0
  745. package/dist/lib/mail/dataExportApi.d.ts +50 -0
  746. package/dist/lib/mail/dataExportApi.js +40 -0
  747. package/dist/lib/mail/directoryApi.d.ts +49 -0
  748. package/dist/lib/mail/directoryApi.js +101 -0
  749. package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
  750. package/dist/lib/mail/erasureRequestApi.js +43 -0
  751. package/dist/lib/mail/flaggedMessages.d.ts +22 -0
  752. package/dist/lib/mail/flaggedMessages.js +73 -0
  753. package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
  754. package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
  755. package/dist/lib/mail/labelsApi.d.ts +47 -0
  756. package/dist/lib/mail/labelsApi.js +38 -0
  757. package/dist/lib/mail/mailAddress.d.ts +27 -0
  758. package/dist/lib/mail/mailAddress.js +54 -0
  759. package/dist/lib/mail/mailApi.d.ts +850 -0
  760. package/dist/lib/mail/mailApi.js +602 -0
  761. package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
  762. package/dist/lib/mail/mailDetailHooks.js +53 -0
  763. package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
  764. package/dist/lib/mail/mailFilterRulesApi.js +45 -0
  765. package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
  766. package/dist/lib/mail/mailSignaturesApi.js +42 -0
  767. package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
  768. package/dist/lib/mail/mailboxAccessApi.js +56 -0
  769. package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
  770. package/dist/lib/mail/mailboxImportApi.js +51 -0
  771. package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
  772. package/dist/lib/mail/messageBodySanitizer.js +118 -0
  773. package/dist/lib/mail/pushClient.d.ts +106 -0
  774. package/dist/lib/mail/pushClient.js +317 -0
  775. package/dist/lib/mail/sendEvents.d.ts +33 -0
  776. package/dist/lib/mail/sendEvents.js +44 -0
  777. package/dist/lib/mail/sendFailure.d.ts +16 -0
  778. package/dist/lib/mail/sendFailure.js +86 -0
  779. package/dist/lib/mail/senderListsApi.d.ts +69 -0
  780. package/dist/lib/mail/senderListsApi.js +126 -0
  781. package/dist/lib/search/queryGrammar.d.ts +51 -0
  782. package/dist/lib/search/queryGrammar.js +111 -0
  783. package/dist/lib/search/searchApi.d.ts +100 -0
  784. package/dist/lib/search/searchApi.js +107 -0
  785. package/dist/lib/search/searchScoring.d.ts +34 -0
  786. package/dist/lib/search/searchScoring.js +44 -0
  787. package/dist/lib/search/searchTier3.d.ts +64 -0
  788. package/dist/lib/search/searchTier3.js +335 -0
  789. package/dist/lib/tasks/tasksApi.d.ts +85 -0
  790. package/dist/lib/tasks/tasksApi.js +55 -0
  791. package/dist/lib/util/api.d.ts +160 -0
  792. package/dist/lib/util/api.js +251 -0
  793. package/dist/lib/util/apiClientContext.d.ts +7 -0
  794. package/dist/lib/util/apiClientContext.js +32 -0
  795. package/dist/lib/util/apiQuery.d.ts +32 -0
  796. package/dist/lib/util/apiQuery.js +42 -0
  797. package/dist/lib/util/clipboard.d.ts +7 -0
  798. package/dist/lib/util/clipboard.js +56 -0
  799. package/dist/lib/util/dateInput.d.ts +6 -0
  800. package/dist/lib/util/dateInput.js +14 -0
  801. package/dist/lib/util/timeZone.d.ts +12 -0
  802. package/dist/lib/util/timeZone.js +32 -0
  803. package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
  804. package/dist/lib/util/useCopyToClipboard.js +34 -0
  805. package/dist/lib/util/useIsMobile.d.ts +10 -0
  806. package/dist/lib/util/useIsMobile.js +29 -0
  807. package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
  808. package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
  809. package/lib/admin/auditLogApi.ts +48 -0
  810. package/lib/admin/distributionListsApi.ts +77 -0
  811. package/lib/admin/domainsApi.ts +113 -0
  812. package/lib/admin/escrowAccessRequestsApi.ts +110 -0
  813. package/lib/admin/escrowAuditLogApi.ts +101 -0
  814. package/lib/admin/escrowScopesApi.ts +112 -0
  815. package/lib/admin/leftoverMailboxApi.ts +120 -0
  816. package/lib/admin/mailboxPolicyApi.ts +38 -0
  817. package/lib/admin/matterExportApi.ts +62 -0
  818. package/lib/admin/matterSearchApi.ts +35 -0
  819. package/lib/admin/mattersApi.ts +86 -0
  820. package/lib/admin/pluginsApi.ts +339 -0
  821. package/lib/admin/retentionPolicyApi.ts +46 -0
  822. package/lib/admin/setupApi.ts +40 -0
  823. package/lib/admin/transportRulesApi.ts +106 -0
  824. package/lib/appearance/preferencesApi.ts +335 -0
  825. package/lib/auth/profileApi.ts +77 -0
  826. package/lib/auth/session.ts +238 -0
  827. package/lib/branding/brandingApi.ts +97 -0
  828. package/lib/branding/useBranding.ts +81 -0
  829. package/lib/calendar/calendarApi.ts +309 -0
  830. package/lib/calendar/calendarColors.ts +45 -0
  831. package/lib/calendar/calendarDragIds.ts +112 -0
  832. package/lib/calendar/calendarMutations.ts +236 -0
  833. package/lib/calendar/eventDescription.ts +211 -0
  834. package/lib/calendar/freeBusyApi.ts +197 -0
  835. package/lib/calendar/inviteApi.ts +154 -0
  836. package/lib/calendar/recurrence.ts +285 -0
  837. package/lib/components/avatar/ContactAvatar.tsx +47 -0
  838. package/lib/components/buttons/Button.tsx +40 -0
  839. package/lib/components/buttons/CopyButton.tsx +45 -0
  840. package/lib/components/feedback/Alert.tsx +13 -0
  841. package/lib/components/feedback/Skeleton.tsx +38 -0
  842. package/lib/components/forms/FormField.tsx +23 -0
  843. package/lib/components/navigation/BottomTabBar.tsx +66 -0
  844. package/lib/components/overlays/Drawer.tsx +72 -0
  845. package/lib/components/overlays/Modal.tsx +64 -0
  846. package/lib/components/overlays/PopoverPortal.tsx +74 -0
  847. package/lib/components/overlays/overlayStack.ts +165 -0
  848. package/lib/components/pickers/MiniDatePicker.tsx +90 -0
  849. package/lib/contacts/contactsApi.ts +289 -0
  850. package/lib/contacts/vcard.ts +197 -0
  851. package/lib/crypto/composeSecurity.ts +140 -0
  852. package/lib/crypto/encoding.ts +36 -0
  853. package/lib/crypto/escrowKeys.ts +74 -0
  854. package/lib/crypto/idleTimeout.ts +49 -0
  855. package/lib/crypto/keySession.ts +483 -0
  856. package/lib/crypto/keys.ts +105 -0
  857. package/lib/crypto/keyvaultApi.ts +674 -0
  858. package/lib/crypto/masterKey.ts +134 -0
  859. package/lib/crypto/masterKeyWraps.ts +269 -0
  860. package/lib/crypto/messageSecurity.ts +600 -0
  861. package/lib/crypto/mime.ts +568 -0
  862. package/lib/crypto/passkeyUnlock.ts +103 -0
  863. package/lib/crypto/passwordUnlock.ts +98 -0
  864. package/lib/crypto/recoveryCode.ts +93 -0
  865. package/lib/crypto/signingProviderApi.ts +135 -0
  866. package/lib/crypto/smime.ts +459 -0
  867. package/lib/crypto/smimeMessage.ts +491 -0
  868. package/lib/crypto/useIdleKeyTimeout.ts +176 -0
  869. package/lib/crypto/verificationSeal.ts +288 -0
  870. package/lib/mail/compose/composeQuoting.ts +243 -0
  871. package/lib/mail/compose/emojiData.ts +34 -0
  872. package/lib/mail/compose/giphyApi.ts +31 -0
  873. package/lib/mail/conversationsApi.ts +123 -0
  874. package/lib/mail/dataExportApi.ts +73 -0
  875. package/lib/mail/directoryApi.ts +161 -0
  876. package/lib/mail/erasureRequestApi.ts +71 -0
  877. package/lib/mail/flaggedMessages.ts +80 -0
  878. package/lib/mail/focusedInboxOverridesApi.ts +64 -0
  879. package/lib/mail/labelsApi.ts +71 -0
  880. package/lib/mail/mailAddress.ts +76 -0
  881. package/lib/mail/mailApi.ts +1286 -0
  882. package/lib/mail/mailDetailHooks.ts +57 -0
  883. package/lib/mail/mailFilterRulesApi.ts +116 -0
  884. package/lib/mail/mailSignaturesApi.ts +84 -0
  885. package/lib/mail/mailboxAccessApi.ts +106 -0
  886. package/lib/mail/mailboxImportApi.ts +87 -0
  887. package/lib/mail/messageBodySanitizer.ts +129 -0
  888. package/lib/mail/pushClient.ts +375 -0
  889. package/lib/mail/sendEvents.ts +72 -0
  890. package/lib/mail/sendFailure.ts +98 -0
  891. package/lib/mail/senderListsApi.ts +161 -0
  892. package/lib/search/queryGrammar.ts +179 -0
  893. package/lib/search/searchApi.ts +199 -0
  894. package/lib/search/searchScoring.ts +46 -0
  895. package/lib/search/searchTier3.ts +406 -0
  896. package/lib/tasks/tasksApi.ts +128 -0
  897. package/lib/util/api.ts +318 -0
  898. package/lib/util/apiClientContext.ts +35 -0
  899. package/lib/util/apiQuery.ts +61 -0
  900. package/lib/util/clipboard.ts +55 -0
  901. package/lib/util/dateInput.ts +15 -0
  902. package/lib/util/timeZone.ts +33 -0
  903. package/lib/util/useCopyToClipboard.ts +48 -0
  904. package/lib/util/useIsMobile.ts +34 -0
  905. package/lib/videoconf/videoMeetingsApi.ts +144 -0
  906. package/package.json +10 -4
@@ -1,621 +1,621 @@
1
- ///////////////////////////////////////////////////////////////////////////////
2
- // Copyright (C) 2026 Jean-Philippe Steinmetz
3
- // SPDX-License-Identifier: MPL-2.0
4
- ///////////////////////////////////////////////////////////////////////////////
5
- import React, { FormEvent, useEffect, useRef, useState } from "react";
6
- import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
- import { closeMatter, getMatter, Matter } from "@rapidmx/react-shared/admin/mattersApi.js";
8
- import {
9
- approveAccessRequest,
10
- createAccessRequest,
11
- denyAccessRequest,
12
- EscrowAccessRequest,
13
- getAccessRequestMaterial,
14
- listAccessRequests,
15
- } from "@rapidmx/react-shared/admin/escrowAccessRequestsApi.js";
16
- import {
17
- createMatterExportRequest,
18
- listMatterExportRequests,
19
- matterExportRequestDownloadUrl,
20
- } from "@rapidmx/react-shared/admin/matterExportApi.js";
21
- import { searchMatter, SearchResultPage } from "@rapidmx/react-shared/admin/matterSearchApi.js";
22
- import { parseSearchQuery } from "@rapidmx/react-shared/search/queryGrammar.js";
23
- import EscrowShell, { EscrowShellProps } from "../../shared/components/escrow/layout/EscrowShell.js";
24
- import { LoadMoreButton, usePagedList } from "../../shared/components/admin/usePagedList.js";
25
- import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
26
- import Button from "@rapidmx/react-shared/components/buttons/Button.js";
27
- import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
28
- import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
29
-
30
- const INPUT_CLASS =
31
- "w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
32
-
33
- /** Request statuses still waiting or running - shown as a neutral in-progress badge, never as success. Includes
34
- * restapi's `processing` (exports/imports) and `in_progress` (an erasure that is running). */
35
- const IN_PROGRESS_STATUSES = new Set(["pending", "processing", "in_progress"]);
36
-
37
- function statusBadgeClass(status: string): string {
38
- if (status === "failed" || status === "denied") return "bg-danger-bg text-danger";
39
- if (IN_PROGRESS_STATUSES.has(status)) return "bg-surface-alt text-text-muted";
40
- return "bg-success text-white";
41
- }
42
-
43
- /** A status as shown on its badge (`in_progress` -> `in progress`). */
44
- function statusLabel(status: string): string {
45
- return status.replace(/_/g, " ");
46
- }
47
-
48
- export default function MatterDetailPage(props: Omit<EscrowShellProps, "active"> & { params: { uid: string } }) {
49
- return (
50
- <EscrowShell {...props} active="matters">
51
- <MatterDetailContent uid={props.params.uid} />
52
- </EscrowShell>
53
- );
54
- }
55
-
56
- function formatDateRange(matter: Matter): string {
57
- return `${new Date(matter.dateRangeStart).toLocaleDateString()} – ${new Date(matter.dateRangeEnd).toLocaleDateString()}`;
58
- }
59
-
60
- function MatterDetailContent({ uid }: { uid: string }) {
61
- const [matter, setMatter] = useState<Matter | null>(null);
62
- const [loading, setLoading] = useState(true);
63
- const [error, setError] = useState<string | null>(null);
64
-
65
- // Both lists are fetched for this matter only (`matterId`), newest first, a page at a time. Rows are still
66
- // checked against this matter in case an older server ignores the filter.
67
- const requests = usePagedList(
68
- (params) => listAccessRequests({ ...params, matterId: uid }),
69
- "Could not load this matter's access requests.",
70
- );
71
- const exportRequests = usePagedList(
72
- (params) => listMatterExportRequests({ ...params, matterId: uid }),
73
- "Could not load this matter's export requests.",
74
- );
75
-
76
- const [confirmingClose, setConfirmingClose] = useState(false);
77
- const [closing, setClosing] = useState(false);
78
- const [closeError, setCloseError] = useState<string | null>(null);
79
- const [approveTarget, setApproveTarget] = useState<EscrowAccessRequest | null>(null);
80
-
81
- const [showNewRequest, setShowNewRequest] = useState(false);
82
- const [newRequestMailboxUid, setNewRequestMailboxUid] = useState("");
83
- const [creatingRequest, setCreatingRequest] = useState(false);
84
- const [newRequestError, setNewRequestError] = useState<string | null>(null);
85
-
86
- // Keyed by request uid — several rows can be independently mid-action at once.
87
- const [actionErrors, setActionErrors] = useState<Record<string, string>>({});
88
- const [actingOn, setActingOn] = useState<Record<string, boolean>>({});
89
-
90
- const [materialFor, setMaterialFor] = useState<EscrowAccessRequest | null>(null);
91
- const [materialText, setMaterialText] = useState("");
92
- const [materialError, setMaterialError] = useState<string | null>(null);
93
- const [loadingMaterial, setLoadingMaterial] = useState(false);
94
- // Bumped whenever the material modal closes (or the matter does), so a slow "Get material" response for a
95
- // modal that's already gone - or for an earlier request - is never shown.
96
- const materialSeq = useRef(0);
97
-
98
- const [creatingExport, setCreatingExport] = useState(false);
99
- const [exportError, setExportError] = useState<string | null>(null);
100
-
101
- const [searchText, setSearchText] = useState("");
102
- const [searching, setSearching] = useState(false);
103
- const [searchError, setSearchError] = useState<string | null>(null);
104
- const [searchResults, setSearchResults] = useState<Record<string, SearchResultPage> | null>(null);
105
- // Bumped by every search and by closing the matter, so a search still in flight when the matter closes (or
106
- // superseded by a newer search) never shows its results.
107
- const searchSeq = useRef(0);
108
-
109
- useEffect(() => {
110
- setLoading(true);
111
- setError(null);
112
- getMatter(uid)
113
- .then(setMatter)
114
- .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this matter."))
115
- .finally(() => setLoading(false));
116
- void requests.reload();
117
- void exportRequests.reload();
118
- }, [uid]);
119
-
120
- // Only ever invoked from the close-confirmation modal below, which itself only renders once `matter` is
121
- // resolved (children only render once loaded — see the early returns further down) — the non-null
122
- // assertion reflects that real invariant, not an unchecked assumption.
123
- async function handleClose() {
124
- setClosing(true);
125
- setCloseError(null);
126
- try {
127
- const updated = await closeMatter(matter!.uid);
128
- setMatter(updated);
129
- setConfirmingClose(false);
130
- // Nothing more may be read under a closed matter - drop any material or search results already on
131
- // screen, including a search still in flight, and any approval still being confirmed.
132
- closeMaterialModal();
133
- searchSeq.current++;
134
- setSearchResults(null);
135
- setSearching(false);
136
- } catch (err) {
137
- setCloseError(err instanceof ApiRequestError ? err.message : "Could not close this matter.");
138
- } finally {
139
- setClosing(false);
140
- }
141
- }
142
-
143
- function closeCloseModal() {
144
- setConfirmingClose(false);
145
- setCloseError(null);
146
- }
147
-
148
- async function handleCreateRequest(e: FormEvent) {
149
- e.preventDefault();
150
- setNewRequestError(null);
151
- if (!newRequestMailboxUid.trim()) {
152
- setNewRequestError("A mailbox uid is required.");
153
- return;
154
- }
155
- setCreatingRequest(true);
156
- try {
157
- await createAccessRequest({ matterId: uid, mailboxUid: newRequestMailboxUid.trim() });
158
- setShowNewRequest(false);
159
- setNewRequestMailboxUid("");
160
- void requests.reload();
161
- } catch (err) {
162
- setNewRequestError(err instanceof ApiRequestError ? err.message : "Could not create this access request.");
163
- } finally {
164
- setCreatingRequest(false);
165
- }
166
- }
167
-
168
- // Only ever invoked from the approve-confirmation modal below, which only renders once `approveTarget` is set.
169
- async function handleApprove() {
170
- const request = approveTarget!;
171
- setApproveTarget(null);
172
- setActingOn((prev) => ({ ...prev, [request.uid]: true }));
173
- setActionErrors((prev) => ({ ...prev, [request.uid]: "" }));
174
- try {
175
- const updated = await approveAccessRequest(request.uid);
176
- requests.replaceItem(updated);
177
- } catch (err) {
178
- setActionErrors((prev) => ({
179
- ...prev,
180
- [request.uid]: err instanceof ApiRequestError ? err.message : "Could not approve this request.",
181
- }));
182
- } finally {
183
- setActingOn((prev) => ({ ...prev, [request.uid]: false }));
184
- }
185
- }
186
-
187
- async function handleDeny(request: EscrowAccessRequest) {
188
- setActingOn((prev) => ({ ...prev, [request.uid]: true }));
189
- setActionErrors((prev) => ({ ...prev, [request.uid]: "" }));
190
- try {
191
- const updated = await denyAccessRequest(request.uid);
192
- requests.replaceItem(updated);
193
- } catch (err) {
194
- setActionErrors((prev) => ({
195
- ...prev,
196
- [request.uid]: err instanceof ApiRequestError ? err.message : "Could not deny this request.",
197
- }));
198
- } finally {
199
- setActingOn((prev) => ({ ...prev, [request.uid]: false }));
200
- }
201
- }
202
-
203
- async function handleGetMaterial(request: EscrowAccessRequest) {
204
- const seq = ++materialSeq.current;
205
- setMaterialFor(request);
206
- setMaterialText("");
207
- setMaterialError(null);
208
- setLoadingMaterial(true);
209
- try {
210
- const material = await getAccessRequestMaterial(request.uid);
211
- if (seq === materialSeq.current) setMaterialText(JSON.stringify(material.masterKeyWraps, null, 2));
212
- } catch (err) {
213
- if (seq === materialSeq.current) {
214
- setMaterialError(err instanceof ApiRequestError ? err.message : "Could not read this request's material.");
215
- }
216
- } finally {
217
- if (seq === materialSeq.current) setLoadingMaterial(false);
218
- }
219
- }
220
-
221
- function closeMaterialModal() {
222
- materialSeq.current++;
223
- setMaterialFor(null);
224
- setMaterialText("");
225
- setMaterialError(null);
226
- setLoadingMaterial(false);
227
- }
228
-
229
- async function handleCreateExport() {
230
- setExportError(null);
231
- setCreatingExport(true);
232
- try {
233
- await createMatterExportRequest(uid);
234
- void exportRequests.reload();
235
- } catch (err) {
236
- setExportError(err instanceof ApiRequestError ? err.message : "Could not start this export.");
237
- } finally {
238
- setCreatingExport(false);
239
- }
240
- }
241
-
242
- async function handleSearch(e: FormEvent) {
243
- e.preventDefault();
244
- const seq = ++searchSeq.current;
245
- setSearchError(null);
246
- setSearching(true);
247
- try {
248
- // Same "parse the operator grammar once, client-side, before hitting the server" convention
249
- // apps/www/index.tsx's own inbox search already establishes - searchMatter()/buildSearchParams()
250
- // expect the caller to have already extracted operators into discrete params, not embedded in
251
- // the raw `q` text (see queryGrammar.ts's own doc comment). Passing the unparsed box text
252
- // straight through, as this previously did, silently treated e.g. "from:alice@example.com" as
253
- // four literal free-text words instead of a sender filter.
254
- const parsed = parseSearchQuery(searchText);
255
- const results = await searchMatter(uid, parsed.text, {
256
- types: parsed.entityTypes,
257
- from: parsed.from,
258
- to: parsed.to,
259
- cc: parsed.cc,
260
- subject: parsed.subject,
261
- hasAttachment: parsed.hasAttachment,
262
- before: parsed.before,
263
- after: parsed.after,
264
- folderUid: parsed.folderUid,
265
- flags: parsed.flags,
266
- labels: parsed.labels,
267
- });
268
- if (seq === searchSeq.current) setSearchResults(results);
269
- } catch (err) {
270
- if (seq === searchSeq.current) {
271
- setSearchError(err instanceof ApiRequestError ? err.message : "Could not search this matter.");
272
- }
273
- } finally {
274
- if (seq === searchSeq.current) setSearching(false);
275
- }
276
- }
277
-
278
- if (loading) {
279
- return <p className="text-sm text-text-muted">Loading&hellip;</p>;
280
- }
281
- if (error || !matter) {
282
- return <Alert>{error ?? "Matter not found."}</Alert>;
283
- }
284
-
285
- const matterRequests = requests.items.filter((request) => request.matterId === uid);
286
- const matterExportRequests = exportRequests.items.filter((request) => request.matterId === uid);
287
-
288
- return (
289
- <div className="max-w-4xl flex flex-col gap-5">
290
- <div className="flex items-start justify-between gap-4">
291
- <div>
292
- <a href="/escrow" className="text-sm text-primary-dark hover:underline">
293
- &larr; All matters
294
- </a>
295
- <h1 className="text-xl font-bold tracking-tight mt-1">{matter.name}</h1>
296
- </div>
297
- {!matter.closedAt && (
298
- <Button
299
- type="button"
300
- variant="secondary"
301
- className="!w-auto shrink-0"
302
- onClick={() => setConfirmingClose(true)}
303
- >
304
- Close matter
305
- </Button>
306
- )}
307
- </div>
308
-
309
- <div className="bg-surface border border-border rounded-md p-6">
310
- <dl className="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-3 text-sm">
311
- <dt className="text-text-muted">Description</dt>
312
- <dd>{matter.description ?? "None"}</dd>
313
- <dt className="text-text-muted">Escrow scope</dt>
314
- <dd className="font-mono text-xs">{matter.escrowScopeId}</dd>
315
- <dt className="text-text-muted">Custodian mailboxes</dt>
316
- <dd>{matter.custodianMailboxUids.join(", ")}</dd>
317
- <dt className="text-text-muted">Date range</dt>
318
- <dd>{formatDateRange(matter)}</dd>
319
- <dt className="text-text-muted">Status</dt>
320
- <dd>{matter.closedAt ? `Closed ${new Date(matter.closedAt).toLocaleString()}` : "Open"}</dd>
321
- </dl>
322
- </div>
323
-
324
- <div className="bg-surface border border-border rounded-md p-6">
325
- <div className="flex items-center justify-between mb-3">
326
- <h2 className="text-base font-bold uppercase tracking-wide">Access requests</h2>
327
- {!matter.closedAt && (
328
- <Button type="button" className="!w-auto" onClick={() => setShowNewRequest(true)}>
329
- + New access request
330
- </Button>
331
- )}
332
- </div>
333
-
334
- {requests.loadError && <Alert>{requests.loadError}</Alert>}
335
- {matterRequests.length === 0 ? (
336
- <p className="text-sm text-text-muted">{requests.loading ? "Loading…" : "No access requests yet."}</p>
337
- ) : (
338
- <ul className="flex flex-col gap-3">
339
- {matterRequests.map((request) => (
340
- <li key={request.uid} className="border border-border rounded-sm p-4">
341
- <div className="flex items-center justify-between gap-3 flex-wrap">
342
- <div>
343
- <div className="text-sm font-semibold">{request.mailboxUid}</div>
344
- <div className="text-xs text-text-muted">
345
- {request.approvals.length} of {request.requiredHoldersAtCreation} approvals
346
- &middot; requested by {request.requestedByUserUid}
347
- </div>
348
- </div>
349
- <span
350
- className={[
351
- "text-xs font-bold uppercase tracking-wide py-0.5 px-2 rounded-pill",
352
- request.status === "denied"
353
- ? "bg-danger-bg text-danger"
354
- : request.status === "pending"
355
- ? "bg-surface-alt text-text-muted"
356
- : "bg-success text-white",
357
- ].join(" ")}
358
- >
359
- {request.status}
360
- </span>
361
- </div>
362
-
363
- {actionErrors[request.uid] && <Alert>{actionErrors[request.uid]}</Alert>}
364
-
365
- <div className="flex gap-3 mt-3">
366
- {/* A closed matter refuses approvals and denials - don't offer them. */}
367
- {!matter.closedAt && request.status === "pending" && (
368
- <>
369
- <Button
370
- type="button"
371
- className="!w-auto"
372
- loading={actingOn[request.uid]}
373
- disabled={actingOn[request.uid]}
374
- onClick={() => setApproveTarget(request)}
375
- >
376
- Approve
377
- </Button>
378
- <Button
379
- type="button"
380
- variant="secondary"
381
- className="!w-auto"
382
- loading={actingOn[request.uid]}
383
- disabled={actingOn[request.uid]}
384
- onClick={() => handleDeny(request)}
385
- >
386
- Deny
387
- </Button>
388
- </>
389
- )}
390
- {!matter.closedAt && (request.status === "approved" || request.status === "fulfilled") && (
391
- <Button
392
- type="button"
393
- variant="secondary"
394
- className="!w-auto"
395
- onClick={() => handleGetMaterial(request)}
396
- >
397
- Get material
398
- </Button>
399
- )}
400
- </div>
401
- </li>
402
- ))}
403
- </ul>
404
- )}
405
- <LoadMoreButton list={requests} label="Load more access requests" />
406
- </div>
407
-
408
- <div className="bg-surface border border-border rounded-md p-6">
409
- <div className="flex items-center justify-between mb-3">
410
- <h2 className="text-base font-bold uppercase tracking-wide">Export this matter</h2>
411
- {!matter.closedAt && (
412
- <Button type="button" className="!w-auto" loading={creatingExport} disabled={creatingExport} onClick={handleCreateExport}>
413
- + New export
414
- </Button>
415
- )}
416
- </div>
417
- <p className="text-xs text-text-muted mb-3">
418
- Exports messages (narrowed to this matter&rsquo;s own date range), plus full contacts,
419
- calendar events, tasks, and notes, from every custodian mailbox actually assigned to this
420
- matter&rsquo;s escrow scope, as one combined NDJSON file.
421
- </p>
422
- {exportError && <Alert>{exportError}</Alert>}
423
- {exportRequests.loadError && <Alert>{exportRequests.loadError}</Alert>}
424
- {matterExportRequests.length === 0 ? (
425
- <p className="text-sm text-text-muted">{exportRequests.loading ? "Loading…" : "No export requests yet."}</p>
426
- ) : (
427
- <ul className="flex flex-col gap-2">
428
- {matterExportRequests.map((request) => (
429
- <li
430
- key={request.uid}
431
- className="flex items-center justify-between gap-3 text-sm py-1.5 px-3 bg-surface-alt rounded-sm"
432
- >
433
- <span>
434
- {new Date(request.dateCreated).toLocaleString()}
435
- {request.status === "failed" && request.errorMessage && (
436
- <span className="text-danger"> — {request.errorMessage}</span>
437
- )}
438
- </span>
439
- <span className="flex items-center gap-3">
440
- <span
441
- className={`text-xs font-bold uppercase tracking-wide py-0.5 px-2 rounded-pill ${statusBadgeClass(request.status)}`}
442
- >
443
- {statusLabel(request.status)}
444
- </span>
445
- {/* The server refuses downloads once the matter is closed. */}
446
- {!matter.closedAt && request.status === "ready" && (
447
- <a
448
- href={matterExportRequestDownloadUrl(request.uid)}
449
- className="text-primary-dark hover:underline font-medium"
450
- >
451
- Download
452
- </a>
453
- )}
454
- </span>
455
- </li>
456
- ))}
457
- </ul>
458
- )}
459
- </div>
460
-
461
- <div className="bg-surface border border-border rounded-md p-6">
462
- <h2 className="text-base font-bold uppercase tracking-wide mb-3">Search this matter&rsquo;s custodians</h2>
463
- {matter.closedAt ? (
464
- <p className="text-sm text-text-muted">This matter is closed, so its custodians can no longer be searched or exported.</p>
465
- ) : (
466
- <form onSubmit={handleSearch} className="flex gap-3 mb-4">
467
- <input
468
- aria-label="Search this matter"
469
- className={INPUT_CLASS}
470
- value={searchText}
471
- onChange={(e) => setSearchText(e.target.value)}
472
- placeholder="Free text or an operator filter, e.g. from:alice@example.com"
473
- />
474
- <Button type="submit" className="!w-auto shrink-0" loading={searching} disabled={searching || !searchText.trim()}>
475
- Search
476
- </Button>
477
- </form>
478
- )}
479
- {searchError && <Alert>{searchError}</Alert>}
480
- {searchResults &&
481
- (Object.keys(searchResults).length === 0 ? (
482
- <p className="text-sm text-text-muted">No custodian mailboxes could be searched.</p>
483
- ) : (
484
- <div className="flex flex-col gap-4">
485
- {Object.entries(searchResults).map(([mailboxUid, page]) => (
486
- <div key={mailboxUid}>
487
- <h3 className="text-sm font-semibold mb-2">{mailboxUid}</h3>
488
- {page.results.length === 0 ? (
489
- <p className="text-xs text-text-muted">No matches.</p>
490
- ) : (
491
- <ul className="flex flex-col gap-1">
492
- {page.results.map((result) => (
493
- <li
494
- key={`${result.entityType}-${result.entityUid}`}
495
- className="text-sm py-1 px-2 bg-surface-alt rounded-sm"
496
- >
497
- {result.entityType} &middot; {result.entityUid}
498
- {result.snippet && <span className="text-text-muted"> — {result.snippet}</span>}
499
- </li>
500
- ))}
501
- </ul>
502
- )}
503
- </div>
504
- ))}
505
- </div>
506
- ))}
507
- </div>
508
-
509
- <Modal open={confirmingClose} onClose={closeCloseModal} title="Close matter">
510
- <p className="text-sm mb-3">
511
- Close <strong>{matter.name}</strong>? Once closed, no new access requests, exports, searches, or key
512
- material reads are allowed under it. This cannot be reopened here.
513
- </p>
514
- <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-sm mb-4">
515
- <dt className="text-text-muted">Custodians</dt>
516
- <dd className="break-all">{matter.custodianMailboxUids.join(", ")}</dd>
517
- <dt className="text-text-muted">Date range</dt>
518
- <dd>{formatDateRange(matter)}</dd>
519
- </dl>
520
- {closeError && <Alert>{closeError}</Alert>}
521
- <div className="flex gap-3 justify-end">
522
- <Button type="button" variant="secondary" className="!w-auto" disabled={closing} onClick={closeCloseModal}>
523
- Cancel
524
- </Button>
525
- <Button
526
- type="button"
527
- className="!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger"
528
- loading={closing}
529
- disabled={closing}
530
- onClick={handleClose}
531
- >
532
- Close matter
533
- </Button>
534
- </div>
535
- </Modal>
536
-
537
- <Modal open={approveTarget !== null} onClose={() => setApproveTarget(null)} title="Approve access request">
538
- <p className="text-sm mb-3">
539
- Approving lets holders of this matter&rsquo;s escrow scope read the key material for this mailbox once
540
- enough holders have approved.
541
- </p>
542
- <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-sm mb-4">
543
- <dt className="text-text-muted">Matter</dt>
544
- <dd>{matter.name}</dd>
545
- <dt className="text-text-muted">Mailbox</dt>
546
- <dd className="break-all">{approveTarget?.mailboxUid}</dd>
547
- <dt className="text-text-muted">Date range</dt>
548
- <dd>{formatDateRange(matter)}</dd>
549
- <dt className="text-text-muted">Approvals</dt>
550
- <dd>
551
- {approveTarget?.approvals.length} of {approveTarget?.requiredHoldersAtCreation} so far, including
552
- requester {approveTarget?.requestedByUserUid}
553
- </dd>
554
- </dl>
555
- <div className="flex gap-3 justify-end">
556
- <Button type="button" variant="secondary" className="!w-auto" onClick={() => setApproveTarget(null)}>
557
- Cancel
558
- </Button>
559
- <Button type="button" className="!w-auto" onClick={() => void handleApprove()}>
560
- Approve request
561
- </Button>
562
- </div>
563
- </Modal>
564
-
565
- <Modal open={showNewRequest} onClose={() => setShowNewRequest(false)} title="New access request">
566
- <form onSubmit={handleCreateRequest} className="flex flex-col gap-4">
567
- {newRequestError && <Alert>{newRequestError}</Alert>}
568
- <FormField label="Mailbox uid" htmlFor="newRequestMailboxUid">
569
- <input
570
- id="newRequestMailboxUid"
571
- type="text"
572
- className={INPUT_CLASS}
573
- value={newRequestMailboxUid}
574
- onChange={(e) => setNewRequestMailboxUid(e.target.value)}
575
- />
576
- </FormField>
577
- <p className="text-xs text-text-muted">
578
- Must be one of this matter's own custodian mailboxes, currently assigned to this matter's
579
- escrow scope. Your own creation counts as the first approval.
580
- </p>
581
- <div className="flex gap-3 justify-end">
582
- <Button
583
- type="button"
584
- variant="secondary"
585
- className="!w-auto"
586
- disabled={creatingRequest}
587
- onClick={() => setShowNewRequest(false)}
588
- >
589
- Cancel
590
- </Button>
591
- <Button type="submit" className="!w-auto" loading={creatingRequest} disabled={creatingRequest}>
592
- Create request
593
- </Button>
594
- </div>
595
- </form>
596
- </Modal>
597
-
598
- <Modal open={materialFor !== null} onClose={closeMaterialModal} title="Escrow key material">
599
- {/* Deliberately raw JSON, not a decrypt/viewer UI: this console never attempts to decrypt
600
- anything, per specs/end-to-end_encryption.md's "this server never holds the scope's
601
- private key" - the holder is expected to copy this into their own offline tooling. See
602
- this session's own report for this documented scope trim. */}
603
- {loadingMaterial ? (
604
- <p className="text-sm text-text-muted">Loading&hellip;</p>
605
- ) : materialError ? (
606
- <Alert>{materialError}</Alert>
607
- ) : (
608
- <>
609
- <p className="text-sm text-text-muted mb-3">
610
- Still-encrypted master key wraps for <strong>{materialFor?.mailboxUid}</strong> — this
611
- server cannot decrypt these either. Copy the JSON below into your own offline tooling.
612
- </p>
613
- <pre className="text-xs font-mono bg-surface-alt border border-border rounded-sm p-3 overflow-x-auto whitespace-pre-wrap break-all">
614
- {materialText}
615
- </pre>
616
- </>
617
- )}
618
- </Modal>
619
- </div>
620
- );
621
- }
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import React, { FormEvent, useEffect, useRef, useState } from "react";
6
+ import { ApiRequestError } from "../../../lib/util/api.js";
7
+ import { closeMatter, getMatter, Matter } from "../../../lib/admin/mattersApi.js";
8
+ import {
9
+ approveAccessRequest,
10
+ createAccessRequest,
11
+ denyAccessRequest,
12
+ EscrowAccessRequest,
13
+ getAccessRequestMaterial,
14
+ listAccessRequests,
15
+ } from "../../../lib/admin/escrowAccessRequestsApi.js";
16
+ import {
17
+ createMatterExportRequest,
18
+ listMatterExportRequests,
19
+ matterExportRequestDownloadUrl,
20
+ } from "../../../lib/admin/matterExportApi.js";
21
+ import { searchMatter, SearchResultPage } from "../../../lib/admin/matterSearchApi.js";
22
+ import { parseSearchQuery } from "../../../lib/search/queryGrammar.js";
23
+ import EscrowShell, { EscrowShellProps } from "../../shared/components/escrow/layout/EscrowShell.js";
24
+ import { LoadMoreButton, usePagedList } from "../../shared/components/admin/usePagedList.js";
25
+ import Alert from "../../../lib/components/feedback/Alert.js";
26
+ import Button from "../../../lib/components/buttons/Button.js";
27
+ import FormField from "../../../lib/components/forms/FormField.js";
28
+ import Modal from "../../../lib/components/overlays/Modal.js";
29
+
30
+ const INPUT_CLASS =
31
+ "w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
32
+
33
+ /** Request statuses still waiting or running - shown as a neutral in-progress badge, never as success. Includes
34
+ * restapi's `processing` (exports/imports) and `in_progress` (an erasure that is running). */
35
+ const IN_PROGRESS_STATUSES = new Set(["pending", "processing", "in_progress"]);
36
+
37
+ function statusBadgeClass(status: string): string {
38
+ if (status === "failed" || status === "denied") return "bg-danger-bg text-danger";
39
+ if (IN_PROGRESS_STATUSES.has(status)) return "bg-surface-alt text-text-muted";
40
+ return "bg-success text-white";
41
+ }
42
+
43
+ /** A status as shown on its badge (`in_progress` -> `in progress`). */
44
+ function statusLabel(status: string): string {
45
+ return status.replace(/_/g, " ");
46
+ }
47
+
48
+ export default function MatterDetailPage(props: Omit<EscrowShellProps, "active"> & { params: { uid: string } }) {
49
+ return (
50
+ <EscrowShell {...props} active="matters">
51
+ <MatterDetailContent uid={props.params.uid} />
52
+ </EscrowShell>
53
+ );
54
+ }
55
+
56
+ function formatDateRange(matter: Matter): string {
57
+ return `${new Date(matter.dateRangeStart).toLocaleDateString()} – ${new Date(matter.dateRangeEnd).toLocaleDateString()}`;
58
+ }
59
+
60
+ function MatterDetailContent({ uid }: { uid: string }) {
61
+ const [matter, setMatter] = useState<Matter | null>(null);
62
+ const [loading, setLoading] = useState(true);
63
+ const [error, setError] = useState<string | null>(null);
64
+
65
+ // Both lists are fetched for this matter only (`matterId`), newest first, a page at a time. Rows are still
66
+ // checked against this matter in case an older server ignores the filter.
67
+ const requests = usePagedList(
68
+ (params) => listAccessRequests({ ...params, matterId: uid }),
69
+ "Could not load this matter's access requests.",
70
+ );
71
+ const exportRequests = usePagedList(
72
+ (params) => listMatterExportRequests({ ...params, matterId: uid }),
73
+ "Could not load this matter's export requests.",
74
+ );
75
+
76
+ const [confirmingClose, setConfirmingClose] = useState(false);
77
+ const [closing, setClosing] = useState(false);
78
+ const [closeError, setCloseError] = useState<string | null>(null);
79
+ const [approveTarget, setApproveTarget] = useState<EscrowAccessRequest | null>(null);
80
+
81
+ const [showNewRequest, setShowNewRequest] = useState(false);
82
+ const [newRequestMailboxUid, setNewRequestMailboxUid] = useState("");
83
+ const [creatingRequest, setCreatingRequest] = useState(false);
84
+ const [newRequestError, setNewRequestError] = useState<string | null>(null);
85
+
86
+ // Keyed by request uid — several rows can be independently mid-action at once.
87
+ const [actionErrors, setActionErrors] = useState<Record<string, string>>({});
88
+ const [actingOn, setActingOn] = useState<Record<string, boolean>>({});
89
+
90
+ const [materialFor, setMaterialFor] = useState<EscrowAccessRequest | null>(null);
91
+ const [materialText, setMaterialText] = useState("");
92
+ const [materialError, setMaterialError] = useState<string | null>(null);
93
+ const [loadingMaterial, setLoadingMaterial] = useState(false);
94
+ // Bumped whenever the material modal closes (or the matter does), so a slow "Get material" response for a
95
+ // modal that's already gone - or for an earlier request - is never shown.
96
+ const materialSeq = useRef(0);
97
+
98
+ const [creatingExport, setCreatingExport] = useState(false);
99
+ const [exportError, setExportError] = useState<string | null>(null);
100
+
101
+ const [searchText, setSearchText] = useState("");
102
+ const [searching, setSearching] = useState(false);
103
+ const [searchError, setSearchError] = useState<string | null>(null);
104
+ const [searchResults, setSearchResults] = useState<Record<string, SearchResultPage> | null>(null);
105
+ // Bumped by every search and by closing the matter, so a search still in flight when the matter closes (or
106
+ // superseded by a newer search) never shows its results.
107
+ const searchSeq = useRef(0);
108
+
109
+ useEffect(() => {
110
+ setLoading(true);
111
+ setError(null);
112
+ getMatter(uid)
113
+ .then(setMatter)
114
+ .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this matter."))
115
+ .finally(() => setLoading(false));
116
+ void requests.reload();
117
+ void exportRequests.reload();
118
+ }, [uid]);
119
+
120
+ // Only ever invoked from the close-confirmation modal below, which itself only renders once `matter` is
121
+ // resolved (children only render once loaded — see the early returns further down) — the non-null
122
+ // assertion reflects that real invariant, not an unchecked assumption.
123
+ async function handleClose() {
124
+ setClosing(true);
125
+ setCloseError(null);
126
+ try {
127
+ const updated = await closeMatter(matter!.uid);
128
+ setMatter(updated);
129
+ setConfirmingClose(false);
130
+ // Nothing more may be read under a closed matter - drop any material or search results already on
131
+ // screen, including a search still in flight, and any approval still being confirmed.
132
+ closeMaterialModal();
133
+ searchSeq.current++;
134
+ setSearchResults(null);
135
+ setSearching(false);
136
+ } catch (err) {
137
+ setCloseError(err instanceof ApiRequestError ? err.message : "Could not close this matter.");
138
+ } finally {
139
+ setClosing(false);
140
+ }
141
+ }
142
+
143
+ function closeCloseModal() {
144
+ setConfirmingClose(false);
145
+ setCloseError(null);
146
+ }
147
+
148
+ async function handleCreateRequest(e: FormEvent) {
149
+ e.preventDefault();
150
+ setNewRequestError(null);
151
+ if (!newRequestMailboxUid.trim()) {
152
+ setNewRequestError("A mailbox uid is required.");
153
+ return;
154
+ }
155
+ setCreatingRequest(true);
156
+ try {
157
+ await createAccessRequest({ matterId: uid, mailboxUid: newRequestMailboxUid.trim() });
158
+ setShowNewRequest(false);
159
+ setNewRequestMailboxUid("");
160
+ void requests.reload();
161
+ } catch (err) {
162
+ setNewRequestError(err instanceof ApiRequestError ? err.message : "Could not create this access request.");
163
+ } finally {
164
+ setCreatingRequest(false);
165
+ }
166
+ }
167
+
168
+ // Only ever invoked from the approve-confirmation modal below, which only renders once `approveTarget` is set.
169
+ async function handleApprove() {
170
+ const request = approveTarget!;
171
+ setApproveTarget(null);
172
+ setActingOn((prev) => ({ ...prev, [request.uid]: true }));
173
+ setActionErrors((prev) => ({ ...prev, [request.uid]: "" }));
174
+ try {
175
+ const updated = await approveAccessRequest(request.uid);
176
+ requests.replaceItem(updated);
177
+ } catch (err) {
178
+ setActionErrors((prev) => ({
179
+ ...prev,
180
+ [request.uid]: err instanceof ApiRequestError ? err.message : "Could not approve this request.",
181
+ }));
182
+ } finally {
183
+ setActingOn((prev) => ({ ...prev, [request.uid]: false }));
184
+ }
185
+ }
186
+
187
+ async function handleDeny(request: EscrowAccessRequest) {
188
+ setActingOn((prev) => ({ ...prev, [request.uid]: true }));
189
+ setActionErrors((prev) => ({ ...prev, [request.uid]: "" }));
190
+ try {
191
+ const updated = await denyAccessRequest(request.uid);
192
+ requests.replaceItem(updated);
193
+ } catch (err) {
194
+ setActionErrors((prev) => ({
195
+ ...prev,
196
+ [request.uid]: err instanceof ApiRequestError ? err.message : "Could not deny this request.",
197
+ }));
198
+ } finally {
199
+ setActingOn((prev) => ({ ...prev, [request.uid]: false }));
200
+ }
201
+ }
202
+
203
+ async function handleGetMaterial(request: EscrowAccessRequest) {
204
+ const seq = ++materialSeq.current;
205
+ setMaterialFor(request);
206
+ setMaterialText("");
207
+ setMaterialError(null);
208
+ setLoadingMaterial(true);
209
+ try {
210
+ const material = await getAccessRequestMaterial(request.uid);
211
+ if (seq === materialSeq.current) setMaterialText(JSON.stringify(material.masterKeyWraps, null, 2));
212
+ } catch (err) {
213
+ if (seq === materialSeq.current) {
214
+ setMaterialError(err instanceof ApiRequestError ? err.message : "Could not read this request's material.");
215
+ }
216
+ } finally {
217
+ if (seq === materialSeq.current) setLoadingMaterial(false);
218
+ }
219
+ }
220
+
221
+ function closeMaterialModal() {
222
+ materialSeq.current++;
223
+ setMaterialFor(null);
224
+ setMaterialText("");
225
+ setMaterialError(null);
226
+ setLoadingMaterial(false);
227
+ }
228
+
229
+ async function handleCreateExport() {
230
+ setExportError(null);
231
+ setCreatingExport(true);
232
+ try {
233
+ await createMatterExportRequest(uid);
234
+ void exportRequests.reload();
235
+ } catch (err) {
236
+ setExportError(err instanceof ApiRequestError ? err.message : "Could not start this export.");
237
+ } finally {
238
+ setCreatingExport(false);
239
+ }
240
+ }
241
+
242
+ async function handleSearch(e: FormEvent) {
243
+ e.preventDefault();
244
+ const seq = ++searchSeq.current;
245
+ setSearchError(null);
246
+ setSearching(true);
247
+ try {
248
+ // Same "parse the operator grammar once, client-side, before hitting the server" convention
249
+ // apps/www/index.tsx's own inbox search already establishes - searchMatter()/buildSearchParams()
250
+ // expect the caller to have already extracted operators into discrete params, not embedded in
251
+ // the raw `q` text (see queryGrammar.ts's own doc comment). Passing the unparsed box text
252
+ // straight through, as this previously did, silently treated e.g. "from:alice@example.com" as
253
+ // four literal free-text words instead of a sender filter.
254
+ const parsed = parseSearchQuery(searchText);
255
+ const results = await searchMatter(uid, parsed.text, {
256
+ types: parsed.entityTypes,
257
+ from: parsed.from,
258
+ to: parsed.to,
259
+ cc: parsed.cc,
260
+ subject: parsed.subject,
261
+ hasAttachment: parsed.hasAttachment,
262
+ before: parsed.before,
263
+ after: parsed.after,
264
+ folderUid: parsed.folderUid,
265
+ flags: parsed.flags,
266
+ labels: parsed.labels,
267
+ });
268
+ if (seq === searchSeq.current) setSearchResults(results);
269
+ } catch (err) {
270
+ if (seq === searchSeq.current) {
271
+ setSearchError(err instanceof ApiRequestError ? err.message : "Could not search this matter.");
272
+ }
273
+ } finally {
274
+ if (seq === searchSeq.current) setSearching(false);
275
+ }
276
+ }
277
+
278
+ if (loading) {
279
+ return <p className="text-sm text-text-muted">Loading&hellip;</p>;
280
+ }
281
+ if (error || !matter) {
282
+ return <Alert>{error ?? "Matter not found."}</Alert>;
283
+ }
284
+
285
+ const matterRequests = requests.items.filter((request) => request.matterId === uid);
286
+ const matterExportRequests = exportRequests.items.filter((request) => request.matterId === uid);
287
+
288
+ return (
289
+ <div className="max-w-4xl flex flex-col gap-5">
290
+ <div className="flex items-start justify-between gap-4">
291
+ <div>
292
+ <a href="/escrow" className="text-sm text-primary-dark hover:underline">
293
+ &larr; All matters
294
+ </a>
295
+ <h1 className="text-xl font-bold tracking-tight mt-1">{matter.name}</h1>
296
+ </div>
297
+ {!matter.closedAt && (
298
+ <Button
299
+ type="button"
300
+ variant="secondary"
301
+ className="!w-auto shrink-0"
302
+ onClick={() => setConfirmingClose(true)}
303
+ >
304
+ Close matter
305
+ </Button>
306
+ )}
307
+ </div>
308
+
309
+ <div className="bg-surface border border-border rounded-md p-6">
310
+ <dl className="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-3 text-sm">
311
+ <dt className="text-text-muted">Description</dt>
312
+ <dd>{matter.description ?? "None"}</dd>
313
+ <dt className="text-text-muted">Escrow scope</dt>
314
+ <dd className="font-mono text-xs">{matter.escrowScopeId}</dd>
315
+ <dt className="text-text-muted">Custodian mailboxes</dt>
316
+ <dd>{matter.custodianMailboxUids.join(", ")}</dd>
317
+ <dt className="text-text-muted">Date range</dt>
318
+ <dd>{formatDateRange(matter)}</dd>
319
+ <dt className="text-text-muted">Status</dt>
320
+ <dd>{matter.closedAt ? `Closed ${new Date(matter.closedAt).toLocaleString()}` : "Open"}</dd>
321
+ </dl>
322
+ </div>
323
+
324
+ <div className="bg-surface border border-border rounded-md p-6">
325
+ <div className="flex items-center justify-between mb-3">
326
+ <h2 className="text-base font-bold uppercase tracking-wide">Access requests</h2>
327
+ {!matter.closedAt && (
328
+ <Button type="button" className="!w-auto" onClick={() => setShowNewRequest(true)}>
329
+ + New access request
330
+ </Button>
331
+ )}
332
+ </div>
333
+
334
+ {requests.loadError && <Alert>{requests.loadError}</Alert>}
335
+ {matterRequests.length === 0 ? (
336
+ <p className="text-sm text-text-muted">{requests.loading ? "Loading…" : "No access requests yet."}</p>
337
+ ) : (
338
+ <ul className="flex flex-col gap-3">
339
+ {matterRequests.map((request) => (
340
+ <li key={request.uid} className="border border-border rounded-sm p-4">
341
+ <div className="flex items-center justify-between gap-3 flex-wrap">
342
+ <div>
343
+ <div className="text-sm font-semibold">{request.mailboxUid}</div>
344
+ <div className="text-xs text-text-muted">
345
+ {request.approvals.length} of {request.requiredHoldersAtCreation} approvals
346
+ &middot; requested by {request.requestedByUserUid}
347
+ </div>
348
+ </div>
349
+ <span
350
+ className={[
351
+ "text-xs font-bold uppercase tracking-wide py-0.5 px-2 rounded-pill",
352
+ request.status === "denied"
353
+ ? "bg-danger-bg text-danger"
354
+ : request.status === "pending"
355
+ ? "bg-surface-alt text-text-muted"
356
+ : "bg-success text-white",
357
+ ].join(" ")}
358
+ >
359
+ {request.status}
360
+ </span>
361
+ </div>
362
+
363
+ {actionErrors[request.uid] && <Alert>{actionErrors[request.uid]}</Alert>}
364
+
365
+ <div className="flex gap-3 mt-3">
366
+ {/* A closed matter refuses approvals and denials - don't offer them. */}
367
+ {!matter.closedAt && request.status === "pending" && (
368
+ <>
369
+ <Button
370
+ type="button"
371
+ className="!w-auto"
372
+ loading={actingOn[request.uid]}
373
+ disabled={actingOn[request.uid]}
374
+ onClick={() => setApproveTarget(request)}
375
+ >
376
+ Approve
377
+ </Button>
378
+ <Button
379
+ type="button"
380
+ variant="secondary"
381
+ className="!w-auto"
382
+ loading={actingOn[request.uid]}
383
+ disabled={actingOn[request.uid]}
384
+ onClick={() => handleDeny(request)}
385
+ >
386
+ Deny
387
+ </Button>
388
+ </>
389
+ )}
390
+ {!matter.closedAt && (request.status === "approved" || request.status === "fulfilled") && (
391
+ <Button
392
+ type="button"
393
+ variant="secondary"
394
+ className="!w-auto"
395
+ onClick={() => handleGetMaterial(request)}
396
+ >
397
+ Get material
398
+ </Button>
399
+ )}
400
+ </div>
401
+ </li>
402
+ ))}
403
+ </ul>
404
+ )}
405
+ <LoadMoreButton list={requests} label="Load more access requests" />
406
+ </div>
407
+
408
+ <div className="bg-surface border border-border rounded-md p-6">
409
+ <div className="flex items-center justify-between mb-3">
410
+ <h2 className="text-base font-bold uppercase tracking-wide">Export this matter</h2>
411
+ {!matter.closedAt && (
412
+ <Button type="button" className="!w-auto" loading={creatingExport} disabled={creatingExport} onClick={handleCreateExport}>
413
+ + New export
414
+ </Button>
415
+ )}
416
+ </div>
417
+ <p className="text-xs text-text-muted mb-3">
418
+ Exports messages (narrowed to this matter&rsquo;s own date range), plus full contacts,
419
+ calendar events, tasks, and notes, from every custodian mailbox actually assigned to this
420
+ matter&rsquo;s escrow scope, as one combined NDJSON file.
421
+ </p>
422
+ {exportError && <Alert>{exportError}</Alert>}
423
+ {exportRequests.loadError && <Alert>{exportRequests.loadError}</Alert>}
424
+ {matterExportRequests.length === 0 ? (
425
+ <p className="text-sm text-text-muted">{exportRequests.loading ? "Loading…" : "No export requests yet."}</p>
426
+ ) : (
427
+ <ul className="flex flex-col gap-2">
428
+ {matterExportRequests.map((request) => (
429
+ <li
430
+ key={request.uid}
431
+ className="flex items-center justify-between gap-3 text-sm py-1.5 px-3 bg-surface-alt rounded-sm"
432
+ >
433
+ <span>
434
+ {new Date(request.dateCreated).toLocaleString()}
435
+ {request.status === "failed" && request.errorMessage && (
436
+ <span className="text-danger"> — {request.errorMessage}</span>
437
+ )}
438
+ </span>
439
+ <span className="flex items-center gap-3">
440
+ <span
441
+ className={`text-xs font-bold uppercase tracking-wide py-0.5 px-2 rounded-pill ${statusBadgeClass(request.status)}`}
442
+ >
443
+ {statusLabel(request.status)}
444
+ </span>
445
+ {/* The server refuses downloads once the matter is closed. */}
446
+ {!matter.closedAt && request.status === "ready" && (
447
+ <a
448
+ href={matterExportRequestDownloadUrl(request.uid)}
449
+ className="text-primary-dark hover:underline font-medium"
450
+ >
451
+ Download
452
+ </a>
453
+ )}
454
+ </span>
455
+ </li>
456
+ ))}
457
+ </ul>
458
+ )}
459
+ </div>
460
+
461
+ <div className="bg-surface border border-border rounded-md p-6">
462
+ <h2 className="text-base font-bold uppercase tracking-wide mb-3">Search this matter&rsquo;s custodians</h2>
463
+ {matter.closedAt ? (
464
+ <p className="text-sm text-text-muted">This matter is closed, so its custodians can no longer be searched or exported.</p>
465
+ ) : (
466
+ <form onSubmit={handleSearch} className="flex gap-3 mb-4">
467
+ <input
468
+ aria-label="Search this matter"
469
+ className={INPUT_CLASS}
470
+ value={searchText}
471
+ onChange={(e) => setSearchText(e.target.value)}
472
+ placeholder="Free text or an operator filter, e.g. from:alice@example.com"
473
+ />
474
+ <Button type="submit" className="!w-auto shrink-0" loading={searching} disabled={searching || !searchText.trim()}>
475
+ Search
476
+ </Button>
477
+ </form>
478
+ )}
479
+ {searchError && <Alert>{searchError}</Alert>}
480
+ {searchResults &&
481
+ (Object.keys(searchResults).length === 0 ? (
482
+ <p className="text-sm text-text-muted">No custodian mailboxes could be searched.</p>
483
+ ) : (
484
+ <div className="flex flex-col gap-4">
485
+ {Object.entries(searchResults).map(([mailboxUid, page]) => (
486
+ <div key={mailboxUid}>
487
+ <h3 className="text-sm font-semibold mb-2">{mailboxUid}</h3>
488
+ {page.results.length === 0 ? (
489
+ <p className="text-xs text-text-muted">No matches.</p>
490
+ ) : (
491
+ <ul className="flex flex-col gap-1">
492
+ {page.results.map((result) => (
493
+ <li
494
+ key={`${result.entityType}-${result.entityUid}`}
495
+ className="text-sm py-1 px-2 bg-surface-alt rounded-sm"
496
+ >
497
+ {result.entityType} &middot; {result.entityUid}
498
+ {result.snippet && <span className="text-text-muted"> — {result.snippet}</span>}
499
+ </li>
500
+ ))}
501
+ </ul>
502
+ )}
503
+ </div>
504
+ ))}
505
+ </div>
506
+ ))}
507
+ </div>
508
+
509
+ <Modal open={confirmingClose} onClose={closeCloseModal} title="Close matter">
510
+ <p className="text-sm mb-3">
511
+ Close <strong>{matter.name}</strong>? Once closed, no new access requests, exports, searches, or key
512
+ material reads are allowed under it. This cannot be reopened here.
513
+ </p>
514
+ <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-sm mb-4">
515
+ <dt className="text-text-muted">Custodians</dt>
516
+ <dd className="break-all">{matter.custodianMailboxUids.join(", ")}</dd>
517
+ <dt className="text-text-muted">Date range</dt>
518
+ <dd>{formatDateRange(matter)}</dd>
519
+ </dl>
520
+ {closeError && <Alert>{closeError}</Alert>}
521
+ <div className="flex gap-3 justify-end">
522
+ <Button type="button" variant="secondary" className="!w-auto" disabled={closing} onClick={closeCloseModal}>
523
+ Cancel
524
+ </Button>
525
+ <Button
526
+ type="button"
527
+ className="!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger"
528
+ loading={closing}
529
+ disabled={closing}
530
+ onClick={handleClose}
531
+ >
532
+ Close matter
533
+ </Button>
534
+ </div>
535
+ </Modal>
536
+
537
+ <Modal open={approveTarget !== null} onClose={() => setApproveTarget(null)} title="Approve access request">
538
+ <p className="text-sm mb-3">
539
+ Approving lets holders of this matter&rsquo;s escrow scope read the key material for this mailbox once
540
+ enough holders have approved.
541
+ </p>
542
+ <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-sm mb-4">
543
+ <dt className="text-text-muted">Matter</dt>
544
+ <dd>{matter.name}</dd>
545
+ <dt className="text-text-muted">Mailbox</dt>
546
+ <dd className="break-all">{approveTarget?.mailboxUid}</dd>
547
+ <dt className="text-text-muted">Date range</dt>
548
+ <dd>{formatDateRange(matter)}</dd>
549
+ <dt className="text-text-muted">Approvals</dt>
550
+ <dd>
551
+ {approveTarget?.approvals.length} of {approveTarget?.requiredHoldersAtCreation} so far, including
552
+ requester {approveTarget?.requestedByUserUid}
553
+ </dd>
554
+ </dl>
555
+ <div className="flex gap-3 justify-end">
556
+ <Button type="button" variant="secondary" className="!w-auto" onClick={() => setApproveTarget(null)}>
557
+ Cancel
558
+ </Button>
559
+ <Button type="button" className="!w-auto" onClick={() => void handleApprove()}>
560
+ Approve request
561
+ </Button>
562
+ </div>
563
+ </Modal>
564
+
565
+ <Modal open={showNewRequest} onClose={() => setShowNewRequest(false)} title="New access request">
566
+ <form onSubmit={handleCreateRequest} className="flex flex-col gap-4">
567
+ {newRequestError && <Alert>{newRequestError}</Alert>}
568
+ <FormField label="Mailbox uid" htmlFor="newRequestMailboxUid">
569
+ <input
570
+ id="newRequestMailboxUid"
571
+ type="text"
572
+ className={INPUT_CLASS}
573
+ value={newRequestMailboxUid}
574
+ onChange={(e) => setNewRequestMailboxUid(e.target.value)}
575
+ />
576
+ </FormField>
577
+ <p className="text-xs text-text-muted">
578
+ Must be one of this matter's own custodian mailboxes, currently assigned to this matter's
579
+ escrow scope. Your own creation counts as the first approval.
580
+ </p>
581
+ <div className="flex gap-3 justify-end">
582
+ <Button
583
+ type="button"
584
+ variant="secondary"
585
+ className="!w-auto"
586
+ disabled={creatingRequest}
587
+ onClick={() => setShowNewRequest(false)}
588
+ >
589
+ Cancel
590
+ </Button>
591
+ <Button type="submit" className="!w-auto" loading={creatingRequest} disabled={creatingRequest}>
592
+ Create request
593
+ </Button>
594
+ </div>
595
+ </form>
596
+ </Modal>
597
+
598
+ <Modal open={materialFor !== null} onClose={closeMaterialModal} title="Escrow key material">
599
+ {/* Deliberately raw JSON, not a decrypt/viewer UI: this console never attempts to decrypt
600
+ anything, per specs/end-to-end_encryption.md's "this server never holds the scope's
601
+ private key" - the holder is expected to copy this into their own offline tooling. See
602
+ this session's own report for this documented scope trim. */}
603
+ {loadingMaterial ? (
604
+ <p className="text-sm text-text-muted">Loading&hellip;</p>
605
+ ) : materialError ? (
606
+ <Alert>{materialError}</Alert>
607
+ ) : (
608
+ <>
609
+ <p className="text-sm text-text-muted mb-3">
610
+ Still-encrypted master key wraps for <strong>{materialFor?.mailboxUid}</strong> — this
611
+ server cannot decrypt these either. Copy the JSON below into your own offline tooling.
612
+ </p>
613
+ <pre className="text-xs font-mono bg-surface-alt border border-border rounded-sm p-3 overflow-x-auto whitespace-pre-wrap break-all">
614
+ {materialText}
615
+ </pre>
616
+ </>
617
+ )}
618
+ </Modal>
619
+ </div>
620
+ );
621
+ }