@anker-in/campaign-ui 0.5.0-beta.2 → 0.5.0-beta.20

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 (137) hide show
  1. package/dist/cjs/components/credits/context/hooks/useCountries.js +1 -1
  2. package/dist/cjs/components/credits/context/hooks/useCountries.js.map +3 -3
  3. package/dist/cjs/components/memberPopup/MemberPopup.d.ts +1 -1
  4. package/dist/cjs/components/memberPopup/MemberPopup.js +1 -1
  5. package/dist/cjs/components/memberPopup/MemberPopup.js.map +3 -3
  6. package/dist/cjs/components/memberPopup/context/provider.d.ts +3 -3
  7. package/dist/cjs/components/memberPopup/context/provider.js +1 -1
  8. package/dist/cjs/components/memberPopup/context/provider.js.map +3 -3
  9. package/dist/cjs/components/memberPopup/panels/ActivitiesSubscribePanel.d.ts +7 -0
  10. package/dist/cjs/components/memberPopup/panels/ActivitiesSubscribePanel.js +2 -0
  11. package/dist/cjs/components/memberPopup/panels/ActivitiesSubscribePanel.js.map +7 -0
  12. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.d.ts +9 -7
  13. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js +1 -1
  14. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js.map +3 -3
  15. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/index.d.ts +1 -2
  16. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/index.js +1 -1
  17. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/index.js.map +3 -3
  18. package/dist/cjs/components/memberPopup/panels/EarnCredits/ActivatePanel.d.ts +1 -2
  19. package/dist/cjs/components/memberPopup/panels/EarnCredits/ActivatePanel.js +1 -1
  20. package/dist/cjs/components/memberPopup/panels/EarnCredits/ActivatePanel.js.map +3 -3
  21. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js +1 -1
  22. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js.map +2 -2
  23. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.d.ts +1 -3
  24. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js +1 -1
  25. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js.map +3 -3
  26. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.d.ts +1 -2
  27. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js +1 -1
  28. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js.map +3 -3
  29. package/dist/cjs/components/memberPopup/panels/EarnCredits/index.d.ts +1 -8
  30. package/dist/cjs/components/memberPopup/panels/EarnCredits/index.js +1 -1
  31. package/dist/cjs/components/memberPopup/panels/EarnCredits/index.js.map +3 -3
  32. package/dist/cjs/components/memberPopup/panels/Referral/index.js +1 -1
  33. package/dist/cjs/components/memberPopup/panels/Referral/index.js.map +3 -3
  34. package/dist/cjs/components/memberPopup/type.d.ts +29 -3
  35. package/dist/cjs/components/memberPopup/type.js +1 -1
  36. package/dist/cjs/components/memberPopup/type.js.map +2 -2
  37. package/dist/esm/components/credits/context/hooks/useCountries.js +1 -1
  38. package/dist/esm/components/credits/context/hooks/useCountries.js.map +3 -3
  39. package/dist/esm/components/memberPopup/MemberPopup.d.ts +1 -1
  40. package/dist/esm/components/memberPopup/MemberPopup.js +1 -1
  41. package/dist/esm/components/memberPopup/MemberPopup.js.map +3 -3
  42. package/dist/esm/components/memberPopup/context/provider.d.ts +3 -3
  43. package/dist/esm/components/memberPopup/context/provider.js +1 -1
  44. package/dist/esm/components/memberPopup/context/provider.js.map +3 -3
  45. package/dist/esm/components/memberPopup/panels/ActivitiesSubscribePanel.d.ts +7 -0
  46. package/dist/esm/components/memberPopup/panels/ActivitiesSubscribePanel.js +2 -0
  47. package/dist/esm/components/memberPopup/panels/ActivitiesSubscribePanel.js.map +7 -0
  48. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.d.ts +9 -7
  49. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js +1 -1
  50. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js.map +3 -3
  51. package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.d.ts +1 -2
  52. package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.js +1 -1
  53. package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.js.map +3 -3
  54. package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.d.ts +1 -2
  55. package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.js +1 -1
  56. package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.js.map +3 -3
  57. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js +1 -1
  58. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js.map +2 -2
  59. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.d.ts +1 -3
  60. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js +1 -1
  61. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js.map +3 -3
  62. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.d.ts +1 -2
  63. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js +1 -1
  64. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js.map +3 -3
  65. package/dist/esm/components/memberPopup/panels/EarnCredits/index.d.ts +1 -8
  66. package/dist/esm/components/memberPopup/panels/EarnCredits/index.js +1 -1
  67. package/dist/esm/components/memberPopup/panels/EarnCredits/index.js.map +3 -3
  68. package/dist/esm/components/memberPopup/panels/Referral/index.js +1 -1
  69. package/dist/esm/components/memberPopup/panels/Referral/index.js.map +3 -3
  70. package/dist/esm/components/memberPopup/type.d.ts +29 -3
  71. package/dist/esm/components/memberPopup/type.js +1 -1
  72. package/dist/esm/components/memberPopup/type.js.map +2 -2
  73. package/package.json +1 -1
  74. package/src/components/credits/context/hooks/useCountries.ts +4 -15
  75. package/src/components/memberPopup/MemberPopup.tsx +170 -177
  76. package/src/components/memberPopup/context/provider.tsx +24 -4
  77. package/src/components/memberPopup/panels/ActivitiesSubscribePanel.tsx +194 -0
  78. package/src/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.tsx +93 -60
  79. package/src/components/memberPopup/panels/ConsumeCredits/index.tsx +199 -87
  80. package/src/components/memberPopup/panels/EarnCredits/ActivatePanel.tsx +30 -15
  81. package/src/components/memberPopup/panels/EarnCredits/CompleteProfileModal.tsx +6 -1
  82. package/src/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.tsx +65 -35
  83. package/src/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.tsx +15 -6
  84. package/src/components/memberPopup/panels/EarnCredits/index.tsx +131 -86
  85. package/src/components/memberPopup/panels/Referral/index.tsx +35 -16
  86. package/src/components/memberPopup/type.ts +26 -3
  87. package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.d.ts +0 -10
  88. package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.js +0 -2
  89. package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.js.map +0 -7
  90. package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.d.ts +0 -7
  91. package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js +0 -2
  92. package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js.map +0 -7
  93. package/dist/cjs/components/memberPopup/panels/Activity/index.d.ts +0 -7
  94. package/dist/cjs/components/memberPopup/panels/Activity/index.js +0 -2
  95. package/dist/cjs/components/memberPopup/panels/Activity/index.js.map +0 -7
  96. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.d.ts +0 -19
  97. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js +0 -2
  98. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js.map +0 -7
  99. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.d.ts +0 -10
  100. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.js +0 -2
  101. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.js.map +0 -7
  102. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.d.ts +0 -9
  103. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.js +0 -2
  104. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.js.map +0 -7
  105. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.d.ts +0 -13
  106. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js +0 -2
  107. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js.map +0 -7
  108. package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.d.ts +0 -10
  109. package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.js +0 -2
  110. package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.js.map +0 -7
  111. package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.d.ts +0 -7
  112. package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js +0 -2
  113. package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js.map +0 -7
  114. package/dist/esm/components/memberPopup/panels/Activity/index.d.ts +0 -7
  115. package/dist/esm/components/memberPopup/panels/Activity/index.js +0 -2
  116. package/dist/esm/components/memberPopup/panels/Activity/index.js.map +0 -7
  117. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.d.ts +0 -19
  118. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js +0 -2
  119. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js.map +0 -7
  120. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.d.ts +0 -10
  121. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.js +0 -2
  122. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.js.map +0 -7
  123. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.d.ts +0 -9
  124. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.js +0 -2
  125. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.js.map +0 -7
  126. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.d.ts +0 -13
  127. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js +0 -2
  128. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js.map +0 -7
  129. package/dist/index.d.mts +0 -1775
  130. package/dist/index.d.ts +0 -1775
  131. package/dist/index.js +0 -18542
  132. package/dist/index.js.map +0 -1
  133. package/dist/index.mjs +0 -18514
  134. package/dist/index.mjs.map +0 -1
  135. package/src/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.tsx +0 -150
  136. package/src/components/memberPopup/panels/EarnCredits/SubscribeModal.tsx +0 -144
  137. package/src/components/memberPopup/panels/EarnCredits/SubscribePanel.tsx +0 -147
