@carlonicora/nextjs-jsonapi 5.2.0 → 5.4.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 (237) hide show
  1. package/dist/{BlockNoteEditor-EGY4ZAAM.js → BlockNoteEditor-4YCAIEPT.js} +23 -22
  2. package/dist/BlockNoteEditor-4YCAIEPT.js.map +1 -0
  3. package/dist/{BlockNoteEditor-2QJVK2HH.mjs → BlockNoteEditor-YQL2SGFR.mjs} +8 -7
  4. package/dist/BlockNoteEditor-YQL2SGFR.mjs.map +1 -0
  5. package/dist/{BlockNoteViewer-6BQXJ4TB.mjs → BlockNoteViewer-AEJKJ27Q.mjs} +2 -2
  6. package/dist/{BlockNoteViewer-I2MMFH3S.js → BlockNoteViewer-YL3J7J2Y.js} +3 -3
  7. package/dist/{BlockNoteViewer-I2MMFH3S.js.map → BlockNoteViewer-YL3J7J2Y.js.map} +1 -1
  8. package/dist/BlogCategoryFields-DO48vOTE.d.mts +14 -0
  9. package/dist/BlogCategoryFields-DO48vOTE.d.ts +14 -0
  10. package/dist/BlogPostInterface-BB_4wcDR.d.mts +124 -0
  11. package/dist/BlogPostInterface-bWAT1rfR.d.ts +124 -0
  12. package/dist/billing/index.js +441 -441
  13. package/dist/billing/index.mjs +3 -3
  14. package/dist/blog-public.types-BbrzsC1c.d.mts +30 -0
  15. package/dist/blog-public.types-BbrzsC1c.d.ts +30 -0
  16. package/dist/{chunk-3VEQ5JZJ.js → chunk-3NLAXVIN.js} +860 -849
  17. package/dist/{chunk-3VEQ5JZJ.js.map → chunk-3NLAXVIN.js.map} +1 -1
  18. package/dist/{chunk-YMUPTXOT.mjs → chunk-3YW35YHQ.mjs} +1 -101
  19. package/dist/chunk-3YW35YHQ.mjs.map +1 -0
  20. package/dist/{chunk-EXICQC4A.js → chunk-5PPG6PL7.js} +3 -3
  21. package/dist/{chunk-EXICQC4A.js.map → chunk-5PPG6PL7.js.map} +1 -1
  22. package/dist/{chunk-NPKFMRH5.mjs → chunk-6DC2WVVB.mjs} +2 -2
  23. package/dist/{chunk-2Z3ZCYXX.mjs → chunk-EUKTBOI2.mjs} +19 -8
  24. package/dist/chunk-EUKTBOI2.mjs.map +1 -0
  25. package/dist/{chunk-THKYH65N.js → chunk-KSYQEKJX.js} +11 -111
  26. package/dist/chunk-KSYQEKJX.js.map +1 -0
  27. package/dist/{chunk-S5MQDWWC.js → chunk-NFBOSPNJ.js} +1451 -176
  28. package/dist/chunk-NFBOSPNJ.js.map +1 -0
  29. package/dist/{chunk-JEZQTZQ4.mjs → chunk-QMYQRPQC.mjs} +2 -2
  30. package/dist/{chunk-HNOWSFM5.mjs → chunk-WC63PJSC.mjs} +1278 -3
  31. package/dist/chunk-WC63PJSC.mjs.map +1 -0
  32. package/dist/chunk-XCHSX3VC.mjs +107 -0
  33. package/dist/chunk-XCHSX3VC.mjs.map +1 -0
  34. package/dist/{chunk-75MLT74B.js → chunk-YNCQSWQW.js} +8 -8
  35. package/dist/{chunk-75MLT74B.js.map → chunk-YNCQSWQW.js.map} +1 -1
  36. package/dist/chunk-YW4ZCEWB.js +107 -0
  37. package/dist/chunk-YW4ZCEWB.js.map +1 -0
  38. package/dist/client/index.d.mts +6 -39
  39. package/dist/client/index.d.ts +6 -39
  40. package/dist/client/index.js +4 -4
  41. package/dist/client/index.mjs +3 -3
  42. package/dist/components/index.d.mts +15 -4
  43. package/dist/components/index.d.ts +15 -4
  44. package/dist/components/index.js +4 -4
  45. package/dist/components/index.mjs +3 -3
  46. package/dist/contexts/index.js +4 -4
  47. package/dist/contexts/index.mjs +3 -3
  48. package/dist/core/index.d.mts +400 -3
  49. package/dist/core/index.d.ts +400 -3
  50. package/dist/core/index.js +64 -2
  51. package/dist/core/index.js.map +1 -1
  52. package/dist/core/index.mjs +63 -1
  53. package/dist/email-automation-enrollment.fields-BZvF14qx.d.mts +82 -0
  54. package/dist/email-automation-enrollment.fields-Cp3VMzFq.d.ts +82 -0
  55. package/dist/email-automations/index.css +30 -0
  56. package/dist/email-automations/index.css.map +1 -0
  57. package/dist/email-automations/index.d.mts +303 -0
  58. package/dist/email-automations/index.d.ts +303 -0
  59. package/dist/email-automations/index.js +1705 -0
  60. package/dist/email-automations/index.js.map +1 -0
  61. package/dist/email-automations/index.mjs +1705 -0
  62. package/dist/email-automations/index.mjs.map +1 -0
  63. package/dist/features/analytics/index.js +122 -122
  64. package/dist/features/analytics/index.mjs +4 -4
  65. package/dist/features/analytics/server.js +6 -5
  66. package/dist/features/analytics/server.js.map +1 -1
  67. package/dist/features/analytics/server.mjs +4 -3
  68. package/dist/features/analytics/server.mjs.map +1 -1
  69. package/dist/features/blog/index.css +30 -0
  70. package/dist/features/blog/index.css.map +1 -0
  71. package/dist/features/blog/index.d.mts +266 -0
  72. package/dist/features/blog/index.d.ts +266 -0
  73. package/dist/features/blog/index.js +1799 -0
  74. package/dist/features/blog/index.js.map +1 -0
  75. package/dist/features/blog/index.mjs +1799 -0
  76. package/dist/features/blog/index.mjs.map +1 -0
  77. package/dist/features/blog/server.d.mts +33 -0
  78. package/dist/features/blog/server.d.ts +33 -0
  79. package/dist/features/blog/server.js +139 -0
  80. package/dist/features/blog/server.js.map +1 -0
  81. package/dist/features/blog/server.mjs +139 -0
  82. package/dist/features/blog/server.mjs.map +1 -0
  83. package/dist/features/help/index.js +37 -37
  84. package/dist/features/help/index.mjs +3 -3
  85. package/dist/features/tokenusage/index.js +125 -125
  86. package/dist/features/tokenusage/index.mjs +4 -4
  87. package/dist/index.d.mts +5 -2
  88. package/dist/index.d.ts +5 -2
  89. package/dist/index.js +65 -3
  90. package/dist/index.js.map +1 -1
  91. package/dist/index.mjs +64 -2
  92. package/dist/{s3.service-CY-jgOAM.d.ts → s3.service-Bz_K-tjz.d.ts} +11 -0
  93. package/dist/{s3.service-Sri0QP7Z.d.mts → s3.service-D5zNCEW3.d.mts} +11 -0
  94. package/dist/server/index.d.mts +1 -1
  95. package/dist/server/index.d.ts +1 -1
  96. package/dist/server/index.js +8 -6
  97. package/dist/server/index.js.map +1 -1
  98. package/dist/server/index.mjs +12 -10
  99. package/dist/types-70acd-8C.d.mts +39 -0
  100. package/dist/types-l8SFleXi.d.ts +39 -0
  101. package/messages/en.json +210 -0
  102. package/messages/it.json +210 -0
  103. package/package.json +21 -1
  104. package/src/components/editors/BlockNoteEditor.tsx +13 -3
  105. package/src/components/forms/FormBlockNote.tsx +4 -0
  106. package/src/components/forms/FormFieldWrapper.tsx +1 -1
  107. package/src/components/forms/FormTextarea.tsx +2 -1
  108. package/src/components/navigations/ContentTitle.tsx +14 -3
  109. package/src/core/index.ts +12 -0
  110. package/src/core/registry/ModuleRegistry.ts +11 -0
  111. package/src/email-automations/index.ts +5 -0
  112. package/src/features/blog/BlogModules.ts +49 -0
  113. package/src/features/blog/components/admin/BlogAdminContainer.tsx +32 -0
  114. package/src/features/blog/components/admin/BlogCategoriesList.tsx +56 -0
  115. package/src/features/blog/components/admin/BlogCategoryEditor.tsx +230 -0
  116. package/src/features/blog/components/admin/BlogCoverImageField.tsx +123 -0
  117. package/src/features/blog/components/admin/BlogLayoutSetting.tsx +86 -0
  118. package/src/features/blog/components/admin/BlogPostComparison.tsx +54 -0
  119. package/src/features/blog/components/admin/BlogPostContainer.tsx +102 -0
  120. package/src/features/blog/components/admin/BlogPostDeleter.tsx +35 -0
  121. package/src/features/blog/components/admin/BlogPostEditor.tsx +207 -0
  122. package/src/features/blog/components/admin/BlogPostPublishButton.tsx +50 -0
  123. package/src/features/blog/components/admin/BlogPostTranslationEditor.tsx +110 -0
  124. package/src/features/blog/components/admin/BlogPostTranslationPanel.tsx +136 -0
  125. package/src/features/blog/components/admin/BlogPostsList.tsx +58 -0
  126. package/src/features/blog/components/admin/BlogTranslationStatus.tsx +104 -0
  127. package/src/features/blog/components/admin/__tests__/BlogPostTranslationPanel.spec.tsx +100 -0
  128. package/src/features/blog/components/admin/__tests__/BlogPostsList.spec.tsx +30 -0
  129. package/src/features/blog/components/admin/index.ts +14 -0
  130. package/src/features/blog/components/public/BlogCategoryChips.tsx +59 -0
  131. package/src/features/blog/components/public/BlogList.tsx +21 -0
  132. package/src/features/blog/components/public/BlogListColumn.tsx +53 -0
  133. package/src/features/blog/components/public/BlogListFeatured.tsx +57 -0
  134. package/src/features/blog/components/public/BlogListParts.tsx +74 -0
  135. package/src/features/blog/components/public/BlogListRail.tsx +62 -0
  136. package/src/features/blog/components/public/BlogPostCard.tsx +132 -0
  137. package/src/features/blog/components/public/BlogPostMeta.tsx +49 -0
  138. package/src/features/blog/components/public/BlogPostView.tsx +74 -0
  139. package/src/features/blog/components/public/BlogSearchBox.tsx +52 -0
  140. package/src/features/blog/components/public/__tests__/BlogList.spec.tsx +173 -0
  141. package/src/features/blog/components/public/__tests__/BlogPostView.spec.tsx +128 -0
  142. package/src/features/blog/components/public/blogPublicHelpers.ts +30 -0
  143. package/src/features/blog/components/public/index.ts +3 -0
  144. package/src/features/blog/contexts/BlogPostContext.tsx +66 -0
  145. package/src/features/blog/contexts/index.ts +1 -0
  146. package/src/features/blog/data/BlogCategory.ts +93 -0
  147. package/src/features/blog/data/BlogCategoryFields.ts +4 -0
  148. package/src/features/blog/data/BlogCategoryInterface.ts +24 -0
  149. package/src/features/blog/data/BlogCategoryService.ts +54 -0
  150. package/src/features/blog/data/BlogCategoryTranslation.ts +90 -0
  151. package/src/features/blog/data/BlogCategoryTranslationInterface.ts +22 -0
  152. package/src/features/blog/data/BlogCategoryTranslationService.ts +30 -0
  153. package/src/features/blog/data/BlogPost.spec.ts +123 -0
  154. package/src/features/blog/data/BlogPost.ts +159 -0
  155. package/src/features/blog/data/BlogPostFields.ts +7 -0
  156. package/src/features/blog/data/BlogPostInterface.ts +39 -0
  157. package/src/features/blog/data/BlogPostService.ts +82 -0
  158. package/src/features/blog/data/BlogPostTranslation.spec.ts +73 -0
  159. package/src/features/blog/data/BlogPostTranslation.ts +110 -0
  160. package/src/features/blog/data/BlogPostTranslationInterface.ts +27 -0
  161. package/src/features/blog/data/BlogPostTranslationService.ts +30 -0
  162. package/src/features/blog/data/BlogSettings.ts +37 -0
  163. package/src/features/blog/data/BlogSettingsInterface.ts +11 -0
  164. package/src/features/blog/data/BlogSettingsService.ts +24 -0
  165. package/src/features/blog/data/index.ts +17 -0
  166. package/src/features/blog/hooks/index.ts +3 -0
  167. package/src/features/blog/hooks/useBlogCategoryNames.ts +34 -0
  168. package/src/features/blog/hooks/useBlogCategoryTableStructure.tsx +84 -0
  169. package/src/features/blog/hooks/useBlogPostTableStructure.tsx +126 -0
  170. package/src/features/blog/index.ts +15 -0
  171. package/src/features/blog/server/fetchers.ts +120 -0
  172. package/src/features/blog/server/toPublic.spec.ts +91 -0
  173. package/src/features/blog/server/toPublic.ts +36 -0
  174. package/src/features/blog/server-entry.ts +17 -0
  175. package/src/features/blog/types/blog-public.types.ts +34 -0
  176. package/src/features/blog/types/blog.constants.ts +40 -0
  177. package/src/features/email-automation/EmailAutomationEnrollmentModule.ts +11 -0
  178. package/src/features/email-automation/EmailAutomationModule.ts +30 -0
  179. package/src/features/email-automation/EmailAutomationStepModule.ts +11 -0
  180. package/src/features/email-automation/EmailAutomationTriggerModule.ts +11 -0
  181. package/src/features/email-automation/components/__tests__/EmailAutomationEditor.spec.tsx +104 -0
  182. package/src/features/email-automation/components/canvas/EmailAutomationActiveSwitch.tsx +49 -0
  183. package/src/features/email-automation/components/canvas/EmailAutomationCanvas.tsx +474 -0
  184. package/src/features/email-automation/components/canvas/EmailAutomationCanvasInspector.tsx +89 -0
  185. package/src/features/email-automation/components/canvas/EmailAutomationCanvasTab.tsx +32 -0
  186. package/src/features/email-automation/components/canvas/__tests__/buildCanvasGraph.spec.ts +67 -0
  187. package/src/features/email-automation/components/canvas/buildCanvasGraph.ts +141 -0
  188. package/src/features/email-automation/components/canvas/edges/DelayEdge.tsx +68 -0
  189. package/src/features/email-automation/components/canvas/nodes/EmailStepNode.tsx +43 -0
  190. package/src/features/email-automation/components/canvas/nodes/TriggerNode.tsx +23 -0
  191. package/src/features/email-automation/components/canvas/shared/CanvasToolbar.tsx +53 -0
  192. package/src/features/email-automation/components/canvas/shared/EmptyCanvas.tsx +11 -0
  193. package/src/features/email-automation/components/canvas/shared/__tests__/autoLayout.spec.ts +42 -0
  194. package/src/features/email-automation/components/canvas/shared/__tests__/longPress.spec.ts +51 -0
  195. package/src/features/email-automation/components/canvas/shared/autoLayout.ts +55 -0
  196. package/src/features/email-automation/components/canvas/shared/index.ts +4 -0
  197. package/src/features/email-automation/components/canvas/shared/longPress.ts +57 -0
  198. package/src/features/email-automation/components/containers/EmailAutomationContainer.tsx +80 -0
  199. package/src/features/email-automation/components/containers/EmailAutomationsListContainer.tsx +27 -0
  200. package/src/features/email-automation/components/forms/EmailAutomationEditor.tsx +153 -0
  201. package/src/features/email-automation/components/forms/EmailStepEditor.tsx +151 -0
  202. package/src/features/email-automation/components/index.ts +15 -0
  203. package/src/features/email-automation/components/lists/EmailAutomationRecipientsList.tsx +90 -0
  204. package/src/features/email-automation/components/lists/EmailAutomationsList.tsx +76 -0
  205. package/src/features/email-automation/components/unsubscribe/EmailUnsubscribe.tsx +99 -0
  206. package/src/features/email-automation/components/unsubscribe/__tests__/EmailUnsubscribe.spec.tsx +55 -0
  207. package/src/features/email-automation/contexts/EmailAutomationContext.tsx +175 -0
  208. package/src/features/email-automation/data/EmailAutomation.ts +87 -0
  209. package/src/features/email-automation/data/EmailAutomationEnrollment.ts +105 -0
  210. package/src/features/email-automation/data/EmailAutomationEnrollmentInterface.ts +18 -0
  211. package/src/features/email-automation/data/EmailAutomationEnrollmentService.ts +31 -0
  212. package/src/features/email-automation/data/EmailAutomationInterface.ts +22 -0
  213. package/src/features/email-automation/data/EmailAutomationService.ts +106 -0
  214. package/src/features/email-automation/data/EmailAutomationStep.ts +104 -0
  215. package/src/features/email-automation/data/EmailAutomationStepInterface.ts +23 -0
  216. package/src/features/email-automation/data/EmailAutomationStepService.ts +80 -0
  217. package/src/features/email-automation/data/EmailAutomationTrigger.ts +28 -0
  218. package/src/features/email-automation/data/EmailAutomationTriggerInterface.ts +6 -0
  219. package/src/features/email-automation/data/__tests__/email-automation-models.spec.ts +103 -0
  220. package/src/features/email-automation/data/email-automation-enrollment.fields.ts +8 -0
  221. package/src/features/email-automation/data/email-automation.fields.ts +9 -0
  222. package/src/features/email-automation/data/index.ts +13 -0
  223. package/src/features/email-automation/hooks/index.ts +2 -0
  224. package/src/features/email-automation/hooks/useEmailAutomationEnrollmentTableStructure.tsx +126 -0
  225. package/src/features/email-automation/hooks/useEmailAutomationTableStructure.tsx +119 -0
  226. package/src/features/email-automation/index.ts +5 -0
  227. package/src/features/s3/data/s3.service.ts +17 -0
  228. package/dist/BlockNoteEditor-2QJVK2HH.mjs.map +0 -1
  229. package/dist/BlockNoteEditor-EGY4ZAAM.js.map +0 -1
  230. package/dist/chunk-2Z3ZCYXX.mjs.map +0 -1
  231. package/dist/chunk-HNOWSFM5.mjs.map +0 -1
  232. package/dist/chunk-S5MQDWWC.js.map +0 -1
  233. package/dist/chunk-THKYH65N.js.map +0 -1
  234. package/dist/chunk-YMUPTXOT.mjs.map +0 -1
  235. /package/dist/{BlockNoteViewer-6BQXJ4TB.mjs.map → BlockNoteViewer-AEJKJ27Q.mjs.map} +0 -0
  236. /package/dist/{chunk-NPKFMRH5.mjs.map → chunk-6DC2WVVB.mjs.map} +0 -0
  237. /package/dist/{chunk-JEZQTZQ4.mjs.map → chunk-QMYQRPQC.mjs.map} +0 -0
