hey-pharmacist-ecommerce 1.1.20 → 1.1.22

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.
@@ -27,6 +27,7 @@ import { Badge } from '@/components/ui/Badge';
27
27
  import { ProductCard } from '@/components/ProductCard';
28
28
  import { useProduct } from '@/hooks/useProducts';
29
29
  import { useCart } from '@/providers/CartProvider';
30
+ import { useAuth } from '@/providers/AuthProvider';
30
31
  import { formatDate, formatPrice } from '@/lib/utils/format';
31
32
  import { useNotification } from '@/providers/NotificationProvider';
32
33
  import { useRouter } from 'next/navigation';
@@ -58,6 +59,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
58
59
  const { buildPath } = useBasePath();
59
60
  const { product: productData, isLoading } = useProduct(productId);
60
61
  const { addToCart } = useCart();
62
+ const { isAuthenticated } = useAuth();
61
63
  const notification = useNotification();
62
64
 
63
65
  // State declarations
@@ -219,6 +221,15 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
219
221
  };
220
222
 
221
223
  const handleAddToCart = async () => {
224
+ if (!isAuthenticated) {
225
+ notification.error(
226
+ 'Sign-in required',
227
+ 'Please sign in to add items to your cart.'
228
+ );
229
+ router.push(buildPath(`/login?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}`));
230
+ return;
231
+ }
232
+
222
233
  if (!product || !selectedVariant) return;
223
234
 
224
235
  setIsAddingToCart(true);
@@ -414,8 +425,8 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
414
425
  type="button"
415
426
  onClick={() => setActiveImageIndex(index)}
416
427
  className={`relative aspect-square overflow-hidden rounded-lg border-2 transition-all ${activeImageIndex === index
417
- ? 'border-primary/50 ring-2 ring-primary/80 ring-offset-2 shadow-md'
418
- : 'border-slate-200 hover:border-primary/50'
428
+ ? 'border-primary/50 ring-2 ring-primary/80 ring-offset-2 shadow-md'
429
+ : 'border-slate-200 hover:border-primary/50'
419
430
  }`}
420
431
  >
421
432
  <Image
@@ -436,50 +447,49 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
436
447
 
437
448
 
438
449
  <aside className="space-y-6 lg:sticky lg:top-24">
439
- {/* Category Path */}
440
- <div className="mb-4">
450
+ {/* Category Path */}
451
+ <div className="mb-4">
441
452
  <p className="font-['Poppins',sans-serif] text-[12px] text-primary uppercase tracking-wide font-medium mb-2">
442
453
  {product.brand} • {product.parentCategories?.[0].name}
443
454
  </p>
444
455
  <h1 className="text-3xl font-['Poppins',sans-serif] font-semibold text-secondary tracking-[-1.5px] mb-3">
445
456
  {selectedVariant?.name || product.name}
446
457
  </h1>
447
- {/* Rating */}
448
- <div className="flex items-center gap-3">
449
- <div className="flex items-center gap-1">
450
- {[...Array(5)].map((_, i) => (
451
- <Star
452
- key={i}
453
- className={`size-4 ${
454
- i < Math.floor(product.rating)
458
+ {/* Rating */}
459
+ <div className="flex items-center gap-3">
460
+ <div className="flex items-center gap-1">
461
+ {[...Array(5)].map((_, i) => (
462
+ <Star
463
+ key={i}
464
+ className={`size-4 ${i < Math.floor(product.rating)
455
465
  ? 'text-[#E67E50] fill-[#E67E50]'
456
466
  : 'text-gray-300'
457
- }`}
458
- />
459
- ))}
467
+ }`}
468
+ />
469
+ ))}
470
+ </div>
471
+ <span className="font-['Poppins',sans-serif] text-[14px] text-muted">
472
+ {product.rating} ({product.reviewCount ? product.reviewCount : 0} reviews)
473
+ </span>
460
474
  </div>
461
- <span className="font-['Poppins',sans-serif] text-[14px] text-muted">
462
- {product.rating} ({product.reviewCount? product.reviewCount : 0} reviews)
463
- </span>
464
- </div>
465
475
 
466
476
  <div className="flex items-center gap-3 mb-6 pb-6 border-b-2 border-gray-100">
467
477
  <span className="font-['Poppins',sans-serif] font-bold text-[40px] text-[#E67E50]">
468
478
  ${variantPrice.toFixed(2)}
469
479
  </span>
470
480
  {variantComparePrice && variantComparePrice > variantPrice && (
471
- <>
472
- <span className="font-['Poppins',sans-serif] text-[24px] text-muted line-through">
473
- ${variantComparePrice.toFixed(2)}
474
- </span>
475
- <div className="px-3 py-1 rounded-full bg-[#E67E50]/10">
476
- <span className="font-['Poppins',sans-serif] font-semibold text-[13px] text-[#E67E50]">
477
- Save ${formatPrice(variantComparePrice - variantPrice)}
481
+ <>
482
+ <span className="font-['Poppins',sans-serif] text-[24px] text-muted line-through">
483
+ ${variantComparePrice.toFixed(2)}
478
484
  </span>
479
- </div>
480
- </>
481
- )}
482
- </div>
485
+ <div className="px-3 py-1 rounded-full bg-[#E67E50]/10">
486
+ <span className="font-['Poppins',sans-serif] font-semibold text-[13px] text-[#E67E50]">
487
+ Save ${formatPrice(variantComparePrice - variantPrice)}
488
+ </span>
489
+ </div>
490
+ </>
491
+ )}
492
+ </div>
483
493
 