@@ -1,6 +1,6 @@
1
- import { useMemo, useState } from 'react'
1
+ import { useEffect, useMemo, useState } from 'react'
2
2
  import { Picture, Button } from '@anker-in/headless-ui'
3
- import { classNames as cn, useProductsByHandles, useHeadlessContext } from '@anker-in/lib'
3
+ import { gaTrack, classNames as cn, useProductsByHandles, useHeadlessContext } from '@anker-in/lib'
4
4
  import { useMemberPopupContext } from '../../context/provider'
5
5
  import { useViewStack } from '../../context/viewStack'
6
6
  import { useRedeemableList } from './hooks/useRedeemableList'
@@ -19,17 +19,18 @@ type EnrichedItem = ConsumeCreditsItem & {
19
19
  alpcData: ConsumeCreditRuleResponse
20
20
  insufficient: boolean
21
21
  soldOut: boolean
22
+ disabled: boolean
22
23
  product?: any
23
24
  variant?: any
24
25
  }
25
26
 
26
27
  type Props = {
27
28
  copy: ConsumeCreditsopy
28
- onClose: () => void
29
29
  }
30
30
 
31
- function ConsumeCredits({ copy, onClose }: Props) {
32
- const { profile, creditInfo, openSignUpPopup, alpcConfig } = useMemberPopupContext()
31
+ function ConsumeCredits({ copy }: Props) {
32
+ const { profile, creditInfo, openSignUpPopup, alpcConfig, isActivateSuccess, fetchCreditInfo, pageHandle } =
33
+ useMemberPopupContext()
33
34
  const { pushView } = useViewStack()
34
35
  const { brand } = useHeadlessContext()
35
36
  const rounded = ROUNDED_BRANDS.includes(brand)
@@ -37,6 +38,10 @@ function ConsumeCredits({ copy, onClose }: Props) {
37
38
  const [redeemAndBuyError, setRedeemAndBuyError] = useState('')
38
39
  const [loadingId, setLoadingId] = useState<string | number | null>(null)
39
40
 
41
+ useEffect(() => {
42
+ if (profile?.user_id) fetchCreditInfo(profile.user_id)
43
+ }, [])
44
+
40
45
  const isLogin = !!profile
41
46
  const { redeemableList, listLoading } = useRedeemableList(alpcConfig)
42
47
  const { trigger: redeemAndBuy } = useRedeemAndBuy(alpcConfig)
@@ -51,30 +56,50 @@ function ConsumeCredits({ copy, onClose }: Props) {
51
56
  if (!alpcData) return null
52
57
 
53
58
  const availableCredit = Number(creditInfo?.available_credit ?? 0)
54
- const insufficient = isLogin && !!profile?.activated && alpcData.consume_credits > availableCredit
55
- const soldOut = alpcData.is_limited && (!alpcData.remaining_inventory || alpcData.remaining_inventory <= 0)
56
-
57
- let product
58
- let variant
59
- if (item.type === ConsumeCreditsItemType.RedeemAndBuy && item.product) {
60
- product = products?.find((p: any) => p.handle === item.product!.handle)
61
- variant = product?.variants?.find((v: any) => v.sku === item.product!.sku) ?? product?.variants?.[0]
62
- if (!product || !variant) return null
63
- }
59
+ const insufficient = alpcData.consume_credits > availableCredit
60
+ const alpcSoldOut = alpcData.is_limited && (!alpcData.remaining_inventory || alpcData.remaining_inventory <= 0)
64
61
 
65
- return { ...item, alpcData, insufficient, soldOut, product, variant }
62
+ const isPhysicalProduct =
63
+ item.type === ConsumeCreditsItemType.RedeemAndBuy || item.type === ConsumeCreditsItemType.Product
64
+ const product = products?.find((p: any) => p.handle === item.product?.handle)
65
+ const variant = product?.variants?.find((v: any) => v.sku === item.product?.sku) ?? product?.variants?.[0]
66
+ if (isPhysicalProduct && (!product || !variant || !variant?.availableForSale || alpcSoldOut)) {
67
+ return null
68
+ }
69
+ return { ...item, alpcData, insufficient, product, variant }
66
70
  })
67
71
  .filter(Boolean) as EnrichedItem[]
68
72
  }, [copy.list, redeemableList, creditInfo, profile, isLogin, products])
