@carlonicora/nextjs-jsonapi 5.1.3 → 5.3.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 (173) hide show
  1. package/dist/{BlockNoteEditor-KO6YFNIU.js → BlockNoteEditor-UJ6363LC.js} +19 -19
  2. package/dist/{BlockNoteEditor-KO6YFNIU.js.map → BlockNoteEditor-UJ6363LC.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-66FJUDL4.mjs → BlockNoteEditor-WTSQANKI.mjs} +4 -4
  4. package/dist/{BlockNoteViewer-LK22AE7Z.js → BlockNoteViewer-EL4JFB6P.js} +3 -3
  5. package/dist/{BlockNoteViewer-LK22AE7Z.js.map → BlockNoteViewer-EL4JFB6P.js.map} +1 -1
  6. package/dist/{BlockNoteViewer-SUK7OMWH.mjs → BlockNoteViewer-JVKWJZ6J.mjs} +2 -2
  7. package/dist/auth.interface-CSzJ1oP1.d.ts +64 -0
  8. package/dist/auth.interface-DD4uQv2K.d.mts +64 -0
  9. package/dist/billing/index.js +441 -441
  10. package/dist/billing/index.mjs +3 -3
  11. package/dist/{chunk-SZQNUGDG.js → chunk-3BAM23VA.js} +1005 -929
  12. package/dist/chunk-3BAM23VA.js.map +1 -0
  13. package/dist/{chunk-OKUGTXDN.mjs → chunk-6WU74VFU.mjs} +121 -3
  14. package/dist/chunk-6WU74VFU.mjs.map +1 -0
  15. package/dist/{chunk-VVJDWQEI.js → chunk-CRI4T47Y.js} +790 -179
  16. package/dist/chunk-CRI4T47Y.js.map +1 -0
  17. package/dist/{chunk-TO6IYGML.js → chunk-EO75CV76.js} +126 -8
  18. package/dist/chunk-EO75CV76.js.map +1 -0
  19. package/dist/{chunk-KL7CAQP3.mjs → chunk-FNTQQFLH.mjs} +2 -2
  20. package/dist/{chunk-OCJEXAGE.mjs → chunk-JQWI62NV.mjs} +617 -6
  21. package/dist/chunk-JQWI62NV.mjs.map +1 -0
  22. package/dist/{chunk-5JADF7ZA.mjs → chunk-MIZTWBPX.mjs} +171 -95
  23. package/dist/chunk-MIZTWBPX.mjs.map +1 -0
  24. package/dist/{chunk-XLVRAHUK.mjs → chunk-QKZUE5OF.mjs} +2 -2
  25. package/dist/{chunk-67NJY7SM.js → chunk-QQQE3N2X.js} +3 -3
  26. package/dist/{chunk-67NJY7SM.js.map → chunk-QQQE3N2X.js.map} +1 -1
  27. package/dist/{chunk-XCHXOCEJ.js → chunk-XGTQWBDJ.js} +4 -4
  28. package/dist/{chunk-XCHXOCEJ.js.map → chunk-XGTQWBDJ.js.map} +1 -1
  29. package/dist/client/index.d.mts +6 -39
  30. package/dist/client/index.d.ts +6 -39
  31. package/dist/client/index.js +4 -4
  32. package/dist/client/index.mjs +3 -3
  33. package/dist/components/index.d.mts +19 -5
  34. package/dist/components/index.d.ts +19 -5
  35. package/dist/components/index.js +6 -4
  36. package/dist/components/index.js.map +1 -1
  37. package/dist/components/index.mjs +5 -3
  38. package/dist/contexts/index.js +4 -4
  39. package/dist/contexts/index.mjs +3 -3
  40. package/dist/core/index.d.mts +214 -8
  41. package/dist/core/index.d.ts +214 -8
  42. package/dist/core/index.js +30 -2
  43. package/dist/core/index.js.map +1 -1
  44. package/dist/core/index.mjs +29 -1
  45. package/dist/email-automation-enrollment.fields-BZvF14qx.d.mts +82 -0
  46. package/dist/email-automation-enrollment.fields-Cp3VMzFq.d.ts +82 -0
  47. package/dist/email-automations/index.css +30 -0
  48. package/dist/email-automations/index.css.map +1 -0
  49. package/dist/email-automations/index.d.mts +303 -0
  50. package/dist/email-automations/index.d.ts +303 -0
  51. package/dist/email-automations/index.js +1705 -0
  52. package/dist/email-automations/index.js.map +1 -0
  53. package/dist/email-automations/index.mjs +1705 -0
  54. package/dist/email-automations/index.mjs.map +1 -0
  55. package/dist/features/analytics/index.js +122 -122
  56. package/dist/features/analytics/index.mjs +4 -4
  57. package/dist/features/analytics/server.js +4 -4
  58. package/dist/features/analytics/server.mjs +2 -2
  59. package/dist/features/help/index.js +37 -37
  60. package/dist/features/help/index.mjs +3 -3
  61. package/dist/features/tokenusage/index.js +125 -125
  62. package/dist/features/tokenusage/index.mjs +4 -4
  63. package/dist/index.d.mts +5 -4
  64. package/dist/index.d.ts +5 -4
  65. package/dist/index.js +41 -3
  66. package/dist/index.js.map +1 -1
  67. package/dist/index.mjs +41 -3
  68. package/dist/{s3.service-C-4YpEMu.d.ts → s3.service-CY-jgOAM.d.ts} +1 -1
  69. package/dist/{s3.service-dVj-nxMl.d.mts → s3.service-Sri0QP7Z.d.mts} +1 -1
  70. package/dist/server/index.d.mts +2 -2
  71. package/dist/server/index.d.ts +2 -2
  72. package/dist/server/index.js +3 -3
  73. package/dist/server/index.mjs +2 -2
  74. package/dist/types-70acd-8C.d.mts +39 -0
  75. package/dist/types-l8SFleXi.d.ts +39 -0
  76. package/dist/{waitlist.config-kPfjImle.d.mts → waitlist.config-Cagmgm7-.d.mts} +7 -0
  77. package/dist/{waitlist.config-kPfjImle.d.ts → waitlist.config-Cagmgm7-.d.ts} +7 -0
  78. package/messages/en.json +132 -1
  79. package/messages/it.json +132 -1
  80. package/package.json +11 -1
  81. package/scripts/validate-messages.mjs +1 -0
  82. package/src/components/forms/FormFieldWrapper.tsx +1 -1
  83. package/src/components/forms/FormTextarea.tsx +2 -1
  84. package/src/components/index.ts +1 -0
  85. package/src/core/index.ts +8 -0
  86. package/src/core/registry/ModuleRegistry.ts +5 -0
  87. package/src/email-automations/index.ts +5 -0
  88. package/src/features/auth/components/buttons/GoogleSignInButton.tsx +8 -4
  89. package/src/features/auth/components/forms/Register.tsx +20 -8
  90. package/src/features/auth/data/auth.interface.ts +2 -0
  91. package/src/features/auth/data/auth.ts +10 -0
  92. package/src/features/email-automation/EmailAutomationEnrollmentModule.ts +11 -0
  93. package/src/features/email-automation/EmailAutomationModule.ts +30 -0
  94. package/src/features/email-automation/EmailAutomationStepModule.ts +11 -0
  95. package/src/features/email-automation/EmailAutomationTriggerModule.ts +11 -0
  96. package/src/features/email-automation/components/__tests__/EmailAutomationEditor.spec.tsx +104 -0
  97. package/src/features/email-automation/components/canvas/EmailAutomationActiveSwitch.tsx +49 -0
  98. package/src/features/email-automation/components/canvas/EmailAutomationCanvas.tsx +474 -0
  99. package/src/features/email-automation/components/canvas/EmailAutomationCanvasInspector.tsx +89 -0
  100. package/src/features/email-automation/components/canvas/EmailAutomationCanvasTab.tsx +32 -0
  101. package/src/features/email-automation/components/canvas/__tests__/buildCanvasGraph.spec.ts +67 -0
  102. package/src/features/email-automation/components/canvas/buildCanvasGraph.ts +141 -0
  103. package/src/features/email-automation/components/canvas/edges/DelayEdge.tsx +68 -0
  104. package/src/features/email-automation/components/canvas/nodes/EmailStepNode.tsx +43 -0
  105. package/src/features/email-automation/components/canvas/nodes/TriggerNode.tsx +23 -0
  106. package/src/features/email-automation/components/canvas/shared/CanvasToolbar.tsx +53 -0
  107. package/src/features/email-automation/components/canvas/shared/EmptyCanvas.tsx +11 -0
  108. package/src/features/email-automation/components/canvas/shared/__tests__/autoLayout.spec.ts +42 -0
  109. package/src/features/email-automation/components/canvas/shared/__tests__/longPress.spec.ts +51 -0
  110. package/src/features/email-automation/components/canvas/shared/autoLayout.ts +55 -0
  111. package/src/features/email-automation/components/canvas/shared/index.ts +4 -0
  112. package/src/features/email-automation/components/canvas/shared/longPress.ts +57 -0
  113. package/src/features/email-automation/components/containers/EmailAutomationContainer.tsx +80 -0
  114. package/src/features/email-automation/components/containers/EmailAutomationsListContainer.tsx +27 -0
  115. package/src/features/email-automation/components/forms/EmailAutomationEditor.tsx +153 -0
  116. package/src/features/email-automation/components/forms/EmailStepEditor.tsx +151 -0
  117. package/src/features/email-automation/components/index.ts +15 -0
  118. package/src/features/email-automation/components/lists/EmailAutomationRecipientsList.tsx +90 -0
  119. package/src/features/email-automation/components/lists/EmailAutomationsList.tsx +76 -0
  120. package/src/features/email-automation/components/unsubscribe/EmailUnsubscribe.tsx +99 -0
  121. package/src/features/email-automation/components/unsubscribe/__tests__/EmailUnsubscribe.spec.tsx +55 -0
  122. package/src/features/email-automation/contexts/EmailAutomationContext.tsx +175 -0
  123. package/src/features/email-automation/data/EmailAutomation.ts +87 -0
  124. package/src/features/email-automation/data/EmailAutomationEnrollment.ts +105 -0
  125. package/src/features/email-automation/data/EmailAutomationEnrollmentInterface.ts +18 -0
  126. package/src/features/email-automation/data/EmailAutomationEnrollmentService.ts +31 -0
  127. package/src/features/email-automation/data/EmailAutomationInterface.ts +22 -0
  128. package/src/features/email-automation/data/EmailAutomationService.ts +106 -0
  129. package/src/features/email-automation/data/EmailAutomationStep.ts +104 -0
  130. package/src/features/email-automation/data/EmailAutomationStepInterface.ts +23 -0
  131. package/src/features/email-automation/data/EmailAutomationStepService.ts +80 -0
  132. package/src/features/email-automation/data/EmailAutomationTrigger.ts +28 -0
  133. package/src/features/email-automation/data/EmailAutomationTriggerInterface.ts +6 -0
  134. package/src/features/email-automation/data/__tests__/email-automation-models.spec.ts +103 -0
  135. package/src/features/email-automation/data/email-automation-enrollment.fields.ts +8 -0
  136. package/src/features/email-automation/data/email-automation.fields.ts +9 -0
  137. package/src/features/email-automation/data/index.ts +13 -0
  138. package/src/features/email-automation/hooks/index.ts +2 -0
  139. package/src/features/email-automation/hooks/useEmailAutomationEnrollmentTableStructure.tsx +126 -0
  140. package/src/features/email-automation/hooks/useEmailAutomationTableStructure.tsx +119 -0
  141. package/src/features/email-automation/index.ts +5 -0
  142. package/src/features/signup-source/SignupSourceCapture.tsx +27 -0
  143. package/src/features/signup-source/__tests__/signup-source.spec.ts +161 -0
  144. package/src/features/signup-source/index.ts +9 -0
  145. package/src/features/signup-source/signup-source.ts +135 -0
  146. package/src/features/waitlist/components/forms/WaitlistForm.tsx +2 -0
  147. package/src/features/waitlist/components/forms/WaitlistQuestionnaireRenderer.tsx +15 -13
  148. package/src/features/waitlist/components/lists/WaitlistList.tsx +60 -22
  149. package/src/features/waitlist/components/sections/WaitlistHeroSection.tsx +6 -5
  150. package/src/features/waitlist/config/waitlist.config.ts +23 -0
  151. package/src/features/waitlist/data/Waitlist.ts +51 -3
  152. package/src/features/waitlist/data/WaitlistInterface.ts +9 -1
  153. package/src/features/waitlist/data/WaitlistService.ts +3 -2
  154. package/src/features/waitlist/data/__tests__/Waitlist.spec.ts +82 -0
  155. package/src/features/waitlist/data/__tests__/WaitlistService.spec.ts +48 -0
  156. package/src/features/waitlist/data/waitlist.fields.ts +1 -0
  157. package/src/features/waitlist/hooks/__tests__/formatSignupSource.spec.ts +24 -0
  158. package/src/features/waitlist/hooks/formatSignupSource.ts +14 -0
  159. package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +26 -14
  160. package/src/hooks/__tests__/usePageTracker.spec.tsx +16 -10
  161. package/src/index.ts +3 -0
  162. package/dist/auth.interface-CeFCz9NU.d.mts +0 -34
  163. package/dist/auth.interface-D1R7VVky.d.ts +0 -34
  164. package/dist/chunk-5JADF7ZA.mjs.map +0 -1
  165. package/dist/chunk-OCJEXAGE.mjs.map +0 -1
  166. package/dist/chunk-OKUGTXDN.mjs.map +0 -1
  167. package/dist/chunk-SZQNUGDG.js.map +0 -1
  168. package/dist/chunk-TO6IYGML.js.map +0 -1
  169. package/dist/chunk-VVJDWQEI.js.map +0 -1
  170. /package/dist/{BlockNoteEditor-66FJUDL4.mjs.map → BlockNoteEditor-WTSQANKI.mjs.map} +0 -0
  171. /package/dist/{BlockNoteViewer-SUK7OMWH.mjs.map → BlockNoteViewer-JVKWJZ6J.mjs.map} +0 -0
  172. /package/dist/{chunk-KL7CAQP3.mjs.map → chunk-FNTQQFLH.mjs.map} +0 -0
  173. /package/dist/{chunk-XLVRAHUK.mjs.map → chunk-QKZUE5OF.mjs.map} +0 -0
