@anker-in/campaign-ui 0.4.5-beta.11 → 0.4.5-beta.13

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 (148) hide show
  1. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  2. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
  3. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
  4. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +2 -2
  5. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  6. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +2 -2
  7. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  8. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js.map +2 -2
  9. package/dist/cjs/components/LiveChatWidget/constants.d.ts +0 -1
  10. package/dist/cjs/components/LiveChatWidget/constants.js +1 -1
  11. package/dist/cjs/components/LiveChatWidget/constants.js.map +3 -3
  12. package/dist/cjs/components/credits/context/hooks/useActivities.js +1 -1
  13. package/dist/cjs/components/credits/context/hooks/useActivities.js.map +2 -2
  14. package/dist/cjs/components/credits/context/hooks/useAlpcFetch.js +1 -1
  15. package/dist/cjs/components/credits/context/hooks/useAlpcFetch.js.map +3 -3
  16. package/dist/cjs/components/credits/context/hooks/useCountries.js +1 -1
  17. package/dist/cjs/components/credits/context/hooks/useCountries.js.map +2 -2
  18. package/dist/cjs/components/credits/context/hooks/useMyRewards.js +1 -1
  19. package/dist/cjs/components/credits/context/hooks/useMyRewards.js.map +2 -2
  20. package/dist/cjs/components/credits/context/hooks/useRedeemAndBuy.js +1 -1
  21. package/dist/cjs/components/credits/context/hooks/useRedeemAndBuy.js.map +3 -3
  22. package/dist/cjs/components/credits/context/hooks/useRedeemableList.js +1 -1
  23. package/dist/cjs/components/credits/context/hooks/useRedeemableList.js.map +2 -2
  24. package/dist/cjs/components/credits/context/hooks/useSubscribed.js +1 -1
  25. package/dist/cjs/components/credits/context/hooks/useSubscribed.js.map +3 -3
  26. package/dist/cjs/components/credits/context/hooks/useUploadReceipt.d.ts +0 -8
  27. package/dist/cjs/components/credits/context/hooks/useUploadReceipt.js +1 -1
  28. package/dist/cjs/components/credits/context/hooks/useUploadReceipt.js.map +3 -3
  29. package/dist/cjs/components/credits/context/provider.d.ts +2 -1
  30. package/dist/cjs/components/credits/context/provider.js +1 -1
  31. package/dist/cjs/components/credits/context/provider.js.map +3 -3
  32. package/dist/cjs/components/credits/context/utils.d.ts +1 -1
  33. package/dist/cjs/components/credits/context/utils.js +1 -1
  34. package/dist/cjs/components/credits/context/utils.js.map +3 -3
  35. package/dist/cjs/components/credits/creditsBenefits/index.js +2 -3
  36. package/dist/cjs/components/credits/creditsBenefits/index.js.map +2 -2
  37. package/dist/cjs/components/credits/creditsCash/CreditsCash.js +1 -1
  38. package/dist/cjs/components/credits/creditsCash/CreditsCash.js.map +3 -3
  39. package/dist/cjs/components/credits/creditsRedeemList/AddressForm/index.js +1 -1
  40. package/dist/cjs/components/credits/creditsRedeemList/AddressForm/index.js.map +2 -2
  41. package/dist/cjs/components/credits/creditsRedeemList/RedeemCouponModal.js +1 -1
  42. package/dist/cjs/components/credits/creditsRedeemList/RedeemCouponModal.js.map +3 -3
  43. package/dist/cjs/components/credits/creditsRedeemList/RedeemVirtualProductModal.js +1 -1
  44. package/dist/cjs/components/credits/creditsRedeemList/RedeemVirtualProductModal.js.map +3 -3
  45. package/dist/cjs/components/credits/modal/ActivitiesModal.js +1 -1
  46. package/dist/cjs/components/credits/modal/ActivitiesModal.js.map +2 -2
  47. package/dist/cjs/components/credits/modal/SubscribeModal.js +1 -1
  48. package/dist/cjs/components/credits/modal/SubscribeModal.js.map +3 -3
  49. package/dist/cjs/components/index.d.ts +2 -2
  50. package/dist/cjs/components/index.js +1 -1
  51. package/dist/cjs/components/index.js.map +2 -2
  52. package/dist/cjs/components/registration/authCodeActivate/index.js +1 -1
  53. package/dist/cjs/components/registration/authCodeActivate/index.js.map +3 -3
  54. package/dist/cjs/templates/Credits.js +1 -1
  55. package/dist/cjs/templates/Credits.js.map +2 -2
  56. package/dist/cjs/templates/Credits.types.d.ts +1 -0
  57. package/dist/cjs/templates/Credits.types.js.map +1 -1
  58. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  59. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
  60. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
  61. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
  62. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  63. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
  64. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  65. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js.map +3 -3
  66. package/dist/esm/components/LiveChatWidget/constants.d.ts +0 -1
  67. package/dist/esm/components/LiveChatWidget/constants.js +1 -1
  68. package/dist/esm/components/LiveChatWidget/constants.js.map +3 -3
  69. package/dist/esm/components/credits/context/hooks/useActivities.js +1 -1
  70. package/dist/esm/components/credits/context/hooks/useActivities.js.map +2 -2
  71. package/dist/esm/components/credits/context/hooks/useAlpcFetch.js +1 -1
  72. package/dist/esm/components/credits/context/hooks/useAlpcFetch.js.map +3 -3
  73. package/dist/esm/components/credits/context/hooks/useCountries.js +1 -1
  74. package/dist/esm/components/credits/context/hooks/useCountries.js.map +2 -2
  75. package/dist/esm/components/credits/context/hooks/useMyRewards.js +1 -1
  76. package/dist/esm/components/credits/context/hooks/useMyRewards.js.map +2 -2
  77. package/dist/esm/components/credits/context/hooks/useRedeemAndBuy.js +1 -1
  78. package/dist/esm/components/credits/context/hooks/useRedeemAndBuy.js.map +3 -3
  79. package/dist/esm/components/credits/context/hooks/useRedeemableList.js +1 -1
  80. package/dist/esm/components/credits/context/hooks/useRedeemableList.js.map +2 -2
  81. package/dist/esm/components/credits/context/hooks/useSubscribed.js +1 -1
  82. package/dist/esm/components/credits/context/hooks/useSubscribed.js.map +3 -3
  83. package/dist/esm/components/credits/context/hooks/useUploadReceipt.d.ts +0 -8
  84. package/dist/esm/components/credits/context/hooks/useUploadReceipt.js +1 -1
  85. package/dist/esm/components/credits/context/hooks/useUploadReceipt.js.map +3 -3
  86. package/dist/esm/components/credits/context/provider.d.ts +2 -1
  87. package/dist/esm/components/credits/context/provider.js +1 -1
  88. package/dist/esm/components/credits/context/provider.js.map +3 -3
  89. package/dist/esm/components/credits/context/utils.d.ts +1 -1
  90. package/dist/esm/components/credits/context/utils.js +1 -1
  91. package/dist/esm/components/credits/context/utils.js.map +3 -3
  92. package/dist/esm/components/credits/creditsBenefits/index.js +2 -3
  93. package/dist/esm/components/credits/creditsBenefits/index.js.map +2 -2
  94. package/dist/esm/components/credits/creditsCash/CreditsCash.js +1 -1
  95. package/dist/esm/components/credits/creditsCash/CreditsCash.js.map +3 -3
  96. package/dist/esm/components/credits/creditsRedeemList/AddressForm/index.js +1 -1
  97. package/dist/esm/components/credits/creditsRedeemList/AddressForm/index.js.map +2 -2
  98. package/dist/esm/components/credits/creditsRedeemList/RedeemCouponModal.js +1 -1
  99. package/dist/esm/components/credits/creditsRedeemList/RedeemCouponModal.js.map +3 -3
  100. package/dist/esm/components/credits/creditsRedeemList/RedeemVirtualProductModal.js +1 -1
  101. package/dist/esm/components/credits/creditsRedeemList/RedeemVirtualProductModal.js.map +3 -3
  102. package/dist/esm/components/credits/modal/ActivitiesModal.js +1 -1
  103. package/dist/esm/components/credits/modal/ActivitiesModal.js.map +2 -2
  104. package/dist/esm/components/credits/modal/SubscribeModal.js +1 -1
  105. package/dist/esm/components/credits/modal/SubscribeModal.js.map +3 -3
  106. package/dist/esm/components/index.d.ts +2 -2
  107. package/dist/esm/components/index.js +1 -1
  108. package/dist/esm/components/index.js.map +2 -2
  109. package/dist/esm/components/registration/authCodeActivate/index.js +1 -1
  110. package/dist/esm/components/registration/authCodeActivate/index.js.map +3 -3
  111. package/dist/esm/templates/Credits.js +1 -1
  112. package/dist/esm/templates/Credits.js.map +2 -2
  113. package/dist/esm/templates/Credits.types.d.ts +1 -0
  114. package/dist/esm/templates/Credits.types.js.map +1 -1
  115. package/package.json +2 -3
  116. package/src/components/LiveChatWidget/components/MessageContent/CartCard.tsx +6 -2
  117. package/src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx +6 -3
  118. package/src/components/LiveChatWidget/components/MessageContent/ProductComparison.tsx +5 -5
  119. package/src/components/LiveChatWidget/components/MessageContent/ProductList.tsx +6 -3
  120. package/src/components/LiveChatWidget/constants.ts +0 -9
  121. package/src/components/credits/context/hooks/useActivities.ts +2 -2
  122. package/src/components/credits/context/hooks/useAlpcFetch.ts +64 -20
  123. package/src/components/credits/context/hooks/useCountries.ts +7 -2
  124. package/src/components/credits/context/hooks/useMyRewards.ts +5 -2
  125. package/src/components/credits/context/hooks/useRedeemAndBuy.ts +3 -2
  126. package/src/components/credits/context/hooks/useRedeemableList.ts +22 -23
  127. package/src/components/credits/context/hooks/useSubscribed.ts +4 -3
  128. package/src/components/credits/context/hooks/useUploadReceipt.tsx +42 -21
  129. package/src/components/credits/context/provider.tsx +4 -0
  130. package/src/components/credits/context/utils.ts +6 -1
  131. package/src/components/credits/creditsBenefits/index.tsx +1 -2
  132. package/src/components/credits/creditsCash/CreditsCash.tsx +12 -5
  133. package/src/components/credits/creditsRedeemList/AddressForm/index.tsx +4 -6
  134. package/src/components/credits/creditsRedeemList/RedeemCouponModal.tsx +3 -2
  135. package/src/components/credits/creditsRedeemList/RedeemVirtualProductModal.tsx +5 -4
  136. package/src/components/credits/modal/ActivitiesModal.tsx +2 -2
  137. package/src/components/credits/modal/SubscribeModal.tsx +4 -6
  138. package/src/components/index.ts +2 -2
  139. package/src/components/registration/authCodeActivate/index.tsx +8 -6
  140. package/src/templates/Credits.tsx +1 -0
  141. package/src/templates/Credits.types.ts +1 -0
  142. package/dist/cjs/stories/CartCard.stories.d.ts +0 -33
  143. package/dist/cjs/stories/CartCard.stories.js +0 -21
  144. package/dist/cjs/stories/CartCard.stories.js.map +0 -7
  145. package/dist/esm/stories/CartCard.stories.d.ts +0 -33
  146. package/dist/esm/stories/CartCard.stories.js +0 -21
  147. package/dist/esm/stories/CartCard.stories.js.map +0 -7
  148. package/src/stories/CartCard.stories.tsx +0 -459
