@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
@@ -0,0 +1,46 @@
1
+ import React from "react";
2
+ import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
3
+ import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
4
+ export interface EventModalProps {
5
+ open: boolean;
6
+ onClose: () => void;
7
+ mailboxUid: string;
8
+ /** The calendar folder a new event is created into (ignored when editing — an existing event keeps
9
+ * its own `folderUid`). When `calendars` names more than one option, a "Calendar" selector lets the
10
+ * user override this default before saving. */
11
+ folderUid: string;
12
+ /** Every calendar the caller could create this event into. Omitted, or a single entry, means "only
13
+ * one calendar exists" — no selector is shown and `folderUid` is used as-is, matching this
14
+ * component's original single-calendar behavior exactly. */
15
+ calendars?: {
16
+ uid: string;
17
+ name: string;
18
+ }[];
19
+ /** Every mailbox a new event could be created in, each with its own calendars. With more than one, a
20
+ * "Mailbox" selector (create mode only) chooses the mailbox, which in turn drives the Calendar selector
21
+ * and the organizer address; `mailboxUid`/`folderUid` are the initial selection. */
22
+ mailboxOptions?: {
23
+ mailbox: Mailbox;
24
+ calendars: {
25
+ uid: string;
26
+ name: string;
27
+ }[];
28
+ }[];
29
+ organizerAddress: string;
30
+ /** The viewing mailbox's alias addresses - an event it organizes (or an invitation to it) may name any of
31
+ * them instead of `organizerAddress`. Defaults to that mailbox's `aliasAddresses` from `mailboxOptions`. */
32
+ organizerAliases?: string[];
33
+ /** `null` when creating a new event. */
34
+ occurrence: CalendarOccurrence | null;
35
+ /** Prefilled start/end for create mode (e.g. the day/slot the user clicked). */
36
+ initialStart?: Date;
37
+ initialEnd?: Date;
38
+ onSaved: () => void;
39
+ onDeleted: () => void;
40
+ }
41
+ /**
42
+ * Create/view/edit/delete for a single calendar event. Editing or deleting a recurring event's
43
+ * occurrence offers a choice between "this event" and "the entire series" (see
44
+ * `calendarMutations.ts`) — Outlook's own convention for the same ambiguity.
45
+ */
46
+ export default function EventModal({ open, onClose, mailboxUid, folderUid, calendars, mailboxOptions, organizerAddress, organizerAliases, occurrence, initialStart, initialEnd, onSaved, onDeleted, }: EventModalProps): React.JSX.Element;
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
3
+ export interface MonthViewProps {
4
+ /** Any date within the month to display. */
5
+ viewDate: Date;
6
+ /** Already expanded for the visible grid range — see `recurrence.ts`'s `expandAllOccurrences`. */
7
+ occurrences: CalendarOccurrence[];
8
+ /** Each occurrence's own calendar color, keyed by `folderUid` — see `calendarColors.ts`. */
9
+ folderColors: Record<string, string>;
10
+ onSelectDay: (date: Date) => void;
11
+ onSelectEvent: (occurrence: CalendarOccurrence) => void;
12
+ }
13
+ /** A real 6-week month grid (Mon-start), matching Outlook/Gmail's month view. Multi-day and all-day
14
+ * events are shown on every day they cover (see `allDay.ts`'s `occursOnDay`). */
15
+ export default function MonthView({ viewDate, occurrences, folderColors, onSelectDay, onSelectEvent }: MonthViewProps): React.JSX.Element;
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import { RecurrenceRule, WeekdayCode } from "@rapidmx/react-shared/calendar/calendarApi.js";
3
+ export interface RecurrenceEditorProps {
4
+ /** `null` means "does not repeat". */
5
+ value: RecurrenceRule | null;
6
+ onChange: (value: RecurrenceRule | null) => void;
7
+ /** Whether the event is all-day - its series expands in UTC, so the inclusive "Ends on" date is stored as
8
+ * the end of that UTC day rather than the local one (see `allDay.ts`'s `recurrenceUntilInstant()`). */
9
+ allDay?: boolean;
10
+ /** The weekday the event starts on (in the frame its series expands in - see `allDay.ts`'s
11
+ * `startWeekdayCode()`): the day a new weekly rule, or one switched to Weekly, repeats on. Defaults to Monday. */
12
+ startWeekday?: WeekdayCode;
13
+ }
14
+ /**
15
+ * A recurring-event editor: frequency + interval, a weekday picker (weekly only), and an end
16
+ * condition (never / after N occurrences / on a date) — everything `RecurrenceRule` can express. The
17
+ * live "every ... until/for ..." summary comes from `describeRecurrence()` (built on `rrule`'s own
18
+ * `.toText()`), so it never drifts out of sync with what will actually be submitted.
19
+ */
20
+ export default function RecurrenceEditor({ value, onChange, allDay, startWeekday }: RecurrenceEditorProps): React.JSX.Element;
@@ -0,0 +1,19 @@
1
+ import React, { RefObject } from "react";
2
+ import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
3
+ export interface ResourcePickerProps {
4
+ anchorRef: RefObject<HTMLElement | null>;
5
+ onClose: () => void;
6
+ onSelect: (mailbox: Mailbox) => void;
7
+ /** Addresses already added as attendees (lowercased by the caller) — excluded from the list so the
8
+ * same room/equipment mailbox can't be picked twice. */
9
+ excludeAddresses: string[];
10
+ }
11
+ /**
12
+ * A searchable dropdown of bookable resource mailboxes (rooms/equipment), for adding one as a `"resource"`
13
+ * attendee in `EventModal` without typing its raw address. Fetches the caller's full visible resource-mailbox
14
+ * list once on open and filters it client-side by name/address as the reader types — the same
15
+ * "fetch-flat-list, filter-client-side" contract every other list endpoint in this codebase already uses
16
+ * (see `calendarApi.ts`'s `listCalendarEvents` doc comment), since there is no dedicated server-side search
17
+ * endpoint for mailboxes and the resource-mailbox count in any one org is expected to be small.
18
+ */
19
+ export default function ResourcePicker({ anchorRef, onClose, onSelect, excludeAddresses }: ResourcePickerProps): React.JSX.Element;
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
3
+ export interface SplitDayColumn {
4
+ folderUid: string;
5
+ name: string;
6
+ color: string;
7
+ }
8
+ export interface SplitDayViewProps {
9
+ day: Date;
10
+ columns: SplitDayColumn[];
11
+ /** Already expanded for `day` — see `recurrence.ts`'s `expandAllOccurrences`. Not pre-filtered by
12
+ * calendar; this component splits them into columns itself by `occurrence.folderUid`. */
13
+ occurrences: CalendarOccurrence[];
14
+ onSelectEvent: (occurrence: CalendarOccurrence) => void;
15
+ /** Click on an empty slot in one calendar's column — the calendar it belongs to is `folderUid`. */
16
+ onSelectSlot: (start: Date, end: Date, folderUid: string) => void;
17
+ }
18
+ /**
19
+ * Outlook's "Split" view: one column per checked calendar, all showing the same single day, so
20
+ * events across calendars can be compared side by side. A deliberately separate component from
21
+ * `TimeGridView` rather than a generalization of it — it shares that component's visual language
22
+ * (same hour rail, same slot height) but is genuinely simpler in one respect: no drag-to-move/resize.
23
+ * Dragging an event between calendar columns would mean reassigning its `folderUid`, a real feature
24
+ * `moveOccurrence`/`resolveDragAction` don't support today; rather than bolt that on, this view is
25
+ * click-only (select an event, or click a slot to create one already targeted at that column's
26
+ * calendar) — a practical scope cut, not an oversight.
27
+ */
28
+ export default function SplitDayView({ day, columns, occurrences, onSelectEvent, onSelectSlot }: SplitDayViewProps): React.JSX.Element;
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
3
+ export interface TimeGridViewProps {
4
+ /** One day for the Day view, seven for the Week view, five for the Work Week view. */
5
+ days: Date[];
6
+ /** Already expanded for the visible range — see `recurrence.ts`'s `expandAllOccurrences`. */
7
+ occurrences: CalendarOccurrence[];
8
+ /** Each occurrence's own calendar color, keyed by `folderUid` — see `calendarColors.ts`. */
9
+ folderColors: Record<string, string>;
10
+ onSelectEvent: (occurrence: CalendarOccurrence) => void;
11
+ /** Click on an empty slot — start/end default to a 30-minute block there, for the "New event" flow. */
12
+ onSelectSlot: (start: Date, end: Date) => void;
13
+ }
14
+ /** An hourly time grid (Week, Work Week, or Day view — they share all their rendering/drag logic and
15
+ * differ only in how many day columns are shown), with a day-header row and all-day events in a
16
+ * header strip above it. */
17
+ export default function TimeGridView({ days, occurrences, folderColors, onSelectEvent, onSelectSlot }: TimeGridViewProps): React.JSX.Element;
@@ -0,0 +1,51 @@
1
+ import { WeekdayCode } from "@rapidmx/react-shared/calendar/calendarApi.js";
2
+ import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
3
+ /**
4
+ * All-day events are stored date-only: `startDate` is UTC midnight of the first day and `endDate` is UTC
5
+ * midnight of the day *after* the last day (exclusive, like iCalendar's `DTEND;VALUE=DATE`). That keeps
6
+ * an all-day event on the same calendar date for every viewer, whatever their timezone.
7
+ *
8
+ * Older events were saved as the creator's *local* midnight converted to UTC (e.g. `...T22:00Z` for a
9
+ * UTC+2 creator), so reading one rounds the instant to the nearest UTC midnight rather than slicing the
10
+ * ISO string — which gives the intended date for both formats.
11
+ */
12
+ export declare function allDayDateKey(iso: string): string;
13
+ /** `YYYY-MM-DD` -> the stored UTC-midnight instant for that date. */
14
+ export declare function allDayInstant(dateKey: string): string;
15
+ /** Adds `days` to a `YYYY-MM-DD` date key. */
16
+ export declare function addDaysToKey(dateKey: string, days: number): string;
17
+ /** The local calendar date `day` (a local-midnight `Date` from a grid view) as a `YYYY-MM-DD` key. */
18
+ export declare function localDateKey(day: Date): string;
19
+ /**
20
+ * A recurrence rule's inclusive "Ends on" date -> its stored `until` instant: the end of that day in the
21
+ * frame the series expands in. A timed series expands in its own (normally the viewer's) timezone, so that's
22
+ * the end of the local day - `new Date("YYYY-MM-DD")` would be UTC midnight, the previous day west of UTC.
23
+ * An all-day series expands in UTC (react-shared's `expandOccurrences`), so it's the end of the UTC day: the
24
+ * local end of day is already the next UTC day west of UTC and would add one more occurrence.
25
+ */
26
+ export declare function recurrenceUntilInstant(dateKey: string, allDay: boolean): string;
27
+ /**
28
+ * Inverse of `recurrenceUntilInstant()`. An all-day series' value is recognized explicitly in its stored
29
+ * `T23:59:59.999Z` form (and as a bare UTC midnight, the oldest form). Anything else is a legacy value stored
30
+ * as the creator's *local* end of day, which can land anywhere from just before 10:00Z on the chosen date (UTC+14)
31
+ * to just before 12:00Z the day after (UTC-12) - a 26-hour spread no UTC-only rounding can resolve (UTC+14 and UTC-10
32
+ * creators' values are exactly 24h apart for the same date), so it reads back on the local calendar, the
33
+ * frame it was written in.
34
+ */
35
+ export declare function recurrenceUntilDateKey(until: string, allDay: boolean): string;
36
+ /** The weekday an event starts on, in the frame its series expands in: the UTC date for an all-day event
37
+ * (`start`'s date part), the event's own timezone otherwise (`start` being a local `datetime-local` value).
38
+ * `undefined` while the start input is empty/invalid. */
39
+ export declare function startWeekdayCode(start: string, allDay: boolean, timezone: string | undefined): WeekdayCode | undefined;
40
+ /**
41
+ * Whether `occurrence` should be shown on the grid's local calendar day `day`. All-day events cover every
42
+ * date from their start date up to (not including) their exclusive end date — at least their start date.
43
+ * Timed events cover every local day their [start, end) interval overlaps, so a multi-day event shows on
44
+ * each day rather than only on its start day.
45
+ */
46
+ export declare function occursOnDay(occurrence: CalendarOccurrence, day: Date): boolean;
47
+ /**
48
+ * Whether `day` is the first day `occurrence` covers. Grid views only make that day's chip/block
49
+ * draggable — a drag id must be unique, so the continuation copies on later days are display-only.
50
+ */
51
+ export declare function startsOnDay(occurrence: CalendarOccurrence, day: Date): boolean;
@@ -0,0 +1,40 @@
1
+ import React, { PropsWithChildren } from "react";
2
+ import { Folder, Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
3
+ import { AppShellProps } from "../../layout/AppShell.js";
4
+ export type CalendarShellProps = Omit<AppShellProps, "active">;
5
+ /** One mailbox's own `calendar`-type folders. `error` is set (and `calendarFolders` left empty) when that
6
+ * mailbox's own `listFolders()` failed - one mailbox's failure never hides every other mailbox's calendars. */
7
+ export interface MailboxCalendars {
8
+ mailbox: Mailbox;
9
+ calendarFolders: Folder[];
10
+ error?: string;
11
+ }
12
+ export interface CalendarShellContextValue {
13
+ /** The default mailbox for "+ New event" (`?mailboxUid=` if accessible, else the caller's own mailbox,
14
+ * else the first accessible one) - every accessible mailbox's calendars are shown regardless. */
15
+ mailboxUid?: string;
16
+ /** `mailboxUid`'s first `calendar`-type folder - the default target for "+ New event". */
17
+ folderUid?: string;
18
+ /** Every accessible mailbox's `calendar`-type folders, flattened - the set of calendars that can be
19
+ * checked/shown (see `CalendarListSidebar`). Each folder's own `mailboxUid` says which mailbox it's in. */
20
+ calendarFolders: Folder[];
21
+ /** The same folders, grouped per mailbox, for the sidebar's per-mailbox sections. */
22
+ mailboxCalendars: MailboxCalendars[];
23
+ mailboxes: Mailbox[];
24
+ /** Re-fetches every mailbox's folders (e.g. after creating a new calendar) without a full reload. */
25
+ reloadFolders: () => void;
26
+ /** The display color for a calendar folder: its own `color`, else the default for the caller's own
27
+ * mailbox, else its mailbox's `accentColorForMailbox()` - so a shared mailbox's calendar is visually
28
+ * distinct from the caller's own even when neither has a color set. */
29
+ colorFor: (folder: Folder) => string;
30
+ }
31
+ /** Reads the calendars the Calendar is currently showing, as resolved by the enclosing `CalendarShell`. */
32
+ export declare function useCalendarShell(): CalendarShellContextValue;
33
+ /**
34
+ * The Calendar app's shell. Fetches every accessible mailbox's calendar folders in parallel so shared
35
+ * mailboxes' calendars show alongside the caller's own, each mailbox color-coded (see `colorFor`) - there
36
+ * is no mailbox switcher. The actual view being looked at (month/week/day, and which one) is *not* shell
37
+ * state — it lives in `apps/www/calendar/index.tsx`'s own local state, since navigating between
38
+ * views/dates must be instant (no full page reload) and this framework has no client-side router.
39
+ */
40
+ export default function CalendarShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }: PropsWithChildren<CalendarShellProps>): React.JSX.Element;
@@ -29,7 +29,7 @@ export function useCalendarShell() {
29
29
  * state — it lives in `apps/www/calendar/index.tsx`'s own local state, since navigating between
30
30
  * views/dates must be instant (no full page reload) and this framework has no client-side router.
31
31
  */
32
- export default function CalendarShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, children, }) {
32
+ export default function CalendarShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }) {
33
33
  const [status, setStatus] = useState("checking");
34
34
  const [error, setError] = useState(null);
35
35
  const [mailboxes, setMailboxes] = useState([]);
@@ -100,5 +100,5 @@ export default function CalendarShell({ userUid, authServerUrl, impersonating, i
100
100
  else if (userUid && status === "ready") {
101
101
  inner = (_jsx("div", { className: "flex-1 min-w-0 flex flex-col", children: _jsx(CalendarShellContext.Provider, { value: contextValue, children: children }) }));
102
102
  }
103
- return (_jsx(AppShell, { active: "calendar", userUid: userUid, authServerUrl: authServerUrl, impersonating: impersonating, impersonationBaseUrl: impersonationBaseUrl, trusted: trusted, children: inner }));
103
+ return (_jsx(AppShell, { active: "calendar", userUid: userUid, authServerUrl: authServerUrl, impersonating: impersonating, impersonationBaseUrl: impersonationBaseUrl, trusted: trusted, pluginNav: pluginNav, children: inner }));
104
104
  }
@@ -0,0 +1,23 @@
1
+ import React from "react";
2
+ import { Contact } from "@rapidmx/react-shared/contacts/contactsApi.js";
3
+ export interface ContactDetailPaneProps {
4
+ contact: Contact;
5
+ onEdit: () => void;
6
+ onDelete: () => void;
7
+ /** Present only on the mobile detail route — renders a "back to contacts" link above the header. Absent
8
+ * on the desktop inline pane, which never navigates away (selecting a different contact just swaps
9
+ * `contact` in place). */
10
+ backHref?: string;
11
+ /** Called after one of the contact's key changes was accepted or kept, or turned out to be stale - the caller
12
+ * re-reads the contact. */
13
+ onKeysChanged?: () => void;
14
+ /** `false` when the reader is known not to be able to change this contact's keys (the actions are hidden); `undefined`
15
+ * when unknown (they're hidden after a 403). */
16
+ canResolveKeys?: boolean;
17
+ }
18
+ /**
19
+ * A contact's read-only detail view. Shared by the desktop inline pane (`apps/www/contacts/index.tsx`,
20
+ * always visible alongside the contact list) and the mobile detail route
21
+ * (`apps/www/contacts/[uid].tsx`, a full page on its own reached by tapping a contact row).
22
+ */
23
+ export default function ContactDetailPane({ contact, onEdit, onDelete, backHref, onKeysChanged, canResolveKeys }: ContactDetailPaneProps): React.JSX.Element;
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import { Contact } from "@rapidmx/react-shared/contacts/contactsApi.js";
3
+ import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
4
+ export interface ContactFormProps {
5
+ contact?: Contact;
6
+ /** Create mode: the default mailbox (and its Contacts folder) to create in. */
7
+ mailboxUid?: string;
8
+ folderUid?: string;
9
+ /** Create mode: every mailbox the contact could be created in - with more than one, a Mailbox selector
10
+ * is shown. Choosing a mailbox other than `mailboxUid` looks up that mailbox's own Contacts folder. */
11
+ mailboxes?: Mailbox[];
12
+ onSaved: (contact: Contact) => void;
13
+ onCancel: () => void;
14
+ }
15
+ /**
16
+ * A contact's create/edit form. Shared by the desktop inline pane (`apps/www/contacts/index.tsx`) and the
17
+ * mobile detail route's own edit mode (`apps/www/contacts/[uid].tsx`) — creation itself stays
18
+ * desktop-and-mobile-inline (see that route's own doc comment on why "new" never gets a dedicated route).
19
+ */
20
+ export default function ContactForm({ contact, mailboxUid, folderUid, mailboxes, onSaved, onCancel }: ContactFormProps): React.JSX.Element;
@@ -0,0 +1,40 @@
1
+ import React from "react";
2
+ import { Contact } from "@rapidmx/react-shared/contacts/contactsApi.js";
3
+ export type ContactsView = {
4
+ type: "all";
5
+ } | {
6
+ type: "favorites";
7
+ } | {
8
+ type: "list";
9
+ uid: string;
10
+ name: string;
11
+ } | {
12
+ type: "deleted";
13
+ } | {
14
+ type: "category";
15
+ name: string;
16
+ };
17
+ export declare function contactsViewKey(view: ContactsView): string;
18
+ export interface ContactsSidebarProps {
19
+ mailboxUid?: string;
20
+ /** The caller's own (non-deleted) contacts — used to compute counts and the distinct category list.
21
+ * Deliberately not fetched again here; the parent already has this loaded for the main table. */
22
+ contacts: Contact[];
23
+ active: ContactsView;
24
+ onSelect: (view: ContactsView) => void;
25
+ /** Whether to offer the "Deleted" view - restapi only honors `deleted=true` for a caller with delete and
26
+ * update rights on the mailbox (anyone else silently gets the live contacts), so the parent hides it
27
+ * otherwise. Defaults to `true`. */
28
+ showDeleted?: boolean;
29
+ /** Bumped by the parent whenever a contact-list membership changes, so a newly-created/renamed list
30
+ * shows up without a full page reload. */
31
+ refreshToken?: number;
32
+ }
33
+ /**
34
+ * Contacts' Outlook-style left sidebar: "Your contacts" (all, with count), "Favorites", "Your contact
35
+ * lists" (real `ContactList` records, fetched here), "Deleted", and "Categories" (derived from the
36
+ * distinct `categories` values across the loaded contacts — no separate category registry exists
37
+ * server-side). Owns its own contact-list data-fetching, per this app's convention of keeping
38
+ * `ContactsShellContext` itself minimal (mailbox/folder resolution only).
39
+ */
40
+ export default function ContactsSidebar({ mailboxUid, contacts, active, onSelect, refreshToken, showDeleted }: ContactsSidebarProps): React.JSX.Element;
@@ -0,0 +1,25 @@
1
+ import React from "react";
2
+ export interface ContactsToolbarProps {
3
+ selectedCount: number;
4
+ /** `true` only when every currently-selected contact is already favorited — flips the "favorite" button
5
+ * to act (and read) as a removal instead of an addition. */
6
+ allSelectedFavorited: boolean;
7
+ onNewContact: () => void;
8
+ onEdit: () => void;
9
+ onDelete: () => void;
10
+ onEmail: () => void;
11
+ onToggleFavorite: () => void;
12
+ onAddCategory: () => void;
13
+ onExportVCard: () => void;
14
+ /** A `.vcf` file the user picked via the toolbar's own hidden file input — parsing/import is the
15
+ * page's responsibility (it has the mailbox/folder context and the actual `createContact` calls). */
16
+ onImportFile: (file: File) => void;
17
+ }
18
+ /**
19
+ * Contacts' Outlook-style ribbon toolbar. Deliberately presentational — every action is a callback the
20
+ * page (`apps/www/contacts/index.tsx`) implements, since it owns the actual contact data, the current
21
+ * selection, and the mailbox/folder context every real action (create/delete/email/favorite/category)
22
+ * needs. "New contact list" isn't duplicated here — `ContactsSidebar` already has its own inline
23
+ * "+ New list" affordance next to the "Your contact lists" heading.
24
+ */
25
+ export default function ContactsToolbar({ selectedCount, allSelectedFavorited, onNewContact, onEdit, onDelete, onEmail, onToggleFavorite, onAddCategory, onExportVCard, onImportFile, }: ContactsToolbarProps): React.JSX.Element;
@@ -0,0 +1,40 @@
1
+ import React from "react";
2
+ import { type KeyConflict } from "@rapidmx/react-shared/crypto/keyvaultApi.js";
3
+ export interface KeyChangeReviewProps {
4
+ /** The mailbox whose contact holds the pinned key. */
5
+ mailboxUid: string;
6
+ /** The contact's address `resolveKeyConflict()` looks the contact up by; absent hides the actions. */
7
+ address?: string;
8
+ useType: "sign" | "encrypt";
9
+ /** Who the key belongs to, for the advice ("confirm with <name>"). */
10
+ ownerName: string;
11
+ /** The currently pinned key and when it was first trusted; absent (couldn't be loaded) hides the actions. */
12
+ current?: {
13
+ fingerprint: string;
14
+ since: number;
15
+ };
16
+ /** The new key: its fingerprint, the addresses its certificate names, and when and how it was recorded (absent when
17
+ * it's only known from the message being read). */
18
+ proposed: {
19
+ fingerprint: string;
20
+ emails?: string[];
21
+ observedAt?: number;
22
+ source?: KeyConflict["source"];
23
+ };
24
+ /** For accepting a key seen in a message: its certificate. Omitted, accepting pins the recorded conflict's key. */
25
+ certificate?: string;
26
+ /** Whether "Keep current key" applies - only when a conflict for this exact key is recorded. */
27
+ canReject: boolean;
28
+ /** `false` when the reader is known to lack the rights to change the contact's keys. */
29
+ canResolve?: boolean;
30
+ /** After a successful accept or reject - the caller refreshes what it shows. */
31
+ onResolved: (action: "accept" | "reject") => void;
32
+ /** After a 409: the pinned key changed meanwhile. The caller reloads and says so (`KEY_CHANGE_STALE_MESSAGE`). */
33
+ onPinnedKeyChanged: () => void;
34
+ }
35
+ /**
36
+ * The old-versus-new comparison for a changed contact key, with Accept new key (confirmed in a dialog) and Keep current
37
+ * key, calling `resolveKeyConflict()` against the pinned fingerprint shown. Shared by a message's "signing key changed"
38
+ * notice and a contact's recorded key conflicts.
39
+ */
40
+ export default function KeyChangeReview({ mailboxUid, address, useType, ownerName, current, proposed, certificate, canReject, canResolve, onResolved, onPinnedKeyChanged, }: KeyChangeReviewProps): React.JSX.Element;
@@ -0,0 +1,29 @@
1
+ import type { Contact } from "@rapidmx/react-shared/contacts/contactsApi.js";
2
+ import { type KeyConflict, type PublicKey } from "@rapidmx/react-shared/crypto/keyvaultApi.js";
3
+ export declare const KEY_CHANGE_STALE_MESSAGE = "This contact's keys changed while you were looking; reload to see the latest.";
4
+ export declare const KEY_CHANGE_INVALID_MESSAGE = "The new key isn't a valid, current certificate for this address, so it can't be accepted. Nothing was changed.";
5
+ export declare const KEY_CHANGE_FORBIDDEN_MESSAGE = "You don't have permission to change this contact's keys.";
6
+ export declare const KEY_CHANGE_NOT_FOUND_MESSAGE = "There's no key change to resolve for this address any more. It may already have been resolved, or the contact was removed.";
7
+ export declare const KEY_CHANGE_GENERIC_MESSAGE = "Couldn't update this contact's keys. Try again.";
8
+ /** The error text for a failed `resolveKeyConflict()` other than `PinnedKeyChangedError` (which the caller reloads
9
+ * on and reports with `KEY_CHANGE_STALE_MESSAGE`). */
10
+ export declare function keyChangeErrorMessage(err: unknown): string;
11
+ /** Groups a fingerprint into 4-character blocks (`ab12 cd34 ...`), ignoring any separators it came with, so it can be
12
+ * read out and compared over another channel. */
13
+ export declare function groupFingerprint(fingerprint: string): string;
14
+ /** Whether two fingerprints name the same key (case and separators ignored). */
15
+ export declare function sameFingerprint(a: string | undefined, b: string | undefined): boolean;
16
+ /** A revoked key's label: `"superseded"` for a routine replacement, `"revoked"` for a compromised key or one revoked
17
+ * with no reason; `undefined` when the key isn't revoked. */
18
+ export declare function revocationLabel(key: PublicKey): "superseded" | "revoked" | undefined;
19
+ /** Where a key conflict was observed, for display. */
20
+ export declare function conflictSourceLabel(source: KeyConflict["source"]): string;
21
+ /** restapi records `keysFirstSeen` on a contact but react-shared's `Contact` type doesn't carry it. */
22
+ type ContactWithFirstSeen = Pick<Contact, "previousKeys"> & {
23
+ keysFirstSeen?: number;
24
+ };
25
+ /** When `key` (one of `contact`'s pinned keys) started being trusted: when it replaced the newest previous key of its
26
+ * use, else the contact's `keysFirstSeen`, else the key's own `notBefore`. */
27
+ export declare function keyPinnedSince(contact: ContactWithFirstSeen, key: PublicKey): number;
28
+ export declare function formatDate(ms: number): string;
29
+ export {};
@@ -0,0 +1,21 @@
1
+ import React, { PropsWithChildren } from "react";
2
+ import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
3
+ import { AppShellProps } from "../../layout/AppShell.js";
4
+ export type ContactsShellProps = Omit<AppShellProps, "active">;
5
+ export interface ContactsShellContextValue {
6
+ /** The mailbox currently selected (`?mailboxUid=`, or the caller's first accessible mailbox). */
7
+ mailboxUid?: string;
8
+ /** The selected mailbox's single `contacts`-type folder. */
9
+ folderUid?: string;
10
+ mailboxes: Mailbox[];
11
+ }
12
+ /** Reads the mailbox/folder Contacts is currently showing, as resolved by the enclosing `ContactsShell`. */
13
+ export declare function useContactsShell(): ContactsShellContextValue;
14
+ /**
15
+ * The Contacts app's shell: unlike `MailShell` (a folder tree, since a mailbox has many mail folders),
16
+ * a mailbox has exactly one well-known `contacts` folder — see `BaseMailboxRoute.create()`'s eager
17
+ * provisioning in `@rapidmx/restapi`, which guarantees it exists for every mailbox. This shell's own
18
+ * sidebar is therefore just a mailbox switcher (when the caller has more than one); the actual contact
19
+ * list/detail UI is `apps/www/contacts/index.tsx`'s own content, rendered as `children` here.
20
+ */
21
+ export default function ContactsShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }: PropsWithChildren<ContactsShellProps>): React.JSX.Element;
@@ -24,7 +24,7 @@ export function useContactsShell() {
24
24
  * sidebar is therefore just a mailbox switcher (when the caller has more than one); the actual contact
25
25
  * list/detail UI is `apps/www/contacts/index.tsx`'s own content, rendered as `children` here.
26
26
  */
27
- export default function ContactsShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, children, }) {
27
+ export default function ContactsShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }) {
28
28
  const [status, setStatus] = useState("checking");
29
29
  const [error, setError] = useState(null);
30
30
  const [mailboxes, setMailboxes] = useState([]);
@@ -89,5 +89,5 @@ export default function ContactsShell({ userUid, authServerUrl, impersonating, i
89
89
  }, children: mailboxes.map((mb) => (_jsxs("option", { value: mb.uid, children: [mb.displayName, mb.ownerUserUid ? "" : " (shared)"] }, mb.uid))) })] })), folderError && _jsx(Alert, { children: folderError })] }));