package/messages/en.json CHANGED
@@ -736,6 +736,15 @@
736
736
  }
737
737
  }
738
738
  },
739
+ "canvas": {
740
+ "toolbar": {
741
+ "fitView": "Fit to view",
742
+ "tidy": "Tidy",
743
+ "tidyTooltip": "Clear manual positions and re-run auto-layout",
744
+ "zoomIn": "Zoom in",
745
+ "zoomOut": "Zoom out"
746
+ }
747
+ },
739
748
  "common": {
740
749
  "actions": {
741
750
  "submitting": "Submitting..."
@@ -906,6 +915,114 @@
906
915
  },
907
916
  "news": "Recent Relevant Contents"
908
917
  },
918
+ "emailAutomations": {
919
+ "admin": {
920
+ "editAutomation": "Edit automation",
921
+ "empty": "No email automations yet.",
922
+ "newAutomation": "New automation",
923
+ "title": "Email automations"
924
+ },
925
+ "canvas": {
926
+ "addEmail": "Add email",
927
+ "chainError": "That connection would break the sequence.",
928
+ "deleteEmail": "Delete email",
929
+ "deleteEmailConfirm": "Delete this email? The sequence closes around it and anyone waiting on it moves to the next email.",
930
+ "emptyBody": "Right-click the canvas or drag from the trigger to add the first one.",
931
+ "emptyTitle": "No emails yet",
932
+ "hoursAfterEntering": "hours after entering the automation",
933
+ "hoursAfterPrevious": "hours after the previous email",
934
+ "noTrigger": "No trigger",
935
+ "orphan": "not in the sequence",
936
+ "sent": "sent {count}"
937
+ },
938
+ "fields": {
939
+ "exitTrigger": {
940
+ "label": "Exit trigger"
941
+ },
942
+ "isActive": {
943
+ "help": "Off holds everyone in place; overdue emails go out when you switch it back on.",
944
+ "label": "Active"
945
+ },
946
+ "name": {
947
+ "error": "Name is required",
948
+ "label": "Name"
949
+ },
950
+ "replyTo": {
951
+ "error": "Enter a valid email address",
952
+ "label": "Reply-to"
953
+ },
954
+ "trigger": {
955
+ "label": "Trigger",
956
+ "none": "— none —"
957
+ }
958
+ },
959
+ "list": {
960
+ "columns": {
961
+ "active": "Active",
962
+ "completed": "Done",
963
+ "name": "Name",
964
+ "status": "Status",
965
+ "steps": "Emails",
966
+ "trigger": "Trigger",
967
+ "unsubscribed": "Left"
968
+ },
969
+ "status": {
970
+ "off": "Off",
971
+ "on": "On"
972
+ }
973
+ },
974
+ "recipients": {
975
+ "columns": {
976
+ "currentStep": "Next email",
977
+ "email": "Email",
978
+ "name": "Name",
979
+ "nextSendAt": "Due",
980
+ "sentCount": "Sent",
981
+ "status": "Status"
982
+ },
983
+ "empty": "Nobody has entered this automation yet.",
984
+ "status": {
985
+ "active": "Active",
986
+ "all": "All",
987
+ "completed": "Completed",
988
+ "exited": "Exited",
989
+ "failed": "Failed",
990
+ "unsubscribed": "Unsubscribed"
991
+ }
992
+ },
993
+ "step": {
994
+ "editTitle": "Edit email",
995
+ "fields": {
996
+ "body": {
997
+ "error": "Body is required",
998
+ "label": "Body (Markdown)"
999
+ },
1000
+ "delayHours": {
1001
+ "label": "Delay"
1002
+ },
1003
+ "subject": {
1004
+ "error": "Subject is required",
1005
+ "label": "Subject"
1006
+ }
1007
+ },
1008
+ "title": "New email",
1009
+ "unsubscribeHint": "The unsubscribe links are always added below the body.",
1010
+ "variablesHint": "Variables:"
1011
+ },
1012
+ "tabs": {
1013
+ "canvas": "Canvas",
1014
+ "recipients": "Recipients"
1015
+ },
1016
+ "unsubscribe": {
1017
+ "alsoStopAll": "Also stop all marketing emails",
1018
+ "invalidBody": "This link is not valid.",
1019
+ "invalidTitle": "Link not valid",
1020
+ "loading": "Updating your email preferences...",
1021
+ "stoppedAll": "You will not receive any more marketing emails from us.",
1022
+ "stoppedAutomation": "You will not receive more emails from {name}.",
1023
+ "title": "You are unsubscribed"
1024
+ }
1025
+ },
909
1026
  "entities": {
910
1027
  "assistants": "{count, plural, =1 {Assistant} other {Assistants}}",
911
1028
  "companies": "{count, plural, =1 {Company} other {Companies}}",
@@ -1600,6 +1717,7 @@
1600
1717
  "actions": "Actions",
1601
1718
  "email": "Email",
1602
1719
  "questionnaire": "Questionnaire",
1720
+ "source": "Source",
1603
1721
  "status": "Status",
1604
1722
  "submitted": "Submitted"
1605
1723
  },
