@weareconceptstudio/cart 0.0.1
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 +350 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useCart.d.ts +131 -0
- package/dist/hooks/useCart.d.ts.map +1 -0
- package/dist/hooks/useCart.js +291 -0
- package/dist/hooks/useCart.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/store/cartStore.d.ts +69 -0
- package/dist/store/cartStore.d.ts.map +1 -0
- package/dist/store/cartStore.js +663 -0
- package/dist/store/cartStore.js.map +1 -0
- package/dist/store/index.d.ts +5 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +5 -0
- package/dist/store/index.js.map +1 -0
- package/dist/types/index.d.ts +151 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/helpers.d.ts +48 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +103 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cart Store - Zustand Implementation
|
|
3
|
+
* Enhanced with all features from old Context implementation
|
|
4
|
+
*/
|
|
5
|
+
import { create } from 'zustand';
|
|
6
|
+
import { devtools } from 'zustand/middleware';
|
|
7
|
+
import { api, getCookie, setCookie, removeCookie, isNumeric } from '@weareconceptstudio/core';
|
|
8
|
+
import { addOrUpdateItem, removeItem, cleanObject, parseCookieCart, serializeCartToCookie, } from '../utils/helpers';
|
|
9
|
+
// Initial state
|
|
10
|
+
const initialCartState = {
|
|
11
|
+
items: [],
|
|
12
|
+
itemsCount: 0,
|
|
13
|
+
subtotal: 0,
|
|
14
|
+
formatted_subtotal: '0',
|
|
15
|
+
total: 0,
|
|
16
|
+
formatted_total: '0',
|
|
17
|
+
currency: 'AMD',
|
|
18
|
+
shippingCost: '-',
|
|
19
|
+
formatted_shippingCost: '-',
|
|
20
|
+
shippingCostValue: null,
|
|
21
|
+
shippingCityName: null,
|
|
22
|
+
discount: null,
|
|
23
|
+
formatted_discount: null,
|
|
24
|
+
remaining: null,
|
|
25
|
+
formatted_remaining: null,
|
|
26
|
+
hasFreeGift: null,
|
|
27
|
+
giftThresholdRemaining: null,
|
|
28
|
+
formatted_giftThresholdRemaining: null,
|
|
29
|
+
appliedPromotions: [],
|
|
30
|
+
loading: true,
|
|
31
|
+
useBalance: null,
|
|
32
|
+
formatted_useBalance: null,
|
|
33
|
+
promotion_code: null,
|
|
34
|
+
promotion_error: null,
|
|
35
|
+
utensils: [],
|
|
36
|
+
};
|
|
37
|
+
// Helper to load checkout data from session storage
|
|
38
|
+
const loadCheckoutDataFromSession = () => {
|
|
39
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
40
|
+
try {
|
|
41
|
+
const stored = sessionStorage.getItem('checkoutData');
|
|
42
|
+
if (stored) {
|
|
43
|
+
return JSON.parse(stored);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error('Error loading checkout data from session:', error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
addressId: null,
|
|
52
|
+
billingAddressId: null,
|
|
53
|
+
shippingAddressId: null,
|
|
54
|
+
useBalance: null,
|
|
55
|
+
note: '',
|
|
56
|
+
paymentType: 'cash_on_delivery',
|
|
57
|
+
gifts: [],
|
|
58
|
+
card_id: null,
|
|
59
|
+
excludedPromotions: [],
|
|
60
|
+
promotion_code: null,
|
|
61
|
+
promotion_error: null,
|
|
62
|
+
utensils: [],
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
const initialCheckoutData = loadCheckoutDataFromSession();
|
|
66
|
+
// Helper to save checkout data to session storage
|
|
67
|
+
const saveCheckoutDataToSession = (data) => {
|
|
68
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
69
|
+
try {
|
|
70
|
+
sessionStorage.setItem('checkoutData', JSON.stringify(data));
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error('Error saving checkout data to session:', error);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
// Helper to get cart resource type based on current page
|
|
78
|
+
const getCartResourceType = () => {
|
|
79
|
+
if (typeof window === 'undefined')
|
|
80
|
+
return 'cart-summary';
|
|
81
|
+
const href = window.location.href;
|
|
82
|
+
return href.includes('checkout') || href.includes('cart') ? 'cart' : 'cart-summary';
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Create cart store
|
|
86
|
+
*/
|
|
87
|
+
export const useCartStore = create()(devtools((set, get) => ({
|
|
88
|
+
...initialCartState,
|
|
89
|
+
config: { hasVariants: false },
|
|
90
|
+
checkoutData: initialCheckoutData,
|
|
91
|
+
isLoggedIn: false,
|
|
92
|
+
selectedLang: 'en',
|
|
93
|
+
user: null,
|
|
94
|
+
idramFormData: null,
|
|
95
|
+
isCheckoutPage: false,
|
|
96
|
+
isCartPage: false,
|
|
97
|
+
/**
|
|
98
|
+
* Set loading state
|
|
99
|
+
*/
|
|
100
|
+
setLoading: (loading) => {
|
|
101
|
+
set({ loading }, false, 'cart/setLoading');
|
|
102
|
+
},
|
|
103
|
+
/**
|
|
104
|
+
* Set user (for card validation, etc.)
|
|
105
|
+
*/
|
|
106
|
+
setUser: (user) => {
|
|
107
|
+
set({ user }, false, 'cart/setUser');
|
|
108
|
+
},
|
|
109
|
+
/**
|
|
110
|
+
* Get cart from server
|
|
111
|
+
*/
|
|
112
|
+
getCart: async (params = {}) => {
|
|
113
|
+
const { config, isLoggedIn, selectedLang, checkoutData } = get();
|
|
114
|
+
const cartResourceType = getCartResourceType();
|
|
115
|
+
try {
|
|
116
|
+
// set({ loading: true }, false, 'cart/getCart/start');
|
|
117
|
+
const requestParams = config.hasVariants
|
|
118
|
+
? { ...params, cartResourceType }
|
|
119
|
+
: { ...cleanObject(checkoutData), ...params, cartResourceType };
|
|
120
|
+
if (isLoggedIn) {
|
|
121
|
+
// Logged in user - fetch from server
|
|
122
|
+
const { data } = await api.get({
|
|
123
|
+
url: 'cart',
|
|
124
|
+
lang: selectedLang,
|
|
125
|
+
params: requestParams,
|
|
126
|
+
});
|
|
127
|
+
set({
|
|
128
|
+
...data,
|
|
129
|
+
itemsCount: data.itemsCount ?? data.items_qty ?? 0,
|
|
130
|
+
items: data.items ?? [],
|
|
131
|
+
useBalance: data.useBalance || null,
|
|
132
|
+
promotion_code: data.promotion_code || null,
|
|
133
|
+
promotion_error: data.promotion_error || null,
|
|
134
|
+
loading: false,
|
|
135
|
+
}, false, 'cart/getCart/success');
|
|
136
|
+
// Auto-sync checkout data
|
|
137
|
+
get().updateCheckoutData({
|
|
138
|
+
useBalance: data.useBalance || null,
|
|
139
|
+
promotion_code: data.promotion_code || null,
|
|
140
|
+
promotion_error: data.promotion_error || null,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// Guest user - use cookies
|
|
145
|
+
const cookieItems = parseCookieCart(getCookie('cart'));
|
|
146
|
+
if (cookieItems.length === 0) {
|
|
147
|
+
set({ ...initialCartState, loading: false }, false, 'cart/getCart/empty');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const payload = config.hasVariants
|
|
151
|
+
? { cart: cookieItems, cartResourceType }
|
|
152
|
+
: { items: cookieItems, ...requestParams };
|
|
153
|
+
const { data } = await api.post('guest-cart', payload, selectedLang);
|
|
154
|
+
set({
|
|
155
|
+
...data,
|
|
156
|
+
itemsCount: data.itemsCount ?? data.items_qty ?? 0,
|
|
157
|
+
items: data.items ?? [],
|
|
158
|
+
useBalance: data.useBalance || null,
|
|
159
|
+
promotion_code: data.promotion_code || null,
|
|
160
|
+
promotion_error: data.promotion_error || null,
|
|
161
|
+
loading: false,
|
|
162
|
+
}, false, 'cart/getCart/success');
|
|
163
|
+
// Auto-sync checkout data
|
|
164
|
+
get().updateCheckoutData({
|
|
165
|
+
useBalance: data.useBalance || null,
|
|
166
|
+
promotion_code: data.promotion_code || null,
|
|
167
|
+
promotion_error: data.promotion_error || null,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.error('Error fetching cart:', error);
|
|
173
|
+
set({ loading: false }, false, 'cart/getCart/error');
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
/**
|
|
177
|
+
* Add item to cart
|
|
178
|
+
*/
|
|
179
|
+
addItem: async (params) => {
|
|
180
|
+
const { config, isLoggedIn, selectedLang, checkoutData } = get();
|
|
181
|
+
const cartResourceType = getCartResourceType();
|
|
182
|
+
try {
|
|
183
|
+
if (isLoggedIn) {
|
|
184
|
+
// Logged in user
|
|
185
|
+
const requestParams = config.hasVariants
|
|
186
|
+
? { ...params, cartResourceType }
|
|
187
|
+
: { ...params, ...cleanObject(checkoutData), cartResourceType };
|
|
188
|
+
// Include itemId if provided for update operations
|
|
189
|
+
if (params.itemId) {
|
|
190
|
+
requestParams.itemId = params.itemId;
|
|
191
|
+
}
|
|
192
|
+
const { data } = await api.post('toggle-cart-item', requestParams, selectedLang);
|
|
193
|
+
set({
|
|
194
|
+
...data,
|
|
195
|
+
itemsCount: data.itemsCount ?? data.items_qty ?? 0,
|
|
196
|
+
items: data.items ?? [],
|
|
197
|
+
useBalance: data.useBalance || null,
|
|
198
|
+
promotion_code: data.promotion_code || null,
|
|
199
|
+
promotion_error: data.promotion_error || null,
|
|
200
|
+
}, false, 'cart/addItem/success');
|
|
201
|
+
// Auto-sync checkout data
|
|
202
|
+
get().updateCheckoutData({
|
|
203
|
+
useBalance: data.useBalance || null,
|
|
204
|
+
promotion_code: data.promotion_code || null,
|
|
205
|
+
promotion_error: data.promotion_error || null,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Guest user - update cookie
|
|
210
|
+
const cookieItems = parseCookieCart(getCookie('cart'));
|
|
211
|
+
const newItem = {
|
|
212
|
+
productId: params.productId,
|
|
213
|
+
qty: params.qty,
|
|
214
|
+
...(config.hasVariants && params.variantId && params.optionId
|
|
215
|
+
? { variantId: params.variantId, optionId: params.optionId }
|
|
216
|
+
: {}),
|
|
217
|
+
};
|
|
218
|
+
const updatedItems = addOrUpdateItem(cookieItems, newItem, config.hasVariants);
|
|
219
|
+
setCookie('cart', serializeCartToCookie(updatedItems));
|
|
220
|
+
// Refresh cart from server
|
|
221
|
+
await get().getCart();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
console.error('Error adding item to cart:', error);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
/**
|
|
229
|
+
* Remove item from cart
|
|
230
|
+
*/
|
|
231
|
+
removeItem: async (params) => {
|
|
232
|
+
const { config, isLoggedIn, selectedLang, checkoutData } = get();
|
|
233
|
+
const cartResourceType = getCartResourceType();
|
|
234
|
+
try {
|
|
235
|
+
if (isLoggedIn) {
|
|
236
|
+
// Logged in user
|
|
237
|
+
const requestParams = config.hasVariants
|
|
238
|
+
? { ...params, cartResourceType }
|
|
239
|
+
: { ...params, ...cleanObject(checkoutData), cartResourceType };
|
|
240
|
+
const { data } = await api.post('delete-cart-item', requestParams, selectedLang);
|
|
241
|
+
set({
|
|
242
|
+
...data,
|
|
243
|
+
itemsCount: data.itemsCount ?? data.items_qty ?? 0,
|
|
244
|
+
items: data.items ?? [],
|
|
245
|
+
useBalance: data.useBalance || null,
|
|
246
|
+
promotion_code: data.promotion_code || null,
|
|
247
|
+
promotion_error: data.promotion_error || null,
|
|
248
|
+
}, false, 'cart/removeItem/success');
|
|
249
|
+
// Auto-sync checkout data
|
|
250
|
+
get().updateCheckoutData({
|
|
251
|
+
useBalance: data.useBalance || null,
|
|
252
|
+
promotion_code: data.promotion_code || null,
|
|
253
|
+
promotion_error: data.promotion_error || null,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// Guest user - update cookie
|
|
258
|
+
const cookieItems = parseCookieCart(getCookie('cart'));
|
|
259
|
+
const targetItem = {
|
|
260
|
+
productId: params.productId,
|
|
261
|
+
qty: 0,
|
|
262
|
+
...(config.hasVariants && params.variantId && params.optionId
|
|
263
|
+
? { variantId: params.variantId, optionId: params.optionId }
|
|
264
|
+
: {}),
|
|
265
|
+
};
|
|
266
|
+
const updatedItems = removeItem(cookieItems, targetItem, config.hasVariants);
|
|
267
|
+
setCookie('cart', serializeCartToCookie(updatedItems));
|
|
268
|
+
// Refresh cart from server
|
|
269
|
+
await get().getCart();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
console.error('Error removing item from cart:', error);
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
/**
|
|
277
|
+
* Update item quantity
|
|
278
|
+
*/
|
|
279
|
+
updateItem: async (params) => {
|
|
280
|
+
const { config } = get();
|
|
281
|
+
// If quantity is 0, remove the item
|
|
282
|
+
if (params.qty === 0) {
|
|
283
|
+
return get().removeItem(params);
|
|
284
|
+
}
|
|
285
|
+
// Otherwise, add/update the item
|
|
286
|
+
return get().addItem(params);
|
|
287
|
+
},
|
|
288
|
+
/**
|
|
289
|
+
* Clear entire cart
|
|
290
|
+
*/
|
|
291
|
+
clearCart: async () => {
|
|
292
|
+
const { isLoggedIn, selectedLang } = get();
|
|
293
|
+
try {
|
|
294
|
+
get().resetCart();
|
|
295
|
+
if (isLoggedIn) {
|
|
296
|
+
const { data } = await api.post('clear-cart', {}, selectedLang);
|
|
297
|
+
set({
|
|
298
|
+
...data,
|
|
299
|
+
itemsCount: data.itemsCount ?? data.items_qty ?? 0,
|
|
300
|
+
items: data.items ?? [],
|
|
301
|
+
}, false, 'cart/clearCart/success');
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
await get().getCart();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
console.error('Error clearing cart:', error);
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
/**
|
|
312
|
+
* Sync cart (for logged in users with cookie cart)
|
|
313
|
+
*/
|
|
314
|
+
syncCart: async () => {
|
|
315
|
+
const { selectedLang } = get();
|
|
316
|
+
const cookieItems = parseCookieCart(getCookie('cart'));
|
|
317
|
+
if (cookieItems.length > 0) {
|
|
318
|
+
try {
|
|
319
|
+
const { data } = await api.post('sync-cart', { items: cookieItems }, selectedLang);
|
|
320
|
+
set({
|
|
321
|
+
...data,
|
|
322
|
+
itemsCount: data.itemsCount ?? data.items_qty ?? 0,
|
|
323
|
+
items: data.items ?? [],
|
|
324
|
+
}, false, 'cart/syncCart/success');
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
console.error('Error syncing cart:', error);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
/**
|
|
332
|
+
* Merge cart (when logging in)
|
|
333
|
+
*/
|
|
334
|
+
mergeCart: async () => {
|
|
335
|
+
const { selectedLang } = get();
|
|
336
|
+
const cookieItems = parseCookieCart(getCookie('cart'));
|
|
337
|
+
if (cookieItems.length > 0) {
|
|
338
|
+
try {
|
|
339
|
+
removeCookie('cart');
|
|
340
|
+
const { data } = await api.post('merge-cart', { items: cookieItems }, selectedLang);
|
|
341
|
+
set({
|
|
342
|
+
...data,
|
|
343
|
+
itemsCount: data.itemsCount ?? data.items_qty ?? 0,
|
|
344
|
+
items: data.items ?? [],
|
|
345
|
+
}, false, 'cart/mergeCart/success');
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
console.error('Error merging cart:', error);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
/**
|
|
353
|
+
* Update checkout data (with session storage persistence)
|
|
354
|
+
*/
|
|
355
|
+
updateCheckoutData: (data) => {
|
|
356
|
+
set((state) => {
|
|
357
|
+
const updatedCheckoutData = { ...state.checkoutData, ...data };
|
|
358
|
+
saveCheckoutDataToSession(updatedCheckoutData);
|
|
359
|
+
return { checkoutData: updatedCheckoutData };
|
|
360
|
+
}, false, 'cart/updateCheckoutData');
|
|
361
|
+
},
|
|
362
|
+
/**
|
|
363
|
+
* Fill checkout data (legacy method with flexible arguments)
|
|
364
|
+
*/
|
|
365
|
+
fillCheckoutData: (keyOrObject, value) => {
|
|
366
|
+
const { checkoutData } = get();
|
|
367
|
+
let updatedData;
|
|
368
|
+
if (typeof keyOrObject === 'string') {
|
|
369
|
+
updatedData = { [keyOrObject]: value };
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
updatedData = keyOrObject;
|
|
373
|
+
}
|
|
374
|
+
const newCheckoutData = { ...checkoutData, ...updatedData };
|
|
375
|
+
saveCheckoutDataToSession(newCheckoutData);
|
|
376
|
+
set({ checkoutData: newCheckoutData }, false, 'cart/fillCheckoutData');
|
|
377
|
+
},
|
|
378
|
+
/**
|
|
379
|
+
* Fill cart (updates checkout data and refreshes cart)
|
|
380
|
+
*/
|
|
381
|
+
fillCart: (key, value) => {
|
|
382
|
+
const updatedData = { [key]: value };
|
|
383
|
+
get().updateCheckoutData(updatedData);
|
|
384
|
+
get().getCart(updatedData);
|
|
385
|
+
},
|
|
386
|
+
/**
|
|
387
|
+
* Set gifts selection
|
|
388
|
+
*/
|
|
389
|
+
setGifts: ({ promotionId, qty, productId }) => {
|
|
390
|
+
const { checkoutData } = get();
|
|
391
|
+
const updatedGifts = checkoutData.gifts.map((item) => {
|
|
392
|
+
if (item.promotionId === promotionId && item.productId === productId) {
|
|
393
|
+
return { ...item, qty };
|
|
394
|
+
}
|
|
395
|
+
return item;
|
|
396
|
+
});
|
|
397
|
+
get().updateCheckoutData({ gifts: updatedGifts });
|
|
398
|
+
},
|
|
399
|
+
/**
|
|
400
|
+
* Toggle promotion (exclude/include)
|
|
401
|
+
*/
|
|
402
|
+
togglePromotion: ({ promotionId }) => {
|
|
403
|
+
const { checkoutData } = get();
|
|
404
|
+
const excludedPromotions = [...checkoutData.excludedPromotions];
|
|
405
|
+
const index = excludedPromotions.indexOf(promotionId);
|
|
406
|
+
if (index > -1) {
|
|
407
|
+
excludedPromotions.splice(index, 1);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
excludedPromotions.push(promotionId);
|
|
411
|
+
}
|
|
412
|
+
const updatedData = { excludedPromotions };
|
|
413
|
+
get().updateCheckoutData(updatedData);
|
|
414
|
+
get().getCart(updatedData);
|
|
415
|
+
},
|
|
416
|
+
/**
|
|
417
|
+
* Reorder from previous order
|
|
418
|
+
*/
|
|
419
|
+
reorder: async (orderId) => {
|
|
420
|
+
const { selectedLang } = get();
|
|
421
|
+
await api.post('reorder', { orderId }, selectedLang);
|
|
422
|
+
// Redirect to cart page
|
|
423
|
+
if (typeof window !== 'undefined') {
|
|
424
|
+
window.location.href = `/${selectedLang}/cart`;
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
/**
|
|
428
|
+
* Set Idram payment form
|
|
429
|
+
*/
|
|
430
|
+
setIdramForm: (form) => {
|
|
431
|
+
set({ idramFormData: form }, false, 'cart/setIdramForm');
|
|
432
|
+
},
|
|
433
|
+
/**
|
|
434
|
+
* Submit Idram payment form
|
|
435
|
+
*/
|
|
436
|
+
submitIdramPayment: () => {
|
|
437
|
+
const { idramFormData } = get();
|
|
438
|
+
if (idramFormData && typeof document !== 'undefined') {
|
|
439
|
+
const form = document.createElement('form');
|
|
440
|
+
form.action = idramFormData.url;
|
|
441
|
+
form.method = 'POST';
|
|
442
|
+
// Add language field
|
|
443
|
+
const langInput = document.createElement('input');
|
|
444
|
+
langInput.type = 'hidden';
|
|
445
|
+
langInput.name = 'EDP_LANGUAGE';
|
|
446
|
+
langInput.value = idramFormData.language;
|
|
447
|
+
form.appendChild(langInput);
|
|
448
|
+
// Add form fields
|
|
449
|
+
Object.keys(idramFormData.form).forEach((key) => {
|
|
450
|
+
const input = document.createElement('input');
|
|
451
|
+
input.type = 'hidden';
|
|
452
|
+
input.name = key;
|
|
453
|
+
input.value = idramFormData.form[key];
|
|
454
|
+
form.appendChild(input);
|
|
455
|
+
});
|
|
456
|
+
document.body.appendChild(form);
|
|
457
|
+
form.submit();
|
|
458
|
+
get().resetCart();
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
/**
|
|
462
|
+
* Checkout (with advanced payment handling)
|
|
463
|
+
*/
|
|
464
|
+
checkout: async () => {
|
|
465
|
+
const { checkoutData, itemsCount, selectedLang, user } = get();
|
|
466
|
+
if (itemsCount === 0) {
|
|
467
|
+
throw new Error('Cart is empty');
|
|
468
|
+
}
|
|
469
|
+
// Card validation
|
|
470
|
+
if (isNumeric(checkoutData.paymentType)) {
|
|
471
|
+
const selectedCard = user?.cards?.find((card) => card.id === checkoutData.paymentType);
|
|
472
|
+
if (!selectedCard || selectedCard.is_expired) {
|
|
473
|
+
alert('The selected card is expired. Please choose another payment method.');
|
|
474
|
+
throw new Error('Card is expired');
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
set({ loading: true }, false, 'cart/checkout/start');
|
|
479
|
+
const payload = {
|
|
480
|
+
...cleanObject(checkoutData),
|
|
481
|
+
};
|
|
482
|
+
// Remove empty gifts array
|
|
483
|
+
if (!payload.gifts?.length) {
|
|
484
|
+
delete payload.gifts;
|
|
485
|
+
}
|
|
486
|
+
// Handle credit card payment
|
|
487
|
+
if (isNumeric(checkoutData.paymentType)) {
|
|
488
|
+
payload.paymentType = 'credit_card';
|
|
489
|
+
payload.card_id = Number(checkoutData.paymentType);
|
|
490
|
+
}
|
|
491
|
+
// Handle new credit card
|
|
492
|
+
if (checkoutData.paymentType === 'credit_card') {
|
|
493
|
+
payload.card_id = 0;
|
|
494
|
+
}
|
|
495
|
+
const response = await api.post('checkout', payload, selectedLang);
|
|
496
|
+
set({ loading: false }, false, 'cart/checkout/success');
|
|
497
|
+
// Handle response
|
|
498
|
+
if (response.redirect_url) {
|
|
499
|
+
// Redirect to success page
|
|
500
|
+
if (typeof window !== 'undefined') {
|
|
501
|
+
window.location.href = response.redirect_url;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
else if (response.url) {
|
|
505
|
+
// Idram payment
|
|
506
|
+
const lang = selectedLang === 'en' ? 'EN' : selectedLang === 'ru' ? 'RU' : 'AM';
|
|
507
|
+
get().setIdramForm({
|
|
508
|
+
url: response.url,
|
|
509
|
+
form: response.form,
|
|
510
|
+
language: lang,
|
|
511
|
+
});
|
|
512
|
+
// Submit form after a short delay
|
|
513
|
+
setTimeout(() => {
|
|
514
|
+
get().submitIdramPayment();
|
|
515
|
+
}, 100);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
// Success without redirect
|
|
519
|
+
get().resetCart();
|
|
520
|
+
}
|
|
521
|
+
return response;
|
|
522
|
+
}
|
|
523
|
+
catch (error) {
|
|
524
|
+
set({ loading: false }, false, 'cart/checkout/error');
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
/**
|
|
529
|
+
* Reset cart to initial state
|
|
530
|
+
*/
|
|
531
|
+
resetCart: () => {
|
|
532
|
+
const { user } = get();
|
|
533
|
+
removeCookie('cart');
|
|
534
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
535
|
+
sessionStorage.clear();
|
|
536
|
+
}
|
|
537
|
+
const resetCheckoutData = {
|
|
538
|
+
addressId: null,
|
|
539
|
+
billingAddressId: null,
|
|
540
|
+
shippingAddressId: null,
|
|
541
|
+
useBalance: null,
|
|
542
|
+
note: '',
|
|
543
|
+
paymentType: user?.payment_method || 'cash_on_delivery',
|
|
544
|
+
gifts: [],
|
|
545
|
+
card_id: user?.payment_method === 'credit_card' ? user.card_id : null,
|
|
546
|
+
excludedPromotions: [],
|
|
547
|
+
promotion_code: null,
|
|
548
|
+
promotion_error: null,
|
|
549
|
+
utensils: [],
|
|
550
|
+
};
|
|
551
|
+
set({
|
|
552
|
+
...initialCartState,
|
|
553
|
+
checkoutData: resetCheckoutData,
|
|
554
|
+
loading: false,
|
|
555
|
+
}, false, 'cart/reset');
|
|
556
|
+
},
|
|
557
|
+
}), { name: 'CartStore' }));
|
|
558
|
+
// Subscribe to cart changes for auto-sync of gifts and utensils
|
|
559
|
+
useCartStore.subscribe((state, prevState) => {
|
|
560
|
+
if (state.items !== prevState.items) {
|
|
561
|
+
const currentGiftMap = new Map(state.checkoutData.gifts?.map((g) => [`${g.promotionId}-${g.productId}`, g]) || []);
|
|
562
|
+
// Build gifts array based on current cart items (source of truth)
|
|
563
|
+
const updatedGifts = state.items
|
|
564
|
+
.filter((item) => item.is_gift && item.appliedPromotion)
|
|
565
|
+
.map((item) => {
|
|
566
|
+
const key = `${item.appliedPromotion.id}-${item.product.id}`;
|
|
567
|
+
if (currentGiftMap.has(key)) {
|
|
568
|
+
const existingGift = currentGiftMap.get(key);
|
|
569
|
+
if (existingGift.qty === 0) {
|
|
570
|
+
return existingGift;
|
|
571
|
+
}
|
|
572
|
+
return {
|
|
573
|
+
...existingGift,
|
|
574
|
+
qty: item.qty,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
productId: item.product.id,
|
|
579
|
+
promotionId: item.appliedPromotion.id,
|
|
580
|
+
qty: item.qty,
|
|
581
|
+
};
|
|
582
|
+
});
|
|
583
|
+
const hasChanged = updatedGifts.length !== (state.checkoutData.gifts?.length || 0) ||
|
|
584
|
+
updatedGifts.some((gift, i) => {
|
|
585
|
+
const oldGift = state.checkoutData.gifts?.[i];
|
|
586
|
+
return (!oldGift ||
|
|
587
|
+
gift.productId !== oldGift.productId ||
|
|
588
|
+
gift.promotionId !== oldGift.promotionId ||
|
|
589
|
+
gift.qty !== oldGift.qty);
|
|
590
|
+
});
|
|
591
|
+
if (hasChanged) {
|
|
592
|
+
useCartStore.getState().updateCheckoutData({ gifts: updatedGifts });
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
// Auto-sync utensils when cart utensils change
|
|
596
|
+
if (state.utensils !== prevState.utensils) {
|
|
597
|
+
const currentUtensils = new Map(state.checkoutData.utensils?.map((g) => [g.utensilId, g]) || []);
|
|
598
|
+
// Build utensils array based on current cart utensils (source of truth)
|
|
599
|
+
const updatedUtensils = state.utensils.map((item) => {
|
|
600
|
+
// If utensil exists in old checkout data, keep it (preserves user selections)
|
|
601
|
+
if (currentUtensils.has(item.id)) {
|
|
602
|
+
return currentUtensils.get(item.id);
|
|
603
|
+
}
|
|
604
|
+
// Otherwise, create new utensil entry
|
|
605
|
+
return {
|
|
606
|
+
utensilId: item.id,
|
|
607
|
+
isChecked: true,
|
|
608
|
+
};
|
|
609
|
+
});
|
|
610
|
+
// Update if utensils changed (added, removed, or modified)
|
|
611
|
+
const hasChanged = updatedUtensils.length !== (state.checkoutData.utensils?.length || 0) ||
|
|
612
|
+
updatedUtensils.some((utensil, i) => {
|
|
613
|
+
const oldUtensil = state.checkoutData.utensils?.[i];
|
|
614
|
+
return !oldUtensil || utensil.utensilId !== oldUtensil.utensilId;
|
|
615
|
+
});
|
|
616
|
+
if (hasChanged) {
|
|
617
|
+
useCartStore.getState().updateCheckoutData({ utensils: updatedUtensils });
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
/**
|
|
622
|
+
* Initialize cart store
|
|
623
|
+
* Call this once when your app starts
|
|
624
|
+
*/
|
|
625
|
+
export function initCartStore(config, isLoggedIn, selectedLang = 'en', user = null) {
|
|
626
|
+
// Check if on cart/checkout page
|
|
627
|
+
const isCheckoutPage = typeof window !== 'undefined' && window.location.href.includes('checkout');
|
|
628
|
+
const isCartPage = typeof window !== 'undefined' && window.location.href.includes('cart');
|
|
629
|
+
// Handle URL parameters for saved card
|
|
630
|
+
if (typeof window !== 'undefined') {
|
|
631
|
+
const params = new URLSearchParams(window.location.search);
|
|
632
|
+
if (params.get('cardSaved') === '1' && params.get('cardId')) {
|
|
633
|
+
const checkoutData = useCartStore.getState().checkoutData;
|
|
634
|
+
useCartStore.getState().updateCheckoutData({
|
|
635
|
+
paymentType: params.get('cardId'),
|
|
636
|
+
card_id: Number(params.get('cardId')),
|
|
637
|
+
});
|
|
638
|
+
// Clean URL
|
|
639
|
+
params.delete('cardSaved');
|
|
640
|
+
params.delete('cardId');
|
|
641
|
+
const newUrl = `${window.location.pathname}${params.toString() ? '?' + params.toString() : ''}`;
|
|
642
|
+
window.history.replaceState({}, '', newUrl);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// Initialize checkout data with user info
|
|
646
|
+
const initialCheckoutWithUser = user
|
|
647
|
+
? {
|
|
648
|
+
...loadCheckoutDataFromSession(),
|
|
649
|
+
paymentType: user.payment_method || 'cash_on_delivery',
|
|
650
|
+
card_id: user.payment_method === 'credit_card' ? user.card_id : null,
|
|
651
|
+
}
|
|
652
|
+
: loadCheckoutDataFromSession();
|
|
653
|
+
useCartStore.setState({
|
|
654
|
+
config,
|
|
655
|
+
isLoggedIn,
|
|
656
|
+
selectedLang,
|
|
657
|
+
user,
|
|
658
|
+
isCheckoutPage,
|
|
659
|
+
isCartPage,
|
|
660
|
+
checkoutData: initialCheckoutWithUser,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
//# sourceMappingURL=cartStore.js.map
|