hey-pharmacist-ecommerce 1.0.5 → 1.0.7

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 (74) hide show
  1. package/README.md +157 -17
  2. package/dist/index.d.mts +3636 -316
  3. package/dist/index.d.ts +3636 -316
  4. package/dist/index.js +6802 -3866
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +6756 -3818
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +18 -15
  9. package/src/components/AddressFormModal.tsx +171 -0
  10. package/src/components/CartItem.tsx +17 -12
  11. package/src/components/FilterChips.tsx +195 -0
  12. package/src/components/Header.tsx +121 -71
  13. package/src/components/OrderCard.tsx +18 -25
  14. package/src/components/ProductCard.tsx +209 -72
  15. package/src/components/ui/Button.tsx +13 -5
  16. package/src/components/ui/Card.tsx +46 -0
  17. package/src/hooks/useAddresses.ts +83 -0
  18. package/src/hooks/useOrders.ts +37 -19
  19. package/src/hooks/useProducts.ts +55 -63
  20. package/src/hooks/useWishlistProducts.ts +75 -0
  21. package/src/index.ts +3 -19
  22. package/src/lib/Apis/api.ts +1 -0
  23. package/src/lib/Apis/apis/cart-api.ts +3 -3
  24. package/src/lib/Apis/apis/inventory-api.ts +0 -108
  25. package/src/lib/Apis/apis/stores-api.ts +70 -0
  26. package/src/lib/Apis/apis/wishlist-api.ts +447 -0
  27. package/src/lib/Apis/models/cart-item-populated.ts +0 -1
  28. package/src/lib/Apis/models/create-single-variant-product-dto.ts +3 -10
  29. package/src/lib/Apis/models/create-variant-dto.ts +26 -33
  30. package/src/lib/Apis/models/extended-product-dto.ts +20 -24
  31. package/src/lib/Apis/models/index.ts +2 -1
  32. package/src/lib/Apis/models/order-time-line-dto.ts +49 -0
  33. package/src/lib/Apis/models/order.ts +3 -8
  34. package/src/lib/Apis/models/populated-order.ts +3 -8
  35. package/src/lib/Apis/models/product-variant.ts +29 -0
  36. package/src/lib/Apis/models/update-product-variant-dto.ts +16 -23
  37. package/src/lib/Apis/models/wishlist.ts +51 -0
  38. package/src/lib/Apis/wrapper.ts +18 -7
  39. package/src/lib/api-adapter/index.ts +0 -12
  40. package/src/lib/types/index.ts +16 -61
  41. package/src/lib/utils/colors.ts +7 -4
  42. package/src/lib/utils/format.ts +1 -1
  43. package/src/lib/validations/address.ts +14 -0
  44. package/src/providers/AuthProvider.tsx +61 -31
  45. package/src/providers/CartProvider.tsx +18 -28
  46. package/src/providers/EcommerceProvider.tsx +7 -0
  47. package/src/providers/FavoritesProvider.tsx +86 -0
  48. package/src/providers/ThemeProvider.tsx +16 -1
  49. package/src/providers/WishlistProvider.tsx +174 -0
  50. package/src/screens/AddressesScreen.tsx +484 -0
  51. package/src/screens/CartScreen.tsx +120 -84
  52. package/src/screens/CategoriesScreen.tsx +120 -0
  53. package/src/screens/CheckoutScreen.tsx +919 -241
  54. package/src/screens/CurrentOrdersScreen.tsx +125 -61
  55. package/src/screens/HomeScreen.tsx +209 -0
  56. package/src/screens/LoginScreen.tsx +133 -88
  57. package/src/screens/NewAddressScreen.tsx +187 -0
  58. package/src/screens/OrdersScreen.tsx +162 -50
  59. package/src/screens/ProductDetailScreen.tsx +641 -190
  60. package/src/screens/ProfileScreen.tsx +192 -116
  61. package/src/screens/RegisterScreen.tsx +193 -144
  62. package/src/screens/SearchResultsScreen.tsx +165 -0
  63. package/src/screens/ShopScreen.tsx +1110 -146
  64. package/src/screens/WishlistScreen.tsx +428 -0
  65. package/src/lib/Apis/models/inventory-paginated-response.ts +0 -75
  66. package/src/lib/api/auth.ts +0 -81
  67. package/src/lib/api/cart.ts +0 -42
  68. package/src/lib/api/orders.ts +0 -53
  69. package/src/lib/api/products.ts +0 -51
  70. package/src/lib/api-adapter/auth-adapter.ts +0 -196
  71. package/src/lib/api-adapter/cart-adapter.ts +0 -193
  72. package/src/lib/api-adapter/mappers.ts +0 -152
  73. package/src/lib/api-adapter/orders-adapter.ts +0 -195
  74. package/src/lib/api-adapter/products-adapter.ts +0 -194
