@carlonicora/nextjs-jsonapi 1.129.1 → 1.130.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/{BlockNoteEditor-RG2YQDMQ.js → BlockNoteEditor-AI5C4PBZ.js} +11 -11
- package/dist/BlockNoteEditor-AI5C4PBZ.js.map +1 -0
- package/dist/{BlockNoteEditor-WIAQJ5HM.mjs → BlockNoteEditor-KARBUMFE.mjs} +4 -4
- package/dist/BlockNoteEditor-KARBUMFE.mjs.map +1 -0
- package/dist/billing/index.js +398 -389
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +131 -122
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-YQTMNZQB.js → chunk-BHZVFSDR.js} +158 -89
- package/dist/chunk-BHZVFSDR.js.map +1 -0
- package/dist/{chunk-F54XZKPY.mjs → chunk-NXHNJCY2.mjs} +2119 -2050
- package/dist/chunk-NXHNJCY2.mjs.map +1 -0
- package/dist/client/index.js +2 -2
- package/dist/client/index.mjs +1 -1
- package/dist/components/index.d.mts +20 -2
- package/dist/components/index.d.ts +20 -2
- package/dist/components/index.js +8 -2
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +7 -1
- package/dist/contexts/index.js +2 -2
- package/dist/contexts/index.mjs +1 -1
- package/dist/features/help/index.js +32 -31
- package/dist/features/help/index.js.map +1 -1
- package/dist/features/help/index.mjs +3 -2
- package/dist/features/help/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/containers/ReactMarkdownContainer.tsx +2 -2
- package/src/components/editors/BlockNoteDiffInlineContent.tsx +10 -10
- package/src/components/editors/BlockNoteEditor.tsx +2 -2
- package/src/components/forms/FormCheckbox.tsx +2 -2
- package/src/components/forms/FormFieldWrapper.tsx +1 -1
- package/src/components/forms/FormSlider.tsx +1 -1
- package/src/components/forms/GdprConsentCheckbox.tsx +1 -1
- package/src/components/hero/EntitySection.tsx +2 -1
- package/src/components/index.ts +1 -0
- package/src/components/typography/DetailField.tsx +35 -0
- package/src/components/typography/MicroLabel.tsx +21 -0
- package/src/components/typography/SectionHeader.tsx +18 -0
- package/src/components/typography/index.ts +6 -0
- package/src/features/assistant/components/parts/AssistantEmptyState.tsx +2 -1
- package/src/features/assistant/components/parts/AssistantSidebar.tsx +2 -1
- package/src/features/auth/components/buttons/GoogleSignInButton.tsx +1 -1
- package/src/features/auth/components/forms/Register.tsx +1 -1
- package/src/features/auth/components/forms/TwoFactorChallenge.tsx +3 -3
- package/src/features/auth/components/two-factor/BackupCodesDialog.tsx +1 -1
- package/src/features/auth/components/two-factor/TotpInput.tsx +2 -2
- package/src/features/auth/components/two-factor/TwoFactorSettings.tsx +3 -3
- package/src/features/billing/components/cards/BillingUsageSummaryCard.tsx +4 -4
- package/src/features/billing/components/cards/CustomerInfoCard.tsx +6 -6
- package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +5 -5
- package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +4 -4
- package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +4 -4
- package/src/features/billing/components/containers/BillingDashboardContainer.tsx +2 -2
- package/src/features/billing/components/widgets/BillingAlertBanner.tsx +8 -8
- package/src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx +2 -2
- package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +8 -2
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +1 -1
- package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +16 -18
- package/src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx +2 -2
- package/src/features/billing/stripe-invoice/components/widgets/InvoiceStatusBadge.tsx +8 -7
- package/src/features/billing/stripe-price/components/lists/PricesList.tsx +6 -13
- package/src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx +3 -3
- package/src/features/billing/stripe-product/components/lists/ProductsList.tsx +4 -7
- package/src/features/billing/stripe-promotion-code/components/PromoCodeInput.tsx +6 -6
- package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +2 -2
- package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +10 -10
- package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +2 -2
- package/src/features/billing/stripe-subscription/components/widgets/ProductPricingRow.tsx +2 -1
- package/src/features/billing/stripe-subscription/components/widgets/ProrationPreview.tsx +1 -1
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx +12 -11
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +2 -1
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +10 -13
- package/src/features/billing/stripe-usage/components/containers/UsageContainer.tsx +2 -2
- package/src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx +6 -6
- package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +3 -2
- package/src/features/company/components/details/CompanyContent.tsx +4 -7
- package/src/features/company/components/details/TokenStatusIndicator.tsx +5 -5
- package/src/features/company/components/forms/CompanyDeleter.tsx +1 -1
- package/src/features/company/components/forms/CompanyEditor.tsx +3 -2
- package/src/features/content/components/lists/ContentsList.tsx +2 -1
- package/src/features/feature/components/forms/FormFeatures.tsx +6 -1
- package/src/features/help/components/HelpTOC.tsx +2 -3
- package/src/features/how-to/components/containers/HowToCommand.tsx +3 -3
- package/src/features/how-to/components/containers/HowToCommandViewer.tsx +4 -1
- package/src/features/notification/components/containers/NotificationsListContainer.tsx +1 -1
- package/src/features/notification/components/modals/NotificationModal.tsx +2 -2
- package/src/features/oauth/components/OAuthClientCard.tsx +1 -1
- package/src/features/oauth/components/OAuthClientDetail.tsx +5 -9
- package/src/features/oauth/components/OAuthClientForm.tsx +3 -3
- package/src/features/oauth/components/OAuthClientList.tsx +5 -4
- package/src/features/oauth/components/OAuthClientSecretDisplay.tsx +3 -3
- package/src/features/oauth/components/OAuthRedirectUriInput.tsx +1 -1
- package/src/features/oauth/components/OAuthScopeSelector.tsx +2 -2
- package/src/features/oauth/components/consent/OAuthConsentHeader.tsx +1 -1
- package/src/features/oauth/components/consent/OAuthConsentScreen.tsx +1 -1
- package/src/features/oauth/components/consent/OAuthScopeList.tsx +2 -3
- package/src/features/rbac/components/RbacContainer.tsx +1 -1
- package/src/features/rbac/components/RbacPermissionCell.tsx +2 -2
- package/src/features/rbac/components/RbacPermissionPicker.tsx +3 -3
- package/src/features/referral/components/ReferralWidget.tsx +7 -6
- package/src/features/waitlist/components/forms/WaitlistForm.tsx +3 -2
- package/src/features/waitlist/components/forms/WaitlistQuestionnaireRenderer.tsx +1 -1
- package/src/features/waitlist/components/lists/WaitlistList.tsx +2 -1
- package/src/features/waitlist/components/sections/WaitlistConfirmation.tsx +3 -2
- package/src/features/waitlist/components/sections/WaitlistSuccessState.tsx +2 -1
- package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +1 -1
- package/src/shadcnui/custom/link.tsx +7 -1
- package/src/shadcnui/ui/alert.tsx +1 -1
- package/src/shadcnui/ui/badge.tsx +7 -0
- package/src/shadcnui/ui/button.tsx +1 -1
- package/src/shadcnui/ui/chart.tsx +1 -3
- package/dist/BlockNoteEditor-RG2YQDMQ.js.map +0 -1
- package/dist/BlockNoteEditor-WIAQJ5HM.mjs.map +0 -1
- package/dist/chunk-F54XZKPY.mjs.map +0 -1
- package/dist/chunk-YQTMNZQB.js.map +0 -1
|
@@ -15,15 +15,15 @@ export function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment
|
|
|
15
15
|
if (subscription.status === SubscriptionStatus.PAST_DUE) {
|
|
16
16
|
return (
|
|
17
17
|
<div className="bg-red-50 border border-red-200 rounded-lg p-4 flex items-start gap-x-3">
|
|
18
|
-
<AlertCircle className="
|
|
18
|
+
<AlertCircle className="text-destructive mt-0.5 h-5 w-5" />
|
|
19
19
|
<div className="flex-1">
|
|
20
|
-
<h3 className="
|
|
21
|
-
<p className="text-
|
|
20
|
+
<h3 className="text-destructive text-sm font-medium">Payment Failed</h3>
|
|
21
|
+
<p className="text-destructive mt-1 text-sm">
|
|
22
22
|
Your last payment failed. Please update your payment method to avoid service interruption.
|
|
23
23
|
</p>
|
|
24
24
|
</div>
|
|
25
25
|
{onUpdatePayment && (
|
|
26
|
-
<Button variant="outline" size="sm" onClick={onUpdatePayment} className="border-red-300
|
|
26
|
+
<Button variant="outline" size="sm" onClick={onUpdatePayment} className="text-destructive border-red-300">
|
|
27
27
|
Update Payment Method
|
|
28
28
|
</Button>
|
|
29
29
|
)}
|
|
@@ -40,16 +40,16 @@ export function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment
|
|
|
40
40
|
if (daysRemaining <= 7) {
|
|
41
41
|
return (
|
|
42
42
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 flex items-start gap-x-3">
|
|
43
|
-
<AlertCircle className="
|
|
43
|
+
<AlertCircle className="text-warning mt-0.5 h-5 w-5" />
|
|
44
44
|
<div className="flex-1">
|
|
45
|
-
<h3 className="
|
|
46
|
-
<p className="text-
|
|
45
|
+
<h3 className="text-warning text-sm font-medium">Trial Ending Soon</h3>
|
|
46
|
+
<p className="text-warning mt-1 text-sm">
|
|
47
47
|
Your trial ends in {daysRemaining} {daysRemaining === 1 ? "day" : "days"}. Add a payment method to
|
|
48
48
|
continue your subscription.
|
|
49
49
|
</p>
|
|
50
50
|
</div>
|
|
51
51
|
{onAddPayment && (
|
|
52
|
-
<Button variant="outline" size="sm" onClick={onAddPayment} className="border-yellow-300
|
|
52
|
+
<Button variant="outline" size="sm" onClick={onAddPayment} className="text-warning border-yellow-300">
|
|
53
53
|
Add Payment Method
|
|
54
54
|
</Button>
|
|
55
55
|
)}
|
package/src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx
CHANGED
|
@@ -42,7 +42,7 @@ export function PaymentMethodsContainer() {
|
|
|
42
42
|
<div className="flex items-center justify-between">
|
|
43
43
|
<div className="flex items-center gap-x-3">
|
|
44
44
|
<CreditCard className="h-8 w-8" />
|
|
45
|
-
<h1 className="text-3xl font-
|
|
45
|
+
<h1 className="text-primary text-3xl font-semibold">Payment Methods</h1>
|
|
46
46
|
</div>
|
|
47
47
|
<Button onClick={() => setShowAddPaymentMethod(true)}>Add Payment Method</Button>
|
|
48
48
|
</div>
|
|
@@ -52,7 +52,7 @@ export function PaymentMethodsContainer() {
|
|
|
52
52
|
<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
53
|
<CreditCard className="h-16 w-16 text-muted-foreground" />
|
|
54
54
|
<div className="text-center">
|
|
55
|
-
<h3 className="mb-2 text-
|
|
55
|
+
<h3 className="mb-2 text-sm font-medium">No payment methods</h3>
|
|
56
56
|
<p className="mb-4 text-muted-foreground">
|
|
57
57
|
Add a payment method to enable subscriptions and secure checkout.
|
|
58
58
|
</p>
|
|
@@ -91,7 +91,9 @@ export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCard
|
|
|
91
91
|
<Card className="relative">
|
|
92
92
|
{/* Default Badge */}
|
|
93
93
|
{isDefault && (
|
|
94
|
-
<Badge className="absolute right-2 top-2
|
|
94
|
+
<Badge variant="softGreen" className="absolute right-2 top-2">
|
|
95
|
+
Default
|
|
96
|
+
</Badge>
|
|
95
97
|
)}
|
|
96
98
|
|
|
97
99
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
@@ -118,7 +120,11 @@ export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCard
|
|
|
118
120
|
Set as Default
|
|
119
121
|
</DropdownMenuItem>
|
|
120
122
|
)}
|
|
121
|
-
<DropdownMenuItem
|
|
123
|
+
<DropdownMenuItem
|
|
124
|
+
onClick={() => setShowRemoveDialog(true)}
|
|
125
|
+
disabled={loading}
|
|
126
|
+
className="text-destructive"
|
|
127
|
+
>
|
|
122
128
|
Remove
|
|
123
129
|
</DropdownMenuItem>
|
|
124
130
|
</DropdownMenuContent>
|
|
@@ -132,7 +132,7 @@ export function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }: Pa
|
|
|
132
132
|
{/* Set as Default Checkbox */}
|
|
133
133
|
<div className="flex items-center gap-x-2">
|
|
134
134
|
<Checkbox id="setAsDefault" checked={setAsDefault} onCheckedChange={(checked) => setSetAsDefault(!!checked)} />
|
|
135
|
-
<Label htmlFor="setAsDefault" className="
|
|
135
|
+
<Label htmlFor="setAsDefault" className="font-normal">
|
|
136
136
|
Set as default payment method
|
|
137
137
|
</Label>
|
|
138
138
|
</div>
|
|
@@ -62,29 +62,29 @@ export function InvoiceDetails({
|
|
|
62
62
|
{/* Invoice Info Grid */}
|
|
63
63
|
<div className="grid grid-cols-2 gap-4">
|
|
64
64
|
<div>
|
|
65
|
-
<span className="text-
|
|
66
|
-
<p className="
|
|
65
|
+
<span className="text-muted-foreground text-xs">Billing Period:</span>
|
|
66
|
+
<p className="text-sm">
|
|
67
67
|
{formatDate(invoice.periodStart)} - {formatDate(invoice.periodEnd)}
|
|
68
68
|
</p>
|
|
69
69
|
</div>
|
|
70
70
|
|
|
71
71
|
{invoice.dueDate && (
|
|
72
72
|
<div>
|
|
73
|
-
<span className="text-
|
|
74
|
-
<p className="
|
|
73
|
+
<span className="text-muted-foreground text-xs">Due Date:</span>
|
|
74
|
+
<p className="text-sm">{formatDate(invoice.dueDate)}</p>
|
|
75
75
|
</div>
|
|
76
76
|
)}
|
|
77
77
|
|
|
78
78
|
{invoice.paidAt && (
|
|
79
79
|
<div>
|
|
80
|
-
<span className="text-
|
|
81
|
-
<p className="
|
|
80
|
+
<span className="text-muted-foreground text-xs">Paid Date:</span>
|
|
81
|
+
<p className="text-sm">{formatDate(invoice.paidAt)}</p>
|
|
82
82
|
</div>
|
|
83
83
|
)}
|
|
84
84
|
|
|
85
85
|
<div>
|
|
86
|
-
<span className="text-
|
|
87
|
-
<p className="
|
|
86
|
+
<span className="text-muted-foreground text-xs">Attempt Count:</span>
|
|
87
|
+
<p className="text-sm">{invoice.attemptCount}</p>
|
|
88
88
|
</div>
|
|
89
89
|
</div>
|
|
90
90
|
|
|
@@ -112,14 +112,14 @@ export function InvoiceDetails({
|
|
|
112
112
|
{/* Amount Breakdown */}
|
|
113
113
|
<div className="space-y-2 border-t pt-4">
|
|
114
114
|
<div className="flex justify-between">
|
|
115
|
-
<span className="text-
|
|
116
|
-
<span className="
|
|
115
|
+
<span className="text-muted-foreground text-xs">Subtotal:</span>
|
|
116
|
+
<span className="text-sm">{formatCurrency(invoice.subtotal, invoice.currency)}</span>
|
|
117
117
|
</div>
|
|
118
118
|
|
|
119
119
|
{invoice.tax !== undefined && invoice.tax > 0 && (
|
|
120
120
|
<div className="flex justify-between">
|
|
121
|
-
<span className="text-
|
|
122
|
-
<span className="
|
|
121
|
+
<span className="text-muted-foreground text-xs">Tax:</span>
|
|
122
|
+
<span className="text-sm">{formatCurrency(invoice.tax, invoice.currency)}</span>
|
|
123
123
|
</div>
|
|
124
124
|
)}
|
|
125
125
|
|
|
@@ -130,17 +130,15 @@ export function InvoiceDetails({
|
|
|
130
130
|
|
|
131
131
|
{invoice.amountPaid > 0 && (
|
|
132
132
|
<div className="flex justify-between">
|
|
133
|
-
<span className="text-
|
|
134
|
-
<span className="
|
|
135
|
-
{formatCurrency(invoice.amountPaid, invoice.currency)}
|
|
136
|
-
</span>
|
|
133
|
+
<span className="text-muted-foreground text-xs">Amount Paid:</span>
|
|
134
|
+
<span className="text-sm text-success">{formatCurrency(invoice.amountPaid, invoice.currency)}</span>
|
|
137
135
|
</div>
|
|
138
136
|
)}
|
|
139
137
|
|
|
140
138
|
{invoice.amountRemaining > 0 && (
|
|
141
139
|
<div className="flex justify-between">
|
|
142
|
-
<span className="text-
|
|
143
|
-
<span className="
|
|
140
|
+
<span className="text-muted-foreground text-xs">Amount Due:</span>
|
|
141
|
+
<span className="text-sm text-destructive">
|
|
144
142
|
{formatCurrency(invoice.amountRemaining, invoice.currency)}
|
|
145
143
|
</span>
|
|
146
144
|
</div>
|
|
@@ -54,12 +54,12 @@ export function InvoicesList({ invoices, onInvoicesChange }: InvoicesListProps)
|
|
|
54
54
|
className="cursor-pointer hover:bg-muted/50"
|
|
55
55
|
>
|
|
56
56
|
<TableCell className="font-medium">{invoiceNumber}</TableCell>
|
|
57
|
-
<TableCell className="text-muted-foreground text-
|
|
57
|
+
<TableCell className="text-muted-foreground text-xs">{date}</TableCell>
|
|
58
58
|
<TableCell>
|
|
59
59
|
<InvoiceStatusBadge status={invoice.status} />
|
|
60
60
|
</TableCell>
|
|
61
61
|
<TableCell className="text-right font-medium">{amount}</TableCell>
|
|
62
|
-
<TableCell className="text-muted-foreground text-
|
|
62
|
+
<TableCell className="text-muted-foreground text-xs">{period}</TableCell>
|
|
63
63
|
</TableRow>
|
|
64
64
|
);
|
|
65
65
|
})}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { Badge, type BadgeProps } from "../../../../../shadcnui";
|
|
3
4
|
import { InvoiceStatus } from "../../data/stripe-invoice.interface";
|
|
4
5
|
|
|
5
6
|
type InvoiceStatusBadgeProps = {
|
|
@@ -8,34 +9,34 @@ type InvoiceStatusBadgeProps = {
|
|
|
8
9
|
|
|
9
10
|
type StatusConfig = {
|
|
10
11
|
label: string;
|
|
11
|
-
|
|
12
|
+
variant: BadgeProps["variant"];
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
const statusConfig: Record<InvoiceStatus, StatusConfig> = {
|
|
15
16
|
[InvoiceStatus.DRAFT]: {
|
|
16
17
|
label: "Draft",
|
|
17
|
-
|
|
18
|
+
variant: "softGray",
|
|
18
19
|
},
|
|
19
20
|
[InvoiceStatus.OPEN]: {
|
|
20
21
|
label: "Open",
|
|
21
|
-
|
|
22
|
+
variant: "softBlue",
|
|
22
23
|
},
|
|
23
24
|
[InvoiceStatus.PAID]: {
|
|
24
25
|
label: "Paid",
|
|
25
|
-
|
|
26
|
+
variant: "softGreen",
|
|
26
27
|
},
|
|
27
28
|
[InvoiceStatus.VOID]: {
|
|
28
29
|
label: "Void",
|
|
29
|
-
|
|
30
|
+
variant: "softGray",
|
|
30
31
|
},
|
|
31
32
|
[InvoiceStatus.UNCOLLECTIBLE]: {
|
|
32
33
|
label: "Uncollectible",
|
|
33
|
-
|
|
34
|
+
variant: "softRed",
|
|
34
35
|
},
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
export function InvoiceStatusBadge({ status }: InvoiceStatusBadgeProps) {
|
|
38
39
|
const config = statusConfig[status] || statusConfig[InvoiceStatus.DRAFT];
|
|
39
40
|
|
|
40
|
-
return <
|
|
41
|
+
return <Badge variant={config.variant}>{config.label}</Badge>;
|
|
41
42
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
AlertDialogFooter,
|
|
12
12
|
AlertDialogHeader,
|
|
13
13
|
AlertDialogTitle,
|
|
14
|
+
Badge,
|
|
14
15
|
Button,
|
|
15
16
|
} from "../../../../../shadcnui";
|
|
16
17
|
import { formatCurrency } from "../../../components/utils/currency";
|
|
@@ -184,24 +185,16 @@ export function PricesList({ productId, onPricesChange }: PricesListProps) {
|
|
|
184
185
|
|
|
185
186
|
<div className="flex flex-wrap gap-2">
|
|
186
187
|
{price.active ? (
|
|
187
|
-
<
|
|
188
|
-
Active
|
|
189
|
-
</span>
|
|
188
|
+
<Badge variant="softGreen">Active</Badge>
|
|
190
189
|
) : (
|
|
191
|
-
<
|
|
192
|
-
Inactive
|
|
193
|
-
</span>
|
|
190
|
+
<Badge variant="softGray">Inactive</Badge>
|
|
194
191
|
)}
|
|
195
192
|
|
|
196
|
-
{price.recurring?.usageType === "metered" &&
|
|
197
|
-
<span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full font-medium">
|
|
198
|
-
Metered
|
|
199
|
-
</span>
|
|
200
|
-
)}
|
|
193
|
+
{price.recurring?.usageType === "metered" && <Badge variant="softBlue">Metered</Badge>}
|
|
201
194
|
|
|
202
|
-
<
|
|
195
|
+
<Badge variant="softGray" className="uppercase">
|
|
203
196
|
{price.currency}
|
|
204
|
-
</
|
|
197
|
+
</Badge>
|
|
205
198
|
</div>
|
|
206
199
|
</div>
|
|
207
200
|
);
|
package/src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx
CHANGED
|
@@ -20,7 +20,7 @@ export function ProductsAdminContainer() {
|
|
|
20
20
|
if (!hasRole(getRoleId().Administrator)) {
|
|
21
21
|
return (
|
|
22
22
|
<div className="flex h-64 items-center justify-center">
|
|
23
|
-
<p className="text-
|
|
23
|
+
<p className="text-destructive text-xs/relaxed">Permission denied. Administrator access required.</p>
|
|
24
24
|
</div>
|
|
25
25
|
);
|
|
26
26
|
}
|
|
@@ -55,7 +55,7 @@ export function ProductsAdminContainer() {
|
|
|
55
55
|
<div className="flex items-center justify-between">
|
|
56
56
|
<div className="flex items-center gap-x-3">
|
|
57
57
|
<Package className="h-8 w-8" />
|
|
58
|
-
<h1 className="text-3xl font-
|
|
58
|
+
<h1 className="text-primary text-3xl font-semibold">Product & Price Management</h1>
|
|
59
59
|
</div>
|
|
60
60
|
<Button onClick={() => setShowCreateProduct(true)}>Create Product</Button>
|
|
61
61
|
</div>
|
|
@@ -65,7 +65,7 @@ export function ProductsAdminContainer() {
|
|
|
65
65
|
<div className="bg-muted/50 flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed p-12">
|
|
66
66
|
<Package className="text-muted-foreground h-16 w-16" />
|
|
67
67
|
<div className="text-center">
|
|
68
|
-
<h3 className="mb-2 text-
|
|
68
|
+
<h3 className="mb-2 text-sm font-medium">No products yet</h3>
|
|
69
69
|
<p className="text-muted-foreground mb-4">
|
|
70
70
|
Create your first product to start offering subscriptions to your customers.
|
|
71
71
|
</p>
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
AlertDialogFooter,
|
|
12
12
|
AlertDialogHeader,
|
|
13
13
|
AlertDialogTitle,
|
|
14
|
+
Badge,
|
|
14
15
|
Button,
|
|
15
16
|
} from "../../../../../shadcnui";
|
|
16
17
|
import { PricesList } from "../../../stripe-price/components/lists/PricesList";
|
|
@@ -85,15 +86,11 @@ export function ProductsList({ products, onProductsChange }: ProductsListProps)
|
|
|
85
86
|
<Package className="h-6 w-6 text-primary" />
|
|
86
87
|
<div className="flex-1">
|
|
87
88
|
<div className="flex items-center gap-x-3">
|
|
88
|
-
<h3 className="text-
|
|
89
|
+
<h3 className="text-sm font-medium">{product.name}</h3>
|
|
89
90
|
{product.active ? (
|
|
90
|
-
<
|
|
91
|
-
Active
|
|
92
|
-
</span>
|
|
91
|
+
<Badge variant="softGreen">Active</Badge>
|
|
93
92
|
) : (
|
|
94
|
-
<
|
|
95
|
-
Inactive
|
|
96
|
-
</span>
|
|
93
|
+
<Badge variant="softGray">Inactive</Badge>
|
|
97
94
|
)}
|
|
98
95
|
</div>
|
|
99
96
|
{product.description && <p className="text-muted-foreground text-sm mt-1">{product.description}</p>}
|
|
@@ -68,21 +68,21 @@ export function PromoCodeInput({
|
|
|
68
68
|
<div className="space-y-2">
|
|
69
69
|
<div className="flex items-center justify-between p-3 bg-green-50 border border-green-200 rounded-lg">
|
|
70
70
|
<div className="flex items-center gap-2">
|
|
71
|
-
<Check className="h-4 w-4
|
|
72
|
-
<span className="font-medium
|
|
73
|
-
<span className="text-
|
|
71
|
+
<Check className="text-success h-4 w-4" />
|
|
72
|
+
<span className="text-success font-medium">{appliedCode.code}</span>
|
|
73
|
+
<span className="text-success text-sm">{formatDiscount(appliedCode)}</span>
|
|
74
74
|
</div>
|
|
75
75
|
<Button
|
|
76
76
|
variant="ghost"
|
|
77
77
|
size="sm"
|
|
78
78
|
onClick={onRemove}
|
|
79
79
|
disabled={disabled}
|
|
80
|
-
className="text-
|
|
80
|
+
className="text-success hover:bg-green-100 hover:text-success"
|
|
81
81
|
>
|
|
82
82
|
<X className="h-4 w-4" />
|
|
83
83
|
</Button>
|
|
84
84
|
</div>
|
|
85
|
-
<p className="text-
|
|
85
|
+
<p className="text-success text-sm">{formatDuration(appliedCode)}</p>
|
|
86
86
|
</div>
|
|
87
87
|
);
|
|
88
88
|
}
|
|
@@ -102,7 +102,7 @@ export function PromoCodeInput({
|
|
|
102
102
|
{isValidating ? <Loader2 className="h-4 w-4 animate-spin" /> : "Apply"}
|
|
103
103
|
</Button>
|
|
104
104
|
</div>
|
|
105
|
-
{error && <p className="text-
|
|
105
|
+
{error && <p className="text-destructive text-xs/relaxed">{error}</p>}
|
|
106
106
|
</div>
|
|
107
107
|
);
|
|
108
108
|
}
|
package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
CHANGED
|
@@ -55,7 +55,7 @@ export function SubscriptionsContainer({ onOpenWizard, hasActiveRecurringSubscri
|
|
|
55
55
|
<div className="flex items-center justify-between">
|
|
56
56
|
<div className="flex items-center gap-x-3">
|
|
57
57
|
<CreditCard className="h-8 w-8" />
|
|
58
|
-
<h1 className="text-3xl font-
|
|
58
|
+
<h1 className="text-primary text-3xl font-semibold">Subscriptions</h1>
|
|
59
59
|
</div>
|
|
60
60
|
{subscriptions.length > 0 && (
|
|
61
61
|
<Button onClick={() => onOpenWizard?.()}>
|
|
@@ -74,7 +74,7 @@ export function SubscriptionsContainer({ onOpenWizard, hasActiveRecurringSubscri
|
|
|
74
74
|
<div className="flex flex-col items-center justify-center py-12 space-y-4">
|
|
75
75
|
<CreditCard className="h-16 w-16 text-muted-foreground" />
|
|
76
76
|
<div className="text-center">
|
|
77
|
-
<h3 className="text-
|
|
77
|
+
<h3 className="text-sm font-medium mb-2">No Active Subscriptions</h3>
|
|
78
78
|
<p className="text-muted-foreground mb-6">Choose a subscription plan to get started with our services.</p>
|
|
79
79
|
<Button onClick={() => onOpenWizard?.()}>Subscribe to a Plan</Button>
|
|
80
80
|
</div>
|
|
@@ -117,7 +117,7 @@ export function SubscriptionDetails({
|
|
|
117
117
|
<div className="space-y-6">
|
|
118
118
|
{/* Status */}
|
|
119
119
|
<div className="flex items-center gap-x-3">
|
|
120
|
-
<span className="text-
|
|
120
|
+
<span className="text-muted-foreground text-xs">Status:</span>
|
|
121
121
|
<SubscriptionStatusBadge
|
|
122
122
|
status={subscription.status}
|
|
123
123
|
cancelAtPeriodEnd={subscription.cancelAtPeriodEnd}
|
|
@@ -127,20 +127,20 @@ export function SubscriptionDetails({
|
|
|
127
127
|
{/* Plan Info */}
|
|
128
128
|
<div className="space-y-2">
|
|
129
129
|
<div className="flex justify-between">
|
|
130
|
-
<span className="text-
|
|
131
|
-
<span className="
|
|
130
|
+
<span className="text-muted-foreground text-xs">Plan:</span>
|
|
131
|
+
<span className="text-sm">{formatPlanName(subscription.price)}</span>
|
|
132
132
|
</div>
|
|
133
133
|
<div className="flex justify-between">
|
|
134
|
-
<span className="text-
|
|
135
|
-
<span className="
|
|
134
|
+
<span className="text-muted-foreground text-xs">Billing Amount:</span>
|
|
135
|
+
<span className="text-sm">{formatBillingAmount(subscription.price)}</span>
|
|
136
136
|
</div>
|
|
137
137
|
</div>
|
|
138
138
|
|
|
139
139
|
{/* Current Period */}
|
|
140
140
|
<div className="space-y-2">
|
|
141
141
|
<div className="flex justify-between">
|
|
142
|
-
<span className="text-
|
|
143
|
-
<span className="
|
|
142
|
+
<span className="text-muted-foreground text-xs">Current Period:</span>
|
|
143
|
+
<span className="text-sm">
|
|
144
144
|
{formatDate(subscription.currentPeriodStart)} - {formatDate(subscription.currentPeriodEnd)}
|
|
145
145
|
</span>
|
|
146
146
|
</div>
|
|
@@ -149,15 +149,15 @@ export function SubscriptionDetails({
|
|
|
149
149
|
{/* Trial Info */}
|
|
150
150
|
{subscription.trialEnd && (
|
|
151
151
|
<div className="flex justify-between">
|
|
152
|
-
<span className="text-
|
|
153
|
-
<span className="
|
|
152
|
+
<span className="text-muted-foreground text-xs">Trial Ends:</span>
|
|
153
|
+
<span className="text-sm">{formatDate(subscription.trialEnd)}</span>
|
|
154
154
|
</div>
|
|
155
155
|
)}
|
|
156
156
|
|
|
157
157
|
{/* Cancellation Warning */}
|
|
158
158
|
{subscription.cancelAtPeriodEnd && (
|
|
159
159
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3">
|
|
160
|
-
<p className="text-
|
|
160
|
+
<p className="text-warning text-xs/relaxed">
|
|
161
161
|
This subscription will be canceled at the end of the current period on{" "}
|
|
162
162
|
{formatDate(subscription.currentPeriodEnd)}.
|
|
163
163
|
</p>
|
|
@@ -67,7 +67,7 @@ export function PricingCard({
|
|
|
67
67
|
)}
|
|
68
68
|
|
|
69
69
|
<CardHeader className="pb-2">
|
|
70
|
-
<h3 className="
|
|
70
|
+
<h3 className="text-sm font-medium">{description}</h3>
|
|
71
71
|
</CardHeader>
|
|
72
72
|
|
|
73
73
|
<CardContent className="pb-4 grow">
|
|
@@ -80,7 +80,7 @@ export function PricingCard({
|
|
|
80
80
|
<ul className="space-y-2">
|
|
81
81
|
{features.map((feature, index) => (
|
|
82
82
|
<li key={index} className="flex items-start gap-2">
|
|
83
|
-
<Check className="
|
|
83
|
+
<Check className="text-success mt-0.5 h-4 w-4 shrink-0" />
|
|
84
84
|
<span className="text-sm text-muted-foreground">{feature}</span>
|
|
85
85
|
</li>
|
|
86
86
|
))}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { StripePriceInterface } from "../../../stripe-price/data/stripe-price.interface";
|
|
4
4
|
import { StripeProductInterface } from "../../../stripe-product";
|
|
5
|
+
import { SectionHeader } from "../../../../../components/typography";
|
|
5
6
|
import { PricingCard } from "./PricingCard";
|
|
6
7
|
|
|
7
8
|
export type ProductPricingRowProps = {
|
|
@@ -28,7 +29,7 @@ export function ProductPricingRow({
|
|
|
28
29
|
return (
|
|
29
30
|
<div className="space-y-3">
|
|
30
31
|
{/* Product name header */}
|
|
31
|
-
<
|
|
32
|
+
<SectionHeader>{product.name}</SectionHeader>
|
|
32
33
|
|
|
33
34
|
{/* Price cards in columns */}
|
|
34
35
|
<div
|
|
@@ -16,7 +16,7 @@ export function ProrationPreview({ preview }: ProrationPreviewProps) {
|
|
|
16
16
|
{preview.lineItems.map((item, index) => (
|
|
17
17
|
<div key={index} className="flex justify-between text-sm">
|
|
18
18
|
<span className="text-blue-800">{item.description}</span>
|
|
19
|
-
<span className={`font-medium ${item.amount < 0 ? "text-
|
|
19
|
+
<span className={`font-medium ${item.amount < 0 ? "text-success" : "text-blue-900"}`}>
|
|
20
20
|
{formatCurrency(item.amount, preview.currency)}
|
|
21
21
|
</span>
|
|
22
22
|
</div>
|
package/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { Badge, type BadgeProps } from "../../../../../shadcnui";
|
|
3
4
|
import { SubscriptionStatus } from "../../data";
|
|
4
5
|
|
|
5
6
|
type SubscriptionStatusBadgeProps = {
|
|
@@ -9,47 +10,47 @@ type SubscriptionStatusBadgeProps = {
|
|
|
9
10
|
|
|
10
11
|
type StatusConfig = {
|
|
11
12
|
label: string;
|
|
12
|
-
|
|
13
|
+
variant: BadgeProps["variant"];
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
const statusConfig: Record<SubscriptionStatus, StatusConfig> = {
|
|
16
17
|
[SubscriptionStatus.ACTIVE]: {
|
|
17
18
|
label: "Active",
|
|
18
|
-
|
|
19
|
+
variant: "softGreen",
|
|
19
20
|
},
|
|
20
21
|
[SubscriptionStatus.TRIALING]: {
|
|
21
22
|
label: "Trial",
|
|
22
|
-
|
|
23
|
+
variant: "softBlue",
|
|
23
24
|
},
|
|
24
25
|
[SubscriptionStatus.PAST_DUE]: {
|
|
25
26
|
label: "Past Due",
|
|
26
|
-
|
|
27
|
+
variant: "softRed",
|
|
27
28
|
},
|
|
28
29
|
[SubscriptionStatus.CANCELED]: {
|
|
29
30
|
label: "Canceled",
|
|
30
|
-
|
|
31
|
+
variant: "softGray",
|
|
31
32
|
},
|
|
32
33
|
[SubscriptionStatus.PAUSED]: {
|
|
33
34
|
label: "Paused",
|
|
34
|
-
|
|
35
|
+
variant: "softYellow",
|
|
35
36
|
},
|
|
36
37
|
[SubscriptionStatus.UNPAID]: {
|
|
37
38
|
label: "Unpaid",
|
|
38
|
-
|
|
39
|
+
variant: "softOrange",
|
|
39
40
|
},
|
|
40
41
|
[SubscriptionStatus.INCOMPLETE]: {
|
|
41
42
|
label: "Incomplete",
|
|
42
|
-
|
|
43
|
+
variant: "softGray",
|
|
43
44
|
},
|
|
44
45
|
[SubscriptionStatus.INCOMPLETE_EXPIRED]: {
|
|
45
46
|
label: "Expired",
|
|
46
|
-
|
|
47
|
+
variant: "softGray",
|
|
47
48
|
},
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
const cancelingConfig: StatusConfig = {
|
|
51
52
|
label: "Canceling",
|
|
52
|
-
|
|
53
|
+
variant: "softAmber",
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
export function SubscriptionStatusBadge({ status, cancelAtPeriodEnd }: SubscriptionStatusBadgeProps) {
|
|
@@ -58,5 +59,5 @@ export function SubscriptionStatusBadge({ status, cancelAtPeriodEnd }: Subscript
|
|
|
58
59
|
? cancelingConfig
|
|
59
60
|
: statusConfig[status] || statusConfig[SubscriptionStatus.CANCELED];
|
|
60
61
|
|
|
61
|
-
return <
|
|
62
|
+
return <Badge variant={config.variant}>{config.label}</Badge>;
|
|
62
63
|
}
|
package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { PaymentMethodForm } from "../../../stripe-customer/components/forms/PaymentMethodForm";
|
|
4
|
+
import { SectionHeader } from "../../../../../components/typography";
|
|
4
5
|
|
|
5
6
|
type WizardStepPaymentMethodProps = {
|
|
6
7
|
onBack: () => void;
|
|
@@ -12,7 +13,7 @@ export function WizardStepPaymentMethod({ onBack, onSuccess, isProcessing }: Wiz
|
|
|
12
13
|
return (
|
|
13
14
|
<div className="space-y-6">
|
|
14
15
|
<div className="text-center">
|
|
15
|
-
<
|
|
16
|
+
<SectionHeader>Add Payment Method</SectionHeader>
|
|
16
17
|
<p className="text-sm text-muted-foreground">Enter your card details to complete your subscription</p>
|
|
17
18
|
</div>
|
|
18
19
|
|