@payloadcms/storage-r2 0.0.1-beta.0 → 3.58.0-internal.df9fce5

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 (89) hide show
  1. package/dist/handleDelete.d.ts +8 -0
  2. package/dist/handleDelete.d.ts.map +1 -0
  3. package/dist/handleDelete.js +8 -0
  4. package/dist/handleDelete.js.map +1 -0
  5. package/dist/handleUpload.d.ts +11 -0
  6. package/dist/handleUpload.d.ts.map +1 -0
  7. package/dist/handleUpload.js +13 -0
  8. package/dist/handleUpload.js.map +1 -0
  9. package/dist/index.d.ts +15 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +59 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/staticHandler.d.ts +11 -0
  14. package/dist/staticHandler.d.ts.map +1 -0
  15. package/dist/staticHandler.js +29 -0
  16. package/dist/staticHandler.js.map +1 -0
  17. package/dist/types.d.ts +16 -0
  18. package/dist/types.d.ts.map +1 -0
  19. package/dist/types.js +3 -0
  20. package/dist/types.js.map +1 -0
  21. package/package.json +57 -1
  22. package/.prettierignore +0 -12
  23. package/.swcrc +0 -24
  24. package/README.md +0 -3
  25. package/eslint.config.js +0 -18
  26. package/src/addresses/addressesCollection.ts +0 -76
  27. package/src/addresses/defaultAddressFields.ts +0 -83
  28. package/src/addresses/defaultCountries.ts +0 -50
  29. package/src/carts/beforeChange.ts +0 -51
  30. package/src/carts/cartsCollection.ts +0 -146
  31. package/src/currencies/index.ts +0 -29
  32. package/src/endpoints/confirmOrder.ts +0 -312
  33. package/src/endpoints/initiatePayment.ts +0 -322
  34. package/src/exports/addresses.ts +0 -2
  35. package/src/exports/currencies.ts +0 -1
  36. package/src/exports/fields.ts +0 -5
  37. package/src/exports/orders.ts +0 -1
  38. package/src/exports/payments/stripe.ts +0 -1
  39. package/src/exports/plugin.ts +0 -1
  40. package/src/exports/products.ts +0 -1
  41. package/src/exports/react.ts +0 -8
  42. package/src/exports/transactions.ts +0 -1
  43. package/src/exports/translations.ts +0 -1
  44. package/src/exports/types.ts +0 -7
  45. package/src/exports/ui.ts +0 -3
  46. package/src/exports/variants.ts +0 -5
  47. package/src/fields/amountField.ts +0 -43
  48. package/src/fields/cartItemsField.ts +0 -84
  49. package/src/fields/currencyField.ts +0 -39
  50. package/src/fields/inventoryField.ts +0 -22
  51. package/src/fields/pricesField.ts +0 -65
  52. package/src/fields/statusField.ts +0 -57
  53. package/src/fields/variantsFields.ts +0 -56
  54. package/src/index.ts +0 -275
  55. package/src/orders/ordersCollection.ts +0 -157
  56. package/src/payments/adapters/stripe/confirmOrder.ts +0 -123
  57. package/src/payments/adapters/stripe/endpoints/webhooks.ts +0 -69
  58. package/src/payments/adapters/stripe/index.ts +0 -135
  59. package/src/payments/adapters/stripe/initiatePayment.ts +0 -131
  60. package/src/products/productsCollection.ts +0 -78
  61. package/src/react/provider/index.tsx +0 -893
  62. package/src/react/provider/types.ts +0 -184
  63. package/src/react/provider/utilities.ts +0 -22
  64. package/src/transactions/transactionsCollection.ts +0 -166
  65. package/src/translations/en.ts +0 -64
  66. package/src/translations/index.ts +0 -11
  67. package/src/translations/translation-schema.json +0 -35
  68. package/src/types.ts +0 -403
  69. package/src/ui/PriceInput/FormattedInput.tsx +0 -134
  70. package/src/ui/PriceInput/index.scss +0 -28
  71. package/src/ui/PriceInput/index.tsx +0 -43
  72. package/src/ui/PriceInput/utilities.ts +0 -46
  73. package/src/ui/PriceRowLabel/index.css +0 -13
  74. package/src/ui/PriceRowLabel/index.tsx +0 -56
  75. package/src/ui/VariantOptionsSelector/ErrorBox.tsx +0 -27
  76. package/src/ui/VariantOptionsSelector/OptionsSelect.tsx +0 -78
  77. package/src/ui/VariantOptionsSelector/index.css +0 -37
  78. package/src/ui/VariantOptionsSelector/index.tsx +0 -83
  79. package/src/utilities/defaultProductsValidation.ts +0 -42
  80. package/src/utilities/errorCodes.ts +0 -14
  81. package/src/utilities/getCollectionSlugMap.ts +0 -84
  82. package/src/utilities/sanitizePluginConfig.ts +0 -80
  83. package/src/variants/variantOptionsCollection.ts +0 -59
  84. package/src/variants/variantTypesCollection.ts +0 -55
  85. package/src/variants/variantsCollection/hooks/beforeChange.ts +0 -47
  86. package/src/variants/variantsCollection/hooks/validateOptions.ts +0 -72
  87. package/src/variants/variantsCollection/index.ts +0 -119
  88. package/tsconfig.json +0 -7
  89. package/tsconfig.tsbuildinfo +0 -1