@@ -6,18 +6,24 @@ import { ShoppingCart, User, Menu, X, Search, Heart } from 'lucide-react';
6
6
  import { useAuth } from '@/providers/AuthProvider';
7
7
  import { useCart } from '@/providers/CartProvider';
8
8
  import { useTheme } from '@/providers/ThemeProvider';
9
+ import { useWishlist } from '@/providers/WishlistProvider';
9
10
  import Link from 'next/link';
10
11
  import Image from 'next/image';
11
12
 
12
13
  export function Header() {
13
14
  const { config } = useTheme();
14
15
  const { user, isAuthenticated } = useAuth();
15
- const { cart } = useCart();
16
+ const { cart } = useCart() || { cart: { itemCount: 0 } };
17
+ const { getWishlistCount } = useWishlist();
18
+ const wishlistCount = getWishlistCount?.() || 0;
16
19
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
20
+ const [isSearchOpen, setIsSearchOpen] = useState(false);
21
+ const [searchQuery, setSearchQuery] = useState('');
17
22
 
18
23
  const navLinks = [
19
24
  { href: '/shop', label: 'Shop' },
20
25
  { href: '/categories', label: 'Categories' },
26
+ { href: '/orders', label: 'Orders' },
21
27
  { href: '/about', label: 'About' },
22
28
  { href: '/contact', label: 'Contact' },
23
29
  ];
@@ -58,49 +64,94 @@ export function Header() {
58
64
  {/* Actions */}
59
65
  <div className="flex items-center gap-4">
60
66
  {/* Search */}
61
- <button className="p-2 hover:bg-gray-100 rounded-lg transition-colors">
62
- <Search className="w-5 h-5 text-gray-700" />
63
- </button>
64
-
65
- {/* Favorites */}
66
- <button className="p-2 hover:bg-gray-100 rounded-lg transition-colors relative">
67
- <Heart className="w-5 h-5 text-gray-700" />
68
- </button>
69
-
70
- {/* Cart */}
71
- <Link
72
- href="/cart"
73
- className="p-2 hover:bg-gray-100 rounded-lg transition-colors relative"
74
- >
75
- <ShoppingCart className="w-5 h-5 text-gray-700" />
76
- {cart && cart.itemCount > 0 && (
77
- <span className="absolute -top-1 -right-1 w-5 h-5 bg-primary-600 text-white text-xs font-bold rounded-full flex items-center justify-center">
78
- {cart.itemCount}
79
- </span>
80
- )}
81
- </Link>
82
-
83
- {/* User Menu */}
84
- {isAuthenticated ? (
85
- <Link
86
- href="/account"
67
+ <div className="relative">
68
+ <button
69
+ onClick={() => setIsSearchOpen(!isSearchOpen)}
87
70
  className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
71
+ aria-label="Search"
88
72
  >
89
- <User className="w-5 h-5 text-gray-700" />
73
+ <Search className="w-5 h-5 text-gray-700" />
74
+ </button>
75
+
76
+ <AnimatePresence>
77
+ {isSearchOpen && (
78
+ <motion.div
79
+ initial={{ opacity: 0, width: 0 }}
80
+ animate={{ opacity: 1, width: 'auto' }}
81
+ exit={{ opacity: 0, width: 0 }}
82
+ className="absolute right-0 top-full mt-2 bg-white rounded-lg shadow-lg overflow-hidden"
83
+ >
84
+ <div className="flex items-center p-2 w-64">
85
+ <Search className="w-5 h-5 text-gray-400 mr-2" />
86
+ <input
87
+ type="text"
88
+ value={searchQuery}
89
+ onChange={(e) => setSearchQuery(e.target.value)}
90
+ onKeyDown={(e) => {
91
+ if (e.key === 'Enter' && searchQuery.trim()) {
92
+ window.location.href = `/search?q=${encodeURIComponent(searchQuery.trim())}`;
93
+ }
94
+ }}
95
+ placeholder="Search products..."
96
+ className="w-full outline-none text-gray-700"
97
+ autoFocus
98
+ />
99
+ {searchQuery && (
100
+ <button
101
+ onClick={() => setSearchQuery('')}
102
+ className="text-gray-400 hover:text-gray-600"
103
+ >
104
+ <X className="w-4 h-4" />
105
+ </button>
106
+ )}
107
+ </div>
108
+ </motion.div>
109
+ )}
110
+ </AnimatePresence>
111
+ </div>
112
+
113
+ {/* Wishlist and Cart */}
114
+ <div className="flex items-center gap-4">
115
+ <Link href="/wishlist" className="relative p-2 text-gray-700 hover:text-red-500 transition-colors">
116
+ <Heart className="w-6 h-6" />
117
+ {wishlistCount > 0 && (
118
+ <span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center">
119
+ {wishlistCount}
120
+ </span>
121
+ )}
90
122
  </Link>
91
- ) : (
92
- <Link
93
- href="/login"
94
- className="hidden sm:block px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors font-medium"
95
- >
96
- Sign In
123
+
124
+ <Link href="/cart" className="relative p-2 text-gray-700 hover:text-primary-600 transition-colors">
125
+ <ShoppingCart className="w-6 h-6" />
126
+ {cart?.cartBody?.items?.length && cart.cartBody?.items?.length > 0 ? (
127
+ <span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center">
128
+ {cart.cartBody?.items?.length}
129
+ </span>
130
+ ) : null}
97
131
  </Link>
98
- )}
132
+
133
+ {/* User Menu */}
134
+ {isAuthenticated ? (
135
+ <Link
136
+ href="/account"
137
+ className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
138
+ >
139
+ <User className="w-6 h-6 text-gray-700" />
140
+ </Link>
141
+ ) : (
142
+ <Link
143
+ href="/login"
144
+ className="hidden sm:block px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors font-medium"
145
+ >
146
+ Sign In
147
+ </Link>
148
+ )}
149
+ </div>
99
150
 
100
151
  {/* Mobile Menu Button */}
101
152
  <button
102
- onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
103
153
  className="lg:hidden p-2 hover:bg-gray-100 rounded-lg transition-colors"
154
+ onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
104
155
  >
105
156
  {isMobileMenuOpen ? (
106
157
  <X className="w-6 h-6" />
@@ -109,43 +160,42 @@ export function Header() {
109
160
  )}
110
161
  </button>
111
162
  </div>
112
- </div>
113
163
 
114
- {/* Mobile Menu */}
115
- <AnimatePresence>
116
- {isMobileMenuOpen && (
117
- <motion.div
118
- initial={{ height: 0, opacity: 0 }}
119
- animate={{ height: 'auto', opacity: 1 }}
120
- exit={{ height: 0, opacity: 0 }}
121
- className="lg:hidden overflow-hidden border-t border-gray-200"
122
- >
123
- <nav className="py-4 space-y-2">
124
- {navLinks.map((link) => (
125
- <Link
126
- key={link.href}
127
- href={link.href}
128
- onClick={() => setIsMobileMenuOpen(false)}
129
- className="block px-4 py-3 hover:bg-gray-50 rounded-lg text-gray-700 font-medium"
130
- >
131
- {link.label}
132
- </Link>
133
- ))}
134
- {!isAuthenticated && (
135
- <Link
136
- href="/login"
137
- onClick={() => setIsMobileMenuOpen(false)}
138
- className="block px-4 py-3 bg-primary-600 text-white rounded-lg text-center font-medium"
139
- >
140
- Sign In
141
- </Link>
142
- )}
143
- </nav>
144
- </motion.div>
145
- )}
146
- </AnimatePresence>
164
+ {/* Mobile Menu */}
165
+ <AnimatePresence>
166
+ {isMobileMenuOpen && (
167
+ <motion.div
168
+ initial={{ opacity: 0, height: 0 }}
169
+ animate={{ opacity: 1, height: 'auto' }}
170
+ exit={{ opacity: 0, height: 0 }}
171
+ className="lg:hidden overflow-hidden border-t border-gray-200"
172
+ >
173
+ <nav className="flex flex-col gap-1 py-2">
174
+ {navLinks.map((link) => (
175
+ <Link
176
+ key={link.href}
177
+ href={link.href}
178
+ className="px-4 py-3 text-gray-700 hover:bg-gray-50 rounded-lg font-medium"
179
+ onClick={() => setIsMobileMenuOpen(false)}
180
+ >
181
+ {link.label}
182
+ </Link>
183
+ ))}
184
+ {!isAuthenticated && (
185
+ <Link
186
+ href="/login"
187
+ onClick={() => setIsMobileMenuOpen(false)}
188
+ className="px-4 py-3 text-primary-600 hover:bg-gray-50 rounded-lg font-medium"
189
+ >
190
+ Sign In
191
+ </Link>
192
+ )}
193
+ </nav>
194
+ </motion.div>
195
+ )}
196
+ </AnimatePresence>
197
+ </div>
147
198
  </div>
148
199
  </header>
149
200
  );
150
201
  }
151
-
@@ -3,25 +3,18 @@
3
3
  import React from 'react';
4
4
  import { motion } from 'framer-motion';
5
5
  import { Package, Calendar, CreditCard, ExternalLink } from 'lucide-react';
6
- import { Order, OrderStatus } from '@/lib/types';
6
+ import { PaymentPaymentMethodEnum, PaymentPaymentStatusEnum, PopulatedOrder } from '@/lib/Apis';
7
7
  import { formatPrice, formatDate } from '@/lib/utils/format';
8
8
  import { Badge } from './ui/Badge';
9
9
  import Link from 'next/link';
10
+ import Image from 'next/image';
10
11
 
11
12
  interface OrderCardProps {
12
- order: Order;
13
+ order: PopulatedOrder;
13
14
  }
14
15
 
15
- const statusConfig: Record<OrderStatus, { variant: 'success' | 'warning' | 'primary' | 'danger' | 'gray', label: string }> = {
16
- [OrderStatus.PENDING]: { variant: 'warning', label: 'Pending' },
17
- [OrderStatus.PROCESSING]: { variant: 'primary', label: 'Processing' },
18
- [OrderStatus.SHIPPED]: { variant: 'primary', label: 'Shipped' },
19
- [OrderStatus.DELIVERED]: { variant: 'success', label: 'Delivered' },
20
- [OrderStatus.CANCELLED]: { variant: 'danger', label: 'Cancelled' },
21
- };
22
-
23
16
  export function OrderCard({ order }: OrderCardProps) {
24
- const config = statusConfig[order.status];
17
+ const config = order.orderStatus;
25
18
 
26
19
  return (
27
20
  <motion.div
@@ -35,29 +28,29 @@ export function OrderCard({ order }: OrderCardProps) {
35
28
  <div>
36
29
  <h3 className="text-lg font-bold text-gray-900 flex items-center gap-2">
37
30
  <Package className="w-5 h-5 text-primary-600" />
38
- Order #{order.orderNumber}
31
+ Order #{order?._id?.slice(0, 6) || ''}
39
32
  </h3>
40
33
  <p className="text-sm text-gray-500 mt-1 flex items-center gap-2">
41
34
  <Calendar className="w-4 h-4" />
42
- {formatDate(order.createdAt, 'long')}
35
+ {formatDate(order.createdAt || new Date(), 'long')}
43
36
  </p>
44
37
  </div>
45
- <Badge variant={config.variant}>{config.label}</Badge>
38
+ <Badge variant={config as 'success' | 'warning' | 'primary' | 'danger' | 'gray'}>{config}</Badge>
46
39
  </div>
47
40
 
48
41
  {/* Items Preview */}
49
42
  <div className="space-y-2 mb-4">
50
- {order.items.slice(0, 2).map((item) => (
51
- <div key={item.id} className="flex items-center gap-3 text-sm">
52
- <div className="w-12 h-12 rounded-lg bg-gray-100 flex-shrink-0" />
43
+ {order.items?.slice(0, 2).map((item) => (
44
+ <div key={item.productVariantId} className="flex items-center gap-3 text-sm">
45
+ <Image src={item?.productVariantData?.productMedia?.[0]?.file || '/placeholder-product.jpg'} alt={item?.productVariantData?.name || 'Product image'} width={48} height={48} className="w-12 h-12 rounded-lg bg-gray-100 flex-shrink-0" />
53
46
  <div className="flex-1 min-w-0">
54
- <p className="font-medium text-gray-900 truncate">{item.productName}</p>
47
+ <p className="font-medium text-gray-900 truncate">{item.productVariantData.name}</p>
55
48
  <p className="text-gray-500">Qty: {item.quantity}</p>
56
49
  </div>
57
- <p className="font-semibold text-gray-900">{formatPrice(item.price)}</p>
50
+ <p className="font-semibold text-gray-900">{formatPrice(item.productVariantData.finalPrice)}</p>
58
51
  </div>
59
52
  ))}
60
- {order.items.length > 2 && (
53
+ {order.items?.length && order.items?.length > 2 && (
61
54
  <p className="text-sm text-gray-500 pl-15">
62
55
  +{order.items.length - 2} more item{order.items.length - 2 > 1 ? 's' : ''}
63
56
  </p>
@@ -68,13 +61,13 @@ export function OrderCard({ order }: OrderCardProps) {
68
61
  <div className="flex justify-between items-center pt-4 border-t border-gray-200">
69
62
  <div>
70
63
  <p className="text-sm text-gray-500">Total Amount</p>
71
- <p className="text-2xl font-bold text-gray-900">{formatPrice(order.total)}</p>
64
+ <p className="text-2xl font-bold text-gray-900">{formatPrice(order.grandTotal || 0)}</p>
72
65
  </div>
73
66
 
74
67
  <div className="flex gap-2">
75
- {order.stripeCheckoutUrl && order.paymentStatus !== 'paid' && (
68
+ {order.payment.paymentStatus !== PaymentPaymentStatusEnum.Paid && order.payment.paymentMethod === PaymentPaymentMethodEnum.Card && (
76
69
  <a
77
- href={order.stripeCheckoutUrl}
70
+ href={order?.payment?.paymentIntent?.hostedInvoiceUrl || ''}
78
71
  target="_blank"
79
72
  rel="noopener noreferrer"
80
73
  className="inline-flex items-center gap-2 px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"
@@ -83,13 +76,13 @@ export function OrderCard({ order }: OrderCardProps) {
83
76
  Pay Now
84
77
  </a>
85
78
  )}
86
- <Link
79
+ {/* <Link
87
80
  href={`/orders/${order.id}`}
88
81
  className="inline-flex items-center gap-2 px-4 py-2 border-2 border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
89
82
  >
90
83
  View Details
91
84
  <ExternalLink className="w-4 h-4" />
92
- </Link>
85
+ </Link> */}
93
86
  </div>
94
87
  </div>
95
88
  </motion.div>