@rapidmx/crm-plugin 0.1.0 → 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.
package/README.md CHANGED
@@ -142,7 +142,7 @@ A deployment administrator has no access to a workspace they aren't a member of.
142
142
 
143
143
  | Resource | Endpoints |
144
144
  | --- | --- |
145
- | `workspaces` | `GET /`, `POST /`, `GET/PUT/DELETE /:workspaceUid`, `GET/POST /:workspaceUid/members`, `PUT/DELETE /:workspaceUid/members/:userUid`, `GET/POST /:workspaceUid/senders`, `PUT/DELETE /:workspaceUid/senders/:senderUid` |
145
+ | `workspaces` | `GET /`, `POST /`, `GET/PUT/DELETE /:workspaceUid`, `GET/POST /:workspaceUid/members`, `PUT/DELETE /:workspaceUid/members/:userUid`, `GET/POST /:workspaceUid/senders`, `PUT/DELETE /:workspaceUid/senders/:senderUid`, `POST /:workspaceUid/sendable-mailboxes` (`{ mailboxUids }`: which of them the caller can send from) |
146
146
  | `contacts`, `companies` | `GET /:workspaceUid`, `POST /:workspaceUid/search`, `POST /:workspaceUid/bulk`, `POST /:workspaceUid/export`, `GET/PUT/DELETE /:workspaceUid/:uid`, `POST /:workspaceUid` |
147
147
  | `properties`, `notes`, `tasks` | `GET /:workspaceUid`, `POST /:workspaceUid/search`, `GET/PUT/DELETE /:workspaceUid/:uid`, `POST /:workspaceUid` |
148
148
  | `timeline` | `GET /:workspaceUid/:subjectType/:subjectUid` |
@@ -26,6 +26,7 @@ import AppShell, { AppShellProps } from "@rapidmx/web-client/shared/components/l
26
26
  import Alert from "@rapidmx/web-client/lib/components/feedback/Alert.js";
27
27
  import Button from "@rapidmx/web-client/lib/components/buttons/Button.js";
28
28
  import FormField from "@rapidmx/web-client/lib/components/forms/FormField.js";
29
+ import Modal from "@rapidmx/web-client/lib/components/overlays/Modal.js";
29
30
  import { Workspace, createWorkspace, errorMessage, listWorkspaces } from "../crmApi.js";
30
31
 
31
32
  /** The props every CRM page receives from the server (the `www` host's), passed straight to its shell. */
@@ -34,6 +35,9 @@ export type CrmPageProps = Omit<AppShellProps, "active">;
34
35
  /** The CRM's sections, each a page under `/crm`. */
35
36
  export type CrmSection = "contacts" | "companies" | "lists" | "deals" | "pipelines" | "segments" | "forms" | "templates" | "campaigns" | "automations" | "scoring" | "tasks" | "imports" | "reports" | "integrations" | "settings";
36
37
 
38
+ /** The workspace switcher's choice that opens the "New workspace" dialog rather than switching. */
39
+ export const NEW_WORKSPACE = "__new__";
40
+
37
41
  /** Where the selected workspace is remembered between visits (per browser). */
38
42
  export const WORKSPACE_STORAGE_KEY = "rapidmx.crm.workspace";
39
43
 
