balibabu commited on
Commit
f64944d
·
1 Parent(s): 4c644f2

Feat: Add PricingCard component #3221 (#3634)

Browse files

### What problem does this PR solve?

Feat: Add PricingCard component #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

web/src/components/ui/badge.tsx CHANGED
@@ -15,6 +15,8 @@ const badgeVariants = cva(
15
  destructive:
16
  'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
17
  outline: 'text-foreground',
 
 
18
  },
19
  },
20
  defaultVariants: {
 
15
  destructive:
16
  'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
17
  outline: 'text-foreground',
18
+ tertiary:
19
+ 'border-transparent bg-colors-text-core-standard text-foreground hover:bg-colors-text-core-standard/80',
20
  },
21
  },
22
  defaultVariants: {
web/src/components/ui/button.tsx CHANGED
@@ -13,11 +13,13 @@ const buttonVariants = cva(
13
  destructive:
14
  'bg-destructive text-destructive-foreground hover:bg-destructive/90',
15
  outline:
16
- 'border border-colors-outline-sentiment-primary bg-background hover:bg-accent hover:text-accent-foreground',
17
  secondary:
18
  'bg-secondary text-secondary-foreground hover:bg-secondary/80',
19
  ghost: 'hover:bg-accent hover:text-accent-foreground',
20
  link: 'text-primary underline-offset-4 hover:underline',
 
 
21
  },
22
  size: {
23
  default: 'h-10 px-4 py-2',
 
13
  destructive:
14
  'bg-destructive text-destructive-foreground hover:bg-destructive/90',
15
  outline:
16
+ 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
17
  secondary:
18
  'bg-secondary text-secondary-foreground hover:bg-secondary/80',
19
  ghost: 'hover:bg-accent hover:text-accent-foreground',
20
  link: 'text-primary underline-offset-4 hover:underline',
21
+ tertiary:
22
+ 'bg-colors-text-core-standard text-foreground hover:bg-colors-text-core-standard/80',
23
  },
24
  size: {
25
  default: 'h-10 px-4 py-2',
web/src/pages/home/datasets.tsx CHANGED
@@ -76,7 +76,7 @@ export function Datasets() {
76
  </CardContent>
77
  </Card>
78
  ))}
79
- <Button className="bg-[#9276ff] h-auto" variant={'secondary'}>
80
  See all
81
  </Button>
82
  </div>
 
76
  </CardContent>
77
  </Card>
78
  ))}
79
+ <Button className="h-auto " variant={'tertiary'}>
80
  See all
81
  </Button>
82
  </div>
web/src/pages/profile-setting/plan/index.tsx CHANGED
@@ -1,3 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  export default function Plan() {
2
- return <div>plan</div>;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  }
 
