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