@rapidmx/web-client 0.5.0 → 0.7.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 (224) hide show
  1. package/README.md +88 -0
  2. package/apps/shared/components/admin/layout/AdminShell.tsx +25 -8
  3. package/apps/shared/components/admin/settings/BrandingForm.tsx +398 -385
  4. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +119 -112
  5. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +159 -152
  6. package/apps/shared/components/admin/settings/PluginsManager.tsx +1288 -1235
  7. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +9 -1
  8. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +288 -290
  9. package/apps/shared/components/admin/setup/SetupWizard.tsx +443 -416
  10. package/apps/shared/components/calendar/layout/CalendarShell.tsx +194 -192
  11. package/apps/shared/components/contacts/layout/ContactsShell.tsx +199 -197
  12. package/apps/shared/components/layout/AppShell.tsx +288 -270
  13. package/apps/shared/components/mail/ConversationList.tsx +225 -33
  14. package/apps/shared/components/mail/ConversationThreadPane.tsx +434 -216
  15. package/apps/shared/components/mail/MailListToolbar.tsx +265 -0
  16. package/apps/shared/components/mail/MailSelectionBar.tsx +222 -0
  17. package/apps/shared/components/mail/MenuButton.tsx +384 -0
  18. package/apps/shared/components/mail/MessageDetailPane.tsx +280 -215
  19. package/apps/shared/components/mail/MoveToFolderDialog.tsx +263 -0
  20. package/apps/shared/components/mail/compose/ComposeContext.tsx +29 -2
  21. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1700 -1652
  22. package/apps/shared/components/mail/compose/RecipientInput.tsx +356 -0
  23. package/apps/shared/components/mail/compose/RichTextEditor.tsx +38 -3
  24. package/apps/shared/components/mail/compose/quotedBody.ts +100 -0
  25. package/apps/shared/components/mail/compose/recipients.ts +104 -0
  26. package/apps/shared/components/mail/labelMenu.tsx +278 -0
  27. package/apps/shared/components/mail/layout/MailShell.tsx +31 -4
  28. package/apps/shared/components/mail/listPreferences.ts +228 -0
  29. package/apps/shared/components/settings/layout/SettingsShell.tsx +23 -10
  30. package/apps/shared/components/tasks/layout/TasksShell.tsx +201 -199
  31. package/apps/shared/plugins/pluginNav.ts +73 -0
  32. package/apps/shared/search/localIndexRpcClient.ts +3 -1
  33. package/apps/shared/styles/app.css +28 -5
  34. package/apps/www/index.tsx +1854 -1314
  35. package/apps/www/messages/[uid].tsx +5 -4
  36. package/dist/apps/admin/_404.d.ts +2 -0
  37. package/dist/apps/admin/_500.d.ts +2 -0
  38. package/dist/apps/admin/_layout.d.ts +8 -0
  39. package/dist/apps/admin/audit-log/index.d.ts +11 -0
  40. package/dist/apps/admin/branding/index.d.ts +3 -0
  41. package/dist/apps/admin/data-requests/index.d.ts +3 -0
  42. package/dist/apps/admin/distribution-lists/[uid].d.ts +7 -0
  43. package/dist/apps/admin/distribution-lists/index.d.ts +3 -0
  44. package/dist/apps/admin/distribution-lists/new/index.d.ts +3 -0
  45. package/dist/apps/admin/domains/[uid].d.ts +7 -0
  46. package/dist/apps/admin/domains/index.d.ts +3 -0
  47. package/dist/apps/admin/domains/new/index.d.ts +3 -0
  48. package/dist/apps/admin/encryption-policy/index.d.ts +3 -0
  49. package/dist/apps/admin/escrow-scopes/[uid].d.ts +21 -0
  50. package/dist/apps/admin/escrow-scopes/index.d.ts +3 -0
  51. package/dist/apps/admin/escrow-scopes/new/index.d.ts +3 -0
  52. package/dist/apps/admin/index.d.ts +3 -0
  53. package/dist/apps/admin/ingest-queue/index.d.ts +4 -0
  54. package/dist/apps/admin/mailbox-policy/index.d.ts +3 -0
  55. package/dist/apps/admin/mailboxes/[uid].d.ts +7 -0
  56. package/dist/apps/admin/mailboxes/new/index.d.ts +3 -0
  57. package/dist/apps/admin/plugins/index.d.ts +3 -0
  58. package/dist/apps/admin/quarantine/index.d.ts +6 -0
  59. package/dist/apps/admin/retention-policy/index.d.ts +3 -0
  60. package/dist/apps/admin/setup/index.d.ts +3 -0
  61. package/dist/apps/admin/transport-rules/[uid].d.ts +7 -0
  62. package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +21 -0
  63. package/dist/apps/admin/transport-rules/index.d.ts +3 -0
  64. package/dist/apps/admin/transport-rules/new/index.d.ts +3 -0
  65. package/dist/apps/escrow/_layout.d.ts +10 -0
  66. package/dist/apps/escrow/audit-log/index.d.ts +9 -0
  67. package/dist/apps/escrow/index.d.ts +3 -0
  68. package/dist/apps/escrow/matters/[uid].d.ts +7 -0
  69. package/dist/apps/escrow/matters/new/index.d.ts +3 -0
  70. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +17 -0
  71. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.d.ts +32 -0
  72. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +32 -0
  73. package/dist/apps/shared/components/admin/layout/AdminShell.js +12 -5
  74. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +12 -0
  75. package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +6 -0
  76. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +19 -0
  77. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.d.ts +12 -0
  78. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +13 -0
  79. package/dist/apps/shared/components/admin/settings/BrandingForm.js +6 -5
  80. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +12 -0
  81. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +15 -0
  82. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +2 -2
  83. package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.d.ts +9 -0
  84. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +19 -0
  85. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +13 -0
  86. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +2 -2
  87. package/dist/apps/shared/components/admin/settings/PluginsManager.d.ts +11 -0
  88. package/dist/apps/shared/components/admin/settings/PluginsManager.js +33 -14
  89. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +12 -0
  90. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +2 -2
  91. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.d.ts +14 -0
  92. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +1 -1
  93. package/dist/apps/shared/components/admin/setup/SetupWizard.d.ts +36 -0
  94. package/dist/apps/shared/components/admin/setup/SetupWizard.js +14 -10
  95. package/dist/apps/shared/components/admin/signOut.d.ts +23 -0
  96. package/dist/apps/shared/components/admin/usePagedList.d.ts +39 -0
  97. package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +26 -0
  98. package/dist/apps/shared/components/calendar/EventModal.d.ts +46 -0
  99. package/dist/apps/shared/components/calendar/MonthView.d.ts +15 -0
  100. package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +20 -0
  101. package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +19 -0
  102. package/dist/apps/shared/components/calendar/SplitDayView.d.ts +28 -0
  103. package/dist/apps/shared/components/calendar/TimeGridView.d.ts +17 -0
  104. package/dist/apps/shared/components/calendar/allDay.d.ts +51 -0
  105. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +40 -0
  106. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +2 -2
  107. package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +23 -0
  108. package/dist/apps/shared/components/contacts/ContactForm.d.ts +20 -0
  109. package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +40 -0
  110. package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +25 -0
  111. package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +40 -0
  112. package/dist/apps/shared/components/contacts/contactKeys.d.ts +29 -0
  113. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +21 -0
  114. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +2 -2
  115. package/dist/apps/shared/components/escrow/layout/EscrowShell.d.ts +25 -0
  116. package/dist/apps/shared/components/forms/StringListField.d.ts +22 -0
  117. package/dist/apps/shared/components/layout/AppShell.d.ts +49 -0
  118. package/dist/apps/shared/components/layout/AppShell.js +15 -13
  119. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +18 -0
  120. package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +51 -0
  121. package/dist/apps/shared/components/layout/MailboxProvisioning.d.ts +13 -0
  122. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +66 -0
  123. package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +45 -0
  124. package/dist/apps/shared/components/layout/UserMenu.d.ts +23 -0
  125. package/dist/apps/shared/components/mail/ConversationList.d.ts +47 -0
  126. package/dist/apps/shared/components/mail/ConversationList.js +84 -10
  127. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +62 -0
  128. package/dist/apps/shared/components/mail/ConversationThreadPane.js +217 -60
  129. package/dist/apps/shared/components/mail/MailListToolbar.d.ts +55 -0
  130. package/dist/apps/shared/components/mail/MailListToolbar.js +149 -0
  131. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +59 -0
  132. package/dist/apps/shared/components/mail/MailSelectionBar.js +48 -0
  133. package/dist/apps/shared/components/mail/MenuButton.d.ts +71 -0
  134. package/dist/apps/shared/components/mail/MenuButton.js +211 -0
  135. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +101 -0
  136. package/dist/apps/shared/components/mail/MessageDetailPane.js +126 -129
  137. package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +57 -0
  138. package/dist/apps/shared/components/mail/MoveToFolderDialog.js +138 -0
  139. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +73 -0
  140. package/dist/apps/shared/components/mail/compose/ComposeContext.js +3 -1
  141. package/dist/apps/shared/components/mail/compose/ComposeToolbar.d.ts +15 -0
  142. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +37 -0
  143. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +52 -20
  144. package/dist/apps/shared/components/mail/compose/EmojiPicker.d.ts +14 -0
  145. package/dist/apps/shared/components/mail/compose/GifPicker.d.ts +13 -0
  146. package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +36 -0
  147. package/dist/apps/shared/components/mail/compose/RecipientInput.js +203 -0
  148. package/dist/apps/shared/components/mail/compose/RichTextEditor.d.ts +43 -0
  149. package/dist/apps/shared/components/mail/compose/RichTextEditor.js +21 -3
  150. package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.d.ts +16 -0
  151. package/dist/apps/shared/components/mail/compose/composeFlushRegistry.d.ts +27 -0
  152. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +37 -0
  153. package/dist/apps/shared/components/mail/compose/quotedBody.js +85 -0
  154. package/dist/apps/shared/components/mail/compose/recipients.d.ts +24 -0
  155. package/dist/apps/shared/components/mail/compose/recipients.js +87 -0
  156. package/dist/apps/shared/components/mail/labelMenu.d.ts +97 -0
  157. package/dist/apps/shared/components/mail/labelMenu.js +134 -0
  158. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +65 -0
  159. package/dist/apps/shared/components/mail/layout/MailShell.js +17 -5
  160. package/dist/apps/shared/components/mail/listPreferences.d.ts +101 -0
  161. package/dist/apps/shared/components/mail/listPreferences.js +186 -0
  162. package/dist/apps/shared/components/mail/pinnedSigners.d.ts +33 -0
  163. package/dist/apps/shared/components/mail/verificationSeals.d.ts +22 -0
  164. package/dist/apps/shared/components/mail/writableMailboxes.d.ts +15 -0
  165. package/dist/apps/shared/components/rules/RuleBuilder.d.ts +70 -0
  166. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +41 -0
  167. package/dist/apps/shared/components/settings/layout/SettingsShell.js +19 -10
  168. package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +41 -0
  169. package/dist/apps/shared/components/tasks/TasksToolbar.d.ts +14 -0
  170. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +24 -0
  171. package/dist/apps/shared/components/tasks/layout/TasksShell.js +2 -2
  172. package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -0
  173. package/dist/apps/shared/mail/listAllPages.d.ts +17 -0
  174. package/dist/apps/shared/plugins/pluginNav.d.ts +39 -0
  175. package/dist/apps/shared/plugins/pluginNav.js +33 -0
  176. package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +13 -0
  177. package/dist/apps/shared/search/localIndexBlockCipher.d.ts +36 -0
  178. package/dist/apps/shared/search/localIndexBuilder.d.ts +85 -0
  179. package/dist/apps/shared/search/localIndexKey.d.ts +4 -0
  180. package/dist/apps/shared/search/localIndexRpcClient.d.ts +71 -0
  181. package/dist/apps/shared/search/localIndexRpcClient.js +3 -1
  182. package/dist/apps/shared/search/localIndexSchema.d.ts +111 -0
  183. package/dist/apps/shared/search/localIndexSizePreference.d.ts +27 -0
  184. package/dist/apps/shared/search/localIndexStorage.d.ts +28 -0
  185. package/dist/apps/shared/search/localIndexVFS.d.ts +101 -0
  186. package/dist/apps/shared/search/localIndexWorker.d.ts +145 -0
  187. package/dist/apps/shared/search/searchTier2.d.ts +30 -0
  188. package/dist/apps/shared/styles/app.css +28 -5
  189. package/dist/apps/www/_404.d.ts +2 -0
  190. package/dist/apps/www/_500.d.ts +2 -0
  191. package/dist/apps/www/_layout.d.ts +14 -0
  192. package/dist/apps/www/calendar/index.d.ts +3 -0
  193. package/dist/apps/www/contacts/[uid].d.ts +13 -0
  194. package/dist/apps/www/contacts/index.d.ts +3 -0
  195. package/dist/apps/www/index.d.ts +3 -0
  196. package/dist/apps/www/index.js +506 -112
  197. package/dist/apps/www/messages/[uid].d.ts +11 -0
  198. package/dist/apps/www/messages/[uid].js +2 -3
  199. package/dist/apps/www/settings/auto-reply/index.d.ts +4 -0
  200. package/dist/apps/www/settings/encryption/index.d.ts +7 -0
  201. package/dist/apps/www/settings/filters/[uid].d.ts +8 -0
  202. package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +11 -0
  203. package/dist/apps/www/settings/filters/index.d.ts +4 -0
  204. package/dist/apps/www/settings/filters/new/index.d.ts +4 -0
  205. package/dist/apps/www/settings/labels/index.d.ts +4 -0
  206. package/dist/apps/www/settings/privacy/index.d.ts +4 -0
  207. package/dist/apps/www/settings/read-receipts/index.d.ts +4 -0
  208. package/dist/apps/www/settings/sharing/index.d.ts +4 -0
  209. package/dist/apps/www/settings/signatures/[uid].d.ts +8 -0
  210. package/dist/apps/www/settings/signatures/index.d.ts +4 -0
  211. package/dist/apps/www/settings/signatures/new/index.d.ts +4 -0
  212. package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +10 -0
  213. package/dist/apps/www/tasks/index.d.ts +3 -0
  214. package/package.json +3 -3
  215. package/apps/shared/components/booking/AvailabilityEditor.tsx +0 -118
  216. package/apps/www/settings/booking-types/[uid].tsx +0 -306
  217. package/apps/www/settings/booking-types/index.tsx +0 -98
  218. package/apps/www/settings/booking-types/new/index.tsx +0 -212
  219. package/apps/www/settings/focused-inbox/index.tsx +0 -150
  220. package/dist/apps/shared/components/booking/AvailabilityEditor.js +0 -45
  221. package/dist/apps/www/settings/booking-types/[uid].js +0 -134
  222. package/dist/apps/www/settings/booking-types/index.js +0 -29
  223. package/dist/apps/www/settings/booking-types/new/index.js +0 -86
  224. package/dist/apps/www/settings/focused-inbox/index.js +0 -71
