@carlonicora/nextjs-jsonapi 4.1.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/scripts/vitest-slow-tests.reporter.spec.ts +54 -0
- 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 +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/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.test.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 +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__/use-ui-text.test.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 +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/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx
CHANGED
|
@@ -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 { SubscriptionStatus } from "../../data";
|
|
5
6
|
|
|
@@ -9,55 +10,57 @@ type SubscriptionStatusBadgeProps = {
|
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
type StatusConfig = {
|
|
13
|
+
/** Message key, translated at render time. */
|
|
12
14
|
label: string;
|
|
13
15
|
variant: BadgeProps["variant"];
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
const statusConfig: Record<SubscriptionStatus, StatusConfig> = {
|
|
17
19
|
[SubscriptionStatus.ACTIVE]: {
|
|
18
|
-
label: "
|
|
20
|
+
label: "billing.ui.subscription_status.active",
|
|
19
21
|
variant: "softGreen",
|
|
20
22
|
},
|
|
21
23
|
[SubscriptionStatus.TRIALING]: {
|
|
22
|
-
label: "
|
|
24
|
+
label: "billing.ui.subscription_status.trialing",
|
|
23
25
|
variant: "softBlue",
|
|
24
26
|
},
|
|
25
27
|
[SubscriptionStatus.PAST_DUE]: {
|
|
26
|
-
label: "
|
|
28
|
+
label: "billing.ui.subscription_status.past_due",
|
|
27
29
|
variant: "softRed",
|
|
28
30
|
},
|
|
29
31
|
[SubscriptionStatus.CANCELED]: {
|
|
30
|
-
label: "
|
|
32
|
+
label: "billing.ui.subscription_status.canceled",
|
|
31
33
|
variant: "softGray",
|
|
32
34
|
},
|
|
33
35
|
[SubscriptionStatus.PAUSED]: {
|
|
34
|
-
label: "
|
|
36
|
+
label: "billing.ui.subscription_status.paused",
|
|
35
37
|
variant: "softYellow",
|
|
36
38
|
},
|
|
37
39
|
[SubscriptionStatus.UNPAID]: {
|
|
38
|
-
label: "
|
|
40
|
+
label: "billing.ui.subscription_status.unpaid",
|
|
39
41
|
variant: "softOrange",
|
|
40
42
|
},
|
|
41
43
|
[SubscriptionStatus.INCOMPLETE]: {
|
|
42
|
-
label: "
|
|
44
|
+
label: "billing.ui.subscription_status.incomplete",
|
|
43
45
|
variant: "softGray",
|
|
44
46
|
},
|
|
45
47
|
[SubscriptionStatus.INCOMPLETE_EXPIRED]: {
|
|
46
|
-
label: "
|
|
48
|
+
label: "billing.ui.subscription_status.incomplete_expired",
|
|
47
49
|
variant: "softGray",
|
|
48
50
|
},
|
|
49
51
|
};
|
|
50
52
|
|
|
51
53
|
const cancelingConfig: StatusConfig = {
|
|
52
|
-
label: "
|
|
54
|
+
label: "billing.ui.cards.subscriptions.canceling",
|
|
53
55
|
variant: "softAmber",
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
export function SubscriptionStatusBadge({ status, cancelAtPeriodEnd }: SubscriptionStatusBadgeProps) {
|
|
59
|
+
const t = useTranslations();
|
|
57
60
|
// Show "Canceling" when subscription is set to cancel at period end
|
|
58
61
|
const config = cancelAtPeriodEnd
|
|
59
62
|
? cancelingConfig
|
|
60
63
|
: statusConfig[status] || statusConfig[SubscriptionStatus.CANCELED];
|
|
61
64
|
|
|
62
|
-
return <Badge variant={config.variant}>{config.label}</Badge>;
|
|
65
|
+
return <Badge variant={config.variant}>{t(config.label)}</Badge>;
|
|
63
66
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { useCallback, useEffect, useRef } from "react";
|
|
4
5
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../../../../../shadcnui";
|
|
5
6
|
import { StripeSubscriptionInterface } from "../../data";
|
|
@@ -24,6 +25,7 @@ export function SubscriptionWizard({
|
|
|
24
25
|
hasActiveRecurringSubscription,
|
|
25
26
|
subscription,
|
|
26
27
|
}: SubscriptionWizardProps) {
|
|
28
|
+
const t = useTranslations();
|
|
27
29
|
const handleClose = useCallback(() => onOpenChange(false), [onOpenChange]);
|
|
28
30
|
|
|
29
31
|
const { state, actions } = useSubscriptionWizard({
|
|
@@ -57,16 +59,16 @@ export function SubscriptionWizard({
|
|
|
57
59
|
const isChangePlanMode = !!subscription;
|
|
58
60
|
|
|
59
61
|
const dialogTitle = subscription
|
|
60
|
-
? "
|
|
62
|
+
? t("billing.ui.wizard.change_title")
|
|
61
63
|
: isPurchasingAddons
|
|
62
|
-
? "
|
|
63
|
-
: "
|
|
64
|
+
? t("billing.ui.subscriptions.purchase_addons")
|
|
65
|
+
: t("billing.ui.subscriptions.subscribe");
|
|
64
66
|
|
|
65
67
|
const dialogDescription = subscription
|
|
66
|
-
? "
|
|
68
|
+
? t("billing.ui.wizard.change_description")
|
|
67
69
|
: isPurchasingAddons
|
|
68
|
-
? "
|
|
69
|
-
: "
|
|
70
|
+
? t("billing.ui.wizard.addons_description")
|
|
71
|
+
: t("billing.ui.wizard.subscribe_description");
|
|
70
72
|
|
|
71
73
|
return (
|
|
72
74
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
package/src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { Check } from "lucide-react";
|
|
4
5
|
import { WizardStep } from "../../hooks/useSubscriptionWizard";
|
|
5
6
|
|
|
@@ -7,10 +8,11 @@ type WizardProgressIndicatorProps = {
|
|
|
7
8
|
currentStep: WizardStep;
|
|
8
9
|
};
|
|
9
10
|
|
|
11
|
+
// `label` is a message key, translated at render time.
|
|
10
12
|
const STEPS: { key: WizardStep; label: string }[] = [
|
|
11
|
-
{ key: "plan-selection", label: "
|
|
12
|
-
{ key: "review", label: "
|
|
13
|
-
{ key: "payment-method", label: "
|
|
13
|
+
{ key: "plan-selection", label: "billing.ui.wizard.steps.select_plan" },
|
|
14
|
+
{ key: "review", label: "billing.ui.wizard.steps.review" },
|
|
15
|
+
{ key: "payment-method", label: "billing.ui.wizard.steps.payment" },
|
|
14
16
|
];
|
|
15
17
|
|
|
16
18
|
function getStepIndex(step: WizardStep): number {
|
|
@@ -18,6 +20,7 @@ function getStepIndex(step: WizardStep): number {
|
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export function WizardProgressIndicator({ currentStep }: WizardProgressIndicatorProps) {
|
|
23
|
+
const t = useTranslations();
|
|
21
24
|
const currentIndex = getStepIndex(currentStep);
|
|
22
25
|
|
|
23
26
|
return (
|
|
@@ -43,7 +46,7 @@ export function WizardProgressIndicator({ currentStep }: WizardProgressIndicator
|
|
|
43
46
|
|
|
44
47
|
{/* Step Label */}
|
|
45
48
|
<span className={`text-sm ${isCurrent ? "font-medium text-foreground" : "text-muted-foreground"}`}>
|
|
46
|
-
{step.label}
|
|
49
|
+
{t(step.label)}
|
|
47
50
|
</span>
|
|
48
51
|
|
|
49
52
|
{/* Connector */}
|
package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { PaymentMethodForm } from "../../../stripe-customer/components/forms/PaymentMethodForm";
|
|
4
5
|
import { SectionHeader } from "../../../../../components/typography";
|
|
5
6
|
|
|
@@ -10,11 +11,12 @@ type WizardStepPaymentMethodProps = {
|
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export function WizardStepPaymentMethod({ onBack, onSuccess, isProcessing }: WizardStepPaymentMethodProps) {
|
|
14
|
+
const t = useTranslations();
|
|
13
15
|
return (
|
|
14
16
|
<div className="space-y-6">
|
|
15
17
|
<div className="text-center">
|
|
16
|
-
<SectionHeader>
|
|
17
|
-
<p className="text-sm text-muted-foreground">
|
|
18
|
+
<SectionHeader>{t("billing.ui.payment_methods.add")}</SectionHeader>
|
|
19
|
+
<p className="text-sm text-muted-foreground">{t("billing.ui.wizard.payment_description")}</p>
|
|
18
20
|
</div>
|
|
19
21
|
|
|
20
22
|
<PaymentMethodForm onSuccess={onSuccess} onCancel={onBack} isLoading={isProcessing} />
|
package/src/features/billing/stripe-subscription/components/wizards/WizardStepPlanSelection.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { useEffect, useMemo, useState } from "react";
|
|
4
5
|
import { Button } from "../../../../../shadcnui";
|
|
5
6
|
import { StripeProductInterface, StripeProductService } from "../../../stripe-product";
|
|
@@ -30,6 +31,7 @@ export function WizardStepPlanSelection({
|
|
|
30
31
|
onNext,
|
|
31
32
|
isProcessing,
|
|
32
33
|
}: WizardStepPlanSelectionProps) {
|
|
34
|
+
const t = useTranslations();
|
|
33
35
|
const [products, setProducts] = useState<StripeProductInterface[]>([]);
|
|
34
36
|
const [loading, setLoading] = useState(true);
|
|
35
37
|
|
|
@@ -100,7 +102,7 @@ export function WizardStepPlanSelection({
|
|
|
100
102
|
{/* Action Buttons */}
|
|
101
103
|
<div className="flex justify-end pt-4 border-t">
|
|
102
104
|
<Button onClick={onNext} disabled={!selectedPrice || isProcessing}>
|
|
103
|
-
{isProcessing ? "
|
|
105
|
+
{isProcessing ? t("common.loading") : t("billing.ui.wizard.next_review")}
|
|
104
106
|
</Button>
|
|
105
107
|
</div>
|
|
106
108
|
</div>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { AlertCircle } from "lucide-react";
|
|
4
5
|
import { Alert, AlertDescription, Badge, Button } from "../../../../../shadcnui";
|
|
5
6
|
import { SectionHeader } from "../../../../../components/typography";
|
|
6
|
-
import { formatCurrency } from "../../../components/utils
|
|
7
|
+
import { formatCurrency, useBillingFormatOptions } from "../../../components/utils";
|
|
7
8
|
import { StripePriceInterface } from "../../../stripe-price/data/stripe-price.interface";
|
|
8
9
|
import { ProrationPreviewInterface } from "../../../stripe-invoice/data/stripe-invoice.interface";
|
|
9
10
|
import { PromotionCodeValidationResult } from "../../../stripe-promotion-code";
|
|
@@ -47,18 +48,20 @@ export function WizardStepReview({
|
|
|
47
48
|
onRemovePromoCode,
|
|
48
49
|
isTrialUpgrade,
|
|
49
50
|
}: WizardStepReviewProps) {
|
|
51
|
+
const t = useTranslations();
|
|
52
|
+
const fmt = useBillingFormatOptions();
|
|
50
53
|
if (!selectedPrice) {
|
|
51
|
-
return (
|
|
52
|
-
<div className="text-center py-8 text-muted-foreground">No plan selected. Please go back and select a plan.</div>
|
|
53
|
-
);
|
|
54
|
+
return <div className="text-center py-8 text-muted-foreground">{t("billing.ui.wizard.review.no_plan")}</div>;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
const isChangingPlan = subscription && subscription.price?.id !== selectedPrice.id;
|
|
57
58
|
|
|
58
59
|
const formatInterval = (price: StripePriceInterface) => {
|
|
59
|
-
if (price.priceType === "one_time") return "
|
|
60
|
+
if (price.priceType === "one_time") return t("billing.ui.wizard.review.interval_one_time");
|
|
60
61
|
const interval = price.recurring?.interval || "month";
|
|
61
|
-
return interval === "year"
|
|
62
|
+
return interval === "year"
|
|
63
|
+
? t("billing.ui.wizard.review.interval_yearly")
|
|
64
|
+
: t("billing.ui.wizard.review.interval_monthly");
|
|
62
65
|
};
|
|
63
66
|
|
|
64
67
|
// Calculate discounted price if promotion code is applied
|
|
@@ -105,11 +108,13 @@ export function WizardStepReview({
|
|
|
105
108
|
if (!promotionCode?.valid) return null;
|
|
106
109
|
|
|
107
110
|
if (promotionCode.discountType === "percent_off" && promotionCode.discountValue) {
|
|
108
|
-
return
|
|
111
|
+
return t("billing.ui.wizard.review.percent_off", { value: promotionCode.discountValue });
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
if (promotionCode.discountType === "amount_off" && promotionCode.discountValue) {
|
|
112
|
-
return
|
|
115
|
+
return t("billing.ui.wizard.review.amount_off", {
|
|
116
|
+
amount: formatCurrency(promotionCode.discountValue, promotionCode.currency || selectedPrice.currency, fmt),
|
|
117
|
+
});
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
return null;
|
|
@@ -121,7 +126,7 @@ export function WizardStepReview({
|
|
|
121
126
|
<div className="space-y-6">
|
|
122
127
|
{/* Selected Plan Summary */}
|
|
123
128
|
<div className="bg-muted/50 rounded-lg p-4 space-y-3">
|
|
124
|
-
<SectionHeader>
|
|
129
|
+
<SectionHeader>{t("billing.ui.wizard.review.selected_plan")}</SectionHeader>
|
|
125
130
|
<div className="flex justify-between items-center">
|
|
126
131
|
<div>
|
|
127
132
|
<p className="font-medium">{selectedPrice.product?.name}</p>
|
|
@@ -131,16 +136,16 @@ export function WizardStepReview({
|
|
|
131
136
|
{discountedPrice !== null ? (
|
|
132
137
|
<>
|
|
133
138
|
<p className="text-sm text-muted-foreground line-through">
|
|
134
|
-
{formatCurrency(selectedPrice.unitAmount || 0, selectedPrice.currency)}
|
|
139
|
+
{formatCurrency(selectedPrice.unitAmount || 0, selectedPrice.currency, fmt)}
|
|
135
140
|
</p>
|
|
136
141
|
<p className="text-success text-lg font-semibold">
|
|
137
|
-
{formatCurrency(discountedPrice, selectedPrice.currency)}
|
|
142
|
+
{formatCurrency(discountedPrice, selectedPrice.currency, fmt)}
|
|
138
143
|
</p>
|
|
139
144
|
{discountDescription && <Badge variant="softGreen">{discountDescription}</Badge>}
|
|
140
145
|
</>
|
|
141
146
|
) : (
|
|
142
147
|
<p className="font-semibold text-lg">
|
|
143
|
-
{formatCurrency(selectedPrice.unitAmount || 0, selectedPrice.currency)}
|
|
148
|
+
{formatCurrency(selectedPrice.unitAmount || 0, selectedPrice.currency, fmt)}
|
|
144
149
|
</p>
|
|
145
150
|
)}
|
|
146
151
|
<p className="text-sm text-muted-foreground">{formatInterval(selectedPrice)}</p>
|
|
@@ -154,29 +159,31 @@ export function WizardStepReview({
|
|
|
154
159
|
className={`${isTrialUpgrade ? "bg-warning/15 border-warning/30" : "bg-primary/10 border-primary/30"} space-y-2 rounded-lg border p-4`}
|
|
155
160
|
>
|
|
156
161
|
<h4 className={`font-medium ${isTrialUpgrade ? "text-warning" : "text-primary"}`}>
|
|
157
|
-
{isTrialUpgrade
|
|
162
|
+
{isTrialUpgrade
|
|
163
|
+
? t("billing.ui.wizard.review.trial_upgrade")
|
|
164
|
+
: t("billing.ui.wizard.review.proration_summary")}
|
|
158
165
|
</h4>
|
|
159
166
|
<p className={`text-sm ${isTrialUpgrade ? "text-warning" : "text-foreground"}`}>
|
|
160
167
|
{isTrialUpgrade
|
|
161
|
-
? "
|
|
162
|
-
: "
|
|
168
|
+
? t("billing.ui.wizard.review.trial_upgrade_description")
|
|
169
|
+
: t("billing.ui.wizard.review.proration_description")}
|
|
163
170
|
</p>
|
|
164
171
|
<div className="flex justify-between text-sm">
|
|
165
172
|
<span className={`${isTrialUpgrade ? "text-warning" : "text-muted-foreground"}`}>
|
|
166
|
-
{isTrialUpgrade ? "
|
|
173
|
+
{isTrialUpgrade ? t("billing.ui.wizard.review.charge_now") : t("billing.ui.wizard.review.due_now")}
|
|
167
174
|
</span>
|
|
168
175
|
<span className={`font-medium ${isTrialUpgrade ? "text-warning" : "text-foreground"}`}>
|
|
169
176
|
{discountedImmediateCharge !== null ? (
|
|
170
177
|
<>
|
|
171
178
|
<span className="line-through text-muted-foreground me-2">
|
|
172
|
-
{formatCurrency(prorationPreview.immediateCharge, prorationPreview.currency)}
|
|
179
|
+
{formatCurrency(prorationPreview.immediateCharge, prorationPreview.currency, fmt)}
|
|
173
180
|
</span>
|
|
174
181
|
<span className="text-success">
|
|
175
|
-
{formatCurrency(discountedImmediateCharge, prorationPreview.currency)}
|
|
182
|
+
{formatCurrency(discountedImmediateCharge, prorationPreview.currency, fmt)}
|
|
176
183
|
</span>
|
|
177
184
|
</>
|
|
178
185
|
) : (
|
|
179
|
-
formatCurrency(prorationPreview.immediateCharge, prorationPreview.currency)
|
|
186
|
+
formatCurrency(prorationPreview.immediateCharge, prorationPreview.currency, fmt)
|
|
180
187
|
)}
|
|
181
188
|
</span>
|
|
182
189
|
</div>
|
|
@@ -185,7 +192,7 @@ export function WizardStepReview({
|
|
|
185
192
|
|
|
186
193
|
{/* Promotion Code */}
|
|
187
194
|
<div className="border rounded-lg p-4 space-y-3">
|
|
188
|
-
<h4 className="font-medium">
|
|
195
|
+
<h4 className="font-medium">{t("billing.ui.wizard.review.promotion_code")}</h4>
|
|
189
196
|
<PromoCodeInput
|
|
190
197
|
appliedCode={promotionCode}
|
|
191
198
|
isValidating={isValidatingPromoCode}
|
|
@@ -200,14 +207,16 @@ export function WizardStepReview({
|
|
|
200
207
|
<div className="border rounded-lg p-4">
|
|
201
208
|
<div className="flex justify-between items-center">
|
|
202
209
|
<div>
|
|
203
|
-
<h4 className="font-medium">
|
|
210
|
+
<h4 className="font-medium">{t("billing.ui.payment_method.title")}</h4>
|
|
204
211
|
<p className="text-sm text-muted-foreground">
|
|
205
|
-
{hasPaymentMethod
|
|
212
|
+
{hasPaymentMethod
|
|
213
|
+
? t("billing.ui.wizard.review.payment_on_file")
|
|
214
|
+
: t("billing.ui.wizard.review.no_payment_on_file")}
|
|
206
215
|
</p>
|
|
207
216
|
</div>
|
|
208
217
|
{!hasPaymentMethod && (
|
|
209
218
|
<Button variant="outline" onClick={onAddPaymentMethod}>
|
|
210
|
-
|
|
219
|
+
{t("billing.ui.payment_methods.add")}
|
|
211
220
|
</Button>
|
|
212
221
|
)}
|
|
213
222
|
</div>
|
|
@@ -224,16 +233,16 @@ export function WizardStepReview({
|
|
|
224
233
|
{/* Action Buttons */}
|
|
225
234
|
<div className="flex justify-between pt-4 border-t">
|
|
226
235
|
<Button variant="outline" onClick={onBack} disabled={isProcessing}>
|
|
227
|
-
|
|
236
|
+
{t("billing.ui.wizard.back")}
|
|
228
237
|
</Button>
|
|
229
238
|
<Button onClick={hasPaymentMethod ? onConfirm : onAddPaymentMethod} disabled={isProcessing}>
|
|
230
239
|
{isProcessing
|
|
231
|
-
? "
|
|
240
|
+
? t("billing.ui.wizard.review.processing")
|
|
232
241
|
: hasPaymentMethod
|
|
233
242
|
? isChangingPlan
|
|
234
|
-
? "
|
|
235
|
-
: "
|
|
236
|
-
: "
|
|
243
|
+
? t("billing.ui.wizard.review.confirm_change")
|
|
244
|
+
: t("billing.ui.wizard.review.subscribe_now")
|
|
245
|
+
: t("billing.ui.payment_methods.add")}
|
|
237
246
|
</Button>
|
|
238
247
|
</div>
|
|
239
248
|
</div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useStripe } from "@stripe/react-stripe-js";
|
|
4
|
+
import { useTranslations } from "next-intl";
|
|
4
5
|
import { useCallback, useState } from "react";
|
|
5
6
|
|
|
6
7
|
export interface ConfirmPaymentResult {
|
|
@@ -42,6 +43,7 @@ export interface UseConfirmSubscriptionPaymentReturn {
|
|
|
42
43
|
*/
|
|
43
44
|
export function useConfirmSubscriptionPayment(): UseConfirmSubscriptionPaymentReturn {
|
|
44
45
|
const stripe = useStripe();
|
|
46
|
+
const t = useTranslations();
|
|
45
47
|
const [isConfirming, setIsConfirming] = useState(false);
|
|
46
48
|
|
|
47
49
|
const confirmPayment = useCallback(
|
|
@@ -50,7 +52,7 @@ export function useConfirmSubscriptionPayment(): UseConfirmSubscriptionPaymentRe
|
|
|
50
52
|
console.error("[useConfirmSubscriptionPayment] Stripe not initialized");
|
|
51
53
|
return {
|
|
52
54
|
success: false,
|
|
53
|
-
error: "
|
|
55
|
+
error: t("billing.ui.payment_confirm.errors.not_initialized"),
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -58,7 +60,7 @@ export function useConfirmSubscriptionPayment(): UseConfirmSubscriptionPaymentRe
|
|
|
58
60
|
console.error("[useConfirmSubscriptionPayment] No client secret provided");
|
|
59
61
|
return {
|
|
60
62
|
success: false,
|
|
61
|
-
error: "
|
|
63
|
+
error: t("billing.ui.payment_confirm.errors.missing_details"),
|
|
62
64
|
};
|
|
63
65
|
}
|
|
64
66
|
|
|
@@ -71,7 +73,7 @@ export function useConfirmSubscriptionPayment(): UseConfirmSubscriptionPaymentRe
|
|
|
71
73
|
console.error("[useConfirmSubscriptionPayment] Stripe error:", stripeError);
|
|
72
74
|
return {
|
|
73
75
|
success: false,
|
|
74
|
-
error: stripeError.message || "
|
|
76
|
+
error: stripeError.message || t("billing.ui.payment_confirm.errors.failed"),
|
|
75
77
|
};
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -83,25 +85,25 @@ export function useConfirmSubscriptionPayment(): UseConfirmSubscriptionPaymentRe
|
|
|
83
85
|
// This shouldn't happen as Stripe handles 3DS inline, but handle it just in case
|
|
84
86
|
return {
|
|
85
87
|
success: false,
|
|
86
|
-
error: "
|
|
88
|
+
error: t("billing.ui.payment_confirm.errors.requires_action"),
|
|
87
89
|
};
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
return {
|
|
91
93
|
success: false,
|
|
92
|
-
error: "
|
|
94
|
+
error: t("billing.ui.payment_confirm.errors.not_completed"),
|
|
93
95
|
};
|
|
94
96
|
} catch (err: any) {
|
|
95
97
|
console.error("[useConfirmSubscriptionPayment] Unexpected error:", err);
|
|
96
98
|
return {
|
|
97
99
|
success: false,
|
|
98
|
-
error: err.message || "
|
|
100
|
+
error: err.message || t("billing.ui.payment_confirm.errors.unexpected"),
|
|
99
101
|
};
|
|
100
102
|
} finally {
|
|
101
103
|
setIsConfirming(false);
|
|
102
104
|
}
|
|
103
105
|
},
|
|
104
|
-
[stripe],
|
|
106
|
+
[stripe, t],
|
|
105
107
|
);
|
|
106
108
|
|
|
107
109
|
return {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { useCallback, useMemo, useReducer, useRef } from "react";
|
|
4
5
|
import { v4 } from "uuid";
|
|
6
|
+
import { useI18nLocale } from "../../../../i18n";
|
|
5
7
|
import { StripeCustomerService } from "../../stripe-customer/data/stripe-customer.service";
|
|
6
8
|
import { ProrationPreviewInterface } from "../../stripe-invoice/data/stripe-invoice.interface";
|
|
7
9
|
import { StripePriceInterface } from "../../stripe-price/data/stripe-price.interface";
|
|
@@ -93,6 +95,8 @@ export type UseSubscriptionWizardOptions = {
|
|
|
93
95
|
};
|
|
94
96
|
|
|
95
97
|
export function useSubscriptionWizard({ subscription, onSuccess, onClose }: UseSubscriptionWizardOptions) {
|
|
98
|
+
const t = useTranslations();
|
|
99
|
+
const locale = useI18nLocale();
|
|
96
100
|
const [state, dispatch] = useReducer(wizardReducer, {
|
|
97
101
|
...initialState,
|
|
98
102
|
selectedPrice: subscription?.price || null,
|
|
@@ -146,7 +150,7 @@ export function useSubscriptionWizard({ subscription, onSuccess, onClose }: UseS
|
|
|
146
150
|
dispatch({ type: "SET_STEP", step: "payment-method" });
|
|
147
151
|
dispatch({
|
|
148
152
|
type: "SET_ERROR",
|
|
149
|
-
error: "
|
|
153
|
+
error: t("billing.ui.wizard.errors.payment_method_required"),
|
|
150
154
|
});
|
|
151
155
|
return;
|
|
152
156
|
}
|
|
@@ -164,11 +168,11 @@ export function useSubscriptionWizard({ subscription, onSuccess, onClose }: UseS
|
|
|
164
168
|
dispatch({ type: "SET_STEP", step: "review" });
|
|
165
169
|
} catch (error: any) {
|
|
166
170
|
console.error("[useSubscriptionWizard] Error preparing review:", error);
|
|
167
|
-
dispatch({ type: "SET_ERROR", error: error?.message || "
|
|
171
|
+
dispatch({ type: "SET_ERROR", error: error?.message || t("billing.ui.wizard.errors.prepare_review") });
|
|
168
172
|
} finally {
|
|
169
173
|
dispatch({ type: "SET_PROCESSING", isProcessing: false });
|
|
170
174
|
}
|
|
171
|
-
}, [state.selectedPrice, state.hasPaymentMethod, subscription, checkPaymentMethod]);
|
|
175
|
+
}, [state.selectedPrice, state.hasPaymentMethod, subscription, checkPaymentMethod, t]);
|
|
172
176
|
|
|
173
177
|
const confirmSubscription = useCallback(async () => {
|
|
174
178
|
if (!state.selectedPrice) return;
|
|
@@ -202,7 +206,7 @@ export function useSubscriptionWizard({ subscription, onSuccess, onClose }: UseS
|
|
|
202
206
|
if (error?.status === 409 || error?.response?.status === 409) {
|
|
203
207
|
dispatch({
|
|
204
208
|
type: "SET_ERROR",
|
|
205
|
-
error: "
|
|
209
|
+
error: t("billing.ui.wizard.errors.already_subscribed"),
|
|
206
210
|
});
|
|
207
211
|
return;
|
|
208
212
|
}
|
|
@@ -214,11 +218,11 @@ export function useSubscriptionWizard({ subscription, onSuccess, onClose }: UseS
|
|
|
214
218
|
return;
|
|
215
219
|
}
|
|
216
220
|
|
|
217
|
-
dispatch({ type: "SET_ERROR", error: error?.message || "
|
|
221
|
+
dispatch({ type: "SET_ERROR", error: error?.message || t("billing.ui.wizard.errors.process_subscription") });
|
|
218
222
|
} finally {
|
|
219
223
|
dispatch({ type: "SET_PROCESSING", isProcessing: false });
|
|
220
224
|
}
|
|
221
|
-
}, [state.selectedPrice, state.promotionCode, subscription]);
|
|
225
|
+
}, [state.selectedPrice, state.promotionCode, subscription, t]);
|
|
222
226
|
|
|
223
227
|
const handlePaymentMethodSuccess = useCallback(async () => {
|
|
224
228
|
dispatch({ type: "SET_HAS_PAYMENT_METHOD", hasMethod: true });
|
|
@@ -239,21 +243,28 @@ export function useSubscriptionWizard({ subscription, onSuccess, onClose }: UseS
|
|
|
239
243
|
const result = await StripePromotionCodeService.validatePromotionCode({
|
|
240
244
|
code,
|
|
241
245
|
stripePriceId: state.selectedPrice?.id,
|
|
246
|
+
language: locale,
|
|
242
247
|
});
|
|
243
248
|
|
|
244
249
|
if (result.valid) {
|
|
245
250
|
dispatch({ type: "SET_PROMOTION_CODE", code: result });
|
|
246
251
|
} else {
|
|
247
|
-
dispatch({
|
|
252
|
+
dispatch({
|
|
253
|
+
type: "SET_PROMO_CODE_ERROR",
|
|
254
|
+
error: result.errorMessage || t("billing.ui.wizard.errors.invalid_promo_code"),
|
|
255
|
+
});
|
|
248
256
|
}
|
|
249
257
|
} catch (error: any) {
|
|
250
258
|
console.error("[useSubscriptionWizard] Promo code validation error:", error);
|
|
251
|
-
dispatch({
|
|
259
|
+
dispatch({
|
|
260
|
+
type: "SET_PROMO_CODE_ERROR",
|
|
261
|
+
error: error?.message || t("billing.ui.wizard.errors.validate_promo_code"),
|
|
262
|
+
});
|
|
252
263
|
} finally {
|
|
253
264
|
dispatch({ type: "SET_VALIDATING_PROMO_CODE", isValidating: false });
|
|
254
265
|
}
|
|
255
266
|
},
|
|
256
|
-
[state.selectedPrice?.id],
|
|
267
|
+
[state.selectedPrice?.id, t, locale],
|
|
257
268
|
);
|
|
258
269
|
|
|
259
270
|
const clearPromoCode = useCallback(() => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { Activity } from "lucide-react";
|
|
4
5
|
import { useEffect, useState } from "react";
|
|
5
6
|
import { StripeSubscriptionInterface, StripeSubscriptionService } from "../../../stripe-subscription";
|
|
@@ -8,6 +9,7 @@ import { StripeUsageService } from "../../data/stripe-usage.service";
|
|
|
8
9
|
import { UsageSummaryCards } from "../widgets/UsageSummaryCards";
|
|
9
10
|
|
|
10
11
|
export function UsageContainer() {
|
|
12
|
+
const t = useTranslations();
|
|
11
13
|
const [meters, setMeters] = useState<MeterInterface[]>([]);
|
|
12
14
|
const [summaries, setSummaries] = useState<Record<string, MeterSummaryInterface | null>>({});
|
|
13
15
|
const [loading, setLoading] = useState<boolean>(true);
|
|
@@ -76,7 +78,7 @@ export function UsageContainer() {
|
|
|
76
78
|
if (loading) {
|
|
77
79
|
return (
|
|
78
80
|
<div className="flex h-64 items-center justify-center">
|
|
79
|
-
<p className="text-muted-foreground">
|
|
81
|
+
<p className="text-muted-foreground">{t("billing.ui.usage.loading")}</p>
|
|
80
82
|
</div>
|
|
81
83
|
);
|
|
82
84
|
}
|
|
@@ -86,7 +88,7 @@ export function UsageContainer() {
|
|
|
86
88
|
{/* Header */}
|
|
87
89
|
<div className="flex items-center gap-x-3">
|
|
88
90
|
<Activity className="h-8 w-8" />
|
|
89
|
-
<h1 className="text-primary text-3xl font-semibold">
|
|
91
|
+
<h1 className="text-primary text-3xl font-semibold">{t("billing.ui.usage.title")}</h1>
|
|
90
92
|
</div>
|
|
91
93
|
|
|
92
94
|
{/* Empty State */}
|
|
@@ -94,10 +96,8 @@ export function UsageContainer() {
|
|
|
94
96
|
<div className="bg-muted/50 flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed border-gray-300 p-12">
|
|
95
97
|
<Activity className="text-muted-foreground h-16 w-16" />
|
|
96
98
|
<div className="text-center">
|
|
97
|
-
<h3 className="mb-2 text-sm font-medium">
|
|
98
|
-
<p className="text-muted-foreground">
|
|
99
|
-
Usage tracking will appear here when you have metered subscriptions with configured meters.
|
|
100
|
-
</p>
|
|
99
|
+
<h3 className="mb-2 text-sm font-medium">{t("billing.ui.usage.empty_title")}</h3>
|
|
100
|
+
<p className="text-muted-foreground">{t("billing.ui.usage.empty_description")}</p>
|
|
101
101
|
</div>
|
|
102
102
|
</div>
|
|
103
103
|
)}
|