@carlonicora/nextjs-jsonapi 4.1.2 → 4.2.1

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 (285) hide show
  1. package/dist/{AssistantMessageInterface-CubxuBIJ.d.ts → AssistantMessageInterface-DHJJO_Z_.d.ts} +1 -1
  2. package/dist/{AssistantMessageInterface-sHjI2zPN.d.mts → AssistantMessageInterface-S7oyERTT.d.mts} +1 -1
  3. package/dist/{BlockNoteEditor-VOYKZOP5.mjs → BlockNoteEditor-HXAHDJA7.mjs} +34 -27
  4. package/dist/BlockNoteEditor-HXAHDJA7.mjs.map +1 -0
  5. package/dist/{BlockNoteEditor-BENYTTCZ.js → BlockNoteEditor-LLCZDFLZ.js} +50 -43
  6. package/dist/BlockNoteEditor-LLCZDFLZ.js.map +1 -0
  7. package/dist/{BlockNoteViewer-USLR4Y7C.js → BlockNoteViewer-77CCTC2U.js} +3 -3
  8. package/dist/{BlockNoteViewer-USLR4Y7C.js.map → BlockNoteViewer-77CCTC2U.js.map} +1 -1
  9. package/dist/{BlockNoteViewer-Y2ZM4ESA.mjs → BlockNoteViewer-MDPO6BXN.mjs} +2 -2
  10. package/dist/{auth.interface-Df4vXbNz.d.mts → auth.interface-CeFCz9NU.d.mts} +1 -1
  11. package/dist/{auth.interface-BzI4onI_.d.ts → auth.interface-D1R7VVky.d.ts} +1 -1
  12. package/dist/billing/index.d.mts +40 -6
  13. package/dist/billing/index.d.ts +40 -6
  14. package/dist/billing/index.js +806 -774
  15. package/dist/billing/index.js.map +1 -1
  16. package/dist/billing/index.mjs +494 -462
  17. package/dist/billing/index.mjs.map +1 -1
  18. package/dist/{chunk-6ISF5KNQ.mjs → chunk-2VXDZRYY.mjs} +1103 -816
  19. package/dist/chunk-2VXDZRYY.mjs.map +1 -0
  20. package/dist/{chunk-4BZJ4LAB.js → chunk-4ORLPEK7.js} +136 -104
  21. package/dist/chunk-4ORLPEK7.js.map +1 -0
  22. package/dist/{chunk-ZZ6E33FO.mjs → chunk-6DRSSW5O.mjs} +36 -4
  23. package/dist/chunk-6DRSSW5O.mjs.map +1 -0
  24. package/dist/{chunk-KS6WZP2E.js → chunk-6SMJRAE3.js} +4 -4
  25. package/dist/{chunk-KS6WZP2E.js.map → chunk-6SMJRAE3.js.map} +1 -1
  26. package/dist/{chunk-WQ77CUUZ.js → chunk-GPXYQMOZ.js} +7 -7
  27. package/dist/{chunk-WQ77CUUZ.js.map → chunk-GPXYQMOZ.js.map} +1 -1
  28. package/dist/{chunk-46S3KPLU.mjs → chunk-ILFFC6BG.mjs} +2 -2
  29. package/dist/{chunk-HZVVRIJQ.mjs → chunk-IUS2RHCX.mjs} +2 -2
  30. package/dist/{chunk-F7RNBGHS.js → chunk-KJMMBUOA.js} +3 -3
  31. package/dist/{chunk-F7RNBGHS.js.map → chunk-KJMMBUOA.js.map} +1 -1
  32. package/dist/{chunk-ZO3QQ5CQ.mjs → chunk-MQDR6MEE.mjs} +2 -2
  33. package/dist/{chunk-ORU7KLCY.js → chunk-VDVIUDY5.js} +1445 -1158
  34. package/dist/chunk-VDVIUDY5.js.map +1 -0
  35. package/dist/client/index.d.mts +20 -6
  36. package/dist/client/index.d.ts +20 -6
  37. package/dist/client/index.js +6 -4
  38. package/dist/client/index.js.map +1 -1
  39. package/dist/client/index.mjs +5 -3
  40. package/dist/components/index.d.mts +59 -21
  41. package/dist/components/index.d.ts +59 -21
  42. package/dist/components/index.js +4 -4
  43. package/dist/components/index.mjs +3 -3
  44. package/dist/{config-CEAq4Ozy.d.mts → config-BP2vyddw.d.mts} +5 -0
  45. package/dist/{config-DydDHCfb.d.ts → config-BhW2QrRD.d.ts} +5 -0
  46. package/dist/contexts/index.d.mts +4 -4
  47. package/dist/contexts/index.d.ts +4 -4
  48. package/dist/contexts/index.js +4 -4
  49. package/dist/contexts/index.mjs +3 -3
  50. package/dist/core/index.d.mts +17 -9
  51. package/dist/core/index.d.ts +17 -9
  52. package/dist/core/index.js +4 -2
  53. package/dist/core/index.js.map +1 -1
  54. package/dist/core/index.mjs +3 -1
  55. package/dist/features/analytics/index.d.mts +1 -1
  56. package/dist/features/analytics/index.d.ts +1 -1
  57. package/dist/features/analytics/index.js +122 -122
  58. package/dist/features/analytics/index.mjs +4 -4
  59. package/dist/features/analytics/server.js +4 -4
  60. package/dist/features/analytics/server.mjs +2 -2
  61. package/dist/features/help/index.d.mts +1 -1
  62. package/dist/features/help/index.d.ts +1 -1
  63. package/dist/features/help/index.js +42 -40
  64. package/dist/features/help/index.js.map +1 -1
  65. package/dist/features/help/index.mjs +8 -6
  66. package/dist/features/help/index.mjs.map +1 -1
  67. package/dist/features/tokenusage/index.js +125 -125
  68. package/dist/features/tokenusage/index.mjs +4 -4
  69. package/dist/index.d.mts +7 -7
  70. package/dist/index.d.ts +7 -7
  71. package/dist/index.js +5 -3
  72. package/dist/index.js.map +1 -1
  73. package/dist/index.mjs +4 -2
  74. package/dist/{notification.interface-P_pNfzZQ.d.mts → notification.interface-BB6GPpZY.d.mts} +1 -1
  75. package/dist/{notification.interface-BNCvnVCW.d.ts → notification.interface-CmAGrijJ.d.ts} +1 -1
  76. package/dist/{s3.service-CbUa6a-8.d.ts → s3.service-C-4YpEMu.d.ts} +7 -3
  77. package/dist/{s3.service-DAp8FbuG.d.mts → s3.service-dVj-nxMl.d.mts} +7 -3
  78. package/dist/server/index.d.mts +4 -4
  79. package/dist/server/index.d.ts +4 -4
  80. package/dist/server/index.js +3 -3
  81. package/dist/server/index.mjs +2 -2
  82. package/dist/{useSocket-BHVIyigI.d.ts → useSocket-B0H9pmmH.d.ts} +1 -1
  83. package/dist/{useSocket-B9gTbuCj.d.mts → useSocket-B5RQb6v2.d.mts} +1 -1
  84. package/dist/{user.interface-CrmwJLYW.d.ts → user.interface-C0ZdsBZj.d.ts} +2 -0
  85. package/dist/{user.interface-CSaGWFbg.d.mts → user.interface-CIWGlbqM.d.mts} +2 -0
  86. package/package.json +1 -1
  87. package/src/components/containers/RoundPageContainer.tsx +3 -1
  88. package/src/components/containers/RoundPageContainerTitle.tsx +5 -2
  89. package/src/components/editors/BlockNoteEditor.tsx +31 -15
  90. package/src/components/editors/BlockNoteEditorAiActions.tsx +11 -7
  91. package/src/components/editors/__tests__/{BlockNoteEditorAiActions.test.ts → BlockNoteEditorAiActions.spec.ts} +10 -3
  92. package/src/components/errors/ErrorDetails.tsx +5 -2
  93. package/src/components/forms/CommonAssociationForm.tsx +1 -1
  94. package/src/components/forms/EntityMultiSelector.tsx +6 -2
  95. package/src/components/forms/EntitySelector.tsx +4 -1
  96. package/src/components/forms/FileUploader.tsx +3 -2
  97. package/src/components/forms/MultiFileUploader.tsx +3 -1
  98. package/src/components/navigations/Breadcrumb.tsx +3 -2
  99. package/src/components/navigations/Header.tsx +3 -1
  100. package/src/components/navigations/MobileNavigationBar.tsx +3 -1
  101. package/src/components/navigations/__tests__/Header.spec.tsx +2 -2
  102. package/src/components/tables/__tests__/ContentListTable.spec.tsx +1 -1
  103. package/src/components/tables/cells/cell.id.tsx +13 -2
  104. package/src/core/abstracts/AbstractService.ts +3 -3
  105. package/src/core/abstracts/ClientAbstractService.ts +3 -2
  106. package/src/features/assistant-action/components/ApprovalActionCard.tsx +74 -5
  107. package/src/features/assistant-action/components/__tests__/ApprovalActionCard.spec.tsx +81 -0
  108. package/src/features/assistant-message/components/parts/RelevanceMeter.tsx +4 -1
  109. package/src/features/assistant-message/components/parts/tabs/UsersTab.tsx +2 -1
  110. package/src/features/auth/components/buttons/GoogleSignInButton.tsx +4 -1
  111. package/src/features/auth/components/details/LandingComponent.tsx +1 -1
  112. package/src/features/auth/components/forms/ForgotPassword.tsx +1 -1
  113. package/src/features/auth/components/forms/Login.tsx +1 -1
  114. package/src/features/auth/components/forms/Register.tsx +1 -1
  115. package/src/features/auth/components/forms/ResetPassword.tsx +2 -2
  116. package/src/features/auth/components/two-factor/BackupCodesDialog.tsx +1 -1
  117. package/src/features/auth/components/two-factor/PasskeyButton.tsx +1 -1
  118. package/src/features/billing/components/cards/BillingUsageSummaryCard.tsx +21 -11
  119. package/src/features/billing/components/cards/CustomerInfoCard.tsx +16 -13
  120. package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +33 -21
  121. package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +20 -10
  122. package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +35 -22
  123. package/src/features/billing/components/containers/BillingDashboardContainer.tsx +24 -24
  124. package/src/features/billing/components/utils/__tests__/billing-formatters.spec.ts +63 -0
  125. package/src/features/billing/components/utils/currency.ts +67 -10
  126. package/src/features/billing/components/utils/date.ts +9 -4
  127. package/src/features/billing/components/utils/format-options.ts +20 -0
  128. package/src/features/billing/components/utils/index.ts +3 -1
  129. package/src/features/billing/components/utils/useBillingFormatOptions.ts +11 -0
  130. package/src/features/billing/components/widgets/BillingAlertBanner.tsx +8 -9
  131. package/src/features/billing/contexts/PriceContext.tsx +18 -6
  132. package/src/features/billing/i18n-keys.ts +212 -0
  133. package/src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx +8 -8
  134. package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +11 -9
  135. package/src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx +4 -4
  136. package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +9 -7
  137. package/src/features/billing/stripe-invoice/components/containers/InvoicesContainer.tsx +10 -8
  138. package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +34 -29
  139. package/src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx +12 -9
  140. package/src/features/billing/stripe-invoice/components/widgets/InvoiceStatusBadge.tsx +9 -6
  141. package/src/features/billing/stripe-price/components/details/PriceDetails.tsx +4 -3
  142. package/src/features/billing/stripe-price/components/forms/PriceArchiver.tsx +3 -1
  143. package/src/features/billing/stripe-price/components/forms/PriceEditor.tsx +3 -1
  144. package/src/features/billing/stripe-price/hooks/useStripePriceTableStructure.tsx +6 -5
  145. package/src/features/billing/stripe-promotion-code/components/PromoCodeInput.tsx +12 -7
  146. package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.service.ts +3 -1
  147. package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +10 -6
  148. package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +44 -29
  149. package/src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx +14 -11
  150. package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +20 -17
  151. package/src/features/billing/stripe-subscription/components/widgets/IntervalToggle.tsx +4 -2
  152. package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +16 -7
  153. package/src/features/billing/stripe-subscription/components/widgets/ProductPricingList.tsx +3 -1
  154. package/src/features/billing/stripe-subscription/components/widgets/ProductPricingRow.tsx +3 -1
  155. package/src/features/billing/stripe-subscription/components/widgets/ProrationPreview.tsx +12 -7
  156. package/src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx +11 -8
  157. package/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx +13 -10
  158. package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +8 -6
  159. package/src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx +7 -4
  160. package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +4 -2
  161. package/src/features/billing/stripe-subscription/components/wizards/WizardStepPlanSelection.tsx +3 -1
  162. package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +37 -28
  163. package/src/features/billing/stripe-subscription/hooks/useConfirmSubscriptionPayment.ts +9 -7
  164. package/src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts +20 -9
  165. package/src/features/billing/stripe-usage/components/containers/UsageContainer.tsx +6 -6
  166. package/src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx +24 -9
  167. package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +16 -12
  168. package/src/features/company/components/details/TokenStatusIndicator.tsx +7 -5
  169. package/src/features/company/components/forms/CompanyEditor.tsx +2 -2
  170. package/src/features/help/components/HelpArticleBody.tsx +1 -1
  171. package/src/features/help/components/HelpHeader.tsx +1 -1
  172. package/src/features/help/components/HelpSideNav.tsx +1 -1
  173. package/src/features/help/i18n-keys.ts +2 -0
  174. package/src/features/notification/components/common/NotificationErrorBoundary.tsx +17 -12
  175. package/src/features/notification/components/containers/NotificationsListContainer.tsx +4 -2
  176. package/src/features/notification/components/notifications/Notification.tsx +6 -3
  177. package/src/features/notification/contexts/NotificationContext.tsx +9 -4
  178. package/src/features/notification/contexts/__tests__/NotificationContext.spec.tsx +13 -0
  179. package/src/features/notification/hooks/useNotificationTableStructure.tsx +4 -2
  180. package/src/features/oauth/components/OAuthClientCard.tsx +13 -7
  181. package/src/features/oauth/components/OAuthClientDetail.tsx +34 -30
  182. package/src/features/oauth/components/OAuthClientForm.tsx +24 -24
  183. package/src/features/oauth/components/OAuthClientList.tsx +10 -6
  184. package/src/features/oauth/components/OAuthClientSecretDisplay.tsx +10 -8
  185. package/src/features/oauth/components/OAuthRedirectUriInput.tsx +9 -13
  186. package/src/features/oauth/components/OAuthScopeSelector.tsx +15 -6
  187. package/src/features/oauth/components/__tests__/OAuthClientForm.spec.tsx +11 -9
  188. package/src/features/oauth/components/consent/OAuthScopeList.tsx +4 -2
  189. package/src/features/oauth/hooks/__tests__/useOAuthScopeText.spec.ts +30 -0
  190. package/src/features/oauth/hooks/index.ts +1 -0
  191. package/src/features/oauth/hooks/useOAuthClient.ts +11 -5
  192. package/src/features/oauth/hooks/useOAuthClients.ts +9 -3
  193. package/src/features/oauth/hooks/useOAuthConsent.ts +11 -5
  194. package/src/features/oauth/hooks/useOAuthScopeText.ts +33 -0
  195. package/src/features/rbac/components/RbacByRoleContainer.tsx +7 -6
  196. package/src/features/rbac/components/RbacContainer.tsx +7 -6
  197. package/src/features/rbac/contexts/RbacContext.tsx +3 -3
  198. package/src/features/user/components/containers/UserContainer.tsx +3 -1
  199. package/src/features/user/data/__tests__/user.spec.ts +66 -0
  200. package/src/features/user/data/user.interface.ts +2 -0
  201. package/src/features/user/data/user.service.ts +9 -0
  202. package/src/features/user/data/user.ts +7 -0
  203. package/src/features/waitlist/components/forms/WaitlistForm.tsx +1 -1
  204. package/src/features/waitlist/components/lists/WaitlistList.tsx +1 -1
  205. package/src/i18n/__tests__/{config.test.ts → config.spec.ts} +21 -1
  206. package/src/i18n/config.ts +13 -0
  207. package/src/i18n/index.ts +1 -0
  208. package/src/shadcnui/custom/multi-select.tsx +47 -14
  209. package/src/shadcnui/custom/multiple-selector.tsx +14 -2
  210. package/src/shadcnui/ui/__tests__/{confirm-dialog.test.tsx → confirm-dialog.spec.tsx} +1 -1
  211. package/src/shadcnui/ui/__tests__/{cursor-controls.test.tsx → cursor-controls.spec.tsx} +1 -1
  212. package/src/shadcnui/ui/__tests__/{cursor-menu-items.test.tsx → cursor-menu-items.spec.tsx} +1 -1
  213. package/src/shadcnui/ui/__tests__/{empty-state.test.tsx → empty-state.spec.tsx} +1 -1
  214. package/src/shadcnui/ui/__tests__/{spinner.test.tsx → spinner.spec.tsx} +1 -1
  215. package/src/shadcnui/ui/__tests__/use-ui-text.spec.tsx +76 -0
  216. package/src/shadcnui/ui/breadcrumb.tsx +9 -2
  217. package/src/shadcnui/ui/carousel.tsx +15 -4
  218. package/src/shadcnui/ui/dialog.tsx +15 -2
  219. package/src/shadcnui/ui/sheet.tsx +6 -1
  220. package/src/shadcnui/ui/sidebar.tsx +33 -7
  221. package/src/shadcnui/ui/spinner.tsx +6 -1
  222. package/src/shadcnui/ui/use-ui-text.ts +49 -0
  223. package/src/utils/__tests__/{date-formatter.test.ts → date-formatter.spec.ts} +24 -1
  224. package/src/utils/date-formatter.ts +16 -0
  225. package/src/utils/index.ts +1 -1
  226. package/dist/BlockNoteEditor-BENYTTCZ.js.map +0 -1
  227. package/dist/BlockNoteEditor-VOYKZOP5.mjs.map +0 -1
  228. package/dist/chunk-4BZJ4LAB.js.map +0 -1
  229. package/dist/chunk-6ISF5KNQ.mjs.map +0 -1
  230. package/dist/chunk-ORU7KLCY.js.map +0 -1
  231. package/dist/chunk-ZZ6E33FO.mjs.map +0 -1
  232. /package/dist/{BlockNoteViewer-Y2ZM4ESA.mjs.map → BlockNoteViewer-MDPO6BXN.mjs.map} +0 -0
  233. /package/dist/{chunk-46S3KPLU.mjs.map → chunk-ILFFC6BG.mjs.map} +0 -0
  234. /package/dist/{chunk-HZVVRIJQ.mjs.map → chunk-IUS2RHCX.mjs.map} +0 -0
  235. /package/dist/{chunk-ZO3QQ5CQ.mjs.map → chunk-MQDR6MEE.mjs.map} +0 -0
  236. /package/scripts/{check-rtl-classes.test.ts → check-rtl-classes.spec.ts} +0 -0
  237. /package/src/client/__tests__/{JsonApiClient.test.ts → JsonApiClient.spec.ts} +0 -0
  238. /package/src/client/__tests__/{request.test.ts → request.spec.ts} +0 -0
  239. /package/src/client/hooks/__tests__/{useJsonApiGet.test.tsx → useJsonApiGet.spec.tsx} +0 -0
  240. /package/src/client/hooks/__tests__/{useJsonApiMutation.test.tsx → useJsonApiMutation.spec.tsx} +0 -0
  241. /package/src/client/hooks/__tests__/{useRehydration.test.ts → useRehydration.spec.ts} +0 -0
  242. /package/src/components/editors/__tests__/{BlockNoteEditorMentionInlineContent.test.ts → BlockNoteEditorMentionInlineContent.spec.ts} +0 -0
  243. /package/src/components/forms/__tests__/{CommonEditorDiscardDialog.test.tsx → CommonEditorDiscardDialog.spec.tsx} +0 -0
  244. /package/src/components/forms/__tests__/{EditorSheet.test.tsx → EditorSheet.spec.tsx} +0 -0
  245. /package/src/components/forms/__tests__/{FormCheckbox.test.tsx → FormCheckbox.spec.tsx} +0 -0
  246. /package/src/components/forms/__tests__/{FormDate.test.tsx → FormDate.spec.tsx} +0 -0
  247. /package/src/components/forms/__tests__/{FormInput.test.tsx → FormInput.spec.tsx} +0 -0
  248. /package/src/components/forms/__tests__/{FormSelect.test.tsx → FormSelect.spec.tsx} +0 -0
  249. /package/src/components/forms/__tests__/{useEditorDialog.test.ts → useEditorDialog.spec.ts} +0 -0
  250. /package/src/components/grids/__tests__/{ContentListGrid.test.tsx → ContentListGrid.spec.tsx} +0 -0
  251. /package/src/components/tables/__tests__/{ContentListTable.test.tsx → ContentListTable.rendering.spec.tsx} +0 -0
  252. /package/src/contexts/__tests__/{DirectionContext.test.tsx → DirectionContext.spec.tsx} +0 -0
  253. /package/src/contexts/__tests__/{SessionContext.test.tsx → SessionContext.spec.tsx} +0 -0
  254. /package/src/contexts/__tests__/{SocketContext.session.test.tsx → SocketContext.session.spec.tsx} +0 -0
  255. /package/src/core/abstracts/__tests__/{AbstractService.locale.test.ts → AbstractService.locale.spec.ts} +0 -0
  256. /package/src/core/endpoint/__tests__/{EndpointCreator.test.ts → EndpointCreator.spec.ts} +0 -0
  257. /package/src/core/factories/__tests__/{JsonApiDataFactory.test.ts → JsonApiDataFactory.spec.ts} +0 -0
  258. /package/src/core/factories/__tests__/{RehydrationFactory.test.ts → RehydrationFactory.spec.ts} +0 -0
  259. /package/src/core/registry/__tests__/{DataClassRegistry.test.ts → DataClassRegistry.spec.ts} +0 -0
  260. /package/src/core/registry/__tests__/{ModuleRegistrar.test.ts → ModuleRegistrar.spec.ts} +0 -0
  261. /package/src/core/registry/__tests__/{viewportStore.test.ts → viewportStore.spec.ts} +0 -0
  262. /package/src/core/utils/__tests__/{translateResponse.test.ts → translateResponse.spec.ts} +0 -0
  263. /package/src/features/ai-connection/data/__tests__/{ai-connection.test.ts → ai-connection.spec.ts} +0 -0
  264. /package/src/features/company/data/{company.test.ts → company.spec.ts} +0 -0
  265. /package/src/features/company/hooks/{useSubscriptionStatus.test.ts → useSubscriptionStatus.spec.ts} +0 -0
  266. /package/src/hooks/__tests__/{useDataListRetriever.test.ts → useDataListRetriever.spec.ts} +0 -0
  267. /package/src/hooks/__tests__/{useDebounce.test.ts → useDebounce.spec.ts} +0 -0
  268. /package/src/hooks/__tests__/{useSocket.render.test.ts → useSocket.render.spec.ts} +0 -0
  269. /package/src/lib/__tests__/{direction.test.ts → direction.spec.ts} +0 -0
  270. /package/src/next-config/__tests__/{createNextConfig.test.ts → createNextConfig.spec.ts} +0 -0
  271. /package/src/server/__tests__/{JsonApiServer.test.ts → JsonApiServer.spec.ts} +0 -0
  272. /package/src/server/__tests__/{ServerSession.read.test.ts → ServerSession.read.spec.ts} +0 -0
  273. /package/src/server/__tests__/{cache.test.ts → cache.spec.ts} +0 -0
  274. /package/src/server/__tests__/{cachedGet.test.ts → cachedGet.spec.ts} +0 -0
  275. /package/src/server/__tests__/{request.test.ts → request.spec.ts} +0 -0
  276. /package/src/shadcnui/ui/__tests__/{sidebar.menu-skeleton.test.tsx → sidebar.menu-skeleton.spec.tsx} +0 -0
  277. /package/src/shadcnui/ui/__tests__/{sidebar.session.test.tsx → sidebar.session.spec.tsx} +0 -0
  278. /package/src/unified/__tests__/{JsonApiGet.cache.test.ts → JsonApiGet.cache.spec.ts} +0 -0
  279. /package/src/unified/__tests__/{JsonApiRequest.test.ts → JsonApiRequest.spec.ts} +0 -0
  280. /package/src/utils/__tests__/{exists.test.ts → exists.spec.ts} +0 -0
  281. /package/src/utils/__tests__/{italian-validators.test.ts → italian-validators.spec.ts} +0 -0
  282. /package/src/utils/__tests__/{map-thenable.test.ts → map-thenable.spec.ts} +0 -0
  283. /package/src/utils/__tests__/{use-mobile.test.tsx → use-mobile.spec.tsx} +0 -0
  284. /package/src/utils/{cn.test.ts → cn.spec.ts} +0 -0
  285. /package/src/utils/{isRemoteImageSrc.test.ts → isRemoteImageSrc.spec.ts} +0 -0
