hey-pharmacist-ecommerce 1.1.12 → 1.1.14

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 (49) hide show
  1. package/dist/index.d.mts +2 -4
  2. package/dist/index.d.ts +2 -4
  3. package/dist/index.js +1123 -972
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1123 -971
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +3 -3
  8. package/src/components/AccountAddressesTab.tsx +209 -0
  9. package/src/components/AccountOrdersTab.tsx +151 -0
  10. package/src/components/AccountOverviewTab.tsx +209 -0
  11. package/src/components/AccountPaymentTab.tsx +116 -0
  12. package/src/components/AccountSavedItemsTab.tsx +76 -0
  13. package/src/components/AccountSettingsTab.tsx +116 -0
  14. package/src/components/AddressFormModal.tsx +23 -10
  15. package/src/components/CartItem.tsx +60 -56
  16. package/src/components/FilterChips.tsx +54 -80
  17. package/src/components/Header.tsx +69 -16
  18. package/src/components/Notification.tsx +148 -0
  19. package/src/components/OrderCard.tsx +89 -56
  20. package/src/components/ProductCard.tsx +215 -178
  21. package/src/components/QuickViewModal.tsx +314 -0
  22. package/src/components/TabNavigation.tsx +48 -0
  23. package/src/components/ui/Button.tsx +1 -1
  24. package/src/components/ui/ConfirmModal.tsx +84 -0
  25. package/src/hooks/useOrders.ts +1 -0
  26. package/src/hooks/usePaymentMethods.ts +58 -0
  27. package/src/index.ts +0 -1
  28. package/src/providers/CartProvider.tsx +22 -6
  29. package/src/providers/EcommerceProvider.tsx +8 -7
  30. package/src/providers/FavoritesProvider.tsx +10 -3
  31. package/src/providers/NotificationProvider.tsx +79 -0
  32. package/src/providers/WishlistProvider.tsx +34 -9
  33. package/src/screens/AddressesScreen.tsx +72 -61
  34. package/src/screens/CartScreen.tsx +48 -32
  35. package/src/screens/ChangePasswordScreen.tsx +155 -0
  36. package/src/screens/CheckoutScreen.tsx +162 -125
  37. package/src/screens/EditProfileScreen.tsx +165 -0
  38. package/src/screens/LoginScreen.tsx +59 -72
  39. package/src/screens/NewAddressScreen.tsx +16 -10
  40. package/src/screens/OrdersScreen.tsx +91 -148
  41. package/src/screens/ProductDetailScreen.tsx +334 -234
  42. package/src/screens/ProfileScreen.tsx +190 -200
  43. package/src/screens/RegisterScreen.tsx +51 -70
  44. package/src/screens/SearchResultsScreen.tsx +2 -1
  45. package/src/screens/ShopScreen.tsx +260 -384
  46. package/src/screens/WishlistScreen.tsx +226 -224
  47. package/src/styles/globals.css +9 -0
  48. package/src/screens/CategoriesScreen.tsx +0 -122
  49. package/src/screens/HomeScreen.tsx +0 -211
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import React, {
3
+ import {
4
4
  useCallback,
5
5
  useEffect,
6
6
  useMemo,
@@ -31,7 +31,6 @@ import {
31
31
  Tag,
32
32
  DollarSign,
33
33
  } from 'lucide-react';
34
- import Image from 'next/image';
35
34
  import { useRouter } from 'next/navigation';
36
35
  import { useBasePath } from '@/providers/BasePathProvider';
37
36
  import { ProductCard } from '@/components/ProductCard';
@@ -42,6 +41,8 @@ import { Input } from '@/components/ui/Input';
42
41
  import { useProducts, useCategories } from '@/hooks/useProducts';
43
42
  import { ProductFilters } from '@/lib/types';
44
43
  import { formatPrice } from '@/lib/utils/format';
44
+ import Image from 'next/image';
45
+ import React from 'react';
45
46
 
46
47
  type SortOption = 'featured' | 'price-low-high' | 'price-high-low' | 'newest';
47
48
  type ViewMode = 'grid' | 'list';
@@ -655,268 +656,200 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
655
656
  const renderFiltersPanel = () => (
656
657
  <>
657
658
  <div className="space-y-6">
658
- {/* Filters Title */}
659
- <h3 className="text-lg font-bold text-slate-900">Filters</h3>
660
-
661
- {/* Search Products */}
662
- <div className="space-y-2">
663
- <label className="text-sm font-medium text-slate-600">Search Products</label>
664
- <div className="relative">
665
- <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" />
666
- <input
667
- type="text"
668
- placeholder="Search..."
669
- value={searchQuery}
670
- onChange={handleInputChange}
671
- className="w-full rounded-lg border border-slate-200 bg-white pl-10 pr-4 py-2 text-sm text-slate-900 placeholder-slate-400 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20"
672
- />
673
- </div>
674
- </div>
675
659
 
676
- {/* Category Section */}
677
- <div className="space-y-3">
678
- <button
679
- type="button"
680
- onClick={() => toggleFilterSection('category')}
681
- className="flex w-full items-center justify-between text-sm font-medium text-slate-600"
682
- >
683
- <span>Category</span>
684
- {expandedFilterSections.category ? (
685
- <ChevronUp className="h-4 w-4" />
686
- ) : (
687
- <ChevronDown className="h-4 w-4" />
688
- )}
689
- </button>
690
-
691
- {expandedFilterSections.category && (
692
- <div className="space-y-2">
693
- {/* All Products Option */}
660
+ <div className={`lg:w-72 ${showFilters ? 'block rounded-full' : 'hidden lg:block'}`}>
661
+ <div className="bg-white rounded-[24px] p-6 border-2 border-gray-100 sticky top-24">
662
+ <h3 className="font-['Poppins',sans-serif] font-semibold text-secondary">
663
+ Filters
664
+ </h3>
665
+ {/* Search */}
666
+ <div className="mb-6">
667
+ <label className="font-['Poppins',sans-serif] text-[12px] text-muted mb-2 block font-medium">
668
+ Search Products
669
+ </label>
670
+ <div className="relative">
671
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted" />
672
+ <input
673
+ type="text"
674
+ placeholder="Search..."
675
+ value={searchQuery}
676
+ onChange={handleInputChange}
677
+ className="w-full pl-10 pr-4 py-2.5 rounded-xl border-2 border-gray-200 focus:border-primary focus:outline-none font-['Poppins',sans-serif] text-[13px] text-secondary"
678
+ />
679
+ </div>
680
+ </div>
681
+
682
+ {/* Category Filter */}
683
+ <div className="mb-6">
694
684
  <button
695
- type="button"
696
- onClick={handleClearCategory}
697
- className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition ${
698
- !categoryFilter
699
- ? 'bg-primary-600 text-white'
700
- : 'bg-white text-slate-700 border border-slate-200 hover:border-primary-300'
701
- }`}
685
+ onClick={() => toggleFilterSection('category')}
686
+ className="w-full flex items-center justify-between mb-3"
702
687
  >
703
- <div className={`flex h-6 w-6 items-center justify-center rounded ${!categoryFilter ? 'bg-white/20' : 'bg-slate-100'}`}>
704
- {!categoryFilter ? (
705
- <Check className="h-4 w-4 text-white" />
706
- ) : (
707
- <ShoppingBag className="h-4 w-4 text-slate-600" />
708
- )}
709
- </div>
710
- <span>All Products</span>
688
+ <label className="font-['Poppins',sans-serif] text-[12px] text-muted font-medium cursor-pointer">
689
+ Category
690
+ </label>
691
+ {expandedFilterSections.category ? (
692
+ <ChevronDown className={`size-4 text-muted transition-transform ${expandedFilterSections.category ? 'rotate-180' : ''}`} />
693
+ ) : (
694
+ <ChevronDown className={`size-4 text-muted transition-transform ${expandedFilterSections.category ? 'rotate-180' : ''}`} />
695
+ )}
711
696
  </button>
712
-
713
- {/* Category Options */}
714
- {sortedCategories.map((category) => {
715
- const isCategoryActive = categoryFilter === category.id;
716
- const isExpanded = !!expandedCategories[category.id as string];
717
- const Icon = getCategoryIconForFilter(category.name ?? '');
718
- return (
719
- <div key={category.id} className="space-y-1">
720
- <button
721
- type="button"
722
- onClick={() => {
723
- if (!isExpanded) toggleCategoryExpand(category.id ?? '');
724
- handleCategoryChange(category.id ?? '');
725
- }}
726
- className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition ${
727
- isCategoryActive
728
- ? 'bg-primary-600 text-white'
729
- : 'bg-white text-slate-700 border border-slate-200 hover:border-primary-300'
730
- }`}
731
- >
732
- <div className={`flex h-6 w-6 items-center justify-center rounded ${isCategoryActive ? 'bg-white/20' : 'bg-slate-100'}`}>
733
- <Icon className={`h-4 w-4 ${isCategoryActive ? 'text-white' : 'text-slate-600'}`} />
734
- </div>
735
- <span className="flex-1 text-left">{category.name}</span>
736
- {Array.isArray(category.categorySubCategories) && category.categorySubCategories.length > 0 && (
737
- <button
738
- type="button"
739
- onClick={(e) => {
740
- e.stopPropagation();
741
- toggleCategoryExpand(category.id ?? '');
742
- }}
743
- className="p-1"
744
- >
745
- <ChevronDown className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-180' : ''} ${isCategoryActive ? 'text-white' : 'text-slate-400'}`} />
746
- </button>
747
- )}
748
- </button>
749
- {isExpanded && Array.isArray(category.categorySubCategories) && category.categorySubCategories.length > 0 && (
750
- <div className="ml-9 space-y-1">
751
- {category.categorySubCategories.map((sub) => {
752
- const isSubActive = subCategoryFilter === sub.id;
753
- return (
754
- <button
755
- key={sub.id}
756
- type="button"
757
- onClick={() => handleSubCategoryChange(category.id ?? '', sub.id)}
758
- className={`block w-full rounded-lg px-3 py-2 text-sm text-left transition ${
759
- isSubActive
760
- ? 'bg-primary-50 text-primary-700 font-medium'
761
- : 'text-slate-600 hover:bg-slate-50'
762
- }`}
763
- >
764
- {sub.name}
765
- </button>
766
- );
767
- })}
768
- </div>
769
- )}
770
- </div>
771
- );
772
- })}
697
+ {expandedFilterSections.category && (
698
+ <div className="space-y-2">
699
+ {sortedCategories.map((category) => {
700
+ const isCategoryActive = categoryFilter === category.id;
701
+ const isExpanded = !!expandedCategories[category.id as string];
702
+ const Icon = getCategoryIconForFilter(category.name ?? '');
703
+ return (
704
+ <button
705
+ key={category.id}
706
+ onClick={() => {
707
+ if (!isExpanded) toggleCategoryExpand(category.id ?? '');
708
+ handleCategoryChange(category.id ?? '');
709
+ }}
710
+ className={`w-full text-left px-4 py-3 rounded-xl font-['Poppins',sans-serif] text-[13px] transition-all flex items-center gap-3 ${isCategoryActive
711
+ ? 'bg-primary text-white shadow-lg'
712
+ : 'text-secondary hover:bg-gray-50 border-2 border-gray-100'
713
+ }`}
714
+ >
715
+ <Icon className="size-4" />
716
+ {category.name}
717
+ </button>
718
+ );
719
+ })}
720
+ </div>
721
+ )}
773
722
  </div>
