@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.
- package/README.md +15 -0
- package/package.json +70 -0
- package/src/analytics/ga4-ecommerce.ts +96 -0
- package/src/config.ts +36 -0
- package/src/constants.tsx +84 -0
- package/src/context/modal-context.tsx +40 -0
- package/src/context/wishlist-context.tsx +96 -0
- package/src/data/cart/abandoned.ts +111 -0
- package/src/data/cart/buyNow.ts +184 -0
- package/src/data/cart/checkout.ts +487 -0
- package/src/data/cart/index.ts +7 -0
- package/src/data/cart/mutations.ts +189 -0
- package/src/data/cart/promotions.ts +121 -0
- package/src/data/cart/region.ts +66 -0
- package/src/data/cart/retrieve.ts +162 -0
- package/src/data/categories.ts +90 -0
- package/src/data/collections.ts +109 -0
- package/src/data/contact.ts +143 -0
- package/src/data/cookies.ts +170 -0
- package/src/data/customer-registration.ts +365 -0
- package/src/data/customer.ts +638 -0
- package/src/data/dynamic-config.ts +420 -0
- package/src/data/fulfillment.ts +95 -0
- package/src/data/guest.ts +357 -0
- package/src/data/locale-actions.ts +74 -0
- package/src/data/locales.ts +28 -0
- package/src/data/newsletter.ts +41 -0
- package/src/data/notifications.ts +22 -0
- package/src/data/onboarding.ts +9 -0
- package/src/data/orders.ts +500 -0
- package/src/data/payment-details.ts +68 -0
- package/src/data/payment.ts +32 -0
- package/src/data/products.ts +424 -0
- package/src/data/regions.ts +64 -0
- package/src/data/returns.ts +305 -0
- package/src/data/reviews.ts +279 -0
- package/src/data/swaps.ts +154 -0
- package/src/data/variants.ts +38 -0
- package/src/data/wishlist.ts +292 -0
- package/src/domain/cart/abandoned-carts.ts +49 -0
- package/src/domain/cart/buy-now.ts +15 -0
- package/src/domain/cart/checkout.ts +25 -0
- package/src/domain/cart/index.ts +8 -0
- package/src/domain/cart/metadata.ts +21 -0
- package/src/domain/cart/payment.ts +21 -0
- package/src/domain/cart/phone.ts +17 -0
- package/src/domain/cart/reorder.ts +19 -0
- package/src/domain/cart/validation.ts +43 -0
- package/src/domain/product/pricing.ts +49 -0
- package/src/domain/product/variant-selection.ts +193 -0
- package/src/firebase.ts +48 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/use-add-to-cart.ts +63 -0
- package/src/hooks/use-cart.ts +132 -0
- package/src/hooks/use-checkout.ts +62 -0
- package/src/hooks/use-in-view.tsx +29 -0
- package/src/hooks/use-product-actions.ts +190 -0
- package/src/hooks/use-product-reviews.ts +18 -0
- package/src/hooks/use-product-variant.ts +142 -0
- package/src/hooks/use-server-action.ts +30 -0
- package/src/hooks/use-toggle-state.tsx +46 -0
- package/src/hooks/use-wishlist.ts +3 -0
- package/src/theme/inline-vars.ts +12 -0
- package/src/types/account.ts +21 -0
- package/src/types/cart.ts +13 -0
- package/src/types/home.ts +52 -0
- package/src/types/layout.ts +29 -0
- package/src/types/product-card.ts +17 -0
- package/src/util/compare-addresses.ts +28 -0
- package/src/util/env.ts +3 -0
- package/src/util/get-locale-header.ts +8 -0
- package/src/util/get-percentage-diff.ts +6 -0
- package/src/util/get-product-price.ts +78 -0
- package/src/util/google-oauth.ts +28 -0
- package/src/util/isEmpty.ts +11 -0
- package/src/util/medusa-error.ts +18 -0
- package/src/util/money.ts +26 -0
- package/src/util/order-status.tsx +179 -0
- package/src/util/product.ts +431 -0
- package/src/util/repeat.ts +5 -0
- package/src/util/returns.ts +71 -0
- package/src/util/sort-products.ts +48 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use server"
|
|
2
|
+
|
|
3
|
+
import { sdk } from "@core/config"
|
|
4
|
+
import { HttpTypes } from "@medusajs/types"
|
|
5
|
+
import { getCacheOptions } from "./cookies"
|
|
6
|
+
|
|
7
|
+
export const retrieveCollection = async (id: string) => {
|
|
8
|
+
const next = {
|
|
9
|
+
...(await getCacheOptions("collections")),
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return sdk.client
|
|
13
|
+
.fetch<{ collection: HttpTypes.StoreCollection }>(`/store/collections/${id}`, {
|
|
14
|
+
next: {
|
|
15
|
+
...next,
|
|
16
|
+
revalidate: 0,
|
|
17
|
+
},
|
|
18
|
+
cache: "no-store",
|
|
19
|
+
})
|
|
20
|
+
.then(({ collection }) => collection)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const listCollections = async (
|
|
24
|
+
queryParams: Record<string, string> = {}
|
|
25
|
+
): Promise<{ collections: HttpTypes.StoreCollection[]; count: number }> => {
|
|
26
|
+
try {
|
|
27
|
+
const next = {
|
|
28
|
+
...(await getCacheOptions("collections")),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
queryParams.limit = queryParams.limit || "100"
|
|
32
|
+
queryParams.offset = queryParams.offset || "0"
|
|
33
|
+
|
|
34
|
+
return sdk.client
|
|
35
|
+
.fetch<{ collections: HttpTypes.StoreCollection[]; count: number }>(
|
|
36
|
+
"/store/collections",
|
|
37
|
+
{
|
|
38
|
+
query: queryParams,
|
|
39
|
+
next: {
|
|
40
|
+
...next,
|
|
41
|
+
revalidate: 0,
|
|
42
|
+
},
|
|
43
|
+
cache: "no-store",
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
.then(({ collections }) => ({ collections, count: collections.length }))
|
|
47
|
+
.catch((error) => {
|
|
48
|
+
if (process.env.NODE_ENV === "production" || process.env.CI) {
|
|
49
|
+
return { collections: [], count: 0 }
|
|
50
|
+
}
|
|
51
|
+
throw error
|
|
52
|
+
})
|
|
53
|
+
} catch (error: any) {
|
|
54
|
+
if (process.env.NODE_ENV === "production" || process.env.CI) {
|
|
55
|
+
return { collections: [], count: 0 }
|
|
56
|
+
}
|
|
57
|
+
throw error
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const getCollectionByHandle = async (
|
|
62
|
+
handle: string
|
|
63
|
+
): Promise<HttpTypes.StoreCollection | null> => {
|
|
64
|
+
try {
|
|
65
|
+
const next = {
|
|
66
|
+
...(await getCacheOptions("collections")),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Ensure handle is decoded
|
|
70
|
+
const decodedHandle = decodeURIComponent(handle)
|
|
71
|
+
|
|
72
|
+
// Try array-based handle filter (standard for v2)
|
|
73
|
+
const result = await sdk.client.fetch<HttpTypes.StoreCollectionListResponse>(
|
|
74
|
+
`/store/collections`,
|
|
75
|
+
{
|
|
76
|
+
query: {
|
|
77
|
+
handle: [decodedHandle],
|
|
78
|
+
},
|
|
79
|
+
next: {
|
|
80
|
+
...next,
|
|
81
|
+
revalidate: 0,
|
|
82
|
+
},
|
|
83
|
+
cache: "no-store",
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if (result.collections && result.collections.length > 0) {
|
|
88
|
+
return result.collections[0]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Fallback: Fetch all and find manually (useful for handles with spaces/special chars)
|
|
92
|
+
|
|
93
|
+
const { collections } = await listCollections({ limit: "100" })
|
|
94
|
+
const matchedCollection = collections.find(
|
|
95
|
+
(c) =>
|
|
96
|
+
c.handle === decodedHandle ||
|
|
97
|
+
c.handle === handle ||
|
|
98
|
+
c.handle?.replace(/-/g, " ") === decodedHandle
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if (matchedCollection) {
|
|
102
|
+
return matchedCollection
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return null
|
|
106
|
+
} catch (error: any) {
|
|
107
|
+
return null
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use server"
|
|
2
|
+
|
|
3
|
+
const getBaseUrl = () => {
|
|
4
|
+
return process.env.MEDUSA_BACKEND_URL || "http://localhost:9000"
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const getPublishableKey = () => {
|
|
8
|
+
return process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Submit a contact request
|
|
13
|
+
*/
|
|
14
|
+
export async function submitContactRequest({
|
|
15
|
+
email,
|
|
16
|
+
payload,
|
|
17
|
+
metadata,
|
|
18
|
+
source = "storefront",
|
|
19
|
+
}: {
|
|
20
|
+
email: string
|
|
21
|
+
payload: {
|
|
22
|
+
full_name?: string
|
|
23
|
+
phone?: string
|
|
24
|
+
topic?: string
|
|
25
|
+
subject?: string
|
|
26
|
+
message: string
|
|
27
|
+
}
|
|
28
|
+
metadata?: Record<string, any>
|
|
29
|
+
source?: string
|
|
30
|
+
}) {
|
|
31
|
+
try {
|
|
32
|
+
const publishableKey = getPublishableKey()
|
|
33
|
+
const baseUrl = getBaseUrl()
|
|
34
|
+
|
|
35
|
+
if (!publishableKey) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: "Configuration error: Publishable API key is missing.",
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const headers: Record<string, string> = {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
"x-publishable-api-key": publishableKey,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const requestBody: any = {
|
|
48
|
+
email,
|
|
49
|
+
payload,
|
|
50
|
+
source,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (metadata) {
|
|
54
|
+
requestBody.metadata = metadata
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const response = await fetch(`${baseUrl}/store/contact-requests`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers,
|
|
60
|
+
body: JSON.stringify(requestBody),
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
const error = await response.json().catch(() => ({ message: "Unknown error" }))
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error:
|
|
68
|
+
error.message || `HTTP ${response.status}: Failed to submit contact request`,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const data = await response.json()
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
data,
|
|
76
|
+
}
|
|
77
|
+
} catch (error: any) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: error.message || "An error occurred while submitting the contact request",
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Subscribe to email newsletter
|
|
87
|
+
*/
|
|
88
|
+
export async function subscribeToNewsletter({
|
|
89
|
+
email,
|
|
90
|
+
status = "subscribed",
|
|
91
|
+
source = "footer",
|
|
92
|
+
}: {
|
|
93
|
+
email: string
|
|
94
|
+
status?: "subscribed" | "unsubscribed"
|
|
95
|
+
source?: string
|
|
96
|
+
}) {
|
|
97
|
+
try {
|
|
98
|
+
const publishableKey = getPublishableKey()
|
|
99
|
+
const baseUrl = getBaseUrl()
|
|
100
|
+
|
|
101
|
+
if (!publishableKey) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
error: "Configuration error: Publishable API key is missing.",
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const headers: Record<string, string> = {
|
|
109
|
+
"Content-Type": "application/json",
|
|
110
|
+
"x-publishable-api-key": publishableKey,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const response = await fetch(`${baseUrl}/store/contact-email-subscriptions`, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers,
|
|
116
|
+
body: JSON.stringify({
|
|
117
|
+
email,
|
|
118
|
+
status,
|
|
119
|
+
source,
|
|
120
|
+
}),
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
const error = await response.json().catch(() => ({ message: "Unknown error" }))
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error:
|
|
128
|
+
error.message || `HTTP ${response.status}: Failed to subscribe to newsletter`,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const data = await response.json()
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
data,
|
|
136
|
+
}
|
|
137
|
+
} catch (error: any) {
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
error: error.message || "An error occurred while subscribing to newsletter",
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import "server-only"
|
|
2
|
+
import { cookies as nextCookies, headers } from "next/headers"
|
|
3
|
+
|
|
4
|
+
export const getAuthHeaders = async (): Promise<{ authorization: string } | {}> => {
|
|
5
|
+
try {
|
|
6
|
+
const cookies = await nextCookies()
|
|
7
|
+
const token = cookies.get("_medusa_jwt")?.value
|
|
8
|
+
|
|
9
|
+
if (!token) {
|
|
10
|
+
return {}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return { authorization: `Bearer ${token}` }
|
|
14
|
+
} catch {
|
|
15
|
+
return {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const getGuestAuthHeaders = async (): Promise<
|
|
20
|
+
{ authorization: string } | {}
|
|
21
|
+
> => {
|
|
22
|
+
try {
|
|
23
|
+
const cookies = await nextCookies()
|
|
24
|
+
const token = cookies.get("_medusa_guest_jwt")?.value
|
|
25
|
+
|
|
26
|
+
if (!token) {
|
|
27
|
+
return {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { authorization: `Bearer ${token}` }
|
|
31
|
+
} catch {
|
|
32
|
+
return {}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const getCacheTag = async (tag: string): Promise<string> => {
|
|
37
|
+
try {
|
|
38
|
+
const cookies = await nextCookies()
|
|
39
|
+
const cacheId = cookies.get("_medusa_cache_id")?.value
|
|
40
|
+
|
|
41
|
+
if (!cacheId) {
|
|
42
|
+
return ""
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return `${tag}-${cacheId}`
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return ""
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const getCacheOptions = async (
|
|
52
|
+
tag: string
|
|
53
|
+
): Promise<{ tags: string[] } | {}> => {
|
|
54
|
+
if (typeof window !== "undefined") {
|
|
55
|
+
return {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const cacheTag = await getCacheTag(tag)
|
|
59
|
+
|
|
60
|
+
if (!cacheTag) {
|
|
61
|
+
return {}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { tags: [`${cacheTag}`] }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const setAuthToken = async (token: string) => {
|
|
68
|
+
const cookies = await nextCookies()
|
|
69
|
+
cookies.set("_medusa_jwt", token, {
|
|
70
|
+
maxAge: 60 * 60 * 24 * 7,
|
|
71
|
+
httpOnly: true,
|
|
72
|
+
sameSite: "strict",
|
|
73
|
+
secure: process.env.NODE_ENV === "production",
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const removeAuthToken = async () => {
|
|
78
|
+
const cookies = await nextCookies()
|
|
79
|
+
cookies.set("_medusa_jwt", "", {
|
|
80
|
+
maxAge: -1,
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const getCartId = async () => {
|
|
85
|
+
const cookies = await nextCookies()
|
|
86
|
+
const headerList = await headers()
|
|
87
|
+
|
|
88
|
+
// Check for x-cart-id header FIRST (as it's the most up-to-date from middleware)
|
|
89
|
+
const headerId = headerList.get("x-cart-id")
|
|
90
|
+
if (headerId) {
|
|
91
|
+
return headerId
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Prioritize buy_now_cart_id if it exists
|
|
95
|
+
const buyNowId = cookies.get("buy_now_cart_id")?.value
|
|
96
|
+
if (buyNowId) {
|
|
97
|
+
return buyNowId
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Fallback to regular cart cookies
|
|
101
|
+
const id =
|
|
102
|
+
cookies.get("_medusa_cart_id")?.value ||
|
|
103
|
+
cookies.get("cart_id")?.value ||
|
|
104
|
+
cookies.get("medusa_cart_id")?.value
|
|
105
|
+
|
|
106
|
+
return id
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const setCartId = async (cartId: string) => {
|
|
110
|
+
const cookies = await nextCookies()
|
|
111
|
+
cookies.set("_medusa_cart_id", cartId, {
|
|
112
|
+
maxAge: 60 * 60 * 24 * 7,
|
|
113
|
+
httpOnly: true,
|
|
114
|
+
sameSite: "strict",
|
|
115
|
+
secure: process.env.NODE_ENV === "production",
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const removeCartId = async () => {
|
|
120
|
+
const cookies = await nextCookies()
|
|
121
|
+
cookies.set("_medusa_cart_id", "", {
|
|
122
|
+
maxAge: -1,
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const removeSyncLock = async () => {
|
|
127
|
+
const cookies = await nextCookies()
|
|
128
|
+
cookies.set("_medusa_cart_synced", "", {
|
|
129
|
+
maxAge: -1,
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const setHoldCartId = async (cartId: string) => {
|
|
134
|
+
const cookies = await nextCookies()
|
|
135
|
+
cookies.set("_medusa_hold_cart_id", cartId, {
|
|
136
|
+
maxAge: 60 * 60 * 24,
|
|
137
|
+
httpOnly: true,
|
|
138
|
+
sameSite: "strict",
|
|
139
|
+
secure: process.env.NODE_ENV === "production",
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const getHoldCartId = async () => {
|
|
144
|
+
const cookies = await nextCookies()
|
|
145
|
+
return cookies.get("_medusa_hold_cart_id")?.value
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const removeHoldCartId = async () => {
|
|
149
|
+
const cookies = await nextCookies()
|
|
150
|
+
cookies.set("_medusa_hold_cart_id", "", {
|
|
151
|
+
maxAge: -1,
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const setBuyNowCartId = async (cartId: string) => {
|
|
156
|
+
const cookies = await nextCookies()
|
|
157
|
+
cookies.set("buy_now_cart_id", cartId, {
|
|
158
|
+
maxAge: 60 * 60 * 24 * 7,
|
|
159
|
+
httpOnly: true,
|
|
160
|
+
sameSite: "strict",
|
|
161
|
+
secure: process.env.NODE_ENV === "production",
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const removeBuyNowCartId = async () => {
|
|
166
|
+
const cookies = await nextCookies()
|
|
167
|
+
cookies.set("buy_now_cart_id", "", {
|
|
168
|
+
maxAge: -1,
|
|
169
|
+
})
|
|
170
|
+
}
|