@anker-in/campaign-ui 0.5.0-beta.3 → 0.5.0-beta.4

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 (227) hide show
  1. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  2. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +3 -3
  3. package/dist/cjs/components/credits/context/hooks/useCountries.js +1 -1
  4. package/dist/cjs/components/credits/context/hooks/useCountries.js.map +3 -3
  5. package/dist/cjs/components/credits/context/hooks/useSubscribed.js.map +2 -2
  6. package/dist/cjs/components/index.d.ts +3 -2
  7. package/dist/cjs/components/index.js +1 -1
  8. package/dist/cjs/components/index.js.map +2 -2
  9. package/dist/cjs/components/memberPopup/MemberPopup.d.ts +9 -0
  10. package/dist/cjs/components/memberPopup/MemberPopup.js +2 -0
  11. package/dist/cjs/components/memberPopup/MemberPopup.js.map +7 -0
  12. package/dist/cjs/components/memberPopup/context/provider.d.ts +11 -0
  13. package/dist/cjs/components/memberPopup/context/provider.js +2 -0
  14. package/dist/cjs/components/memberPopup/context/provider.js.map +7 -0
  15. package/dist/cjs/components/memberPopup/context/viewStack.d.ts +12 -0
  16. package/dist/cjs/components/memberPopup/context/viewStack.js +2 -0
  17. package/dist/cjs/components/memberPopup/context/viewStack.js.map +7 -0
  18. package/dist/cjs/components/memberPopup/icons.d.ts +27 -0
  19. package/dist/cjs/components/memberPopup/icons.js +2 -0
  20. package/dist/cjs/components/memberPopup/icons.js.map +7 -0
  21. package/dist/cjs/components/memberPopup/index.d.ts +4 -0
  22. package/dist/cjs/components/memberPopup/index.js +2 -0
  23. package/dist/cjs/components/memberPopup/index.js.map +7 -0
  24. package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.d.ts +10 -0
  25. package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.js +2 -0
  26. package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.js.map +7 -0
  27. package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.d.ts +7 -0
  28. package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js +2 -0
  29. package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js.map +7 -0
  30. package/dist/cjs/components/memberPopup/panels/Activity/index.d.ts +7 -0
  31. package/dist/cjs/components/memberPopup/panels/Activity/index.js +2 -0
  32. package/dist/cjs/components/memberPopup/panels/Activity/index.js.map +7 -0
  33. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.d.ts +19 -0
  34. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js +2 -0
  35. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js.map +7 -0
  36. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.d.ts +20 -0
  37. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js +2 -0
  38. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js.map +7 -0
  39. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.d.ts +12 -0
  40. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.js +2 -0
  41. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.js.map +7 -0
  42. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.d.ts +17 -0
  43. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.js +2 -0
  44. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.js.map +7 -0
  45. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.d.ts +8 -0
  46. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.js +2 -0
  47. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.js.map +7 -0
  48. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/index.d.ts +7 -0
  49. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/index.js +2 -0
  50. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/index.js.map +7 -0
  51. package/dist/cjs/components/memberPopup/panels/EarnCredits/ActivatePanel.d.ts +8 -0
  52. package/dist/cjs/components/memberPopup/panels/EarnCredits/ActivatePanel.js +2 -0
  53. package/dist/cjs/components/memberPopup/panels/EarnCredits/ActivatePanel.js.map +7 -0
  54. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfileModal.d.ts +10 -0
  55. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js +2 -0
  56. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js.map +7 -0
  57. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.d.ts +9 -0
  58. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js +2 -0
  59. package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js.map +7 -0
  60. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.d.ts +9 -0
  61. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js +2 -0
  62. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js.map +7 -0
  63. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.d.ts +10 -0
  64. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.js +2 -0
  65. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.js.map +7 -0
  66. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.d.ts +9 -0
  67. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.js +2 -0
  68. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.js.map +7 -0
  69. package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useActivities.d.ts +18 -0
  70. package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useActivities.js +2 -0
  71. package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useActivities.js.map +7 -0
  72. package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.d.ts +9 -0
  73. package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.js +2 -0
  74. package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.js.map +7 -0
  75. package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.d.ts +17 -0
  76. package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.js +2 -0
  77. package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.js.map +7 -0
  78. package/dist/cjs/components/memberPopup/panels/EarnCredits/index.d.ts +13 -0
  79. package/dist/cjs/components/memberPopup/panels/EarnCredits/index.js +2 -0
  80. package/dist/cjs/components/memberPopup/panels/EarnCredits/index.js.map +7 -0
  81. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.d.ts +13 -0
  82. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js +2 -0
  83. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js.map +7 -0
  84. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralOverview.d.ts +12 -0
  85. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralOverview.js +2 -0
  86. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralOverview.js.map +7 -0
  87. package/dist/cjs/components/memberPopup/panels/Referral/index.d.ts +7 -0
  88. package/dist/cjs/components/memberPopup/panels/Referral/index.js +2 -0
  89. package/dist/cjs/components/memberPopup/panels/Referral/index.js.map +7 -0
  90. package/dist/cjs/components/memberPopup/type.d.ts +216 -0
  91. package/dist/cjs/components/memberPopup/type.js +2 -0
  92. package/dist/cjs/components/memberPopup/type.js.map +7 -0
  93. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  94. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +3 -3
  95. package/dist/esm/components/credits/context/hooks/useCountries.js +1 -1
  96. package/dist/esm/components/credits/context/hooks/useCountries.js.map +3 -3
  97. package/dist/esm/components/credits/context/hooks/useSubscribed.js.map +2 -2
  98. package/dist/esm/components/index.d.ts +3 -2
  99. package/dist/esm/components/index.js +1 -1
  100. package/dist/esm/components/index.js.map +2 -2
  101. package/dist/esm/components/memberPopup/MemberPopup.d.ts +9 -0
  102. package/dist/esm/components/memberPopup/MemberPopup.js +2 -0
  103. package/dist/esm/components/memberPopup/MemberPopup.js.map +7 -0
  104. package/dist/esm/components/memberPopup/context/provider.d.ts +11 -0
  105. package/dist/esm/components/memberPopup/context/provider.js +2 -0
  106. package/dist/esm/components/memberPopup/context/provider.js.map +7 -0
  107. package/dist/esm/components/memberPopup/context/viewStack.d.ts +12 -0
  108. package/dist/esm/components/memberPopup/context/viewStack.js +2 -0
  109. package/dist/esm/components/memberPopup/context/viewStack.js.map +7 -0
  110. package/dist/esm/components/memberPopup/icons.d.ts +27 -0
  111. package/dist/esm/components/memberPopup/icons.js +2 -0
  112. package/dist/esm/components/memberPopup/icons.js.map +7 -0
  113. package/dist/esm/components/memberPopup/index.d.ts +4 -0
  114. package/dist/esm/components/memberPopup/index.js +2 -0
  115. package/dist/esm/components/memberPopup/index.js.map +7 -0
  116. package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.d.ts +10 -0
  117. package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.js +2 -0
  118. package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.js.map +7 -0
  119. package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.d.ts +7 -0
  120. package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js +2 -0
  121. package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js.map +7 -0
  122. package/dist/esm/components/memberPopup/panels/Activity/index.d.ts +7 -0
  123. package/dist/esm/components/memberPopup/panels/Activity/index.js +2 -0
  124. package/dist/esm/components/memberPopup/panels/Activity/index.js.map +7 -0
  125. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.d.ts +19 -0
  126. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js +2 -0
  127. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js.map +7 -0
  128. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.d.ts +20 -0
  129. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js +2 -0
  130. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js.map +7 -0
  131. package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.d.ts +12 -0
  132. package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.js +2 -0
  133. package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.js.map +7 -0
  134. package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.d.ts +17 -0
  135. package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.js +2 -0
  136. package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.js.map +7 -0
  137. package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.d.ts +8 -0
  138. package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.js +2 -0
  139. package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.js.map +7 -0
  140. package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.d.ts +7 -0
  141. package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.js +2 -0
  142. package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.js.map +7 -0
  143. package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.d.ts +8 -0
  144. package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.js +2 -0
  145. package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.js.map +7 -0
  146. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.d.ts +10 -0
  147. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js +2 -0
  148. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js.map +7 -0
  149. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.d.ts +9 -0
  150. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js +2 -0
  151. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js.map +7 -0
  152. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.d.ts +9 -0
  153. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js +2 -0
  154. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js.map +7 -0
  155. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.d.ts +10 -0
  156. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.js +2 -0
  157. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.js.map +7 -0
  158. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.d.ts +9 -0
  159. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.js +2 -0
  160. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.js.map +7 -0
  161. package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useActivities.d.ts +18 -0
  162. package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useActivities.js +2 -0
  163. package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useActivities.js.map +7 -0
  164. package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.d.ts +9 -0
  165. package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.js +2 -0
  166. package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.js.map +7 -0
  167. package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.d.ts +17 -0
  168. package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.js +2 -0
  169. package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.js.map +7 -0
  170. package/dist/esm/components/memberPopup/panels/EarnCredits/index.d.ts +13 -0
  171. package/dist/esm/components/memberPopup/panels/EarnCredits/index.js +2 -0
  172. package/dist/esm/components/memberPopup/panels/EarnCredits/index.js.map +7 -0
  173. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.d.ts +13 -0
  174. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js +2 -0
  175. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js.map +7 -0
  176. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralOverview.d.ts +12 -0
  177. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralOverview.js +2 -0
  178. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralOverview.js.map +7 -0
  179. package/dist/esm/components/memberPopup/panels/Referral/index.d.ts +7 -0
  180. package/dist/esm/components/memberPopup/panels/Referral/index.js +2 -0
  181. package/dist/esm/components/memberPopup/panels/Referral/index.js.map +7 -0
  182. package/dist/esm/components/memberPopup/type.d.ts +216 -0
  183. package/dist/esm/components/memberPopup/type.js +2 -0
  184. package/dist/esm/components/memberPopup/type.js.map +7 -0
  185. package/dist/index.d.mts +1775 -0
  186. package/dist/index.d.ts +1775 -0
  187. package/dist/index.js +18542 -0
  188. package/dist/index.js.map +1 -0
  189. package/dist/index.mjs +18514 -0
  190. package/dist/index.mjs.map +1 -0
  191. package/package.json +1 -2
  192. package/src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx +5 -7
  193. package/src/components/credits/context/hooks/useCountries.ts +5 -15
  194. package/src/components/credits/context/hooks/useSubscribed.ts +0 -1
  195. package/src/components/index.ts +4 -2
  196. package/src/components/memberPopup/MemberPopup.tsx +325 -0
  197. package/src/components/memberPopup/context/provider.tsx +52 -0
  198. package/src/components/memberPopup/context/viewStack.tsx +23 -0
  199. package/src/components/memberPopup/icons.tsx +147 -0
  200. package/src/components/memberPopup/index.ts +24 -0
  201. package/src/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.tsx +150 -0
  202. package/src/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.tsx +142 -0
  203. package/src/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.ts +33 -0
  204. package/src/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.ts +38 -0
  205. package/src/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.ts +55 -0
  206. package/src/components/memberPopup/panels/ConsumeCredits/index.tsx +220 -0
  207. package/src/components/memberPopup/panels/EarnCredits/ActivatePanel.tsx +323 -0
  208. package/src/components/memberPopup/panels/EarnCredits/CompleteProfileModal.tsx +191 -0
  209. package/src/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.tsx +184 -0
  210. package/src/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.tsx +130 -0
  211. package/src/components/memberPopup/panels/EarnCredits/SubscribeModal.tsx +144 -0
  212. package/src/components/memberPopup/panels/EarnCredits/SubscribePanel.tsx +147 -0
  213. package/src/components/memberPopup/panels/EarnCredits/hooks/useActivities.ts +64 -0
  214. package/src/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.ts +32 -0
  215. package/src/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.ts +50 -0
  216. package/src/components/memberPopup/panels/EarnCredits/index.tsx +215 -0
  217. package/src/components/memberPopup/panels/Referral/hooks/useReferralOverview.ts +41 -0
  218. package/src/components/memberPopup/panels/Referral/index.tsx +108 -0
  219. package/src/components/memberPopup/type.ts +230 -0
  220. package/tailwind.config.ts +4 -0
  221. package/dist/cjs/stories/CartCard.stories.d.ts +0 -33
  222. package/dist/cjs/stories/CartCard.stories.js +0 -21
  223. package/dist/cjs/stories/CartCard.stories.js.map +0 -7
  224. package/dist/esm/stories/CartCard.stories.d.ts +0 -33
  225. package/dist/esm/stories/CartCard.stories.js +0 -21
  226. package/dist/esm/stories/CartCard.stories.js.map +0 -7
  227. package/src/stories/CartCard.stories.tsx +0 -459