484
494
  {/* Stock Status */}
485
495
  {selectedVariant && (
@@ -494,26 +504,26 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
494
504
  </span>
495
505
  </>
496
506
  ) : selectedVariant.inventoryCount <= 10 ? (
497
- <>
498
- <div className="size-3 rounded-full bg-primary animate-pulse" />
499
- <span className="font-['Poppins',sans-serif] text-[13px] text-primary font-medium">
500
- Only {selectedVariant.inventoryCount} left in stock - Order soon!
501
- </span>
502
- </>
503
- ) : (
504
- <>
505
- <div className="size-3 rounded-full bg-green-500" />
506
- <span className="font-['Poppins',sans-serif] text-[13px] text-green-600 font-medium">
507
- In Stock
508
- </span>
509
- </>
510
- )}
511
-
507
+ <>
508
+ <div className="size-3 rounded-full bg-primary animate-pulse" />
509
+ <span className="font-['Poppins',sans-serif] text-[13px] text-primary font-medium">
510
+ Only {selectedVariant.inventoryCount} left in stock - Order soon!
511
+ </span>
512
+ </>
513
+ ) : (
514
+ <>
515
+ <div className="size-3 rounded-full bg-green-500" />
516
+ <span className="font-['Poppins',sans-serif] text-[13px] text-green-600 font-medium">
517
+ In Stock
518
+ </span>
519
+ </>
520
+ )}
521
+
522
+ </div>
523
+ <p className="font-['Poppins',sans-serif] text-[12px] text-muted">
524
+ SKU: {selectedVariant?.sku}
525
+ </p>
512
526
  </div>
513
- <p className="font-['Poppins',sans-serif] text-[12px] text-muted">
514
- SKU: {selectedVariant?.sku}
515
- </p>
516
- </div>
517
527
  )}
518
528
 
519
529
  {/* Description */}
@@ -526,7 +536,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
526
536
  {/* Variant Selector with Images */}
