@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,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 {
|
|
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 "
|
|
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: "
|
|
27
|
-
week: "
|
|
28
|
-
month: "
|
|
29
|
-
year: "
|
|
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 || "
|
|
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(
|
|
46
|
-
|
|
47
|
-
|
|
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>
|
|
114
|
-
<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">
|
|
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">
|
|
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">
|
|
135
|
-
<span className="text-sm">
|
|
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">
|
|
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">
|
|
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
|
-
|
|
162
|
-
|
|
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
|
-
|
|
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 ? "
|
|
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 ? "
|
|
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
|
-
|
|
204
|
+
{t("ui.buttons.cancel")}
|
|
190
205
|
</Button>
|
|
191
206
|
)}
|
|
192
207
|
|
|
193
208
|
<Button variant="outline" onClick={handleManageViaPortal}>
|
|
194
|
-
|
|
209
|
+
{t("billing.ui.subscription.manage_portal")}
|
|
195
210
|
</Button>
|
|
196
211
|
</div>
|
|
197
212
|
</div>
|
package/src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx
CHANGED
|
@@ -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>
|
|
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
|
-
|
|
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="
|
|
86
|
-
placeholder="
|
|
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
|
-
|
|
93
|
+
{t("billing.ui.subscription.cancel.keep")}
|
|
93
94
|
</Button>
|
|
94
95
|
<Button type="submit" variant="destructive" disabled={isSubmitting}>
|
|
95
|
-
{isSubmitting
|
|
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 "
|
|
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: "
|
|
26
|
-
week: "
|
|
27
|
-
month: "
|
|
28
|
-
year: "
|
|
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 || "
|
|
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>
|
|
61
|
-
<TableHead>
|
|
62
|
-
<TableHead>
|
|
63
|
-
<TableHead className="text-end">
|
|
64
|
-
<TableHead className="text-end">
|
|
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
|
-
|
|
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">
|
|
24
|
-
<TabsTrigger value="year">
|
|
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
|
|
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={
|
|
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
|
-
|
|
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
|
|
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">
|
|
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={
|
|
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">
|
|
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">
|
|
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
|
-
|
|
35
|
-
|
|
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>
|
package/src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx
CHANGED
|
@@ -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
|
|
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">
|
|
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
|
-
|
|
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
|
-
|
|
61
|
+
{t("billing.ui.wizard.review.processing")}
|
|
59
62
|
</>
|
|
60
63
|
) : (
|
|
61
|
-
"
|
|
64
|
+
t("billing.ui.confirmation.subscribe")
|
|
62
65
|
)}
|
|
63
66
|
</Button>
|
|
64
67
|
</div>
|