1
+ import { Button } from '@/components/ui/button';
2
+ import { Card, CardContent } from '@/components/ui/card';
3
+ import { Segmented, SegmentedValue } from '@/components/ui/segmented ';
4
+ import { CircleCheckBig, LogOut } from 'lucide-react';
5
+ import { useMemo, useState } from 'react';
6
+ import { PricingCard } from './pricing-card';
7
+
8
+ const pricingData = [
9
+ {
10
+ title: 'Free',
11
+ price: '$0',
12
+ description: 'Meh, just looking',
13
+ features: [
14
+ { name: 'Project', value: '1 project' },
15
+ { name: 'Storage', value: '1 Gb' },
16
+ { name: 'Team', value: '2 members' },
17
+ { name: 'Features', value: 'Basic features' },
18
+ ],
19
+ buttonText: 'Current plan',
20
+ buttonVariant: 'outline' as const,
21
+ },
22
+ {
23
+ title: 'Pro',
24
+ price: '$16.00',
25
+ description: 'For professional use.',
26
+ features: [
27
+ { name: 'Project', value: 'Unlimited projects' },
28
+ { name: 'Storage', value: '100 Gb' },
29
+ { name: 'Team', value: 'Unlimited members' },
30
+ { name: 'Features', value: 'Basic features All advanced features' },
31
+ ],
32
+ buttonText: 'Upgrade',
33
+ buttonVariant: 'default' as const,
34
+ isPro: true,
35
+ },
36
+ {
37
+ title: 'Enterprise',
38
+ price: 'Customed',
39
+ description:
40
+ 'Get full capabilities and support for large-scale mission-critical systems.',
41
+ features: [
42
+ { name: 'Project', value: 'Unlimited projects' },
43
+ { name: 'Storage', value: '100 Gb' },
44
+ { name: 'Team', value: 'Unlimited members' },
45
+ { name: 'Features', value: 'Basic features All advanced features' },
46
+ ],
47
+ buttonText: 'Contact us',
48
+ buttonVariant: 'secondary' as const,
49
+ isEnterprise: true,
50
+ },
51
+ ];
52
+
53
  export default function Plan() {
54
+ const [val, setVal] = useState('monthly');
55
+ const options = useMemo(() => {
56
+ return [
57
+ {
58
+ label: 'Monthly',
59
+ value: 'monthly',
60
+ },
61
+ {
62
+ label: 'Yearly',
63
+ value: 'yearly',
64
+ },
65
+ ];
66
+ }, []);
67
+
68
+ const handleChange = (path: SegmentedValue) => {
69
+ setVal(path as string);
70
+ };
71
+
72
+ const list = [
73
+ 'Full access to pro features',
74
+ 'Exclusive analyze models',
75
+ 'Create more teams',
76
+ 'Invite more collaborators',
77
+ ];
78
+
79
+ return (
80
+ <section className="p-8">
81
+ <h1 className="text-3xl font-bold mb-6">Plan & balance</h1>
82
+ <Card className="border-0 p-6 mb-6 bg-colors-background-inverse-weak divide-y divide-colors-outline-neutral-strong">
83
+ <div className="pb-2 flex justify-between text-xl">
84
+ <span className="font-bold ">Balance</span>
85
+ <span className="font-medium">$ 100.00</span>
86
+ </div>
87
+ <div className="flex items-center justify-between pt-3">
88
+ <span>The value equals to 1,000 tokens or 10.00 GBs of storage</span>
89
+ <Button variant={'tertiary'} size={'sm'}>
90
+ <LogOut />
91
+ Recharge
92
+ </Button>
93
+ </div>
94
+ </Card>
95
+ <Card className="pt-6 bg-colors-background-inverse-weak">
96
+ <CardContent className="space-y-4">
97
+ <div className="font-bold text-xl">Upgrade to access</div>
98
+ <section className="grid grid-cols-2 gap-3">
99
+ {list.map((x, idx) => (
100
+ <div key={idx} className="flex items-center gap-2">
101
+ <CircleCheckBig className="size-4" />
102
+ <span>{x}</span>
103
+ </div>
104
+ ))}
105
+ </section>
106
+ <Segmented
107
+ options={options}
108
+ value={val}
109
+ onChange={handleChange}
110
+ className="bg-colors-background-inverse-standard inline-flex"
111
+ ></Segmented>
112
+ <div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
113
+ {pricingData.map((plan, index) => (
114
+ <PricingCard key={index} {...plan} />
115
+ ))}
116
+ </div>
117
+ </CardContent>
118
+ </Card>
119
+ </section>
120
+ );
121
  }
