@pradip1995/commerce-core 1.0.0

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 (82) hide show
  1. package/README.md +15 -0
  2. package/package.json +70 -0
  3. package/src/analytics/ga4-ecommerce.ts +96 -0
  4. package/src/config.ts +36 -0
  5. package/src/constants.tsx +84 -0
  6. package/src/context/modal-context.tsx +40 -0
  7. package/src/context/wishlist-context.tsx +96 -0
  8. package/src/data/cart/abandoned.ts +111 -0
  9. package/src/data/cart/buyNow.ts +184 -0
  10. package/src/data/cart/checkout.ts +487 -0
  11. package/src/data/cart/index.ts +7 -0
  12. package/src/data/cart/mutations.ts +189 -0
  13. package/src/data/cart/promotions.ts +121 -0
  14. package/src/data/cart/region.ts +66 -0
  15. package/src/data/cart/retrieve.ts +162 -0
  16. package/src/data/categories.ts +90 -0
  17. package/src/data/collections.ts +109 -0
  18. package/src/data/contact.ts +143 -0
  19. package/src/data/cookies.ts +170 -0
  20. package/src/data/customer-registration.ts +365 -0
  21. package/src/data/customer.ts +638 -0
  22. package/src/data/dynamic-config.ts +420 -0
  23. package/src/data/fulfillment.ts +95 -0
  24. package/src/data/guest.ts +357 -0
  25. package/src/data/locale-actions.ts +74 -0
  26. package/src/data/locales.ts +28 -0
  27. package/src/data/newsletter.ts +41 -0
  28. package/src/data/notifications.ts +22 -0
  29. package/src/data/onboarding.ts +9 -0
  30. package/src/data/orders.ts +500 -0
  31. package/src/data/payment-details.ts +68 -0
  32. package/src/data/payment.ts +32 -0
  33. package/src/data/products.ts +424 -0
  34. package/src/data/regions.ts +64 -0
  35. package/src/data/returns.ts +305 -0
  36. package/src/data/reviews.ts +279 -0
  37. package/src/data/swaps.ts +154 -0
  38. package/src/data/variants.ts +38 -0
  39. package/src/data/wishlist.ts +292 -0
  40. package/src/domain/cart/abandoned-carts.ts +49 -0
  41. package/src/domain/cart/buy-now.ts +15 -0
  42. package/src/domain/cart/checkout.ts +25 -0
  43. package/src/domain/cart/index.ts +8 -0
  44. package/src/domain/cart/metadata.ts +21 -0
  45. package/src/domain/cart/payment.ts +21 -0
  46. package/src/domain/cart/phone.ts +17 -0
  47. package/src/domain/cart/reorder.ts +19 -0
  48. package/src/domain/cart/validation.ts +43 -0
  49. package/src/domain/product/pricing.ts +49 -0
  50. package/src/domain/product/variant-selection.ts +193 -0
  51. package/src/firebase.ts +48 -0
  52. package/src/hooks/index.ts +8 -0
  53. package/src/hooks/use-add-to-cart.ts +63 -0
  54. package/src/hooks/use-cart.ts +132 -0
  55. package/src/hooks/use-checkout.ts +62 -0
  56. package/src/hooks/use-in-view.tsx +29 -0
  57. package/src/hooks/use-product-actions.ts +190 -0
  58. package/src/hooks/use-product-reviews.ts +18 -0
  59. package/src/hooks/use-product-variant.ts +142 -0
  60. package/src/hooks/use-server-action.ts +30 -0
  61. package/src/hooks/use-toggle-state.tsx +46 -0
  62. package/src/hooks/use-wishlist.ts +3 -0
  63. package/src/theme/inline-vars.ts +12 -0
  64. package/src/types/account.ts +21 -0
  65. package/src/types/cart.ts +13 -0
  66. package/src/types/home.ts +52 -0
  67. package/src/types/layout.ts +29 -0
  68. package/src/types/product-card.ts +17 -0
  69. package/src/util/compare-addresses.ts +28 -0
  70. package/src/util/env.ts +3 -0
  71. package/src/util/get-locale-header.ts +8 -0
  72. package/src/util/get-percentage-diff.ts +6 -0
  73. package/src/util/get-product-price.ts +78 -0
  74. package/src/util/google-oauth.ts +28 -0
  75. package/src/util/isEmpty.ts +11 -0
  76. package/src/util/medusa-error.ts +18 -0
  77. package/src/util/money.ts +26 -0
  78. package/src/util/order-status.tsx +179 -0
  79. package/src/util/product.ts +431 -0
  80. package/src/util/repeat.ts +5 -0
  81. package/src/util/returns.ts +71 -0
  82. package/src/util/sort-products.ts +48 -0
