@carlonicora/nextjs-jsonapi 5.4.0 → 5.6.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-4YCAIEPT.js → BlockNoteEditor-AE3ZTEEH.js} +20 -20
  2. package/dist/{BlockNoteEditor-4YCAIEPT.js.map → BlockNoteEditor-AE3ZTEEH.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-YQL2SGFR.mjs → BlockNoteEditor-NEYG5L6W.mjs} +5 -5
  4. package/dist/{BlockNoteViewer-AEJKJ27Q.mjs → BlockNoteViewer-3OUT5UFY.mjs} +3 -3
  5. package/dist/{BlockNoteViewer-YL3J7J2Y.js → BlockNoteViewer-4CFKQM7U.js} +4 -4
  6. package/dist/{BlockNoteViewer-YL3J7J2Y.js.map → BlockNoteViewer-4CFKQM7U.js.map} +1 -1
  7. package/dist/billing/index.d.mts +186 -25
  8. package/dist/billing/index.d.ts +186 -25
  9. package/dist/billing/index.js +1149 -588
  10. package/dist/billing/index.js.map +1 -1
  11. package/dist/billing/index.mjs +786 -225
  12. package/dist/billing/index.mjs.map +1 -1
  13. package/dist/{chunk-5PPG6PL7.js → chunk-3I5AAJXV.js} +3 -3
  14. package/dist/{chunk-5PPG6PL7.js.map → chunk-3I5AAJXV.js.map} +1 -1
  15. package/dist/{chunk-YW4ZCEWB.js → chunk-53BAULDP.js} +4 -4
  16. package/dist/{chunk-YW4ZCEWB.js.map → chunk-53BAULDP.js.map} +1 -1
  17. package/dist/{chunk-XCHSX3VC.mjs → chunk-BTPUYUQ3.mjs} +2 -2
  18. package/dist/{chunk-WC63PJSC.mjs → chunk-EYA2F6AN.mjs} +756 -24
  19. package/dist/chunk-EYA2F6AN.mjs.map +1 -0
  20. package/dist/{chunk-NFBOSPNJ.js → chunk-HKZAESS6.js} +954 -222
  21. package/dist/chunk-HKZAESS6.js.map +1 -0
  22. package/dist/{chunk-6DC2WVVB.mjs → chunk-KQHBONL4.mjs} +2 -2
  23. package/dist/{chunk-3NLAXVIN.js → chunk-LQ4DK4JF.js} +1988 -1089
  24. package/dist/chunk-LQ4DK4JF.js.map +1 -0
  25. package/dist/{chunk-YNCQSWQW.js → chunk-RL3PL3KV.js} +8 -8
  26. package/dist/{chunk-YNCQSWQW.js.map → chunk-RL3PL3KV.js.map} +1 -1
  27. package/dist/{chunk-EUKTBOI2.mjs → chunk-SFYPXCK4.mjs} +3382 -2483
  28. package/dist/chunk-SFYPXCK4.mjs.map +1 -0
  29. package/dist/{chunk-QMYQRPQC.mjs → chunk-Y7RR57EW.mjs} +2 -2
  30. package/dist/client/index.js +5 -5
  31. package/dist/client/index.js.map +1 -1
  32. package/dist/client/index.mjs +4 -4
  33. package/dist/components/editors/server.d.mts +15 -0
  34. package/dist/components/editors/server.d.ts +15 -0
  35. package/dist/components/editors/server.js +38 -0
  36. package/dist/components/editors/server.js.map +1 -0
  37. package/dist/components/editors/server.mjs +38 -0
  38. package/dist/components/editors/server.mjs.map +1 -0
  39. package/dist/components/index.d.mts +33 -10
  40. package/dist/components/index.d.ts +33 -10
  41. package/dist/components/index.js +7 -5
  42. package/dist/components/index.js.map +1 -1
  43. package/dist/components/index.mjs +8 -6
  44. package/dist/contexts/index.d.mts +9 -1
  45. package/dist/contexts/index.d.ts +9 -1
  46. package/dist/contexts/index.js +5 -5
  47. package/dist/contexts/index.mjs +4 -4
  48. package/dist/core/index.d.mts +242 -3
  49. package/dist/core/index.d.ts +242 -3
  50. package/dist/core/index.js +36 -4
  51. package/dist/core/index.js.map +1 -1
  52. package/dist/core/index.mjs +35 -3
  53. package/dist/email-automations/index.d.mts +3 -0
  54. package/dist/email-automations/index.d.ts +3 -0
  55. package/dist/email-automations/index.js +99 -95
  56. package/dist/email-automations/index.js.map +1 -1
  57. package/dist/email-automations/index.mjs +30 -26
  58. package/dist/email-automations/index.mjs.map +1 -1
  59. package/dist/features/analytics/index.d.mts +11 -4
  60. package/dist/features/analytics/index.d.ts +11 -4
  61. package/dist/features/analytics/index.js +127 -135
  62. package/dist/features/analytics/index.js.map +1 -1
  63. package/dist/features/analytics/index.mjs +16 -24
  64. package/dist/features/analytics/index.mjs.map +1 -1
  65. package/dist/features/analytics/server.d.mts +5 -2
  66. package/dist/features/analytics/server.d.ts +5 -2
  67. package/dist/features/analytics/server.js +7 -7
  68. package/dist/features/analytics/server.js.map +1 -1
  69. package/dist/features/analytics/server.mjs +5 -5
  70. package/dist/features/analytics/server.mjs.map +1 -1
  71. package/dist/features/blog/index.d.mts +12 -2
  72. package/dist/features/blog/index.d.ts +12 -2
  73. package/dist/features/blog/index.js +206 -195
  74. package/dist/features/blog/index.js.map +1 -1
  75. package/dist/features/blog/index.mjs +71 -60
  76. package/dist/features/blog/index.mjs.map +1 -1
  77. package/dist/features/blog/server.d.mts +4 -1
  78. package/dist/features/blog/server.d.ts +4 -1
  79. package/dist/features/blog/server.js +49 -21
  80. package/dist/features/blog/server.js.map +1 -1
  81. package/dist/features/blog/server.mjs +36 -8
  82. package/dist/features/blog/server.mjs.map +1 -1
  83. package/dist/features/help/index.js +38 -38
  84. package/dist/features/help/index.mjs +4 -4
  85. package/dist/features/tokenusage/index.d.mts +1 -3
  86. package/dist/features/tokenusage/index.d.ts +1 -3
  87. package/dist/features/tokenusage/index.js +127 -135
  88. package/dist/features/tokenusage/index.js.map +1 -1
  89. package/dist/features/tokenusage/index.mjs +10 -18
  90. package/dist/features/tokenusage/index.mjs.map +1 -1
  91. package/dist/{stripe-promotion-code.interface-C3qqh3mi.d.ts → format-options-BtX3o0Yu.d.ts} +104 -1
  92. package/dist/{stripe-promotion-code.interface-ClZ7DxS9.d.mts → format-options-D6c7FNDi.d.mts} +104 -1
  93. package/dist/index.d.mts +2 -2
  94. package/dist/index.d.ts +2 -2
  95. package/dist/index.js +37 -5
  96. package/dist/index.js.map +1 -1
  97. package/dist/index.mjs +36 -4
  98. package/dist/server/index.js +4 -4
  99. package/dist/server/index.mjs +3 -3
  100. package/messages/en.json +125 -3
  101. package/messages/it.json +125 -3
  102. package/package.json +9 -1
  103. package/scripts/validate-messages.mjs +1 -0
  104. package/src/billing/index.ts +1 -0
  105. package/src/client/index.ts +4 -0
  106. package/src/components/editors/server/BlockNoteServerContent.tsx +28 -0
  107. package/src/components/editors/server/__tests__/renderBlockNoteHTML.spec.ts +44 -0
  108. package/src/components/editors/server/renderBlockNoteHTML.ts +26 -0
  109. package/src/components/editors/server-entry.ts +7 -0
  110. package/src/components/index.ts +1 -0
  111. package/src/components/viewers/BlockNoteStaticViewer.tsx +44 -0
  112. package/src/core/index.ts +1 -0
  113. package/src/core/registry/ModuleRegistry.ts +3 -0
  114. package/src/features/administration/components/AdminIndexContainer.tsx +61 -33
  115. package/src/features/administration/components/__tests__/AdminIndexContainer.spec.tsx +61 -1
  116. package/src/features/administration/components/index.ts +2 -1
  117. package/src/features/administration/contexts/AdministrationContext.tsx +11 -3
  118. package/src/features/administration/contexts/__tests__/AdministrationContext.spec.tsx +40 -0
  119. package/src/features/ai-connection/ai-connection.module.ts +2 -0
  120. package/src/features/ai-connection/contexts/AiConnectionsContext.tsx +3 -16
  121. package/src/features/ai-connection/contexts/__tests__/AiConnectionsContext.spec.tsx +33 -0
  122. package/src/features/analytics/__tests__/server-entry.spec.tsx +2 -1
  123. package/src/features/analytics/analytics.modules.ts +22 -5
  124. package/src/features/analytics/components/AnalyticsAdminContainer.tsx +4 -3
  125. package/src/features/analytics/components/AnalyticsAdminPageContainer.tsx +6 -3
  126. package/src/features/analytics/components/AnalyticsJourneyContainer.tsx +4 -3
  127. package/src/features/analytics/components/__tests__/AnalyticsJourneyContainer.spec.tsx +11 -0
  128. package/src/features/analytics/components/__tests__/AnalyticsPageContainers.spec.tsx +12 -1
  129. package/src/features/analytics/components/server/AnalyticsAdminPage.tsx +6 -3
  130. package/src/features/analytics/contexts/AnalyticsAdminContext.tsx +8 -17
  131. package/src/features/analytics/contexts/__tests__/AnalyticsAdminContext.spec.tsx +17 -0
  132. package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +3 -2
  133. package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +4 -3
  134. package/src/features/billing/components/containers/BillingDashboardContainer.tsx +22 -5
  135. package/src/features/billing/components/utils/__tests__/discount.spec.ts +59 -0
  136. package/src/features/billing/components/utils/__tests__/url.spec.ts +32 -0
  137. package/src/features/billing/components/utils/discount.ts +38 -0
  138. package/src/features/billing/components/utils/index.ts +2 -0
  139. package/src/features/billing/components/utils/url.ts +12 -0
  140. package/src/features/billing/contexts/CouponContext.tsx +211 -0
  141. package/src/features/billing/contexts/index.ts +1 -0
  142. package/src/features/billing/i18n-keys.ts +79 -0
  143. package/src/features/billing/stripe-coupon/components/containers/CouponContainer.tsx +38 -0
  144. package/src/features/billing/stripe-coupon/components/containers/index.ts +1 -0
  145. package/src/features/billing/stripe-coupon/components/details/CouponDetails.tsx +69 -0
  146. package/src/features/billing/stripe-coupon/components/details/index.ts +1 -0
  147. package/src/features/billing/stripe-coupon/components/forms/CouponCodeEditor.tsx +79 -0
  148. package/src/features/billing/stripe-coupon/components/forms/CouponCodeFields.tsx +192 -0
  149. package/src/features/billing/stripe-coupon/components/forms/CouponEditor.tsx +333 -0
  150. package/src/features/billing/stripe-coupon/components/forms/CouponRefresher.tsx +45 -0
  151. package/src/features/billing/stripe-coupon/components/forms/index.ts +6 -0
  152. package/src/features/billing/stripe-coupon/components/index.ts +5 -0
  153. package/src/features/billing/stripe-coupon/components/lists/CouponCodesList.tsx +61 -0
  154. package/src/features/billing/stripe-coupon/components/lists/CouponsList.tsx +67 -0
  155. package/src/features/billing/stripe-coupon/components/lists/index.ts +2 -0
  156. package/src/features/billing/stripe-coupon/components/widgets/CouponCodeActions.tsx +80 -0
  157. package/src/features/billing/stripe-coupon/components/widgets/index.ts +1 -0
  158. package/src/features/billing/stripe-coupon/data/__tests__/stripe-coupon-models.spec.ts +114 -0
  159. package/src/features/billing/stripe-coupon/data/index.ts +10 -0
  160. package/src/features/billing/stripe-coupon/data/stripe-coupon-code.fields.ts +8 -0
  161. package/src/features/billing/stripe-coupon/data/stripe-coupon-code.interface.ts +44 -0
  162. package/src/features/billing/stripe-coupon/data/stripe-coupon-code.service.ts +92 -0
  163. package/src/features/billing/stripe-coupon/data/stripe-coupon-code.ts +132 -0
  164. package/src/features/billing/stripe-coupon/data/stripe-coupon-offer.interface.ts +17 -0
  165. package/src/features/billing/stripe-coupon/data/stripe-coupon-offer.ts +70 -0
  166. package/src/features/billing/stripe-coupon/data/stripe-coupon.fields.ts +7 -0
  167. package/src/features/billing/stripe-coupon/data/stripe-coupon.interface.ts +51 -0
  168. package/src/features/billing/stripe-coupon/data/stripe-coupon.service.ts +93 -0
  169. package/src/features/billing/stripe-coupon/data/stripe-coupon.ts +165 -0
  170. package/src/features/billing/stripe-coupon/hooks/useStripeCouponCodeTableStructure.tsx +154 -0
  171. package/src/features/billing/stripe-coupon/hooks/useStripeCouponTableStructure.tsx +113 -0
  172. package/src/features/billing/stripe-coupon/index.ts +5 -0
  173. package/src/features/billing/stripe-coupon/stripe-coupon-code.module.ts +14 -0
  174. package/src/features/billing/stripe-coupon/stripe-coupon-offer.module.ts +10 -0
  175. package/src/features/billing/stripe-coupon/stripe-coupon.module.ts +18 -0
  176. package/src/features/billing/stripe-coupon/utils/__tests__/coupon-places.spec.ts +19 -0
  177. package/src/features/billing/stripe-coupon/utils/__tests__/to-promotion-code-result.spec.ts +41 -0
  178. package/src/features/billing/stripe-coupon/utils/coupon-places.ts +23 -0
  179. package/src/features/billing/stripe-coupon/utils/format-coupon-discount.ts +36 -0
  180. package/src/features/billing/stripe-coupon/utils/index.ts +3 -0
  181. package/src/features/billing/stripe-coupon/utils/to-promotion-code-result.ts +23 -0
  182. package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +24 -8
  183. package/src/features/billing/stripe-price/stripe-price.module.ts +2 -0
  184. package/src/features/billing/stripe-product/components/containers/ProductsListContainer.tsx +21 -6
  185. package/src/features/billing/stripe-product/stripe-product.module.ts +2 -0
  186. package/src/features/billing/stripe-promotion-code/components/PromoCodeInput.tsx +60 -19
  187. package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.interface.ts +2 -0
  188. package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +24 -5
  189. package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +21 -2
  190. package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +1 -1
  191. package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +1 -1
  192. package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +60 -49
  193. package/src/features/billing/stripe-subscription/components/wizards/__tests__/review-card.spec.ts +34 -0
  194. package/src/features/billing/stripe-subscription/components/wizards/review-card.ts +24 -0
  195. package/src/features/billing/stripe-subscription/data/__tests__/stripe-subscription.discount.spec.ts +67 -0
  196. package/src/features/billing/stripe-subscription/data/stripe-subscription.interface.ts +14 -0
  197. package/src/features/billing/stripe-subscription/data/stripe-subscription.service.ts +39 -0
  198. package/src/features/billing/stripe-subscription/data/stripe-subscription.ts +21 -0
  199. package/src/features/billing/stripe-subscription/hooks/__tests__/wizard-payment.spec.ts +48 -0
  200. package/src/features/billing/stripe-subscription/hooks/__tests__/wizard-promotion.spec.ts +132 -0
  201. package/src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts +138 -12
  202. package/src/features/billing/stripe-subscription/hooks/wizard-payment.ts +30 -0
  203. package/src/features/billing/stripe-subscription/hooks/wizard-promotion.ts +54 -0
  204. package/src/features/blog/components/admin/BlogAdminContainer.tsx +13 -6
  205. package/src/features/blog/components/admin/BlogCategoriesList.tsx +1 -1
  206. package/src/features/blog/components/admin/BlogCoverImageField.tsx +1 -1
  207. package/src/features/blog/components/admin/BlogPostContainer.tsx +0 -1
  208. package/src/features/blog/components/admin/BlogPostEditor.tsx +42 -33
  209. package/src/features/blog/components/admin/BlogPostTranslationEditor.tsx +12 -4
  210. package/src/features/blog/components/admin/BlogPostsList.tsx +1 -1
  211. package/src/features/blog/components/public/BlogPostCard.tsx +2 -9
  212. package/src/features/blog/components/public/BlogPostMeta.tsx +2 -2
  213. package/src/features/blog/components/public/BlogPostView.tsx +9 -3
  214. package/src/features/blog/components/public/__tests__/BlogList.spec.tsx +2 -0
  215. package/src/features/blog/components/public/__tests__/BlogPostView.spec.tsx +17 -11
  216. package/src/features/blog/server/fetchers.ts +57 -8
  217. package/src/features/email-automation/components/containers/EmailAutomationsListContainer.tsx +12 -3
  218. package/src/features/rbac/contexts/RbacContext.tsx +4 -7
  219. package/src/features/rbac/contexts/__tests__/RbacContext.spec.tsx +42 -0
  220. package/src/features/tokenusage/components/TokenUsageAdminContainer.tsx +4 -3
  221. package/src/features/tokenusage/contexts/TokenUsageAdminContext.tsx +3 -18
  222. package/src/features/tokenusage/contexts/__tests__/TokenUsageAdminContext.spec.tsx +17 -0
  223. package/src/features/tokenusage/tokenusage-admin.module.ts +4 -3
  224. package/src/features/tokenusage/tokenusage.modules.ts +12 -2
  225. package/src/features/user/contexts/CurrentUserContext.tsx +25 -0
  226. package/src/features/user/contexts/UserContext.tsx +18 -3
  227. package/src/features/user/contexts/__tests__/CurrentUserContext.spec.tsx +121 -2
  228. package/src/features/user/contexts/__tests__/UserContext.spec.tsx +95 -0
  229. package/dist/chunk-3NLAXVIN.js.map +0 -1
  230. package/dist/chunk-EUKTBOI2.mjs.map +0 -1
  231. package/dist/chunk-NFBOSPNJ.js.map +0 -1
  232. package/dist/chunk-WC63PJSC.mjs.map +0 -1
  233. /package/dist/{BlockNoteEditor-YQL2SGFR.mjs.map → BlockNoteEditor-NEYG5L6W.mjs.map} +0 -0
  234. /package/dist/{BlockNoteViewer-AEJKJ27Q.mjs.map → BlockNoteViewer-3OUT5UFY.mjs.map} +0 -0
  235. /package/dist/{chunk-XCHSX3VC.mjs.map → chunk-BTPUYUQ3.mjs.map} +0 -0
  236. /package/dist/{chunk-6DC2WVVB.mjs.map → chunk-KQHBONL4.mjs.map} +0 -0
  237. /package/dist/{chunk-QMYQRPQC.mjs.map → chunk-Y7RR57EW.mjs.map} +0 -0
