@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.1",
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="grid gap-6 md:grid-cols-3">
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-8 pt-6">
503
- <CardTitle className="text-xl font-semibold">{product.name}</CardTitle>
504
- <CardDescription className="mt-2">{product.description}</CardDescription>
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-8">
507
- <div className="mb-8">
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-5xl font-bold tracking-tight">
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-lg text-muted-foreground">/month</span>
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-4">
531
+ <div className="space-y-2">
521
532
  {features.map((feature: any) => (
522
- <div key={feature.id} className="border-t pt-4">
523
- <div className="text-2xl font-semibold text-foreground">
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-4">
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
- <span>{feature.value} {feature.units} - {feature.description}</span>
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 }