medusa-storefront-data 1.0.0 → 2.1.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/dist/cookies.d.ts +2 -2
- package/dist/cookies.d.ts.map +1 -1
- package/dist/edge.d.ts +3 -0
- package/dist/edge.d.ts.map +1 -0
- package/dist/edge.js +1 -0
- package/dist/middleware.d.ts +3 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +1 -0
- package/dist/server/cart.d.ts +9 -5
- package/dist/server/cart.d.ts.map +1 -1
- package/dist/server/cart.js +164 -194
- package/dist/server/categories.d.ts +3 -2
- package/dist/server/categories.d.ts.map +1 -1
- package/dist/server/categories.js +14 -51
- package/dist/server/collections.d.ts.map +1 -1
- package/dist/server/collections.js +16 -61
- package/dist/server/contact.d.ts +34 -0
- package/dist/server/contact.d.ts.map +1 -0
- package/dist/server/contact.js +57 -0
- package/dist/server/customer.d.ts +7 -7
- package/dist/server/customer.d.ts.map +1 -1
- package/dist/server/customer.js +96 -145
- package/dist/server/dynamic-config.d.ts.map +1 -1
- package/dist/server/dynamic-config.js +38 -12
- package/dist/server/fulfillment.d.ts +4 -3
- package/dist/server/fulfillment.d.ts.map +1 -1
- package/dist/server/fulfillment.js +16 -41
- package/dist/server/guest.d.ts +35 -63
- package/dist/server/guest.d.ts.map +1 -1
- package/dist/server/guest.js +81 -202
- package/dist/server/home.d.ts +15 -0
- package/dist/server/home.d.ts.map +1 -0
- package/dist/server/home.js +45 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/locale-actions.d.ts +1 -1
- package/dist/server/locale-actions.d.ts.map +1 -1
- package/dist/server/locale-actions.js +8 -13
- package/dist/server/locales.d.ts +2 -4
- package/dist/server/locales.d.ts.map +1 -1
- package/dist/server/locales.js +5 -13
- package/dist/server/orders.d.ts +5 -11
- package/dist/server/orders.d.ts.map +1 -1
- package/dist/server/orders.js +126 -267
- package/dist/server/payment-details.d.ts +4 -4
- package/dist/server/payment-details.d.ts.map +1 -1
- package/dist/server/payment-details.js +17 -42
- package/dist/server/payment.d.ts +2 -1
- package/dist/server/payment.d.ts.map +1 -1
- package/dist/server/payment.js +9 -21
- package/dist/server/pincode.d.ts +7 -0
- package/dist/server/pincode.d.ts.map +1 -0
- package/dist/server/pincode.js +30 -0
- package/dist/server/products.d.ts +15 -19
- package/dist/server/products.d.ts.map +1 -1
- package/dist/server/products.js +47 -178
- package/dist/server/regions.d.ts +1 -1
- package/dist/server/regions.d.ts.map +1 -1
- package/dist/server/regions.js +6 -3
- package/dist/server/returns.d.ts +4 -4
- package/dist/server/returns.d.ts.map +1 -1
- package/dist/server/returns.js +50 -154
- package/dist/server/swaps.d.ts +7 -6
- package/dist/server/swaps.d.ts.map +1 -1
- package/dist/server/swaps.js +23 -57
- package/dist/server/variants.d.ts.map +1 -1
- package/dist/server/variants.js +11 -22
- package/dist/server/wishlist.d.ts +11 -0
- package/dist/server/wishlist.d.ts.map +1 -0
- package/dist/server/wishlist.js +49 -0
- package/dist/util/get-locale-header.d.ts +1 -1
- package/dist/util/revalidate-cart.d.ts +2 -0
- package/dist/util/revalidate-cart.d.ts.map +1 -0
- package/dist/util/revalidate-cart.js +8 -0
- package/dist/util/sort-products.d.ts +3 -0
- package/dist/util/sort-products.d.ts.map +1 -0
- package/dist/util/sort-products.js +1 -0
- package/dist/util/store-client.d.ts +13 -0
- package/dist/util/store-client.d.ts.map +1 -0
- package/dist/util/store-client.js +77 -0
- package/package.json +95 -37
- package/src/edge.ts +2 -0
- package/src/middleware.ts +2 -2
- package/src/server/cart.ts +214 -267
- package/src/server/categories.ts +19 -72
- package/src/server/collections.ts +25 -82
- package/src/server/contact.ts +92 -0
- package/src/server/customer.ts +146 -190
- package/src/server/dynamic-config.ts +38 -12
- package/src/server/fulfillment.ts +27 -53
- package/src/server/guest.ts +159 -276
- package/src/server/home.ts +68 -0
- package/src/server/index.ts +1 -0
- package/src/server/locale-actions.ts +8 -15
- package/src/server/locales.ts +6 -18
- package/src/server/orders.ts +167 -337
- package/src/server/payment-details.ts +24 -52
- package/src/server/payment.ts +8 -28
- package/src/server/pincode.ts +49 -0
- package/src/server/products.ts +72 -235
- package/src/server/regions.ts +10 -6
- package/src/server/returns.ts +75 -189
- package/src/server/swaps.ts +94 -123
- package/src/server/variants.ts +9 -28
- package/src/server/wishlist.ts +1 -1
- package/src/util/revalidate-cart.ts +10 -0
- package/src/util/sort-products.ts +2 -47
- package/src/util/store-client.ts +93 -0
- package/src/services/middleware.ts +0 -54
package/src/server/customer.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use server"
|
|
2
2
|
|
|
3
|
-
import { sdk } from "../config"
|
|
4
3
|
import medusaError from "../util/medusa-error"
|
|
5
4
|
import { HttpTypes } from "@medusajs/types"
|
|
6
5
|
import { revalidateTag } from "next/cache"
|
|
@@ -8,7 +7,6 @@ import { redirect } from "next/navigation"
|
|
|
8
7
|
import { cookies } from "next/headers"
|
|
9
8
|
import {
|
|
10
9
|
getAuthHeaders,
|
|
11
|
-
getCacheOptions,
|
|
12
10
|
getCacheTag,
|
|
13
11
|
getCartId,
|
|
14
12
|
removeAuthToken,
|
|
@@ -17,35 +15,48 @@ import {
|
|
|
17
15
|
setAuthToken,
|
|
18
16
|
setCartId,
|
|
19
17
|
} from "../cookies"
|
|
18
|
+
import {
|
|
19
|
+
medusaAuthLogin,
|
|
20
|
+
medusaAuthLogout,
|
|
21
|
+
medusaAuthRegister,
|
|
22
|
+
} from "medusa-services/auth"
|
|
23
|
+
import {
|
|
24
|
+
isAccountDeletionPendingError,
|
|
25
|
+
medusaCustomerCreate,
|
|
26
|
+
medusaCustomerCreateAddress,
|
|
27
|
+
medusaCustomerDeleteAddress,
|
|
28
|
+
medusaCustomerRetrieve,
|
|
29
|
+
medusaCustomerUpdate,
|
|
30
|
+
medusaCustomerUpdateAddress,
|
|
31
|
+
} from "medusa-services/customer"
|
|
32
|
+
import { medusaCartMerge, medusaCartTransfer } from "medusa-services/cart"
|
|
33
|
+
import {
|
|
34
|
+
getAuthClientOptions,
|
|
35
|
+
getMedusaBackendUrl,
|
|
36
|
+
getStoreCartClientOptions,
|
|
37
|
+
} from "../util/store-client"
|
|
20
38
|
|
|
21
39
|
import { cache } from "react"
|
|
22
40
|
|
|
41
|
+
function throwCustomerError(error: unknown): never {
|
|
42
|
+
if (error && typeof error === "object" && "response" in error) {
|
|
43
|
+
medusaError(error)
|
|
44
|
+
}
|
|
45
|
+
throw error instanceof Error ? error : new Error(String(error))
|
|
46
|
+
}
|
|
47
|
+
|
|
23
48
|
export const retrieveCustomer = cache(
|
|
24
49
|
async (): Promise<HttpTypes.StoreCustomer | null> => {
|
|
25
50
|
const authHeaders = await getAuthHeaders()
|
|
26
51
|
|
|
27
|
-
if (!authHeaders) return null
|
|
28
|
-
|
|
29
|
-
const headers = {
|
|
30
|
-
...authHeaders,
|
|
31
|
-
}
|
|
52
|
+
if (!("authorization" in authHeaders)) return null
|
|
32
53
|
|
|
33
54
|
try {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
headers: headers as Record<string, string>,
|
|
40
|
-
cache: "no-store",
|
|
41
|
-
}
|
|
42
|
-
)
|
|
43
|
-
return customer
|
|
44
|
-
} catch (error: any) {
|
|
45
|
-
const errStr = error.toString().toLowerCase()
|
|
46
|
-
// If the error indicates a pending deletion, return a special object
|
|
47
|
-
if (errStr.includes("active account deletion request") ||
|
|
48
|
-
(error.message && error.message.toLowerCase().includes("active account deletion request"))) {
|
|
55
|
+
const options = await getStoreCartClientOptions()
|
|
56
|
+
const { customer } = await medusaCustomerRetrieve(options, { fields: "*addresses" })
|
|
57
|
+
return customer as unknown as HttpTypes.StoreCustomer
|
|
58
|
+
} catch (error: unknown) {
|
|
59
|
+
if (isAccountDeletionPendingError(error)) {
|
|
49
60
|
const cookieStore = await cookies()
|
|
50
61
|
const cookieEmail = cookieStore.get("_medusa_customer_email")?.value
|
|
51
62
|
return { id: "pending_deletion", email: cookieEmail || "pending" } as any
|
|
@@ -56,19 +67,17 @@ export const retrieveCustomer = cache(
|
|
|
56
67
|
)
|
|
57
68
|
|
|
58
69
|
export const updateCustomer = async (body: HttpTypes.StoreUpdateCustomer) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const updateRes = await sdk.store.customer
|
|
64
|
-
.update(body, {}, headers)
|
|
65
|
-
.then(({ customer }) => customer)
|
|
66
|
-
.catch(medusaError)
|
|
70
|
+
try {
|
|
71
|
+
const options = await getStoreCartClientOptions()
|
|
72
|
+
const { customer } = await medusaCustomerUpdate(body as Record<string, unknown>, options)
|
|
67
73
|
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
const cacheTag = await getCacheTag("customers")
|
|
75
|
+
revalidateTag(cacheTag)
|
|
70
76
|
|
|
71
|
-
|
|
77
|
+
return customer as unknown as HttpTypes.StoreCustomer
|
|
78
|
+
} catch (e) {
|
|
79
|
+
throwCustomerError(e)
|
|
80
|
+
}
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
export async function signup(_currentState: unknown, formData: FormData) {
|
|
@@ -81,34 +90,28 @@ export async function signup(_currentState: unknown, formData: FormData) {
|
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
try {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
const authOptions = await getAuthClientOptions()
|
|
94
|
+
const token = await medusaAuthRegister(
|
|
95
|
+
{ email: customerForm.email, password },
|
|
96
|
+
authOptions
|
|
97
|
+
)
|
|
88
98
|
|
|
89
|
-
await setAuthToken(token
|
|
99
|
+
await setAuthToken(token)
|
|
90
100
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
}
|
|
101
|
+
const options = await getStoreCartClientOptions()
|
|
102
|
+
const { customer: createdCustomer } = await medusaCustomerCreate(customerForm, options)
|
|
94
103
|
|
|
95
|
-
const
|
|
96
|
-
customerForm,
|
|
97
|
-
|
|
98
|
-
headers
|
|
104
|
+
const loginToken = await medusaAuthLogin(
|
|
105
|
+
{ email: customerForm.email, password },
|
|
106
|
+
authOptions
|
|
99
107
|
)
|
|
100
108
|
|
|
101
|
-
|
|
102
|
-
email: customerForm.email,
|
|
103
|
-
password,
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
await setAuthToken(loginToken as string)
|
|
109
|
+
await setAuthToken(loginToken)
|
|
107
110
|
|
|
108
111
|
const customerCacheTag = await getCacheTag("customers")
|
|
109
112
|
revalidateTag(customerCacheTag)
|
|
110
113
|
|
|
111
|
-
await transferCart(loginToken
|
|
114
|
+
await transferCart(loginToken)
|
|
112
115
|
|
|
113
116
|
return createdCustomer
|
|
114
117
|
} catch (error: any) {
|
|
@@ -116,12 +119,13 @@ export async function signup(_currentState: unknown, formData: FormData) {
|
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
121
|
|
|
119
|
-
export async function login(
|
|
122
|
+
export async function login(
|
|
123
|
+
_currentState: string | null,
|
|
124
|
+
formData: FormData
|
|
125
|
+
): Promise<string | null> {
|
|
120
126
|
let email = formData.get("email_or_phone") as string
|
|
121
127
|
const password = formData.get("password") as string
|
|
122
128
|
|
|
123
|
-
// Sanitize phone number: remove spaces, dashes, etc if it looks like a phone number
|
|
124
|
-
// If it doesn't have '@', we treat it as a potential phone number and clean it
|
|
125
129
|
if (email && !email.includes("@")) {
|
|
126
130
|
email = email.replace(/[^\d+]/g, "")
|
|
127
131
|
}
|
|
@@ -130,30 +134,28 @@ export async function login(_currentState: unknown, formData: FormData) {
|
|
|
130
134
|
let countryCode = "in"
|
|
131
135
|
|
|
132
136
|
try {
|
|
133
|
-
const
|
|
134
|
-
await
|
|
137
|
+
const authOptions = await getAuthClientOptions()
|
|
138
|
+
const token = await medusaAuthLogin({ email_or_phone: email, password }, authOptions)
|
|
139
|
+
await setAuthToken(token)
|
|
135
140
|
|
|
136
|
-
// Store email in cookie for deletion flow (needed for cancellation modal)
|
|
137
141
|
const cookieStore = await cookies()
|
|
138
142
|
cookieStore.set("_medusa_customer_email", email, { maxAge: 60 * 60 * 24 * 7, path: "/" })
|
|
139
143
|
|
|
140
|
-
// Force a check to see if we are blocked
|
|
141
144
|
try {
|
|
142
|
-
await
|
|
143
|
-
|
|
145
|
+
const options = await getStoreCartClientOptions()
|
|
146
|
+
await medusaCustomerRetrieve({
|
|
147
|
+
...options,
|
|
148
|
+
authorization: `Bearer ${token}`,
|
|
144
149
|
})
|
|
145
|
-
} catch (err:
|
|
146
|
-
|
|
147
|
-
if (errStr.includes("active account deletion request")) {
|
|
150
|
+
} catch (err: unknown) {
|
|
151
|
+
if (isAccountDeletionPendingError(err)) {
|
|
148
152
|
return "ACCOUNT_DELETION_PENDING"
|
|
149
153
|
}
|
|
150
|
-
// If it's another error, we might still want to know, but deletion is our priority
|
|
151
154
|
throw err
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
// Transfer cart after login success - Moved here to ensure execution
|
|
155
157
|
try {
|
|
156
|
-
await transferCart(token
|
|
158
|
+
await transferCart(token)
|
|
157
159
|
} catch (e) {
|
|
158
160
|
console.error("Transfer cart failed in login:", e)
|
|
159
161
|
}
|
|
@@ -161,43 +163,44 @@ export async function login(_currentState: unknown, formData: FormData) {
|
|
|
161
163
|
const customerCacheTag = await getCacheTag("customers")
|
|
162
164
|
revalidateTag(customerCacheTag)
|
|
163
165
|
|
|
164
|
-
countryCode = formData.get("country_code") as string || "in"
|
|
166
|
+
countryCode = (formData.get("country_code") as string) || "in"
|
|
165
167
|
shouldRedirect = true
|
|
166
|
-
} catch (error:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (errorStr.includes("active account deletion request") ||
|
|
170
|
-
(error.message && error.message.toLowerCase().includes("active account deletion request"))) {
|
|
171
|
-
// Still set email cookie even if blocked
|
|
168
|
+
} catch (error: unknown) {
|
|
169
|
+
if (isAccountDeletionPendingError(error)) {
|
|
172
170
|
const cookieStore = await cookies()
|
|
173
171
|
cookieStore.set("_medusa_customer_email", email, { maxAge: 60 * 60 * 24 * 7, path: "/" })
|
|
174
172
|
return "ACCOUNT_DELETION_PENDING"
|
|
175
173
|
}
|
|
176
174
|
|
|
175
|
+
const errorStr = String(error).toLowerCase()
|
|
177
176
|
if (errorStr.includes("invalid") || errorStr.includes("401") || errorStr.includes("not found")) {
|
|
178
177
|
return "Invalid credentials. Please try again."
|
|
179
178
|
}
|
|
180
|
-
|
|
179
|
+
|
|
180
|
+
return error instanceof Error ? error.message : String(error)
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
if (shouldRedirect) {
|
|
184
184
|
const redirectUrl = formData.get("redirect_url") as string
|
|
185
|
-
|
|
186
|
-
// Check if it's a "keep-me-here" context (Wishlist or Review Popups)
|
|
187
185
|
const hasKeepContext = redirectUrl?.includes("login_context=keep")
|
|
188
186
|
|
|
189
|
-
// Security + Context check
|
|
190
187
|
if (redirectUrl && redirectUrl.startsWith("/") && hasKeepContext) {
|
|
191
188
|
redirect(redirectUrl)
|
|
192
189
|
} else {
|
|
193
|
-
// DEFAULT: Always Home Page
|
|
194
190
|
redirect(`/${countryCode}`)
|
|
195
191
|
}
|
|
196
192
|
}
|
|
193
|
+
|
|
194
|
+
return null
|
|
197
195
|
}
|
|
198
196
|
|
|
199
197
|
export async function signout(countryCode: string) {
|
|
200
|
-
|
|
198
|
+
try {
|
|
199
|
+
const authOptions = await getAuthClientOptions()
|
|
200
|
+
await medusaAuthLogout(authOptions)
|
|
201
|
+
} catch {
|
|
202
|
+
// Continue local cleanup even if remote logout fails
|
|
203
|
+
}
|
|
201
204
|
|
|
202
205
|
await removeAuthToken()
|
|
203
206
|
await removeCartId()
|
|
@@ -216,58 +219,38 @@ export async function signout(countryCode: string) {
|
|
|
216
219
|
|
|
217
220
|
export async function transferCart(token?: string) {
|
|
218
221
|
const cartId = await getCartId()
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
:
|
|
222
|
+
const options = await getStoreCartClientOptions()
|
|
223
|
+
const mergeOptions = token
|
|
224
|
+
? { ...options, authorization: `Bearer ${token}` }
|
|
225
|
+
: options
|
|
222
226
|
|
|
223
227
|
console.log("--- TransferCart Debug ---")
|
|
224
228
|
console.log("Token provided:", !!token)
|
|
225
229
|
console.log("Guest Cart ID:", cartId)
|
|
226
230
|
|
|
227
|
-
const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY
|
|
228
|
-
const backendUrl = process.env.MEDUSA_BACKEND_URL || process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL || "http://localhost:9000"
|
|
229
|
-
|
|
230
231
|
try {
|
|
231
232
|
console.log("Calling merge API (always)...")
|
|
232
|
-
|
|
233
|
-
const response = await fetch(`${backendUrl}/store/carts/merge`, {
|
|
234
|
-
method: "POST",
|
|
235
|
-
headers: {
|
|
236
|
-
"Content-Type": "application/json",
|
|
237
|
-
"x-publishable-api-key": publishableKey || "",
|
|
238
|
-
...(authHeaders as any),
|
|
239
|
-
},
|
|
240
|
-
// Only include body if cartId exists
|
|
241
|
-
...(cartId ? { body: JSON.stringify({ cart_id: cartId }) } : {}),
|
|
242
|
-
cache: "no-store",
|
|
243
|
-
})
|
|
244
233
|
|
|
245
|
-
|
|
246
|
-
|
|
234
|
+
const data = await medusaCartMerge(mergeOptions, cartId ?? undefined)
|
|
235
|
+
|
|
236
|
+
if (data) {
|
|
247
237
|
console.log("--- Login Merge Response (JSON) ---")
|
|
248
238
|
console.log(JSON.stringify(data, null, 2))
|
|
249
239
|
console.log("-----------------------------------")
|
|
250
|
-
|
|
240
|
+
|
|
251
241
|
const activeCartId = data.cart?.id || data.id || data.cart_id
|
|
252
|
-
|
|
253
|
-
if (activeCartId) {
|
|
242
|
+
|
|
243
|
+
if (activeCartId && typeof activeCartId === "string") {
|
|
254
244
|
console.log("Storing Cart ID in cookie:", activeCartId)
|
|
255
245
|
await setCartId(activeCartId)
|
|
256
246
|
}
|
|
257
|
-
} else if (response.status === 404) {
|
|
258
|
-
// 404 is fine - it just means the customer has no active cart yet
|
|
259
|
-
console.log("No active cart found for this customer (expected).")
|
|
260
247
|
} else {
|
|
261
|
-
|
|
262
|
-
console.log(`Merge API Info (${response.status}):`, errorText)
|
|
263
|
-
if (cartId) {
|
|
264
|
-
await sdk.store.cart.transferCart(cartId, {}, authHeaders).catch(() => { })
|
|
265
|
-
}
|
|
248
|
+
console.log("No active cart found for this customer (expected).")
|
|
266
249
|
}
|
|
267
|
-
} catch (error:
|
|
268
|
-
console.error("Merge API error:", error.message)
|
|
250
|
+
} catch (error: unknown) {
|
|
251
|
+
console.error("Merge API error:", error instanceof Error ? error.message : error)
|
|
269
252
|
if (cartId) {
|
|
270
|
-
await
|
|
253
|
+
await medusaCartTransfer(cartId, mergeOptions).catch(() => {})
|
|
271
254
|
}
|
|
272
255
|
}
|
|
273
256
|
|
|
@@ -298,42 +281,34 @@ export const addCustomerAddress = async (
|
|
|
298
281
|
is_default_shipping: isDefaultShipping,
|
|
299
282
|
metadata: {
|
|
300
283
|
address_type: (formData.get("address_type") as string) || "HOME",
|
|
301
|
-
}
|
|
284
|
+
},
|
|
302
285
|
}
|
|
303
286
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
287
|
+
try {
|
|
288
|
+
const options = await getStoreCartClientOptions()
|
|
289
|
+
await medusaCustomerCreateAddress(address, options)
|
|
307
290
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
})
|
|
315
|
-
.catch((err) => {
|
|
316
|
-
return { success: false, error: err.toString() }
|
|
317
|
-
})
|
|
291
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
292
|
+
revalidateTag(customerCacheTag)
|
|
293
|
+
return { success: true, error: null }
|
|
294
|
+
} catch (err) {
|
|
295
|
+
return { success: false, error: String(err) }
|
|
296
|
+
}
|
|
318
297
|
}
|
|
319
298
|
|
|
320
299
|
export const deleteCustomerAddress = async (
|
|
321
300
|
addressId: string
|
|
322
301
|
): Promise<void> => {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
302
|
+
try {
|
|
303
|
+
const options = await getStoreCartClientOptions()
|
|
304
|
+
await medusaCustomerDeleteAddress(addressId, options)
|
|
326
305
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
})
|
|
334
|
-
.catch((err) => {
|
|
335
|
-
return { success: false, error: err.toString() }
|
|
336
|
-
})
|
|
306
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
307
|
+
revalidateTag(customerCacheTag)
|
|
308
|
+
return { success: true, error: null } as any
|
|
309
|
+
} catch (err) {
|
|
310
|
+
return { success: false, error: String(err) } as any
|
|
311
|
+
}
|
|
337
312
|
}
|
|
338
313
|
|
|
339
314
|
export const updateCustomerAddress = async (
|
|
@@ -359,7 +334,7 @@ export const updateCustomerAddress = async (
|
|
|
359
334
|
country_code: formData.get("country_code") as string,
|
|
360
335
|
metadata: {
|
|
361
336
|
address_type: (formData.get("address_type") as string) || "HOME",
|
|
362
|
-
}
|
|
337
|
+
},
|
|
363
338
|
} as any
|
|
364
339
|
|
|
365
340
|
const phone = formData.get("phone") as string
|
|
@@ -368,42 +343,34 @@ export const updateCustomerAddress = async (
|
|
|
368
343
|
address.phone = phone
|
|
369
344
|
}
|
|
370
345
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
346
|
+
try {
|
|
347
|
+
const options = await getStoreCartClientOptions()
|
|
348
|
+
await medusaCustomerUpdateAddress(addressId, address, options)
|
|
374
349
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
})
|
|
382
|
-
.catch((err) => {
|
|
383
|
-
return { success: false, error: err.toString() }
|
|
384
|
-
})
|
|
350
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
351
|
+
revalidateTag(customerCacheTag)
|
|
352
|
+
return { success: true, error: null }
|
|
353
|
+
} catch (err) {
|
|
354
|
+
return { success: false, error: String(err) }
|
|
355
|
+
}
|
|
385
356
|
}
|
|
386
357
|
|
|
387
358
|
export const setDefaultAddress = async (addressId: string) => {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
359
|
+
try {
|
|
360
|
+
const options = await getStoreCartClientOptions()
|
|
361
|
+
await medusaCustomerUpdateAddress(addressId, { is_default_shipping: true }, options)
|
|
391
362
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
})
|
|
399
|
-
.catch((err) => {
|
|
400
|
-
return { success: false, error: err.toString() }
|
|
401
|
-
})
|
|
363
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
364
|
+
revalidateTag(customerCacheTag)
|
|
365
|
+
return { success: true, error: null }
|
|
366
|
+
} catch (err) {
|
|
367
|
+
return { success: false, error: String(err) }
|
|
368
|
+
}
|
|
402
369
|
}
|
|
403
370
|
|
|
404
371
|
export async function initiateGoogleAuth() {
|
|
405
372
|
try {
|
|
406
|
-
const backendUrl =
|
|
373
|
+
const backendUrl = getMedusaBackendUrl()
|
|
407
374
|
|
|
408
375
|
if (!backendUrl) {
|
|
409
376
|
return { error: "Backend URL not configured" }
|
|
@@ -428,25 +395,19 @@ export async function initiateGoogleAuth() {
|
|
|
428
395
|
return { error: "No redirect URL received from Google OAuth" }
|
|
429
396
|
}
|
|
430
397
|
|
|
431
|
-
// Fix redirect URL: Remove country code prefix from callback URL if present
|
|
432
|
-
// Example: https://chocomelon.in/in/auth/customer/google/callback -> https://chocomelon.in/auth/customer/google/callback
|
|
433
398
|
try {
|
|
434
399
|
const url = new URL(redirectUrl)
|
|
435
|
-
const pathParts = url.pathname.split(
|
|
400
|
+
const pathParts = url.pathname.split("/").filter(Boolean)
|
|
436
401
|
|
|
437
|
-
// Check if path starts with country code pattern (2-3 letter code)
|
|
438
402
|
if (pathParts.length > 0 && /^[a-z]{2,3}$/i.test(pathParts[0])) {
|
|
439
|
-
|
|
440
|
-
if (pathParts.length > 1 && pathParts[1] === 'auth') {
|
|
441
|
-
// Remove country code from path
|
|
403
|
+
if (pathParts.length > 1 && pathParts[1] === "auth") {
|
|
442
404
|
pathParts.shift()
|
|
443
|
-
url.pathname =
|
|
405
|
+
url.pathname = "/" + pathParts.join("/")
|
|
444
406
|
redirectUrl = url.toString()
|
|
445
407
|
}
|
|
446
408
|
}
|
|
447
409
|
} catch (e) {
|
|
448
410
|
// If URL parsing fails, use original redirectUrl
|
|
449
|
-
|
|
450
411
|
}
|
|
451
412
|
|
|
452
413
|
return { redirectUrl }
|
|
@@ -461,7 +422,7 @@ export async function handleGoogleAuthCallback(params: Record<string, string>) {
|
|
|
461
422
|
return { error: "Missing authentication parameters" }
|
|
462
423
|
}
|
|
463
424
|
|
|
464
|
-
const backendUrl =
|
|
425
|
+
const backendUrl = getMedusaBackendUrl()
|
|
465
426
|
|
|
466
427
|
if (!backendUrl) {
|
|
467
428
|
return { error: "Backend URL not configured" }
|
|
@@ -502,23 +463,22 @@ export async function handleGoogleCallback(token: string, email?: string, first_
|
|
|
502
463
|
}
|
|
503
464
|
|
|
504
465
|
await setAuthToken(token)
|
|
505
|
-
await transferCart(token).catch(() => {
|
|
466
|
+
await transferCart(token).catch(() => {})
|
|
506
467
|
|
|
507
468
|
if (email && typeof email === "string") {
|
|
508
469
|
const cookieStore = await cookies()
|
|
509
470
|
cookieStore.set("_medusa_customer_email", email, { maxAge: 60 * 60 * 24 * 7, path: "/" })
|
|
510
471
|
|
|
511
|
-
const
|
|
512
|
-
if (
|
|
472
|
+
const options = await getStoreCartClientOptions()
|
|
473
|
+
if (options.authorization) {
|
|
513
474
|
try {
|
|
514
|
-
await
|
|
475
|
+
await medusaCustomerCreate(
|
|
515
476
|
{ email, ...(first_name && { first_name }), ...(last_name && { last_name }) },
|
|
516
|
-
|
|
517
|
-
headers
|
|
477
|
+
options
|
|
518
478
|
)
|
|
519
479
|
} catch (err: any) {
|
|
520
480
|
if (!err.message?.includes("already exists") && !err.message?.includes("duplicate")) {
|
|
521
|
-
|
|
481
|
+
// Ignore duplicate customer errors for OAuth sign-in
|
|
522
482
|
}
|
|
523
483
|
}
|
|
524
484
|
}
|
|
@@ -541,7 +501,6 @@ export async function uploadProfileImage(formData: FormData) {
|
|
|
541
501
|
return { error: "No image file provided" }
|
|
542
502
|
}
|
|
543
503
|
|
|
544
|
-
// Create new FormData with 'files' field as expected by the backend
|
|
545
504
|
const uploadFormData = new FormData()
|
|
546
505
|
uploadFormData.append("files", imageFile)
|
|
547
506
|
|
|
@@ -557,7 +516,6 @@ export async function uploadProfileImage(formData: FormData) {
|
|
|
557
516
|
throw new Error("No image URL returned from server")
|
|
558
517
|
}
|
|
559
518
|
|
|
560
|
-
// Fetch current customer to preserve existing metadata
|
|
561
519
|
const currentCustomer = await retrieveCustomer()
|
|
562
520
|
const existingMetadata = (currentCustomer?.metadata as Record<string, any>) || {}
|
|
563
521
|
const newMetadata = {
|
|
@@ -565,7 +523,6 @@ export async function uploadProfileImage(formData: FormData) {
|
|
|
565
523
|
profile_image_url: imageUrl,
|
|
566
524
|
}
|
|
567
525
|
|
|
568
|
-
// Update customer metadata
|
|
569
526
|
await updateCustomer({ metadata: newMetadata })
|
|
570
527
|
|
|
571
528
|
const customerCacheTag = await getCacheTag("customers")
|
|
@@ -575,7 +532,6 @@ export async function uploadProfileImage(formData: FormData) {
|
|
|
575
532
|
|
|
576
533
|
return { success: true, imageUrl }
|
|
577
534
|
} catch (error: any) {
|
|
578
|
-
|
|
579
535
|
return { error: error.message || "Failed to upload image" }
|
|
580
536
|
}
|
|
581
537
|
}
|
|
@@ -186,10 +186,10 @@ export const getHomeBannersFromConfig = async (): Promise<Array<{
|
|
|
186
186
|
const banner = item["homepage-banner"]
|
|
187
187
|
return {
|
|
188
188
|
image: banner?.["homepage-banner-image"],
|
|
189
|
-
title: banner?.["homepage-banner-title"],
|
|
190
|
-
subtitle: banner?.["homepage-banner-subtitle"],
|
|
191
|
-
description: banner?.["homepage-banner-description"],
|
|
192
|
-
buttonName: banner?.["homepage-banner-button-name"],
|
|
189
|
+
title: normalizeCmsText(banner?.["homepage-banner-title"]),
|
|
190
|
+
subtitle: normalizeCmsText(banner?.["homepage-banner-subtitle"]),
|
|
191
|
+
description: normalizeCmsText(banner?.["homepage-banner-description"]),
|
|
192
|
+
buttonName: normalizeBannerButtonName(banner?.["homepage-banner-button-name"]),
|
|
193
193
|
buttonLink: banner?.["homepage-banner-button-link"],
|
|
194
194
|
}
|
|
195
195
|
})
|
|
@@ -218,10 +218,10 @@ export const getAppBannersFromConfig = async (): Promise<Array<{
|
|
|
218
218
|
const banner = item["app-banner"]
|
|
219
219
|
return {
|
|
220
220
|
image: banner?.["app-banner-image"],
|
|
221
|
-
title: banner?.["app-banner-title"],
|
|
222
|
-
subtitle: banner?.["app-banner-subtitle"],
|
|
223
|
-
description: banner?.["app-banner-description"],
|
|
224
|
-
buttonName: banner?.["app-banner-button-name"],
|
|
221
|
+
title: normalizeCmsText(banner?.["app-banner-title"]),
|
|
222
|
+
subtitle: normalizeCmsText(banner?.["app-banner-subtitle"]),
|
|
223
|
+
description: normalizeCmsText(banner?.["app-banner-description"]),
|
|
224
|
+
buttonName: normalizeBannerButtonName(banner?.["app-banner-button-name"]),
|
|
225
225
|
buttonLink: banner?.["app-banner-button-link"],
|
|
226
226
|
}
|
|
227
227
|
})
|
|
@@ -260,7 +260,7 @@ export const getFeaturesFromConfig = async (): Promise<{ title: string; features
|
|
|
260
260
|
|
|
261
261
|
if (!homepageConfig) return null
|
|
262
262
|
|
|
263
|
-
const title = homepageConfig["why-choose-us-title"] || "Why Choose
|
|
263
|
+
const title = homepageConfig["why-choose-us-title"] || "Why Choose Us?"
|
|
264
264
|
const featuresRaw = homepageConfig["why-choose-us-features"] || []
|
|
265
265
|
|
|
266
266
|
const features = featuresRaw.map((item: any) => {
|
|
@@ -279,6 +279,32 @@ export const getFeaturesFromConfig = async (): Promise<{ title: string; features
|
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
const EMAIL_PATTERN = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
|
283
|
+
|
|
284
|
+
/** Fix common UTF-8 mojibake from CMS copy-paste (e.g. em dash, apostrophe). */
|
|
285
|
+
function normalizeCmsText(value: unknown): string | undefined {
|
|
286
|
+
if (typeof value !== "string" || !value.trim()) return undefined
|
|
287
|
+
return value
|
|
288
|
+
.trim()
|
|
289
|
+
.replace(/ΓÇö/g, "—")
|
|
290
|
+
.replace(/ΓÇÖ/g, "'")
|
|
291
|
+
.replace(/ΓÇ£|ΓÇ¥/g, '"')
|
|
292
|
+
.replace(/≡ƒÿÄ≡ƒæòΓ£¿/g, " 👕✨")
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function normalizeBannerButtonName(value: unknown): string | undefined {
|
|
296
|
+
const text = normalizeCmsText(value)
|
|
297
|
+
if (!text) return undefined
|
|
298
|
+
if (/^shop\s+now\s*1$/i.test(text)) return "Shop Now"
|
|
299
|
+
return text
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function normalizeConfigEmail(value: unknown): string | undefined {
|
|
303
|
+
if (typeof value !== "string") return undefined
|
|
304
|
+
const trimmed = value.trim()
|
|
305
|
+
return EMAIL_PATTERN.test(trimmed) ? trimmed : undefined
|
|
306
|
+
}
|
|
307
|
+
|
|
282
308
|
export const getContactInfoFromConfig = async (): Promise<{ phone?: string; email?: string; address?: string } | null> => {
|
|
283
309
|
try {
|
|
284
310
|
const config = await getDynamicConfig()
|
|
@@ -293,7 +319,7 @@ export const getContactInfoFromConfig = async (): Promise<{ phone?: string; emai
|
|
|
293
319
|
|
|
294
320
|
return {
|
|
295
321
|
phone: contactConfig["contact-phone"],
|
|
296
|
-
email: contactConfig["contact-email"],
|
|
322
|
+
email: normalizeConfigEmail(contactConfig["contact-email"]),
|
|
297
323
|
address: contactConfig["contact-address"],
|
|
298
324
|
}
|
|
299
325
|
} catch (error) {
|
|
@@ -392,10 +418,10 @@ export const getPromoBarConfig = async (): Promise<{ text: string | null; code:
|
|
|
392
418
|
const promoConfig = config?.["homepage-config"]?.["promo-bar"]
|
|
393
419
|
|
|
394
420
|
return {
|
|
395
|
-
text: promoConfig?.["promo-text"] || null,
|
|
421
|
+
text: normalizeCmsText(promoConfig?.["promo-text"]) || null,
|
|
396
422
|
code: promoConfig?.["promo-code"] || null,
|
|
397
423
|
value: promoConfig?.["promo-value"] || null,
|
|
398
|
-
active: promoConfig?.["promo-active"] ?? false
|
|
424
|
+
active: promoConfig?.["promo-active"] ?? false,
|
|
399
425
|
}
|
|
400
426
|
} catch (error) {
|
|
401
427
|
return { text: null, code: null, value: null, active: false }
|