@@ -1609,13 +1727,25 @@
1609
1727
  "questionnaire": {
1610
1728
  "view_answers": "View answers"
1611
1729
  },
1730
+ "source": {
1731
+ "direct": "Direct",
1732
+ "fields": {
1733
+ "sourceLandingPath": "Landing page",
1734
+ "sourceRef": "Ref",
1735
+ "sourceReferrer": "Referring site",
1736
+ "sourceUtmCampaign": "UTM campaign",
1737
+ "sourceUtmMedium": "UTM medium",
1738
+ "sourceUtmSource": "UTM source"
1739
+ }
1740
+ },
1612
1741
  "status": {
1613
1742
  "confirmed": "Confirmed",
1614
1743
  "invited": "Invited",
1615
1744
  "pending": "Pending",
1616
1745
  "registered": "Registered"
1617
1746
  },
1618
- "title": "Waitlist Management"
1747
+ "title": "Waitlist Management",
1748
+ "total": "Total"
1619
1749
  },
1620
1750
  "buttons": {
1621
1751
  "join": "Join Waitlist",
@@ -1632,6 +1762,7 @@
1632
1762
  "success_title": "Email Confirmed!"
1633
1763
  },
1634
1764
  "invite": {
1765
+ "email_locked_hint": "Invitations work only for the email they were sent to.",
1635
1766
  "error_expired": "This invitation has expired",
1636
1767
  "error_invalid": "Invalid invitation code",
1637
1768
  "error_validation_failed": "Failed to validate invitation",
package/messages/it.json CHANGED
@@ -736,6 +736,15 @@
736
736
  }
