@rapidmx/web-client 0.2.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 (168) hide show
  1. package/LICENSE +382 -0
  2. package/apps/admin/_404.tsx +9 -0
  3. package/apps/admin/_500.tsx +9 -0
  4. package/apps/admin/_layout.tsx +41 -0
  5. package/apps/admin/audit-log/index.tsx +187 -0
  6. package/apps/admin/branding/index.tsx +404 -0
  7. package/apps/admin/distribution-lists/[uid].tsx +70 -0
  8. package/apps/admin/distribution-lists/index.tsx +115 -0
  9. package/apps/admin/distribution-lists/new/index.tsx +114 -0
  10. package/apps/admin/domains/[uid].tsx +259 -0
  11. package/apps/admin/domains/index.tsx +125 -0
  12. package/apps/admin/domains/new/index.tsx +84 -0
  13. package/apps/admin/index.tsx +82 -0
  14. package/apps/admin/ingest-queue/index.tsx +116 -0
  15. package/apps/admin/mailboxes/[uid].tsx +126 -0
  16. package/apps/admin/mailboxes/new/index.tsx +291 -0
  17. package/apps/admin/quarantine/index.tsx +171 -0
  18. package/apps/admin/transport-rules/[uid].tsx +143 -0
  19. package/apps/admin/transport-rules/_transportRuleConfig.tsx +75 -0
  20. package/apps/admin/transport-rules/index.tsx +117 -0
  21. package/apps/admin/transport-rules/new/index.tsx +105 -0
  22. package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +117 -0
  23. package/apps/shared/components/admin/layout/AdminShell.tsx +186 -0
  24. package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +73 -0
  25. package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +154 -0
  26. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +124 -0
  27. package/apps/shared/components/booking/AvailabilityEditor.tsx +118 -0
  28. package/apps/shared/components/calendar/CalendarListSidebar.tsx +119 -0
  29. package/apps/shared/components/calendar/EventModal.tsx +513 -0
  30. package/apps/shared/components/calendar/MonthView.tsx +139 -0
  31. package/apps/shared/components/calendar/RecurrenceEditor.tsx +180 -0
  32. package/apps/shared/components/calendar/ResourcePicker.tsx +80 -0
  33. package/apps/shared/components/calendar/SplitDayView.tsx +137 -0
  34. package/apps/shared/components/calendar/TimeGridView.tsx +198 -0
  35. package/apps/shared/components/calendar/layout/CalendarShell.tsx +211 -0
  36. package/apps/shared/components/contacts/ContactDetailPane.tsx +99 -0
  37. package/apps/shared/components/contacts/ContactForm.tsx +314 -0
  38. package/apps/shared/components/contacts/ContactsSidebar.tsx +242 -0
  39. package/apps/shared/components/contacts/ContactsToolbar.tsx +115 -0
  40. package/apps/shared/components/contacts/layout/ContactsShell.tsx +197 -0
  41. package/apps/shared/components/layout/AppShell.tsx +165 -0
  42. package/apps/shared/components/layout/BrandingChrome.tsx +25 -0
  43. package/apps/shared/components/layout/MailboxProvisioning.tsx +110 -0
  44. package/apps/shared/components/layout/UserMenu.tsx +150 -0
  45. package/apps/shared/components/mail/ConversationList.tsx +61 -0
  46. package/apps/shared/components/mail/ConversationThreadPane.tsx +174 -0
  47. package/apps/shared/components/mail/MessageDetailPane.tsx +390 -0
  48. package/apps/shared/components/mail/compose/ComposeContext.tsx +114 -0
  49. package/apps/shared/components/mail/compose/ComposeToolbar.tsx +418 -0
  50. package/apps/shared/components/mail/compose/ComposeWindow.tsx +518 -0
  51. package/apps/shared/components/mail/compose/EmojiPicker.tsx +64 -0
  52. package/apps/shared/components/mail/compose/GifPicker.tsx +82 -0
  53. package/apps/shared/components/mail/compose/RichTextEditor.tsx +83 -0
  54. package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +64 -0
  55. package/apps/shared/components/mail/layout/MailShell.tsx +287 -0
  56. package/apps/shared/components/rules/RuleBuilder.tsx +276 -0
  57. package/apps/shared/components/settings/layout/SettingsShell.tsx +223 -0
  58. package/apps/shared/components/tasks/TasksSidebar.tsx +201 -0
  59. package/apps/shared/components/tasks/TasksToolbar.tsx +68 -0
  60. package/apps/shared/components/tasks/layout/TasksShell.tsx +199 -0
  61. package/apps/shared/styles/app.css +265 -0
  62. package/apps/www/_404.tsx +9 -0
  63. package/apps/www/_500.tsx +9 -0
  64. package/apps/www/_layout.tsx +45 -0
  65. package/apps/www/calendar/index.tsx +407 -0
  66. package/apps/www/contacts/[uid].tsx +88 -0
  67. package/apps/www/contacts/index.tsx +436 -0
  68. package/apps/www/index.tsx +375 -0
  69. package/apps/www/messages/[uid].tsx +70 -0
  70. package/apps/www/settings/auto-reply/index.tsx +129 -0
  71. package/apps/www/settings/booking-types/[uid].tsx +300 -0
  72. package/apps/www/settings/booking-types/index.tsx +98 -0
  73. package/apps/www/settings/booking-types/new/index.tsx +212 -0
  74. package/apps/www/settings/filters/[uid].tsx +155 -0
  75. package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +102 -0
  76. package/apps/www/settings/filters/index.tsx +99 -0
  77. package/apps/www/settings/filters/new/index.tsx +126 -0
  78. package/apps/www/settings/focused-inbox/index.tsx +148 -0
  79. package/apps/www/settings/read-receipts/index.tsx +125 -0
  80. package/apps/www/settings/signatures/[uid].tsx +167 -0
  81. package/apps/www/settings/signatures/index.tsx +85 -0
  82. package/apps/www/settings/signatures/new/index.tsx +129 -0
  83. package/apps/www/settings/signatures/signatureDefaults.ts +36 -0
  84. package/apps/www/tasks/index.tsx +530 -0
  85. package/dist/apps/admin/_404.js +4 -0
  86. package/dist/apps/admin/_500.js +4 -0
  87. package/dist/apps/admin/_layout.js +10 -0
  88. package/dist/apps/admin/audit-log/index.js +66 -0
  89. package/dist/apps/admin/branding/index.js +126 -0
  90. package/dist/apps/admin/distribution-lists/[uid].js +34 -0
  91. package/dist/apps/admin/distribution-lists/index.js +31 -0
  92. package/dist/apps/admin/distribution-lists/new/index.js +51 -0
  93. package/dist/apps/admin/domains/[uid].js +102 -0
  94. package/dist/apps/admin/domains/index.js +31 -0
  95. package/dist/apps/admin/domains/new/index.js +41 -0
  96. package/dist/apps/admin/index.js +32 -0
  97. package/dist/apps/admin/ingest-queue/index.js +48 -0
  98. package/dist/apps/admin/mailboxes/[uid].js +59 -0
  99. package/dist/apps/admin/mailboxes/new/index.js +93 -0
  100. package/dist/apps/admin/quarantine/index.js +66 -0
  101. package/dist/apps/admin/transport-rules/[uid].js +87 -0
  102. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +37 -0
  103. package/dist/apps/admin/transport-rules/index.js +32 -0
  104. package/dist/apps/admin/transport-rules/new/index.js +50 -0
  105. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +64 -0
  106. package/dist/apps/shared/components/admin/layout/AdminShell.js +94 -0
  107. package/dist/apps/shared/components/admin/mailboxes/MailboxTable.js +16 -0
  108. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +59 -0
  109. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +67 -0
  110. package/dist/apps/shared/components/booking/AvailabilityEditor.js +45 -0
  111. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +44 -0
  112. package/dist/apps/shared/components/calendar/EventModal.js +167 -0
  113. package/dist/apps/shared/components/calendar/MonthView.js +38 -0
  114. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +55 -0
  115. package/dist/apps/shared/components/calendar/ResourcePicker.js +36 -0
  116. package/dist/apps/shared/components/calendar/SplitDayView.js +44 -0
  117. package/dist/apps/shared/components/calendar/TimeGridView.js +58 -0
  118. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +96 -0
  119. package/dist/apps/shared/components/contacts/ContactDetailPane.js +11 -0
  120. package/dist/apps/shared/components/contacts/ContactForm.js +85 -0
  121. package/dist/apps/shared/components/contacts/ContactsSidebar.js +105 -0
  122. package/dist/apps/shared/components/contacts/ContactsToolbar.js +32 -0
  123. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +93 -0
  124. package/dist/apps/shared/components/layout/AppShell.js +68 -0
  125. package/dist/apps/shared/components/layout/BrandingChrome.js +18 -0
  126. package/dist/apps/shared/components/layout/MailboxProvisioning.js +75 -0
  127. package/dist/apps/shared/components/layout/UserMenu.js +59 -0
  128. package/dist/apps/shared/components/mail/ConversationList.js +17 -0
  129. package/dist/apps/shared/components/mail/ConversationThreadPane.js +112 -0
  130. package/dist/apps/shared/components/mail/MessageDetailPane.js +153 -0
  131. package/dist/apps/shared/components/mail/compose/ComposeContext.js +55 -0
  132. package/dist/apps/shared/components/mail/compose/ComposeToolbar.js +88 -0
  133. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +261 -0
  134. package/dist/apps/shared/components/mail/compose/EmojiPicker.js +25 -0
  135. package/dist/apps/shared/components/mail/compose/GifPicker.js +36 -0
  136. package/dist/apps/shared/components/mail/compose/RichTextEditor.js +60 -0
  137. package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +33 -0
  138. package/dist/apps/shared/components/mail/layout/MailShell.js +139 -0
  139. package/dist/apps/shared/components/rules/RuleBuilder.js +78 -0
  140. package/dist/apps/shared/components/settings/layout/SettingsShell.js +100 -0
  141. package/dist/apps/shared/components/tasks/TasksSidebar.js +81 -0
  142. package/dist/apps/shared/components/tasks/TasksToolbar.js +15 -0
  143. package/dist/apps/shared/components/tasks/layout/TasksShell.js +92 -0
  144. package/dist/apps/shared/styles/app.css +265 -0
  145. package/dist/apps/www/_404.js +4 -0
  146. package/dist/apps/www/_500.js +4 -0
  147. package/dist/apps/www/_layout.js +8 -0
  148. package/dist/apps/www/calendar/index.js +228 -0
  149. package/dist/apps/www/contacts/[uid].js +62 -0
  150. package/dist/apps/www/contacts/index.js +262 -0
  151. package/dist/apps/www/index.js +228 -0
  152. package/dist/apps/www/messages/[uid].js +47 -0
  153. package/dist/apps/www/settings/auto-reply/index.js +54 -0
  154. package/dist/apps/www/settings/booking-types/[uid].js +129 -0
  155. package/dist/apps/www/settings/booking-types/index.js +29 -0
  156. package/dist/apps/www/settings/booking-types/new/index.js +86 -0
  157. package/dist/apps/www/settings/filters/[uid].js +94 -0
  158. package/dist/apps/www/settings/filters/_mailFilterRuleConfig.js +65 -0
  159. package/dist/apps/www/settings/filters/index.js +30 -0
  160. package/dist/apps/www/settings/filters/new/index.js +61 -0
  161. package/dist/apps/www/settings/focused-inbox/index.js +69 -0
  162. package/dist/apps/www/settings/read-receipts/index.js +51 -0
  163. package/dist/apps/www/settings/signatures/[uid].js +96 -0
  164. package/dist/apps/www/settings/signatures/index.js +34 -0
  165. package/dist/apps/www/settings/signatures/new/index.js +63 -0
  166. package/dist/apps/www/settings/signatures/signatureDefaults.js +30 -0
  167. package/dist/apps/www/tasks/index.js +280 -0
  168. package/package.json +87 -0
