hey-pharmacist-ecommerce 1.1.28 → 1.1.30

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 (91) hide show
  1. package/dist/index.d.mts +10552 -1370
  2. package/dist/index.d.ts +10552 -1370
  3. package/dist/index.js +4696 -1281
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +4640 -1283
  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/useStoreCapabilities.ts +87 -0
  19. package/src/hooks/useWishlistProducts.ts +4 -5
  20. package/src/index.ts +6 -0
  21. package/src/lib/Apis/api.ts +0 -1
  22. package/src/lib/Apis/apis/auth-api.ts +37 -36
  23. package/src/lib/Apis/apis/categories-api.ts +97 -0
  24. package/src/lib/Apis/apis/products-api.ts +942 -405
  25. package/src/lib/Apis/apis/shipping-api.ts +105 -0
  26. package/src/lib/Apis/apis/stores-api.ts +356 -0
  27. package/src/lib/Apis/apis/sub-categories-api.ts +97 -0
  28. package/src/lib/Apis/apis/users-api.ts +8 -8
  29. package/src/lib/Apis/models/address-created-request.ts +0 -12
  30. package/src/lib/Apis/models/address.ts +0 -12
  31. package/src/lib/Apis/models/api-key-info-dto.ts +49 -0
  32. package/src/lib/Apis/models/category-populated.ts +0 -12
  33. package/src/lib/Apis/models/category-sub-category-populated.ts +2 -2
  34. package/src/lib/Apis/models/category.ts +0 -18
  35. package/src/lib/Apis/models/{table-cell-dto.ts → change-password-dto.ts} +6 -6
  36. package/src/lib/Apis/models/create-address-dto.ts +0 -12
  37. package/src/lib/Apis/models/create-discount-dto.ts +0 -8
  38. package/src/lib/Apis/models/create-product-dto.ts +30 -23
  39. package/src/lib/Apis/models/create-store-address-dto.ts +0 -12
  40. package/src/lib/Apis/models/create-store-dto-settings.ts +51 -0
  41. package/src/lib/Apis/models/create-store-dto.ts +7 -0
  42. package/src/lib/Apis/models/create-sub-category-dto.ts +6 -0
  43. package/src/lib/Apis/models/create-variant-dto.ts +26 -32
  44. package/src/lib/Apis/models/discount.ts +0 -8
  45. package/src/lib/Apis/models/index.ts +16 -7
  46. package/src/lib/Apis/models/paginated-products-dto.ts +6 -6
  47. package/src/lib/Apis/models/populated-discount.ts +0 -8
  48. package/src/lib/Apis/models/product-summary.ts +69 -0
  49. package/src/lib/Apis/models/product-variant.ts +31 -68
  50. package/src/lib/Apis/models/product.ts +138 -0
  51. package/src/lib/Apis/models/products-insights-dto.ts +12 -0
  52. package/src/lib/Apis/models/reorder-categories-dto.ts +27 -0
  53. package/src/lib/Apis/models/reorder-products-dto.ts +49 -0
  54. package/src/lib/Apis/models/{table-dto.ts → reorder-products-success-response-dto.ts} +8 -9
  55. package/src/lib/Apis/models/reorder-subcategories-dto.ts +33 -0
  56. package/src/lib/Apis/models/{shallow-parent-category-dto.ts → reorder-success-response-dto.ts} +7 -7
  57. package/src/lib/Apis/models/shipment-with-order.ts +18 -0
  58. package/src/lib/Apis/models/shipment.ts +18 -0
  59. package/src/lib/Apis/models/single-product-media.ts +0 -12
  60. package/src/lib/Apis/models/store-api-keys-response-dto.ts +34 -0
  61. package/src/lib/Apis/models/store-capabilities-dto.ts +63 -0
  62. package/src/lib/Apis/models/store-entity.ts +7 -0
  63. package/src/lib/Apis/models/store.ts +7 -0
  64. package/src/lib/Apis/models/sub-category.ts +6 -12
  65. package/src/lib/Apis/models/update-address-dto.ts +0 -12
  66. package/src/lib/Apis/models/update-api-keys-dto.ts +39 -0
  67. package/src/lib/Apis/models/update-discount-dto.ts +0 -8
  68. package/src/lib/Apis/models/update-manual-shipment-status-dto.ts +47 -0
  69. package/src/lib/Apis/models/update-product-dto.ts +30 -19
  70. package/src/lib/Apis/models/update-store-dto.ts +7 -0
  71. package/src/lib/Apis/models/update-sub-category-dto.ts +6 -0
  72. package/src/lib/Apis/models/{update-product-variant-dto.ts → update-variant-dto.ts} +46 -46
  73. package/src/lib/Apis/models/variant-id-inventory-body.ts +27 -0
  74. package/src/lib/api-adapter/config.ts +53 -0
  75. package/src/lib/validations/address.ts +1 -1
  76. package/src/providers/FavoritesProvider.tsx +5 -5
  77. package/src/providers/WishlistProvider.tsx +4 -4
  78. package/src/screens/CartScreen.tsx +1 -1
  79. package/src/screens/ChangePasswordScreen.tsx +2 -6
  80. package/src/screens/CheckoutScreen.tsx +402 -288
  81. package/src/screens/ForgotPasswordScreen.tsx +153 -0
  82. package/src/screens/ProductDetailScreen.tsx +51 -60
  83. package/src/screens/RegisterScreen.tsx +31 -31
  84. package/src/screens/ResetPasswordScreen.tsx +208 -0
  85. package/src/screens/SearchResultsScreen.tsx +264 -26
  86. package/src/screens/ShopScreen.tsx +42 -45
  87. package/src/screens/WishlistScreen.tsx +35 -31
  88. package/src/lib/Apis/apis/product-variants-api.ts +0 -552
  89. package/src/lib/Apis/models/create-single-variant-product-dto.ts +0 -154
  90. package/src/lib/Apis/models/extended-product-dto.ts +0 -206
  91. package/src/lib/Apis/models/frequently-bought-product-dto.ts +0 -71
