@rapidmx/web-client 0.3.1 → 0.5.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 (202) hide show
  1. package/apps/admin/audit-log/index.tsx +35 -8
  2. package/apps/admin/branding/index.tsx +39 -404
  3. package/apps/admin/data-requests/index.tsx +480 -485
  4. package/apps/admin/distribution-lists/[uid].tsx +19 -1
  5. package/apps/admin/domains/[uid].tsx +92 -259
  6. package/apps/admin/encryption-policy/index.tsx +19 -0
  7. package/apps/admin/escrow-scopes/[uid].tsx +348 -237
  8. package/apps/admin/escrow-scopes/new/index.tsx +8 -2
  9. package/apps/admin/index.tsx +118 -82
  10. package/apps/admin/ingest-queue/index.tsx +44 -5
  11. package/apps/admin/mailbox-policy/index.tsx +19 -0
  12. package/apps/admin/mailboxes/[uid].tsx +211 -184
  13. package/apps/admin/mailboxes/new/index.tsx +28 -291
  14. package/apps/admin/plugins/index.tsx +15 -0
  15. package/apps/admin/quarantine/index.tsx +46 -14
  16. package/apps/admin/retention-policy/index.tsx +39 -132
  17. package/apps/admin/setup/index.tsx +15 -0
  18. package/apps/admin/transport-rules/[uid].tsx +30 -1
  19. package/apps/admin/transport-rules/_transportRuleConfig.tsx +38 -2
  20. package/apps/admin/transport-rules/index.tsx +24 -6
  21. package/apps/admin/transport-rules/new/index.tsx +30 -1
  22. package/apps/escrow/_layout.tsx +2 -3
  23. package/apps/escrow/audit-log/index.tsx +196 -168
  24. package/apps/escrow/matters/[uid].tsx +621 -521
  25. package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +22 -1
  26. package/apps/shared/components/admin/layout/AdminShell.tsx +251 -210
  27. package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +152 -0
  28. package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +3 -3
  29. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +55 -11
  30. package/apps/shared/components/admin/settings/BrandingForm.tsx +385 -0
  31. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +187 -0
  32. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +112 -0
  33. package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +34 -0
  34. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +308 -0
  35. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +152 -0
  36. package/apps/shared/components/admin/settings/PluginsManager.tsx +1235 -0
  37. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +174 -0
  38. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +290 -0
  39. package/apps/shared/components/admin/setup/SetupWizard.tsx +416 -0
  40. package/apps/shared/components/admin/signOut.ts +79 -0
  41. package/apps/shared/components/admin/usePagedList.tsx +129 -0
  42. package/apps/shared/components/calendar/CalendarListSidebar.tsx +120 -76
  43. package/apps/shared/components/calendar/EventModal.tsx +717 -513
  44. package/apps/shared/components/calendar/MonthView.tsx +173 -139
  45. package/apps/shared/components/calendar/RecurrenceEditor.tsx +222 -180
  46. package/apps/shared/components/calendar/SplitDayView.tsx +143 -137
  47. package/apps/shared/components/calendar/TimeGridView.tsx +244 -198
  48. package/apps/shared/components/calendar/allDay.ts +124 -0
  49. package/apps/shared/components/calendar/layout/CalendarShell.tsx +80 -99
  50. package/apps/shared/components/contacts/ContactDetailPane.tsx +100 -21
  51. package/apps/shared/components/contacts/ContactForm.tsx +377 -314
  52. package/apps/shared/components/contacts/ContactsSidebar.tsx +6 -2
  53. package/apps/shared/components/contacts/KeyChangeReview.tsx +192 -0
  54. package/apps/shared/components/contacts/contactKeys.ts +78 -0
  55. package/apps/shared/components/escrow/layout/EscrowShell.tsx +148 -134
  56. package/apps/shared/components/layout/AppShell.tsx +270 -169
  57. package/apps/shared/components/layout/BrandingChrome.tsx +48 -10
  58. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +150 -26
  59. package/apps/shared/components/layout/MailboxProvisioning.tsx +19 -3
  60. package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +350 -0
  61. package/apps/shared/components/layout/UnlockPromptProvider.tsx +257 -0
  62. package/apps/shared/components/mail/ConversationThreadPane.tsx +216 -181
  63. package/apps/shared/components/mail/MessageDetailPane.tsx +799 -76
  64. package/apps/shared/components/mail/compose/ComposeContext.tsx +26 -16
  65. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1652 -765
  66. package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -0
  67. package/apps/shared/components/mail/layout/MailShell.tsx +418 -302
  68. package/apps/shared/components/mail/pinnedSigners.ts +124 -0
  69. package/apps/shared/components/mail/verificationSeals.ts +125 -0
  70. package/apps/shared/components/mail/writableMailboxes.ts +101 -0
  71. package/apps/shared/components/rules/RuleBuilder.tsx +311 -276
  72. package/apps/shared/components/settings/layout/SettingsShell.tsx +1 -0
  73. package/apps/shared/mail/findWellKnownFolderUid.ts +13 -0
  74. package/apps/shared/mail/listAllPages.ts +39 -0
  75. package/apps/shared/search/LocalIndexLifecycle.tsx +98 -0
  76. package/apps/shared/search/localIndexBlockCipher.ts +83 -0
  77. package/apps/shared/search/localIndexBuilder.ts +481 -0
  78. package/apps/shared/search/localIndexKey.ts +27 -0
  79. package/apps/shared/search/localIndexRpcClient.ts +316 -0
  80. package/apps/shared/search/localIndexSchema.ts +236 -0
  81. package/apps/shared/search/localIndexSizePreference.ts +88 -0
  82. package/apps/shared/search/localIndexStorage.ts +98 -0
  83. package/apps/shared/search/localIndexVFS.ts +266 -0
  84. package/apps/shared/search/localIndexWorker.ts +926 -0
  85. package/apps/shared/search/searchTier2.ts +79 -0
  86. package/apps/shared/search/wa-sqlite-shims.d.ts +44 -0
  87. package/apps/www/calendar/index.tsx +438 -407
  88. package/apps/www/contacts/[uid].tsx +21 -2
  89. package/apps/www/contacts/index.tsx +148 -17
  90. package/apps/www/index.tsx +1314 -486
  91. package/apps/www/messages/[uid].tsx +23 -7
  92. package/apps/www/settings/auto-reply/index.tsx +134 -129
  93. package/apps/www/settings/booking-types/[uid].tsx +306 -300
  94. package/apps/www/settings/encryption/index.tsx +800 -215
  95. package/apps/www/settings/filters/[uid].tsx +171 -155
  96. package/apps/www/settings/filters/new/index.tsx +8 -1
  97. package/apps/www/settings/focused-inbox/index.tsx +150 -148
  98. package/apps/www/settings/privacy/index.tsx +494 -436
  99. package/apps/www/settings/read-receipts/index.tsx +148 -125
  100. package/apps/www/settings/sharing/index.tsx +274 -0
  101. package/apps/www/tasks/index.tsx +638 -530
  102. package/dist/apps/admin/audit-log/index.js +38 -8
  103. package/dist/apps/admin/branding/index.js +4 -98
  104. package/dist/apps/admin/data-requests/index.js +52 -72
  105. package/dist/apps/admin/distribution-lists/[uid].js +1 -1
  106. package/dist/apps/admin/domains/[uid].js +5 -68
  107. package/dist/apps/admin/encryption-policy/index.js +8 -0
  108. package/dist/apps/admin/escrow-scopes/[uid].js +91 -12
  109. package/dist/apps/admin/escrow-scopes/new/index.js +8 -3
  110. package/dist/apps/admin/index.js +17 -1
  111. package/dist/apps/admin/ingest-queue/index.js +24 -6
  112. package/dist/apps/admin/mailbox-policy/index.js +8 -0
  113. package/dist/apps/admin/mailboxes/[uid].js +20 -4
  114. package/dist/apps/admin/mailboxes/new/index.js +4 -89
  115. package/dist/apps/admin/plugins/index.js +6 -0
  116. package/dist/apps/admin/quarantine/index.js +20 -11
  117. package/dist/apps/admin/retention-policy/index.js +3 -36
  118. package/dist/apps/admin/setup/index.js +6 -0
  119. package/dist/apps/admin/transport-rules/[uid].js +18 -2
  120. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +19 -0
  121. package/dist/apps/admin/transport-rules/index.js +22 -6
  122. package/dist/apps/admin/transport-rules/new/index.js +18 -2
  123. package/dist/apps/escrow/_layout.js +3 -3
  124. package/dist/apps/escrow/audit-log/index.js +26 -1
  125. package/dist/apps/escrow/matters/[uid].js +77 -42
  126. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +11 -2
  127. package/dist/apps/shared/components/admin/layout/AdminShell.js +37 -4
  128. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +72 -0
  129. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +3 -3
  130. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +22 -4
  131. package/dist/apps/shared/components/admin/settings/BrandingForm.js +116 -0
  132. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +83 -0
  133. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +62 -0
  134. package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +25 -0
  135. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +112 -0
  136. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +74 -0
  137. package/dist/apps/shared/components/admin/settings/PluginsManager.js +572 -0
  138. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +73 -0
  139. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +147 -0
  140. package/dist/apps/shared/components/admin/setup/SetupWizard.js +208 -0
  141. package/dist/apps/shared/components/admin/signOut.js +75 -0
  142. package/dist/apps/shared/components/admin/usePagedList.js +99 -0
  143. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +24 -13
  144. package/dist/apps/shared/components/calendar/EventModal.js +154 -21
  145. package/dist/apps/shared/components/calendar/MonthView.js +12 -5
  146. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +40 -5
  147. package/dist/apps/shared/components/calendar/SplitDayView.js +10 -4
  148. package/dist/apps/shared/components/calendar/TimeGridView.js +23 -7
  149. package/dist/apps/shared/components/calendar/allDay.js +111 -0
  150. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +47 -39
  151. package/dist/apps/shared/components/contacts/ContactDetailPane.js +43 -5
  152. package/dist/apps/shared/components/contacts/ContactForm.js +45 -12
  153. package/dist/apps/shared/components/contacts/ContactsSidebar.js +2 -2
  154. package/dist/apps/shared/components/contacts/KeyChangeReview.js +63 -0
  155. package/dist/apps/shared/components/contacts/contactKeys.js +64 -0
  156. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +24 -5
  157. package/dist/apps/shared/components/layout/AppShell.js +100 -8
  158. package/dist/apps/shared/components/layout/BrandingChrome.js +45 -10
  159. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +63 -14
  160. package/dist/apps/shared/components/layout/MailboxProvisioning.js +10 -2
  161. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +174 -0
  162. package/dist/apps/shared/components/layout/UnlockPromptProvider.js +136 -0
  163. package/dist/apps/shared/components/mail/ConversationThreadPane.js +50 -12
  164. package/dist/apps/shared/components/mail/MessageDetailPane.js +464 -34
  165. package/dist/apps/shared/components/mail/compose/ComposeContext.js +9 -6
  166. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +807 -116
  167. package/dist/apps/shared/components/mail/compose/composeFlushRegistry.js +50 -0
  168. package/dist/apps/shared/components/mail/layout/MailShell.js +116 -41
  169. package/dist/apps/shared/components/mail/pinnedSigners.js +98 -0
  170. package/dist/apps/shared/components/mail/verificationSeals.js +112 -0
  171. package/dist/apps/shared/components/mail/writableMailboxes.js +87 -0
  172. package/dist/apps/shared/components/rules/RuleBuilder.js +34 -6
  173. package/dist/apps/shared/components/settings/layout/SettingsShell.js +1 -0
  174. package/dist/apps/shared/mail/findWellKnownFolderUid.js +12 -0
  175. package/dist/apps/shared/mail/listAllPages.js +25 -0
  176. package/dist/apps/shared/search/LocalIndexLifecycle.js +80 -0
  177. package/dist/apps/shared/search/localIndexBlockCipher.js +63 -0
  178. package/dist/apps/shared/search/localIndexBuilder.js +408 -0
  179. package/dist/apps/shared/search/localIndexKey.js +25 -0
  180. package/dist/apps/shared/search/localIndexRpcClient.js +241 -0
  181. package/dist/apps/shared/search/localIndexSchema.js +185 -0
  182. package/dist/apps/shared/search/localIndexSizePreference.js +78 -0
  183. package/dist/apps/shared/search/localIndexStorage.js +86 -0
  184. package/dist/apps/shared/search/localIndexVFS.js +258 -0
  185. package/dist/apps/shared/search/localIndexWorker.js +687 -0
  186. package/dist/apps/shared/search/searchTier2.js +38 -0
  187. package/dist/apps/www/calendar/index.js +38 -18
  188. package/dist/apps/www/contacts/[uid].js +13 -2
  189. package/dist/apps/www/contacts/index.js +94 -15
  190. package/dist/apps/www/index.js +770 -137
  191. package/dist/apps/www/messages/[uid].js +23 -7
  192. package/dist/apps/www/settings/auto-reply/index.js +9 -4
  193. package/dist/apps/www/settings/booking-types/[uid].js +17 -12
  194. package/dist/apps/www/settings/encryption/index.js +541 -96
  195. package/dist/apps/www/settings/filters/[uid].js +22 -7
  196. package/dist/apps/www/settings/filters/new/index.js +8 -2
  197. package/dist/apps/www/settings/focused-inbox/index.js +3 -1
  198. package/dist/apps/www/settings/privacy/index.js +57 -22
  199. package/dist/apps/www/settings/read-receipts/index.js +10 -3
  200. package/dist/apps/www/settings/sharing/index.js +124 -0
  201. package/dist/apps/www/tasks/index.js +76 -14
  202. package/package.json +3 -2
