@nozle-js/react 0.5.0 → 0.5.2
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/README.md +5 -5
- package/dist/index.cjs +329 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -27
- package/dist/index.d.ts +22 -27
- package/dist/index.js +329 -85
- package/dist/index.js.map +1 -1
- package/package.json +20 -6
package/dist/index.d.cts
CHANGED
|
@@ -124,6 +124,27 @@ interface BillingPortalProviderProps {
|
|
|
124
124
|
*/
|
|
125
125
|
declare function BillingPortalProvider({ customerId, apiKey, children, }: BillingPortalProviderProps): React.ReactElement;
|
|
126
126
|
|
|
127
|
+
interface PricingPlan {
|
|
128
|
+
code: string;
|
|
129
|
+
name: string;
|
|
130
|
+
amount_cents: number;
|
|
131
|
+
amount_currency: string;
|
|
132
|
+
interval: string;
|
|
133
|
+
description?: string;
|
|
134
|
+
}
|
|
135
|
+
interface PricingTableProps {
|
|
136
|
+
customerId?: string;
|
|
137
|
+
currentPlanCode?: string;
|
|
138
|
+
plans?: PricingPlan[];
|
|
139
|
+
features?: string[][];
|
|
140
|
+
onSelect?: (plan: PricingPlan) => void;
|
|
141
|
+
showToggle?: boolean;
|
|
142
|
+
highlightPlan?: string;
|
|
143
|
+
className?: string;
|
|
144
|
+
enterpriseEmail?: string;
|
|
145
|
+
}
|
|
146
|
+
declare function PricingTable({ customerId, currentPlanCode, plans: propPlans, features, onSelect, showToggle, highlightPlan, className, enterpriseEmail, }: PricingTableProps): React.ReactElement;
|
|
147
|
+
|
|
127
148
|
/**
|
|
128
149
|
* PlanCard — Individual pricing plan card component.
|
|
129
150
|
* UI-01: Shows plan name, price, interval, features list, and CTA button.
|
|
@@ -150,32 +171,6 @@ interface PlanCardProps extends Plan {
|
|
|
150
171
|
*/
|
|
151
172
|
declare function PlanCard({ id, name, monthlyPrice, annualPrice, features, description, isAnnual, isCurrent, onSelect, }: PlanCardProps): React.ReactElement;
|
|
152
173
|
|
|
153
|
-
/**
|
|
154
|
-
* PricingTable — Renders plan cards with monthly/annual toggle.
|
|
155
|
-
* UI-01: Responsive grid, monthly/annual toggle, highlights currentPlanId.
|
|
156
|
-
* Uses CSS variable theming with --nozle-* namespace.
|
|
157
|
-
*/
|
|
158
|
-
|
|
159
|
-
interface PricingTableProps {
|
|
160
|
-
plans: Plan[];
|
|
161
|
-
currentPlanId?: string;
|
|
162
|
-
onSelect?: (planId: string) => void;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* PricingTable renders plan cards in a responsive grid with monthly/annual toggle.
|
|
166
|
-
* The current plan is highlighted with aria-current="true" and border-primary class.
|
|
167
|
-
*
|
|
168
|
-
* Usage:
|
|
169
|
-
* ```tsx
|
|
170
|
-
* <PricingTable
|
|
171
|
-
* plans={plans}
|
|
172
|
-
* currentPlanId="pro"
|
|
173
|
-
* onSelect={(planId) => handleUpgrade(planId)}
|
|
174
|
-
* />
|
|
175
|
-
* ```
|
|
176
|
-
*/
|
|
177
|
-
declare function PricingTable({ plans, currentPlanId, onSelect, }: PricingTableProps): React.ReactElement;
|
|
178
|
-
|
|
179
174
|
/**
|
|
180
175
|
* PlanComparison — Side-by-side feature comparison table.
|
|
181
176
|
* UI-01: Renders shadcn-style table with plan names as headers and features as rows.
|
|
@@ -633,4 +628,4 @@ interface CancelSubscriptionButtonProps {
|
|
|
633
628
|
}
|
|
634
629
|
declare function CancelSubscriptionButton({ subscriptionId, onCancelled, }: CancelSubscriptionButtonProps): react_jsx_runtime.JSX.Element;
|
|
635
630
|
|
|
636
|
-
export { BillingContext, type BillingContextValue, BillingPortal, type BillingPortalContextValue, type BillingPortalProps, BillingPortalProvider, type BillingPortalProviderProps, BillingProvider, type BillingProviderProps, CANCEL_REASONS, type CanState, CancelSubscriptionButton, type CancelSubscriptionButtonProps, Checkout, CheckoutButton, type CheckoutButtonProps, type CheckoutProps, type ComparisonFeature, CreditBalance, type CreditBalanceProps, CreditHistory, type CreditHistoryProps, CreditTopUpButton, type CreditTopUpButtonProps, type CreditTransaction, type CreditsState, CurrentPlan, type CurrentPlanData, type CurrentPlanProps, FeatureGate, type FeatureGateProps, type Invoice, InvoiceList, type InvoiceListProps, LockedOverlay, type LockedOverlayProps, type NozleClient, type PaymentMethod, PaymentMethodDisplay, type PaymentMethodDisplayProps, type Plan, PlanBadge, type PlanBadgeProps, PlanCard, type PlanCardProps, PlanComparison, type PlanComparisonProps, PlanGate, type PlanGateProps, PricingTable, type PricingTableProps, type ProrationPreview, UpgradeButton, type UpgradeButtonProps, UpgradeModal, type UpgradeModalProps, UpgradePrompt, type UpgradePromptProps, UsageAlert, type UsageAlertFeature, type UsageAlertProps, UsageDashboard, type UsageDashboardFeature, type UsageDashboardProps, UsageGate, type UsageGateProps, UsageMeter, type UsageMeterProps, type UsageState, type UseCheckoutResult, getUsageMeterColor, useBillingContext, useBillingPortal, useCan, useCheckout, useCredits, useNozleClient, useUsage };
|
|
631
|
+
export { BillingContext, type BillingContextValue, BillingPortal, type BillingPortalContextValue, type BillingPortalProps, BillingPortalProvider, type BillingPortalProviderProps, BillingProvider, type BillingProviderProps, CANCEL_REASONS, type CanState, CancelSubscriptionButton, type CancelSubscriptionButtonProps, Checkout, CheckoutButton, type CheckoutButtonProps, type CheckoutProps, type ComparisonFeature, CreditBalance, type CreditBalanceProps, CreditHistory, type CreditHistoryProps, CreditTopUpButton, type CreditTopUpButtonProps, type CreditTransaction, type CreditsState, CurrentPlan, type CurrentPlanData, type CurrentPlanProps, FeatureGate, type FeatureGateProps, type Invoice, InvoiceList, type InvoiceListProps, LockedOverlay, type LockedOverlayProps, type NozleClient, type PaymentMethod, PaymentMethodDisplay, type PaymentMethodDisplayProps, type Plan, PlanBadge, type PlanBadgeProps, PlanCard, type PlanCardProps, PlanComparison, type PlanComparisonProps, PlanGate, type PlanGateProps, type PricingPlan, PricingTable, type PricingTableProps, type ProrationPreview, UpgradeButton, type UpgradeButtonProps, UpgradeModal, type UpgradeModalProps, UpgradePrompt, type UpgradePromptProps, UsageAlert, type UsageAlertFeature, type UsageAlertProps, UsageDashboard, type UsageDashboardFeature, type UsageDashboardProps, UsageGate, type UsageGateProps, UsageMeter, type UsageMeterProps, type UsageState, type UseCheckoutResult, getUsageMeterColor, useBillingContext, useBillingPortal, useCan, useCheckout, useCredits, useNozleClient, useUsage };
|
package/dist/index.d.ts
CHANGED
|
@@ -124,6 +124,27 @@ interface BillingPortalProviderProps {
|
|
|
124
124
|
*/
|
|
125
125
|
declare function BillingPortalProvider({ customerId, apiKey, children, }: BillingPortalProviderProps): React.ReactElement;
|
|
126
126
|
|
|
127
|
+
interface PricingPlan {
|
|
128
|
+
code: string;
|
|
129
|
+
name: string;
|
|
130
|
+
amount_cents: number;
|
|
131
|
+
amount_currency: string;
|
|
132
|
+
interval: string;
|
|
133
|
+
description?: string;
|
|
134
|
+
}
|
|
135
|
+
interface PricingTableProps {
|
|
136
|
+
customerId?: string;
|
|
137
|
+
currentPlanCode?: string;
|
|
138
|
+
plans?: PricingPlan[];
|
|
139
|
+
features?: string[][];
|
|
140
|
+
onSelect?: (plan: PricingPlan) => void;
|
|
141
|
+
showToggle?: boolean;
|
|
142
|
+
highlightPlan?: string;
|
|
143
|
+
className?: string;
|
|
144
|
+
enterpriseEmail?: string;
|
|
145
|
+
}
|
|
146
|
+
declare function PricingTable({ customerId, currentPlanCode, plans: propPlans, features, onSelect, showToggle, highlightPlan, className, enterpriseEmail, }: PricingTableProps): React.ReactElement;
|
|
147
|
+
|
|
127
148
|
/**
|
|
128
149
|
* PlanCard — Individual pricing plan card component.
|
|
129
150
|
* UI-01: Shows plan name, price, interval, features list, and CTA button.
|
|
@@ -150,32 +171,6 @@ interface PlanCardProps extends Plan {
|
|
|
150
171
|
*/
|
|
151
172
|
declare function PlanCard({ id, name, monthlyPrice, annualPrice, features, description, isAnnual, isCurrent, onSelect, }: PlanCardProps): React.ReactElement;
|
|
152
173
|
|
|
153
|
-
/**
|
|
154
|
-
* PricingTable — Renders plan cards with monthly/annual toggle.
|
|
155
|
-
* UI-01: Responsive grid, monthly/annual toggle, highlights currentPlanId.
|
|
156
|
-
* Uses CSS variable theming with --nozle-* namespace.
|
|
157
|
-
*/
|
|
158
|
-
|
|
159
|
-
interface PricingTableProps {
|
|
160
|
-
plans: Plan[];
|
|
161
|
-
currentPlanId?: string;
|
|
162
|
-
onSelect?: (planId: string) => void;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* PricingTable renders plan cards in a responsive grid with monthly/annual toggle.
|
|
166
|
-
* The current plan is highlighted with aria-current="true" and border-primary class.
|
|
167
|
-
*
|
|
168
|
-
* Usage:
|
|
169
|
-
* ```tsx
|
|
170
|
-
* <PricingTable
|
|
171
|
-
* plans={plans}
|
|
172
|
-
* currentPlanId="pro"
|
|
173
|
-
* onSelect={(planId) => handleUpgrade(planId)}
|
|
174
|
-
* />
|
|
175
|
-
* ```
|
|
176
|
-
*/
|
|
177
|
-
declare function PricingTable({ plans, currentPlanId, onSelect, }: PricingTableProps): React.ReactElement;
|
|
178
|
-
|
|
179
174
|
/**
|
|
180
175
|
* PlanComparison — Side-by-side feature comparison table.
|
|
181
176
|
* UI-01: Renders shadcn-style table with plan names as headers and features as rows.
|
|
@@ -633,4 +628,4 @@ interface CancelSubscriptionButtonProps {
|
|
|
633
628
|
}
|
|
634
629
|
declare function CancelSubscriptionButton({ subscriptionId, onCancelled, }: CancelSubscriptionButtonProps): react_jsx_runtime.JSX.Element;
|
|
635
630
|
|
|
636
|
-
export { BillingContext, type BillingContextValue, BillingPortal, type BillingPortalContextValue, type BillingPortalProps, BillingPortalProvider, type BillingPortalProviderProps, BillingProvider, type BillingProviderProps, CANCEL_REASONS, type CanState, CancelSubscriptionButton, type CancelSubscriptionButtonProps, Checkout, CheckoutButton, type CheckoutButtonProps, type CheckoutProps, type ComparisonFeature, CreditBalance, type CreditBalanceProps, CreditHistory, type CreditHistoryProps, CreditTopUpButton, type CreditTopUpButtonProps, type CreditTransaction, type CreditsState, CurrentPlan, type CurrentPlanData, type CurrentPlanProps, FeatureGate, type FeatureGateProps, type Invoice, InvoiceList, type InvoiceListProps, LockedOverlay, type LockedOverlayProps, type NozleClient, type PaymentMethod, PaymentMethodDisplay, type PaymentMethodDisplayProps, type Plan, PlanBadge, type PlanBadgeProps, PlanCard, type PlanCardProps, PlanComparison, type PlanComparisonProps, PlanGate, type PlanGateProps, PricingTable, type PricingTableProps, type ProrationPreview, UpgradeButton, type UpgradeButtonProps, UpgradeModal, type UpgradeModalProps, UpgradePrompt, type UpgradePromptProps, UsageAlert, type UsageAlertFeature, type UsageAlertProps, UsageDashboard, type UsageDashboardFeature, type UsageDashboardProps, UsageGate, type UsageGateProps, UsageMeter, type UsageMeterProps, type UsageState, type UseCheckoutResult, getUsageMeterColor, useBillingContext, useBillingPortal, useCan, useCheckout, useCredits, useNozleClient, useUsage };
|
|
631
|
+
export { BillingContext, type BillingContextValue, BillingPortal, type BillingPortalContextValue, type BillingPortalProps, BillingPortalProvider, type BillingPortalProviderProps, BillingProvider, type BillingProviderProps, CANCEL_REASONS, type CanState, CancelSubscriptionButton, type CancelSubscriptionButtonProps, Checkout, CheckoutButton, type CheckoutButtonProps, type CheckoutProps, type ComparisonFeature, CreditBalance, type CreditBalanceProps, CreditHistory, type CreditHistoryProps, CreditTopUpButton, type CreditTopUpButtonProps, type CreditTransaction, type CreditsState, CurrentPlan, type CurrentPlanData, type CurrentPlanProps, FeatureGate, type FeatureGateProps, type Invoice, InvoiceList, type InvoiceListProps, LockedOverlay, type LockedOverlayProps, type NozleClient, type PaymentMethod, PaymentMethodDisplay, type PaymentMethodDisplayProps, type Plan, PlanBadge, type PlanBadgeProps, PlanCard, type PlanCardProps, PlanComparison, type PlanComparisonProps, PlanGate, type PlanGateProps, type PricingPlan, PricingTable, type PricingTableProps, type ProrationPreview, UpgradeButton, type UpgradeButtonProps, UpgradeModal, type UpgradeModalProps, UpgradePrompt, type UpgradePromptProps, UsageAlert, type UsageAlertFeature, type UsageAlertProps, UsageDashboard, type UsageDashboardFeature, type UsageDashboardProps, UsageGate, type UsageGateProps, UsageMeter, type UsageMeterProps, type UsageState, type UseCheckoutResult, getUsageMeterColor, useBillingContext, useBillingPortal, useCan, useCheckout, useCredits, useNozleClient, useUsage };
|
package/dist/index.js
CHANGED
|
@@ -297,6 +297,335 @@ function BillingPortalProvider({
|
|
|
297
297
|
}) {
|
|
298
298
|
return /* @__PURE__ */ jsx(BillingPortalContext.Provider, { value: { customerId, apiKey }, children });
|
|
299
299
|
}
|
|
300
|
+
var SYMBOLS = { USD: "$", EUR: "\u20AC", GBP: "\xA3", JPY: "\xA5" };
|
|
301
|
+
function fmtPrice(cents, cur) {
|
|
302
|
+
const s = SYMBOLS[cur] ?? cur + " ";
|
|
303
|
+
const val = cents % 100 === 0 ? String(cents / 100) : (cents / 100).toFixed(2);
|
|
304
|
+
return `${s}${val}`;
|
|
305
|
+
}
|
|
306
|
+
function isEnterprise(p) {
|
|
307
|
+
return /enterprise|custom/i.test(p.name + p.code);
|
|
308
|
+
}
|
|
309
|
+
function v(name, fb) {
|
|
310
|
+
return `var(${name}, ${fb})`;
|
|
311
|
+
}
|
|
312
|
+
var C = {
|
|
313
|
+
bg: v("--nozle-pricing-bg", v("--nozle-background", "#ffffff")),
|
|
314
|
+
cardBg: v("--nozle-pricing-card-bg", v("--nozle-card", "#ffffff")),
|
|
315
|
+
hl: v("--nozle-pricing-highlight", v("--nozle-primary", "#6366f1")),
|
|
316
|
+
border: v("--nozle-pricing-border", v("--nozle-border", "#e5e7eb")),
|
|
317
|
+
radius: v("--nozle-pricing-radius", v("--nozle-radius", "12px")),
|
|
318
|
+
text: v("--nozle-foreground", "#111827"),
|
|
319
|
+
muted: v("--nozle-muted-foreground", "#6b7280"),
|
|
320
|
+
mutedBg: v("--nozle-muted", "#f3f4f6"),
|
|
321
|
+
hlFg: v("--nozle-primary-foreground", "#ffffff")
|
|
322
|
+
};
|
|
323
|
+
function Check() {
|
|
324
|
+
return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx("path", { d: "M3.5 8.5L6.5 11.5L12.5 4.5", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
325
|
+
}
|
|
326
|
+
function Toggle({ isAnnual, onChange }) {
|
|
327
|
+
const pill = {
|
|
328
|
+
display: "inline-flex",
|
|
329
|
+
borderRadius: "9999px",
|
|
330
|
+
padding: "4px",
|
|
331
|
+
background: C.mutedBg,
|
|
332
|
+
gap: "2px"
|
|
333
|
+
};
|
|
334
|
+
const tab = (active) => ({
|
|
335
|
+
padding: "8px 20px",
|
|
336
|
+
borderRadius: "9999px",
|
|
337
|
+
border: "none",
|
|
338
|
+
background: active ? C.cardBg : "transparent",
|
|
339
|
+
color: active ? C.text : C.muted,
|
|
340
|
+
fontWeight: active ? 600 : 400,
|
|
341
|
+
fontSize: "0.875rem",
|
|
342
|
+
cursor: "pointer",
|
|
343
|
+
boxShadow: active ? "0 1px 3px rgba(0,0,0,0.08)" : "none",
|
|
344
|
+
transition: "all 0.15s ease"
|
|
345
|
+
});
|
|
346
|
+
return /* @__PURE__ */ jsx("div", { style: { display: "flex", justifyContent: "center", marginBottom: "2.5rem" }, children: /* @__PURE__ */ jsxs("div", { style: pill, children: [
|
|
347
|
+
/* @__PURE__ */ jsx("button", { onClick: () => onChange(false), style: tab(!isAnnual), "aria-pressed": !isAnnual, children: "Monthly" }),
|
|
348
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => onChange(true), style: tab(isAnnual), "aria-pressed": isAnnual, children: [
|
|
349
|
+
"Annual",
|
|
350
|
+
/* @__PURE__ */ jsx("span", { style: {
|
|
351
|
+
marginLeft: "6px",
|
|
352
|
+
fontSize: "0.7rem",
|
|
353
|
+
fontWeight: 600,
|
|
354
|
+
color: isAnnual ? C.hl : C.muted
|
|
355
|
+
}, children: "Save 20%" })
|
|
356
|
+
] })
|
|
357
|
+
] }) });
|
|
358
|
+
}
|
|
359
|
+
function Card({
|
|
360
|
+
plan,
|
|
361
|
+
features,
|
|
362
|
+
isCurrent,
|
|
363
|
+
isHighlighted,
|
|
364
|
+
isEnt,
|
|
365
|
+
enterpriseEmail,
|
|
366
|
+
onSelect
|
|
367
|
+
}) {
|
|
368
|
+
const [hover, setHover] = useState(false);
|
|
369
|
+
const shadow = isHighlighted ? "0 8px 28px rgba(99,102,241,0.12), 0 2px 8px rgba(99,102,241,0.06)" : hover ? "0 8px 24px rgba(0,0,0,0.06), 0 2px 8px rgba(0,0,0,0.03)" : "0 1px 3px rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.03)";
|
|
370
|
+
const interval = plan.interval === "yearly" ? "/year" : "/month";
|
|
371
|
+
const handleClick = () => {
|
|
372
|
+
if (isCurrent) return;
|
|
373
|
+
if (isEnt && enterpriseEmail) {
|
|
374
|
+
window.location.href = `mailto:${enterpriseEmail}?subject=Enterprise Plan Inquiry`;
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
onSelect?.();
|
|
378
|
+
};
|
|
379
|
+
return /* @__PURE__ */ jsxs(
|
|
380
|
+
"div",
|
|
381
|
+
{
|
|
382
|
+
onMouseEnter: () => setHover(true),
|
|
383
|
+
onMouseLeave: () => setHover(false),
|
|
384
|
+
style: {
|
|
385
|
+
position: "relative",
|
|
386
|
+
display: "flex",
|
|
387
|
+
flexDirection: "column",
|
|
388
|
+
background: C.cardBg,
|
|
389
|
+
border: `1px solid ${isHighlighted ? C.hl : C.border}`,
|
|
390
|
+
borderTopWidth: isHighlighted ? "3px" : "1px",
|
|
391
|
+
borderRadius: C.radius,
|
|
392
|
+
padding: "2rem",
|
|
393
|
+
transition: "box-shadow 0.2s ease, transform 0.2s ease",
|
|
394
|
+
boxShadow: shadow,
|
|
395
|
+
transform: hover && !isCurrent ? "translateY(-2px)" : "none"
|
|
396
|
+
},
|
|
397
|
+
children: [
|
|
398
|
+
isHighlighted && /* @__PURE__ */ jsx("div", { style: {
|
|
399
|
+
position: "absolute",
|
|
400
|
+
top: "-1px",
|
|
401
|
+
left: "50%",
|
|
402
|
+
transform: "translate(-50%, -50%)",
|
|
403
|
+
background: C.hl,
|
|
404
|
+
color: C.hlFg,
|
|
405
|
+
fontSize: "0.7rem",
|
|
406
|
+
fontWeight: 700,
|
|
407
|
+
padding: "4px 12px",
|
|
408
|
+
borderRadius: "9999px",
|
|
409
|
+
letterSpacing: "0.025em",
|
|
410
|
+
textTransform: "uppercase",
|
|
411
|
+
whiteSpace: "nowrap"
|
|
412
|
+
}, children: isCurrent ? "Current Plan" : "Most Popular" }),
|
|
413
|
+
isCurrent && !isHighlighted && /* @__PURE__ */ jsx("div", { style: {
|
|
414
|
+
position: "absolute",
|
|
415
|
+
top: "0",
|
|
416
|
+
left: "50%",
|
|
417
|
+
transform: "translate(-50%, -50%)",
|
|
418
|
+
background: C.mutedBg,
|
|
419
|
+
color: C.muted,
|
|
420
|
+
fontSize: "0.7rem",
|
|
421
|
+
fontWeight: 700,
|
|
422
|
+
padding: "4px 12px",
|
|
423
|
+
borderRadius: "9999px",
|
|
424
|
+
letterSpacing: "0.025em",
|
|
425
|
+
textTransform: "uppercase",
|
|
426
|
+
whiteSpace: "nowrap"
|
|
427
|
+
}, children: "Current Plan" }),
|
|
428
|
+
/* @__PURE__ */ jsxs("div", { style: { marginBottom: "1.25rem", marginTop: isHighlighted || isCurrent ? "0.5rem" : 0 }, children: [
|
|
429
|
+
/* @__PURE__ */ jsx("h3", { style: {
|
|
430
|
+
fontSize: "1.25rem",
|
|
431
|
+
fontWeight: 700,
|
|
432
|
+
color: C.text,
|
|
433
|
+
margin: 0,
|
|
434
|
+
lineHeight: 1.3
|
|
435
|
+
}, children: plan.name }),
|
|
436
|
+
plan.description && /* @__PURE__ */ jsx("p", { style: {
|
|
437
|
+
fontSize: "0.875rem",
|
|
438
|
+
color: C.muted,
|
|
439
|
+
margin: "6px 0 0",
|
|
440
|
+
lineHeight: 1.5
|
|
441
|
+
}, children: plan.description })
|
|
442
|
+
] }),
|
|
443
|
+
/* @__PURE__ */ jsx("div", { style: { marginBottom: "1.5rem" }, children: isEnt ? /* @__PURE__ */ jsx("span", { style: { fontSize: "2.25rem", fontWeight: 700, color: C.text, lineHeight: 1 }, children: "Custom" }) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "baseline", gap: "2px" }, children: [
|
|
444
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "2.75rem", fontWeight: 700, color: C.text, lineHeight: 1, letterSpacing: "-0.025em" }, children: fmtPrice(plan.amount_cents, plan.amount_currency) }),
|
|
445
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.9375rem", color: C.muted, marginLeft: "4px" }, children: interval })
|
|
446
|
+
] }) }),
|
|
447
|
+
features && features.length > 0 && /* @__PURE__ */ jsx("ul", { style: {
|
|
448
|
+
listStyle: "none",
|
|
449
|
+
padding: 0,
|
|
450
|
+
margin: "0 0 2rem",
|
|
451
|
+
display: "flex",
|
|
452
|
+
flexDirection: "column",
|
|
453
|
+
gap: "0.75rem",
|
|
454
|
+
flex: 1
|
|
455
|
+
}, children: features.map((feat) => /* @__PURE__ */ jsxs("li", { style: {
|
|
456
|
+
display: "flex",
|
|
457
|
+
alignItems: "flex-start",
|
|
458
|
+
gap: "10px",
|
|
459
|
+
fontSize: "0.875rem",
|
|
460
|
+
color: C.text,
|
|
461
|
+
lineHeight: 1.5
|
|
462
|
+
}, children: [
|
|
463
|
+
/* @__PURE__ */ jsx("span", { style: { color: C.hl, marginTop: "2px" }, children: /* @__PURE__ */ jsx(Check, {}) }),
|
|
464
|
+
/* @__PURE__ */ jsx("span", { children: feat })
|
|
465
|
+
] }, feat)) }),
|
|
466
|
+
(!features || features.length === 0) && /* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
|
|
467
|
+
onSelect && /* @__PURE__ */ jsx(
|
|
468
|
+
CTAButton,
|
|
469
|
+
{
|
|
470
|
+
onClick: handleClick,
|
|
471
|
+
disabled: isCurrent,
|
|
472
|
+
variant: isCurrent ? "current" : isEnt ? "outline" : isHighlighted ? "primary" : "secondary",
|
|
473
|
+
children: isCurrent ? "Current Plan" : isEnt ? "Contact Sales" : `Get ${plan.name}`
|
|
474
|
+
}
|
|
475
|
+
)
|
|
476
|
+
]
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
function CTAButton({
|
|
481
|
+
children,
|
|
482
|
+
onClick,
|
|
483
|
+
disabled,
|
|
484
|
+
variant
|
|
485
|
+
}) {
|
|
486
|
+
const [hover, setHover] = useState(false);
|
|
487
|
+
const base = {
|
|
488
|
+
width: "100%",
|
|
489
|
+
padding: "12px 24px",
|
|
490
|
+
borderRadius: "10px",
|
|
491
|
+
fontSize: "0.9375rem",
|
|
492
|
+
fontWeight: 600,
|
|
493
|
+
cursor: disabled ? "default" : "pointer",
|
|
494
|
+
border: "none",
|
|
495
|
+
transition: "all 0.15s ease",
|
|
496
|
+
lineHeight: 1.4
|
|
497
|
+
};
|
|
498
|
+
const variants = {
|
|
499
|
+
primary: {
|
|
500
|
+
background: hover && !disabled ? "#4f46e5" : C.hl,
|
|
501
|
+
color: C.hlFg
|
|
502
|
+
},
|
|
503
|
+
secondary: {
|
|
504
|
+
background: hover && !disabled ? "#f0f0f3" : C.mutedBg,
|
|
505
|
+
color: C.text,
|
|
506
|
+
border: `1px solid ${C.border}`
|
|
507
|
+
},
|
|
508
|
+
outline: {
|
|
509
|
+
background: hover && !disabled ? "rgba(99,102,241,0.04)" : "transparent",
|
|
510
|
+
color: C.hl,
|
|
511
|
+
border: `1.5px solid ${C.hl}`
|
|
512
|
+
},
|
|
513
|
+
current: {
|
|
514
|
+
background: C.mutedBg,
|
|
515
|
+
color: C.muted
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
return /* @__PURE__ */ jsx(
|
|
519
|
+
"button",
|
|
520
|
+
{
|
|
521
|
+
onClick: disabled ? void 0 : onClick,
|
|
522
|
+
disabled,
|
|
523
|
+
onMouseEnter: () => setHover(true),
|
|
524
|
+
onMouseLeave: () => setHover(false),
|
|
525
|
+
style: { ...base, ...variants[variant] },
|
|
526
|
+
children
|
|
527
|
+
}
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
function Skeleton() {
|
|
531
|
+
return /* @__PURE__ */ jsx("div", { style: {
|
|
532
|
+
display: "grid",
|
|
533
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
|
|
534
|
+
gap: "1.5rem"
|
|
535
|
+
}, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxs("div", { style: {
|
|
536
|
+
background: C.cardBg,
|
|
537
|
+
border: `1px solid ${C.border}`,
|
|
538
|
+
borderRadius: C.radius,
|
|
539
|
+
padding: "2rem",
|
|
540
|
+
height: "380px"
|
|
541
|
+
}, children: [
|
|
542
|
+
/* @__PURE__ */ jsx("div", { style: { height: 20, width: "40%", borderRadius: 6, background: "#f0f0f0", marginBottom: 12 } }),
|
|
543
|
+
/* @__PURE__ */ jsx("div", { style: { height: 40, width: "50%", borderRadius: 6, background: "#f0f0f0", marginBottom: 24 } }),
|
|
544
|
+
/* @__PURE__ */ jsx("div", { style: { height: 14, width: "80%", borderRadius: 6, background: "#f0f0f0", marginBottom: 10 } }),
|
|
545
|
+
/* @__PURE__ */ jsx("div", { style: { height: 14, width: "70%", borderRadius: 6, background: "#f0f0f0", marginBottom: 10 } }),
|
|
546
|
+
/* @__PURE__ */ jsx("div", { style: { height: 14, width: "60%", borderRadius: 6, background: "#f0f0f0" } })
|
|
547
|
+
] }, i)) });
|
|
548
|
+
}
|
|
549
|
+
function PricingTable({
|
|
550
|
+
customerId,
|
|
551
|
+
currentPlanCode,
|
|
552
|
+
plans: propPlans,
|
|
553
|
+
features,
|
|
554
|
+
onSelect,
|
|
555
|
+
showToggle = true,
|
|
556
|
+
highlightPlan,
|
|
557
|
+
className,
|
|
558
|
+
enterpriseEmail
|
|
559
|
+
}) {
|
|
560
|
+
const client = useNozleClient();
|
|
561
|
+
const [plans, setPlans] = useState(propPlans ?? []);
|
|
562
|
+
const [currentPlan, setCurrentPlan] = useState(currentPlanCode ?? null);
|
|
563
|
+
const [isAnnual, setIsAnnual] = useState(false);
|
|
564
|
+
const [loading, setLoading] = useState(!propPlans);
|
|
565
|
+
useEffect(() => {
|
|
566
|
+
if (propPlans) {
|
|
567
|
+
setPlans(propPlans);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
let cancelled = false;
|
|
571
|
+
client.fetch("/api/v1/plans").then(async (res) => {
|
|
572
|
+
if (!cancelled && res.ok) {
|
|
573
|
+
const data = await res.json();
|
|
574
|
+
setPlans(data.plans || []);
|
|
575
|
+
}
|
|
576
|
+
}).finally(() => {
|
|
577
|
+
if (!cancelled) setLoading(false);
|
|
578
|
+
});
|
|
579
|
+
return () => {
|
|
580
|
+
cancelled = true;
|
|
581
|
+
};
|
|
582
|
+
}, [client, propPlans]);
|
|
583
|
+
useEffect(() => {
|
|
584
|
+
if (currentPlanCode !== void 0) {
|
|
585
|
+
setCurrentPlan(currentPlanCode);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (!customerId) return;
|
|
589
|
+
let cancelled = false;
|
|
590
|
+
client.fetch(`/api/v1/billing/status?customer_id=${encodeURIComponent(customerId)}`).then(async (res) => {
|
|
591
|
+
if (!cancelled && res.ok) {
|
|
592
|
+
const data = await res.json();
|
|
593
|
+
setCurrentPlan(data.plan ?? data.planName ?? null);
|
|
594
|
+
}
|
|
595
|
+
}).catch(() => {
|
|
596
|
+
});
|
|
597
|
+
return () => {
|
|
598
|
+
cancelled = true;
|
|
599
|
+
};
|
|
600
|
+
}, [client, customerId, currentPlanCode]);
|
|
601
|
+
const hasAnnual = plans.some((p) => p.interval === "yearly");
|
|
602
|
+
const filtered = showToggle && hasAnnual ? plans.filter((p) => p.interval === (isAnnual ? "yearly" : "monthly") || isEnterprise(p)) : plans;
|
|
603
|
+
if (loading) return /* @__PURE__ */ jsx(Skeleton, {});
|
|
604
|
+
return /* @__PURE__ */ jsxs("div", { className, style: {
|
|
605
|
+
fontFamily: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
606
|
+
color: C.text,
|
|
607
|
+
background: C.bg
|
|
608
|
+
}, children: [
|
|
609
|
+
showToggle && hasAnnual && /* @__PURE__ */ jsx(Toggle, { isAnnual, onChange: setIsAnnual }),
|
|
610
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
611
|
+
display: "grid",
|
|
612
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
|
|
613
|
+
gap: "1.5rem"
|
|
614
|
+
}, children: filtered.map((plan, i) => /* @__PURE__ */ jsx(
|
|
615
|
+
Card,
|
|
616
|
+
{
|
|
617
|
+
plan,
|
|
618
|
+
features: features?.[i],
|
|
619
|
+
isCurrent: currentPlan === plan.code,
|
|
620
|
+
isHighlighted: highlightPlan === plan.code,
|
|
621
|
+
isEnt: isEnterprise(plan),
|
|
622
|
+
enterpriseEmail,
|
|
623
|
+
onSelect: onSelect ? () => onSelect(plan) : void 0
|
|
624
|
+
},
|
|
625
|
+
plan.code
|
|
626
|
+
)) })
|
|
627
|
+
] });
|
|
628
|
+
}
|
|
300
629
|
function PlanCard({
|
|
301
630
|
id,
|
|
302
631
|
name,
|
|
@@ -441,91 +770,6 @@ function PlanCard({
|
|
|
441
770
|
}
|
|
442
771
|
);
|
|
443
772
|
}
|
|
444
|
-
function PricingTable({
|
|
445
|
-
plans,
|
|
446
|
-
currentPlanId,
|
|
447
|
-
onSelect
|
|
448
|
-
}) {
|
|
449
|
-
const [isAnnual, setIsAnnual] = useState(false);
|
|
450
|
-
return /* @__PURE__ */ jsxs(
|
|
451
|
-
"div",
|
|
452
|
-
{
|
|
453
|
-
style: {
|
|
454
|
-
background: "var(--nozle-background, var(--background))",
|
|
455
|
-
color: "var(--nozle-foreground, var(--foreground))"
|
|
456
|
-
},
|
|
457
|
-
children: [
|
|
458
|
-
/* @__PURE__ */ jsxs(
|
|
459
|
-
"div",
|
|
460
|
-
{
|
|
461
|
-
style: {
|
|
462
|
-
display: "flex",
|
|
463
|
-
gap: "0.5rem",
|
|
464
|
-
justifyContent: "center",
|
|
465
|
-
marginBottom: "1.5rem"
|
|
466
|
-
},
|
|
467
|
-
children: [
|
|
468
|
-
/* @__PURE__ */ jsx(
|
|
469
|
-
"button",
|
|
470
|
-
{
|
|
471
|
-
onClick: () => setIsAnnual(false),
|
|
472
|
-
style: {
|
|
473
|
-
padding: "0.5rem 1rem",
|
|
474
|
-
borderRadius: "var(--nozle-radius, 0.5rem)",
|
|
475
|
-
border: "1px solid var(--nozle-border, var(--border))",
|
|
476
|
-
background: !isAnnual ? "var(--nozle-primary, var(--primary))" : "transparent",
|
|
477
|
-
color: !isAnnual ? "var(--nozle-primary-foreground, var(--primary-foreground))" : "var(--nozle-foreground, var(--foreground))",
|
|
478
|
-
cursor: "pointer",
|
|
479
|
-
fontWeight: !isAnnual ? 600 : 400
|
|
480
|
-
},
|
|
481
|
-
"aria-pressed": !isAnnual,
|
|
482
|
-
children: "Monthly"
|
|
483
|
-
}
|
|
484
|
-
),
|
|
485
|
-
/* @__PURE__ */ jsx(
|
|
486
|
-
"button",
|
|
487
|
-
{
|
|
488
|
-
onClick: () => setIsAnnual(true),
|
|
489
|
-
style: {
|
|
490
|
-
padding: "0.5rem 1rem",
|
|
491
|
-
borderRadius: "var(--nozle-radius, 0.5rem)",
|
|
492
|
-
border: "1px solid var(--nozle-border, var(--border))",
|
|
493
|
-
background: isAnnual ? "var(--nozle-primary, var(--primary))" : "transparent",
|
|
494
|
-
color: isAnnual ? "var(--nozle-primary-foreground, var(--primary-foreground))" : "var(--nozle-foreground, var(--foreground))",
|
|
495
|
-
cursor: "pointer",
|
|
496
|
-
fontWeight: isAnnual ? 600 : 400
|
|
497
|
-
},
|
|
498
|
-
"aria-pressed": isAnnual,
|
|
499
|
-
children: "Annual"
|
|
500
|
-
}
|
|
501
|
-
)
|
|
502
|
-
]
|
|
503
|
-
}
|
|
504
|
-
),
|
|
505
|
-
/* @__PURE__ */ jsx(
|
|
506
|
-
"div",
|
|
507
|
-
{
|
|
508
|
-
style: {
|
|
509
|
-
display: "grid",
|
|
510
|
-
gridTemplateColumns: `repeat(${Math.min(plans.length, 3)}, 1fr)`,
|
|
511
|
-
gap: "1rem"
|
|
512
|
-
},
|
|
513
|
-
children: plans.map((plan) => /* @__PURE__ */ jsx(
|
|
514
|
-
PlanCard,
|
|
515
|
-
{
|
|
516
|
-
...plan,
|
|
517
|
-
isAnnual,
|
|
518
|
-
isCurrent: plan.id === currentPlanId,
|
|
519
|
-
onSelect: onSelect ? () => onSelect(plan.id) : void 0
|
|
520
|
-
},
|
|
521
|
-
plan.id
|
|
522
|
-
))
|
|
523
|
-
}
|
|
524
|
-
)
|
|
525
|
-
]
|
|
526
|
-
}
|
|
527
|
-
);
|
|
528
|
-
}
|
|
529
773
|
function PlanComparison({
|
|
530
774
|
plans,
|
|
531
775
|
features
|