69
73
 
74
+ const isActivate = profile?.activated || isActivateSuccess
75
+
70
76
  function getButtonLabel(item: (typeof list)[number]) {
71
- if (!isLogin) return copy.unlockRewards
72
- if (item.soldOut) return copy.soldOut
77
+ if (!isLogin || !isActivate) {
78
+ return copy.unlockRewards
79
+ }
80
+ if (item.soldOut) {
81
+ return copy.soldOut
82
+ }
73
83
  return copy.btnRedeem
74
84
  }
75
85
 
76
86
  function isDisabled(item: (typeof list)[number]) {
77
- return item.soldOut || item.insufficient
87
+ if (!isLogin || !isActivate) {
88
+ return false
89
+ }
90
+ return item.insufficient
91
+ }
92
+
93
+ function trackRedeemButton(itemTitle: string) {
94
+ gaTrack({
95
+ event: 'ga4Event',
96
+ event_name: 'lp_button',
97
+ event_parameters: {
98
+ position: 'consume_credits',
99
+ button_name: 'redeem',
100
+ info: 'Member Floating Window',
101
+ },
102
+ })
78
103
  }
79
104
 
80
105
  async function handleRedeem(item: (typeof list)[number]) {
@@ -82,7 +107,27 @@ function ConsumeCredits({ copy, onClose }: Props) {
82
107
  openSignUpPopup()
83
108
  return
84
109
  }
110
+ if (!isActivate) {
111
+ pushView({
112
+ type: MemberPopupPanelType.Activate,
113
+ })
114
+ return
115
+ }
116
+ if (item.type === ConsumeCreditsItemType.GiftCard || item.type === ConsumeCreditsItemType.Product) {
117
+ if (copy.creditsPage?.url) {
118
+ const sectionId = copy.creditsPage.redeemListSectionId ?? 'creditsRedeemList'
119
+ const targetUrl = new URL(copy.creditsPage.url, window.location.href)
120
+ targetUrl.searchParams.set('section', sectionId)
121
+ if (pageHandle) {
122
+ targetUrl.searchParams.set('ref', `${pageHandle}_consume_credits`)
123
+ }
124
+ trackRedeemButton(item.title)
125
+ window.open(targetUrl.toString(), '_blank', 'noopener,noreferrer')
126
+ }
127
+ return
128
+ }
85
129
  if (item.type === ConsumeCreditsItemType.Coupon) {
130
+ trackRedeemButton(item.title)
86
131
  pushView({
87
132
  type: MemberPopupPanelType.RedeemCoupon,
88
133
  props: {
@@ -92,7 +137,7 @@ function ConsumeCredits({ copy, onClose }: Props) {
92
137
  ? { url: copy.imageMapping[item.type]!.url, alt: item.image?.alt }
93
138
  : item.image,
94
139
  itemValue: item.value,
95
- itemOff: copy.off,
140
+ itemRules: item.rules,
96
141
  redeemId: Number(item.redeemId),
97
142
  userId: profile?.user_id,
98
143
  alpcConfig,
@@ -101,12 +146,22 @@ function ConsumeCredits({ copy, onClose }: Props) {
101
146
  } else {
102
147
  setRedeemAndBuyError('')
103
148
  setLoadingId(item.redeemId)
149
+ trackRedeemButton(item.title)
104
150
  try {
105
151
  await redeemAndBuy({
106
152
  productVariant: item.variant,
107
153
  redeemId: Number(item.redeemId),
108
154
  userId: profile?.user_id,
109
155
  })
156
+ gaTrack({
157
+ event: 'ga4Event',
158
+ event_name: 'lp_button',
159
+ event_parameters: {
160
+ position: 'consume_credits',
161
+ button_name: 'redeem_success',
162
+ info: 'Member Floating Window',
163
+ },
164
+ })
110
165
  } catch (e: any) {
111
166
  setRedeemAndBuyError(e?.message ?? copy.redeemModal.commonError)
112
167
  } finally {
@@ -117,97 +172,154 @@ function ConsumeCredits({ copy, onClose }: Props) {
117
172
 
118
173
  return (
119
174
  <>
120
- <div className="flex flex-1 flex-col gap-[12px]">
175
+ <div className="flex flex-1 flex-col">
121
176
  <div className="flex flex-col gap-[8px]">
122
177
  {copy.title && <h2 className="text-[24px] font-[700] leading-[1.2] text-[#080a0f]">{copy.title}</h2>}
123
- {copy.subtitle && <p className="text-[16px] font-[700] leading-snug text-[#4a4c56]">{copy.subtitle}</p>}
178
+ {copy.subtitle && (
179
+ <p className="text-[16px] mt-[8px] font-[700] leading-snug text-[#4a4c56]">{copy.subtitle}</p>
180
+ )}
124
181
  </div>
125
182
 
126
- {copy.insufficientCredits && creditInfo && (
127
- <div className="text-[14px] font-[700] text-[#1d1d1f]">
128
- {copy.insufficientCredits}: {numberFormat(creditInfo.available_credit)} {copy.pointUnit}
183
+ {isLogin && (
184
+ <div
185
+ className={cn(
186
+ 'flex items-center mt-[16px] gap-[12px] bg-[#f5f6f7] p-[16px]',
187
+ rounded ? 'rounded-[8px]' : 'rounded-none'
188
+ )}
189
+ >
190
+ <div className="flex flex-1 flex-col gap-[4px]">
191
+ <div className="text-[14px] font-[700] text-[#1e2024]">{copy.balanceLabel ?? 'AnkerCredits Balance'}</div>
192
+ <div className="flex items-end gap-[4px]">
193
+ <span className="text-[24px] font-[700] leading-[1.2] text-[#1e2024]">
194
+ {numberFormat(creditInfo?.available_credit || 0)}
195
+ </span>
196
+ <span className="text-[14px] font-[700] leading-[1.4] text-[#1e2024]">
197
+ {copy.creditUnit ?? 'ankercredits'}
198
+ </span>
199
+ </div>
200
+ </div>
201
+ <div className="h-full w-px bg-[#dadce0]" />
202
+ <div className="flex flex-1 flex-col gap-[4px]">
203
+ <div className="text-[14px] font-[700] text-[#1e2024]">{copy.comingSoonLabel ?? 'Coming Soon'}</div>
204
+ <div className="flex items-end gap-[4px]">
205
+ <span className="text-[24px] font-[700] leading-[1.2] text-[#1e2024]">
206
+ {numberFormat(creditInfo?.pending_credit || 0)}
207
+ </span>
208
+ <span className="text-[14px] font-[700] leading-[1.4] text-[#1e2024]">
209
+ {copy.creditUnit ?? 'ankercredits'}
210
+ </span>
211
+ </div>
212
+ </div>
129
213
  </div>
130
214
  )}
131
- {redeemAndBuyError && <div className="text-[14px] font-[700] text-[#f84d4f]">{redeemAndBuyError}</div>}
215
+ {redeemAndBuyError && <div className="text-[14px] mt-[8px] font-[700] text-[#f84d4f]">{redeemAndBuyError}</div>}
132
216
 
133
217
  {listLoading ? (
134
218
  <div className="flex flex-1 items-center justify-center py-[32px]">
135
219
  <div className="size-[32px] animate-spin rounded-full border-[3px] border-[#080a0f] border-t-transparent" />
136
220
  </div>
137
221
  ) : (
138
- <div className="flex flex-col">
139
- {list.map((item, idx) => (
140
- <div key={idx} className="relative py-[16px]">
141
- {idx > 0 && <div className="absolute inset-x-0 top-0 h-px bg-[#dadce0]" />}
142
- <div className="flex items-center gap-[12px]">
143
- {(() => {
144
- const isRedeemAndBuy = item.type === ConsumeCreditsItemType.RedeemAndBuy
145
- const mappedImgUrl = copy.imageMapping?.[item.type]?.url
146
- const imgUrl = isRedeemAndBuy
147
- ? (item.variant?.image?.url ?? item.product?.images?.[0]?.url ?? item.image?.url)
148
- : (mappedImgUrl ?? item.image?.url)
149
- const imgAlt = isRedeemAndBuy
150
- ? (item.variant?.image?.altText ?? item.product?.images?.[0]?.alt ?? item.image?.alt ?? '')
151
- : (item.image?.alt ?? '')
152
- if (!imgUrl) return null
153
- return (
154
- <div className={cn('relative size-[64px] shrink-0 overflow-hidden', rounded ? 'rounded-[4px]' : 'rounded-none')}>
155
- <Picture
156
- source={imgUrl}
157
- alt={imgAlt}
158
- className="flex size-full items-center justify-center"
159
- imgClassName={isRedeemAndBuy ? 'object-cover' : 'h-full w-auto object-contain'}
160
- />
161
- {!isRedeemAndBuy && item.value && (
162
- <div className="absolute bottom-[20px] left-[4px] text-[14px] font-[800] leading-none text-white">
163
- {item.value} {copy.off}
222
+ <div className="flex mt-[16px] flex-col">
223
+ {list.map((item, idx) => {
224
+ const hasProduct = !!item.product
225
+ return (
226
+ <div key={idx} className="relative py-[16px]">
227
+ {idx > 0 && <div className="absolute inset-x-0 top-0 h-px bg-[#dadce0]" />}
228
+ <div className="flex items-center gap-[12px]">
229
+ {(() => {
230
+ const mappedImgUrl = copy.imageMapping?.[item.type]?.url
231
+ const imgUrl = hasProduct
232
+ ? (item.variant?.image?.url ?? item.product?.images?.[0]?.url ?? item.image?.url)
233
+ : (mappedImgUrl ?? item.image?.url)
234
+ const imgAlt = hasProduct
235
+ ? (item.variant?.image?.altText ?? item.product?.images?.[0]?.alt ?? item.image?.alt ?? '')
236
+ : (item.image?.alt ?? '')
237
+ if (!imgUrl) return null
238
+ return (
239
+ <div
240
+ className={cn(
241
+ 'relative size-[64px] shrink-0 overflow-hidden',
242
+ rounded ? 'rounded-[4px]' : 'rounded-none'
243
+ )}
244
+ >
245
+ <Picture
246
+ source={imgUrl}
247
+ alt={imgAlt}
248
+ className="flex size-full items-center justify-center"
249
+ imgClassName={hasProduct ? 'object-cover' : 'h-full w-auto object-contain'}
250
+ />
251
+ {!hasProduct && item.value && (
252
+ <div className="absolute bottom-[16px] left-[4px] text-[12px] font-[800] leading-none text-white">
253
+ {item.value} {copy.off}
254
+ </div>
255
+ )}
256
+ </div>
257
+ )
258
+ })()}
259
+ <div className="min-w-0 flex-1">
260
+ <div className="text-[16px] font-[700] text-[#1e2024] line-clamp-2">
261
+ {hasProduct && item.product?.title ? item.product.title : item.title}
262
+ </div>
263
+ <div className="mt-[4px] text-[14px] text-[#666]">
264
+ {hasProduct && item.price != null
265
+ ? `${numberFormat(item.alpcData.consume_credits)} ${copy.creditUnit ?? ''} + $${Number(item.price).toFixed(2)}`
266
+ : `${numberFormat(item.alpcData.consume_credits)} ${copy.creditUnit ?? ''}`}
267
+ </div>
268
+ </div>
269
+ <div className="relative group">
270
+ <Button
271
+ className={cn(
272
+ 'bg-[#080a0f] px-[20px] pb-[10px] pt-[11px] text-[14px] font-[700] leading-[1.2] text-white',
273
+ rounded ? 'rounded-[4px]' : 'rounded-none'
274
+ )}
275
+ disabled={isDisabled(item)}
276
+ loading={loadingId === item.redeemId}
277
+ onClick={() => handleRedeem(item)}
278
+ >
279
+ {getButtonLabel(item)}
280
+ </Button>
281
+ {item.insufficient && isLogin && isActivate && (
282
+ <div className="pointer-events-none absolute right-0 top-full z-[100] mt-[10px] hidden w-[200px] group-hover:block">
283
+ <div className="relative bg-[#6D6D6F] p-[16px] text-[14px] font-[700] text-white shadow">
284
+ <p className="text-pretty tracking-[-0.02em] text-[14px]">{copy.insufficientCredits}</p>
285
+ <div className="absolute -top-[8px] right-[20px] size-[16px] origin-top-left rotate-45 rounded-[2px] bg-[#6D6D6F]" />
286
+ </div>
164
287
  </div>
165
288
  )}
166
289
  </div>
167
- )
168
- })()}
169
- <div className="min-w-0 flex-1">
170
- <div className="text-[16px] font-[700] text-[#1e2024] line-clamp-2">
171
- {item.type === ConsumeCreditsItemType.RedeemAndBuy && item.product?.title
172
- ? item.product.title
173
- : item.title}
174
290
  </div>
175
- <div className="mt-[4px] text-[14px] text-[#666]">
176
- {item.type === ConsumeCreditsItemType.RedeemAndBuy && item.price != null
177
- ? `${numberFormat(item.alpcData.consume_credits)} ${copy.pointUnit ?? ''} + $${Number(item.price).toFixed(2)}`
178
- : `${numberFormat(item.alpcData.consume_credits)} ${copy.pointUnit ?? ''}`}
179
- </div>
180
- </div>
181
- <div className="relative group">
182
- <Button
183
- className={cn('bg-[#080a0f] px-[20px] pb-[10px] pt-[11px] text-[14px] font-[700] leading-[1.2] text-white', rounded ? 'rounded-[4px]' : 'rounded-none')}
184
- disabled={isDisabled(item)}
185
- loading={loadingId === item.redeemId}
186
- onClick={() => handleRedeem(item)}
187
- >
188
- {getButtonLabel(item)}
189
- </Button>
190
- {item.insufficient && (
191
- <div className="pointer-events-none absolute left-[48px] top-full mt-[10px] hidden -translate-x-1/2 group-hover:block">
192
- <div className="relative bg-[#6D6D6F] p-[16px] text-[14px] font-[700] text-white shadow md:px-[10px] md:py-[8px]">
193
- <p className="text-pretty tracking-[-0.02em] text-[14px]">{copy.insufficientCredits}</p>
194
- <div className="absolute -top-[8px] left-[48px] size-[16px] origin-top-left rotate-45 rounded-[2px] bg-[#6D6D6F]" />
195
- </div>
196
- </div>
197
- )}
198
291
  </div>
199
- </div>
200
- </div>
201
- ))}
202
- </div>
292
+ )
293
+ })}
294
+ </div>
203
295
  )}
204
296
 
205
297
  {copy.creditsPage?.url && (
206
298
  <a
207
- href={copy.creditsPage.url}
299
+ href={(() => {
300
+ if (!pageHandle) return copy.creditsPage!.url
301
+ try {
302
+ const url = new URL(copy.creditsPage!.url, window.location.href)
303
+ url.searchParams.set('ref', `${pageHandle}_consume_credits`)
304
+ return url.toString()
305
+ } catch {
306
+ return copy.creditsPage!.url
307
+ }
308
+ })()}
208
309
  target="_blank"
209
310
  rel="noopener noreferrer"
210
311
  className="mt-auto self-start text-[14px] font-[700] text-[#080a0f] underline"
312
+ onClick={() =>
313
+ gaTrack({
314
+ event: 'ga4Event',
315
+ event_name: 'lp_button',
316
+ event_parameters: {
317
+ position: 'consume_credits',
318
+ button_name: 'credits_page_link',
319
+ info: 'Member Floating Window',
320
+ },
321
+ })
322
+ }
211
323
  >
212
324
  {copy.creditsPage.label ?? 'Go to Credits Page'}
213
325
  </a>
@@ -11,15 +11,16 @@ import {
11
11
  import { Message } from '../../../registration/authCodeActivate/Message'
12
12
  import type { AuthCodeActivateCopy } from '../../type'
13
13
  import { useRegistration } from '../../../registration'
14
+ import { useMemberPopupContext } from '../../context/provider'
14
15
  import { ROUNDED_BRANDS } from '../../../../constants'
15
16
 
16
17
  type Props = {
17
18
  copy: AuthCodeActivateCopy
18
19
  onClose: () => void
19
- onSuccess?: () => void
20
20
  }
21
21
 
22
- export default function ActivatePanel({ copy, onClose, onSuccess }: Props) {
22
+ export default function ActivatePanel({ copy, onClose }: Props) {
23
+ const { setIsActivateSuccess, fetchCreditInfo, profile } = useMemberPopupContext()
23
24
  const { locale, brand } = useHeadlessContext()
24
25
  const { email } = useRegistration()
25
26
  const rounded = ROUNDED_BRANDS.includes(brand)
@@ -165,14 +166,36 @@ export default function ActivatePanel({ copy, onClose, onSuccess }: Props) {
165
166
  if (data.status >= 200 && data.status < 300) {
166
167
  setMessageStatus('success')
167
168
  setMessageText(copy?.authSuccessMessage || '')
168
- onSuccess?.()
169
+ setIsActivateSuccess(true)
170
+ if (profile?.user_id) fetchCreditInfo(profile.user_id)
171
+ gaTrack({
172
+ event: 'ga4Event',
173
+ event_name: 'lp_button',
174
+ event_parameters: {
175
+ page_group: 'reg_sub_pop',
176
+ position: 'auth_code',
177
+ button_name: 'check_and_active',
178
+ info: 'Member Floating Window',
179
+ },
180
+ })
169
181
  setTimeout(() => {
170
182
  onClose()
171
183
  }, 3000)
172
184
  } else {
173
185
  const errorCode = data?.data?.error_code ?? data?.error_code
174
186
  if (errorCode === '104') {
175
- onSuccess?.()
187
+ setIsActivateSuccess(true)
188
+ if (profile?.user_id) fetchCreditInfo(profile.user_id)
189
+ gaTrack({
190
+ event: 'ga4Event',
191
+ event_name: 'lp_button',
192
+ event_parameters: {
193
+ page_group: 'reg_sub_pop',
194
+ position: 'auth_code',
195
+ button_name: 'check_and_active',
196
+ info: 'Member Floating Window',
197
+ },
198
+ })
176
199
  setTimeout(() => {
177
200
  onClose()
178
201
  }, 3000)
@@ -194,16 +217,6 @@ export default function ActivatePanel({ copy, onClose, onSuccess }: Props) {
194
217
  setMessageText('')
195
218
  clearTimeout(timer)
196
219
  }, 5000)
197
-
198
- gaTrack({
199
- event: 'ga4Event',
200
- event_name: 'lp_button',
201
- event_parameters: {
202
- page_group: 'reg_sub_pop',
203
- position: 'auth_code',
204
- button_name: 'check_and_active',
205
- },
206
- })
207
220
  }, [
208
221
  code,
209
222
  copy?.codeEmptyError,
@@ -215,7 +228,9 @@ export default function ActivatePanel({ copy, onClose, onSuccess }: Props) {
215
228
  sendEmailData,
216
229
  triggerEmailValidation,
217
230
  onClose,
218
- onSuccess,
231
+ setIsActivateSuccess,
232
+ fetchCreditInfo,
233
+ profile,
219
234
  ])
220
235
 
221
236
  return (
@@ -40,6 +40,12 @@ export function CompleteProfileModal({ isOpen, onClose, onSuccess, copy, shopify
40
40
  }
41
41
  }, [profile])
42
42
 
43
+ useEffect(() => {
44
+ if (countries.length > 0 && !country) {
45
+ setCountry(countries[0].id)
46
+ }
47
+ }, [countries])
48
+
43
49
  const validate = useCallback(() => {
44
50
  let hasError = false
45
51
  if (!firstName.trim()) {
@@ -153,7 +159,6 @@ export function CompleteProfileModal({ isOpen, onClose, onSuccess, copy, shopify
153
159
  value={country}
154
160
  onChange={e => setCountry(e.target.value)}
155
161
  >
156
- <option value="" />
157
162
  {countries.map((c: { id: string; name: string }) => (
158
163
  <option key={c.id} value={c.id}>{c.name}</option>
159
164
  ))}