@@ -24,7 +24,7 @@ export default function MessageDetailPage(props: MailShellProps & { params: { ui
24
24
  }
25
25
 
26
26
  function MessageDetailContent({ uid }: { uid: string }) {
27
- const { mailboxFolders, mailboxUid } = useMailShell();
27
+ const { mailboxFolders, mailboxUid, onFolderCreated } = useMailShell();
28
28
  const [message, setMessage] = useState<Message | null>(null);
29
29
  const [loading, setLoading] = useState(true);
30
30
  const [error, setError] = useState<string | null>(null);
@@ -77,7 +77,6 @@ function MessageDetailContent({ uid }: { uid: string }) {
77
77
  const backHref = `/?mailboxUid=${encodeURIComponent(message.mailboxUid)}&folderUid=${encodeURIComponent(message.folderUid)}`;
78
78
  const isSentItems = folders.find((f) => f.uid === message.folderUid)?.type === "sent_items";
79
79
  const isOutbox = folders.find((f) => f.uid === message.folderUid)?.type === "outbox";
80
- const isInbox = folders.find((f) => f.uid === message.folderUid)?.type === "inbox";
81
80
  const draftsFolderUid = folders.find((f) => f.type === "drafts")?.uid;
82
81
  return (
83
82
  <MessageDetailPane
@@ -87,14 +86,16 @@ function MessageDetailContent({ uid }: { uid: string }) {
87
86
  isSentItems={isSentItems}
88
87
  onRecalled={setMessage}
89
88
  isOutbox={isOutbox}
90
- isInbox={isInbox}
91
- onClassified={setMessage}
92
89
  onReceiptHandled={setMessage}
93
90
  draftsFolderUid={draftsFolderUid}
91
+ folders={folders}
92
+ onMoved={setMessage}
93
+ onFolderCreated={onFolderCreated}
94
94
  onScheduledSendCanceled={setMessage}
95
95
  onArchived={setMessage}
96
96
  labels={labels}
97
97
  onLabelsChanged={setMessage}
98
+ onLabelCreated={(label) => setLabels((prev) => [...prev, label])}
98
99
  />
99
100
  );
100
101
  }
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export default function NotFoundPage(): React.JSX.Element;
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export default function ErrorPage(): React.JSX.Element;
@@ -0,0 +1,8 @@
1
+ import React, { PropsWithChildren } from "react";
2
+ import { Branding } from "@rapidmx/react-shared/branding/brandingApi.js";
3
+ export interface LayoutProps {
4
+ /** See `apps/www/_layout.tsx`'s `LayoutProps` doc comment - identical mechanism, supplied by
5
+ * `AdminConsoleRoute`'s `fetchProps()` override. */
6
+ branding?: Branding;
7
+ }
8
+ export default function Layout({ children, branding }: PropsWithChildren<LayoutProps>): React.JSX.Element;
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { AuditLogFilters } from "@rapidmx/react-shared/admin/auditLogApi.js";
3
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
4
+ /** How long typing in a filter box has to pause before the log is re-fetched. */
5
+ export declare const FILTER_DEBOUNCE_MS = 300;
6
+ /** Seeds the filter form from the query string on first render — lets a link (e.g. a future "View audit
7
+ * log" deep link from a mailbox's detail page) land here pre-filtered, same convention as `quarantine`'s
8
+ * `readMailboxUid()`. Filters are also written back to the URL as they change, so the filtered view
9
+ * itself is shareable/bookmarkable. */
10
+ export declare function readFiltersFromUrl(): AuditLogFilters;
11
+ export default function AuditLogPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function BrandingPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function DataRequestsPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function DistributionListDetailPage(props: Omit<AdminShellProps, "active"> & {
4
+ params: {
5
+ uid: string;
6
+ };
7
+ }): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function DistributionListsPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../../shared/components/admin/layout/AdminShell.js";
3
+ export default function NewDistributionListPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function DomainDetailPage(props: Omit<AdminShellProps, "active"> & {
4
+ params: {
5
+ uid: string;
6
+ };
7
+ }): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function DomainsListPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../../shared/components/admin/layout/AdminShell.js";
3
+ export default function NewDomainPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function EncryptionPolicyPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { EscrowScope, EscrowScopePublicKey } from "@rapidmx/react-shared/admin/escrowScopesApi.js";
3
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
4
+ import { EscrowScopeKeyAndHoldersValue } from "../../shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js";
5
+ /**
6
+ * Describes every change to the security-relevant parts of a scope - who holds it, how many holders must approve,
7
+ * and its public key - so the administrator confirms exactly what they're changing before it's saved.
8
+ */
9
+ export declare function describeSecurityChanges(original: EscrowScope, next: EscrowScopeKeyAndHoldersValue): string[];
10
+ /**
11
+ * The `publicKey` to send with an update, or `undefined` when the admin didn't touch any key field - the form only
12
+ * shows dates to the minute, so re-sending a rebuilt key would silently truncate the stored validity window. A date
13
+ * field left as loaded keeps its original epoch-ms value. `revokedAt` isn't editable here: it's carried over when
14
+ * only the validity window changed, and dropped when the key material itself is replaced (a new key isn't revoked).
15
+ */
16
+ export declare function buildPublicKeyUpdate(original: EscrowScope, next: EscrowScopeKeyAndHoldersValue): EscrowScopePublicKey | undefined;
17
+ export default function EscrowScopeDetailPage(props: Omit<AdminShellProps, "active"> & {
18
+ params: {
19
+ uid: string;
20
+ };
21
+ }): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function EscrowScopesPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../../shared/components/admin/layout/AdminShell.js";
3
+ export default function NewEscrowScopePage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../shared/components/admin/layout/AdminShell.js";
3
+ export default function MailboxesListPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export declare function readMailboxUid(): string | null;
4
+ export default function IngestQueuePage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function MailboxPolicyPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function MailboxDetailPage(props: Omit<AdminShellProps, "active"> & {
4
+ params: {
5
+ uid: string;
6
+ };
7
+ }): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../../shared/components/admin/layout/AdminShell.js";
3
+ export default function NewMailboxPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function PluginsPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export declare function readMailboxUid(): string | null;
4
+ export default function QuarantinePage(props: Omit<AdminShellProps, "active"> & {
5
+ userUid?: string;
6
+ }): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function RetentionPolicyPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function SetupPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function TransportRuleDetailPage(props: Omit<AdminShellProps, "active"> & {
4
+ params: {
5
+ uid: string;
6
+ };
7
+ }): React.JSX.Element;
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { TransportRuleAction, TransportRuleConditions } from "@rapidmx/react-shared/admin/transportRulesApi.js";
3
+ import { ActionTypeDef, ConditionFieldDef } from "../../shared/components/rules/RuleBuilder.js";
4
+ /** Refused outright when a rule has no conditions: it would reject or quarantine every message the server receives. */
5
+ export declare const NO_CONDITIONS_BLOCKED_MESSAGE = "Add at least one condition. A rule without conditions that rejects or quarantines mail would do so for every message.";
6
+ /**
7
+ * How a save should proceed given a rule's conditions: `"ok"` with at least one condition, `"blocked"` with none and a
8
+ * reject/quarantine action, or `"confirm"` with none and only other actions (which then apply to every message).
9
+ */
10
+ export declare function checkRuleConditions(rule: {
11
+ conditions: TransportRuleConditions;
12
+ actions: TransportRuleAction[];
13
+ }): "ok" | "blocked" | "confirm";
14
+ /** Asks before saving a rule with no conditions whose actions will run on every message. */
15
+ export declare function NoConditionsConfirmModal({ open, onCancel, onConfirm }: {
16
+ open: boolean;
17
+ onCancel: () => void;
18
+ onConfirm: () => void;
19
+ }): React.JSX.Element;
20
+ export declare const TRANSPORT_RULE_CONDITION_FIELDS: ConditionFieldDef[];
21
+ export declare const TRANSPORT_RULE_ACTION_TYPES: ActionTypeDef<TransportRuleAction>[];
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
3
+ export default function TransportRulesPage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { AdminShellProps } from "../../../shared/components/admin/layout/AdminShell.js";
3
+ export default function NewTransportRulePage(props: Omit<AdminShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,10 @@
1
+ import React, { PropsWithChildren } from "react";
2
+ import { Branding } from "@rapidmx/react-shared/branding/brandingApi.js";
3
+ export interface LayoutProps {
4
+ /** See `apps/www/_layout.tsx`'s `LayoutProps` doc comment — identical mechanism, supplied by
5
+ * `EscrowConsoleRoute`'s `fetchProps()`. */
6
+ branding?: Branding;
7
+ }
8
+ /** Unlike the other apps, never links the admin-configured custom stylesheet: the escrow console handles escrow
9
+ * key material, so no admin-supplied CSS is loaded on it. */
10
+ export default function Layout({ children, branding }: PropsWithChildren<LayoutProps>): React.JSX.Element;
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import { EscrowAuditVerificationResult } from "@rapidmx/react-shared/admin/escrowAuditLogApi.js";
3
+ import { EscrowShellProps } from "../../shared/components/escrow/layout/EscrowShell.js";
4
+ /** The label for an audit action - the raw action name for one this console doesn't know yet (a newer server). */
5
+ export declare function actionLabel(action: string): string;
6
+ /** Explains a failed chain verification by its `reason` (plus where it broke, when known). An unrecognized or missing
7
+ * reason (a newer or older server) reads as a generic failure. */
8
+ export declare function describeVerificationFailure(result: EscrowAuditVerificationResult): string;
9
+ export default function EscrowAuditLogPage(props: Omit<EscrowShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { EscrowShellProps } from "../shared/components/escrow/layout/EscrowShell.js";
3
+ export default function MattersPage(props: Omit<EscrowShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { EscrowShellProps } from "../../shared/components/escrow/layout/EscrowShell.js";
3
+ export default function MatterDetailPage(props: Omit<EscrowShellProps, "active"> & {
4
+ params: {
5
+ uid: string;
6
+ };
7
+ }): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { EscrowShellProps } from "../../../shared/components/escrow/layout/EscrowShell.js";
3
+ export default function NewMatterPage(props: Omit<EscrowShellProps, "active">): React.JSX.Element;
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import { DistributionList } from "@rapidmx/react-shared/admin/distributionListsApi.js";
3
+ export interface MemberListCardProps {
4
+ list: DistributionList;
5
+ /** Called with the freshly-saved list (new `version`, updated `memberAddresses`) after a successful
6
+ * add/remove, so the parent detail page can keep its own copy of the list in sync — mirrors
7
+ * `ShareAccessCard`'s `reload()` pattern, just pushed up rather than re-fetched, since the caller
8
+ * already has everything the response would return. */
9
+ onUpdate: (list: DistributionList) => void;
10
+ }
11
+ /**
12
+ * Lets an admin view and edit a distribution list's members — same load/inline-add-row/per-row-remove
13
+ * interaction shape as `ShareAccessCard`, but against a plain `memberAddresses: string[]` field on the
14
+ * `DistributionList` itself via read-modify-write `PUT`, not a separate ACL endpoint (a distribution
15
+ * list has no per-record ACL to speak of — see `BaseDistributionListRoute`'s own doc comment).
16
+ */
17
+ export default function MemberListCard({ list, onUpdate }: MemberListCardProps): React.JSX.Element;
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ /** The part of an `EscrowScope` shared by the "new" and "[uid]" pages — everything except `name`/
3
+ * `description`, which stay plain inline `FormField`s on each page (same split `distribution-lists`
4
+ * already uses: simple top-level fields inline, the more involved sub-structure factored out). An admin
5
+ * pastes in the fields of an already-issued certificate here; nothing in this app generates a keypair —
6
+ * see `EscrowScopePublicKey`'s own doc comment in `escrowScopesApi.ts`. */
7
+ export interface EscrowScopeKeyAndHoldersValue {
8
+ publicKey: string;
9
+ keyType: string;
10
+ fingerprint: string;
11
+ /** `<input type="datetime-local">` value — see `toDatetimeLocal()`. */
12
+ notBefore: string;
13
+ notAfter: string;
14
+ holderUserUids: string[];
15
+ requiredHolders: number;
16
+ notifySubjectOnAccess: boolean;
17
+ }
18
+ export interface EscrowScopeKeyAndHoldersFieldsProps {
19
+ value: EscrowScopeKeyAndHoldersValue;
20
+ onChange: (value: EscrowScopeKeyAndHoldersValue) => void;
21
+ disabled?: boolean;
22
+ /** Shows the public key fields without letting them be edited, e.g. for keys generated in the browser. */
23
+ keyReadOnly?: boolean;
24
+ }
25
+ /** Builds an all-zeroes starting point for the "new" page — `notBefore`/`notAfter` default to "now" so an
26
+ * admin pasting in a cert's own dates only has to touch what actually differs. */
27
+ export declare function emptyEscrowScopeKeyAndHoldersValue(): EscrowScopeKeyAndHoldersValue;
28
+ /** The message shown when an administrator tries to make themselves a holder: whoever configures a scope must not
29
+ * also be able to approve access under it (separation of duties - the server refuses it too). Holders who were
30
+ * already on the scope (`existingHolderUserUids`) aren't treated as newly added. */
31
+ export declare function selfAsHolderError(holderUserUids: string[], adminUid: string | undefined, existingHolderUserUids?: string[]): string | null;
32
+ export default function EscrowScopeKeyAndHoldersFields({ value, onChange, disabled, keyReadOnly }: EscrowScopeKeyAndHoldersFieldsProps): React.JSX.Element;
@@ -0,0 +1,32 @@
1
+ import "../../../styles/app.css";
2
+ import React, { PropsWithChildren } from "react";
3
+ import { NavItem } from "@rapidmx/react-shared/components/navigation/BottomTabBar.js";
4
+ import { PluginNav, PluginNavProps } from "../../../plugins/pluginNav.js";
5
+ export type AdminSection = "mailboxes" | "quarantine" | "ingestQueue" | "domains" | "auditLog" | "distributionLists" | "transportRules" | "escrowScopes" | "retentionPolicy" | "encryptionPolicy" | "mailboxPolicy" | "setup" | "dataRequests" | "plugins" | "branding";
6
+ /** A core `AdminSection`, or a plugin's `adminNav` item id (see `PluginNav`). */
7
+ export type AdminShellActive = AdminSection | (string & {});
8
+ export interface AdminShellProps extends PluginNavProps {
9
+ /** Which icon in the rail is highlighted as the current section - a plugin admin page passes its own
10
+ * `adminNav` item id. */
11
+ active: AdminShellActive;
12
+ /** Populated automatically by the framework from an authenticated request (e.g. a valid `jwt` cookie). */
13
+ userUid?: string;
14
+ /** auth-server's base URL, injected via the route's `fetchProps` — see `src/mongo/routes/AdminConsoleRoute.ts`. */
15
+ authServerUrl?: string;
16
+ /**
17
+ * Where `mailApi.ts`'s `impersonateUser()`/`stopImpersonating()` should call: the real auth-server in
18
+ * production, or `""` under `yarn dev` to call this app's own local dev-only impersonation endpoint
19
+ * instead — see `AdminConsoleRoute`'s `fetchProps` and `src/dev/DevImpersonationRoute.ts`.
20
+ */
21
+ impersonationBaseUrl?: string;
22
+ }
23
+ /** `NAV_ITEMS` followed by the plugins' `adminNav` items (generic icon). Core ids win, including the
24
+ * off-rail sections' - see `mergePluginNavItems`. */
25
+ export declare function adminNavItems(pluginNav?: PluginNav): NavItem[];
26
+ /**
27
+ * Gates every `apps/admin` page behind the `admin` trusted role. Uses `GET /api/admin/release-notes` (any
28
+ * `BaseAdminRoute` endpoint works — this one is side-effect-free) purely as a canary: a 200 means the
29
+ * caller's JWT carries a trusted role, a 403 means it doesn't. There is no local step-up/elevation flow
30
+ * (that would need a cross-origin call to auth-server's own elevation endpoint — not wired up yet).
31
+ */
32
+ export default function AdminShell({ active, userUid, authServerUrl, pluginNav, children }: PropsWithChildren<AdminShellProps>): React.JSX.Element;
@@ -15,6 +15,7 @@ import BottomTabBar from "@rapidmx/react-shared/components/navigation/BottomTabB
15
15
  import { BrandingFooter, BrandingHeader } from "../../layout/BrandingChrome.js";
16
16
  import UserMenu from "../../layout/UserMenu.js";
17
17
  import { signOutOfConsole } from "../signOut.js";
18
+ import { mergePluginNavItems } from "../../../plugins/pluginNav.js";
18
19
  /** Sections shown in the persistent icon rail / mobile tab bar — every admin area reachable from
19
20
  * anywhere in the console. Deliberately excludes `quarantine`/`ingestQueue`: those are scoped to a
20
21
  * single mailbox and only ever reached via links on that mailbox's own detail page (see
@@ -76,14 +77,19 @@ const MAILBOX_SCOPED_ITEMS = [
76
77
  ];
77
78
  /** The setup wizard - reached by redirect or from the Mailboxes page, not from the rail. */
78
79
  const SETUP_ITEM = { id: "setup", href: "/admin/setup", label: "Setup", icon: HiOutlineRocketLaunch };
79
- const ALL_ITEMS = [...NAV_ITEMS, ...MAILBOX_SCOPED_ITEMS, SETUP_ITEM];
80
+ const OFF_RAIL_ITEMS = [...MAILBOX_SCOPED_ITEMS, SETUP_ITEM];
81
+ /** `NAV_ITEMS` followed by the plugins' `adminNav` items (generic icon). Core ids win, including the
82
+ * off-rail sections' - see `mergePluginNavItems`. */
83
+ export function adminNavItems(pluginNav) {
84
+ return mergePluginNavItems(NAV_ITEMS, pluginNav?.adminNav, ({ id, href, label }) => ({ id, href, label, icon: HiOutlinePuzzlePiece }), OFF_RAIL_ITEMS.map((item) => item.id));
85
+ }
80
86
  /**
81
87
  * Gates every `apps/admin` page behind the `admin` trusted role. Uses `GET /api/admin/release-notes` (any
82
88
  * `BaseAdminRoute` endpoint works — this one is side-effect-free) purely as a canary: a 200 means the
83
89
  * caller's JWT carries a trusted role, a 403 means it doesn't. There is no local step-up/elevation flow
84
90
  * (that would need a cross-origin call to auth-server's own elevation endpoint — not wired up yet).
85
91
  */
86
- export default function AdminShell({ active, userUid, authServerUrl, children }) {
92
+ export default function AdminShell({ active, userUid, authServerUrl, pluginNav, children }) {
87
93
  const [status, setStatus] = useState("checking");
88
94
  const [error, setError] = useState(null);
89
95
  const { branding, iconSrc } = useBranding();
@@ -133,13 +139,14 @@ export default function AdminShell({ active, userUid, authServerUrl, children })
133
139
  content = (_jsx("div", { className: "min-h-screen flex items-center justify-center p-8", children: _jsx("div", { className: "w-full max-w-md", children: _jsx(Alert, { children: error }) }) }));
134
140
  }
135
141
  else {
136
- const activeItem = ALL_ITEMS.find((item) => item.id === active);
137
- content = (_jsx("div", { className: "min-h-screen flex flex-col bg-surface-alt", children: _jsxs("div", { className: "flex-1 flex min-h-0", children: [_jsxs("nav", { "aria-label": "Admin sections", className: "hidden md:flex w-16 shrink-0 bg-surface border-r border-border flex-col items-center py-3 gap-1", children: [_jsx("img", { src: iconSrc, width: "96", height: "96", alt: "", className: "mb-3" }), NAV_ITEMS.map(({ id, href, label, icon: Icon }) => (_jsx("a", { href: href, "aria-label": label, "aria-current": id === active ? "page" : undefined, title: label, className: [
142
+ const navItems = adminNavItems(pluginNav);
143
+ const activeItem = [...navItems, ...OFF_RAIL_ITEMS].find((item) => item.id === active);
144
+ content = (_jsx("div", { className: "min-h-screen flex flex-col bg-surface-alt", children: _jsxs("div", { className: "flex-1 flex min-h-0", children: [_jsxs("nav", { "aria-label": "Admin sections", className: "hidden md:flex w-16 shrink-0 bg-surface border-r border-border flex-col items-center py-3 gap-1", children: [_jsx("img", { src: iconSrc, width: "96", height: "96", alt: "", className: "mb-3" }), navItems.map(({ id, href, label, icon: Icon }) => (_jsx("a", { href: href, "aria-label": label, "aria-current": id === active ? "page" : undefined, title: label, className: [
138
145
  "w-10 h-10 flex items-center justify-center rounded-sm",
139
146
  id === active
140
147
  ? "bg-primary/10 text-primary-dark"
141
148
  : "text-text-muted hover:bg-surface-alt hover:text-text",
142
- ].join(" "), children: _jsx(Icon, { size: 20, "aria-hidden": "true" }) }, id)))] }), _jsx(BottomTabBar, { apps: NAV_ITEMS, active: active }), _jsxs("div", { className: "flex-1 flex flex-col min-w-0", children: [_jsxs("header", { className: "h-16 shrink-0 bg-surface border-b border-border flex items-center justify-between gap-4 px-6", children: [_jsx("span", { className: "font-display font-bold text-lg uppercase tracking-wide", children: activeItem?.label }), _jsx(UserMenu, { userUid: userUid, authServerUrl: authServerUrl, onSignOut: handleSignOut })] }), _jsx("div", { className: "flex-1 pb-14 md:pb-0", children: _jsx("main", { className: "max-w-6xl mx-auto px-6 py-8", children: children }) })] })] }) }));
149
+ ].join(" "), children: _jsx(Icon, { size: 20, "aria-hidden": "true" }) }, id)))] }), _jsx(BottomTabBar, { apps: navItems, active: active }), _jsxs("div", { className: "flex-1 flex flex-col min-w-0", children: [_jsxs("header", { className: "h-16 shrink-0 bg-surface border-b border-border flex items-center justify-between gap-4 px-6", children: [_jsx("span", { className: "font-display font-bold text-lg uppercase tracking-wide", children: activeItem?.label }), _jsx(UserMenu, { userUid: userUid, authServerUrl: authServerUrl, onSignOut: handleSignOut })] }), _jsx("div", { className: "flex-1 pb-14 md:pb-0", children: _jsx("main", { className: "max-w-6xl mx-auto px-6 py-8", children: children }) })] })] }) }));
143
150
  }
144
151
  return (_jsxs(_Fragment, { children: [_jsx(BrandingHeader, { branding: branding }), content, _jsx(BrandingFooter, { branding: branding })] }));
145
152
  }
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
3
+ export interface EscrowScopeCardProps {
4
+ mailbox: Mailbox;
5
+ /** Called with the saved mailbox, same push-up pattern as `ResourceSettingsCard`. */
6
+ onUpdate: (mailbox: Mailbox) => void;
7
+ }
8
+ /**
9
+ * Assigns a mailbox to an escrow scope (or none), which decides whose holders can recover its encrypted mail. Only
10
+ * shown in the admin console, whose trusted-role gate the server enforces again for `escrowScopeId`.
11
+ */
12
+ export default function EscrowScopeCard({ mailbox, onUpdate }: EscrowScopeCardProps): React.JSX.Element;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
3
+ export interface MailboxTableProps {
4
+ mailboxes: Mailbox[];
5
+ }
6
+ export default function MailboxTable({ mailboxes }: MailboxTableProps): React.JSX.Element;
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
3
+ export interface ResourceSettingsCardProps {
4
+ mailbox: Mailbox;
5
+ /** Called with the freshly-saved mailbox after a successful save — mirrors `MemberListCard`'s
6
+ * identical push-up-rather-than-reload pattern, since the caller already has everything the
7
+ * response would return. */
8
+ onUpdate: (mailbox: Mailbox) => void;
9
+ }
10
+ /**
11
+ * Lets an admin view and edit a resource mailbox's booking settings — same load/edit/PUT/reload
12
+ * interaction shape as `ShareAccessCard`, adapted the same way `MemberListCard` already adapted it:
13
+ * these seven fields live directly on the `Mailbox` the parent detail page already loaded (not a
14
+ * separate resource fetched by its own endpoint, unlike a mailbox's ACL), so this receives the
15
+ * already-loaded `mailbox` and pushes the `PUT` response back up rather than fetching independently.
16
+ * Only rendered by the detail page when `mailbox.isResource` is already `true` — a mailbox becomes a
17
+ * resource at creation time (`apps/admin/mailboxes/new`), not retroactively from here.
18
+ */
19
+ export default function ResourceSettingsCard({ mailbox, onUpdate }: ResourceSettingsCardProps): React.JSX.Element;
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ export interface ShareAccessCardProps {
3
+ mailboxUid: string;
4
+ /** The mailbox's owner - their own grant is shown but can't be revoked from here. */
5
+ ownerUserUid?: string;
6
+ }
7
+ /**
8
+ * Lets an admin (or, eventually, the mailbox owner) view and edit a mailbox's delegate access — the
9
+ * mechanism behind Exchange-style shared mailboxes. `records` on a mailbox's own ACL doubles as both the
10
+ * owner's grant (if any) and every delegate's — see `BaseMailboxRoute`'s doc comment in `@rapidmx/restapi`.
11
+ */
12
+ export default function ShareAccessCard({ mailboxUid, ownerUserUid }: ShareAccessCardProps): React.JSX.Element;
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import { Branding } from "@rapidmx/react-shared/branding/brandingApi.js";
3
+ /** SVG logo/icon uploads are refused (the server refuses them too): an SVG served from this origin can carry script. */
4
+ export declare function isSvgFile(file: File): boolean;
5
+ export interface BrandingFormProps {
6
+ branding: Branding;
7
+ onChange: (b: Branding) => void;
8
+ /** Set when shown in a page that already introduces branding (the setup wizard): the title and introduction are
9
+ * left out, and the section headings sit one level lower. */
10
+ embedded?: boolean;
11
+ }
12
+ /** The branding editor, shared by the Branding page and the setup wizard. */
13
+ export default function BrandingForm({ branding, onChange, embedded }: BrandingFormProps): React.JSX.Element;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  ///////////////////////////////////////////////////////////////////////////////
3
3
  // Copyright (C) 2026 Jean-Philippe Steinmetz
4
4
  // SPDX-License-Identifier: MPL-2.0
@@ -16,7 +16,8 @@ export function isSvgFile(file) {
16
16
  }
17
17
  const RASTER_IMAGE_ACCEPT = "image/png,image/jpeg,image/gif,image/webp,image/x-icon,image/vnd.microsoft.icon";
18
18
  /** The branding editor, shared by the Branding page and the setup wizard. */
19
- export default function BrandingForm({ branding, onChange }) {
19
+ export default function BrandingForm({ branding, onChange, embedded = false }) {
20
+ const SectionHeading = embedded ? "h3" : "h2";
20
21
  const [companyName, setCompanyName] = useState(branding.companyName);
21
22
  const [title, setTitle] = useState(branding.title);
22
23
  const [headerHtml, setHeaderHtml] = useState(branding.headerHtml ?? "");
@@ -103,13 +104,13 @@ export default function BrandingForm({ branding, onChange }) {
103
104
  setSaving(false);
104
105
  }
105
106
  }
106
- return (_jsxs("div", { className: "max-w-2xl", children: [_jsx("h1", { className: "text-xl font-bold uppercase tracking-wide mb-1", children: "Branding" }), _jsx("p", { className: "text-sm text-text-muted mb-5", children: "Customize the logo, nav-header icon, product name, and chrome shown to every visitor of the webmail and admin console \u2014 including anonymous booking-page visitors." }), error && _jsx(Alert, { children: error }), saved && !error && _jsx("div", { className: "mb-4 text-sm text-success font-medium", children: "Saved." }), _jsxs("div", { className: "flex flex-col gap-6", children: [_jsxs("section", { className: "flex flex-col gap-3", children: [_jsx("h2", { className: "text-sm font-bold uppercase tracking-wide text-text-muted", children: "Logo" }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("img", { src: branding.logoUrl || "/images/logo.svg", alt: "Current logo", width: "64", height: "64", className: "border border-border rounded-sm bg-surface-alt p-1" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", loading: assetBusy === "logo-upload", disabled: assetBusy !== null, onClick: () => logoFileRef.current?.click(), children: "Upload logo" }), _jsx("input", { ref: logoFileRef, type: "file", accept: RASTER_IMAGE_ACCEPT, "aria-label": "Upload logo", className: "hidden", onChange: handleLogoFileChange }), branding.logoUrl && (_jsx(Button, { type: "button", variant: "text", disabled: assetBusy !== null, loading: assetBusy === "logo-delete", onClick: () => void runAsset("logo-delete", async () => {
107
+ return (_jsxs("div", { className: "max-w-2xl", children: [!embedded && (_jsxs(_Fragment, { children: [_jsx("h1", { className: "text-xl font-bold uppercase tracking-wide mb-1", children: "Branding" }), _jsx("p", { className: "text-sm text-text-muted mb-5", children: "Customize the logo, nav-header icon, product name, and chrome shown to every visitor of the webmail and admin console \u2014 including anonymous booking-page visitors." })] })), error && _jsx(Alert, { children: error }), saved && !error && _jsx("div", { className: "mb-4 text-sm text-success font-medium", children: "Saved." }), _jsxs("div", { className: "flex flex-col gap-6", children: [_jsxs("section", { className: "flex flex-col gap-3", children: [_jsx(SectionHeading, { className: "text-sm font-bold uppercase tracking-wide text-text-muted", children: "Logo" }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("img", { src: branding.logoUrl || "/images/logo.svg", alt: "Current logo", width: "64", height: "64", className: "border border-border rounded-sm bg-surface-alt p-1" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", loading: assetBusy === "logo-upload", disabled: assetBusy !== null, onClick: () => logoFileRef.current?.click(), children: "Upload logo" }), _jsx("input", { ref: logoFileRef, type: "file", accept: RASTER_IMAGE_ACCEPT, "aria-label": "Upload logo", className: "hidden", onChange: handleLogoFileChange }), branding.logoUrl && (_jsx(Button, { type: "button", variant: "text", disabled: assetBusy !== null, loading: assetBusy === "logo-delete", onClick: () => void runAsset("logo-delete", async () => {
107
108
  await deleteBrandingLogo();
108
109
  return { ...branding, logoUrl: undefined };
109
- }), children: "Remove logo" }))] })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external image URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Logo URL", className: INPUT_CLASS, value: logoUrlInput, onChange: (e) => setLogoUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || logoUrlInput === (branding.logoUrl ?? ""), loading: assetBusy === "logo-url", onClick: () => void runAsset("logo-url", () => updateBranding({ logoUrl: logoUrlInput })), children: "Set" })] })] })] }), _jsxs("section", { className: "flex flex-col gap-3", children: [_jsx("h2", { className: "text-sm font-bold uppercase tracking-wide text-text-muted", children: "Icon" }), _jsx("p", { className: "text-xs text-text-muted -mt-1", children: "A compact mark for navigation headers, independent of the full logo above. Falls back to the logo, then a default asset, when not set." }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("img", { src: branding.iconUrl || branding.logoUrl || "/images/logo.svg", alt: "Current icon", width: "64", height: "64", className: "border border-border rounded-sm bg-surface-alt p-1" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", loading: assetBusy === "icon-upload", disabled: assetBusy !== null, onClick: () => iconFileRef.current?.click(), children: "Upload icon" }), _jsx("input", { ref: iconFileRef, type: "file", accept: RASTER_IMAGE_ACCEPT, "aria-label": "Upload icon", className: "hidden", onChange: handleIconFileChange }), branding.iconUrl && (_jsx(Button, { type: "button", variant: "text", disabled: assetBusy !== null, loading: assetBusy === "icon-delete", onClick: () => void runAsset("icon-delete", async () => {
110
+ }), children: "Remove logo" }))] })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external image URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Logo URL", className: INPUT_CLASS, value: logoUrlInput, onChange: (e) => setLogoUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || logoUrlInput === (branding.logoUrl ?? ""), loading: assetBusy === "logo-url", onClick: () => void runAsset("logo-url", () => updateBranding({ logoUrl: logoUrlInput })), children: "Set" })] })] })] }), _jsxs("section", { className: "flex flex-col gap-3", children: [_jsx(SectionHeading, { className: "text-sm font-bold uppercase tracking-wide text-text-muted", children: "Icon" }), _jsx("p", { className: "text-xs text-text-muted -mt-1", children: "A compact mark for navigation headers, independent of the full logo above. Falls back to the logo, then a default asset, when not set." }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("img", { src: branding.iconUrl || branding.logoUrl || "/images/logo.svg", alt: "Current icon", width: "64", height: "64", className: "border border-border rounded-sm bg-surface-alt p-1" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", loading: assetBusy === "icon-upload", disabled: assetBusy !== null, onClick: () => iconFileRef.current?.click(), children: "Upload icon" }), _jsx("input", { ref: iconFileRef, type: "file", accept: RASTER_IMAGE_ACCEPT, "aria-label": "Upload icon", className: "hidden", onChange: handleIconFileChange }), branding.iconUrl && (_jsx(Button, { type: "button", variant: "text", disabled: assetBusy !== null, loading: assetBusy === "icon-delete", onClick: () => void runAsset("icon-delete", async () => {
110
111
  await deleteBrandingIcon();
111
112
  return { ...branding, iconUrl: undefined };
112
- }), children: "Remove icon" }))] })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external image URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Icon URL", className: INPUT_CLASS, value: iconUrlInput, onChange: (e) => setIconUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || iconUrlInput === (branding.iconUrl ?? ""), loading: assetBusy === "icon-url", onClick: () => void runAsset("icon-url", () => updateBranding({ iconUrl: iconUrlInput })), children: "Set" })] })] })] }), _jsxs("section", { className: "flex flex-col gap-3", children: [_jsx("h2", { className: "text-sm font-bold uppercase tracking-wide text-text-muted", children: "Custom stylesheet" }), _jsx("div", { className: "flex items-center gap-3", children: _jsx("span", { className: "text-sm text-text-muted truncate", children: branding.stylesheetUrl || "None configured" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", loading: assetBusy === "stylesheet-upload", disabled: assetBusy !== null, onClick: () => stylesheetFileRef.current?.click(), children: "Upload CSS file" }), _jsx("input", { ref: stylesheetFileRef, type: "file", accept: "text/css,.css", "aria-label": "Upload stylesheet", className: "hidden", onChange: handleStylesheetFileChange }), branding.stylesheetUrl && (_jsx(Button, { type: "button", variant: "text", disabled: assetBusy !== null, loading: assetBusy === "stylesheet-delete", onClick: () => void runAsset("stylesheet-delete", async () => {
113
+ }), children: "Remove icon" }))] })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external image URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Icon URL", className: INPUT_CLASS, value: iconUrlInput, onChange: (e) => setIconUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || iconUrlInput === (branding.iconUrl ?? ""), loading: assetBusy === "icon-url", onClick: () => void runAsset("icon-url", () => updateBranding({ iconUrl: iconUrlInput })), children: "Set" })] })] })] }), _jsxs("section", { className: "flex flex-col gap-3", children: [_jsx(SectionHeading, { className: "text-sm font-bold uppercase tracking-wide text-text-muted", children: "Custom stylesheet" }), _jsx("div", { className: "flex items-center gap-3", children: _jsx("span", { className: "text-sm text-text-muted truncate", children: branding.stylesheetUrl || "None configured" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", loading: assetBusy === "stylesheet-upload", disabled: assetBusy !== null, onClick: () => stylesheetFileRef.current?.click(), children: "Upload CSS file" }), _jsx("input", { ref: stylesheetFileRef, type: "file", accept: "text/css,.css", "aria-label": "Upload stylesheet", className: "hidden", onChange: handleStylesheetFileChange }), branding.stylesheetUrl && (_jsx(Button, { type: "button", variant: "text", disabled: assetBusy !== null, loading: assetBusy === "stylesheet-delete", onClick: () => void runAsset("stylesheet-delete", async () => {
113
114
  await deleteBrandingStylesheet();
114
115
  return { ...branding, stylesheetUrl: undefined };
115
116
  }), children: "Remove stylesheet" }))] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external stylesheet URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Stylesheet URL", className: INPUT_CLASS, value: stylesheetUrlInput, onChange: (e) => setStylesheetUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || stylesheetUrlInput === (branding.stylesheetUrl ?? ""), loading: assetBusy === "stylesheet-url", onClick: () => void runAsset("stylesheet-url", () => updateBranding({ stylesheetUrl: stylesheetUrlInput })), children: "Set" })] })] })] }), _jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4 pt-2 border-t border-border", children: [_jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Company name" }), _jsx("input", { "aria-label": "Company name", className: INPUT_CLASS, value: companyName, onChange: (e) => setCompanyName(e.target.value) })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Product title" }), _jsx("input", { "aria-label": "Product title", className: INPUT_CLASS, value: title, onChange: (e) => setTitle(e.target.value) }), _jsx("span", { className: "text-xs text-text-muted", children: "Shown as the browser tab title." })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Header HTML (optional)" }), _jsx("textarea", { "aria-label": "Header HTML", className: `${INPUT_CLASS} font-mono`, rows: 3, value: headerHtml, onChange: (e) => setHeaderHtml(e.target.value) })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Footer HTML (optional)" }), _jsx("textarea", { "aria-label": "Footer HTML", className: `${INPUT_CLASS} font-mono`, rows: 3, value: footerHtml, onChange: (e) => setFooterHtml(e.target.value) })] }), _jsx("div", { children: _jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Save" }) })] })] })] }));
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { Domain } from "@rapidmx/react-shared/admin/domainsApi.js";
3
+ export interface DomainDnsSetupProps {
4
+ uid: string;
5
+ /** Called with the domain every time it's (re)loaded - e.g. after "Verify now". */
6
+ onLoaded?: (domain: Domain) => void;
7
+ }
8
+ /**
9
+ * A domain's status, its ownership TXT record with "Verify now", and the DNS setup checklist - shared by the domain
10
+ * detail page and the setup wizard.
11
+ */
12
+ export default function DomainDnsSetup({ uid, onLoaded }: DomainDnsSetupProps): React.JSX.Element;
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { EncryptionPolicy } from "@rapidmx/react-shared/crypto/keyvaultApi.js";
3
+ /** Whether end-to-end encryption can be used at all under `policy` - i.e. any tier allows it. */
4
+ export declare function isEncryptionEnabled(policy: EncryptionPolicy): boolean;
5
+ export interface EncryptionPolicyFormProps {
6
+ policy: EncryptionPolicy;
7
+ onChange: (policy: EncryptionPolicy) => void;
8
+ /** Told whether the form has edits that haven't been saved. */
9
+ onDirtyChange?: (dirty: boolean) => void;
10
+ /** Set when shown as one section of a page with its own heading (the setup wizard), so the title is a smaller,
11
+ * lower-level heading. */
12
+ embedded?: boolean;
13
+ }
14
+ /** The end-to-end encryption policy editor, shared by the Encryption Policy page and the setup wizard. */
15
+ export default function EncryptionPolicyForm({ policy, onChange, onDirtyChange, embedded }: EncryptionPolicyFormProps): React.JSX.Element;