hey-pharmacist-ecommerce 1.1.12 → 1.1.14

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.
Files changed (49) hide show
  1. package/dist/index.d.mts +2 -4
  2. package/dist/index.d.ts +2 -4
  3. package/dist/index.js +1123 -972
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1123 -971
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +3 -3
  8. package/src/components/AccountAddressesTab.tsx +209 -0
  9. package/src/components/AccountOrdersTab.tsx +151 -0
  10. package/src/components/AccountOverviewTab.tsx +209 -0
  11. package/src/components/AccountPaymentTab.tsx +116 -0
  12. package/src/components/AccountSavedItemsTab.tsx +76 -0
  13. package/src/components/AccountSettingsTab.tsx +116 -0
  14. package/src/components/AddressFormModal.tsx +23 -10
  15. package/src/components/CartItem.tsx +60 -56
  16. package/src/components/FilterChips.tsx +54 -80
  17. package/src/components/Header.tsx +69 -16
  18. package/src/components/Notification.tsx +148 -0
  19. package/src/components/OrderCard.tsx +89 -56
  20. package/src/components/ProductCard.tsx +215 -178
  21. package/src/components/QuickViewModal.tsx +314 -0
  22. package/src/components/TabNavigation.tsx +48 -0
  23. package/src/components/ui/Button.tsx +1 -1
  24. package/src/components/ui/ConfirmModal.tsx +84 -0
  25. package/src/hooks/useOrders.ts +1 -0
  26. package/src/hooks/usePaymentMethods.ts +58 -0
  27. package/src/index.ts +0 -1
  28. package/src/providers/CartProvider.tsx +22 -6
  29. package/src/providers/EcommerceProvider.tsx +8 -7
  30. package/src/providers/FavoritesProvider.tsx +10 -3
  31. package/src/providers/NotificationProvider.tsx +79 -0
  32. package/src/providers/WishlistProvider.tsx +34 -9
  33. package/src/screens/AddressesScreen.tsx +72 -61
  34. package/src/screens/CartScreen.tsx +48 -32
  35. package/src/screens/ChangePasswordScreen.tsx +155 -0
  36. package/src/screens/CheckoutScreen.tsx +162 -125
  37. package/src/screens/EditProfileScreen.tsx +165 -0
  38. package/src/screens/LoginScreen.tsx +59 -72
  39. package/src/screens/NewAddressScreen.tsx +16 -10
  40. package/src/screens/OrdersScreen.tsx +91 -148
  41. package/src/screens/ProductDetailScreen.tsx +334 -234
  42. package/src/screens/ProfileScreen.tsx +190 -200
  43. package/src/screens/RegisterScreen.tsx +51 -70
  44. package/src/screens/SearchResultsScreen.tsx +2 -1
  45. package/src/screens/ShopScreen.tsx +260 -384
  46. package/src/screens/WishlistScreen.tsx +226 -224
  47. package/src/styles/globals.css +9 -0
  48. package/src/screens/CategoriesScreen.tsx +0 -122
  49. package/src/screens/HomeScreen.tsx +0 -211
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { CreditCard, Plus, Trash2 } from 'lucide-react';
5
+ import { usePaymentMethods } from '@/hooks/usePaymentMethods';
6
+ import { EmptyState } from './EmptyState';
7
+ import { Button } from './ui/Button';
8
+ import { Badge } from './ui/Badge';
9
+
10
+ export function AccountPaymentTab() {
11
+ const { paymentMethods, isLoading, error, deletePaymentMethod } = usePaymentMethods();
12
+ const [deletingId, setDeletingId] = useState<string | null>(null);
13
+
14
+ const handleDelete = async (paymentMethodId: string) => {
15
+ if (!confirm('Are you sure you want to remove this payment method?')) return;
16
+
17
+ setDeletingId(paymentMethodId);
18
+ try {
19
+ await deletePaymentMethod(paymentMethodId);
20
+ } catch (error) {
21
+ console.error('Failed to delete payment method:', error);
22
+ alert('Failed to delete payment method. Please try again.');
23
+ } finally {
24
+ setDeletingId(null);
25
+ }
26
+ };
27
+
28
+ if (isLoading) {
29
+ return (
30
+ <div className="p-6">
31
+ <div className="space-y-4">
32
+ {Array.from({ length: 2 }).map((_, index) => (
33
+ <div
34
+ key={index}
35
+ className="h-24 animate-pulse rounded-lg border border-slate-100 bg-slate-50"
36
+ />
37
+ ))}
38
+ </div>
39
+ </div>
40
+ );
41
+ }
42
+
43
+ if (error) {
44
+ return (
45
+ <div className="p-6">
46
+ <div className="rounded-lg border border-red-100 bg-red-50 p-6 text-sm text-red-700">
47
+ {error.message}
48
+ </div>
49
+ </div>
50
+ );
51
+ }
52
+
53
+ return (
54
+ <div className="p-6">
55
+ <div className="mb-6 flex items-center justify-between">
56
+ <div>
57
+ <h3 className="text-lg font-semibold text-slate-900">Payment Methods</h3>
58
+ <p className="text-sm text-slate-600">Manage your saved payment methods</p>
59
+ </div>
60
+ <Button variant="primary" size="sm">
61
+ <Plus className="h-4 w-4" />
62
+ Add Card
63
+ </Button>
64
+ </div>
65
+
66
+ {paymentMethods.length === 0 ? (
67
+ <EmptyState
68
+ icon={CreditCard}
69
+ title="No payment methods"
70
+ description="Add a payment method to make checkout faster and easier."
71
+ actionLabel="Add your first card"
72
+ onAction={() => {
73
+ // TODO: Implement add payment method modal
74
+ alert('Add payment method functionality coming soon');
75
+ }}
76
+ />
77
+ ) : (
78
+ <div className="space-y-4">
79
+ {paymentMethods.map((method) => (
80
+ <div
81
+ key={method.id}
82
+ className="flex items-center justify-between rounded-lg border border-slate-200 bg-white p-4 hover:shadow-md transition-shadow"
83
+ >
84
+ <div className="flex items-center gap-4">
85
+ <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary-50">
86
+ <CreditCard className="h-6 w-6 text-primary-600" />
87
+ </div>
88
+ {/* <div>
89
+ <div className="flex items-center gap-2">
90
+ <p className="font-medium text-slate-900">
91
+ {method.card?.brand || 'Card'} •••• {method.card?.last4 || '****'}
92
+ </p>
93
+ {method.isDefault && (
94
+ <Badge variant="success">Default</Badge>
95
+ )}
96
+ </div>
97
+ <p className="text-sm text-slate-500">
98
+ Expires {method.card?.expMonth}/{method.card?.expYear}
99
+ </p>
100
+ </div> */}
101
+ </div>
102
+ <button
103
+ onClick={() => handleDelete(method.id)}
104
+ disabled={deletingId === method.id}
105
+ className="inline-flex items-center gap-2 rounded-full border border-red-200 px-3 py-1 text-sm font-semibold text-red-600 transition hover:border-red-300 hover:text-red-700 disabled:opacity-50"
106
+ >
107
+ <Trash2 className="h-4 w-4" />
108
+ {deletingId === method.id ? 'Deleting...' : 'Delete'}
109
+ </button>
110
+ </div>
111
+ ))}
112
+ </div>
113
+ )}
114
+ </div>
115
+ );
116
+ }
@@ -0,0 +1,76 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { Heart, ShoppingCart } from 'lucide-react';
5
+ import { useWishlist } from '@/providers/WishlistProvider';
6
+ import { useWishlistProducts } from '@/hooks/useWishlistProducts';
7
+ import { ProductCard } from './ProductCard';
8
+ import { EmptyState } from './EmptyState';
9
+
10
+ export function AccountSavedItemsTab() {
11
+ const { products: wishlistProductIds } = useWishlist();
12
+
13
+ const { products: wishlistProducts, isLoading, error } = useWishlistProducts(
14
+ wishlistProductIds as string[]
15
+ );
16
+
17
+ const products = wishlistProducts || [];
18
+ console.log(products);
19
+
20
+ if (isLoading) {
21
+ return (
22
+ <div className="p-6">
23
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
24
+ {Array.from({ length: 3 }).map((_, index) => (
25
+ <div
26
+ key={index}
27
+ className="h-80 animate-pulse rounded-lg border border-slate-100 bg-slate-50"
28
+ />
29
+ ))}
30
+ </div>
31
+ </div>
32
+ );
33
+ }
34
+
35
+ if (error) {
36
+ return (
37
+ <div className="p-6">
38
+ <div className="rounded-lg border border-red-100 bg-red-50 p-6 text-sm text-red-700">
39
+ {error.message}
40
+ </div>
41
+ </div>
42
+ );
43
+ }
44
+
45
+ if (products.length === 0) {
46
+ return (
47
+ <div className="p-6">
48
+ <EmptyState
49
+ icon={Heart}
50
+ title="No saved items"
51
+ description="Items you save will appear here for easy access later."
52
+ />
53
+ </div>
54
+ );
55
+ }
56
+
57
+ return (
58
+ <div className="pb-24 p-6">
59
+ <div className="p-6 bg-white rounded-xl">
60
+ <div className="mb-4">
61
+ <h3 className="text-lg font-semibold text-secondary">
62
+ Saved for Later
63
+ </h3>
64
+ <p className="text-sm text-secondary">
65
+ {products.length} {products.length === 1 ? 'item' : 'items'} saved
66
+ </p>
67
+ </div>
68
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
69
+ {products.map((product) => (
70
+ <ProductCard key={product._id} product={product} />
71
+ ))}
72
+ </div>
73
+ </div>
74
+ </div>
75
+ );
76
+ }
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { Bell, Lock, Trash2 } from 'lucide-react';
5
+ import { Button } from './ui/Button';
6
+ import { useRouter } from 'next/navigation';
7
+ import { useBasePath } from '@/providers/BasePathProvider';
8
+
9
+ export function AccountSettingsTab() {
10
+ const router = useRouter();
11
+ const { buildPath } = useBasePath();
12
+ const [emailNotifications, setEmailNotifications] = useState(true);
13
+ const [orderUpdates, setOrderUpdates] = useState(true);
14
+ const [promotionalEmails, setPromotionalEmails] = useState(false);
15
+
16
+ return (
17
+ <div className="p-6 space-y-6">
18
+ {/* Account Preferences */}
19
+ <div className="rounded-2xl border border-slate-200 bg-white p-6">
20
+ <div className="flex items-center gap-2 mb-4">
21
+ <Bell className="h-5 w-5 text-secondary" />
22
+ <h3 className="text-lg font-semibold text-secondary">Account Preferences</h3>
23
+ </div>
24
+ <div className="space-y-4">
25
+ <label className="flex items-center justify-between gap-4 rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
26
+ <div>
27
+ <p className="text-sm font-medium text-secondary">Email Notifications</p>
28
+ <p className="text-xs text-muted">Receive updates about your orders</p>
29
+ </div>
30
+ <input
31
+ type="checkbox"
32
+ checked={emailNotifications}
33
+ onChange={(e) => setEmailNotifications(e.target.checked)}
34
+ className="h-4 w-4 rounded border-primary-300 text-secondary focus:ring-primary-500"
35
+ />
36
+ </label>
37
+ <label className="flex items-center justify-between gap-4 rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
38
+ <div>
39
+ <p className="text-sm font-medium text-secondary">Order Updates</p>
40
+ <p className="text-xs text-muted">Get notified about shipping status</p>
41
+ </div>
42
+ <input
43
+ type="checkbox"
44
+ checked={orderUpdates}
45
+ onChange={(e) => setOrderUpdates(e.target.checked)}
46
+ className="h-4 w-4 rounded border-primary-300 text-secondary focus:ring-primary-500"
47
+ />
48
+ </label>
49
+ <label className="flex items-center justify-between gap-4 rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
50
+ <div>
51
+ <p className="text-sm font-medium text-secondary">Promotional Emails</p>
52
+ <p className="text-xs text-muted">Receive special offers and discounts</p>
53
+ </div>
54
+ <input
55
+ type="checkbox"
56
+ checked={promotionalEmails}
57
+ onChange={(e) => setPromotionalEmails(e.target.checked)}
58
+ className="h-4 w-4 rounded border-primary-300 text-secondary focus:ring-primary-500"
59
+ />
60
+ </label>
61
+ </div>
62
+ </div>
63
+
64
+ {/* Security */}
65
+ <div className="rounded-2xl border border-slate-200 bg-white p-6">
66
+ <div className="flex items-center gap-2 mb-4">
67
+ <Lock className="h-5 w-5 text-secondary" />
68
+ <h3 className="text-lg font-semibold text-secondary">Security</h3>
69
+ </div>
70
+ <div className="space-y-3">
71
+ <div className="flex items-center justify-between rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
72
+ <div>
73
+ <p className="text-sm font-medium text-secondary">Change Password</p>
74
+ <p className="text-xs text-muted">Update your account password</p>
75
+ </div>
76
+ <Button
77
+ variant="outline"
78
+ size="sm"
79
+ onClick={() => router.push(buildPath('/account/change-password'))}
80
+ >
81
+ Change
82
+ </Button>
83
+ </div>
84
+ {/* <div className="flex items-center justify-between rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
85
+ <div>
86
+ <p className="text-sm font-medium text-secondary">Two-Factor Authentication</p>
87
+ <p className="text-xs text-muted">Add an extra layer of security</p>
88
+ </div>
89
+ <Button variant="outline" size="sm">
90
+ Enable
91
+ </Button>
92
+ </div> */}
93
+ <div className="flex items-center justify-between rounded-lg border border-red-100 bg-red-50 px-4 py-3">
94
+ <div>
95
+ <p className="text-sm font-medium text-red-900">Delete Account</p>
96
+ <p className="text-xs text-red-600">Permanently delete your account and data</p>
97
+ </div>
98
+ <Button
99
+ variant="outline"
100
+ size="sm"
101
+ className="border-red-200 text-red-600 hover:bg-red-50"
102
+ onClick={() => {
103
+ if (confirm('Are you sure you want to delete your account? This action cannot be undone.')) {
104
+ alert('Account deletion functionality coming soon');
105
+ }
106
+ }}
107
+ >
108
+ <Trash2 className="h-4 w-4" />
109
+ Delete
110
+ </Button>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ );
116
+ }
@@ -8,7 +8,7 @@ import { addressSchema, type AddressFormData } from '@/lib/validations/address';
8
8
  import { AddressesApi } from '@/lib/Apis/apis/addresses-api';
