@rapidmx/web-client 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/apps/admin/audit-log/index.tsx +35 -8
  2. package/apps/admin/data-requests/index.tsx +480 -485
  3. package/apps/admin/distribution-lists/[uid].tsx +19 -1
  4. package/apps/admin/escrow-scopes/[uid].tsx +348 -237
  5. package/apps/admin/escrow-scopes/new/index.tsx +8 -2
  6. package/apps/admin/index.tsx +19 -1
  7. package/apps/admin/ingest-queue/index.tsx +44 -5
  8. package/apps/admin/mailboxes/[uid].tsx +211 -184
  9. package/apps/admin/quarantine/index.tsx +46 -14
  10. package/apps/admin/transport-rules/[uid].tsx +30 -1
  11. package/apps/admin/transport-rules/_transportRuleConfig.tsx +38 -2
  12. package/apps/admin/transport-rules/index.tsx +24 -6
  13. package/apps/admin/transport-rules/new/index.tsx +30 -1
  14. package/apps/escrow/_layout.tsx +2 -3
  15. package/apps/escrow/audit-log/index.tsx +196 -168
  16. package/apps/escrow/matters/[uid].tsx +621 -521
  17. package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +22 -1
  18. package/apps/shared/components/admin/layout/AdminShell.tsx +3 -1
  19. package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +152 -0
  20. package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +3 -3
  21. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +55 -11
  22. package/apps/shared/components/admin/settings/BrandingForm.tsx +15 -4
  23. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +18 -7
  24. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +10 -2
  25. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +6 -0
  26. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +45 -12
  27. package/apps/shared/components/admin/settings/PluginsManager.tsx +1235 -595
  28. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +174 -98
  29. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +49 -13
  30. package/apps/shared/components/admin/setup/SetupWizard.tsx +150 -34
  31. package/apps/shared/components/admin/signOut.ts +79 -0
  32. package/apps/shared/components/admin/usePagedList.tsx +129 -0
  33. package/apps/shared/components/calendar/EventModal.tsx +717 -549
  34. package/apps/shared/components/calendar/MonthView.tsx +173 -139
  35. package/apps/shared/components/calendar/RecurrenceEditor.tsx +222 -180
  36. package/apps/shared/components/calendar/SplitDayView.tsx +143 -137
  37. package/apps/shared/components/calendar/TimeGridView.tsx +244 -198
  38. package/apps/shared/components/calendar/allDay.ts +124 -0
  39. package/apps/shared/components/calendar/layout/CalendarShell.tsx +1 -1
  40. package/apps/shared/components/contacts/ContactDetailPane.tsx +100 -21
  41. package/apps/shared/components/contacts/ContactForm.tsx +377 -348
  42. package/apps/shared/components/contacts/ContactsSidebar.tsx +6 -2
  43. package/apps/shared/components/contacts/KeyChangeReview.tsx +192 -0
  44. package/apps/shared/components/contacts/contactKeys.ts +78 -0
  45. package/apps/shared/components/escrow/layout/EscrowShell.tsx +148 -134
  46. package/apps/shared/components/layout/AppShell.tsx +87 -15
  47. package/apps/shared/components/layout/BrandingChrome.tsx +48 -10
  48. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +389 -298
  49. package/apps/shared/components/layout/MailboxProvisioning.tsx +19 -3
  50. package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +350 -0
  51. package/apps/shared/components/layout/UnlockPromptProvider.tsx +257 -128
  52. package/apps/shared/components/mail/ConversationThreadPane.tsx +216 -181
  53. package/apps/shared/components/mail/MessageDetailPane.tsx +1318 -630
  54. package/apps/shared/components/mail/compose/ComposeContext.tsx +20 -15
  55. package/apps/shared/components/mail/compose/ComposeWindow.tsx +854 -132
  56. package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -0
  57. package/apps/shared/components/mail/layout/MailShell.tsx +12 -2
  58. package/apps/shared/components/mail/pinnedSigners.ts +124 -0
  59. package/apps/shared/components/mail/verificationSeals.ts +125 -0
  60. package/apps/shared/components/mail/writableMailboxes.ts +101 -0
  61. package/apps/shared/components/rules/RuleBuilder.tsx +311 -276
  62. package/apps/shared/mail/listAllPages.ts +39 -0
  63. package/apps/shared/search/LocalIndexLifecycle.tsx +64 -55
  64. package/apps/shared/search/localIndexBuilder.ts +481 -179
  65. package/apps/shared/search/localIndexRpcClient.ts +228 -28
  66. package/apps/shared/search/localIndexSchema.ts +15 -6
  67. package/apps/shared/search/localIndexStorage.ts +98 -0
  68. package/apps/shared/search/localIndexVFS.ts +32 -1
  69. package/apps/shared/search/localIndexWorker.ts +648 -186
  70. package/apps/www/calendar/index.tsx +438 -417
  71. package/apps/www/contacts/[uid].tsx +21 -2
  72. package/apps/www/contacts/index.tsx +116 -12
  73. package/apps/www/index.tsx +1314 -1041
  74. package/apps/www/messages/[uid].tsx +18 -6
  75. package/apps/www/settings/auto-reply/index.tsx +134 -129
  76. package/apps/www/settings/booking-types/[uid].tsx +306 -300
  77. package/apps/www/settings/encryption/index.tsx +739 -216
  78. package/apps/www/settings/filters/[uid].tsx +171 -155
  79. package/apps/www/settings/filters/new/index.tsx +8 -1
  80. package/apps/www/settings/focused-inbox/index.tsx +150 -148
  81. package/apps/www/settings/privacy/index.tsx +494 -436
  82. package/apps/www/settings/read-receipts/index.tsx +148 -125
  83. package/apps/www/settings/sharing/index.tsx +17 -14
  84. package/apps/www/tasks/index.tsx +638 -579
  85. package/dist/apps/admin/audit-log/index.js +38 -8
  86. package/dist/apps/admin/data-requests/index.js +52 -72
  87. package/dist/apps/admin/distribution-lists/[uid].js +1 -1
  88. package/dist/apps/admin/escrow-scopes/[uid].js +91 -12
  89. package/dist/apps/admin/escrow-scopes/new/index.js +8 -3
  90. package/dist/apps/admin/index.js +4 -1
  91. package/dist/apps/admin/ingest-queue/index.js +24 -6
  92. package/dist/apps/admin/mailboxes/[uid].js +20 -4
  93. package/dist/apps/admin/quarantine/index.js +20 -11
  94. package/dist/apps/admin/transport-rules/[uid].js +18 -2
  95. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +19 -0
  96. package/dist/apps/admin/transport-rules/index.js +22 -6
  97. package/dist/apps/admin/transport-rules/new/index.js +18 -2
  98. package/dist/apps/escrow/_layout.js +3 -3
  99. package/dist/apps/escrow/audit-log/index.js +26 -1
  100. package/dist/apps/escrow/matters/[uid].js +77 -42
  101. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +11 -2
  102. package/dist/apps/shared/components/admin/layout/AdminShell.js +3 -1
  103. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +72 -0
  104. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +3 -3
  105. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +22 -4
  106. package/dist/apps/shared/components/admin/settings/BrandingForm.js +15 -4
  107. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +13 -7
  108. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +7 -2
  109. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +6 -0
  110. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +34 -12
  111. package/dist/apps/shared/components/admin/settings/PluginsManager.js +390 -78
  112. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +40 -11
  113. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +29 -13
  114. package/dist/apps/shared/components/admin/setup/SetupWizard.js +87 -21
  115. package/dist/apps/shared/components/admin/signOut.js +75 -0
  116. package/dist/apps/shared/components/admin/usePagedList.js +99 -0
  117. package/dist/apps/shared/components/calendar/EventModal.js +142 -21
  118. package/dist/apps/shared/components/calendar/MonthView.js +12 -5
  119. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +40 -5
  120. package/dist/apps/shared/components/calendar/SplitDayView.js +10 -4
  121. package/dist/apps/shared/components/calendar/TimeGridView.js +23 -7
  122. package/dist/apps/shared/components/calendar/allDay.js +111 -0
  123. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +1 -1
  124. package/dist/apps/shared/components/contacts/ContactDetailPane.js +43 -5
  125. package/dist/apps/shared/components/contacts/ContactForm.js +32 -10
  126. package/dist/apps/shared/components/contacts/ContactsSidebar.js +2 -2
  127. package/dist/apps/shared/components/contacts/KeyChangeReview.js +63 -0
  128. package/dist/apps/shared/components/contacts/contactKeys.js +64 -0
  129. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +24 -5
  130. package/dist/apps/shared/components/layout/AppShell.js +83 -14
  131. package/dist/apps/shared/components/layout/BrandingChrome.js +45 -10
  132. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +49 -12
  133. package/dist/apps/shared/components/layout/MailboxProvisioning.js +10 -2
  134. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +174 -0
  135. package/dist/apps/shared/components/layout/UnlockPromptProvider.js +73 -14
  136. package/dist/apps/shared/components/mail/ConversationThreadPane.js +50 -12
  137. package/dist/apps/shared/components/mail/MessageDetailPane.js +441 -36
  138. package/dist/apps/shared/components/mail/compose/ComposeContext.js +9 -6
  139. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +699 -128
  140. package/dist/apps/shared/components/mail/compose/composeFlushRegistry.js +50 -0
  141. package/dist/apps/shared/components/mail/layout/MailShell.js +12 -3
  142. package/dist/apps/shared/components/mail/pinnedSigners.js +98 -0
  143. package/dist/apps/shared/components/mail/verificationSeals.js +112 -0
  144. package/dist/apps/shared/components/mail/writableMailboxes.js +87 -0
  145. package/dist/apps/shared/components/rules/RuleBuilder.js +34 -6
  146. package/dist/apps/shared/mail/listAllPages.js +25 -0
  147. package/dist/apps/shared/search/LocalIndexLifecycle.js +58 -54
  148. package/dist/apps/shared/search/localIndexBuilder.js +292 -37
  149. package/dist/apps/shared/search/localIndexRpcClient.js +188 -25
  150. package/dist/apps/shared/search/localIndexSchema.js +12 -6
  151. package/dist/apps/shared/search/localIndexStorage.js +86 -0
  152. package/dist/apps/shared/search/localIndexVFS.js +68 -40
  153. package/dist/apps/shared/search/localIndexWorker.js +519 -173
  154. package/dist/apps/www/calendar/index.js +28 -10
  155. package/dist/apps/www/contacts/[uid].js +13 -2
  156. package/dist/apps/www/contacts/index.js +80 -10
  157. package/dist/apps/www/index.js +339 -108
  158. package/dist/apps/www/messages/[uid].js +18 -6
  159. package/dist/apps/www/settings/auto-reply/index.js +9 -4
  160. package/dist/apps/www/settings/booking-types/[uid].js +17 -12
  161. package/dist/apps/www/settings/encryption/index.js +514 -97
  162. package/dist/apps/www/settings/filters/[uid].js +22 -7
  163. package/dist/apps/www/settings/filters/new/index.js +8 -2
  164. package/dist/apps/www/settings/focused-inbox/index.js +3 -1
  165. package/dist/apps/www/settings/privacy/index.js +57 -22
  166. package/dist/apps/www/settings/read-receipts/index.js +10 -3
  167. package/dist/apps/www/settings/sharing/index.js +7 -11
  168. package/dist/apps/www/tasks/index.js +50 -10
  169. package/package.json +2 -2
