1'use client'
2
3import * as React from 'react'
4import { toast } from 'sonner'
5
6import { Badge } from '@/components/ui/badge'
7import { Button } from '@/components/ui/button'
8import {
9 Card,
10 CardContent,
11 CardDescription,
12 CardFooter,
13 CardHeader,
14 CardTitle,
15} from '@/components/ui/card'
16import { Checkbox } from '@/components/ui/checkbox'
17import { Label } from '@/components/ui/label'
18import { Separator } from '@/components/ui/separator'
19import { Slider } from '@/components/ui/slider'
20
21export function Slider01() {
22 const [price, setPrice] = React.useState<[number, number]>([120, 350])
23
24 const onSubmit = () => {
25 toast.success(`Price selected : Min $${price[0]} - Max $${price[1]}`)
26 }
27
28 return (
29 <Card className="max-w-md rounded-2xl border border-gray-200 bg-white shadow-sm">
30 <CardHeader>
31 <CardTitle>Hotel Filter</CardTitle>
32 <CardDescription>Get your preferred hotel</CardDescription>
33 </CardHeader>
34 <CardContent>
35 <div className="mb-3 flex items-center justify-between">
36 <div className="text-md font-medium text-black">
37 Amenities
38 </div>
39 <Button variant="link">View All</Button>
40 </div>
41 <div className="space-y-2">
42 <div className="flex items-center gap-2">
43 <Checkbox id="wifi" defaultChecked />
44 <Label
45 htmlFor="wifi"
46 className="flex items-center gap-2 text-sm font-normal"
47 >
48 Free Wi-Fi
49 <Badge
50 variant="secondary"
51 className="bg-success text-[10px] text-white"
52 >
53 Top Rated
54 </Badge>
55 </Label>
56 </div>
57 <div className="flex items-center gap-2">
58 <Checkbox id="pool" />
59 <Label htmlFor="pool" className="text-sm font-normal">
60 Swimming Pool
61 </Label>
62 </div>
63 <div className="flex items-center gap-2">
64 <Checkbox id="parking" />
65 <Label
66 htmlFor="parking"
67 className="text-sm font-normal"
68 >
69 Free Parking
70 </Label>
71 </div>
72 <div className="flex items-center gap-2">
73 <Checkbox id="spa" />
74 <Label htmlFor="spa" className="text-sm font-normal">
75 Spa & Wellness
76 </Label>
77 </div>
78 </div>
79 <Separator className="my-4" />
80 <div>
81 <div className="text-md text-black">Price per night</div>
82 <div className="flex items-center justify-between space-y-3">
83 <span className="rounded bg-black px-2 py-1 text-xs text-white">
84 $ {price[0]}
85 </span>
86 <span className="rounded bg-black px-2 py-1 text-xs text-white">
87 $ {price[1]}
88 </span>
89 </div>
90 <Slider
91 value={price}
92 min={50}
93 max={700}
94 step={10}
95 onValueChange={(v) => setPrice(v as [number, number])}
96 />
97 </div>
98 <Separator className="my-4" />
99 <div>
100 <h3 className="mb-2 text-sm font-medium text-black">
101 Cancellation Policy
102 </h3>
103 <div className="space-y-2">
104 <div className="flex items-center gap-2">
105 <Checkbox id="free-cancel" defaultChecked />
106 <Label
107 htmlFor="free-cancel"
108 className="text-sm font-normal"
109 >
110 Free Cancellation
111 </Label>
112 </div>
113 <div className="flex items-center gap-2">
114 <Checkbox id="partial" />
115 <Label
116 htmlFor="partial"
117 className="text-sm font-normal"
118 >
119 Partial Refund
120 </Label>
121 </div>
122 <div className="flex items-center gap-2">
123 <Checkbox id="non-refundable" />
124 <Label
125 htmlFor="non-refundable"
126 className="text-sm font-normal"
127 >
128 Non-Refundable
129 </Label>
130 </div>
131 </div>
132 </div>
133 </CardContent>
134 <CardFooter className="flex justify-end gap-3 border-t pt-4">
135 <Button onClick={onSubmit}>Submit</Button>
136 </CardFooter>
137 </Card>
138 )
139}
1401'use client'
2
3import * as React from 'react'
4import { toast } from 'sonner'
5
6import { Badge } from '@/components/ui/badge'
7import { Button } from '@/components/ui/button'
8import {
9 Card,
10 CardContent,
11 CardDescription,
12 CardFooter,
13 CardHeader,
14 CardTitle,
15} from '@/components/ui/card'
16import { Checkbox } from '@/components/ui/checkbox'
17import { Label } from '@/components/ui/label'
18import { Separator } from '@/components/ui/separator'
19import { Slider } from '@/components/ui/slider'
20
21export function Slider01() {
22 const [price, setPrice] = React.useState<[number, number]>([120, 350])
23
24 const onSubmit = () => {
25 toast.success(`Price selected : Min $${price[0]} - Max $${price[1]}`)
26 }
27
28 return (
29 <Card className="max-w-md rounded-2xl border border-gray-200 bg-white shadow-sm">
30 <CardHeader>
31 <CardTitle>Hotel Filter</CardTitle>
32 <CardDescription>Get your preferred hotel</CardDescription>
33 </CardHeader>
34 <CardContent>
35 <div className="mb-3 flex items-center justify-between">
36 <div className="text-md font-medium text-black">
37 Amenities
38 </div>
39 <Button variant="link">View All</Button>
40 </div>
41 <div className="space-y-2">
42 <div className="flex items-center gap-2">
43 <Checkbox id="wifi" defaultChecked />
44 <Label
45 htmlFor="wifi"
46 className="flex items-center gap-2 text-sm font-normal"
47 >
48 Free Wi-Fi
49 <Badge
50 variant="secondary"
51 className="bg-success text-[10px] text-white"
52 >
53 Top Rated
54 </Badge>
55 </Label>
56 </div>
57 <div className="flex items-center gap-2">
58 <Checkbox id="pool" />
59 <Label htmlFor="pool" className="text-sm font-normal">
60 Swimming Pool
61 </Label>
62 </div>
63 <div className="flex items-center gap-2">
64 <Checkbox id="parking" />
65 <Label
66 htmlFor="parking"
67 className="text-sm font-normal"
68 >
69 Free Parking
70 </Label>
71 </div>
72 <div className="flex items-center gap-2">
73 <Checkbox id="spa" />
74 <Label htmlFor="spa" className="text-sm font-normal">
75 Spa & Wellness
76 </Label>
77 </div>
78 </div>
79 <Separator className="my-4" />
80 <div>
81 <div className="text-md text-black">Price per night</div>
82 <div className="flex items-center justify-between space-y-3">
83 <span className="rounded bg-black px-2 py-1 text-xs text-white">
84 $ {price[0]}
85 </span>
86 <span className="rounded bg-black px-2 py-1 text-xs text-white">
87 $ {price[1]}
88 </span>
89 </div>
90 <Slider
91 value={price}
92 min={50}
93 max={700}
94 step={10}
95 onValueChange={(v) => setPrice(v as [number, number])}
96 />
97 </div>
98 <Separator className="my-4" />
99 <div>
100 <h3 className="mb-2 text-sm font-medium text-black">
101 Cancellation Policy
102 </h3>
103 <div className="space-y-2">
104 <div className="flex items-center gap-2">
105 <Checkbox id="free-cancel" defaultChecked />
106 <Label
107 htmlFor="free-cancel"
108 className="text-sm font-normal"
109 >
110 Free Cancellation
111 </Label>
112 </div>
113 <div className="flex items-center gap-2">
114 <Checkbox id="partial" />
115 <Label
116 htmlFor="partial"
117 className="text-sm font-normal"
118 >
119 Partial Refund
120 </Label>
121 </div>
122 <div className="flex items-center gap-2">
123 <Checkbox id="non-refundable" />
124 <Label
125 htmlFor="non-refundable"
126 className="text-sm font-normal"
127 >
128 Non-Refundable
129 </Label>
130 </div>
131 </div>
132 </div>
133 </CardContent>
134 <CardFooter className="flex justify-end gap-3 border-t pt-4">
135 <Button onClick={onSubmit}>Submit</Button>
136 </CardFooter>
137 </Card>
138 )
139}
1401'use client'
2
3import * as React from 'react'
4import Image from 'next/image'
5import { RotateCw, ZoomIn, ZoomOut } from 'lucide-react'
6
7import { Button } from '@/components/ui/button'
8import {
9 Card,
10 CardContent,
11 CardHeader,
12 CardTitle,
13} from '@/components/ui/card'
14import { Slider } from '@/components/ui/slider'
15
16export function ImageZoomSlider() {
17 const [zoom, setZoom] = React.useState<number>(1)
18 const [rotation, setRotation] = React.useState<number>(0)
19
20 const handleZoomIn = () => setZoom((prev) => Math.min(prev + 0.1, 2))
21 const handleZoomOut = () => setZoom((prev) => Math.max(prev - 0.1, 0.5))
22 const handleRotate = () => setRotation((prev) => (prev + 90) % 360)
23
24 const handleClear = () => {
25 setZoom(1)
26 setRotation(0)
27 }
28
29 return (
30 <Card className="max-w-lg rounded-2xl border border-gray-200 bg-white shadow-sm">
31 <CardHeader className="flex flex-row items-center justify-between">
32 <CardTitle className="text-gray text-base font-semibold">
33 Image Zoom Example
34 </CardTitle>
35 <Button variant="ghost" size="sm" onClick={handleClear}>
36 Clear
37 </Button>
38 </CardHeader>
39
40 <CardContent className="flex w-full flex-col items-center gap-4">
41 <div className="relative w-full overflow-hidden rounded-lg bg-gray-100">
42 <div
43 className="flex items-center justify-center transition-transform duration-200"
44 style={{
45 transform: `scale(${zoom}) rotate(${rotation}deg)`,
46 }}
47 >
48 <Image
49 src="/images/random-image/random-6.jpg"
50 alt="Uploaded"
51 width={400}
52 height={400}
53 className="rounded-md object-contain"
54 />
55 </div>
56 </div>
57
58 <div className="w-full">
59 <div className="text-gray mb-2 flex items-center justify-between text-sm">
60 <Button
61 variant="ghost"
62 size="icon"
63 onClick={handleZoomOut}
64 >
65 <ZoomOut className="h-4 w-4" />
66 </Button>
67 <span>Zoom</span>
68 <Button
69 variant="ghost"
70 size="icon"
71 onClick={handleZoomIn}
72 >
73 <ZoomIn className="h-4 w-4" />
74 </Button>
75 </div>
76 <Slider
77 value={[zoom]}
78 min={0.5}
79 max={2}
80 step={0.1}
81 onValueChange={(v) => setZoom(v[0])}
82 />
83 </div>
84 <div className="flex items-center justify-center gap-3">
85 <Button
86 variant="outline"
87 size="icon"
88 onClick={handleRotate}
89 >
90 <RotateCw className="h-4 w-4" />
91 </Button>
92 </div>
93 </CardContent>
94 </Card>
95 )
96}
97
1'use client'
2
3import * as React from 'react'
4import Image from 'next/image'
5import { RotateCw, ZoomIn, ZoomOut } from 'lucide-react'
6
7import { Button } from '@/components/ui/button'
8import {
9 Card,
10 CardContent,
11 CardHeader,
12 CardTitle,
13} from '@/components/ui/card'
14import { Slider } from '@/components/ui/slider'
15
16export function ImageZoomSlider() {
17 const [zoom, setZoom] = React.useState<number>(1)
18 const [rotation, setRotation] = React.useState<number>(0)
19
20 const handleZoomIn = () => setZoom((prev) => Math.min(prev + 0.1, 2))
21 const handleZoomOut = () => setZoom((prev) => Math.max(prev - 0.1, 0.5))
22 const handleRotate = () => setRotation((prev) => (prev + 90) % 360)
23
24 const handleClear = () => {
25 setZoom(1)
26 setRotation(0)
27 }
28
29 return (
30 <Card className="max-w-lg rounded-2xl border border-gray-200 bg-white shadow-sm">
31 <CardHeader className="flex flex-row items-center justify-between">
32 <CardTitle className="text-gray text-base font-semibold">
33 Image Zoom Example
34 </CardTitle>
35 <Button variant="ghost" size="sm" onClick={handleClear}>
36 Clear
37 </Button>
38 </CardHeader>
39
40 <CardContent className="flex w-full flex-col items-center gap-4">
41 <div className="relative w-full overflow-hidden rounded-lg bg-gray-100">
42 <div
43 className="flex items-center justify-center transition-transform duration-200"
44 style={{
45 transform: `scale(${zoom}) rotate(${rotation}deg)`,
46 }}
47 >
48 <Image
49 src="/images/random-image/random-6.jpg"
50 alt="Uploaded"
51 width={400}
52 height={400}
53 className="rounded-md object-contain"
54 />
55 </div>
56 </div>
57
58 <div className="w-full">
59 <div className="text-gray mb-2 flex items-center justify-between text-sm">
60 <Button
61 variant="ghost"
62 size="icon"
63 onClick={handleZoomOut}
64 >
65 <ZoomOut className="h-4 w-4" />
66 </Button>
67 <span>Zoom</span>
68 <Button
69 variant="ghost"
70 size="icon"
71 onClick={handleZoomIn}
72 >
73 <ZoomIn className="h-4 w-4" />
74 </Button>
75 </div>
76 <Slider
77 value={[zoom]}
78 min={0.5}
79 max={2}
80 step={0.1}
81 onValueChange={(v) => setZoom(v[0])}
82 />
83 </div>
84 <div className="flex items-center justify-center gap-3">
85 <Button
86 variant="outline"
87 size="icon"
88 onClick={handleRotate}
89 >
90 <RotateCw className="h-4 w-4" />
91 </Button>
92 </div>
93 </CardContent>
94 </Card>
95 )
96}
971'use client'
2
3import React, { useState } from 'react'
4import { AlertCircle, CheckCircle, CrownIcon, Shield, Zap } from 'lucide-react'
5
6import { Button } from '@/components/ui/button'
7import {
8 Card,
9 CardContent,
10 CardDescription,
11 CardHeader,
12 CardTitle,
13} from '@/components/ui/card'
14import { Checkbox } from '@/components/ui/checkbox'
15import { Label } from '@/components/ui/label'
16import {
17 Select,
18 SelectContent,
19 SelectItem,
20 SelectTrigger,
21 SelectValue,
22} from '@/components/ui/select'
23import { Separator } from '@/components/ui/separator'
24import { Slider } from '@/components/ui/slider'
25
26export function Slider03() {
27 const [billingCycle, setBillingCycle] = useState('yearly')
28 const [teamMembers, setTeamMembers] = useState(1)
29 const [apiCalls, setApiCalls] = useState(50)
30 const [prioritySupport, setPrioritySupport] = useState(true)
31
32 const memberPrice = billingCycle === 'yearly' ? 45 : 55
33 const discount = billingCycle === 'yearly' ? 0.2 : 0
34 const premiumPlanPrice = memberPrice * 12 * (1 - discount)
35 const teamLicensePrice = teamMembers * 199.99
36 const apiPrice = (apiCalls / 50) * 399.0
37 const supportPrice = prioritySupport ? 149.99 : 0
38
39 const subtotal =
40 premiumPlanPrice + teamLicensePrice + apiPrice + supportPrice
41 const taxAmount = subtotal * 0.08
42 const totalAtRenewal = subtotal + taxAmount
43 const dueNow = totalAtRenewal
44
45 return (
46 <>
47 <Card>
48 <CardHeader>
49 <div className="text-gray hidden h-9 w-9 items-center justify-center rounded-full border md:flex">
50 <CrownIcon className="text-gray h-4 w-4" />
51 </div>
52 <div className="flex flex-col space-y-2">
53 <CardTitle>Upgrade to Premium Pro</CardTitle>
54 <CardDescription>
55 Manage team size, billing, and integrations
56 </CardDescription>
57 </div>
58 </CardHeader>
59 <CardContent className="flex flex-col gap-7 md:flex-row">
60 <div className="space-y-2">
61 <div className="space-y-2">
62 <Label>Billing cycle</Label>
63 <Select
64 value={billingCycle}
65 onValueChange={(value) =>
66 setBillingCycle(value)
67 }
68 >
69 <SelectTrigger className="w-full">
70 <SelectValue placeholder="Select billing cycle" />
71 </SelectTrigger>
72 <SelectContent>
73 <SelectItem value="yearly">
74 Yearly - $118.00 / user / month
75 </SelectItem>
76 <SelectItem value="monthly">
77 Monthly - $55.00 / user / month
78 </SelectItem>
79 <SelectItem value="quarterly">
80 Quarterly - $49.00 / user / month
81 </SelectItem>
82 </SelectContent>
83 </Select>
84 </div>
85 <div className="space-y-2">
86 <Label>Team members</Label>
87 <div className="border-border rounded-lg border p-5">
88 <div className="mb-3 flex items-center justify-between">
89 <span className="text-gray font-semibold">
90 Number of team members
91 </span>
92 <div className="flex items-center gap-3">
93 <Button
94 variant="outline"
95 size="sm"
96 onClick={() =>
97 setTeamMembers(
98 Math.max(
99 1,
100 teamMembers - 1,
101 ),
102 )
103 }
104 >
105 −
106 </Button>
107 <span className="w-8 text-center font-semibold">
108 {teamMembers}
109 </span>
110 <Button
111 variant="outline"
112 size="sm"
113 onClick={() =>
114 setTeamMembers(teamMembers + 1)
115 }
116 >
117 +
118 </Button>
119 </div>
120 </div>
121 <p className="text-gray mb-3 text-sm">
122 Add team members to collaborate on projects
123 </p>
124 <div className="border-border flex items-start gap-2 rounded-lg border p-3">
125 <AlertCircle className="text-gray mt-0.5 h-5 w-5 flex-shrink-0" />
126 <p className="text-gray text-sm">
127 Each team member gets full access to
128 premium features
129 </p>
130 </div>
131 </div>
132 </div>
133 <div className="space-y-2">
134 <Label className="">API call limit</Label>
135 <div className="rounded-lg border border-gray-200 bg-white p-6">
136 <div className="mb-4 flex items-center justify-between">
137 <div>
138 <h3 className="text-gray flex items-center gap-2 font-semibold">
139 <Zap className="h-5 w-5" />
140 API Credits
141 </h3>
142 <p className="text-gray text-sm">
143 10K included + additional capacity
144 </p>
145 </div>
146 <div className="text-right">
147 <span className="text-gray text-2xl font-bold">
148 {apiCalls}K
149 </span>
150 <span className="text-gray block text-sm">
151 requests/month
152 </span>
153 </div>
154 </div>
155
156 <div>
157 <div className="text-gray mb-2 flex items-center justify-between text-sm">
158 <span>10K</span>
159 <span>500K</span>
160 </div>
161 <Slider
162 value={[apiCalls]}
163 min={10}
164 max={500}
165 step={10}
166 onValueChange={(v) => setApiCalls(v[0])}
167 className="cursor-pointer"
168 />
169 </div>
170 </div>
171 </div>
172 <div className="space-y-2">
173 <Label> Additional services</Label>
174 <div
175 onClick={() =>
176 setPrioritySupport(!prioritySupport)
177 }
178 className={`cursor-pointer rounded-lg border-2 bg-white p-5 transition-all ${
179 prioritySupport
180 ? 'bg-border border-black'
181 : 'hover:border-gray border-border'
182 }`}
183 >
184 <div className="flex items-start justify-between">
185 <div className="flex items-start gap-3">
186 <Checkbox
187 id="priority-support"
188 checked={prioritySupport}
189 onCheckedChange={(checked) =>
190 setPrioritySupport(!!checked)
191 }
192 />
193 <div>
194 <Label className="flex items-center gap-2 font-semibold">
195 <Shield className="h-5 w-5" />
196 Priority Support
197 </Label>
198 <p className="text-gray mt-1 text-sm">
199 24/7 dedicated support with
200 2-hour response time
201 </p>
202 </div>
203 </div>
204 <span className="text-gray text-lg font-bold">
205 $149.99
206 </span>
207 </div>
208 </div>
209 </div>
210 </div>
211 <div>
212 <div className="sticky top-8 rounded-2xl bg-white p-6 shadow-lg">
213 <h2 className="mb-6 text-xl font-bold text-black">
214 Order Summary
215 </h2>
216
217 <div className="mb-6 space-y-4">
218 <div className="flex justify-between text-sm">
219 <span>Premium Pro Plan</span>
220 <span className="font-semibold">
221 ${premiumPlanPrice.toFixed(2)}
222 </span>
223 </div>
224 <div className="flex justify-between text-sm">
225 <span>{teamMembers}× Team License(s)</span>
226 <span className="font-semibold">
227 ${teamLicensePrice.toFixed(2)}
228 </span>
229 </div>
230 <div className="flex justify-between text-sm">
231 <span>API Credits ({apiCalls}K)</span>
232 <span className="font-semibold">
233 ${apiPrice.toFixed(2)}
234 </span>
235 </div>
236 {prioritySupport && (
237 <div className="flex justify-between text-sm">
238 <span>Priority Support</span>
239 <span className="font-semibold">
240 ${supportPrice.toFixed(2)}
241 </span>
242 </div>
243 )}
244 </div>
245 <Separator />
246 <div className="mb-6 space-y-3 border-gray-200 pt-4">
247 <div className="flex justify-between">
248 <span className="text-black">Subtotal</span>
249 <span className="font-bold text-black">
250 ${subtotal.toFixed(2)}
251 </span>
252 </div>
253 <div className="flex justify-between text-sm">
254 <span>Tax (8%)</span>
255 <span>${taxAmount.toFixed(2)}</span>
256 </div>
257 <div className="flex justify-between">
258 <span>Total at renewal</span>
259 <span className="font-bold">
260 ${totalAtRenewal.toFixed(2)}
261 </span>
262 </div>
263 <Separator />
264 <div className="flex justify-between pt-2 text-lg">
265 <span className="font-semibold text-black">
266 Due today
267 </span>
268 <span className="font-bold text-black">
269 ${dueNow.toFixed(2)}
270 </span>
271 </div>
272 </div>
273
274 <Button className="shadow-gray mb-4 w-full rounded-lg px-4 py-3 font-semibold text-white shadow-lg transition-all">
275 Complete Upgrade
276 </Button>
277
278 <p className="text-gray flex items-center justify-center gap-1 text-center text-xs">
279 <CheckCircle className="size-4" />
280 Instant activation after payment
281 </p>
282 </div>
283 </div>
284 </CardContent>
285 </Card>
286 </>
287 )
288}
289Add team members to collaborate on projects
Each team member gets full access to premium features
10K included + additional capacity
24/7 dedicated support with 2-hour response time
Instant activation after payment
1'use client'
2
3import React, { useState } from 'react'
4import { AlertCircle, CheckCircle, CrownIcon, Shield, Zap } from 'lucide-react'
5
6import { Button } from '@/components/ui/button'
7import {
8 Card,
9 CardContent,
10 CardDescription,
11 CardHeader,
12 CardTitle,
13} from '@/components/ui/card'
14import { Checkbox } from '@/components/ui/checkbox'
15import { Label } from '@/components/ui/label'
16import {
17 Select,
18 SelectContent,
19 SelectItem,
20 SelectTrigger,
21 SelectValue,
22} from '@/components/ui/select'
23import { Separator } from '@/components/ui/separator'
24import { Slider } from '@/components/ui/slider'
25
26export function Slider03() {
27 const [billingCycle, setBillingCycle] = useState('yearly')
28 const [teamMembers, setTeamMembers] = useState(1)
29 const [apiCalls, setApiCalls] = useState(50)
30 const [prioritySupport, setPrioritySupport] = useState(true)
31
32 const memberPrice = billingCycle === 'yearly' ? 45 : 55
33 const discount = billingCycle === 'yearly' ? 0.2 : 0
34 const premiumPlanPrice = memberPrice * 12 * (1 - discount)
35 const teamLicensePrice = teamMembers * 199.99
36 const apiPrice = (apiCalls / 50) * 399.0
37 const supportPrice = prioritySupport ? 149.99 : 0
38
39 const subtotal =
40 premiumPlanPrice + teamLicensePrice + apiPrice + supportPrice
41 const taxAmount = subtotal * 0.08
42 const totalAtRenewal = subtotal + taxAmount
43 const dueNow = totalAtRenewal
44
45 return (
46 <>
47 <Card>
48 <CardHeader>
49 <div className="text-gray hidden h-9 w-9 items-center justify-center rounded-full border md:flex">
50 <CrownIcon className="text-gray h-4 w-4" />
51 </div>
52 <div className="flex flex-col space-y-2">
53 <CardTitle>Upgrade to Premium Pro</CardTitle>
54 <CardDescription>
55 Manage team size, billing, and integrations
56 </CardDescription>
57 </div>
58 </CardHeader>
59 <CardContent className="flex flex-col gap-7 md:flex-row">
60 <div className="space-y-2">
61 <div className="space-y-2">
62 <Label>Billing cycle</Label>
63 <Select
64 value={billingCycle}
65 onValueChange={(value) =>
66 setBillingCycle(value)
67 }
68 >
69 <SelectTrigger className="w-full">
70 <SelectValue placeholder="Select billing cycle" />
71 </SelectTrigger>
72 <SelectContent>
73 <SelectItem value="yearly">
74 Yearly - $118.00 / user / month
75 </SelectItem>
76 <SelectItem value="monthly">
77 Monthly - $55.00 / user / month
78 </SelectItem>
79 <SelectItem value="quarterly">
80 Quarterly - $49.00 / user / month
81 </SelectItem>
82 </SelectContent>
83 </Select>
84 </div>
85 <div className="space-y-2">
86 <Label>Team members</Label>
87 <div className="border-border rounded-lg border p-5">
88 <div className="mb-3 flex items-center justify-between">
89 <span className="text-gray font-semibold">
90 Number of team members
91 </span>
92 <div className="flex items-center gap-3">
93 <Button
94 variant="outline"
95 size="sm"
96 onClick={() =>
97 setTeamMembers(
98 Math.max(
99 1,
100 teamMembers - 1,
101 ),
102 )
103 }
104 >
105 −
106 </Button>
107 <span className="w-8 text-center font-semibold">
108 {teamMembers}
109 </span>
110 <Button
111 variant="outline"
112 size="sm"
113 onClick={() =>
114 setTeamMembers(teamMembers + 1)
115 }
116 >
117 +
118 </Button>
119 </div>
120 </div>
121 <p className="text-gray mb-3 text-sm">
122 Add team members to collaborate on projects
123 </p>
124 <div className="border-border flex items-start gap-2 rounded-lg border p-3">
125 <AlertCircle className="text-gray mt-0.5 h-5 w-5 flex-shrink-0" />
126 <p className="text-gray text-sm">
127 Each team member gets full access to
128 premium features
129 </p>
130 </div>
131 </div>
132 </div>
133 <div className="space-y-2">
134 <Label className="">API call limit</Label>
135 <div className="rounded-lg border border-gray-200 bg-white p-6">
136 <div className="mb-4 flex items-center justify-between">
137 <div>
138 <h3 className="text-gray flex items-center gap-2 font-semibold">
139 <Zap className="h-5 w-5" />
140 API Credits
141 </h3>
142 <p className="text-gray text-sm">
143 10K included + additional capacity
144 </p>
145 </div>
146 <div className="text-right">
147 <span className="text-gray text-2xl font-bold">
148 {apiCalls}K
149 </span>
150 <span className="text-gray block text-sm">
151 requests/month
152 </span>
153 </div>
154 </div>
155
156 <div>
157 <div className="text-gray mb-2 flex items-center justify-between text-sm">
158 <span>10K</span>
159 <span>500K</span>
160 </div>
161 <Slider
162 value={[apiCalls]}
163 min={10}
164 max={500}
165 step={10}
166 onValueChange={(v) => setApiCalls(v[0])}
167 className="cursor-pointer"
168 />
169 </div>
170 </div>
171 </div>
172 <div className="space-y-2">
173 <Label> Additional services</Label>
174 <div
175 onClick={() =>
176 setPrioritySupport(!prioritySupport)
177 }
178 className={`cursor-pointer rounded-lg border-2 bg-white p-5 transition-all ${
179 prioritySupport
180 ? 'bg-border border-black'
181 : 'hover:border-gray border-border'
182 }`}
183 >
184 <div className="flex items-start justify-between">
185 <div className="flex items-start gap-3">
186 <Checkbox
187 id="priority-support"
188 checked={prioritySupport}
189 onCheckedChange={(checked) =>
190 setPrioritySupport(!!checked)
191 }
192 />
193 <div>
194 <Label className="flex items-center gap-2 font-semibold">
195 <Shield className="h-5 w-5" />
196 Priority Support
197 </Label>
198 <p className="text-gray mt-1 text-sm">
199 24/7 dedicated support with
200 2-hour response time
201 </p>
202 </div>
203 </div>
204 <span className="text-gray text-lg font-bold">
205 $149.99
206 </span>
207 </div>
208 </div>
209 </div>
210 </div>
211 <div>
212 <div className="sticky top-8 rounded-2xl bg-white p-6 shadow-lg">
213 <h2 className="mb-6 text-xl font-bold text-black">
214 Order Summary
215 </h2>
216
217 <div className="mb-6 space-y-4">
218 <div className="flex justify-between text-sm">
219 <span>Premium Pro Plan</span>
220 <span className="font-semibold">
221 ${premiumPlanPrice.toFixed(2)}
222 </span>
223 </div>
224 <div className="flex justify-between text-sm">
225 <span>{teamMembers}× Team License(s)</span>
226 <span className="font-semibold">
227 ${teamLicensePrice.toFixed(2)}
228 </span>
229 </div>
230 <div className="flex justify-between text-sm">
231 <span>API Credits ({apiCalls}K)</span>
232 <span className="font-semibold">
233 ${apiPrice.toFixed(2)}
234 </span>
235 </div>
236 {prioritySupport && (
237 <div className="flex justify-between text-sm">
238 <span>Priority Support</span>
239 <span className="font-semibold">
240 ${supportPrice.toFixed(2)}
241 </span>
242 </div>
243 )}
244 </div>
245 <Separator />
246 <div className="mb-6 space-y-3 border-gray-200 pt-4">
247 <div className="flex justify-between">
248 <span className="text-black">Subtotal</span>
249 <span className="font-bold text-black">
250 ${subtotal.toFixed(2)}
251 </span>
252 </div>
253 <div className="flex justify-between text-sm">
254 <span>Tax (8%)</span>
255 <span>${taxAmount.toFixed(2)}</span>
256 </div>
257 <div className="flex justify-between">
258 <span>Total at renewal</span>
259 <span className="font-bold">
260 ${totalAtRenewal.toFixed(2)}
261 </span>
262 </div>
263 <Separator />
264 <div className="flex justify-between pt-2 text-lg">
265 <span className="font-semibold text-black">
266 Due today
267 </span>
268 <span className="font-bold text-black">
269 ${dueNow.toFixed(2)}
270 </span>
271 </div>
272 </div>
273
274 <Button className="shadow-gray mb-4 w-full rounded-lg px-4 py-3 font-semibold text-white shadow-lg transition-all">
275 Complete Upgrade
276 </Button>
277
278 <p className="text-gray flex items-center justify-center gap-1 text-center text-xs">
279 <CheckCircle className="size-4" />
280 Instant activation after payment
281 </p>
282 </div>
283 </div>
284 </CardContent>
285 </Card>
286 </>
287 )
288}
2891'use client'
2
3import React, { useState } from 'react'
4import { Bell, X } from 'lucide-react'
5import { toast } from 'sonner'
6
7import { Button } from '@/components/ui/button'
8import {
9 Card,
10 CardContent,
11 CardDescription,
12 CardFooter,
13 CardHeader,
14 CardTitle,
15} from '@/components/ui/card'
16import { Slider } from '@/components/ui/slider'
17
18export function NotificationPreference() {
19 const [emailFrequency, setEmailFrequency] = useState(15)
20
21 const calculateCost = (frequency: number) => {
22 const baseCost = 5.99
23 const additionalCost = (frequency / 30) * 14.01
24 return (baseCost + additionalCost).toFixed(2)
25 }
26
27 const onSubmit = () => {
28 toast.success(
29 `Preferences saved! You'll receive ${emailFrequency} emails per month.`,
30 )
31 }
32
33 return (
34 <Card>
35 <CardHeader className="flex flex-wrap items-center justify-between gap-3 sm:gap-4">
36 <div className="flex items-center gap-3 sm:gap-4">
37 <div className="text-gray hidden h-9 w-9 items-center justify-center rounded-full border md:flex">
38 <Bell className="text-gray h-5 w-5" />
39 </div>
40 <div className="flex flex-col space-y-2">
41 <CardTitle>Email Digest Preferences</CardTitle>
42 <CardDescription>
43 Customize how often you receive update emails.
44 </CardDescription>
45 </div>
46 </div>
47 <Button variant="ghost" size="icon">
48 <X className="h-5 w-5" />
49 </Button>
50 </CardHeader>
51
52 <CardContent>
53 <div className="p-6">
54 <div className="mb-6 text-center">
55 <p className="mb-2 text-xs font-semibold text-black uppercase">
56 Monthly Email Frequency
57 </p>
58 <div className="mb-1 text-5xl font-bold text-black">
59 {emailFrequency}
60 </div>
61 <p className="text-sm text-black">emails per month</p>
62 </div>
63
64 <div className="mb-6">
65 <div className="relative mb-3">
66 <Slider
67 value={[emailFrequency]}
68 min={0}
69 max={30}
70 step={1}
71 onValueChange={(v) => setEmailFrequency(v[0])}
72 className="w-full"
73 />
74
75 <div
76 className="pointer-events-none absolute top-0 h-4 w-4 -translate-y-[6px] rounded-full shadow-lg transition-all"
77 style={{
78 left: `calc(${(emailFrequency / 30) * 100}% - 8px)`,
79 }}
80 />
81 </div>
82
83 <div className="text-gray flex justify-between px-1 text-xs">
84 {[0, 5, 10, 15, 20, 25, 30].map((mark) => (
85 <span key={mark}>{mark}</span>
86 ))}
87 </div>
88 </div>
89
90 <div className="rounded-lg p-4">
91 <div className="text-gray flex items-center gap-2 text-sm">
92 <span>
93 You will be charged{' '}
94 <span className="text-gray font-bold">
95 ${calculateCost(emailFrequency)}
96 </span>{' '}
97 to receive {emailFrequency} monthly digest
98 emails.
99 </span>
100 </div>
101 </div>
102
103 {emailFrequency >= 20 && (
104 <div className="border-secondary rounded-lg border p-3">
105 <p className="text-secondary text-xs">
106 <span className="font-semibold">💡 Tip:</span>{' '}
107 Consider reducing frequency to save costs while
108 staying informed.
109 </p>
110 </div>
111 )}
112
113 {emailFrequency === 0 && (
114 <div className="border-warning rounded-lg border p-3">
115 <p className="text-warning text-xs">
116 <span className="font-semibold">
117 ⚠️ Warning:
118 </span>{' '}
119 You won't receive any email updates.
120 Important notifications may be missed.
121 </p>
122 </div>
123 )}
124 </div>
125 </CardContent>
126
127 <CardFooter className="gap-2">
128 <Button variant="outline" className="w-1/2">
129 Cancel
130 </Button>
131 <Button onClick={onSubmit} className="w-1/2">
132 Continue
133 </Button>
134 </CardFooter>
135 </Card>
136 )
137}
138Monthly Email Frequency
emails per month
1'use client'
2
3import React, { useState } from 'react'
4import { Bell, X } from 'lucide-react'
5import { toast } from 'sonner'
6
7import { Button } from '@/components/ui/button'
8import {
9 Card,
10 CardContent,
11 CardDescription,
12 CardFooter,
13 CardHeader,
14 CardTitle,
15} from '@/components/ui/card'
16import { Slider } from '@/components/ui/slider'
17
18export function NotificationPreference() {
19 const [emailFrequency, setEmailFrequency] = useState(15)
20
21 const calculateCost = (frequency: number) => {
22 const baseCost = 5.99
23 const additionalCost = (frequency / 30) * 14.01
24 return (baseCost + additionalCost).toFixed(2)
25 }
26
27 const onSubmit = () => {
28 toast.success(
29 `Preferences saved! You'll receive ${emailFrequency} emails per month.`,
30 )
31 }
32
33 return (
34 <Card>
35 <CardHeader className="flex flex-wrap items-center justify-between gap-3 sm:gap-4">
36 <div className="flex items-center gap-3 sm:gap-4">
37 <div className="text-gray hidden h-9 w-9 items-center justify-center rounded-full border md:flex">
38 <Bell className="text-gray h-5 w-5" />
39 </div>
40 <div className="flex flex-col space-y-2">
41 <CardTitle>Email Digest Preferences</CardTitle>
42 <CardDescription>
43 Customize how often you receive update emails.
44 </CardDescription>
45 </div>
46 </div>
47 <Button variant="ghost" size="icon">
48 <X className="h-5 w-5" />
49 </Button>
50 </CardHeader>
51
52 <CardContent>
53 <div className="p-6">
54 <div className="mb-6 text-center">
55 <p className="mb-2 text-xs font-semibold text-black uppercase">
56 Monthly Email Frequency
57 </p>
58 <div className="mb-1 text-5xl font-bold text-black">
59 {emailFrequency}
60 </div>
61 <p className="text-sm text-black">emails per month</p>
62 </div>
63
64 <div className="mb-6">
65 <div className="relative mb-3">
66 <Slider
67 value={[emailFrequency]}
68 min={0}
69 max={30}
70 step={1}
71 onValueChange={(v) => setEmailFrequency(v[0])}
72 className="w-full"
73 />
74
75 <div
76 className="pointer-events-none absolute top-0 h-4 w-4 -translate-y-[6px] rounded-full shadow-lg transition-all"
77 style={{
78 left: `calc(${(emailFrequency / 30) * 100}% - 8px)`,
79 }}
80 />
81 </div>
82
83 <div className="text-gray flex justify-between px-1 text-xs">
84 {[0, 5, 10, 15, 20, 25, 30].map((mark) => (
85 <span key={mark}>{mark}</span>
86 ))}
87 </div>
88 </div>
89
90 <div className="rounded-lg p-4">
91 <div className="text-gray flex items-center gap-2 text-sm">
92 <span>
93 You will be charged{' '}
94 <span className="text-gray font-bold">
95 ${calculateCost(emailFrequency)}
96 </span>{' '}
97 to receive {emailFrequency} monthly digest
98 emails.
99 </span>
100 </div>
101 </div>
102
103 {emailFrequency >= 20 && (
104 <div className="border-secondary rounded-lg border p-3">
105 <p className="text-secondary text-xs">
106 <span className="font-semibold">💡 Tip:</span>{' '}
107 Consider reducing frequency to save costs while
108 staying informed.
109 </p>
110 </div>
111 )}
112
113 {emailFrequency === 0 && (
114 <div className="border-warning rounded-lg border p-3">
115 <p className="text-warning text-xs">
116 <span className="font-semibold">
117 ⚠️ Warning:
118 </span>{' '}
119 You won't receive any email updates.
120 Important notifications may be missed.
121 </p>
122 </div>
123 )}
124 </div>
125 </CardContent>
126
127 <CardFooter className="gap-2">
128 <Button variant="outline" className="w-1/2">
129 Cancel
130 </Button>
131 <Button onClick={onSubmit} className="w-1/2">
132 Continue
133 </Button>
134 </CardFooter>
135 </Card>
136 )
137}
138