90
90
  inner = (_jsxs(_Fragment, { children: [_jsx("aside", { className: "hidden md:flex w-56 shrink-0 bg-surface border-r border-border flex-col p-3 gap-3", children: sidebarContent("desktop") }), _jsx(Drawer, { open: drawerOpen, onClose: () => setDrawerOpen(false), title: "Mailbox", children: _jsx("div", { className: "flex flex-col gap-3", children: sidebarContent("mobile") }) }), _jsxs("main", { className: "flex-1 min-w-0 flex flex-col", children: [(mailboxes.length > 1 || folderError) && (_jsx("button", { type: "button", className: "md:hidden m-3 w-9 h-9 flex items-center justify-center rounded-sm text-text-muted hover:bg-surface-alt hover:text-text", "aria-label": "Open mailbox switcher", onClick: () => setDrawerOpen(true), children: _jsx(HiOutlineBars3, { size: 20, "aria-hidden": "true" }) })), _jsx(ContactsShellContext.Provider, { value: contextValue, children: children })] })] }));
91
91
  }
92
- return (_jsx(AppShell, { active: "contacts", userUid: userUid, authServerUrl: authServerUrl, impersonating: impersonating, impersonationBaseUrl: impersonationBaseUrl, trusted: trusted, children: inner }));
92
+ return (_jsx(AppShell, { active: "contacts", userUid: userUid, authServerUrl: authServerUrl, impersonating: impersonating, impersonationBaseUrl: impersonationBaseUrl, trusted: trusted, pluginNav: pluginNav, children: inner }));
93
93
  }