@@ -1,82 +1,118 @@
1
- ///////////////////////////////////////////////////////////////////////////////
2
- // Copyright (C) 2026 Jean-Philippe Steinmetz
3
- // SPDX-License-Identifier: MPL-2.0
4
- ///////////////////////////////////////////////////////////////////////////////
5
- import React, { useEffect, useState } from "react";
6
- import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
- import { listMailboxes, Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
8
- import AdminShell, { AdminShellProps } from "../shared/components/admin/layout/AdminShell.js";
9
- import MailboxTable from "../shared/components/admin/mailboxes/MailboxTable.js";
10
- import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
11
- import Button from "@rapidmx/react-shared/components/buttons/Button.js";
12
-
13
- const PAGE_SIZE = 25;
14
-
15
- export default function MailboxesListPage(props: Omit<AdminShellProps, "active">) {
16
- return (
17
- <AdminShell {...props} active="mailboxes">
18
- <MailboxesListContent />
19
- </AdminShell>
20
- );
21
- }
22
-
23
- function MailboxesListContent() {
24
- const [page, setPage] = useState(0);
25
- const [mailboxes, setMailboxes] = useState<Mailbox[]>([]);
26
- const [loading, setLoading] = useState(true);
27
- const [error, setError] = useState<string | null>(null);
28
-
29
- useEffect(() => {
30
- setLoading(true);
31
- setError(null);
32
- listMailboxes({ page, limit: PAGE_SIZE })
33
- .then(setMailboxes)
34
- .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load mailboxes."))
35
- .finally(() => setLoading(false));
36
- }, [page]);
37
-
38
- const hasNextPage = mailboxes.length === PAGE_SIZE;
39
-
40
- return (
41
- <>
42
- <div className="flex items-center justify-between mb-5">
43
- <h1 className="text-xl font-bold uppercase tracking-wide">Mailboxes</h1>
44
- <a href="/admin/mailboxes/new">
45
- <Button type="button" className="!w-auto">
46
- + New mailbox
47
- </Button>
48
- </a>
49
- </div>
50
-
51
- {error && <Alert>{error}</Alert>}
52
-
53
- {loading ? (
54
- <p className="text-sm text-text-muted">Loading&hellip;</p>
55
- ) : (
56
- <MailboxTable mailboxes={mailboxes} />
57
- )}
58
-
59
- <div className="flex gap-3 items-center mt-4">
60
- <Button
61
- variant="secondary"
62
- type="button"
63
- className="!w-auto"
64
- disabled={page === 0 || loading}
65
- onClick={() => setPage((p) => Math.max(0, p - 1))}
66
- >
67
- Previous
68
- </Button>
69
- <span className="text-sm text-text-muted">Page {page + 1}</span>
70
- <Button
71
- variant="secondary"
72
- type="button"
73
- className="!w-auto"
74
- disabled={!hasNextPage || loading}
75
- onClick={() => setPage((p) => p + 1)}
76
- >
77
- Next
78
- </Button>
79
- </div>
80
- </>
81
- );
82
- }
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import React, { useEffect, useState } from "react";
6
+ import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
+ import { listMailboxes, Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
8
+ import { reopenSetup } from "@rapidmx/react-shared/admin/setupApi.js";
9
+ import AdminShell, { AdminShellProps } from "../shared/components/admin/layout/AdminShell.js";
10
+ import MailboxTable from "../shared/components/admin/mailboxes/MailboxTable.js";
11
+ import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
12
+ import Button from "@rapidmx/react-shared/components/buttons/Button.js";
13
+ import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
14
+
15
+ const PAGE_SIZE = 25;
16
+
17
+ export default function MailboxesListPage(props: Omit<AdminShellProps, "active">) {
18
+ return (
19
+ <AdminShell {...props} active="mailboxes">
20
+ <MailboxesListContent />
21
+ </AdminShell>
22
+ );
23
+ }
24
+
25
+ function MailboxesListContent() {
26
+ const [page, setPage] = useState(0);
27
+ const [mailboxes, setMailboxes] = useState<Mailbox[]>([]);
28
+ const [loading, setLoading] = useState(true);
29
+ const [error, setError] = useState<string | null>(null);
30
+
31
+ useEffect(() => {
32
+ setLoading(true);
33
+ setError(null);
34
+ listMailboxes({ page, limit: PAGE_SIZE })
35
+ .then(setMailboxes)
36
+ .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load mailboxes."))
37
+ .finally(() => setLoading(false));
38
+ }, [page]);
39
+
40
+ const hasNextPage = mailboxes.length === PAGE_SIZE;
41
+ const [reopening, setReopening] = useState(false);
42
+ const [confirmingSetup, setConfirmingSetup] = useState(false);
43
+
44
+ async function handleRunSetup() {
45
+ setReopening(true);
46
+ try {
47
+ await reopenSetup();
48
+ window.location.href = "/admin/setup";
49
+ } catch (err) {
50
+ setError(err instanceof ApiRequestError ? err.message : "Could not reopen setup.");
51
+ setReopening(false);
52
+ setConfirmingSetup(false);
53
+ }
54
+ }
55
+
56
+ return (
57
+ <>
58
+ <div className="flex items-center justify-between mb-5">
59
+ <h1 className="text-xl font-bold uppercase tracking-wide">Mailboxes</h1>
60
+ <div className="flex gap-2">
61
+ <Button type="button" variant="secondary" className="!w-auto" disabled={reopening} onClick={() => setConfirmingSetup(true)}>
62
+ Run setup again
63
+ </Button>
64
+ <a href="/admin/mailboxes/new">
65
+ <Button type="button" className="!w-auto">
66
+ + New mailbox
67
+ </Button>
68
+ </a>
69
+ </div>
70
+ </div>
71
+
72
+ {error && <Alert>{error}</Alert>}
73
+
74
+ <Modal open={confirmingSetup} onClose={() => setConfirmingSetup(false)} title="Run setup again?">
75
+ <p className="text-sm mb-4">
76
+ Setup starts again from its first step, and the admin console sends administrators to it until
77
+ it&apos;s finished. Your existing settings, domains and mailboxes are kept.
78
+ </p>
79
+ <div className="flex gap-2 justify-end">
80
+ <Button type="button" variant="secondary" className="!w-auto" onClick={() => setConfirmingSetup(false)}>
81
+ Cancel
82
+ </Button>
83
+ <Button type="button" className="!w-auto" loading={reopening} disabled={reopening} onClick={() => void handleRunSetup()}>
84
+ Run setup
85
+ </Button>
86
+ </div>
87
+ </Modal>
88
+
89
+ {loading ? (
90
+ <p className="text-sm text-text-muted">Loading&hellip;</p>
91
+ ) : (
92
+ <MailboxTable mailboxes={mailboxes} />
93
+ )}
94
+
95
+ <div className="flex gap-3 items-center mt-4">
96
+ <Button
97
+ variant="secondary"
98
+ type="button"
99
+ className="!w-auto"
100
+ disabled={page === 0 || loading}
101
+ onClick={() => setPage((p) => Math.max(0, p - 1))}
102
+ >
103
+ Previous
104
+ </Button>
105
+ <span className="text-sm text-text-muted">Page {page + 1}</span>
106
+ <Button
107
+ variant="secondary"
108
+ type="button"
109
+ className="!w-auto"
110
+ disabled={!hasNextPage || loading}
111
+ onClick={() => setPage((p) => p + 1)}
112
+ >
113
+ Next
114
+ </Button>
115
+ </div>
116
+ </>
117
+ );
118
+ }
@@ -7,6 +7,9 @@ import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
7
  import { IngestQueueEntry, listIngestQueue } from "@rapidmx/react-shared/mail/mailApi.js";
8
8
  import AdminShell, { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
9
9
  import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
10
+ import Button from "@rapidmx/react-shared/components/buttons/Button.js";
11
+
12
+ const PAGE_SIZE = 25;
10
13
 
11
14
  export function readMailboxUid(): string | null {
12
15
  if (typeof window === "undefined") return null;
@@ -30,6 +33,7 @@ export default function IngestQueuePage(props: Omit<AdminShellProps, "active">)
30
33
 
31
34
  function IngestQueueContent() {
32
35
  const [mailboxUid, setMailboxUid] = useState<string | null>(null);
36
+ const [page, setPage] = useState(0);
33
37
  const [entries, setEntries] = useState<IngestQueueEntry[]>([]);
34
38
  const [loading, setLoading] = useState(true);
35
39
  const [error, setError] = useState<string | null>(null);
@@ -42,13 +46,26 @@ function IngestQueueContent() {
42
46
  if (!mailboxUid) {
43
47
  return;
44
48
  }
49
+ // Paging quickly can leave an older page's response landing last - only the latest request's is applied.
50
+ let cancelled = false;
45
51
  setLoading(true);
46
52
  setError(null);
47
- listIngestQueue(mailboxUid)
48
- .then(setEntries)
49
- .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load the ingest queue."))
50
- .finally(() => setLoading(false));
51
- }, [mailboxUid]);
53
+ listIngestQueue(mailboxUid, { page, limit: PAGE_SIZE })
54
+ .then((data) => {
55
+ if (!cancelled) setEntries(data);
56
+ })
57
+ .catch((err) => {
58
+ if (!cancelled) setError(err instanceof ApiRequestError ? err.message : "Could not load the ingest queue.");
59
+ })
60
+ .finally(() => {
61
+ if (!cancelled) setLoading(false);
62
+ });
63
+ return () => {
64
+ cancelled = true;
65
+ };
66
+ }, [mailboxUid, page]);
67
+
68
+ const hasNextPage = entries.length === PAGE_SIZE;
52
69
 
53
70
  if (!mailboxUid) {
54
71
  return <Alert>No mailbox specified. Open a mailbox's detail page and choose "View ingest queue".</Alert>;
@@ -111,6 +128,28 @@ function IngestQueueContent() {
111
128
  </table>
112
129
  </div>
113
130
  )}
131
+
132
+ <div className="flex gap-3 items-center">
133
+ <Button
134
+ variant="secondary"
135
+ type="button"
136
+ className="!w-auto"
137
+ disabled={page === 0 || loading}
138
+ onClick={() => setPage((p) => Math.max(0, p - 1))}
139
+ >
140
+ Previous
141
+ </Button>
142
+ <span className="text-sm text-text-muted">Page {page + 1}</span>
143
+ <Button
144
+ variant="secondary"
145
+ type="button"
146
+ className="!w-auto"
147
+ disabled={!hasNextPage || loading}
148
+ onClick={() => setPage((p) => p + 1)}
149
+ >
150
+ Next
151
+ </Button>
152
+ </div>
114
153
  </div>
115
154
  );
116
155
  }
@@ -0,0 +1,19 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import React from "react";
6
+ import { getMailboxPolicy } from "@rapidmx/react-shared/admin/mailboxPolicyApi.js";
7
+ import AdminShell, { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
8
+ import LoadedSettingsForm from "../../shared/components/admin/settings/LoadedSettingsForm.js";
9
+ import MailboxPolicyForm from "../../shared/components/admin/settings/MailboxPolicyForm.js";
10
+
11
+ export default function MailboxPolicyPage(props: Omit<AdminShellProps, "active">) {
12
+ return (
13
+ <AdminShell {...props} active="mailboxPolicy">
14
+ <LoadedSettingsForm load={getMailboxPolicy} loadErrorMessage="Could not load the mailbox policy.">
15
+ {(policy, onChange) => <MailboxPolicyForm policy={policy} onChange={onChange} />}
16
+ </LoadedSettingsForm>
17
+ </AdminShell>
18
+ );
19
+ }