@@ -1,893 +0,0 @@
1
- 'use client'
2
- import { type DefaultDocumentIDType, type TypedCollection, type TypedUser } from 'payload'
3
- import { deepMergeSimple } from 'payload/shared'
4
- import * as qs from 'qs-esm'
5
- import React, { createContext, use, useCallback, useEffect, useMemo, useRef, useState } from 'react'
6
-
7
- import type { CartItem, Currency } from '../../types.js'
8
- import type { ContextProps, EcommerceContext as EcommerceContextType } from './types.js'
9
-
10
- const defaultContext: EcommerceContextType = {
11
- addItem: async () => {},
12
- clearCart: async () => {},
13
- confirmOrder: async () => {},
14
- createAddress: async () => {},
15
- currenciesConfig: {
16
- defaultCurrency: 'USD',
17
- supportedCurrencies: [
18
- {
19
- code: 'USD',
20
- decimals: 2,
21
- label: 'US Dollar',
22
- symbol: '$',
23
- },
24
- ],
25
- },
26
- currency: {
27
- code: 'USD',
28
- decimals: 2,
29
- label: 'US Dollar',
30
- symbol: '$',
31
- },
32
- decrementItem: async () => {},
33
- incrementItem: async () => {},
34
- initiatePayment: async () => {},
35
- paymentMethods: [],
36
- removeItem: async () => {},
37
- setCurrency: () => {},
38
- updateAddress: async () => {},
39
- }
40
-
41
- const EcommerceContext = createContext<EcommerceContextType>(defaultContext)
42
-
43
- const defaultLocalStorage = {
44
- key: 'cart',
45
- }
46
-
47
- export const EcommerceProvider: React.FC<ContextProps> = ({
48
- addressesSlug = 'addresses',
49
- api,
50
- cartsSlug = 'carts',
51
- children,
52
- currenciesConfig = {
53
- defaultCurrency: 'USD',
54
- supportedCurrencies: [
55
- {
56
- code: 'USD',
57
- decimals: 2,
58
- label: 'US Dollar',
59
- symbol: '$',
60
- },
61
- ],
62
- },
63
- customersSlug = 'users',
64
- debug = false,
65
- paymentMethods = [],
66
- syncLocalStorage = true,
67
- }) => {
68
- const localStorageConfig =
69
- syncLocalStorage && typeof syncLocalStorage === 'object'
70
- ? {
71
- ...defaultLocalStorage,
72
- ...syncLocalStorage,
73
- }
74
- : defaultLocalStorage
75
-
76
- const { apiRoute = '/api', cartsFetchQuery = {}, serverURL = '' } = api || {}
77
- const baseAPIURL = `${serverURL}${apiRoute}`
78
-
79
- /**
80
- * The payment data received from the payment initiation process, this is then threaded through to the payment confirmation process.
81
- * Useful for storing things like payment intent IDs, session IDs, etc.
82
- */
83
- const [paymentData, setPaymentData] = useState<EcommerceContextType['paymentData']>()
84
-
85
- const [user, setUser] = useState<null | TypedUser>(null)
86
-
87
- const [addresses, setAddresses] = useState<TypedCollection['addresses'][]>()
88
-
89
- const hasRendered = useRef(false)
90
-
91
- /**
92
- * The ID of the cart associated with the current session.
93
- * This is used to identify the cart in the database or local storage.
94
- * It can be null if no cart has been created yet.
95
- */
96
- const [cartID, setCartID] = useState<DefaultDocumentIDType>()
97
- const [cart, setCart] = useState<TypedCollection['carts']>()
98
-
99
- const [selectedCurrency, setSelectedCurrency] = useState<Currency>(
100
- () =>
101
- currenciesConfig.supportedCurrencies.find(
102
- (c) => c.code === currenciesConfig.defaultCurrency,
103
- )!,
104
- )
105
-
106
- const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<null | string>(null)
107
-
108
- const cartQuery = useMemo(() => {
109
- const priceField = `priceIn${selectedCurrency.code}`
110
-
111
- const baseQuery = {
112
- depth: 0,
113
- populate: {
114
- products: {
115
- [priceField]: true,
116
- },
117
- variants: {
118
- options: true,
119
- [priceField]: true,
120
- },
121
- },
122
- select: {
123
- items: true,
124
- subtotal: true,
125
- },
126
- }
127
-
128
- return deepMergeSimple(baseQuery, cartsFetchQuery)
129
- }, [selectedCurrency.code, cartsFetchQuery])
130
-
131
- const createCart = useCallback(
132
- async (initialData: Record<string, unknown>) => {
133
- const query = qs.stringify(cartQuery)
134
-
135
- const response = await fetch(`${baseAPIURL}/${cartsSlug}?${query}`, {
136
- body: JSON.stringify({
137
- ...initialData,
138
- currency: selectedCurrency.code,
139
- customer: user?.id,
140
- }),
141
- credentials: 'include',
142
- headers: {
143
- 'Content-Type': 'application/json',
144
- },
145
- method: 'POST',
146
- })
147
-
148
- if (!response.ok) {
149
- const errorText = await response.text()
150
- throw new Error(`Failed to create cart: ${errorText}`)
151
- }
152
-
153
- const data = await response.json()
154
-
155
- if (data.error) {
156
- throw new Error(`Cart creation error: ${data.error}`)
157
- }
158
-
159
- return data.doc as TypedCollection['carts']
160
- },
161
- [baseAPIURL, cartQuery, cartsSlug, selectedCurrency.code, user?.id],
162
- )
163
-
164
- const getCart = useCallback(
165
- async (cartID: DefaultDocumentIDType) => {
166
- const query = qs.stringify(cartQuery)
167
-
168
- const response = await fetch(`${baseAPIURL}/${cartsSlug}/${cartID}?${query}`, {
169
- credentials: 'include',
170
- headers: {
171
- 'Content-Type': 'application/json',
172
- },
173
- method: 'GET',
174
- })
175
- if (!response.ok) {
176
- const errorText = await response.text()
177
- throw new Error(`Failed to fetch cart: ${errorText}`)
178
- }
179
- const data = await response.json()
180
- if (data.error) {
181
- throw new Error(`Cart fetch error: ${data.error}`)
182
- }
183
-
184
- return data as TypedCollection['carts']
185
- },
186
- [baseAPIURL, cartQuery, cartsSlug],
187
- )
188
-
189
- const updateCart = useCallback(
190
- async (cartID: DefaultDocumentIDType, data: Partial<TypedCollection['carts']>) => {
191
- const query = qs.stringify(cartQuery)
192
-
193
- const response = await fetch(`${baseAPIURL}/${cartsSlug}/${cartID}?${query}`, {
194
- body: JSON.stringify(data),
195
- credentials: 'include',
196
- headers: {
197
- 'Content-Type': 'application/json',
198
- },
199
- method: 'PATCH',
200
- })
201
-
202
- if (!response.ok) {
203
- const errorText = await response.text()
204
- throw new Error(`Failed to update cart: ${errorText}`)
205
- }
206
-
207
- const updatedCart = await response.json()
208
- setCart(updatedCart.doc as TypedCollection['carts'])
209
- },
210
- [baseAPIURL, cartQuery, cartsSlug],
211
- )
212
-
213
- const deleteCart = useCallback(
214
- async (cartID: DefaultDocumentIDType) => {
215
- const response = await fetch(`${baseAPIURL}/${cartsSlug}/${cartID}`, {
216
- credentials: 'include',
217
- headers: {
218
- 'Content-Type': 'application/json',
219
- },
220
- method: 'DELETE',
221
- })
222
-
223
- if (!response.ok) {
224
- const errorText = await response.text()
225
- throw new Error(`Failed to update cart: ${errorText}`)
226
- }
227
-
228
- setCart(undefined)
229
- setCartID(undefined)
230
- },
231
- [baseAPIURL, cartsSlug],
232
- )
233
-
234
- useEffect(() => {
235
- if (hasRendered.current) {
236
- if (syncLocalStorage && cartID) {
237
- localStorage.setItem(localStorageConfig.key, cartID as string)
238
- }
239
- }
240
- }, [cartID, localStorageConfig.key, syncLocalStorage])
241
-
242
- const addItem: EcommerceContextType['addItem'] = useCallback(
243
- async (item, quantity = 1) => {
244
- if (cartID) {
245
- const existingCart = await getCart(cartID)
246
-
247
- if (!existingCart) {
248
- // console.error(`Cart with ID "${cartID}" not found`)
249
-
250
- setCartID(undefined)
251
- setCart(undefined)
252
- return
253
- }
254
-
255
- // Check if the item already exists in the cart
256
- const existingItemIndex = existingCart.items.findIndex((cartItem: CartItem) => {
257
- const productID =
258
- typeof cartItem.product === 'object' ? cartItem.product.id : item.product
259
- const variantID =
260
- cartItem.variant && typeof cartItem.variant === 'object'
261
- ? cartItem.variant.id
262
- : item.variant
263
-
264
- return (
265
- productID === item.product &&
266
- (item.variant && variantID ? variantID === item.variant : true)
267
- )
268
- })
269
-
270
- let updatedItems = [...existingCart.items]
271
- if (existingItemIndex !== -1) {
272
- // If the item exists, update its quantity
273
- updatedItems[existingItemIndex].quantity =
274
- updatedItems[existingItemIndex].quantity + quantity
275
-
276
- // Update the cart with the new items
277
- await updateCart(cartID, {
278
- items: updatedItems,
279
- })
280
- } else {
281
- // If the item does not exist, add it to the cart
282
- updatedItems = [...existingCart.items, { ...item, quantity }]
283
- }
284
-
285
- // Update the cart with the new items
286
- await updateCart(cartID, {
287
- items: updatedItems,
288
- })
289
- } else {
290
- // If no cartID exists, create a new cart
291
- const newCart = await createCart({ items: [{ ...item, quantity }] })
292
-
293
- setCartID(newCart.id)
294
- setCart(newCart)
295
- }
296
- },
297
- [cartID, createCart, getCart, updateCart],
298
- )
299
-
300
- const removeItem: EcommerceContextType['removeItem'] = useCallback(
301
- async (targetID) => {
302
- if (!cartID) {
303
- return
304
- }
305
-
306
- const existingCart = await getCart(cartID)
307
-
308
- if (!existingCart) {
309
- // console.error(`Cart with ID "${cartID}" not found`)
310
- setCartID(undefined)
311
- setCart(undefined)
312
- return
313
- }
314
-
315
- // Check if the item already exists in the cart
316
- const existingItemIndex = existingCart.items.findIndex(
317
- (cartItem: CartItem) => cartItem.id === targetID,
318
- )
319
-
320
- if (existingItemIndex !== -1) {
321
- // If the item exists, remove it from the cart
322
- const updatedItems = [...existingCart.items]
323
- updatedItems.splice(existingItemIndex, 1)
324
-
325
- // Update the cart with the new items
326
- await updateCart(cartID, {
327
- items: updatedItems,
328
- })
329
- }
330
- },
331
- [cartID, getCart, updateCart],
332
- )
333
-
334
- const incrementItem: EcommerceContextType['incrementItem'] = useCallback(
335
- async (targetID) => {
336
- if (!cartID) {
337
- return
338
- }
339
-
340
- const existingCart = await getCart(cartID)
341
-
342
- if (!existingCart) {
343
- // console.error(`Cart with ID "${cartID}" not found`)
344
- setCartID(undefined)
345
- setCart(undefined)
346
- return
347
- }
348
-
349
- // Check if the item already exists in the cart
350
- const existingItemIndex = existingCart.items.findIndex(
351
- (cartItem: CartItem) => cartItem.id === targetID,
352
- )
353
-
354
- let updatedItems = [...existingCart.items]
355
-
356
- if (existingItemIndex !== -1) {
357
- // If the item exists, increment its quantity
358
- updatedItems[existingItemIndex].quantity = updatedItems[existingItemIndex].quantity + 1 // Increment by 1
359
- // Update the cart with the new items
360
- await updateCart(cartID, {
361
- items: updatedItems,
362
- })
363
- } else {
364
- // If the item does not exist, add it to the cart with quantity 1
365
- updatedItems = [...existingCart.items, { product: targetID, quantity: 1 }]
366
- // Update the cart with the new items
367
- await updateCart(cartID, {
368
- items: updatedItems,
369
- })
370
- }
371
- },
372
- [cartID, getCart, updateCart],
373
- )
374
-
375
- const decrementItem: EcommerceContextType['decrementItem'] = useCallback(
376
- async (targetID) => {
377
- if (!cartID) {
378
- return
379
- }
380
-
381
- const existingCart = await getCart(cartID)
382
-
383
- if (!existingCart) {
384
- // console.error(`Cart with ID "${cartID}" not found`)
385
- setCartID(undefined)
386
- setCart(undefined)
387
- return
388
- }
389
-
390
- // Check if the item already exists in the cart
391
- const existingItemIndex = existingCart.items.findIndex(
392
- (cartItem: CartItem) => cartItem.id === targetID,
393
- )
394
-
395
- const updatedItems = [...existingCart.items]
396
-
397
- if (existingItemIndex !== -1) {
398
- // If the item exists, decrement its quantity
399
- updatedItems[existingItemIndex].quantity = updatedItems[existingItemIndex].quantity - 1 // Decrement by 1
400
-
401
- // If the quantity reaches 0, remove the item from the cart
402
- if (updatedItems[existingItemIndex].quantity <= 0) {
403
- updatedItems.splice(existingItemIndex, 1)
404
- }
405
-
406
- // Update the cart with the new items
407
- await updateCart(cartID, {
408
- items: updatedItems,
409
- })
410
- }
411
- },
412
- [cartID, getCart, updateCart],
413
- )
414
-
415
- const clearCart: EcommerceContextType['clearCart'] = useCallback(async () => {
416
- if (cartID) {
417
- await deleteCart(cartID)
418
- }
419
- }, [cartID, deleteCart])
420
-
421
- const setCurrency: EcommerceContextType['setCurrency'] = useCallback(
422
- (currency) => {
423
- if (selectedCurrency.code === currency) {
424
- return
425
- }
426
-
427
- const foundCurrency = currenciesConfig.supportedCurrencies.find((c) => c.code === currency)
428
- if (!foundCurrency) {
429
- throw new Error(`Currency with code "${currency}" not found in config`)
430
- }
431
-
432
- setSelectedCurrency(foundCurrency)
433
- },
434
- [currenciesConfig.supportedCurrencies, selectedCurrency.code],
435
- )
436
-
437
- const initiatePayment = useCallback<EcommerceContextType['initiatePayment']>(
438
- async (paymentMethodID, options) => {
439
- const paymentMethod = paymentMethods.find((method) => method.name === paymentMethodID)
440
-
441
- if (!paymentMethod) {
442
- throw new Error(`Payment method with ID "${paymentMethodID}" not found`)
443
- }
444
-
445
- if (!cartID) {
446
- throw new Error(`No cart is provided.`)
447
- }
448
-
449
- setSelectedPaymentMethod(paymentMethodID)
450
-
451
- if (paymentMethod.initiatePayment) {
452
- const fetchURL = `${baseAPIURL}/payments/${paymentMethodID}/initiate`
453
-
454
- const data = {
455
- cartID,
456
- currency: selectedCurrency.code,
457
- }
458
-
459
- const response = await fetch(fetchURL, {
460
- body: JSON.stringify({
461
- ...data,
462
- ...(options?.additionalData || {}),
463
- }),
464
- credentials: 'include',
465
- headers: {
466
- 'Content-Type': 'application/json',
467
- },
468
- method: 'POST',
469
- })
470
-
471
- if (!response.ok) {
472
- const errorText = await response.text()
473
- throw new Error(`Failed to initiate payment: ${errorText}`)
474
- }
475
-
476
- const responseData = await response.json()
477
-
478
- if (responseData.error) {
479
- throw new Error(`Payment initiation error: ${responseData.error}`)
480
- }
481
-
482
- setPaymentData(responseData)
483
-
484
- return responseData
485
- } else {
486
- throw new Error(`Payment method "${paymentMethodID}" does not support payment initiation`)
487
- }
488
- },
489
- [baseAPIURL, cartID, paymentMethods, selectedCurrency.code],
490
- )
491
-
492
- const confirmOrder = useCallback<EcommerceContextType['initiatePayment']>(
493
- async (paymentMethodID, options) => {
494
- if (!cartID) {
495
- throw new Error(`Cart is empty.`)
496
- }
497
-
498
- const paymentMethod = paymentMethods.find((pm) => pm.name === paymentMethodID)
499
-
500
- if (!paymentMethod) {
501
- throw new Error(`Payment method with ID "${paymentMethodID}" not found`)
502
- }
503
-
504
- if (paymentMethod.confirmOrder) {
505
- const fetchURL = `${baseAPIURL}/payments/${paymentMethodID}/confirm-order`
506
-
507
- const data = {
508
- cartID,
509
- currency: selectedCurrency.code,
510
- ...paymentData,
511
- }
512
-
513
- const response = await fetch(fetchURL, {
514
- body: JSON.stringify({
515
- ...data,
516
- ...(options?.additionalData || {}),
517
- }),
518
- credentials: 'include',
519
- headers: {
520
- 'Content-Type': 'application/json',
521
- },
522
- method: 'POST',
523
- })
524
-
525
- if (!response.ok) {
526
- const errorText = await response.text()
527
- throw new Error(`Failed to confirm order: ${errorText}`)
528
- }
529
-
530
- const responseData = await response.json()
531
-
532
- if (responseData.error) {
533
- throw new Error(`Order confirmation error: ${responseData.error}`)
534
- }
535
-
536
- // Clear the payment data
537
- setPaymentData(undefined)
538
-
539
- return responseData
540
- } else {
541
- throw new Error(`Payment method "${paymentMethodID}" does not support order confirmation`)
542
- }
543
- },
544
- [baseAPIURL, cartID, paymentData, paymentMethods, selectedCurrency.code],
545
- )
546
-
547
- const getUser = useCallback(async () => {
548
- try {
549
- const query = qs.stringify({
550
- depth: 0,
551
- select: {
552
- id: true,
553
- carts: true,
554
- },
555
- })
556
-
557
- const response = await fetch(`${baseAPIURL}/${customersSlug}/me?${query}`, {
558
- credentials: 'include',
559
- headers: {
560
- 'Content-Type': 'application/json',
561
- },
562
- method: 'GET',
563
- })
564
-
565
- if (!response.ok) {
566
- const errorText = await response.text()
567
- throw new Error(`Failed to fetch user: ${errorText}`)
568
- }
569
-
570
- const userData = await response.json()
571
-
572
- if (userData.error) {
573
- throw new Error(`User fetch error: ${userData.error}`)
574
- }
575
-
576
- if (userData.user) {
577
- setUser(userData.user as TypedUser)
578
- return userData.user as TypedUser
579
- }
580
- } catch (error) {
581
- if (debug) {
582
- // eslint-disable-next-line no-console
583
- console.error('Error fetching user:', error)
584
- }
585
- setUser(null)
586
- throw new Error(
587
- `Failed to fetch user: ${error instanceof Error ? error.message : 'Unknown error'}`,
588
- )
589
- }
590
- }, [baseAPIURL, customersSlug, debug])
591
-
592
- const getAddresses = useCallback(async () => {
593
- if (!user) {
594
- return
595
- }
596
-
597
- try {
598
- const query = qs.stringify({
599
- depth: 0,
600
- limit: 0,
601
- pagination: false,
602
- })
603
-
604
- const response = await fetch(`${baseAPIURL}/${addressesSlug}?${query}`, {
605
- credentials: 'include',
606
- headers: {
607
- 'Content-Type': 'application/json',
608
- },
609
- method: 'GET',
610
- })
611
-
612
- if (!response.ok) {
613
- const errorText = await response.text()
614
-
615
- throw new Error(errorText)
616
- }
617
-
618
- const data = await response.json()
619
-
620
- if (data.error) {
621
- throw new Error(`Address fetch error: ${data.error}`)
622
- }
623
-
624
- if (data.docs && data.docs.length > 0) {
625
- setAddresses(data.docs)
626
- }
627
- } catch (error) {
628
- if (debug) {
629
- // eslint-disable-next-line no-console
630
- console.error('Error fetching addresses:', error)
631
- }
632
- setAddresses(undefined)
633
- throw new Error(
634
- `Failed to fetch addresses: ${error instanceof Error ? error.message : 'Unknown error'}`,
635
- )
636
- }
637
- }, [user, baseAPIURL, addressesSlug, debug])
638
-
639
- const updateAddress = useCallback<EcommerceContextType['updateAddress']>(
640
- async (addressID, address) => {
641
- if (!user) {
642
- throw new Error('User must be logged in to update or create an address')
643
- }
644
-
645
- try {
646
- const response = await fetch(`${baseAPIURL}/${addressesSlug}/${addressID}`, {
647
- body: JSON.stringify(address),
648
- credentials: 'include',
649
- headers: {
650
- 'Content-Type': 'application/json',
651
- },
652
- method: 'PATCH',
653
- })
654
-
655
- if (!response.ok) {
656
- const errorText = await response.text()
657
- throw new Error(`Failed to update or create address: ${errorText}`)
658
- }
659
-
660
- const data = await response.json()
661
-
662
- if (data.error) {
663
- throw new Error(`Address update/create error: ${data.error}`)
664
- }
665
-
666
- // Refresh addresses after updating or creating
667
- await getAddresses()
668
- } catch (error) {
669
- if (debug) {
670
- // eslint-disable-next-line no-console
671
- console.error('Error updating or creating address:', error)
672
- }
673
-
674
- throw new Error(
675
- `Failed to update or create address: ${error instanceof Error ? error.message : 'Unknown error'}`,
676
- )
677
- }
678
- },
679
- [user, baseAPIURL, addressesSlug, getAddresses, debug],
680
- )
681
-
682
- const createAddress = useCallback<EcommerceContextType['createAddress']>(
683
- async (address) => {
684
- if (!user) {
685
- throw new Error('User must be logged in to update or create an address')
686
- }
687
-
688
- try {
689
- const response = await fetch(`${baseAPIURL}/${addressesSlug}`, {
690
- body: JSON.stringify(address),
691
- credentials: 'include',
692
- headers: {
693
- 'Content-Type': 'application/json',
694
- },
695
- method: 'POST',
696
- })
697
-
698
- if (!response.ok) {
699
- const errorText = await response.text()
700
- throw new Error(`Failed to update or create address: ${errorText}`)
701
- }
702
-
703
- const data = await response.json()
704
-
705
- if (data.error) {
706
- throw new Error(`Address update/create error: ${data.error}`)
707
- }
708
-
709
- // Refresh addresses after updating or creating
710
- await getAddresses()
711
- } catch (error) {
712
- if (debug) {
713
- // eslint-disable-next-line no-console
714
- console.error('Error updating or creating address:', error)
715
- }
716
-
717
- throw new Error(
718
- `Failed to update or create address: ${error instanceof Error ? error.message : 'Unknown error'}`,
719
- )
720
- }
721
- },
722
- [user, baseAPIURL, addressesSlug, getAddresses, debug],
723
- )
724
-
725
- // If localStorage is enabled, we can add logic to persist the cart state
726
- useEffect(() => {
727
- if (!hasRendered.current) {
728
- if (syncLocalStorage) {
729
- const storedCart = localStorage.getItem(localStorageConfig.key)
730
- if (storedCart) {
731
- getCart(storedCart)
732
- .then((fetchedCart) => {
733
- setCart(fetchedCart)
734
- setCartID(storedCart as DefaultDocumentIDType)
735
- })
736
- .catch((_) => {
737
- // console.error('Error fetching cart from localStorage:', error)
738
- // If there's an error fetching the cart, we can clear it from localStorage
739
- localStorage.removeItem(localStorageConfig.key)
740
- setCartID(undefined)
741
- setCart(undefined)
742
- })
743
- }
744
- }
745
-
746
- hasRendered.current = true
747
-
748
- void getUser().then((user) => {
749
- if (user && user.cart?.docs && user.cart.docs.length > 0) {
750
- // If the user has carts, we can set the cartID to the first cart
751
- const cartID =
752
- typeof user.cart.docs[0] === 'object' ? user.cart.docs[0].id : user.cart.docs[0]
753
-
754
- if (cartID) {
755
- getCart(cartID)
756
- .then((fetchedCart) => {
757
- setCart(fetchedCart)
758
- setCartID(cartID)
759
- })
760
- .catch((error) => {
761
- if (debug) {
762
- // eslint-disable-next-line no-console
763
- console.error('Error fetching user cart:', error)
764
- }
765
-
766
- setCart(undefined)
767
- setCartID(undefined)
768
-
769
- throw new Error(`Failed to fetch user cart: ${error.message}`)
770
- })
771
- }
772
- }
773
- })
774
- }
775
- }, [debug, getAddresses, getCart, getUser, localStorageConfig.key, syncLocalStorage])
776
-
777
- useEffect(() => {
778
- if (user) {
779
- // If the user is logged in, fetch their addresses
780
- void getAddresses()
781
- } else {
782
- // If no user is logged in, clear addresses
783
- setAddresses(undefined)
784
- }
785
- }, [getAddresses, user])
786
-
787
- return (
788
- <EcommerceContext
789
- value={{
790
- addItem,
791
- addresses,
792
- cart,
793
- clearCart,
794
- confirmOrder,
795
- createAddress,
796
- currenciesConfig,
797
- currency: selectedCurrency,
798
- decrementItem,
799
- incrementItem,
800
- initiatePayment,
801
- paymentData,
802
- paymentMethods,
803
- removeItem,
804
- selectedPaymentMethod,
805
- setCurrency,
806
- updateAddress,
807
- }}
808
- >
809
- {children}
810
- </EcommerceContext>
811
- )
812
- }
813
-
814
- export const useEcommerce = () => {
815
- const context = use(EcommerceContext)
816
-
817
- if (!context) {
818
- throw new Error('useEcommerce must be used within an EcommerceProvider')
819
- }
820
-
821
- return context
822
- }
823
-
824
- export const useCurrency = () => {
825
- const { currenciesConfig, currency, setCurrency } = useEcommerce()
826
-
827
- const formatCurrency = useCallback(
828
- (value?: null | number, options?: { currency?: Currency }): string => {
829
- if (value === undefined || value === null) {
830
- return ''
831
- }
832
-
833
- const currencyToUse = options?.currency || currency
834
-
835
- if (!currencyToUse) {
836
- return value.toString()
837
- }
838
-
839
- if (value === 0) {
840
- return `${currencyToUse.symbol}0.${'0'.repeat(currencyToUse.decimals)}`
841
- }
842
-
843
- // Convert from base value (e.g., cents) to decimal value (e.g., dollars)
844
- const decimalValue = value / Math.pow(10, currencyToUse.decimals)
845
-
846
- // Format with the correct number of decimal places
847
- return `${currencyToUse.symbol}${decimalValue.toFixed(currencyToUse.decimals)}`
848
- },
849
- [currency],
850
- )
851
-
852
- if (!currency) {
853
- throw new Error('useCurrency must be used within an EcommerceProvider')
854
- }
855
-
856
- return {
857
- currency,
858
- formatCurrency,
859
- setCurrency,
860
- supportedCurrencies: currenciesConfig.supportedCurrencies,
861
- }
862
- }
863
-
864
- export const useCart = () => {
865
- const { addItem, cart, clearCart, decrementItem, incrementItem, removeItem } = useEcommerce()
866
-
867
- if (!addItem) {
868
- throw new Error('useCart must be used within an EcommerceProvider')
869
- }
870
-
871
- return { addItem, cart, clearCart, decrementItem, incrementItem, removeItem }
872
- }
873
-
874
- export const usePayments = () => {
875
- const { confirmOrder, initiatePayment, paymentData, paymentMethods, selectedPaymentMethod } =
876
- useEcommerce()
877
-
878
- if (!initiatePayment) {
879
- throw new Error('usePayments must be used within an EcommerceProvider')
880
- }
881
-
882
- return { confirmOrder, initiatePayment, paymentData, paymentMethods, selectedPaymentMethod }
883
- }
884
-
885
- export const useAddresses = () => {
886
- const { addresses, createAddress, updateAddress } = useEcommerce()
887
-
888
- if (!createAddress) {
889
- throw new Error('usePayments must be used within an EcommerceProvider')
890
- }
891
-
892
- return { addresses, createAddress, updateAddress }
893
- }