@omnikit-js/ui 0.3.1 → 0.3.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnikit-js/ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "A SaaS billing component for Next.js with Stripe integration",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
+
"next": "^14.0.0 || ^15.0.0",
|
|
38
39
|
"react": "^18.0.0 || ^19.0.0",
|
|
39
|
-
"react-dom": "^18.0.0 || ^19.0.0"
|
|
40
|
-
"next": "^14.0.0 || ^15.0.0"
|
|
40
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@radix-ui/react-avatar": "^1.1.10",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"@radix-ui/react-slot": "^1.2.3",
|
|
53
53
|
"@radix-ui/react-switch": "^1.2.5",
|
|
54
54
|
"@radix-ui/react-tabs": "^1.1.12",
|
|
55
|
+
"@radix-ui/react-tooltip": "^1.2.7",
|
|
55
56
|
"@stripe/react-stripe-js": "^3.7.0",
|
|
56
57
|
"@stripe/stripe-js": "^7.4.0",
|
|
57
58
|
"class-variance-authority": "^0.7.1",
|
|
@@ -70,9 +71,9 @@
|
|
|
70
71
|
"@rollup/plugin-replace": "^6.0.2",
|
|
71
72
|
"@rollup/plugin-terser": "^0.4.4",
|
|
72
73
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
74
|
+
"@types/node": "^20",
|
|
73
75
|
"@types/react": "^18.3.3",
|
|
74
76
|
"@types/react-dom": "^18.3.0",
|
|
75
|
-
"@types/node": "^20",
|
|
76
77
|
"autoprefixer": "^10.4.19",
|
|
77
78
|
"postcss": "^8.4.38",
|
|
78
79
|
"react": "^18.3.1",
|
|
@@ -11,6 +11,7 @@ import { Progress } from "./ui/progress";
|
|
|
11
11
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
|
|
12
12
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog";
|
|
13
13
|
import { Check, CreditCard, FileText, AlertCircle, AlertTriangle, CheckCircle, Info, Loader2 } from "lucide-react";
|
|
14
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
|
|
14
15
|
import PaymentMethodForm from './PaymentMethodForm';
|
|
15
16
|
import SubscriptionConfirmModal from './SubscriptionConfirmModal';
|
|
16
17
|
import { CardBrandIcon } from './ui/card-brand-icon';
|
|
@@ -147,6 +148,16 @@ export default function BillingClient({ customerData, pricingData, paymentMethod
|
|
|
147
148
|
const [selectedPlan, setSelectedPlan] = useState<any>(null);
|
|
148
149
|
const [activeTab, setActiveTab] = useState('overview');
|
|
149
150
|
const router = useRouter();
|
|
151
|
+
|
|
152
|
+
const formatFeatureValue = (value: any) => {
|
|
153
|
+
if (typeof value === 'boolean') {
|
|
154
|
+
return value ? '✓' : '✗'
|
|
155
|
+
}
|
|
156
|
+
if (typeof value === 'number' && value >= 1000) {
|
|
157
|
+
return new Intl.NumberFormat('en-US').format(value)
|
|
158
|
+
}
|
|
159
|
+
return value
|
|
160
|
+
}
|
|
150
161
|
|
|
151
162
|
const currentSubscription = customerData?.subscriptions?.[0];
|
|
152
163
|
const currentPlanItem = currentSubscription?.items?.[0];
|
|
@@ -465,7 +476,7 @@ export default function BillingClient({ customerData, pricingData, paymentMethod
|
|
|
465
476
|
</TabsContent>
|
|
466
477
|
|
|
467
478
|
<TabsContent value="plans" className="space-y-4">
|
|
468
|
-
<div className=
|
|
479
|
+
<div className={`grid gap-4 ${pricingData?.products?.length === 3 ? 'md:grid-cols-3' : 'md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'}`}>
|
|
469
480
|
{pricingData?.products?.map((product: any) => {
|
|
470
481
|
const price = product.prices?.[0];
|
|
471
482
|
const features = product.features || [];
|
|
@@ -499,38 +510,47 @@ export default function BillingClient({ customerData, pricingData, paymentMethod
|
|
|
499
510
|
POPULAR
|
|
500
511
|
</div>
|
|
501
512
|
)}
|
|
502
|
-
<CardHeader className="text-center pb-
|
|
503
|
-
<CardTitle className="text-
|
|
504
|
-
<CardDescription className="mt-
|
|
513
|
+
<CardHeader className="text-center pb-4 pt-4">
|
|
514
|
+
<CardTitle className="text-lg font-semibold">{product.name}</CardTitle>
|
|
515
|
+
<CardDescription className="mt-1 text-sm">{product.description}</CardDescription>
|
|
505
516
|
</CardHeader>
|
|
506
|
-
<CardContent className="text-center pb-
|
|
507
|
-
<div className="mb-
|
|
517
|
+
<CardContent className="text-center pb-4">
|
|
518
|
+
<div className="mb-4">
|
|
508
519
|
<div className="flex items-baseline justify-center">
|
|
509
|
-
<span className="text-
|
|
520
|
+
<span className="text-3xl font-bold tracking-tight">
|
|
510
521
|
{parseFloat(price?.unit_amount) === 0 ? 'Free' : `$${parseFloat(price?.unit_amount).toFixed(0)}`}
|
|
511
522
|
</span>
|
|
512
523
|
{parseFloat(price?.unit_amount) > 0 && (
|
|
513
|
-
<span className="ml-1 text-
|
|
524
|
+
<span className="ml-1 text-sm text-muted-foreground">/month</span>
|
|
514
525
|
)}
|
|
515
526
|
</div>
|
|
516
527
|
{isCurrentPlan && (
|
|
517
528
|
<Badge variant="secondary" className="mt-2">Current Plan</Badge>
|
|
518
529
|
)}
|
|
519
530
|
</div>
|
|
520
|
-
<div className="space-y-
|
|
531
|
+
<div className="space-y-2">
|
|
521
532
|
{features.map((feature: any) => (
|
|
522
|
-
<div key={feature.id} className="border-t pt-
|
|
523
|
-
<div className="text-
|
|
524
|
-
{feature.value} {feature.units}
|
|
525
|
-
</div>
|
|
526
|
-
<div className="text-sm text-muted-foreground mt-1">
|
|
527
|
-
{feature.description}
|
|
533
|
+
<div key={feature.id} className="border-t pt-2">
|
|
534
|
+
<div className="text-lg font-semibold text-foreground">
|
|
535
|
+
{formatFeatureValue(feature.value)} {feature.units}
|
|
528
536
|
</div>
|
|
537
|
+
<TooltipProvider>
|
|
538
|
+
<Tooltip>
|
|
539
|
+
<TooltipTrigger asChild>
|
|
540
|
+
<div className="text-xs text-muted-foreground mt-1 cursor-help">
|
|
541
|
+
{feature.name || feature.description}
|
|
542
|
+
</div>
|
|
543
|
+
</TooltipTrigger>
|
|
544
|
+
<TooltipContent>
|
|
545
|
+
<p>{feature.description}</p>
|
|
546
|
+
</TooltipContent>
|
|
547
|
+
</Tooltip>
|
|
548
|
+
</TooltipProvider>
|
|
529
549
|
</div>
|
|
530
550
|
))}
|
|
531
551
|
</div>
|
|
532
552
|
</CardContent>
|
|
533
|
-
<CardFooter className="pt-
|
|
553
|
+
<CardFooter className="pt-3">
|
|
534
554
|
<Button
|
|
535
555
|
className="w-full"
|
|
536
556
|
variant={isCurrentPlan ? "outline" : product.metadata?.popular === 'true' ? "default" : "secondary"}
|
|
@@ -6,6 +6,7 @@ import { Button } from './ui/button';
|
|
|
6
6
|
import { Card } from './ui/card';
|
|
7
7
|
import { Check, Loader2, CheckCircle, AlertCircle } from 'lucide-react';
|
|
8
8
|
import { Alert, AlertDescription } from './ui/alert';
|
|
9
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip';
|
|
9
10
|
import { RadioGroup, RadioGroupItem } from './ui/radio-group';
|
|
10
11
|
import { Label } from './ui/label';
|
|
11
12
|
import { CardBrandIcon } from './ui/card-brand-icon';
|
|
@@ -45,6 +46,16 @@ export default function SubscriptionConfirmModal({
|
|
|
45
46
|
}).format(amount / 100);
|
|
46
47
|
};
|
|
47
48
|
|
|
49
|
+
const formatFeatureValue = (value: any) => {
|
|
50
|
+
if (typeof value === 'boolean') {
|
|
51
|
+
return value ? '✓' : '✗'
|
|
52
|
+
}
|
|
53
|
+
if (typeof value === 'number' && value >= 1000) {
|
|
54
|
+
return new Intl.NumberFormat('en-US').format(value)
|
|
55
|
+
}
|
|
56
|
+
return value
|
|
57
|
+
};
|
|
58
|
+
|
|
48
59
|
const handleConfirm = async () => {
|
|
49
60
|
setIsSubscribing(true);
|
|
50
61
|
setError(null);
|
|
@@ -120,7 +131,18 @@ export default function SubscriptionConfirmModal({
|
|
|
120
131
|
{plan.features?.map((feature: any) => (
|
|
121
132
|
<li key={feature.id} className="flex items-start text-sm">
|
|
122
133
|
<Check className="h-4 w-4 mr-2 text-primary mt-0.5 flex-shrink-0" />
|
|
123
|
-
<
|
|
134
|
+
<TooltipProvider>
|
|
135
|
+
<Tooltip>
|
|
136
|
+
<TooltipTrigger asChild>
|
|
137
|
+
<span className="cursor-help">
|
|
138
|
+
{formatFeatureValue(feature.value)} {feature.units} - {feature.name || feature.description}
|
|
139
|
+
</span>
|
|
140
|
+
</TooltipTrigger>
|
|
141
|
+
<TooltipContent>
|
|
142
|
+
<p>{feature.description}</p>
|
|
143
|
+
</TooltipContent>
|
|
144
|
+
</Tooltip>
|
|
145
|
+
</TooltipProvider>
|
|
124
146
|
</li>
|
|
125
147
|
))}
|
|
126
148
|
</ul>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
|
|
8
|
+
const TooltipProvider = TooltipPrimitive.Provider
|
|
9
|
+
|
|
10
|
+
const Tooltip = TooltipPrimitive.Root
|
|
11
|
+
|
|
12
|
+
const TooltipTrigger = TooltipPrimitive.Trigger
|
|
13
|
+
|
|
14
|
+
const TooltipContent = React.forwardRef<
|
|
15
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
16
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
17
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
18
|
+
<TooltipPrimitive.Content
|
|
19
|
+
ref={ref}
|
|
20
|
+
sideOffset={sideOffset}
|
|
21
|
+
className={cn(
|
|
22
|
+
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
))
|
|
28
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
|
29
|
+
|
|
30
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|