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

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 (142) 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/cjs/templates/Credits.js +1 -1
  38. package/dist/cjs/templates/Credits.js.map +3 -3
  39. package/dist/esm/components/credits/context/hooks/useCountries.js +1 -1
  40. package/dist/esm/components/credits/context/hooks/useCountries.js.map +3 -3
  41. package/dist/esm/components/memberPopup/MemberPopup.d.ts +1 -1
  42. package/dist/esm/components/memberPopup/MemberPopup.js +1 -1
  43. package/dist/esm/components/memberPopup/MemberPopup.js.map +3 -3
  44. package/dist/esm/components/memberPopup/context/provider.d.ts +3 -3
  45. package/dist/esm/components/memberPopup/context/provider.js +1 -1
  46. package/dist/esm/components/memberPopup/context/provider.js.map +3 -3
  47. package/dist/esm/components/memberPopup/panels/ActivitiesSubscribePanel.d.ts +7 -0
  48. package/dist/esm/components/memberPopup/panels/ActivitiesSubscribePanel.js +2 -0
  49. package/dist/esm/components/memberPopup/panels/ActivitiesSubscribePanel.js.map +7 -0
  50. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.d.ts +9 -7
  51. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js +1 -1
  52. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js.map +3 -3
  53. package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.d.ts +1 -2
  54. package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.js +1 -1
  55. package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.js.map +3 -3
  56. package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.d.ts +1 -2
  57. package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.js +1 -1
  58. package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.js.map +3 -3
  59. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js +1 -1
  60. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js.map +2 -2
  61. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.d.ts +1 -3
  62. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js +1 -1
  63. package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js.map +3 -3
  64. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.d.ts +1 -2
  65. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js +1 -1
  66. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js.map +3 -3
  67. package/dist/esm/components/memberPopup/panels/EarnCredits/index.d.ts +1 -8
  68. package/dist/esm/components/memberPopup/panels/EarnCredits/index.js +1 -1
  69. package/dist/esm/components/memberPopup/panels/EarnCredits/index.js.map +3 -3
  70. package/dist/esm/components/memberPopup/panels/Referral/index.js +1 -1
  71. package/dist/esm/components/memberPopup/panels/Referral/index.js.map +3 -3
  72. package/dist/esm/components/memberPopup/type.d.ts +29 -3
  73. package/dist/esm/components/memberPopup/type.js +1 -1
  74. package/dist/esm/components/memberPopup/type.js.map +2 -2
  75. package/dist/esm/templates/Credits.js +1 -1
  76. package/dist/esm/templates/Credits.js.map +3 -3
  77. package/package.json +1 -1
  78. package/src/components/credits/context/hooks/useCountries.ts +4 -15
  79. package/src/components/memberPopup/MemberPopup.tsx +170 -177
  80. package/src/components/memberPopup/context/provider.tsx +24 -4
  81. package/src/components/memberPopup/panels/ActivitiesSubscribePanel.tsx +194 -0
  82. package/src/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.tsx +93 -60
  83. package/src/components/memberPopup/panels/ConsumeCredits/index.tsx +200 -87
  84. package/src/components/memberPopup/panels/EarnCredits/ActivatePanel.tsx +30 -15
  85. package/src/components/memberPopup/panels/EarnCredits/CompleteProfileModal.tsx +6 -1
  86. package/src/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.tsx +65 -35
  87. package/src/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.tsx +15 -6
  88. package/src/components/memberPopup/panels/EarnCredits/index.tsx +131 -86
  89. package/src/components/memberPopup/panels/Referral/index.tsx +35 -16
  90. package/src/components/memberPopup/type.ts +26 -3
  91. package/src/templates/Credits.tsx +25 -0
  92. package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.d.ts +0 -10
  93. package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.js +0 -2
  94. package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.js.map +0 -7
  95. package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.d.ts +0 -7
  96. package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js +0 -2
  97. package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js.map +0 -7
  98. package/dist/cjs/components/memberPopup/panels/Activity/index.d.ts +0 -7
  99. package/dist/cjs/components/memberPopup/panels/Activity/index.js +0 -2
  100. package/dist/cjs/components/memberPopup/panels/Activity/index.js.map +0 -7
  101. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.d.ts +0 -19
  102. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js +0 -2
  103. package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js.map +0 -7
  104. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.d.ts +0 -10
  105. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.js +0 -2
  106. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.js.map +0 -7
  107. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.d.ts +0 -9
  108. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.js +0 -2
  109. package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.js.map +0 -7
  110. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.d.ts +0 -13
  111. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js +0 -2
  112. package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js.map +0 -7
  113. package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.d.ts +0 -10
  114. package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.js +0 -2
  115. package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.js.map +0 -7
  116. package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.d.ts +0 -7
  117. package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js +0 -2
  118. package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js.map +0 -7
  119. package/dist/esm/components/memberPopup/panels/Activity/index.d.ts +0 -7
  120. package/dist/esm/components/memberPopup/panels/Activity/index.js +0 -2
  121. package/dist/esm/components/memberPopup/panels/Activity/index.js.map +0 -7
  122. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.d.ts +0 -19
  123. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js +0 -2
  124. package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js.map +0 -7
  125. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.d.ts +0 -10
  126. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.js +0 -2
  127. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.js.map +0 -7
  128. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.d.ts +0 -9
  129. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.js +0 -2
  130. package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.js.map +0 -7
  131. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.d.ts +0 -13
  132. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js +0 -2
  133. package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js.map +0 -7
  134. package/dist/index.d.mts +0 -1775
  135. package/dist/index.d.ts +0 -1775
  136. package/dist/index.js +0 -18542
  137. package/dist/index.js.map +0 -1
  138. package/dist/index.mjs +0 -18514
  139. package/dist/index.mjs.map +0 -1
  140. package/src/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.tsx +0 -150
  141. package/src/components/memberPopup/panels/EarnCredits/SubscribeModal.tsx +0 -144
  142. package/src/components/memberPopup/panels/EarnCredits/SubscribePanel.tsx +0 -147