737
737
  }
738
738
  },
739
+ "canvas": {
740
+ "toolbar": {
741
+ "fitView": "Adatta alla vista",
742
+ "tidy": "Riordina",
743
+ "tidyTooltip": "Cancella le posizioni manuali e rifai il layout automatico",
744
+ "zoomIn": "Ingrandisci",
745
+ "zoomOut": "Riduci"
746
+ }
747
+ },
739
748
  "common": {
740
749
  "actions": {
741
750
  "submitting": "Invio in corso..."
@@ -906,6 +915,114 @@
906
915
  },
907
916
  "news": "Contenuti recenti pertinenti"
908
917
  },
918
+ "emailAutomations": {
919
+ "admin": {
920
+ "editAutomation": "Modifica automazione",
921
+ "empty": "Nessuna automazione email.",
922
+ "newAutomation": "Nuova automazione",
923
+ "title": "Automazioni email"
924
+ },
925
+ "canvas": {
926
+ "addEmail": "Aggiungi email",
927
+ "chainError": "Questo collegamento romperebbe la sequenza.",
928
+ "deleteEmail": "Elimina email",
929
+ "deleteEmailConfirm": "Eliminare questa email? La sequenza si richiude e chi la stava aspettando passa all'email successiva.",
930
+ "emptyBody": "Fai clic destro sul canvas o trascina dall'evento per aggiungere la prima.",
931
+ "emptyTitle": "Ancora nessuna email",
932
+ "hoursAfterEntering": "ore dopo l'ingresso nell'automazione",
933
+ "hoursAfterPrevious": "ore dopo l'email precedente",
934
+ "noTrigger": "Nessun evento",
935
+ "orphan": "fuori dalla sequenza",
936
+ "sent": "inviate {count}"
937
+ },
938
+ "fields": {
939
+ "exitTrigger": {
940
+ "label": "Evento di uscita"
941
+ },
942
+ "isActive": {
943
+ "help": "Spenta, tiene tutti fermi dove sono; le email in ritardo partono quando la riaccendi.",
944
+ "label": "Attiva"
945
+ },
946
+ "name": {
947
+ "error": "Il nome è obbligatorio",
948
+ "label": "Nome"
949
+ },
950
+ "replyTo": {
951
+ "error": "Inserisci un indirizzo email valido",
952
+ "label": "Rispondi a"
953
+ },
954
+ "trigger": {
955
+ "label": "Evento di ingresso",
956
+ "none": "— nessuno —"
957
+ }
958
+ },
959
+ "list": {
960
+ "columns": {
961
+ "active": "Attivi",
962
+ "completed": "Completati",
963
+ "name": "Nome",
964
+ "status": "Stato",
965
+ "steps": "Email",
966
+ "trigger": "Evento",
967
+ "unsubscribed": "Disiscritti"
968
+ },
969
+ "status": {
970
+ "off": "Spenta",
971
+ "on": "Attiva"
972
+ }
973
+ },
974
+ "recipients": {
975
+ "columns": {
976
+ "currentStep": "Prossima email",
977
+ "email": "Email",
978
+ "name": "Nome",
979
+ "nextSendAt": "In programma",
980
+ "sentCount": "Inviate",
981
+ "status": "Stato"
982
+ },
983
+ "empty": "Nessuno è ancora entrato in questa automazione.",
984
+ "status": {
985
+ "active": "Attivi",
986
+ "all": "Tutti",
987
+ "completed": "Completati",
988
+ "exited": "Usciti",
989
+ "failed": "Non riusciti",
990
+ "unsubscribed": "Disiscritti"
991
+ }
992
+ },
993
+ "step": {
994
+ "editTitle": "Modifica email",
995
+ "fields": {
996
+ "body": {
997
+ "error": "Il testo è obbligatorio",
998
+ "label": "Testo (Markdown)"
999
+ },
1000
+ "delayHours": {
1001
+ "label": "Attesa"
1002
+ },
1003
+ "subject": {
1004
+ "error": "L'oggetto è obbligatorio",
1005
+ "label": "Oggetto"
1006
+ }
1007
+ },
1008
+ "title": "Nuova email",
1009
+ "unsubscribeHint": "I link per disiscriversi vengono sempre aggiunti sotto il testo.",
1010
+ "variablesHint": "Variabili:"
1011
+ },
1012
+ "tabs": {
1013
+ "canvas": "Canvas",
1014
+ "recipients": "Destinatari"
1015
+ },
1016
+ "unsubscribe": {
1017
+ "alsoStopAll": "Interrompi anche tutte le email promozionali",
1018
+ "invalidBody": "Questo link non è valido.",
1019
+ "invalidTitle": "Link non valido",
1020
+ "loading": "Aggiornamento delle tue preferenze email in corso...",
1021
+ "stoppedAll": "Non riceverai più nessuna email promozionale da noi.",
1022
+ "stoppedAutomation": "Non riceverai altre email da {name}.",
1023
+ "title": "Disiscrizione completata"
1024
+ }
1025
+ },
909
1026
  "entities": {
910
1027
  "assistants": "{count, plural, =1 {Assistente} other {Assistenti}}",
911
1028
  "companies": "{count, plural, =1 {Azienda} other {Aziende}}",
@@ -1600,6 +1717,7 @@
1600
1717
  "actions": "Azioni",
1601
1718
  "email": "Email",
1602
1719
  "questionnaire": "Questionario",
1720
+ "source": "Provenienza",
1603
1721
  "status": "Stato",
1604
1722
  "submitted": "Inviato"
1605
1723
  },