@@ -0,0 +1,153 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { motion } from 'framer-motion';
5
+ import { useForm } from 'react-hook-form';
6
+ import { zodResolver } from '@hookform/resolvers/zod';
7
+ import { z } from 'zod';
8
+ import Link from 'next/link';
9
+ import { ArrowLeft, Mail, Send } from 'lucide-react';
10
+ import { Input } from '@/components/ui/Input';
11
+ import { Button } from '@/components/ui/Button';
12
+ import { AuthApi } from '@/lib/Apis/apis/auth-api';
13
+ import { AXIOS_CONFIG } from '@/lib/Apis/sharedConfig';
14
+ import { useBasePath } from '@/providers/BasePathProvider';
15
+
16
+ const forgotPasswordSchema = z.object({
17
+ email: z.string().email('Enter a valid email address'),
18
+ });
19
+
20
+ type ForgotPasswordFormData = z.infer<typeof forgotPasswordSchema>;
21
+
22
+ export function ForgotPasswordScreen() {
23
+ const { buildPath } = useBasePath();
24
+ const [isSubmitting, setIsSubmitting] = useState(false);
25
+ const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(
26
+ null
27
+ );
28
+
29
+ const {
30
+ register,
31
+ handleSubmit,
32
+ formState: { errors },
33
+ } = useForm<ForgotPasswordFormData>({
34
+ resolver: zodResolver(forgotPasswordSchema),
35
+ });
36
+
37
+ const onSubmit = async (data: ForgotPasswordFormData) => {
38
+ setIsSubmitting(true);
39
+ setStatus(null);
40
+ try {
41
+ const authApi = new AuthApi(AXIOS_CONFIG);
42
+ await authApi.sendForgetPasswordEmail({ email: data.email });
43
+ setStatus({
44
+ type: 'success',
45
+ message: 'Password reset link sent! Check your email inbox.',
46
+ });
47
+ } catch (error: any) {
48
+ setStatus({
49
+ type: 'error',
50
+ message: error?.response?.data?.message || 'Unable to send reset email. Please try again.',
51
+ });
52
+ } finally {
53
+ setIsSubmitting(false);
54
+ }
55
+ };
56
+
57
+ return (
58
+ <div className="min-h-screen bg-linear-to-b from-[#F8FAFC] to-[#EBF4FB]">
59
+ <div className="grid min-h-screen overflow-hidden pb-12">
60
+ <motion.section
61
+ initial={{ opacity: 0, x: 24 }}
62
+ animate={{ opacity: 1, x: 0 }}
63
+ transition={{ duration: 0.4 }}
64
+ className="flex items-center justify-center px-6 py-12 lg:px-16"
65
+ >
66
+ <div className="w-full max-w-lg space-y-10 text-center">
67
+ <div className="space-y-2">
68
+ <Mail
69
+ strokeWidth={2}
70
+ className="h-16 w-16 mx-auto text-white rounded-full bg-secondary m-2 mb-4 px-4"
71
+ />
72
+ <h2 className="text-4xl text-secondary">Forgot Password?</h2>
73
+ <p className="text-sm text-muted">
74
+ No worries! Enter your email and we'll send you reset instructions.
75
+ </p>
76
+ </div>
77
+
78
+ <form
79
+ onSubmit={handleSubmit(onSubmit)}
80
+ className="space-y-6 rounded-3xl border bg-white p-8"
81
+ style={{
82
+ boxShadow: '0px 4px 6px -4px #0000001A, 0px 10px 15px -3px #0000001A',
83
+ }}
84
+ >
85
+ {status && (
86
+ <div
87
+ className={`flex items-start gap-2 rounded-2xl border px-4 py-3 text-sm ${
88
+ status.type === 'success'
89
+ ? 'border-green-200 bg-green-50 text-green-800'
90
+ : 'border-red-200 bg-red-50 text-red-700'
91
+ }`}
92
+ >
93
+ <span className="mt-[2px] text-base">{status.type === 'success' ? '✔' : '!'}</span>
94
+ <span>{status.message}</span>
95
+ </div>
96
+ )}
97
+
98
+ <div className="text-start text-secondary">
99
+ <h2 className="text-sm text-secondary mb-3">
100
+ Email Address <span className="text-primary-500">*</span>
101
+ </h2>
102
+ <Input
103
+ type="email"
104
+ placeholder="you@example.com"
105
+ {...register('email')}
106
+ error={errors.email?.message}
107
+ className="text-secondary"
108
+ />
109
+ </div>
110
+
111
+ <button
112
+ type="submit"
113
+ disabled={isSubmitting}
114
+ className="w-full bg-secondary hover:opacity-80 text-white font-medium py-3 px-4 rounded-lg transition-colors disabled:opacity-70 disabled:cursor-not-allowed flex items-center justify-center gap-2"
115
+ >
116
+ {isSubmitting ? (
117
+ 'Sending...'
118
+ ) : (
119
+ <>
120
+ <Send className="h-4 w-4" />
121
+ Send Reset Link
122
+ </>
123
+ )}
124
+ </button>
125
+
126
+ <div className="pt-4 border-t border-slate-200">
127
+ <Link
128
+ href={buildPath('/login')}
129
+ className="flex items-center justify-center gap-2 text-sm font-medium text-primary transition hover:opacity-80"
130
+ >
131
+ <ArrowLeft className="h-4 w-4" />
132
+ Back to login
133
+ </Link>
134
+ </div>
135
+ </form>
136
+
137
+ <div className="mt-4">
138
+ <p className="text-muted">
139
+ Don't have an account?{' '}
140
+ <Link
141
+ href={buildPath('/register')}
142
+ className="font-medium text-primary transition hover:opacity-90"
143
+ >
144
+ Sign up
145
+ </Link>
146
+ </p>
147
+ </div>
148
+ </div>
149
+ </motion.section>
150
+ </div>
151
+ </div>
152
+ );
153
+ }
@@ -31,7 +31,6 @@ import { useAuth } from '@/providers/AuthProvider';
31
31
  import { formatDate, formatPrice } from '@/lib/utils/format';
