hey-pharmacist-ecommerce 1.1.9 → 1.1.11
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/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/screens/ShopScreen.tsx +107 -116
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hey-pharmacist-ecommerce",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
4
4
|
"description": "Production-ready, multi-tenant e‑commerce UI + API adapter for Next.js with auth, carts, checkout, orders, theming, and pharmacist-focused UX.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -573,103 +573,101 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
573
573
|
|
|
574
574
|
const renderFiltersPanel = () => (
|
|
575
575
|
<>
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
)}
|
|
594
|
-
</div>
|
|
595
|
-
|
|
596
|
-
<div className="space-y-6">
|
|
597
|
-
<div className="space-y-3">
|
|
598
|
-
<h4 className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-500">Categories</h4>
|
|
599
|
-
<div className="space-y-2">
|
|
600
|
-
{sortedCategories.length === 0 && (
|
|
601
|
-
<span className="text-sm text-gray-500">No categories available yet.</span>
|
|
576
|
+
<div className="space-y-8">
|
|
577
|
+
<div className="space-y-8">
|
|
578
|
+
<div className="flex items-start justify-between gap-3">
|
|
579
|
+
<div>
|
|
580
|
+
<h3 className="text-lg font-semibold text-gray-900">Refine results</h3>
|
|
581
|
+
<p className="mt-1 text-sm text-gray-500">
|
|
582
|
+
Filter by category, price, and availability to find the perfect fit faster.
|
|
583
|
+
</p>
|
|
584
|
+
</div>
|
|
585
|
+
{hasActiveFilters && (
|
|
586
|
+
<button
|
|
587
|
+
type="button"
|
|
588
|
+
onClick={handleClearFilters}
|
|
589
|
+
className="text-sm font-semibold text-primary-600 hover:text-primary-700"
|
|
590
|
+
>
|
|
591
|
+
Clear all
|
|
592
|
+
</button>
|
|
602
593
|
)}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
594
|
+
</div>
|
|
595
|
+
|
|
596
|
+
<div className="space-y-6">
|
|
597
|
+
<div className="space-y-3">
|
|
598
|
+
<h4 className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-500">Categories</h4>
|
|
599
|
+
<div className="space-y-2">
|
|
600
|
+
{sortedCategories.length === 0 && (
|
|
601
|
+
<span className="text-sm text-gray-500">No categories available yet.</span>
|
|
602
|
+
)}
|
|
603
|
+
{sortedCategories.map((category) => {
|
|
604
|
+
const isCategoryActive = categoryFilter === category.id;
|
|
605
|
+
const isExpanded = !!expandedCategories[category.id as string];
|
|
606
|
+
return (
|
|
607
|
+
<div key={category.id} className="rounded-xl border-gray-100 bg-white/50">
|
|
608
|
+
<div
|
|
609
|
+
role="button"
|
|
610
|
+
tabIndex={0}
|
|
611
|
+
onClick={() => {
|
|
612
|
+
// Selecting a category also expands it for quick access to subcategories
|
|
613
|
+
if (!isExpanded) toggleCategoryExpand(category.id ?? '');
|
|
614
|
+
handleCategoryChange(category.id ?? '');
|
|
615
|
+
}}
|
|
616
|
+
className={`flex w-full items-center justify-between rounded-xl px-3 py-2 text-sm font-medium transition ${isCategoryActive
|
|
617
|
+
? 'text-primary-700 bg-primary-50'
|
|
618
|
+
: 'text-gray-700 hover:text-primary-700 hover:bg-primary-50/50'
|
|
619
|
+
}`}
|
|
620
|
+
>
|
|
621
|
+
<span className="flex items-center gap-2">
|
|
622
|
+
<span className='font-medium'>{category.name}</span>
|
|
623
|
+
{category.productCount > 0 && (
|
|
624
|
+
<span className={`ml-1 rounded-full bg-gray-100 px-2 py-0.5 text-xs ${isCategoryActive ? 'text-primary-700' : 'text-gray-500'}`}>
|
|
625
|
+
{category.productCount}
|
|
626
|
+
</span>
|
|
627
|
+
)}
|
|
627
628
|
</span>
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
<ChevronDown className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-180 text-primary-600' : 'rotate-0 text-gray-400'}`} />
|
|
641
|
-
</button>
|
|
642
|
-
</div>
|
|
643
|
-
{isExpanded && Array.isArray(category.categorySubCategories) && category.categorySubCategories.length > 0 && (
|
|
644
|
-
<div className="mt-1 border-gray-100 px-2 pb-2 pl-4">
|
|
645
|
-
<div className="divide-y divide-gray-100">
|
|
646
|
-
{category.categorySubCategories.map((sub) => {
|
|
647
|
-
const isSubActive = subCategoryFilter === sub.id;
|
|
648
|
-
return (
|
|
649
|
-
<button
|
|
650
|
-
key={sub.id}
|
|
651
|
-
type="button"
|
|
652
|
-
onClick={() => handleSubCategoryChange(category.id ?? '', sub.id)}
|
|
653
|
-
className={`block w-full px-2 py-2 text-sm text-start transition ${
|
|
654
|
-
isSubActive
|
|
655
|
-
? 'text-primary-700'
|
|
656
|
-
: 'text-gray-600 hover:text-primary-700 '
|
|
657
|
-
}`}
|
|
658
|
-
>
|
|
659
|
-
{sub.name}
|
|
660
|
-
</button>
|
|
661
|
-
);
|
|
662
|
-
})}
|
|
629
|
+
<button
|
|
630
|
+
type="button"
|
|
631
|
+
onClick={(e) => {
|
|
632
|
+
e.preventDefault();
|
|
633
|
+
e.stopPropagation();
|
|
634
|
+
toggleCategoryExpand(category.id ?? '');
|
|
635
|
+
}}
|
|
636
|
+
className="rounded-md p-1 hover:bg-gray-100"
|
|
637
|
+
aria-label={isExpanded ? 'Collapse' : 'Expand'}
|
|
638
|
+
>
|
|
639
|
+
<ChevronDown className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-180 text-primary-600' : 'rotate-0 text-gray-400'}`} />
|
|
640
|
+
</button>
|
|
663
641
|
</div>
|
|
642
|
+
{isExpanded && Array.isArray(category.categorySubCategories) && category.categorySubCategories.length > 0 && (
|
|
643
|
+
<div className="mt-1 border-gray-100 px-2 pb-2 pl-4">
|
|
644
|
+
<div className="divide-y divide-gray-100">
|
|
645
|
+
{category.categorySubCategories.map((sub) => {
|
|
646
|
+
const isSubActive = subCategoryFilter === sub.id;
|
|
647
|
+
return (
|
|
648
|
+
<button
|
|
649
|
+
key={sub.id}
|
|
650
|
+
type="button"
|
|
651
|
+
onClick={() => handleSubCategoryChange(category.id ?? '', sub.id)}
|
|
652
|
+
className={`block w-full px-2 py-2 text-sm text-start transition ${isSubActive
|
|
653
|
+
? 'text-primary-700'
|
|
654
|
+
: 'text-gray-600 hover:text-primary-700 '
|
|
655
|
+
}`}
|
|
656
|
+
>
|
|
657
|
+
{sub.name}
|
|
658
|
+
</button>
|
|
659
|
+
);
|
|
660
|
+
})}
|
|
661
|
+
</div>
|
|
662
|
+
</div>
|
|
663
|
+
)}
|
|
664
664
|
</div>
|
|
665
|
-
)
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
665
|
+
);
|
|
666
|
+
})}
|
|
667
|
+
</div>
|
|
668
|
+
</div>
|
|
669
669
|
</div>
|
|
670
670
|
</div>
|
|
671
|
-
</div>
|
|
672
|
-
</div>
|
|
673
671
|
|
|
674
672
|
<div className="space-y-4">
|
|
675
673
|
<h4 className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-500">
|
|
@@ -683,11 +681,10 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
683
681
|
type="button"
|
|
684
682
|
key={range.value}
|
|
685
683
|
onClick={() => handlePriceRangeSelect(range.value)}
|
|
686
|
-
className={`rounded-full border px-3 py-1.5 text-sm font-medium transition ${
|
|
687
|
-
isActive
|
|
684
|
+
className={`rounded-full border px-3 py-1.5 text-sm font-medium transition ${isActive
|
|
688
685
|
? 'border-secondary-600 bg-secondary-600 text-white shadow-lg shadow-secondary-500/30'
|
|
689
686
|
: 'border-gray-200 bg-white text-gray-600 hover:border-secondary-300 hover:text-secondary-600'
|
|
690
|
-
|
|
687
|
+
}`}
|
|
691
688
|
>
|
|
692
689
|
{range.label}
|
|
693
690
|
</button>
|
|
@@ -731,11 +728,10 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
731
728
|
<button
|
|
732
729
|
type="button"
|
|
733
730
|
onClick={handleToggleStock}
|
|
734
|
-
className={`flex w-full items-center justify-between rounded-xl border px-4 py-3 text-sm font-medium transition ${
|
|
735
|
-
inStockOnly
|
|
731
|
+
className={`flex w-full items-center justify-between rounded-xl border px-4 py-3 text-sm font-medium transition ${inStockOnly
|
|
736
732
|
? 'border-primary-500 bg-primary-50 text-primary-700'
|
|
737
733
|
: 'border-gray-200 bg-white text-gray-600 hover:border-primary-300 hover:text-primary-600'
|
|
738
|
-
|
|
734
|
+
}`}
|
|
739
735
|
>
|
|
740
736
|
<span>In stock only</span>
|
|
741
737
|
<Sparkles className="h-4 w-4 text-primary-500" />
|
|
@@ -743,11 +739,10 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
743
739
|
<button
|
|
744
740
|
type="button"
|
|
745
741
|
onClick={handleToggleNewArrivals}
|
|
746
|
-
className={`mt-2 flex w-full items-center justify-between rounded-xl border px-4 py-3 text-sm font-medium transition ${
|
|
747
|
-
newArrivals
|
|
742
|
+
className={`mt-2 flex w-full items-center justify-between rounded-xl border px-4 py-3 text-sm font-medium transition ${newArrivals
|
|
748
743
|
? 'border-secondary-500 bg-secondary-50 text-secondary-700'
|
|
749
744
|
: 'border-gray-200 bg-white text-gray-600 hover:border-secondary-300 hover:text-secondary-600'
|
|
750
|
-
|
|
745
|
+
}`}
|
|
751
746
|
>
|
|
752
747
|
<span>New arrivals (last 30 days)</span>
|
|
753
748
|
<Sparkles className="h-4 w-4 text-secondary-500" />
|
|
@@ -864,11 +859,10 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
864
859
|
initial={{ opacity: 0, y: 20 }}
|
|
865
860
|
animate={{ opacity: 1, y: 0 }}
|
|
866
861
|
transition={{ delay: 0.2 + index * 0.05 }}
|
|
867
|
-
className={`rounded-2xl border p-5 backdrop-blur ${
|
|
868
|
-
card.id === 'new' && newArrivals
|
|
862
|
+
className={`rounded-2xl border p-5 backdrop-blur ${card.id === 'new' && newArrivals
|
|
869
863
|
? 'border-white/40 bg-white/25 ring-2 ring-white/30'
|
|
870
864
|
: 'border-white/20 bg-white/15'
|
|
871
|
-
|
|
865
|
+
} ${card.id === 'new' ? 'cursor-pointer hover:bg-white/20' : ''}`}
|
|
872
866
|
onClick={card.id === 'new' ? handleToggleNewArrivals : undefined}
|
|
873
867
|
role={card.id === 'new' ? 'button' : undefined}
|
|
874
868
|
aria-pressed={card.id === 'new' ? (newArrivals ? 'true' : 'false') : undefined}
|
|
@@ -932,11 +926,10 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
932
926
|
<button
|
|
933
927
|
type="button"
|
|
934
928
|
onClick={() => setViewMode('grid')}
|
|
935
|
-
className={`flex items-center gap-2 rounded-l-xl px-4 py-2 text-sm font-medium transition ${
|
|
936
|
-
viewMode === 'grid'
|
|
929
|
+
className={`flex items-center gap-2 rounded-l-xl px-4 py-2 text-sm font-medium transition ${viewMode === 'grid'
|
|
937
930
|
? 'bg-primary-50 text-primary-600'
|
|
938
931
|
: 'text-gray-500 hover:text-gray-700'
|
|
939
|
-
|
|
932
|
+
}`}
|
|
940
933
|
aria-pressed={viewMode === 'grid'}
|
|
941
934
|
>
|
|
942
935
|
<LayoutGrid className="h-4 w-4" />
|
|
@@ -945,11 +938,10 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
945
938
|
<button
|
|
946
939
|
type="button"
|
|
947
940
|
onClick={() => setViewMode('list')}
|
|
948
|
-
className={`flex items-center gap-2 rounded-r-xl px-4 py-2 text-sm font-medium transition ${
|
|
949
|
-
viewMode === 'list'
|
|
941
|
+
className={`flex items-center gap-2 rounded-r-xl px-4 py-2 text-sm font-medium transition ${viewMode === 'list'
|
|
950
942
|
? 'bg-primary-50 text-primary-600'
|
|
951
943
|
: 'text-gray-500 hover:text-gray-700'
|
|
952
|
-
|
|
944
|
+
}`}
|
|
953
945
|
aria-pressed={viewMode === 'list'}
|
|
954
946
|
>
|
|
955
947
|
<LayoutList className="h-4 w-4" />
|
|
@@ -1027,8 +1019,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
1027
1019
|
<ProductCard
|
|
1028
1020
|
product={product}
|
|
1029
1021
|
onClickProduct={(item) => {
|
|
1030
|
-
|
|
1031
|
-
router.push(buildPath(`/products/${item.id}?product=${productData}`));
|
|
1022
|
+
router.push(buildPath(`/products/${item._id}`));
|
|
1032
1023
|
}}
|
|
1033
1024
|
/>
|
|
1034
1025
|
</div>
|
|
@@ -1040,10 +1031,10 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
1040
1031
|
const discount =
|
|
1041
1032
|
product.priceBeforeDiscount && product.priceBeforeDiscount > product.finalPrice
|
|
1042
1033
|
? Math.round(
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1034
|
+
((product.priceBeforeDiscount - product.finalPrice) /
|
|
1035
|
+
product.priceBeforeDiscount) *
|
|
1036
|
+
100
|
|
1037
|
+
)
|
|
1047
1038
|
: 0;
|
|
1048
1039
|
|
|
1049
1040
|
return (
|
|
@@ -1051,7 +1042,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
1051
1042
|
key={product.id}
|
|
1052
1043
|
whileHover={{ y: -4 }}
|
|
1053
1044
|
className="group flex cursor-pointer flex-col gap-6 rounded-2xl border border-gray-100 bg-white p-5 shadow-sm transition hover:shadow-xl md:flex-row md:items-start"
|
|
1054
|
-
onClick={() => router.push(buildPath(`/products/${product.
|
|
1045
|
+
onClick={() => router.push(buildPath(`/products/${product._id}`))}
|
|
1055
1046
|
>
|
|
1056
1047
|
<div className="relative h-48 w-full overflow-hidden rounded-2xl bg-gray-100 md:h-40 md:w-40">
|
|
1057
1048
|
<Image
|