@@ -0,0 +1,189 @@
1
+ "use server"
2
+
3
+ import { sdk } from "@core/config"
4
+ import medusaError from "@core/util/medusa-error"
5
+ import { HttpTypes } from "@medusajs/types"
6
+ import { revalidateTag, revalidatePath } from "next/cache"
7
+ import { redirect } from "next/navigation"
8
+ import { headers as getRequestHeaders } from "next/headers"
9
+ import {
10
+ getAuthHeaders,
11
+ getCacheOptions,
12
+ getCacheTag,
13
+ getCartId,
14
+ removeCartId,
15
+ setCartId,
16
+ getHoldCartId,
17
+ setHoldCartId,
18
+ removeHoldCartId,
19
+ setBuyNowCartId,
20
+ removeBuyNowCartId,
21
+ } from "../cookies"
22
+ import { getRegion } from "../regions"
23
+ import { getLocale } from "@core/data/locale-actions"
24
+ import { retrieveCustomer, transferCart } from "@core/data/customer"
25
+ import { cache } from "react"
26
+ import {
27
+ createBuyNowMetadata,
28
+ filterOutCurrentCart,
29
+ getBuyNowCheckoutUrl,
30
+ getPostOrderCookieAction,
31
+ getResumeAbandonedCheckoutUrl,
32
+ isEphemeralCart,
33
+ normalizePhoneForRazorpay,
34
+ partitionAbandonedCarts,
35
+ shouldCheckEphemeralCartMetadata,
36
+ shouldSkipToPayment,
37
+ validateAddToCartInput,
38
+ validateCartId,
39
+ validateLineItemDeleteInput,
40
+ validateLineItemUpdateInput,
41
+ validateRazorpayPreflight,
42
+ } from "@core/domain/cart"
43
+ import { getOrSetCart } from "./retrieve"
44
+
45
+ export async function updateCart(data: HttpTypes.StoreUpdateCart) {
46
+ const cartId = await getCartId()
47
+
48
+ if (!cartId) {
49
+ throw new Error("No existing cart found, please create one before updating")
50
+ }
51
+
52
+ const headers = {
53
+ ...(await getAuthHeaders()),
54
+ }
55
+
56
+ return sdk.store.cart
57
+ .update(cartId, data, {}, headers)
58
+ .then(async ({ cart }: { cart: HttpTypes.StoreCart }) => {
59
+ const cartCacheTag = await getCacheTag("carts")
60
+ revalidateTag(cartCacheTag)
61
+
62
+ const fulfillmentCacheTag = await getCacheTag("fulfillment")
63
+ revalidateTag(fulfillmentCacheTag)
64
+
65
+ return cart
66
+ })
67
+ .catch(medusaError)
68
+ }
69
+
70
+ export async function addToCart({
71
+ variantId,
72
+ quantity,
73
+ countryCode,
74
+ }: {
75
+ variantId: string
76
+ quantity: number
77
+ countryCode: string
78
+ }) {
79
+ validateAddToCartInput({ variantId, quantity })
80
+
81
+ const cart = await getOrSetCart(countryCode)
82
+
83
+ if (!cart) {
84
+ throw new Error("Error retrieving or creating cart")
85
+ }
86
+
87
+ const headers = {
88
+ ...(await getAuthHeaders()),
89
+ }
90
+
91
+ await sdk.store.cart
92
+ .createLineItem(
93
+ cart.id,
94
+ {
95
+ variant_id: variantId,
96
+ quantity,
97
+ },
98
+ {},
99
+ headers
100
+ )
101
+ .then(async () => {
102
+ const cartCacheTag = await getCacheTag("carts")
103
+ revalidateTag(cartCacheTag)
104
+
105
+ const fulfillmentCacheTag = await getCacheTag("fulfillment")
106
+ revalidateTag(fulfillmentCacheTag)
107
+ })
108
+ .catch(medusaError)
109
+ }
110
+
111
+ export async function updateLineItem({
112
+ lineId,
113
+ quantity,
114
+ }: {
115
+ lineId: string
116
+ quantity: number
117
+ }) {
118
+ validateLineItemUpdateInput({ lineId, quantity })
119
+
120
+ const cartId = await getCartId()
121
+ validateCartId(cartId)
122
+
123
+ const headers = {
124
+ ...(await getAuthHeaders()),
125
+ }
126
+
127
+ await sdk.store.cart
128
+ .updateLineItem(cartId!, lineId, { quantity }, {}, headers)
129
+ .then(async () => {
130
+ const cartCacheTag = await getCacheTag("carts")
131
+ revalidateTag(cartCacheTag)
132
+
133
+ const fulfillmentCacheTag = await getCacheTag("fulfillment")
134
+ revalidateTag(fulfillmentCacheTag)
135
+ })
136
+ .catch(medusaError)
137
+ }
138
+
139
+ export async function deleteLineItem(lineId: string) {
140
+ validateLineItemDeleteInput(lineId)
141
+
142
+ const cartId = await getCartId()
143
+ validateCartId(cartId)
144
+
145
+ const headers = {
146
+ ...(await getAuthHeaders()),
147
+ }
148
+
149
+ await sdk.store.cart
150
+ .deleteLineItem(cartId!, lineId, {}, headers)
151
+ .then(async () => {
152
+ const cartCacheTag = await getCacheTag("carts")
153
+ revalidateTag(cartCacheTag)
154
+
155
+ const fulfillmentCacheTag = await getCacheTag("fulfillment")
156
+ revalidateTag(fulfillmentCacheTag)
157
+ })
158
+ .catch(medusaError)
159
+ }
160
+
161
+ export async function updateLineItemVariant({
162
+ lineId,
163
+ variantId,
164
+ quantity,
165
+ countryCode,
166
+ }: {
167
+ lineId: string
168
+ variantId: string
169
+ quantity: number
170
+ countryCode: string
171
+ }) {
172
+ if (!lineId) {
173
+ throw new Error("Missing lineItem ID when updating variant")
174
+ }
175
+
176
+ if (!variantId) {
177
+ throw new Error("Missing variant ID when updating variant")
178
+ }
179
+
180
+ // Delete the old line item
181
+ await deleteLineItem(lineId)
182
+
183
+ // Add new line item with the new variant
184
+ await addToCart({
185
+ variantId,
186
+ quantity,
187
+ countryCode,
188
+ })
189
+ }
@@ -0,0 +1,121 @@
1
+ "use server"
2
+
3
+ import { sdk } from "@core/config"
4
+ import medusaError from "@core/util/medusa-error"
5
+ import { HttpTypes } from "@medusajs/types"
6
+ import { revalidateTag, revalidatePath } from "next/cache"
7
+ import { redirect } from "next/navigation"
8
+ import { headers as getRequestHeaders } from "next/headers"
9
+ import {
10
+ getAuthHeaders,
11
+ getCacheOptions,
12
+ getCacheTag,
13
+ getCartId,
14
+ removeCartId,
15
+ setCartId,
16
+ getHoldCartId,
17
+ setHoldCartId,
18
+ removeHoldCartId,
19
+ setBuyNowCartId,
20
+ removeBuyNowCartId,
21
+ } from "../cookies"
22
+ import { getRegion } from "../regions"
23
+ import { getLocale } from "@core/data/locale-actions"
24
+ import { retrieveCustomer, transferCart } from "@core/data/customer"
25
+ import { cache } from "react"
26
+ import {
27
+ createBuyNowMetadata,
28
+ filterOutCurrentCart,
29
+ getBuyNowCheckoutUrl,
30
+ getPostOrderCookieAction,
31
+ getResumeAbandonedCheckoutUrl,
32
+ isEphemeralCart,
33
+ normalizePhoneForRazorpay,
34
+ partitionAbandonedCarts,
35
+ shouldCheckEphemeralCartMetadata,
36
+ shouldSkipToPayment,
37
+ validateAddToCartInput,
38
+ validateCartId,
39
+ validateLineItemDeleteInput,
40
+ validateLineItemUpdateInput,
41
+ validateRazorpayPreflight,
42
+ } from "@core/domain/cart"
43
+
44
+ export async function applyPromotions(codes: string[]) {
45
+ const cartId = await getCartId()
46
+
47
+ if (!cartId) {
48
+ throw new Error("No existing cart found")
49
+ }
50
+
51
+ const headers = {
52
+ ...(await getAuthHeaders()),
53
+ }
54
+
55
+ return sdk.store.cart
56
+ .update(cartId, { promo_codes: codes }, {}, headers)
57
+ .then(async ({ cart }) => {
58
+ const cartCacheTag = await getCacheTag("carts")
59
+ revalidateTag(cartCacheTag)
60
+
61
+ const fulfillmentCacheTag = await getCacheTag("fulfillment")
62
+ revalidateTag(fulfillmentCacheTag)
63
+
64
+ return cart
65
+ })
66
+ .catch(medusaError)
67
+ }
68
+
69
+ export async function applyGiftCard(code: string) {
70
+ // const cartId = getCartId()
71
+ // if (!cartId) return "No cartId cookie found"
72
+ // try {
73
+ // await updateCart(cartId, { gift_cards: [{ code }] }).then(() => {
74
+ // revalidateTag("cart")
75
+ // })
76
+ // } catch (error: any) {
77
+ // throw error
78
+ // }
79
+ }
80
+
81
+ export async function removeDiscount(code: string) {
82
+ // const cartId = getCartId()
83
+ // if (!cartId) return "No cartId cookie found"
84
+ // try {
85
+ // await deleteDiscount(cartId, code)
86
+ // revalidateTag("cart")
87
+ // } catch (error: any) {
88
+ // throw error
89
+ // }
90
+ }
91
+
92
+ export async function removeGiftCard(
93
+ codeToRemove: string,
94
+ giftCards: any[]
95
+ // giftCards: GiftCard[]
96
+ ) {
97
+ // const cartId = getCartId()
98
+ // if (!cartId) return "No cartId cookie found"
99
+ // try {
100
+ // await updateCart(cartId, {
101
+ // gift_cards: [...giftCards]
102
+ // .filter((gc) => gc.code !== codeToRemove)
103
+ // .map((gc) => ({ code: gc.code })),
104
+ // }).then(() => {
105
+ // revalidateTag("cart")
106
+ // })
107
+ // } catch (error: any) {
108
+ // throw error
109
+ // }
110
+ }
111
+
112
+ export async function submitPromotionForm(currentState: unknown, formData: FormData) {
113
+ const code = formData.get("code") as string
114
+ try {
115
+ await applyPromotions([code])
116
+ } catch (e: any) {
117
+ return e.message
118
+ }
119
+ }
120
+
121
+ // TODO: Pass a POJO instead of a form entity here
@@ -0,0 +1,66 @@
1
+ "use server"
2
+
3
+ import { sdk } from "@core/config"
4
+ import medusaError from "@core/util/medusa-error"
5
+ import { HttpTypes } from "@medusajs/types"
6
+ import { revalidateTag, revalidatePath } from "next/cache"
7
+ import { redirect } from "next/navigation"
8
+ import { headers as getRequestHeaders } from "next/headers"
9
+ import {
10
+ getAuthHeaders,
11
+ getCacheOptions,
12
+ getCacheTag,
13
+ getCartId,
14
+ removeCartId,
15
+ setCartId,
16
+ getHoldCartId,
17
+ setHoldCartId,
18
+ removeHoldCartId,
19
+ setBuyNowCartId,
20
+ removeBuyNowCartId,
21
+ } from "../cookies"
22
+ import { getRegion } from "../regions"
23
+ import { getLocale } from "@core/data/locale-actions"
24
+ import { retrieveCustomer, transferCart } from "@core/data/customer"
25
+ import { cache } from "react"
26
+ import {
27
+ createBuyNowMetadata,
28
+ filterOutCurrentCart,
29
+ getBuyNowCheckoutUrl,
30
+ getPostOrderCookieAction,
31
+ getResumeAbandonedCheckoutUrl,
32
+ isEphemeralCart,
33
+ normalizePhoneForRazorpay,
34
+ partitionAbandonedCarts,
35
+ shouldCheckEphemeralCartMetadata,
36
+ shouldSkipToPayment,
37
+ validateAddToCartInput,
38
+ validateCartId,
39
+ validateLineItemDeleteInput,
40
+ validateLineItemUpdateInput,
41
+ validateRazorpayPreflight,
42
+ } from "@core/domain/cart"
43
+ import { updateCart } from "./mutations"
44
+
45
+ export async function updateRegion(countryCode: string, currentPath: string) {
46
+ const cartId = await getCartId()
47
+ const region = await getRegion(countryCode)
48
+
49
+ if (!region) {
50
+ throw new Error(`Region not found for country code: ${countryCode}`)
51
+ }
52
+
53
+ if (cartId) {
54
+ await updateCart({ region_id: region.id })
55
+ const cartCacheTag = await getCacheTag("carts")
56
+ revalidateTag(cartCacheTag)
57
+ }
58
+
59
+ const regionCacheTag = await getCacheTag("regions")
60
+ revalidateTag(regionCacheTag)
61
+
62
+ const productsCacheTag = await getCacheTag("products")
63
+ revalidateTag(productsCacheTag)
64
+
65
+ redirect(`/${countryCode}${currentPath}`)
66
+ }
@@ -0,0 +1,162 @@
1
+ "use server"
2
+
3
+ import { sdk } from "@core/config"
4
+ import medusaError from "@core/util/medusa-error"
5
+ import { HttpTypes } from "@medusajs/types"
6
+ import { revalidateTag, revalidatePath } from "next/cache"
7
+ import { redirect } from "next/navigation"
8
+ import { headers as getRequestHeaders } from "next/headers"
9
+ import {
10
+ getAuthHeaders,
11
+ getCacheOptions,
12
+ getCacheTag,
13
+ getCartId,
14
+ removeCartId,
15
+ setCartId,
16
+ getHoldCartId,
17
+ setHoldCartId,
18
+ removeHoldCartId,
19
+ setBuyNowCartId,
20
+ removeBuyNowCartId,
21
+ } from "../cookies"
22
+ import { getRegion } from "../regions"
23
+ import { getLocale } from "@core/data/locale-actions"
24
+ import { retrieveCustomer, transferCart } from "@core/data/customer"
25
+ import { cache } from "react"
26
+ import {
27
+ createBuyNowMetadata,
28
+ filterOutCurrentCart,
29
+ getBuyNowCheckoutUrl,
30
+ getPostOrderCookieAction,
31
+ getResumeAbandonedCheckoutUrl,
32
+ isEphemeralCart,
33
+ normalizePhoneForRazorpay,
34
+ partitionAbandonedCarts,
35
+ shouldCheckEphemeralCartMetadata,
36
+ shouldSkipToPayment,
37
+ validateAddToCartInput,
38
+ validateCartId,
39
+ validateLineItemDeleteInput,
40
+ validateLineItemUpdateInput,
41
+ validateRazorpayPreflight,
42
+ } from "@core/domain/cart"
43
+
44
+ export const retrieveCart = cache(async (cartId?: string, fields?: string) => {
45
+ let id = cartId || (await getCartId())
46
+ const heldId = await getHoldCartId()
47
+
48
+ // AUTO-RESTORE LOGIC:
49
+ // If we're not explicitly asking for a specific cartId and we have an active cart
50
+ if (!cartId && id) {
51
+ try {
52
+ const headerList = await getRequestHeaders()
53
+ const pathname = headerList.get("x-pathname") || headerList.get("referer") || ""
54
+
55
+ if (shouldCheckEphemeralCartMetadata({ explicitCartId: cartId, pathname })) {
56
+ const currentCart = await sdk.client
57
+ .fetch<HttpTypes.StoreCartResponse>(`/store/carts/${id}`, {
58
+ method: "GET",
59
+ query: { fields: "metadata" },
60
+ headers: await getAuthHeaders(),
61
+ cache: "no-store",
62
+ })
63
+ .then(({ cart }) => cart)
64
+ .catch(() => null)
65
+
66
+ if (isEphemeralCart(currentCart?.metadata)) {
67
+ await removeBuyNowCartId()
68
+ id = await getCartId() // Re-fetch ID which will now fall back to the real main cart
69
+ revalidateTag("carts")
70
+ }
71
+ }
72
+ } catch (e) {
73
+ // Silent fail
74
+ }
75
+ }
76
+
77
+ fields ??=
78
+ "*items, *region, *items.product, *items.product.thumbnail, *items.product.images, *items.product.options, *items.product.variants, +items.product.variants.inventory_quantity, +items.product.variants.manage_inventory, +items.product.variants.allow_backorder, *items.product.variants.options, *items.variant, +items.variant.inventory_quantity, +items.variant.manage_inventory, +items.variant.allow_backorder, *items.variant.images, *items.variant.product, *items.variant.product.thumbnail, *items.variant.product.images, *items.variant.options, *items.thumbnail, *items.metadata, +items.total, +items.adjustments, *promotions, +shipping_methods.name, +shipping_methods.adjustments"
79
+
80
+ if (!id) {
81
+ return null
82
+ }
83
+
84
+ const headers = {
85
+ ...(await getAuthHeaders()),
86
+ }
87
+
88
+ const next = {
89
+ ...(await getCacheOptions("carts")),
90
+ }
91
+
92
+ return await sdk.client
93
+ .fetch<HttpTypes.StoreCartResponse>(`/store/carts/${id}`, {
94
+ method: "GET",
95
+ query: {
96
+ fields,
97
+ },
98
+ headers,
99
+ next,
100
+ cache: "no-store",
101
+ })
102
+ .then(({ cart }: { cart: HttpTypes.StoreCart }) => {
103
+ return cart
104
+ })
105
+ .catch(() => null)
106
+ })
107
+
108
+ export async function getOrSetCart(countryCode: string) {
109
+ const region = await getRegion(countryCode)
110
+
111
+ if (!region) {
112
+ throw new Error(`Region not found for country code: ${countryCode}`)
113
+ }
114
+
115
+ let cart = await retrieveCart(undefined, "id,region_id")
116
+
117
+ const headers = {
118
+ ...(await getAuthHeaders()),
119
+ }
120
+
121
+ if (!cart) {
122
+ const locale = await getLocale()
123
+ const cartResp = await sdk.store.cart.create(
124
+ { region_id: region.id, locale: locale || undefined },
125
+ {},
126
+ headers
127
+ )
128
+ cart = cartResp.cart
129
+
130
+ await setCartId(cart.id)
131
+
132
+ const cartCacheTag = await getCacheTag("carts")
133
+ revalidateTag(cartCacheTag)
134
+ }
135
+
136
+ if (cart && cart?.region_id !== region.id) {
137
+ await sdk.store.cart.update(cart.id, { region_id: region.id }, {}, headers)
138
+ const cartCacheTag = await getCacheTag("carts")
139
+ revalidateTag(cartCacheTag)
140
+ }
141
+
142
+ return cart
143
+ }
144
+
145
+ export async function listCartOptions() {
146
+ const cartId = await getCartId()
147
+ const headers = {
148
+ ...(await getAuthHeaders()),
149
+ }
150
+ const next = {
151
+ ...(await getCacheOptions("shippingOptions")),
152
+ }
153
+
154
+ return await sdk.client.fetch<{
155
+ shipping_options: HttpTypes.StoreCartShippingOption[]
156
+ }>("/store/shipping-options", {
157
+ query: { cart_id: cartId },
158
+ next,
159
+ headers,
160
+ cache: "force-cache",
161
+ })
162
+ }
@@ -0,0 +1,90 @@
1
+ import { sdk } from "@core/config"
2
+ import { HttpTypes } from "@medusajs/types"
3
+ import { getCacheOptions } from "./cookies"
4
+
5
+ export const listCategories = async (query?: Record<string, any>) => {
6
+ try {
7
+ const next = {
8
+ ...(await getCacheOptions("categories")),
9
+ }
10
+
11
+ const limit = query?.limit || 100
12
+
13
+ return sdk.client
14
+ .fetch<{ product_categories: HttpTypes.StoreProductCategory[] }>(
15
+ "/store/product-categories",
16
+ {
17
+ query: {
18
+ fields:
19
+ "*category_children, *products, *parent_category, *parent_category.parent_category",
20
+ limit,
21
+ ...query,
22
+ },
23
+ next: {
24
+ ...next,
25
+ revalidate: 0,
26
+ },
27
+ cache: "no-store",
28
+ }
29
+ )
30
+ .then(({ product_categories }) => product_categories)
31
+ .catch((error) => {
32
+ if (process.env.NODE_ENV === "production" || process.env.CI) {
33
+ return []
34
+ }
35
+ throw error
36
+ })
37
+ } catch (error: any) {
38
+ if (process.env.NODE_ENV === "production" || process.env.CI) {
39
+ return []
40
+ }
41
+ throw error
42
+ }
43
+ }
44
+
45
+ export const getCategoryByHandle = async (categoryHandle: string[]) => {
46
+ try {
47
+ const handle = `${categoryHandle.join("/")}`
48
+ const decodedHandle = decodeURIComponent(handle)
49
+
50
+ const next = {
51
+ ...(await getCacheOptions("categories")),
52
+ }
53
+
54
+ const result = await sdk.client.fetch<HttpTypes.StoreProductCategoryListResponse>(
55
+ `/store/product-categories`,
56
+ {
57
+ query: {
58
+ handle: [decodedHandle],
59
+ },
60
+ next: {
61
+ ...next,
62
+ revalidate: 0,
63
+ },
64
+ cache: "no-store",
65
+ }
66
+ )
67
+
68
+ if (result.product_categories && result.product_categories.length > 0) {
69
+ return result.product_categories[0]
70
+ }
71
+
72
+ // Fallback: Fetch all categories and find manually
73
+
74
+ const allCategories = await listCategories({ limit: "200" })
75
+ const matchedCategory = allCategories.find(
76
+ (c) =>
77
+ c.handle === decodedHandle ||
78
+ c.handle === handle ||
79
+ c.handle?.replace(/-/g, " ") === decodedHandle
80
+ )
81
+
82
+ if (matchedCategory) {
83
+ return matchedCategory
84
+ }
85
+
86
+ return null
87
+ } catch (error: any) {
88
+ return null
89
+ }
90
+ }