@carlonicora/nextjs-jsonapi 6.7.0 → 6.9.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 (137) hide show
  1. package/dist/{AssistantMessageInterface-0ZQ1EJKO.d.mts → AssistantMessageInterface-BE3QxI54.d.mts} +1 -1
  2. package/dist/{AssistantMessageInterface-CA-hZ5Wj.d.ts → AssistantMessageInterface-nXpD_d-m.d.ts} +1 -1
  3. package/dist/{BlockNoteEditor-HWJBQAKG.mjs → BlockNoteEditor-WN73IHXR.mjs} +4 -4
  4. package/dist/{BlockNoteEditor-BSFR2GPI.js → BlockNoteEditor-XXH3VBZ7.js} +19 -19
  5. package/dist/{BlockNoteEditor-BSFR2GPI.js.map → BlockNoteEditor-XXH3VBZ7.js.map} +1 -1
  6. package/dist/{BlockNoteViewer-NHS6CCMS.js → BlockNoteViewer-EKAWVLIR.js} +3 -3
  7. package/dist/{BlockNoteViewer-NHS6CCMS.js.map → BlockNoteViewer-EKAWVLIR.js.map} +1 -1
  8. package/dist/{BlockNoteViewer-IIBUU7J7.mjs → BlockNoteViewer-RWRI3QO2.mjs} +2 -2
  9. package/dist/{BlogPostInterface-CTGfsR1I.d.ts → BlogPostInterface-2sVqvXFd.d.ts} +1 -1
  10. package/dist/{BlogPostInterface-BN2mXc46.d.mts → BlogPostInterface-BgJLFpaa.d.mts} +1 -1
  11. package/dist/{HandbookSectionInterface-BYsmnPEz.d.mts → HandbookSectionInterface-BPgqqwgw.d.mts} +20 -1
  12. package/dist/{HandbookSectionInterface-OePKPbqN.d.ts → HandbookSectionInterface-_51oA3om.d.ts} +20 -1
  13. package/dist/{auth.interface-DuX87BZ3.d.mts → auth.interface-BAYLbYjs.d.mts} +1 -1
  14. package/dist/{auth.interface-B4tRE0k0.d.ts → auth.interface-BjYwwfy9.d.ts} +1 -1
  15. package/dist/billing/index.js +492 -492
  16. package/dist/billing/index.mjs +3 -3
  17. package/dist/{chunk-EC4PQ44E.mjs → chunk-3TNGKMJU.mjs} +2 -2
  18. package/dist/{chunk-QNIWATQD.mjs → chunk-6HFRXEF5.mjs} +331 -3
  19. package/dist/chunk-6HFRXEF5.mjs.map +1 -0
  20. package/dist/{chunk-OYRJ32KR.mjs → chunk-AWLTSKMM.mjs} +2 -2
  21. package/dist/{chunk-XYKDBOCE.mjs → chunk-DPAM7BCE.mjs} +2 -2
  22. package/dist/{chunk-PYDDFFQM.js → chunk-G3YKDIAL.js} +3 -3
  23. package/dist/{chunk-PYDDFFQM.js.map → chunk-G3YKDIAL.js.map} +1 -1
  24. package/dist/{chunk-3HOJNAZB.js → chunk-KRQRJA4J.js} +8 -8
  25. package/dist/{chunk-3HOJNAZB.js.map → chunk-KRQRJA4J.js.map} +1 -1
  26. package/dist/{chunk-S4NKWMX5.mjs → chunk-Q4XLE3QO.mjs} +1738 -1308
  27. package/dist/chunk-Q4XLE3QO.mjs.map +1 -0
  28. package/dist/{chunk-73XWQA6U.js → chunk-RNZ3IK5J.js} +4 -4
  29. package/dist/{chunk-73XWQA6U.js.map → chunk-RNZ3IK5J.js.map} +1 -1
  30. package/dist/{chunk-6KE7UBYY.js → chunk-SQTXJPRE.js} +505 -177
  31. package/dist/chunk-SQTXJPRE.js.map +1 -0
  32. package/dist/{chunk-PCCUPEBU.js → chunk-ZCM4JLM7.js} +1381 -951
  33. package/dist/chunk-ZCM4JLM7.js.map +1 -0
  34. package/dist/client/index.d.mts +3 -3
  35. package/dist/client/index.d.ts +3 -3
  36. package/dist/client/index.js +4 -4
  37. package/dist/client/index.mjs +3 -3
  38. package/dist/components/index.d.mts +62 -8
  39. package/dist/components/index.d.ts +62 -8
  40. package/dist/components/index.js +14 -4
  41. package/dist/components/index.js.map +1 -1
  42. package/dist/components/index.mjs +13 -3
  43. package/dist/contexts/index.d.mts +4 -4
  44. package/dist/contexts/index.d.ts +4 -4
  45. package/dist/contexts/index.js +4 -4
  46. package/dist/contexts/index.mjs +3 -3
  47. package/dist/core/index.d.mts +146 -13
  48. package/dist/core/index.d.ts +146 -13
  49. package/dist/core/index.js +24 -2
  50. package/dist/core/index.js.map +1 -1
  51. package/dist/core/index.mjs +23 -1
  52. package/dist/email-automations/index.d.mts +6 -1
  53. package/dist/email-automations/index.d.ts +6 -1
  54. package/dist/email-automations/index.js +101 -94
  55. package/dist/email-automations/index.js.map +1 -1
  56. package/dist/email-automations/index.mjs +12 -5
  57. package/dist/email-automations/index.mjs.map +1 -1
  58. package/dist/features/analytics/index.d.mts +1 -1
  59. package/dist/features/analytics/index.d.ts +1 -1
  60. package/dist/features/analytics/index.js +121 -121
  61. package/dist/features/analytics/index.mjs +4 -4
  62. package/dist/features/analytics/server.js +4 -4
  63. package/dist/features/analytics/server.mjs +2 -2
  64. package/dist/features/blog/index.d.mts +3 -3
  65. package/dist/features/blog/index.d.ts +3 -3
  66. package/dist/features/blog/index.js +150 -150
  67. package/dist/features/blog/index.mjs +3 -3
  68. package/dist/features/blog/server.d.mts +3 -3
  69. package/dist/features/blog/server.d.ts +3 -3
  70. package/dist/features/blog/server.js +16 -16
  71. package/dist/features/blog/server.mjs +1 -1
  72. package/dist/features/help/index.js +37 -37
  73. package/dist/features/help/index.mjs +3 -3
  74. package/dist/features/tokenusage/index.js +127 -127
  75. package/dist/features/tokenusage/index.mjs +4 -4
  76. package/dist/index.d.mts +8 -8
  77. package/dist/index.d.ts +8 -8
  78. package/dist/index.js +25 -3
  79. package/dist/index.js.map +1 -1
  80. package/dist/index.mjs +24 -2
  81. package/dist/{notification.interface-DQReXAD7.d.ts → notification.interface-DQItyY_Q.d.ts} +1 -1
  82. package/dist/{notification.interface-CF5h90U2.d.mts → notification.interface-DoVS5RZB.d.mts} +1 -1
  83. package/dist/{s3.service-CRGJbO-6.d.mts → s3.service-BG136Btz.d.mts} +3 -3
  84. package/dist/{s3.service-DZG-VyLY.d.ts → s3.service-CsrsRPRn.d.ts} +3 -3
  85. package/dist/server/index.d.mts +4 -4
  86. package/dist/server/index.d.ts +4 -4
  87. package/dist/server/index.js +3 -3
  88. package/dist/server/index.mjs +2 -2
  89. package/dist/{useSocket-CwSoq6VS.d.mts → useSocket-C8fqU4od.d.mts} +1 -1
  90. package/dist/{useSocket-BM3q9A0C.d.ts → useSocket-IRHtk2xc.d.ts} +1 -1
  91. package/dist/{user.interface-BYnGJ7ss.d.mts → user.interface-Bm-0TbsB.d.mts} +6 -0
  92. package/dist/{user.interface-BqPjVyJI.d.ts → user.interface-CKBIEvNu.d.ts} +6 -0
  93. package/messages/en.json +62 -0
  94. package/messages/it.json +62 -0
  95. package/package.json +1 -1
  96. package/src/components/index.ts +1 -0
  97. package/src/core/index.ts +3 -0
  98. package/src/core/registry/ModuleRegistry.ts +4 -0
  99. package/src/features/company/data/company.interface.ts +7 -0
  100. package/src/features/company/data/company.spec.ts +23 -0
  101. package/src/features/company/data/company.ts +6 -0
  102. package/src/features/company/hooks/useSubscriptionStatus.spec.ts +59 -0
  103. package/src/features/company/hooks/useSubscriptionStatus.ts +5 -3
  104. package/src/features/email-automation/components/unsubscribe/EmailUnsubscribe.tsx +14 -2
  105. package/src/features/email-blast/EmailBlastAudienceModule.ts +12 -0
  106. package/src/features/email-blast/EmailBlastDeliveryModule.ts +12 -0
  107. package/src/features/email-blast/EmailBlastModule.ts +28 -0
  108. package/src/features/email-blast/components/containers/EmailBlastContainer.tsx +145 -0
  109. package/src/features/email-blast/components/containers/EmailBlastsListContainer.tsx +36 -0
  110. package/src/features/email-blast/components/forms/EmailBlastEditor.tsx +132 -0
  111. package/src/features/email-blast/components/forms/EmailBlastSendDialog.tsx +104 -0
  112. package/src/features/email-blast/components/index.ts +5 -0
  113. package/src/features/email-blast/components/lists/EmailBlastsList.tsx +57 -0
  114. package/src/features/email-blast/data/EmailBlast.ts +89 -0
  115. package/src/features/email-blast/data/EmailBlastAudience.ts +22 -0
  116. package/src/features/email-blast/data/EmailBlastAudienceInterface.ts +5 -0
  117. package/src/features/email-blast/data/EmailBlastDelivery.ts +36 -0
  118. package/src/features/email-blast/data/EmailBlastDeliveryInterface.ts +7 -0
  119. package/src/features/email-blast/data/EmailBlastDeliveryService.ts +25 -0
  120. package/src/features/email-blast/data/EmailBlastInterface.ts +22 -0
  121. package/src/features/email-blast/data/EmailBlastService.ts +101 -0
  122. package/src/features/email-blast/data/__tests__/EmailBlast.spec.ts +73 -0
  123. package/src/features/email-blast/data/email-blast.fields.ts +8 -0
  124. package/src/features/email-blast/data/email-blast.segments.ts +10 -0
  125. package/src/features/email-blast/data/index.ts +10 -0
  126. package/src/features/email-blast/hooks/useEmailBlastTableStructure.tsx +104 -0
  127. package/src/features/email-blast/index.ts +4 -0
  128. package/src/features/index.ts +1 -0
  129. package/dist/chunk-6KE7UBYY.js.map +0 -1
  130. package/dist/chunk-PCCUPEBU.js.map +0 -1
  131. package/dist/chunk-QNIWATQD.mjs.map +0 -1
  132. package/dist/chunk-S4NKWMX5.mjs.map +0 -1
  133. /package/dist/{BlockNoteEditor-HWJBQAKG.mjs.map → BlockNoteEditor-WN73IHXR.mjs.map} +0 -0
  134. /package/dist/{BlockNoteViewer-IIBUU7J7.mjs.map → BlockNoteViewer-RWRI3QO2.mjs.map} +0 -0
  135. /package/dist/{chunk-EC4PQ44E.mjs.map → chunk-3TNGKMJU.mjs.map} +0 -0
  136. /package/dist/{chunk-OYRJ32KR.mjs.map → chunk-AWLTSKMM.mjs.map} +0 -0
  137. /package/dist/{chunk-XYKDBOCE.mjs.map → chunk-DPAM7BCE.mjs.map} +0 -0
