@instockng/storefront-ui 1.0.55 → 1.0.56
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/dist/components/ProductAddOns.d.ts +22 -0
- package/dist/components/ProductAddOns.d.ts.map +1 -0
- package/dist/components/ui/switch.d.ts +8 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/index.mjs +58 -56
- package/dist/index101.mjs +1 -1
- package/dist/index102.mjs +1 -1
- package/dist/index103.mjs +6 -6
- package/dist/index104.mjs +4 -4
- package/dist/index105.mjs +3 -3
- package/dist/index106.mjs +1 -1
- package/dist/index107.mjs +1 -1
- package/dist/index108.mjs +1 -1
- package/dist/index109.mjs +2 -2
- package/dist/index110.mjs +2 -2
- package/dist/index111.mjs +3 -3
- package/dist/index112.mjs +1 -1
- package/dist/index113.mjs +3 -3
- package/dist/index114.mjs +2 -2
- package/dist/index115.mjs +2 -2
- package/dist/index116.mjs +1 -1
- package/dist/index117.mjs +3 -3
- package/dist/index118.mjs +1 -1
- package/dist/index119.mjs +1 -1
- package/dist/index120.mjs +3 -3
- package/dist/index121.mjs +2 -2
- package/dist/index122.mjs +4 -4
- package/dist/index123.mjs +1 -1
- package/dist/index124.mjs +3 -3
- package/dist/index125.mjs +4 -4
- package/dist/index126.mjs +1 -1
- package/dist/index127.mjs +1 -1
- package/dist/index128.mjs +1 -1
- package/dist/index129.mjs +1 -1
- package/dist/index130.mjs +1 -1
- package/dist/index131.mjs +1 -1
- package/dist/index132.mjs +1 -1
- package/dist/index133.mjs +2 -2
- package/dist/index134.mjs +3 -3
- package/dist/index137.mjs +1 -1
- package/dist/index139.mjs +21 -2
- package/dist/index140.mjs +56 -2
- package/dist/index141.mjs +29 -2
- package/dist/index142.mjs +7 -2
- package/dist/index143.mjs +50 -16
- package/dist/index144.mjs +6 -32
- package/dist/index145.mjs +11 -37
- package/dist/index146.mjs +7 -18
- package/dist/index147.mjs +28 -18
- package/dist/index148.mjs +2 -2
- package/dist/index149.mjs +70 -20
- package/dist/index150.mjs +163 -42
- package/dist/index151.mjs +2 -2
- package/dist/index152.mjs +2 -30
- package/dist/index153.mjs +2 -18
- package/dist/index154.mjs +18 -2
- package/dist/index155.mjs +32 -2
- package/dist/index156.mjs +38 -2
- package/dist/index157.mjs +18 -2
- package/dist/index158.mjs +13 -67
- package/dist/index159.mjs +2 -2
- package/dist/index160.mjs +15 -48
- package/dist/index161.mjs +46 -2
- package/dist/index162.mjs +2 -36
- package/dist/index163.mjs +20 -142
- package/dist/index164.mjs +18 -2
- package/dist/index165.mjs +2 -2
- package/dist/index166.mjs +2 -19
- package/dist/index168.mjs +2 -2
- package/dist/index169.mjs +64 -18
- package/dist/index170.mjs +2 -2
- package/dist/index171.mjs +53 -2
- package/dist/index172.mjs +2 -24
- package/dist/index173.mjs +28 -15
- package/dist/index174.mjs +152 -2
- package/dist/index175.mjs +2 -2
- package/dist/index176.mjs +2 -2
- package/dist/index177.mjs +10 -14
- package/dist/index178.mjs +2 -2
- package/dist/index179.mjs +2 -23
- package/dist/index180.mjs +26 -2
- package/dist/index181.mjs +2 -2
- package/dist/index182.mjs +2 -2
- package/dist/index183.mjs +17 -16
- package/dist/index184.mjs +23 -2
- package/dist/index185.mjs +2 -23
- package/dist/index186.mjs +2 -2
- package/dist/index187.mjs +2 -2
- package/dist/index188.mjs +23 -2
- package/dist/index189.mjs +2 -2
- package/dist/index190.mjs +23 -2
- package/dist/index191.mjs +2 -2
- package/dist/index192.mjs +2 -2
- package/dist/index193.mjs +2 -2
- package/dist/index194.mjs +20 -124
- package/dist/index195.mjs +2 -2
- package/dist/index196.mjs +20 -71
- package/dist/index197.mjs +2 -74
- package/dist/index198.mjs +2 -21
- package/dist/index199.mjs +2 -56
- package/dist/index20.mjs +24 -22
- package/dist/index200.mjs +2 -29
- package/dist/index201.mjs +2 -7
- package/dist/index202.mjs +2 -52
- package/dist/index203.mjs +2 -6
- package/dist/index204.mjs +2 -12
- package/dist/index205.mjs +127 -7
- package/dist/index206.mjs +2 -28
- package/dist/index207.mjs +74 -2
- package/dist/index208.mjs +73 -69
- package/dist/index209.mjs +2 -167
- package/dist/index210.mjs +31 -2
- package/dist/index211.mjs +11 -2
- package/dist/index212.mjs +4 -2
- package/dist/index213.mjs +4 -2
- package/dist/index214.mjs +13 -2
- package/dist/index215.mjs +7 -2
- package/dist/index216.mjs +11 -107
- package/dist/index217.mjs +5 -2
- package/dist/index218.mjs +33 -2
- package/dist/index219.mjs +29 -35
- package/dist/index220.mjs +28 -2
- package/dist/index221.mjs +58 -241
- package/dist/index222.mjs +2 -2
- package/dist/index223.mjs +103 -28
- package/dist/index224.mjs +2 -65
- package/dist/index225.mjs +2 -25
- package/dist/index226.mjs +2 -2
- package/dist/index227.mjs +2 -2
- package/dist/index228.mjs +2 -2
- package/dist/index229.mjs +2 -2
- package/dist/index230.mjs +2 -2
- package/dist/index232.mjs +2 -2
- package/dist/index233.mjs +37 -2
- package/dist/index235.mjs +244 -2
- package/dist/index236.mjs +2 -2
- package/dist/index237.mjs +33 -2
- package/dist/index238.mjs +65 -4
- package/dist/index239.mjs +25 -2
- package/dist/index24.mjs +28 -26
- package/dist/index240.mjs +2 -2
- package/dist/index241.mjs +2 -31
- package/dist/index242.mjs +2 -11
- package/dist/index243.mjs +2 -4
- package/dist/index244.mjs +2 -4
- package/dist/index245.mjs +2 -13
- package/dist/index246.mjs +2 -7
- package/dist/index247.mjs +2 -12
- package/dist/index248.mjs +2 -5
- package/dist/index249.mjs +2 -33
- package/dist/index25.mjs +18 -8
- package/dist/index250.mjs +4 -31
- package/dist/index251.mjs +2 -28
- package/dist/index252.mjs +2 -61
- package/dist/index253.mjs +3 -2
- package/dist/index254.mjs +2 -2
- package/dist/index255.mjs +2 -18
- package/dist/index256.mjs +16 -46
- package/dist/index257.mjs +13 -2
- package/dist/index258.mjs +6 -2
- package/dist/index259.mjs +30 -2
- package/dist/index261.mjs +2 -91
- package/dist/index262.mjs +2 -2
- package/dist/index263.mjs +2 -3
- package/dist/index264.mjs +2 -2
- package/dist/index265.mjs +91 -2
- package/dist/index266.mjs +2 -17
- package/dist/index267.mjs +17 -12
- package/dist/index268.mjs +47 -6
- package/dist/index269.mjs +2 -30
- package/dist/index30.mjs +6 -4
- package/dist/index31.mjs +13 -12
- package/dist/index32.mjs +56 -54
- package/dist/index33.mjs +1 -1
- package/dist/index34.mjs +1 -1
- package/dist/index35.mjs +20 -12
- package/dist/index36.mjs +1 -1
- package/dist/index37.mjs +1 -1
- package/dist/index40.mjs +1 -1
- package/dist/index43.mjs +3 -3
- package/dist/index52.mjs +17 -17
- package/dist/index53.mjs +2 -2
- package/dist/index54.mjs +1 -1
- package/dist/index55.mjs +1 -1
- package/dist/index56.mjs +68 -38
- package/dist/index57.mjs +1 -1
- package/dist/index58.mjs +1 -1
- package/dist/index59.mjs +1 -1
- package/dist/index60.mjs +1 -1
- package/dist/index61.mjs +1 -1
- package/dist/index62.mjs +1 -1
- package/dist/index63.mjs +1 -1
- package/dist/index64.mjs +1 -1
- package/dist/index67.mjs +4 -152
- package/dist/index69.mjs +149 -71
- package/dist/index70.mjs +74 -14
- package/dist/index71.mjs +14 -62
- package/dist/index72.mjs +62 -4
- package/dist/index78.mjs +1 -1
- package/dist/index80.mjs +235 -2
- package/dist/index81.mjs +6 -34
- package/dist/index82.mjs +130 -39
- package/dist/index83.mjs +62 -229
- package/dist/index84.mjs +86 -5
- package/dist/index85.mjs +26 -131
- package/dist/index86.mjs +8 -67
- package/dist/index87.mjs +71 -83
- package/dist/index88.mjs +3 -28
- package/dist/index89.mjs +2 -9
- package/dist/index90.mjs +78 -70
- package/dist/index91.mjs +53 -3
- package/dist/index92.mjs +6 -2
- package/dist/index93.mjs +4 -82
- package/dist/index94.mjs +176 -51
- package/dist/index95.mjs +53 -6
- package/dist/index96.mjs +69 -5
- package/dist/index97.mjs +34 -179
- package/dist/index98.mjs +41 -51
- package/dist/index99.mjs +2 -69
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
- package/src/components/Checkout.stories.tsx +9 -4
- package/src/components/ProductAddOns.stories.tsx +109 -0
- package/src/components/ProductAddOns.tsx +242 -0
- package/src/components/ShoppingCart.stories.tsx +4 -1
- package/src/components/ui/switch.tsx +37 -0
- package/src/lib/utils.ts +3 -1
- package/src/test-utils/MockCartProvider.tsx +14 -7
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useRef } from 'react';
|
|
4
|
+
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
|
5
|
+
import { Button } from './ui/button';
|
|
6
|
+
import { Switch } from './ui/switch';
|
|
7
|
+
import { Badge } from './ui/badge';
|
|
8
|
+
import { cn, formatCurrency } from '../lib/utils';
|
|
9
|
+
import { ProductCardProduct } from './ProductCard';
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
import { useGetProductAddOns } from '@instockng/api-client';
|
|
12
|
+
import { useCart } from '../contexts/CartContext';
|
|
13
|
+
|
|
14
|
+
export interface AddOnProduct extends ProductCardProduct {
|
|
15
|
+
slug: string;
|
|
16
|
+
compareAtPrice?: number | null;
|
|
17
|
+
addonDescription?: string | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ProductAddOnsProps {
|
|
21
|
+
/** Product slug to fetch add-ons for */
|
|
22
|
+
slug?: string;
|
|
23
|
+
/** Manual product list (overrides slug fetching) */
|
|
24
|
+
products?: AddOnProduct[];
|
|
25
|
+
/** Manual toggle handler */
|
|
26
|
+
onToggle?: (product: AddOnProduct, isSelected: boolean) => void;
|
|
27
|
+
/** Manual selection list (list of product IDs) */
|
|
28
|
+
selectedProductIds?: string[];
|
|
29
|
+
className?: string;
|
|
30
|
+
title?: string;
|
|
31
|
+
limit?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function ProductAddOns({
|
|
35
|
+
slug,
|
|
36
|
+
products: manualProducts,
|
|
37
|
+
onToggle: manualOnToggle,
|
|
38
|
+
selectedProductIds: manualSelectedIds,
|
|
39
|
+
className,
|
|
40
|
+
title = 'Popular add-ons',
|
|
41
|
+
limit = 6,
|
|
42
|
+
}: ProductAddOnsProps) {
|
|
43
|
+
const { cart, addItem, removeItem } = useCart();
|
|
44
|
+
|
|
45
|
+
// Fetch products if slug is provided
|
|
46
|
+
const { data: fetchedProducts, isLoading } = useGetProductAddOns(slug || '', limit, {
|
|
47
|
+
enabled: !!slug && !manualProducts
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const products = manualProducts || fetchedProducts || [];
|
|
51
|
+
|
|
52
|
+
// Calculate selected IDs if not provided manually
|
|
53
|
+
const selectedProductIds = manualSelectedIds || products
|
|
54
|
+
.filter((addon: AddOnProduct) => cart?.items.some(item => item.variant.id === addon.variants[0]?.id))
|
|
55
|
+
.map((addon: AddOnProduct) => addon.id);
|
|
56
|
+
|
|
57
|
+
// Internal toggle logic
|
|
58
|
+
const handleToggle = async (product: AddOnProduct, isSelected: boolean) => {
|
|
59
|
+
if (manualOnToggle) {
|
|
60
|
+
manualOnToggle(product, isSelected);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const variant = product.variants[0];
|
|
65
|
+
if (!variant) return;
|
|
66
|
+
|
|
67
|
+
if (isSelected) {
|
|
68
|
+
await addItem(
|
|
69
|
+
product.slug,
|
|
70
|
+
product.name,
|
|
71
|
+
variant.price,
|
|
72
|
+
variant.sku,
|
|
73
|
+
1
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
const cartItem = cart?.items.find(item => item.variant.id === variant.id);
|
|
77
|
+
if (cartItem) {
|
|
78
|
+
await removeItem(cartItem.id);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
84
|
+
const [scrollProgress, setScrollProgress] = useState(0);
|
|
85
|
+
|
|
86
|
+
const handleScroll = () => {
|
|
87
|
+
if (scrollContainerRef.current) {
|
|
88
|
+
const { scrollLeft, scrollWidth, clientWidth } = scrollContainerRef.current;
|
|
89
|
+
const progress = scrollLeft / (scrollWidth - clientWidth);
|
|
90
|
+
setScrollProgress(progress || 0);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const scroll = (direction: 'left' | 'right') => {
|
|
95
|
+
if (scrollContainerRef.current) {
|
|
96
|
+
const { clientWidth } = scrollContainerRef.current;
|
|
97
|
+
const scrollAmount = clientWidth * 0.9;
|
|
98
|
+
scrollContainerRef.current.scrollBy({
|
|
99
|
+
left: direction === 'left' ? -scrollAmount : scrollAmount,
|
|
100
|
+
behavior: 'smooth',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if ((!products || products.length === 0) && !isLoading) return null;
|
|
106
|
+
|
|
107
|
+
if (isLoading) {
|
|
108
|
+
return (
|
|
109
|
+
<div className={cn('w-full py-8 space-y-4 animate-pulse', className)}>
|
|
110
|
+
<div className="h-4 w-32 bg-gray-200 rounded px-1 mb-6" />
|
|
111
|
+
<div className="flex gap-4 overflow-x-hidden">
|
|
112
|
+
<div className="min-w-[340px] h-[148px] bg-gray-100 rounded-[2rem]" />
|
|
113
|
+
<div className="min-w-[340px] h-[148px] bg-gray-100 rounded-[2rem]" />
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div className={cn('w-full py-8', className)}>
|
|
121
|
+
<div className="px-1 mb-3">
|
|
122
|
+
<h3 className="text-lg font-bold tracking-tight text-gray-900">{title}</h3>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div className="relative">
|
|
126
|
+
<div
|
|
127
|
+
ref={scrollContainerRef}
|
|
128
|
+
onScroll={handleScroll}
|
|
129
|
+
className="flex gap-4 overflow-x-auto scrollbar-hide snap-x snap-mandatory px-1"
|
|
130
|
+
style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
|
|
131
|
+
>
|
|
132
|
+
{products.map((product: AddOnProduct) => {
|
|
133
|
+
const isSelected = selectedProductIds.includes(product.id);
|
|
134
|
+
const variant = product.variants[0];
|
|
135
|
+
const price = variant?.price || 0;
|
|
136
|
+
const compareAtPrice = product.compareAtPrice || (variant as any).compareAtPrice;
|
|
137
|
+
const hasDiscount = compareAtPrice && compareAtPrice > price;
|
|
138
|
+
const discountPercent = hasDiscount
|
|
139
|
+
? Math.round(((compareAtPrice - price) / compareAtPrice) * 100)
|
|
140
|
+
: 0;
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<div
|
|
144
|
+
key={product.id}
|
|
145
|
+
className="relative flex min-w-[280px] md:min-w-[420px] snap-start items-center gap-4 rounded-xl bg-[#EBEBEB] p-3 transition-all"
|
|
146
|
+
>
|
|
147
|
+
{/* Image Container */}
|
|
148
|
+
<div className="h-20 w-20 flex-shrink-0 overflow-hidden rounded-xl bg-white/40 flex items-center justify-center">
|
|
149
|
+
{product.imageUrl ? (
|
|
150
|
+
<img
|
|
151
|
+
src={product.imageUrl}
|
|
152
|
+
alt={product.name}
|
|
153
|
+
className="h-full w-full object-cover"
|
|
154
|
+
/>
|
|
155
|
+
) : (
|
|
156
|
+
<div className="flex h-full w-full items-center justify-center text-[10px] text-gray-400">No Img</div>
|
|
157
|
+
)}
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
{/* Content Info */}
|
|
161
|
+
<div className="flex flex-1 flex-col justify-center min-w-0">
|
|
162
|
+
<h4 className="text-md font-bold text-[#333] truncate pr-2" title={product.name}>
|
|
163
|
+
{product.name}
|
|
164
|
+
</h4>
|
|
165
|
+
|
|
166
|
+
{product.addonDescription && (
|
|
167
|
+
<p className="text-[11px] text-gray-500 line-clamp-2 italic mb-1">
|
|
168
|
+
{product.addonDescription}
|
|
169
|
+
</p>
|
|
170
|
+
)}
|
|
171
|
+
|
|
172
|
+
<div className="flex items-center gap-2 mt-1">
|
|
173
|
+
<span className="text-md font-bold text-[#333]">
|
|
174
|
+
{formatCurrency(price)}
|
|
175
|
+
</span>
|
|
176
|
+
{hasDiscount && (
|
|
177
|
+
<>
|
|
178
|
+
<span className="text-sm text-[#DC143C] line-through">
|
|
179
|
+
{formatCurrency(compareAtPrice)}
|
|
180
|
+
</span>
|
|
181
|
+
<Badge className="bg-[#DC143C]/10 hover:bg-[#DC143C]/20 text-[#DC143C] border-none text-[10px] font-bold px-2 py-0.5 rounded-lg uppercase tracking-tight">
|
|
182
|
+
{discountPercent}% OFF
|
|
183
|
+
</Badge>
|
|
184
|
+
</>
|
|
185
|
+
)}
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
{/* Toggle Switch */}
|
|
190
|
+
<div className="pl-2">
|
|
191
|
+
<Switch
|
|
192
|
+
checked={isSelected}
|
|
193
|
+
// @ts-ignore
|
|
194
|
+
onCheckedChange={(checked) => handleToggle(product, checked)}
|
|
195
|
+
className="scale-125"
|
|
196
|
+
/>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
})}
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* Bottom Controls */}
|
|
205
|
+
<div className="flex items-center justify-between px-2 mt-2">
|
|
206
|
+
{/* Pagination Dots */}
|
|
207
|
+
<div className="flex gap-1.5 focus:outline-none">
|
|
208
|
+
{Array.from({ length: Math.min(products.length, 6) }).map((_, i) => {
|
|
209
|
+
const isActive = Math.round(scrollProgress * (Math.min(products.length, 6) - 1)) === i;
|
|
210
|
+
return (
|
|
211
|
+
<div
|
|
212
|
+
key={i}
|
|
213
|
+
className={cn(
|
|
214
|
+
"h-2 w-2 rounded-full transition-all duration-300",
|
|
215
|
+
isActive ? "bg-gray-600 scale-110" : "bg-gray-300"
|
|
216
|
+
)}
|
|
217
|
+
/>
|
|
218
|
+
);
|
|
219
|
+
})}
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
{/* Navigation Arrows */}
|
|
223
|
+
<div className="flex gap-4">
|
|
224
|
+
<button
|
|
225
|
+
onClick={() => scroll('left')}
|
|
226
|
+
className="text-gray-400 hover:text-gray-900 transition-colors"
|
|
227
|
+
aria-label="Previous"
|
|
228
|
+
>
|
|
229
|
+
<ChevronLeft className="h-6 w-6 stroke-[2.5px]" />
|
|
230
|
+
</button>
|
|
231
|
+
<button
|
|
232
|
+
onClick={() => scroll('right')}
|
|
233
|
+
className="text-gray-400 hover:text-gray-900 transition-colors"
|
|
234
|
+
aria-label="Next"
|
|
235
|
+
>
|
|
236
|
+
<ChevronRight className="h-6 w-6 stroke-[2.5px]" />
|
|
237
|
+
</button>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
@@ -50,9 +50,12 @@ const mockCart: Cart = {
|
|
|
50
50
|
logoUrl: 'https://images.unsplash.com/photo-1599305445671-ac291c95aaa9?w=200&h=80&fit=crop',
|
|
51
51
|
siteUrl: 'https://demo-store.com',
|
|
52
52
|
domain: 'f',
|
|
53
|
+
metaPixelId: null as any,
|
|
54
|
+
tiktokPixelId: null as any,
|
|
55
|
+
paystackPublicKey: null as any,
|
|
56
|
+
paystackSecretKey: null as any,
|
|
53
57
|
createdAt: new Date().toISOString(),
|
|
54
58
|
updatedAt: new Date().toISOString(),
|
|
55
|
-
metaPixelId: null as any,
|
|
56
59
|
deletedAt: null as any,
|
|
57
60
|
},
|
|
58
61
|
customerPhone: null as any,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
|
|
6
|
+
interface SwitchProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
7
|
+
checked?: boolean;
|
|
8
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
|
|
12
|
+
({ className, checked, onCheckedChange, disabled, ...props }, ref) => {
|
|
13
|
+
return (
|
|
14
|
+
<label
|
|
15
|
+
className={cn(
|
|
16
|
+
'inline-flex cursor-pointer items-center',
|
|
17
|
+
disabled && 'cursor-not-allowed opacity-50',
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
>
|
|
21
|
+
<input
|
|
22
|
+
type="checkbox"
|
|
23
|
+
className="peer sr-only"
|
|
24
|
+
ref={ref}
|
|
25
|
+
checked={checked}
|
|
26
|
+
onChange={(e) => onCheckedChange?.(e.target.checked)}
|
|
27
|
+
disabled={disabled}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
<div className="peer h-6 w-11 rounded-full bg-[#ccc] after:absolute after:start-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:bg-white after:transition-all after:content-[''] peer-checked:bg-accent-600 peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 dark:border-accent-600 dark:bg-[#ccc] dark:peer-focus:ring-blue-800 rtl:peer-checked:after:-translate-x-full"></div>
|
|
31
|
+
</label>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
Switch.displayName = 'Switch';
|
|
36
|
+
|
|
37
|
+
export { Switch };
|
package/src/lib/utils.ts
CHANGED
|
@@ -6,9 +6,11 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function formatCurrency(amount: number): string {
|
|
9
|
-
return new Intl.NumberFormat('en-
|
|
9
|
+
return new Intl.NumberFormat('en-NG', {
|
|
10
10
|
style: 'currency',
|
|
11
11
|
currency: 'NGN',
|
|
12
|
+
minimumFractionDigits: 0,
|
|
13
|
+
maximumFractionDigits: 0,
|
|
12
14
|
}).format(amount)
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -24,6 +24,9 @@ const mockCart: Cart = {
|
|
|
24
24
|
siteUrl: 'https://demo-store.com',
|
|
25
25
|
domain: 'f',
|
|
26
26
|
metaPixelId: null as any,
|
|
27
|
+
tiktokPixelId: null as any,
|
|
28
|
+
paystackPublicKey: null as any,
|
|
29
|
+
paystackSecretKey: null as any,
|
|
27
30
|
createdAt: new Date().toISOString(),
|
|
28
31
|
updatedAt: new Date().toISOString(),
|
|
29
32
|
deletedAt: null as any,
|
|
@@ -53,6 +56,7 @@ const mockCart: Cart = {
|
|
|
53
56
|
createdAt: new Date().toISOString(),
|
|
54
57
|
updatedAt: new Date().toISOString(),
|
|
55
58
|
deletedAt: null as any,
|
|
59
|
+
compareAtPrice: "0",
|
|
56
60
|
product: {
|
|
57
61
|
id: 'product-1',
|
|
58
62
|
name: 'Premium Cotton T-Shirt',
|
|
@@ -64,9 +68,9 @@ const mockCart: Cart = {
|
|
|
64
68
|
createdAt: new Date().toISOString(),
|
|
65
69
|
updatedAt: new Date().toISOString(),
|
|
66
70
|
deletedAt: null as any,
|
|
67
|
-
quantityDiscounts:
|
|
71
|
+
quantityDiscounts: [] as any,
|
|
68
72
|
},
|
|
69
|
-
},
|
|
73
|
+
} as any,
|
|
70
74
|
quantity: 2,
|
|
71
75
|
basePrice: 29.99,
|
|
72
76
|
discountPercent: 0,
|
|
@@ -87,7 +91,8 @@ const mockCart: Cart = {
|
|
|
87
91
|
isActive: true,
|
|
88
92
|
createdAt: new Date().toISOString(),
|
|
89
93
|
updatedAt: new Date().toISOString(),
|
|
90
|
-
deletedAt: null,
|
|
94
|
+
deletedAt: null as any,
|
|
95
|
+
compareAtPrice: "0",
|
|
91
96
|
product: {
|
|
92
97
|
id: 'product-2',
|
|
93
98
|
name: 'Slim Fit Jeans',
|
|
@@ -98,10 +103,10 @@ const mockCart: Cart = {
|
|
|
98
103
|
thumbnailUrl: 'https://images.unsplash.com/photo-1542272604-787c3835535d?w=400',
|
|
99
104
|
createdAt: new Date().toISOString(),
|
|
100
105
|
updatedAt: new Date().toISOString(),
|
|
101
|
-
deletedAt: null,
|
|
102
|
-
quantityDiscounts:
|
|
106
|
+
deletedAt: null as any,
|
|
107
|
+
quantityDiscounts: [] as any,
|
|
103
108
|
},
|
|
104
|
-
},
|
|
109
|
+
} as any,
|
|
105
110
|
quantity: 1,
|
|
106
111
|
basePrice: 34.99,
|
|
107
112
|
discountPercent: 0,
|
|
@@ -191,7 +196,7 @@ export function MockCartProvider({
|
|
|
191
196
|
isSuccess: false,
|
|
192
197
|
data: undefined,
|
|
193
198
|
error: null,
|
|
194
|
-
reset: () => {},
|
|
199
|
+
reset: () => { },
|
|
195
200
|
status: 'idle' as const,
|
|
196
201
|
failureCount: 0,
|
|
197
202
|
failureReason: null,
|
|
@@ -244,6 +249,8 @@ export function MockCartProvider({
|
|
|
244
249
|
brandId: cart.brand.id,
|
|
245
250
|
deliveryCost: 1500,
|
|
246
251
|
freeShippingThreshold: null as any,
|
|
252
|
+
noteTitle: null as any,
|
|
253
|
+
noteContent: null as any,
|
|
247
254
|
allowCOD: true,
|
|
248
255
|
allowOnline: true,
|
|
249
256
|
waybillOnly: false,
|