web/src/pages/profile-setting/plan/pricing-card.tsx ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Badge } from '@/components/ui/badge';
2
+ import { Button } from '@/components/ui/button';
3
+ import { Card, CardContent, CardHeader } from '@/components/ui/card';
4
+ import { cn } from '@/lib/utils';
5
+ import { Mail, Zap } from 'lucide-react';
6
+
7
+ interface PricingFeature {
8
+ name: string;
9
+ value: string;
10
+ tooltip?: string;
11
+ }
12
+
13
+ interface PricingCardProps {
14
+ title: string;
15
+ price: string;
16
+ description: string;
17
+ features: PricingFeature[];
18
+ buttonText: string;
19
+ buttonVariant?: 'default' | 'outline' | 'secondary';
20
+ badge?: string;
21
+ isPro?: boolean;
22
+ isEnterprise?: boolean;
23
+ }
24
+
25
+ export function PricingCard({
26
+ title,
27
+ price,
28
+ description,
29
+ features,
30
+ buttonText,
31
+ isPro,
32
+ isEnterprise,
33
+ }: PricingCardProps) {
34
+ const isFree = title === 'Free';
35
+
36
+ return (
37
+ <Card className="flex flex-col bg-colors-background-neutral-weak divide-y divide-colors-outline-neutral-strong p-4">
38
+ <CardHeader className=" justify-between p-0 pb-3 h-52">
39
+ <section>
40
+ <div className="flex items-center justify-between mb-2">
41
+ <Badge
42
+ variant={isFree ? 'secondary' : 'tertiary'}
43
+ className="text-xs"
44
+ >
45
+ {isPro && <Zap className="mr-2 h-4 w-4" />}
46
+ {isEnterprise && <Mail className="mr-2 h-4 w-4" />}
47
+ {title}
48
+ </Badge>
49
+ </div>
50
+ <p className="text-sm text-colors-text-neutral-standard">
51
+ {description}
52
+ </p>
53
+ </section>
54
+ <section>
55
+ <div className="flex items-baseline text-3xl font-bold pb-3">
56
+ {price}
57
+ {price !== 'Customed' && (
58
+ <span className="text-sm font-normal">/mo</span>
59
+ )}
60
+ </div>
61
+ <Button
62
+ variant={isFree ? 'secondary' : 'tertiary'}
63
+ className={cn('w-full', {
64
+ 'bg-colors-text-core-standard': !isFree,
65
+ })}
66
+ size={'sm'}
67
+ >
68
+ {isPro && <Zap className="mr-2 h-4 w-4" />}
69
+ {isEnterprise && <Mail />}
70
+ {buttonText}
71
+ </Button>
72
+ </section>
73
+ </CardHeader>
74
+ <CardContent className=" p-0 pt-3">
75
+ <ul className="space-y-2">
76
+ {features.map((feature, index) => (
77
+ <li key={index} className="">
78
+ <div className="text-colors-text-core-standard">
79
+ {feature.name}
80
+ </div>
81
+ <span className="text-sm">
82
+ <span className="font-medium">{feature.value}</span>
83
+ </span>
84
+ </li>
85
+ ))}
86
+ </ul>
87
+ </CardContent>
88
+ </Card>
89
+ );
90
+ }
web/tailwind.config.js CHANGED
@@ -28,8 +28,11 @@ module.exports = {
28
 
29
  'colors-outline-sentiment-primary':
30
  'var(--colors-outline-sentiment-primary)',
 
31
 
32
  'colors-text-core-standard': 'var(--colors-text-core-standard)',
 
 
33
 
34
  primary: {
35
  DEFAULT: 'hsl(var(--primary))',
 
28
 
29
  'colors-outline-sentiment-primary':
30
  'var(--colors-outline-sentiment-primary)',
31
+ 'colors-outline-neutral-strong': 'var(--colors-outline-neutral-strong)',
32
 
33
  'colors-text-core-standard': 'var(--colors-text-core-standard)',
34
+ 'colors-text-neutral-strong': 'var(--colors-text-neutral-strong)',
35
+ 'colors-text-neutral-standard': 'var(--colors-text-neutral-standard)',
36
 
37
  primary: {
38
  DEFAULT: 'hsl(var(--primary))',
web/tailwind.css CHANGED
@@ -41,8 +41,11 @@
41
  --button-blue-text: rgb(22, 119, 255);
42
 
43
  --colors-outline-sentiment-primary: rgba(127, 105, 255, 1);
 
44
 
45
  --colors-text-core-standard: rgba(127, 105, 255, 1);
 
 
46
  }
47
 
48
  .dark {
@@ -114,8 +117,11 @@
114
  --colors-background-neutral-weak: rgba(17, 16, 23, 1);
115
 
116
  --colors-outline-sentiment-primary: rgba(146, 118, 255, 1);
 
117
 
118
  --colors-text-core-standard: rgba(137, 126, 255, 1);
 
 
119
  }
120
  }
121
 
 
41
  --button-blue-text: rgb(22, 119, 255);
42
 
43
  --colors-outline-sentiment-primary: rgba(127, 105, 255, 1);
44
+ --colors-outline-neutral-strong: rgba(112, 107, 107, 0.15);
45
 
46
  --colors-text-core-standard: rgba(127, 105, 255, 1);
47
+ --colors-text-neutral-strong: rgb(130, 121, 121);
48
+ --colors-text-neutral-standard: rgba(230, 227, 246, 1);
49
  }
50
 
51
  .dark {
 
117
  --colors-background-neutral-weak: rgba(17, 16, 23, 1);
118
 
119
  --colors-outline-sentiment-primary: rgba(146, 118, 255, 1);
120
+ --colors-outline-neutral-strong: rgba(255, 255, 255, 0.15);
121
 
122
  --colors-text-core-standard: rgba(137, 126, 255, 1);
123
+ --colors-text-neutral-strong: rgba(255, 255, 255, 1);
124
+ --colors-text-neutral-standard: rgba(230, 227, 246, 1);
125
  }
126
  }
127