32
32
  import { useNotification } from '@/providers/NotificationProvider';
33
33
  import { useRouter } from 'next/navigation';
34
- import { ProductVariantsApi } from '@/lib/Apis/apis/product-variants-api';
35
34
  import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
36
35
  import { useWishlist } from '@/providers/WishlistProvider';
37
36
  import { ProductsApi, ProductVariant, ProductVariantInventoryStatusEnum } from '@/lib/Apis';
@@ -84,13 +83,13 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
84
83
 
85
84
  if (!productData) return null;
86
85
 
87
- if (productData.productVariants?.length && selectedVariant) {
86
+ if (productData.variants?.length && selectedVariant) {
88
87
  return {
89
88
  ...productData,
90
89
  price: selectedVariant.finalPrice,
91
90
  inStock: selectedVariant.isActive,
92
91
  sku: selectedVariant.sku || productData.sku,
93
- variantId: selectedVariant.id,
92
+ variantId: selectedVariant._id,
94
93
  variantName: selectedVariant.name
95
94
  };
96
95
  }
@@ -99,8 +98,8 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
99
98
  }, [productData, selectedVariant, initialProductData]);
100
99
 
101
100
  const getVariantImages = () => {
102
- if (selectedVariant?.productMedia?.length) {
103
- return selectedVariant.productMedia.map((media: any) => ({
101
+ if (selectedVariant?.media?.length) {
102
+ return selectedVariant.media.map((media: any) => ({
104
103
  src: media.file,
105
104
  width: 800,
106
105
  height: 1000,
@@ -111,8 +110,8 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
111
110
  }
112
111
 
113
112
  // Fallback to product media if no variant media
114
- if (product?.productMedia?.length) {
115
- return product.productMedia.map((media: any) => ({
113
+ if (product?.media?.length) {
114
+ return product.media.map((media: any) => ({
116
115
  src: media.file,
117
116
  width: 800,
118
117
  height: 1000,
@@ -122,14 +121,14 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
122
121
  }));
123
122
  }
124
123
 
125
- if (product?.images?.length) {
126
- return product.images.map((image: string) => ({
127
- src: image,
124
+ if (product?.media?.length) {
125
+ return product.media.map((media: any) => ({
126
+ src: media.file,
128
127
  width: 800,
129
128
  height: 1000,
130
129
  alt: product?.name || 'Product image',
131
- url: image,
132
- file: image,
130
+ url: media.file,
131
+ file: media.file,
133
132
  type: 'image' as const
134
133
  }));
135
134
  }
@@ -189,26 +188,19 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
189
188
  }, [product?.productVariants]);
190
189
 
191
190
  useEffect(() => {
192
- if (!product?.id) return;
191
+ if (!product?._id) return;
193
192
 
194
- let isMounted = true;
195
193
  const fetchRelated = async () => {
196
194
  try {
197
- const response = await new ProductsApi(AXIOS_CONFIG).getRelatedProducts(product.id, {
198
- params: {
199
- limit: 4
200
- }
201
- });
195
+ const response = await new ProductsApi(AXIOS_CONFIG).getRelatedProducts(product._id, 4);
196
+ console.log("response.data", response.data);
202
197
  setRelatedProducts(response.data || []);
203
198
  } catch (error: any) {
204
199
  console.error('Failed to fetch related products', error);
205
200
  }
206
201
  };
207
202
  fetchRelated();
208
- return () => {
209
- isMounted = false;
210
- };
211
- }, [product?.id]);
203
+ }, [product?._id]);
212
204
 
213
205
 
214
206
  const handleVariantSelect = async (variant: ProductVariant) => {
@@ -232,9 +224,9 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
232
224
  setIsAddingToCart(true);
233
225
  try {
234
226
  await addToCart(
235
- product.id,
227
+ product._id,
236
228
  quantity,
237
- selectedVariant.id
229
+ selectedVariant._id
238
230
  );
239
231
  notification.success(
240
232
  'Added to cart',
@@ -256,7 +248,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
256
248
  // Initialize isFavorited based on whether the product is in the wishlist
257
249
  useEffect(() => {
258
250
  if (product) {
259
- setIsFavorited(isInWishlist(product.id));
251
+ setIsFavorited(isInWishlist(product._id));
260
252
  }
261
253
  }, [product, isInWishlist]);
262
254
 
@@ -265,7 +257,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
265
257
 
266
258
  try {
267
259
  if (isFavorited) {
268
- await removeFromWishlist(product.id);
260
+ await removeFromWishlist(product._id);
269
261
  notification.info(
270
262
  'Removed from saved items',
271
263
  `${product.name} was removed from your saved items.`
@@ -362,7 +354,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
362
354
  <section className="rounded-3xl ">
363
355
  <div className="space-y-6">
364
356
  <motion.div
365
- key={selectedVariant?.id || 'default'}
357
+ key={selectedVariant?._id || 'default'}
366
358
  initial={{ opacity: 0.4, scale: 0.98 }}
367
359
  animate={{ opacity: 1, scale: 1 }}
368
360
  transition={{ duration: 0.35 }}
@@ -447,7 +439,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
447
439
  {/* Category Path */}
448
440
  <div className="mb-4">
449
441
  <p className="font-['Poppins',sans-serif] text-[12px] text-primary uppercase tracking-wide font-medium mb-2">
450
- {product.brand} • {product.parentCategories?.[0]?.name || 'Uncategorized'}
442
+ {product.brand} • {product.categoryIds?.[0]?.name || 'Uncategorized'}
451
443
  </p>
452
444
  <h1 className="text-3xl font-['Poppins',sans-serif] font-semibold text-secondary tracking-[-1.5px] mb-3">
453
445
  {selectedVariant?.name || product.name}
@@ -469,38 +461,37 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
469
461
  {product.rating} ({product.reviewCount ? product.reviewCount : 0} reviews)
470
462
  </span>
471
463
  </div>
472
-
473
- <div className="flex items-center gap-3 mb-6 pb-6 border-b-2 border-gray-100">
474
- <span className="font-['Poppins',sans-serif] font-bold text-[40px] text-[#E67E50]">
475
- ${variantPrice.toFixed(2)}
476
- </span>
477
- {variantComparePrice && variantComparePrice > variantPrice && (
478
- <>
479
- <span className="font-['Poppins',sans-serif] text-[24px] text-muted line-through">
480
- ${variantComparePrice.toFixed(2)}
481
- </span>
482
- <div className="px-3 py-1 rounded-full bg-[#E67E50]/10">
483
- <span className="font-['Poppins',sans-serif] font-semibold text-[13px] text-[#E67E50]">
484
- Save ${formatPrice(variantComparePrice - variantPrice)}
464
+ {selectedVariant && (
465
+ <div className="flex items-center gap-3 mb-6 pb-6 border-b-2 border-gray-100">
466
+ <span className="font-['Poppins',sans-serif] font-bold text-[40px] text-[#E67E50]">
467
+ ${variantPrice.toFixed(2)}
468
+ </span>
469
+ {variantComparePrice && variantComparePrice > variantPrice && (
470
+ <>
471
+ <span className="font-['Poppins',sans-serif] text-[24px] text-muted line-through">
472
+ ${variantComparePrice.toFixed(2)}
485
473
  </span>
486
- </div>
487
- </>
488
- )}
489
- </div>
490
-
474
+ <div className="px-3 py-1 rounded-full bg-[#E67E50]/10">
475
+ <span className="font-['Poppins',sans-serif] font-semibold text-[13px] text-[#E67E50]">
476
+ Save ${formatPrice(variantComparePrice - variantPrice)}
477
+ </span>
478
+ </div>
479
+ </>
480
+ )}
481
+ </div>
482
+ )}
491
483
  {/* Stock Status */}
492
484
  {selectedVariant && (
493
485
  <div className="mb-6">
494
486
  <div className="flex items-center gap-2 mb-2">
495
- {selectedVariant.inventoryStatus === ProductVariantInventoryStatusEnum.OUTOFSTOCK ||
496
- selectedVariant.inventoryStatus === ProductVariantInventoryStatusEnum.LOWSTOCK ? (
487
+ {selectedVariant.inventoryStatus === ProductVariantInventoryStatusEnum.OUTOFSTOCK ? (
497
488
  <>
498
489
  <div className="size-3 rounded-full bg-red-500" />
499
490
  <span className="font-['Poppins',sans-serif] text-[13px] text-red-600 font-medium">
500
491
  Out of Stock
501
492
  </span>
502
493
  </>
503
- ) : selectedVariant.inventoryCount <= 10 ? (
494
+ ) : selectedVariant.inventoryStatus === ProductVariantInventoryStatusEnum.LOWSTOCK || selectedVariant.inventoryCount <= 10 ? (
504
495
  <>
505
496
  <div className="size-3 rounded-full bg-primary animate-pulse" />
506
497
  <span className="font-['Poppins',sans-serif] text-[13px] text-primary font-medium">
@@ -518,7 +509,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
518
509
 
519
510
  </div>
520
511
  <p className="font-['Poppins',sans-serif] text-[12px] text-muted">
521
- SKU: {selectedVariant?.sku}
512
+ SKU: {selectedVariant?.sku || product?.sku}
522
513
  </p>
523
514
  </div>
524
515
  )}
@@ -532,21 +523,21 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
532
523
  )}
533
524
 
534
525
  {/* Variant Selector with Images */}
535
- {product?.productVariants && product.productVariants.length > 0 && (
526
+ {product?.variants && product.variants.length > 0 && (
536
527
  <div className="mb-6">
537
528
  <h3 className="font-['Poppins',sans-serif] font-semibold text-[14px] text-secondary mb-3">
538
529
  Select Variant
539
530
  </h3>
540
531
  <div className="flex flex-wrap gap-3">
541
- {product.productVariants.map((variant: ProductVariant) => {
542
- const isSelected = selectedVariant?.id === variant.id;
543
- const variantImage = variant.productMedia?.[0]?.file
544
- || product.productMedia?.[0]?.file
532
+ {product.variants.map((variant: ProductVariant) => {
533
+ const isSelected = selectedVariant?._id === variant._id;
534
+ const variantImage = variant.media?.[0]?.file
535
+ || product.media?.[0]?.file
545
536
  || product.images?.[0]
546
537
  || '/placeholder-product.jpg';
547
538
  return (
548
539
  <button
549
- key={variant.id}
540
+ key={variant._id}
550
541
  type="button"
551
542
  onClick={() => handleVariantSelect(variant)}
552
543
  className={`flex items-start gap-3 px-4 py-2.5 rounded-xl border-2 transition-all ${isSelected
@@ -623,7 +614,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
623
614
  <button
624
615
  className="flex-1 font-['Poppins',sans-serif] font-medium text-[14px] px-8 py-4 rounded-full transition-all duration-300 flex items-center justify-center gap-3 bg-[#E67E50] text-white hover:bg-[#d66f45] hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed"
625
616
  onClick={handleAddToCart}
626
- disabled={!selectedVariant || selectedVariant.inventoryStatus === ProductVariantInventoryStatusEnum.OUTOFSTOCK}
617
+ disabled={!selectedVariant || selectedVariant.inventoryStatus === ProductVariantInventoryStatusEnum.OUTOFSTOCK || isAddingToCart}
627
618
  >
628
619
  {isAddingToCart ? (
629
620
  <>
@@ -631,7 +622,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
631
622
  <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
632
623
  <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
633
624
  </svg>
634
- Loading...
625
+ Adding...
635
626
  </>
636
627
  ) : (
637
628
  <>
@@ -787,7 +778,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
787
778
  {relatedProducts.map(relatedProduct => (
788
779
 
789
780
  <ProductCard
790
- key={relatedProduct.id}
781
+ key={relatedProduct._id}
791
782
  product={relatedProduct}
792
783
  // viewMode="grid"
793
784
  onClickProduct={(item) => {
@@ -25,8 +25,8 @@ import { useBasePath } from '@/providers/BasePathProvider';
25
25
 
26
26
  const registerSchema = z
27
27
  .object({
28
- firstName: z.string().min(2, 'First name is required'),
29
- lastName: z.string().min(2, 'Last name is required'),
28
+ firstname: z.string().min(2, 'First name is required'),
29
+ lastname: z.string().min(2, 'Last name is required'),
30
30
  email: z.string().email('Enter a valid email'),
31
31
  phone: z.string().optional(),
32
32
  password: z.string().min(6, 'Password must be at least 6 characters'),
@@ -108,44 +108,44 @@ export function RegisterScreen() {
108
108
  onSubmit={handleSubmit(onSubmit)}
109
109
  className="text-start space-y-6 rounded-3xl border border-slate-100 bg-white p-8 shadow-lg shadow-primary-50"
110
110
  style={{
111
- boxShadow: '0px 4px 6px -4px #0000001A, 0px 10px 15px -3px #0000001A',
112
- }}
111
+ boxShadow: '0px 4px 6px -4px #0000001A, 0px 10px 15px -3px #0000001A',
112
+ }}
113
113
  >
114
114
  <div className="grid gap-4 md:grid-cols-2 text-start">
115
- <div>
116
- <h2 className="text-sm text-secondary mb-3">First name <span className='text-primary-500'>*</span></h2>
117
- <Input
118
- placeholder="Alex"
119
- {...register('firstName')}
120
- error={errors.firstName?.message}
121
- />
115
+ <div>
116
+ <h2 className="text-sm text-secondary mb-3">First name <span className='text-primary-500'>*</span></h2>
117
+ <Input
118
+ placeholder="Alex"
119
+ {...register('firstname')}
120
+ error={errors.firstname?.message}
121
+ />
122
122
  </div>
123
123
  <div>
124
- <h2 className="text-sm text-secondary mb-3">Last name <span className='text-primary-500'>*</span></h2>
125
- <Input
126
- placeholder="Morgan"
127
- {...register('lastName')}
128
- error={errors.lastName?.message}
129
- />
124
+ <h2 className="text-sm text-secondary mb-3">Last name <span className='text-primary-500'>*</span></h2>
125
+ <Input
126
+ placeholder="Morgan"
127
+ {...register('lastname')}
128
+ error={errors.lastname?.message}
129
+ />
130
130
  </div>
131
131
  </div>
132
132
  <div>
133
- <h2 className="text-sm text-secondary mb-3">Email Address <span className='text-primary-500'>*</span></h2>
134
- <Input
135
- type="email"
136
- placeholder="you@example.com"
137
- {...register('email')}
138
- error={errors.email?.message}
139
- />
133
+ <h2 className="text-sm text-secondary mb-3">Email Address <span className='text-primary-500'>*</span></h2>
134
+ <Input
135
+ type="email"
136
+ placeholder="you@example.com"
137
+ {...register('email')}
138
+ error={errors.email?.message}
139
+ />
140
140
  </div>
141
141
  <div>
142
- <h2 className="text-sm text-secondary mb-3">Phone Number</h2>
143
- <Input
144
- type="tel"
145
- placeholder="+1 (555) 123-4567"
146
- {...register('phone')}
147
- error={errors.phone?.message}
148
- />
142
+ <h2 className="text-sm text-secondary mb-3">Phone Number</h2>
143
+ <Input
144
+ type="tel"
145
+ placeholder="+1 (555) 123-4567"
146
+ {...register('phone')}
147
+ error={errors.phone?.message}
148
+ />
149
149
  </div>
150
150
  <div className="relative">
151
151