hey-pharmacist-ecommerce 1.0.5 → 1.0.6
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 +107 -1
- package/dist/index.d.mts +3636 -316
- package/dist/index.d.ts +3636 -316
- package/dist/index.js +6802 -3866
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6756 -3818
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -14
- package/src/components/AddressFormModal.tsx +171 -0
- package/src/components/CartItem.tsx +17 -12
- package/src/components/FilterChips.tsx +195 -0
- package/src/components/Header.tsx +121 -71
- package/src/components/OrderCard.tsx +18 -25
- package/src/components/ProductCard.tsx +209 -72
- package/src/components/ui/Button.tsx +13 -5
- package/src/components/ui/Card.tsx +46 -0
- package/src/hooks/useAddresses.ts +83 -0
- package/src/hooks/useOrders.ts +37 -19
- package/src/hooks/useProducts.ts +55 -63
- package/src/hooks/useWishlistProducts.ts +75 -0
- package/src/index.ts +3 -19
- package/src/lib/Apis/api.ts +1 -0
- package/src/lib/Apis/apis/cart-api.ts +3 -3
- package/src/lib/Apis/apis/inventory-api.ts +0 -108
- package/src/lib/Apis/apis/stores-api.ts +70 -0
- package/src/lib/Apis/apis/wishlist-api.ts +447 -0
- package/src/lib/Apis/models/cart-item-populated.ts +0 -1
- package/src/lib/Apis/models/create-single-variant-product-dto.ts +3 -10
- package/src/lib/Apis/models/create-variant-dto.ts +26 -33
- package/src/lib/Apis/models/extended-product-dto.ts +20 -24
- package/src/lib/Apis/models/index.ts +2 -1
- package/src/lib/Apis/models/order-time-line-dto.ts +49 -0
- package/src/lib/Apis/models/order.ts +3 -8
- package/src/lib/Apis/models/populated-order.ts +3 -8
- package/src/lib/Apis/models/product-variant.ts +29 -0
- package/src/lib/Apis/models/update-product-variant-dto.ts +16 -23
- package/src/lib/Apis/models/wishlist.ts +51 -0
- package/src/lib/Apis/wrapper.ts +18 -7
- package/src/lib/api-adapter/index.ts +0 -12
- package/src/lib/types/index.ts +16 -61
- package/src/lib/utils/colors.ts +7 -4
- package/src/lib/utils/format.ts +1 -1
- package/src/lib/validations/address.ts +14 -0
- package/src/providers/AuthProvider.tsx +61 -31
- package/src/providers/CartProvider.tsx +18 -28
- package/src/providers/EcommerceProvider.tsx +7 -0
- package/src/providers/FavoritesProvider.tsx +86 -0
- package/src/providers/ThemeProvider.tsx +16 -1
- package/src/providers/WishlistProvider.tsx +174 -0
- package/src/screens/AddressesScreen.tsx +484 -0
- package/src/screens/CartScreen.tsx +120 -84
- package/src/screens/CategoriesScreen.tsx +120 -0
- package/src/screens/CheckoutScreen.tsx +919 -241
- package/src/screens/CurrentOrdersScreen.tsx +125 -61
- package/src/screens/HomeScreen.tsx +209 -0
- package/src/screens/LoginScreen.tsx +133 -88
- package/src/screens/NewAddressScreen.tsx +187 -0
- package/src/screens/OrdersScreen.tsx +162 -50
- package/src/screens/ProductDetailScreen.tsx +641 -190
- package/src/screens/ProfileScreen.tsx +192 -116
- package/src/screens/RegisterScreen.tsx +193 -144
- package/src/screens/SearchResultsScreen.tsx +165 -0
- package/src/screens/ShopScreen.tsx +1110 -146
- package/src/screens/WishlistScreen.tsx +428 -0
- package/src/lib/Apis/models/inventory-paginated-response.ts +0 -75
- package/src/lib/api/auth.ts +0 -81
- package/src/lib/api/cart.ts +0 -42
- package/src/lib/api/orders.ts +0 -53
- package/src/lib/api/products.ts +0 -51
- package/src/lib/api-adapter/auth-adapter.ts +0 -196
- package/src/lib/api-adapter/cart-adapter.ts +0 -193
- package/src/lib/api-adapter/mappers.ts +0 -152
- package/src/lib/api-adapter/orders-adapter.ts +0 -195
- package/src/lib/api-adapter/products-adapter.ts +0 -194
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
|
|
4
|
-
import { Cart, CartItem } from '@/lib/types';
|
|
5
|
-
import { cartApi } from '@/lib/api/cart';
|
|
6
4
|
import { useAuth } from './AuthProvider';
|
|
7
5
|
import { toast } from 'sonner';
|
|
6
|
+
import { CartResponseDto } from '@/lib/Apis/models';
|
|
7
|
+
import { CartApi } from '@/lib/Apis';
|
|
8
|
+
import { getApiConfiguration } from '@/lib/api-adapter';
|
|
8
9
|
|
|
9
10
|
interface CartContextValue {
|
|
10
|
-
cart:
|
|
11
|
+
cart: CartResponseDto | null;
|
|
11
12
|
isLoading: boolean;
|
|
12
|
-
addToCart: (productId: string, quantity?: number) => Promise<void>;
|
|
13
|
+
addToCart: (productId: string, quantity?: number, variantId?: string) => Promise<void>;
|
|
13
14
|
updateQuantity: (productId: string, quantity: number) => Promise<void>;
|
|
14
15
|
removeFromCart: (productId: string) => Promise<void>;
|
|
15
16
|
clearCart: () => Promise<void>;
|
|
@@ -31,7 +32,7 @@ interface CartProviderProps {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export function CartProvider({ children }: CartProviderProps) {
|
|
34
|
-
const [cart, setCart] = useState<
|
|
35
|
+
const [cart, setCart] = useState<CartResponseDto | null>(null);
|
|
35
36
|
const [isLoading, setIsLoading] = useState(false);
|
|
36
37
|
const { isAuthenticated } = useAuth();
|
|
37
38
|
|
|
@@ -42,10 +43,8 @@ export function CartProvider({ children }: CartProviderProps) {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
try {
|
|
45
|
-
const response = await
|
|
46
|
-
|
|
47
|
-
setCart(response.data);
|
|
48
|
-
}
|
|
46
|
+
const response = await new CartApi(getApiConfiguration()).getUserCart();
|
|
47
|
+
setCart(response.data);
|
|
49
48
|
} catch (error) {
|
|
50
49
|
console.error('Failed to fetch cart:', error);
|
|
51
50
|
}
|
|
@@ -55,14 +54,12 @@ export function CartProvider({ children }: CartProviderProps) {
|
|
|
55
54
|
refreshCart();
|
|
56
55
|
}, [refreshCart]);
|
|
57
56
|
|
|
58
|
-
const addToCart = async (productId: string, quantity: number = 1) => {
|
|
57
|
+
const addToCart = async (productId: string, quantity: number = 1, variantId?: string) => {
|
|
59
58
|
setIsLoading(true);
|
|
60
59
|
try {
|
|
61
|
-
const response = await
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
toast.success('Added to cart!');
|
|
65
|
-
}
|
|
60
|
+
const response = await new CartApi(getApiConfiguration()).handleUserCart({ items: [{ productVariantId: variantId || productId, quantity }] });
|
|
61
|
+
setCart(response.data);
|
|
62
|
+
toast.success('Added to cart!');
|
|
66
63
|
} catch (error: any) {
|
|
67
64
|
toast.error(error.response?.data?.message || 'Failed to add to cart');
|
|
68
65
|
throw error;
|
|
@@ -74,10 +71,8 @@ export function CartProvider({ children }: CartProviderProps) {
|
|
|
74
71
|
const updateQuantity = async (productId: string, quantity: number) => {
|
|
75
72
|
setIsLoading(true);
|
|
76
73
|
try {
|
|
77
|
-
const response = await
|
|
78
|
-
|
|
79
|
-
setCart(response.data);
|
|
80
|
-
}
|
|
74
|
+
const response = await new CartApi(getApiConfiguration()).handleUserCart({ items: [{ productVariantId: productId, quantity }] });
|
|
75
|
+
setCart(response.data);
|
|
81
76
|
} catch (error: any) {
|
|
82
77
|
toast.error(error.response?.data?.message || 'Failed to update cart');
|
|
83
78
|
throw error;
|
|
@@ -89,11 +84,8 @@ export function CartProvider({ children }: CartProviderProps) {
|
|
|
89
84
|
const removeFromCart = async (productId: string) => {
|
|
90
85
|
setIsLoading(true);
|
|
91
86
|
try {
|
|
92
|
-
const response = await
|
|
93
|
-
|
|
94
|
-
setCart(response.data);
|
|
95
|
-
toast.success('Removed from cart');
|
|
96
|
-
}
|
|
87
|
+
const response = await new CartApi(getApiConfiguration()).handleUserCart({ items: [{ productVariantId: productId, quantity: 0 }] });
|
|
88
|
+
setCart(response.data);
|
|
97
89
|
} catch (error: any) {
|
|
98
90
|
toast.error(error.response?.data?.message || 'Failed to remove from cart');
|
|
99
91
|
throw error;
|
|
@@ -105,9 +97,8 @@ export function CartProvider({ children }: CartProviderProps) {
|
|
|
105
97
|
const clearCart = async () => {
|
|
106
98
|
setIsLoading(true);
|
|
107
99
|
try {
|
|
108
|
-
await
|
|
109
|
-
setCart(
|
|
110
|
-
toast.success('Cart cleared');
|
|
100
|
+
const response = await new CartApi(getApiConfiguration()).clearCart();
|
|
101
|
+
setCart(null);
|
|
111
102
|
} catch (error: any) {
|
|
112
103
|
toast.error(error.response?.data?.message || 'Failed to clear cart');
|
|
113
104
|
throw error;
|
|
@@ -115,7 +106,6 @@ export function CartProvider({ children }: CartProviderProps) {
|
|
|
115
106
|
setIsLoading(false);
|
|
116
107
|
}
|
|
117
108
|
};
|
|
118
|
-
|
|
119
109
|
const value: CartContextValue = {
|
|
120
110
|
cart,
|
|
121
111
|
isLoading,
|
|
@@ -7,6 +7,8 @@ import { AuthProvider } from './AuthProvider';
|
|
|
7
7
|
import { CartProvider } from './CartProvider';
|
|
8
8
|
import { initializeApiAdapter } from '@/lib/api-adapter';
|
|
9
9
|
import { Toaster } from 'sonner';
|
|
10
|
+
import { QueryClientProvider } from '@tanstack/react-query';
|
|
11
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
10
12
|
|
|
11
13
|
interface EcommerceProviderProps {
|
|
12
14
|
config: EcommerceConfig;
|
|
@@ -20,7 +22,11 @@ export function EcommerceProvider({ config, children }: EcommerceProviderProps)
|
|
|
20
22
|
initializeApiAdapter(config);
|
|
21
23
|
}, [config]);
|
|
22
24
|
|
|
25
|
+
const [client] = React.useState(
|
|
26
|
+
new QueryClient({ defaultOptions: { queries: { staleTime: 5000 } } })
|
|
27
|
+
);
|
|
23
28
|
return (
|
|
29
|
+
<QueryClientProvider client={client}>
|
|
24
30
|
<ThemeProvider config={config}>
|
|
25
31
|
<AuthProvider>
|
|
26
32
|
<CartProvider>
|
|
@@ -29,6 +35,7 @@ export function EcommerceProvider({ config, children }: EcommerceProviderProps)
|
|
|
29
35
|
</CartProvider>
|
|
30
36
|
</AuthProvider>
|
|
31
37
|
</ThemeProvider>
|
|
38
|
+
</QueryClientProvider>
|
|
32
39
|
);
|
|
33
40
|
}
|
|
34
41
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ExtendedProductDTO } from '@/lib/Apis';
|
|
4
|
+
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
|
5
|
+
import { toast } from 'sonner';
|
|
6
|
+
|
|
7
|
+
interface FavoritesContextType {
|
|
8
|
+
favorites: string[];
|
|
9
|
+
isFavorite: (productId: string) => boolean;
|
|
10
|
+
toggleFavorite: (product: ExtendedProductDTO) => void;
|
|
11
|
+
addToFavorites: (product: ExtendedProductDTO) => void;
|
|
12
|
+
removeFromFavorites: (productId: string) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const FavoritesContext = createContext<FavoritesContextType | undefined>(undefined);
|
|
16
|
+
|
|
17
|
+
export function FavoritesProvider({ children }: { children: ReactNode }) {
|
|
18
|
+
const [favorites, setFavorites] = useState<string[]>([]);
|
|
19
|
+
const [isClient, setIsClient] = useState(false);
|
|
20
|
+
|
|
21
|
+
// Initialize state on client-side to avoid hydration issues
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setIsClient(true);
|
|
24
|
+
const savedFavorites = localStorage?.getItem('favorites');
|
|
25
|
+
if (savedFavorites) {
|
|
26
|
+
try {
|
|
27
|
+
setFavorites(JSON.parse(savedFavorites));
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('Failed to parse favorites from localStorage', error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
// Save to localStorage whenever favorites change
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (isClient) {
|
|
37
|
+
localStorage.setItem('favorites', JSON.stringify(favorites));
|
|
38
|
+
}
|
|
39
|
+
}, [favorites, isClient]);
|
|
40
|
+
|
|
41
|
+
const isFavorite = (productId: string) => {
|
|
42
|
+
return favorites.includes(productId);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const addToFavorites = (product: ExtendedProductDTO) => {
|
|
46
|
+
if (!favorites.includes(product.id)) {
|
|
47
|
+
setFavorites(prev => [...prev, product.id]);
|
|
48
|
+
toast.success(`${product.name} added to favorites`);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const removeFromFavorites = (productId: string) => {
|
|
53
|
+
setFavorites(prev => prev.filter(id => id !== productId));
|
|
54
|
+
toast.info('Removed from favorites');
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const toggleFavorite = (product: ExtendedProductDTO) => {
|
|
58
|
+
if (isFavorite(product.id)) {
|
|
59
|
+
removeFromFavorites(product.id);
|
|
60
|
+
} else {
|
|
61
|
+
addToFavorites(product);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<FavoritesContext.Provider
|
|
67
|
+
value={{
|
|
68
|
+
favorites,
|
|
69
|
+
isFavorite,
|
|
70
|
+
toggleFavorite,
|
|
71
|
+
addToFavorites,
|
|
72
|
+
removeFromFavorites
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
{children}
|
|
76
|
+
</FavoritesContext.Provider>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function useFavorites() {
|
|
81
|
+
const context = useContext(FavoritesContext);
|
|
82
|
+
if (context === undefined) {
|
|
83
|
+
throw new Error('useFavorites must be used within a FavoritesProvider');
|
|
84
|
+
}
|
|
85
|
+
return context;
|
|
86
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { createContext, useContext, useEffect } from 'react';
|
|
4
4
|
import { EcommerceConfig } from '@/lib/types';
|
|
5
|
-
import { generateColorShades } from '@/lib/utils/colors';
|
|
5
|
+
import { generateColorShades, hexToRgb } from '@/lib/utils/colors';
|
|
6
6
|
|
|
7
7
|
interface ThemeContextValue {
|
|
8
8
|
config: EcommerceConfig;
|
|
@@ -46,6 +46,21 @@ export function ThemeProvider({ config, children }: ThemeProviderProps) {
|
|
|
46
46
|
Object.entries(accentShades).forEach(([shade, rgb]) => {
|
|
47
47
|
root.style.setProperty(`--color-accent-${shade}`, rgb);
|
|
48
48
|
});
|
|
49
|
+
|
|
50
|
+
// Header gradient variables (rgb triplets)
|
|
51
|
+
if (config.headerGradient) {
|
|
52
|
+
const [fr, fg, fb] = hexToRgb(config.headerGradient.from);
|
|
53
|
+
const [vr, vg, vb] = hexToRgb(config.headerGradient.via);
|
|
54
|
+
const [tr, tg, tb] = hexToRgb(config.headerGradient.to);
|
|
55
|
+
root.style.setProperty(`--header-from`, `${fr} ${fg} ${fb}`);
|
|
56
|
+
root.style.setProperty(`--header-via`, `${vr} ${vg} ${vb}`);
|
|
57
|
+
root.style.setProperty(`--header-to`, `${tr} ${tg} ${tb}`);
|
|
58
|
+
} else {
|
|
59
|
+
// Fallback to theme shades matching existing design
|
|
60
|
+
root.style.setProperty(`--header-from`, primaryShades[700]);
|
|
61
|
+
root.style.setProperty(`--header-via`, primaryShades[600]);
|
|
62
|
+
root.style.setProperty(`--header-to`, secondaryShades[600]);
|
|
63
|
+
}
|
|
49
64
|
}, [config.colors]);
|
|
50
65
|
|
|
51
66
|
return (
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
|
4
|
+
import { WishlistApi } from '@/lib/Apis/apis/wishlist-api';
|
|
5
|
+
import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
|
|
6
|
+
import { useAuth } from './AuthProvider';
|
|
7
|
+
import { toast } from 'sonner';
|
|
8
|
+
import { useMemo } from 'react';
|
|
9
|
+
import { Wishlist } from '@/lib/Apis';
|
|
10
|
+
import { Product } from '@/lib/Apis/models';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
interface WishlistContextType extends Wishlist {
|
|
14
|
+
addToWishlist: (product: Product) => Promise<void>;
|
|
15
|
+
removeFromWishlist: (productId: string) => Promise<void>;
|
|
16
|
+
isInWishlist: (productId: string) => boolean;
|
|
17
|
+
getWishlistCount: () => number;
|
|
18
|
+
refreshWishlist: () => Promise<void>;
|
|
19
|
+
clearWishlist: () => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const WishlistContext = createContext<WishlistContextType | undefined>(undefined);
|
|
23
|
+
|
|
24
|
+
export function WishlistProvider({ children }: { children: ReactNode }) {
|
|
25
|
+
const [state, setState] = useState<Wishlist>({
|
|
26
|
+
_id: '',
|
|
27
|
+
createdAt: new Date(),
|
|
28
|
+
updatedAt: new Date(),
|
|
29
|
+
userId: '',
|
|
30
|
+
products: [],
|
|
31
|
+
} as unknown as Wishlist);
|
|
32
|
+
|
|
33
|
+
const { isAuthenticated } = useAuth() || {};
|
|
34
|
+
const wishlistApi = useMemo(() => new WishlistApi(AXIOS_CONFIG), []);
|
|
35
|
+
|
|
36
|
+
const fetchWishlist = useCallback(async () => {
|
|
37
|
+
if (!isAuthenticated) {
|
|
38
|
+
setState(prev => ({
|
|
39
|
+
...prev,
|
|
40
|
+
isLoading: false,
|
|
41
|
+
products: [],
|
|
42
|
+
}));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const response = await wishlistApi.getWishlist();
|
|
50
|
+
const responseData = response?.data;
|
|
51
|
+
|
|
52
|
+
setState({
|
|
53
|
+
_id: responseData._id,
|
|
54
|
+
createdAt: responseData.createdAt,
|
|
55
|
+
updatedAt: responseData.updatedAt,
|
|
56
|
+
userId: responseData.userId,
|
|
57
|
+
products: responseData.products,
|
|
58
|
+
} as unknown as Wishlist);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Error fetching wishlist:', error);
|
|
61
|
+
setState(prev => ({
|
|
62
|
+
...prev,
|
|
63
|
+
isLoading: false,
|
|
64
|
+
error: error instanceof Error ? error.message : 'Failed to load wishlist',
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
68
|
+
}, [isAuthenticated]);
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
fetchWishlist();
|
|
73
|
+
}, [fetchWishlist]);
|
|
74
|
+
|
|
75
|
+
const addToWishlist = async (product: Product) => {
|
|
76
|
+
if (!isAuthenticated) {
|
|
77
|
+
toast.error('Please sign in to add items to your wishlist');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// First check if the item is already in the wishlist
|
|
83
|
+
if (isInWishlist(product?._id || '')) {
|
|
84
|
+
toast.info('This item is already in your wishlist');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Make the API call
|
|
89
|
+
await wishlistApi.addToWishlist(product?._id || '');
|
|
90
|
+
|
|
91
|
+
// Instead of updating state directly, refresh the entire wishlist from the server
|
|
92
|
+
// This ensures we're in sync with the server and prevents duplicates
|
|
93
|
+
await fetchWishlist();
|
|
94
|
+
|
|
95
|
+
toast.success('Added to wishlist');
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('Error adding to wishlist:', error);
|
|
98
|
+
toast.error('Failed to add to wishlist');
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const removeFromWishlist = async (productId: string) => {
|
|
104
|
+
try {
|
|
105
|
+
await wishlistApi.removeFromWishlist(productId);
|
|
106
|
+
|
|
107
|
+
setState(prev => {
|
|
108
|
+
const newProducts = prev.products.filter((product: any) => product?._id !== productId);
|
|
109
|
+
return {
|
|
110
|
+
...prev,
|
|
111
|
+
products: newProducts,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
toast.success('Removed from wishlist');
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error('Error removing from wishlist:', error);
|
|
119
|
+
toast.error('Failed to remove from wishlist');
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const clearWishlist = async () => {
|
|
125
|
+
try {
|
|
126
|
+
await wishlistApi.clearWishlist();
|
|
127
|
+
setState(prev => ({
|
|
128
|
+
...prev,
|
|
129
|
+
products: [],
|
|
130
|
+
}));
|
|
131
|
+
toast.success('Wishlist cleared');
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error('Error clearing wishlist:', error);
|
|
134
|
+
toast.error('Failed to clear wishlist');
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const isInWishlist = (productId: string): boolean => {
|
|
140
|
+
return state.products.some((product: any) => product?._id === productId);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const getWishlistCount = (): number => {
|
|
144
|
+
return state.products.length;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const refreshWishlist = async () => {
|
|
148
|
+
await fetchWishlist();
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<WishlistContext.Provider
|
|
153
|
+
value={{
|
|
154
|
+
...state,
|
|
155
|
+
addToWishlist,
|
|
156
|
+
removeFromWishlist,
|
|
157
|
+
isInWishlist,
|
|
158
|
+
getWishlistCount,
|
|
159
|
+
refreshWishlist,
|
|
160
|
+
clearWishlist,
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
{children}
|
|
164
|
+
</WishlistContext.Provider>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const useWishlist = (): WishlistContextType => {
|
|
169
|
+
const context = useContext(WishlistContext);
|
|
170
|
+
if (context === undefined) {
|
|
171
|
+
throw new Error('useWishlist must be used within a WishlistProvider');
|
|
172
|
+
}
|
|
173
|
+
return context;
|
|
174
|
+
};
|