hey-pharmacist-ecommerce 1.1.42 → 1.1.44
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.
- package/README.md +70 -8
- package/dist/index.d.mts +2550 -3081
- package/dist/index.d.ts +2550 -3081
- package/dist/index.js +506 -399
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +506 -397
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/AccountOverviewTab.tsx +5 -5
- package/src/components/AccountReviewsTab.tsx +4 -4
- package/src/components/CartItem.tsx +15 -15
- package/src/components/Header.tsx +1 -1
- package/src/components/Notification.tsx +3 -3
- package/src/components/OrderCard.tsx +1 -1
- package/src/components/ProductCard.tsx +11 -11
- package/src/components/QuickViewModal.tsx +16 -16
- package/src/components/RatingDistribution.tsx +2 -2
- package/src/components/ReviewCard.tsx +2 -2
- package/src/components/ReviewForm.tsx +3 -3
- package/src/components/ReviewPromptBanner.tsx +4 -4
- package/src/components/ReviewsList.tsx +9 -11
- package/src/components/StarRating.tsx +3 -3
- package/src/hooks/useProducts.ts +0 -1
- package/src/hooks/useSmartSearch.ts +68 -0
- package/src/lib/Apis/api.ts +1 -1
- package/src/lib/Apis/apis/analytics-api.ts +809 -0
- package/src/lib/Apis/apis/notifications-api.ts +8 -6
- package/src/lib/Apis/apis/products-api.ts +390 -15
- package/src/lib/Apis/apis/stores-api.ts +26 -149
- package/src/lib/Apis/apis/web-hooks-api.ts +8 -17
- package/src/lib/Apis/models/analytics-period-dto.ts +45 -0
- package/src/lib/Apis/models/appointment-overview-dto.ts +71 -0
- package/src/lib/Apis/models/category-sales-dto.ts +51 -0
- package/src/lib/Apis/models/create-store-dto.ts +12 -0
- package/src/lib/Apis/models/customer-overview-dto.ts +96 -0
- package/src/lib/Apis/models/{country-stats-response-dto.ts → customer-segment-dto.ts} +12 -13
- package/src/lib/Apis/models/dashboard-overview-dto.ts +70 -0
- package/src/lib/Apis/models/discount-overview-dto.ts +71 -0
- package/src/lib/Apis/models/group-with-no-users-dto.ts +0 -6
- package/src/lib/Apis/models/group-with-users-dto.ts +0 -6
- package/src/lib/Apis/models/index.ts +29 -38
- package/src/lib/Apis/models/{single-recipient-dto.ts → inline-response200.ts} +11 -10
- package/src/lib/Apis/models/{create-contact-dto.ts → inline-response2001.ts} +11 -11
- package/src/lib/Apis/models/inventory-alert-dto.ts +67 -0
- package/src/lib/Apis/models/notification-dto.ts +107 -0
- package/src/lib/Apis/models/notifications-paginated-response-dto.ts +58 -0
- package/src/lib/Apis/models/order-overview-dto.ts +89 -0
- package/src/lib/Apis/models/{single-link-stats-dto.ts → order-status-count-dto.ts} +10 -10
- package/src/lib/Apis/models/order.ts +0 -6
- package/src/lib/Apis/models/payout-history-item-dto.ts +45 -0
- package/src/lib/Apis/models/{single-country-stats-dto.ts → popular-time-slot-dto.ts} +10 -10
- package/src/lib/Apis/models/populated-order.ts +0 -6
- package/src/lib/Apis/models/preference-update-item.ts +1 -0
- package/src/lib/Apis/models/product-overview-dto.ts +90 -0
- package/src/lib/Apis/models/product.ts +6 -0
- package/src/lib/Apis/models/{create-contact-list-dto.ts → products-aidraft-body.ts} +4 -4
- package/src/lib/Apis/models/{schedule-campaign-draft-dto.ts → products-processimage-body.ts} +5 -5
- package/src/lib/Apis/models/{contact-full-response-dto.ts → rating-distribution-dto.ts} +9 -10
- package/src/lib/Apis/models/recent-review-dto.ts +63 -0
- package/src/lib/Apis/models/review-overview-dto.ts +65 -0
- package/src/lib/Apis/models/store-balance-dto.ts +39 -0
- package/src/lib/Apis/models/store-entity.ts +12 -0
- package/src/lib/Apis/models/{link-stats-response-dto.ts → store-finance-overview-dto.ts} +10 -15
- package/src/lib/Apis/models/store.ts +12 -0
- package/src/lib/Apis/models/{add-contact-to-list-dto.ts → time-series-point-dto.ts} +9 -9
- package/src/lib/Apis/models/{create-email-template-dto.ts → top-customer-dto.ts} +20 -14
- package/src/lib/Apis/models/{marketing-list-contact-dto.ts → top-discount-dto.ts} +12 -12
- package/src/lib/Apis/models/{single-browser-stats-dto.ts → top-product-dto.ts} +18 -12
- package/src/lib/Apis/models/{marketing-campaign-content-dto.ts → unread-count-dto.ts} +6 -6
- package/src/lib/Apis/models/update-store-dto.ts +12 -0
- package/src/lib/Apis/models/update-user-dto.ts +0 -6
- package/src/lib/Apis/models/user-group.ts +0 -6
- package/src/lib/Apis/models/user-with-no-id.ts +0 -6
- package/src/lib/Apis/sharedConfig.ts +1 -1
- package/src/screens/CartScreen.tsx +10 -10
- package/src/screens/CheckoutScreen.tsx +12 -12
- package/src/screens/OrderReviewsScreen.tsx +6 -6
- package/src/screens/ProductDetailScreen.tsx +40 -40
- package/src/screens/SearchResultsScreen.tsx +17 -21
- package/src/screens/ShopScreen.tsx +20 -82
- package/src/lib/Apis/apis/marketing-api.ts +0 -3099
- package/src/lib/Apis/models/api-key-info-dto.ts +0 -49
- package/src/lib/Apis/models/browser-stats-response-dto.ts +0 -40
- package/src/lib/Apis/models/campaign-content-response-dto.ts +0 -40
- package/src/lib/Apis/models/campaign-draft-dto.ts +0 -175
- package/src/lib/Apis/models/campaign-draft-response-dto.ts +0 -40
- package/src/lib/Apis/models/campaign-draft-schedule-dto.ts +0 -49
- package/src/lib/Apis/models/campaign-draft-schedule-response-dto.ts +0 -40
- package/src/lib/Apis/models/campaign-draft-sending-dto.ts +0 -43
- package/src/lib/Apis/models/campaign-draft-sending-response-dto.ts +0 -40
- package/src/lib/Apis/models/contact-aggregated-stats-response-dto.ts +0 -40
- package/src/lib/Apis/models/contact-full-dto.ts +0 -93
- package/src/lib/Apis/models/contact-list-stats-response-dto.ts +0 -40
- package/src/lib/Apis/models/contact-lists-response-dto.ts +0 -40
- package/src/lib/Apis/models/create-marketing-campaign-dto.ts +0 -81
- package/src/lib/Apis/models/email-template-response-dto.ts +0 -117
- package/src/lib/Apis/models/general-stats-response-dto.ts +0 -40
- package/src/lib/Apis/models/send-test-email-dto.ts +0 -28
- package/src/lib/Apis/models/single-contact-aggregated-stats-dto.ts +0 -129
- package/src/lib/Apis/models/single-contact-list-stats-dto.ts +0 -117
- package/src/lib/Apis/models/single-general-stats.ts +0 -153
- package/src/lib/Apis/models/store-api-keys-response-dto.ts +0 -34
- package/src/lib/Apis/models/update-api-keys-dto.ts +0 -39
- package/src/lib/Apis/models/update-campaign-draft-content-dto.ts +0 -27
- package/src/lib/Apis/models/update-marketing-camp-draft-dto.ts +0 -81
|
@@ -361,7 +361,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
361
361
|
<div className="max-w-[1400px] mx-auto px-8 md:px-12">
|
|
362
362
|
<button
|
|
363
363
|
onClick={() => router.push(buildPath('/shop'))}
|
|
364
|
-
className="flex items-center gap-2
|
|
364
|
+
className="flex items-center gap-2 text-[13px] text-hmuted hover:text-hprimary transition-colors"
|
|
365
365
|
>
|
|
366
366
|
<ChevronLeft className="size-4" />
|
|
367
367
|
Back to Shop
|
|
@@ -405,22 +405,22 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
405
405
|
|
|
406
406
|
<div className="absolute top-6 left-6 flex flex-col gap-3">
|
|
407
407
|
{discount > 0 && (
|
|
408
|
-
<div className="bg-
|
|
409
|
-
<span className="font-
|
|
408
|
+
<div className="bg-hsecondary text-white rounded-full px-4 py-2">
|
|
409
|
+
<span className="font-bold text-[12px] uppercase tracking-wide">
|
|
410
410
|
Save {discount}%
|
|
411
411
|
</span>
|
|
412
412
|
</div>
|
|
413
413
|
)}
|
|
414
414
|
{/* {product.bestseller && (
|
|
415
415
|
<div className="bg-hsecondary text-white rounded-full px-4 py-2">
|
|
416
|
-
<span className="font-
|
|
416
|
+
<span className="font-semibold text-[11px] uppercase tracking-wide">
|
|
417
417
|
Bestseller
|
|
418
418
|
</span>
|
|
419
419
|
</div>
|
|
420
420
|
)}
|
|
421
421
|
{product.newArrival && (
|
|
422
422
|
<div className="bg-hprimary text-white rounded-full px-4 py-2">
|
|
423
|
-
<span className="font-
|
|
423
|
+
<span className="font-semibold text-[11px] uppercase tracking-wide">
|
|
424
424
|
New Arrival
|
|
425
425
|
</span>
|
|
426
426
|
</div>
|
|
@@ -461,10 +461,10 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
461
461
|
<aside className="space-y-6 lg:sticky lg:top-24">
|
|
462
462
|
{/* Category Path */}
|
|
463
463
|
<div className="mb-4">
|
|
464
|
-
<p className="
|
|
464
|
+
<p className="text-[12px] text-hprimary uppercase tracking-wide font-medium mb-2">
|
|
465
465
|
{product.brand} • {product.categoryIds?.[0]?.name || 'Uncategorized'}
|
|
466
466
|
</p>
|
|
467
|
-
<h1 className="text-3xl font-
|
|
467
|
+
<h1 className="text-3xl font-semibold text-hsecondary tracking-[-1.5px] mb-3">
|
|
468
468
|
{selectedVariant?.name || product.name}
|
|
469
469
|
</h1>
|
|
470
470
|
{/* Rating */}
|
|
@@ -474,28 +474,28 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
474
474
|
<Star
|
|
475
475
|
key={i}
|
|
476
476
|
className={`size-4 ${i < Math.floor(reviewStats.averageRating)
|
|
477
|
-
? 'text-
|
|
477
|
+
? 'text-haccent-500 fill-haccent-500'
|
|
478
478
|
: 'text-gray-300'
|
|
479
479
|
}`}
|
|
480
480
|
/>
|
|
481
481
|
))}
|
|
482
482
|
</div>
|
|
483
|
-
<span className="
|
|
483
|
+
<span className="text-[14px] text-hmuted">
|
|
484
484
|
{reviewStats.averageRating} ({reviewStats.reviewCount} {reviewStats.reviewCount === 1 ? 'review' : 'reviews'})
|
|
485
485
|
</span>
|
|
486
486
|
</div>
|
|
487
487
|
{selectedVariant && (
|
|
488
488
|
<div className="flex items-center gap-3 mb-6 pb-6 border-b-2 border-gray-100">
|
|
489
|
-
<span className="font-
|
|
489
|
+
<span className="font-bold text-[40px] text-hsecondary">
|
|
490
490
|
${variantPrice.toFixed(2)}
|
|
491
491
|
</span>
|
|
492
492
|
{variantComparePrice && variantComparePrice > variantPrice && (
|
|
493
493
|
<>
|
|
494
|
-
<span className="
|
|
494
|
+
<span className="text-[24px] text-hmuted line-through">
|
|
495
495
|
${variantComparePrice.toFixed(2)}
|
|
496
496
|
</span>
|
|
497
|
-
<div className="px-3 py-1 rounded-full bg-
|
|
498
|
-
<span className="font-
|
|
497
|
+
<div className="px-3 py-1 rounded-full bg-hsecondary/10">
|
|
498
|
+
<span className="font-semibold text-[13px] text-hsecondary">
|
|
499
499
|
Save ${formatPrice(variantComparePrice - variantPrice)}
|
|
500
500
|
</span>
|
|
501
501
|
</div>
|
|
@@ -510,28 +510,28 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
510
510
|
{selectedVariant.inventoryStatus === ProductVariantInventoryStatusEnum.OUTOFSTOCK ? (
|
|
511
511
|
<>
|
|
512
512
|
<div className="size-3 rounded-full bg-red-500" />
|
|
513
|
-
<span className="
|
|
513
|
+
<span className="text-[13px] text-red-600 font-medium">
|
|
514
514
|
Out of Stock
|
|
515
515
|
</span>
|
|
516
516
|
</>
|
|
517
517
|
) : selectedVariant.inventoryStatus === ProductVariantInventoryStatusEnum.LOWSTOCK || selectedVariant.inventoryCount <= 10 ? (
|
|
518
518
|
<>
|
|
519
519
|
<div className="size-3 rounded-full bg-hprimary animate-pulse" />
|
|
520
|
-
<span className="
|
|
520
|
+
<span className="text-[13px] text-hprimary font-medium">
|
|
521
521
|
Only {selectedVariant.inventoryCount} left in stock - Order soon!
|
|
522
522
|
</span>
|
|
523
523
|
</>
|
|
524
524
|
) : (
|
|
525
525
|
<>
|
|
526
526
|
<div className="size-3 rounded-full bg-green-500" />
|
|
527
|
-
<span className="
|
|
527
|
+
<span className="text-[13px] text-green-600 font-medium">
|
|
528
528
|
In Stock
|
|
529
529
|
</span>
|
|
530
530
|
</>
|
|
531
531
|
)}
|
|
532
532
|
|
|
533
533
|
</div>
|
|
534
|
-
<p className="
|
|
534
|
+
<p className="text-[12px] text-hmuted">
|
|
535
535
|
SKU: {selectedVariant?.sku || product?.sku}
|
|
536
536
|
</p>
|
|
537
537
|
</div>
|
|
@@ -540,7 +540,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
540
540
|
{/* Description */}
|
|
541
541
|
{product.description && (
|
|
542
542
|
<div
|
|
543
|
-
className="
|
|
543
|
+
className="text-[14px] text-hmuted leading-[1.7] mb-8 max-w-full overflow-hidden break-words"
|
|
544
544
|
dangerouslySetInnerHTML={{ __html: product.description }}
|
|
545
545
|
/>
|
|
546
546
|
)}
|
|
@@ -548,7 +548,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
548
548
|
{/* Variant Selector with Images */}
|
|
549
549
|
{product?.variants && product.variants.length > 0 && (
|
|
550
550
|
<div className="mb-6">
|
|
551
|
-
<h3 className="font-
|
|
551
|
+
<h3 className="font-semibold text-[14px] text-hsecondary mb-3">
|
|
552
552
|
Select Variant
|
|
553
553
|
</h3>
|
|
554
554
|
<div className="flex flex-wrap gap-3">
|
|
@@ -598,7 +598,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
598
598
|
|
|
599
599
|
{/* Quantity Selector */}
|
|
600
600
|
<div className="mb-8">
|
|
601
|
-
<h3 className="font-
|
|
601
|
+
<h3 className="font-semibold text-[14px] text-hsecondary mb-3">
|
|
602
602
|
Quantity
|
|
603
603
|
</h3>
|
|
604
604
|
<div className="flex items-center gap-3">
|
|
@@ -608,18 +608,18 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
608
608
|
type="button"
|
|
609
609
|
onClick={() => setQuantity((current) => Math.max(1, current - 1))}
|
|
610
610
|
|
|
611
|
-
className="font-
|
|
611
|
+
className="font-bold text-[18px] text-hsecondary hover:text-hprimary transition-colors"
|
|
612
612
|
aria-label="Increase quantity"
|
|
613
613
|
>
|
|
614
614
|
-
|
|
615
615
|
</button>
|
|
616
|
-
<span className="font-
|
|
616
|
+
<span className="font-semibold text-[16px] text-hsecondary min-w-[30px] text-center">
|
|
617
617
|
{quantity}
|
|
618
618
|
</span>
|
|
619
619
|
<button
|
|
620
620
|
onClick={() => setQuantity(Math.min(selectedVariant?.inventoryCount || product.inventoryCount || 999, quantity + 1))}
|
|
621
621
|
disabled={quantity >= (selectedVariant?.inventoryCount || product.inventoryCount || 0)}
|
|
622
|
-
className="font-
|
|
622
|
+
className="font-bold text-[18px] text-hsecondary hover:text-hprimary transition-colors disabled:opacity-50"
|
|
623
623
|
>
|
|
624
624
|
+
|
|
625
625
|
</button>
|
|
@@ -635,7 +635,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
635
635
|
{/* Action Buttons */}
|
|
636
636
|
<div className="flex flex-col sm:flex-row gap-3 mb-8">
|
|
637
637
|
<button
|
|
638
|
-
className="flex-1 font-
|
|
638
|
+
className="flex-1 font-medium text-[14px] px-8 py-4 rounded-full transition-all duration-300 flex items-center justify-center gap-3 bg-hsecondary text-white hover:bg-hsecondary/80 hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed"
|
|
639
639
|
onClick={handleAddToCart}
|
|
640
640
|
disabled={!selectedVariant || selectedVariant.inventoryStatus === ProductVariantInventoryStatusEnum.OUTOFSTOCK || isAddingToCart}
|
|
641
641
|
>
|
|
@@ -675,10 +675,10 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
675
675
|
<Truck className="size-5 text-hprimary" />
|
|
676
676
|
</div>
|
|
677
677
|
<div>
|
|
678
|
-
<p className="font-
|
|
678
|
+
<p className="font-semibold text-[12px] text-hsecondary mb-1">
|
|
679
679
|
Free Shipping
|
|
680
680
|
</p>
|
|
681
|
-
<p className="
|
|
681
|
+
<p className="text-[11px] text-hmuted">
|
|
682
682
|
On all orders
|
|
683
683
|
</p>
|
|
684
684
|
</div>
|
|
@@ -688,10 +688,10 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
688
688
|
<RotateCcw className="size-5 text-hprimary" />
|
|
689
689
|
</div>
|
|
690
690
|
<div>
|
|
691
|
-
<p className="font-
|
|
691
|
+
<p className="font-semibold text-[12px] text-hsecondary mb-1">
|
|
692
692
|
Easy Returns
|
|
693
693
|
</p>
|
|
694
|
-
<p className="
|
|
694
|
+
<p className="text-[11px] text-hmuted">
|
|
695
695
|
30-day return policy
|
|
696
696
|
</p>
|
|
697
697
|
</div>
|
|
@@ -701,10 +701,10 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
701
701
|
<Shield className="size-5 text-hprimary" />
|
|
702
702
|
</div>
|
|
703
703
|
<div>
|
|
704
|
-
<p className="font-
|
|
704
|
+
<p className="font-semibold text-[12px] text-hsecondary mb-1">
|
|
705
705
|
Secure Checkout
|
|
706
706
|
</p>
|
|
707
|
-
<p className="
|
|
707
|
+
<p className="text-[11px] text-hmuted">
|
|
708
708
|
Safe & protected
|
|
709
709
|
</p>
|
|
710
710
|
</div>
|
|
@@ -714,10 +714,10 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
714
714
|
<Package className="size-5 text-hprimary" />
|
|
715
715
|
</div>
|
|
716
716
|
<div>
|
|
717
|
-
<p className="font-
|
|
717
|
+
<p className="font-semibold text-[12px] text-hsecondary mb-1">
|
|
718
718
|
Quality Guaranteed
|
|
719
719
|
</p>
|
|
720
|
-
<p className="
|
|
720
|
+
<p className="text-[11px] text-hmuted">
|
|
721
721
|
Premium materials
|
|
722
722
|
</p>
|
|
723
723
|
</div>
|
|
@@ -734,7 +734,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
734
734
|
<button
|
|
735
735
|
key={tab}
|
|
736
736
|
onClick={() => setActiveTab(tab)}
|
|
737
|
-
className={`font-
|
|
737
|
+
className={`font-medium text-[14px] px-6 py-4 transition-all ${activeTab === tab
|
|
738
738
|
? 'text-hprimary border-b-2 border-hprimary -mb-0.5'
|
|
739
739
|
: 'text-hmuted hover:text-hprimary'
|
|
740
740
|
}`}
|
|
@@ -747,29 +747,29 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
747
747
|
<div className="bg-white rounded-[24px] p-8 border-2 border-gray-100">
|
|
748
748
|
{activeTab === 'description' && (
|
|
749
749
|
<div>
|
|
750
|
-
<h3 className="font-
|
|
750
|
+
<h3 className="font-semibold text-hsecondary mb-4">
|
|
751
751
|
Product Description
|
|
752
752
|
</h3>
|
|
753
753
|
<div
|
|
754
|
-
className="
|
|
754
|
+
className="text-[14px] text-hmuted leading-[1.8] mb-4 max-w-full overflow-hidden break-words"
|
|
755
755
|
dangerouslySetInnerHTML={{ __html: product.description }}
|
|
756
756
|
/>
|
|
757
757
|
|
|
758
758
|
<div className="mt-6">
|
|
759
|
-
<h4 className="font-
|
|
759
|
+
<h4 className="font-semibold text-[13px] text-hsecondary mb-3">
|
|
760
760
|
Last updated:
|
|
761
761
|
</h4>
|
|
762
|
-
<p className="
|
|
762
|
+
<p className="text-[14px] text-hmuted">
|
|
763
763
|
{lastUpdatedLabel}
|
|
764
764
|
</p>
|
|
765
765
|
</div>
|
|
766
766
|
|
|
767
767
|
{/* shipped from */}
|
|
768
768
|
<div className="mt-6">
|
|
769
|
-
<h4 className="font-
|
|
769
|
+
<h4 className="font-semibold text-[13px] text-hsecondary mb-3">
|
|
770
770
|
Shipped from:
|
|
771
771
|
</h4>
|
|
772
|
-
<p className="
|
|
772
|
+
<p className="text-[14px] text-hmuted">
|
|
773
773
|
Local pharmacy distribution center
|
|
774
774
|
</p>
|
|
775
775
|
</div>
|
|
@@ -786,7 +786,7 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
786
786
|
{/* Related Products */}
|
|
787
787
|
{relatedProducts.length > 0 && (
|
|
788
788
|
<div>
|
|
789
|
-
<h2 className="font-
|
|
789
|
+
<h2 className="font-semibold text-hsecondary mb-8 text-2xl">
|
|
790
790
|
You May Also Like
|
|
791
791
|
</h2>
|
|
792
792
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
@@ -112,34 +112,30 @@ export default function SearchPage() {
|
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// Require at least 2 characters for search
|
|
116
|
+
if (sanitizedQuery.length < 2) {
|
|
117
|
+
setProducts([]);
|
|
118
|
+
setIsLoading(false);
|
|
119
|
+
setHasSearched(false);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
115
123
|
try {
|
|
116
124
|
setIsLoading(true);
|
|
117
125
|
const api = new ProductsApi(AXIOS_CONFIG);
|
|
118
126
|
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
undefined, // minPrice
|
|
124
|
-
undefined, // brandFilter
|
|
125
|
-
undefined, // availability
|
|
126
|
-
'relevance', // sort
|
|
127
|
-
undefined, // subCategoryId
|
|
128
|
-
undefined, // categoryId
|
|
129
|
-
true, // isActive
|
|
130
|
-
20, // limit
|
|
131
|
-
1 // page
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
console.log('Search API Response:', {
|
|
127
|
+
// Use AI-powered smart search for typo tolerance and semantic matching
|
|
128
|
+
const response = await api.smartSearch(sanitizedQuery, 20);
|
|
129
|
+
|
|
130
|
+
console.log('Smart Search API Response:', {
|
|
135
131
|
query: sanitizedQuery,
|
|
136
132
|
status: response.status,
|
|
137
|
-
|
|
138
|
-
|
|
133
|
+
productsCount: response.data?.products?.length,
|
|
134
|
+
scores: response.data?.scores
|
|
139
135
|
});
|
|
140
136
|
|
|
141
|
-
if (response.data?.
|
|
142
|
-
const transformedProducts = response.data.
|
|
137
|
+
if (response.data?.products) {
|
|
138
|
+
const transformedProducts = response.data.products.map((item: Product) => ({
|
|
143
139
|
...item,
|
|
144
140
|
id: item._id || '',
|
|
145
141
|
}));
|
|
@@ -155,7 +151,7 @@ export default function SearchPage() {
|
|
|
155
151
|
}
|
|
156
152
|
setHasSearched(true);
|
|
157
153
|
} catch (error) {
|
|
158
|
-
console.error('Error fetching search results:', error);
|
|
154
|
+
console.error('Error fetching smart search results:', error);
|
|
159
155
|
if (error instanceof Error) {
|
|
160
156
|
console.error('Error details:', error.message);
|
|
161
157
|
}
|
|
@@ -151,68 +151,6 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
151
151
|
[categories]
|
|
152
152
|
);
|
|
153
153
|
|
|
154
|
-
const productInsights = useMemo(() => {
|
|
155
|
-
if (!products.length) {
|
|
156
|
-
return { newArrivals: 0, inStockCount: 0 };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const monthAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
160
|
-
let newArrivals = 0;
|
|
161
|
-
let inStockCount = 0;
|
|
162
|
-
|
|
163
|
-
products.forEach((product) => {
|
|
164
|
-
if (product.summary?.totalInventory > 0) inStockCount += 1;
|
|
165
|
-
if (new Date(product.createdAt).getTime() >= monthAgo) newArrivals += 1;
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
return { newArrivals, inStockCount };
|
|
169
|
-
}, [products]);
|
|
170
|
-
const insightCards = useMemo(
|
|
171
|
-
() => [
|
|
172
|
-
{
|
|
173
|
-
id: 'new',
|
|
174
|
-
label: 'New arrivals',
|
|
175
|
-
value: productInsights.newArrivals
|
|
176
|
-
? productInsights.newArrivals.toLocaleString()
|
|
177
|
-
: isLoading
|
|
178
|
-
? '...'
|
|
179
|
-
: '0',
|
|
180
|
-
helper: filters.newArrivals ? 'Filter active: showing last 30 days' : 'Click to show last 30 days',
|
|
181
|
-
icon: Sparkles,
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
id: 'stock',
|
|
185
|
-
label: 'Available now',
|
|
186
|
-
value: productInsights.inStockCount
|
|
187
|
-
? productInsights.inStockCount.toLocaleString()
|
|
188
|
-
: isLoading
|
|
189
|
-
? '...'
|
|
190
|
-
: '0',
|
|
191
|
-
helper: 'Ready to ship today',
|
|
192
|
-
icon: ShieldCheck,
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
id: 'catalogue',
|
|
196
|
-
label: 'Total products',
|
|
197
|
-
value:
|
|
198
|
-
pagination.total || products.length
|
|
199
|
-
? (pagination.total || products.length).toLocaleString()
|
|
200
|
-
: isLoading
|
|
201
|
-
? '...'
|
|
202
|
-
: '0',
|
|
203
|
-
helper: 'Across all categories',
|
|
204
|
-
icon: TrendingUp,
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
[
|
|
208
|
-
isLoading,
|
|
209
|
-
pagination.total,
|
|
210
|
-
productInsights.inStockCount,
|
|
211
|
-
productInsights.newArrivals,
|
|
212
|
-
products.length,
|
|
213
|
-
filters.newArrivals,
|
|
214
|
-
]
|
|
215
|
-
);
|
|
216
154
|
|
|
217
155
|
const filteredProducts = useMemo(() => {
|
|
218
156
|
if (isLoading) return products;
|
|
@@ -664,12 +602,12 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
664
602
|
|
|
665
603
|
<div className={`lg:w-72 ${showFilters ? 'block rounded-full' : 'hidden lg:block'}`}>
|
|
666
604
|
<div className="bg-white rounded-[24px] p-6 border-2 border-gray-100 sticky top-24">
|
|
667
|
-
<h3 className="font-
|
|
605
|
+
<h3 className="font-semibold text-hsecondary">
|
|
668
606
|
Filters
|
|
669
607
|
</h3>
|
|
670
608
|
{/* Search */}
|
|
671
609
|
<div className="mb-6">
|
|
672
|
-
<label className="
|
|
610
|
+
<label className="text-[12px] text-hmuted mb-2 block font-medium">
|
|
673
611
|
Search Products
|
|
674
612
|
</label>
|
|
675
613
|
<div className="relative">
|
|
@@ -679,7 +617,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
679
617
|
placeholder="Search..."
|
|
680
618
|
value={searchQuery}
|
|
681
619
|
onChange={handleInputChange}
|
|
682
|
-
className="w-full pl-10 pr-4 py-2.5 rounded-xl border-2 border-gray-200 focus:border-hprimary focus:outline-hidden
|
|
620
|
+
className="w-full pl-10 pr-4 py-2.5 rounded-xl border-2 border-gray-200 focus:border-hprimary focus:outline-hidden text-[13px] text-hsecondary"
|
|
683
621
|
/>
|
|
684
622
|
</div>
|
|
685
623
|
</div>
|
|
@@ -690,7 +628,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
690
628
|
onClick={() => toggleFilterSection('category')}
|
|
691
629
|
className="w-full flex items-center justify-between mb-3"
|
|
692
630
|
>
|
|
693
|
-
<label className="
|
|
631
|
+
<label className="text-[12px] text-hmuted font-medium cursor-pointer">
|
|
694
632
|
Category
|
|
695
633
|
</label>
|
|
696
634
|
{expandedFilterSections.category ? (
|
|
@@ -713,7 +651,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
713
651
|
if (!isExpanded) toggleCategoryExpand(categoryId);
|
|
714
652
|
handleCategoryChange(categoryId);
|
|
715
653
|
}}
|
|
716
|
-
className={`w-full text-left px-4 py-3 rounded-xl
|
|
654
|
+
className={`w-full text-left px-4 py-3 rounded-xl text-[13px] transition-all flex items-center gap-3 ${isCategoryActive
|
|
717
655
|
? 'bg-hprimary text-white shadow-lg'
|
|
718
656
|
: 'text-hsecondary hover:bg-gray-50 border-2 border-gray-100'
|
|
719
657
|
}`}
|
|
@@ -733,7 +671,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
733
671
|
onClick={() => toggleFilterSection('brand')}
|
|
734
672
|
className="w-full flex items-center justify-between mb-3"
|
|
735
673
|
>
|
|
736
|
-
<label className="
|
|
674
|
+
<label className="text-[12px] text-hmuted font-medium cursor-pointer">
|
|
737
675
|
Brand
|
|
738
676
|
</label>
|
|
739
677
|
<ChevronDown className={`size-4 text-hmuted transition-transform ${expandedFilterSections.brand ? 'rotate-180' : ''}`} />
|
|
@@ -748,7 +686,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
748
686
|
<button
|
|
749
687
|
key={brand}
|
|
750
688
|
onClick={() => handleBrandChange(brand)}
|
|
751
|
-
className={`w-full text-left px-4 py-3 rounded-xl
|
|
689
|
+
className={`w-full text-left px-4 py-3 rounded-xl text-[13px] transition-all ${isSelected
|
|
752
690
|
? 'bg-hprimary text-white shadow-lg'
|
|
753
691
|
: 'text-hsecondary hover:bg-gray-50 border-2 border-gray-100'
|
|
754
692
|
}`}
|
|
@@ -769,7 +707,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
769
707
|
onClick={() => toggleFilterSection('availability')}
|
|
770
708
|
className="w-full flex items-center justify-between mb-3"
|
|
771
709
|
>
|
|
772
|
-
<label className="
|
|
710
|
+
<label className="text-[12px] text-hmuted font-medium cursor-pointer">
|
|
773
711
|
Availability
|
|
774
712
|
</label>
|
|
775
713
|
<ChevronDown className={`size-4 text-hmuted transition-transform ${expandedFilterSections.availability ? 'rotate-180' : ''}`} />
|
|
@@ -778,7 +716,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
778
716
|
<div className="space-y-2">
|
|
779
717
|
<button
|
|
780
718
|
onClick={handleToggleStock}
|
|
781
|
-
className={`w-full flex items-center justify-between px-4 py-3 rounded-xl
|
|
719
|
+
className={`w-full flex items-center justify-between px-4 py-3 rounded-xl text-[13px] transition-all border-2 ${inStockOnly
|
|
782
720
|
? 'bg-hprimary text-white shadow-lg'
|
|
783
721
|
: 'text-hsecondary hover:bg-gray-50 border-2 border-gray-100'
|
|
784
722
|
}`}
|
|
@@ -794,7 +732,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
794
732
|
onClick={() => toggleFilterSection('price')}
|
|
795
733
|
className="w-full flex items-center justify-between mb-3"
|
|
796
734
|
>
|
|
797
|
-
<label className="
|
|
735
|
+
<label className="text-[12px] text-hmuted font-medium cursor-pointer">
|
|
798
736
|
Price Range
|
|
799
737
|
</label>
|
|
800
738
|
<ChevronDown className={`size-4 text-hmuted transition-transform ${expandedFilterSections.price ? 'rotate-180' : ''}`} />
|
|
@@ -809,7 +747,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
809
747
|
type="button"
|
|
810
748
|
key={range.value}
|
|
811
749
|
onClick={() => handlePriceRangeSelect(range.value)}
|
|
812
|
-
className={`w-full flex items-center justify-between px-4 py-3 rounded-xl
|
|
750
|
+
className={`w-full flex items-center justify-between px-4 py-3 rounded-xl text-[13px] transition-all border-2 ${isActive
|
|
813
751
|
? 'bg-hprimary text-white shadow-lg'
|
|
814
752
|
: 'text-hsecondary hover:bg-gray-50 border-2 border-gray-100'
|
|
815
753
|
}`}
|
|
@@ -828,7 +766,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
828
766
|
onChange={(event) =>
|
|
829
767
|
setCustomPrice((current) => ({ ...current, min: event.target.value }))
|
|
830
768
|
}
|
|
831
|
-
className="w-1/2 px-4 py-2.5 rounded-xl border-2 border-gray-200 focus:border-hprimary focus:outline-hidden
|
|
769
|
+
className="w-1/2 px-4 py-2.5 rounded-xl border-2 border-gray-200 focus:border-hprimary focus:outline-hidden text-[13px] text-hsecondary"
|
|
832
770
|
/>
|
|
833
771
|
<Input
|
|
834
772
|
type="number"
|
|
@@ -838,7 +776,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
838
776
|
onChange={(event) =>
|
|
839
777
|
setCustomPrice((current) => ({ ...current, max: event.target.value }))
|
|
840
778
|
}
|
|
841
|
-
className="w-1/2 px-4 py-2.5 rounded-xl border-2 border-gray-200 focus:border-hprimary focus:outline-hidden
|
|
779
|
+
className="w-1/2 px-4 py-2.5 rounded-xl border-2 border-gray-200 focus:border-hprimary focus:outline-hidden text-[13px] text-hsecondary"
|
|
842
780
|
/>
|
|
843
781
|
</div>
|
|
844
782
|
<button
|
|
@@ -926,9 +864,9 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
926
864
|
</section>
|
|
927
865
|
|
|
928
866
|
{/* Shop by Category Section */}
|
|
929
|
-
<section className="py-8 bg-white">
|
|
867
|
+
<section className="py-8 bg-white/50">
|
|
930
868
|
<div className="container mx-auto px-4">
|
|
931
|
-
<h2 className="text-2xl md:text-3xl font-
|
|
869
|
+
<h2 className="text-2xl md:text-3xl font-semibold text-hsecondary mb-6">
|
|
932
870
|
Shop by Category
|
|
933
871
|
</h2>
|
|
934
872
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
|
|
@@ -948,11 +886,11 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
948
886
|
<Package className={`size-6 ${!categoryFilter ? 'text-white' : 'text-hprimary'
|
|
949
887
|
}`} />
|
|
950
888
|
</div>
|
|
951
|
-
<h3 className={`font-
|
|
889
|
+
<h3 className={`font-semibold text-[14px] mb-1.5 ${!categoryFilter ? 'text-white' : 'text-hsecondary'
|
|
952
890
|
}`}>
|
|
953
891
|
All Products
|
|
954
892
|
</h3>
|
|
955
|
-
<p className={`
|
|
893
|
+
<p className={`text-[11px] ${!categoryFilter ? 'text-white/80' : 'text-hmuted'
|
|
956
894
|
}`}>
|
|
957
895
|
Browse Everything
|
|
958
896
|
</p>
|
|
@@ -983,11 +921,11 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
983
921
|
<Icon className={`size-6 ${isSelected ? 'text-white' : 'text-hprimary'
|
|
984
922
|
}`} />
|
|
985
923
|
</div>
|
|
986
|
-
<h3 className={`font-
|
|
924
|
+
<h3 className={`font-semibold text-[14px] mb-1.5 ${isSelected ? 'text-white' : 'text-hsecondary'
|
|
987
925
|
}`}>
|
|
988
926
|
{category.name}
|
|
989
927
|
</h3>
|
|
990
|
-
<p className={`
|
|
928
|
+
<p className={`text-[11px] ${isSelected ? 'text-white/80' : 'text-hmuted'
|
|
991
929
|
}`}>
|
|
992
930
|
{category.description}
|
|
993
931
|
</p>
|
|
@@ -1177,7 +1115,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
1177
1115
|
event.stopPropagation();
|
|
1178
1116
|
router.push(buildPath(`/products/${product._id}`));
|
|
1179
1117
|
}}
|
|
1180
|
-
className="w-full font-
|
|
1118
|
+
className="w-full font-medium text-sm px-3 py-2 rounded-xl bg-hsecondary text-white hover:opacity-80 hover:shadow-lg transition-all duration-300 flex items-center justify-center gap-1.5 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
1181
1119
|
>
|
|
1182
1120
|
View product
|
|
1183
1121
|
</button>
|