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.
Files changed (74) hide show
  1. package/README.md +107 -1
  2. package/dist/index.d.mts +3636 -316
  3. package/dist/index.d.ts +3636 -316
  4. package/dist/index.js +6802 -3866
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +6756 -3818
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +17 -14
  9. package/src/components/AddressFormModal.tsx +171 -0
  10. package/src/components/CartItem.tsx +17 -12
  11. package/src/components/FilterChips.tsx +195 -0
  12. package/src/components/Header.tsx +121 -71
  13. package/src/components/OrderCard.tsx +18 -25
  14. package/src/components/ProductCard.tsx +209 -72
  15. package/src/components/ui/Button.tsx +13 -5
  16. package/src/components/ui/Card.tsx +46 -0
  17. package/src/hooks/useAddresses.ts +83 -0
  18. package/src/hooks/useOrders.ts +37 -19
  19. package/src/hooks/useProducts.ts +55 -63
  20. package/src/hooks/useWishlistProducts.ts +75 -0
  21. package/src/index.ts +3 -19
  22. package/src/lib/Apis/api.ts +1 -0
  23. package/src/lib/Apis/apis/cart-api.ts +3 -3
  24. package/src/lib/Apis/apis/inventory-api.ts +0 -108
  25. package/src/lib/Apis/apis/stores-api.ts +70 -0
  26. package/src/lib/Apis/apis/wishlist-api.ts +447 -0
  27. package/src/lib/Apis/models/cart-item-populated.ts +0 -1
  28. package/src/lib/Apis/models/create-single-variant-product-dto.ts +3 -10
  29. package/src/lib/Apis/models/create-variant-dto.ts +26 -33
  30. package/src/lib/Apis/models/extended-product-dto.ts +20 -24
  31. package/src/lib/Apis/models/index.ts +2 -1
  32. package/src/lib/Apis/models/order-time-line-dto.ts +49 -0
  33. package/src/lib/Apis/models/order.ts +3 -8
  34. package/src/lib/Apis/models/populated-order.ts +3 -8
  35. package/src/lib/Apis/models/product-variant.ts +29 -0
  36. package/src/lib/Apis/models/update-product-variant-dto.ts +16 -23
  37. package/src/lib/Apis/models/wishlist.ts +51 -0
  38. package/src/lib/Apis/wrapper.ts +18 -7
  39. package/src/lib/api-adapter/index.ts +0 -12
  40. package/src/lib/types/index.ts +16 -61
  41. package/src/lib/utils/colors.ts +7 -4
  42. package/src/lib/utils/format.ts +1 -1
  43. package/src/lib/validations/address.ts +14 -0
  44. package/src/providers/AuthProvider.tsx +61 -31
  45. package/src/providers/CartProvider.tsx +18 -28
  46. package/src/providers/EcommerceProvider.tsx +7 -0
  47. package/src/providers/FavoritesProvider.tsx +86 -0
  48. package/src/providers/ThemeProvider.tsx +16 -1
  49. package/src/providers/WishlistProvider.tsx +174 -0
  50. package/src/screens/AddressesScreen.tsx +484 -0
  51. package/src/screens/CartScreen.tsx +120 -84
  52. package/src/screens/CategoriesScreen.tsx +120 -0
  53. package/src/screens/CheckoutScreen.tsx +919 -241
  54. package/src/screens/CurrentOrdersScreen.tsx +125 -61
  55. package/src/screens/HomeScreen.tsx +209 -0
  56. package/src/screens/LoginScreen.tsx +133 -88
  57. package/src/screens/NewAddressScreen.tsx +187 -0
  58. package/src/screens/OrdersScreen.tsx +162 -50
  59. package/src/screens/ProductDetailScreen.tsx +641 -190
  60. package/src/screens/ProfileScreen.tsx +192 -116
  61. package/src/screens/RegisterScreen.tsx +193 -144
  62. package/src/screens/SearchResultsScreen.tsx +165 -0
  63. package/src/screens/ShopScreen.tsx +1110 -146
  64. package/src/screens/WishlistScreen.tsx +428 -0
  65. package/src/lib/Apis/models/inventory-paginated-response.ts +0 -75
  66. package/src/lib/api/auth.ts +0 -81
  67. package/src/lib/api/cart.ts +0 -42
  68. package/src/lib/api/orders.ts +0 -53
  69. package/src/lib/api/products.ts +0 -51
  70. package/src/lib/api-adapter/auth-adapter.ts +0 -196
  71. package/src/lib/api-adapter/cart-adapter.ts +0 -193
  72. package/src/lib/api-adapter/mappers.ts +0 -152
  73. package/src/lib/api-adapter/orders-adapter.ts +0 -195
  74. 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: Cart | null;
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<Cart | null>(null);
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 cartApi.getCart();
46
- if (response.success) {
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 cartApi.addToCart(productId, quantity);
62
- if (response.success) {
63
- setCart(response.data);
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 cartApi.updateCartItem(productId, quantity);
78
- if (response.success) {
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 cartApi.removeFromCart(productId);
93
- if (response.success) {
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 cartApi.clearCart();
109
- setCart({ items: [], total: 0, itemCount: 0 });
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
+ };