hey-pharmacist-ecommerce 1.0.4 → 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 -3865
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6756 -3817
- 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 -147
- package/src/lib/api-adapter/orders-adapter.ts +0 -195
- package/src/lib/api-adapter/products-adapter.ts +0 -194
package/src/lib/api/orders.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { ordersAdapter, CreateOrderData as AdapterCreateOrderData } from '@/lib/api-adapter';
|
|
2
|
-
import { Order, ApiResponse, PaginatedResponse, Address } from '@/lib/types';
|
|
3
|
-
|
|
4
|
-
export interface CreateOrderData {
|
|
5
|
-
shippingAddress: Address;
|
|
6
|
-
billingAddress: Address;
|
|
7
|
-
sameAsShipping?: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const ordersApi = {
|
|
11
|
-
/**
|
|
12
|
-
* Get user's orders with pagination
|
|
13
|
-
*/
|
|
14
|
-
async getOrders(page: number = 1, limit: number = 10): Promise<PaginatedResponse<Order>> {
|
|
15
|
-
return await ordersAdapter.getOrders(page, limit);
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Get current/active orders
|
|
20
|
-
*/
|
|
21
|
-
async getCurrentOrders(): Promise<ApiResponse<Order[]>> {
|
|
22
|
-
return await ordersAdapter.getCurrentOrders();
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Get a single order by ID
|
|
27
|
-
*/
|
|
28
|
-
async getOrder(id: string): Promise<ApiResponse<Order>> {
|
|
29
|
-
return await ordersAdapter.getOrder(id);
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Create a new order
|
|
34
|
-
*/
|
|
35
|
-
async createOrder(data: CreateOrderData): Promise<ApiResponse<Order>> {
|
|
36
|
-
return await ordersAdapter.createOrder(data);
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Cancel an order
|
|
41
|
-
*/
|
|
42
|
-
async cancelOrder(id: string): Promise<ApiResponse<Order>> {
|
|
43
|
-
return await ordersAdapter.cancelOrder(id);
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Get order tracking information
|
|
48
|
-
*/
|
|
49
|
-
async getOrderTracking(id: string): Promise<ApiResponse<any>> {
|
|
50
|
-
return await ordersAdapter.getOrderTracking(id);
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
|
package/src/lib/api/products.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { productsAdapter } from '@/lib/api-adapter';
|
|
2
|
-
import { Product, ApiResponse, PaginatedResponse, ProductFilters, Category } from '@/lib/types';
|
|
3
|
-
|
|
4
|
-
export const productsApi = {
|
|
5
|
-
/**
|
|
6
|
-
* Get all products with optional filters and pagination
|
|
7
|
-
*/
|
|
8
|
-
async getProducts(
|
|
9
|
-
filters?: ProductFilters,
|
|
10
|
-
page: number = 1,
|
|
11
|
-
limit: number = 20
|
|
12
|
-
): Promise<PaginatedResponse<Product>> {
|
|
13
|
-
return await productsAdapter.getProducts(filters, page, limit);
|
|
14
|
-
},
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Get a single product by ID
|
|
18
|
-
*/
|
|
19
|
-
async getProduct(id: string): Promise<ApiResponse<Product>> {
|
|
20
|
-
return await productsAdapter.getProduct(id);
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Get featured products
|
|
25
|
-
*/
|
|
26
|
-
async getFeaturedProducts(limit: number = 8): Promise<ApiResponse<Product[]>> {
|
|
27
|
-
return await productsAdapter.getFeaturedProducts(limit);
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Get related products
|
|
32
|
-
*/
|
|
33
|
-
async getRelatedProducts(productId: string, limit: number = 4): Promise<ApiResponse<Product[]>> {
|
|
34
|
-
return await productsAdapter.getRelatedProducts(productId, limit);
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Get all categories
|
|
39
|
-
*/
|
|
40
|
-
async getCategories(): Promise<ApiResponse<Category[]>> {
|
|
41
|
-
return await productsAdapter.getCategories();
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Search products
|
|
46
|
-
*/
|
|
47
|
-
async searchProducts(query: string, limit: number = 10): Promise<ApiResponse<Product[]>> {
|
|
48
|
-
return await productsAdapter.searchProducts(query, limit);
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth API Adapter
|
|
3
|
-
* Connects frontend auth to real backend auth APIs
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { AuthApi } from '../Apis/apis/auth-api';
|
|
7
|
-
import { UsersApi } from '../Apis/apis/users-api';
|
|
8
|
-
import { LoginDto, CreateUserDto } from '../Apis/models';
|
|
9
|
-
import { getApiConfiguration, setAuthToken, clearAuthToken } from './config';
|
|
10
|
-
import { mapSessionData, mapUser } from './mappers';
|
|
11
|
-
import { User, LoginCredentials, RegisterData, ApiResponse } from '../types';
|
|
12
|
-
|
|
13
|
-
export const authAdapter = {
|
|
14
|
-
/**
|
|
15
|
-
* Register a new user
|
|
16
|
-
*/
|
|
17
|
-
async register(data: RegisterData): Promise<ApiResponse<{ user: User; token: string }>> {
|
|
18
|
-
try {
|
|
19
|
-
const authApi = new AuthApi(getApiConfiguration());
|
|
20
|
-
|
|
21
|
-
// Note: CreateUserDto doesn't include password
|
|
22
|
-
// Password is sent via LoginDto in signup endpoint
|
|
23
|
-
const loginDto: LoginDto = {
|
|
24
|
-
email: data.email,
|
|
25
|
-
password: data.password,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// Try signing up - backend signup uses email/password in LoginDto format
|
|
29
|
-
const response = await authApi.signup(loginDto as any);
|
|
30
|
-
const { user, token } = mapSessionData(response.data);
|
|
31
|
-
|
|
32
|
-
setAuthToken(token);
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
success: true,
|
|
36
|
-
data: { user, token },
|
|
37
|
-
};
|
|
38
|
-
} catch (error: any) {
|
|
39
|
-
return {
|
|
40
|
-
success: false,
|
|
41
|
-
message: error.response?.data?.message || 'Registration failed',
|
|
42
|
-
data: { user: {} as User, token: '' },
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Login user
|
|
49
|
-
*/
|
|
50
|
-
async login(credentials: LoginCredentials): Promise<ApiResponse<{ user: User; token: string }>> {
|
|
51
|
-
try {
|
|
52
|
-
const authApi = new AuthApi(getApiConfiguration());
|
|
53
|
-
|
|
54
|
-
const loginDto: LoginDto = {
|
|
55
|
-
email: credentials.email,
|
|
56
|
-
password: credentials.password,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const response = await authApi.signin(loginDto);
|
|
60
|
-
const { user, token } = mapSessionData(response.data);
|
|
61
|
-
|
|
62
|
-
setAuthToken(token);
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
success: true,
|
|
66
|
-
data: { user, token },
|
|
67
|
-
};
|
|
68
|
-
} catch (error: any) {
|
|
69
|
-
return {
|
|
70
|
-
success: false,
|
|
71
|
-
message: error.response?.data?.message || 'Login failed',
|
|
72
|
-
data: { user: {} as User, token: '' },
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Logout user
|
|
79
|
-
*/
|
|
80
|
-
async logout(): Promise<void> {
|
|
81
|
-
clearAuthToken();
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Get current user
|
|
86
|
-
*/
|
|
87
|
-
async getCurrentUser(): Promise<ApiResponse<User>> {
|
|
88
|
-
try {
|
|
89
|
-
const usersApi = new UsersApi(getApiConfiguration());
|
|
90
|
-
const response = await usersApi.getMyProfile();
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
success: true,
|
|
94
|
-
data: mapUser(response.data),
|
|
95
|
-
};
|
|
96
|
-
} catch (error: any) {
|
|
97
|
-
return {
|
|
98
|
-
success: false,
|
|
99
|
-
message: error.response?.data?.message || 'Failed to get user',
|
|
100
|
-
data: {} as User,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Update user profile
|
|
107
|
-
*/
|
|
108
|
-
async updateProfile(data: Partial<User>): Promise<ApiResponse<User>> {
|
|
109
|
-
try {
|
|
110
|
-
const usersApi = new UsersApi(getApiConfiguration());
|
|
111
|
-
|
|
112
|
-
const updateData: any = {};
|
|
113
|
-
if (data.firstName) updateData.firstname = data.firstName;
|
|
114
|
-
if (data.lastName) updateData.lastname = data.lastName;
|
|
115
|
-
if (data.phone) updateData.phoneNumber = data.phone;
|
|
116
|
-
if (data.email) updateData.email = data.email;
|
|
117
|
-
|
|
118
|
-
const response = await usersApi.updateMyProfile(updateData);
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
success: true,
|
|
122
|
-
data: mapUser(response.data),
|
|
123
|
-
};
|
|
124
|
-
} catch (error: any) {
|
|
125
|
-
return {
|
|
126
|
-
success: false,
|
|
127
|
-
message: error.response?.data?.message || 'Failed to update profile',
|
|
128
|
-
data: {} as User,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Change password
|
|
135
|
-
*/
|
|
136
|
-
async changePassword(currentPassword: string, newPassword: string): Promise<ApiResponse<void>> {
|
|
137
|
-
try {
|
|
138
|
-
const authApi = new AuthApi(getApiConfiguration());
|
|
139
|
-
await authApi.changePassword(currentPassword, newPassword);
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
success: true,
|
|
143
|
-
data: undefined,
|
|
144
|
-
};
|
|
145
|
-
} catch (error: any) {
|
|
146
|
-
return {
|
|
147
|
-
success: false,
|
|
148
|
-
message: error.response?.data?.message || 'Failed to change password',
|
|
149
|
-
data: undefined,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Request password reset
|
|
156
|
-
*/
|
|
157
|
-
async forgotPassword(email: string): Promise<ApiResponse<void>> {
|
|
158
|
-
try {
|
|
159
|
-
const authApi = new AuthApi(getApiConfiguration());
|
|
160
|
-
await authApi.sendForgetPasswordEmail({ email });
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
success: true,
|
|
164
|
-
data: undefined,
|
|
165
|
-
};
|
|
166
|
-
} catch (error: any) {
|
|
167
|
-
return {
|
|
168
|
-
success: false,
|
|
169
|
-
message: error.response?.data?.message || 'Failed to send reset email',
|
|
170
|
-
data: undefined,
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Reset password with token
|
|
177
|
-
*/
|
|
178
|
-
async resetPassword(token: string, newPassword: string): Promise<ApiResponse<void>> {
|
|
179
|
-
try {
|
|
180
|
-
const authApi = new AuthApi(getApiConfiguration());
|
|
181
|
-
await authApi.resetPassword(newPassword, token);
|
|
182
|
-
|
|
183
|
-
return {
|
|
184
|
-
success: true,
|
|
185
|
-
data: undefined,
|
|
186
|
-
};
|
|
187
|
-
} catch (error: any) {
|
|
188
|
-
return {
|
|
189
|
-
success: false,
|
|
190
|
-
message: error.response?.data?.message || 'Failed to reset password',
|
|
191
|
-
data: undefined,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cart API Adapter
|
|
3
|
-
* Connects frontend cart to real backend cart APIs
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { CartApi } from '../Apis/apis/cart-api';
|
|
7
|
-
import { CartBodyDTO } from '../Apis/models';
|
|
8
|
-
import { getApiConfiguration } from './config';
|
|
9
|
-
import { mapCart } from './mappers';
|
|
10
|
-
import { Cart, ApiResponse } from '../types';
|
|
11
|
-
|
|
12
|
-
export const cartAdapter = {
|
|
13
|
-
/**
|
|
14
|
-
* Get user's cart
|
|
15
|
-
*/
|
|
16
|
-
async getCart(): Promise<ApiResponse<Cart>> {
|
|
17
|
-
try {
|
|
18
|
-
const cartApi = new CartApi(getApiConfiguration());
|
|
19
|
-
const response = await cartApi.getUserCart();
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
success: true,
|
|
23
|
-
data: mapCart(response.data),
|
|
24
|
-
};
|
|
25
|
-
} catch (error: any) {
|
|
26
|
-
return {
|
|
27
|
-
success: false,
|
|
28
|
-
message: error.response?.data?.message || 'Failed to get cart',
|
|
29
|
-
data: { items: [], total: 0, itemCount: 0 },
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Add item to cart
|
|
36
|
-
* In your backend, cart is updated as a whole, not individual items
|
|
37
|
-
*/
|
|
38
|
-
async addToCart(productId: string, variantId: string, quantity: number = 1): Promise<ApiResponse<Cart>> {
|
|
39
|
-
try {
|
|
40
|
-
const cartApi = new CartApi(getApiConfiguration());
|
|
41
|
-
|
|
42
|
-
// First get current cart
|
|
43
|
-
const currentCartResponse = await cartApi.getUserCart();
|
|
44
|
-
const currentCart = currentCartResponse.data;
|
|
45
|
-
|
|
46
|
-
// Build updated cart body
|
|
47
|
-
const existingItems = currentCart.cartBody?.items || [];
|
|
48
|
-
const existingIndex = existingItems.findIndex(
|
|
49
|
-
(p: any) => p.productVariantId === variantId
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
let updatedItems;
|
|
53
|
-
if (existingIndex >= 0) {
|
|
54
|
-
// Update existing item
|
|
55
|
-
updatedItems = [...existingItems];
|
|
56
|
-
updatedItems[existingIndex] = {
|
|
57
|
-
...updatedItems[existingIndex],
|
|
58
|
-
quantity: updatedItems[existingIndex].quantity + quantity,
|
|
59
|
-
};
|
|
60
|
-
} else {
|
|
61
|
-
// Add new item
|
|
62
|
-
updatedItems = [
|
|
63
|
-
...existingItems,
|
|
64
|
-
{
|
|
65
|
-
productVariantId: variantId,
|
|
66
|
-
quantity,
|
|
67
|
-
},
|
|
68
|
-
];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const cartBodyDTO: CartBodyDTO = {
|
|
72
|
-
items: updatedItems,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
await cartApi.handleUserCart(cartBodyDTO);
|
|
76
|
-
|
|
77
|
-
// Get updated cart
|
|
78
|
-
const updatedCartResponse = await cartApi.getUserCart();
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
success: true,
|
|
82
|
-
data: mapCart(updatedCartResponse.data),
|
|
83
|
-
};
|
|
84
|
-
} catch (error: any) {
|
|
85
|
-
return {
|
|
86
|
-
success: false,
|
|
87
|
-
message: error.response?.data?.message || 'Failed to add to cart',
|
|
88
|
-
data: { items: [], total: 0, itemCount: 0 },
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Update cart item quantity
|
|
95
|
-
*/
|
|
96
|
-
async updateCartItem(productId: string, variantId: string, quantity: number): Promise<ApiResponse<Cart>> {
|
|
97
|
-
try {
|
|
98
|
-
const cartApi = new CartApi(getApiConfiguration());
|
|
99
|
-
|
|
100
|
-
// Get current cart
|
|
101
|
-
const currentCartResponse = await cartApi.getUserCart();
|
|
102
|
-
const currentCart = currentCartResponse.data;
|
|
103
|
-
|
|
104
|
-
// Update the specific item
|
|
105
|
-
const updatedItems = (currentCart.cartBody?.items || []).map((p: any) => {
|
|
106
|
-
if (p.productVariantId === variantId) {
|
|
107
|
-
return { ...p, quantity };
|
|
108
|
-
}
|
|
109
|
-
return p;
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const cartBodyDTO: CartBodyDTO = {
|
|
113
|
-
items: updatedItems,
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
await cartApi.handleUserCart(cartBodyDTO);
|
|
117
|
-
|
|
118
|
-
// Get updated cart
|
|
119
|
-
const updatedCartResponse = await cartApi.getUserCart();
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
success: true,
|
|
123
|
-
data: mapCart(updatedCartResponse.data),
|
|
124
|
-
};
|
|
125
|
-
} catch (error: any) {
|
|
126
|
-
return {
|
|
127
|
-
success: false,
|
|
128
|
-
message: error.response?.data?.message || 'Failed to update cart',
|
|
129
|
-
data: { items: [], total: 0, itemCount: 0 },
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Remove item from cart
|
|
136
|
-
*/
|
|
137
|
-
async removeFromCart(productId: string, variantId: string): Promise<ApiResponse<Cart>> {
|
|
138
|
-
try {
|
|
139
|
-
const cartApi = new CartApi(getApiConfiguration());
|
|
140
|
-
|
|
141
|
-
// Get current cart
|
|
142
|
-
const currentCartResponse = await cartApi.getUserCart();
|
|
143
|
-
const currentCart = currentCartResponse.data;
|
|
144
|
-
|
|
145
|
-
// Filter out the item
|
|
146
|
-
const updatedItems = (currentCart.cartBody?.items || []).filter(
|
|
147
|
-
(p: any) => p.productVariantId !== variantId
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const cartBodyDTO: CartBodyDTO = {
|
|
151
|
-
items: updatedItems,
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
await cartApi.handleUserCart(cartBodyDTO);
|
|
155
|
-
|
|
156
|
-
// Get updated cart
|
|
157
|
-
const updatedCartResponse = await cartApi.getUserCart();
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
success: true,
|
|
161
|
-
data: mapCart(updatedCartResponse.data),
|
|
162
|
-
};
|
|
163
|
-
} catch (error: any) {
|
|
164
|
-
return {
|
|
165
|
-
success: false,
|
|
166
|
-
message: error.response?.data?.message || 'Failed to remove from cart',
|
|
167
|
-
data: { items: [], total: 0, itemCount: 0 },
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Clear cart
|
|
174
|
-
*/
|
|
175
|
-
async clearCart(): Promise<ApiResponse<void>> {
|
|
176
|
-
try {
|
|
177
|
-
const cartApi = new CartApi(getApiConfiguration());
|
|
178
|
-
await cartApi.clearCart();
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
success: true,
|
|
182
|
-
data: undefined,
|
|
183
|
-
};
|
|
184
|
-
} catch (error: any) {
|
|
185
|
-
return {
|
|
186
|
-
success: false,
|
|
187
|
-
message: error.response?.data?.message || 'Failed to clear cart',
|
|
188
|
-
data: undefined,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
},
|
|
192
|
-
};
|
|
193
|
-
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Data Mappers
|
|
3
|
-
* Convert backend models to frontend types
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { UserEntity, TUserSessionData } from '../Apis/models';
|
|
7
|
-
import { ExtendedProductDTO } from '../Apis/models';
|
|
8
|
-
import { CartResponseDto } from '../Apis/models';
|
|
9
|
-
import { PopulatedOrder } from '../Apis/models';
|
|
10
|
-
import { User, Product, Cart, Order, OrderStatus, CartItem } from '../types';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Map backend UserEntity to frontend User
|
|
14
|
-
*/
|
|
15
|
-
export function mapUser(backendUser: UserEntity): User {
|
|
16
|
-
return {
|
|
17
|
-
id: backendUser.id || backendUser._id || '',
|
|
18
|
-
email: backendUser.email || '',
|
|
19
|
-
firstName: backendUser.firstname || '',
|
|
20
|
-
lastName: backendUser.lastname || '',
|
|
21
|
-
phone: backendUser.phoneNumber,
|
|
22
|
-
avatar: backendUser.avatar,
|
|
23
|
-
createdAt: backendUser.createdAt ? backendUser.createdAt.toString() : new Date().toISOString(),
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Map backend session data to user and token
|
|
29
|
-
*/
|
|
30
|
-
export function mapSessionData(sessionData: TUserSessionData): { user: User; token: string } {
|
|
31
|
-
return {
|
|
32
|
-
user: mapUser(sessionData.userData),
|
|
33
|
-
token: sessionData.authToken,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Map backend product to frontend Product
|
|
39
|
-
*/
|
|
40
|
-
export function mapProduct(backendProduct: ExtendedProductDTO): Product {
|
|
41
|
-
const images = backendProduct.productMedia?.map((media) => media.file) || [];
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
id: backendProduct.id || backendProduct._id || '',
|
|
45
|
-
name: backendProduct.name || '',
|
|
46
|
-
description: backendProduct.description || '',
|
|
47
|
-
price: backendProduct.finalPrice || 0,
|
|
48
|
-
compareAtPrice: backendProduct.isDiscounted ? backendProduct.priceBeforeDiscount : undefined,
|
|
49
|
-
images: images.filter(Boolean),
|
|
50
|
-
category: backendProduct.parentCategories?.[0] || '',
|
|
51
|
-
inStock: (backendProduct.inventoryCount || 0) > 0,
|
|
52
|
-
stock: backendProduct.inventoryCount,
|
|
53
|
-
sku: backendProduct.sku,
|
|
54
|
-
tags: backendProduct.tags || [],
|
|
55
|
-
createdAt: backendProduct.createdAt ? backendProduct.createdAt.toString() : new Date().toISOString(),
|
|
56
|
-
updatedAt: backendProduct.updatedAt ? backendProduct.updatedAt.toString() : new Date().toISOString(),
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Map backend cart to frontend Cart
|
|
62
|
-
*/
|
|
63
|
-
export function mapCart(backendCart: CartResponseDto): Cart {
|
|
64
|
-
const items: CartItem[] = [];
|
|
65
|
-
|
|
66
|
-
// Map cart body items
|
|
67
|
-
if (backendCart.cartBody?.items) {
|
|
68
|
-
backendCart.cartBody.items.forEach((item: any) => {
|
|
69
|
-
if (item.productVariantData) {
|
|
70
|
-
// Extract product info from variant data
|
|
71
|
-
const productData = item.productVariantData.product || item.productVariantData;
|
|
72
|
-
items.push({
|
|
73
|
-
productId: productData.id || productData._id || '',
|
|
74
|
-
quantity: item.quantity || 0,
|
|
75
|
-
product: mapProduct(productData),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
items,
|
|
83
|
-
total: backendCart.total || 0,
|
|
84
|
-
itemCount: items.reduce((sum, item) => sum + item.quantity, 0),
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Map backend order status to frontend OrderStatus
|
|
90
|
-
*/
|
|
91
|
-
export function mapOrderStatus(backendStatus: string): OrderStatus {
|
|
92
|
-
const statusMap: Record<string, OrderStatus> = {
|
|
93
|
-
'pending': OrderStatus.PENDING,
|
|
94
|
-
'processing': OrderStatus.PROCESSING,
|
|
95
|
-
'shipped': OrderStatus.SHIPPED,
|
|
96
|
-
'delivered': OrderStatus.DELIVERED,
|
|
97
|
-
'cancelled': OrderStatus.CANCELLED,
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
return statusMap[backendStatus.toLowerCase()] || OrderStatus.PENDING;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Map backend order to frontend Order
|
|
105
|
-
*/
|
|
106
|
-
export function mapOrder(backendOrder: PopulatedOrder): Order {
|
|
107
|
-
return {
|
|
108
|
-
id: backendOrder.id || backendOrder._id || '',
|
|
109
|
-
orderNumber: backendOrder.stringId || backendOrder.id || backendOrder._id || '',
|
|
110
|
-
items: backendOrder.items?.map((item: any) => ({
|
|
111
|
-
id: item.id || item._id || '',
|
|
112
|
-
productId: item.productVariantData?.product?.id || item.productVariantData?.id || '',
|
|
113
|
-
productName: item.productVariantData?.product?.name || item.productVariantData?.name || '',
|
|
114
|
-
productImage: item.productVariantData?.product?.productMedia?.[0]?.url ||
|
|
115
|
-
item.productVariantData?.productMedia?.[0]?.url || '',
|
|
116
|
-
quantity: item.quantity || 0,
|
|
117
|
-
price: item.productVariantData?.finalPrice || 0,
|
|
118
|
-
})) || [],
|
|
119
|
-
total: backendOrder.grandTotal || 0,
|
|
120
|
-
status: mapOrderStatus(backendOrder.orderStatus || 'pending'),
|
|
121
|
-
shippingAddress: backendOrder.shippingInfo?.addressTo ? {
|
|
122
|
-
fullName: backendOrder.shippingInfo.addressTo.name || '',
|
|
123
|
-
addressLine1: backendOrder.shippingInfo.addressTo.street1 || '',
|
|
124
|
-
addressLine2: backendOrder.shippingInfo.addressTo.street2 || '',
|
|
125
|
-
city: backendOrder.shippingInfo.addressTo.city || '',
|
|
126
|
-
state: backendOrder.shippingInfo.addressTo.state || '',
|
|
127
|
-
zipCode: backendOrder.shippingInfo.addressTo.zip || '',
|
|
128
|
-
country: backendOrder.shippingInfo.addressTo.country || '',
|
|
129
|
-
phone: backendOrder.shippingInfo.addressTo.phone || '',
|
|
130
|
-
} : {} as any,
|
|
131
|
-
billingAddress: backendOrder.shippingInfo?.addressTo ? {
|
|
132
|
-
fullName: backendOrder.shippingInfo.addressTo.name || '',
|
|
133
|
-
addressLine1: backendOrder.shippingInfo.addressTo.street1 || '',
|
|
134
|
-
addressLine2: backendOrder.shippingInfo.addressTo.street2 || '',
|
|
135
|
-
city: backendOrder.shippingInfo.addressTo.city || '',
|
|
136
|
-
state: backendOrder.shippingInfo.addressTo.state || '',
|
|
137
|
-
zipCode: backendOrder.shippingInfo.addressTo.zip || '',
|
|
138
|
-
country: backendOrder.shippingInfo.addressTo.country || '',
|
|
139
|
-
phone: backendOrder.shippingInfo.addressTo.phone || '',
|
|
140
|
-
} : {} as any,
|
|
141
|
-
paymentStatus: (backendOrder.payment?.paymentStatus?.toLowerCase() || 'unpaid') as any,
|
|
142
|
-
stripeCheckoutUrl: backendOrder.payment?.hostedInvoiceUrl,
|
|
143
|
-
createdAt: backendOrder.createdAt ? backendOrder.createdAt.toString() : new Date().toISOString(),
|
|
144
|
-
updatedAt: backendOrder.updatedAt ? backendOrder.updatedAt.toString() : new Date().toISOString(),
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|