@@ -0,0 +1,118 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import React, { useState } from "react";
6
+ import { BookingAvailabilityWindow } from "@rapidmx/react-shared/booking/bookingApi.js";
7
+ import Button from "@rapidmx/react-shared/components/buttons/Button.js";
8
+
9
+ const DAY_LABELS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
10
+ const INPUT_CLASS =
11
+ "text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
12
+
13
+ function minutesToTimeValue(minutes: number): string {
14
+ const h = Math.floor(minutes / 60)
15
+ .toString()
16
+ .padStart(2, "0");
17
+ const m = (minutes % 60).toString().padStart(2, "0");
18
+ return `${h}:${m}`;
19
+ }
20
+
21
+ function timeValueToMinutes(value: string): number {
22
+ const [h, m] = value.split(":").map(Number);
23
+ return h * 60 + m;
24
+ }
25
+
26
+ export interface AvailabilityEditorProps {
27
+ value: BookingAvailabilityWindow[];
28
+ onChange: (value: BookingAvailabilityWindow[]) => void;
29
+ }
30
+
31
+ /**
32
+ * Adds/removes `BookingType.availability` weekly windows — deliberately does not cover `dateOverrides`
33
+ * (per-date blackouts/exceptions), a separate, less commonly needed escape hatch left for a future pass;
34
+ * `BookingUtils.generateCandidateSlots()` already handles an empty `dateOverrides` array gracefully.
35
+ */
36
+ export default function AvailabilityEditor({ value, onChange }: AvailabilityEditorProps) {
37
+ const [dayOfWeek, setDayOfWeek] = useState(1);
38
+ const [startTime, setStartTime] = useState("09:00");
39
+ const [endTime, setEndTime] = useState("17:00");
40
+ const [error, setError] = useState<string | null>(null);
41
+
42
+ function handleAdd() {
43
+ const startMinute = timeValueToMinutes(startTime);
44
+ const endMinute = timeValueToMinutes(endTime);
45
+ if (startMinute >= endMinute) {
46
+ setError("Start time must be before end time.");
47
+ return;
48
+ }
49
+ setError(null);
50
+ onChange([...value, { dayOfWeek, startMinute, endMinute }]);
51
+ }
52
+
53
+ function handleRemove(index: number) {
54
+ onChange(value.filter((_, i) => i !== index));
55
+ }
56
+
57
+ return (
58
+ <div className="flex flex-col gap-3">
59
+ {error && <p className="text-sm text-danger">{error}</p>}
60
+ {value.length === 0 ? (
61
+ <p className="text-sm text-text-muted">No availability windows yet — add one below.</p>
62
+ ) : (
63
+ <ul className="flex flex-col gap-2">
64
+ {value.map((window, index) => (
65
+ <li
66
+ key={index}
67
+ className="flex items-center justify-between gap-3 py-2 px-3 border border-border rounded-sm text-sm"
68
+ >
69
+ <span>
70
+ {DAY_LABELS[window.dayOfWeek]} {minutesToTimeValue(window.startMinute)}&ndash;
71
+ {minutesToTimeValue(window.endMinute)}
72
+ </span>
73
+ <button
74
+ type="button"
75
+ onClick={() => handleRemove(index)}
76
+ className="text-sm text-danger hover:underline"
77
+ >
78
+ Remove
79
+ </button>
80
+ </li>
81
+ ))}
82
+ </ul>
83
+ )}
84
+ <div className="flex flex-wrap items-center gap-2">
85
+ <select
86
+ aria-label="Day of week"
87
+ value={dayOfWeek}
88
+ onChange={(e) => setDayOfWeek(Number(e.target.value))}
89
+ className={INPUT_CLASS}
90
+ >
91
+ {DAY_LABELS.map((label, i) => (
92
+ <option key={label} value={i}>
93
+ {label}
94
+ </option>
95
+ ))}
96
+ </select>
97
+ <input
98
+ type="time"
99
+ aria-label="Start time"
100
+ value={startTime}
101
+ onChange={(e) => setStartTime(e.target.value)}
102
+ className={INPUT_CLASS}
103
+ />
104
+ <span className="text-sm text-text-muted">to</span>
105
+ <input
106
+ type="time"
107
+ aria-label="End time"
108
+ value={endTime}
109
+ onChange={(e) => setEndTime(e.target.value)}
110
+ className={INPUT_CLASS}
111
+ />
112
+ <Button type="button" variant="secondary" className="!w-auto" onClick={handleAdd}>
113
+ Add window
114
+ </Button>
115
+ </div>
116
+ </div>
117
+ );
118
+ }
@@ -0,0 +1,119 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import React, { FormEvent, useState } from "react";
6
+ import { CALENDAR_COLOR_PALETTE, colorForFolder } from "@rapidmx/react-shared/calendar/calendarColors.js";
7
+ import { Folder } from "@rapidmx/react-shared/mail/mailApi.js";
8
+ import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
9
+
10
+ export interface CalendarListSidebarProps {
11
+ /** Every `type === "calendar"` folder for the currently-resolved mailbox. */
12
+ calendars: Folder[];
13
+ checkedFolderUids: Set<string>;
14
+ onToggle: (folderUid: string) => void;
15
+ onAddCalendar: (name: string, color: string) => Promise<void>;
16
+ }
17
+
18
+ /**
19
+ * Calendar's left-sidebar checklist of "My calendars" — one row per `calendar`-type folder, a color
20
+ * swatch matching `colorForFolder`, and a checkbox controlling whether that calendar's events are
21
+ * currently shown (the checked set is owned by `apps/www/calendar/index.tsx`, since it drives which
22
+ * folders get fanned out to on every reload). "+ Add calendar" is a real `createFolder({type:
23
+ * "calendar", ...})` call — see the Phase 4 plan's own note that folder creation has no type
24
+ * restriction server-side, so this needed no new backend route.
25
+ */
26
+ export default function CalendarListSidebar({ calendars, checkedFolderUids, onToggle, onAddCalendar }: CalendarListSidebarProps) {
27
+ const [adding, setAdding] = useState(false);
28
+ const [name, setName] = useState("");
29
+ const [color, setColor] = useState(CALENDAR_COLOR_PALETTE[0]);
30
+ const [error, setError] = useState<string | null>(null);
31
+ const [saving, setSaving] = useState(false);
32
+
33
+ async function handleAdd(e: FormEvent) {
34
+ e.preventDefault();
35
+ if (!name.trim()) {
36
+ return;
37
+ }
38
+ setError(null);
39
+ setSaving(true);
40
+ try {
41
+ await onAddCalendar(name.trim(), color);
42
+ setName("");
43
+ setColor(CALENDAR_COLOR_PALETTE[0]);
44
+ setAdding(false);
45
+ } catch {
46
+ setError("Could not create this calendar.");
47
+ } finally {
48
+ setSaving(false);
49
+ }
50
+ }
51
+
52
+ return (
53
+ <nav aria-label="My calendars" className="p-3 border-t border-border">
54
+ <div className="flex items-center justify-between mb-1">
55
+ <h2 className="text-xs font-bold uppercase tracking-wide text-text-muted">My calendars</h2>
56
+ <button
57
+ type="button"
58
+ onClick={() => setAdding(true)}
59
+ aria-label="Add calendar"
60
+ className="text-xs font-bold text-primary-dark hover:underline"
61
+ >
62
+ +
63
+ </button>
64
+ </div>
65
+ {error && (
66
+ <div className="mb-1">
67
+ <Alert>{error}</Alert>
68
+ </div>
69
+ )}
70
+ <div className="flex flex-col gap-0.5">
71
+ {calendars.map((folder) => (
72
+ <label key={folder.uid} className="flex items-center gap-2 text-sm px-1 py-1 rounded-sm hover:bg-surface-alt cursor-pointer">
73
+ <input
74
+ type="checkbox"
75
+ checked={checkedFolderUids.has(folder.uid)}
76
+ onChange={() => onToggle(folder.uid)}
77
+ style={{ accentColor: colorForFolder(folder) }}
78
+ />
79
+ <span aria-hidden="true" className="w-2.5 h-2.5 rounded-full shrink-0" style={{ backgroundColor: colorForFolder(folder) }} />
80
+ <span className="truncate">{folder.name}</span>
81
+ </label>
82
+ ))}
83
+ </div>
84
+ {adding && (
85
+ <form onSubmit={handleAdd} className="flex flex-col gap-1.5 mt-2">
86
+ <input
87
+ type="text"
88
+ autoFocus
89
+ aria-label="New calendar name"
90
+ value={name}
91
+ onChange={(e) => setName(e.target.value)}
92
+ className="text-xs py-1 px-1.5 border border-border rounded-sm bg-surface"
93
+ />
94
+ <div className="flex items-center gap-1" role="radiogroup" aria-label="Calendar color">
95
+ {CALENDAR_COLOR_PALETTE.map((swatch) => (
96
+ <button
97
+ key={swatch}
98
+ type="button"
99
+ aria-label={`Color ${swatch}`}
100
+ aria-pressed={color === swatch}
101
+ onClick={() => setColor(swatch)}
102
+ className={["w-4 h-4 rounded-full shrink-0", color === swatch ? "ring-2 ring-offset-1 ring-text" : ""].join(" ")}
103
+ style={{ backgroundColor: swatch }}
104
+ />
105
+ ))}
106
+ </div>
107
+ <div className="flex items-center gap-2">
108
+ <button type="submit" disabled={saving} className="text-xs font-semibold text-primary-dark">
109
+ Add
110
+ </button>
111
+ <button type="button" onClick={() => setAdding(false)} className="text-xs text-text-muted">
112
+ Cancel
113
+ </button>
114
+ </div>
115
+ </form>
116
+ )}
117
+ </nav>
118
+ );
119
+ }