@@ -1609,13 +1727,25 @@
1609
1727
  "questionnaire": {
1610
1728
  "view_answers": "Vedi le risposte"
1611
1729
  },
1730
+ "source": {
1731
+ "direct": "Diretto",
1732
+ "fields": {
1733
+ "sourceLandingPath": "Pagina d'arrivo",
1734
+ "sourceRef": "Ref",
1735
+ "sourceReferrer": "Sito di provenienza",
1736
+ "sourceUtmCampaign": "UTM campaign",
1737
+ "sourceUtmMedium": "UTM medium",
1738
+ "sourceUtmSource": "UTM source"
1739
+ }
1740
+ },
1612
1741
  "status": {
1613
1742
  "confirmed": "Confermato",
1614
1743
  "invited": "Invitato",
1615
1744
  "pending": "In attesa",
1616
1745
  "registered": "Registrato"
1617
1746
  },
1618
- "title": "Gestione lista d'attesa"
1747
+ "title": "Gestione lista d'attesa",
1748
+ "total": "Totale"
1619
1749
  },
1620
1750
  "buttons": {
1621
1751
  "join": "Iscriviti alla lista d'attesa",
@@ -1632,6 +1762,7 @@
1632
1762
  "success_title": "Email confermata!"
1633
1763
  },