@@ -0,0 +1,73 @@
1
+ import { beforeAll, describe, expect, it } from "vitest";
2
+ import { ModuleRegistry } from "../../../../core/registry/ModuleRegistry";
3
+ import { ModuleFactory, ModuleWithPermissions } from "../../../../permissions/types";
4
+ import { EmailBlastAudienceModule } from "../../EmailBlastAudienceModule";
5
+ import { EmailBlastDeliveryModule } from "../../EmailBlastDeliveryModule";
6
+ import { EmailBlastModule } from "../../EmailBlastModule";
7
+ import { EmailBlast } from "../EmailBlast";
8
+ import { EmailBlastAudience } from "../EmailBlastAudience";
9
+
10
+ // createJsonApi reads Modules.X.name (resolved lazily via ModuleRegistry), so
11
+ // the modules must be registered before a model serialises — same setup as
12
+ // email-automation-models.spec.
13
+ const moduleFactory: ModuleFactory = (params) =>
14
+ ({
15
+ pageUrl: params.pageUrl,
16
+ name: params.name,
17
+ model: params.model,
18
+ }) as ModuleWithPermissions;
19
+
20
+ beforeAll(() => {
21
+ ModuleRegistry.register("EmailBlast", EmailBlastModule(moduleFactory));
22
+ ModuleRegistry.register("EmailBlastDelivery", EmailBlastDeliveryModule(moduleFactory));
23
+ ModuleRegistry.register("EmailBlastAudience", EmailBlastAudienceModule(moduleFactory));
24
+ });
25
+
26
+ const hydrate = (attributes: Record<string, unknown>) =>
27
+ ({ jsonApi: { id: "x", type: "t", attributes }, included: [] }) as any;
28
+
29
+ describe("email blast models", () => {
30
+ it("rehydrate parses sentAt to a Date and defaults counters to 0", () => {
31
+ const sent = new EmailBlast().rehydrate(
32
+ hydrate({
33
+ subject: "News",
34
+ body: "Hello",
35
+ status: "sent",
36
+ sentAt: "2026-10-27T08:00:00.000Z",
37
+ recipientCount: 12,
38
+ }),
39
+ );
40
+ expect(sent.sentAt).toBeInstanceOf(Date);
41
+ expect(sent.sentAt?.toISOString()).toBe("2026-10-27T08:00:00.000Z");
42
+ expect(sent.recipientCount).toBe(12);
43
+ expect(sent.status).toBe("sent");
44
+ expect(sent.sentCount).toBe(0);
45
+ expect(sent.failedCount).toBe(0);
46
+ expect(sent.unsubscribedCount).toBe(0);
47
+
48
+ const draft = new EmailBlast().rehydrate(hydrate({ subject: "News", body: "Hello" }));
49
+ expect(draft.sentAt).toBeUndefined();
50
+ expect(draft.recipientCount).toBeUndefined();
51
+ expect(draft.replyTo).toBeUndefined();
52
+ expect(draft.status).toBe("draft");
53
+ });
54
+
55
+ it("createJsonApi emits only subject, body and replyTo", () => {
56
+ const body = new EmailBlast().createJsonApi({
57
+ id: "b1",
58
+ subject: "News",
59
+ body: "Hello",
60
+ replyTo: "gm@example.com",
61
+ ...({ status: "sent", sentCount: 3, failedCount: 1, unsubscribedCount: 1, sentAt: new Date() } as object),
62
+ });
63
+ expect(body.data.type).toBe("email-blasts");
64
+ expect(body.data.id).toBe("b1");
65
+ expect(body.data.attributes).toEqual({ subject: "News", body: "Hello", replyTo: "gm@example.com" });
66
+ });
67
+
68
+ it("EmailBlastAudience rehydrates recipientCount", () => {
69
+ const audience = new EmailBlastAudience().rehydrate(hydrate({ recipientCount: 42 }));
70
+ expect(audience.recipientCount).toBe(42);
71
+ expect(new EmailBlastAudience().rehydrate(hydrate({})).recipientCount).toBe(0);
72
+ });
73
+ });
@@ -0,0 +1,8 @@
1
+ export enum EmailBlastFields {
2
+ subject = "subject",
3
+ status = "status",
4
+ sentAt = "sentAt",
5
+ sent = "sent",
6
+ failed = "failed",
7
+ unsubscribed = "unsubscribed",
8
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * URL segments of the email-blast child routes. The only place these strings
3
+ * are written: services build every child URL from here through EndpointCreator.
4
+ */
5
+ export const EMAIL_BLAST_SEGMENTS = {
6
+ audience: "recipient-count",
7
+ test: "test",
8
+ send: "send",
9
+ unsubscribe: "unsubscribe",
10
+ } as const;
@@ -0,0 +1,10 @@
1
+ export * from "./email-blast.segments";
2
+ export * from "./EmailBlastInterface";
3
+ export * from "./EmailBlast";
4
+ export * from "./EmailBlastService";
5
+ export * from "./EmailBlastDeliveryInterface";
6
+ export * from "./EmailBlastDelivery";
7
+ export * from "./EmailBlastDeliveryService";
8
+ export * from "./EmailBlastAudienceInterface";
9
+ export * from "./EmailBlastAudience";
10
+ export * from "./email-blast.fields";
@@ -0,0 +1,104 @@
1
+ "use client";
2
+
3
+ import { ColumnDef } from "@tanstack/react-table";
4
+ import { useTranslations } from "next-intl";
5
+ import { useMemo } from "react";
6
+ import { Badge, Link } from "../../../shadcnui";
7
+ import { Modules } from "../../../core";
8
+ import { registerTableGenerator, TableContent, usePageUrlGenerator, UseTableStructureHook } from "../../../hooks";
9
+ import { useI18nLocale } from "../../../i18n";
10
+ import { EmailBlastFields } from "../data/email-blast.fields";
11
+ import { EmailBlastInterface, EmailBlastStatus } from "../data/EmailBlastInterface";
12
+
13
+ const STATUS_VARIANTS: Record<EmailBlastStatus, "softGray" | "softAmber" | "softGreen"> = {
14
+ draft: "softGray",
15
+ sending: "softAmber",
16
+ sent: "softGreen",
17
+ };
18
+
19
+ export const useEmailBlastTableStructure: UseTableStructureHook<EmailBlastInterface, EmailBlastFields> = (params) => {
20
+ const t = useTranslations();
21
+ const locale = useI18nLocale();
22
+ const generateUrl = usePageUrlGenerator();
23
+
24
+ const tableData = useMemo(() => {
25
+ return params.data.map((blast: EmailBlastInterface) => {
26
+ const entry: TableContent<EmailBlastInterface> = { jsonApiData: blast };
27
+ params.fields.forEach((field) => {
28
+ entry[field] = (blast as any)[field as keyof EmailBlastInterface];
29
+ });
30
+ return entry;
31
+ });
32
+ }, [params.data, params.fields]);
33
+
34
+ // Typography role 15 (numeric): tabular-nums, end-aligned. NEVER
35
+ // font-mono — no monospace font is loaded in these apps.
36
+ const numericColumn = (field: EmailBlastFields, header: string, read: (b: EmailBlastInterface) => number) => ({
37
+ id: field,
38
+ accessorKey: field,
39
+ header: header,
40
+ cell: ({ row }: { row: TableContent<EmailBlastInterface> }) => {
41
+ const blast: EmailBlastInterface = row.original.jsonApiData;
42
+ return <span className="block text-xs tabular-nums text-end">{read(blast) ?? 0}</span>;
43
+ },
44
+ enableSorting: false,
45
+ enableHiding: false,
46
+ });
47
+
48
+ const fieldColumnMap: Partial<Record<EmailBlastFields, () => any>> = {
49
+ [EmailBlastFields.subject]: () => ({
50
+ id: "subject",
51
+ accessorKey: "subject",
52
+ header: t("emailBlasts.list.columns.subject"),
53
+ cell: ({ row }: { row: TableContent<EmailBlastInterface> }) => {
54
+ const blast: EmailBlastInterface = row.original.jsonApiData;
55
+ return <Link href={generateUrl({ page: Modules.EmailBlast, id: blast.id })}>{blast.subject}</Link>;
56
+ },
57
+ enableSorting: false,
58
+ enableHiding: false,
59
+ }),
60
+ [EmailBlastFields.status]: () => ({
61
+ id: "status",
62
+ accessorKey: "status",
63
+ header: t("emailBlasts.list.columns.status"),
64
+ cell: ({ row }: { row: TableContent<EmailBlastInterface> }) => {
65
+ const blast: EmailBlastInterface = row.original.jsonApiData;
66
+ return <Badge variant={STATUS_VARIANTS[blast.status]}>{t(`emailBlasts.list.status.${blast.status}`)}</Badge>;
67
+ },
68
+ enableSorting: false,
69
+ enableHiding: false,
70
+ }),
71
+ [EmailBlastFields.sentAt]: () => ({
72
+ id: "sentAt",
73
+ accessorKey: "sentAt",
74
+ header: t("emailBlasts.list.columns.sentAt"),
75
+ cell: ({ row }: { row: TableContent<EmailBlastInterface> }) => {
76
+ const blast: EmailBlastInterface = row.original.jsonApiData;
77
+ if (!blast.sentAt) return <span className="text-muted-foreground text-xs">—</span>;
78
+ return <span className="text-sm">{blast.sentAt.toLocaleDateString(locale)}</span>;
79
+ },
80
+ enableSorting: false,
81
+ enableHiding: false,
82
+ }),
83
+ [EmailBlastFields.sent]: () =>
84
+ numericColumn(EmailBlastFields.sent, t("emailBlasts.list.columns.sent"), (b) => b.sentCount),
85
+ [EmailBlastFields.failed]: () =>
86
+ numericColumn(EmailBlastFields.failed, t("emailBlasts.list.columns.failed"), (b) => b.failedCount),
87
+ [EmailBlastFields.unsubscribed]: () =>
88
+ numericColumn(
89
+ EmailBlastFields.unsubscribed,
90
+ t("emailBlasts.list.columns.unsubscribed"),
91
+ (b) => b.unsubscribedCount,
92
+ ),
93
+ };
94
+
95
+ const columns = useMemo(() => {
96
+ return params.fields.map((field) => fieldColumnMap[field]?.()).filter((col) => col !== undefined) as ColumnDef<
97
+ TableContent<EmailBlastInterface>
98
+ >[];
99
+ }, [params.fields, fieldColumnMap, t, generateUrl, locale]);
100
+
101
+ return useMemo(() => ({ data: tableData, columns: columns }), [tableData, columns]);
102
+ };
103
+
104
+ registerTableGenerator("email-blasts", useEmailBlastTableStructure);
@@ -0,0 +1,4 @@
1
+ export * from "./EmailBlastModule";
2
+ export * from "./EmailBlastDeliveryModule";
3
+ export * from "./EmailBlastAudienceModule";
4
+ export * from "./data";
@@ -12,6 +12,7 @@ export * from "./billing/stripe-usage";
12
12
  export * from "./billing/stripe-promotion-code";
13
13
  export * from "./company";
14
14
  export * from "./content";
15
+ export * from "./email-blast";
15
16
  export * from "./feature";
16
17
  export * from "./module";
17
18
  export * from "./notification";