774
- )}
775
- </div>
776
723
 
777
- {/* Brand Section */}
778
- <div className="space-y-3">
779
- <button
780
- type="button"
781
- onClick={() => toggleFilterSection('brand')}
782
- className="flex w-full items-center justify-between text-sm font-medium text-slate-600"
783
- >
784
- <span>Brand</span>
785
- {expandedFilterSections.brand ? (
786
- <ChevronUp className="h-4 w-4" />
787
- ) : (
788
- <ChevronDown className="h-4 w-4" />
789
- )}
790
- </button>
791
- {expandedFilterSections.brand && (
792
- <div className="space-y-1.5 max-h-64 overflow-y-auto">
793
- {availableBrands.length === 0 ? (
794
- <div className="text-xs text-slate-500">No brands available</div>
795
- ) : (
796
- availableBrands.map((brand) => {
797
- const isSelected = brandFilter === brand;
798
- return (
799
- <button
800
- key={brand}
801
- type="button"
802
- onClick={() => handleBrandChange(brand)}
803
- className={`flex w-full items-center justify-between rounded-md border px-2.5 py-1.5 text-xs font-medium transition ${
804
- isSelected
805
- ? 'border-primary-500 bg-primary-50 text-primary-700'
806
- : 'border-slate-200 bg-white text-slate-600 hover:border-primary-300'
807
- }`}
808
- >
809
- <span className="truncate">{brand}</span>
810
- {isSelected && <Check className="h-3 w-3 text-primary-600 flex-shrink-0 ml-2" />}
811
- </button>
812
- );
813
- })
724
+ {/* Brand Filter */}
725
+ <div className="mb-6">
726
+ <button
727
+ onClick={() => toggleFilterSection('brand')}
728
+ className="w-full flex items-center justify-between mb-3"
729
+ >
730
+ <label className="font-['Poppins',sans-serif] text-[12px] text-muted font-medium cursor-pointer">
731
+ Brand
732
+ </label>
733
+ <ChevronDown className={`size-4 text-muted transition-transform ${expandedFilterSections.brand ? 'rotate-180' : ''}`} />
734
+ </button>
735
+ {expandedFilterSections.brand && (
736
+ <div className="space-y-2">
737
+ {availableBrands.length === 0 ? (
738
+ <div className="text-xs text-slate-500">No brands available</div>
739
+ ) : (availableBrands.map((brand) => {
740
+ const isSelected = brandFilter === brand;
741
+ return (
742
+ <button
743
+ key={brand}
744
+ onClick={() => handleBrandChange(brand)}
745
+ className={`w-full text-left px-4 py-3 rounded-xl font-['Poppins',sans-serif] text-[13px] transition-all ${isSelected
746
+ ? 'bg-primary text-white shadow-lg'
747
+ : 'text-secondary hover:bg-gray-50 border-2 border-gray-100'
748
+ }`}
749
+ >
750
+ {brand}
751
+ </button>
752
+ )
753
+ })
754
+ )}
755
+ </div>
814
756
  )}
757
+
815
758
  </div>
816
- )}
817
- </div>
818
759
 
819
- {/* Availability Section */}
820
- <div className="space-y-3">
821
- <button
822
- type="button"
823
- onClick={() => toggleFilterSection('availability')}
824
- className="flex w-full items-center justify-between text-sm font-medium text-slate-600"
825
- >
826
- <span>Availability</span>
827
- {expandedFilterSections.availability ? (
828
- <ChevronUp className="h-4 w-4" />
829
- ) : (
830
- <ChevronDown className="h-4 w-4" />
831
- )}
832
- </button>
833
- {expandedFilterSections.availability && (
834
- <div className="space-y-2">
760
+ {/* In Stock Filter */}
761
+ <div className="mb-6">
835
762
  <button
836
- type="button"
837
- onClick={handleToggleStock}
838
- className={`flex w-full items-center justify-between rounded-lg border px-3 py-2.5 text-sm font-medium transition ${
839
- inStockOnly
840
- ? 'border-primary-500 bg-primary-50 text-primary-700'
841
- : 'border-slate-200 bg-white text-slate-600 hover:border-primary-300'
842
- }`}
763
+ onClick={() => toggleFilterSection('availability')}
764
+ className="w-full flex items-center justify-between mb-3"
843
765
  >
844
- <span>In stock only</span>
845
- {inStockOnly && <Check className="h-4 w-4 text-primary-600" />}
766
+ <label className="font-['Poppins',sans-serif] text-[12px] text-muted font-medium cursor-pointer">
767
+ Availability
768
+ </label>
769
+ <ChevronDown className={`size-4 text-muted transition-transform ${expandedFilterSections.availability ? 'rotate-180' : ''}`} />
846
770
  </button>
771
+ {expandedFilterSections.availability && (
772
+ <div className="space-y-2">
773
+ <button
774
+ onClick={handleToggleStock}
775
+ className={`w-full flex items-center justify-between px-4 py-3 rounded-xl font-['Poppins',sans-serif] text-[13px] transition-all border-2 ${inStockOnly
776
+ ? 'bg-primary text-white shadow-lg'
777
+ : 'text-secondary hover:bg-gray-50 border-2 border-gray-100'
778
+ }`}
779
+ >
780
+ In Stock Only
781
+ </button>
782
+ </div>
783
+ )}
847
784
  </div>
848
- )}
849
- </div>
850
785
 
851
- {/* Price Range Section */}
852
- <div className="space-y-3">
853
- <button
854
- type="button"
855
- onClick={() => toggleFilterSection('price')}
856
- className="flex w-full items-center justify-between text-sm font-medium text-slate-600"
857
- >
858
- <span>Price Range</span>
859
- {expandedFilterSections.price ? (
860
- <ChevronUp className="h-4 w-4" />
861
- ) : (
862
- <ChevronDown className="h-4 w-4" />
863
- )}
864
- </button>
865
- {expandedFilterSections.price && (
866
- <div className="space-y-3">
867
- <div className="flex flex-wrap gap-2">
868
- {priceRanges.map((range) => {
869
- const isActive = selectedPriceRange === range.value;
870
- return (
871
- <button
872
- type="button"
873
- key={range.value}
874
- onClick={() => handlePriceRangeSelect(range.value)}
875
- className={`rounded-lg border px-3 py-1.5 text-sm font-medium transition ${
876
- isActive
877
- ? 'border-primary-600 bg-primary-600 text-white'
878
- : 'border-slate-200 bg-white text-slate-600 hover:border-primary-300'
879
- }`}
880
- >
881
- {range.label}
882
- </button>
883
- );
884
- })}
885
- </div>
886
- <div className="grid grid-cols-2 gap-2">
887
- <Input
888
- type="number"
889
- min="0"
890
- placeholder="Min"
891
- value={customPrice.min}
892
- onChange={(event) =>
893
- setCustomPrice((current) => ({ ...current, min: event.target.value }))
894
- }
895
- className="text-sm"
896
- />
897
- <Input
898
- type="number"
899
- min="0"
900
- placeholder="Max"
901
- value={customPrice.max}
902
- onChange={(event) =>
903
- setCustomPrice((current) => ({ ...current, max: event.target.value }))
904
- }
905
- className="text-sm"
906
- />
907
- </div>
786
+ <div className="mb-6">
908
787
  <button
909
- type="button"
910
- onClick={applyCustomPrice}
911
- disabled={!isCustomPriceDirty}
912
- className="w-full rounded-lg border border-primary-500 bg-primary-500/10 px-4 py-2 text-sm font-medium text-primary-700 transition hover:bg-primary-500/20 disabled:cursor-not-allowed disabled:border-slate-200 disabled:text-slate-400"
788
+ onClick={() => toggleFilterSection('price')}
789
+ className="w-full flex items-center justify-between mb-3"
913
790
  >
914
- Apply
791
+ <label className="font-['Poppins',sans-serif] text-[12px] text-muted font-medium cursor-pointer">
792
+ Price Range
793
+ </label>
794
+ <ChevronDown className={`size-4 text-muted transition-transform ${expandedFilterSections.price ? 'rotate-180' : ''}`} />
915
795
  </button>
796
+ {expandedFilterSections.price && (
797
+ <div className="space-y-2">
798
+ <div className="flex flex-wrap gap-2">
799
+ {priceRanges.map((range) => {
800
+ const isActive = selectedPriceRange === range.value;
801
+ return (
802
+ <button
803
+ type="button"
804
+ key={range.value}
805
+ onClick={() => handlePriceRangeSelect(range.value)}
806
+ className={`w-full flex items-center justify-between px-4 py-3 rounded-xl font-['Poppins',sans-serif] text-[13px] transition-all border-2 ${isActive
807
+ ? 'bg-primary text-white shadow-lg'
808
+ : 'text-secondary hover:bg-gray-50 border-2 border-gray-100'
809
+ }`}
810
+ >
811
+ {range.label}
812
+ </button>
813
+ );
814
+ })}
815
+ </div>
816
+ <div className="grid grid-cols-2 gap-2">
817
+ <Input
818
+ type="number"
819
+ min="0"
820
+ placeholder="Min"
821
+ value={customPrice.min}
822
+ onChange={(event) =>
823
+ setCustomPrice((current) => ({ ...current, min: event.target.value }))
824
+ }
825
+ className="w-1/2 px-4 py-2.5 rounded-xl border-2 border-gray-200 focus:border-primary focus:outline-none font-['Poppins',sans-serif] text-[13px] text-secondary"
826
+ />
827
+ <Input
828
+ type="number"
829
+ min="0"
830
+ placeholder="Max"
831
+ value={customPrice.max}
832
+ onChange={(event) =>
833
+ setCustomPrice((current) => ({ ...current, max: event.target.value }))
834
+ }
835
+ className="w-1/2 px-4 py-2.5 rounded-xl border-2 border-gray-200 focus:border-primary focus:outline-none font-['Poppins',sans-serif] text-[13px] text-secondary"
836
+ />
837
+ </div>
838
+ <button
839
+ type="button"
840
+ onClick={applyCustomPrice}
841
+ disabled={!isCustomPriceDirty}
842
+ className="w-full rounded-lg border border-primary bg-primary/10 px-4 py-2 text-sm font-medium text-primary transition hover:bg-primary/20 disabled:cursor-not-allowed disabled:border-slate-200 disabled:text-slate-400"
843
+ >
844
+ Apply
845
+ </button>
846
+ </div>
847
+ )}
916
848
  </div>
917
- )}
849
+ </div>
918
850
  </div>
919
851
 
852
+
920
853
  </div>
921
854
  </>
922
855
  );
@@ -939,7 +872,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
939
872
  return (
940
873
  <div className="min-h-screen bg-[#F9FAFB]">
941
874
  {/* Header Section */}
942
- <section className="relative overflow-hidden bg-[#E6EBF0] py-16 md:py-24">
875
+ <section className="relative overflow-hidden bg-primary-bg py-16 md:py-24">
943
876
  <div className="container mx-auto px-4">
944
877
  <motion.div
945
878
  initial={{ opacity: 0, y: 24 }}
@@ -987,92 +920,88 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
987
920
  </section>
988
921
 
989
922
  {/* Shop by Category Section */}
990
- <section className="bg-white py-8 ">
991
- <div className="container mx-auto px-4">
992
- <h2 className="text-2xl md:text-3xl font-bold text-slate-900 mb-8">
923
+ <section className="py-8 bg-white">
924
+ <div className="max-w-[1400px] mx-auto px-8 md:px-12">
925
+ <h2 className="text-2xl md:text-3xl font-['Poppins',sans-serif] font-semibold text-secondary mb-6">
993
926
  Shop by Category
994
927
  </h2>
995
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-6">
996
- {isLoadingCategories ? (
997
- // Skeleton loaders
998
- <>
999
- {Array.from({ length: 6 }).map((_, index) => (
1000
- <div
1001
- key={index}
1002
- className="rounded-xl border border-slate-200 bg-white p-8"
928
+ <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
929
+ {displayCategories.map((category, index) => {
930
+ const Icon = getCategoryIcon(category.name ?? '');
931
+ const isSelected = categoryFilter === category.id;
932
+
933
+ return (
934
+ <>
935
+
936
+ <motion.button
937
+ onClick={handleClearCategory}
938
+ initial={{ opacity: 0, y: 20 }}
939
+ animate={{ opacity: 1, y: 0 }}
940
+ transition={{ delay: index * 0.1 }}
941
+ className={`group relative overflow-hidden rounded-[24px] p-6 transition-all duration-300 ${!categoryFilter ? 'bg-gradient-to-br from-primary to-secondary text-white shadow-xl scale-105'
942
+ : 'bg-gradient-to-br from-gray-50 to-white hover:shadow-lg border-2 border-gray-100 hover:border-primary'
943
+ }`}
1003
944
  >
1004
- <Skeleton className="h-10 w-10 mb-4 rounded-lg" />
1005
- <Skeleton className="h-6 w-3/4 mb-2 rounded" />
1006
- <Skeleton className="h-4 w-1/2 rounded" />
1007
- </div>
1008
- ))}
1009
- </>
1010
- ) : (
1011
- <>
1012
- {/* All Products Card */}
1013
- <motion.button
1014
- initial={{ opacity: 0, y: 20 }}
1015
- animate={{ opacity: 1, y: 0 }}
1016
- onClick={handleClearCategory}
1017
- className={`group relative overflow-hidden rounded-xl p-8 text-left transition ${
1018
- !categoryFilter
1019
- ? 'bg-gradient-to-b from-primary-500 to-primary-300 text-white shadow-lg scale-105'
1020
- : 'border border-slate-200 bg-white hover:border-primary-300 hover:shadow-md'
1021
- }`}
1022
- >
1023
- <ShoppingBag className={`h-10 w-10 mb-4 ${!categoryFilter ? 'text-white' : 'text-primary-500'}`} />
1024
- <h3 className={`text-xl font-bold mb-2 ${!categoryFilter ? 'text-white' : 'text-slate-900 group-hover:text-primary-600'}`}>
1025
- All Products
1026
- </h3>
1027
- <p className={`text-sm ${!categoryFilter ? 'text-white/90' : 'text-slate-500'}`}>
1028
- Browse everything
1029
- </p>
1030
- </motion.button>
1031
-
1032
- {/* Category Cards */}
1033
- {displayCategories.map((category, index) => {
1034
- const Icon = getCategoryIcon(category.name ?? '');
1035
- const isSelected = categoryFilter === category.id;
1036
- return (
1037
- <motion.button
1038
- key={category.id}
1039
- initial={{ opacity: 0, y: 20 }}
1040
- animate={{ opacity: 1, y: 0 }}
1041
- transition={{ delay: index * 0.1 }}
1042
- onClick={() => handleCategoryChange(category.id ?? '')}
1043
- className={`group rounded-xl p-8 text-left transition ${
1044
- isSelected
1045
- ? 'bg-gradient-to-b from-primary-500 to-primary-300 text-white shadow-lg'
1046
- : 'border border-slate-200 bg-white hover:border-primary-300 hover:shadow-md'
945
+ <div className="relative">
946
+ <div className={`size-12 rounded-full mb-3 mx-auto flex items-center justify-center transition-all ${!categoryFilter
947
+ ? 'bg-white/20'
948
+ : 'bg-gradient-to-br from-primary/10 to-secondary/10 group-hover:scale-110'
949
+ }`}>
950
+ <Icon className={`size-6 ${!categoryFilter ? 'text-white' : 'text-primary'
951
+ }`} />
952
+ </div>
953
+ <h3 className={`font-['Poppins',sans-serif] font-semibold text-[14px] mb-1.5 ${!categoryFilter ? 'text-white' : 'text-secondary'
954
+ }`}>
955
+ All Products
956
+ </h3>
957
+ <p className={`font-['Poppins',sans-serif] text-[11px] ${!categoryFilter ? 'text-white/80' : 'text-muted'
958
+ }`}>
959
+ Browse Everything
960
+ </p>
961
+ </div>
962
+ </motion.button>
963
+
964
+
965
+ <motion.button
966
+ key={category.id}
967
+ initial={{ opacity: 0, y: 20 }}
968
+ animate={{ opacity: 1, y: 0 }}
969
+ transition={{ delay: index * 0.1 }}
970
+ onClick={() => handleCategoryChange(category.id ?? '')}
971
+ className={`group relative overflow-hidden rounded-[24px] p-6 transition-all duration-300 ${isSelected ? 'bg-gradient-to-br from-primary to-secondary text-white shadow-xl scale-105'
972
+ : 'bg-gradient-to-br from-gray-50 to-white hover:shadow-lg border-2 border-gray-100 hover:border-primary'
1047
973
  }`}
1048
- >
1049
- <Icon className={`h-10 w-10 mb-4 ${isSelected ? 'text-white' : 'text-primary-500'}`} />
1050
- <h3 className={`text-xl font-bold mb-2 transition-colors ${
1051
- isSelected ? 'text-white' : 'text-slate-900 group-hover:text-primary-600'
1052
- }`}>
974
+ >
975
+ <div className="relative">
976
+ <div className={`size-12 rounded-full mb-3 mx-auto flex items-center justify-center transition-all ${isSelected
977
+ ? 'bg-white/20'
978
+ : 'bg-gradient-to-br from-primary/10 to-secondary/10 group-hover:scale-110'
979
+ }`}>
980
+ <Icon className={`size-6 ${isSelected ? 'text-white' : 'text-primary'
981
+ }`} />
982
+ </div>
983
+ <h3 className={`font-['Poppins',sans-serif] font-semibold text-[14px] mb-1.5 ${isSelected ? 'text-white' : 'text-secondary'
984
+ }`}>
1053
985
  {category.name}
1054
986
  </h3>
1055
- <p className={`text-sm ${isSelected ? 'text-white/90' : 'text-slate-500'}`}>
1056
- {category.name?.toLowerCase().includes('scrub') ? 'Professional uniforms' :
1057
- category.name?.toLowerCase().includes('vitamin') ? 'Health supplements' :
1058
- category.name?.toLowerCase().includes('medicine') ? 'OTC medications' :
1059
- category.name?.toLowerCase().includes('care') ? 'Daily essentials' :
1060
- 'Shop now'}
987
+ <p className={`font-['Poppins',sans-serif] text-[11px] ${isSelected ? 'text-white/80' : 'text-muted'
988
+ }`}>
989
+ {category.description}
1061
990
  </p>
1062
- </motion.button>
1063
- );
1064
- })}
1065
- </>
1066
- )}
991
+ </div>
992
+ </motion.button>
993
+ </>
994
+ );
995
+ })}
1067
996
  </div>
1068
997
  </div>
1069
998
  </section>
1070
999
 
1071
- <div className="relative z-10 pb-16 mt-8">
1000
+ <div className="relative pb-16 mt-8">
1072
1001
  <div className="container mx-auto px-4">
1073
1002
  <div className="flex flex-col gap-8 lg:flex-row">
1074
1003
  <aside className="hidden w-72 flex-shrink-0 lg:block">
1075
- <div className="sticky top-24 rounded-lg bg-white p-6">
1004
+ <div className="sticky top-24 rounded-lg bg-white">
1076
1005
  {renderFiltersPanel()}
1077
1006
  </div>
1078
1007
  </aside>
@@ -1084,7 +1013,8 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
1084
1013
  <h2 className="text-base font-medium text-gray-700">
1085
1014
  {isLoading
1086
1015
  ? 'Loading products...'
1087
- : `Showing ${displayedProducts.length} of ${pagination.total || displayedProducts.length} products`}
1016
+ : `${displayedProducts.length} products found`}
1017
+
1088
1018
  </h2>
1089
1019
  </div>
1090
1020
  <div className="flex flex-col gap-3 md:flex-row md:items-center">
@@ -1095,7 +1025,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
1095
1025
  onChange={(event) => {
1096
1026
  setSortOption(event.target.value as SortOption);
1097
1027
  }}
1098
- className="appearance-none rounded-xl border border-gray-200 bg-white py-2.5 pl-10 pr-9 text-sm font-medium text-gray-700 shadow-sm transition focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/30"
1028
+ className="appearance-none rounded-full border border-gray-200 bg-white py-2.5 pl-10 pr-9 text-sm font-medium text-gray-700 shadow-sm transition focus:outline-none focus:ring-1 focus:ring-secondary focus:border-primary"
1099
1029
  >
1100
1030
  <option value="featured">Featured products</option>
1101
1031
  <option value="price-low-high">Price: low to high</option>
@@ -1104,51 +1034,33 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
1104
1034
  </select>
1105
1035
  <ChevronDown className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" />
1106
1036
  </div>
1107
- <div className="flex items-center rounded-xl border border-gray-200 bg-white shadow-sm">
1037
+ <div className="flex items-center rounded-full border border-gray-200 bg-gray-100 shadow-sm p-1">
1108
1038
  <button
1109
1039
  type="button"
1110
1040
  onClick={() => setViewMode('grid')}
1111
- className={`flex items-center gap-2 rounded-l-xl px-4 py-2 text-sm font-medium transition ${viewMode === 'grid'
1112
- ? 'bg-primary-50 text-primary-600'
1113
- : 'text-gray-500 hover:text-gray-700'
1041
+ className={`flex items-center gap-2 rounded-full px-4 py-2 text-sm font-medium transition ${viewMode === 'grid'
1042
+ ? 'bg-white text-primary shadow-md'
1043
+ : 'text-gray-500 hover:text-gray-700'
1114
1044
  }`}
1115
1045
  aria-pressed={viewMode === 'grid'}
1116
1046
  >
1117
1047
  <LayoutGrid className="h-4 w-4" />
1118
- Grid
1119
1048
  </button>
1120
1049
  <button
1121
1050
  type="button"
1122
1051
  onClick={() => setViewMode('list')}
1123
- className={`flex items-center gap-2 rounded-r-xl px-4 py-2 text-sm font-medium transition ${viewMode === 'list'
1124
- ? 'bg-primary-50 text-primary-600'
1125
- : 'text-gray-500 hover:text-gray-700'
1052
+ className={`flex items-center gap-2 rounded-full px-4 py-2 text-sm font-medium transition ${viewMode === 'list'
1053
+ ? 'bg-white text-primary shadow-md'
1054
+ : 'text-gray-500 hover:text-gray-700'
1126
1055
  }`}
1127
1056
  aria-pressed={viewMode === 'list'}
1128
1057
  >
1129
1058
  <LayoutList className="h-4 w-4" />
1130
- List
1131
1059
  </button>
1132
1060
  </div>
1133
1061
  </div>
1134
1062
  </div>
1135
1063
 
1136
- <div className="mt-4 md:hidden">
1137
- <Button
1138
- variant="outline"
1139
- className="w-full"
1140
- onClick={() => setShowFilters(true)}
1141
- >
1142
- <SlidersHorizontal className="h-5 w-5" />
1143
- Filters
1144
- {hasActiveFilters && (
1145
- <span className="ml-2 rounded-full bg-primary-600 px-2 py-0.5 text-xs font-semibold text-white">
1146
- {activeFilterChips.length}
1147
- </span>
1148
- )}
1149
- </Button>
1150
- </div>
1151
-
1152
1064
  {hasActiveFilters && (
1153
1065
  <div className="mt-6 flex flex-wrap items-center gap-2 border-t border-gray-100 pt-4">
1154
1066
  {activeFilterChips.map((chip) => (
@@ -1243,11 +1155,6 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
1243
1155
  <h3 className="text-xl font-semibold text-gray-900">
1244
1156
  {product.name}
1245
1157
  </h3>
1246
- {/* {product.description && (
1247
- <p className="text-sm leading-relaxed text-gray-600 line-clamp-2">
1248
- {product.description}
1249
- </p>
1250
- )} */}
1251
1158
  <div className="flex flex-wrap items-center gap-4 text-sm text-gray-500">
1252
1159
  <span className="inline-flex items-center gap-2 font-medium text-primary-600">
1253
1160
  <ShieldCheck className="h-4 w-4" />
@@ -1265,21 +1172,21 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
1265
1172
  <p className="text-3xl font-semibold text-gray-900">
1266
1173
  {formatPrice(product.finalPrice)}
1267
1174
  </p>
1268
- {product.priceBeforeDiscount && (
1175
+ {product.isDiscounted && (
1269
1176
  <p className="text-sm text-gray-400 line-through">
1270
1177
  {formatPrice(product.priceBeforeDiscount)}
1271
1178
  </p>
1272
1179
  )}
1273
1180
  </div>
1274
- <Button
1275
- size="sm"
1181
+ <button
1276
1182
  onClick={(event) => {
1277
1183
  event.stopPropagation();
1278
1184
  router.push(buildPath(`/products/${product._id}`));
1279
1185
  }}
1186
+ className="w-full font-['Poppins',sans-serif] font-medium text-sm px-3 py-2 rounded-xl bg-secondary 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"
1280
1187
  >
1281
1188
  View product
1282
- </Button>
1189
+ </button>
1283
1190
  </div>
1284
1191
  </motion.div>
1285
1192
  );
@@ -1325,37 +1232,6 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
1325
1232
  </div>
1326
1233
  </div>
1327
1234
  </div>
1328
-
1329
- <AnimatePresence>
1330
- {showFilters && (
1331
- <motion.div
1332
- initial={{ opacity: 0 }}
1333
- animate={{ opacity: 1 }}
1334
- exit={{ opacity: 0 }}
1335
- className="fixed inset-0 z-50 bg-black/40 backdrop-blur-sm lg:hidden"
1336
- >
1337
- <motion.div
1338
- initial={{ y: '100%' }}
1339
- animate={{ y: 0 }}
1340
- exit={{ y: '100%' }}
1341
- transition={{ type: 'spring', stiffness: 260, damping: 26 }}
1342
- className="absolute inset-x-0 bottom-0 max-h-[85vh] overflow-y-auto rounded-t-3xl bg-white p-6 shadow-2xl"
1343
- >
1344
- <div className="mb-6 flex items-center justify-between">
1345
- <h3 className="text-lg font-semibold text-gray-900">Filters</h3>
1346
- <button
1347
- type="button"
1348
- onClick={() => setShowFilters(false)}
1349
- className="rounded-full border border-gray-200 p-2 text-gray-500 hover:text-gray-700"
1350
- >
1351
- <X className="h-4 w-4" />
1352
- </button>
1353
- </div>
1354
- {renderFiltersPanel()}
1355
- </motion.div>
1356
- </motion.div>
1357
- )}
1358
- </AnimatePresence>
1359
1235
  </div>
1360
1236
  );
1361
1237
  }