hey-pharmacist-ecommerce 1.1.27 → 1.1.29

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 (61) hide show
  1. package/dist/index.d.mts +344 -640
  2. package/dist/index.d.ts +344 -640
  3. package/dist/index.js +1814 -835
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1814 -837
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +1 -1
  8. package/src/components/AccountOrdersTab.tsx +1 -1
  9. package/src/components/AccountSettingsTab.tsx +88 -6
  10. package/src/components/CartItem.tsx +1 -1
  11. package/src/components/Header.tsx +8 -2
  12. package/src/components/OrderCard.tsx +4 -4
  13. package/src/components/ProductCard.tsx +59 -42
  14. package/src/components/QuickViewModal.tsx +13 -13
  15. package/src/hooks/useAddresses.ts +4 -1
  16. package/src/hooks/usePaymentMethods.ts +26 -31
  17. package/src/hooks/useProducts.ts +63 -64
  18. package/src/hooks/useWishlistProducts.ts +4 -5
  19. package/src/index.ts +2 -0
  20. package/src/lib/Apis/api.ts +0 -1
  21. package/src/lib/Apis/apis/auth-api.ts +18 -29
  22. package/src/lib/Apis/apis/products-api.ts +845 -405
  23. package/src/lib/Apis/models/category-populated.ts +0 -12
  24. package/src/lib/Apis/models/category-sub-category-populated.ts +2 -2
  25. package/src/lib/Apis/models/category.ts +0 -18
  26. package/src/lib/Apis/models/{table-cell-dto.ts → change-password-dto.ts} +6 -6
  27. package/src/lib/Apis/models/create-product-dto.ts +30 -23
  28. package/src/lib/Apis/models/create-sub-category-dto.ts +6 -0
  29. package/src/lib/Apis/models/create-variant-dto.ts +29 -29
  30. package/src/lib/Apis/models/index.ts +5 -7
  31. package/src/lib/Apis/models/paginated-products-dto.ts +6 -6
  32. package/src/lib/Apis/models/product-summary.ts +69 -0
  33. package/src/lib/Apis/models/product-variant.ts +34 -65
  34. package/src/lib/Apis/models/product.ts +138 -0
  35. package/src/lib/Apis/models/products-insights-dto.ts +12 -0
  36. package/src/lib/Apis/models/single-product-media.ts +0 -12
  37. package/src/lib/Apis/models/sub-category.ts +6 -12
  38. package/src/lib/Apis/models/update-product-dto.ts +30 -19
  39. package/src/lib/Apis/models/update-sub-category-dto.ts +6 -0
  40. package/src/lib/Apis/models/{update-product-variant-dto.ts → update-variant-dto.ts} +51 -45
  41. package/src/lib/Apis/models/{shallow-parent-category-dto.ts → variant-id-inventory-body.ts} +5 -11
  42. package/src/lib/api-adapter/config.ts +53 -0
  43. package/src/lib/validations/address.ts +1 -1
  44. package/src/providers/FavoritesProvider.tsx +5 -5
  45. package/src/providers/WishlistProvider.tsx +4 -4
  46. package/src/screens/CartScreen.tsx +1 -1
  47. package/src/screens/ChangePasswordScreen.tsx +2 -6
  48. package/src/screens/CheckoutScreen.tsx +40 -11
  49. package/src/screens/EditProfileScreen.tsx +5 -1
  50. package/src/screens/ForgotPasswordScreen.tsx +153 -0
  51. package/src/screens/ProductDetailScreen.tsx +51 -60
  52. package/src/screens/RegisterScreen.tsx +31 -31
  53. package/src/screens/ResetPasswordScreen.tsx +202 -0
  54. package/src/screens/SearchResultsScreen.tsx +264 -26
  55. package/src/screens/ShopScreen.tsx +42 -45
  56. package/src/screens/WishlistScreen.tsx +35 -31
  57. package/src/lib/Apis/apis/product-variants-api.ts +0 -552
  58. package/src/lib/Apis/models/create-single-variant-product-dto.ts +0 -154
  59. package/src/lib/Apis/models/extended-product-dto.ts +0 -206
  60. package/src/lib/Apis/models/frequently-bought-product-dto.ts +0 -71
  61. package/src/lib/Apis/models/table-dto.ts +0 -34
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hey-pharmacist-ecommerce",
3
- "version": "1.1.27",
3
+ "version": "1.1.29",
4
4
  "description": "Production-ready, multi-tenant e‑commerce UI + API adapter for Next.js with auth, carts, checkout, orders, theming, and pharmacist-focused UX.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -102,7 +102,7 @@ export function AccountOrdersTab() {
