@carlonicora/nextjs-jsonapi 6.14.1 → 6.16.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/dist/{BlockNoteEditor-UIAX72XW.js → BlockNoteEditor-GWDYMS6R.js} +20 -20
- package/dist/{BlockNoteEditor-UIAX72XW.js.map → BlockNoteEditor-GWDYMS6R.js.map} +1 -1
- package/dist/{BlockNoteEditor-I3J6YYXK.mjs → BlockNoteEditor-HNP5UAJT.mjs} +5 -5
- package/dist/{BlockNoteViewer-HIUAJQRI.mjs → BlockNoteViewer-X4UEVI2A.mjs} +3 -3
- package/dist/{BlockNoteViewer-APBOPT4B.js → BlockNoteViewer-XYKFY77F.js} +4 -4
- package/dist/{BlockNoteViewer-APBOPT4B.js.map → BlockNoteViewer-XYKFY77F.js.map} +1 -1
- package/dist/EmailPurpose-DOsSqM5K.d.mts +3 -0
- package/dist/EmailPurpose-DOsSqM5K.d.ts +3 -0
- package/dist/billing/index.js +492 -492
- package/dist/billing/index.mjs +4 -4
- package/dist/{chunk-4XLDBOSE.js → chunk-A7Y5YW7Q.js} +1 -1
- package/dist/{chunk-4XLDBOSE.js.map → chunk-A7Y5YW7Q.js.map} +1 -1
- package/dist/{chunk-ZYSNJKOP.js → chunk-DFA566KY.js} +4 -4
- package/dist/{chunk-ZYSNJKOP.js.map → chunk-DFA566KY.js.map} +1 -1
- package/dist/{chunk-DJXSCARG.js → chunk-I5BQLY7V.js} +9 -9
- package/dist/{chunk-DJXSCARG.js.map → chunk-I5BQLY7V.js.map} +1 -1
- package/dist/{chunk-RFKSJIQB.mjs → chunk-KMPBSOFB.mjs} +138 -5
- package/dist/chunk-KMPBSOFB.mjs.map +1 -0
- package/dist/{chunk-UOTHRJD2.mjs → chunk-OKBJEIOP.mjs} +1 -1
- package/dist/chunk-OKBJEIOP.mjs.map +1 -0
- package/dist/{chunk-PMESKNKL.js → chunk-OO2DBP6L.js} +1106 -973
- package/dist/chunk-OO2DBP6L.js.map +1 -0
- package/dist/{chunk-HAW5PNJG.mjs → chunk-TY5YWBLL.mjs} +3 -3
- package/dist/{chunk-RT7XFGIN.js → chunk-UGDYZW4K.js} +1035 -997
- package/dist/chunk-UGDYZW4K.js.map +1 -0
- package/dist/{chunk-RYY7USSW.mjs → chunk-YEZGASBX.mjs} +50 -12
- package/dist/{chunk-RYY7USSW.mjs.map → chunk-YEZGASBX.mjs.map} +1 -1
- package/dist/{chunk-OUEYZOLB.mjs → chunk-YVKWACDP.mjs} +2 -2
- package/dist/client/index.js +5 -5
- package/dist/client/index.mjs +4 -4
- package/dist/components/editors/server.d.mts +7 -1
- package/dist/components/editors/server.d.ts +7 -1
- package/dist/components/editors/server.js +17 -4
- package/dist/components/editors/server.js.map +1 -1
- package/dist/components/editors/server.mjs +17 -4
- package/dist/components/editors/server.mjs.map +1 -1
- package/dist/components/index.d.mts +3 -2
- package/dist/components/index.d.ts +3 -2
- package/dist/components/index.js +5 -5
- package/dist/components/index.mjs +4 -4
- package/dist/contexts/index.js +5 -5
- package/dist/contexts/index.mjs +4 -4
- package/dist/core/index.d.mts +92 -7
- package/dist/core/index.d.ts +92 -7
- package/dist/core/index.js +7 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +6 -2
- package/dist/{email-automation-enrollment.fields-BZvF14qx.d.mts → email-automation-enrollment.fields-CPsgxLif.d.mts} +11 -1
- package/dist/{email-automation-enrollment.fields-Cp3VMzFq.d.ts → email-automation-enrollment.fields-CVicSamF.d.ts} +11 -1
- package/dist/email-automations/index.d.mts +19 -5
- package/dist/email-automations/index.d.ts +19 -5
- package/dist/email-automations/index.js +356 -129
- package/dist/email-automations/index.js.map +1 -1
- package/dist/email-automations/index.mjs +306 -79
- package/dist/email-automations/index.mjs.map +1 -1
- package/dist/features/analytics/index.js +121 -121
- package/dist/features/analytics/index.mjs +4 -4
- package/dist/features/analytics/server.js +5 -5
- package/dist/features/analytics/server.mjs +3 -3
- package/dist/features/blog/index.js +151 -151
- package/dist/features/blog/index.mjs +4 -4
- package/dist/features/blog/server.js +17 -17
- package/dist/features/blog/server.mjs +2 -2
- package/dist/features/help/index.js +38 -38
- package/dist/features/help/index.mjs +4 -4
- package/dist/features/tokenusage/index.js +127 -127
- package/dist/features/tokenusage/index.mjs +4 -4
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -3
- package/dist/offline/index.js +14 -14
- package/dist/offline/index.mjs +1 -1
- package/dist/{referral-admin.interface-RVO-ZTJz.d.mts → referral-admin.interface-DTj4LzXx.d.mts} +3 -0
- package/dist/{referral-admin.interface-CnytdDaQ.d.ts → referral-admin.interface-DeSCw3Pp.d.ts} +3 -0
- package/dist/server/index.js +4 -4
- package/dist/server/index.mjs +3 -3
- package/messages/en.json +35 -3
- package/messages/it.json +35 -3
- package/package.json +1 -1
- package/src/components/editors/server/__tests__/renderBlockNoteMarkdown.spec.ts +45 -0
- package/src/components/editors/server/renderBlockNoteHTML.ts +2 -18
- package/src/components/editors/server/renderBlockNoteMarkdown.ts +10 -0
- package/src/components/editors/server/serverEditor.ts +25 -0
- package/src/components/editors/server-entry.ts +1 -0
- package/src/core/index.ts +1 -0
- package/src/core/registry/ModuleRegistry.ts +1 -0
- package/src/features/email-automation/EmailAutomationCandidateModule.ts +12 -0
- package/src/features/email-automation/EmailAutomationModule.ts +1 -0
- package/src/features/email-automation/components/__tests__/EmailAutomationEditor.spec.tsx +74 -9
- package/src/features/email-automation/components/forms/EmailAutomationAddPersonDialog.tsx +226 -0
- package/src/features/email-automation/components/forms/EmailAutomationEditor.tsx +23 -0
- package/src/features/email-automation/components/forms/EmailStepEditor.tsx +7 -3
- package/src/features/email-automation/components/index.ts +1 -0
- package/src/features/email-automation/components/lists/EmailAutomationRecipientsList.tsx +38 -20
- package/src/features/email-automation/components/lists/EmailAutomationsList.tsx +1 -0
- package/src/features/email-automation/components/unsubscribe/EmailUnsubscribe.tsx +9 -4
- package/src/features/email-automation/data/EmailAutomation.ts +9 -0
- package/src/features/email-automation/data/EmailAutomationCandidate.ts +66 -0
- package/src/features/email-automation/data/EmailAutomationCandidateInterface.ts +19 -0
- package/src/features/email-automation/data/EmailAutomationEnrollment.ts +16 -2
- package/src/features/email-automation/data/EmailAutomationEnrollmentInterface.ts +7 -0
- package/src/features/email-automation/data/EmailAutomationInterface.ts +3 -0
- package/src/features/email-automation/data/EmailAutomationService.ts +53 -1
- package/src/features/email-automation/data/EmailPurpose.ts +1 -0
- package/src/features/email-automation/data/__tests__/email-automation-models.spec.ts +70 -0
- package/src/features/email-automation/data/email-automation.fields.ts +1 -0
- package/src/features/email-automation/data/index.ts +2 -0
- package/src/features/email-automation/hooks/useEmailAutomationTableStructure.tsx +19 -0
- package/src/features/email-automation/index.ts +2 -0
- package/src/features/email-blast/EmailBlastModule.ts +1 -0
- package/src/features/email-blast/components/forms/EmailBlastEditor.tsx +33 -4
- package/src/features/email-blast/components/lists/EmailBlastsList.tsx +1 -0
- package/src/features/email-blast/data/EmailBlast.ts +10 -1
- package/src/features/email-blast/data/EmailBlastInterface.ts +3 -0
- package/src/features/email-blast/data/__tests__/EmailBlast.spec.ts +18 -0
- package/src/features/email-blast/data/email-blast.fields.ts +1 -0
- package/src/features/email-blast/hooks/useEmailBlastTableStructure.tsx +15 -0
- package/dist/chunk-PMESKNKL.js.map +0 -1
- package/dist/chunk-RFKSJIQB.mjs.map +0 -1
- package/dist/chunk-RT7XFGIN.js.map +0 -1
- package/dist/chunk-UOTHRJD2.mjs.map +0 -1
- /package/dist/{BlockNoteEditor-I3J6YYXK.mjs.map → BlockNoteEditor-HNP5UAJT.mjs.map} +0 -0
- /package/dist/{BlockNoteViewer-HIUAJQRI.mjs.map → BlockNoteViewer-X4UEVI2A.mjs.map} +0 -0
- /package/dist/{chunk-HAW5PNJG.mjs.map → chunk-TY5YWBLL.mjs.map} +0 -0
- /package/dist/{chunk-OUEYZOLB.mjs.map → chunk-YVKWACDP.mjs.map} +0 -0
|
@@ -6,14 +6,17 @@ import { ReactNode, useCallback, useMemo } from "react";
|
|
|
6
6
|
import { useForm } from "react-hook-form";
|
|
7
7
|
import { v4 } from "uuid";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
-
import { EditorSheet, FormInput, FormTextarea } from "../../../../components";
|
|
9
|
+
import { EditorSheet, FormInput, FormSelect, FormTextarea } from "../../../../components";
|
|
10
10
|
import { Modules } from "../../../../core";
|
|
11
11
|
import { useI18nRouter } from "../../../../i18n";
|
|
12
|
+
import { EmailPurpose } from "../../../email-automation/data/EmailPurpose";
|
|
12
13
|
import { EmailBlastInput, EmailBlastInterface } from "../../data/EmailBlastInterface";
|
|
13
14
|
import { EmailBlastService } from "../../data/EmailBlastService";
|
|
14
15
|
|
|
15
16
|
/** The fixed variable set the blast renderer fills. */
|
|
16
|
-
const TEMPLATE_VARIABLES = ["{{name}}", "{{email}}", "{{url}}"
|
|
17
|
+
const TEMPLATE_VARIABLES = ["{{name}}", "{{email}}", "{{url}}"];
|
|
18
|
+
/** Only a Marketing blast fills it: a Service blast carries no unsubscribe link. */
|
|
19
|
+
const MARKETING_ONLY_VARIABLES = ["{{unsubscribeAllUrl}}"];
|
|
17
20
|
|
|
18
21
|
export type EmailBlastEditorProps = {
|
|
19
22
|
blast?: EmailBlastInterface;
|
|
@@ -49,6 +52,7 @@ function EmailBlastEditorInternal({
|
|
|
49
52
|
replyTo: z.string().refine((value) => value === "" || z.email().safeParse(value).success, {
|
|
50
53
|
message: t("emailBlasts.editor.fields.replyTo.error"),
|
|
51
54
|
}),
|
|
55
|
+
purpose: z.enum(["service", "marketing"], { message: t("emailPurpose.error") }),
|
|
52
56
|
}),
|
|
53
57
|
[t],
|
|
54
58
|
);
|
|
@@ -59,6 +63,8 @@ function EmailBlastEditorInternal({
|
|
|
59
63
|
subject: blast?.subject ?? "",
|
|
60
64
|
body: blast?.body ?? "",
|
|
61
65
|
replyTo: blast?.replyTo ?? "",
|
|
66
|
+
// No preselected value on create: the form refuses to save without a choice.
|
|
67
|
+
purpose: (blast?.purpose ?? "") as EmailPurpose,
|
|
62
68
|
}),
|
|
63
69
|
[blast],
|
|
64
70
|
);
|
|
@@ -68,6 +74,17 @@ function EmailBlastEditorInternal({
|
|
|
68
74
|
defaultValues: getDefaultValues(),
|
|
69
75
|
});
|
|
70
76
|
|
|
77
|
+
const purpose = form.watch("purpose");
|
|
78
|
+
const templateVariables =
|
|
79
|
+
purpose === "service" ? TEMPLATE_VARIABLES : [...TEMPLATE_VARIABLES, ...MARKETING_ONLY_VARIABLES];
|
|
80
|
+
const purposeValues = useMemo(
|
|
81
|
+
() => [
|
|
82
|
+
{ id: "service", text: t("emailPurpose.service") },
|
|
83
|
+
{ id: "marketing", text: t("emailPurpose.marketing") },
|
|
84
|
+
],
|
|
85
|
+
[t],
|
|
86
|
+
);
|
|
87
|
+
|
|
71
88
|
return (
|
|
72
89
|
<EditorSheet
|
|
73
90
|
form={form}
|
|
@@ -86,6 +103,7 @@ function EmailBlastEditorInternal({
|
|
|
86
103
|
subject: values.subject,
|
|
87
104
|
body: values.body,
|
|
88
105
|
replyTo: values.replyTo || undefined,
|
|
106
|
+
purpose: values.purpose,
|
|
89
107
|
};
|
|
90
108
|
return isEdit ? await EmailBlastService.update(payload) : await EmailBlastService.create(payload);
|
|
91
109
|
}}
|
|
@@ -98,6 +116,15 @@ function EmailBlastEditorInternal({
|
|
|
98
116
|
>
|
|
99
117
|
<div className="flex w-full flex-col gap-y-4">
|
|
100
118
|
<FormInput form={form} id="subject" name={t("emailBlasts.editor.fields.subject.label")} isRequired />
|
|
119
|
+
<FormSelect
|
|
120
|
+
form={form}
|
|
121
|
+
id="purpose"
|
|
122
|
+
name={t("emailPurpose.label")}
|
|
123
|
+
placeholder={t("emailPurpose.placeholder")}
|
|
124
|
+
values={purposeValues}
|
|
125
|
+
description={purpose === "service" ? t("emailPurpose.serviceWarning") : undefined}
|
|
126
|
+
isRequired
|
|
127
|
+
/>
|
|
101
128
|
<FormInput form={form} id="replyTo" name={t("emailBlasts.editor.fields.replyTo.label")} />
|
|
102
129
|
{/* FormTextarea has no `rows` prop; min-h-72 is the height of ~14 rows. */}
|
|
103
130
|
<FormTextarea
|
|
@@ -110,13 +137,15 @@ function EmailBlastEditorInternal({
|
|
|
110
137
|
<>
|
|
111
138
|
<span className="block">
|
|
112
139
|
{t("emailBlasts.editor.variablesHint")}{" "}
|
|
113
|
-
{
|
|
140
|
+
{templateVariables.map((variable) => (
|
|
114
141
|
<span key={variable} className="me-2 text-foreground">
|
|
115
142
|
{variable}
|
|
116
143
|
</span>
|
|
117
144
|
))}
|
|
118
145
|
</span>
|
|
119
|
-
<span className="mt-1 block">
|
|
146
|
+
<span className="mt-1 block">
|
|
147
|
+
{purpose === "service" ? t("emailBlasts.editor.serviceHint") : t("emailBlasts.editor.unsubscribeHint")}
|
|
148
|
+
</span>
|
|
120
149
|
</>
|
|
121
150
|
}
|
|
122
151
|
/>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from "../../../core";
|
|
2
|
+
import { EmailPurpose } from "../../email-automation/data/EmailPurpose";
|
|
2
3
|
import { EmailBlastInput, EmailBlastInterface, EmailBlastStatus } from "./EmailBlastInterface";
|
|
3
4
|
|
|
4
5
|
export class EmailBlast extends AbstractApiData implements EmailBlastInterface {
|
|
5
6
|
private _subject?: string;
|
|
6
7
|
private _body?: string;
|
|
7
8
|
private _replyTo?: string;
|
|
9
|
+
private _purpose: EmailPurpose = "marketing";
|
|
8
10
|
private _status: EmailBlastStatus = "draft";
|
|
9
11
|
private _sentAt?: Date;
|
|
10
12
|
private _recipientCount?: number;
|
|
@@ -24,6 +26,10 @@ export class EmailBlast extends AbstractApiData implements EmailBlastInterface {
|
|
|
24
26
|
return this._replyTo;
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
get purpose(): EmailPurpose {
|
|
30
|
+
return this._purpose;
|
|
31
|
+
}
|
|
32
|
+
|
|
27
33
|
get status(): EmailBlastStatus {
|
|
28
34
|
return this._status;
|
|
29
35
|
}
|
|
@@ -55,6 +61,8 @@ export class EmailBlast extends AbstractApiData implements EmailBlastInterface {
|
|
|
55
61
|
this._subject = attributes.subject;
|
|
56
62
|
this._body = attributes.body;
|
|
57
63
|
this._replyTo = attributes.replyTo ?? undefined;
|
|
64
|
+
// A record saved before purpose existed counts as Marketing.
|
|
65
|
+
this._purpose = attributes.purpose === "service" ? "service" : "marketing";
|
|
58
66
|
this._status = attributes.status ?? "draft";
|
|
59
67
|
this._sentAt = attributes.sentAt ? new Date(attributes.sentAt) : undefined;
|
|
60
68
|
this._recipientCount = attributes.recipientCount ?? undefined;
|
|
@@ -66,7 +74,7 @@ export class EmailBlast extends AbstractApiData implements EmailBlastInterface {
|
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
/**
|
|
69
|
-
* Only the editable text
|
|
77
|
+
* Only the editable text and the purpose travel: status, counters and sentAt are set by the API.
|
|
70
78
|
*/
|
|
71
79
|
createJsonApi(data: EmailBlastInput) {
|
|
72
80
|
const response: any = {
|
|
@@ -83,6 +91,7 @@ export class EmailBlast extends AbstractApiData implements EmailBlastInterface {
|
|
|
83
91
|
if (data.subject !== undefined) response.data.attributes.subject = data.subject;
|
|
84
92
|
if (data.body !== undefined) response.data.attributes.body = data.body;
|
|
85
93
|
if (data.replyTo !== undefined) response.data.attributes.replyTo = data.replyTo;
|
|
94
|
+
if (data.purpose !== undefined) response.data.attributes.purpose = data.purpose;
|
|
86
95
|
|
|
87
96
|
return response;
|
|
88
97
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiDataInterface } from "../../../core";
|
|
2
|
+
import { EmailPurpose } from "../../email-automation/data/EmailPurpose";
|
|
2
3
|
|
|
3
4
|
export type EmailBlastStatus = "draft" | "sending" | "sent";
|
|
4
5
|
|
|
@@ -7,12 +8,14 @@ export type EmailBlastInput = {
|
|
|
7
8
|
subject: string;
|
|
8
9
|
body: string;
|
|
9
10
|
replyTo?: string;
|
|
11
|
+
purpose?: EmailPurpose;
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
export interface EmailBlastInterface extends ApiDataInterface {
|
|
13
15
|
get subject(): string;
|
|
14
16
|
get body(): string;
|
|
15
17
|
get replyTo(): string | undefined;
|
|
18
|
+
get purpose(): EmailPurpose;
|
|
16
19
|
get status(): EmailBlastStatus;
|
|
17
20
|
get sentAt(): Date | undefined;
|
|
18
21
|
get recipientCount(): number | undefined;
|
|
@@ -70,4 +70,22 @@ describe("email blast models", () => {
|
|
|
70
70
|
expect(audience.recipientCount).toBe(42);
|
|
71
71
|
expect(new EmailBlastAudience().rehydrate(hydrate({})).recipientCount).toBe(0);
|
|
72
72
|
});
|
|
73
|
+
it('EmailBlast rehydrates purpose "service"', () => {
|
|
74
|
+
const blast = new EmailBlast().rehydrate(hydrate({ subject: "News", body: "Hello", purpose: "service" }));
|
|
75
|
+
expect(blast.purpose).toBe("service");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("missing purpose rehydrates as marketing", () => {
|
|
79
|
+
expect(new EmailBlast().rehydrate(hydrate({ subject: "News", body: "Hello" })).purpose).toBe("marketing");
|
|
80
|
+
expect(new EmailBlast().rehydrate(hydrate({ subject: "News", body: "Hello", purpose: "x" })).purpose).toBe(
|
|
81
|
+
"marketing",
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("createJsonApi sends purpose when set", () => {
|
|
86
|
+
const body = new EmailBlast().createJsonApi({ id: "b1", subject: "News", body: "Hello", purpose: "service" });
|
|
87
|
+
expect(body.data.attributes.purpose).toBe("service");
|
|
88
|
+
const without = new EmailBlast().createJsonApi({ id: "b1", subject: "News", body: "Hello" });
|
|
89
|
+
expect("purpose" in without.data.attributes).toBe(false);
|
|
90
|
+
});
|
|
73
91
|
});
|
|
@@ -57,6 +57,21 @@ export const useEmailBlastTableStructure: UseTableStructureHook<EmailBlastInterf
|
|
|
57
57
|
enableSorting: false,
|
|
58
58
|
enableHiding: false,
|
|
59
59
|
}),
|
|
60
|
+
[EmailBlastFields.purpose]: () => ({
|
|
61
|
+
id: "purpose",
|
|
62
|
+
accessorKey: "purpose",
|
|
63
|
+
header: t("emailBlasts.list.columns.purpose"),
|
|
64
|
+
cell: ({ row }: { row: TableContent<EmailBlastInterface> }) => {
|
|
65
|
+
const blast: EmailBlastInterface = row.original.jsonApiData;
|
|
66
|
+
return blast.purpose === "service" ? (
|
|
67
|
+
<Badge variant="softBlue">{t("emailPurpose.service")}</Badge>
|
|
68
|
+
) : (
|
|
69
|
+
<Badge variant="softGray">{t("emailPurpose.marketing")}</Badge>
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
enableSorting: false,
|
|
73
|
+
enableHiding: false,
|
|
74
|
+
}),
|
|
60
75
|
[EmailBlastFields.status]: () => ({
|
|
61
76
|
id: "status",
|
|
62
77
|
accessorKey: "status",
|