@@ -13,6 +13,8 @@ export const BILLING_ADMIN_I18N_KEYS = [
13
13
  "billing.admin.products.entity",
14
14
  "billing.admin.products.create",
15
15
  "billing.admin.products.tabs.details",
16
+ "billing.admin.products.tabs.products",
17
+ "billing.admin.products.tabs.codes",
16
18
  "billing.admin.products.empty.title",
17
19
  "billing.admin.products.empty.description",
18
20
  "billing.admin.products.fields.name",
@@ -92,6 +94,74 @@ export const BILLING_ADMIN_I18N_KEYS = [
92
94
  "billing.admin.prices.restore.title",
93
95
  "billing.admin.prices.restore.description",
94
96
  "billing.admin.prices.restore.confirm",
97
+ "billing.admin.coupons.all_products",
98
+ "billing.admin.coupons.codes.automatic",
99
+ "billing.admin.coupons.codes.empty.description",
100
+ "billing.admin.coupons.codes.empty.title",
101
+ "billing.admin.coupons.codes.entity",
102
+ "billing.admin.coupons.codes.never",
103
+ "billing.admin.coupons.codes.title",
104
+ "billing.admin.coupons.discount.amount",
105
+ "billing.admin.coupons.discount.forever",
106
+ "billing.admin.coupons.discount.once",
107
+ "billing.admin.coupons.discount.percent",
108
+ "billing.admin.coupons.discount.repeating",
109
+ "billing.admin.coupons.duration.forever",
110
+ "billing.admin.coupons.duration.once",
111
+ "billing.admin.coupons.duration.repeating",
112
+ "billing.admin.coupons.empty.description",
113
+ "billing.admin.coupons.empty.title",
114
+ "billing.admin.coupons.entity",
115
+ "billing.admin.coupons.errors.amountOff",
116
+ "billing.admin.coupons.errors.durationInMonths",
117
+ "billing.admin.coupons.errors.minimumAmount",
118
+ "billing.admin.coupons.errors.name",
119
+ "billing.admin.coupons.errors.percentOff",
120
+ "billing.admin.coupons.fields.amountOff",
121
+ "billing.admin.coupons.fields.automatic",
122
+ "billing.admin.coupons.fields.code",
123
+ "billing.admin.coupons.fields.codeMaxRedemptions",
124
+ "billing.admin.coupons.fields.codes",
125
+ "billing.admin.coupons.fields.currency",
126
+ "billing.admin.coupons.fields.discount",
127
+ "billing.admin.coupons.fields.discountType",
128
+ "billing.admin.coupons.fields.duration",
129
+ "billing.admin.coupons.fields.durationInMonths",
130
+ "billing.admin.coupons.fields.expiresAt",
131
+ "billing.admin.coupons.fields.first_time_warning",
132
+ "billing.admin.coupons.fields.firstTimeTransaction",
133
+ "billing.admin.coupons.fields.maxRedemptions",
134
+ "billing.admin.coupons.fields.minimumAmount",
135
+ "billing.admin.coupons.fields.name",
136
+ "billing.admin.coupons.fields.percentOff",
137
+ "billing.admin.coupons.fields.places",
138
+ "billing.admin.coupons.fields.products",
139
+ "billing.admin.coupons.fields.redeemBy",
140
+ "billing.admin.coupons.fields.status",
141
+ "billing.admin.coupons.fields.stripeCouponId",
142
+ "billing.admin.coupons.fields.stripeCustomerId",
143
+ "billing.admin.coupons.fields.stripeId",
144
+ "billing.admin.coupons.first_code",
145
+ "billing.admin.coupons.fixed_notice",
146
+ "billing.admin.coupons.help.automatic",
147
+ "billing.admin.coupons.help.products",
148
+ "billing.admin.coupons.placeholders.amountOff",
149
+ "billing.admin.coupons.placeholders.code",
150
+ "billing.admin.coupons.placeholders.durationInMonths",
151
+ "billing.admin.coupons.placeholders.maxRedemptions",
152
+ "billing.admin.coupons.placeholders.minimumAmount",
153
+ "billing.admin.coupons.placeholders.name",
154
+ "billing.admin.coupons.placeholders.percentOff",
155
+ "billing.admin.coupons.placeholders.stripeCouponId",
156
+ "billing.admin.coupons.places.limited",
157
+ "billing.admin.coupons.places.no_limit",
158
+ "billing.admin.coupons.places.unlimited",
159
+ "billing.admin.coupons.refresh.title",
160
+ "billing.admin.coupons.status.active",
161
+ "billing.admin.coupons.status.ended",
162
+ "billing.admin.coupons.title",
163
+ "billing.admin.coupons.type.amount_off",
164
+ "billing.admin.coupons.type.percent_off",
95
165
  ] as const;
96
166
 
97
167
  /**
@@ -242,10 +312,12 @@ export const BILLING_UI_I18N_KEYS = [
242
312
  "billing.ui.pricing.selected",
243
313
  "billing.ui.pricing.standard",
244
314
  "billing.ui.promo_code.apply",
315
+ "billing.ui.promo_code.automatic_label",
245
316
  "billing.ui.promo_code.duration_forever",
246
317
  "billing.ui.promo_code.duration_once",
247
318
  "billing.ui.promo_code.duration_repeating",
248
319
  "billing.ui.promo_code.placeholder",
320
+ "billing.ui.promo_code.use_different",
249
321
  "billing.ui.proration.net_due_today",
250
322
  "billing.ui.proration.next_invoice",
251
323
  "billing.ui.proration.title",
@@ -289,8 +361,11 @@ export const BILLING_UI_I18N_KEYS = [
289
361
  "billing.ui.wizard.addons_description",
290
362
  "billing.ui.wizard.back",
291
363
  "billing.ui.wizard.errors.already_subscribed",
364
+ "billing.ui.wizard.errors.discount_sold_out",
365
+ "billing.ui.wizard.errors.discount_unavailable",
292
366
  "billing.ui.wizard.errors.invalid_promo_code",
293
367
  "billing.ui.wizard.errors.payment_method_required",
368
+ "billing.ui.wizard.errors.payment_not_confirmed",
294
369
  "billing.ui.wizard.errors.prepare_review",
295
370
  "billing.ui.wizard.errors.process_subscription",
296
371
  "billing.ui.wizard.errors.validate_promo_code",
@@ -299,8 +374,10 @@ export const BILLING_UI_I18N_KEYS = [
299
374
  "billing.ui.wizard.next_review",
300
375
  "billing.ui.wizard.payment_description",
301
376
  "billing.ui.wizard.review.amount_off",
377
+ "billing.ui.wizard.review.card_on_file",
302
378
  "billing.ui.wizard.review.charge_now",
303
379
  "billing.ui.wizard.review.confirm_change",
380
+ "billing.ui.wizard.review.discount_carries_over",
304
381
  "billing.ui.wizard.review.due_now",
305
382
  "billing.ui.wizard.review.interval_monthly",
306
383
  "billing.ui.wizard.review.interval_one_time",
@@ -317,6 +394,8 @@ export const BILLING_UI_I18N_KEYS = [
317
394
  "billing.ui.wizard.review.subscribe_now",
318
395
  "billing.ui.wizard.review.trial_upgrade",
319
396
  "billing.ui.wizard.review.trial_upgrade_description",
397
+ "billing.ui.wizard.review.trial_upgrade_description_discounted",
398
+ "billing.ui.wizard.review.use_another_card",
320
399
  "billing.ui.wizard.steps.select_plan",
321
400
  "billing.ui.wizard.subscribe_description",
322
401
  ] as const;
@@ -0,0 +1,38 @@
1
+ "use client";
2
+
3
+ import { useTranslations } from "next-intl";
4
+ import { RoundPageContainer } from "../../../../../components";
5
+ import { Modules } from "../../../../../core";
6
+ import { useCouponContext } from "../../../contexts/CouponContext";
7
+ import { CouponDetails } from "../details/CouponDetails";
8
+ import { CouponCodesList } from "../lists/CouponCodesList";
9
+
10
+ export function CouponContainer() {
11
+ const t = useTranslations();
12
+ const { coupon } = useCouponContext();
13
+
14
+ if (!coupon) return null;
15
+
16
+ return (
17
+ <RoundPageContainer
18
+ module={Modules.StripeCoupon}
19
+ id={coupon.id}
20
+ tabs={[
21
+ {
22
+ sectionKey: "details",
23
+ label: t("billing.admin.products.tabs.details"),
24
+ content: <CouponDetails />,
25
+ },
26
+ {
27
+ sectionKey: "codes",
28
+ label: t("billing.admin.coupons.codes.title"),
29
+ // No `fillHeight` and no `fullWidth` on the list: a ContentListTable in
30
+ // a tab keeps its own bordered card and flows with the panel, exactly
31
+ // as PricesList does inside ProductContainer. The codes list reads the
32
+ // page's own CouponProvider, so no nested provider is needed.
33
+ content: <CouponCodesList couponId={coupon.id} />,
34
+ },
35
+ ]}
36
+ />
37
+ );
38
+ }
@@ -0,0 +1 @@
1
+ export * from "./CouponContainer";
@@ -0,0 +1,69 @@
1
+ "use client";
2
+
3
+ import { useTranslations } from "next-intl";
4
+ import { DetailField, SectionHeader } from "../../../../../components";
5
+ import { Badge } from "../../../../../shadcnui";
6
+ import { formatDate, useBillingFormatOptions } from "../../../components/utils";
7
+ import { useCouponContext } from "../../../contexts/CouponContext";
8
+ import { formatCouponDiscount } from "../../utils/format-coupon-discount";
9
+
10
+ export function CouponDetails() {
11
+ const t = useTranslations();
12
+ const fmt = useBillingFormatOptions();
13
+ const { coupon } = useCouponContext();
14
+
15
+ if (!coupon) return null;
16
+
17
+ return (
18
+ <div className="flex w-full flex-col gap-y-4">
19
+ <SectionHeader>{t("billing.admin.products.tabs.details")}</SectionHeader>
20
+ {/* Typography role 10 (helper text). */}
21
+ <p className="text-muted-foreground text-xs/relaxed">{t("billing.admin.coupons.fixed_notice")}</p>
22
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
23
+ <DetailField label={t("billing.admin.coupons.fields.name")} value={coupon.name} />
24
+ <DetailField
25
+ label={t("billing.admin.coupons.fields.status")}
26
+ value={
27
+ coupon.valid ? (
28
+ <Badge variant="softGreen">{t("billing.admin.coupons.status.active")}</Badge>
29
+ ) : (
30
+ <Badge variant="softGray">{t("billing.admin.coupons.status.ended")}</Badge>
31
+ )
32
+ }
33
+ />
34
+ <DetailField label={t("billing.admin.coupons.fields.discount")} value={formatCouponDiscount(coupon, t, fmt)} />
35
+ <DetailField
36
+ label={t("billing.admin.coupons.fields.places")}
37
+ value={
38
+ <span className="tabular-nums">
39
+ {coupon.maxRedemptions !== undefined
40
+ ? t("billing.admin.coupons.places.limited", {
41
+ used: String(coupon.timesRedeemed),
42
+ limit: String(coupon.maxRedemptions),
43
+ })
44
+ : t("billing.admin.coupons.places.unlimited", { used: String(coupon.timesRedeemed) })}
45
+ </span>
46
+ }
47
+ />
48
+ <DetailField
49
+ label={t("billing.admin.coupons.fields.products")}
50
+ value={
51
+ coupon.stripeProductIds.length > 0 ? (
52
+ <span className="tabular-nums">{coupon.stripeProductIds.join(", ")}</span>
53
+ ) : (
54
+ t("billing.admin.coupons.all_products")
55
+ )
56
+ }
57
+ />
58
+ <DetailField
59
+ label={t("billing.admin.coupons.fields.redeemBy")}
60
+ value={coupon.redeemBy ? formatDate(coupon.redeemBy, fmt) : "—"}
61
+ />
62
+ <DetailField
63
+ label={t("billing.admin.coupons.fields.stripeId")}
64
+ value={<span className="tabular-nums">{coupon.stripeCouponId}</span>}
65
+ />
66
+ </div>
67
+ </div>
68
+ );
69
+ }
@@ -0,0 +1 @@
1
+ export * from "./CouponDetails";
@@ -0,0 +1,79 @@
1
+ "use client";
2
+
3
+ import { zodResolver } from "@hookform/resolvers/zod";
4
+ import { useTranslations } from "next-intl";
5
+ import { ReactNode, useCallback, useMemo } from "react";
6
+ import { useForm } from "react-hook-form";
7
+ import { v4 } from "uuid";
8
+ import { z } from "zod";
9
+ import { EditorSheet } from "../../../../../components";
10
+ import { Modules } from "../../../../../core";
11
+ import { useI18nRouter } from "../../../../../i18n";
12
+ import { useCouponContext } from "../../../contexts/CouponContext";
13
+ import { CouponCodeFields, couponCodeDefaults, couponCodeSchema, toCouponCodeInput } from "./CouponCodeFields";
14
+
15
+ export type CouponCodeEditorProps = {
16
+ couponId: string;
17
+ onSuccess?: () => void | Promise<void>;
18
+ trigger?: ReactNode;
19
+ forceShow?: boolean;
20
+ onClose?: () => void;
21
+ dialogOpen?: boolean;
22
+ onDialogOpenChange?: (open: boolean) => void;
23
+ };
24
+
25
+ /**
26
+ * Create-only: Stripe allows changing a promotion code's `active` flag and
27
+ * little else, so a code is never edited here. "Apply automatically" and
28
+ * deactivate / reactivate live on the row (CouponCodeActions).
29
+ */
30
+ function CouponCodeEditorInternal({
31
+ couponId,
32
+ onSuccess,
33
+ trigger,
34
+ forceShow,
35
+ onClose,
36
+ dialogOpen,
37
+ onDialogOpenChange,
38
+ }: CouponCodeEditorProps) {
39
+ const t = useTranslations();
40
+ const router = useI18nRouter();
41
+ const { createCode } = useCouponContext();
42
+
43
+ const formSchema = useMemo(() => z.object({ id: z.uuidv4() }).and(couponCodeSchema(t)), [t]);
44
+
45
+ const getDefaultValues = useCallback(() => ({ id: v4(), ...couponCodeDefaults() }), []);
46
+
47
+ const form = useForm<any>({
48
+ resolver: zodResolver(formSchema as any) as any,
49
+ defaultValues: getDefaultValues(),
50
+ });
51
+
52
+ return (
53
+ <EditorSheet
54
+ form={form}
55
+ entityType={t("billing.admin.coupons.codes.entity")}
56
+ isEdit={false}
57
+ module={Modules.StripeCouponCode}
58
+ size="md"
59
+ // A code has no page of its own: without onSuccess EditorSheet would
60
+ // navigate to `/administration/coupons/<codeId>`, which does not exist.
61
+ // The context's createCode already reloads the coupon and the codes list.
62
+ onSuccess={onSuccess ?? (() => {})}
63
+ onNavigate={(url) => router.push(url)}
64
+ onSubmit={async (values: any) => await createCode({ id: values.id, couponId, ...toCouponCodeInput(values) })}
65
+ onReset={getDefaultValues}
66
+ trigger={trigger}
67
+ forceShow={forceShow}
68
+ onClose={onClose}
69
+ dialogOpen={dialogOpen}
70
+ onDialogOpenChange={onDialogOpenChange}
71
+ >
72
+ <CouponCodeFields form={form} />
73
+ </EditorSheet>
74
+ );
75
+ }
76
+
77
+ export default function CouponCodeEditor(props: CouponCodeEditorProps) {
78
+ return <CouponCodeEditorInternal {...props} />;
79
+ }
@@ -0,0 +1,192 @@
1
+ "use client";
2
+
3
+ import { useTranslations } from "next-intl";
4
+ import { z } from "zod";
5
+ import { FormCheckbox, FormDateTime, FormInput, FormSelect, FormSwitch } from "../../../../../components";
6
+ import { FieldDescription } from "../../../../../shadcnui";
7
+ import { StripeCouponCodeInput } from "../../data/stripe-coupon-code.interface";
8
+
9
+ /**
10
+ * Optional whole number of at least 1. The fields render as
11
+ * `<FormInput type="number">`, which hands back `+""` (0) for a cleared input,
12
+ * so 0, "" and NaN all mean "not set" — every limit here has a floor of 1.
13
+ */
14
+ export const optionalPositiveInt = () =>
15
+ z.preprocess(
16
+ (value) =>
17
+ value === undefined ||
18
+ value === null ||
19
+ value === "" ||
20
+ value === 0 ||
21
+ (typeof value === "number" && Number.isNaN(value))
22
+ ? undefined
23
+ : typeof value === "string"
24
+ ? parseInt(value, 10)
25
+ : value,
26
+ z.number().int().min(1).optional(),
27
+ );
28
+
29
+ /**
30
+ * Optional decimal in MAJOR units, from `<FormInput type="decimal">` (a string
31
+ * that may use a comma as the separator). Converted to minor units on submit,
32
+ * exactly as PriceEditor does (`Math.round(value * 100)`).
33
+ */
34
+ export const optionalDecimal = () =>
35
+ z.preprocess(
36
+ (value) =>
37
+ typeof value === "string"
38
+ ? value.trim() === ""
39
+ ? undefined
40
+ : parseFloat(value.replace(",", "."))
41
+ : value === null || (typeof value === "number" && Number.isNaN(value))
42
+ ? undefined
43
+ : value,
44
+ z.number().optional(),
45
+ );
46
+
47
+ /** Mirrors StripeCouponCodePostAttributesDTO. */
48
+ /**
49
+ * Optional point in time from `<FormDateTime allowEmpty>`, which writes "" when
50
+ * the value is cleared (FormDateTime.tsx:136, :241).
51
+ */
52
+ export const optionalDate = () =>
53
+ z.preprocess((value) => (value === "" || value === null ? undefined : value), z.date().optional());
54
+
55
+ export const couponCodeSchema = (t: (key: string) => string) =>
56
+ z
57
+ .object({
58
+ code: z.string().optional(),
59
+ firstTimeTransaction: z.boolean(),
60
+ minimumAmount: optionalDecimal(),
61
+ minimumAmountCurrency: z.string().min(1),
62
+ stripeCustomerId: z.string().optional(),
63
+ expiresAt: optionalDate(),
64
+ maxRedemptions: optionalPositiveInt(),
65
+ automatic: z.boolean(),
66
+ })
67
+ .superRefine((values, ctx) => {
68
+ if (values.minimumAmount !== undefined && values.minimumAmount <= 0)
69
+ ctx.addIssue({
70
+ code: "custom",
71
+ path: ["minimumAmount"],
72
+ message: t("billing.admin.coupons.errors.minimumAmount"),
73
+ });
74
+ });
75
+
76
+ export type CouponCodeFormValues = z.infer<ReturnType<typeof couponCodeSchema>>;
77
+
78
+ /**
79
+ * Every text/number input starts as "" (never undefined): FormInput spreads
80
+ * `field.value` onto a Base UI input, and undefined → value flips it from
81
+ * uncontrolled to controlled (console warning). The schema preprocessors map
82
+ * "" back to undefined, so empty fields are still sent as absent.
83
+ */
84
+ export const couponCodeDefaults = (): z.input<ReturnType<typeof couponCodeSchema>> => ({
85
+ code: "",
86
+ firstTimeTransaction: false,
87
+ minimumAmount: "",
88
+ minimumAmountCurrency: "eur",
89
+ stripeCustomerId: "",
90
+ expiresAt: undefined,
91
+ maxRedemptions: "",
92
+ automatic: false,
93
+ });
94
+
95
+ /** Form values → the code attributes the models serialise. Unset fields are left out. */
96
+ export const toCouponCodeInput = (values: CouponCodeFormValues): Omit<StripeCouponCodeInput, "id" | "couponId"> => {
97
+ const input: Omit<StripeCouponCodeInput, "id" | "couponId"> = {
98
+ firstTimeTransaction: values.firstTimeTransaction,
99
+ automatic: values.automatic,
100
+ };
101
+ if (values.code?.trim()) input.code = values.code.trim();
102
+ if (values.minimumAmount !== undefined) {
103
+ input.minimumAmount = Math.round(values.minimumAmount * 100);
104
+ input.minimumAmountCurrency = values.minimumAmountCurrency;
105
+ }
106
+ if (values.stripeCustomerId?.trim()) input.stripeCustomerId = values.stripeCustomerId.trim();
107
+ if (values.expiresAt) input.expiresAt = values.expiresAt;
108
+ if (values.maxRedemptions !== undefined) input.maxRedemptions = values.maxRedemptions;
109
+ return input;
110
+ };
111
+
112
+ export const couponCurrencyOptions = [
113
+ { id: "usd", text: "USD ($)" },
114
+ { id: "eur", text: "EUR (€)" },
115
+ { id: "gbp", text: "GBP (£)" },
116
+ ];
117
+
118
+ type CouponCodeFieldsProps = {
119
+ form: any;
120
+ /** "" for CouponCodeEditor, "firstCode." when nested inside CouponEditor. */
121
+ prefix?: string;
122
+ };
123
+
124
+ /** The fields of a customer-facing code, shared by CouponEditor (first code) and CouponCodeEditor. */
125
+ export function CouponCodeFields({ form, prefix = "" }: CouponCodeFieldsProps) {
126
+ const t = useTranslations();
127
+
128
+ return (
129
+ <div className="flex w-full flex-col gap-y-4">
130
+ <FormInput
131
+ form={form}
132
+ id={`${prefix}code`}
133
+ name={t("billing.admin.coupons.fields.code")}
134
+ placeholder={t("billing.admin.coupons.placeholders.code")}
135
+ />
136
+ {/* The warning sits visibly under the option (spec), not in FormCheckbox's
137
+ hover tooltip — same layout as FormSwitch's description. */}
138
+ <div className="flex w-full flex-col">
139
+ <FormCheckbox
140
+ form={form}
141
+ id={`${prefix}firstTimeTransaction`}
142
+ name={t("billing.admin.coupons.fields.firstTimeTransaction")}
143
+ />
144
+ <FieldDescription>{t("billing.admin.coupons.fields.first_time_warning")}</FieldDescription>
145
+ </div>
146
+ <div className="grid grid-cols-2 gap-x-4">
147
+ {/* type="decimal", NOT type="currency": the currency variant hardcodes
148
+ a Euro symbol (FormInput.tsx:105-106), which contradicts the
149
+ currency selector sitting next to it. */}
150
+ <FormInput
151
+ form={form}
152
+ id={`${prefix}minimumAmount`}
153
+ type="decimal"
154
+ name={t("billing.admin.coupons.fields.minimumAmount")}
155
+ placeholder={t("billing.admin.coupons.placeholders.minimumAmount")}
156
+ />
157
+ <FormSelect
158
+ form={form}
159
+ id={`${prefix}minimumAmountCurrency`}
160
+ name={t("billing.admin.coupons.fields.currency")}
161
+ values={couponCurrencyOptions}
162
+ />
163
+ </div>
164
+ <FormInput
165
+ form={form}
166
+ id={`${prefix}stripeCustomerId`}
167
+ name={t("billing.admin.coupons.fields.stripeCustomerId")}
168
+ placeholder="cus_…"
169
+ />
170
+ <FormDateTime
171
+ form={form}
172
+ id={`${prefix}expiresAt`}
173
+ name={t("billing.admin.coupons.fields.expiresAt")}
174
+ minDate={new Date()}
175
+ allowEmpty
176
+ />
177
+ <FormInput
178
+ form={form}
179
+ id={`${prefix}maxRedemptions`}
180
+ type="number"
181
+ name={t("billing.admin.coupons.fields.codeMaxRedemptions")}
182
+ placeholder={t("billing.admin.coupons.placeholders.maxRedemptions")}
183
+ />
184
+ <FormSwitch
185
+ form={form}
186
+ id={`${prefix}automatic`}
187
+ name={t("billing.admin.coupons.fields.automatic")}
188
+ description={t("billing.admin.coupons.help.automatic")}
189
+ />
190
+ </div>
191
+ );
192
+ }