@@ -0,0 +1,25 @@
1
+ import "../../../styles/app.css";
2
+ import React, { PropsWithChildren } from "react";
3
+ export type EscrowSection = "matters" | "auditLog";
4
+ export interface EscrowShellProps {
5
+ active: EscrowSection;
6
+ userUid?: string;
7
+ authServerUrl?: string;
8
+ }
9
+ /**
10
+ * Gates every `apps/escrow` page behind "is this an authenticated caller at all" — deliberately **not**
11
+ * the same "does a canary endpoint 403 me" pattern `AdminShell` uses for the trusted `admin` role.
12
+ * `specs/end-to-end_encryption.md`'s "Separation of duties" makes holder-ness a per-`EscrowScope` grant
13
+ * (`EscrowScope.holderUserUids`), checked directly against `holderUserUids` in every restapi route that
14
+ * needs it (`requireEscrowHolder()`) — never a role, and never global. There is no canary endpoint a
15
+ * non-holder gets a clean 403 from: `GET /escrow/matters` (`BaseMatterRoute.find()`) returns `200 []` for
16
+ * *any* authenticated caller who holds no scope at all, the same as it would for a holder of zero matters
17
+ * — so unlike `AdminShell`, this shell cannot distinguish "holds nothing" from "holds no scope at all" up
18
+ * front, and does not try to. It only confirms the caller is signed in and the API is reachable; every
19
+ * actual holder-gated action (create a Matter, approve/deny a request, read material) is enforced
20
+ * server-side and surfaces its own 403 `ApiRequestError` on the specific page that attempted it, same as
21
+ * any other gated action elsewhere in this codebase. A non-holder who reaches `/escrow` simply sees an
22
+ * empty Matters list and gets a real error if they try to act on something they don't hold — this is a
23
+ * deliberate, documented scope trim rather than an oversight (see this session's own report on it).
24
+ */
25
+ export default function EscrowShell({ active, userUid, authServerUrl, children }: PropsWithChildren<EscrowShellProps>): React.JSX.Element;
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ export interface StringListFieldProps {
3
+ label: string;
4
+ /** Associates the label with the add-input via `htmlFor`/`id`, same as `FormField` — required so the
5
+ * field is reachable via `getByLabelText()` in tests and by assistive tech, not just visually. */
6
+ id: string;
7
+ values: string[];
8
+ onChange: (values: string[]) => void;
9
+ placeholder?: string;
10
+ emptyMessage?: string;
11
+ disabled?: boolean;
12
+ }
13
+ /**
14
+ * A plain, controlled add/remove list-of-strings editor — the same visual add-row/per-item-remove shape
15
+ * `MemberListCard` already established for a `DistributionList`'s `memberAddresses`, but operating on a
16
+ * local `value`/`onChange` pair rather than persisting itself via its own API call. `MemberListCard`
17
+ * isn't reusable here as-is: `EscrowScope.holderUserUids` (at least one required) and
18
+ * `Matter.custodianMailboxUids` (also non-empty) are both required *at creation time*, before there's any
19
+ * uid yet to `PUT` against — this needs to compose into an ordinary create/edit form's own local state
20
+ * instead, same as every other field on those forms.
21
+ */
22
+ export default function StringListField({ label, id, values, onChange, placeholder, emptyMessage, disabled, }: StringListFieldProps): React.JSX.Element;
@@ -0,0 +1,49 @@
1
+ import "../../styles/app.css";
2
+ import React, { PropsWithChildren } from "react";
3
+ import type { IconType } from "react-icons";
4
+ import { NavItem } from "@rapidmx/react-shared/components/navigation/BottomTabBar.js";
5
+ import { PluginNav, PluginNavProps } from "../../plugins/pluginNav.js";
6
+ /** How long sign-out waits for auth-server's logout before navigating anyway. */
7
+ export declare const LOGOUT_TIMEOUT_MS = 3000;
8
+ export type AppShellApp = "mail" | "calendar" | "contacts" | "tasks";
9
+ /** `"settings"` is a valid `active` value but deliberately has no entry in `APPS` below — Settings is
10
+ * reached via a `UserMenu` item, not a 5th rail icon (see `SettingsShell.tsx`), so it highlights no
11
+ * rail/tab icon at all; only the header title needs to account for it. Any other string is a plugin's
12
+ * `appRail` item id (see `PluginNav`). */
13
+ export type AppShellActive = AppShellApp | "settings" | (string & {});
14
+ export interface AppShellProps extends PluginNavProps {
15
+ /** Which icon in the rail is highlighted as the current app — `"settings"` highlights none, and a plugin
16
+ * app page passes its own `appRail` item id. */
17
+ active: AppShellActive;
18
+ /** Populated automatically by the framework from an authenticated request (e.g. a valid `jwt` cookie). */
19
+ userUid?: string;
20
+ /** auth-server's base URL, injected via the route's `fetchProps`. */
21
+ authServerUrl?: string;
22
+ /**
23
+ * `true` when this session is an admin "log in as user" impersonation (a `jwt_impersonator` cookie is
24
+ * present) — see `MailShell`'s original doc comment, unchanged now that this lives here. Drives the
25
+ * "you are viewing as this user — stop impersonating" banner below.
26
+ */
27
+ impersonating?: boolean;
28
+ /** Where `mailApi.ts`'s `stopImpersonating()` should call — see `MailShell`'s original doc comment. */
29
+ impersonationBaseUrl?: string;
30
+ /** `true` when the caller's JWT carries a trusted role — shows an "Admin" item in the user menu below. */
31
+ trusted?: boolean;
32
+ }
33
+ export interface AppDef {
34
+ id: AppShellApp;
35
+ href: string;
36
+ label: string;
37
+ icon: IconType;
38
+ }
39
+ export declare const APPS: AppDef[];
40
+ /** `APPS` followed by the plugins' `appRail` items (generic icon), core ids winning - see `mergePluginNavItems`. */
41
+ export declare function appRailItems(pluginNav?: PluginNav): NavItem[];
42
+ /**
43
+ * The persistent chrome shared by every webmail app (Mail, Calendar, Contacts, Tasks): a left icon rail for
44
+ * switching apps (full page loads — see `MailShell`'s original doc comment on this framework having no
45
+ * client-side router), a header with the current app's name and `UserMenu`, and the impersonation banner.
46
+ * Each app's own shell (e.g. `MailShell`) renders its own contextual sidebar + content as `children`, inside
47
+ * the area to the right of the icon rail and below the header.
48
+ */
49
+ export default function AppShell({ active, userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }: PropsWithChildren<AppShellProps>): React.JSX.Element;