@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.
- package/dist/{AssistantMessageInterface-CubxuBIJ.d.ts → AssistantMessageInterface-DHJJO_Z_.d.ts} +1 -1
- package/dist/{AssistantMessageInterface-sHjI2zPN.d.mts → AssistantMessageInterface-S7oyERTT.d.mts} +1 -1
- package/dist/{BlockNoteEditor-VOYKZOP5.mjs → BlockNoteEditor-HXAHDJA7.mjs} +34 -27
- package/dist/BlockNoteEditor-HXAHDJA7.mjs.map +1 -0
- package/dist/{BlockNoteEditor-BENYTTCZ.js → BlockNoteEditor-LLCZDFLZ.js} +50 -43
- package/dist/BlockNoteEditor-LLCZDFLZ.js.map +1 -0
- package/dist/{BlockNoteViewer-USLR4Y7C.js → BlockNoteViewer-77CCTC2U.js} +3 -3
- package/dist/{BlockNoteViewer-USLR4Y7C.js.map → BlockNoteViewer-77CCTC2U.js.map} +1 -1
- package/dist/{BlockNoteViewer-Y2ZM4ESA.mjs → BlockNoteViewer-MDPO6BXN.mjs} +2 -2
- package/dist/{auth.interface-Df4vXbNz.d.mts → auth.interface-CeFCz9NU.d.mts} +1 -1
- package/dist/{auth.interface-BzI4onI_.d.ts → auth.interface-D1R7VVky.d.ts} +1 -1
- package/dist/billing/index.d.mts +40 -6
- package/dist/billing/index.d.ts +40 -6
- package/dist/billing/index.js +806 -774
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +494 -462
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-6ISF5KNQ.mjs → chunk-2VXDZRYY.mjs} +1103 -816
- package/dist/chunk-2VXDZRYY.mjs.map +1 -0
- package/dist/{chunk-4BZJ4LAB.js → chunk-4ORLPEK7.js} +136 -104
- package/dist/chunk-4ORLPEK7.js.map +1 -0
- package/dist/{chunk-ZZ6E33FO.mjs → chunk-6DRSSW5O.mjs} +36 -4
- package/dist/chunk-6DRSSW5O.mjs.map +1 -0
- package/dist/{chunk-KS6WZP2E.js → chunk-6SMJRAE3.js} +4 -4
- package/dist/{chunk-KS6WZP2E.js.map → chunk-6SMJRAE3.js.map} +1 -1
- package/dist/{chunk-WQ77CUUZ.js → chunk-GPXYQMOZ.js} +7 -7
- package/dist/{chunk-WQ77CUUZ.js.map → chunk-GPXYQMOZ.js.map} +1 -1
- package/dist/{chunk-46S3KPLU.mjs → chunk-ILFFC6BG.mjs} +2 -2
- package/dist/{chunk-HZVVRIJQ.mjs → chunk-IUS2RHCX.mjs} +2 -2
- package/dist/{chunk-F7RNBGHS.js → chunk-KJMMBUOA.js} +3 -3
- package/dist/{chunk-F7RNBGHS.js.map → chunk-KJMMBUOA.js.map} +1 -1
- package/dist/{chunk-ZO3QQ5CQ.mjs → chunk-MQDR6MEE.mjs} +2 -2
- package/dist/{chunk-ORU7KLCY.js → chunk-VDVIUDY5.js} +1445 -1158
- package/dist/chunk-VDVIUDY5.js.map +1 -0
- package/dist/client/index.d.mts +20 -6
- package/dist/client/index.d.ts +20 -6
- package/dist/client/index.js +6 -4
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +5 -3
- package/dist/components/index.d.mts +59 -21
- package/dist/components/index.d.ts +59 -21
- package/dist/components/index.js +4 -4
- package/dist/components/index.mjs +3 -3
- package/dist/{config-CEAq4Ozy.d.mts → config-BP2vyddw.d.mts} +5 -0
- package/dist/{config-DydDHCfb.d.ts → config-BhW2QrRD.d.ts} +5 -0
- package/dist/contexts/index.d.mts +4 -4
- package/dist/contexts/index.d.ts +4 -4
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +17 -9
- package/dist/core/index.d.ts +17 -9
- package/dist/core/index.js +4 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +3 -1
- package/dist/features/analytics/index.d.mts +1 -1
- package/dist/features/analytics/index.d.ts +1 -1
- package/dist/features/analytics/index.js +122 -122
- package/dist/features/analytics/index.mjs +4 -4
- package/dist/features/analytics/server.js +4 -4
- package/dist/features/analytics/server.mjs +2 -2
- package/dist/features/help/index.d.mts +1 -1
- package/dist/features/help/index.d.ts +1 -1
- package/dist/features/help/index.js +42 -40
- package/dist/features/help/index.js.map +1 -1
- package/dist/features/help/index.mjs +8 -6
- package/dist/features/help/index.mjs.map +1 -1
- package/dist/features/tokenusage/index.js +125 -125
- package/dist/features/tokenusage/index.mjs +4 -4
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -2
- package/dist/{notification.interface-P_pNfzZQ.d.mts → notification.interface-BB6GPpZY.d.mts} +1 -1
- package/dist/{notification.interface-BNCvnVCW.d.ts → notification.interface-CmAGrijJ.d.ts} +1 -1
- package/dist/{s3.service-CbUa6a-8.d.ts → s3.service-C-4YpEMu.d.ts} +7 -3
- package/dist/{s3.service-DAp8FbuG.d.mts → s3.service-dVj-nxMl.d.mts} +7 -3
- package/dist/server/index.d.mts +4 -4
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +2 -2
- package/dist/{useSocket-BHVIyigI.d.ts → useSocket-B0H9pmmH.d.ts} +1 -1
- package/dist/{useSocket-B9gTbuCj.d.mts → useSocket-B5RQb6v2.d.mts} +1 -1
- package/dist/{user.interface-CrmwJLYW.d.ts → user.interface-C0ZdsBZj.d.ts} +2 -0
- package/dist/{user.interface-CSaGWFbg.d.mts → user.interface-CIWGlbqM.d.mts} +2 -0
- package/package.json +1 -1
- package/src/components/containers/RoundPageContainer.tsx +3 -1
- package/src/components/containers/RoundPageContainerTitle.tsx +5 -2
- package/src/components/editors/BlockNoteEditor.tsx +31 -15
- package/src/components/editors/BlockNoteEditorAiActions.tsx +11 -7
- package/src/components/editors/__tests__/{BlockNoteEditorAiActions.test.ts → BlockNoteEditorAiActions.spec.ts} +10 -3
- package/src/components/errors/ErrorDetails.tsx +5 -2
- package/src/components/forms/CommonAssociationForm.tsx +1 -1
- package/src/components/forms/EntityMultiSelector.tsx +6 -2
- package/src/components/forms/EntitySelector.tsx +4 -1
- package/src/components/forms/FileUploader.tsx +3 -2
- package/src/components/forms/MultiFileUploader.tsx +3 -1
- package/src/components/navigations/Breadcrumb.tsx +3 -2
- package/src/components/navigations/Header.tsx +3 -1
- package/src/components/navigations/MobileNavigationBar.tsx +3 -1
- package/src/components/navigations/__tests__/Header.spec.tsx +2 -2
- package/src/components/tables/__tests__/ContentListTable.spec.tsx +1 -1
- package/src/components/tables/cells/cell.id.tsx +13 -2
- package/src/core/abstracts/AbstractService.ts +3 -3
- package/src/core/abstracts/ClientAbstractService.ts +3 -2
- package/src/features/assistant-action/components/ApprovalActionCard.tsx +74 -5
- package/src/features/assistant-action/components/__tests__/ApprovalActionCard.spec.tsx +81 -0
- package/src/features/assistant-message/components/parts/RelevanceMeter.tsx +4 -1
- package/src/features/assistant-message/components/parts/tabs/UsersTab.tsx +2 -1
- package/src/features/auth/components/buttons/GoogleSignInButton.tsx +4 -1
- package/src/features/auth/components/details/LandingComponent.tsx +1 -1
- package/src/features/auth/components/forms/ForgotPassword.tsx +1 -1
- package/src/features/auth/components/forms/Login.tsx +1 -1
- package/src/features/auth/components/forms/Register.tsx +1 -1
- package/src/features/auth/components/forms/ResetPassword.tsx +2 -2
- package/src/features/auth/components/two-factor/BackupCodesDialog.tsx +1 -1
- package/src/features/auth/components/two-factor/PasskeyButton.tsx +1 -1
- package/src/features/billing/components/cards/BillingUsageSummaryCard.tsx +21 -11
- package/src/features/billing/components/cards/CustomerInfoCard.tsx +16 -13
- package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +33 -21
- package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +20 -10
- package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +35 -22
- package/src/features/billing/components/containers/BillingDashboardContainer.tsx +24 -24
- package/src/features/billing/components/utils/__tests__/billing-formatters.spec.ts +63 -0
- package/src/features/billing/components/utils/currency.ts +67 -10
- package/src/features/billing/components/utils/date.ts +9 -4
- package/src/features/billing/components/utils/format-options.ts +20 -0
- package/src/features/billing/components/utils/index.ts +3 -1
- package/src/features/billing/components/utils/useBillingFormatOptions.ts +11 -0
- package/src/features/billing/components/widgets/BillingAlertBanner.tsx +8 -9
- package/src/features/billing/contexts/PriceContext.tsx +18 -6
- package/src/features/billing/i18n-keys.ts +212 -0
- package/src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx +8 -8
- package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +11 -9
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx +4 -4
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +9 -7
- package/src/features/billing/stripe-invoice/components/containers/InvoicesContainer.tsx +10 -8
- package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +34 -29
- package/src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx +12 -9
- package/src/features/billing/stripe-invoice/components/widgets/InvoiceStatusBadge.tsx +9 -6
- package/src/features/billing/stripe-price/components/details/PriceDetails.tsx +4 -3
- package/src/features/billing/stripe-price/components/forms/PriceArchiver.tsx +3 -1
- package/src/features/billing/stripe-price/components/forms/PriceEditor.tsx +3 -1
- package/src/features/billing/stripe-price/hooks/useStripePriceTableStructure.tsx +6 -5
- package/src/features/billing/stripe-promotion-code/components/PromoCodeInput.tsx +12 -7
- package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.service.ts +3 -1
- package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +10 -6
- package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +44 -29
- package/src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx +14 -11
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +20 -17
- package/src/features/billing/stripe-subscription/components/widgets/IntervalToggle.tsx +4 -2
- package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +16 -7
- package/src/features/billing/stripe-subscription/components/widgets/ProductPricingList.tsx +3 -1
- package/src/features/billing/stripe-subscription/components/widgets/ProductPricingRow.tsx +3 -1
- package/src/features/billing/stripe-subscription/components/widgets/ProrationPreview.tsx +12 -7
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx +11 -8
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx +13 -10
- package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +8 -6
- package/src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx +7 -4
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +4 -2
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPlanSelection.tsx +3 -1
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +37 -28
- package/src/features/billing/stripe-subscription/hooks/useConfirmSubscriptionPayment.ts +9 -7
- package/src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts +20 -9
- package/src/features/billing/stripe-usage/components/containers/UsageContainer.tsx +6 -6
- package/src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx +24 -9
- package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +16 -12
- package/src/features/company/components/details/TokenStatusIndicator.tsx +7 -5
- package/src/features/company/components/forms/CompanyEditor.tsx +2 -2
- package/src/features/help/components/HelpArticleBody.tsx +1 -1
- package/src/features/help/components/HelpHeader.tsx +1 -1
- package/src/features/help/components/HelpSideNav.tsx +1 -1
- package/src/features/help/i18n-keys.ts +2 -0
- package/src/features/notification/components/common/NotificationErrorBoundary.tsx +17 -12
- package/src/features/notification/components/containers/NotificationsListContainer.tsx +4 -2
- package/src/features/notification/components/notifications/Notification.tsx +6 -3
- package/src/features/notification/contexts/NotificationContext.tsx +9 -4
- package/src/features/notification/contexts/__tests__/NotificationContext.spec.tsx +13 -0
- package/src/features/notification/hooks/useNotificationTableStructure.tsx +4 -2
- package/src/features/oauth/components/OAuthClientCard.tsx +13 -7
- package/src/features/oauth/components/OAuthClientDetail.tsx +34 -30
- package/src/features/oauth/components/OAuthClientForm.tsx +24 -24
- package/src/features/oauth/components/OAuthClientList.tsx +10 -6
- package/src/features/oauth/components/OAuthClientSecretDisplay.tsx +10 -8
- package/src/features/oauth/components/OAuthRedirectUriInput.tsx +9 -13
- package/src/features/oauth/components/OAuthScopeSelector.tsx +15 -6
- package/src/features/oauth/components/__tests__/OAuthClientForm.spec.tsx +11 -9
- package/src/features/oauth/components/consent/OAuthScopeList.tsx +4 -2
- package/src/features/oauth/hooks/__tests__/useOAuthScopeText.spec.ts +30 -0
- package/src/features/oauth/hooks/index.ts +1 -0
- package/src/features/oauth/hooks/useOAuthClient.ts +11 -5
- package/src/features/oauth/hooks/useOAuthClients.ts +9 -3
- package/src/features/oauth/hooks/useOAuthConsent.ts +11 -5
- package/src/features/oauth/hooks/useOAuthScopeText.ts +33 -0
- package/src/features/rbac/components/RbacByRoleContainer.tsx +7 -6
- package/src/features/rbac/components/RbacContainer.tsx +7 -6
- package/src/features/rbac/contexts/RbacContext.tsx +3 -3
- package/src/features/user/components/containers/UserContainer.tsx +3 -1
- package/src/features/user/data/__tests__/user.spec.ts +66 -0
- package/src/features/user/data/user.interface.ts +2 -0
- package/src/features/user/data/user.service.ts +9 -0
- package/src/features/user/data/user.ts +7 -0
- package/src/features/waitlist/components/forms/WaitlistForm.tsx +1 -1
- package/src/features/waitlist/components/lists/WaitlistList.tsx +1 -1
- package/src/i18n/__tests__/{config.test.ts → config.spec.ts} +21 -1
- package/src/i18n/config.ts +13 -0
- package/src/i18n/index.ts +1 -0
- package/src/shadcnui/custom/multi-select.tsx +47 -14
- package/src/shadcnui/custom/multiple-selector.tsx +14 -2
- package/src/shadcnui/ui/__tests__/{confirm-dialog.test.tsx → confirm-dialog.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/{cursor-controls.test.tsx → cursor-controls.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/{cursor-menu-items.test.tsx → cursor-menu-items.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/{empty-state.test.tsx → empty-state.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/{spinner.test.tsx → spinner.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/use-ui-text.spec.tsx +76 -0
- package/src/shadcnui/ui/breadcrumb.tsx +9 -2
- package/src/shadcnui/ui/carousel.tsx +15 -4
- package/src/shadcnui/ui/dialog.tsx +15 -2
- package/src/shadcnui/ui/sheet.tsx +6 -1
- package/src/shadcnui/ui/sidebar.tsx +33 -7
- package/src/shadcnui/ui/spinner.tsx +6 -1
- package/src/shadcnui/ui/use-ui-text.ts +49 -0
- package/src/utils/__tests__/{date-formatter.test.ts → date-formatter.spec.ts} +24 -1
- package/src/utils/date-formatter.ts +16 -0
- package/src/utils/index.ts +1 -1
- package/dist/BlockNoteEditor-BENYTTCZ.js.map +0 -1
- package/dist/BlockNoteEditor-VOYKZOP5.mjs.map +0 -1
- package/dist/chunk-4BZJ4LAB.js.map +0 -1
- package/dist/chunk-6ISF5KNQ.mjs.map +0 -1
- package/dist/chunk-ORU7KLCY.js.map +0 -1
- package/dist/chunk-ZZ6E33FO.mjs.map +0 -1
- /package/dist/{BlockNoteViewer-Y2ZM4ESA.mjs.map → BlockNoteViewer-MDPO6BXN.mjs.map} +0 -0
- /package/dist/{chunk-46S3KPLU.mjs.map → chunk-ILFFC6BG.mjs.map} +0 -0
- /package/dist/{chunk-HZVVRIJQ.mjs.map → chunk-IUS2RHCX.mjs.map} +0 -0
- /package/dist/{chunk-ZO3QQ5CQ.mjs.map → chunk-MQDR6MEE.mjs.map} +0 -0
- /package/scripts/{check-rtl-classes.test.ts → check-rtl-classes.spec.ts} +0 -0
- /package/src/client/__tests__/{JsonApiClient.test.ts → JsonApiClient.spec.ts} +0 -0
- /package/src/client/__tests__/{request.test.ts → request.spec.ts} +0 -0
- /package/src/client/hooks/__tests__/{useJsonApiGet.test.tsx → useJsonApiGet.spec.tsx} +0 -0
- /package/src/client/hooks/__tests__/{useJsonApiMutation.test.tsx → useJsonApiMutation.spec.tsx} +0 -0
- /package/src/client/hooks/__tests__/{useRehydration.test.ts → useRehydration.spec.ts} +0 -0
- /package/src/components/editors/__tests__/{BlockNoteEditorMentionInlineContent.test.ts → BlockNoteEditorMentionInlineContent.spec.ts} +0 -0
- /package/src/components/forms/__tests__/{CommonEditorDiscardDialog.test.tsx → CommonEditorDiscardDialog.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{EditorSheet.test.tsx → EditorSheet.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{FormCheckbox.test.tsx → FormCheckbox.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{FormDate.test.tsx → FormDate.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{FormInput.test.tsx → FormInput.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{FormSelect.test.tsx → FormSelect.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{useEditorDialog.test.ts → useEditorDialog.spec.ts} +0 -0
- /package/src/components/grids/__tests__/{ContentListGrid.test.tsx → ContentListGrid.spec.tsx} +0 -0
- /package/src/components/tables/__tests__/{ContentListTable.test.tsx → ContentListTable.rendering.spec.tsx} +0 -0
- /package/src/contexts/__tests__/{DirectionContext.test.tsx → DirectionContext.spec.tsx} +0 -0
- /package/src/contexts/__tests__/{SessionContext.test.tsx → SessionContext.spec.tsx} +0 -0
- /package/src/contexts/__tests__/{SocketContext.session.test.tsx → SocketContext.session.spec.tsx} +0 -0
- /package/src/core/abstracts/__tests__/{AbstractService.locale.test.ts → AbstractService.locale.spec.ts} +0 -0
- /package/src/core/endpoint/__tests__/{EndpointCreator.test.ts → EndpointCreator.spec.ts} +0 -0
- /package/src/core/factories/__tests__/{JsonApiDataFactory.test.ts → JsonApiDataFactory.spec.ts} +0 -0
- /package/src/core/factories/__tests__/{RehydrationFactory.test.ts → RehydrationFactory.spec.ts} +0 -0
- /package/src/core/registry/__tests__/{DataClassRegistry.test.ts → DataClassRegistry.spec.ts} +0 -0
- /package/src/core/registry/__tests__/{ModuleRegistrar.test.ts → ModuleRegistrar.spec.ts} +0 -0
- /package/src/core/registry/__tests__/{viewportStore.test.ts → viewportStore.spec.ts} +0 -0
- /package/src/core/utils/__tests__/{translateResponse.test.ts → translateResponse.spec.ts} +0 -0
- /package/src/features/ai-connection/data/__tests__/{ai-connection.test.ts → ai-connection.spec.ts} +0 -0
- /package/src/features/company/data/{company.test.ts → company.spec.ts} +0 -0
- /package/src/features/company/hooks/{useSubscriptionStatus.test.ts → useSubscriptionStatus.spec.ts} +0 -0
- /package/src/hooks/__tests__/{useDataListRetriever.test.ts → useDataListRetriever.spec.ts} +0 -0
- /package/src/hooks/__tests__/{useDebounce.test.ts → useDebounce.spec.ts} +0 -0
- /package/src/hooks/__tests__/{useSocket.render.test.ts → useSocket.render.spec.ts} +0 -0
- /package/src/lib/__tests__/{direction.test.ts → direction.spec.ts} +0 -0
- /package/src/next-config/__tests__/{createNextConfig.test.ts → createNextConfig.spec.ts} +0 -0
- /package/src/server/__tests__/{JsonApiServer.test.ts → JsonApiServer.spec.ts} +0 -0
- /package/src/server/__tests__/{ServerSession.read.test.ts → ServerSession.read.spec.ts} +0 -0
- /package/src/server/__tests__/{cache.test.ts → cache.spec.ts} +0 -0
- /package/src/server/__tests__/{cachedGet.test.ts → cachedGet.spec.ts} +0 -0
- /package/src/server/__tests__/{request.test.ts → request.spec.ts} +0 -0
- /package/src/shadcnui/ui/__tests__/{sidebar.menu-skeleton.test.tsx → sidebar.menu-skeleton.spec.tsx} +0 -0
- /package/src/shadcnui/ui/__tests__/{sidebar.session.test.tsx → sidebar.session.spec.tsx} +0 -0
- /package/src/unified/__tests__/{JsonApiGet.cache.test.ts → JsonApiGet.cache.spec.ts} +0 -0
- /package/src/unified/__tests__/{JsonApiRequest.test.ts → JsonApiRequest.spec.ts} +0 -0
- /package/src/utils/__tests__/{exists.test.ts → exists.spec.ts} +0 -0
- /package/src/utils/__tests__/{italian-validators.test.ts → italian-validators.spec.ts} +0 -0
- /package/src/utils/__tests__/{map-thenable.test.ts → map-thenable.spec.ts} +0 -0
- /package/src/utils/__tests__/{use-mobile.test.tsx → use-mobile.spec.tsx} +0 -0
- /package/src/utils/{cn.test.ts → cn.spec.ts} +0 -0
- /package/src/utils/{isRemoteImageSrc.test.ts → isRemoteImageSrc.spec.ts} +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { Download, ExternalLink, RefreshCw } from "lucide-react";
|
|
4
5
|
import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../../../../../shadcnui";
|
|
5
|
-
import { formatCurrency, formatDate } from "../../../components/utils";
|
|
6
|
+
import { formatCurrency, formatDate, useBillingFormatOptions } from "../../../components/utils";
|
|
6
7
|
import { InvoiceStatus, StripeInvoiceInterface } from "../../data/stripe-invoice.interface";
|
|
7
8
|
import { InvoiceStatusBadge } from "../widgets/InvoiceStatusBadge";
|
|
8
9
|
|
|
@@ -19,6 +20,8 @@ export function InvoiceDetails({
|
|
|
19
20
|
onOpenChange,
|
|
20
21
|
onInvoiceChange: _onInvoiceChange,
|
|
21
22
|
}: InvoiceDetailsProps) {
|
|
23
|
+
const t = useTranslations();
|
|
24
|
+
const fmt = useBillingFormatOptions();
|
|
22
25
|
const handleDownloadPDF = () => {
|
|
23
26
|
if (invoice.stripePdfUrl) {
|
|
24
27
|
window.open(invoice.stripePdfUrl, "_blank");
|
|
@@ -42,67 +45,67 @@ export function InvoiceDetails({
|
|
|
42
45
|
return invoice.stripeInvoiceId.slice(-8);
|
|
43
46
|
};
|
|
44
47
|
|
|
45
|
-
const productName = invoice.subscription?.price?.product?.name || "
|
|
48
|
+
const productName = invoice.subscription?.price?.product?.name || t("billing.ui.cards.subscriptions.fallback_name");
|
|
46
49
|
|
|
47
50
|
return (
|
|
48
51
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
49
52
|
<DialogContent className="max-w-2xl">
|
|
50
53
|
<DialogHeader>
|
|
51
|
-
<DialogTitle>
|
|
52
|
-
<DialogDescription>{formatDate(invoice.periodStart)}</DialogDescription>
|
|
54
|
+
<DialogTitle>{t("billing.ui.invoice.title", { number: getInvoiceNumber() })}</DialogTitle>
|
|
55
|
+
<DialogDescription>{formatDate(invoice.periodStart, fmt)}</DialogDescription>
|
|
53
56
|
</DialogHeader>
|
|
54
57
|
|
|
55
58
|
<div className="space-y-6">
|
|
56
59
|
{/* Status */}
|
|
57
60
|
<div className="flex items-center gap-x-3">
|
|
58
|
-
<span className="text-sm font-medium text-muted-foreground">
|
|
61
|
+
<span className="text-sm font-medium text-muted-foreground">{t("billing.ui.labels.status")}</span>
|
|
59
62
|
<InvoiceStatusBadge status={invoice.status} />
|
|
60
63
|
</div>
|
|
61
64
|
|
|
62
65
|
{/* Invoice Info Grid */}
|
|
63
66
|
<div className="grid grid-cols-2 gap-4">
|
|
64
67
|
<div>
|
|
65
|
-
<span className="text-muted-foreground text-xs">
|
|
68
|
+
<span className="text-muted-foreground text-xs">{t("billing.ui.invoice.billing_period")}</span>
|
|
66
69
|
<p className="text-sm">
|
|
67
|
-
{formatDate(invoice.periodStart)} - {formatDate(invoice.periodEnd)}
|
|
70
|
+
{formatDate(invoice.periodStart, fmt)} - {formatDate(invoice.periodEnd, fmt)}
|
|
68
71
|
</p>
|
|
69
72
|
</div>
|
|
70
73
|
|
|
71
74
|
{invoice.dueDate && (
|
|
72
75
|
<div>
|
|
73
|
-
<span className="text-muted-foreground text-xs">
|
|
74
|
-
<p className="text-sm">{formatDate(invoice.dueDate)}</p>
|
|
76
|
+
<span className="text-muted-foreground text-xs">{t("billing.ui.invoice.due_date")}</span>
|
|
77
|
+
<p className="text-sm">{formatDate(invoice.dueDate, fmt)}</p>
|
|
75
78
|
</div>
|
|
76
79
|
)}
|
|
77
80
|
|
|
78
81
|
{invoice.paidAt && (
|
|
79
82
|
<div>
|
|
80
|
-
<span className="text-muted-foreground text-xs">
|
|
81
|
-
<p className="text-sm">{formatDate(invoice.paidAt)}</p>
|
|
83
|
+
<span className="text-muted-foreground text-xs">{t("billing.ui.invoice.paid_date")}</span>
|
|
84
|
+
<p className="text-sm">{formatDate(invoice.paidAt, fmt)}</p>
|
|
82
85
|
</div>
|
|
83
86
|
)}
|
|
84
87
|
|
|
85
88
|
<div>
|
|
86
|
-
<span className="text-muted-foreground text-xs">
|
|
89
|
+
<span className="text-muted-foreground text-xs">{t("billing.ui.invoice.attempt_count")}</span>
|
|
87
90
|
<p className="text-sm">{invoice.attemptCount}</p>
|
|
88
91
|
</div>
|
|
89
92
|
</div>
|
|
90
93
|
|
|
91
94
|
{/* Line Items */}
|
|
92
95
|
<div>
|
|
93
|
-
<h4 className="text-sm font-medium text-muted-foreground mb-2">
|
|
96
|
+
<h4 className="text-sm font-medium text-muted-foreground mb-2">{t("billing.ui.invoice.line_items")}</h4>
|
|
94
97
|
<div className="border rounded-lg overflow-clip">
|
|
95
98
|
<table className="w-full">
|
|
96
99
|
<thead className="bg-muted">
|
|
97
100
|
<tr>
|
|
98
|
-
<th className="text-start p-3 text-sm font-medium">
|
|
99
|
-
<th className="text-end p-3 text-sm font-medium">
|
|
101
|
+
<th className="text-start p-3 text-sm font-medium">{t("billing.ui.columns.description")}</th>
|
|
102
|
+
<th className="text-end p-3 text-sm font-medium">{t("billing.ui.columns.amount")}</th>
|
|
100
103
|
</tr>
|
|
101
104
|
</thead>
|
|
102
105
|
<tbody>
|
|
103
106
|
<tr className="border-t">
|
|
104
107
|
<td className="p-3">{productName}</td>
|
|
105
|
-
<td className="p-3 text-end">{formatCurrency(invoice.subtotal, invoice.currency)}</td>
|
|
108
|
+
<td className="p-3 text-end">{formatCurrency(invoice.subtotal, invoice.currency, fmt)}</td>
|
|
106
109
|
</tr>
|
|
107
110
|
</tbody>
|
|
108
111
|
</table>
|
|
@@ -112,34 +115,36 @@ export function InvoiceDetails({
|
|
|
112
115
|
{/* Amount Breakdown */}
|
|
113
116
|
<div className="space-y-2 border-t pt-4">
|
|
114
117
|
<div className="flex justify-between">
|
|
115
|
-
<span className="text-muted-foreground text-xs">
|
|
116
|
-
<span className="text-sm">{formatCurrency(invoice.subtotal, invoice.currency)}</span>
|
|
118
|
+
<span className="text-muted-foreground text-xs">{t("billing.ui.invoice.subtotal")}</span>
|
|
119
|
+
<span className="text-sm">{formatCurrency(invoice.subtotal, invoice.currency, fmt)}</span>
|
|
117
120
|
</div>
|
|
118
121
|
|
|
119
122
|
{invoice.tax !== undefined && invoice.tax > 0 && (
|
|
120
123
|
<div className="flex justify-between">
|
|
121
|
-
<span className="text-muted-foreground text-xs">
|
|
122
|
-
<span className="text-sm">{formatCurrency(invoice.tax, invoice.currency)}</span>
|
|
124
|
+
<span className="text-muted-foreground text-xs">{t("billing.ui.invoice.tax")}</span>
|
|
125
|
+
<span className="text-sm">{formatCurrency(invoice.tax, invoice.currency, fmt)}</span>
|
|
123
126
|
</div>
|
|
124
127
|
)}
|
|
125
128
|
|
|
126
129
|
<div className="flex justify-between text-lg font-semibold border-t pt-2">
|
|
127
|
-
<span>
|
|
128
|
-
<span>{formatCurrency(invoice.total, invoice.currency)}</span>
|
|
130
|
+
<span>{t("billing.ui.invoice.total")}</span>
|
|
131
|
+
<span>{formatCurrency(invoice.total, invoice.currency, fmt)}</span>
|
|
129
132
|
</div>
|
|
130
133
|
|
|
131
134
|
{invoice.amountPaid > 0 && (
|
|
132
135
|
<div className="flex justify-between">
|
|
133
|
-
<span className="text-muted-foreground text-xs">
|
|
134
|
-
<span className="text-sm text-success">
|
|
136
|
+
<span className="text-muted-foreground text-xs">{t("billing.ui.invoice.amount_paid")}</span>
|
|
137
|
+
<span className="text-sm text-success">
|
|
138
|
+
{formatCurrency(invoice.amountPaid, invoice.currency, fmt)}
|
|
139
|
+
</span>
|
|
135
140
|
</div>
|
|
136
141
|
)}
|
|
137
142
|
|
|
138
143
|
{invoice.amountRemaining > 0 && (
|
|
139
144
|
<div className="flex justify-between">
|
|
140
|
-
<span className="text-muted-foreground text-xs">
|
|
145
|
+
<span className="text-muted-foreground text-xs">{t("billing.ui.invoice.amount_due")}</span>
|
|
141
146
|
<span className="text-sm text-destructive">
|
|
142
|
-
{formatCurrency(invoice.amountRemaining, invoice.currency)}
|
|
147
|
+
{formatCurrency(invoice.amountRemaining, invoice.currency, fmt)}
|
|
143
148
|
</span>
|
|
144
149
|
</div>
|
|
145
150
|
)}
|
|
@@ -150,21 +155,21 @@ export function InvoiceDetails({
|
|
|
150
155
|
{invoice.stripePdfUrl && (
|
|
151
156
|
<Button variant="outline" onClick={handleDownloadPDF}>
|
|
152
157
|
<Download className="me-2 h-4 w-4" />
|
|
153
|
-
|
|
158
|
+
{t("billing.ui.invoice.download_pdf")}
|
|
154
159
|
</Button>
|
|
155
160
|
)}
|
|
156
161
|
|
|
157
162
|
{invoice.status === InvoiceStatus.OPEN && invoice.attempted && (
|
|
158
163
|
<Button variant="default" onClick={handleRetryPayment}>
|
|
159
164
|
<RefreshCw className="me-2 h-4 w-4" />
|
|
160
|
-
|
|
165
|
+
{t("billing.ui.invoice.retry_payment")}
|
|
161
166
|
</Button>
|
|
162
167
|
)}
|
|
163
168
|
|
|
164
169
|
{invoice.stripeHostedInvoiceUrl && (
|
|
165
170
|
<Button variant="outline" onClick={handleViewInStripe}>
|
|
166
171
|
<ExternalLink className="me-2 h-4 w-4" />
|
|
167
|
-
|
|
172
|
+
{t("billing.ui.invoice.view_in_stripe")}
|
|
168
173
|
</Button>
|
|
169
174
|
)}
|
|
170
175
|
</div>
|
|
@@ -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 { 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 { StripeInvoiceInterface } from "../../data/stripe-invoice.interface";
|
|
7
8
|
import { InvoiceDetails } from "../details/InvoiceDetails";
|
|
8
9
|
import { InvoiceStatusBadge } from "../widgets/InvoiceStatusBadge";
|
|
@@ -13,6 +14,8 @@ type InvoicesListProps = {
|
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export function InvoicesList({ invoices, onInvoicesChange }: InvoicesListProps) {
|
|
17
|
+
const t = useTranslations();
|
|
18
|
+
const fmt = useBillingFormatOptions();
|
|
16
19
|
const [selectedInvoice, setSelectedInvoice] = useState<StripeInvoiceInterface | null>(null);
|
|
17
20
|
|
|
18
21
|
const handleRowClick = (invoice: StripeInvoiceInterface) => {
|
|
@@ -33,19 +36,19 @@ export function InvoicesList({ invoices, onInvoicesChange }: InvoicesListProps)
|
|
|
33
36
|
<Table>
|
|
34
37
|
<TableHeader className="bg-muted">
|
|
35
38
|
<TableRow>
|
|
36
|
-
<TableHead>
|
|
37
|
-
<TableHead>
|
|
38
|
-
<TableHead>
|
|
39
|
-
<TableHead className="text-end">
|
|
40
|
-
<TableHead>
|
|
39
|
+
<TableHead>{t("billing.ui.columns.invoice_number")}</TableHead>
|
|
40
|
+
<TableHead>{t("billing.ui.columns.date")}</TableHead>
|
|
41
|
+
<TableHead>{t("billing.ui.columns.status")}</TableHead>
|
|
42
|
+
<TableHead className="text-end">{t("billing.ui.columns.amount")}</TableHead>
|
|
43
|
+
<TableHead>{t("billing.ui.columns.period")}</TableHead>
|
|
41
44
|
</TableRow>
|
|
42
45
|
</TableHeader>
|
|
43
46
|
<TableBody>
|
|
44
47
|
{invoices.map((invoice) => {
|
|
45
48
|
const invoiceNumber = getInvoiceNumber(invoice);
|
|
46
|
-
const date = formatDate(invoice.periodStart);
|
|
47
|
-
const amount = formatCurrency(invoice.total, invoice.currency);
|
|
48
|
-
const period = `${formatDate(invoice.periodStart)} - ${formatDate(invoice.periodEnd)}`;
|
|
49
|
+
const date = formatDate(invoice.periodStart, fmt);
|
|
50
|
+
const amount = formatCurrency(invoice.total, invoice.currency, fmt);
|
|
51
|
+
const period = `${formatDate(invoice.periodStart, fmt)} - ${formatDate(invoice.periodEnd, fmt)}`;
|
|
49
52
|
|
|
50
53
|
return (
|
|
51
54
|
<TableRow
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { Badge, type BadgeProps } from "../../../../../shadcnui";
|
|
4
5
|
import { InvoiceStatus } from "../../data/stripe-invoice.interface";
|
|
5
6
|
|
|
@@ -8,35 +9,37 @@ type InvoiceStatusBadgeProps = {
|
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
type StatusConfig = {
|
|
12
|
+
/** Message key, translated at render time. */
|
|
11
13
|
label: string;
|
|
12
14
|
variant: BadgeProps["variant"];
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
const statusConfig: Record<InvoiceStatus, StatusConfig> = {
|
|
16
18
|
[InvoiceStatus.DRAFT]: {
|
|
17
|
-
label: "
|
|
19
|
+
label: "billing.ui.invoice_status.draft",
|
|
18
20
|
variant: "softGray",
|
|
19
21
|
},
|
|
20
22
|
[InvoiceStatus.OPEN]: {
|
|
21
|
-
label: "
|
|
23
|
+
label: "billing.ui.invoice_status.open",
|
|
22
24
|
variant: "softBlue",
|
|
23
25
|
},
|
|
24
26
|
[InvoiceStatus.PAID]: {
|
|
25
|
-
label: "
|
|
27
|
+
label: "billing.ui.invoice_status.paid",
|
|
26
28
|
variant: "softGreen",
|
|
27
29
|
},
|
|
28
30
|
[InvoiceStatus.VOID]: {
|
|
29
|
-
label: "
|
|
31
|
+
label: "billing.ui.invoice_status.void",
|
|
30
32
|
variant: "softGray",
|
|
31
33
|
},
|
|
32
34
|
[InvoiceStatus.UNCOLLECTIBLE]: {
|
|
33
|
-
label: "
|
|
35
|
+
label: "billing.ui.invoice_status.uncollectible",
|
|
34
36
|
variant: "softRed",
|
|
35
37
|
},
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
export function InvoiceStatusBadge({ status }: InvoiceStatusBadgeProps) {
|
|
41
|
+
const t = useTranslations();
|
|
39
42
|
const config = statusConfig[status] || statusConfig[InvoiceStatus.DRAFT];
|
|
40
43
|
|
|
41
|
-
return <Badge variant={config.variant}>{config.label}</Badge>;
|
|
44
|
+
return <Badge variant={config.variant}>{t(config.label)}</Badge>;
|
|
42
45
|
}
|
|
@@ -5,11 +5,12 @@ import { DetailField, SectionHeader } from "../../../../../components";
|
|
|
5
5
|
import { Modules } from "../../../../../core";
|
|
6
6
|
import { usePageUrlGenerator } from "../../../../../hooks";
|
|
7
7
|
import { Badge, Link } from "../../../../../shadcnui";
|
|
8
|
-
import { formatCurrency, formatInterval } from "../../../components/utils
|
|
8
|
+
import { formatCurrency, formatInterval, useBillingFormatOptions } from "../../../components/utils";
|
|
9
9
|
import { usePriceContext } from "../../../contexts/PriceContext";
|
|
10
10
|
|
|
11
11
|
export function PriceDetails() {
|
|
12
12
|
const t = useTranslations();
|
|
13
|
+
const fmt = useBillingFormatOptions();
|
|
13
14
|
const generateUrl = usePageUrlGenerator();
|
|
14
15
|
const { price } = usePriceContext();
|
|
15
16
|
|
|
@@ -49,14 +50,14 @@ export function PriceDetails() {
|
|
|
49
50
|
/>
|
|
50
51
|
<DetailField
|
|
51
52
|
label={t("billing.admin.prices.fields.amount")}
|
|
52
|
-
value={<span className="tabular-nums">{formatCurrency(price.unitAmount, price.currency)}</span>}
|
|
53
|
+
value={<span className="tabular-nums">{formatCurrency(price.unitAmount, price.currency, fmt)}</span>}
|
|
53
54
|
/>
|
|
54
55
|
<DetailField label={t("billing.admin.prices.fields.currency")} value={price.currency.toUpperCase()} />
|
|
55
56
|
<DetailField
|
|
56
57
|
label={t("billing.admin.prices.fields.type")}
|
|
57
58
|
value={isRecurring ? t("billing.admin.prices.type.recurring") : t("billing.admin.prices.type.oneTime")}
|
|
58
59
|
/>
|
|
59
|
-
<DetailField label={t("billing.admin.prices.fields.billing")} value={formatInterval(price)} />
|
|
60
|
+
<DetailField label={t("billing.admin.prices.fields.billing")} value={formatInterval(price, fmt)} />
|
|
60
61
|
{isRecurring && (
|
|
61
62
|
<DetailField
|
|
62
63
|
label={t("billing.admin.prices.fields.intervalCount")}
|
|
@@ -5,6 +5,7 @@ import { useTranslations } from "next-intl";
|
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { errorToast } from "../../../../../components/errors";
|
|
7
7
|
import { Button, ConfirmDialog } from "../../../../../shadcnui";
|
|
8
|
+
import { useBillingFormatOptions } from "../../../components/utils";
|
|
8
9
|
import { priceLabel, usePriceContext } from "../../../contexts/PriceContext";
|
|
9
10
|
import { StripePriceInterface } from "../../data/stripe-price.interface";
|
|
10
11
|
|
|
@@ -20,6 +21,7 @@ type PriceArchiverProps = {
|
|
|
20
21
|
*/
|
|
21
22
|
export function PriceArchiver({ price }: PriceArchiverProps) {
|
|
22
23
|
const t = useTranslations();
|
|
24
|
+
const fmt = useBillingFormatOptions();
|
|
23
25
|
const { archivePrice, restorePrice } = usePriceContext();
|
|
24
26
|
const [open, setOpen] = useState<boolean>(false);
|
|
25
27
|
|
|
@@ -36,7 +38,7 @@ export function PriceArchiver({ price }: PriceArchiverProps) {
|
|
|
36
38
|
open={open}
|
|
37
39
|
onOpenChange={setOpen}
|
|
38
40
|
title={t(`billing.admin.prices.${scope}.title`)}
|
|
39
|
-
description={t(`billing.admin.prices.${scope}.description`, { label: priceLabel(price) })}
|
|
41
|
+
description={t(`billing.admin.prices.${scope}.description`, { label: priceLabel(price, fmt) })}
|
|
40
42
|
confirmLabel={t(`billing.admin.prices.${scope}.confirm`)}
|
|
41
43
|
cancelLabel={t("ui.buttons.cancel")}
|
|
42
44
|
destructive={!isArchived}
|
|
@@ -13,6 +13,7 @@ import { useI18nRouter } from "../../../../../i18n";
|
|
|
13
13
|
import { Alert, AlertDescription, AlertTitle, Button, Checkbox, Input } from "../../../../../shadcnui";
|
|
14
14
|
import { showToast } from "../../../../../utils/toast";
|
|
15
15
|
import { FeatureInterface, FeatureService } from "../../../../feature";
|
|
16
|
+
import { useBillingFormatOptions } from "../../../components/utils";
|
|
16
17
|
import { priceLabel, usePriceContext } from "../../../contexts/PriceContext";
|
|
17
18
|
import { StripePriceInput, StripePriceInterface } from "../../data/stripe-price.interface";
|
|
18
19
|
import { StripePriceSeed } from "../../data/stripe-price.transfer";
|
|
@@ -54,6 +55,7 @@ function PriceEditorInternal({
|
|
|
54
55
|
onDialogOpenChange,
|
|
55
56
|
}: PriceEditorProps) {
|
|
56
57
|
const t = useTranslations();
|
|
58
|
+
const fmt = useBillingFormatOptions();
|
|
57
59
|
const router = useI18nRouter();
|
|
58
60
|
const { createPrice, updatePrice, productId: contextProductId } = usePriceContext();
|
|
59
61
|
const [allFeatures, setAllFeatures] = useState<FeatureInterface[]>([]);
|
|
@@ -266,7 +268,7 @@ function PriceEditorInternal({
|
|
|
266
268
|
<EditorSheet
|
|
267
269
|
form={form}
|
|
268
270
|
entityType={t("billing.admin.prices.entity")}
|
|
269
|
-
entityName={price ? priceLabel(price) : undefined}
|
|
271
|
+
entityName={price ? priceLabel(price, fmt) : undefined}
|
|
270
272
|
isEdit={isEdit}
|
|
271
273
|
module={Modules.StripePrice}
|
|
272
274
|
size="lg"
|
|
@@ -7,7 +7,7 @@ import { useMemo } from "react";
|
|
|
7
7
|
import { Modules } from "../../../../core";
|
|
8
8
|
import { registerTableGenerator, TableContent, usePageUrlGenerator, UseTableStructureHook } from "../../../../hooks";
|
|
9
9
|
import { Badge, Button, Link } from "../../../../shadcnui";
|
|
10
|
-
import { formatCurrency, formatInterval } from "../../components/utils
|
|
10
|
+
import { formatCurrency, formatInterval, useBillingFormatOptions } from "../../components/utils";
|
|
11
11
|
import { StripePriceFields } from "../data/stripe-price.fields";
|
|
12
12
|
import { StripePriceInterface } from "../data/stripe-price.interface";
|
|
13
13
|
|
|
@@ -31,6 +31,7 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
31
31
|
params,
|
|
32
32
|
) => {
|
|
33
33
|
const t = useTranslations();
|
|
34
|
+
const fmt = useBillingFormatOptions();
|
|
34
35
|
const generateUrl = usePageUrlGenerator();
|
|
35
36
|
|
|
36
37
|
const onClone = params.context?.onClone as StripePriceRowActions["onClone"];
|
|
@@ -56,7 +57,7 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
56
57
|
const price: StripePriceInterface = row.original.jsonApiData;
|
|
57
58
|
return (
|
|
58
59
|
<Link href={generateUrl({ page: Modules.StripePrice, id: price.id })}>
|
|
59
|
-
{price.nickname ?? formatCurrency(price.unitAmount, price.currency)}
|
|
60
|
+
{price.nickname ?? formatCurrency(price.unitAmount, price.currency, fmt)}
|
|
60
61
|
</Link>
|
|
61
62
|
);
|
|
62
63
|
},
|
|
@@ -72,7 +73,7 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
72
73
|
// Typography role 15 (numeric): tabular-nums, end-aligned. NEVER font-mono.
|
|
73
74
|
return (
|
|
74
75
|
<span className="block text-xs tabular-nums text-end">
|
|
75
|
-
{formatCurrency(price.unitAmount, price.currency)}
|
|
76
|
+
{formatCurrency(price.unitAmount, price.currency, fmt)}
|
|
76
77
|
</span>
|
|
77
78
|
);
|
|
78
79
|
},
|
|
@@ -86,7 +87,7 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
86
87
|
cell: ({ row }: { row: TableContent<StripePriceInterface> }) => {
|
|
87
88
|
const price: StripePriceInterface = row.original.jsonApiData;
|
|
88
89
|
// Typography role 8 (UI chrome): single-line, plain text-xs.
|
|
89
|
-
return <span className="text-xs">{formatInterval(price)}</span>;
|
|
90
|
+
return <span className="text-xs">{formatInterval(price, fmt)}</span>;
|
|
90
91
|
},
|
|
91
92
|
enableSorting: false,
|
|
92
93
|
enableHiding: false,
|
|
@@ -175,7 +176,7 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
175
176
|
return params.fields.map((field) => fieldColumnMap[field]?.()).filter((col) => col !== undefined) as ColumnDef<
|
|
176
177
|
TableContent<StripePriceInterface>
|
|
177
178
|
>[];
|
|
178
|
-
}, [params.fields, fieldColumnMap, t, generateUrl, onClone, onExport]);
|
|
179
|
+
}, [params.fields, fieldColumnMap, t, fmt, generateUrl, onClone, onExport]);
|
|
179
180
|
|
|
180
181
|
return useMemo(() => ({ data: tableData, columns: columns }), [tableData, columns]);
|
|
181
182
|
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { useState } from "react";
|
|
4
5
|
import { Check, X, Loader2 } from "lucide-react";
|
|
5
6
|
import { Button, Input } from "../../../../shadcnui";
|
|
7
|
+
import { useI18nLocale } from "../../../../i18n";
|
|
8
|
+
import { formatFixed } from "../../components/utils";
|
|
6
9
|
import { PromotionCodeValidationResult } from "../data/stripe-promotion-code.interface";
|
|
7
10
|
|
|
8
11
|
type PromoCodeInputProps = {
|
|
@@ -22,6 +25,8 @@ export function PromoCodeInput({
|
|
|
22
25
|
onRemove,
|
|
23
26
|
disabled = false,
|
|
24
27
|
}: PromoCodeInputProps) {
|
|
28
|
+
const t = useTranslations();
|
|
29
|
+
const locale = useI18nLocale();
|
|
25
30
|
const [code, setCode] = useState("");
|
|
26
31
|
|
|
27
32
|
const handleApply = () => {
|
|
@@ -40,23 +45,23 @@ export function PromoCodeInput({
|
|
|
40
45
|
// Format discount for display
|
|
41
46
|
const formatDiscount = (result: PromotionCodeValidationResult): string => {
|
|
42
47
|
if (result.discountType === "percent_off") {
|
|
43
|
-
return
|
|
48
|
+
return t("billing.ui.wizard.review.percent_off", { value: String(result.discountValue) });
|
|
44
49
|
}
|
|
45
50
|
// amount_off is in cents, convert to dollars
|
|
46
51
|
const amount = (result.discountValue || 0) / 100;
|
|
47
52
|
const currency = result.currency?.toUpperCase() || "USD";
|
|
48
|
-
return `${currency} ${amount
|
|
53
|
+
return t("billing.ui.wizard.review.amount_off", { amount: `${currency} ${formatFixed(amount, locale, 2)}` });
|
|
49
54
|
};
|
|
50
55
|
|
|
51
56
|
// Format duration for display
|
|
52
57
|
const formatDuration = (result: PromotionCodeValidationResult): string => {
|
|
53
58
|
switch (result.duration) {
|
|
54
59
|
case "forever":
|
|
55
|
-
return "
|
|
60
|
+
return t("billing.ui.promo_code.duration_forever");
|
|
56
61
|
case "once":
|
|
57
|
-
return "
|
|
62
|
+
return t("billing.ui.promo_code.duration_once");
|
|
58
63
|
case "repeating":
|
|
59
|
-
return
|
|
64
|
+
return t("billing.ui.promo_code.duration_repeating", { months: String(result.durationInMonths) });
|
|
60
65
|
default:
|
|
61
66
|
return "";
|
|
62
67
|
}
|
|
@@ -91,7 +96,7 @@ export function PromoCodeInput({
|
|
|
91
96
|
<div className="space-y-2">
|
|
92
97
|
<div className="flex gap-2">
|
|
93
98
|
<Input
|
|
94
|
-
placeholder="
|
|
99
|
+
placeholder={t("billing.ui.promo_code.placeholder")}
|
|
95
100
|
value={code}
|
|
96
101
|
onChange={(e) => setCode(e.target.value.toUpperCase())}
|
|
97
102
|
onKeyDown={handleKeyDown}
|
|
@@ -99,7 +104,7 @@ export function PromoCodeInput({
|
|
|
99
104
|
className="flex-1"
|
|
100
105
|
/>
|
|
101
106
|
<Button variant="outline" onClick={handleApply} disabled={disabled || isValidating || !code.trim()}>
|
|
102
|
-
{isValidating ? <Loader2 className="h-4 w-4 animate-spin" /> : "
|
|
107
|
+
{isValidating ? <Loader2 className="h-4 w-4 animate-spin" /> : t("billing.ui.promo_code.apply")}
|
|
103
108
|
</Button>
|
|
104
109
|
</div>
|
|
105
110
|
{error && <p className="text-destructive text-xs/relaxed">{error}</p>}
|
|
@@ -42,7 +42,9 @@ export class StripePromotionCodeService {
|
|
|
42
42
|
return {
|
|
43
43
|
valid: false,
|
|
44
44
|
code: params.code,
|
|
45
|
-
|
|
45
|
+
// No English fallback here: a service cannot translate, so the caller words
|
|
46
|
+
// the missing message in the active language.
|
|
47
|
+
errorMessage: response.error || undefined,
|
|
46
48
|
};
|
|
47
49
|
}
|
|
48
50
|
|
package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { CreditCard } from "lucide-react";
|
|
4
5
|
import { useCallback, useEffect, useState } from "react";
|
|
5
6
|
import { Button } from "../../../../../shadcnui";
|
|
@@ -13,6 +14,7 @@ type SubscriptionsContainerProps = {
|
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export function SubscriptionsContainer({ onOpenWizard, hasActiveRecurringSubscription }: SubscriptionsContainerProps) {
|
|
17
|
+
const t = useTranslations();
|
|
16
18
|
const [subscriptions, setSubscriptions] = useState<StripeSubscriptionInterface[]>([]);
|
|
17
19
|
const [loading, setLoading] = useState<boolean>(true);
|
|
18
20
|
|
|
@@ -44,7 +46,7 @@ export function SubscriptionsContainer({ onOpenWizard, hasActiveRecurringSubscri
|
|
|
44
46
|
if (loading) {
|
|
45
47
|
return (
|
|
46
48
|
<div className="flex h-64 items-center justify-center">
|
|
47
|
-
<p className="text-muted-foreground">
|
|
49
|
+
<p className="text-muted-foreground">{t("billing.ui.subscriptions.loading")}</p>
|
|
48
50
|
</div>
|
|
49
51
|
);
|
|
50
52
|
}
|
|
@@ -55,11 +57,13 @@ export function SubscriptionsContainer({ onOpenWizard, hasActiveRecurringSubscri
|
|
|
55
57
|
<div className="flex items-center justify-between">
|
|
56
58
|
<div className="flex items-center gap-x-3">
|
|
57
59
|
<CreditCard className="h-8 w-8" />
|
|
58
|
-
<h1 className="text-primary text-3xl font-semibold">
|
|
60
|
+
<h1 className="text-primary text-3xl font-semibold">{t("billing.ui.subscriptions.title")}</h1>
|
|
59
61
|
</div>
|
|
60
62
|
{subscriptions.length > 0 && (
|
|
61
63
|
<Button onClick={() => onOpenWizard?.()}>
|
|
62
|
-
{hasActiveRecurringSubscription
|
|
64
|
+
{hasActiveRecurringSubscription
|
|
65
|
+
? t("billing.ui.subscriptions.purchase_addons")
|
|
66
|
+
: t("billing.ui.subscriptions.subscribe")}
|
|
63
67
|
</Button>
|
|
64
68
|
)}
|
|
65
69
|
</div>
|
|
@@ -74,9 +78,9 @@ export function SubscriptionsContainer({ onOpenWizard, hasActiveRecurringSubscri
|
|
|
74
78
|
<div className="flex flex-col items-center justify-center py-12 space-y-4">
|
|
75
79
|
<CreditCard className="h-16 w-16 text-muted-foreground" />
|
|
76
80
|
<div className="text-center">
|
|
77
|
-
<h3 className="text-sm font-medium mb-2">
|
|
78
|
-
<p className="text-muted-foreground mb-6">
|
|
79
|
-
<Button onClick={() => onOpenWizard?.()}>
|
|
81
|
+
<h3 className="text-sm font-medium mb-2">{t("billing.ui.subscriptions.empty_title")}</h3>
|
|
82
|
+
<p className="text-muted-foreground mb-6">{t("billing.ui.subscriptions.empty_description")}</p>
|
|
83
|
+
<Button onClick={() => onOpenWizard?.()}>{t("billing.ui.subscriptions.subscribe")}</Button>
|
|
80
84
|
</div>
|
|
81
85
|
</div>
|
|
82
86
|
)}
|