1634
1764
  "invite": {
1765
+ "email_locked_hint": "Gli inviti valgono solo per l'email a cui sono stati mandati.",
1635
1766
  "error_expired": "Questo invito è scaduto",
1636
1767
  "error_invalid": "Codice d'invito non valido",
1637
1768
  "error_validation_failed": "Impossibile verificare l'invito",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carlonicora/nextjs-jsonapi",
3
- "version": "5.1.3",
3
+ "version": "5.3.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",
@@ -118,6 +123,7 @@
118
123
  "@stripe/react-stripe-js": ">=6.8.0",
119
124
  "@stripe/stripe-js": ">=9.13.0",
120
125
  "@tanstack/react-table": ">=8.0.0",
126
+ "@xyflow/react": ">=12.0.0",
121
127
  "next": ">=16.4.0",
122
128
  "next-intl": ">=4.0.0",
123
129
  "react": ">=19.3.0",
@@ -134,6 +140,9 @@
134
140
  "@tanstack/react-table": {
135
141
  "optional": true
136
142
  },
143
+ "@xyflow/react": {
144
+ "optional": true
145
+ },
137
146
  "next-intl": {
138
147
  "optional": true
139
148
  },
@@ -220,6 +229,7 @@
220
229
  "@vitejs/plugin-react": "^6.1.2",
221
230
  "@vitest/coverage-v8": "^5.0.3",
222
231
  "@vitest/ui": "^5.0.3",
232
+ "@xyflow/react": "^12.12.0",
223
233
  "conventional-changelog-conventionalcommits": "9.3.1",
224
234
  "eslint": "^9.39.5",
225
235
  "eslint-config-prettier": "^10.1.8",
@@ -41,6 +41,7 @@ export const APP_PROVIDED_KEYS = [
41
41
  // meaning, not an app's domain. Each entry carries its reason.
42
42
  const APP_WORD_EXEMPT_KEYS = new Map([
43
43
  ["analytics.admin.dimensions.campaign", "the utm_campaign dimension of web analytics"],
44
+ ["waitlist.admin.source.fields.sourceUtmCampaign", "the utm_campaign tag of a waitlist sign-up source"],
44
45
  ["help.modes.how-to", 'Italian "pratiche" = practical (how-to guides), not a case file'],
45
46
  ["howto.howToType.how-to", 'Italian "pratica" = practical (how-to guide), not a case file'],
46
47
  ]);
@@ -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
  }) {
@@ -34,6 +34,7 @@ export * from "../features/feature/components";
34
34
  export * from "../features/notification/components";
35
35
  export * from "../features/onboarding/components";
36
36
  export * from "../features/referral/components";
37
+ export { SignupSourceCapture } from "../features/signup-source/SignupSourceCapture";
37
38
  export * from "../features/role/components";
38
39
  export * from "../features/user/components";
39
40
  export * from "../features/oauth/components";
package/src/core/index.ts CHANGED
@@ -67,6 +67,14 @@ export * from "../features/content/content.module";
67
67
  export * from "../features/content/data";
68
68
  export * from "../features/how-to/HowToModule";
69
69
  export * from "../features/how-to/data";
70
+ export * from "../features/email-automation/EmailAutomationModule";
71
+ export * from "../features/email-automation/EmailAutomationStepModule";
72
+ export * from "../features/email-automation/EmailAutomationEnrollmentModule";
73
+ export * from "../features/email-automation/EmailAutomationTriggerModule";
74
+ // Email automation data (models, services, interfaces, fields): server-safe, so a
75
+ // server page can wrap EmailAutomationService.findOne in cache(). The client-marked
76
+ // ./email-automations entry carries only components, contexts and hooks.
77
+ export * from "../features/email-automation/data";
70
78
  export * from "../features/handbook/HandbookPageModule";
71
79
  export * from "../features/handbook/HandbookSectionModule";
72
80
  export * from "../features/handbook/HandbookThreadMessageModule";
@@ -41,6 +41,11 @@ export interface FoundationModuleDefinitions {
41
41
  // Waitlist modules
42
42
  Waitlist: ModuleWithPermissions;
43
43
  WaitlistStats: ModuleWithPermissions;
44
+ // Email automation modules
45
+ EmailAutomation: ModuleWithPermissions;
46
+ EmailAutomationStep: ModuleWithPermissions;
47
+ EmailAutomationEnrollment: ModuleWithPermissions;
48
+ EmailAutomationTrigger: ModuleWithPermissions;
44
49
  // Referral modules
45
50
  Referral: ModuleWithPermissions;
46
51
  ReferralStats: ModuleWithPermissions;
@@ -0,0 +1,5 @@
1
+ // Email automation components - separate entry point to avoid loading @xyflow/react globally
2
+ export * from "../features/email-automation/components";
3
+ export * from "../features/email-automation/contexts/EmailAutomationContext";
4
+ export * from "../features/email-automation/components/canvas/shared";
5
+ export * from "../features/email-automation/hooks";
@@ -4,23 +4,27 @@ import { useTranslations } from "next-intl";
4
4
  import { getPublicApiUrl } from "../../../../client/config";
5
5
  import { isGoogleAuthEnabled } from "../../../../login";
6
6
  import { Button, Link } from "../../../../shadcnui";
7
+ import { SignupSource } from "../../../signup-source/signup-source";
7
8
 
8
9
  interface GoogleSignInButtonProps {
9
10
  referralCode?: string | null;
11
+ source?: SignupSource;
10
12
  }
11
13
 
12
- export function GoogleSignInButton({ referralCode }: GoogleSignInButtonProps) {
14
+ export function GoogleSignInButton({ referralCode, source }: GoogleSignInButtonProps) {
13
15
  const t = useTranslations();
14
16
 
15
17
  if (!isGoogleAuthEnabled()) {
16
18
  return null;
17
19
  }
18
20
 
19
- // Build OAuth URL with referral parameter if available
21
+ // Build OAuth URL with referral and sign-up source parameters if available
20
22
  const buildGoogleOAuthUrl = (): string => {
21
23
  const baseUrl = `${getPublicApiUrl()}auth/google`;
22
- if (!referralCode) return baseUrl;
23
- return `${baseUrl}?referral=${encodeURIComponent(referralCode)}`;
24
+ const params: string[] = [];
25
+ if (referralCode) params.push(`referral=${encodeURIComponent(referralCode)}`);
26
+ if (source) params.push(`source=${encodeURIComponent(JSON.stringify(source))}`);
27
+ return params.length ? `${baseUrl}?${params.join("&")}` : baseUrl;
24
28
  };
25
29
 
26
30
  return (
@@ -22,6 +22,7 @@ import {
22
22
  Form,
23
23
  Link,
24
24
  } from "../../../../shadcnui";
25
+ import { getSignupSource, SignupSource } from "../../../signup-source/signup-source";
25
26
  import { WaitlistService } from "../../../waitlist/data/WaitlistService";
26
27
  import { useAuthContext } from "../../contexts";
27
28
  import { AuthService } from "../../data/auth.service";
@@ -48,12 +49,17 @@ function clearReferralCode(): void {
48
49
  document.cookie = `${REFERRAL_COOKIE_NAME}=; path=/; max-age=0; SameSite=Lax`;
49
50
  }
50
51
 
51
- // Helper function to build OAuth URLs with both invite and referral codes
52
- function buildOAuthQueryParams(inviteCode?: string | null, referralCode?: string | null): string {
53
- const params = new URLSearchParams();
54
- if (inviteCode) params.set("invite", inviteCode);
55
- if (referralCode) params.set("referral", referralCode);
56
- return params.toString() ? `?${params.toString()}` : "";
52
+ // Helper function to build OAuth URLs with invite code, referral code and sign-up source
53
+ function buildOAuthQueryParams(
54
+ inviteCode?: string | null,
55
+ referralCode?: string | null,
56
+ source?: SignupSource,
57
+ ): string {
58
+ const params: string[] = [];
59
+ if (inviteCode) params.push(`invite=${encodeURIComponent(inviteCode)}`);
60
+ if (referralCode) params.push(`referral=${encodeURIComponent(referralCode)}`);
61
+ if (source) params.push(`source=${encodeURIComponent(JSON.stringify(source))}`);
62
+ return params.length ? `?${params.join("&")}` : "";
57
63
  }
58
64
 
59
65
  export default function Register() {
@@ -73,9 +79,12 @@ export default function Register() {
73
79
 
74
80
  // Store referral code from cookie on mount (must use useState to trigger re-render for OAuth links)
75
81
  const [referralCode, setReferralCode] = useState<string | null>(null);
82
+ // Sign-up source (memory or cookie) read on mount for the OAuth links
83
+ const [signupSource, setSignupSource] = useState<SignupSource | undefined>(undefined);
76
84
  useEffect(() => {
77
85
  const code = getReferralCode();
78
86
  setReferralCode(code);
87
+ setSignupSource(getSignupSource());
79
88
  }, []);
80
89
 
81
90
  const formSchema = z.object({
@@ -152,6 +161,7 @@ export default function Register() {
152
161
  marketingConsentAt: values.marketingConsent ? new Date().toISOString() : null,
153
162
  inviteCode: inviteCode ?? undefined,
154
163
  referralCode: referralCode ?? undefined,
164
+ source: getSignupSource(),
155
165
  };
156
166
 
157
167
  await AuthService.register(payload);
@@ -267,6 +277,8 @@ export default function Register() {
267
277
  id="email"
268
278
  name={t(`common.fields.email.label`)}
269
279
  placeholder={t(`common.fields.email.placeholder`)}
280
+ disabled={inviteValidated}
281
+ description={inviteValidated ? t("waitlist.invite.email_locked_hint") : undefined}
270
282
  />
271
283
  <FormPassword
272
284
  isRequired
@@ -296,7 +308,7 @@ export default function Register() {
296
308
  <div className="space-y-2">
297
309
  {isGoogleAuthEnabled() && (
298
310
  <Link
299
- href={`${getPublicApiUrl()}auth/google${buildOAuthQueryParams(inviteCode, referralCode)}`}
311
+ href={`${getPublicApiUrl()}auth/google${buildOAuthQueryParams(inviteCode, referralCode, signupSource)}`}
300
312
  className="flex w-full"
301
313
  >
302
314
  <Button
@@ -328,7 +340,7 @@ export default function Register() {
328
340
  )}
329
341
  {isDiscordAuthEnabled() && (
330
342
  <Link
331
- href={`${getPublicApiUrl()}auth/discord${buildOAuthQueryParams(inviteCode, referralCode)}`}
343
+ href={`${getPublicApiUrl()}auth/discord${buildOAuthQueryParams(inviteCode, referralCode, signupSource)}`}
332
344
  className="flex w-full"
333
345
  >
334
346
  <Button className="w-full" variant="outline" type="button">
@@ -1,4 +1,5 @@
1
1
  import { ApiDataInterface } from "../../../core";
2
+ import { SignupSource } from "../../signup-source/signup-source";
2
3
  import { UserInterface } from "../../user";
3
4
 
4
5
  export type AuthInput = {
@@ -14,6 +15,7 @@ export type AuthInput = {
14
15
  marketingConsentAt?: string | null;
15
16
  inviteCode?: string;
16
17
  referralCode?: string;
18
+ source?: SignupSource;
17
19
  };
18
20
 
19
21
  export type AuthQuery = {
@@ -52,6 +52,16 @@ export class Auth extends AbstractApiData implements AuthInterface {
52
52
  if (data.inviteCode !== undefined) response.data.attributes.inviteCode = data.inviteCode;
53
53
  if (data.referralCode !== undefined) response.data.attributes.referralCode = data.referralCode;
54
54
 
55
+ if (data.source !== undefined) {
56
+ const attributes = response.data.attributes;
57
+ if (data.source.sourceRef !== undefined) attributes.sourceRef = data.source.sourceRef;
58
+ if (data.source.sourceUtmSource !== undefined) attributes.sourceUtmSource = data.source.sourceUtmSource;
59
+ if (data.source.sourceUtmMedium !== undefined) attributes.sourceUtmMedium = data.source.sourceUtmMedium;
60
+ if (data.source.sourceUtmCampaign !== undefined) attributes.sourceUtmCampaign = data.source.sourceUtmCampaign;
61
+ if (data.source.sourceReferrer !== undefined) attributes.sourceReferrer = data.source.sourceReferrer;
62
+ if (data.source.sourceLandingPath !== undefined) attributes.sourceLandingPath = data.source.sourceLandingPath;
63
+ }
64
+
55
65
  return response;
56
66
  }
57
67
  }
@@ -0,0 +1,11 @@
1
+ import { UsersIcon } from "lucide-react";
2
+ import { ModuleFactory } from "../../permissions";
3
+ import { EmailAutomationEnrollment } from "./data/EmailAutomationEnrollment";
4
+
5
+ export const EmailAutomationEnrollmentModule = (factory: ModuleFactory) =>
6
+ factory({
7
+ pageUrl: undefined,
8
+ name: "email-automation-enrollments",
9
+ model: EmailAutomationEnrollment,
10
+ icon: UsersIcon,
11
+ });
@@ -0,0 +1,30 @@
1
+ import { MailIcon } from "lucide-react";
2
+ import { createJsonApiInclusion } from "../../core";
3
+ import { ModuleFactory } from "../../permissions";
4
+ import { EmailAutomation } from "./data/EmailAutomation";
5
+
6
+ export const EmailAutomationModule = (factory: ModuleFactory) =>
7
+ factory({
8
+ moduleId: "3f6c1a2e-7b94-4d1e-8c55-9a0b2e4f6d71",
9
+ pageUrl: "/administration/email-automations",
10
+ name: "email-automations",
11
+ model: EmailAutomation,
12
+ icon: MailIcon,
13
+ inclusions: {
14
+ lists: {
15
+ fields: [
16
+ createJsonApiInclusion("email-automations", [
17
+ "name",
18
+ "trigger",
19
+ "exitTrigger",
20
+ "replyTo",
21
+ "isActive",
22
+ "stepCount",
23
+ "activeCount",
24
+ "completedCount",
25
+ "unsubscribedCount",
26
+ ]),
27
+ ],
28
+ },
29
+ },
30
+ });