527
537
  {product?.productVariants && product.productVariants.length > 0 && (
528
538
  <div className="mb-6">
529
- <h3 className="font-['Poppins',sans-serif] font-semibold text-[14px] text-secondary mb-3">
539
+ <h3 className="font-['Poppins',sans-serif] font-semibold text-[14px] text-secondary mb-3">
530
540
  Select Variant
531
541
  </h3>
532
542
  <div className="flex flex-wrap gap-3">
@@ -541,11 +551,10 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
541
551
  key={variant.id}
542
552
  type="button"
543
553
  onClick={() => handleVariantSelect(variant)}
544
- className={`flex items-start gap-3 px-4 py-2.5 rounded-xl border-2 transition-all ${
545
- isSelected
554
+ className={`flex items-start gap-3 px-4 py-2.5 rounded-xl border-2 transition-all ${isSelected
546
555
  ? 'border-primary bg-primary/5'
547
556
  : 'border-gray-200 hover:border-primary/50'
548
- }`}
557
+ }`}
549
558
  >
550
559
  <div className={`relative h-12 w-12 shrink-0 overflow-hidden rounded-full border-2 ${isSelected ? 'border-primary' : 'border-slate-200'}`}>
551
560
  <Image
@@ -577,9 +586,9 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
577
586
 
578
587
  {/* Quantity Selector */}
579
588
  <div className="mb-8">
580
- <h3 className="font-['Poppins',sans-serif] font-semibold text-[14px] text-secondary mb-3">
581
- Quantity
582
- </h3>
589
+ <h3 className="font-['Poppins',sans-serif] font-semibold text-[14px] text-secondary mb-3">
590
+ Quantity
591
+ </h3>
583
592
  <div className="flex items-center gap-3">
584
593
  <div className="flex items-center gap-4 bg-gray-100 rounded-full px-6 py-3">
585
594
 
@@ -592,20 +601,20 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
592
601
  >
593
602
  -
594
603
  </button>
595
- <span className="font-['Poppins',sans-serif] font-semibold text-[16px] text-secondary min-w-[30px] text-center">
604
+ <span className="font-['Poppins',sans-serif] font-semibold text-[16px] text-secondary min-w-[30px] text-center">
596
605
  {quantity}
597
- </span>
598
- <button
599
- onClick={() => setQuantity(Math.min(selectedVariant?.inventoryCount || product.inventoryCount || 999, quantity + 1))}
600
- disabled={quantity >= (selectedVariant?.inventoryCount || product.inventoryCount || 0)}
601
- className="font-['Poppins',sans-serif] font-bold text-[18px] text-secondary hover:text-primary transition-colors disabled:opacity-50"
602
- >
603
- +
604
- </button>
606
+ </span>
607
+ <button
608
+ onClick={() => setQuantity(Math.min(selectedVariant?.inventoryCount || product.inventoryCount || 999, quantity + 1))}
609
+ disabled={quantity >= (selectedVariant?.inventoryCount || product.inventoryCount || 0)}
610
+ className="font-['Poppins',sans-serif] font-bold text-[18px] text-secondary hover:text-primary transition-colors disabled:opacity-50"
611
+ >
612
+ +
613
+ </button>
605
614
  </div>
606
615
  {selectedVariant && (
607
616
  <span className="text-sm text-slate-500">
608
- {selectedVariant.inventoryCount || product.inventoryCount || 0} available
617
+ {selectedVariant.inventoryCount || 0} available
609
618
  </span>
610
619
  )}
611
620
  </div>
@@ -640,68 +649,68 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
640
649
  }
641
650
  </button>
642
651
  <button className="sm:w-auto px-6 py-4 rounded-full border-2 border-primary hover:bg-primary/5 transition-all flex items-center justify-center"
643
- onClick={handleToggleFavorite}>
644
- <Heart className={`h-4 w-4 ${isFavorited ? 'fill-red-500 text-red-500' : 'text-primary'}`}/>
645
- </button>
652
+ onClick={handleToggleFavorite}>
653
+ <Heart className={`h-4 w-4 ${isFavorited ? 'fill-red-500 text-red-500' : 'text-primary'}`} />
654
+ </button>
646
655
  </div>
647
656
 
648
657
  </div>
649
658
 
650
659
  {/* Benefits */}
651
- <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 p-6 bg-linear-to-br from-[#5B9BD5]/5 to-[#2B4B7C]/5 rounded-[24px]">
652
- <div className="flex items-start gap-3">
653
- <div className="size-10 rounded-full bg-white flex items-center justify-center shrink-0">
654
- <Truck className="size-5 text-primary" />
655
- </div>
656
- <div>
657
- <p className="font-['Poppins',sans-serif] font-semibold text-[12px] text-secondary mb-1">
658
- Free Shipping
659
- </p>
660
- <p className="font-['Poppins',sans-serif] text-[11px] text-muted">
661
- On all orders
662
- </p>
663
- </div>
664
- </div>
665
- <div className="flex items-start gap-3">
666
- <div className="size-10 rounded-full bg-white flex items-center justify-center shrink-0">
667
- <RotateCcw className="size-5 text-primary" />
668
- </div>
669
- <div>
670
- <p className="font-['Poppins',sans-serif] font-semibold text-[12px] text-secondary mb-1">
671
- Easy Returns
672
- </p>
673
- <p className="font-['Poppins',sans-serif] text-[11px] text-muted">
674
- 30-day return policy
675
- </p>
676
- </div>
677
- </div>
678
- <div className="flex items-start gap-3">
679
- <div className="size-10 rounded-full bg-white flex items-center justify-center shrink-0">
680
- <Shield className="size-5 text-primary" />
660
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 p-6 bg-linear-to-br from-[#5B9BD5]/5 to-[#2B4B7C]/5 rounded-[24px]">
661
+ <div className="flex items-start gap-3">
662
+ <div className="size-10 rounded-full bg-white flex items-center justify-center shrink-0">
663
+ <Truck className="size-5 text-primary" />
664
+ </div>
665
+ <div>
666
+ <p className="font-['Poppins',sans-serif] font-semibold text-[12px] text-secondary mb-1">
667
+ Free Shipping
668
+ </p>
669
+ <p className="font-['Poppins',sans-serif] text-[11px] text-muted">
670
+ On all orders
671
+ </p>
672
+ </div>
681
673
  </div>
682
- <div>
683
- <p className="font-['Poppins',sans-serif] font-semibold text-[12px] text-secondary mb-1">
684
- Secure Checkout
685
- </p>
686
- <p className="font-['Poppins',sans-serif] text-[11px] text-muted">
687
- Safe & protected
688
- </p>
674
+ <div className="flex items-start gap-3">
675
+ <div className="size-10 rounded-full bg-white flex items-center justify-center shrink-0">
676
+ <RotateCcw className="size-5 text-primary" />
677
+ </div>
678
+ <div>
679
+ <p className="font-['Poppins',sans-serif] font-semibold text-[12px] text-secondary mb-1">
680
+ Easy Returns
681
+ </p>
682
+ <p className="font-['Poppins',sans-serif] text-[11px] text-muted">
683
+ 30-day return policy
684
+ </p>
685
+ </div>
689
686
  </div>
690
- </div>
691
- <div className="flex items-start gap-3">
692
- <div className="size-10 rounded-full bg-white flex items-center justify-center shrink-0">
693
- <Package className="size-5 text-primary" />
687
+ <div className="flex items-start gap-3">
688
+ <div className="size-10 rounded-full bg-white flex items-center justify-center shrink-0">
689
+ <Shield className="size-5 text-primary" />
690
+ </div>
691
+ <div>
692
+ <p className="font-['Poppins',sans-serif] font-semibold text-[12px] text-secondary mb-1">
693
+ Secure Checkout
694
+ </p>
695
+ <p className="font-['Poppins',sans-serif] text-[11px] text-muted">
696
+ Safe & protected
697
+ </p>
698
+ </div>
694
699
  </div>
695
- <div>
696
- <p className="font-['Poppins',sans-serif] font-semibold text-[12px] text-secondary mb-1">
697
- Quality Guaranteed
698
- </p>
699
- <p className="font-['Poppins',sans-serif] text-[11px] text-muted">
700
- Premium materials
701
- </p>
700
+ <div className="flex items-start gap-3">
701
+ <div className="size-10 rounded-full bg-white flex items-center justify-center shrink-0">
702
+ <Package className="size-5 text-primary" />
703
+ </div>
704
+ <div>
705
+ <p className="font-['Poppins',sans-serif] font-semibold text-[12px] text-secondary mb-1">
706
+ Quality Guaranteed
707
+ </p>
708
+ <p className="font-['Poppins',sans-serif] text-[11px] text-muted">
709
+ Premium materials
710
+ </p>
711
+ </div>
702
712
  </div>
703
713
  </div>
704
- </div>
705
714
  </aside>
706
715
  </div>
707
716
 
@@ -713,11 +722,10 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
713
722
  <button
714
723
  key={tab}
715
724
  onClick={() => setActiveTab(tab)}
716
- className={`font-['Poppins',sans-serif] font-medium text-[14px] px-6 py-4 transition-all ${
717
- activeTab === tab
718
- ? 'text-primary border-b-2 border-primary -mb-0.5'
719
- : 'text-muted hover:text-primary'
720
- }`}
725
+ className={`font-['Poppins',sans-serif] font-medium text-[14px] px-6 py-4 transition-all ${activeTab === tab
726
+ ? 'text-primary border-b-2 border-primary -mb-0.5'
727
+ : 'text-muted hover:text-primary'
728
+ }`}
721
729
  >
722
730
  {tab.charAt(0).toUpperCase() + tab.slice(1)}
723
731
  </button>
@@ -750,7 +758,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
750
758
  Shipped from:
751
759
  </h4>
752
760
  <p className="font-['Poppins',sans-serif] text-[14px] text-muted">
753
- Local pharmacy distribution center
761
+ Local pharmacy distribution center
754
762
  </p>
755
763
  </div>
756
764
  </div>
@@ -771,14 +779,14 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
771
779
  </div>
772
780
 
773
781
 
774
- {/* Related Products */}
775
- {relatedProducts.length > 0 && (
776
- <div>
777
- <h2 className="font-['Poppins',sans-serif] font-semibold text-secondary mb-8 text-2xl">
778
- You May Also Like
779
- </h2>
780
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
781
- {relatedProducts.map(relatedProduct => (
782
+ {/* Related Products */}
783
+ {relatedProducts.length > 0 && (
784
+ <div>
785
+ <h2 className="font-['Poppins',sans-serif] font-semibold text-secondary mb-8 text-2xl">
786
+ You May Also Like
787
+ </h2>
788
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
789
+ {relatedProducts.map(relatedProduct => (
782
790
 
783
791
  <ProductCard
784
792
  key={relatedProduct.id}
@@ -10,7 +10,8 @@
10
10
  --color-primary-300: rgb(var(--color-primary-300));
11
11
  --color-primary-400: rgb(var(--color-primary-400));
12
12
  --color-primary-500: rgb(var(--color-primary-500));
13
- --color-primary-600: rgb(var(--color-primary-600));
13
+ --color-primary-600: var(--color-primary-600, #E67E50);
14
+ ;
14
15
  --color-primary-700: rgb(var(--color-primary-700));
15
16
  --color-primary-800: rgb(var(--color-primary-800));
16
17
  --color-primary-900: rgb(var(--color-primary-900));
@@ -57,35 +58,42 @@
57
58
  0% {
58
59
  opacity: 0;
59
60
  }
61
+
60
62
  100% {
61
63
  opacity: 1;
62
64
  }
63
65
  }
66
+
64
67
  @keyframes slideUp {
65
68
  0% {
66
69
  transform: translateY(10px);
67
70
  opacity: 0;
68
71
  }
72
+
69
73
  100% {
70
74
  transform: translateY(0);
71
75
  opacity: 1;
72
76
  }
73
77
  }
78
+
74
79
  @keyframes slideDown {
75
80
  0% {
76
81
  transform: translateY(-10px);
77
82
  opacity: 0;
78
83
  }
84
+
79
85
  100% {
80
86
  transform: translateY(0);
81
87
  opacity: 1;
82
88
  }
83
89
  }
90
+
84
91
  @keyframes scaleIn {
85
92
  0% {
86
93
  transform: scale(0.95);
87
94
  opacity: 0;
88
95
  }
96
+
89
97
  100% {
90
98
  transform: scale(1);
91
99
  opacity: 1;
@@ -102,6 +110,7 @@
102
110
  color utility to any element that depends on these defaults.
103
111
  */
104
112
  @layer base {
113
+
105
114
  *,
106
115
  ::after,
107
116
  ::before,
@@ -129,21 +138,23 @@
129
138
  * {
130
139
  @apply border-gray-200;
131
140
  }
132
-
141
+
133
142
  body {
134
143
  @apply bg-gray-50 text-gray-900 antialiased;
135
144
  }
136
145
  }
137
146
 
138
147
  @layer utilities {
139
-
148
+
140
149
  @keyframes gradient {
141
150
  0% {
142
151
  background-position: 0% 50%;
143
152
  }
153
+
144
154
  50% {
145
155
  background-position: 100% 50%;
146
156
  }
157
+
147
158
  100% {
148
159
  background-position: 0% 50%;
149
160
  }
@@ -166,5 +177,4 @@
166
177
 
167
178
  ::-webkit-scrollbar-thumb:hover {
168
179
  @apply bg-gray-400;
169
- }
170
-
180
+ }