102
102
  <div key={item.productVariantId || index} className="flex items-center gap-3">
103
103
  <div className="relative w-12 h-12 rounded-lg bg-slate-100 shrink-0 overflow-hidden">
104
104
  <Image
105
- src={item?.productVariantData?.productMedia?.[0]?.file || '/placeholder-product.jpg'}
105
+ src={item?.productVariantData?.media?.[0]?.file || '/placeholder-product.jpg'}
106
106
  alt={item?.productVariantData?.name || 'Product image'}
107
107
  fill
108
108
  className="object-cover"
@@ -1,17 +1,42 @@
1
1
  'use client';
2
2
 
3
3
  import React, { useState } from 'react';
4
- import { Bell, Lock, Trash2 } from 'lucide-react';
4
+ import { Bell, Lock, Trash2, AlertTriangle } from 'lucide-react';
5
5
  import { Button } from './ui/Button';
6
+ import { Modal } from './ui/Modal';
6
7
  import { useRouter } from 'next/navigation';
7
8
  import { useBasePath } from '@/providers/BasePathProvider';
9
+ import { useAuth } from '@/providers/AuthProvider';
10
+ import { UsersApi } from '@/lib/Apis/apis/users-api';
11
+ import { AXIOS_CONFIG } from '@/lib/Apis/sharedConfig';
8
12
 
9
13
  export function AccountSettingsTab() {
10
14
  const router = useRouter();
11
15
  const { buildPath } = useBasePath();
16
+ const { logout } = useAuth();
12
17
  const [emailNotifications, setEmailNotifications] = useState(true);
13
18
  const [orderUpdates, setOrderUpdates] = useState(true);
14
19
  const [promotionalEmails, setPromotionalEmails] = useState(false);
20
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
21
+ const [isDeleting, setIsDeleting] = useState(false);
22
+ const [deleteError, setDeleteError] = useState<string | null>(null);
23
+
24
+ const handleDeleteAccount = async () => {
25
+ setIsDeleting(true);
26
+ setDeleteError(null);
27
+ try {
28
+ const usersApi = new UsersApi(AXIOS_CONFIG);
29
+ await usersApi.deleteMyProfile();
30
+ // Logout and redirect to home
31
+ logout();
32
+ router.push(buildPath('/'));
33
+ } catch (error: any) {
34
+ setDeleteError(
35
+ error?.response?.data?.message || 'Failed to delete account. Please try again.'
36
+ );
37
+ setIsDeleting(false);
38
+ }
39
+ };
15
40
 
16
41
  return (
17
42
  <div className="p-6 space-y-6">
@@ -99,11 +124,7 @@ export function AccountSettingsTab() {
99
124
  variant="outline-solid"
100
125
  size="sm"
101
126
  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
- }}
127
+ onClick={() => setShowDeleteModal(true)}
107
128
  >
108
129
  <Trash2 className="h-4 w-4" />
109
130
  Delete
@@ -111,6 +132,67 @@ export function AccountSettingsTab() {
111
132
  </div>
112
133
  </div>
113
134
  </div>
135
+
136
+ {/* Delete Account Confirmation Modal */}
137
+ <Modal
138
+ isOpen={showDeleteModal}
139
+ onClose={() => {
140
+ setShowDeleteModal(false);
141
+ setDeleteError(null);
142
+ }}
143
+ title="Delete Account"
144
+ >
145
+ <div className="space-y-4">
146
+ <div className="flex items-start gap-3 rounded-lg border border-red-200 bg-red-50 p-4">
147
+ <AlertTriangle className="h-5 w-5 text-red-600 mt-0.5 flex-shrink-0" />
148
+ <div>
149
+ <p className="text-sm font-semibold text-red-900">Warning: This action is permanent</p>
150
+ <p className="text-sm text-red-700 mt-1">
151
+ Deleting your account will permanently remove all your data, including:
152
+ </p>
153
+ <ul className="text-sm text-red-700 mt-2 ml-4 list-disc space-y-1">
154
+ <li>Personal information and profile</li>
155
+ <li>Order history and tracking</li>
156
+ <li>Saved addresses and payment methods</li>
157
+ <li>Wishlist items</li>
158
+ </ul>
159
+ </div>
160
+ </div>
161
+
162
+ {deleteError && (
163
+ <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
164
+ <span className="font-semibold">Error: </span>
165
+ {deleteError}
166
+ </div>
167
+ )}
168
+
169
+ <p className="text-sm text-slate-600">
170
+ Are you absolutely sure you want to delete your account? This action cannot be undone.
171
+ </p>
172
+
173
+ <div className="flex gap-3 pt-2">
174
+ <Button
175
+ variant="outline-solid"
176
+ onClick={() => {
177
+ setShowDeleteModal(false);
178
+ setDeleteError(null);
179
+ }}
180
+ disabled={isDeleting}
181
+ className="flex-1"
182
+ >
183
+ Cancel
184
+ </Button>
185
+ <Button
186
+ onClick={handleDeleteAccount}
187
+ isLoading={isDeleting}
188
+ disabled={isDeleting}
189
+ className="flex-1 bg-red-600 hover:bg-red-700 text-white"
190
+ >
191
+ {isDeleting ? 'Deleting...' : 'Delete Account'}
192
+ </Button>
193
+ </div>
194
+ </div>
195
+ </Modal>
114
196
  </div>
115
197
  );
116
198
  }