@@ -112,12 +116,14 @@ function remember(workspaceUid: string): void {
112
116
  /**
113
117
  * The frame of every CRM page: the web client's app chrome (with CRM highlighted on the app rail), a sidebar with the workspace
114
118
  * switcher and the CRM's sections, and the page. It loads the caller's workspaces and picks one (`?w=`, else the last used, else the
115
- * first); a caller with none is asked to create one. Pages read the selection with `useCrm()`.
119
+ * first); a caller with none is asked to create one, and the switcher offers to create another. Pages read the selection with
120
+ * `useCrm()`.
116
121
  */
117
122
  export default function CrmShell({ section, children, ...props }: PropsWithChildren<CrmPageProps & { section: CrmSection }>) {
118
123
  const [workspaces, setWorkspaces] = useState<Workspace[] | null>(null);
119
124
  const [selected, setSelected] = useState<Workspace | null>(null);
120
125
  const [error, setError] = useState<string | null>(null);
126
+ const [creating, setCreating] = useState<boolean>(false);
121
127
 
122
128
  async function load(preferred: string | null): Promise<void> {
123
129
  try {
@@ -161,12 +167,14 @@ export default function CrmShell({ section, children, ...props }: PropsWithChild
161
167
  <label htmlFor="crm-workspace" className="text-xs uppercase tracking-wide text-text-muted px-2">
162
168
  Workspace
163
169
  </label>
164
- <select id="crm-workspace" className={`${INPUT_CLASS} mb-3`} value={context.workspace.uid} onChange={(event) => switchTo(event.target.value)}>
170
+ <select id="crm-workspace" className={`${INPUT_CLASS} mb-3`} value={context.workspace.uid} onChange={(event) => (event.target.value === NEW_WORKSPACE ? setCreating(true) : switchTo(event.target.value))}
171
+ >
165
172
  {context.workspaces.map((workspace) => (
166
173
  <option key={workspace.uid} value={workspace.uid}>
167
174
  {workspace.name}
168
175
  </option>
169
176
  ))}
177
+ <option value={NEW_WORKSPACE}>+ New workspace…</option>
170
178
  </select>
171
179
  {SECTIONS.map(({ id, label, path, icon: Icon }) => (
172
180
  <a
@@ -187,6 +195,9 @@ export default function CrmShell({ section, children, ...props }: PropsWithChild
187
195
  {error && <Alert>{error}</Alert>}
188
196
  {!error && workspaces === null && <p className="text-sm text-text-muted">Loading&hellip;</p>}
189
197
  {workspaces !== null && !context && <CreateFirstWorkspace onCreated={(workspace) => switchTo(workspace.uid)} />}
198
+ <Modal open={creating} onClose={() => setCreating(false)} title="New workspace">
199
+ <CreateWorkspaceForm onCreated={(workspace) => switchTo(workspace.uid)} />
200
+ </Modal>
190
201
  {context && <CrmContext.Provider value={context}>{children}</CrmContext.Provider>}
191
202
  </main>
192
203
  </div>
@@ -196,6 +207,19 @@ export default function CrmShell({ section, children, ...props }: PropsWithChild
196
207
 
197
208
  /** The form a caller with no workspace sees. */
198
209
  export function CreateFirstWorkspace({ onCreated }: { onCreated: (workspace: Workspace) => void }) {
210
+ return (
211
+ <div className="max-w-md">
212
+ <h1 className="text-lg font-bold tracking-tight mb-2">Create your CRM workspace</h1>
213
+ <p className="text-sm text-text-muted mb-4">
214
+ A workspace holds your team&apos;s contacts, companies and tasks. You can invite colleagues to it once it exists.
215
+ </p>
216
+ <CreateWorkspaceForm onCreated={onCreated} />
217
+ </div>
218
+ );
219
+ }
220
+
221
+ /** Names and creates a workspace (in the browser's time zone - changeable in its settings), then hands it to `onCreated`. */
222
+ export function CreateWorkspaceForm({ onCreated }: { onCreated: (workspace: Workspace) => void }) {
199
223
  const [name, setName] = useState("");
200
224
  const [error, setError] = useState<string | null>(null);
201
225
  const [saving, setSaving] = useState(false);
@@ -212,11 +236,7 @@ export function CreateFirstWorkspace({ onCreated }: { onCreated: (workspace: Wor
212
236
  }
213
237
 
214
238
  return (
215
- <form onSubmit={submit} className="max-w-md">
216
- <h1 className="text-lg font-bold tracking-tight mb-2">Create your CRM workspace</h1>
217
- <p className="text-sm text-text-muted mb-4">
218
- A workspace holds your team&apos;s contacts, companies and tasks. You can invite colleagues to it once it exists.
219
- </p>
239
+ <form onSubmit={submit} className="min-w-[20rem]">
220
240
  {error && <Alert>{error}</Alert>}
221
241
  <FormField label="Name" htmlFor="crm-new-workspace">
222
242
  <input id="crm-new-workspace" className={INPUT_CLASS} value={name} onChange={(event) => setName(event.target.value)} required />
@@ -2,14 +2,20 @@
2
2
  // Copyright (C) 2026 Jean-Philippe Steinmetz
3
3
  // SPDX-License-Identifier: MPL-2.0
4
4
  ///////////////////////////////////////////////////////////////////////////////
5
- import React, { FormEvent, useEffect, useState } from "react";
5
+ import React, { FormEvent, useEffect, useMemo, useState } from "react";
6
6
  import Alert from "@rapidmx/web-client/lib/components/feedback/Alert.js";
7
7
  import Button from "@rapidmx/web-client/lib/components/buttons/Button.js";
8
8
  import FormField from "@rapidmx/web-client/lib/components/forms/FormField.js";
9
+ import TimeZonePicker from "@rapidmx/web-client/lib/components/pickers/TimeZonePicker.js";
10
+ import { timeZoneOptions } from "@rapidmx/web-client/lib/util/timeZone.js";
11
+ import { listMailboxes } from "@rapidmx/web-client/lib/mail/mailApi.js";
12
+ import { RecipientSuggestion, searchDirectory } from "@rapidmx/web-client/lib/mail/directoryApi.js";
13
+ import RecipientInput from "@rapidmx/web-client/shared/components/mail/compose/RecipientInput.js";
9
14
  import {
10
15
  CrmObjectType,
11
16
  PropertyDefinition,
12
17
  PropertyType,
18
+ SendableMailbox,
13
19
  WorkspaceMember,
14
20
  WorkspaceRole,
15
21
  WorkspaceSender,
@@ -28,6 +34,7 @@ import {
28
34
  listSenders,
29
35
  removeMember,
30
36
  removeSender,
37
+ sendableMailboxes,
31
38
  updateMember,
32
39
  updateWorkspace,
33
40
  updateSender,
@@ -93,13 +100,13 @@ function Details() {
93
100
  }
94
101
  }
95
102
 
96
- const fields: [keyof typeof values, string][] = [
103
+ const fields: [Exclude<keyof typeof values, "timezone">, string][] = [
97
104
  ["name", "Name"],
98
105
  ["description", "Description"],
99
- ["timezone", "Time zone"],
100
106
  ["postalAddress", "Postal address (shown in marketing email footers)"],
101
107
  ["website", "Website"],
102
108
  ];
109
+ const zones: string[] = useMemo(() => timeZoneOptions(workspace.timezone), [workspace.timezone]);
103
110
  return (
104
111
  <form onSubmit={save} className="flex flex-col gap-3" aria-label="Workspace details">
105
112
  <h2 className="text-sm font-semibold uppercase tracking-wide text-text-muted">Workspace</h2>
@@ -116,6 +123,13 @@ function Details() {
116
123
  />
117
124
  </FormField>
118
125
  ))}
126
+ <FormField label="Time zone" htmlFor="workspace-timezone">
127
+ {canManage ? (
128
+ <TimeZonePicker id="workspace-timezone" value={values.timezone} zones={zones} onChange={(timezone) => setValues((current) => ({ ...current, timezone }))} />
129
+ ) : (
130
+ <input id="workspace-timezone" className={INPUT_CLASS} value={values.timezone} disabled />
131
+ )}
132
+ </FormField>
119
133
  {canManage && (
120
134
  <Button type="submit" className="!w-auto self-start">
121
135
  Save
@@ -125,12 +139,25 @@ function Details() {
125
139
  );
126
140
  }
127
141
 
142
+ /** The address of an entry of the people field: `Name <address>` or a bare address. */
143
+ export function chipAddress(chip: string): string {
144
+ const match: RegExpExecArray | null = /<([^<>]+)>\s*$/.exec(chip);
145
+ return (match ? match[1] : chip).trim().toLowerCase();
146
+ }
147
+
148
+ /** Suggests only people with an account on this server - the only ones who can be members. */
149
+ export async function suggestPeople(query: string, options: { limit?: number; signal?: AbortSignal }): Promise<RecipientSuggestion[]> {
150
+ return (await searchDirectory(query, options)).filter((suggestion) => suggestion.kind === "user");
151
+ }
152
+
128
153
  function Members() {
129
154
  const { workspace, canManage } = useCrm();
130
155
  const [members, setMembers] = useState<WorkspaceMember[]>([]);
131
- const [address, setAddress] = useState("");
156
+ const [text, setText] = useState("");
157
+ const [people, setPeople] = useState<{ chips: string[]; pending: string }>({ chips: [], pending: "" });
132
158
  const [role, setRole] = useState<WorkspaceRole>("editor");
133
159
  const [error, setError] = useState<string | null>(null);
160
+ const [adding, setAdding] = useState(false);
134
161
 
135
162
  async function load(): Promise<void> {
136
163
  setMembers(await listMembers(workspace.uid));
@@ -150,55 +177,90 @@ function Members() {
150
177
  }
151
178
  }
152
179
 
180
+ /** Adds everyone in the field; those that fail stay in it, with why. */
181
+ async function add(event: FormEvent): Promise<void> {
182
+ event.preventDefault();
183
+ const entries: string[] = [...people.chips, ...(people.pending.trim() ? [people.pending.trim()] : [])];
184
+ setAdding(true);
185
+ const failed: string[] = [];
186
+ const reasons: string[] = [];
187
+ for (const entry of entries) {
188
+ try {
189
+ await addMember(workspace.uid, { address: chipAddress(entry), role });
190
+ } catch (err) {
191
+ failed.push(entry);
192
+ reasons.push(`${chipAddress(entry)}: ${errorMessage(err, "could not be added.")}`);
193
+ }
194
+ }
195
+ setText(failed.join(", "));
196
+ setPeople({ chips: failed, pending: "" });
197
+ setAdding(false);
198
+ try {
199
+ await load();
200
+ setError(reasons.length > 0 ? reasons.join(" ") : null);
201
+ } catch (err) {
202
+ setError([...reasons, errorMessage(err, "Could not load the members.")].join(" "));
203
+ }
204
+ }
205
+
206
+ const count: number = people.chips.length + (people.pending.trim() ? 1 : 0);
153
207
  return (
154
208
  <section aria-label="Members" className="flex flex-col gap-3">
155
209
  <h2 className="text-sm font-semibold uppercase tracking-wide text-text-muted">Members</h2>
156
210
  {error && <Alert>{error}</Alert>}
157
211
  <ul className="flex flex-col divide-y divide-border border-y border-border">
158
- {members.map((member) => (
159
- <li key={member.userUid} className="flex items-center gap-3 py-2 text-sm">
160
- <span className="flex-1">{member.displayName || member.address || member.userUid}</span>
161
- {canManage ? (
162
- <select
163
- aria-label={`Role of ${member.displayName || member.address || member.userUid}`}
164
- className={`${INPUT_CLASS} !w-32`}
165
- value={member.role}
166
- onChange={(event) => void act(() => updateMember(workspace.uid, member.userUid, event.target.value as WorkspaceRole), "Could not change the role.")}
167
- >
168
- {ROLES.map((entry) => (
169
- <option key={entry.value} value={entry.value}>
170
- {entry.label}
171
- </option>
172
- ))}
173
- </select>
174
- ) : (
175
- <span className="text-text-muted">{member.role}</span>
176
- )}
177
- {canManage && (
178
- <button
179
- type="button"
180
- className="text-xs text-text-muted hover:text-danger"
181
- onClick={() => void act(() => removeMember(workspace.uid, member.userUid), "Could not remove the member.")}
182
- >
183
- Remove
184
- </button>
185
- )}
186
- </li>
187
- ))}
212
+ {members.map((member) => {
213
+ const name: string = member.displayName || member.address || member.userUid;
214
+ return (
215
+ <li key={member.userUid} className="flex items-center gap-3 py-2 text-sm">
216
+ <span className="flex-1 min-w-0">
217
+ <span className="block truncate">{name}</span>
218
+ {member.displayName && member.address && <span className="block truncate text-xs text-text-muted">{member.address}</span>}
219
+ </span>
220
+ {canManage ? (
221
+ <select
222
+ aria-label={`Role of ${name}`}
223
+ className={`${INPUT_CLASS} !w-32`}
224
+ value={member.role}
225
+ onChange={(event) => void act(() => updateMember(workspace.uid, member.userUid, event.target.value as WorkspaceRole), "Could not change the role.")}
226
+ >
227
+ {ROLES.map((entry) => (
228
+ <option key={entry.value} value={entry.value}>
229
+ {entry.label}
230
+ </option>
231
+ ))}
232
+ </select>
233
+ ) : (
234
+ <span className="text-text-muted">{member.role}</span>
235
+ )}
236
+ {canManage && (
237
+ <button
238
+ type="button"
239
+ className="text-xs text-text-muted hover:text-danger"
240
+ onClick={() => void act(() => removeMember(workspace.uid, member.userUid), "Could not remove the member.")}
241
+ >
242
+ Remove
243
+ </button>
244
+ )}
245
+ </li>
246
+ );
247
+ })}
188
248
  </ul>
189
249
  {canManage && (
190
- <form
191
- className="flex flex-wrap gap-2"
192
- aria-label="Add member"
193
- onSubmit={(event) => {
194
- event.preventDefault();
195
- void act(async () => {
196
- await addMember(workspace.uid, { address, role });
197
- setAddress("");
198
- }, "Could not add the member.");
199
- }}
200
- >
201
- <input aria-label="Member address" placeholder="colleague@example.com" className={`${INPUT_CLASS} !w-72`} value={address} onChange={(event) => setAddress(event.target.value)} />
250
+ <form className="flex flex-wrap items-start gap-2" aria-label="Add members" onSubmit={(event) => void add(event)}>
251
+ <div className="flex-1 min-w-[18rem]">
252
+ <RecipientInput
253
+ id="crm-new-members"
254
+ label="People"
255
+ ariaLabel="People to add"
256
+ placeholder="Search people by name or address"
257
+ value={text}
258
+ onChange={setText}
259
+ onEdit={(chips, pending) => setPeople({ chips, pending })}
260
+ fetchSuggestions={suggestPeople}
261
+ className={INPUT_CLASS}
262
+ />
263
+ </div>
202
264
  <select aria-label="New member role" className={`${INPUT_CLASS} !w-32`} value={role} onChange={(event) => setRole(event.target.value as WorkspaceRole)}>
203
265
  {ROLES.map((entry) => (
204
266
  <option key={entry.value} value={entry.value}>
@@ -206,8 +268,8 @@ function Members() {
206
268
  </option>
207
269
  ))}
208
270
  </select>
209
- <Button type="submit" variant="secondary" disabled={address.trim() === ""} className="!w-auto">
210
- Add member
271
+ <Button type="submit" variant="secondary" loading={adding} disabled={adding || count === 0} className="!w-auto">
272
+ {count > 1 ? `Add ${count} members` : "Add member"}
211
273
  </Button>
212
274
  </form>
213
275
  )}
@@ -218,7 +280,8 @@ function Members() {
218
280
  function Senders() {
219
281
  const { workspace, canManage } = useCrm();
220
282
  const [senders, setSenders] = useState<WorkspaceSender[]>([]);
221
- const [fromAddress, setFromAddress] = useState("");
283
+ const [mailboxes, setMailboxes] = useState<SendableMailbox[] | null>(null);
284
+ const [mailboxUid, setMailboxUid] = useState("");
222
285
  const [fromName, setFromName] = useState("");
223
286
  const [error, setError] = useState<string | null>(null);
224
287
 
@@ -230,11 +293,24 @@ function Senders() {
230
293
  load().catch((err) => setError(errorMessage(err, "Could not load the senders.")));
231
294
  }, [workspace.uid]);
232
295
 
296
+ useEffect(() => {
297
+ if (!canManage) {
298
+ return;
299
+ }
300
+ (async () => {
301
+ const own = await listMailboxes({ limit: 200 });
302
+ setMailboxes(own.length > 0 ? await sendableMailboxes(workspace.uid, own.map((mailbox) => mailbox.uid)) : []);
303
+ })().catch(() => setMailboxes([]));
304
+ }, [workspace.uid, canManage]);
305
+
306
+ const choices: SendableMailbox[] = (mailboxes ?? []).filter((mailbox) => !senders.some((sender) => sender.fromAddress === mailbox.address));
307
+ const chosen: SendableMailbox | undefined = choices.find((mailbox) => mailbox.uid === mailboxUid);
308
+
233
309
  async function add(event: FormEvent): Promise<void> {
234
310
  event.preventDefault();
235
311
  try {
236
- await addSender(workspace.uid, { fromAddress, ...(fromName.trim() ? { fromName } : {}) });
237
- setFromAddress("");
312
+ await addSender(workspace.uid, { fromAddress: chosen!.address, ...(fromName.trim() ? { fromName: fromName.trim() } : {}) });
313
+ setMailboxUid("");
238
314
  setFromName("");
239
315
  setError(null);
240
316
  await load();
@@ -246,7 +322,7 @@ function Senders() {
246
322
  return (
247
323
  <section aria-label="Senders" className="flex flex-col gap-3">
248
324
  <h2 className="text-sm font-semibold uppercase tracking-wide text-text-muted">Send as</h2>
249
- <p className="text-sm text-text-muted">Addresses of mailboxes you can send from. Replies to mail sent from the CRM arrive in these mailboxes.</p>
325
+ <p className="text-sm text-text-muted">Mailboxes the CRM sends from. Replies to mail sent from the CRM arrive in these mailboxes.</p>
250
326
  {error && <Alert>{error}</Alert>}
251
327
  <ul className="flex flex-col gap-1">
252
328
  {senders.map((sender) => (
@@ -285,11 +361,29 @@ function Senders() {
285
361
  </li>
286
362
  ))}
287
363
  </ul>
288
- {canManage && (
289
- <form className="flex flex-wrap gap-2" aria-label="Add sender" onSubmit={add}>
290
- <input aria-label="Sender address" placeholder="sales@example.com" className={`${INPUT_CLASS} !w-64`} value={fromAddress} onChange={(event) => setFromAddress(event.target.value)} />
364
+ {canManage && mailboxes !== null && mailboxes.length === 0 && (
365
+ <p className="text-sm text-text-muted">You have no mailbox you can send from. Its owner can add it, or give you send access to it.</p>
366
+ )}
367
+ {canManage && choices.length > 0 && (
368
+ <form className="flex flex-wrap gap-2" aria-label="Add sender" onSubmit={(event) => void add(event)}>
369
+ <select
370
+ aria-label="Mailbox"
371
+ className={`${INPUT_CLASS} !w-72`}
372
+ value={mailboxUid}
373
+ onChange={(event) => {
374
+ setMailboxUid(event.target.value);
375
+ setFromName(choices.find((mailbox) => mailbox.uid === event.target.value)?.displayName ?? "");
376
+ }}
377
+ >
378
+ <option value="">Choose a mailbox…</option>
379
+ {choices.map((mailbox) => (
380
+ <option key={mailbox.uid} value={mailbox.uid}>
381
+ {mailbox.displayName ? `${mailbox.displayName} <${mailbox.address}>` : mailbox.address}
382
+ </option>
383
+ ))}
384
+ </select>
291
385
  <input aria-label="Sender name" placeholder="Name shown to recipients" className={`${INPUT_CLASS} !w-56`} value={fromName} onChange={(event) => setFromName(event.target.value)} />
292
- <Button type="submit" variant="secondary" disabled={fromAddress.trim() === ""} className="!w-auto">
386
+ <Button type="submit" variant="secondary" disabled={!chosen} className="!w-auto">
293
387
  Add sender
294
388
  </Button>
295
389
  </form>
@@ -209,6 +209,16 @@ export const updateSender = (uid: string, senderUid: string, input: { fromName?:
209
209
  apiFetch(`/mail/crm/workspaces/${enc(uid)}/senders/${enc(senderUid)}`, json("PUT", input));
210
210
  export const addSender = (uid: string, input: { fromAddress: string; fromName?: string; replyToAddress?: string; logEmail?: boolean }): Promise<WorkspaceSender> =>
211
211
  apiFetch(`/mail/crm/workspaces/${enc(uid)}/senders`, json("POST", input));
212
+ /** A mailbox the caller may add as a sender. */
213
+ export interface SendableMailbox {
214
+ uid: string;
215
+ address: string;
216
+ displayName: string;
217
+ }
218
+
219
+ /** Which of `mailboxUids` (the caller's mailboxes) the caller can send from, for the "Send as" picker. */
220
+ export const sendableMailboxes = (uid: string, mailboxUids: string[]): Promise<SendableMailbox[]> =>
221
+ apiFetch(`/mail/crm/workspaces/${enc(uid)}/sendable-mailboxes`, json("POST", { mailboxUids }));
212
222
  export const removeSender = (uid: string, senderUid: string): Promise<void> => apiFetch(`/mail/crm/workspaces/${enc(uid)}/senders/${enc(senderUid)}`, json("DELETE"));
213
223
 
214
224
  // Contacts and companies
@@ -5,6 +5,8 @@ import { Workspace } from "../crmApi.js";
5
5
  export type CrmPageProps = Omit<AppShellProps, "active">;
6
6
  /** The CRM's sections, each a page under `/crm`. */
7
7
  export type CrmSection = "contacts" | "companies" | "lists" | "deals" | "pipelines" | "segments" | "forms" | "templates" | "campaigns" | "automations" | "scoring" | "tasks" | "imports" | "reports" | "integrations" | "settings";
8
+ /** The workspace switcher's choice that opens the "New workspace" dialog rather than switching. */
9
+ export declare const NEW_WORKSPACE = "__new__";
8
10
  /** Where the selected workspace is remembered between visits (per browser). */
9
11
  export declare const WORKSPACE_STORAGE_KEY = "rapidmx.crm.workspace";
10
12
  export declare const INPUT_CLASS = "w-full text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
@@ -28,7 +30,8 @@ export declare function workspaceHref(path: string, workspaceUid: string): strin
28
30
  /**
29
31
  * The frame of every CRM page: the web client's app chrome (with CRM highlighted on the app rail), a sidebar with the workspace
30
32
  * switcher and the CRM's sections, and the page. It loads the caller's workspaces and picks one (`?w=`, else the last used, else the
31
- * first); a caller with none is asked to create one. Pages read the selection with `useCrm()`.
33
+ * first); a caller with none is asked to create one, and the switcher offers to create another. Pages read the selection with
34
+ * `useCrm()`.
32
35
  */
33
36
  export default function CrmShell({ section, children, ...props }: PropsWithChildren<CrmPageProps & {
34
37
  section: CrmSection;
@@ -37,3 +40,7 @@ export default function CrmShell({ section, children, ...props }: PropsWithChild
37
40
  export declare function CreateFirstWorkspace({ onCreated }: {
38
41
  onCreated: (workspace: Workspace) => void;
39
42
  }): React.JSX.Element;
43
+ /** Names and creates a workspace (in the browser's time zone - changeable in its settings), then hands it to `onCreated`. */
44
+ export declare function CreateWorkspaceForm({ onCreated }: {
45
+ onCreated: (workspace: Workspace) => void;
46
+ }): React.JSX.Element;
@@ -9,7 +9,10 @@ import AppShell from "@rapidmx/web-client/shared/components/layout/AppShell.js";
9
9
  import Alert from "@rapidmx/web-client/lib/components/feedback/Alert.js";
10
10
  import Button from "@rapidmx/web-client/lib/components/buttons/Button.js";
11
11
  import FormField from "@rapidmx/web-client/lib/components/forms/FormField.js";
12
+ import Modal from "@rapidmx/web-client/lib/components/overlays/Modal.js";
12
13
  import { createWorkspace, errorMessage, listWorkspaces } from "../crmApi.js";
14
+ /** The workspace switcher's choice that opens the "New workspace" dialog rather than switching. */
15
+ export const NEW_WORKSPACE = "__new__";
13
16
  /** Where the selected workspace is remembered between visits (per browser). */
14
17
  export const WORKSPACE_STORAGE_KEY = "rapidmx.crm.workspace";
15
18
  export const INPUT_CLASS = "w-full text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
@@ -68,12 +71,14 @@ function remember(workspaceUid) {
68
71
  /**
69
72
  * The frame of every CRM page: the web client's app chrome (with CRM highlighted on the app rail), a sidebar with the workspace
70
73
  * switcher and the CRM's sections, and the page. It loads the caller's workspaces and picks one (`?w=`, else the last used, else the
71
- * first); a caller with none is asked to create one. Pages read the selection with `useCrm()`.
74
+ * first); a caller with none is asked to create one, and the switcher offers to create another. Pages read the selection with
75
+ * `useCrm()`.
72
76
  */
73
77
  export default function CrmShell({ section, children, ...props }) {
74
78
  const [workspaces, setWorkspaces] = useState(null);
75
79
  const [selected, setSelected] = useState(null);
76
80
  const [error, setError] = useState(null);
81
+ const [creating, setCreating] = useState(false);
77
82
  async function load(preferred) {
78
83
  try {
79
84
  const list = await listWorkspaces();
@@ -105,10 +110,14 @@ export default function CrmShell({ section, children, ...props }) {
105
110
  reload: () => load(selected.uid),
106
111
  }
107
112
  : null;
108
- return (_jsx(AppShell, { ...props, active: "crm", children: _jsxs("div", { className: "flex flex-1 min-w-0 min-h-0", children: [context && (_jsxs("nav", { "aria-label": "CRM", className: "w-56 shrink-0 border-r border-border p-3 flex flex-col gap-1 overflow-y-auto", children: [_jsx("label", { htmlFor: "crm-workspace", className: "text-xs uppercase tracking-wide text-text-muted px-2", children: "Workspace" }), _jsx("select", { id: "crm-workspace", className: `${INPUT_CLASS} mb-3`, value: context.workspace.uid, onChange: (event) => switchTo(event.target.value), children: context.workspaces.map((workspace) => (_jsx("option", { value: workspace.uid, children: workspace.name }, workspace.uid))) }), SECTIONS.map(({ id, label, path, icon: Icon }) => (_jsxs("a", { href: context.href(path), "aria-current": id === section ? "page" : undefined, className: `flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm ${id === section ? "bg-primary-lightest text-primary-dark font-semibold" : "text-text hover:bg-surface-alt"}`, children: [_jsx(Icon, { "aria-hidden": true, className: "w-4 h-4" }), label] }, id)))] })), _jsxs("main", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: [error && _jsx(Alert, { children: error }), !error && workspaces === null && _jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" }), workspaces !== null && !context && _jsx(CreateFirstWorkspace, { onCreated: (workspace) => switchTo(workspace.uid) }), context && _jsx(CrmContext.Provider, { value: context, children: children })] })] }) }));
113
+ return (_jsx(AppShell, { ...props, active: "crm", children: _jsxs("div", { className: "flex flex-1 min-w-0 min-h-0", children: [context && (_jsxs("nav", { "aria-label": "CRM", className: "w-56 shrink-0 border-r border-border p-3 flex flex-col gap-1 overflow-y-auto", children: [_jsx("label", { htmlFor: "crm-workspace", className: "text-xs uppercase tracking-wide text-text-muted px-2", children: "Workspace" }), _jsxs("select", { id: "crm-workspace", className: `${INPUT_CLASS} mb-3`, value: context.workspace.uid, onChange: (event) => (event.target.value === NEW_WORKSPACE ? setCreating(true) : switchTo(event.target.value)), children: [context.workspaces.map((workspace) => (_jsx("option", { value: workspace.uid, children: workspace.name }, workspace.uid))), _jsx("option", { value: NEW_WORKSPACE, children: "+ New workspace\u2026" })] }), SECTIONS.map(({ id, label, path, icon: Icon }) => (_jsxs("a", { href: context.href(path), "aria-current": id === section ? "page" : undefined, className: `flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm ${id === section ? "bg-primary-lightest text-primary-dark font-semibold" : "text-text hover:bg-surface-alt"}`, children: [_jsx(Icon, { "aria-hidden": true, className: "w-4 h-4" }), label] }, id)))] })), _jsxs("main", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: [error && _jsx(Alert, { children: error }), !error && workspaces === null && _jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" }), workspaces !== null && !context && _jsx(CreateFirstWorkspace, { onCreated: (workspace) => switchTo(workspace.uid) }), _jsx(Modal, { open: creating, onClose: () => setCreating(false), title: "New workspace", children: _jsx(CreateWorkspaceForm, { onCreated: (workspace) => switchTo(workspace.uid) }) }), context && _jsx(CrmContext.Provider, { value: context, children: children })] })] }) }));
109
114
  }
110
115
  /** The form a caller with no workspace sees. */
111
116
  export function CreateFirstWorkspace({ onCreated }) {
117
+ return (_jsxs("div", { className: "max-w-md", children: [_jsx("h1", { className: "text-lg font-bold tracking-tight mb-2", children: "Create your CRM workspace" }), _jsx("p", { className: "text-sm text-text-muted mb-4", children: "A workspace holds your team's contacts, companies and tasks. You can invite colleagues to it once it exists." }), _jsx(CreateWorkspaceForm, { onCreated: onCreated })] }));
118
+ }
119
+ /** Names and creates a workspace (in the browser's time zone - changeable in its settings), then hands it to `onCreated`. */
120
+ export function CreateWorkspaceForm({ onCreated }) {
112
121
  const [name, setName] = useState("");
113
122
  const [error, setError] = useState(null);
114
123
  const [saving, setSaving] = useState(false);
@@ -123,5 +132,5 @@ export function CreateFirstWorkspace({ onCreated }) {
123
132
  setSaving(false);
124
133
  }
125
134
  }
126
- return (_jsxs("form", { onSubmit: submit, className: "max-w-md", children: [_jsx("h1", { className: "text-lg font-bold tracking-tight mb-2", children: "Create your CRM workspace" }), _jsx("p", { className: "text-sm text-text-muted mb-4", children: "A workspace holds your team's contacts, companies and tasks. You can invite colleagues to it once it exists." }), error && _jsx(Alert, { children: error }), _jsx(FormField, { label: "Name", htmlFor: "crm-new-workspace", children: _jsx("input", { id: "crm-new-workspace", className: INPUT_CLASS, value: name, onChange: (event) => setName(event.target.value), required: true }) }), _jsx(Button, { type: "submit", loading: saving, disabled: saving || name.trim().length === 0, className: "!w-auto mt-3", children: "Create workspace" })] }));
135
+ return (_jsxs("form", { onSubmit: submit, className: "min-w-[20rem]", children: [error && _jsx(Alert, { children: error }), _jsx(FormField, { label: "Name", htmlFor: "crm-new-workspace", children: _jsx("input", { id: "crm-new-workspace", className: INPUT_CLASS, value: name, onChange: (event) => setName(event.target.value), required: true }) }), _jsx(Button, { type: "submit", loading: saving, disabled: saving || name.trim().length === 0, className: "!w-auto mt-3", children: "Create workspace" })] }));
127
136
  }
@@ -1,6 +1,14 @@
1
1
  import React from "react";
2
+ import { RecipientSuggestion } from "@rapidmx/web-client/lib/mail/directoryApi.js";
2
3
  /**
3
4
  * A workspace's settings: its details, members and their roles, the addresses it sends as, and its custom properties - changeable by
4
5
  * its admins and owners (everyone else sees them read-only) - and, for owners, deleting it.
5
6
  */
6
7
  export default function WorkspaceSettings(): React.JSX.Element;
8
+ /** The address of an entry of the people field: `Name <address>` or a bare address. */
9
+ export declare function chipAddress(chip: string): string;
10
+ /** Suggests only people with an account on this server - the only ones who can be members. */
11
+ export declare function suggestPeople(query: string, options: {
12
+ limit?: number;
13
+ signal?: AbortSignal;
14
+ }): Promise<RecipientSuggestion[]>;
@@ -3,11 +3,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  // Copyright (C) 2026 Jean-Philippe Steinmetz
4
4
  // SPDX-License-Identifier: MPL-2.0
5
5
  ///////////////////////////////////////////////////////////////////////////////
6
- import { useEffect, useState } from "react";
6
+ import { useEffect, useMemo, useState } from "react";
7
7
  import Alert from "@rapidmx/web-client/lib/components/feedback/Alert.js";
8
8
  import Button from "@rapidmx/web-client/lib/components/buttons/Button.js";
9
9
  import FormField from "@rapidmx/web-client/lib/components/forms/FormField.js";
10
- import { addMember, addSender, createProperty, createSuppression, deleteSuppression, listSuppressions, deleteProperty, deleteWorkspace, errorMessage, listMembers, listProperties, listSenders, removeMember, removeSender, updateMember, updateWorkspace, updateSender, } from "../crmApi.js";
10
+ import TimeZonePicker from "@rapidmx/web-client/lib/components/pickers/TimeZonePicker.js";
11
+ import { timeZoneOptions } from "@rapidmx/web-client/lib/util/timeZone.js";
12
+ import { listMailboxes } from "@rapidmx/web-client/lib/mail/mailApi.js";
13
+ import { searchDirectory } from "@rapidmx/web-client/lib/mail/directoryApi.js";
14
+ import RecipientInput from "@rapidmx/web-client/shared/components/mail/compose/RecipientInput.js";
15
+ import { addMember, addSender, createProperty, createSuppression, deleteSuppression, listSuppressions, deleteProperty, deleteWorkspace, errorMessage, listMembers, listProperties, listSenders, removeMember, removeSender, sendableMailboxes, updateMember, updateWorkspace, updateSender, } from "../crmApi.js";
11
16
  import { INPUT_CLASS, WORKSPACE_STORAGE_KEY, useCrm } from "./CrmShell.js";
12
17
  const ROLES = [
13
18
  { value: "owner", label: "Owner" },
@@ -57,18 +62,29 @@ function Details() {
57
62
  const fields = [
58
63
  ["name", "Name"],
59
64
  ["description", "Description"],
60
- ["timezone", "Time zone"],
61
65
  ["postalAddress", "Postal address (shown in marketing email footers)"],
62
66
  ["website", "Website"],
63
67
  ];
64
- return (_jsxs("form", { onSubmit: save, className: "flex flex-col gap-3", "aria-label": "Workspace details", children: [_jsx("h2", { className: "text-sm font-semibold uppercase tracking-wide text-text-muted", children: "Workspace" }), error && _jsx(Alert, { children: error }), message && _jsx("p", { className: "text-sm text-success", children: message }), fields.map(([field, label]) => (_jsx(FormField, { label: label, htmlFor: `workspace-${field}`, children: _jsx("input", { id: `workspace-${field}`, className: INPUT_CLASS, value: values[field], disabled: !canManage, onChange: (event) => setValues((current) => ({ ...current, [field]: event.target.value })) }) }, field))), canManage && (_jsx(Button, { type: "submit", className: "!w-auto self-start", children: "Save" }))] }));
68
+ const zones = useMemo(() => timeZoneOptions(workspace.timezone), [workspace.timezone]);
69
+ return (_jsxs("form", { onSubmit: save, className: "flex flex-col gap-3", "aria-label": "Workspace details", children: [_jsx("h2", { className: "text-sm font-semibold uppercase tracking-wide text-text-muted", children: "Workspace" }), error && _jsx(Alert, { children: error }), message && _jsx("p", { className: "text-sm text-success", children: message }), fields.map(([field, label]) => (_jsx(FormField, { label: label, htmlFor: `workspace-${field}`, children: _jsx("input", { id: `workspace-${field}`, className: INPUT_CLASS, value: values[field], disabled: !canManage, onChange: (event) => setValues((current) => ({ ...current, [field]: event.target.value })) }) }, field))), _jsx(FormField, { label: "Time zone", htmlFor: "workspace-timezone", children: canManage ? (_jsx(TimeZonePicker, { id: "workspace-timezone", value: values.timezone, zones: zones, onChange: (timezone) => setValues((current) => ({ ...current, timezone })) })) : (_jsx("input", { id: "workspace-timezone", className: INPUT_CLASS, value: values.timezone, disabled: true })) }), canManage && (_jsx(Button, { type: "submit", className: "!w-auto self-start", children: "Save" }))] }));
70
+ }
71
+ /** The address of an entry of the people field: `Name <address>` or a bare address. */
72
+ export function chipAddress(chip) {
73
+ const match = /<([^<>]+)>\s*$/.exec(chip);
74
+ return (match ? match[1] : chip).trim().toLowerCase();
75
+ }
76
+ /** Suggests only people with an account on this server - the only ones who can be members. */
77
+ export async function suggestPeople(query, options) {
78
+ return (await searchDirectory(query, options)).filter((suggestion) => suggestion.kind === "user");
65
79
  }
66
80
  function Members() {
67
81
  const { workspace, canManage } = useCrm();
68
82
  const [members, setMembers] = useState([]);
69
- const [address, setAddress] = useState("");
83
+ const [text, setText] = useState("");
84
+ const [people, setPeople] = useState({ chips: [], pending: "" });
70
85
  const [role, setRole] = useState("editor");
71
86
  const [error, setError] = useState(null);
87
+ const [adding, setAdding] = useState(false);
72
88
  async function load() {
73
89
  setMembers(await listMembers(workspace.uid));
74
90
  }
@@ -85,18 +101,44 @@ function Members() {
85
101
  setError(errorMessage(err, failure));
86
102
  }
87
103
  }
88
- return (_jsxs("section", { "aria-label": "Members", className: "flex flex-col gap-3", children: [_jsx("h2", { className: "text-sm font-semibold uppercase tracking-wide text-text-muted", children: "Members" }), error && _jsx(Alert, { children: error }), _jsx("ul", { className: "flex flex-col divide-y divide-border border-y border-border", children: members.map((member) => (_jsxs("li", { className: "flex items-center gap-3 py-2 text-sm", children: [_jsx("span", { className: "flex-1", children: member.displayName || member.address || member.userUid }), canManage ? (_jsx("select", { "aria-label": `Role of ${member.displayName || member.address || member.userUid}`, className: `${INPUT_CLASS} !w-32`, value: member.role, onChange: (event) => void act(() => updateMember(workspace.uid, member.userUid, event.target.value), "Could not change the role."), children: ROLES.map((entry) => (_jsx("option", { value: entry.value, children: entry.label }, entry.value))) })) : (_jsx("span", { className: "text-text-muted", children: member.role })), canManage && (_jsx("button", { type: "button", className: "text-xs text-text-muted hover:text-danger", onClick: () => void act(() => removeMember(workspace.uid, member.userUid), "Could not remove the member."), children: "Remove" }))] }, member.userUid))) }), canManage && (_jsxs("form", { className: "flex flex-wrap gap-2", "aria-label": "Add member", onSubmit: (event) => {
89
- event.preventDefault();
90
- void act(async () => {
91
- await addMember(workspace.uid, { address, role });
92
- setAddress("");
93
- }, "Could not add the member.");
94
- }, children: [_jsx("input", { "aria-label": "Member address", placeholder: "colleague@example.com", className: `${INPUT_CLASS} !w-72`, value: address, onChange: (event) => setAddress(event.target.value) }), _jsx("select", { "aria-label": "New member role", className: `${INPUT_CLASS} !w-32`, value: role, onChange: (event) => setRole(event.target.value), children: ROLES.map((entry) => (_jsx("option", { value: entry.value, children: entry.label }, entry.value))) }), _jsx(Button, { type: "submit", variant: "secondary", disabled: address.trim() === "", className: "!w-auto", children: "Add member" })] }))] }));
104
+ /** Adds everyone in the field; those that fail stay in it, with why. */
105
+ async function add(event) {
106
+ event.preventDefault();
107
+ const entries = [...people.chips, ...(people.pending.trim() ? [people.pending.trim()] : [])];
108
+ setAdding(true);
109
+ const failed = [];
110
+ const reasons = [];
111
+ for (const entry of entries) {
112
+ try {
113
+ await addMember(workspace.uid, { address: chipAddress(entry), role });
114
+ }
115
+ catch (err) {
116
+ failed.push(entry);
117
+ reasons.push(`${chipAddress(entry)}: ${errorMessage(err, "could not be added.")}`);
118
+ }
119
+ }
120
+ setText(failed.join(", "));
121
+ setPeople({ chips: failed, pending: "" });
122
+ setAdding(false);
123
+ try {
124
+ await load();
125
+ setError(reasons.length > 0 ? reasons.join(" ") : null);
126
+ }
127
+ catch (err) {
128
+ setError([...reasons, errorMessage(err, "Could not load the members.")].join(" "));
129
+ }
130
+ }
131
+ const count = people.chips.length + (people.pending.trim() ? 1 : 0);
132
+ return (_jsxs("section", { "aria-label": "Members", className: "flex flex-col gap-3", children: [_jsx("h2", { className: "text-sm font-semibold uppercase tracking-wide text-text-muted", children: "Members" }), error && _jsx(Alert, { children: error }), _jsx("ul", { className: "flex flex-col divide-y divide-border border-y border-border", children: members.map((member) => {
133
+ const name = member.displayName || member.address || member.userUid;
134
+ return (_jsxs("li", { className: "flex items-center gap-3 py-2 text-sm", children: [_jsxs("span", { className: "flex-1 min-w-0", children: [_jsx("span", { className: "block truncate", children: name }), member.displayName && member.address && _jsx("span", { className: "block truncate text-xs text-text-muted", children: member.address })] }), canManage ? (_jsx("select", { "aria-label": `Role of ${name}`, className: `${INPUT_CLASS} !w-32`, value: member.role, onChange: (event) => void act(() => updateMember(workspace.uid, member.userUid, event.target.value), "Could not change the role."), children: ROLES.map((entry) => (_jsx("option", { value: entry.value, children: entry.label }, entry.value))) })) : (_jsx("span", { className: "text-text-muted", children: member.role })), canManage && (_jsx("button", { type: "button", className: "text-xs text-text-muted hover:text-danger", onClick: () => void act(() => removeMember(workspace.uid, member.userUid), "Could not remove the member."), children: "Remove" }))] }, member.userUid));
135
+ }) }), canManage && (_jsxs("form", { className: "flex flex-wrap items-start gap-2", "aria-label": "Add members", onSubmit: (event) => void add(event), children: [_jsx("div", { className: "flex-1 min-w-[18rem]", children: _jsx(RecipientInput, { id: "crm-new-members", label: "People", ariaLabel: "People to add", placeholder: "Search people by name or address", value: text, onChange: setText, onEdit: (chips, pending) => setPeople({ chips, pending }), fetchSuggestions: suggestPeople, className: INPUT_CLASS }) }), _jsx("select", { "aria-label": "New member role", className: `${INPUT_CLASS} !w-32`, value: role, onChange: (event) => setRole(event.target.value), children: ROLES.map((entry) => (_jsx("option", { value: entry.value, children: entry.label }, entry.value))) }), _jsx(Button, { type: "submit", variant: "secondary", loading: adding, disabled: adding || count === 0, className: "!w-auto", children: count > 1 ? `Add ${count} members` : "Add member" })] }))] }));
95
136
  }
96
137
  function Senders() {
97
138
  const { workspace, canManage } = useCrm();
98
139
  const [senders, setSenders] = useState([]);
99
- const [fromAddress, setFromAddress] = useState("");
140
+ const [mailboxes, setMailboxes] = useState(null);
141
+ const [mailboxUid, setMailboxUid] = useState("");
100
142
  const [fromName, setFromName] = useState("");
101
143
  const [error, setError] = useState(null);
102
144
  async function load() {
@@ -105,11 +147,22 @@ function Senders() {
105
147
  useEffect(() => {
106
148
  load().catch((err) => setError(errorMessage(err, "Could not load the senders.")));
107
149
  }, [workspace.uid]);
150
+ useEffect(() => {
151
+ if (!canManage) {
152
+ return;
153
+ }
154
+ (async () => {
155
+ const own = await listMailboxes({ limit: 200 });
156
+ setMailboxes(own.length > 0 ? await sendableMailboxes(workspace.uid, own.map((mailbox) => mailbox.uid)) : []);
157
+ })().catch(() => setMailboxes([]));
158
+ }, [workspace.uid, canManage]);
159
+ const choices = (mailboxes ?? []).filter((mailbox) => !senders.some((sender) => sender.fromAddress === mailbox.address));
160
+ const chosen = choices.find((mailbox) => mailbox.uid === mailboxUid);
108
161
  async function add(event) {
109
162
  event.preventDefault();
110
163
  try {
111
- await addSender(workspace.uid, { fromAddress, ...(fromName.trim() ? { fromName } : {}) });
112
- setFromAddress("");
164
+ await addSender(workspace.uid, { fromAddress: chosen.address, ...(fromName.trim() ? { fromName: fromName.trim() } : {}) });
165
+ setMailboxUid("");
113
166
  setFromName("");
114
167
  setError(null);
115
168
  await load();
@@ -118,7 +171,7 @@ function Senders() {
118
171
  setError(errorMessage(err, "Could not add the sender."));
119
172
  }
120
173
  }
121
- return (_jsxs("section", { "aria-label": "Senders", className: "flex flex-col gap-3", children: [_jsx("h2", { className: "text-sm font-semibold uppercase tracking-wide text-text-muted", children: "Send as" }), _jsx("p", { className: "text-sm text-text-muted", children: "Addresses of mailboxes you can send from. Replies to mail sent from the CRM arrive in these mailboxes." }), error && _jsx(Alert, { children: error }), _jsx("ul", { className: "flex flex-col gap-1", children: senders.map((sender) => (_jsxs("li", { className: "flex items-center gap-3 text-sm", children: [_jsxs("span", { className: "flex-1", children: [sender.fromName, " <", sender.fromAddress, ">"] }), _jsxs("label", { className: "flex items-center gap-1 text-xs text-text-muted", title: "Mail this mailbox exchanges with contacts goes on their timelines", children: [_jsx("input", { type: "checkbox", disabled: !canManage, checked: !!sender.logEmail, onChange: async (event) => {
174
+ return (_jsxs("section", { "aria-label": "Senders", className: "flex flex-col gap-3", children: [_jsx("h2", { className: "text-sm font-semibold uppercase tracking-wide text-text-muted", children: "Send as" }), _jsx("p", { className: "text-sm text-text-muted", children: "Mailboxes the CRM sends from. Replies to mail sent from the CRM arrive in these mailboxes." }), error && _jsx(Alert, { children: error }), _jsx("ul", { className: "flex flex-col gap-1", children: senders.map((sender) => (_jsxs("li", { className: "flex items-center gap-3 text-sm", children: [_jsxs("span", { className: "flex-1", children: [sender.fromName, " <", sender.fromAddress, ">"] }), _jsxs("label", { className: "flex items-center gap-1 text-xs text-text-muted", title: "Mail this mailbox exchanges with contacts goes on their timelines", children: [_jsx("input", { type: "checkbox", disabled: !canManage, checked: !!sender.logEmail, onChange: async (event) => {
122
175
  try {
123
176
  await updateSender(workspace.uid, sender.uid, { logEmail: event.target.checked });
124
177
  }
@@ -129,7 +182,10 @@ function Senders() {
129
182
  } }), "Log email with contacts"] }), canManage && (_jsx("button", { type: "button", className: "text-xs text-text-muted hover:text-danger", onClick: async () => {
130
183
  await removeSender(workspace.uid, sender.uid);
131
184
  await load();
132
- }, children: "Remove" }))] }, sender.uid))) }), canManage && (_jsxs("form", { className: "flex flex-wrap gap-2", "aria-label": "Add sender", onSubmit: add, children: [_jsx("input", { "aria-label": "Sender address", placeholder: "sales@example.com", className: `${INPUT_CLASS} !w-64`, value: fromAddress, onChange: (event) => setFromAddress(event.target.value) }), _jsx("input", { "aria-label": "Sender name", placeholder: "Name shown to recipients", className: `${INPUT_CLASS} !w-56`, value: fromName, onChange: (event) => setFromName(event.target.value) }), _jsx(Button, { type: "submit", variant: "secondary", disabled: fromAddress.trim() === "", className: "!w-auto", children: "Add sender" })] }))] }));
185
+ }, children: "Remove" }))] }, sender.uid))) }), canManage && mailboxes !== null && mailboxes.length === 0 && (_jsx("p", { className: "text-sm text-text-muted", children: "You have no mailbox you can send from. Its owner can add it, or give you send access to it." })), canManage && choices.length > 0 && (_jsxs("form", { className: "flex flex-wrap gap-2", "aria-label": "Add sender", onSubmit: (event) => void add(event), children: [_jsxs("select", { "aria-label": "Mailbox", className: `${INPUT_CLASS} !w-72`, value: mailboxUid, onChange: (event) => {
186
+ setMailboxUid(event.target.value);
187
+ setFromName(choices.find((mailbox) => mailbox.uid === event.target.value)?.displayName ?? "");
188
+ }, children: [_jsx("option", { value: "", children: "Choose a mailbox\u2026" }), choices.map((mailbox) => (_jsx("option", { value: mailbox.uid, children: mailbox.displayName ? `${mailbox.displayName} <${mailbox.address}>` : mailbox.address }, mailbox.uid)))] }), _jsx("input", { "aria-label": "Sender name", placeholder: "Name shown to recipients", className: `${INPUT_CLASS} !w-56`, value: fromName, onChange: (event) => setFromName(event.target.value) }), _jsx(Button, { type: "submit", variant: "secondary", disabled: !chosen, className: "!w-auto", children: "Add sender" })] }))] }));
133
189
  }
134
190
  function Properties() {
135
191
  const { workspace, canManage } = useCrm();
@@ -188,6 +188,14 @@ export declare const addSender: (uid: string, input: {
188
188
  replyToAddress?: string;
189
189
  logEmail?: boolean;
190
190
  }) => Promise<WorkspaceSender>;
191
+ /** A mailbox the caller may add as a sender. */
192
+ export interface SendableMailbox {
193
+ uid: string;
194
+ address: string;
195
+ displayName: string;
196
+ }
197
+ /** Which of `mailboxUids` (the caller's mailboxes) the caller can send from, for the "Send as" picker. */
198
+ export declare const sendableMailboxes: (uid: string, mailboxUids: string[]) => Promise<SendableMailbox[]>;
191
199
  export declare const removeSender: (uid: string, senderUid: string) => Promise<void>;
192
200
  export declare function searchRecords<T extends CrmRecord>(objectType: CrmObjectType, workspaceUid: string, request: SearchRequest): Promise<SearchResult<T>>;
193
201
  export declare function getRecord<T extends CrmRecord>(objectType: CrmObjectType, workspaceUid: string, uid: string): Promise<T>;
@@ -26,6 +26,8 @@ export const removeMember = (uid, userUid) => apiFetch(`/mail/crm/workspaces/${e
26
26
  export const listSenders = (uid) => apiFetch(`/mail/crm/workspaces/${enc(uid)}/senders`);
27
27
  export const updateSender = (uid, senderUid, input) => apiFetch(`/mail/crm/workspaces/${enc(uid)}/senders/${enc(senderUid)}`, json("PUT", input));
28
28
  export const addSender = (uid, input) => apiFetch(`/mail/crm/workspaces/${enc(uid)}/senders`, json("POST", input));
29
+ /** Which of `mailboxUids` (the caller's mailboxes) the caller can send from, for the "Send as" picker. */
30
+ export const sendableMailboxes = (uid, mailboxUids) => apiFetch(`/mail/crm/workspaces/${enc(uid)}/sendable-mailboxes`, json("POST", { mailboxUids }));
29
31
  export const removeSender = (uid, senderUid) => apiFetch(`/mail/crm/workspaces/${enc(uid)}/senders/${enc(senderUid)}`, json("DELETE"));
30
32
  // Contacts and companies
31
33
  export function searchRecords(objectType, workspaceUid, request) {
@@ -63,6 +63,8 @@ const WORKSPACE_DATA = [
63
63
  "workspaceSender",
64
64
  "workspaceMember",
65
65
  ];
66
+ /** How many mailboxes one "Send as" check may name. */
67
+ export const MAX_SENDABLE_CHECK = 200;
66
68
  /**
67
69
  * CRM workspaces (`/api/mail/crm/workspaces`): creating one (the creator becomes its owner), listing the caller's, changing and deleting
68
70
  * them, and managing who is a member with which role, and which addresses the workspace sends as.
@@ -115,7 +117,7 @@ export class BaseWorkspaceRoute extends CrmRouteBase {
115
117
  acl: { uid: instance.uid, records: [memberRecord(userUid, WorkspaceRole.OWNER)] },
116
118
  });
117
119
  const memberClass = this.classes.workspaceMember;
118
- await (await this.repo("workspaceMember")).create(new memberClass({ workspaceUid: workspace.uid, userUid, role: WorkspaceRole.OWNER }), {
120
+ await (await this.repo("workspaceMember")).create(new memberClass({ workspaceUid: workspace.uid, userUid, role: WorkspaceRole.OWNER, ...(await this.describeUser(userUid)) }), {
119
121
  ignoreACL: true,
120
122
  });
121
123
  return { ...this.plain(workspace), role: WorkspaceRole.OWNER };
@@ -148,7 +150,12 @@ export class BaseWorkspaceRoute extends CrmRouteBase {
148
150
  }
149
151
  async listMembers(workspaceUid, user) {
150
152
  await this.requireAccess(user, workspaceUid, WorkspaceAction.READ);
151
- return (await this.members(workspaceUid)).map((member) => this.plain(member));
153
+ const views = [];
154
+ for (const member of await this.members(workspaceUid)) {
155
+ const view = this.plain(member);
156
+ views.push(view.address ? view : { ...view, ...(await this.describeUser(member.userUid)) });
157
+ }
158
+ return views;
152
159
  }
153
160
  /**
154
161
  * Adds a member by `userUid`, or by `address` - resolved to the owner of the mailbox with that address, so a colleague can be
@@ -248,6 +255,26 @@ export class BaseWorkspaceRoute extends CrmRouteBase {
248
255
  this.notify(workspaceUid, "WorkspaceSender", "create", this.plain(sender));
249
256
  return this.plain(sender);
250
257
  }
258
+ /**
259
+ * Which of `mailboxUids` (the caller's mailboxes, as the web client lists them) the caller may add as senders - those they can
260
+ * send from (update access) - with each one's address and name, for the "Send as" picker. Up to `MAX_SENDABLE_CHECK` at once.
261
+ */
262
+ async sendableMailboxes(workspaceUid, body, user) {
263
+ await this.requireAccess(user, workspaceUid, WorkspaceAction.MANAGE);
264
+ const uids = requireObject(body).mailboxUids;
265
+ if (!Array.isArray(uids) || uids.length > MAX_SENDABLE_CHECK || uids.some((uid) => typeof uid !== "string" || uid.length === 0 || uid.length > 128)) {
266
+ throw badRequest(`'mailboxUids' must be a list of up to ${MAX_SENDABLE_CHECK} mailbox uids.`);
267
+ }
268
+ const repo = await this.repo("mailbox");
269
+ const sendable = [];
270
+ for (const uid of new Set(uids)) {
271
+ const mailbox = await repo.findOne(uid, { ignoreACL: true, skipCache: true });
272
+ if (mailbox && (await hasMailAccess(this.aclUtils, this.trustedRoles, user, mailbox.uid, ACLAction.UPDATE))) {
273
+ sendable.push({ uid: mailbox.uid, address: mailbox.primarySmtpAddress, displayName: mailbox.displayName ?? "" });
274
+ }
275
+ }
276
+ return sendable;
277
+ }
251
278
  async updateSender(workspaceUid, senderUid, body, user) {
252
279
  await this.requireAccess(user, workspaceUid, WorkspaceAction.MANAGE);
253
280
  const sender = await this.requireWorkspaceSender(workspaceUid, senderUid);
@@ -320,7 +347,8 @@ export class BaseWorkspaceRoute extends CrmRouteBase {
320
347
  /** Who a member request names: `userUid` as given, or `address` as the owner of the mailbox with that address. */
321
348
  async resolvePerson(body) {
322
349
  if (typeof body.userUid === "string" && body.userUid.trim().length > 0 && body.userUid.length <= 128) {
323
- return { userUid: body.userUid.trim().toLowerCase() };
350
+ const userUid = body.userUid.trim().toLowerCase();
351
+ return { userUid, ...(await this.describeUser(userUid)) };
324
352
  }
325
353
  const address = readEmail(body, "address");
326
354
  if (!address) {
@@ -340,6 +368,14 @@ export class BaseWorkspaceRoute extends CrmRouteBase {
340
368
  const matches = await (await this.repo("mailbox")).find({ primarySmtpAddress: ModelUtils.literal(address) }, { ignoreACL: true, limit: 1, skipCache: true });
341
369
  return matches[0];
342
370
  }
371
+ /**
372
+ * A user's name and address, as their own mailbox (the first they own) has them - users are known to the mail server only by
373
+ * uid. Nothing for a user without a mailbox.
374
+ */
375
+ async describeUser(userUid) {
376
+ const owned = (await (await this.repo("mailbox")).find({ ownerUserUid: ModelUtils.literal(userUid), sort: { dateCreated: "ASC" } }, { ignoreACL: true, limit: 1, skipCache: true }))[0];
377
+ return owned ? { address: owned.primarySmtpAddress, ...(owned.displayName ? { displayName: owned.displayName } : {}) } : {};
378
+ }
343
379
  /** The mailbox `address` belongs to, when the caller may send from it (update access to the mailbox), else a 400. */
344
380
  async requireSendableMailbox(user, address) {
345
381
  const mailbox = await this.findMailboxByAddress(address);
@@ -509,6 +545,14 @@ __decorate([
509
545
  __metadata("design:paramtypes", [String, Object, Object]),
510
546
  __metadata("design:returntype", Promise)
511
547
  ], BaseWorkspaceRoute.prototype, "addSender", null);
548
+ __decorate([
549
+ Post("/:workspaceUid/sendable-mailboxes"),
550
+ __param(0, Param("workspaceUid")),
551
+ __param(2, AuthUser),
552
+ __metadata("design:type", Function),
553
+ __metadata("design:paramtypes", [String, Object, Object]),
554
+ __metadata("design:returntype", Promise)
555
+ ], BaseWorkspaceRoute.prototype, "sendableMailboxes", null);
512
556
  __decorate([
513
557
  Put("/:workspaceUid/senders/:senderUid"),
514
558
  __param(0, Param("workspaceUid")),
@@ -1 +1 @@
1
- {"version":3,"file":"BaseWorkspaceRoute.js","sourceRoot":"","sources":["../../../src/routes/BaseWorkspaceRoute.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BaseWorkspaceRoute.js","sourceRoot":"","sources":["../../../src/routes/BaseWorkspaceRoute.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,14 @@ import { CrmRouteBase } from "./CrmRouteBase.js";
4
4
  /** How many members and senders one workspace may have, by default (`mail:crm:max_members`, `mail:crm:max_senders`). */
5
5
  export declare const MAX_MEMBERS = 500;
6
6
  export declare const MAX_SENDERS = 50;
7
+ /** How many mailboxes one "Send as" check may name. */
8
+ export declare const MAX_SENDABLE_CHECK = 200;
9
+ /** A mailbox the caller may add as a sender. */
10
+ export interface SendableMailbox {
11
+ uid: string;
12
+ address: string;
13
+ displayName: string;
14
+ }
7
15
  /** A workspace as its members see it: the record plus the caller's own role. */
8
16
  export interface WorkspaceView extends Workspace {
9
17
  role: WorkspaceRole;
@@ -45,6 +53,11 @@ export declare abstract class BaseWorkspaceRoute extends CrmRouteBase {
45
53
  * mailbox's primary address or one of its aliases.
46
54
  */
47
55
  addSender(workspaceUid: string, body: unknown, user?: JWTUser): Promise<WorkspaceSender>;
56
+ /**
57
+ * Which of `mailboxUids` (the caller's mailboxes, as the web client lists them) the caller may add as senders - those they can
58
+ * send from (update access) - with each one's address and name, for the "Send as" picker. Up to `MAX_SENDABLE_CHECK` at once.
59
+ */
60
+ sendableMailboxes(workspaceUid: string, body: unknown, user?: JWTUser): Promise<SendableMailbox[]>;
48
61
  updateSender(workspaceUid: string, senderUid: string, body: unknown, user?: JWTUser): Promise<WorkspaceSender>;
49
62
  removeSender(workspaceUid: string, senderUid: string, user?: JWTUser): Promise<void>;
50
63
  /** The caller's uid, lowercased, or a 401 for an anonymous caller. */
@@ -61,6 +74,11 @@ export declare abstract class BaseWorkspaceRoute extends CrmRouteBase {
61
74
  * mailbox's own address, so an alias never silently picks some other mailbox.
62
75
  */
63
76
  private findMailboxByAddress;
77
+ /**
78
+ * A user's name and address, as their own mailbox (the first they own) has them - users are known to the mail server only by
79
+ * uid. Nothing for a user without a mailbox.
80
+ */
81
+ private describeUser;
64
82
  /** The mailbox `address` belongs to, when the caller may send from it (update access to the mailbox), else a 400. */
65
83
  private requireSendableMailbox;
66
84
  private requireWorkspace;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rapidmx/crm-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "RapidMX plugin that provides a customer relationship management and marketing platform: contacts, companies, mailing lists, campaigns, automations and sales pipelines.",
5
5
  "repository": "https://github.com/rapidmx/crm-plugin.git",
6
6
  "author": "RapidREST <rapidrests@gmail.com>",