@carlonicora/nextjs-jsonapi 1.28.0 → 1.29.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-CAUNVZUF.js → BlockNoteEditor-YBVEOPV4.js} +13 -13
- package/dist/{BlockNoteEditor-CAUNVZUF.js.map → BlockNoteEditor-YBVEOPV4.js.map} +1 -1
- package/dist/{BlockNoteEditor-EOA4OEVX.mjs → BlockNoteEditor-ZM4YPXHO.mjs} +3 -3
- package/dist/billing/index.d.mts +47 -17
- package/dist/billing/index.d.ts +47 -17
- package/dist/billing/index.js +1241 -1073
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +1375 -1207
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-IXI4GAKB.js → chunk-3X7EEFMN.js} +488 -431
- package/dist/chunk-3X7EEFMN.js.map +1 -0
- package/dist/{chunk-ORFXBO7F.mjs → chunk-DU64WMZD.mjs} +6 -3
- package/dist/chunk-DU64WMZD.mjs.map +1 -0
- package/dist/{chunk-TSEU4KZ2.js → chunk-J22NEVSK.js} +21 -18
- package/dist/chunk-J22NEVSK.js.map +1 -0
- package/dist/{chunk-PYASRX75.mjs → chunk-UCD5CUE4.mjs} +81 -24
- package/dist/chunk-UCD5CUE4.mjs.map +1 -0
- package/dist/client/index.d.mts +14 -5
- package/dist/client/index.d.ts +14 -5
- package/dist/client/index.js +5 -3
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +4 -2
- package/dist/components/index.d.mts +2 -2
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.js +3 -3
- package/dist/components/index.mjs +2 -2
- package/dist/{config-B4pZpLT9.d.ts → config-CHwoRDOp.d.ts} +1 -1
- package/dist/{config-DT1K-t6I.d.mts → config-DiWyJzk9.d.mts} +1 -1
- package/dist/{content.interface-B2Ldg0vg.d.mts → content.interface-BSpowEiW.d.mts} +1 -1
- package/dist/{content.interface-D8NHv3DX.d.ts → content.interface-DFQ7mkpL.d.ts} +1 -1
- package/dist/contexts/index.d.mts +2 -2
- package/dist/contexts/index.d.ts +2 -2
- package/dist/contexts/index.js +3 -3
- package/dist/contexts/index.mjs +2 -2
- package/dist/core/index.d.mts +39 -37
- package/dist/core/index.d.ts +39 -37
- package/dist/core/index.js +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{notification.interface-H0L9WBge.d.ts → notification.interface-CmKmObIU.d.ts} +1 -0
- package/dist/{notification.interface-DEn-Yp_b.d.mts → notification.interface-D5MbtfZK.d.mts} +1 -0
- package/dist/{s3.service-BNytYanU.d.mts → s3.service-BMT7W6KS.d.mts} +19 -19
- package/dist/{s3.service-C7f_Ygz5.d.ts → s3.service-DsXo9nop.d.ts} +19 -19
- package/dist/server/index.d.mts +3 -3
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/dist/{useSocket-BcnThTD0.d.mts → useSocket-DUqGoPya.d.mts} +1 -1
- package/dist/{useSocket-QZTOCzRF.d.ts → useSocket-QuHa0ZmO.d.ts} +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +1 -0
- package/src/components/forms/FormSelect.tsx +2 -1
- package/src/features/auth/data/auth.ts +0 -2
- package/src/features/billing/components/containers/BillingDashboardContainer.tsx +60 -3
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx +12 -152
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +168 -0
- package/src/features/billing/stripe-customer/components/forms/index.ts +1 -0
- package/src/features/billing/stripe-price/components/forms/PriceEditor.tsx +19 -1
- package/src/features/billing/stripe-product/components/forms/ProductEditor.tsx +2 -2
- package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +24 -235
- package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +7 -18
- package/src/features/billing/stripe-subscription/components/forms/index.ts +0 -1
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +10 -1
- package/src/features/billing/stripe-subscription/components/widgets/IntervalToggle.tsx +28 -0
- package/src/features/billing/stripe-subscription/components/widgets/ProductPricingList.tsx +128 -0
- package/src/features/billing/stripe-subscription/components/widgets/ProductPricingRow.tsx +54 -0
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx +68 -0
- package/src/features/billing/stripe-subscription/components/widgets/index.ts +4 -1
- package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +114 -0
- package/src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx +66 -0
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +32 -0
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPlanSelection.tsx +103 -0
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +133 -0
- package/src/features/billing/stripe-subscription/components/wizards/index.ts +6 -0
- package/src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts +217 -0
- package/src/features/billing/stripe-subscription/index.ts +3 -2
- package/src/features/company/components/details/TokenStatusIndicator.tsx +19 -9
- package/src/features/company/data/company.interface.ts +2 -0
- package/src/features/company/data/company.ts +7 -0
- package/src/features/company/hooks/index.ts +1 -0
- package/src/features/company/hooks/useSubscriptionStatus.ts +71 -0
- package/src/features/user/components/forms/UserEditor.tsx +1 -1
- package/src/features/user/components/lists/AdminUsersList.tsx +1 -1
- package/src/features/user/contexts/CurrentUserContext.tsx +1 -1
- package/src/features/user/data/user.ts +1 -1
- package/dist/chunk-IXI4GAKB.js.map +0 -1
- package/dist/chunk-ORFXBO7F.mjs.map +0 -1
- package/dist/chunk-PYASRX75.mjs.map +0 -1
- package/dist/chunk-TSEU4KZ2.js.map +0 -1
- package/src/features/billing/stripe-subscription/components/forms/SubscriptionEditor.tsx +0 -331
- package/src/features/billing/stripe-subscription/components/widgets/PricingCardsGrid.tsx +0 -110
- /package/dist/{BlockNoteEditor-EOA4OEVX.mjs.map → BlockNoteEditor-ZM4YPXHO.mjs.map} +0 -0
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
|
|
4
|
-
import { useEffect, useState } from "react";
|
|
5
3
|
import {
|
|
6
|
-
Alert,
|
|
7
|
-
AlertDescription,
|
|
8
|
-
Button,
|
|
9
|
-
Checkbox,
|
|
10
4
|
Dialog,
|
|
11
5
|
DialogContent,
|
|
12
6
|
DialogDescription,
|
|
13
7
|
DialogHeader,
|
|
14
8
|
DialogTitle,
|
|
15
|
-
Label,
|
|
16
9
|
} from "../../../../../shadcnui";
|
|
17
|
-
import {
|
|
10
|
+
import { PaymentMethodForm } from "./PaymentMethodForm";
|
|
18
11
|
|
|
19
12
|
type PaymentMethodEditorProps = {
|
|
20
13
|
open: boolean;
|
|
@@ -23,86 +16,13 @@ type PaymentMethodEditorProps = {
|
|
|
23
16
|
};
|
|
24
17
|
|
|
25
18
|
export function PaymentMethodEditor({ open, onOpenChange, onSuccess }: PaymentMethodEditorProps) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const [loading, setLoading] = useState<boolean>(true);
|
|
31
|
-
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
|
|
32
|
-
const [error, setError] = useState<string | null>(null);
|
|
33
|
-
const [setAsDefault, setSetAsDefault] = useState<boolean>(true);
|
|
34
|
-
|
|
35
|
-
// Fetch setup intent on component mount
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
const fetchSetupIntent = async () => {
|
|
38
|
-
setLoading(true);
|
|
39
|
-
try {
|
|
40
|
-
const intent = await StripeCustomerService.createSetupIntent();
|
|
41
|
-
setSetupIntent(intent);
|
|
42
|
-
} catch (err) {
|
|
43
|
-
console.error("[PaymentMethodEditor] Failed to create setup intent:", err);
|
|
44
|
-
setError("Failed to initialize payment form. Please try again.");
|
|
45
|
-
} finally {
|
|
46
|
-
setLoading(false);
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
if (open) {
|
|
51
|
-
fetchSetupIntent();
|
|
52
|
-
}
|
|
53
|
-
}, [open]);
|
|
54
|
-
|
|
55
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
56
|
-
e.preventDefault();
|
|
57
|
-
|
|
58
|
-
if (!stripe || !elements || !setupIntent) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
setIsSubmitting(true);
|
|
63
|
-
setError(null);
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
const cardElement = elements.getElement(CardElement);
|
|
67
|
-
if (!cardElement) {
|
|
68
|
-
throw new Error("Card element not found");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Confirm card setup with Stripe
|
|
72
|
-
const { error: stripeError, setupIntent: confirmedSetupIntent } = await stripe.confirmCardSetup(
|
|
73
|
-
setupIntent.clientSecret,
|
|
74
|
-
{
|
|
75
|
-
payment_method: {
|
|
76
|
-
card: cardElement,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
if (stripeError) {
|
|
82
|
-
console.error("[PaymentMethodEditor] Stripe error:", stripeError);
|
|
83
|
-
setError(stripeError.message || "Failed to add payment method. Please check your card details.");
|
|
84
|
-
setIsSubmitting(false);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Set as default if checkbox is checked
|
|
89
|
-
if (setAsDefault && confirmedSetupIntent?.payment_method) {
|
|
90
|
-
await StripeCustomerService.setDefaultPaymentMethod({
|
|
91
|
-
paymentMethodId:
|
|
92
|
-
typeof confirmedSetupIntent.payment_method === "string"
|
|
93
|
-
? confirmedSetupIntent.payment_method
|
|
94
|
-
: confirmedSetupIntent.payment_method.id,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
19
|
+
const handleSuccess = () => {
|
|
20
|
+
onSuccess();
|
|
21
|
+
onOpenChange(false);
|
|
22
|
+
};
|
|
97
23
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
} catch (err: any) {
|
|
101
|
-
console.error("[PaymentMethodEditor] Error:", err);
|
|
102
|
-
setError(err.message || "An unexpected error occurred. Please try again.");
|
|
103
|
-
} finally {
|
|
104
|
-
setIsSubmitting(false);
|
|
105
|
-
}
|
|
24
|
+
const handleCancel = () => {
|
|
25
|
+
onOpenChange(false);
|
|
106
26
|
};
|
|
107
27
|
|
|
108
28
|
return (
|
|
@@ -114,71 +34,11 @@ export function PaymentMethodEditor({ open, onOpenChange, onSuccess }: PaymentMe
|
|
|
114
34
|
Add a new payment method to your account. Your card information is securely processed by Stripe.
|
|
115
35
|
</DialogDescription>
|
|
116
36
|
</DialogHeader>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)}
|
|
123
|
-
|
|
124
|
-
{!loading && setupIntent && (
|
|
125
|
-
<form onSubmit={handleSubmit} className="flex flex-col gap-y-4">
|
|
126
|
-
{/* Card Element */}
|
|
127
|
-
<div className="rounded-md border border-gray-300 p-3">
|
|
128
|
-
<CardElement
|
|
129
|
-
options={{
|
|
130
|
-
style: {
|
|
131
|
-
base: {
|
|
132
|
-
fontSize: "16px",
|
|
133
|
-
color: "#424770",
|
|
134
|
-
"::placeholder": {
|
|
135
|
-
color: "#aab7c4",
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
invalid: {
|
|
139
|
-
color: "#9e2146",
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
}}
|
|
143
|
-
/>
|
|
144
|
-
</div>
|
|
145
|
-
|
|
146
|
-
{/* Set as Default Checkbox */}
|
|
147
|
-
<div className="flex items-center gap-x-2">
|
|
148
|
-
<Checkbox
|
|
149
|
-
id="setAsDefault"
|
|
150
|
-
checked={setAsDefault}
|
|
151
|
-
onCheckedChange={(checked) => setSetAsDefault(!!checked)}
|
|
152
|
-
/>
|
|
153
|
-
<Label htmlFor="setAsDefault" className="text-sm font-normal">
|
|
154
|
-
Set as default payment method
|
|
155
|
-
</Label>
|
|
156
|
-
</div>
|
|
157
|
-
|
|
158
|
-
{/* Error Alert */}
|
|
159
|
-
{error && (
|
|
160
|
-
<Alert variant="destructive" className="bg-red-50 border-red-200">
|
|
161
|
-
<AlertDescription>{error}</AlertDescription>
|
|
162
|
-
</Alert>
|
|
163
|
-
)}
|
|
164
|
-
|
|
165
|
-
{/* Action Buttons */}
|
|
166
|
-
<div className="flex justify-end gap-x-2">
|
|
167
|
-
<Button type="button" variant="outline" onClick={() => onOpenChange(false)} disabled={isSubmitting}>
|
|
168
|
-
Cancel
|
|
169
|
-
</Button>
|
|
170
|
-
<Button type="submit" disabled={!stripe || isSubmitting}>
|
|
171
|
-
{isSubmitting ? "Processing..." : "Add Card"}
|
|
172
|
-
</Button>
|
|
173
|
-
</div>
|
|
174
|
-
</form>
|
|
175
|
-
)}
|
|
176
|
-
|
|
177
|
-
{/* Error State */}
|
|
178
|
-
{!loading && !setupIntent && error && (
|
|
179
|
-
<Alert variant="destructive" className="bg-red-50 border-red-200">
|
|
180
|
-
<AlertDescription>{error}</AlertDescription>
|
|
181
|
-
</Alert>
|
|
37
|
+
{open && (
|
|
38
|
+
<PaymentMethodForm
|
|
39
|
+
onSuccess={handleSuccess}
|
|
40
|
+
onCancel={handleCancel}
|
|
41
|
+
/>
|
|
182
42
|
)}
|
|
183
43
|
</DialogContent>
|
|
184
44
|
</Dialog>
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import {
|
|
6
|
+
Alert,
|
|
7
|
+
AlertDescription,
|
|
8
|
+
Button,
|
|
9
|
+
Checkbox,
|
|
10
|
+
Label,
|
|
11
|
+
} from "../../../../../shadcnui";
|
|
12
|
+
import { StripeCustomerService } from "../../data";
|
|
13
|
+
|
|
14
|
+
type PaymentMethodFormProps = {
|
|
15
|
+
onSuccess: () => void;
|
|
16
|
+
onCancel: () => void;
|
|
17
|
+
isLoading?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }: PaymentMethodFormProps) {
|
|
21
|
+
const stripe = useStripe();
|
|
22
|
+
const elements = useElements();
|
|
23
|
+
|
|
24
|
+
const [setupIntent, setSetupIntent] = useState<{ clientSecret: string } | null>(null);
|
|
25
|
+
const [loading, setLoading] = useState<boolean>(true);
|
|
26
|
+
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
|
|
27
|
+
const [error, setError] = useState<string | null>(null);
|
|
28
|
+
const [setAsDefault, setSetAsDefault] = useState<boolean>(true);
|
|
29
|
+
|
|
30
|
+
// Fetch setup intent on component mount
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const fetchSetupIntent = async () => {
|
|
33
|
+
setLoading(true);
|
|
34
|
+
try {
|
|
35
|
+
const intent = await StripeCustomerService.createSetupIntent();
|
|
36
|
+
setSetupIntent(intent);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error("[PaymentMethodForm] Failed to create setup intent:", err);
|
|
39
|
+
setError("Failed to initialize payment form. Please try again.");
|
|
40
|
+
} finally {
|
|
41
|
+
setLoading(false);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
fetchSetupIntent();
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
|
|
51
|
+
if (!stripe || !elements || !setupIntent) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
setIsSubmitting(true);
|
|
56
|
+
setError(null);
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const cardElement = elements.getElement(CardElement);
|
|
60
|
+
if (!cardElement) {
|
|
61
|
+
throw new Error("Card element not found");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Confirm card setup with Stripe
|
|
65
|
+
const { error: stripeError, setupIntent: confirmedSetupIntent } = await stripe.confirmCardSetup(
|
|
66
|
+
setupIntent.clientSecret,
|
|
67
|
+
{
|
|
68
|
+
payment_method: {
|
|
69
|
+
card: cardElement,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (stripeError) {
|
|
75
|
+
console.error("[PaymentMethodForm] Stripe error:", stripeError);
|
|
76
|
+
setError(stripeError.message || "Failed to add payment method. Please check your card details.");
|
|
77
|
+
setIsSubmitting(false);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Set as default if checkbox is checked
|
|
82
|
+
if (setAsDefault && confirmedSetupIntent?.payment_method) {
|
|
83
|
+
await StripeCustomerService.setDefaultPaymentMethod({
|
|
84
|
+
paymentMethodId:
|
|
85
|
+
typeof confirmedSetupIntent.payment_method === "string"
|
|
86
|
+
? confirmedSetupIntent.payment_method
|
|
87
|
+
: confirmedSetupIntent.payment_method.id,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
onSuccess();
|
|
92
|
+
} catch (err: any) {
|
|
93
|
+
console.error("[PaymentMethodForm] Error:", err);
|
|
94
|
+
setError(err.message || "An unexpected error occurred. Please try again.");
|
|
95
|
+
} finally {
|
|
96
|
+
setIsSubmitting(false);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (loading) {
|
|
101
|
+
return (
|
|
102
|
+
<div className="flex items-center justify-center py-8">
|
|
103
|
+
<p className="text-muted-foreground">Loading payment form...</p>
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!setupIntent && error) {
|
|
109
|
+
return (
|
|
110
|
+
<Alert variant="destructive" className="bg-red-50 border-red-200">
|
|
111
|
+
<AlertDescription>{error}</AlertDescription>
|
|
112
|
+
</Alert>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<form onSubmit={handleSubmit} className="flex flex-col gap-y-4">
|
|
118
|
+
{/* Card Element */}
|
|
119
|
+
<div className="rounded-md border border-gray-300 p-3">
|
|
120
|
+
<CardElement
|
|
121
|
+
options={{
|
|
122
|
+
style: {
|
|
123
|
+
base: {
|
|
124
|
+
fontSize: "16px",
|
|
125
|
+
color: "#424770",
|
|
126
|
+
"::placeholder": {
|
|
127
|
+
color: "#aab7c4",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
invalid: {
|
|
131
|
+
color: "#9e2146",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}}
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
{/* Set as Default Checkbox */}
|
|
139
|
+
<div className="flex items-center gap-x-2">
|
|
140
|
+
<Checkbox
|
|
141
|
+
id="setAsDefault"
|
|
142
|
+
checked={setAsDefault}
|
|
143
|
+
onCheckedChange={(checked) => setSetAsDefault(!!checked)}
|
|
144
|
+
/>
|
|
145
|
+
<Label htmlFor="setAsDefault" className="text-sm font-normal">
|
|
146
|
+
Set as default payment method
|
|
147
|
+
</Label>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
{/* Error Alert */}
|
|
151
|
+
{error && (
|
|
152
|
+
<Alert variant="destructive" className="bg-red-50 border-red-200">
|
|
153
|
+
<AlertDescription>{error}</AlertDescription>
|
|
154
|
+
</Alert>
|
|
155
|
+
)}
|
|
156
|
+
|
|
157
|
+
{/* Action Buttons */}
|
|
158
|
+
<div className="flex justify-end gap-x-2">
|
|
159
|
+
<Button type="button" variant="outline" onClick={onCancel} disabled={isSubmitting || isLoading}>
|
|
160
|
+
Cancel
|
|
161
|
+
</Button>
|
|
162
|
+
<Button type="submit" disabled={!stripe || isSubmitting || isLoading}>
|
|
163
|
+
{isSubmitting ? "Processing..." : "Add Card"}
|
|
164
|
+
</Button>
|
|
165
|
+
</div>
|
|
166
|
+
</form>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
4
4
|
import { AlertCircle, PlusIcon, XIcon } from "lucide-react";
|
|
5
|
-
import { useState } from "react";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
6
|
import { SubmitHandler, useForm } from "react-hook-form";
|
|
7
7
|
import { v4 } from "uuid";
|
|
8
8
|
import { z } from "zod";
|
|
@@ -85,6 +85,24 @@ export function PriceEditor({ productId, price, open, onOpenChange, onSuccess }:
|
|
|
85
85
|
},
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
+
// Reset form when dialog opens to ensure fresh state
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (open) {
|
|
91
|
+
form.reset({
|
|
92
|
+
unitAmount: price?.unitAmount ? price.unitAmount / 100 : 0,
|
|
93
|
+
currency: price?.currency || "usd",
|
|
94
|
+
interval: price?.priceType === "one_time" ? "one_time" : price?.recurring?.interval || "month",
|
|
95
|
+
intervalCount: price?.recurring?.intervalCount || 1,
|
|
96
|
+
usageType: price?.recurring?.usageType || "licensed",
|
|
97
|
+
nickname: price?.nickname || "",
|
|
98
|
+
active: price?.active ?? true,
|
|
99
|
+
description: price?.description || "",
|
|
100
|
+
features: price?.features || [],
|
|
101
|
+
token: price?.token?.toString() ?? "",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}, [open, price?.id]);
|
|
105
|
+
|
|
88
106
|
const watchInterval = form.watch("interval");
|
|
89
107
|
const isRecurring = watchInterval !== "one_time";
|
|
90
108
|
|
|
@@ -23,7 +23,7 @@ export function ProductEditor({ product, open, onOpenChange, onSuccess }: Produc
|
|
|
23
23
|
|
|
24
24
|
const formSchema = z.object({
|
|
25
25
|
name: z.string().min(1, { message: "Product name is required" }),
|
|
26
|
-
description: z.string().
|
|
26
|
+
description: z.string().min(1, { message: "Description is required" }),
|
|
27
27
|
active: z.boolean(),
|
|
28
28
|
});
|
|
29
29
|
|
|
@@ -85,7 +85,7 @@ export function ProductEditor({ product, open, onOpenChange, onSuccess }: Produc
|
|
|
85
85
|
form={form}
|
|
86
86
|
id="description"
|
|
87
87
|
name="Description"
|
|
88
|
-
placeholder="Enter product description
|
|
88
|
+
placeholder="Enter product description"
|
|
89
89
|
className="min-h-32"
|
|
90
90
|
/>
|
|
91
91
|
|