@@ -33,13 +33,25 @@ function MessageDetailContent({ uid }) {
33
33
  .finally(() => setLoading(false));
34
34
  }, [uid]);
35
35
  // See `apps/www/index.tsx`'s identical effect's own doc comment - a failure here just hides the
36
- // Labels control rather than blocking the rest of the page. No `mailboxUid` guard needed - `MailShell`
37
- // never renders this component at all until `mailboxUid` has resolved.
36
+ // Labels control rather than blocking the rest of the page. Keyed on the message's own mailbox, not the
37
+ // shell's ambient one: this route is reached from aggregate/search rows too, where they can differ.
38
+ const messageMailboxUid = message?.mailboxUid;
38
39
  useEffect(() => {
39
- listLabels(mailboxUid, { limit: 200 })
40
- .then(setLabels)
41
- .catch(() => setLabels([]));
42
- }, [mailboxUid]);
40
+ if (!messageMailboxUid) {
41
+ return;
42
+ }
43
+ let cancelled = false;
44
+ listLabels(messageMailboxUid, { limit: 200 })
45
+ .then((result) => {
46
+ if (!cancelled) {
47
+ setLabels(result);
48
+ }
49
+ })
50
+ .catch(() => undefined);
51
+ return () => {
52
+ cancelled = true;
53
+ };
54
+ }, [messageMailboxUid]);
43
55
  const attachments = useMessageAttachments(message);