@@ -1,8 +1,14 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { useState } from "react";
4
5
  import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../../../../../shadcnui";
5
- import { formatCurrency, formatDate } from "../../../components/utils";
6
+ import {
7
+ type BillingFormatOptions,
8
+ formatCurrency,
9
+ formatDate,
10
+ useBillingFormatOptions,
11
+ } from "../../../components/utils";
6
12
  import { StripeCustomerService } from "../../../stripe-customer";
7
13
  import { StripePriceInterface } from "../../../stripe-price/data/stripe-price.interface";
8
14
  import { StripeSubscriptionInterface, StripeSubscriptionService, SubscriptionStatus } from "../../data";
@@ -13,8 +19,8 @@ import { SubscriptionStatusBadge } from "../widgets/SubscriptionStatusBadge";
13
19
  * Formats the plan name from price data.
14
20
  * Format: "Product Name - Nickname (Interval)" e.g., "Only 35 - Pro (Monthly)"
15
21
  */
16
- function formatPlanName(price: StripePriceInterface | undefined): string {
17
- if (!price) return "N/A";
22
+ function formatPlanName(price: StripePriceInterface | undefined, t: ReturnType<typeof useTranslations>): string {
23
+ if (!price) return t("billing.ui.not_available");
18
24
 
19
25
  const productName = price.product?.name || "";
20
26
  const nickname = price.nickname || "";
@@ -23,10 +29,10 @@ function formatPlanName(price: StripePriceInterface | undefined): string {
23
29
  let interval = "";
24
30
  if (price.recurring?.interval) {
25
31
  const intervalMap: Record<string, string> = {
26
- day: "Daily",
27
- week: "Weekly",
28
- month: "Monthly",
29
- year: "Yearly",
32
+ day: t("billing.admin.prices.interval.day"),
33
+ week: t("billing.admin.prices.interval.week"),
34
+ month: t("billing.admin.prices.interval.month"),
35
+ year: t("billing.admin.prices.interval.year"),
30
36
  };
31
37
  interval = intervalMap[price.recurring.interval] || price.recurring.interval;
32
38
  }
@@ -36,15 +42,19 @@ function formatPlanName(price: StripePriceInterface | undefined): string {
36
42
  const planLabel = parts.join(" - ");
37
43
 
38
44
  // Add interval in parentheses if available
39
- return interval ? `${planLabel} (${interval})` : planLabel || "N/A";
45
+ return interval ? `${planLabel} (${interval})` : planLabel || t("billing.ui.not_available");
40
46
  }
41
47
 
42
48
  /**
43
49
  * Formats the billing amount from price data.
44
50
  */
45
- function formatBillingAmount(price: StripePriceInterface | undefined): string {
46
- if (!price?.unitAmount) return "N/A";
47
- return formatCurrency(price.unitAmount, price.currency);
51
+ function formatBillingAmount(
52
+ price: StripePriceInterface | undefined,
53
+ notAvailable: string,
54
+ fmt: BillingFormatOptions,
55
+ ): string {
56
+ if (!price?.unitAmount) return notAvailable;
57
+ return formatCurrency(price.unitAmount, price.currency, fmt);
48
58
  }
49
59
 
50
60
  type SubscriptionDetailsProps = {
@@ -62,6 +72,8 @@ export function SubscriptionDetails({
62
72
  onSubscriptionChange,
63
73
  onChangePlan,
64
74
  }: SubscriptionDetailsProps) {
75
+ const t = useTranslations();
76
+ const fmt = useBillingFormatOptions();
65
77
  const [showCancel, setShowCancel] = useState<boolean>(false);
66
78
  const [isProcessing, setIsProcessing] = useState<boolean>(false);
67
79
 
@@ -110,14 +122,14 @@ export function SubscriptionDetails({
110
122
  <Dialog open={open} onOpenChange={onOpenChange}>
111
123
  <DialogContent className="max-w-2xl">
112
124
  <DialogHeader>
113
- <DialogTitle>Subscription Details</DialogTitle>
114
- <DialogDescription>View and manage your subscription</DialogDescription>
125
+ <DialogTitle>{t("billing.ui.subscription.details_title")}</DialogTitle>
126
+ <DialogDescription>{t("billing.ui.subscription.details_description")}</DialogDescription>
115
127
  </DialogHeader>
116
128
 
117
129
  <div className="space-y-6">
118
130
  {/* Status */}
119
131
  <div className="flex items-center gap-x-3">
120
- <span className="text-muted-foreground text-xs">Status:</span>
132
+ <span className="text-muted-foreground text-xs">{t("billing.ui.labels.status")}</span>
121
133
  <SubscriptionStatusBadge
122
134
  status={subscription.status}
123
135
  cancelAtPeriodEnd={subscription.cancelAtPeriodEnd}
@@ -127,21 +139,23 @@ export function SubscriptionDetails({
127
139
  {/* Plan Info */}
128
140
  <div className="space-y-2">
129
141
  <div className="flex justify-between">
130
- <span className="text-muted-foreground text-xs">Plan:</span>
131
- <span className="text-sm">{formatPlanName(subscription.price)}</span>
142
+ <span className="text-muted-foreground text-xs">{t("billing.ui.labels.plan")}</span>
143
+ <span className="text-sm">{formatPlanName(subscription.price, t)}</span>
132
144
  </div>
133
145
  <div className="flex justify-between">
134
- <span className="text-muted-foreground text-xs">Billing Amount:</span>
135
- <span className="text-sm">{formatBillingAmount(subscription.price)}</span>
146
+ <span className="text-muted-foreground text-xs">{t("billing.ui.subscription.billing_amount")}</span>
147
+ <span className="text-sm">
148
+ {formatBillingAmount(subscription.price, t("billing.ui.not_available"), fmt)}
149
+ </span>
136
150
  </div>
137
151
  </div>
138
152
 
139
153
  {/* Current Period */}
140
154
  <div className="space-y-2">
141
155
  <div className="flex justify-between">
142
- <span className="text-muted-foreground text-xs">Current Period:</span>
156
+ <span className="text-muted-foreground text-xs">{t("billing.ui.subscription.current_period")}</span>
143
157
  <span className="text-sm">
144
- {formatDate(subscription.currentPeriodStart)} - {formatDate(subscription.currentPeriodEnd)}
158
+ {formatDate(subscription.currentPeriodStart, fmt)} - {formatDate(subscription.currentPeriodEnd, fmt)}
145
159
  </span>
146
160
  </div>
147
161
  </div>
@@ -149,8 +163,8 @@ export function SubscriptionDetails({
149
163
  {/* Trial Info */}
150
164
  {subscription.trialEnd && (
151
165
  <div className="flex justify-between">
152
- <span className="text-muted-foreground text-xs">Trial Ends:</span>
153
- <span className="text-sm">{formatDate(subscription.trialEnd)}</span>
166
+ <span className="text-muted-foreground text-xs">{t("billing.ui.subscription.trial_ends")}</span>
167
+ <span className="text-sm">{formatDate(subscription.trialEnd, fmt)}</span>
154
168
  </div>
155
169
  )}
156
170
 
@@ -158,8 +172,9 @@ export function SubscriptionDetails({
158
172
  {subscription.cancelAtPeriodEnd && (
159
173
  <div className="bg-warning/15 border-warning/30 rounded-lg border p-3">
160
174
  <p className="text-warning text-xs/relaxed">
161
- This subscription will be canceled at the end of the current period on{" "}
162
- {formatDate(subscription.currentPeriodEnd)}.
175
+ {t("billing.ui.subscription.cancel_at_period_end", {
176
+ date: formatDate(subscription.currentPeriodEnd, fmt),
177
+ })}
163
178
  </p>
164
179
  </div>
165
180
  )}
@@ -168,30 +183,30 @@ export function SubscriptionDetails({
168
183
  <div className="flex flex-wrap gap-2 pt-4 border-t">
169
184
  {onChangePlan && (
170
185
  <Button variant="default" onClick={() => onChangePlan(subscription)}>
171
- Change Plan
186
+ {t("billing.ui.subscription.change_plan")}
172
187
  </Button>
173
188
  )}
174
189
 
175
190
  {canPause && (
176
191
  <Button variant="outline" onClick={handlePause} disabled={isProcessing}>
177
- {isProcessing ? "Pausing..." : "Pause"}
192
+ {isProcessing ? t("billing.ui.subscription.pausing") : t("billing.ui.subscription.pause")}
178
193
  </Button>
179
194
  )}
180
195
 
181
196
  {canResume && (
182
197
  <Button variant="outline" onClick={handleResume} disabled={isProcessing}>
183
- {isProcessing ? "Resuming..." : "Resume"}
198
+ {isProcessing ? t("billing.ui.subscription.resuming") : t("billing.ui.subscription.resume")}
184
199
  </Button>
185
200
  )}
186
201
 
187
202
  {canCancel && (
188
203
  <Button variant="destructive" onClick={() => setShowCancel(true)}>
189
- Cancel
204
+ {t("ui.buttons.cancel")}
190
205
  </Button>
191
206
  )}
192
207
 
193
208
  <Button variant="outline" onClick={handleManageViaPortal}>
194
- Manage via Portal
209
+ {t("billing.ui.subscription.manage_portal")}
195
210
  </Button>
196
211
  </div>
197
212
  </div>
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { zodResolver } from "@hookform/resolvers/zod";
4
5
  import { useState } from "react";
5
6
  import { SubmitHandler, useForm } from "react-hook-form";
@@ -14,7 +15,7 @@ import {
14
15
  DialogTitle,
15
16
  Form,
16
17
  } from "../../../../../shadcnui";
17
- import { formatDate } from "../../../components/utils";
18
+ import { formatDate, useBillingFormatOptions } from "../../../components/utils";
18
19
  import { StripeSubscriptionInterface, StripeSubscriptionService } from "../../data";
19
20
 
20
21
  type CancelSubscriptionDialogProps = {
@@ -34,6 +35,8 @@ export function CancelSubscriptionDialog({
34
35
  onOpenChange,
35
36
  onSuccess,
36
37
  }: CancelSubscriptionDialogProps) {
38
+ const t = useTranslations();
39
+ const fmt = useBillingFormatOptions();
37
40
  const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
38
41
 
39
42
  const form = useForm<z.infer<typeof formSchema>>({
@@ -61,38 +64,38 @@ export function CancelSubscriptionDialog({
61
64
  }
62
65
  };
63
66
 
64
- const periodEndDate = formatDate(subscription.currentPeriodEnd);
67
+ const periodEndDate = formatDate(subscription.currentPeriodEnd, fmt);
65
68
 
66
69
  return (
67
70
  <Dialog open={open} onOpenChange={onOpenChange}>
68
71
  <DialogContent className="max-w-md">
69
72
  <DialogHeader>
70
- <DialogTitle>Cancel Subscription</DialogTitle>
71
- <DialogDescription>
72
- Are you sure you want to cancel this subscription? This action cannot be undone.
73
- </DialogDescription>
73
+ <DialogTitle>{t("billing.ui.subscription.cancel.title")}</DialogTitle>
74
+ <DialogDescription>{t("billing.ui.subscription.cancel.description")}</DialogDescription>
74
75
  </DialogHeader>
75
76
 
76
77
  <Form {...form}>
77
78
  <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-y-4">
78
79
  <div className="bg-primary/10 border-primary/30 text-foreground rounded-lg border p-3 text-sm">
79
- Your subscription will remain active until {periodEndDate}. You can continue using the service until then.
80
+ {t("billing.ui.subscription.cancel.remains_active", { date: periodEndDate })}
80
81
  </div>
81
82
 
82
83
  <FormTextarea
83
84
  form={form}
84
85
  id="reason"
85
- name="Reason (Optional)"
86
- placeholder="Let us know why you're canceling..."
86
+ name={t("billing.ui.subscription.cancel.reason")}
87
+ placeholder={t("billing.ui.subscription.cancel.reason_placeholder")}
87
88
  className="min-h-24"
88
89
  />
89
90
 
90
91
  <div className="flex gap-x-2 justify-end pt-2">
91
92
  <Button type="button" variant="outline" onClick={() => onOpenChange(false)} disabled={isSubmitting}>
92
- Keep Subscription
93
+ {t("billing.ui.subscription.cancel.keep")}
93
94
  </Button>
94
95
  <Button type="submit" variant="destructive" disabled={isSubmitting}>
95
- {isSubmitting ? "Canceling..." : "Confirm Cancellation"}
96
+ {isSubmitting
97
+ ? t("billing.ui.subscription.cancel.canceling")
98
+ : t("billing.ui.subscription.cancel.confirm")}
96
99
  </Button>
97
100
  </div>
98
101
  </form>
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { useState } from "react";
4
5
  import { Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../../../../../shadcnui";
5
- import { formatCurrency, formatDate } from "../../../components/utils";
6
+ import { formatCurrency, formatDate, useBillingFormatOptions } from "../../../components/utils";
6
7
  import { StripePriceInterface } from "../../../stripe-price/data/stripe-price.interface";
7
8
  import { StripeSubscriptionInterface, SubscriptionStatus } from "../../data";
8
9
  import { SubscriptionDetails } from "../details/SubscriptionDetails";
@@ -12,8 +13,8 @@ import { SubscriptionStatusBadge } from "../widgets/SubscriptionStatusBadge";
12
13
  * Formats the plan name from price data.
13
14
  * Format: "Product Name - Nickname (Interval)" e.g., "Only 35 - Pro (Monthly)"
14
15
  */
15
- function formatPlanName(price: StripePriceInterface | undefined): string {
16
- if (!price) return "N/A";
16
+ function formatPlanName(price: StripePriceInterface | undefined, t: ReturnType<typeof useTranslations>): string {
17
+ if (!price) return t("billing.ui.not_available");
17
18
 
18
19
  const productName = price.product?.name || "";
19
20
  const nickname = price.nickname || "";
@@ -22,10 +23,10 @@ function formatPlanName(price: StripePriceInterface | undefined): string {
22
23
  let interval = "";
23
24
  if (price.recurring?.interval) {
24
25
  const intervalMap: Record<string, string> = {
25
- day: "Daily",
26
- week: "Weekly",
27
- month: "Monthly",
28
- year: "Yearly",
26
+ day: t("billing.admin.prices.interval.day"),
27
+ week: t("billing.admin.prices.interval.week"),
28
+ month: t("billing.admin.prices.interval.month"),
29
+ year: t("billing.admin.prices.interval.year"),
29
30
  };
30
31
  interval = intervalMap[price.recurring.interval] || price.recurring.interval;
31
32
  }
@@ -35,7 +36,7 @@ function formatPlanName(price: StripePriceInterface | undefined): string {
35
36
  const planLabel = parts.join(" - ");
36
37
 
37
38
  // Add interval in parentheses if available
38
- return interval ? `${planLabel} (${interval})` : planLabel || "N/A";
39
+ return interval ? `${planLabel} (${interval})` : planLabel || t("billing.ui.not_available");
39
40
  }
40
41
 
41
42
  type SubscriptionsListProps = {
@@ -45,6 +46,8 @@ type SubscriptionsListProps = {
45
46
  };
46
47
 
47
48
  export function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChangePlan }: SubscriptionsListProps) {
49
+ const t = useTranslations();
50
+ const fmt = useBillingFormatOptions();
48
51
  const [selectedSub, setSelectedSub] = useState<StripeSubscriptionInterface | null>(null);
49
52
 
50
53
  const handleRowClick = (subscription: StripeSubscriptionInterface) => {
@@ -57,18 +60,18 @@ export function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChan
57
60
  <Table>
58
61
  <TableHeader className="bg-muted">
59
62
  <TableRow>
60
- <TableHead>Status</TableHead>
61
- <TableHead>Plan</TableHead>
62
- <TableHead>Period</TableHead>
63
- <TableHead className="text-end">Amount</TableHead>
64
- <TableHead className="text-end">Actions</TableHead>
63
+ <TableHead>{t("billing.ui.columns.status")}</TableHead>
64
+ <TableHead>{t("billing.ui.columns.plan")}</TableHead>
65
+ <TableHead>{t("billing.ui.columns.period")}</TableHead>
66
+ <TableHead className="text-end">{t("billing.ui.columns.amount")}</TableHead>
67
+ <TableHead className="text-end">{t("billing.ui.columns.actions")}</TableHead>
65
68
  </TableRow>
66
69
  </TableHeader>
67
70
  <TableBody>
68
71
  {subscriptions.map((subscription) => {
69
72
  const price = subscription.price;
70
- const amount = price?.unitAmount ? formatCurrency(price.unitAmount, price.currency) : "0";
71
- const period = `${formatDate(subscription.currentPeriodStart)} - ${formatDate(subscription.currentPeriodEnd)}`;
73
+ const amount = price?.unitAmount ? formatCurrency(price.unitAmount, price.currency, fmt) : "0";
74
+ const period = `${formatDate(subscription.currentPeriodStart, fmt)} - ${formatDate(subscription.currentPeriodEnd, fmt)}`;
72
75
 
73
76
  return (
74
77
  <TableRow
@@ -82,7 +85,7 @@ export function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChan
82
85
  cancelAtPeriodEnd={subscription.cancelAtPeriodEnd}
83
86
  />
84
87
  </TableCell>
85
- <TableCell className="font-medium">{formatPlanName(price)}</TableCell>
88
+ <TableCell className="font-medium">{formatPlanName(price, t)}</TableCell>
86
89
  <TableCell className="text-muted-foreground text-sm">{period}</TableCell>
87
90
  <TableCell className="text-end font-medium">{amount}</TableCell>
88
91
  <TableCell className="text-end">
@@ -97,7 +100,7 @@ export function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChan
97
100
  onChangePlan(subscription);
98
101
  }}
99
102
  >
100
- Upgrade
103
+ {t("billing.ui.subscription.upgrade")}
101
104
  </Button>
102
105
  )}
103
106
  </TableCell>
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { Tabs, TabsList, TabsTrigger } from "../../../../../shadcnui";
4
5
 
5
6
  export type BillingInterval = "month" | "year";
@@ -12,6 +13,7 @@ export type IntervalToggleProps = {
12
13
  };
13
14
 
14
15
  export function IntervalToggle({ value, onChange, hasMonthly, hasYearly }: IntervalToggleProps) {
16
+ const t = useTranslations();
15
17
  // Only render if BOTH intervals are available
16
18
  if (!hasMonthly || !hasYearly) {
17
19
  return null;
@@ -20,8 +22,8 @@ export function IntervalToggle({ value, onChange, hasMonthly, hasYearly }: Inter
20
22
  return (
21
23
  <Tabs value={value} onValueChange={(v) => onChange(v as BillingInterval)}>
22
24
  <TabsList>
23
- <TabsTrigger value="month">Monthly</TabsTrigger>
24
- <TabsTrigger value="year">Yearly</TabsTrigger>
25
+ <TabsTrigger value="month">{t("billing.admin.prices.interval.month")}</TabsTrigger>
26
+ <TabsTrigger value="year">{t("billing.admin.prices.interval.year")}</TabsTrigger>
25
27
  </TabsList>
26
28
  </Tabs>
27
29
  );
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { Check } from "lucide-react";
4
5
  import { KeyboardEvent } from "react";
5
6
  import { Badge, Button, Card, CardContent, CardFooter, CardHeader } from "../../../../../shadcnui";
6
- import { formatCurrency, formatInterval } from "../../../components/utils";
7
+ import { formatCurrency, formatInterval, useBillingFormatOptions } from "../../../components/utils";
7
8
  import { StripePriceInterface } from "../../../stripe-price/data/stripe-price.interface";
8
9
  import { cn } from "../../../../../utils/cn";
9
10
 
@@ -24,10 +25,12 @@ export function PricingCard({
24
25
  isLoading = false,
25
26
  onSelect,
26
27
  }: PricingCardProps) {
27
- const description = price.description || price.nickname || "Standard";
28
+ const t = useTranslations();
29
+ const fmt = useBillingFormatOptions();
30
+ const description = price.description || price.nickname || t("billing.ui.pricing.standard");
28
31
  const features = price.features || [];
29
- const formattedPrice = formatCurrency(price.unitAmount, price.currency);
30
- const interval = formatInterval(price);
32
+ const formattedPrice = formatCurrency(price.unitAmount, price.currency, fmt);
33
+ const interval = formatInterval(price, fmt);
31
34
 
32
35
  const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
33
36
  if ((e.key === "Enter" || e.key === " ") && !isDisabled && !isCurrentPlan) {
@@ -46,7 +49,7 @@ export function PricingCard({
46
49
  <Card
47
50
  role="radio"
48
51
  aria-checked={isSelected}
49
- aria-label={`${description} plan at ${formattedPrice} ${interval}`}
52
+ aria-label={t("billing.ui.pricing.card_aria", { description, price: formattedPrice, interval })}
50
53
  tabIndex={isDisabled ? -1 : 0}
51
54
  onKeyDown={handleKeyDown}
52
55
  onClick={handleClick}
@@ -62,7 +65,7 @@ export function PricingCard({
62
65
  >
63
66
  {isCurrentPlan && (
64
67
  <Badge variant="secondary" className="absolute top-2 end-2">
65
- Current
68
+ {t("billing.ui.pricing.current")}
66
69
  </Badge>
67
70
  )}
68
71
 
@@ -94,7 +97,13 @@ export function PricingCard({
94
97
  className="w-full"
95
98
  disabled={isDisabled || isCurrentPlan || isLoading}
96
99
  >
97
- {isLoading ? "Processing..." : isCurrentPlan ? "Current Plan" : isSelected ? "Selected" : "Select Plan"}
100
+ {isLoading
101
+ ? t("billing.ui.wizard.review.processing")
102
+ : isCurrentPlan
103
+ ? t("billing.ui.pricing.current_plan")
104
+ : isSelected
105
+ ? t("billing.ui.pricing.selected")
106
+ : t("billing.ui.wizard.steps.select_plan")}
98
107
  </Button>
99
108
  </CardFooter>
100
109
  </Card>
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { Skeleton } from "../../../../../shadcnui";
4
5
  import { StripePriceInterface } from "../../../stripe-price/data/stripe-price.interface";
5
6
  import { StripeProductInterface } from "../../../stripe-product";
@@ -52,12 +53,13 @@ export function ProductPricingList({
52
53
  hideRecurringPrices = false,
53
54
  hideOneTimePrices = false,
54
55
  }: ProductPricingListProps) {
56
+ const t = useTranslations();
55
57
  if (loading) {
56
58
  return <ProductPricingListSkeleton />;
57
59
  }
58
60
 
59
61
  if (products.length === 0) {
60
- return <div className="text-center py-8 text-muted-foreground">No plans available</div>;
62
+ return <div className="text-center py-8 text-muted-foreground">{t("billing.ui.pricing.no_plans")}</div>;
61
63
  }
62
64
 
63
65
  const sortedProducts = [...products].sort((a, b) => {
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { StripePriceInterface } from "../../../stripe-price/data/stripe-price.interface";
4
5
  import { StripeProductInterface } from "../../../stripe-product";
5
6
  import { SectionHeader } from "../../../../../components/typography";
@@ -22,6 +23,7 @@ export function ProductPricingRow({
22
23
  loadingPriceId,
23
24
  onSelectPrice,
24
25
  }: ProductPricingRowProps) {
26
+ const t = useTranslations();
25
27
  if (prices.length === 0) {
26
28
  return null;
27
29
  }
@@ -35,7 +37,7 @@ export function ProductPricingRow({
35
37
  <div
36
38
  className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"
37
39
  role="radiogroup"
38
- aria-label={`Pricing options for ${product.name}`}
40
+ aria-label={t("billing.ui.pricing.options_aria", { name: product.name })}
39
41
  >
40
42
  {prices
41
43
  .sort((a, b) => (a.unitAmount ?? 0) - (b.unitAmount ?? 0))
@@ -1,38 +1,43 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { ProrationPreviewInterface } from "../../../stripe-invoice/data/stripe-invoice.interface";
4
- import { formatCurrency, formatDate } from "../../../components/utils";
5
+ import { formatCurrency, formatDate, useBillingFormatOptions } from "../../../components/utils";
5
6
 
6
7
  type ProrationPreviewProps = {
7
8
  preview: ProrationPreviewInterface;
8
9
  };
9
10
 
10
11
  export function ProrationPreview({ preview }: ProrationPreviewProps) {
12
+ const t = useTranslations();
13
+ const fmt = useBillingFormatOptions();
11
14
  return (
12
15
  <div className="bg-primary/10 border-primary/30 rounded-lg border p-4">
13
- <h4 className="text-primary mb-3 font-semibold">Proration Breakdown</h4>
16
+ <h4 className="text-primary mb-3 font-semibold">{t("billing.ui.proration.title")}</h4>
14
17
 
15
18
  <div className="space-y-2">
16
19
  {preview.lineItems.map((item, index) => (
17
20
  <div key={index} className="flex justify-between text-sm">
18
21
  <span className="text-muted-foreground">{item.description}</span>
19
22
  <span className={`font-medium ${item.amount < 0 ? "text-success" : "text-foreground"}`}>
20
- {formatCurrency(item.amount, preview.currency)}
23
+ {formatCurrency(item.amount, preview.currency, fmt)}
21
24
  </span>
22
25
  </div>
23
26
  ))}
24
27
 
25
28
  <div className="border-primary/30 mt-2 border-t pt-2">
26
29
  <div className="flex justify-between font-semibold">
27
- <span className="text-foreground">Net Due Today</span>
28
- <span className="text-foreground">{formatCurrency(preview.immediateCharge, preview.currency)}</span>
30
+ <span className="text-foreground">{t("billing.ui.proration.net_due_today")}</span>
31
+ <span className="text-foreground">{formatCurrency(preview.immediateCharge, preview.currency, fmt)}</span>
29
32
  </div>
30
33
  </div>
31
34
 
32
35
  {preview.lineItems.length > 0 && preview.lineItems[0].period && (
33
36
  <div className="text-muted-foreground mt-2 text-xs">
34
- Next invoice on {formatDate(preview.lineItems[0].period.end)} for{" "}
35
- {formatCurrency(preview.amountDue, preview.currency)}
37
+ {t("billing.ui.proration.next_invoice", {
38
+ date: formatDate(preview.lineItems[0].period.end, fmt),
39
+ amount: formatCurrency(preview.amountDue, preview.currency, fmt),
40
+ })}
36
41
  </div>
37
42
  )}
38
43
  </div>
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { Check, Loader2 } from "lucide-react";
4
5
  import { Button } from "../../../../../shadcnui";
5
- import { formatCurrency, formatInterval } from "../../../components/utils";
6
+ import { formatCurrency, formatInterval, useBillingFormatOptions } from "../../../components/utils";
6
7
  import { StripePriceInterface } from "../../../stripe-price/data/stripe-price.interface";
7
8
 
8
9
  type SubscriptionConfirmationProps = {
@@ -13,13 +14,15 @@ type SubscriptionConfirmationProps = {
13
14
  };
14
15
 
15
16
  export function SubscriptionConfirmation({ price, isLoading, onConfirm, onCancel }: SubscriptionConfirmationProps) {
16
- const productName = price.product?.name || price.nickname || "Selected Plan";
17
+ const t = useTranslations();
18
+ const fmt = useBillingFormatOptions();
19
+ const productName = price.product?.name || price.nickname || t("billing.ui.wizard.review.selected_plan");
17
20
  const productDescription = price.product?.description || price.description;
18
21
  const features = price.features || [];
19
22
 
20
23
  return (
21
24
  <div className="bg-accent/10 border border-accent/30 rounded-lg p-4">
22
- <h4 className="font-semibold mb-3">Confirm Your Subscription</h4>
25
+ <h4 className="font-semibold mb-3">{t("billing.ui.confirmation.title")}</h4>
23
26
 
24
27
  <div className="space-y-3">
25
28
  {/* Plan name and description */}
@@ -30,8 +33,8 @@ export function SubscriptionConfirmation({ price, isLoading, onConfirm, onCancel
30
33
 
31
34
  {/* Price */}
32
35
  <div className="text-lg font-semibold">
33
- {formatCurrency(price.unitAmount, price.currency)}
34
- <span className="text-sm font-normal text-muted-foreground">{formatInterval(price)}</span>
36
+ {formatCurrency(price.unitAmount, price.currency, fmt)}
37
+ <span className="text-sm font-normal text-muted-foreground">{formatInterval(price, fmt)}</span>
35
38
  </div>
36
39
 
37
40
  {/* Features */}
@@ -49,16 +52,16 @@ export function SubscriptionConfirmation({ price, isLoading, onConfirm, onCancel
49
52
  {/* Action buttons */}
50
53
  <div className="flex justify-end gap-3 pt-2 border-t border-accent/30">
51
54
  <Button variant="outline" onClick={onCancel} disabled={isLoading}>
52
- Cancel
55
+ {t("ui.buttons.cancel")}
53
56
  </Button>
54
57
  <Button onClick={onConfirm} disabled={isLoading}>
55
58
  {isLoading ? (
56
59
  <>
57
60
  <Loader2 className="h-4 w-4 animate-spin me-2" />
58
- Processing...
61
+ {t("billing.ui.wizard.review.processing")}
59
62
  </>
60
63
  ) : (
61
- "Subscribe"
64
+ t("billing.ui.confirmation.subscribe")
62
65
  )}
63
66
  </Button>
64
67
  </div>