9
9
  import { Address } from '@/lib/Apis';
10
10
  import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
11
- import { toast } from 'sonner';
11
+ import { useNotification } from '@/providers/NotificationProvider';
12
12
 
13
13
  interface AddressFormModalProps {
14
14
  isOpen: boolean;
@@ -20,6 +20,7 @@ interface AddressFormModalProps {
20
20
 
21
21
  export function AddressFormModal({ isOpen, onClose, onAddressAdded, onAddressUpdated, initialAddress }: AddressFormModalProps) {
22
22
  const [isSubmitting, setIsSubmitting] = useState(false);
23
+ const notification = useNotification();
23
24
 
24
25
  const {
25
26
  register,
@@ -69,7 +70,10 @@ export function AddressFormModal({ isOpen, onClose, onAddressAdded, onAddressUpd
69
70
  country: data.country,
70
71
  phone: data.phone,
71
72
  }, initialAddress.id);
72
- toast.success('Address updated successfully');
73
+ notification.success(
74
+ 'Address updated',
75
+ 'Your address has been updated successfully.'
76
+ );
73
77
  reset();
74
78
  onClose();
75
79
  if (onAddressUpdated) onAddressUpdated(response.data);
@@ -85,16 +89,25 @@ export function AddressFormModal({ isOpen, onClose, onAddressAdded, onAddressUpd
85
89
  phone: data.phone,
86
90
  });
87
91
  if (response.status === 201) {
88
- toast.success('Address added successfully');
92
+ notification.success(
93
+ 'Address added',
94
+ 'Your new address has been saved to your account.'
95
+ );
89
96
  reset();
90
97
  onClose();
91
98
  if (onAddressAdded) onAddressAdded(response.data);
92
99
  } else {
93
- toast.error('Failed to add address');
100
+ notification.error(
101
+ 'Could not add address',
102
+ 'We could not save this address. Please try again.'
103
+ );
94
104
  }
95
105
  }
96
106
  } catch (error) {
97
- toast.error('Failed to add address');
107
+ notification.error(
108
+ 'Could not save address',
109
+ 'Something went wrong while saving your address. Please try again.'
110
+ );
98
111
  } finally {
99
112
  setIsSubmitting(false);
100
113
  }
@@ -162,16 +175,16 @@ export function AddressFormModal({ isOpen, onClose, onAddressAdded, onAddressUpd
162
175
  />
163
176
  </div>
164
177
  <div className="flex justify-end gap-4">
165
- <Button
178
+ <button
166
179
  type="button"
167
- variant="outline"
168
180
  onClick={onClose}
181
+ className='flex flex-row items-center gap-2 px-6 py-2 border border-slate-200 rounded-xl text-slate-700 hover:opacity-80 transition-colors text-secondary text-sm'
169
182
  >
170
183
  Cancel
171
- </Button>
172
- <Button type="submit" disabled={isSubmitting}>
184
+ </button>
185
+ <button type="submit" disabled={isSubmitting} className='flex flex-row items-center gap-2 px-6 py-2 border border-slate-200 rounded-xl text-slate-700 hover:opacity-80 transition-colors bg-secondary text-white text-sm'>
173
186
  {isSubmitting ? 'Adding Address...' : 'Add Address'}
174
- </Button>
187
+ </button>
175
188
  </div>
176
189
  </form>
177
190
  </Modal>
@@ -51,77 +51,81 @@ export function CartItem({ item }: CartItemProps) {
51
51
  initial={{ opacity: 0, y: 20 }}
52
52
  animate={{ opacity: 1, y: 0 }}
53
53
  exit={{ opacity: 0, x: -100 }}
54
- className="relative bg-white rounded-lg shadow-sm border border-gray-200 p-4"
54
+ className="bg-white border-2 border-gray-100 rounded-[24px] p-6 hover:border-[#5B9BD5]/30 transition-all duration-300"
55
55
  >
56
- {/* Delete Icon - Top Right */}
57
- <button
58
- onClick={handleRemove}
59
- className="absolute top-4 right-4 p-1 text-gray-400 hover:text-red-600 transition-colors"
60
- aria-label="Remove item"
61
- >
62
- <Trash2 className="w-5 h-5" />
63
- </button>
56
+
64
57
 
65
58
  <div className="flex gap-4 pr-8">
66
59
  {/* Product Image */}
67
- <div className="relative w-20 h-24 rounded-md overflow-hidden flex-shrink-0 bg-gray-100">
60
+ <div className="w-28 h-28 rounded-[16px] overflow-hidden bg-gray-50 shrink-0">
68
61
  <Image
69
62
  src={item.productVariantData.productMedia[0]?.file || '/placeholder-product.jpg'}
70
63
  alt={item.productVariantData.name}
71
- fill
72
- className="object-cover"
73
- sizes="80px"
64
+ className="w-full h-full object-cover"
65
+ height={112}
66
+ width={112}
74
67
  />
75
68
  </div>
76
69
 
77
70
  {/* Product Info */}
78
71
  <div className="flex-1 min-w-0">
79
- <h3 className="text-base font-bold text-gray-900">
80
- {item.productVariantData.name}
81
- </h3>
82
-
83
- {/* Attributes */}
84
- {attributes.length > 0 && (
85
- <p className="text-sm text-gray-500 mt-1">
86
- {attributes.join(' ')}
87
- </p>
88
- )}
89
-
90
- {/* Quantity Controls */}
91
- <div className="flex items-center gap-2 mt-3">
92
- <button
93
- onClick={() => handleUpdateQuantity(item.quantity - 1)}
94
- disabled={isUpdating || item.quantity <= 1}
95
- className="p-1 hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors rounded"
96
- >
97
- <Minus className="w-4 h-4 text-gray-600" />
98
- </button>
99
- <span className="px-3 font-medium min-w-[2rem] text-center text-gray-900">
100
- {isUpdating ? (
101
- <span className="inline-block h-4 w-4 align-middle animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" />
102
- ) : (
103
- item.quantity
104
- )}
72
+ <div className="flex items-start justify-between gap-4 mb-3">
73
+ <div className="flex-1 min-w-0">
74
+ <h3 className="font-['Poppins',sans-serif] font-semibold text-[#2B4B7C] mb-2">
75
+ {item.productVariantData.name}
76
+ </h3>
77
+ <div className="flex flex-wrap items-center gap-3">
78
+ <span className="font-['Poppins',sans-serif] text-[12px] text-[#676c80]">
79
+ Variant: <span className="font-medium text-[#2B4B7C]">{item.productVariantData.name}</span>
105
80
  </span>
106
- <button
107
- onClick={() => handleUpdateQuantity(item.quantity + 1)}
108
- disabled={isUpdating || item.quantity >= (item.productVariantData.inventoryCount || 999)}
109
- className="p-1 hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors rounded"
110
- >
111
- <Plus className="w-4 h-4 text-gray-600" />
112
- </button>
113
81
  </div>
114
- </div>
82
+ </div>
83
+
84
+ {/* Delete Icon - Top Right */}
85
+ <button
86
+ onClick={handleRemove}
87
+ className="p-2 hover:bg-red-50 rounded-full transition-colors group"
88
+ aria-label="Remove item"
89
+ >
90
+ <Trash2 className="size-5 text-[#676c80] group-hover:text-red-500 transition-colors" />
91
+ </button>
92
+ </div>
93
+
94
+ {/* Quantity and Price */}
95
+ <div className="flex items-center justify-between gap-4">
96
+ {/* Quantity Controls */}
97
+ <div className="flex items-center gap-3 bg-gray-50 rounded-full px-4 py-2">
98
+ <button
99
+ onClick={() => handleUpdateQuantity(item.quantity - 1)}
100
+ disabled={isUpdating || item.quantity <= 1}
101
+ className="p-1 hover:bg-white rounded-full transition-colors"
102
+ >
103
+ <Minus className="size-4 text-[#2B4B7C]" />
104
+ </button>
105
+ <span className="font-['Poppins',sans-serif] font-semibold text-[14px] text-[#2B4B7C] min-w-[20px] text-center">
106
+ {item.quantity}
107
+ </span>
108
+ <button
109
+ onClick={() => handleUpdateQuantity(item.quantity + 1)}
110
+ disabled={isUpdating || item.quantity >= (item.productVariantData.inventoryCount || 999)}
111
+ className="p-1 hover:bg-white rounded-full transition-colors"
112
+ >
113
+ <Plus className="size-4 text-[#2B4B7C]" />
114
+ </button>
115
+ </div>
116
+
117
+ {/* Price */}
118
+ <div className="text-right">
119
+ <p className="font-['Poppins',sans-serif] font-bold text-[18px] text-[#E67E50]">
120
+ {formatPrice(itemTotal)}
121
+ </p>
122
+ <p className="font-['Poppins',sans-serif] text-[11px] text-[#676c80]">
123
+ {formatPrice(unitPrice)} each
124
+ </p>
125
+ </div>
126
+ </div>
127
+ </div>
115
128
 
116
- {/* Pricing - Right Side */}
117
- <div className="text-right flex-shrink-0">
118
- <p className="text-lg font-bold text-orange-600">
119
- {formatPrice(itemTotal)}
120
- </p>
121
- <p className="text-sm text-gray-500 mt-1">
122
- {formatPrice(unitPrice)} each
123
- </p>
124
- </div>
125
129
  </div>
126
130
  </motion.div>
127
131
  );