44
56
  useMarkMessageRead(message, setMessage);
45
57
  if (loading) {
@@ -24,6 +24,9 @@ function AutoReplyContent() {
24
24
  const [oofMessage, setOofMessage] = useState(mailbox.oofMessage ?? "");
25
25
  const [oofStartTime, setOofStartTime] = useState(mailbox.oofStartTime ? toDatetimeLocal(mailbox.oofStartTime) : "");
26
26
  const [oofEndTime, setOofEndTime] = useState(mailbox.oofEndTime ? toDatetimeLocal(mailbox.oofEndTime) : "");
27
+ // `SettingsShell`'s `mailboxes` is fetched once - every save bumps the server's version, so a second
28
+ // save without a reload must send the version the previous save returned, not the stale original.
29
+ const [version, setVersion] = useState(mailbox.version);
27
30
  const [error, setError] = useState(null);
28
31
  const [saving, setSaving] = useState(false);
29
32
  const [saved, setSaved] = useState(false);
@@ -33,14 +36,16 @@ function AutoReplyContent() {
33
36
  setSaved(false);
34
37
  setSaving(true);
35
38
  try {
36
- await updateMailbox({
39
+ const updated = await updateMailbox({
37
40
  uid: mailbox.uid,
38
- version: mailbox.version,
41
+ version,
39
42
  oofEnabled,
40
43
  oofMessage,
41
- oofStartTime: oofStartTime ? new Date(oofStartTime).toISOString() : undefined,
42
- oofEndTime: oofEndTime ? new Date(oofEndTime).toISOString() : undefined,
44
+ // `null` (not omitted) clears a previously saved window - an omitted field is left as-is.
45
+ oofStartTime: oofStartTime ? new Date(oofStartTime).toISOString() : null,
46
+ oofEndTime: oofEndTime ? new Date(oofEndTime).toISOString() : null,
43
47
  });
48
+ setVersion(updated.version);
44
49
  setSaved(true);
45
50
  }
46
51
  catch (err) {
@@ -30,7 +30,10 @@ function BookingTypeDetailContent({ uid }) {
30
30
  const [requiresApproval, setRequiresApproval] = useState(false);
31
31
  const [enabled, setEnabled] = useState(true);
32
32
  const [loading, setLoading] = useState(true);
33
- const [error, setError] = useState(null);
33
+ // Split so a failed save keeps the form (and the user's edits) on screen - only a failed *load* has
34
+ // nothing to show but the error.
35
+ const [loadError, setLoadError] = useState(null);
36
+ const [saveError, setSaveError] = useState(null);
34
37
  const [saving, setSaving] = useState(false);
35
38
  const [saved, setSaved] = useState(false);
36
39
  const [copied, setCopied] = useState(false);
@@ -39,7 +42,7 @@ function BookingTypeDetailContent({ uid }) {
39
42
  const [deleteError, setDeleteError] = useState(null);
40
43
  useEffect(() => {
41
44
  setLoading(true);
42
- setError(null);
45
+ setLoadError(null);
43
46
  getBookingType(uid)
44
47
  .then((bt) => {
45
48
  setBookingType(bt);
@@ -54,12 +57,12 @@ function BookingTypeDetailContent({ uid }) {
54
57
  setRequiresApproval(bt.requiresApproval);
55
58
  setEnabled(bt.enabled);
56
59
  })
57
- .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this booking link."))
60
+ .catch((err) => setLoadError(err instanceof ApiRequestError ? err.message : "Could not load this booking link."))
58
61
  .finally(() => setLoading(false));
59
62
  }, [uid]);
60
63
  async function handleSubmit(e) {
61
64
  e.preventDefault();
62
- setError(null);
65
+ setSaveError(null);
63
66
  setSaved(false);
64
67
  setSaving(true);
65
68
  try {
@@ -67,7 +70,9 @@ function BookingTypeDetailContent({ uid }) {
67
70
  uid: bookingType.uid,
68
71
  version: bookingType.version,
69
72
  name,
70
- description: description.trim() || undefined,
73
+ // `null` (not omitted) clears a saved description - an omitted field is left unchanged by the
74
+ // server's partial update. `UpdateBookingTypeInput` doesn't declare `null` yet, hence the cast.
75
+ description: description.trim() || null,
71
76
  hostDisplayName,
72
77
  durationMinutes,
73
78
  timezone,
@@ -81,7 +86,7 @@ function BookingTypeDetailContent({ uid }) {
81
86
  setSaved(true);
82
87
  }
83
88
  catch (err) {
84
- setError(err instanceof ApiRequestError ? err.message : "Could not save this booking link.");
89
+ setSaveError(err instanceof ApiRequestError ? err.message : "Could not save this booking link.");
85
90
  }
86
91
  finally {
87
92
  setSaving(false);
@@ -112,18 +117,18 @@ function BookingTypeDetailContent({ uid }) {
112
117
  if (loading) {
113
118
  return _jsx("p", { className: "p-6 text-sm text-text-muted", children: "Loading\u2026" });
114
119
  }
115
- if (error || !bookingType) {
116
- // `error` is always set whenever `bookingType` is falsy here: the load effect above destructures
120
+ if (loadError || !bookingType) {
121
+ // `loadError` is always set whenever `bookingType` is falsy here: the load effect above destructures
117
122
  // every field off the resolved value directly (`bt.name`, `bt.hostDisplayName`, ...), so a
118
- // successful-but-empty response throws into `.catch()` — which sets `error` — before this
119
- // component ever renders past the `loading` guard above. `!bookingType` alone (with `error` still
123
+ // successful-but-empty response throws into `.catch()` — which sets `loadError` — before this
124
+ // component ever renders past the `loading` guard above. `!bookingType` alone (with `loadError` still
120
125
  // `null`) is therefore unreachable, unlike e.g. `messages/[uid].tsx`'s identical-looking check,
121
126
  // which stores its fetched value whole rather than destructuring it.
122
- return (_jsx("div", { className: "p-6", children: _jsx(Alert, { children: error }) }));
127
+ return (_jsx("div", { className: "p-6", children: _jsx(Alert, { children: loadError }) }));
123
128
  }
124
129
  // `bookingType` only ever becomes non-null via the client-only fetch above, so by the time this line
125
130
  // runs `window` is always defined — the `!bookingType` early return just above covers every real SSR
126
131
  // pass, where `bookingType` is still `null`.
127
132
  const publicUrl = `${window.location.origin}/book/${encodeURIComponent(bookingType.slug)}`;
128
- return (_jsx("div", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: _jsxs("div", { className: "max-w-2xl", children: [_jsxs("div", { className: "flex items-start justify-between gap-4 mb-1", children: [_jsxs("div", { children: [_jsx("a", { href: `/settings/booking-types?mailboxUid=${encodeURIComponent(mailboxUid)}`, className: "text-sm text-primary-dark hover:underline", children: "\u2190 All booking links" }), _jsx("h1", { className: "text-xl font-bold tracking-tight mt-1", children: bookingType.name })] }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto shrink-0 !border-danger !text-danger hover:!border-danger hover:!text-danger", onClick: () => setConfirmingDelete(true), children: "Delete" })] }), _jsxs("div", { className: "flex items-center gap-2 mb-5", children: [_jsx("code", { className: "flex-1 text-xs bg-surface-alt border border-border rounded-sm py-2 px-3 overflow-x-auto whitespace-nowrap", children: publicUrl }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto shrink-0", onClick: () => handleCopy(publicUrl), children: copied ? "Copied" : "Copy" })] }), saved && !error && _jsx("div", { className: "mb-4 text-sm text-success font-medium", children: "Saved." }), _jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-1", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm mb-3", children: [_jsx("input", { type: "checkbox", checked: enabled, onChange: (e) => setEnabled(e.target.checked) }), "Enabled (publicly bookable)"] }), _jsx(FormField, { label: "Name", htmlFor: "name", children: _jsx("input", { id: "name", type: "text", className: INPUT_CLASS, value: name, onChange: (e) => setName(e.target.value) }) }), _jsx(FormField, { label: "Description (optional)", htmlFor: "description", children: _jsx("textarea", { id: "description", className: INPUT_CLASS, rows: 2, value: description, onChange: (e) => setDescription(e.target.value) }) }), _jsx(FormField, { label: "Host name shown to visitors", htmlFor: "hostDisplayName", children: _jsx("input", { id: "hostDisplayName", type: "text", className: INPUT_CLASS, value: hostDisplayName, onChange: (e) => setHostDisplayName(e.target.value) }) }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(FormField, { label: "Duration (minutes)", htmlFor: "durationMinutes", children: _jsx("input", { id: "durationMinutes", type: "number", min: 1, className: INPUT_CLASS, value: durationMinutes, onChange: (e) => setDurationMinutes(Number(e.target.value)) }) }), _jsx(FormField, { label: "Timezone", htmlFor: "timezone", children: _jsx("input", { id: "timezone", type: "text", className: INPUT_CLASS, value: timezone, onChange: (e) => setTimezone(e.target.value) }) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(FormField, { label: "Minimum notice (minutes)", htmlFor: "minimumNoticeMinutes", children: _jsx("input", { id: "minimumNoticeMinutes", type: "number", min: 0, className: INPUT_CLASS, value: minimumNoticeMinutes, onChange: (e) => setMinimumNoticeMinutes(Number(e.target.value)) }) }), _jsx(FormField, { label: "Booking window (days ahead)", htmlFor: "bookingWindowDays", children: _jsx("input", { id: "bookingWindowDays", type: "number", min: 1, className: INPUT_CLASS, value: bookingWindowDays, onChange: (e) => setBookingWindowDays(Number(e.target.value)) }) })] }), _jsxs("label", { className: "flex items-center gap-2 text-sm my-3", children: [_jsx("input", { type: "checkbox", checked: requiresApproval, onChange: (e) => setRequiresApproval(e.target.checked) }), "Require my approval before confirming a booking"] }), _jsxs("div", { className: "mb-4", children: [_jsx("span", { className: "block text-sm font-semibold mb-1.5 text-text", children: "Weekly availability" }), _jsx(AvailabilityEditor, { value: availability, onChange: setAvailability })] }), _jsx("div", { children: _jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Save" }) })] }), _jsxs(Modal, { open: confirmingDelete, onClose: () => setConfirmingDelete(false), title: "Delete booking link", children: [_jsxs("p", { className: "text-sm mb-5", children: ["Are you sure you want to delete ", _jsx("strong", { children: bookingType.name }), "? Its public link will stop working immediately. This cannot be undone."] }), 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: () => setConfirmingDelete(false), 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" })] })] })] }) }));
133
+ return (_jsx("div", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: _jsxs("div", { className: "max-w-2xl", children: [_jsxs("div", { className: "flex items-start justify-between gap-4 mb-1", children: [_jsxs("div", { children: [_jsx("a", { href: `/settings/booking-types?mailboxUid=${encodeURIComponent(mailboxUid)}`, className: "text-sm text-primary-dark hover:underline", children: "\u2190 All booking links" }), _jsx("h1", { className: "text-xl font-bold tracking-tight mt-1", children: bookingType.name })] }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto shrink-0 !border-danger !text-danger hover:!border-danger hover:!text-danger", onClick: () => setConfirmingDelete(true), children: "Delete" })] }), _jsxs("div", { className: "flex items-center gap-2 mb-5", children: [_jsx("code", { className: "flex-1 text-xs bg-surface-alt border border-border rounded-sm py-2 px-3 overflow-x-auto whitespace-nowrap", children: publicUrl }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto shrink-0", onClick: () => handleCopy(publicUrl), children: copied ? "Copied" : "Copy" })] }), saveError && _jsx(Alert, { children: saveError }), saved && !saveError && _jsx("div", { className: "mb-4 text-sm text-success font-medium", children: "Saved." }), _jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-1", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm mb-3", children: [_jsx("input", { type: "checkbox", checked: enabled, onChange: (e) => setEnabled(e.target.checked) }), "Enabled (publicly bookable)"] }), _jsx(FormField, { label: "Name", htmlFor: "name", children: _jsx("input", { id: "name", type: "text", className: INPUT_CLASS, value: name, onChange: (e) => setName(e.target.value) }) }), _jsx(FormField, { label: "Description (optional)", htmlFor: "description", children: _jsx("textarea", { id: "description", className: INPUT_CLASS, rows: 2, value: description, onChange: (e) => setDescription(e.target.value) }) }), _jsx(FormField, { label: "Host name shown to visitors", htmlFor: "hostDisplayName", children: _jsx("input", { id: "hostDisplayName", type: "text", className: INPUT_CLASS, value: hostDisplayName, onChange: (e) => setHostDisplayName(e.target.value) }) }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(FormField, { label: "Duration (minutes)", htmlFor: "durationMinutes", children: _jsx("input", { id: "durationMinutes", type: "number", min: 1, className: INPUT_CLASS, value: durationMinutes, onChange: (e) => setDurationMinutes(Number(e.target.value)) }) }), _jsx(FormField, { label: "Timezone", htmlFor: "timezone", children: _jsx("input", { id: "timezone", type: "text", className: INPUT_CLASS, value: timezone, onChange: (e) => setTimezone(e.target.value) }) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(FormField, { label: "Minimum notice (minutes)", htmlFor: "minimumNoticeMinutes", children: _jsx("input", { id: "minimumNoticeMinutes", type: "number", min: 0, className: INPUT_CLASS, value: minimumNoticeMinutes, onChange: (e) => setMinimumNoticeMinutes(Number(e.target.value)) }) }), _jsx(FormField, { label: "Booking window (days ahead)", htmlFor: "bookingWindowDays", children: _jsx("input", { id: "bookingWindowDays", type: "number", min: 1, className: INPUT_CLASS, value: bookingWindowDays, onChange: (e) => setBookingWindowDays(Number(e.target.value)) }) })] }), _jsxs("label", { className: "flex items-center gap-2 text-sm my-3", children: [_jsx("input", { type: "checkbox", checked: requiresApproval, onChange: (e) => setRequiresApproval(e.target.checked) }), "Require my approval before confirming a booking"] }), _jsxs("div", { className: "mb-4", children: [_jsx("span", { className: "block text-sm font-semibold mb-1.5 text-text", children: "Weekly availability" }), _jsx(AvailabilityEditor, { value: availability, onChange: setAvailability })] }), _jsx("div", { children: _jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Save" }) })] }), _jsxs(Modal, { open: confirmingDelete, onClose: () => setConfirmingDelete(false), title: "Delete booking link", children: [_jsxs("p", { className: "text-sm mb-5", children: ["Are you sure you want to delete ", _jsx("strong", { children: bookingType.name }), "? Its public link will stop working immediately. This cannot be undone."] }), 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: () => setConfirmingDelete(false), 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" })] })] })] }) }));
129
134
  }