@@ -0,0 +1,39 @@
1
+ import { ColumnDef } from '@tanstack/react-table';
2
+ import { P as PageUrl } from './types-bsm8Fp55.js';
3
+ import { D as DataListRetriever } from './useDataListRetriever-WEXL4bqB.js';
4
+
5
+ type TableContent<T> = {
6
+ jsonApiData: T;
7
+ [key: string]: any;
8
+ };
9
+ interface TableStructureGeneratorInterface<T, U> {
10
+ generateTableStructure: (params: {
11
+ t: (key: string) => string;
12
+ generateUrl: (params: {
13
+ page: string | PageUrl | string;
14
+ id?: string;
15
+ }) => string;
16
+ data: T[];
17
+ fields: Array<U>;
18
+ checkedIds?: string[];
19
+ toggleId?: (id: string) => void;
20
+ }) => {
21
+ data: TableContent<T>[];
22
+ columns: ColumnDef<TableContent<T>>[];
23
+ };
24
+ }
25
+ type UseTableStructureHookParams<T, U> = {
26
+ data: T[];
27
+ fields: Array<U>;
28
+ checkedIds?: string[];
29
+ toggleId?: (id: string) => void;
30
+ dataRetriever?: DataListRetriever<T>;
31
+ context?: Record<string, any>;
32
+ };
33
+ type UseTableStructureHookReturn<T> = {
34
+ data: TableContent<T>[];
35
+ columns: ColumnDef<TableContent<T>>[];
36
+ };
37
+ type UseTableStructureHook<T, U> = (params: UseTableStructureHookParams<T, U>) => UseTableStructureHookReturn<T>;
38
+
39
+ export type { TableContent as T, UseTableStructureHook as U, UseTableStructureHookParams as a, UseTableStructureHookReturn as b, TableStructureGeneratorInterface as c };
package/messages/en.json CHANGED
@@ -736,6 +736,108 @@
736
736
  }