@@ -26,17 +26,36 @@ interface FetcherOptions {
26
26
  fetchOptions: RequestInit
27
27
  onReAuth: () => Promise<boolean>
28
28
  onUnAuth: () => void
29
+ apiBaseUrl?: string
29
30
  }
30
31
 
31
32
  let reAuthPromise: Promise<boolean> | undefined
32
33
 
33
34
  type Fetcher<TData, TBody> = (url: string, data: { arg: TBody }) => Promise<TData>
34
35
 
36
+ /**
37
+ * 统一处理 ALPC 接口返回的数据结构
38
+ * 兼容一层 data 和两层 data 的场景:
39
+ * - 两层 data: {data: {data: ..., code: 27004}}
40
+ * - 一层 data: {data: ..., code: 27004}
41
+ */
42
+ const normalizeResponseData = (responseData: any) => {
43
+ if (!responseData) return responseData
44
+
45
+ // 如果 responseData.data 存在且是一个对象(不是数组),说明是两层 data 结构
46
+ if (responseData.data && typeof responseData.data === 'object' && !Array.isArray(responseData.data)) {
47
+ return responseData.data
48
+ }
49
+
50
+ // 一层 data 结构或其他情况,直接返回
51
+ return responseData
52
+ }
53
+
35
54
  const fetcher = (options: FetcherOptions) => {
36
- return fetch(getAlpcPath(options.locale) + options.url, options.fetchOptions).then(async response => {
55
+ return fetch(getAlpcPath(options.locale, options.apiBaseUrl) + options.url, options.fetchOptions).then(async response => {
37
56
  if (response.status < 300) {
38
57
  const responseData = await response.json()
39
- return responseData.data
58
+ return normalizeResponseData(responseData)
40
59
  }
41
60
 
42
61
  // 错误处理
@@ -53,10 +72,10 @@ const fetcher = (options: FetcherOptions) => {
53
72
 
54
73
  if (result) {
55
74
  // 跨区重新登录后需要替换区域标志,所以不能用最开始请求的那个链接
56
- return fetch(getAlpcPath(options.locale) + options.url, options.fetchOptions).then(async response => {
75
+ return fetch(getAlpcPath(options.locale, options.apiBaseUrl) + options.url, options.fetchOptions).then(async response => {
57
76
  if (response.status < 300) {
58
77
  const responseData = await response.json()
59
- return responseData.data
78
+ return normalizeResponseData(responseData)
60
79
  }
61
80
 
62
81
  if (response.status === 401) {
@@ -77,14 +96,16 @@ const fetcher = (options: FetcherOptions) => {
77
96
  })
78
97
  }
79
98
 
80
- const reAuth = async (locale: string, retry: boolean, brand: string) => {
81
- let reloginResponse = await fetch(`${getAlpcPath(locale)}/cloud/login`, {
99
+ const reAuth = async (locale: string, retry: boolean, brand: string, apiBaseUrl?: string) => {
100
+ let reloginResponse = await fetch(`${getAlpcPath(locale, apiBaseUrl)}/cloud/login`, {
82
101
  method: 'POST',
83
102
  })
84
103
  let reloginResponseData = await reloginResponse.json()
104
+ // 兼容两层 data 结构
105
+ let normalizedData = normalizeResponseData(reloginResponseData)
85
106
 
86
107
  if (reloginResponse!.status < 300 && !retry) {
87
- if (reloginResponseData?.data?.code === 27004) {
108
+ if (normalizedData?.code === 27004) {
88
109
  // 跨区登录错误
89
110
  const alpcEUCookie = Cookies.get('alpcEU')
90
111
 
@@ -101,17 +122,18 @@ const reAuth = async (locale: string, retry: boolean, brand: string) => {
101
122
  }
102
123
 
103
124
  // 重新尝试换区登录
104
- reloginResponse = await fetch(`${getAlpcPath(locale)}/cloud/login`, {
125
+ reloginResponse = await fetch(`${getAlpcPath(locale, apiBaseUrl)}/cloud/login`, {
105
126
  method: 'POST',
106
127
  })
107
128
  reloginResponseData = await reloginResponse.json()
129
+ normalizedData = normalizeResponseData(reloginResponseData)
108
130
 
109
131
  if (reloginResponse.status > 300) {
110
132
  return false
111
133
  }
112
134
  }
113
135
  }
114
- if (!reloginResponseData?.data?.code) {
136
+ if (!normalizedData?.code) {
115
137
  // 重新登录成功
116
138
  return true
117
139
  }
@@ -127,7 +149,8 @@ export interface UseMutationConfig<TData> {
127
149
  const useMutation = <TData, TBody>(
128
150
  url: string,
129
151
  fetcher: Fetcher<TData, TBody>,
130
- mutationConfig: UseMutationConfig<TData> = {}
152
+ mutationConfig: UseMutationConfig<TData> = {},
153
+ brandRef: React.MutableRefObject<string | undefined>
131
154
  ) => {
132
155
  const innerMutating = useRef<boolean>(false)
133
156
 
@@ -140,7 +163,15 @@ const useMutation = <TData, TBody>(
140
163
 
141
164
  const trigger = useCallback(
142
165
  async (fetchData: TBody, opts: UseMutationConfig<TData> = {}) => {
166
+ console.log('[useAlpcMutation] trigger called with:', { url: urlRef.current, fetchData, brand: brandRef.current })
167
+
143
168
  if (innerMutating.current) {
169
+ console.log('[useAlpcMutation] already mutating, skipping')
170
+ return
171
+ }
172
+
173
+ if (!brandRef.current) {
174
+ console.log('[useAlpcMutation] brand not available, skipping request')
144
175
  return
145
176
  }
146
177
 
@@ -192,7 +223,7 @@ export const useAlpcFetch = <TData>(
192
223
  swrOptions?: UseAlpcFetchOptionsSwrOptions<TData, Error>
193
224
  ) => {
194
225
  const [retry, setRetry] = useState(false)
195
- const { removeProfile, alpcBrand } = useCreditsContext()
226
+ const { removeProfile, alpcBrand, apiBaseUrl } = useCreditsContext()
196
227
 
197
228
  const { enable, ...otherSwrOptions } = swrOptions || {}
198
229
 
@@ -207,12 +238,13 @@ export const useAlpcFetch = <TData>(
207
238
 
208
239
  const context = useSWR<TData>(
209
240
  [url, requestBody],
210
- !fetchEnable
241
+ !fetchEnable || !brand
211
242
  ? null
212
243
  : ([requestUrl, data]: [string, Record<string, any>]) =>
213
244
  fetcher({
214
245
  url: requestUrl,
215
246
  locale,
247
+ apiBaseUrl,
216
248
  fetchOptions: {
217
249
  method: 'POST',
218
250
  headers: {
@@ -226,7 +258,7 @@ export const useAlpcFetch = <TData>(
226
258
  ...fetchOptions,
227
259
  },
228
260
  onReAuth: async () => {
229
- const result = await reAuth(locale, retry, brand)
261
+ const result = await reAuth(locale, retry, brand, apiBaseUrl)
230
262
 
231
263
  if (result) {
232
264
  setRetry(true)
@@ -251,20 +283,30 @@ export const useAlpcFetch = <TData>(
251
283
 
252
284
  export const useAlpcMutation = <TData, TBody>(options: MutationOptions, mutationOptions?: UseMutationConfig<TData>) => {
253
285
  const [retry, setRetry] = useState(false)
254
- const { removeProfile, alpcBrand } = useCreditsContext()
286
+ const { removeProfile, alpcBrand, apiBaseUrl } = useCreditsContext()
255
287
 
256
288
  const { locale = '', brand: headlessBrand } = useHeadlessContext()
257
289
  // 优先使用 creditsContext 中的 alpcBrand,如果没有则使用 headlessConfig 中的 brand
258
290
  const brand = alpcBrand || headlessBrand
291
+ const brandRef = useRef(brand)
292
+ brandRef.current = brand
259
293
 
260
294
  const { url, initData, headers, ...fetchOptions } = options
295
+ const initDataRef = useRef(initData)
296
+ initDataRef.current = initData
261
297
 
262
298
  const context = useMutation<TData, TBody>(
263
299
  url,
264
- (requestUrl, data: { arg: TBody }) =>
265
- fetcher({
300
+ (requestUrl, data: { arg: TBody }) => {
301
+ // Prevent requests when brand is not available
302
+ if (!brandRef.current) {
303
+ return Promise.resolve(undefined as unknown as TData)
304
+ }
305
+
306
+ return fetcher({
266
307
  url: requestUrl,
267
308
  locale,
309
+ apiBaseUrl,
268
310
  fetchOptions: {
269
311
  method: 'POST',
270
312
  headers: {
@@ -273,12 +315,12 @@ export const useAlpcMutation = <TData, TBody>(options: MutationOptions, mutation
273
315
  },
274
316
  ...fetchOptions,
275
317
  body: JSON.stringify({
276
- ...initData,
318
+ ...initDataRef.current,
277
319
  ...data.arg,
278
320
  }),
279
321
  },
280
322
  onReAuth: async () => {
281
- const result = await reAuth(locale, retry, brand)
323
+ const result = await reAuth(locale, retry, brandRef.current!, apiBaseUrl)
282
324
 
283
325
  if (result) {
284
326
  setRetry(true)
@@ -291,8 +333,10 @@ export const useAlpcMutation = <TData, TBody>(options: MutationOptions, mutation
291
333
  // 失败后退出登录
292
334
  removeProfile()
293
335
  },
294
- }),
295
- mutationOptions
336
+ })
337
+ },
338
+ mutationOptions,
339
+ brandRef
296
340
  )
297
341
 
298
342
  return context
@@ -13,13 +13,18 @@ function useCountries({ shopifyStoreDomain }: { shopifyStoreDomain: string }) {
13
13
  const fetchCountries = async () => {
14
14
  setLoading(true)
15
15
  try {
16
- const response = await fetch(`/api/multipass/rainbowbridge/uc/shop/shipping_zones?shop=${shopifyStoreDomain}`)
16
+ const response = await fetch(`/api/multipass/rainbowbridge/uc/shop/shipping_zones?shop=${shopifyStoreDomain}`, {
17
+ headers: {
18
+ 'current-language': locale,
19
+ },
20
+ })
17
21
  const res: any = await response.json()
18
22
 
19
23
  // 国家过滤
20
24
  let shipsToCountries: ShippingCountry[] = []
21
25
  const countrySet = new Set()
22
- res.data.data.forEach((shippingZoneItem: ShippingZone) => {
26
+ const shippingZones = res?.data?.data || res?.data || []
27
+ shippingZones.forEach((shippingZoneItem: ShippingZone) => {
23
28
  shippingZoneItem.countries.forEach(shippingZoneCountryItem => {
24
29
  if (!countrySet.has(shippingZoneCountryItem.code)) {
25
30
  countrySet.add(shippingZoneCountryItem.code)
@@ -15,7 +15,7 @@ function useMyRewards({
15
15
  pageSize: number
16
16
  consumeType?: AlpcConsumeType
17
17
  }) {
18
- const { profile, alpcBrand } = useCreditsContext()
18
+ const { profile, alpcBrand } = useCreditsContext()
19
19
  const { brand: headlessBrand, locale, appName } = useHeadlessContext()
20
20
  const brand = alpcBrand || headlessBrand
21
21
 
@@ -60,8 +60,11 @@ function useMyRewards({
60
60
  }, [consumeType, page, pageSize, profile?.user_id])
61
61
 
62
62
  useEffect(() => {
63
+ if (!brand || !profile?.user_id) {
64
+ return
65
+ }
63
66
  getMyRewards()
64
- }, [getMyRewards])
67
+ }, [getMyRewards, brand])
65
68
 
66
69
  return {
67
70
  myRewards,
@@ -38,9 +38,10 @@ export const useRedeemAndBuy = (
38
38
  user_id: profile?.user_id,
39
39
  rule_id: arg.redeemId,
40
40
  })
41
- if (res && res?.data?.coupon_code) {
41
+ const data = (res?.data || res) as { success?: boolean; coupon_code?: string }
42
+ if (res && data?.coupon_code) {
42
43
  buyNow({
43
- discountCodes: [res.data.coupon_code],
44
+ discountCodes: [data.coupon_code],
44
45
  lineItems: [
45
46
  {
46
47
  variant: arg.productVariant,
@@ -23,7 +23,7 @@ function useRedeemableList(props?: { consumeType: AlpcConsumeType }) {
23
23
  {
24
24
  data: { consume_credit_rules: ConsumeCreditRuleResponse[] }
25
25
  },
26
- { consume_type?: AlpcConsumeType, user_id?: string}
26
+ { consume_type?: AlpcConsumeType; user_id?: string }
27
27
  >({
28
28
  url: `/v1/credit/consume_credit_rules`,
29
29
  initData: {
@@ -34,32 +34,31 @@ function useRedeemableList(props?: { consumeType: AlpcConsumeType }) {
34
34
  },
35
35
  })
36
36
 
37
- const getRedeemableList = useCallback((props?: { consumeType?: AlpcConsumeType}) => {
38
- trigger(
39
- {
40
- user_id: profile?.user_id,
41
- ...(props?.consumeType && {consume_type: props.consumeType})
42
- },
43
- {
44
- onSuccess(responseData) {
45
- console.log('responseData', responseData)
46
- let list = responseData.data.consume_credit_rules || []
47
- // let cList = list.filter(i => i.consume_type == 1) || []
48
- // let pList = list.filter(i => i.consume_type == 2) || []
49
- // cList = cList.sort((prev, next) => prev.consume_credits - next.consume_credits)
50
- // pList = pList.sort((prev, next) => prev.consume_credits - next.consume_credits)
51
- // const resultList = cList.concat(pList)
52
- setRedeemableList(list)
37
+ const getRedeemableList = useCallback(
38
+ (props?: { consumeType?: AlpcConsumeType }) => {
39
+ trigger(
40
+ {
41
+ user_id: profile?.user_id,
42
+ ...(props?.consumeType && { consume_type: props.consumeType }),
53
43
  },
54
- }
55
- )
56
- }, [profile, trigger, locale])
44
+ {
45
+ onSuccess(responseData) {
46
+ console.log('responseData', responseData)
47
+ let list = responseData?.data?.consume_credit_rules || []
48
+ setRedeemableList(list)
49
+ },
50
+ }
51
+ )
52
+ },
53
+ [profile, trigger, locale]
54
+ )
57
55
 
58
56
  useEffect(() => {
59
- if (!isLoadingProfile) {
60
- getRedeemableList({ consumeType: props?.consumeType })
57
+ if (!brand || isLoadingProfile) {
58
+ return
61
59
  }
62
- }, [getRedeemableList, isLoadingProfile])
60
+ getRedeemableList({ consumeType: props?.consumeType })
61
+ }, [getRedeemableList, isLoadingProfile, brand])
63
62
 
64
63
  return {
65
64
  listLoading,
@@ -13,13 +13,14 @@ export const useSubscribed = () => {
13
13
 
14
14
  const { activities } = useActivities(activitiesOptions)
15
15
  const { data: subscription } = useSubscriptions({ email: profile?.email })
16
+ const subscriptionData = subscription?.data || subscription
16
17
 
17
18
  const isSubscribed = useMemo(() => {
18
- if (!subscription?.preference) {
19
+ if (!subscriptionData?.preference) {
19
20
  return false
20
21
  }
21
22
 
22
- const subscribeResult = subscription.preference.some(item => {
23
+ const subscribeResult = subscriptionData.preference.some((item: any) => {
23
24
  return item.brand === brand && item.subscribe
24
25
  })
25
26
 
@@ -30,7 +31,7 @@ export const useSubscribed = () => {
30
31
  return activities?.some(item => {
31
32
  return item.task_sub_type === TaskSubType.Subscription
32
33
  })
33
- }, [subscription?.preference, activities])
34
+ }, [subscriptionData?.preference, activities])
34
35
 
35
36
  return isSubscribed
36
37
  }
@@ -1,29 +1,50 @@
1
1
  import useSWRMutation from 'swr/mutation'
2
+ import { useHeadlessContext } from '@anker-in/lib'
2
3
 
3
- export const uploadReceipt = async (
4
- _: any,
5
- { arg: { orderName } }: { arg: { orderName: string } }
6
- ): Promise<{
7
- ok: boolean
8
- code: number
9
- }> => {
10
- const response = await fetch(`/api/multipass/mktsrv/v1/credit/upload_third_party_order`, {
11
- credentials: 'include',
12
- method: 'POST',
13
- headers: {
14
- 'Content-Type': 'application/json',
15
- },
16
- body: JSON.stringify({
17
- order_name: orderName,
18
- }),
19
- })
20
- const { data } = await response.json()
21
- return {
22
- ok: data?.code === 0,
23
- code: data?.code,
4
+ /**
5
+ * 统一处理 ALPC 接口返回的数据结构
6
+ * 兼容一层 data 和两层 data 的场景
7
+ */
8
+ const normalizeResponseData = (responseData: any) => {
9
+ if (!responseData) return responseData
10
+
11
+ // 如果 responseData.data 存在且是一个对象(不是数组),说明是两层 data 结构
12
+ if (responseData.data && typeof responseData.data === 'object' && !Array.isArray(responseData.data)) {
13
+ return responseData.data
24
14
  }
15
+
16
+ // 一层 data 结构或其他情况,直接返回
17
+ return responseData
25
18
  }
26
19
 
27
20
  export const useUploadReceipt = () => {
21
+ const { locale } = useHeadlessContext()
22
+
23
+ const uploadReceipt = async (
24
+ _: any,
25
+ { arg: { orderName } }: { arg: { orderName: string } }
26
+ ): Promise<{
27
+ ok: boolean
28
+ code: number
29
+ }> => {
30
+ const response = await fetch(`/api/multipass/mktsrv/v1/credit/upload_third_party_order`, {
31
+ credentials: 'include',
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ 'current-language': locale,
36
+ },
37
+ body: JSON.stringify({
38
+ order_name: orderName,
39
+ }),
40
+ })
41
+ const result = await response.json()
42
+ const normalizedData = normalizeResponseData(result)
43
+ return {
44
+ ok: normalizedData?.data?.code === 0,
45
+ code: normalizedData?.data?.code ?? -1,
46
+ }
47
+ }
48
+
28
49
  return useSWRMutation('/api/multipass/mktsrv/v1/credit/upload_third_party_order', uploadReceipt)
29
50
  }
@@ -37,6 +37,7 @@ type Context = {
37
37
  setOpenMyRewardsModal: (open: boolean) => void
38
38
  openActivitiesModal: boolean
39
39
  setOpenActivitiesModal: (open: boolean) => void
40
+ apiBaseUrl?: string
40
41
  }
41
42
 
42
43
  export const CreditsContext = createContext<Context>({
@@ -61,6 +62,7 @@ export const CreditsContext = createContext<Context>({
61
62
  setOpenMyRewardsModal: () => {},
62
63
  openActivitiesModal: false,
63
64
  setOpenActivitiesModal: () => {},
65
+ apiBaseUrl: undefined,
64
66
  })
65
67
 
66
68
  export function CreditsProvider({
@@ -80,6 +82,7 @@ export function CreditsProvider({
80
82
  memberPriceDiscount,
81
83
  alpcBrand,
82
84
  cartConfig,
85
+ apiBaseUrl,
83
86
  }: PropsWithChildren<Omit<Context, 'openMyRewardsModal' | 'setOpenMyRewardsModal' | 'openActivitiesModal' | 'setOpenActivitiesModal'>>) {
84
87
  const [openMyRewardsModal, setOpenMyRewardsModal] = useState(false)
85
88
  const [openActivitiesModal, setOpenActivitiesModal] = useState(false)
@@ -102,6 +105,7 @@ export function CreditsProvider({
102
105
  memberPriceDiscount,
103
106
  alpcBrand,
104
107
  cartConfig,
108
+ apiBaseUrl,
105
109
  openMyRewardsModal,
106
110
  setOpenMyRewardsModal,
107
111
  openActivitiesModal,
@@ -1,7 +1,7 @@
1
1
  import Cookies from 'js-cookie'
2
2
  import { PRICE_SYMBOL } from './const'
3
3
 
4
- export function getAlpcPath(locale = '') {
4
+ export function getAlpcPath(locale = '', apiBaseUrl?: string) {
5
5
  let isEU = false
6
6
  const alpcEUCookie = Cookies.get('alpcEU')
7
7
  if (alpcEUCookie === undefined || alpcEUCookie === '') {
@@ -15,6 +15,11 @@ export function getAlpcPath(locale = '') {
15
15
  }
16
16
  }
17
17
 
18
+ // 如果提供了自定义的 apiBaseUrl,拼接对应的路径
19
+ if (apiBaseUrl) {
20
+ return isEU ? `${apiBaseUrl}/api/multipass/alpc-eu` : `${apiBaseUrl}/api/multipass/alpc`
21
+ }
22
+
18
23
  if (isEU) {
19
24
  return '/api/multipass/alpc-eu'
20
25
  }
@@ -103,8 +103,7 @@ export const CreditsBenefits = ({ copy, id }: { copy: CreditsBenefitsCopy; id?:
103
103
  </Swiper>
104
104
  <div
105
105
  id="benefits-pagination"
106
- className="mx-auto mt-[12px] grid !w-fit grid-flow-col [&_.swiper-pagination-bullet-active>div]:!bg-[#151515]
107
- [&_.swiper-pagination-bullet]:cursor-pointer [&_.swiper-pagination-bullet]:rounded-full"
106
+ className="mx-auto mt-[12px] grid !w-fit grid-flow-col [&_.swiper-pagination-bullet-active]:!bg-[#151515]"
108
107
  ></div>
109
108
  </div>
110
109
 
@@ -28,13 +28,20 @@ export const CreditsCash = ({ copy, id }: { copy: CreditsCashCopy; id?: string }
28
28
  })
29
29
 
30
30
  const list = useMemo(() => {
31
- return productByHandles
32
- ?.map((product: Product) => {
33
- const config = (copy.list || []).find(item => item.products?.[0]?.handle === product.handle)
31
+ return (copy?.list || [])
32
+ .map(config => {
33
+ const handle = config?.products?.[0]?.handle
34
+ const sku = config?.products?.[0]?.sku
34
35
  const alpcData = redeemableList.find(item => item.id?.toString() === config?.redeemId?.toString())
35
- const productVariant =
36
- product.variants?.find((variant: any) => variant.sku === config?.products?.[0]?.sku) || product.variants?.[0]
36
+
37
+ // productByHandles 中找到对应 handle product
38
+ const product = productByHandles?.find((p: Product) => p.handle === handle)
39
+ if (!product) return null
40
+
41
+ // 在 product 的 variants 中找到对应 sku 的 variant
42
+ const productVariant = product.variants?.find((variant: any) => variant.sku === sku) || product.variants?.[0]
37
43
  if (!alpcData || !productVariant) return null
44
+
38
45
  return {
39
46
  product,
40
47
  productVariant,
@@ -60,12 +60,10 @@ export const AddressForm = ({ countries, countriesLoading, form, validate, error
60
60
  }, [customer, profile?.email, locale])
61
61
 
62
62
  useEffect(() => {
63
- if (customer) {
64
- const defaultAddress = getDefaultAddress()
65
- setAddress(defaultAddress)
66
- onChange(defaultAddress)
67
- }
68
- }, [customer, getDefaultAddress])
63
+ const defaultAddress = getDefaultAddress()
64
+ setAddress(defaultAddress)
65
+ onChange(defaultAddress)
66
+ }, [getDefaultAddress])
69
67
 
70
68
  if (!address) {
71
69
  return null
@@ -32,8 +32,9 @@ function RedeemCouponModal({
32
32
  return
33
33
  }
34
34
 
35
- if (responseData?.data?.success) {
36
- setCouponCode(responseData.data.coupon_code)
35
+ const data = responseData?.data || responseData
36
+ if (data?.success) {
37
+ setCouponCode(data.coupon_code)
37
38
  fetchCreditInfo(profile?.user_id)
38
39
  } else {
39
40
  let errorMsg
@@ -40,8 +40,9 @@ function RedeemVirtualProductModal({
40
40
  return
41
41
  }
42
42
 
43
- if (responseData?.data?.id) {
44
- setGiftCardId(responseData.data.id)
43
+ const data = responseData?.data || responseData
44
+ if (data?.id) {
45
+ setGiftCardId(data.id)
45
46
  fetchCreditInfo(profile?.user_id)
46
47
  } else {
47
48
  handleRedeemError(responseData.code)
@@ -120,8 +121,8 @@ function RedeemVirtualProductModal({
120
121
  // GiftCard 类型使用 Product 接口,传递默认地址
121
122
  const defaultAddress = {
122
123
  email: profile?.email || '',
123
- firstName: profile?.firstName || '',
124
- lastName: profile?.lastName || '',
124
+ first_name: profile?.firstName || 'Anker',
125
+ last_name: profile?.lastName || 'Direct',
125
126
  address1: 'Default Address',
126
127
  city: 'Default City',
127
128
  province: 'Default Province',
@@ -108,7 +108,7 @@ function ActivitiesModal({ data, ...props }: ActivitiesModalProps) {
108
108
  // 新增的部分任务都使用了 alpc 同一个任务类型,需要用任务 id 来区分
109
109
  return item.task_name.replace('Refund', data.productUnApprovedTask)
110
110
  } else if (taskIdToTypeMapping?.[String(item.task_rule_id)]) {
111
- return dtcTaskTypeToNameMap[taskIdToTypeMapping?.[String(item.task_rule_id)]]
111
+ return dtcTaskTypeToNameMap[taskIdToTypeMapping?.[String(item.task_rule_id)]] || item.task_name
112
112
  } else {
113
113
  return taskNameMap[item.task_sub_type] || item.task_name
114
114
  }
@@ -198,7 +198,7 @@ function ActivitiesModal({ data, ...props }: ActivitiesModalProps) {
198
198
  </Tabs>
199
199
  </div>
200
200
 
201
- <div className="grid gap-[18px] overflow-auto overscroll-contain md:gap-[12px]">
201
+ <div className="grid gap-[18px] overflow-auto md:gap-[12px]">
202
202
  {isLoading && (
203
203
  <div className="flex h-full flex-col items-center justify-center">
204
204
  <LoadingDots />
@@ -1,5 +1,4 @@
1
1
  import { useCallback, useEffect, useState } from 'react'
2
- import { useRouter } from 'next/router'
3
2
  import { Button, Checkbox, Picture, Text } from '@anker-in/headless-ui'
4
3
  import { classNames, fetcher, gaTrack, useHeadlessContext } from '@anker-in/lib'
5
4
  import Cookies from 'js-cookie'
@@ -43,9 +42,8 @@ const getAdCookie = () => {
43
42
  }
44
43
 
45
44
  export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubscribeModalProps) {
46
- const { brand } = useHeadlessContext()
45
+ const { brand, locale } = useHeadlessContext()
47
46
  const rounded = ROUNDED_BRANDS.includes(brand)
48
- const { locale } = useRouter()
49
47
  const [policy, setPolicy] = useState(false)
50
48
  const [email, setEmail] = useState('')
51
49
  const [errorMessage, setErrorMessage] = useState('')
@@ -137,7 +135,7 @@ export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubs
137
135
  animationClassName="md:translate-y-[100vh]"
138
136
  {...props}
139
137
  >
140
- <div className="flex flex-col gap-[16px] text-center min-l:px-[16px]">
138
+ <div className="min-l:px-[16px] flex flex-col gap-[16px] text-center">
141
139
  <div>
142
140
  <Text className={classNames('mb-[24px] text-[22px] font-bold')} html={copy.title}></Text>
143
141
  </div>
@@ -173,7 +171,7 @@ export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubs
173
171
  >
174
172
  <Picture
175
173
  source="https://cdn.shopify.com/s/files/1/0512/8568/8505/files/icon_email.png?v=1697527383"
176
- className="h-[24px] w-[24px]"
174
+ className="size-[24px]"
177
175
  alt="email"
178
176
  />
179
177
  </Button>
@@ -181,7 +179,7 @@ export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubs
181
179
  <div className="flex w-full">
182
180
  <Checkbox checked={policy} onCheckedChange={() => setPolicy(!policy)} required className="border-[#1d1d1f]" />
183
181
  <label
184
- className="text-left ml-2 text-[14px] font-semibold text-[#777] [&_a]:underline"
182
+ className="ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline"
185
183
  dangerouslySetInnerHTML={{
186
184
  __html: copy?.policy || '',
187
185
  }}
@@ -1,4 +1,4 @@
1
- export { default as Chat } from './chat/index.js'
1
+ export { default as Chat } from './chat/index'
2
2
 
3
3
  export {
4
4
  Role,
@@ -7,7 +7,7 @@ export {
7
7
  useCopilotChat,
8
8
  useCopilotAction,
9
9
  useCopilotReadable,
10
- } from './chat/utils.js'
10
+ } from './chat/utils'
11
11
 
12
12
  export * from './credits/index.js'
13
13