hey-pharmacist-ecommerce 1.1.2 → 1.1.3
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.d.mts +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +610 -588
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +416 -395
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ProductCard.tsx +3 -1
- package/src/index.ts +1 -0
- package/src/providers/BasePathProvider.tsx +36 -0
- package/src/providers/EcommerceProvider.tsx +13 -9
- package/src/screens/HomeScreen.tsx +4 -2
- package/src/screens/SearchResultsScreen.tsx +4 -2
- package/src/screens/ShopScreen.tsx +5 -3
- package/src/screens/WishlistScreen.tsx +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hey-pharmacist-ecommerce",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
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",
|
|
@@ -9,6 +9,7 @@ import { useWishlist } from '@/providers/WishlistProvider';
|
|
|
9
9
|
import Image from 'next/image';
|
|
10
10
|
import { toast } from 'sonner';
|
|
11
11
|
import { useRouter } from 'next/navigation';
|
|
12
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
interface ProductCardProps {
|
|
@@ -28,6 +29,7 @@ export function ProductCard({
|
|
|
28
29
|
className
|
|
29
30
|
}: ProductCardProps & { className?: string }) {
|
|
30
31
|
const router = useRouter();
|
|
32
|
+
const { buildPath } = useBasePath();
|
|
31
33
|
const [isFavorite, setIsFavorite] = useState(isFavorited);
|
|
32
34
|
const { addToWishlist, removeFromWishlist, isInWishlist } = useWishlist();
|
|
33
35
|
const [isHovered, setIsHovered] = useState(false);
|
|
@@ -233,7 +235,7 @@ export function ProductCard({
|
|
|
233
235
|
type="button"
|
|
234
236
|
onClick={(e) => {
|
|
235
237
|
e.stopPropagation();
|
|
236
|
-
router.push(`/products/${product._id}`);
|
|
238
|
+
router.push(buildPath(`/products/${product._id}`));
|
|
237
239
|
}}
|
|
238
240
|
className={`w-full flex items-center justify-center rounded-md px-3 py-2 text-sm font-medium bg-primary-600 hover:bg-primary-700 text-white`}
|
|
239
241
|
>
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { ThemeProvider, useTheme } from './providers/ThemeProvider';
|
|
|
6
6
|
export { AuthProvider, useAuth } from './providers/AuthProvider';
|
|
7
7
|
export { CartProvider, useCart } from './providers/CartProvider';
|
|
8
8
|
export { WishlistProvider, useWishlist } from './providers/WishlistProvider';
|
|
9
|
+
export { useBasePath } from './providers/BasePathProvider';
|
|
9
10
|
|
|
10
11
|
// Screens
|
|
11
12
|
export { ShopScreen } from './screens/ShopScreen';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext } from 'react';
|
|
4
|
+
|
|
5
|
+
interface BasePathContextValue {
|
|
6
|
+
basePath: string;
|
|
7
|
+
buildPath: (path: string) => string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const BasePathContext = createContext<BasePathContextValue | undefined>(undefined);
|
|
11
|
+
|
|
12
|
+
export function BasePathProvider({ basePath = '', children }: { basePath?: string; children: React.ReactNode }) {
|
|
13
|
+
const normalized = basePath ? (basePath.startsWith('/') ? basePath : `/${basePath}`) : '';
|
|
14
|
+
|
|
15
|
+
const buildPath = (path: string) => {
|
|
16
|
+
if (!normalized) return path;
|
|
17
|
+
if (!path) return normalized;
|
|
18
|
+
if (path.startsWith(normalized + '/')) return path; // already prefixed
|
|
19
|
+
if (path.startsWith('/')) return `${normalized}${path}`;
|
|
20
|
+
return `${normalized}/${path}`;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<BasePathContext.Provider value={{ basePath: normalized, buildPath }}>
|
|
25
|
+
{children}
|
|
26
|
+
</BasePathContext.Provider>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useBasePath(): BasePathContextValue {
|
|
31
|
+
const ctx = useContext(BasePathContext);
|
|
32
|
+
if (!ctx) throw new Error('useBasePath must be used within BasePathProvider');
|
|
33
|
+
return ctx;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
@@ -6,6 +6,7 @@ import { ThemeProvider } from './ThemeProvider';
|
|
|
6
6
|
import { AuthProvider } from './AuthProvider';
|
|
7
7
|
import { CartProvider } from './CartProvider';
|
|
8
8
|
import { WishlistProvider } from './WishlistProvider';
|
|
9
|
+
import { BasePathProvider } from './BasePathProvider';
|
|
9
10
|
import { initializeApiAdapter } from '@/lib/api-adapter';
|
|
10
11
|
import { Toaster } from 'sonner';
|
|
11
12
|
import { QueryClientProvider } from '@tanstack/react-query';
|
|
@@ -15,9 +16,10 @@ interface EcommerceProviderProps {
|
|
|
15
16
|
config: EcommerceConfig;
|
|
16
17
|
children: React.ReactNode;
|
|
17
18
|
withToaster?: boolean;
|
|
19
|
+
basePath?: string;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export function EcommerceProvider({ config, children, withToaster = true }: EcommerceProviderProps) {
|
|
22
|
+
export function EcommerceProvider({ config, children, withToaster = true, basePath = '' }: EcommerceProviderProps) {
|
|
21
23
|
useEffect(() => {
|
|
22
24
|
// Initialize API adapter with store configuration
|
|
23
25
|
// This sets up the real backend APIs with proper authentication and store ID
|
|
@@ -30,14 +32,16 @@ export function EcommerceProvider({ config, children, withToaster = true }: Ecom
|
|
|
30
32
|
return (
|
|
31
33
|
<QueryClientProvider client={client}>
|
|
32
34
|
<ThemeProvider config={config}>
|
|
33
|
-
<
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
<BasePathProvider basePath={basePath}>
|
|
36
|
+
<AuthProvider>
|
|
37
|
+
<CartProvider>
|
|
38
|
+
<WishlistProvider>
|
|
39
|
+
{children}
|
|
40
|
+
{withToaster && <Toaster position="top-right" richColors />}
|
|
41
|
+
</WishlistProvider>
|
|
42
|
+
</CartProvider>
|
|
43
|
+
</AuthProvider>
|
|
44
|
+
</BasePathProvider>
|
|
41
45
|
</ThemeProvider>
|
|
42
46
|
</QueryClientProvider>
|
|
43
47
|
);
|
|
@@ -8,10 +8,12 @@ import { ProductCard } from '@/components/ProductCard';
|
|
|
8
8
|
import { useWishlist } from '@/providers/WishlistProvider';
|
|
9
9
|
import { ProductCardSkeleton } from '@/components/ui/Skeleton';
|
|
10
10
|
import { useRouter } from 'next/navigation';
|
|
11
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
11
12
|
import { useProducts } from '@/hooks/useProducts';
|
|
12
13
|
|
|
13
14
|
export default function HomeScreen() {
|
|
14
|
-
|
|
15
|
+
const router = useRouter();
|
|
16
|
+
const { buildPath } = useBasePath();
|
|
15
17
|
const { products, isLoading } = useProducts();
|
|
16
18
|
const { isInWishlist } = useWishlist();
|
|
17
19
|
|
|
@@ -156,7 +158,7 @@ export default function HomeScreen() {
|
|
|
156
158
|
<ProductCard
|
|
157
159
|
product={product}
|
|
158
160
|
isFavorited={isInWishlist(product.id)}
|
|
159
|
-
onClickProduct={(p) => router.push(`/products/${p.id}`)}
|
|
161
|
+
onClickProduct={(p) => router.push(buildPath(`/products/${p.id}`))}
|
|
160
162
|
/>
|
|
161
163
|
</motion.div>
|
|
162
164
|
);
|
|
@@ -12,9 +12,11 @@ import Link from 'next/link';
|
|
|
12
12
|
import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
|
|
13
13
|
import { useWishlist } from '@/providers/WishlistProvider';
|
|
14
14
|
import { useRouter } from 'next/navigation';
|
|
15
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
15
16
|
|
|
16
17
|
export default function SearchPage() {
|
|
17
|
-
|
|
18
|
+
const router = useRouter();
|
|
19
|
+
const { buildPath } = useBasePath();
|
|
18
20
|
const searchParams = useSearchParams();
|
|
19
21
|
const searchQuery = searchParams.get('q') || '';
|
|
20
22
|
const [products, setProducts] = useState<ExtendedProductDTO[]>([]);
|
|
@@ -136,7 +138,7 @@ export default function SearchPage() {
|
|
|
136
138
|
<ProductCard
|
|
137
139
|
product={product}
|
|
138
140
|
isFavorited={isInWishlist(product.id)}
|
|
139
|
-
onClickProduct={(p) => router.push(`/products/${p.id}`)}
|
|
141
|
+
onClickProduct={(p) => router.push(buildPath(`/products/${p.id}`))}
|
|
140
142
|
/>
|
|
141
143
|
))}
|
|
142
144
|
</div>
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from 'lucide-react';
|
|
24
24
|
import Image from 'next/image';
|
|
25
25
|
import { useRouter } from 'next/navigation';
|
|
26
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
26
27
|
import { ProductCard } from '@/components/ProductCard';
|
|
27
28
|
import { ProductCardSkeleton } from '@/components/ui/Skeleton';
|
|
28
29
|
import { EmptyState } from '@/components/EmptyState';
|
|
@@ -42,6 +43,7 @@ interface ShopScreenProps {
|
|
|
42
43
|
|
|
43
44
|
export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProps) {
|
|
44
45
|
const router = useRouter();
|
|
46
|
+
const { buildPath } = useBasePath();
|
|
45
47
|
const [filters, setFilters] = useState<ProductFilters>(initialFilters);
|
|
46
48
|
const [page, setPage] = useState(1);
|
|
47
49
|
const [showFilters, setShowFilters] = useState(false);
|
|
@@ -1026,7 +1028,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
1026
1028
|
product={product}
|
|
1027
1029
|
onClickProduct={(item) => {
|
|
1028
1030
|
const productData = encodeURIComponent(JSON.stringify(item));
|
|
1029
|
-
router.push(`/products/${item.id}?product=${productData}`);
|
|
1031
|
+
router.push(buildPath(`/products/${item.id}?product=${productData}`));
|
|
1030
1032
|
}}
|
|
1031
1033
|
/>
|
|
1032
1034
|
</div>
|
|
@@ -1049,7 +1051,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
1049
1051
|
key={product.id}
|
|
1050
1052
|
whileHover={{ y: -4 }}
|
|
1051
1053
|
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"
|
|
1052
|
-
onClick={() => router.push(`/products/${product.id}`)}
|
|
1054
|
+
onClick={() => router.push(buildPath(`/products/${product.id}`))}
|
|
1053
1055
|
>
|
|
1054
1056
|
<div className="relative h-48 w-full overflow-hidden rounded-2xl bg-gray-100 md:h-40 md:w-40">
|
|
1055
1057
|
<Image
|
|
@@ -1111,7 +1113,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
|
|
|
1111
1113
|
size="sm"
|
|
1112
1114
|
onClick={(event) => {
|
|
1113
1115
|
event.stopPropagation();
|
|
1114
|
-
router.push(`/products/${product._id}`);
|
|
1116
|
+
router.push(buildPath(`/products/${product._id}`));
|
|
1115
1117
|
}}
|
|
1116
1118
|
>
|
|
1117
1119
|
View product
|
|
@@ -23,6 +23,7 @@ import { useWishlistProducts } from '@/hooks/useWishlistProducts';
|
|
|
23
23
|
import Image from 'next/image';
|
|
24
24
|
import { formatPrice } from '@/lib/utils/format';
|
|
25
25
|
import { ExtendedProductDTO } from '@/lib/Apis';
|
|
26
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
26
27
|
|
|
27
28
|
type SortOption = 'featured' | 'price-low' | 'price-high' | 'name' | 'availability';
|
|
28
29
|
|
|
@@ -45,6 +46,7 @@ interface InsightCardProps {
|
|
|
45
46
|
|
|
46
47
|
export default function WishlistScreen() {
|
|
47
48
|
const router = useRouter();
|
|
49
|
+
const { buildPath } = useBasePath();
|
|
48
50
|
const { isAuthenticated } = useAuth() || {};
|
|
49
51
|
const {
|
|
50
52
|
products: wishlistItems,
|
|
@@ -314,7 +316,7 @@ export default function WishlistScreen() {
|
|
|
314
316
|
>
|
|
315
317
|
<ProductCard
|
|
316
318
|
product={product as ExtendedProductDTO}
|
|
317
|
-
|
|
319
|
+
onClickProduct={(p) => router.push(buildPath(`/products/${p.id}`))}
|
|
318
320
|
onFavorite={() => handleRemoveFromWishlist(product.id)}
|
|
319
321
|
isFavorited
|
|
320
322
|
/>
|