737
737
  }
738
738
  },
739
+ "blog": {
740
+ "admin": {
741
+ "title": "Blog",
742
+ "newPost": "New post",
743
+ "editPost": "Edit post",
744
+ "newCategory": "New category",
745
+ "editCategory": "Edit category",
746
+ "empty": "No posts yet. Write the first one.",
747
+ "emptyCategories": "No categories yet."
748
+ },
749
+ "tabs": {
750
+ "posts": "Posts",
751
+ "categories": "Categories",
752
+ "layout": "Layout"
753
+ },
754
+ "fields": {
755
+ "title": "Title",
756
+ "slug": "Slug",
757
+ "slugHelp": "The last part of the post's address. Changing it breaks links that already point to the post.",
758
+ "summary": "Summary",
759
+ "summaryHelp": "Leave it empty to let AI write it from the body.",
760
+ "body": "Body",
761
+ "coverImage": "Cover image",
762
+ "coverImageAlt": "Cover image description",
763
+ "categories": "Categories",
764
+ "originalLanguage": "Original language",
765
+ "name": "Name",
766
+ "description": "Description",
767
+ "image": "Image",
768
+ "status": "Status",
769
+ "publishedAt": "Published",
770
+ "languages": "Languages ready"
771
+ },
772
+ "status": {
773
+ "draft": "Draft",
774
+ "published": "Published",
775
+ "unpublished": "Unpublished"
776
+ },
777
+ "actions": {
778
+ "publish": "Publish",
779
+ "unpublish": "Unpublish",
780
+ "edit": "Edit",
781
+ "translateAgain": "Translate again",
782
+ "compareWithOriginal": "Compare with original",
783
+ "hideComparison": "Hide comparison"
784
+ },
785
+ "translation": {
786
+ "original": "Original",
787
+ "translatedFrom": "Translated from {language} on {date}",
788
+ "editedByHand": "Edited by hand",
789
+ "overwriteWarning": "Editing the original will translate it again and replace these edits.",
790
+ "translating": "Translating…",
791
+ "failed": "Translation failed",
792
+ "pending": "Waiting to translate"
793
+ },
794
+ "layout": {
795
+ "title": "Public list layout",
796
+ "featured": "Featured",
797
+ "featuredDescription": "The newest post as a large hero, then a three-column grid of cards.",
798
+ "column": "Column",
799
+ "columnDescription": "A narrow reading column with one row per post, image beside the text.",
800
+ "rail": "Rail",
801
+ "railDescription": "Search and categories in a side rail, the newest post large, the rest in two columns.",
802
+ "saved": "Layout saved"
803
+ },
804
+ "public": {
805
+ "eyebrow": "Blog",
806
+ "title": "Notes and guides",
807
+ "subtitle": "Ideas, guides and news from the team.",
808
+ "all": "All",
809
+ "search": "Search",
810
+ "searchPlaceholder": "Search posts",
811
+ "loadMore": "Load more",
812
+ "readingTime": "{minutes} min read",
813
+ "updated": "Updated {date}",
814
+ "by": "By {author}",
815
+ "back": "All posts",
816
+ "moreIn": "More in {category}",
817
+ "empty": "No posts yet.",
818
+ "noResults": "No posts match your search."
819
+ },
820
+ "entities": {
821
+ "post": "Post",
822
+ "posts": "Posts",
823
+ "category": "Category",
824
+ "categories": "Categories"
825
+ },
826
+ "errors": {
827
+ "title": "A title is required",
828
+ "name": "A name is required",
829
+ "language": "Choose the original language"
830
+ }
831
+ },
832
+ "canvas": {
833
+ "toolbar": {
834
+ "fitView": "Fit to view",
835
+ "tidy": "Tidy",
836
+ "tidyTooltip": "Clear manual positions and re-run auto-layout",
837
+ "zoomIn": "Zoom in",
838
+ "zoomOut": "Zoom out"
839
+ }
840
+ },
739
841
  "common": {
740
842
  "actions": {
741
843
  "submitting": "Submitting..."
@@ -906,6 +1008,114 @@
906
1008
  },
907
1009
  "news": "Recent Relevant Contents"
908
1010
  },