@@ -0,0 +1,24 @@
1
+ export { MemberPopup } from './MemberPopup'
2
+ export { useMemberPopupContext } from './context/provider'
3
+
4
+ export {
5
+ MemberPopupPanelType,
6
+ MemberPopupActionType,
7
+ EarnCreditsTaskType,
8
+ ConsumeCreditsItemType,
9
+ } from './type'
10
+
11
+ export type {
12
+ MemberPopupCopy,
13
+ MemberPopupItem,
14
+ MemberPopupLink,
15
+ EarnCreditsCopy,
16
+ CompleteProfileCopy,
17
+ SubscribeModalCopy,
18
+ ConsumeCreditsopy,
19
+ ConsumeCreditsItem,
20
+ RedeemCouponModalCopy,
21
+ ReferralCopy,
22
+ AlpcConfig,
23
+ MemberPopupContextValue,
24
+ } from './type'
@@ -0,0 +1,150 @@
1
+ import { useCallback, useEffect, useState } from 'react'
2
+ import { Button, Text } from '@anker-in/headless-ui'
3
+ import { classNames as cn } from '@anker-in/lib'
4
+ import { CreditsModalContainer } from '../../../credits/modal/ModalContainer'
5
+ import { AlpcErrorCode } from '../../../credits/context/const'
6
+ import { useRedeemCoupon } from './hooks/useRedeemCoupon'
7
+ import useCopy from '../../../credits/context/hooks/useCopy'
8
+ import type { RedeemCouponModalCopy, AlpcConfig } from '../../type'
9
+
10
+ type Props = {
11
+ isOpen: boolean
12
+ onClose: () => void
13
+ itemTitle: string
14
+ itemCredits: number
15
+ itemImage?: { url: string; alt?: string }
16
+ redeemId: number
17
+ userId?: string
18
+ copy: RedeemCouponModalCopy
19
+ pointUnit?: string
20
+ alpcConfig: AlpcConfig
21
+ onError?: (code: number) => void
22
+ }
23
+
24
+ export function RedeemCouponModal({
25
+ isOpen,
26
+ onClose,
27
+ itemTitle,
28
+ itemCredits,
29
+ itemImage,
30
+ redeemId,
31
+ userId,
32
+ copy,
33
+ pointUnit = 'pts',
34
+ alpcConfig,
35
+ onError,
36
+ }: Props) {
37
+ const [couponCode, setCouponCode] = useState<string>()
38
+ const [errorInfo, setErrorInfo] = useState('')
39
+ const [disabled, setDisabled] = useState(false)
40
+ const { copy: copyText, copied } = useCopy()
41
+
42
+ const { isMutating: loading, trigger } = useRedeemCoupon(alpcConfig)
43
+
44
+ const handleConfirm = useCallback(async () => {
45
+ setErrorInfo('')
46
+ const { data } = await trigger({ user_id: userId, rule_id: redeemId })
47
+ if (!data) {
48
+ setErrorInfo(copy.commonError)
49
+ return
50
+ }
51
+ if (data.data?.success) {
52
+ setCouponCode(data.data.coupon_code)
53
+ } else {
54
+ let msg = copy.commonError
55
+ if (data.code === AlpcErrorCode.CodeLpcShopifyCouponRuleRedeemLimit) {
56
+ msg = copy.redeemLimitError
57
+ setDisabled(true)
58
+ } else if (data.code === AlpcErrorCode.CodeLpcRuleInventoryNotEnough) {
59
+ msg = copy.inventoryNotEnough
60
+ setDisabled(true)
61
+ } else if (data.code === AlpcErrorCode.CodeLpcNotEnoughCredits) {
62
+ msg = copy.creditsNotEnough
63
+ setDisabled(true)
64
+ } else if (data.code === AlpcErrorCode.CodeCrossSiteError) {
65
+ msg = copy.crossSiteError
66
+ setDisabled(true)
67
+ }
68
+ setErrorInfo(msg)
69
+ onError?.(data.code)
70
+ }
71
+ }, [trigger, userId, redeemId, copy, onError])
72
+
73
+ useEffect(() => {
74
+ if (!isOpen) {
75
+ setCouponCode(undefined)
76
+ setErrorInfo('')
77
+ setDisabled(false)
78
+ }
79
+ }, [isOpen])
80
+
81
+ return (
82
+ <CreditsModalContainer
83
+ isOpen={isOpen}
84
+ onClose={onClose}
85
+ className="w-[540px] md:h-auto"
86
+ scrollClassName="mb-[40px] px-0 min-l:px-0 md:mb-[24px]"
87
+ titleClassName="h-[56px] bg-[#f5f5f7]"
88
+ >
89
+ <div className="bg-[#f5f5f7] px-[48px] pb-[24px] md:px-[16px]">
90
+ {itemImage?.url && (
91
+ <div className="relative mx-auto h-[164px] w-[324px]">
92
+ <img src={itemImage.url} alt={itemImage.alt ?? ''} className="h-full w-full object-cover" />
93
+ </div>
94
+ )}
95
+ <p className={cn('mt-[30px] text-center text-[24px] font-bold md:mt-[18px] md:text-[20px]')}>
96
+ {itemCredits} {pointUnit}
97
+ </p>
98
+ </div>
99
+
100
+ <div className="bg-white px-[48px] pt-[24px] text-center md:px-[16px]">
101
+ {!couponCode ? (
102
+ <>
103
+ <Text className="block text-[24px] font-bold md:text-[20px]" html={copy.confirmTitle} />
104
+ <Button
105
+ loading={loading}
106
+ variant="primary"
107
+ size="lg"
108
+ className="mt-[16px] w-full"
109
+ onClick={handleConfirm}
110
+ disabled={disabled}
111
+ >
112
+ {copy.confirmButton}
113
+ </Button>
114
+ </>
115
+ ) : (
116
+ <>
117
+ <div className="flex items-center justify-center gap-[10px]">
118
+ <span className="text-[24px] font-bold">{couponCode}</span>
119
+ <button
120
+ className={cn('text-[16px] font-bold underline', copied && 'pointer-events-none')}
121
+ onClick={() => copyText(couponCode)}
122
+ >
123
+ {copied ? 'Copied' : 'Copy'}
124
+ </button>
125
+ </div>
126
+ <Text className="mb-[4px] mt-[16px] text-[24px] font-bold md:text-[20px]" html={copy.successTitle} />
127
+ <Text className="text-[16px] font-bold text-[#6d6d6f] md:text-[14px]" html={copy.successDesc} />
128
+ <Button
129
+ variant="primary"
130
+ size="lg"
131
+ className="mt-[16px] w-full"
132
+ onClick={() => {
133
+ if (copy.successButtonUrl) window.location.href = copy.successButtonUrl
134
+ onClose()
135
+ }}
136
+ >
137
+ {copy.successButton}
138
+ </Button>
139
+ </>
140
+ )}
141
+ </div>
142
+
143
+ {errorInfo && (
144
+ <div className="bg-white px-[48px] py-[12px] text-center md:px-[16px]">
145
+ <Text className="text-[14px] font-semibold text-[#ff0000]" html={errorInfo} />
146
+ </div>
147
+ )}
148
+ </CreditsModalContainer>
149
+ )
150
+ }
@@ -0,0 +1,142 @@
1
+ import { useCallback, useEffect, useState } from 'react'
2
+ import { Text, Button } from '@anker-in/headless-ui'
3
+ import { useHeadlessContext, classNames as cn } from '@anker-in/lib'
4
+ import { AlpcErrorCode } from '../../../credits/context/const'
5
+ import { useRedeemCoupon } from './hooks/useRedeemCoupon'
6
+ import useCopy from '../../../credits/context/hooks/useCopy'
7
+ import type { RedeemCouponModalCopy, AlpcConfig } from '../../type'
8
+ import { ROUNDED_BRANDS } from '../../../../constants'
9
+
10
+ type Props = {
11
+ onClose: () => void
12
+ itemTitle: string
13
+ itemCredits: number
14
+ itemImage?: { url: string; alt?: string }
15
+ itemValue?: string
16
+ itemOff?: string
17
+ redeemId: number
18
+ userId?: string
19
+ copy: RedeemCouponModalCopy
20
+ pointUnit?: string
21
+ alpcConfig: AlpcConfig
22
+ onError?: (code: number) => void
23
+ }
24
+
25
+ export default function RedeemCouponPanel({
26
+ onClose,
27
+ itemTitle,
28
+ itemCredits,
29
+ itemImage,
30
+ itemValue,
31
+ itemOff,
32
+ redeemId,
33
+ userId,
34
+ copy,
35
+ pointUnit = 'pts',
36
+ alpcConfig,
37
+ onError,
38
+ }: Props) {
39
+ const [couponCode, setCouponCode] = useState<string>()
40
+ const [errorInfo, setErrorInfo] = useState('')
41
+ const [disabled, setDisabled] = useState(false)
42
+ const { copy: copyText, copied } = useCopy()
43
+ const { brand } = useHeadlessContext()
44
+ const rounded = ROUNDED_BRANDS.includes(brand)
45
+
46
+ const { isMutating: loading, trigger } = useRedeemCoupon(alpcConfig)
47
+
48
+ const handleConfirm = useCallback(async () => {
49
+ setErrorInfo('')
50
+ const { data } = await trigger({ user_id: userId, rule_id: redeemId })
51
+ if (!data) {
52
+ setErrorInfo(copy.commonError)
53
+ return
54
+ }
55
+ if (data.data?.success) {
56
+ setCouponCode(data.data.coupon_code)
57
+ } else {
58
+ let msg = copy.commonError
59
+ if (data.code === AlpcErrorCode.CodeLpcShopifyCouponRuleRedeemLimit) {
60
+ msg = copy.redeemLimitError
61
+ setDisabled(true)
62
+ } else if (data.code === AlpcErrorCode.CodeLpcRuleInventoryNotEnough) {
63
+ msg = copy.inventoryNotEnough
64
+ setDisabled(true)
65
+ } else if (data.code === AlpcErrorCode.CodeLpcNotEnoughCredits) {
66
+ msg = copy.creditsNotEnough
67
+ setDisabled(true)
68
+ } else if (data.code === AlpcErrorCode.CodeCrossSiteError) {
69
+ msg = copy.crossSiteError
70
+ setDisabled(true)
71
+ }
72
+ setErrorInfo(msg)
73
+ onError?.(data.code)
74
+ }
75
+ }, [trigger, userId, redeemId, copy, onError])
76
+
77
+ useEffect(() => {
78
+ setCouponCode(undefined)
79
+ setErrorInfo('')
80
+ setDisabled(false)
81
+ }, [redeemId])
82
+
83
+ return (
84
+ <div className="flex flex-col gap-[16px]">
85
+ {itemImage?.url && (
86
+ <div className={cn('relative overflow-hidden w-fit mx-auto', rounded ? 'rounded-[8px]' : 'rounded-none')}>
87
+ <img src={itemImage.url} alt={itemImage.alt ?? ''} className="mx-auto h-[140px] object-contain" />
88
+ {itemValue && (
89
+ <div className="absolute bottom-[20px] left-[20px] text-[32px] font-[800] leading-none text-white">
90
+ {itemValue}
91
+ {itemOff ? ` ${itemOff}` : ''}
92
+ </div>
93
+ )}
94
+ </div>
95
+ )}
96
+
97
+ <p className="text-center text-[24px] font-[700] text-[#1d1d1f]">
98
+ {itemCredits} {pointUnit}
99
+ </p>
100
+
101
+ {!couponCode ? (
102
+ <div className="flex flex-col gap-[12px] text-center">
103
+ <Text className="block text-[20px] font-[700]" html={copy.confirmTitle} />
104
+ <Button
105
+ className={cn('w-full py-[12px] text-[14px] font-[700] text-white disabled:opacity-60 bg-[#080a0f]', rounded ? 'rounded-[4px]' : 'rounded-none')}
106
+ disabled={disabled}
107
+ loading={loading}
108
+ onClick={handleConfirm}
109
+ >
110
+ {copy.confirmButton}
111
+ </Button>
112
+ </div>
113
+ ) : (
114
+ <div className="flex flex-col gap-[12px] text-center">
115
+ <div className="flex items-center justify-center gap-[10px]">
116
+ <span className="text-[24px] font-[700]">{couponCode}</span>
117
+ <button
118
+ className="cursor-pointer text-[14px] font-[700] underline disabled:pointer-events-none"
119
+ disabled={copied}
120
+ onClick={() => copyText(couponCode)}
121
+ >
122
+ {copied ? 'Copied' : 'Copy'}
123
+ </button>
124
+ </div>
125
+ <Text className="text-[20px] font-[700]" html={copy.successTitle} />
126
+ <Text className="text-[14px] font-[700] text-[#6d6d6f]" html={copy.successDesc} />
127
+ <Button
128
+ className={cn('w-full py-[12px] text-[14px] font-[700] text-white bg-[#080a0f]', rounded ? 'rounded-[4px]' : 'rounded-none')}
129
+ onClick={() => {
130
+ if (copy.successButtonUrl) window.location.href = copy.successButtonUrl
131
+ onClose()
132
+ }}
133
+ >
134
+ {copy.successButton}
135
+ </Button>
136
+ </div>
137
+ )}
138
+
139
+ {errorInfo && <Text className="text-center text-[14px] font-[700] text-[#ff0000]" html={errorInfo} />}
140
+ </div>
141
+ )
142
+ }
@@ -0,0 +1,33 @@
1
+ import { useBuyNow } from '@anker-in/lib'
2
+ import { useRedeemCoupon } from './useRedeemCoupon'
3
+ import type { AlpcConfig } from '../../../type'
4
+ import type { ProductVariant } from '@anker-in/lib'
5
+
6
+ type RedeemAndBuyArg = {
7
+ productVariant: ProductVariant
8
+ quantity?: number
9
+ redeemId: number
10
+ userId?: string
11
+ }
12
+
13
+ export function useRedeemAndBuy(config: AlpcConfig) {
14
+ const { trigger: redeemTrigger } = useRedeemCoupon(config)
15
+ const { trigger: buyNow } = useBuyNow({}, { throwOnError: true })
16
+
17
+ const trigger = async (arg: RedeemAndBuyArg) => {
18
+ const { data } = await redeemTrigger({
19
+ user_id: arg.userId,
20
+ rule_id: arg.redeemId,
21
+ })
22
+ if (data?.data?.coupon_code) {
23
+ await buyNow({
24
+ discountCodes: [data.data.coupon_code],
25
+ lineItems: [{ variant: arg.productVariant, quantity: arg.quantity ?? 1 }],
26
+ })
27
+ } else {
28
+ throw new Error(data?.msg ?? 'Redeem failed')
29
+ }
30
+ }
31
+
32
+ return { trigger }
33
+ }
@@ -0,0 +1,38 @@
1
+ import useSWRMutation from 'swr/mutation'
2
+ import { getAlpcPath } from '../../../../credits/context/utils'
3
+ import { ALPC_COUNTRY_MAP } from '../../../../credits/context/const'
4
+ import type { AlpcConfig } from '../../../type'
5
+
6
+ type RedeemCouponResponse = {
7
+ data: {
8
+ data: { success: boolean; coupon_code: string }
9
+ msg: string
10
+ code: number
11
+ }
12
+ }
13
+
14
+ type RedeemCouponParams = {
15
+ user_id?: string
16
+ rule_id: number
17
+ }
18
+
19
+ export function useRedeemCoupon(config: AlpcConfig) {
20
+ const { brand, locale, appName, apiBaseUrl } = config
21
+
22
+ return useSWRMutation<RedeemCouponResponse, Error, string, RedeemCouponParams>(
23
+ `/v1/credit/redeem/coupon`,
24
+ async (url: string, { arg }) => {
25
+ const response = await fetch(getAlpcPath(locale, apiBaseUrl) + url, {
26
+ method: 'POST',
27
+ headers: { 'Content-Type': 'application/json', 'current-language': locale },
28
+ body: JSON.stringify({
29
+ brand,
30
+ country: ALPC_COUNTRY_MAP.get(locale) || locale,
31
+ app_name: appName,
32
+ ...arg,
33
+ }),
34
+ })
35
+ return response.json()
36
+ }
37
+ )
38
+ }
@@ -0,0 +1,55 @@
1
+ import { useCallback, useEffect, useState } from 'react'
2
+ import useSWRMutation from 'swr/mutation'
3
+ import { getAlpcPath, languageTerritory } from '../../../../credits/context/utils'
4
+ import { ALPC_COUNTRY_MAP, AlpcConsumeType } from '../../../../credits/context/const'
5
+ import type { ConsumeCreditRuleResponse } from '../../../../credits/context/response'
6
+ import type { AlpcConfig } from '../../../type'
7
+
8
+ export function useRedeemableList(config: AlpcConfig) {
9
+ const { brand, locale, appName, apiBaseUrl } = config
10
+
11
+ let resolvedLocale = locale
12
+ if (locale === 'pl') resolvedLocale = 'eu'
13
+
14
+ const [redeemableList, setRedeemableList] = useState<ConsumeCreditRuleResponse[]>([])
15
+
16
+ const { isMutating: listLoading, trigger } = useSWRMutation(
17
+ `/v1/credit/consume_credit_rules`,
18
+ async (url: string, { arg }: { arg: Record<string, any> }) => {
19
+ const response = await fetch(getAlpcPath(resolvedLocale, apiBaseUrl) + url, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json', 'current-language': resolvedLocale },
22
+ body: JSON.stringify(arg),
23
+ })
24
+ return response.json()
25
+ }
26
+ )
27
+
28
+ const getRedeemableList = useCallback(
29
+ (consumeType?: AlpcConsumeType) => {
30
+ trigger(
31
+ {
32
+ brand,
33
+ page: 1,
34
+ page_size: 999,
35
+ country: (ALPC_COUNTRY_MAP.get(resolvedLocale) || resolvedLocale)?.toUpperCase(),
36
+ app_name: appName,
37
+ ...(consumeType !== undefined && { consume_type: consumeType }),
38
+ },
39
+ {
40
+ onSuccess({ data }) {
41
+ setRedeemableList(data?.data?.consume_credit_rules ?? [])
42
+ },
43
+ }
44
+ )
45
+ },
46
+ [brand, resolvedLocale, appName, trigger]
47
+ )
48
+
49
+ useEffect(() => {
50
+ if (!brand) return
51
+ getRedeemableList()
52
+ }, [brand, getRedeemableList])
53
+
54
+ return { redeemableList, listLoading, getRedeemableList }
55
+ }
@@ -0,0 +1,220 @@
1
+ import { useMemo, useState } from 'react'
2
+ import { Picture, Button } from '@anker-in/headless-ui'
3
+ import { classNames as cn, useProductsByHandles, useHeadlessContext } from '@anker-in/lib'
4
+ import { useMemberPopupContext } from '../../context/provider'
5
+ import { useViewStack } from '../../context/viewStack'
6
+ import { useRedeemableList } from './hooks/useRedeemableList'
7
+ import { useRedeemAndBuy } from './hooks/useRedeemAndBuy'
8
+ import {
9
+ ConsumeCreditsItemType,
10
+ MemberPopupPanelType,
11
+ type ConsumeCreditsopy,
12
+ type ConsumeCreditsItem,
13
+ } from '../../type'
14
+ import { numberFormat } from '../../../credits/context/utils'
15
+ import type { ConsumeCreditRuleResponse } from '../../../credits/context/response'
16
+ import { ROUNDED_BRANDS } from '../../../../constants'
17
+
18
+ type EnrichedItem = ConsumeCreditsItem & {
19
+ alpcData: ConsumeCreditRuleResponse
20
+ insufficient: boolean
21
+ soldOut: boolean
22
+ product?: any
23
+ variant?: any
24
+ }
25
+
26
+ type Props = {
27
+ copy: ConsumeCreditsopy
28
+ onClose: () => void
29
+ }
30
+
31
+ function ConsumeCredits({ copy, onClose }: Props) {
32
+ const { profile, creditInfo, openSignUpPopup, alpcConfig } = useMemberPopupContext()
33
+ const { pushView } = useViewStack()
34
+ const { brand } = useHeadlessContext()
35
+ const rounded = ROUNDED_BRANDS.includes(brand)
36
+
37
+ const [redeemAndBuyError, setRedeemAndBuyError] = useState('')
38
+ const [loadingId, setLoadingId] = useState<string | number | null>(null)
39
+
40
+ const isLogin = !!profile
41
+ const { redeemableList, listLoading } = useRedeemableList(alpcConfig)
42
+ const { trigger: redeemAndBuy } = useRedeemAndBuy(alpcConfig)
43
+
44
+ const handles = copy.list.map(item => item.product?.handle).filter(Boolean) as string[]
45
+ const { data: products } = useProductsByHandles({ handles })
46
+
47
+ const list = useMemo(() => {
48
+ return copy.list
49
+ .map(item => {
50
+ const alpcData = redeemableList.find(r => String(r.id) === String(item.redeemId))
51
+ if (!alpcData) return null
52
+
53
+ 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
+ }
64
+
65
+ return { ...item, alpcData, insufficient, soldOut, product, variant }
66
+ })
67
+ .filter(Boolean) as EnrichedItem[]
68
+ }, [copy.list, redeemableList, creditInfo, profile, isLogin, products])
69
+
70
+ function getButtonLabel(item: (typeof list)[number]) {
71
+ if (!isLogin) return copy.unlockRewards
72
+ if (item.soldOut) return copy.soldOut
73
+ return copy.btnRedeem
74
+ }
75
+
76
+ function isDisabled(item: (typeof list)[number]) {
77
+ return item.soldOut || item.insufficient
78
+ }
79
+
80
+ async function handleRedeem(item: (typeof list)[number]) {
81
+ if (!isLogin) {
82
+ openSignUpPopup()
83
+ return
84
+ }
85
+ if (item.type === ConsumeCreditsItemType.Coupon) {
86
+ pushView({
87
+ type: MemberPopupPanelType.RedeemCoupon,
88
+ props: {
89
+ itemTitle: item.title,
90
+ itemCredits: item.alpcData.consume_credits,
91
+ itemImage: copy.imageMapping?.[item.type]
92
+ ? { url: copy.imageMapping[item.type]!.url, alt: item.image?.alt }
93
+ : item.image,
94
+ itemValue: item.value,
95
+ itemOff: copy.off,
96
+ redeemId: Number(item.redeemId),
97
+ userId: profile?.user_id,
98
+ alpcConfig,
99
+ },
100
+ })
101
+ } else {
102
+ setRedeemAndBuyError('')
103
+ setLoadingId(item.redeemId)
104
+ try {
105
+ await redeemAndBuy({
106
+ productVariant: item.variant,
107
+ redeemId: Number(item.redeemId),
108
+ userId: profile?.user_id,
109
+ })
110
+ } catch (e: any) {
111
+ setRedeemAndBuyError(e?.message ?? copy.redeemModal.commonError)
112
+ } finally {
113
+ setLoadingId(null)
114
+ }
115
+ }
116
+ }
117
+
118
+ return (
119
+ <>
120
+ <div className="flex flex-1 flex-col gap-[12px]">
121
+ <div className="flex flex-col gap-[8px]">
122
+ {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>}
124
+ </div>
125
+
126
+ {copy.insufficientCredits && creditInfo && (
127
+ <div className="text-[14px] font-[700] text-[#1d1d1f]">
128
+ {copy.insufficientCredits}: {numberFormat(creditInfo.available_credit)} {copy.pointUnit}
129
+ </div>
130
+ )}
131
+ {redeemAndBuyError && <div className="text-[14px] font-[700] text-[#f84d4f]">{redeemAndBuyError}</div>}
132
+
133
+ {listLoading ? (
134
+ <div className="flex flex-1 items-center justify-center py-[32px]">
135
+ <div className="size-[32px] animate-spin rounded-full border-[3px] border-[#080a0f] border-t-transparent" />
136
+ </div>
137
+ ) : (
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}
164
+ </div>
165
+ )}
166
+ </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
+ </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
+ </div>
199
+ </div>
200
+ </div>
201
+ ))}
202
+ </div>
203
+ )}
204
+
205
+ {copy.creditsPage?.url && (
206
+ <a
207
+ href={copy.creditsPage.url}
208
+ target="_blank"
209
+ rel="noopener noreferrer"
210
+ className="mt-auto self-start text-[14px] font-[700] text-[#080a0f] underline"
211
+ >
212
+ {copy.creditsPage.label ?? 'Go to Credits Page'}
213
+ </a>
214
+ )}
215
+ </div>
216
+ </>
217
+ )
218
+ }
219
+
220
+ export default ConsumeCredits