@@ -1,150 +0,0 @@
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
- }
@@ -1,144 +0,0 @@
1
- import { useCallback, useEffect, useState } from 'react'
2
- import { Button, Checkbox, Text } from '@anker-in/headless-ui'
3
- import { classNames as cn, useHeadlessContext } from '@anker-in/lib'
4
- import Cookies from 'js-cookie'
5
- import { parse } from 'query-string'
6
- import { CreditsModalContainer } from '../../../credits/modal/ModalContainer'
7
- import { emailValidate } from '../../../credits/context/utils'
8
- import { useMemberPopupContext } from '../../context/provider'
9
- import type { SubscribeModalCopy } from '../../type'
10
- import { ROUNDED_BRANDS } from '../../../../constants'
11
-
12
- type Props = {
13
- isOpen: boolean
14
- onClose: () => void
15
- onSuccess?: () => void
16
- copy: SubscribeModalCopy
17
- email?: string
18
- }
19
-
20
- function getAdCookie() {
21
- if (typeof window === 'undefined') return { register_source: '' }
22
- const { search, href } = window.location
23
- const query = parse(search)
24
- return {
25
- fbuy_ref_code: Cookies.get('fbuy_ref_code'),
26
- ref: Cookies.get('ref_ads'),
27
- inviter_code: query.ic ?? query.inviter_code ?? Cookies.get('inviter_code'),
28
- register_source: query.redirect ?? Cookies.get('reg_source') ?? href,
29
- }
30
- }
31
-
32
- async function getRecaptchaToken(sitekey: string): Promise<string> {
33
- if (typeof window === 'undefined' || !window?.grecaptcha?.execute) return ''
34
- if (!sitekey) return ''
35
- try {
36
- return await window.grecaptcha.execute(sitekey, { action: 'activity' })
37
- } catch {
38
- return ''
39
- }
40
- }
41
-
42
- export function EarnCreditsSubscribeModal({ isOpen, onClose, onSuccess, copy, email: initialEmail }: Props) {
43
- const { alpcConfig } = useMemberPopupContext()
44
- const { locale } = alpcConfig
45
- const { brand: headlessBrand, storeDomain, recaptchaSitekey } = useHeadlessContext()
46
- const rounded = ROUNDED_BRANDS.includes(headlessBrand)
47
- const [policy, setPolicy] = useState(false)
48
- const [email, setEmail] = useState(initialEmail ?? '')
49
- const [errorMessage, setErrorMessage] = useState('')
50
- const [successMessage, setSuccessMessage] = useState('')
51
- const [loading, setLoading] = useState(false)
52
-
53
- useEffect(() => {
54
- if (initialEmail) setEmail(initialEmail)
55
- }, [initialEmail])
56
-
57
- const handleSubmit = useCallback(async () => {
58
- setErrorMessage('')
59
- if (!email) return setErrorMessage('Please fill in your email')
60
- if (!emailValidate(email)) return setErrorMessage('Invalid email address')
61
- if (!policy) return setErrorMessage('Please agree to the policy')
62
-
63
- setLoading(true)
64
- const { register_source } = getAdCookie()
65
- const recaptchaToken = await getRecaptchaToken(recaptchaSitekey || '')
66
- try {
67
- const response = await fetch('/api/multipass/rainbowbridge/activities', {
68
- method: 'POST',
69
- headers: {
70
- 'Content-Type': 'application/json',
71
- 'current-language': locale,
72
- 'X-Recaptcha-Token': recaptchaToken,
73
- },
74
- body: JSON.stringify({
75
- email,
76
- register_source,
77
- shopify_domain: storeDomain,
78
- single_brand_subscribe: copy.single_brand_subscribe ?? true,
79
- genre: copy.genre ?? copy.dealsType ?? '',
80
- sub_brand_type: copy.sub_brand_type ?? '',
81
- }),
82
- })
83
- const result = await response.json()
84
- if (!result.errors) {
85
- setSuccessMessage(copy.successTips ?? 'Subscribed successfully!')
86
- onSuccess?.()
87
- setTimeout(() => {
88
- setSuccessMessage('')
89
- onClose()
90
- }, 3000)
91
- } else {
92
- setErrorMessage(result.errors ?? 'Error occurred')
93
- }
94
- } catch {
95
- setErrorMessage('Network error')
96
- } finally {
97
- setLoading(false)
98
- }
99
- }, [email, policy, locale, storeDomain, copy, onSuccess, onClose])
100
-
101
- return (
102
- <CreditsModalContainer
103
- isOpen={isOpen}
104
- onClose={onClose}
105
- className="w-[540px] md:h-auto"
106
- scrollClassName="mb-[40px] md:mb-[24px]"
107
- useAnimation
108
- animationClassName="md:translate-y-[100vh]"
109
- >
110
- <div className="flex flex-col gap-[16px] text-center">
111
- <Text className="text-[22px] font-bold" html={copy.title} />
112
- {copy.desc && (
113
- <Text className="text-left text-[16px] font-semibold text-[#1f2021]" html={copy.desc} />
114
- )}
115
- <div className={cn('flex h-[52px] w-full overflow-hidden md:flex-col md:h-auto md:gap-[16px]', rounded ? 'rounded-[2px]' : 'rounded-none')}>
116
- <input
117
- className={cn('h-full flex-1 border border-[#d8d8d8] px-[14px] text-[16px] outline-none placeholder:text-[#999] md:h-[52px]', rounded ? 'rounded-l-[2px] md:rounded-[2px]' : 'rounded-none')}
118
- placeholder={copy.placeholder}
119
- value={email}
120
- onChange={e => { setErrorMessage(''); setEmail(e.target.value.trim()) }}
121
- />
122
- <Button
123
- variant="primary"
124
- loading={loading}
125
- className={cn('!h-full !min-w-0 !px-[24px] md:!h-[46px]', rounded ? '!rounded-l-none !rounded-r-[2px] md:!rounded-[2px]' : '!rounded-none')}
126
- onClick={handleSubmit}
127
- >
128
- {copy.buttonLabel ?? 'Subscribe'}
129
- </Button>
130
- </div>
131
- <div className="flex w-full items-start gap-[8px]">
132
- <Checkbox checked={policy} onCheckedChange={() => setPolicy(v => !v)} className="border-[#1d1d1f]" />
133
- <label
134
- className="text-left text-[14px] font-semibold text-[#777] [&_a]:underline"
135
- dangerouslySetInnerHTML={{ __html: copy.policy ?? '' }}
136
- />
137
- </div>
138
- <div className={cn('text-left text-[16px] font-semibold', errorMessage && 'text-[#f84d4f]', successMessage && 'text-[#52c41a]')}>
139
- <Text html={successMessage || errorMessage || ''} />
140
- </div>
141
- </div>
142
- </CreditsModalContainer>
143
- )
144
- }
@@ -1,147 +0,0 @@
1
- import { useCallback, useEffect, useState } from 'react'
2
- import { Checkbox, Text } from '@anker-in/headless-ui'
3
- import { classNames as cn, useHeadlessContext } from '@anker-in/lib'
4
- import Cookies from 'js-cookie'
5
- import { parse } from 'query-string'
6
- import { emailValidate } from '../../../credits/context/utils'
7
- import { useMemberPopupContext } from '../../context/provider'
8
- import type { SubscribeModalCopy } from '../../type'
9
- import { ROUNDED_BRANDS } from '../../../../constants'
10
-
11
- type Props = {
12
- copy: SubscribeModalCopy
13
- email?: string
14
- onClose: () => void
15
- onSuccess?: () => void
16
- }
17
-
18
- function getAdCookie() {
19
- if (typeof window === 'undefined') return { register_source: '' }
20
- const { search, href } = window.location
21
- const query = parse(search)
22
- return {
23
- fbuy_ref_code: Cookies.get('fbuy_ref_code'),
24
- ref: Cookies.get('ref_ads'),
25
- inviter_code: query.ic ?? query.inviter_code ?? Cookies.get('inviter_code'),
26
- register_source: query.redirect ?? Cookies.get('reg_source') ?? href,
27
- }
28
- }
29
-
30
- async function getRecaptchaToken(sitekey: string): Promise<string> {
31
- if (typeof window === 'undefined' || !window?.grecaptcha?.execute) return ''
32
- if (!sitekey) return ''
33
- try {
34
- return await window.grecaptcha.execute(sitekey, { action: 'activity' })
35
- } catch {
36
- return ''
37
- }
38
- }
39
-
40
- export default function SubscribePanel({ copy, email: initialEmail, onClose, onSuccess }: Props) {
41
- const { alpcConfig } = useMemberPopupContext()
42
- const { locale } = alpcConfig
43
- const { brand: headlessBrand, storeDomain, recaptchaSitekey } = useHeadlessContext()
44
- const rounded = ROUNDED_BRANDS.includes(headlessBrand)
45
- const [policy, setPolicy] = useState(false)
46
- const [email, setEmail] = useState(initialEmail ?? '')
47
- const [errorMessage, setErrorMessage] = useState('')
48
- const [successMessage, setSuccessMessage] = useState('')
49
- const [loading, setLoading] = useState(false)
50
-
51
- useEffect(() => {
52
- if (initialEmail) setEmail(initialEmail)
53
- }, [initialEmail])
54
-
55
- const handleSubmit = useCallback(async () => {
56
- setErrorMessage('')
57
- if (!email) return setErrorMessage('Please fill in your email')
58
- if (!emailValidate(email)) return setErrorMessage('Invalid email address')
59
- if (!policy) return setErrorMessage('Please agree to the policy')
60
-
61
- setLoading(true)
62
- const { register_source } = getAdCookie()
63
- const recaptchaToken = await getRecaptchaToken(recaptchaSitekey || '')
64
- try {
65
- const response = await fetch('/api/multipass/rainbowbridge/activities', {
66
- method: 'POST',
67
- headers: {
68
- 'Content-Type': 'application/json',
69
- 'current-language': locale,
70
- 'X-Recaptcha-Token': recaptchaToken,
71
- },
72
- body: JSON.stringify({
73
- email,
74
- register_source,
75
- shopify_domain: storeDomain,
76
- single_brand_subscribe: copy.single_brand_subscribe ?? true,
77
- genre: copy.genre ?? copy.dealsType ?? '',
78
- sub_brand_type: copy.sub_brand_type ?? '',
79
- }),
80
- })
81
- const result = await response.json()
82
- if (!result.errors) {
83
- setSuccessMessage(copy.successTips ?? 'Subscribed successfully!')
84
- onSuccess?.()
85
- setTimeout(() => {
86
- setSuccessMessage('')
87
- onClose()
88
- }, 3000)
89
- } else {
90
- setErrorMessage(result.errors ?? 'Error occurred')
91
- }
92
- } catch {
93
- setErrorMessage('Network error')
94
- } finally {
95
- setLoading(false)
96
- }
97
- }, [email, policy, locale, storeDomain, copy, onClose, onSuccess])
98
-
99
- return (
100
- <div className="flex flex-col gap-[12px]">
101
- <div className="flex flex-col gap-[8px]">
102
- {copy.title && (
103
- <h2 className="text-[24px] font-[700] leading-[1.2] text-[#080a0f]">{copy.title}</h2>
104
- )}
105
- {copy.desc && (
106
- <Text className="text-[16px] text-[#1e2024]" html={copy.desc} />
107
- )}
108
- </div>
109
-
110
- <div className="flex flex-col gap-[16px]">
111
- <input
112
- className={cn(
113
- 'h-[46px] w-full border px-[14px] text-[16px] outline-none placeholder:text-[#999]',
114
- errorMessage ? 'border-[#f84d4f]' : 'border-[#d8d8d8]',
115
- rounded ? 'rounded-[4px]' : 'rounded-none'
116
- )}
117
- placeholder={copy.placeholder}
118
- value={email}
119
- onChange={e => { setErrorMessage(''); setEmail(e.target.value.trim()) }}
120
- />
121
-
122
- <div className="flex w-full items-start gap-[8px]">
123
- <Checkbox checked={policy} onCheckedChange={() => setPolicy(v => !v)} className="mt-[2px] border-[#1d1d1f]" />
124
- <label
125
- className="cursor-pointer text-left text-[14px] text-[#1e2024] [&_a]:underline"
126
- onClick={() => setPolicy(v => !v)}
127
- dangerouslySetInnerHTML={{ __html: copy.policy ?? '' }}
128
- />
129
- </div>
130
-
131
- {(errorMessage || successMessage) && (
132
- <div className={cn('text-[14px] font-[700]', errorMessage && 'text-[#f84d4f]', successMessage && 'text-[#52c41a]')}>
133
- <Text html={successMessage || errorMessage} />
134
- </div>
135
- )}
136
-
137
- <button
138
- className={cn('w-full cursor-pointer bg-[#080a0f] py-[14px] text-[14px] font-[700] text-white disabled:opacity-60', rounded ? 'rounded-[4px]' : 'rounded-none')}
139
- disabled={loading}
140
- onClick={handleSubmit}
141
- >
142
- {copy.buttonLabel ?? 'Subscribe'}
143
- </button>
144
- </div>
145
- </div>
146
- )
147
- }