1011
+ "emailAutomations": {
1012
+ "admin": {
1013
+ "editAutomation": "Edit automation",
1014
+ "empty": "No email automations yet.",
1015
+ "newAutomation": "New automation",
1016
+ "title": "Email automations"
1017
+ },
1018
+ "canvas": {
1019
+ "addEmail": "Add email",
1020
+ "chainError": "That connection would break the sequence.",
1021
+ "deleteEmail": "Delete email",
1022
+ "deleteEmailConfirm": "Delete this email? The sequence closes around it and anyone waiting on it moves to the next email.",
1023
+ "emptyBody": "Right-click the canvas or drag from the trigger to add the first one.",
1024
+ "emptyTitle": "No emails yet",
1025
+ "hoursAfterEntering": "hours after entering the automation",
1026
+ "hoursAfterPrevious": "hours after the previous email",
1027
+ "noTrigger": "No trigger",
1028
+ "orphan": "not in the sequence",
1029
+ "sent": "sent {count}"
1030
+ },
1031
+ "fields": {
1032
+ "exitTrigger": {
1033
+ "label": "Exit trigger"
1034
+ },
1035
+ "isActive": {
1036
+ "help": "Off holds everyone in place; overdue emails go out when you switch it back on.",
1037
+ "label": "Active"
1038
+ },
1039
+ "name": {
1040
+ "error": "Name is required",
1041
+ "label": "Name"
1042
+ },
1043
+ "replyTo": {
1044
+ "error": "Enter a valid email address",
1045
+ "label": "Reply-to"
1046
+ },
1047
+ "trigger": {
1048
+ "label": "Trigger",
1049
+ "none": "— none —"
1050
+ }
1051
+ },
1052
+ "list": {
1053
+ "columns": {
1054
+ "active": "Active",
1055
+ "completed": "Done",
1056
+ "name": "Name",
1057
+ "status": "Status",
1058
+ "steps": "Emails",
1059
+ "trigger": "Trigger",
1060
+ "unsubscribed": "Left"
1061
+ },
1062
+ "status": {
1063
+ "off": "Off",
1064
+ "on": "On"
1065
+ }
1066
+ },
1067
+ "recipients": {
1068
+ "columns": {
1069
+ "currentStep": "Next email",
1070
+ "email": "Email",
1071
+ "name": "Name",
1072
+ "nextSendAt": "Due",
1073
+ "sentCount": "Sent",
1074
+ "status": "Status"
1075
+ },
1076
+ "empty": "Nobody has entered this automation yet.",
1077
+ "status": {
1078
+ "active": "Active",
1079
+ "all": "All",
1080
+ "completed": "Completed",
1081
+ "exited": "Exited",
1082
+ "failed": "Failed",
1083
+ "unsubscribed": "Unsubscribed"
1084
+ }
1085
+ },
1086
+ "step": {
1087
+ "editTitle": "Edit email",
1088
+ "fields": {
1089
+ "body": {
1090
+ "error": "Body is required",
1091
+ "label": "Body (Markdown)"
1092
+ },
1093
+ "delayHours": {
1094
+ "label": "Delay"
1095
+ },
1096
+ "subject": {
1097
+ "error": "Subject is required",
1098
+ "label": "Subject"
1099
+ }
1100
+ },
1101
+ "title": "New email",
1102
+ "unsubscribeHint": "The unsubscribe links are always added below the body.",
1103
+ "variablesHint": "Variables:"
1104
+ },
1105
+ "tabs": {
1106
+ "canvas": "Canvas",
1107
+ "recipients": "Recipients"
1108
+ },
1109
+ "unsubscribe": {
1110
+ "alsoStopAll": "Also stop all marketing emails",
1111
+ "invalidBody": "This link is not valid.",
1112
+ "invalidTitle": "Link not valid",
1113
+ "loading": "Updating your email preferences...",
1114
+ "stoppedAll": "You will not receive any more marketing emails from us.",
1115
+ "stoppedAutomation": "You will not receive more emails from {name}.",
1116
+ "title": "You are unsubscribed"
1117
+ }
1118
+ },
909
1119
  "entities": {
910
1120
  "assistants": "{count, plural, =1 {Assistant} other {Assistants}}",
911
1121
  "companies": "{count, plural, =1 {Company} other {Companies}}",
package/messages/it.json CHANGED
@@ -736,6 +736,108 @@
736
736
  }