@@ -55,7 +55,7 @@ export function CartItem({ item }: CartItemProps) {
55
55
  {/* Product Image */}
56
56
  <div className="w-28 h-28 rounded-[16px] overflow-hidden bg-gray-50 shrink-0">
57
57
  <Image
58
- src={item.productVariantData.productMedia[0]?.file || '/placeholder-product.jpg'}
58
+ src={item.productVariantData.media[0]?.file || '/placeholder-product.jpg'}
59
59
  alt={item.productVariantData.name}
60
60
  className="w-full h-full object-cover"
61
61
  height={112}
@@ -101,13 +101,19 @@ export function Header() {
101
101
  value={searchQuery}
102
102
  onChange={(e) => setSearchQuery(e.target.value)}
103
103
  onKeyDown={(e) => {
104
- if (e.key === 'Enter' && searchQuery.trim()) {
105
- window.location.href = buildPath(`/search?q=${encodeURIComponent(searchQuery.trim())}`);
104
+ if (e.key === 'Enter') {
105
+ const sanitized = searchQuery.trim().replace(/\s+/g, ' ');
106
+ if (sanitized) {
107
+ router.push(buildPath(`/search?q=${encodeURIComponent(sanitized)}`));
108
+ setIsSearchOpen(false);
109
+ setSearchQuery('');
110
+ }
106
111
  }
107
112
  }}
108
113
  placeholder="Search products..."
109
114
  className="w-full outline-hidden text-gray-700"
110
115
  autoFocus
116
+ autoComplete="off"
111
117
  />
112
118
  {searchQuery && (
113
119
  <button
@@ -15,9 +15,9 @@ interface OrderCardProps {
15
15
  export function OrderCard({ order }: OrderCardProps) {
16
16
  const config = order.orderStatus;
17
17
  const itemCount = order.items?.length || 0;
18
- const showPriceBreakdown = (order.shippingCost && order.shippingCost > 0) ||
19
- (order.tax && order.tax > 0) ||
20
- (order.discountedAmount && order.discountedAmount > 0);
18
+ const showPriceBreakdown = (order.shippingCost && order.shippingCost > 0) ||
19
+ (order.tax && order.tax > 0) ||
20
+ (order.discountedAmount && order.discountedAmount > 0);
21
21
 
22
22
  return (
23
23
  <motion.div
@@ -55,7 +55,7 @@ export function OrderCard({ order }: OrderCardProps) {
55
55
  <div key={item.productVariantId || item._id} className="flex items-center gap-2 text-sm">
56
56
  <div className="relative w-12 h-12 rounded-sm bg-gray-100 shrink-0 overflow-hidden">
57
57
  <Image
58
- src={item?.productVariantData?.productMedia?.[0]?.file || '/placeholder-product.jpg'}
58
+ src={item?.productVariantData?.media?.[0]?.file || '/placeholder-product.jpg'}
59
59
  alt={item?.productVariantData?.name || 'Product image'}
60
60
  fill
61
61
  className="object-cover"
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import React, { useState, useEffect, useMemo, useCallback } from 'react';
4
- import { motion, AnimatePresence } from 'framer-motion';
4
+ import { motion } from 'framer-motion';
5
5
  import { Star, ShoppingCart, Eye } from 'lucide-react';
6
- import { ExtendedProductDTO, } from '@/lib/Apis';
6
+ import { Product, } from '@/lib/Apis';
7
7
  import { formatPrice } from '@/lib/utils/format';
8
8
  import { useWishlist } from '@/providers/WishlistProvider';
9
9
  import { useCart } from '@/providers/CartProvider';
@@ -12,11 +12,12 @@ import { useRouter } from 'next/navigation';
12
12
  import { useBasePath } from '@/providers/BasePathProvider';
13
13
  import { QuickViewModal } from './QuickViewModal';
14
14
  import { useNotification } from '@/providers/NotificationProvider';
15
+ import { useAuth } from '@/providers/AuthProvider';
15
16
 
16
17
  interface ProductCardProps {
17
- product: ExtendedProductDTO;
18
- onClickProduct?: (product: ExtendedProductDTO) => void;
19
- onFavorite?: (product: ExtendedProductDTO) => void;
18
+ product: Product;
19
+ onClickProduct?: (product: Product) => void;
20
+ onFavorite?: (product: Product) => void;
20
21
  isFavorited?: boolean;
21
22
  showFavoriteButton?: boolean;
22
23
  }
@@ -30,6 +31,7 @@ export function ProductCard({
30
31
  className
31
32
  }: ProductCardProps & { className?: string }) {
32
33
  const router = useRouter();
34
+ const { isAuthenticated } = useAuth();
33
35
  const { buildPath } = useBasePath();
34
36
  const [isFavorite, setIsFavorite] = useState(isFavorited);
35
37
  const { addToWishlist, removeFromWishlist, isInWishlist } = useWishlist();
@@ -65,7 +67,7 @@ export function ProductCard({
65
67
  `${product.name} was removed from your wishlist.`
66
68
  );
67
69
  } else {
68
- await addToWishlist(product as unknown as ExtendedProductDTO);
70
+ await addToWishlist(product as unknown as Product);
69
71
  setIsFavorite(true);
70
72
  notification.success(
71
73
  'Added to wishlist',
@@ -95,14 +97,14 @@ export function ProductCard({
95
97
  setIsImageLoaded(false);
96
98
 
97
99
  // Auto-select first variant if available
98
- if (product.productVariants && product.productVariants.length > 0) {
99
- const firstVariant = product.productVariants[0];
100
- if (firstVariant.productMedia && firstVariant.productMedia.length > 0) {
101
- setSelectedVariantImage(firstVariant.productMedia[0].file);
100
+ if (product.variants && product.variants.length > 0) {
101
+ const firstVariant = product.variants[0];
102
+ if (firstVariant.media && firstVariant.media.length > 0) {
103
+ setSelectedVariantImage(firstVariant.media[0].file);
102
104
  setSelectedVariantId(firstVariant.id || firstVariant._id || null);
103
105
  }
104
106
  }
105
- }, [product._id, product.productVariants]);
107
+ }, [product._id, product.variants]);
106
108
 
107
109
  // Handle keyboard navigation
108
110
  const handleKeyDown = (e: React.KeyboardEvent) => {
@@ -115,58 +117,53 @@ export function ProductCard({
115
117
 
116
118
  // Get variant images (first image from each variant that has images)
117
119
  const variantImages = useMemo(() => {
118
- if (!product.productVariants || product.productVariants.length === 0) {
120
+ if (!product.variants || product.variants.length === 0) {
119
121
  return [];
120
122
  }
121
- return product.productVariants
122
- .filter((variant) => variant.productMedia && variant.productMedia.length > 0)
123
+ return product.variants
124
+ .filter((variant) => variant.media && variant.media.length > 0)
123
125
  .map((variant) => ({
124
126
  variantId: variant.id || variant._id,
125
127
  variantName: variant.name,
126
- image: variant.productMedia[0].file,
128
+ image: variant.media[0].file,
127
129
  }));
128
- }, [product.productVariants]);
130
+ }, [product.variants]);
129
131
 
130
132
  // Get selected variant
131
133
  const selectedVariant = useMemo(() => {
132
- if (!selectedVariantId || !product.productVariants) return null;
133
- return product.productVariants.find(
134
+ if (!selectedVariantId || !product.variants) return null;
135
+ return product.variants.find(
134
136
  (variant) => (variant.id || variant._id) === selectedVariantId
135
137
  );
136
- }, [selectedVariantId, product.productVariants]);
137
-
138
- // Get display name (variant name if selected, otherwise product name)
139
- const displayName = useMemo(() => {
140
- return selectedVariant?.name || product.name;
141
- }, [selectedVariant, product.name]);
138
+ }, [selectedVariantId, product.variants]);
142
139
 
143
140
  const displayFinalPrice = useMemo(() => {
144
- return selectedVariant?.finalPrice ?? product.finalPrice;
145
- }, [selectedVariant, product.finalPrice]);
141
+ return selectedVariant?.finalPrice ?? product.variants?.[0]?.finalPrice ?? 0;
142
+ }, [selectedVariant, product.variants]);
146
143
 
147
144
  const displayPriceBeforeDiscount = useMemo(() => {
148
- return selectedVariant?.retailPrice ?? product.priceBeforeDiscount;
149
- }, [selectedVariant, product.priceBeforeDiscount]);
145
+ return selectedVariant?.retailPrice ?? product.variants?.[0]?.retailPrice ?? 0;
146
+ }, [selectedVariant, product.variants]);
150
147
 
151
148
  const displayIsDiscounted = useMemo(() => {
152
- return selectedVariant ? selectedVariant.isDiscounted : product.isDiscounted;
153
- }, [selectedVariant, product.isDiscounted]);
149
+ return selectedVariant ? selectedVariant.isDiscounted : product.variants?.[0]?.isDiscounted;
150
+ }, [selectedVariant, product.variants]);
154
151
 
155
152
  const displayDiscountAmount = useMemo(() => {
156
- return selectedVariant ? selectedVariant.discountAmount : product.discountAmount;
157
- }, [selectedVariant, product.discountAmount]);
153
+ return selectedVariant ? selectedVariant.discountAmount : product.variants?.[0]?.discountAmount;
154
+ }, [selectedVariant, product.variants]);
158
155
 
159
156
  const displayInventoryCount = useMemo(() => {
160
- return selectedVariant ? selectedVariant.inventoryCount : product.inventoryCount;
161
- }, [selectedVariant, product.inventoryCount]);
157
+ return selectedVariant ? selectedVariant.inventoryCount : product.variants?.[0]?.inventoryCount;
158
+ }, [selectedVariant, product.variants]);
162
159
 
163
160
  const imageSource = useMemo(() => {
164
- const src = selectedVariantImage || product.productMedia?.[0]?.file || '/placeholder-product.jpg';
161
+ const src = selectedVariantImage || selectedVariant?.media?.[0]?.file || product.media?.[0]?.file || '/placeholder-product.jpg';
165
162
  return {
166
163
  src,
167
164
  alt: product.name || 'Product image'
168
165
  };
169
- }, [product.productMedia, product.name, selectedVariantImage]);
166
+ }, [product.media, product.name, selectedVariantImage]);
170
167
 
171
168
  // Reset image loaded state when image source changes
172
169
  useEffect(() => {
@@ -261,24 +258,29 @@ export function ProductCard({
261
258
 
262
259
  <div className="h-[40px] mb-3">
263
260
  <h3 className="text-sm font-['Poppins',sans-serif] font-semibold text-[#2B4B7C] line-clamp-2">
264
- {displayName}
261
+ {product.name}
265
262
  </h3>
263
+ {selectedVariant && (
264
+ <p className="text-xs font-['Poppins',sans-serif] text-[#676c80]">
265
+ {selectedVariant.name}
266
+ </p>
267
+ )}
266
268
  </div>
267
269
  {/* Rating */}
268
- <div className="flex items-center gap-1.5 mb-2">
270
+ <div className="flex items-center gap-1.5 ">
269
271
  <div className="flex items-center gap-0.5">
270
272
  {[...Array(5)].map((_, i) => (
271
273
  <Star
272
274
  key={i}
273
- className={`size-4 ${i < Math.floor(product.rating ? product.rating : 0)
275
+ className={`size-4 ${i < Math.floor(product.summary?.averageRating || 0)
274
276
  ? 'text-[#E67E50] fill-[#E67E50]'
275
277
  : 'text-gray-300'
276
278
  }`}
277
279
  />
278
280
  ))}
279
281
  </div>
280
- <span className="font-['Poppins',sans-serif] text-[10px] text-[#676c80]">
281
- ({product.reviews?.length || 0})
282
+ <span className="font-['Poppins',sans-serif] text-[10px] text-[#676c80] mt-4">
283
+ ({product.summary?.reviewCount || 0})
282
284
  </span>
283
285
  </div>
284
286
 
@@ -289,7 +291,7 @@ export function ProductCard({
289
291
  </span>
290
292
  {displayIsDiscounted && (
291
293
  <span className="font-['Poppins',sans-serif] text-sm text-[#676c80] line-through">
292
- ${formatPrice(displayPriceBeforeDiscount)}
294
+ ${displayPriceBeforeDiscount.toFixed(2)}
293
295
  </span>
294
296
  )}
295
297
  </div>
@@ -342,6 +344,21 @@ export function ProductCard({
342
344
  );
343
345
  return;
344
346
  }
347
+ if (displayInventoryCount === 0) {
348
+ notification.error(
349
+ 'Out of Stock',
350
+ 'This item is currently out of stock.'
351
+ );
352
+ return;
353
+ }
354
+ if (!isAuthenticated) {
355
+ notification.error(
356
+ 'Sign-in required',
357
+ 'Please sign in to add items to your cart.'
358
+ );
359
+ router.push(buildPath(`/login?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}`));
360
+ return;
361
+ }
345
362
  try {
346
363
  await addToCart(
347
364
  product._id || product.id,
@@ -1,6 +1,6 @@
1
1
  import { useState } from 'react';
2
2
  import { X, ShoppingCart, Check, Star, Package, ExternalLink, Minus, Plus } from 'lucide-react';
3
- import { ExtendedProductDTO, Product } from '@/lib/Apis';
3
+ import { Product } from '@/lib/Apis';
4
4
  import { useCart } from '@/providers/CartProvider';
5
5
  import Image from 'next/image';
6
6
  import { ProductVariantInventoryStatusEnum } from '@/lib/Apis';
@@ -8,7 +8,7 @@ import { useNotification } from '@/providers/NotificationProvider';
8
8
  import Link from 'next/link';
9
9
 
10
10
  interface QuickViewModalProps {
11
- product: ExtendedProductDTO;
11
+ product: Product;
12
12
  onClose: () => void;
13
13
  onNavigateToProduct?: (productId: string) => void;
14
14
  }
@@ -28,8 +28,8 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
28
28
  setQuantity(newQuantity);
29
29
  }
30
30
  };
31
- const selectedVariant = product.productVariants[selectedVariantIndex];
32
- const selectedSize = selectedVariant.productMedia[selectedSizeIndex];
31
+ const selectedVariant = product.variants[selectedVariantIndex];
32
+ const selectedSize = selectedVariant.media[selectedSizeIndex];
33
33
  const displayPrice = selectedVariant.finalPrice;
34
34
  const displayOriginalPrice = selectedVariant.retailPrice;
35
35
  const isDiscounted = selectedVariant.isDiscounted;
@@ -75,7 +75,7 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
75
75
  <div className="flex items-start justify-between mb-6">
76
76
  <div>
77
77
  <p className="font-['Poppins',sans-serif] text-[11px] text-primary uppercase tracking-wide font-medium mb-2">
78
- {product.brand} • {product.parentCategories?.[0]?.name || 'Uncategorized'}
78
+ {product.brand}
79
79
  </p>
80
80
  <h2 className="font-['Poppins',sans-serif] font-semibold text-secondary tracking-[-1px]">
81
81
  {displayName}
@@ -87,7 +87,7 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
87
87
  {[...Array(5)].map((_, i) => (
88
88
  <Star
89
89
  key={i}
90
- className={`size-4 ${i < Math.floor(product.rating ? product.rating : 0)
90
+ className={`size-4 ${i < Math.floor(product.summary?.averageRating || 0)
91
91
  ? 'text-accent fill-accent'
92
92
  : 'text-gray-300'
93
93
  }`}
@@ -95,7 +95,7 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
95
95
  ))}
96
96
  </div>
97
97
  <span className="font-['Poppins',sans-serif] text-[13px] text-muted">
98
- {product.rating} ({product.reviews ? product.reviews.length : 0} reviews)
98
+ {product.summary?.averageRating || 0} ({product.summary?.reviewCount || 0} reviews)
99
99
  </span>
100
100
  </div>
101
101
  </div>
@@ -112,7 +112,7 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
112
112
  <div className="space-y-4">
113
113
  <div className="relative aspect-3/4 rounded-[24px] overflow-hidden bg-gray-50">
114
114
  <img
115
- src={selectedVariant.productMedia[selectedImageIndex]?.file || selectedVariant.productMedia[0]?.file}
115
+ src={selectedVariant.media[selectedImageIndex]?.file || selectedVariant.media[0]?.file}
116
116
  alt={product.name}
117
117
  className="w-full h-full object-contain"
118
118
  />
@@ -136,9 +136,9 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
136
136
  </div>
137
137
 
138
138
  {/* Thumbnail Images */}
139
- {selectedVariant.productMedia.length > 1 && (
139
+ {selectedVariant.media.length > 1 && (
140
140
  <div className="grid grid-cols-4 gap-3">
141
- {selectedVariant.productMedia.map((image: any, index: any) => (
141
+ {selectedVariant.media.map((image: any, index: any) => (
142
142
  <div
143
143
  key={index}
144
144
  className={`aspect-square rounded-xl overflow-hidden cursor-pointer transition-opacity ${selectedImageIndex === index ? 'ring-2 ring-primary' : 'bg-gray-50 hover:opacity-75'}`}
@@ -197,10 +197,10 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
197
197
  {/* Color Selection */}
198
198
  <div className="mb-6">
199
199
  <h3 className="font-['Poppins',sans-serif] font-semibold text-[13px] text-secondary mb-3">
200
- Selected Variant: <span className="font-normal text-muted">{product.productVariants[selectedVariantIndex].name}</span>
200
+ Selected Variant: <span className="font-normal text-muted">{product.variants[selectedVariantIndex].name}</span>
201
201
  </h3>
202
202
  <div className="flex flex-wrap gap-3">
203
- {product.productVariants.map((variant: any, index: any) => (
203
+ {product.variants.map((variant: any, index: any) => (
204
204
  <button
205
205
  key={variant.id}
206
206
  onClick={() => {
@@ -216,7 +216,7 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
216
216
  title={variant.color}
217
217
  >
218
218
  <Image
219
- src={variant.productMedia?.[0]?.file || ''}
219
+ src={variant.media?.[0]?.file || ''}
220
220
  alt={variant.color || `Variant ${index + 1}`}
221
221
  className="object-cover"
222
222
  height={32}
@@ -34,7 +34,10 @@ export function useAddresses(): UseAddressesReturn {
34
34
  const sortedAddresses = useMemo(() => {
35
35
  return [...addresses].sort((a, b) => {
36
36
  if (a.isDefault === b.isDefault) {
37
- return (b.updatedAt.toISOString() || '').localeCompare(a.updatedAt.toISOString() || '');
37
+ // updatedAt is already a string from the API, so we can compare directly
38
+ const dateA = a.updatedAt || '';
39
+ const dateB = b.updatedAt || '';
40
+ return dateB.toString().localeCompare(dateA.toString());
38
41
  }
39
42
  return a.isDefault ? -1 : 1;
40
43
  });
@@ -1,47 +1,42 @@
1
1
  import { useState, useEffect, useCallback } from 'react';
2
- import { PaymentMethodsApi } from '@/lib/Apis';
3
- import { getApiConfiguration } from '@/lib/api-adapter';
4
- import { PaymentMethod } from '@/lib/Apis/models';
2
+
3
+ // NOTE: PaymentMethodsApi and PaymentMethod were removed in the API refactoring.
4
+ // This hook is stubbed out until the API is restored.
5
+ // The AccountPaymentTab component will display an empty state.
6
+
7
+ interface PaymentMethod {
8
+ id: string;
9
+ type: string;
10
+ last4?: string;
11
+ brand?: string;
12
+ expiryMonth?: number;
13
+ expiryYear?: number;
14
+ isDefault?: boolean;
15
+ }
5
16
 
6
17
  export function usePaymentMethods() {
7
18
  const [paymentMethods, setPaymentMethods] = useState<PaymentMethod[]>([]);
8
- const [isLoading, setIsLoading] = useState(true);
19
+ const [isLoading, setIsLoading] = useState(false);
9
20
  const [error, setError] = useState<Error | null>(null);
10
21
 
11
22
  const fetchPaymentMethods = useCallback(async () => {
12
- setIsLoading(true);
23
+ // Stubbed: API not available
24
+ setIsLoading(false);
25
+ setPaymentMethods([]);
13
26
  setError(null);
14
- try {
15
- const response = await new PaymentMethodsApi(getApiConfiguration()).getPaymentMethods();
16
- setPaymentMethods(response.data.paymentMethods || []);
17
- } catch (err) {
18
- setError(err as Error);
19
- } finally {
20
- setIsLoading(false);
21
- }
22
27
  }, []);
23
28
 
24
29
  const deletePaymentMethod = useCallback(async (paymentMethodId: string) => {
25
- try {
26
- await new PaymentMethodsApi(getApiConfiguration()).detachPaymentMethod({
27
- paymentMethodId,
28
- });
29
- await fetchPaymentMethods();
30
- } catch (err) {
31
- throw err;
32
- }
33
- }, [fetchPaymentMethods]);
30
+ // Stubbed: API not available
31
+ console.warn('PaymentMethodsApi not available - deletePaymentMethod stubbed');
32
+ throw new Error('Payment methods API is not available');
33
+ }, []);
34
34
 
35
35
  const setDefaultPaymentMethod = useCallback(async (paymentMethodId: string) => {
36
- try {
37
- await new PaymentMethodsApi(getApiConfiguration()).changeDefaultPayment({
38
- paymentMethodId,
39
- });
40
- await fetchPaymentMethods();
41
- } catch (err) {
42
- throw err;
43
- }
44
- }, [fetchPaymentMethods]);
36
+ // Stubbed: API not available
37
+ console.warn('PaymentMethodsApi not available - setDefaultPaymentMethod stubbed');
38
+ throw new Error('Payment methods API is not available');
39
+ }, []);
45
40
 
46
41
  useEffect(() => {
47
42
  fetchPaymentMethods();