@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { AlertCircle } from "lucide-react";
|
|
4
5
|
import { Button } from "../../../../shadcnui";
|
|
5
6
|
import { StripeSubscriptionInterface, SubscriptionStatus } from "../../stripe-subscription";
|
|
@@ -11,20 +12,19 @@ type BillingAlertBannerProps = {
|
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment }: BillingAlertBannerProps) {
|
|
15
|
+
const t = useTranslations();
|
|
14
16
|
// Payment failed alert (past_due)
|
|
15
17
|
if (subscription.status === SubscriptionStatus.PAST_DUE) {
|
|
16
18
|
return (
|
|
17
19
|
<div className="bg-red-50 border border-red-200 rounded-lg p-4 flex items-start gap-x-3">
|
|
18
20
|
<AlertCircle className="text-destructive mt-0.5 h-5 w-5" />
|
|
19
21
|
<div className="flex-1">
|
|
20
|
-
<h3 className="text-destructive text-sm font-medium">
|
|
21
|
-
<p className="text-destructive mt-1 text-sm">
|
|
22
|
-
Your last payment failed. Please update your payment method to avoid service interruption.
|
|
23
|
-
</p>
|
|
22
|
+
<h3 className="text-destructive text-sm font-medium">{t("billing.ui.alert.payment_failed_title")}</h3>
|
|
23
|
+
<p className="text-destructive mt-1 text-sm">{t("billing.ui.alert.payment_failed_description")}</p>
|
|
24
24
|
</div>
|
|
25
25
|
{onUpdatePayment && (
|
|
26
26
|
<Button variant="outline" size="sm" onClick={onUpdatePayment} className="text-destructive border-red-300">
|
|
27
|
-
|
|
27
|
+
{t("billing.ui.alert.update_payment_method")}
|
|
28
28
|
</Button>
|
|
29
29
|
)}
|
|
30
30
|
</div>
|
|
@@ -42,15 +42,14 @@ export function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment
|
|
|
42
42
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 flex items-start gap-x-3">
|
|
43
43
|
<AlertCircle className="text-warning mt-0.5 h-5 w-5" />
|
|
44
44
|
<div className="flex-1">
|
|
45
|
-
<h3 className="text-warning text-sm font-medium">
|
|
45
|
+
<h3 className="text-warning text-sm font-medium">{t("billing.ui.alert.trial_ending_title")}</h3>
|
|
46
46
|
<p className="text-warning mt-1 text-sm">
|
|
47
|
-
|
|
48
|
-
continue your subscription.
|
|
47
|
+
{t("billing.ui.alert.trial_ending_description", { count: daysRemaining })}
|
|
49
48
|
</p>
|
|
50
49
|
</div>
|
|
51
50
|
{onAddPayment && (
|
|
52
51
|
<Button variant="outline" size="sm" onClick={onAddPayment} className="text-warning border-yellow-300">
|
|
53
|
-
|
|
52
|
+
{t("billing.ui.payment_methods.add")}
|
|
54
53
|
</Button>
|
|
55
54
|
)}
|
|
56
55
|
</div>
|
|
@@ -8,7 +8,12 @@ import { usePageUrlGenerator } from "../../../hooks";
|
|
|
8
8
|
import { BreadcrumbItemData } from "../../../interfaces";
|
|
9
9
|
import { getRoleId } from "../../../roles";
|
|
10
10
|
import { useCurrentUserContext } from "../../user/contexts";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
type BillingFormatOptions,
|
|
13
|
+
formatCurrency,
|
|
14
|
+
formatInterval,
|
|
15
|
+
useBillingFormatOptions,
|
|
16
|
+
} from "../components/utils";
|
|
12
17
|
import { PriceArchiver } from "../stripe-price/components/forms/PriceArchiver";
|
|
13
18
|
import PriceEditor from "../stripe-price/components/forms/PriceEditor";
|
|
14
19
|
import { StripePriceInput, StripePriceInterface } from "../stripe-price/data/stripe-price.interface";
|
|
@@ -63,9 +68,15 @@ const defaultContextValue: PriceContextType = {
|
|
|
63
68
|
productId: undefined,
|
|
64
69
|
};
|
|
65
70
|
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Human label for a price: its nickname, else amount + interval. Pass the
|
|
73
|
+
* caller's `useBillingFormatOptions()` so the amount and interval follow the
|
|
74
|
+
* active locale; without it the label is formatted in English.
|
|
75
|
+
*/
|
|
76
|
+
export function priceLabel(price: StripePriceInterface, fmt: BillingFormatOptions = {}): string {
|
|
77
|
+
return (
|
|
78
|
+
price.nickname ?? `${formatCurrency(price.unitAmount, price.currency, fmt)} ${formatInterval(price, fmt)}`.trim()
|
|
79
|
+
);
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
export const PriceProvider = ({
|
|
@@ -75,6 +86,7 @@ export const PriceProvider = ({
|
|
|
75
86
|
productId: productIdProp,
|
|
76
87
|
}: PriceProviderProps) => {
|
|
77
88
|
const t = useTranslations();
|
|
89
|
+
const fmt = useBillingFormatOptions();
|
|
78
90
|
const generateUrl = usePageUrlGenerator();
|
|
79
91
|
const { hasRole } = useCurrentUserContext();
|
|
80
92
|
|
|
@@ -170,7 +182,7 @@ export const PriceProvider = ({
|
|
|
170
182
|
name: price.product.name,
|
|
171
183
|
href: generateUrl({ page: Modules.StripeProduct, id: price.product.id }),
|
|
172
184
|
});
|
|
173
|
-
if (price) items.push({ name: priceLabel(price) });
|
|
185
|
+
if (price) items.push({ name: priceLabel(price, fmt) });
|
|
174
186
|
return items;
|
|
175
187
|
};
|
|
176
188
|
|
|
@@ -178,7 +190,7 @@ export const PriceProvider = ({
|
|
|
178
190
|
const response: any = { type: t("billing.admin.prices.title") };
|
|
179
191
|
if (!price) return response;
|
|
180
192
|
|
|
181
|
-
response.element = priceLabel(price);
|
|
193
|
+
response.element = priceLabel(price, fmt);
|
|
182
194
|
|
|
183
195
|
// narr8 note: gate on hasRole ONLY. hasPermissionToModule always returns
|
|
184
196
|
// false in this app, so gating an editor on it hides the editor outright.
|
|
@@ -108,3 +108,215 @@ export const BILLING_TOKENS_I18N_KEYS = [
|
|
|
108
108
|
"billing.tokens.purchase_extra",
|
|
109
109
|
"billing.tokens.upgrade_plan",
|
|
110
110
|
] as const;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The `billing.ui.*` namespace (plus `billing.title`), read by the customer-facing
|
|
114
|
+
* billing surfaces: dashboard, cards, subscription wizard, invoices, payment
|
|
115
|
+
* methods and usage. A third export because these screens are neither the admin
|
|
116
|
+
* catalogue above nor the token indicator.
|
|
117
|
+
*/
|
|
118
|
+
export const BILLING_UI_I18N_KEYS = [
|
|
119
|
+
"billing.title",
|
|
120
|
+
"billing.ui.alert.payment_failed_description",
|
|
121
|
+
"billing.ui.alert.payment_failed_title",
|
|
122
|
+
"billing.ui.alert.trial_ending_description",
|
|
123
|
+
"billing.ui.alert.trial_ending_title",
|
|
124
|
+
"billing.ui.alert.update_payment_method",
|
|
125
|
+
"billing.ui.cards.customer.credit_balance",
|
|
126
|
+
"billing.ui.cards.customer.manage_portal",
|
|
127
|
+
"billing.ui.cards.customer.not_set_up",
|
|
128
|
+
"billing.ui.cards.customer.not_set_up_description",
|
|
129
|
+
"billing.ui.cards.customer.title",
|
|
130
|
+
"billing.ui.cards.invoices.empty_description",
|
|
131
|
+
"billing.ui.cards.invoices.invoice_from",
|
|
132
|
+
"billing.ui.cards.invoices.open_count",
|
|
133
|
+
"billing.ui.cards.invoices.paid_count",
|
|
134
|
+
"billing.ui.cards.invoices.title",
|
|
135
|
+
"billing.ui.cards.payment_method.add_card",
|
|
136
|
+
"billing.ui.cards.payment_method.empty_description",
|
|
137
|
+
"billing.ui.cards.payment_method.empty_title",
|
|
138
|
+
"billing.ui.cards.payment_method.more_cards",
|
|
139
|
+
"billing.ui.cards.payment_method.more_methods",
|
|
140
|
+
"billing.ui.cards.subscriptions.canceling",
|
|
141
|
+
"billing.ui.cards.subscriptions.cancels_on",
|
|
142
|
+
"billing.ui.cards.subscriptions.empty_description",
|
|
143
|
+
"billing.ui.cards.subscriptions.empty_title",
|
|
144
|
+
"billing.ui.cards.subscriptions.ends_on",
|
|
145
|
+
"billing.ui.cards.subscriptions.fallback_name",
|
|
146
|
+
"billing.ui.cards.subscriptions.more",
|
|
147
|
+
"billing.ui.cards.subscriptions.renews_on",
|
|
148
|
+
"billing.ui.cards.subscriptions.view_plans",
|
|
149
|
+
"billing.ui.cards.usage.across_meters",
|
|
150
|
+
"billing.ui.cards.usage.no_meters",
|
|
151
|
+
"billing.ui.cards.usage.no_meters_description",
|
|
152
|
+
"billing.ui.cards.usage.title",
|
|
153
|
+
"billing.ui.cards.usage.units",
|
|
154
|
+
"billing.ui.cards.view_all",
|
|
155
|
+
"billing.ui.cards.view_details",
|
|
156
|
+
"billing.ui.columns.actions",
|
|
157
|
+
"billing.ui.columns.amount",
|
|
158
|
+
"billing.ui.columns.date",
|
|
159
|
+
"billing.ui.columns.date_time",
|
|
160
|
+
"billing.ui.columns.description",
|
|
161
|
+
"billing.ui.columns.event_id",
|
|
162
|
+
"billing.ui.columns.invoice_number",
|
|
163
|
+
"billing.ui.columns.meter_event",
|
|
164
|
+
"billing.ui.columns.period",
|
|
165
|
+
"billing.ui.columns.plan",
|
|
166
|
+
"billing.ui.columns.quantity",
|
|
167
|
+
"billing.ui.columns.status",
|
|
168
|
+
"billing.ui.confirmation.subscribe",
|
|
169
|
+
"billing.ui.confirmation.title",
|
|
170
|
+
"billing.ui.dashboard.modal.invoices",
|
|
171
|
+
"billing.ui.dashboard.modal.subscriptions",
|
|
172
|
+
"billing.ui.dashboard.setting_up",
|
|
173
|
+
"billing.ui.dashboard.setup_button",
|
|
174
|
+
"billing.ui.dashboard.setup_description",
|
|
175
|
+
"billing.ui.dashboard.setup_title",
|
|
176
|
+
"billing.ui.interval.one_time",
|
|
177
|
+
"billing.ui.interval.per_day",
|
|
178
|
+
"billing.ui.interval.per_days",
|
|
179
|
+
"billing.ui.interval.per_month",
|
|
180
|
+
"billing.ui.interval.per_months",
|
|
181
|
+
"billing.ui.interval.per_week",
|
|
182
|
+
"billing.ui.interval.per_weeks",
|
|
183
|
+
"billing.ui.interval.per_year",
|
|
184
|
+
"billing.ui.interval.per_years",
|
|
185
|
+
"billing.ui.invalid_date",
|
|
186
|
+
"billing.ui.invoice_status.open",
|
|
187
|
+
"billing.ui.invoice_status.paid",
|
|
188
|
+
"billing.ui.invoice_status.uncollectible",
|
|
189
|
+
"billing.ui.invoice_status.void",
|
|
190
|
+
"billing.ui.invoice.amount_due",
|
|
191
|
+
"billing.ui.invoice.amount_paid",
|
|
192
|
+
"billing.ui.invoice.attempt_count",
|
|
193
|
+
"billing.ui.invoice.billing_period",
|
|
194
|
+
"billing.ui.invoice.download_pdf",
|
|
195
|
+
"billing.ui.invoice.due_date",
|
|
196
|
+
"billing.ui.invoice.line_items",
|
|
197
|
+
"billing.ui.invoice.paid_date",
|
|
198
|
+
"billing.ui.invoice.retry_payment",
|
|
199
|
+
"billing.ui.invoice.subtotal",
|
|
200
|
+
"billing.ui.invoice.tax",
|
|
201
|
+
"billing.ui.invoice.title",
|
|
202
|
+
"billing.ui.invoice.total",
|
|
203
|
+
"billing.ui.invoice.view_in_stripe",
|
|
204
|
+
"billing.ui.invoices.empty_description",
|
|
205
|
+
"billing.ui.invoices.empty_title",
|
|
206
|
+
"billing.ui.invoices.filter_all",
|
|
207
|
+
"billing.ui.invoices.loading",
|
|
208
|
+
"billing.ui.labels.plan",
|
|
209
|
+
"billing.ui.labels.status",
|
|
210
|
+
"billing.ui.not_available",
|
|
211
|
+
"billing.ui.payment_confirm.errors.failed",
|
|
212
|
+
"billing.ui.payment_confirm.errors.missing_details",
|
|
213
|
+
"billing.ui.payment_confirm.errors.not_completed",
|
|
214
|
+
"billing.ui.payment_confirm.errors.not_initialized",
|
|
215
|
+
"billing.ui.payment_confirm.errors.requires_action",
|
|
216
|
+
"billing.ui.payment_confirm.errors.unexpected",
|
|
217
|
+
"billing.ui.payment_form.errors.add",
|
|
218
|
+
"billing.ui.payment_form.errors.init",
|
|
219
|
+
"billing.ui.payment_form.errors.unexpected",
|
|
220
|
+
"billing.ui.payment_form.loading",
|
|
221
|
+
"billing.ui.payment_form.set_default",
|
|
222
|
+
"billing.ui.payment_method.expires",
|
|
223
|
+
"billing.ui.payment_method.title",
|
|
224
|
+
"billing.ui.payment_methods.add",
|
|
225
|
+
"billing.ui.payment_methods.add_description",
|
|
226
|
+
"billing.ui.payment_methods.add_first",
|
|
227
|
+
"billing.ui.payment_methods.default",
|
|
228
|
+
"billing.ui.payment_methods.empty_description",
|
|
229
|
+
"billing.ui.payment_methods.empty_title",
|
|
230
|
+
"billing.ui.payment_methods.loading",
|
|
231
|
+
"billing.ui.payment_methods.remove_default_warning",
|
|
232
|
+
"billing.ui.payment_methods.remove_description",
|
|
233
|
+
"billing.ui.payment_methods.remove_title",
|
|
234
|
+
"billing.ui.payment_methods.removing",
|
|
235
|
+
"billing.ui.payment_methods.set_default",
|
|
236
|
+
"billing.ui.payment_methods.title",
|
|
237
|
+
"billing.ui.pricing.card_aria",
|
|
238
|
+
"billing.ui.pricing.current",
|
|
239
|
+
"billing.ui.pricing.current_plan",
|
|
240
|
+
"billing.ui.pricing.no_plans",
|
|
241
|
+
"billing.ui.pricing.options_aria",
|
|
242
|
+
"billing.ui.pricing.selected",
|
|
243
|
+
"billing.ui.pricing.standard",
|
|
244
|
+
"billing.ui.promo_code.apply",
|
|
245
|
+
"billing.ui.promo_code.duration_forever",
|
|
246
|
+
"billing.ui.promo_code.duration_once",
|
|
247
|
+
"billing.ui.promo_code.duration_repeating",
|
|
248
|
+
"billing.ui.promo_code.placeholder",
|
|
249
|
+
"billing.ui.proration.net_due_today",
|
|
250
|
+
"billing.ui.proration.next_invoice",
|
|
251
|
+
"billing.ui.proration.title",
|
|
252
|
+
"billing.ui.subscription.billing_amount",
|
|
253
|
+
"billing.ui.subscription.cancel_at_period_end",
|
|
254
|
+
"billing.ui.subscription.cancel.canceling",
|
|
255
|
+
"billing.ui.subscription.cancel.confirm",
|
|
256
|
+
"billing.ui.subscription.cancel.description",
|
|
257
|
+
"billing.ui.subscription.cancel.keep",
|
|
258
|
+
"billing.ui.subscription.cancel.reason",
|
|
259
|
+
"billing.ui.subscription.cancel.reason_placeholder",
|
|
260
|
+
"billing.ui.subscription.cancel.remains_active",
|
|
261
|
+
"billing.ui.subscription.cancel.title",
|
|
262
|
+
"billing.ui.subscription.change_plan",
|
|
263
|
+
"billing.ui.subscription.current_period",
|
|
264
|
+
"billing.ui.subscription.details_description",
|
|
265
|
+
"billing.ui.subscription.details_title",
|
|
266
|
+
"billing.ui.subscription.manage_portal",
|
|
267
|
+
"billing.ui.subscription.pause",
|
|
268
|
+
"billing.ui.subscription.pausing",
|
|
269
|
+
"billing.ui.subscription.resume",
|
|
270
|
+
"billing.ui.subscription.resuming",
|
|
271
|
+
"billing.ui.subscription.trial_ends",
|
|
272
|
+
"billing.ui.subscription.upgrade",
|
|
273
|
+
"billing.ui.subscriptions.empty_description",
|
|
274
|
+
"billing.ui.subscriptions.empty_title",
|
|
275
|
+
"billing.ui.subscriptions.loading",
|
|
276
|
+
"billing.ui.subscriptions.purchase_addons",
|
|
277
|
+
"billing.ui.subscriptions.subscribe",
|
|
278
|
+
"billing.ui.subscriptions.title",
|
|
279
|
+
"billing.ui.usage.empty_description",
|
|
280
|
+
"billing.ui.usage.empty_title",
|
|
281
|
+
"billing.ui.usage.history_empty",
|
|
282
|
+
"billing.ui.usage.history_title",
|
|
283
|
+
"billing.ui.usage.loading",
|
|
284
|
+
"billing.ui.usage.no_limit",
|
|
285
|
+
"billing.ui.usage.of_limit_used",
|
|
286
|
+
"billing.ui.usage.percent_used",
|
|
287
|
+
"billing.ui.usage.period",
|
|
288
|
+
"billing.ui.usage.title",
|
|
289
|
+
"billing.ui.wizard.addons_description",
|
|
290
|
+
"billing.ui.wizard.back",
|
|
291
|
+
"billing.ui.wizard.errors.already_subscribed",
|
|
292
|
+
"billing.ui.wizard.errors.invalid_promo_code",
|
|
293
|
+
"billing.ui.wizard.errors.payment_method_required",
|
|
294
|
+
"billing.ui.wizard.errors.prepare_review",
|
|
295
|
+
"billing.ui.wizard.errors.process_subscription",
|
|
296
|
+
"billing.ui.wizard.errors.validate_promo_code",
|
|
297
|
+
"billing.ui.wizard.change_description",
|
|
298
|
+
"billing.ui.wizard.change_title",
|
|
299
|
+
"billing.ui.wizard.next_review",
|
|
300
|
+
"billing.ui.wizard.payment_description",
|
|
301
|
+
"billing.ui.wizard.review.amount_off",
|
|
302
|
+
"billing.ui.wizard.review.charge_now",
|
|
303
|
+
"billing.ui.wizard.review.confirm_change",
|
|
304
|
+
"billing.ui.wizard.review.due_now",
|
|
305
|
+
"billing.ui.wizard.review.interval_monthly",
|
|
306
|
+
"billing.ui.wizard.review.interval_one_time",
|
|
307
|
+
"billing.ui.wizard.review.interval_yearly",
|
|
308
|
+
"billing.ui.wizard.review.no_payment_on_file",
|
|
309
|
+
"billing.ui.wizard.review.no_plan",
|
|
310
|
+
"billing.ui.wizard.review.payment_on_file",
|
|
311
|
+
"billing.ui.wizard.review.percent_off",
|
|
312
|
+
"billing.ui.wizard.review.processing",
|
|
313
|
+
"billing.ui.wizard.review.promotion_code",
|
|
314
|
+
"billing.ui.wizard.review.proration_description",
|
|
315
|
+
"billing.ui.wizard.review.proration_summary",
|
|
316
|
+
"billing.ui.wizard.review.selected_plan",
|
|
317
|
+
"billing.ui.wizard.review.subscribe_now",
|
|
318
|
+
"billing.ui.wizard.review.trial_upgrade",
|
|
319
|
+
"billing.ui.wizard.review.trial_upgrade_description",
|
|
320
|
+
"billing.ui.wizard.steps.select_plan",
|
|
321
|
+
"billing.ui.wizard.subscribe_description",
|
|
322
|
+
] as const;
|
package/src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { CreditCard } from "lucide-react";
|
|
4
5
|
import { useEffect, useState } from "react";
|
|
5
6
|
import { Button } from "../../../../../shadcnui";
|
|
@@ -8,6 +9,7 @@ import { PaymentMethodEditor } from "../forms/PaymentMethodEditor";
|
|
|
8
9
|
import { PaymentMethodsList } from "../lists/PaymentMethodsList";
|
|
9
10
|
|
|
10
11
|
export function PaymentMethodsContainer() {
|
|
12
|
+
const t = useTranslations();
|
|
11
13
|
const [paymentMethods, setPaymentMethods] = useState<PaymentMethodInterface[]>([]);
|
|
12
14
|
const [loading, setLoading] = useState<boolean>(true);
|
|
13
15
|
const [showAddPaymentMethod, setShowAddPaymentMethod] = useState<boolean>(false);
|
|
@@ -31,7 +33,7 @@ export function PaymentMethodsContainer() {
|
|
|
31
33
|
if (loading) {
|
|
32
34
|
return (
|
|
33
35
|
<div className="flex h-64 items-center justify-center">
|
|
34
|
-
<p className="text-muted-foreground">
|
|
36
|
+
<p className="text-muted-foreground">{t("billing.ui.payment_methods.loading")}</p>
|
|
35
37
|
</div>
|
|
36
38
|
);
|
|
37
39
|
}
|
|
@@ -42,9 +44,9 @@ export function PaymentMethodsContainer() {
|
|
|
42
44
|
<div className="flex items-center justify-between">
|
|
43
45
|
<div className="flex items-center gap-x-3">
|
|
44
46
|
<CreditCard className="h-8 w-8" />
|
|
45
|
-
<h1 className="text-primary text-3xl font-semibold">
|
|
47
|
+
<h1 className="text-primary text-3xl font-semibold">{t("billing.ui.payment_methods.title")}</h1>
|
|
46
48
|
</div>
|
|
47
|
-
<Button onClick={() => setShowAddPaymentMethod(true)}>
|
|
49
|
+
<Button onClick={() => setShowAddPaymentMethod(true)}>{t("billing.ui.payment_methods.add")}</Button>
|
|
48
50
|
</div>
|
|
49
51
|
|
|
50
52
|
{/* Empty State */}
|
|
@@ -52,11 +54,9 @@ export function PaymentMethodsContainer() {
|
|
|
52
54
|
<div className="flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed border-gray-300 bg-muted/50 p-12">
|
|
53
55
|
<CreditCard className="h-16 w-16 text-muted-foreground" />
|
|
54
56
|
<div className="text-center">
|
|
55
|
-
<h3 className="mb-2 text-sm font-medium">
|
|
56
|
-
<p className="mb-4 text-muted-foreground">
|
|
57
|
-
|
|
58
|
-
</p>
|
|
59
|
-
<Button onClick={() => setShowAddPaymentMethod(true)}>Add Your First Card</Button>
|
|
57
|
+
<h3 className="mb-2 text-sm font-medium">{t("billing.ui.payment_methods.empty_title")}</h3>
|
|
58
|
+
<p className="mb-4 text-muted-foreground">{t("billing.ui.payment_methods.empty_description")}</p>
|
|
59
|
+
<Button onClick={() => setShowAddPaymentMethod(true)}>{t("billing.ui.payment_methods.add_first")}</Button>
|
|
60
60
|
</div>
|
|
61
61
|
</div>
|
|
62
62
|
)}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { MoreVertical } from "lucide-react";
|
|
4
5
|
import { useEffect, useState } from "react";
|
|
5
6
|
import {
|
|
@@ -37,6 +38,7 @@ const brandIcons: Record<string, string> = {
|
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCardProps) {
|
|
41
|
+
const t = useTranslations();
|
|
40
42
|
const [loading, setLoading] = useState<boolean>(false);
|
|
41
43
|
const [customer, setCustomer] = useState<StripeCustomerInterface | null>(null);
|
|
42
44
|
const [showRemoveDialog, setShowRemoveDialog] = useState<boolean>(false);
|
|
@@ -92,7 +94,7 @@ export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCard
|
|
|
92
94
|
{/* Default Badge */}
|
|
93
95
|
{isDefault && (
|
|
94
96
|
<Badge variant="softGreen" className="absolute end-2 top-2">
|
|
95
|
-
|
|
97
|
+
{t("billing.ui.payment_methods.default")}
|
|
96
98
|
</Badge>
|
|
97
99
|
)}
|
|
98
100
|
|
|
@@ -117,7 +119,7 @@ export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCard
|
|
|
117
119
|
<DropdownMenuContent align="end">
|
|
118
120
|
{!isDefault && (
|
|
119
121
|
<DropdownMenuItem onClick={handleSetDefault} disabled={loading}>
|
|
120
|
-
|
|
122
|
+
{t("billing.ui.payment_methods.set_default")}
|
|
121
123
|
</DropdownMenuItem>
|
|
122
124
|
)}
|
|
123
125
|
<DropdownMenuItem
|
|
@@ -125,7 +127,7 @@ export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCard
|
|
|
125
127
|
disabled={loading}
|
|
126
128
|
className="text-destructive"
|
|
127
129
|
>
|
|
128
|
-
|
|
130
|
+
{t("ui.buttons.remove")}
|
|
129
131
|
</DropdownMenuItem>
|
|
130
132
|
</DropdownMenuContent>
|
|
131
133
|
</DropdownMenu>
|
|
@@ -135,7 +137,7 @@ export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCard
|
|
|
135
137
|
<div className="flex flex-col gap-y-1">
|
|
136
138
|
<p className="text-lg font-semibold">•••• {last4}</p>
|
|
137
139
|
<p className="text-sm text-muted-foreground">
|
|
138
|
-
|
|
140
|
+
{t("billing.ui.payment_method.expires", { date: `${String(expMonth).padStart(2, "0")}/${expYear}` })}
|
|
139
141
|
</p>
|
|
140
142
|
</div>
|
|
141
143
|
</CardContent>
|
|
@@ -145,16 +147,16 @@ export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCard
|
|
|
145
147
|
<AlertDialog open={showRemoveDialog} onOpenChange={setShowRemoveDialog}>
|
|
146
148
|
<AlertDialogContent>
|
|
147
149
|
<AlertDialogHeader>
|
|
148
|
-
<AlertDialogTitle>
|
|
150
|
+
<AlertDialogTitle>{t("billing.ui.payment_methods.remove_title")}</AlertDialogTitle>
|
|
149
151
|
<AlertDialogDescription>
|
|
150
|
-
|
|
151
|
-
{isDefault && "
|
|
152
|
+
{t("billing.ui.payment_methods.remove_description")}
|
|
153
|
+
{isDefault && ` ${t("billing.ui.payment_methods.remove_default_warning")}`}
|
|
152
154
|
</AlertDialogDescription>
|
|
153
155
|
</AlertDialogHeader>
|
|
154
156
|
<AlertDialogFooter>
|
|
155
|
-
<AlertDialogCancel disabled={loading}>
|
|
157
|
+
<AlertDialogCancel disabled={loading}>{t("ui.buttons.cancel")}</AlertDialogCancel>
|
|
156
158
|
<AlertDialogAction onClick={handleRemove} disabled={loading} className="bg-red-600 hover:bg-red-700">
|
|
157
|
-
{loading ? "
|
|
159
|
+
{loading ? t("billing.ui.payment_methods.removing") : t("ui.buttons.remove")}
|
|
158
160
|
</AlertDialogAction>
|
|
159
161
|
</AlertDialogFooter>
|
|
160
162
|
</AlertDialogContent>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../../../../../shadcnui";
|
|
4
5
|
import { PaymentMethodForm } from "./PaymentMethodForm";
|
|
5
6
|
|
|
@@ -10,6 +11,7 @@ type PaymentMethodEditorProps = {
|
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export function PaymentMethodEditor({ open, onOpenChange, onSuccess }: PaymentMethodEditorProps) {
|
|
14
|
+
const t = useTranslations();
|
|
13
15
|
const handleSuccess = () => {
|
|
14
16
|
onSuccess();
|
|
15
17
|
onOpenChange(false);
|
|
@@ -23,10 +25,8 @@ export function PaymentMethodEditor({ open, onOpenChange, onSuccess }: PaymentMe
|
|
|
23
25
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
24
26
|
<DialogContent className="max-w-md">
|
|
25
27
|
<DialogHeader>
|
|
26
|
-
<DialogTitle>
|
|
27
|
-
<DialogDescription>
|
|
28
|
-
Add a new payment method to your account. Your card information is securely processed by Stripe.
|
|
29
|
-
</DialogDescription>
|
|
28
|
+
<DialogTitle>{t("billing.ui.payment_methods.add")}</DialogTitle>
|
|
29
|
+
<DialogDescription>{t("billing.ui.payment_methods.add_description")}</DialogDescription>
|
|
30
30
|
</DialogHeader>
|
|
31
31
|
{open && <PaymentMethodForm onSuccess={handleSuccess} onCancel={handleCancel} />}
|
|
32
32
|
</DialogContent>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
|
|
4
5
|
import { useEffect, useState } from "react";
|
|
5
6
|
import { Alert, AlertDescription, Button, Checkbox, Label } from "../../../../../shadcnui";
|
|
@@ -12,6 +13,7 @@ type PaymentMethodFormProps = {
|
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
export function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }: PaymentMethodFormProps) {
|
|
16
|
+
const t = useTranslations();
|
|
15
17
|
const stripe = useStripe();
|
|
16
18
|
const elements = useElements();
|
|
17
19
|
|
|
@@ -30,7 +32,7 @@ export function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }: Pa
|
|
|
30
32
|
setSetupIntent(intent);
|
|
31
33
|
} catch (err) {
|
|
32
34
|
console.error("[PaymentMethodForm] Failed to create setup intent:", err);
|
|
33
|
-
setError("
|
|
35
|
+
setError(t("billing.ui.payment_form.errors.init"));
|
|
34
36
|
} finally {
|
|
35
37
|
setLoading(false);
|
|
36
38
|
}
|
|
@@ -67,7 +69,7 @@ export function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }: Pa
|
|
|
67
69
|
|
|
68
70
|
if (stripeError) {
|
|
69
71
|
console.error("[PaymentMethodForm] Stripe error:", stripeError);
|
|
70
|
-
setError(stripeError.message || "
|
|
72
|
+
setError(stripeError.message || t("billing.ui.payment_form.errors.add"));
|
|
71
73
|
setIsSubmitting(false);
|
|
72
74
|
return;
|
|
73
75
|
}
|
|
@@ -85,7 +87,7 @@ export function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }: Pa
|
|
|
85
87
|
onSuccess();
|
|
86
88
|
} catch (err: any) {
|
|
87
89
|
console.error("[PaymentMethodForm] Error:", err);
|
|
88
|
-
setError(err.message || "
|
|
90
|
+
setError(err.message || t("billing.ui.payment_form.errors.unexpected"));
|
|
89
91
|
} finally {
|
|
90
92
|
setIsSubmitting(false);
|
|
91
93
|
}
|
|
@@ -94,7 +96,7 @@ export function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }: Pa
|
|
|
94
96
|
if (loading) {
|
|
95
97
|
return (
|
|
96
98
|
<div className="flex items-center justify-center py-8">
|
|
97
|
-
<p className="text-muted-foreground">
|
|
99
|
+
<p className="text-muted-foreground">{t("billing.ui.payment_form.loading")}</p>
|
|
98
100
|
</div>
|
|
99
101
|
);
|
|
100
102
|
}
|
|
@@ -133,7 +135,7 @@ export function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }: Pa
|
|
|
133
135
|
<div className="flex items-center gap-x-2">
|
|
134
136
|
<Checkbox id="setAsDefault" checked={setAsDefault} onCheckedChange={(checked) => setSetAsDefault(!!checked)} />
|
|
135
137
|
<Label htmlFor="setAsDefault" className="cursor-pointer font-normal">
|
|
136
|
-
|
|
138
|
+
{t("billing.ui.payment_form.set_default")}
|
|
137
139
|
</Label>
|
|
138
140
|
</div>
|
|
139
141
|
|
|
@@ -147,10 +149,10 @@ export function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }: Pa
|
|
|
147
149
|
{/* Action Buttons */}
|
|
148
150
|
<div className="flex justify-end gap-x-2">
|
|
149
151
|
<Button type="button" variant="outline" onClick={onCancel} disabled={isSubmitting || isLoading}>
|
|
150
|
-
|
|
152
|
+
{t("ui.buttons.cancel")}
|
|
151
153
|
</Button>
|
|
152
154
|
<Button type="submit" disabled={!stripe || isSubmitting || isLoading}>
|
|
153
|
-
{isSubmitting ? "
|
|
155
|
+
{isSubmitting ? t("billing.ui.wizard.review.processing") : t("billing.ui.cards.payment_method.add_card")}
|
|
154
156
|
</Button>
|
|
155
157
|
</div>
|
|
156
158
|
</form>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { useEffect, useState } from "react";
|
|
4
5
|
import { Tabs, TabsList, TabsTrigger } from "../../../../../shadcnui";
|
|
5
6
|
import { InvoiceStatus, StripeInvoiceInterface } from "../../data/stripe-invoice.interface";
|
|
@@ -9,6 +10,7 @@ import { InvoicesList } from "../lists/InvoicesList";
|
|
|
9
10
|
type StatusFilter = InvoiceStatus | "all";
|
|
10
11
|
|
|
11
12
|
export function InvoicesContainer() {
|
|
13
|
+
const t = useTranslations();
|
|
12
14
|
const [invoices, setInvoices] = useState<StripeInvoiceInterface[]>([]);
|
|
13
15
|
const [loading, setLoading] = useState<boolean>(true);
|
|
14
16
|
const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
|
|
@@ -40,22 +42,22 @@ export function InvoicesContainer() {
|
|
|
40
42
|
{/* Status Filter Tabs */}
|
|
41
43
|
<Tabs value={statusFilter} onValueChange={handleFilterChange}>
|
|
42
44
|
<TabsList>
|
|
43
|
-
<TabsTrigger value="all">
|
|
44
|
-
<TabsTrigger value={InvoiceStatus.PAID}>
|
|
45
|
-
<TabsTrigger value={InvoiceStatus.OPEN}>
|
|
46
|
-
<TabsTrigger value={InvoiceStatus.VOID}>
|
|
47
|
-
<TabsTrigger value={InvoiceStatus.UNCOLLECTIBLE}>
|
|
45
|
+
<TabsTrigger value="all">{t("billing.ui.invoices.filter_all")}</TabsTrigger>
|
|
46
|
+
<TabsTrigger value={InvoiceStatus.PAID}>{t("billing.ui.invoice_status.paid")}</TabsTrigger>
|
|
47
|
+
<TabsTrigger value={InvoiceStatus.OPEN}>{t("billing.ui.invoice_status.open")}</TabsTrigger>
|
|
48
|
+
<TabsTrigger value={InvoiceStatus.VOID}>{t("billing.ui.invoice_status.void")}</TabsTrigger>
|
|
49
|
+
<TabsTrigger value={InvoiceStatus.UNCOLLECTIBLE}>{t("billing.ui.invoice_status.uncollectible")}</TabsTrigger>
|
|
48
50
|
</TabsList>
|
|
49
51
|
</Tabs>
|
|
50
52
|
|
|
51
53
|
{/* Loading State */}
|
|
52
|
-
{loading && <div className="text-center py-8 text-muted-foreground">
|
|
54
|
+
{loading && <div className="text-center py-8 text-muted-foreground">{t("billing.ui.invoices.loading")}</div>}
|
|
53
55
|
|
|
54
56
|
{/* Empty State */}
|
|
55
57
|
{!loading && invoices.length === 0 && (
|
|
56
58
|
<div className="border border-dashed border-gray-300 rounded-lg p-8 text-center">
|
|
57
|
-
<p className="text-lg font-medium text-muted-foreground mb-2">
|
|
58
|
-
<p className="text-sm text-muted-foreground">
|
|
59
|
+
<p className="text-lg font-medium text-muted-foreground mb-2">{t("billing.ui.invoices.empty_title")}</p>
|
|
60
|
+
<p className="text-sm text-muted-foreground">{t("billing.ui.invoices.empty_description")}</p>
|
|
59
61
|
</div>
|
|
60
62
|
)}
|
|
61
63
|
|