737
737
  }
738
738
  },
739
+ "blog": {
740
+ "admin": {
741
+ "title": "Blog",
742
+ "newPost": "Nuovo articolo",
743
+ "editPost": "Modifica articolo",
744
+ "newCategory": "Nuova categoria",
745
+ "editCategory": "Modifica categoria",
746
+ "empty": "Nessun articolo. Scrivi il primo.",
747
+ "emptyCategories": "Nessuna categoria."
748
+ },
749
+ "tabs": {
750
+ "posts": "Articoli",
751
+ "categories": "Categorie",
752
+ "layout": "Impaginazione"
753
+ },
754
+ "fields": {
755
+ "title": "Titolo",
756
+ "slug": "Slug",
757
+ "slugHelp": "L'ultima parte dell'indirizzo dell'articolo. Cambiarlo rompe i link che puntano già all'articolo.",
758
+ "summary": "Sommario",
759
+ "summaryHelp": "Lascialo vuoto e lo scriverà l'AI a partire dal testo.",
760
+ "body": "Testo",
761
+ "coverImage": "Immagine di copertina",
762
+ "coverImageAlt": "Descrizione dell'immagine di copertina",
763
+ "categories": "Categorie",
764
+ "originalLanguage": "Lingua originale",
765
+ "name": "Nome",
766
+ "description": "Descrizione",
767
+ "image": "Immagine",
768
+ "status": "Stato",
769
+ "publishedAt": "Pubblicato",
770
+ "languages": "Lingue pronte"
771
+ },
772
+ "status": {
773
+ "draft": "Bozza",
774
+ "published": "Pubblicato",
775
+ "unpublished": "Ritirato"
776
+ },
777
+ "actions": {
778
+ "publish": "Pubblica",
779
+ "unpublish": "Ritira",
780
+ "edit": "Modifica",
781
+ "translateAgain": "Traduci di nuovo",
782
+ "compareWithOriginal": "Confronta con l'originale",
783
+ "hideComparison": "Nascondi confronto"
784
+ },
785
+ "translation": {
786
+ "original": "Originale",
787
+ "translatedFrom": "Lingua originale: {language}, tradotto il {date}",
788
+ "editedByHand": "Modificato a mano",
789
+ "overwriteWarning": "Modificare l'originale lo farà tradurre di nuovo e sostituirà queste modifiche.",
790
+ "translating": "Traduzione in corso…",
791
+ "failed": "Traduzione non riuscita",
792
+ "pending": "In attesa di traduzione"
793
+ },
794
+ "layout": {
795
+ "title": "Impaginazione dell'elenco pubblico",
796
+ "featured": "In evidenza",
797
+ "featuredDescription": "L'articolo più recente in grande, poi una griglia di schede su tre colonne.",
798
+ "column": "Colonna",
799
+ "columnDescription": "Una colonna di lettura stretta, una riga per articolo con l'immagine accanto al testo.",
800
+ "rail": "Barra laterale",
801
+ "railDescription": "Ricerca e categorie in una barra laterale, l'articolo più recente in grande, gli altri su due colonne.",
802
+ "saved": "Impaginazione salvata"
803
+ },
804
+ "public": {
805
+ "eyebrow": "Blog",
806
+ "title": "Note e guide",
807
+ "subtitle": "Idee, guide e novità dal team.",
808
+ "all": "Tutti",
809
+ "search": "Cerca",
810
+ "searchPlaceholder": "Cerca negli articoli",
811
+ "loadMore": "Carica altri",
812
+ "readingTime": "{minutes} min di lettura",
813
+ "updated": "Aggiornato il {date}",
814
+ "by": "Di {author}",
815
+ "back": "Tutti gli articoli",
816
+ "moreIn": "Altro in {category}",
817
+ "empty": "Ancora nessun articolo.",
818
+ "noResults": "Nessun articolo corrisponde alla ricerca."
819
+ },
820
+ "entities": {
821
+ "post": "Articolo",
822
+ "posts": "Articoli",
823
+ "category": "Categoria",
824
+ "categories": "Categorie"
825
+ },
826
+ "errors": {
827
+ "title": "Il titolo è obbligatorio",
828
+ "name": "Il nome è obbligatorio",
829
+ "language": "Scegli la lingua originale"
830
+ }
831
+ },
832
+ "canvas": {
833
+ "toolbar": {
834
+ "fitView": "Adatta alla vista",
835
+ "tidy": "Riordina",
836
+ "tidyTooltip": "Cancella le posizioni manuali e rifai il layout automatico",
837
+ "zoomIn": "Ingrandisci",
838
+ "zoomOut": "Riduci"
839
+ }
840
+ },
739
841
  "common": {
740
842
  "actions": {
741
843
  "submitting": "Invio in corso..."
@@ -906,6 +1008,114 @@
906
1008
  },
907
1009
  "news": "Contenuti recenti pertinenti"
908
1010
  },
