@rapidmx/web-client 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -2
- package/apps/admin/_layout.tsx +8 -2
- package/apps/admin/index.tsx +7 -1
- package/apps/admin/ingest-queue/index.tsx +2 -1
- package/apps/admin/mailboxes/[uid].tsx +23 -10
- package/apps/admin/quarantine/index.tsx +2 -1
- package/apps/admin/signing-certificates/index.tsx +343 -0
- package/apps/escrow/_layout.tsx +8 -2
- package/apps/shared/appearance/AppearanceForm.tsx +392 -0
- package/apps/shared/appearance/AppearanceHead.tsx +49 -0
- package/apps/shared/appearance/AppearanceProvider.tsx +470 -0
- package/apps/shared/appearance/ColorField.tsx +107 -0
- package/apps/shared/appearance/appearanceCache.ts +106 -0
- package/apps/shared/appearance/appearanceContext.ts +77 -0
- package/apps/shared/appearance/bootScript.ts +35 -0
- package/apps/shared/appearance/color.ts +169 -0
- package/apps/shared/appearance/photo.ts +88 -0
- package/apps/shared/appearance/resolvedTheme.ts +67 -0
- package/apps/shared/appearance/theme.ts +355 -0
- package/apps/shared/components/admin/layout/AdminShell.tsx +22 -10
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +107 -60
- package/apps/shared/components/admin/settings/BrandingForm.tsx +24 -0
- package/apps/shared/components/admin/settings/PluginsManager.tsx +228 -25
- package/apps/shared/components/admin/setup/SetupWizard.tsx +2 -1
- package/apps/shared/components/calendar/CalendarListSidebar.tsx +4 -5
- package/apps/shared/components/contacts/ContactForm.tsx +11 -5
- package/apps/shared/components/contacts/ContactsSidebar.tsx +5 -3
- package/apps/shared/components/contacts/ContactsToolbar.tsx +25 -52
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +10 -5
- package/apps/shared/components/escrow/layout/EscrowShell.tsx +13 -6
- package/apps/shared/components/layout/AppShell.tsx +98 -32
- package/apps/shared/components/layout/BrandingChrome.tsx +200 -13
- package/apps/shared/components/layout/RailIcon.tsx +100 -0
- package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -0
- package/apps/shared/components/layout/ThemeSwitch.tsx +84 -0
- package/apps/shared/components/layout/UserMenu.tsx +75 -6
- package/apps/shared/components/mail/ConversationList.tsx +4 -2
- package/apps/shared/components/mail/ConversationThreadPane.tsx +487 -475
- package/apps/shared/components/mail/LazyReadingPane.tsx +14 -16
- package/apps/shared/components/mail/MailSelectionBar.tsx +2 -10
- package/apps/shared/components/mail/MessageDetailPane.tsx +366 -287
- package/apps/shared/components/mail/OutboxBadge.tsx +44 -0
- package/apps/shared/components/mail/OutboxRowStatus.tsx +31 -0
- package/apps/shared/components/mail/compose/ComposeContext.tsx +35 -2
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +239 -437
- package/apps/shared/components/mail/compose/encryptionRequirement.ts +86 -0
- package/apps/shared/components/mail/layout/MailShell.tsx +39 -25
- package/apps/shared/components/mail/reading/EncryptedBody.tsx +65 -0
- package/apps/shared/components/mail/reading/EncryptedPreview.tsx +35 -0
- package/apps/shared/components/mail/reading/MessageBody.tsx +240 -0
- package/apps/shared/components/mail/reading/MessageCard.tsx +174 -0
- package/apps/shared/components/mail/reading/bodyContent.ts +100 -0
- package/apps/shared/components/mail/reading/bodyHtml.ts +298 -0
- package/apps/shared/components/mail/reading/color.ts +245 -0
- package/apps/shared/components/mail/reading/frameControl.ts +142 -0
- package/apps/shared/components/mail/reading/frameDocument.ts +123 -0
- package/apps/shared/components/mail/reading/safeDocument.ts +24 -0
- package/apps/shared/components/mail/reading/themeAdaptation.ts +221 -0
- package/apps/shared/components/mail/reading/themeSurface.ts +90 -0
- package/apps/shared/components/mail/reading/viewOriginal.ts +38 -0
- package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -0
- package/apps/shared/components/settings/layout/SettingsShell.tsx +1 -0
- package/apps/shared/components/sharing/PrincipalPicker.tsx +145 -0
- package/apps/shared/components/tasks/TasksSidebar.tsx +5 -3
- package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -5
- package/apps/shared/mail/folderCounts.ts +9 -3
- package/apps/shared/mail/folderTree.ts +143 -0
- package/apps/shared/mail/outbox/UnlockBridge.tsx +23 -0
- package/apps/shared/mail/outbox/composeBridge.ts +69 -0
- package/apps/shared/mail/outbox/outboxState.ts +88 -0
- package/apps/shared/mail/outbox/pendingSends.ts +146 -0
- package/apps/shared/mail/outbox/sendDecision.ts +157 -0
- package/apps/shared/mail/outbox/sendJob.ts +427 -0
- package/apps/shared/mail/outbox/sendOutcomes.ts +146 -0
- package/apps/shared/mail/outbox/sendState.ts +35 -0
- package/apps/shared/mail/outbox/useOutboxStatus.ts +61 -0
- package/apps/shared/mail/useMailConnection.ts +81 -28
- package/apps/shared/mail/useMailLiveUpdates.ts +55 -10
- package/apps/shared/mail/useNewMailNotifications.ts +47 -32
- package/apps/shared/navigation/AppRouter.tsx +2 -2
- package/apps/shared/notifications/NotificationCenter.tsx +215 -0
- package/apps/shared/notifications/NotificationHistoryDialog.tsx +90 -0
- package/apps/shared/notifications/apiErrors.ts +98 -0
- package/apps/shared/notifications/headerOffset.ts +35 -0
- package/apps/shared/notifications/pushStatus.ts +56 -0
- package/apps/shared/notifications/store.ts +549 -0
- package/apps/shared/notifications/systemErrors.ts +63 -0
- package/apps/shared/notifications/useNotifications.ts +41 -0
- package/apps/shared/signing/enrollmentStorage.ts +33 -0
- package/apps/shared/signing/enrollmentTracker.ts +385 -0
- package/apps/shared/signing/enrollmentView.ts +251 -0
- package/apps/shared/signing/signingInfo.ts +58 -0
- package/apps/shared/signing/useNow.ts +19 -0
- package/apps/shared/signing/useSigningEnrollmentWatcher.ts +90 -0
- package/apps/shared/styles/app.css +80 -0
- package/apps/www/_layout.tsx +8 -2
- package/apps/www/_routes.ts +1 -0
- package/apps/www/calendar/index.tsx +9 -14
- package/apps/www/contacts/[uid].tsx +5 -8
- package/apps/www/contacts/index.tsx +587 -584
- package/apps/www/index.tsx +38 -27
- package/apps/www/messages/[uid].tsx +3 -1
- package/apps/www/settings/appearance/index.tsx +21 -0
- package/apps/www/settings/auto-reply/index.tsx +3 -7
- package/apps/www/settings/encryption/index.tsx +151 -116
- package/apps/www/settings/filters/[uid].tsx +3 -1
- package/apps/www/settings/filters/new/index.tsx +3 -1
- package/apps/www/settings/privacy/index.tsx +3 -8
- package/apps/www/settings/read-receipts/index.tsx +3 -7
- package/apps/www/settings/sharing/index.tsx +44 -65
- package/apps/www/settings/signatures/[uid].tsx +3 -1
- package/apps/www/settings/signatures/new/index.tsx +3 -2
- package/apps/www/tasks/index.tsx +11 -16
- package/dist/apps/admin/_layout.d.ts +5 -1
- package/dist/apps/admin/_layout.js +3 -2
- package/dist/apps/admin/index.js +3 -2
- package/dist/apps/admin/ingest-queue/index.js +2 -1
- package/dist/apps/admin/mailboxes/[uid].js +9 -6
- package/dist/apps/admin/quarantine/index.js +2 -1
- package/dist/apps/admin/signing-certificates/index.d.ts +3 -0
- package/dist/apps/admin/signing-certificates/index.js +161 -0
- package/dist/apps/escrow/_layout.d.ts +5 -1
- package/dist/apps/escrow/_layout.js +3 -2
- package/dist/apps/shared/appearance/AppearanceForm.d.ts +7 -0
- package/dist/apps/shared/appearance/AppearanceForm.js +131 -0
- package/dist/apps/shared/appearance/AppearanceHead.d.ts +15 -0
- package/dist/apps/shared/appearance/AppearanceHead.js +31 -0
- package/dist/apps/shared/appearance/AppearanceProvider.d.ts +32 -0
- package/dist/apps/shared/appearance/AppearanceProvider.js +390 -0
- package/dist/apps/shared/appearance/ColorField.d.ts +22 -0
- package/dist/apps/shared/appearance/ColorField.js +40 -0
- package/dist/apps/shared/appearance/appearanceCache.d.ts +45 -0
- package/dist/apps/shared/appearance/appearanceCache.js +80 -0
- package/dist/apps/shared/appearance/appearanceContext.d.ts +45 -0
- package/dist/apps/shared/appearance/appearanceContext.js +29 -0
- package/dist/apps/shared/appearance/bootScript.d.ts +16 -0
- package/dist/apps/shared/appearance/bootScript.js +33 -0
- package/dist/apps/shared/appearance/color.d.ts +65 -0
- package/dist/apps/shared/appearance/color.js +132 -0
- package/dist/apps/shared/appearance/photo.d.ts +17 -0
- package/dist/apps/shared/appearance/photo.js +79 -0
- package/dist/apps/shared/appearance/resolvedTheme.d.ts +21 -0
- package/dist/apps/shared/appearance/resolvedTheme.js +55 -0
- package/dist/apps/shared/appearance/theme.d.ts +111 -0
- package/dist/apps/shared/appearance/theme.js +251 -0
- package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
- package/dist/apps/shared/components/admin/layout/AdminShell.js +16 -6
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.d.ts +10 -5
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +49 -34
- package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +2 -0
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +7 -1
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +106 -20
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +2 -1
- package/dist/apps/shared/components/calendar/CalendarListSidebar.js +8 -8
- package/dist/apps/shared/components/contacts/ContactForm.js +1 -1
- package/dist/apps/shared/components/contacts/ContactsSidebar.js +4 -2
- package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +3 -1
- package/dist/apps/shared/components/contacts/ContactsToolbar.js +24 -9
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +4 -1
- package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -4
- package/dist/apps/shared/components/layout/AppShell.d.ts +6 -1
- package/dist/apps/shared/components/layout/AppShell.js +59 -13
- package/dist/apps/shared/components/layout/BrandingChrome.d.ts +69 -10
- package/dist/apps/shared/components/layout/BrandingChrome.js +140 -12
- package/dist/apps/shared/components/layout/RailIcon.d.ts +21 -0
- package/dist/apps/shared/components/layout/RailIcon.js +85 -0
- package/dist/apps/shared/components/layout/ResponsiveToolbar.d.ts +55 -0
- package/dist/apps/shared/components/layout/ResponsiveToolbar.js +199 -0
- package/dist/apps/shared/components/layout/ThemeSwitch.d.ts +9 -0
- package/dist/apps/shared/components/layout/ThemeSwitch.js +54 -0
- package/dist/apps/shared/components/layout/UserMenu.d.ts +7 -1
- package/dist/apps/shared/components/layout/UserMenu.js +41 -7
- package/dist/apps/shared/components/mail/ConversationList.js +3 -2
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +2 -1
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +42 -20
- package/dist/apps/shared/components/mail/LazyReadingPane.js +9 -8
- package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +3 -4
- package/dist/apps/shared/components/mail/MailSelectionBar.js +2 -3
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +18 -7
- package/dist/apps/shared/components/mail/MessageDetailPane.js +86 -41
- package/dist/apps/shared/components/mail/OutboxBadge.d.ts +17 -0
- package/dist/apps/shared/components/mail/OutboxBadge.js +18 -0
- package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +10 -0
- package/dist/apps/shared/components/mail/OutboxRowStatus.js +19 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +8 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +28 -3
- package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +2 -1
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +229 -328
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +49 -0
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +35 -0
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +2 -0
- package/dist/apps/shared/components/mail/layout/MailShell.js +20 -14
- package/dist/apps/shared/components/mail/reading/EncryptedBody.d.ts +21 -0
- package/dist/apps/shared/components/mail/reading/EncryptedBody.js +22 -0
- package/dist/apps/shared/components/mail/reading/EncryptedPreview.d.ts +18 -0
- package/dist/apps/shared/components/mail/reading/EncryptedPreview.js +22 -0
- package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +32 -0
- package/dist/apps/shared/components/mail/reading/MessageBody.js +136 -0
- package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +62 -0
- package/dist/apps/shared/components/mail/reading/MessageCard.js +58 -0
- package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +38 -0
- package/dist/apps/shared/components/mail/reading/bodyContent.js +84 -0
- package/dist/apps/shared/components/mail/reading/bodyHtml.d.ts +47 -0
- package/dist/apps/shared/components/mail/reading/bodyHtml.js +267 -0
- package/dist/apps/shared/components/mail/reading/color.d.ts +63 -0
- package/dist/apps/shared/components/mail/reading/color.js +201 -0
- package/dist/apps/shared/components/mail/reading/frameControl.d.ts +33 -0
- package/dist/apps/shared/components/mail/reading/frameControl.js +120 -0
- package/dist/apps/shared/components/mail/reading/frameDocument.d.ts +52 -0
- package/dist/apps/shared/components/mail/reading/frameDocument.js +98 -0
- package/dist/apps/shared/components/mail/reading/safeDocument.d.ts +3 -0
- package/dist/apps/shared/components/mail/reading/safeDocument.js +12 -0
- package/dist/apps/shared/components/mail/reading/themeAdaptation.d.ts +56 -0
- package/dist/apps/shared/components/mail/reading/themeAdaptation.js +144 -0
- package/dist/apps/shared/components/mail/reading/themeSurface.d.ts +17 -0
- package/dist/apps/shared/components/mail/reading/themeSurface.js +83 -0
- package/dist/apps/shared/components/mail/reading/viewOriginal.d.ts +9 -0
- package/dist/apps/shared/components/mail/reading/viewOriginal.js +32 -0
- package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +32 -0
- package/dist/apps/shared/components/settings/SigningCertificateCard.js +98 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +1 -0
- package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +26 -0
- package/dist/apps/shared/components/sharing/PrincipalPicker.js +77 -0
- package/dist/apps/shared/components/tasks/TasksSidebar.js +4 -2
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +3 -1
- package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
- package/dist/apps/shared/mail/folderCounts.js +9 -3
- package/dist/apps/shared/mail/folderTree.d.ts +55 -0
- package/dist/apps/shared/mail/folderTree.js +116 -0
- package/dist/apps/shared/mail/outbox/UnlockBridge.d.ts +5 -0
- package/dist/apps/shared/mail/outbox/UnlockBridge.js +18 -0
- package/dist/apps/shared/mail/outbox/composeBridge.d.ts +34 -0
- package/dist/apps/shared/mail/outbox/composeBridge.js +32 -0
- package/dist/apps/shared/mail/outbox/outboxState.d.ts +41 -0
- package/dist/apps/shared/mail/outbox/outboxState.js +42 -0
- package/dist/apps/shared/mail/outbox/pendingSends.d.ts +39 -0
- package/dist/apps/shared/mail/outbox/pendingSends.js +109 -0
- package/dist/apps/shared/mail/outbox/sendDecision.d.ts +80 -0
- package/dist/apps/shared/mail/outbox/sendDecision.js +94 -0
- package/dist/apps/shared/mail/outbox/sendJob.d.ts +68 -0
- package/dist/apps/shared/mail/outbox/sendJob.js +319 -0
- package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +3 -0
- package/dist/apps/shared/mail/outbox/sendOutcomes.js +141 -0
- package/dist/apps/shared/mail/outbox/sendState.d.ts +25 -0
- package/dist/apps/shared/mail/outbox/sendState.js +8 -0
- package/dist/apps/shared/mail/outbox/useOutboxStatus.d.ts +11 -0
- package/dist/apps/shared/mail/outbox/useOutboxStatus.js +49 -0
- package/dist/apps/shared/mail/useMailConnection.d.ts +9 -8
- package/dist/apps/shared/mail/useMailConnection.js +64 -22
- package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +17 -4
- package/dist/apps/shared/mail/useMailLiveUpdates.js +36 -10
- package/dist/apps/shared/mail/useNewMailNotifications.d.ts +4 -10
- package/dist/apps/shared/mail/useNewMailNotifications.js +43 -23
- package/dist/apps/shared/navigation/AppRouter.js +2 -2
- package/dist/apps/shared/notifications/NotificationCenter.d.ts +19 -0
- package/dist/apps/shared/notifications/NotificationCenter.js +90 -0
- package/dist/apps/shared/notifications/NotificationHistoryDialog.d.ts +10 -0
- package/dist/apps/shared/notifications/NotificationHistoryDialog.js +36 -0
- package/dist/apps/shared/notifications/apiErrors.d.ts +19 -0
- package/dist/apps/shared/notifications/apiErrors.js +86 -0
- package/dist/apps/shared/notifications/headerOffset.d.ts +8 -0
- package/dist/apps/shared/notifications/headerOffset.js +33 -0
- package/dist/apps/shared/notifications/pushStatus.d.ts +9 -0
- package/dist/apps/shared/notifications/pushStatus.js +54 -0
- package/dist/apps/shared/notifications/store.d.ts +136 -0
- package/dist/apps/shared/notifications/store.js +416 -0
- package/dist/apps/shared/notifications/systemErrors.d.ts +9 -0
- package/dist/apps/shared/notifications/systemErrors.js +58 -0
- package/dist/apps/shared/notifications/useNotifications.d.ts +17 -0
- package/dist/apps/shared/notifications/useNotifications.js +18 -0
- package/dist/apps/shared/signing/enrollmentStorage.d.ts +10 -0
- package/dist/apps/shared/signing/enrollmentStorage.js +33 -0
- package/dist/apps/shared/signing/enrollmentTracker.d.ts +73 -0
- package/dist/apps/shared/signing/enrollmentTracker.js +307 -0
- package/dist/apps/shared/signing/enrollmentView.d.ts +57 -0
- package/dist/apps/shared/signing/enrollmentView.js +214 -0
- package/dist/apps/shared/signing/signingInfo.d.ts +11 -0
- package/dist/apps/shared/signing/signingInfo.js +48 -0
- package/dist/apps/shared/signing/useNow.d.ts +2 -0
- package/dist/apps/shared/signing/useNow.js +18 -0
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +16 -0
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +87 -0
- package/dist/apps/shared/styles/app.css +80 -0
- package/dist/apps/www/_layout.d.ts +5 -1
- package/dist/apps/www/_layout.js +3 -2
- package/dist/apps/www/_routes.js +1 -0
- package/dist/apps/www/calendar/index.js +3 -4
- package/dist/apps/www/contacts/[uid].js +5 -7
- package/dist/apps/www/contacts/index.js +14 -21
- package/dist/apps/www/index.js +37 -19
- package/dist/apps/www/messages/[uid].js +3 -1
- package/dist/apps/www/settings/appearance/index.d.ts +4 -0
- package/dist/apps/www/settings/appearance/index.js +13 -0
- package/dist/apps/www/settings/auto-reply/index.js +3 -6
- package/dist/apps/www/settings/encryption/index.js +103 -95
- package/dist/apps/www/settings/filters/[uid].js +3 -1
- package/dist/apps/www/settings/filters/new/index.js +3 -1
- package/dist/apps/www/settings/privacy/index.js +5 -8
- package/dist/apps/www/settings/read-receipts/index.js +3 -6
- package/dist/apps/www/settings/sharing/index.js +19 -33
- package/dist/apps/www/settings/signatures/[uid].js +3 -1
- package/dist/apps/www/settings/signatures/new/index.js +3 -2
- package/dist/apps/www/tasks/index.js +8 -14
- package/package.json +2 -2
- package/apps/shared/components/mail/NewMailToasts.tsx +0 -141
- package/apps/shared/components/mail/compose/SendFailureAlert.tsx +0 -48
- package/dist/apps/shared/components/mail/NewMailToasts.d.ts +0 -19
- package/dist/apps/shared/components/mail/NewMailToasts.js +0 -55
- package/dist/apps/shared/components/mail/compose/SendFailureAlert.d.ts +0 -14
- package/dist/apps/shared/components/mail/compose/SendFailureAlert.js +0 -11
|
@@ -3,21 +3,22 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import { routedPage } from "../../_routedPage.js";
|
|
6
|
-
import React, {
|
|
6
|
+
import React, { useEffect, useState } from "react";
|
|
7
7
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
8
|
import {
|
|
9
9
|
MailboxAccessMember,
|
|
10
10
|
MailboxAccessRole,
|
|
11
11
|
listMailboxAccess,
|
|
12
|
-
lookupMailboxOwnerByEmail,
|
|
13
12
|
removeMailboxAccess,
|
|
14
13
|
setMailboxAccess,
|
|
15
14
|
} from "@rapidmx/react-shared/mail/mailboxAccessApi.js";
|
|
16
15
|
import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
16
|
+
import PrincipalPicker from "../../../shared/components/sharing/PrincipalPicker.js";
|
|
17
17
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
18
18
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
19
19
|
import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
|
|
20
20
|
import { SkeletonList } from "@rapidmx/react-shared/components/feedback/Skeleton.js";
|
|
21
|
+
import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
|
|
21
22
|
|
|
22
23
|
const ROLE_LABELS: Record<MailboxAccessRole, string> = { viewer: "Can view", manager: "Can manage" };
|
|
23
24
|
|
|
@@ -49,10 +50,8 @@ function SharingContent() {
|
|
|
49
50
|
const [status, setStatus] = useState<Status>("loading");
|
|
50
51
|
const [error, setError] = useState<string | null>(null);
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
const [
|
|
54
|
-
const [adding, setAdding] = useState(false);
|
|
55
|
-
const [addError, setAddError] = useState<string | null>(null);
|
|
53
|
+
// An entry that is not a user uid, being replaced with the user it was meant for.
|
|
54
|
+
const [replacing, setReplacing] = useState<MailboxAccessMember | null>(null);
|
|
56
55
|
|
|
57
56
|
const [pendingRoleChange, setPendingRoleChange] = useState<string | null>(null);
|
|
58
57
|
const [removeTarget, setRemoveTarget] = useState<MailboxAccessMember | null>(null);
|
|
@@ -84,39 +83,27 @@ function SharingContent() {
|
|
|
84
83
|
|
|
85
84
|
useEffect(reload, [mailboxUid]);
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const found = await lookupMailboxOwnerByEmail(email);
|
|
97
|
-
if (!found) {
|
|
98
|
-
setAddError("No one found with that email on this platform.");
|
|
99
|
-
return;
|
|
86
|
+
// Once the person is granted, the entry they replace (if any) is removed: the string it named never matched anyone.
|
|
87
|
+
async function handleGranted() {
|
|
88
|
+
const stale = replacing;
|
|
89
|
+
setReplacing(null);
|
|
90
|
+
if (stale) {
|
|
91
|
+
try {
|
|
92
|
+
await removeMailboxAccess(mailboxUid!, stale.userOrRoleId);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
notifyApiError(err, "Couldn't remove the old entry");
|
|
100
95
|
}
|
|
101
|
-
await setMailboxAccess(mailboxUid!, found.userUid, newRole);
|
|
102
|
-
setEmailInput("");
|
|
103
|
-
setNewRole("viewer");
|
|
104
|
-
reload();
|
|
105
|
-
} catch (err) {
|
|
106
|
-
setAddError(err instanceof ApiRequestError ? err.message : "Could not grant access.");
|
|
107
|
-
} finally {
|
|
108
|
-
setAdding(false);
|
|
109
96
|
}
|
|
97
|
+
reload();
|
|
110
98
|
}
|
|
111
99
|
|
|
112
100
|
async function handleRoleChange(userOrRoleId: string, role: MailboxAccessRole) {
|
|
113
101
|
setPendingRoleChange(userOrRoleId);
|
|
114
|
-
setError(null);
|
|
115
102
|
try {
|
|
116
103
|
await setMailboxAccess(mailboxUid!, userOrRoleId, role);
|
|
117
104
|
reload();
|
|
118
105
|
} catch (err) {
|
|
119
|
-
|
|
106
|
+
notifyApiError(err, "Couldn't change this member's role");
|
|
120
107
|
} finally {
|
|
121
108
|
setPendingRoleChange(null);
|
|
122
109
|
}
|
|
@@ -130,13 +117,12 @@ function SharingContent() {
|
|
|
130
117
|
// Only reachable from the confirm modal's own "Remove" button, and `Modal` renders nothing at all
|
|
131
118
|
// unless `removeTarget` is set, so `removeTarget` is always non-null here.
|
|
132
119
|
setRemoving(true);
|
|
133
|
-
setError(null);
|
|
134
120
|
try {
|
|
135
121
|
await removeMailboxAccess(mailboxUid!, removeTarget!.userOrRoleId);
|
|
136
122
|
setRemoveTarget(null);
|
|
137
123
|
reload();
|
|
138
124
|
} catch (err) {
|
|
139
|
-
|
|
125
|
+
notifyApiError(err, "Couldn't remove this member");
|
|
140
126
|
} finally {
|
|
141
127
|
setRemoving(false);
|
|
142
128
|
}
|
|
@@ -164,8 +150,6 @@ function SharingContent() {
|
|
|
164
150
|
|
|
165
151
|
{status === "ready" && (
|
|
166
152
|
<>
|
|
167
|
-
{error && <Alert>{error}</Alert>}
|
|
168
|
-
|
|
169
153
|
<div>
|
|
170
154
|
<h2 className="text-sm font-semibold mb-2">People with access</h2>
|
|
171
155
|
{members.length === 0 ? (
|
|
@@ -177,8 +161,20 @@ function SharingContent() {
|
|
|
177
161
|
key={member.userOrRoleId}
|
|
178
162
|
className="flex items-center justify-between gap-3 text-sm py-1.5 px-3 bg-surface-alt rounded-sm"
|
|
179
163
|
>
|
|
180
|
-
<span className="truncate">
|
|
164
|
+
<span className="truncate">
|
|
165
|
+
{member.userOrRoleId}
|
|
166
|
+
{member.noEffect && (
|
|
167
|
+
<span className="block text-xs text-danger">
|
|
168
|
+
Not a user - this entry has no effect. Replace it with the person it was meant for, or remove it.
|
|
169
|
+
</span>
|
|
170
|
+
)}
|
|
171
|
+
</span>
|
|
181
172
|
<span className="flex items-center gap-2 shrink-0">
|
|
173
|
+
{member.noEffect && (
|
|
174
|
+
<Button type="button" variant="text" className="!w-auto" onClick={() => setReplacing(member)}>
|
|
175
|
+
Replace with a user
|
|
176
|
+
</Button>
|
|
177
|
+
)}
|
|
182
178
|
<select
|
|
183
179
|
aria-label={`Role for ${member.userOrRoleId}`}
|
|
184
180
|
className="text-sm border border-border rounded-sm py-1 px-2 bg-surface"
|
|
@@ -216,37 +212,20 @@ function SharingContent() {
|
|
|
216
212
|
)}
|
|
217
213
|
</div>
|
|
218
214
|
|
|
219
|
-
<
|
|
220
|
-
<h2 className="text-sm font-semibold">Add someone</h2>
|
|
221
|
-
{
|
|
222
|
-
<
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
aria-label="Role to grant"
|
|
234
|
-
className="text-sm border border-border rounded-sm py-1.5 px-2 bg-surface"
|
|
235
|
-
value={newRole}
|
|
236
|
-
onChange={(e) => setNewRole(e.target.value as MailboxAccessRole)}
|
|
237
|
-
disabled={adding}
|
|
238
|
-
>
|
|
239
|
-
{(Object.keys(ROLE_LABELS) as MailboxAccessRole[]).map((role) => (
|
|
240
|
-
<option key={role} value={role}>
|
|
241
|
-
{ROLE_LABELS[role]}
|
|
242
|
-
</option>
|
|
243
|
-
))}
|
|
244
|
-
</select>
|
|
245
|
-
<Button type="submit" loading={adding} disabled={adding} className="!w-auto">
|
|
246
|
-
Add
|
|
247
|
-
</Button>
|
|
248
|
-
</div>
|
|
249
|
-
</form>
|
|
215
|
+
<div className="flex flex-col gap-2">
|
|
216
|
+
<h2 className="text-sm font-semibold">{replacing ? `Replace ${replacing.userOrRoleId}` : "Add someone"}</h2>
|
|
217
|
+
{/* The person is looked up and shown - name and address - before anything is saved. */}
|
|
218
|
+
<PrincipalPicker
|
|
219
|
+
key={replacing?.userOrRoleId ?? "new"}
|
|
220
|
+
mailboxUid={mailboxUid!}
|
|
221
|
+
roleLabels={ROLE_LABELS}
|
|
222
|
+
initialPrincipal={replacing?.userOrRoleId}
|
|
223
|
+
defaultRole={replacing?.role === "manager" ? "manager" : "viewer"}
|
|
224
|
+
placeholder="Email address or username"
|
|
225
|
+
onGranted={handleGranted}
|
|
226
|
+
onCancel={replacing ? () => setReplacing(null) : undefined}
|
|
227
|
+
/>
|
|
228
|
+
</div>
|
|
250
229
|
</>
|
|
251
230
|
)}
|
|
252
231
|
|
|
@@ -12,6 +12,7 @@ import RichTextEditor from "../../../shared/components/mail/compose/RichTextEdit
|
|
|
12
12
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
13
13
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
14
14
|
import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
|
|
15
|
+
import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
|
|
15
16
|
|
|
16
17
|
const INPUT_CLASS =
|
|
17
18
|
"w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -91,7 +92,8 @@ function SignatureDetailContent({ uid }: { uid: string }) {
|
|
|
91
92
|
setOriginal(updated);
|
|
92
93
|
setSaved(true);
|
|
93
94
|
} catch (err) {
|
|
94
|
-
|
|
95
|
+
// A pop-up: `error` is only this form's own validation (and the load failure above).
|
|
96
|
+
notifyApiError(err, "Couldn't save the signature");
|
|
95
97
|
} finally {
|
|
96
98
|
setSaving(false);
|
|
97
99
|
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import { routedPage } from "../../../_routedPage.js";
|
|
6
6
|
import { useNavigate } from "../../../../shared/navigation/AppRouter.js";
|
|
7
7
|
import React, { FormEvent, useState } from "react";
|
|
8
|
-
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
9
8
|
import { createMailSignature, listMailSignatures } from "@rapidmx/react-shared/mail/mailSignaturesApi.js";
|
|
10
9
|
import { clearPreviousDefaults } from "../signatureDefaults.js";
|
|
11
10
|
import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../../shared/components/settings/layout/SettingsShell.js";
|
|
@@ -13,6 +12,7 @@ import RichTextEditor from "../../../../shared/components/mail/compose/RichTextE
|
|
|
13
12
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
14
13
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
15
14
|
import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
|
|
15
|
+
import { notifyApiError } from "../../../../shared/notifications/apiErrors.js";
|
|
16
16
|
|
|
17
17
|
const INPUT_CLASS =
|
|
18
18
|
"w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -68,7 +68,8 @@ function NewSignatureForm() {
|
|
|
68
68
|
});
|
|
69
69
|
navigate(`/settings/signatures/${encodeURIComponent(created.uid)}?mailboxUid=${encodeURIComponent(mailboxUid!)}`);
|
|
70
70
|
} catch (err) {
|
|
71
|
-
|
|
71
|
+
// A pop-up: `error` above is only the form's own validation.
|
|
72
|
+
notifyApiError(err, "Couldn't create the signature");
|
|
72
73
|
} finally {
|
|
73
74
|
setSaving(false);
|
|
74
75
|
}
|
package/apps/www/tasks/index.tsx
CHANGED
|
@@ -30,6 +30,7 @@ import { LIST_PAGE_SIZE, MAX_LIST_PAGES, listAllPages } from "../../shared/mail/
|
|
|
30
30
|
import { SHORTCUTS } from "../../shared/keyboard/keymap.js";
|
|
31
31
|
import { useShortcut } from "../../shared/keyboard/useShortcut.js";
|
|
32
32
|
import { useShortcutProps } from "../../shared/keyboard/useShortcutProps.js";
|
|
33
|
+
import { notifyApiError } from "../../shared/notifications/apiErrors.js";
|
|
33
34
|
|
|
34
35
|
const INPUT_CLASS =
|
|
35
36
|
"text-sm py-1.5 px-2 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -198,7 +199,6 @@ function TasksContent() {
|
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
async function handleToggle(task: Task) {
|
|
201
|
-
setError(null);
|
|
202
202
|
const nextCompleted = !task.completed;
|
|
203
203
|
setTasks((prev) => prev.map((t) => (t.uid === task.uid ? { ...t, completed: nextCompleted } : t)));
|
|
204
204
|
try {
|
|
@@ -206,17 +206,16 @@ function TasksContent() {
|
|
|
206
206
|
setTasks((prev) => prev.map((t) => (t.uid === task.uid ? updated : t)));
|
|
207
207
|
} catch (err) {
|
|
208
208
|
setTasks((prev) => prev.map((t) => (t.uid === task.uid ? task : t)));
|
|
209
|
-
|
|
209
|
+
notifyApiError(err, "Couldn't update the task");
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
async function handleDelete(task: Task) {
|
|
214
|
-
setError(null);
|
|
215
214
|
try {
|
|
216
215
|
await deleteTask(task.uid, task.version);
|
|
217
216
|
setTasks((prev) => prev.filter((t) => t.uid !== task.uid));
|
|
218
217
|
} catch (err) {
|
|
219
|
-
|
|
218
|
+
notifyApiError(err, "Couldn't delete the task");
|
|
220
219
|
}
|
|
221
220
|
}
|
|
222
221
|
|
|
@@ -258,46 +257,41 @@ function TasksContent() {
|
|
|
258
257
|
|
|
259
258
|
const checkedTasks = viewFiltered.filter((t) => checkedUids.has(t.uid));
|
|
260
259
|
|
|
260
|
+
// The bulk handlers try every task and report each failure: the same failure repeated is one pop-up with a count.
|
|
261
261
|
async function handleBulkComplete() {
|
|
262
|
-
let bulkError: string | null = null;
|
|
263
262
|
for (const task of checkedTasks) {
|
|
264
263
|
try {
|
|
265
264
|
await setTaskCompleted(task, true);
|
|
266
265
|
} catch (err) {
|
|
267
|
-
|
|
266
|
+
notifyApiError(err, "Couldn't update some of the tasks");
|
|
268
267
|
}
|
|
269
268
|
}
|
|
270
269
|
setCheckedUids(new Set());
|
|
271
270
|
await reload();
|
|
272
|
-
setError(bulkError);
|
|
273
271
|
}
|
|
274
272
|
|
|
275
273
|
async function handleBulkAddToMyDay() {
|
|
276
|
-
let bulkError: string | null = null;
|
|
277
274
|
for (const task of checkedTasks) {
|
|
278
275
|
try {
|
|
279
276
|
await setTaskMyDay(task, true);
|
|
280
277
|
} catch (err) {
|
|
281
|
-
|
|
278
|
+
notifyApiError(err, "Couldn't update some of the tasks");
|
|
282
279
|
}
|
|
283
280
|
}
|
|
284
281
|
setCheckedUids(new Set());
|
|
285
282
|
await reload();
|
|
286
|
-
setError(bulkError);
|
|
287
283
|
}
|
|
288
284
|
|
|
289
285
|
async function handleBulkDelete() {
|
|
290
|
-
let bulkError: string | null = null;
|
|
291
286
|
for (const task of checkedTasks) {
|
|
292
287
|
try {
|
|
293
288
|
await deleteTask(task.uid, task.version);
|
|
294
289
|
} catch (err) {
|
|
295
|
-
|
|
290
|
+
notifyApiError(err, "Couldn't delete some of the tasks");
|
|
296
291
|
}
|
|
297
292
|
}
|
|
298
293
|
setCheckedUids(new Set());
|
|
299
294
|
await reload();
|
|
300
|
-
setError(bulkError);
|
|
301
295
|
}
|
|
302
296
|
|
|
303
297
|
function requestDelete(task: Task) {
|
|
@@ -329,7 +323,8 @@ function TasksContent() {
|
|
|
329
323
|
const completedTasks = viewFiltered.filter((t) => t.completed);
|
|
330
324
|
|
|
331
325
|
return (
|
|
332
|
-
|
|
326
|
+
// Below `lg` the tasks menu is a drawer (its button sits above the list), so the list keeps the whole width.
|
|
327
|
+
<div className="flex-1 min-w-0 min-h-0 flex flex-col lg:flex-row">
|
|
333
328
|
<TasksSidebar mailboxUid={mailboxUid} tasks={tasks} userUid={userUid} active={view} onSelect={handleSelectView} />
|
|
334
329
|
<div className="flex-1 min-w-0 flex flex-col overflow-y-auto">
|
|
335
330
|
<TasksToolbar
|
|
@@ -354,13 +349,13 @@ function TasksContent() {
|
|
|
354
349
|
<FlaggedEmailList loading={flaggedLoading} error={flaggedError} messages={flaggedMessages} />
|
|
355
350
|
) : (
|
|
356
351
|
<>
|
|
357
|
-
<form onSubmit={handleCreate} className="flex items-center gap-2 bg-surface border border-border rounded-md p-2">
|
|
352
|
+
<form onSubmit={handleCreate} className="flex flex-wrap items-center gap-2 bg-surface border border-border rounded-md p-2">
|
|
358
353
|
<input
|
|
359
354
|
ref={addTaskRef}
|
|
360
355
|
type="text"
|
|
361
356
|
aria-label="Add a task"
|
|
362
357
|
{...addTaskHint}
|
|
363
|
-
className={`${INPUT_CLASS} flex-1`}
|
|
358
|
+
className={`${INPUT_CLASS} min-w-40 basis-40 flex-1`}
|
|
364
359
|
value={title}
|
|
365
360
|
onChange={(e) => setTitle(e.target.value)}
|
|
366
361
|
placeholder="Add a task"
|
|
@@ -4,5 +4,9 @@ export interface LayoutProps {
|
|
|
4
4
|
/** See `apps/www/_layout.tsx`'s `LayoutProps` doc comment - identical mechanism, supplied by
|
|
5
5
|
* `AdminConsoleRoute`'s `fetchProps()` override. */
|
|
6
6
|
branding?: Branding;
|
|
7
|
+
/** The signed-in user's stored appearance preferences (a page prop from the same `fetchProps()`, present only on the www pages and only when the user has any): rendered as a stylesheet in the `<head>` so the very first paint is themed - see `AppearanceHead`. */
|
|
8
|
+
appearance?: unknown;
|
|
9
|
+
/** The signed-in user (supplied by the framework), so a cached appearance from another account on this browser is never applied. */
|
|
10
|
+
userUid?: string;
|
|
7
11
|
}
|
|
8
|
-
export default function Layout({ children, branding }: PropsWithChildren<LayoutProps>): React.JSX.Element;
|
|
12
|
+
export default function Layout({ children, branding, appearance, userUid }: PropsWithChildren<LayoutProps>): React.JSX.Element;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AppearanceHead, appearanceHtmlAttributes } from "../shared/appearance/AppearanceHead.js";
|
|
2
3
|
import { CUSTOM_STYLESHEET_LINK_ID } from "@rapidmx/react-shared/branding/useBranding.js";
|
|
3
|
-
export default function Layout({ children, branding }) {
|
|
4
|
+
export default function Layout({ children, branding, appearance, userUid }) {
|
|
4
5
|
const title = branding?.title || branding?.companyName
|
|
5
6
|
? `${branding?.title || branding?.companyName}: Mail Admin Console`
|
|
6
7
|
: "RapidMX: Mail Admin Console";
|
|
7
8
|
const iconHref = branding?.iconUrl || branding?.logoUrl || "/images/logo.svg";
|
|
8
9
|
const stylesheetHref = branding?.stylesheetUrl;
|
|
9
|
-
return (_jsxs("html", { lang: "en", children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("title", { children: title }), _jsx("link", { rel: "icon", type: "image/svg+xml", href: iconHref }), _jsx("link", { rel: "alternate icon", href: "/favicon.ico" }), _jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }), _jsx("link", { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" }), stylesheetHref && _jsx("link", { rel: "stylesheet", href: stylesheetHref, id: CUSTOM_STYLESHEET_LINK_ID })] }), _jsx("body", { children: children })] }));
|
|
10
|
+
return (_jsxs("html", { lang: "en", ...appearanceHtmlAttributes(appearance, userUid), children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("title", { children: title }), _jsx("link", { rel: "icon", type: "image/svg+xml", href: iconHref }), _jsx("link", { rel: "alternate icon", href: "/favicon.ico" }), _jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }), _jsx("link", { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" }), stylesheetHref && _jsx("link", { rel: "stylesheet", href: stylesheetHref, id: CUSTOM_STYLESHEET_LINK_ID }), _jsx(AppearanceHead, { appearance: appearance })] }), _jsx("body", { children: children })] }));
|
|
10
11
|
}
|
package/dist/apps/admin/index.js
CHANGED
|
@@ -24,7 +24,8 @@ function MailboxesListContent() {
|
|
|
24
24
|
useEffect(() => {
|
|
25
25
|
setLoading(true);
|
|
26
26
|
setError(null);
|
|
27
|
-
|
|
27
|
+
// The administration scope: every mailbox as administrative metadata - the console never shows anybody's mail.
|
|
28
|
+
listMailboxes({ page, limit: PAGE_SIZE, scope: "admin" })
|
|
28
29
|
.then(setMailboxes)
|
|
29
30
|
.catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load mailboxes."))
|
|
30
31
|
.finally(() => setLoading(false));
|
|
@@ -44,5 +45,5 @@ function MailboxesListContent() {
|
|
|
44
45
|
setConfirmingSetup(false);
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-between mb-5", children: [_jsx("h1", { className: "text-xl font-bold uppercase tracking-wide", children: "Mailboxes" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: reopening, onClick: () => setConfirmingSetup(true), children: "Run setup again" }), _jsx("a", { href: "/admin/mailboxes/new", children: _jsx(Button, { type: "button", className: "!w-auto", children: "+ New mailbox" }) })] })] }), error && _jsx(Alert, { children: error }), _jsxs(Modal, { open: confirmingSetup, onClose: () => setConfirmingSetup(false), title: "Run setup again?", children: [_jsx("p", { className: "text-sm mb-4", children: "Setup starts again from its first step, and the admin console sends administrators to it until it's finished. Your existing settings, domains and mailboxes are kept." }), _jsxs("div", { className: "flex gap-2 justify-end", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingSetup(false), children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: reopening, disabled: reopening, onClick: () => void handleRunSetup(), children: "Run setup" })] })] }), loading ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : (_jsx(MailboxTable, { mailboxes: mailboxes })), _jsxs("div", { className: "flex gap-3 items-center mt-4", children: [_jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: page === 0 || loading, onClick: () => setPage((p) => Math.max(0, p - 1)), children: "Previous" }), _jsxs("span", { className: "text-sm text-text-muted", children: ["Page ", page + 1] }), _jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: !hasNextPage || loading, onClick: () => setPage((p) => p + 1), children: "Next" })] })] }));
|
|
48
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-between mb-5", children: [_jsx("h1", { className: "text-xl font-bold uppercase tracking-wide", children: "Mailboxes" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: reopening, onClick: () => setConfirmingSetup(true), children: "Run setup again" }), _jsx("a", { href: "/admin/mailboxes/new", children: _jsx(Button, { type: "button", className: "!w-auto", children: "+ New mailbox" }) })] })] }), _jsx("p", { className: "text-sm text-text-muted mb-4", children: "Administrative details only (addresses, owners, quota). The console never shows a mailbox\u2019s mail: to see someone\u2019s account, impersonate them from their mailbox page." }), error && _jsx(Alert, { children: error }), _jsxs(Modal, { open: confirmingSetup, onClose: () => setConfirmingSetup(false), title: "Run setup again?", children: [_jsx("p", { className: "text-sm mb-4", children: "Setup starts again from its first step, and the admin console sends administrators to it until it's finished. Your existing settings, domains and mailboxes are kept." }), _jsxs("div", { className: "flex gap-2 justify-end", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingSetup(false), children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: reopening, disabled: reopening, onClick: () => void handleRunSetup(), children: "Run setup" })] })] }), loading ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : (_jsx(MailboxTable, { mailboxes: mailboxes })), _jsxs("div", { className: "flex gap-3 items-center mt-4", children: [_jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: page === 0 || loading, onClick: () => setPage((p) => Math.max(0, p - 1)), children: "Previous" }), _jsxs("span", { className: "text-sm text-text-muted", children: ["Page ", page + 1] }), _jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: !hasNextPage || loading, onClick: () => setPage((p) => p + 1), children: "Next" })] })] }));
|
|
48
49
|
}
|
|
@@ -41,7 +41,8 @@ function IngestQueueContent() {
|
|
|
41
41
|
let cancelled = false;
|
|
42
42
|
setLoading(true);
|
|
43
43
|
setError(null);
|
|
44
|
-
|
|
44
|
+
// The administration scope: any mailbox's pending mail, recorded in the audit log.
|
|
45
|
+
listIngestQueue(mailboxUid, { page, limit: PAGE_SIZE, scope: "admin" })
|
|
45
46
|
.then((data) => {
|
|
46
47
|
if (!cancelled)
|
|
47
48
|
setEntries(data);
|
|
@@ -23,9 +23,9 @@ function formatBytes(bytes) {
|
|
|
23
23
|
return `${bytes} B`;
|
|
24
24
|
}
|
|
25
25
|
export default function MailboxDetailPage(props) {
|
|
26
|
-
return (_jsx(AdminShell, { ...props, active: "mailboxes", children: _jsx(MailboxDetailContent, { uid: props.params.uid, impersonationBaseUrl: props.impersonationBaseUrl }) }));
|
|
26
|
+
return (_jsx(AdminShell, { ...props, active: "mailboxes", children: _jsx(MailboxDetailContent, { uid: props.params.uid, impersonationBaseUrl: props.impersonationBaseUrl, currentUserUid: props.userUid }) }));
|
|
27
27
|
}
|
|
28
|
-
function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
28
|
+
function MailboxDetailContent({ uid, impersonationBaseUrl, currentUserUid, }) {
|
|
29
29
|
const [mailbox, setMailbox] = useState(null);
|
|
30
30
|
const [loading, setLoading] = useState(true);
|
|
31
31
|
const [error, setError] = useState(null);
|
|
@@ -43,7 +43,7 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
|
43
43
|
setConfirmingAccess(false);
|
|
44
44
|
setAccessError(null);
|
|
45
45
|
}
|
|
46
|
-
// Only ever invoked from the
|
|
46
|
+
// Only ever invoked from the impersonation-confirmation modal below, which only renders for a loaded mailbox that has
|
|
47
47
|
// an owner. A failure stays in the modal rather than replacing the whole page.
|
|
48
48
|
async function handleAccessMailbox() {
|
|
49
49
|
setImpersonating(true);
|
|
@@ -53,7 +53,7 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
|
53
53
|
window.location.href = "/";
|
|
54
54
|
}
|
|
55
55
|
catch (err) {
|
|
56
|
-
setAccessError(err instanceof ApiRequestError ? err.message : "Could not
|
|
56
|
+
setAccessError(err instanceof ApiRequestError ? err.message : "Could not impersonate this user.");
|
|
57
57
|
setImpersonating(false);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -82,7 +82,8 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
|
82
82
|
useEffect(() => {
|
|
83
83
|
setLoading(true);
|
|
84
84
|
setError(null);
|
|
85
|
-
|
|
85
|
+
// The administration scope: administrative metadata only - the console never shows a mailbox's mail or settings.
|
|
86
|
+
getMailbox(uid, { scope: "admin" })
|
|
86
87
|
.then(setMailbox)
|
|
87
88
|
.catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this mailbox."))
|
|
88
89
|
.finally(() => setLoading(false));
|
|
@@ -95,5 +96,7 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
|
95
96
|
}
|
|
96
97
|
const quarantineHref = `/admin/quarantine?mailboxUid=${encodeURIComponent(mailbox.uid)}`;
|
|
97
98
|
const ingestQueueHref = `/admin/ingest-queue?mailboxUid=${encodeURIComponent(mailbox.uid)}`;
|
|
98
|
-
return (_jsxs("div", { className: "max-w-3xl flex flex-col gap-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsx("a", { href: "/admin", className: "text-sm text-primary-dark hover:underline", children: "\u2190 All mailboxes" }), _jsx("h1", { className: "text-xl font-bold tracking-tight mt-1", children: mailbox.primarySmtpAddress })] }), _jsxs("div", { className: "flex gap-3 shrink-0", children: [mailbox.ownerUserUid && (_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingAccess(true), children: "
|
|
99
|
+
return (_jsxs("div", { className: "max-w-3xl flex flex-col gap-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsx("a", { href: "/admin", className: "text-sm text-primary-dark hover:underline", children: "\u2190 All mailboxes" }), _jsx("h1", { className: "text-xl font-bold tracking-tight mt-1", children: mailbox.primarySmtpAddress })] }), _jsxs("div", { className: "flex gap-3 shrink-0", children: [mailbox.ownerUserUid && (_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingAccess(true), children: "Impersonate this user" })), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto !border-danger !text-danger hover:!border-danger hover:!text-danger", onClick: () => setConfirmingDelete(true), children: "Delete mailbox" })] })] }), _jsxs("p", { className: "text-sm text-text-muted", children: ["The console shows administrative details only - never a mailbox\u2019s mail or settings.", mailbox.ownerUserUid
|
|
100
|
+
? " To see this mailbox as its owner sees it, impersonate them: that is recorded, and you can stop at any time."
|
|
101
|
+
: " This shared mailbox has no owner: add yourself under Shared access to open it in the mail client."] }), _jsxs("div", { className: "bg-surface border border-border rounded-md p-6", children: [_jsxs("dl", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-3 text-sm", children: [_jsx("dt", { className: "text-text-muted", children: "Display name" }), _jsx("dd", { children: mailbox.displayName }), _jsx("dt", { className: "text-text-muted", children: "Owner" }), _jsx("dd", { children: mailbox.ownerUserUid ?? "None (shared mailbox)" }), _jsx("dt", { className: "text-text-muted", children: "Timezone" }), _jsx("dd", { children: mailbox.timezone }), _jsx("dt", { className: "text-text-muted", children: "Quota" }), _jsxs("dd", { children: [formatBytes(mailbox.usedBytes), " / ", formatBytes(mailbox.quotaBytes)] }), _jsx("dt", { className: "text-text-muted", children: "Alias addresses" }), _jsx("dd", { children: mailbox.aliasAddresses.length > 0 ? mailbox.aliasAddresses.join(", ") : "None" }), mailbox.isResource && (_jsxs(_Fragment, { children: [_jsx("dt", { className: "text-text-muted", children: "Resource type" }), _jsx("dd", { className: "capitalize", children: mailbox.resourceType ?? "room" })] })), _jsx("dt", { className: "text-text-muted", children: "Created" }), _jsx("dd", { children: new Date(mailbox.dateCreated).toLocaleString() })] }), _jsxs("div", { className: "flex gap-4 mt-5 pt-5 border-t border-border text-sm font-medium", children: [_jsx("a", { href: quarantineHref, className: "text-primary-dark hover:underline", children: "View quarantine" }), _jsx("a", { href: ingestQueueHref, className: "text-primary-dark hover:underline", children: "View ingest queue" })] })] }), _jsx(ShareAccessCard, { mailboxUid: mailbox.uid, ownerUserUid: mailbox.ownerUserUid, currentUserUid: currentUserUid }), _jsx(EscrowScopeCard, { mailbox: mailbox, onUpdate: setMailbox }), mailbox.isResource && _jsx(ResourceSettingsCard, { mailbox: mailbox, onUpdate: setMailbox }), _jsxs(Modal, { open: confirmingAccess, onClose: closeAccessModal, title: "Impersonate this user", children: [_jsxs("p", { className: "text-sm mb-3", children: ["You\u2019ll be signed in as ", _jsx("strong", { className: "break-all", children: mailbox.ownerUserUid }), ", the owner of ", _jsx("strong", { className: "break-all", children: mailbox.primarySmtpAddress }), ", and see everything they can - and act as them, until you stop impersonating. This is the only way an administrator sees another user\u2019s mail."] }), accessError && _jsx(Alert, { children: accessError }), _jsxs("div", { className: "flex gap-3 justify-end mt-5", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: impersonating, onClick: closeAccessModal, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: impersonating, disabled: impersonating, onClick: handleAccessMailbox, children: "Impersonate" })] })] }), _jsxs(Modal, { open: confirmingDelete, onClose: closeDeleteModal, title: "Delete mailbox", children: [_jsxs("p", { className: "text-sm mb-5", children: ["Are you sure you want to delete ", _jsx("strong", { children: mailbox.primarySmtpAddress }), "? This permanently deletes the mailbox and everything in it, and cannot be undone. It fails if this mailbox is a custodian on an active legal hold."] }), deleteError && _jsx(Alert, { children: deleteError }), _jsxs("div", { className: "flex gap-3 justify-end mt-5", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: deleting, onClick: closeDeleteModal, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger", loading: deleting, disabled: deleting, onClick: handleDelete, children: "Delete" })] })] })] }));
|
|
99
102
|
}
|
|
@@ -34,7 +34,8 @@ function QuarantineContent({ userUid }) {
|
|
|
34
34
|
function reload(uid) {
|
|
35
35
|
setLoading(true);
|
|
36
36
|
setError(null);
|
|
37
|
-
|
|
37
|
+
// The administration scope: any mailbox's held mail, recorded in the audit log.
|
|
38
|
+
listQuarantine(uid, { page, limit: PAGE_SIZE, scope: "admin" })
|
|
38
39
|
.then(setEntries)
|
|
39
40
|
.catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load quarantine."))
|
|
40
41
|
.finally(() => setLoading(false));
|