@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.
Files changed (127) hide show
  1. package/dist/{BlockNoteEditor-UIAX72XW.js → BlockNoteEditor-GWDYMS6R.js} +20 -20
  2. package/dist/{BlockNoteEditor-UIAX72XW.js.map → BlockNoteEditor-GWDYMS6R.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-I3J6YYXK.mjs → BlockNoteEditor-HNP5UAJT.mjs} +5 -5
  4. package/dist/{BlockNoteViewer-HIUAJQRI.mjs → BlockNoteViewer-X4UEVI2A.mjs} +3 -3
  5. package/dist/{BlockNoteViewer-APBOPT4B.js → BlockNoteViewer-XYKFY77F.js} +4 -4
  6. package/dist/{BlockNoteViewer-APBOPT4B.js.map → BlockNoteViewer-XYKFY77F.js.map} +1 -1
  7. package/dist/EmailPurpose-DOsSqM5K.d.mts +3 -0
  8. package/dist/EmailPurpose-DOsSqM5K.d.ts +3 -0
  9. package/dist/billing/index.js +492 -492
  10. package/dist/billing/index.mjs +4 -4
  11. package/dist/{chunk-4XLDBOSE.js → chunk-A7Y5YW7Q.js} +1 -1
  12. package/dist/{chunk-4XLDBOSE.js.map → chunk-A7Y5YW7Q.js.map} +1 -1
  13. package/dist/{chunk-ZYSNJKOP.js → chunk-DFA566KY.js} +4 -4
  14. package/dist/{chunk-ZYSNJKOP.js.map → chunk-DFA566KY.js.map} +1 -1
  15. package/dist/{chunk-DJXSCARG.js → chunk-I5BQLY7V.js} +9 -9
  16. package/dist/{chunk-DJXSCARG.js.map → chunk-I5BQLY7V.js.map} +1 -1
  17. package/dist/{chunk-RFKSJIQB.mjs → chunk-KMPBSOFB.mjs} +138 -5
  18. package/dist/chunk-KMPBSOFB.mjs.map +1 -0
  19. package/dist/{chunk-UOTHRJD2.mjs → chunk-OKBJEIOP.mjs} +1 -1
  20. package/dist/chunk-OKBJEIOP.mjs.map +1 -0
  21. package/dist/{chunk-PMESKNKL.js → chunk-OO2DBP6L.js} +1106 -973
  22. package/dist/chunk-OO2DBP6L.js.map +1 -0
  23. package/dist/{chunk-HAW5PNJG.mjs → chunk-TY5YWBLL.mjs} +3 -3
  24. package/dist/{chunk-RT7XFGIN.js → chunk-UGDYZW4K.js} +1035 -997
  25. package/dist/chunk-UGDYZW4K.js.map +1 -0
  26. package/dist/{chunk-RYY7USSW.mjs → chunk-YEZGASBX.mjs} +50 -12
  27. package/dist/{chunk-RYY7USSW.mjs.map → chunk-YEZGASBX.mjs.map} +1 -1
  28. package/dist/{chunk-OUEYZOLB.mjs → chunk-YVKWACDP.mjs} +2 -2
  29. package/dist/client/index.js +5 -5
  30. package/dist/client/index.mjs +4 -4
  31. package/dist/components/editors/server.d.mts +7 -1
  32. package/dist/components/editors/server.d.ts +7 -1
  33. package/dist/components/editors/server.js +17 -4
  34. package/dist/components/editors/server.js.map +1 -1
  35. package/dist/components/editors/server.mjs +17 -4
  36. package/dist/components/editors/server.mjs.map +1 -1
  37. package/dist/components/index.d.mts +3 -2
  38. package/dist/components/index.d.ts +3 -2
  39. package/dist/components/index.js +5 -5
  40. package/dist/components/index.mjs +4 -4
  41. package/dist/contexts/index.js +5 -5
  42. package/dist/contexts/index.mjs +4 -4
  43. package/dist/core/index.d.mts +92 -7
  44. package/dist/core/index.d.ts +92 -7
  45. package/dist/core/index.js +7 -3
  46. package/dist/core/index.js.map +1 -1
  47. package/dist/core/index.mjs +6 -2
  48. package/dist/{email-automation-enrollment.fields-BZvF14qx.d.mts → email-automation-enrollment.fields-CPsgxLif.d.mts} +11 -1
  49. package/dist/{email-automation-enrollment.fields-Cp3VMzFq.d.ts → email-automation-enrollment.fields-CVicSamF.d.ts} +11 -1
  50. package/dist/email-automations/index.d.mts +19 -5
  51. package/dist/email-automations/index.d.ts +19 -5
  52. package/dist/email-automations/index.js +356 -129
  53. package/dist/email-automations/index.js.map +1 -1
  54. package/dist/email-automations/index.mjs +306 -79
  55. package/dist/email-automations/index.mjs.map +1 -1
  56. package/dist/features/analytics/index.js +121 -121
  57. package/dist/features/analytics/index.mjs +4 -4
  58. package/dist/features/analytics/server.js +5 -5
  59. package/dist/features/analytics/server.mjs +3 -3
  60. package/dist/features/blog/index.js +151 -151
  61. package/dist/features/blog/index.mjs +4 -4
  62. package/dist/features/blog/server.js +17 -17
  63. package/dist/features/blog/server.mjs +2 -2
  64. package/dist/features/help/index.js +38 -38
  65. package/dist/features/help/index.mjs +4 -4
  66. package/dist/features/tokenusage/index.js +127 -127
  67. package/dist/features/tokenusage/index.mjs +4 -4
  68. package/dist/index.d.mts +4 -3
  69. package/dist/index.d.ts +4 -3
  70. package/dist/index.js +8 -4
  71. package/dist/index.js.map +1 -1
  72. package/dist/index.mjs +7 -3
  73. package/dist/offline/index.js +14 -14
  74. package/dist/offline/index.mjs +1 -1
  75. package/dist/{referral-admin.interface-RVO-ZTJz.d.mts → referral-admin.interface-DTj4LzXx.d.mts} +3 -0
  76. package/dist/{referral-admin.interface-CnytdDaQ.d.ts → referral-admin.interface-DeSCw3Pp.d.ts} +3 -0
  77. package/dist/server/index.js +4 -4
  78. package/dist/server/index.mjs +3 -3
  79. package/messages/en.json +35 -3
  80. package/messages/it.json +35 -3
  81. package/package.json +1 -1
  82. package/src/components/editors/server/__tests__/renderBlockNoteMarkdown.spec.ts +45 -0
  83. package/src/components/editors/server/renderBlockNoteHTML.ts +2 -18
  84. package/src/components/editors/server/renderBlockNoteMarkdown.ts +10 -0
  85. package/src/components/editors/server/serverEditor.ts +25 -0
  86. package/src/components/editors/server-entry.ts +1 -0
  87. package/src/core/index.ts +1 -0
  88. package/src/core/registry/ModuleRegistry.ts +1 -0
  89. package/src/features/email-automation/EmailAutomationCandidateModule.ts +12 -0
  90. package/src/features/email-automation/EmailAutomationModule.ts +1 -0
  91. package/src/features/email-automation/components/__tests__/EmailAutomationEditor.spec.tsx +74 -9
  92. package/src/features/email-automation/components/forms/EmailAutomationAddPersonDialog.tsx +226 -0
  93. package/src/features/email-automation/components/forms/EmailAutomationEditor.tsx +23 -0
  94. package/src/features/email-automation/components/forms/EmailStepEditor.tsx +7 -3
  95. package/src/features/email-automation/components/index.ts +1 -0
  96. package/src/features/email-automation/components/lists/EmailAutomationRecipientsList.tsx +38 -20
  97. package/src/features/email-automation/components/lists/EmailAutomationsList.tsx +1 -0
  98. package/src/features/email-automation/components/unsubscribe/EmailUnsubscribe.tsx +9 -4
  99. package/src/features/email-automation/data/EmailAutomation.ts +9 -0
  100. package/src/features/email-automation/data/EmailAutomationCandidate.ts +66 -0
  101. package/src/features/email-automation/data/EmailAutomationCandidateInterface.ts +19 -0
  102. package/src/features/email-automation/data/EmailAutomationEnrollment.ts +16 -2
  103. package/src/features/email-automation/data/EmailAutomationEnrollmentInterface.ts +7 -0
  104. package/src/features/email-automation/data/EmailAutomationInterface.ts +3 -0
  105. package/src/features/email-automation/data/EmailAutomationService.ts +53 -1
  106. package/src/features/email-automation/data/EmailPurpose.ts +1 -0
  107. package/src/features/email-automation/data/__tests__/email-automation-models.spec.ts +70 -0
  108. package/src/features/email-automation/data/email-automation.fields.ts +1 -0
  109. package/src/features/email-automation/data/index.ts +2 -0
  110. package/src/features/email-automation/hooks/useEmailAutomationTableStructure.tsx +19 -0
  111. package/src/features/email-automation/index.ts +2 -0
  112. package/src/features/email-blast/EmailBlastModule.ts +1 -0
  113. package/src/features/email-blast/components/forms/EmailBlastEditor.tsx +33 -4
  114. package/src/features/email-blast/components/lists/EmailBlastsList.tsx +1 -0
  115. package/src/features/email-blast/data/EmailBlast.ts +10 -1
  116. package/src/features/email-blast/data/EmailBlastInterface.ts +3 -0
  117. package/src/features/email-blast/data/__tests__/EmailBlast.spec.ts +18 -0
  118. package/src/features/email-blast/data/email-blast.fields.ts +1 -0
  119. package/src/features/email-blast/hooks/useEmailBlastTableStructure.tsx +15 -0
  120. package/dist/chunk-PMESKNKL.js.map +0 -1
  121. package/dist/chunk-RFKSJIQB.mjs.map +0 -1
  122. package/dist/chunk-RT7XFGIN.js.map +0 -1
  123. package/dist/chunk-UOTHRJD2.mjs.map +0 -1
  124. /package/dist/{BlockNoteEditor-I3J6YYXK.mjs.map → BlockNoteEditor-HNP5UAJT.mjs.map} +0 -0
  125. /package/dist/{BlockNoteViewer-HIUAJQRI.mjs.map → BlockNoteViewer-X4UEVI2A.mjs.map} +0 -0
  126. /package/dist/{chunk-HAW5PNJG.mjs.map → chunk-TY5YWBLL.mjs.map} +0 -0
  127. /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}}", "{{unsubscribeAllUrl}}"];
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
- {TEMPLATE_VARIABLES.map((variable) => (
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">{t("emailBlasts.editor.unsubscribeHint")}</span>
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
  />
@@ -41,6 +41,7 @@ export function EmailBlastsList({ fullWidth }: EmailBlastsListProps = {}) {
41
41
  data={data}
42
42
  fields={[
43
43
  EmailBlastFields.subject,
44
+ EmailBlastFields.purpose,
44
45
  EmailBlastFields.status,
45
46
  EmailBlastFields.sentAt,
46
47
  EmailBlastFields.sent,
@@ -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 travels: status, counters and sentAt are set by the API.
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
  });
@@ -1,5 +1,6 @@
1
1
  export enum EmailBlastFields {
2
2
  subject = "subject",
3
+ purpose = "purpose",
3
4
  status = "status",
4
5
  sentAt = "sentAt",
5
6
  sent = "sent",
@@ -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",