1011
+ "emailAutomations": {
1012
+ "admin": {
1013
+ "editAutomation": "Modifica automazione",
1014
+ "empty": "Nessuna automazione email.",
1015
+ "newAutomation": "Nuova automazione",
1016
+ "title": "Automazioni email"
1017
+ },
1018
+ "canvas": {
1019
+ "addEmail": "Aggiungi email",
1020
+ "chainError": "Questo collegamento romperebbe la sequenza.",
1021
+ "deleteEmail": "Elimina email",
1022
+ "deleteEmailConfirm": "Eliminare questa email? La sequenza si richiude e chi la stava aspettando passa all'email successiva.",
1023
+ "emptyBody": "Fai clic destro sul canvas o trascina dall'evento per aggiungere la prima.",
1024
+ "emptyTitle": "Ancora nessuna email",
1025
+ "hoursAfterEntering": "ore dopo l'ingresso nell'automazione",
1026
+ "hoursAfterPrevious": "ore dopo l'email precedente",
1027
+ "noTrigger": "Nessun evento",
1028
+ "orphan": "fuori dalla sequenza",
1029
+ "sent": "inviate {count}"
1030
+ },
1031
+ "fields": {
1032
+ "exitTrigger": {
1033
+ "label": "Evento di uscita"
1034
+ },
1035
+ "isActive": {
1036
+ "help": "Spenta, tiene tutti fermi dove sono; le email in ritardo partono quando la riaccendi.",
1037
+ "label": "Attiva"
1038
+ },
1039
+ "name": {
1040
+ "error": "Il nome è obbligatorio",
1041
+ "label": "Nome"
1042
+ },
1043
+ "replyTo": {
1044
+ "error": "Inserisci un indirizzo email valido",
1045
+ "label": "Rispondi a"
1046
+ },
1047
+ "trigger": {
1048
+ "label": "Evento di ingresso",
1049
+ "none": "— nessuno —"
1050
+ }
1051
+ },
1052
+ "list": {
1053
+ "columns": {
1054
+ "active": "Attivi",
1055
+ "completed": "Completati",
1056
+ "name": "Nome",
1057
+ "status": "Stato",
1058
+ "steps": "Email",
1059
+ "trigger": "Evento",
1060
+ "unsubscribed": "Disiscritti"
1061
+ },
1062
+ "status": {
1063
+ "off": "Spenta",
1064
+ "on": "Attiva"
1065
+ }
1066
+ },
1067
+ "recipients": {
1068
+ "columns": {
1069
+ "currentStep": "Prossima email",
1070
+ "email": "Email",
1071
+ "name": "Nome",
1072
+ "nextSendAt": "In programma",
1073
+ "sentCount": "Inviate",
1074
+ "status": "Stato"
1075
+ },
1076
+ "empty": "Nessuno è ancora entrato in questa automazione.",
1077
+ "status": {
1078
+ "active": "Attivi",
1079
+ "all": "Tutti",
1080
+ "completed": "Completati",
1081
+ "exited": "Usciti",
1082
+ "failed": "Non riusciti",
1083
+ "unsubscribed": "Disiscritti"
1084
+ }
1085
+ },
1086
+ "step": {
1087
+ "editTitle": "Modifica email",
1088
+ "fields": {
1089
+ "body": {
1090
+ "error": "Il testo è obbligatorio",
1091
+ "label": "Testo (Markdown)"
1092
+ },
1093
+ "delayHours": {
1094
+ "label": "Attesa"
1095
+ },
1096
+ "subject": {
1097
+ "error": "L'oggetto è obbligatorio",
1098
+ "label": "Oggetto"
1099
+ }
1100
+ },
1101
+ "title": "Nuova email",
1102
+ "unsubscribeHint": "I link per disiscriversi vengono sempre aggiunti sotto il testo.",
1103
+ "variablesHint": "Variabili:"
1104
+ },
1105
+ "tabs": {
1106
+ "canvas": "Canvas",
1107
+ "recipients": "Destinatari"
1108
+ },
1109
+ "unsubscribe": {
1110
+ "alsoStopAll": "Interrompi anche tutte le email promozionali",
1111
+ "invalidBody": "Questo link non è valido.",
1112
+ "invalidTitle": "Link non valido",
1113
+ "loading": "Aggiornamento delle tue preferenze email in corso...",
1114
+ "stoppedAll": "Non riceverai più nessuna email promozionale da noi.",
1115
+ "stoppedAutomation": "Non riceverai altre email da {name}.",
1116
+ "title": "Disiscrizione completata"
1117
+ }
1118
+ },
909
1119
  "entities": {
910
1120
  "assistants": "{count, plural, =1 {Assistente} other {Assistenti}}",
911
1121
  "companies": "{count, plural, =1 {Azienda} other {Aziende}}",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carlonicora/nextjs-jsonapi",
3
- "version": "5.2.0",
3
+ "version": "5.4.0",
4
4
  "description": "Next.js JSON:API client with server/client support and caching",
5
5
  "author": "Carlo Nicora",
6
6
  "license": "GPL-3.0-or-later",
@@ -59,6 +59,11 @@
59
59
  "import": "./dist/billing/index.mjs",
60
60
  "require": "./dist/billing/index.js"
61
61
  },
62
+ "./email-automations": {
63
+ "types": "./dist/email-automations/index.d.ts",
64
+ "import": "./dist/email-automations/index.mjs",
65
+ "require": "./dist/email-automations/index.js"
66
+ },
62
67
  "./help": {
63
68
  "types": "./dist/features/help/index.d.ts",
64
69
  "import": "./dist/features/help/index.mjs",
@@ -69,6 +74,16 @@
69
74
  "import": "./dist/features/help/server.mjs",
70
75
  "require": "./dist/features/help/server.js"
71
76
  },
77
+ "./blog": {
78
+ "types": "./dist/features/blog/index.d.ts",
79
+ "import": "./dist/features/blog/index.mjs",
80
+ "require": "./dist/features/blog/index.js"
81
+ },
82
+ "./blog/server": {
83
+ "types": "./dist/features/blog/server.d.ts",
84
+ "import": "./dist/features/blog/server.mjs",
85
+ "require": "./dist/features/blog/server.js"
86
+ },
72
87
  "./tokenusage": {
73
88
  "types": "./dist/features/tokenusage/index.d.ts",
74
89
  "import": "./dist/features/tokenusage/index.mjs",
@@ -118,6 +133,7 @@
118
133
  "@stripe/react-stripe-js": ">=6.8.0",
119
134
  "@stripe/stripe-js": ">=9.13.0",
120
135
  "@tanstack/react-table": ">=8.0.0",
136
+ "@xyflow/react": ">=12.0.0",
121
137
  "next": ">=16.4.0",
122
138
  "next-intl": ">=4.0.0",
123
139
  "react": ">=19.3.0",
@@ -134,6 +150,9 @@
134
150
  "@tanstack/react-table": {
135
151
  "optional": true
136
152
  },
153
+ "@xyflow/react": {
154
+ "optional": true
155
+ },
137
156
  "next-intl": {
138
157
  "optional": true
139
158
  },
@@ -220,6 +239,7 @@
220
239
  "@vitejs/plugin-react": "^6.1.2",
221
240
  "@vitest/coverage-v8": "^5.0.3",
222
241
  "@vitest/ui": "^5.0.3",
242
+ "@xyflow/react": "^12.12.0",
223
243
  "conventional-changelog-conventionalcommits": "9.3.1",
224
244
  "eslint": "^9.39.5",
225
245
  "eslint-config-prettier": "^10.1.8",
@@ -78,6 +78,13 @@ export type BlockNoteEditorProps = {
78
78
  diffContent?: PartialBlock[];
79
79
  placeholder?: string;
80
80
  bordered?: boolean;
81
+ /**
82
+ * Storage folder for pasted images, used instead of the current company's
83
+ * `companies/<companyId>/<type>` folder: the image key becomes
84
+ * `<uploadKeyPrefix>/<id>/<file name>`. For platform content edited by users
85
+ * who belong to no company (the blog). Omitted: the company folder, as before.
86
+ */
87
+ uploadKeyPrefix?: string;
81
88
  inlineContentSpecs?: Record<string, any>;
82
89
  renderOverlays?: (editor: any) => React.ReactNode;
83
90
  enableMentions?: boolean;
@@ -405,6 +412,7 @@ export default function BlockNoteEditor({
405
412
  diffContent,
406
413
  placeholder,
407
414
  bordered,
415
+ uploadKeyPrefix,
408
416
  inlineContentSpecs,
409
417
  renderOverlays,
410
418
  enableMentions,
@@ -547,7 +555,7 @@ export default function BlockNoteEditor({
547
555
 
548
556
  const uploadImage = useCallback(
549
557
  async (file: File): Promise<string> => {
550
- if (!company) {
558
+ if (!company && !uploadKeyPrefix) {
551
559
  errorToast({
552
560
  title: t(`common.errors.upload`),
553
561
  error: t(`common.errors.upload_description`),
@@ -556,7 +564,9 @@ export default function BlockNoteEditor({
556
564
  }
557
565
 
558
566
  const fileType = file.type;
559
- const key = `companies/${company.id}/${type}/${id}/${file.name}`;
567
+ const key = uploadKeyPrefix
568
+ ? `${uploadKeyPrefix}/${id}/${file.name}`
569
+ : `companies/${company!.id}/${type}/${id}/${file.name}`;
560
570
 
561
571
  const s3: S3Interface = await S3Service.getPreSignedUrl({
562
572
  key: key,
@@ -577,7 +587,7 @@ export default function BlockNoteEditor({
577
587
 
578
588
  return signedImage.url;
579
589
  },
580
- [company, id, t],
590
+ [company, id, t, type, uploadKeyPrefix],
581
591
  );
582
592
 
583
593
  // Utility: Remove trailing empty blocks for read-only display
@@ -41,6 +41,7 @@ export function FormBlockNote({
41
41
  aiConfig,
42
42
  aiHandleRef,
43
43
  headerAction,
44
+ uploadKeyPrefix,
44
45
  }: {
45
46
  form: any;
46
47
  id: string;
@@ -74,6 +75,8 @@ export function FormBlockNote({
74
75
  aiHandleRef?: React.MutableRefObject<import("../editors/BlockNoteEditor").BlockNoteAiHandle | null>;
75
76
  /** Rendered on the label row, right-aligned — e.g. a "Suggest impression" button. */
76
77
  headerAction?: React.ReactNode;
78
+ /** See `BlockNoteEditorProps.uploadKeyPrefix`. */
79
+ uploadKeyPrefix?: string;
77
80
  }) {
78
81
  const initialContentRef = useRef<any>(null);
79
82
  const lastEditorContentRef = useRef<any>(undefined);
@@ -133,6 +136,7 @@ export function FormBlockNote({
133
136
  bordered
134
137
  inlineContentSpecs={inlineContentSpecs}
135
138
  renderOverlays={renderOverlays}
139
+ uploadKeyPrefix={uploadKeyPrefix}
136
140
  enableMentions={enableMentions}
137
141
  mentionSearchFn={mentionSearchFn}
138
142
  mentionSearchParams={mentionSearchParams}
@@ -21,7 +21,7 @@ type FormFieldWrapperProps<T extends FieldValues> = {
21
21
  * label built by the caller, so the label row stays one element.
22
22
  */
23
23
  labelAction?: ReactNode;
24
- description?: string;
24
+ description?: ReactNode;
25
25
  isRequired?: boolean;
26
26
  orientation?: "vertical" | "horizontal" | "responsive";
27
27
  className?: string;
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { ReactNode } from "react";
3
4
  import { Textarea } from "../../shadcnui";
4
5
  import { cn } from "../../utils";
5
6
  import { FormFieldWrapper } from "./FormFieldWrapper";
@@ -21,7 +22,7 @@ export function FormTextarea({
21
22
  placeholder?: string;
22
23
  className?: string;
23
24
  testId?: string;
24
- description?: string;
25
+ description?: ReactNode;
25
26
  isRequired?: boolean;
26
27
  textareaRef?: React.RefObject<HTMLTextAreaElement>;
27
28
  }) {
@@ -11,9 +11,20 @@ type TitleProps = {
11
11
  className?: string;
12
12
  module?: ModuleWithPermissions;
13
13
  prioritizeFunctions?: boolean;
14
+ /** Renders the title line as a real heading (public SEO pages). Omitted: a plain div, as before. */
15
+ headingLevel?: 1 | 2;
14
16
  };
15
17
 
16
- export function ContentTitle({ module, type, element, functions, className, prioritizeFunctions }: TitleProps) {
18
+ export function ContentTitle({
19
+ module,
20
+ type,
21
+ element,
22
+ functions,
23
+ className,
24
+ prioritizeFunctions,
25
+ headingLevel,
26
+ }: TitleProps) {
27
+ const ElementTag = headingLevel === 1 ? "h1" : headingLevel === 2 ? "h2" : "div";
17
28
  const [clientFunctions, setClientFunctions] = useState<ReactNode>(null);
18
29
  const [isClient, setIsClient] = useState(false);
19
30
 
@@ -43,9 +54,9 @@ export function ContentTitle({ module, type, element, functions, className, prio
43
54
  </div>
44
55
  )}
45
56
  {/* Element (name) - truncates with ellipsis when space is limited */}
46
- <div className={cn("text-primary text-3xl font-semibold", prioritizeFunctions ? "truncate" : "w-full")}>
57
+ <ElementTag className={cn("text-primary text-3xl font-semibold", prioritizeFunctions ? "truncate" : "w-full")}>
47
58
  {element}
48
- </div>
59
+ </ElementTag>
49
60
  </div>
50
61
  {/* Functions section - doesn't shrink when prioritizeFunctions is true */}
51
62
  {isClient && clientFunctions && (