@anker-in/campaign-ui 0.5.22 → 0.5.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -57,7 +57,7 @@ import '@anker-in/campaign-ui/livechat.css'
57
57
  />
58
58
  ```
59
59
 
60
- 支持自定义渲染器、品牌色、Logo 等,详见组件内 Storybook 文档(`src/stories/LiveChatWidget.stories.tsx`)。
60
+ 支持自定义渲染器、品牌色、Logo 等。本仓库没有 Storybook / 本地预览环境(拆分时配置未随仓库带出),调试请将组件接入宿主项目运行。
61
61
 
62
62
  ### Credits(积分)
63
63
 
@@ -109,7 +109,27 @@ pnpm run lint # eslint(不含 src/lib)
109
109
 
110
110
  ## 发布
111
111
 
112
+ 本仓库通过 CI(Jenkins)自动发布,测试环境对应 `beta` 分支,线上环境对应 `main` 分支。
113
+
114
+ - **`beta`(测试环境)**:合并到该分支即自动触发发布,无需手动改动 `package.json` 版本号——CI 会基于当前版本号自动生成一个带时间戳的 `alpha` 版本(如 `0.5.22-alpha.1789381484328`)并发布到 npm 的 `beta` tag。发布完成后会通过飞书机器人推送通知,包含最终发布的版本号。
115
+ - **`main`(线上环境)**:合并前需要手动更新 `package.json` 中的 `version` 字段,合并后 CI 会读取该版本号自动发布到 npm 的 `latest` tag。
116
+
117
+ ### 手动本地发布(不走 CI 时)
118
+
119
+ **发到 beta(测试环境)**:不需要手动改版本号,本地跑一次 bump 脚本生成临时 alpha 版本再发布:
120
+
121
+ ```shell
122
+ node ./scripts/bump-beta-version.cjs # 自动生成形如 0.5.22-alpha.<timestamp> 的版本号
123
+ pnpm run build
124
+ npm publish --tag beta
125
+ ```
126
+
127
+ 发布完成后建议 `git checkout package.json` 撤销本地这次版本号改动,避免误提交。
128
+
129
+ **发到线上(main,latest tag)**:先手动把 `package.json` 的 `version` 改成正式版本号,再发布:
130
+
112
131
  ```shell
132
+ # 手动编辑 package.json 的 version 字段
113
133
  pnpm run build
114
134
  npm publish
115
135
  ```
@@ -4,6 +4,7 @@ export type CreditsSubscribeModalCopy = {
4
4
  desc?: string;
5
5
  placeholder: string;
6
6
  policy?: string;
7
+ oneankerSubscribeAgree?: string;
7
8
  successTips?: string;
8
9
  dealsType?: string;
9
10
  };
@@ -1,2 +1,2 @@
1
- "use strict";var B=Object.create;var f=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var R=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var O=(e,t)=>{for(var r in t)f(e,r,{get:t[r],enumerable:!0})},N=(e,t,r,c)=>{if(t&&typeof t=="object"||typeof t=="function")for(let l of H(t))!q.call(e,l)&&l!==r&&f(e,l,{get:()=>t[l],enumerable:!(c=F(t,l))||c.enumerable});return e};var T=(e,t,r)=>(r=e!=null?B(R(e)):{},N(t||!e||!e.__esModule?f(r,"default",{value:e,enumerable:!0}):r,e)),z=e=>N(f({},"__esModule",{value:!0}),e);var G={};O(G,{CreditsSubscribeModal:()=>V});module.exports=z(G);var s=require("react/jsx-runtime"),a=require("react"),o=require("@anker-in/headless-ui"),i=require("../../../lib"),n=T(require("js-cookie")),M=require("query-string"),w=T(require("crypto-js/sha256")),S=require("../context/provider"),E=require("../../../constants"),P=require("../context/utils"),k=require("./ModalContainer");const U=()=>{if(typeof window>"u")return{register_source:""};const{search:e,href:t}=window.location||{},r=(0,M.parse)(e);return{fbuy_ref_code:n.default.get("fbuy_ref_code"),affiliate:n.default.get("affiliate"),ref:n.default.get("ref_ads"),inviter_code:r.ic||r.inviter_code||n.default.get("inviter_code"),register_source:r.redirect||n.default.get("reg_source")||t,deals_type:n.default.get("deal"),transfer_id:n.default.get("transfer_id")}};function V({copy:e,onSuccess:t,...r}){const{brand:c,locale:l}=(0,i.useHeadlessContext)(),x=E.ROUNDED_BRANDS.includes(c),[m,A]=(0,a.useState)(!1),[d,h]=(0,a.useState)(""),[v,p]=(0,a.useState)(""),[_,C]=(0,a.useState)(""),[D,y]=(0,a.useState)(!1),{profile:u}=(0,S.useCreditsContext)(),I=(0,a.useCallback)(b=>{p(""),h(b.target?.value?.trim())},[]),L=(0,a.useCallback)(async()=>{if(p(""),!d)return p("Please fill in your email");if(!(0,P.emailValidate)(d))return p("Invalid email address");if(!m)return p("Please agree to the policy");y(!0);const{register_source:b}=U(),g=await(0,i.fetcher)({locale:l,action:"subscribe",url:"/api/multipass/subscribe/subscribe_emails",method:"POST",body:{email:d,register_source:b,brand:c,deals_type:e.dealsType||"vip_subscribe"},headers:{},type:""});y(!1),g.errors?p(g.errors||g.statusText):(C(e.successTips||"Subscribed successfully!"),t&&t(),setTimeout(()=>{C("")},3e3),(0,i.gaTrack)({subscribe_hashed_email:d?(0,w.default)(d):""}),(0,i.gaTrack)({event:"ga4Event",event_name:"subscribe",event_parameters:{page_group:e.dealsType||"vip_subscribe",position:"pop_up"}}),(0,i.gaTrack)({event:"uaEvent",eventCategory:"subscribe",eventAction:e.dealsType||"vip_subscribe",eventLabel:"pop_up",nonInteraction:!1}))},[d,m,l,c,e.dealsType,e.successTips,t]);return(0,a.useEffect)(()=>{u&&u?.email&&h(u?.email)},[u]),(0,s.jsx)(k.CreditsModalContainer,{className:"w-[540px] md:h-auto",scrollClassName:"mb-[40px] min-l:px-[48px] md:mb-[24px]",titleClassName:"h-[56px]",useAnimation:!0,animationClassName:"md:translate-y-[100vh]",...r,children:(0,s.jsxs)("div",{className:"min-l:px-[16px] flex flex-col gap-[16px] text-center",children:[(0,s.jsx)("div",{children:(0,s.jsx)(o.Text,{className:(0,i.classNames)("mb-[24px] text-[22px] font-bold"),html:e.title})}),e.desc&&(0,s.jsx)(o.Text,{className:"mb-[8px] text-left text-[16px] font-semibold leading-[1.4] text-[#1F2021]",html:e.desc}),(0,s.jsxs)("div",{className:(0,i.classNames)("relative flex h-[52px] w-full overflow-hidden rounded-[2px] md:grid md:h-auto md:grid-rows-1 md:gap-[16px]",!x&&"rounded-none"),children:[(0,s.jsx)("input",{className:(0,i.classNames)("h-full flex-1 rounded-l-[2px] border-[1px] border-[#d8d8d8] px-[14px] py-[17px] text-[16px] font-semibold leading-[1] outline-none placeholder:text-[#999] md:h-[52px] md:rounded-[2px]",!x&&"rounded-none"),placeholder:e.placeholder,value:d,onChange:I}),(0,s.jsx)(o.Button,{variant:"primary",onClick:L,loading:D,className:(0,i.classNames)("legacy-headless-ui-primary-button !h-full !min-w-0 !rounded-l-none !rounded-r-[2px] !px-[40px] !py-[13px]",!x&&"rounded-none"),children:(0,s.jsx)(o.Picture,{source:"https://cdn.shopify.com/s/files/1/0512/8568/8505/files/icon_email.png?v=1697527383",className:"size-[24px]",alt:"email"})})]}),(0,s.jsxs)("div",{className:"flex w-full",children:[(0,s.jsx)(o.Checkbox,{checked:m,onCheckedChange:()=>A(!m),required:!0,className:"border-[#1d1d1f]"}),(0,s.jsx)("label",{className:"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline",dangerouslySetInnerHTML:{__html:e?.policy||""}})]}),(0,s.jsx)("div",{className:(0,i.classNames)("w-full text-left text-[16px] font-semibold",v&&"text-[#F84D4F]",_&&"text-[#52C41A]"),children:(0,s.jsx)(o.Text,{html:_||v||""})})]})})}
1
+ "use strict";var I=Object.create;var u=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var U=(e,s)=>{for(var r in s)u(e,r,{get:s[r],enumerable:!0})},T=(e,s,r,d)=>{if(s&&typeof s=="object"||typeof s=="function")for(let l of B(s))!z.call(e,l)&&l!==r&&u(e,l,{get:()=>s[l],enumerable:!(d=R(s,l))||d.enumerable});return e};var S=(e,s,r)=>(r=e!=null?I(O(e)):{},T(s||!e||!e.__esModule?u(r,"default",{value:e,enumerable:!0}):r,e)),V=e=>T(u({},"__esModule",{value:!0}),e);var K={};U(K,{CreditsSubscribeModal:()=>J});module.exports=V(K);var t=require("react/jsx-runtime"),i=require("react"),o=require("@anker-in/headless-ui"),a=require("../../../lib"),n=S(require("js-cookie")),M=require("query-string"),k=S(require("crypto-js/sha256")),w=require("../context/provider"),E=require("../../../constants"),A=require("./ModalContainer");const G=()=>{if(typeof window>"u")return{register_source:""};const{search:e,href:s}=window.location||{},r=(0,M.parse)(e);return{fbuy_ref_code:n.default.get("fbuy_ref_code"),affiliate:n.default.get("affiliate"),ref:n.default.get("ref_ads"),inviter_code:r.ic||r.inviter_code||n.default.get("inviter_code"),register_source:r.redirect||n.default.get("reg_source")||s,deals_type:n.default.get("deal"),transfer_id:n.default.get("transfer_id")}};function J({copy:e,onSuccess:s,...r}){const{brand:d,locale:l}=(0,a.useHeadlessContext)(),p=E.ROUNDED_BRANDS.includes(d),[g,P]=(0,i.useState)(!1),[h,L]=(0,i.useState)(!1),[c,v]=(0,i.useState)(""),[_,f]=(0,i.useState)(""),[C,y]=(0,i.useState)(""),[D,N]=(0,i.useState)(!1),{profile:m}=(0,w.useCreditsContext)(),F=(0,i.useCallback)(b=>{f(""),v(b.target?.value?.trim())},[]),H=(0,i.useCallback)(async b=>{b.preventDefault(),f(""),N(!0);const{register_source:q}=G(),x=await(0,a.fetcher)({locale:l,action:"subscribe",url:"/api/multipass/subscribe/subscribe_emails",method:"POST",body:{email:c,register_source:q,brand:d,deals_type:e.dealsType||"vip_subscribe"},headers:{},type:""});N(!1),x.errors?f(x.errors||x.statusText):(y(e.successTips||"Subscribed successfully!"),s&&s(),setTimeout(()=>{y("")},3e3),(0,a.gaTrack)({subscribe_hashed_email:c?(0,k.default)(c):""}),(0,a.gaTrack)({event:"ga4Event",event_name:"subscribe",event_parameters:{page_group:e.dealsType||"vip_subscribe",position:"pop_up"}}),(0,a.gaTrack)({event:"uaEvent",eventCategory:"subscribe",eventAction:e.dealsType||"vip_subscribe",eventLabel:"pop_up",nonInteraction:!1}))},[c,l,d,e.dealsType,e.successTips,s]);return(0,i.useEffect)(()=>{m&&m?.email&&v(m?.email)},[m]),(0,t.jsx)(A.CreditsModalContainer,{className:"w-[540px] md:h-auto",scrollClassName:"mb-[40px] min-l:px-[48px] md:mb-[24px]",titleClassName:"h-[56px]",useAnimation:!0,animationClassName:"md:translate-y-[100vh]",...r,children:(0,t.jsxs)("form",{className:"min-l:px-[16px] flex flex-col gap-[16px] text-center",onSubmit:H,noValidate:!1,children:[(0,t.jsx)("div",{children:(0,t.jsx)(o.Text,{className:(0,a.classNames)("mb-[24px] text-[22px] font-bold"),html:e.title})}),e.desc&&(0,t.jsx)(o.Text,{className:"mb-[8px] text-left text-[16px] font-semibold leading-[1.4] text-[#1F2021]",html:e.desc}),(0,t.jsxs)("div",{className:(0,a.classNames)("relative flex h-[52px] w-full overflow-hidden rounded-[2px] md:grid md:h-auto md:grid-rows-1 md:gap-[16px]",!p&&"rounded-none"),children:[(0,t.jsx)("input",{className:(0,a.classNames)("h-full flex-1 rounded-l-[2px] border-[1px] border-[#d8d8d8] px-[14px] py-[17px] text-[16px] font-semibold leading-[1] outline-none placeholder:text-[#999] md:h-[52px] md:rounded-[2px]",!p&&"rounded-none"),type:"email",required:!0,placeholder:e.placeholder,value:c,onChange:F}),(0,t.jsx)(o.Button,{type:"submit",variant:"primary",loading:D,className:(0,a.classNames)("legacy-headless-ui-primary-button !h-full !min-w-0 !rounded-l-none !rounded-r-[2px] !px-[40px] !py-[13px]",!p&&"rounded-none"),children:(0,t.jsx)(o.Picture,{source:"https://cdn.shopify.com/s/files/1/0512/8568/8505/files/icon_email.png?v=1697527383",className:"size-[24px]",alt:"email"})})]}),e?.oneankerSubscribeAgree&&(0,t.jsxs)("div",{className:"flex w-full",children:[(0,t.jsx)(o.Checkbox,{checked:h,onCheckedChange:()=>L(!h),required:!0,className:"border-[#1d1d1f]"}),(0,t.jsx)("label",{className:"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline",dangerouslySetInnerHTML:{__html:e.oneankerSubscribeAgree}})]}),(0,t.jsxs)("div",{className:"flex w-full",children:[(0,t.jsx)(o.Checkbox,{checked:g,onCheckedChange:()=>P(!g),required:!0,className:"border-[#1d1d1f]"}),(0,t.jsx)("label",{className:"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline",dangerouslySetInnerHTML:{__html:e?.policy||""}})]}),(0,t.jsx)("div",{className:(0,a.classNames)("w-full text-left text-[16px] font-semibold",_&&"text-[#F84D4F]",C&&"text-[#52C41A]"),children:(0,t.jsx)(o.Text,{html:C||_||""})})]})})}
2
2
  //# sourceMappingURL=SubscribeModal.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/components/credits/modal/SubscribeModal.tsx"],
4
- "sourcesContent": ["import { useCallback, useEffect, useState } from 'react'\nimport { Button, Checkbox, Picture, Text } from '@anker-in/headless-ui'\nimport { classNames, fetcher, gaTrack, useHeadlessContext } from '../../../lib'\nimport Cookies from 'js-cookie'\nimport { parse } from 'query-string'\nimport sha256 from 'crypto-js/sha256'\nimport { useCreditsContext } from '../context/provider'\nimport { ROUNDED_BRANDS } from '../../../constants'\nimport { emailValidate } from '../context/utils'\nimport { CreditsModalContainer, type ModalContainerProps } from './ModalContainer'\n\nexport type CreditsSubscribeModalCopy = {\n title: string\n desc?: string\n placeholder: string\n policy?: string\n successTips?: string\n dealsType?: string\n}\n\ninterface CreditsSubscribeModalProps extends ModalContainerProps {\n copy: CreditsSubscribeModalCopy\n onSuccess?: () => void\n}\n\nconst getAdCookie = () => {\n if (typeof window === 'undefined') {\n return { register_source: '' }\n }\n const { search, href } = window.location || {}\n const query = parse(search)\n\n return {\n fbuy_ref_code: Cookies.get('fbuy_ref_code'),\n affiliate: Cookies.get('affiliate'),\n ref: Cookies.get('ref_ads'),\n inviter_code: query.ic || query.inviter_code || Cookies.get('inviter_code'),\n register_source: query.redirect || Cookies.get('reg_source') || href,\n deals_type: Cookies.get('deal'),\n transfer_id: Cookies.get('transfer_id'),\n }\n}\n\nexport function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubscribeModalProps) {\n const { brand, locale } = useHeadlessContext()\n const rounded = ROUNDED_BRANDS.includes(brand)\n const [policy, setPolicy] = useState(false)\n const [email, setEmail] = useState('')\n const [errorMessage, setErrorMessage] = useState('')\n const [successMessage, setSuccessMessage] = useState('')\n const [loading, setLoading] = useState(false)\n const { profile } = useCreditsContext()\n\n const handleEmailChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n setErrorMessage('')\n setEmail(event.target?.value?.trim())\n }, [])\n\n const handleSubmit = useCallback(async () => {\n setErrorMessage('')\n if (!email) {\n return setErrorMessage('Please fill in your email')\n }\n\n if (!emailValidate(email)) {\n return setErrorMessage('Invalid email address')\n }\n\n if (!policy) {\n return setErrorMessage('Please agree to the policy')\n }\n\n setLoading(true)\n const { register_source } = getAdCookie()\n\n const result = await fetcher({\n locale,\n action: 'subscribe',\n url: '/api/multipass/subscribe/subscribe_emails',\n method: 'POST',\n body: {\n email,\n register_source,\n brand,\n deals_type: copy.dealsType || 'vip_subscribe',\n },\n headers: {},\n type: '',\n })\n setLoading(false)\n\n if (!result.errors) {\n setSuccessMessage(copy.successTips || 'Subscribed successfully!')\n if (onSuccess) {\n onSuccess()\n }\n setTimeout(() => {\n setSuccessMessage('')\n }, 3000)\n gaTrack({\n subscribe_hashed_email: email ? sha256(email) : '',\n })\n gaTrack({\n event: 'ga4Event',\n event_name: 'subscribe',\n event_parameters: {\n page_group: copy.dealsType || 'vip_subscribe',\n position: 'pop_up',\n },\n })\n gaTrack({\n event: 'uaEvent',\n eventCategory: 'subscribe',\n eventAction: copy.dealsType || 'vip_subscribe',\n eventLabel: 'pop_up',\n nonInteraction: false,\n })\n } else {\n setErrorMessage(result.errors || result.statusText)\n }\n }, [email, policy, locale, brand, copy.dealsType, copy.successTips, onSuccess])\n\n useEffect(() => {\n if (profile && profile?.email) {\n setEmail(profile?.email)\n }\n }, [profile])\n\n return (\n <CreditsModalContainer\n className=\"w-[540px] md:h-auto\"\n scrollClassName=\"mb-[40px] min-l:px-[48px] md:mb-[24px]\"\n titleClassName=\"h-[56px]\"\n useAnimation\n animationClassName=\"md:translate-y-[100vh]\"\n {...props}\n >\n <div className=\"min-l:px-[16px] flex flex-col gap-[16px] text-center\">\n <div>\n <Text className={classNames('mb-[24px] text-[22px] font-bold')} html={copy.title}></Text>\n </div>\n {copy.desc && (\n <Text\n className=\"mb-[8px] text-left text-[16px] font-semibold leading-[1.4] text-[#1F2021]\"\n html={copy.desc}\n ></Text>\n )}\n <div\n className={classNames(\n 'relative flex h-[52px] w-full overflow-hidden rounded-[2px] md:grid md:h-auto md:grid-rows-1 md:gap-[16px]',\n !rounded && 'rounded-none'\n )}\n >\n <input\n className={classNames(\n 'h-full flex-1 rounded-l-[2px] border-[1px] border-[#d8d8d8] px-[14px] py-[17px] text-[16px] font-semibold leading-[1] outline-none placeholder:text-[#999] md:h-[52px] md:rounded-[2px]',\n !rounded && 'rounded-none'\n )}\n placeholder={copy.placeholder}\n value={email}\n onChange={handleEmailChange}\n />\n <Button\n variant=\"primary\"\n onClick={handleSubmit}\n loading={loading}\n className={classNames(\n 'legacy-headless-ui-primary-button !h-full !min-w-0 !rounded-l-none !rounded-r-[2px] !px-[40px] !py-[13px]',\n !rounded && 'rounded-none'\n )}\n >\n <Picture\n source=\"https://cdn.shopify.com/s/files/1/0512/8568/8505/files/icon_email.png?v=1697527383\"\n className=\"size-[24px]\"\n alt=\"email\"\n />\n </Button>\n </div>\n <div className=\"flex w-full\">\n <Checkbox checked={policy} onCheckedChange={() => setPolicy(!policy)} required className=\"border-[#1d1d1f]\" />\n <label\n className=\"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline\"\n dangerouslySetInnerHTML={{\n __html: copy?.policy || '',\n }}\n />\n </div>\n <div\n className={classNames(\n 'w-full text-left text-[16px] font-semibold',\n errorMessage && 'text-[#F84D4F]',\n successMessage && 'text-[#52C41A]'\n )}\n >\n <Text html={successMessage || errorMessage || ''} />\n </div>\n </div>\n </CreditsModalContainer>\n )\n}\n"],
5
- "mappings": "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",
6
- "names": ["SubscribeModal_exports", "__export", "CreditsSubscribeModal", "__toCommonJS", "import_jsx_runtime", "import_react", "import_headless_ui", "import_lib", "import_js_cookie", "import_query_string", "import_sha256", "import_provider", "import_constants", "import_utils", "import_ModalContainer", "getAdCookie", "search", "href", "query", "Cookies", "copy", "onSuccess", "props", "brand", "locale", "rounded", "policy", "setPolicy", "email", "setEmail", "errorMessage", "setErrorMessage", "successMessage", "setSuccessMessage", "loading", "setLoading", "profile", "handleEmailChange", "event", "handleSubmit", "register_source", "result", "sha256"]
4
+ "sourcesContent": ["import { useCallback, useEffect, useState } from 'react'\nimport { Button, Checkbox, Picture, Text } from '@anker-in/headless-ui'\nimport { classNames, fetcher, gaTrack, useHeadlessContext } from '../../../lib'\nimport Cookies from 'js-cookie'\nimport { parse } from 'query-string'\nimport sha256 from 'crypto-js/sha256'\nimport { useCreditsContext } from '../context/provider'\nimport { ROUNDED_BRANDS } from '../../../constants'\nimport { CreditsModalContainer, type ModalContainerProps } from './ModalContainer'\n\nexport type CreditsSubscribeModalCopy = {\n title: string\n desc?: string\n placeholder: string\n policy?: string\n oneankerSubscribeAgree?: string\n successTips?: string\n dealsType?: string\n}\n\ninterface CreditsSubscribeModalProps extends ModalContainerProps {\n copy: CreditsSubscribeModalCopy\n onSuccess?: () => void\n}\n\nconst getAdCookie = () => {\n if (typeof window === 'undefined') {\n return { register_source: '' }\n }\n const { search, href } = window.location || {}\n const query = parse(search)\n\n return {\n fbuy_ref_code: Cookies.get('fbuy_ref_code'),\n affiliate: Cookies.get('affiliate'),\n ref: Cookies.get('ref_ads'),\n inviter_code: query.ic || query.inviter_code || Cookies.get('inviter_code'),\n register_source: query.redirect || Cookies.get('reg_source') || href,\n deals_type: Cookies.get('deal'),\n transfer_id: Cookies.get('transfer_id'),\n }\n}\n\nexport function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubscribeModalProps) {\n const { brand, locale } = useHeadlessContext()\n const rounded = ROUNDED_BRANDS.includes(brand)\n const [policy, setPolicy] = useState(false)\n const [oneankerSubscribeAgree, setOneankerSubscribeAgree] = useState(false)\n const [email, setEmail] = useState('')\n const [errorMessage, setErrorMessage] = useState('')\n const [successMessage, setSuccessMessage] = useState('')\n const [loading, setLoading] = useState(false)\n const { profile } = useCreditsContext()\n\n const handleEmailChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n setErrorMessage('')\n setEmail(event.target?.value?.trim())\n }, [])\n\n const handleSubmit = useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n setErrorMessage('')\n\n setLoading(true)\n const { register_source } = getAdCookie()\n\n const result = await fetcher({\n locale,\n action: 'subscribe',\n url: '/api/multipass/subscribe/subscribe_emails',\n method: 'POST',\n body: {\n email,\n register_source,\n brand,\n deals_type: copy.dealsType || 'vip_subscribe',\n },\n headers: {},\n type: '',\n })\n setLoading(false)\n\n if (!result.errors) {\n setSuccessMessage(copy.successTips || 'Subscribed successfully!')\n if (onSuccess) {\n onSuccess()\n }\n setTimeout(() => {\n setSuccessMessage('')\n }, 3000)\n gaTrack({\n subscribe_hashed_email: email ? sha256(email) : '',\n })\n gaTrack({\n event: 'ga4Event',\n event_name: 'subscribe',\n event_parameters: {\n page_group: copy.dealsType || 'vip_subscribe',\n position: 'pop_up',\n },\n })\n gaTrack({\n event: 'uaEvent',\n eventCategory: 'subscribe',\n eventAction: copy.dealsType || 'vip_subscribe',\n eventLabel: 'pop_up',\n nonInteraction: false,\n })\n } else {\n setErrorMessage(result.errors || result.statusText)\n }\n },\n [email, locale, brand, copy.dealsType, copy.successTips, onSuccess]\n )\n\n useEffect(() => {\n if (profile && profile?.email) {\n setEmail(profile?.email)\n }\n }, [profile])\n\n return (\n <CreditsModalContainer\n className=\"w-[540px] md:h-auto\"\n scrollClassName=\"mb-[40px] min-l:px-[48px] md:mb-[24px]\"\n titleClassName=\"h-[56px]\"\n useAnimation\n animationClassName=\"md:translate-y-[100vh]\"\n {...props}\n >\n <form className=\"min-l:px-[16px] flex flex-col gap-[16px] text-center\" onSubmit={handleSubmit} noValidate={false}>\n <div>\n <Text className={classNames('mb-[24px] text-[22px] font-bold')} html={copy.title}></Text>\n </div>\n {copy.desc && (\n <Text\n className=\"mb-[8px] text-left text-[16px] font-semibold leading-[1.4] text-[#1F2021]\"\n html={copy.desc}\n ></Text>\n )}\n <div\n className={classNames(\n 'relative flex h-[52px] w-full overflow-hidden rounded-[2px] md:grid md:h-auto md:grid-rows-1 md:gap-[16px]',\n !rounded && 'rounded-none'\n )}\n >\n <input\n className={classNames(\n 'h-full flex-1 rounded-l-[2px] border-[1px] border-[#d8d8d8] px-[14px] py-[17px] text-[16px] font-semibold leading-[1] outline-none placeholder:text-[#999] md:h-[52px] md:rounded-[2px]',\n !rounded && 'rounded-none'\n )}\n type=\"email\"\n required\n placeholder={copy.placeholder}\n value={email}\n onChange={handleEmailChange}\n />\n <Button\n type=\"submit\"\n variant=\"primary\"\n loading={loading}\n className={classNames(\n 'legacy-headless-ui-primary-button !h-full !min-w-0 !rounded-l-none !rounded-r-[2px] !px-[40px] !py-[13px]',\n !rounded && 'rounded-none'\n )}\n >\n <Picture\n source=\"https://cdn.shopify.com/s/files/1/0512/8568/8505/files/icon_email.png?v=1697527383\"\n className=\"size-[24px]\"\n alt=\"email\"\n />\n </Button>\n </div>\n {copy?.oneankerSubscribeAgree && (\n <div className=\"flex w-full\">\n <Checkbox\n checked={oneankerSubscribeAgree}\n onCheckedChange={() => setOneankerSubscribeAgree(!oneankerSubscribeAgree)}\n required\n className=\"border-[#1d1d1f]\"\n />\n <label\n className=\"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline\"\n dangerouslySetInnerHTML={{\n __html: copy.oneankerSubscribeAgree,\n }}\n />\n </div>\n )}\n <div className=\"flex w-full\">\n <Checkbox checked={policy} onCheckedChange={() => setPolicy(!policy)} required className=\"border-[#1d1d1f]\" />\n <label\n className=\"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline\"\n dangerouslySetInnerHTML={{\n __html: copy?.policy || '',\n }}\n />\n </div>\n <div\n className={classNames(\n 'w-full text-left text-[16px] font-semibold',\n errorMessage && 'text-[#F84D4F]',\n successMessage && 'text-[#52C41A]'\n )}\n >\n <Text html={successMessage || errorMessage || ''} />\n </div>\n </form>\n </CreditsModalContainer>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["SubscribeModal_exports", "__export", "CreditsSubscribeModal", "__toCommonJS", "import_jsx_runtime", "import_react", "import_headless_ui", "import_lib", "import_js_cookie", "import_query_string", "import_sha256", "import_provider", "import_constants", "import_ModalContainer", "getAdCookie", "search", "href", "query", "Cookies", "copy", "onSuccess", "props", "brand", "locale", "rounded", "policy", "setPolicy", "oneankerSubscribeAgree", "setOneankerSubscribeAgree", "email", "setEmail", "errorMessage", "setErrorMessage", "successMessage", "setSuccessMessage", "loading", "setLoading", "profile", "handleEmailChange", "event", "handleSubmit", "register_source", "result", "sha256"]
7
7
  }
@@ -4,6 +4,7 @@ export type CreditsSubscribeModalCopy = {
4
4
  desc?: string;
5
5
  placeholder: string;
6
6
  policy?: string;
7
+ oneankerSubscribeAgree?: string;
7
8
  successTips?: string;
8
9
  dealsType?: string;
9
10
  };
@@ -1,2 +1,2 @@
1
- import{jsx as t,jsxs as g}from"react/jsx-runtime";import{useCallback as T,useEffect as P,useState as l}from"react";import{Button as k,Checkbox as A,Picture as D,Text as x}from"@anker-in/headless-ui";import{classNames as n,fetcher as I,gaTrack as b,useHeadlessContext as L}from"../../../lib";import r from"js-cookie";import{parse as B}from"query-string";import F from"crypto-js/sha256";import{useCreditsContext as H}from"../context/provider";import{ROUNDED_BRANDS as R}from"../../../constants";import{emailValidate as q}from"../context/utils";import{CreditsModalContainer as O}from"./ModalContainer";const z=()=>{if(typeof window>"u")return{register_source:""};const{search:e,href:a}=window.location||{},o=B(e);return{fbuy_ref_code:r.get("fbuy_ref_code"),affiliate:r.get("affiliate"),ref:r.get("ref_ads"),inviter_code:o.ic||o.inviter_code||r.get("inviter_code"),register_source:o.redirect||r.get("reg_source")||a,deals_type:r.get("deal"),transfer_id:r.get("transfer_id")}};function $({copy:e,onSuccess:a,...o}){const{brand:p,locale:h}=L(),m=R.includes(p),[d,M]=l(!1),[s,v]=l(""),[_,i]=l(""),[C,y]=l(""),[w,N]=l(!1),{profile:c}=H(),S=T(u=>{i(""),v(u.target?.value?.trim())},[]),E=T(async()=>{if(i(""),!s)return i("Please fill in your email");if(!q(s))return i("Invalid email address");if(!d)return i("Please agree to the policy");N(!0);const{register_source:u}=z(),f=await I({locale:h,action:"subscribe",url:"/api/multipass/subscribe/subscribe_emails",method:"POST",body:{email:s,register_source:u,brand:p,deals_type:e.dealsType||"vip_subscribe"},headers:{},type:""});N(!1),f.errors?i(f.errors||f.statusText):(y(e.successTips||"Subscribed successfully!"),a&&a(),setTimeout(()=>{y("")},3e3),b({subscribe_hashed_email:s?F(s):""}),b({event:"ga4Event",event_name:"subscribe",event_parameters:{page_group:e.dealsType||"vip_subscribe",position:"pop_up"}}),b({event:"uaEvent",eventCategory:"subscribe",eventAction:e.dealsType||"vip_subscribe",eventLabel:"pop_up",nonInteraction:!1}))},[s,d,h,p,e.dealsType,e.successTips,a]);return P(()=>{c&&c?.email&&v(c?.email)},[c]),t(O,{className:"w-[540px] md:h-auto",scrollClassName:"mb-[40px] min-l:px-[48px] md:mb-[24px]",titleClassName:"h-[56px]",useAnimation:!0,animationClassName:"md:translate-y-[100vh]",...o,children:g("div",{className:"min-l:px-[16px] flex flex-col gap-[16px] text-center",children:[t("div",{children:t(x,{className:n("mb-[24px] text-[22px] font-bold"),html:e.title})}),e.desc&&t(x,{className:"mb-[8px] text-left text-[16px] font-semibold leading-[1.4] text-[#1F2021]",html:e.desc}),g("div",{className:n("relative flex h-[52px] w-full overflow-hidden rounded-[2px] md:grid md:h-auto md:grid-rows-1 md:gap-[16px]",!m&&"rounded-none"),children:[t("input",{className:n("h-full flex-1 rounded-l-[2px] border-[1px] border-[#d8d8d8] px-[14px] py-[17px] text-[16px] font-semibold leading-[1] outline-none placeholder:text-[#999] md:h-[52px] md:rounded-[2px]",!m&&"rounded-none"),placeholder:e.placeholder,value:s,onChange:S}),t(k,{variant:"primary",onClick:E,loading:w,className:n("legacy-headless-ui-primary-button !h-full !min-w-0 !rounded-l-none !rounded-r-[2px] !px-[40px] !py-[13px]",!m&&"rounded-none"),children:t(D,{source:"https://cdn.shopify.com/s/files/1/0512/8568/8505/files/icon_email.png?v=1697527383",className:"size-[24px]",alt:"email"})})]}),g("div",{className:"flex w-full",children:[t(A,{checked:d,onCheckedChange:()=>M(!d),required:!0,className:"border-[#1d1d1f]"}),t("label",{className:"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline",dangerouslySetInnerHTML:{__html:e?.policy||""}})]}),t("div",{className:n("w-full text-left text-[16px] font-semibold",_&&"text-[#F84D4F]",C&&"text-[#52C41A]"),children:t(x,{html:C||_||""})})]})})}export{$ as CreditsSubscribeModal};
1
+ import{jsx as t,jsxs as d}from"react/jsx-runtime";import{useCallback as S,useEffect as D,useState as r}from"react";import{Button as F,Checkbox as M,Picture as H,Text as b}from"@anker-in/headless-ui";import{classNames as l,fetcher as q,gaTrack as x,useHeadlessContext as I}from"../../../lib";import s from"js-cookie";import{parse as R}from"query-string";import B from"crypto-js/sha256";import{useCreditsContext as O}from"../context/provider";import{ROUNDED_BRANDS as z}from"../../../constants";import{CreditsModalContainer as U}from"./ModalContainer";const V=()=>{if(typeof window>"u")return{register_source:""};const{search:e,href:a}=window.location||{},i=R(e);return{fbuy_ref_code:s.get("fbuy_ref_code"),affiliate:s.get("affiliate"),ref:s.get("ref_ads"),inviter_code:i.ic||i.inviter_code||s.get("inviter_code"),register_source:i.redirect||s.get("reg_source")||a,deals_type:s.get("deal"),transfer_id:s.get("transfer_id")}};function j({copy:e,onSuccess:a,...i}){const{brand:c,locale:g}=I(),m=z.includes(c),[h,k]=r(!1),[v,w]=r(!1),[o,_]=r(""),[C,u]=r(""),[y,N]=r(""),[E,T]=r(!1),{profile:n}=O(),A=S(p=>{u(""),_(p.target?.value?.trim())},[]),P=S(async p=>{p.preventDefault(),u(""),T(!0);const{register_source:L}=V(),f=await q({locale:g,action:"subscribe",url:"/api/multipass/subscribe/subscribe_emails",method:"POST",body:{email:o,register_source:L,brand:c,deals_type:e.dealsType||"vip_subscribe"},headers:{},type:""});T(!1),f.errors?u(f.errors||f.statusText):(N(e.successTips||"Subscribed successfully!"),a&&a(),setTimeout(()=>{N("")},3e3),x({subscribe_hashed_email:o?B(o):""}),x({event:"ga4Event",event_name:"subscribe",event_parameters:{page_group:e.dealsType||"vip_subscribe",position:"pop_up"}}),x({event:"uaEvent",eventCategory:"subscribe",eventAction:e.dealsType||"vip_subscribe",eventLabel:"pop_up",nonInteraction:!1}))},[o,g,c,e.dealsType,e.successTips,a]);return D(()=>{n&&n?.email&&_(n?.email)},[n]),t(U,{className:"w-[540px] md:h-auto",scrollClassName:"mb-[40px] min-l:px-[48px] md:mb-[24px]",titleClassName:"h-[56px]",useAnimation:!0,animationClassName:"md:translate-y-[100vh]",...i,children:d("form",{className:"min-l:px-[16px] flex flex-col gap-[16px] text-center",onSubmit:P,noValidate:!1,children:[t("div",{children:t(b,{className:l("mb-[24px] text-[22px] font-bold"),html:e.title})}),e.desc&&t(b,{className:"mb-[8px] text-left text-[16px] font-semibold leading-[1.4] text-[#1F2021]",html:e.desc}),d("div",{className:l("relative flex h-[52px] w-full overflow-hidden rounded-[2px] md:grid md:h-auto md:grid-rows-1 md:gap-[16px]",!m&&"rounded-none"),children:[t("input",{className:l("h-full flex-1 rounded-l-[2px] border-[1px] border-[#d8d8d8] px-[14px] py-[17px] text-[16px] font-semibold leading-[1] outline-none placeholder:text-[#999] md:h-[52px] md:rounded-[2px]",!m&&"rounded-none"),type:"email",required:!0,placeholder:e.placeholder,value:o,onChange:A}),t(F,{type:"submit",variant:"primary",loading:E,className:l("legacy-headless-ui-primary-button !h-full !min-w-0 !rounded-l-none !rounded-r-[2px] !px-[40px] !py-[13px]",!m&&"rounded-none"),children:t(H,{source:"https://cdn.shopify.com/s/files/1/0512/8568/8505/files/icon_email.png?v=1697527383",className:"size-[24px]",alt:"email"})})]}),e?.oneankerSubscribeAgree&&d("div",{className:"flex w-full",children:[t(M,{checked:v,onCheckedChange:()=>w(!v),required:!0,className:"border-[#1d1d1f]"}),t("label",{className:"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline",dangerouslySetInnerHTML:{__html:e.oneankerSubscribeAgree}})]}),d("div",{className:"flex w-full",children:[t(M,{checked:h,onCheckedChange:()=>k(!h),required:!0,className:"border-[#1d1d1f]"}),t("label",{className:"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline",dangerouslySetInnerHTML:{__html:e?.policy||""}})]}),t("div",{className:l("w-full text-left text-[16px] font-semibold",C&&"text-[#F84D4F]",y&&"text-[#52C41A]"),children:t(b,{html:y||C||""})})]})})}export{j as CreditsSubscribeModal};
2
2
  //# sourceMappingURL=SubscribeModal.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/components/credits/modal/SubscribeModal.tsx"],
4
- "sourcesContent": ["import { useCallback, useEffect, useState } from 'react'\nimport { Button, Checkbox, Picture, Text } from '@anker-in/headless-ui'\nimport { classNames, fetcher, gaTrack, useHeadlessContext } from '../../../lib'\nimport Cookies from 'js-cookie'\nimport { parse } from 'query-string'\nimport sha256 from 'crypto-js/sha256'\nimport { useCreditsContext } from '../context/provider'\nimport { ROUNDED_BRANDS } from '../../../constants'\nimport { emailValidate } from '../context/utils'\nimport { CreditsModalContainer, type ModalContainerProps } from './ModalContainer'\n\nexport type CreditsSubscribeModalCopy = {\n title: string\n desc?: string\n placeholder: string\n policy?: string\n successTips?: string\n dealsType?: string\n}\n\ninterface CreditsSubscribeModalProps extends ModalContainerProps {\n copy: CreditsSubscribeModalCopy\n onSuccess?: () => void\n}\n\nconst getAdCookie = () => {\n if (typeof window === 'undefined') {\n return { register_source: '' }\n }\n const { search, href } = window.location || {}\n const query = parse(search)\n\n return {\n fbuy_ref_code: Cookies.get('fbuy_ref_code'),\n affiliate: Cookies.get('affiliate'),\n ref: Cookies.get('ref_ads'),\n inviter_code: query.ic || query.inviter_code || Cookies.get('inviter_code'),\n register_source: query.redirect || Cookies.get('reg_source') || href,\n deals_type: Cookies.get('deal'),\n transfer_id: Cookies.get('transfer_id'),\n }\n}\n\nexport function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubscribeModalProps) {\n const { brand, locale } = useHeadlessContext()\n const rounded = ROUNDED_BRANDS.includes(brand)\n const [policy, setPolicy] = useState(false)\n const [email, setEmail] = useState('')\n const [errorMessage, setErrorMessage] = useState('')\n const [successMessage, setSuccessMessage] = useState('')\n const [loading, setLoading] = useState(false)\n const { profile } = useCreditsContext()\n\n const handleEmailChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n setErrorMessage('')\n setEmail(event.target?.value?.trim())\n }, [])\n\n const handleSubmit = useCallback(async () => {\n setErrorMessage('')\n if (!email) {\n return setErrorMessage('Please fill in your email')\n }\n\n if (!emailValidate(email)) {\n return setErrorMessage('Invalid email address')\n }\n\n if (!policy) {\n return setErrorMessage('Please agree to the policy')\n }\n\n setLoading(true)\n const { register_source } = getAdCookie()\n\n const result = await fetcher({\n locale,\n action: 'subscribe',\n url: '/api/multipass/subscribe/subscribe_emails',\n method: 'POST',\n body: {\n email,\n register_source,\n brand,\n deals_type: copy.dealsType || 'vip_subscribe',\n },\n headers: {},\n type: '',\n })\n setLoading(false)\n\n if (!result.errors) {\n setSuccessMessage(copy.successTips || 'Subscribed successfully!')\n if (onSuccess) {\n onSuccess()\n }\n setTimeout(() => {\n setSuccessMessage('')\n }, 3000)\n gaTrack({\n subscribe_hashed_email: email ? sha256(email) : '',\n })\n gaTrack({\n event: 'ga4Event',\n event_name: 'subscribe',\n event_parameters: {\n page_group: copy.dealsType || 'vip_subscribe',\n position: 'pop_up',\n },\n })\n gaTrack({\n event: 'uaEvent',\n eventCategory: 'subscribe',\n eventAction: copy.dealsType || 'vip_subscribe',\n eventLabel: 'pop_up',\n nonInteraction: false,\n })\n } else {\n setErrorMessage(result.errors || result.statusText)\n }\n }, [email, policy, locale, brand, copy.dealsType, copy.successTips, onSuccess])\n\n useEffect(() => {\n if (profile && profile?.email) {\n setEmail(profile?.email)\n }\n }, [profile])\n\n return (\n <CreditsModalContainer\n className=\"w-[540px] md:h-auto\"\n scrollClassName=\"mb-[40px] min-l:px-[48px] md:mb-[24px]\"\n titleClassName=\"h-[56px]\"\n useAnimation\n animationClassName=\"md:translate-y-[100vh]\"\n {...props}\n >\n <div className=\"min-l:px-[16px] flex flex-col gap-[16px] text-center\">\n <div>\n <Text className={classNames('mb-[24px] text-[22px] font-bold')} html={copy.title}></Text>\n </div>\n {copy.desc && (\n <Text\n className=\"mb-[8px] text-left text-[16px] font-semibold leading-[1.4] text-[#1F2021]\"\n html={copy.desc}\n ></Text>\n )}\n <div\n className={classNames(\n 'relative flex h-[52px] w-full overflow-hidden rounded-[2px] md:grid md:h-auto md:grid-rows-1 md:gap-[16px]',\n !rounded && 'rounded-none'\n )}\n >\n <input\n className={classNames(\n 'h-full flex-1 rounded-l-[2px] border-[1px] border-[#d8d8d8] px-[14px] py-[17px] text-[16px] font-semibold leading-[1] outline-none placeholder:text-[#999] md:h-[52px] md:rounded-[2px]',\n !rounded && 'rounded-none'\n )}\n placeholder={copy.placeholder}\n value={email}\n onChange={handleEmailChange}\n />\n <Button\n variant=\"primary\"\n onClick={handleSubmit}\n loading={loading}\n className={classNames(\n 'legacy-headless-ui-primary-button !h-full !min-w-0 !rounded-l-none !rounded-r-[2px] !px-[40px] !py-[13px]',\n !rounded && 'rounded-none'\n )}\n >\n <Picture\n source=\"https://cdn.shopify.com/s/files/1/0512/8568/8505/files/icon_email.png?v=1697527383\"\n className=\"size-[24px]\"\n alt=\"email\"\n />\n </Button>\n </div>\n <div className=\"flex w-full\">\n <Checkbox checked={policy} onCheckedChange={() => setPolicy(!policy)} required className=\"border-[#1d1d1f]\" />\n <label\n className=\"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline\"\n dangerouslySetInnerHTML={{\n __html: copy?.policy || '',\n }}\n />\n </div>\n <div\n className={classNames(\n 'w-full text-left text-[16px] font-semibold',\n errorMessage && 'text-[#F84D4F]',\n successMessage && 'text-[#52C41A]'\n )}\n >\n <Text html={successMessage || errorMessage || ''} />\n </div>\n </div>\n </CreditsModalContainer>\n )\n}\n"],
5
- "mappings": "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",
6
- "names": ["jsx", "jsxs", "useCallback", "useEffect", "useState", "Button", "Checkbox", "Picture", "Text", "classNames", "fetcher", "gaTrack", "useHeadlessContext", "Cookies", "parse", "sha256", "useCreditsContext", "ROUNDED_BRANDS", "emailValidate", "CreditsModalContainer", "getAdCookie", "search", "href", "query", "CreditsSubscribeModal", "copy", "onSuccess", "props", "brand", "locale", "rounded", "policy", "setPolicy", "email", "setEmail", "errorMessage", "setErrorMessage", "successMessage", "setSuccessMessage", "loading", "setLoading", "profile", "handleEmailChange", "event", "handleSubmit", "register_source", "result"]
4
+ "sourcesContent": ["import { useCallback, useEffect, useState } from 'react'\nimport { Button, Checkbox, Picture, Text } from '@anker-in/headless-ui'\nimport { classNames, fetcher, gaTrack, useHeadlessContext } from '../../../lib'\nimport Cookies from 'js-cookie'\nimport { parse } from 'query-string'\nimport sha256 from 'crypto-js/sha256'\nimport { useCreditsContext } from '../context/provider'\nimport { ROUNDED_BRANDS } from '../../../constants'\nimport { CreditsModalContainer, type ModalContainerProps } from './ModalContainer'\n\nexport type CreditsSubscribeModalCopy = {\n title: string\n desc?: string\n placeholder: string\n policy?: string\n oneankerSubscribeAgree?: string\n successTips?: string\n dealsType?: string\n}\n\ninterface CreditsSubscribeModalProps extends ModalContainerProps {\n copy: CreditsSubscribeModalCopy\n onSuccess?: () => void\n}\n\nconst getAdCookie = () => {\n if (typeof window === 'undefined') {\n return { register_source: '' }\n }\n const { search, href } = window.location || {}\n const query = parse(search)\n\n return {\n fbuy_ref_code: Cookies.get('fbuy_ref_code'),\n affiliate: Cookies.get('affiliate'),\n ref: Cookies.get('ref_ads'),\n inviter_code: query.ic || query.inviter_code || Cookies.get('inviter_code'),\n register_source: query.redirect || Cookies.get('reg_source') || href,\n deals_type: Cookies.get('deal'),\n transfer_id: Cookies.get('transfer_id'),\n }\n}\n\nexport function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubscribeModalProps) {\n const { brand, locale } = useHeadlessContext()\n const rounded = ROUNDED_BRANDS.includes(brand)\n const [policy, setPolicy] = useState(false)\n const [oneankerSubscribeAgree, setOneankerSubscribeAgree] = useState(false)\n const [email, setEmail] = useState('')\n const [errorMessage, setErrorMessage] = useState('')\n const [successMessage, setSuccessMessage] = useState('')\n const [loading, setLoading] = useState(false)\n const { profile } = useCreditsContext()\n\n const handleEmailChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n setErrorMessage('')\n setEmail(event.target?.value?.trim())\n }, [])\n\n const handleSubmit = useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n setErrorMessage('')\n\n setLoading(true)\n const { register_source } = getAdCookie()\n\n const result = await fetcher({\n locale,\n action: 'subscribe',\n url: '/api/multipass/subscribe/subscribe_emails',\n method: 'POST',\n body: {\n email,\n register_source,\n brand,\n deals_type: copy.dealsType || 'vip_subscribe',\n },\n headers: {},\n type: '',\n })\n setLoading(false)\n\n if (!result.errors) {\n setSuccessMessage(copy.successTips || 'Subscribed successfully!')\n if (onSuccess) {\n onSuccess()\n }\n setTimeout(() => {\n setSuccessMessage('')\n }, 3000)\n gaTrack({\n subscribe_hashed_email: email ? sha256(email) : '',\n })\n gaTrack({\n event: 'ga4Event',\n event_name: 'subscribe',\n event_parameters: {\n page_group: copy.dealsType || 'vip_subscribe',\n position: 'pop_up',\n },\n })\n gaTrack({\n event: 'uaEvent',\n eventCategory: 'subscribe',\n eventAction: copy.dealsType || 'vip_subscribe',\n eventLabel: 'pop_up',\n nonInteraction: false,\n })\n } else {\n setErrorMessage(result.errors || result.statusText)\n }\n },\n [email, locale, brand, copy.dealsType, copy.successTips, onSuccess]\n )\n\n useEffect(() => {\n if (profile && profile?.email) {\n setEmail(profile?.email)\n }\n }, [profile])\n\n return (\n <CreditsModalContainer\n className=\"w-[540px] md:h-auto\"\n scrollClassName=\"mb-[40px] min-l:px-[48px] md:mb-[24px]\"\n titleClassName=\"h-[56px]\"\n useAnimation\n animationClassName=\"md:translate-y-[100vh]\"\n {...props}\n >\n <form className=\"min-l:px-[16px] flex flex-col gap-[16px] text-center\" onSubmit={handleSubmit} noValidate={false}>\n <div>\n <Text className={classNames('mb-[24px] text-[22px] font-bold')} html={copy.title}></Text>\n </div>\n {copy.desc && (\n <Text\n className=\"mb-[8px] text-left text-[16px] font-semibold leading-[1.4] text-[#1F2021]\"\n html={copy.desc}\n ></Text>\n )}\n <div\n className={classNames(\n 'relative flex h-[52px] w-full overflow-hidden rounded-[2px] md:grid md:h-auto md:grid-rows-1 md:gap-[16px]',\n !rounded && 'rounded-none'\n )}\n >\n <input\n className={classNames(\n 'h-full flex-1 rounded-l-[2px] border-[1px] border-[#d8d8d8] px-[14px] py-[17px] text-[16px] font-semibold leading-[1] outline-none placeholder:text-[#999] md:h-[52px] md:rounded-[2px]',\n !rounded && 'rounded-none'\n )}\n type=\"email\"\n required\n placeholder={copy.placeholder}\n value={email}\n onChange={handleEmailChange}\n />\n <Button\n type=\"submit\"\n variant=\"primary\"\n loading={loading}\n className={classNames(\n 'legacy-headless-ui-primary-button !h-full !min-w-0 !rounded-l-none !rounded-r-[2px] !px-[40px] !py-[13px]',\n !rounded && 'rounded-none'\n )}\n >\n <Picture\n source=\"https://cdn.shopify.com/s/files/1/0512/8568/8505/files/icon_email.png?v=1697527383\"\n className=\"size-[24px]\"\n alt=\"email\"\n />\n </Button>\n </div>\n {copy?.oneankerSubscribeAgree && (\n <div className=\"flex w-full\">\n <Checkbox\n checked={oneankerSubscribeAgree}\n onCheckedChange={() => setOneankerSubscribeAgree(!oneankerSubscribeAgree)}\n required\n className=\"border-[#1d1d1f]\"\n />\n <label\n className=\"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline\"\n dangerouslySetInnerHTML={{\n __html: copy.oneankerSubscribeAgree,\n }}\n />\n </div>\n )}\n <div className=\"flex w-full\">\n <Checkbox checked={policy} onCheckedChange={() => setPolicy(!policy)} required className=\"border-[#1d1d1f]\" />\n <label\n className=\"ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline\"\n dangerouslySetInnerHTML={{\n __html: copy?.policy || '',\n }}\n />\n </div>\n <div\n className={classNames(\n 'w-full text-left text-[16px] font-semibold',\n errorMessage && 'text-[#F84D4F]',\n successMessage && 'text-[#52C41A]'\n )}\n >\n <Text html={successMessage || errorMessage || ''} />\n </div>\n </form>\n </CreditsModalContainer>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["jsx", "jsxs", "useCallback", "useEffect", "useState", "Button", "Checkbox", "Picture", "Text", "classNames", "fetcher", "gaTrack", "useHeadlessContext", "Cookies", "parse", "sha256", "useCreditsContext", "ROUNDED_BRANDS", "CreditsModalContainer", "getAdCookie", "search", "href", "query", "CreditsSubscribeModal", "copy", "onSuccess", "props", "brand", "locale", "rounded", "policy", "setPolicy", "oneankerSubscribeAgree", "setOneankerSubscribeAgree", "email", "setEmail", "errorMessage", "setErrorMessage", "successMessage", "setSuccessMessage", "loading", "setLoading", "profile", "handleEmailChange", "event", "handleSubmit", "register_source", "result"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anker-in/campaign-ui",
3
- "version": "0.5.22",
3
+ "version": "0.5.23",
4
4
  "description": "Campaign UI components and utilities for Anker projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -6,7 +6,6 @@ import { parse } from 'query-string'
6
6
  import sha256 from 'crypto-js/sha256'
7
7
  import { useCreditsContext } from '../context/provider'
8
8
  import { ROUNDED_BRANDS } from '../../../constants'
9
- import { emailValidate } from '../context/utils'
10
9
  import { CreditsModalContainer, type ModalContainerProps } from './ModalContainer'
11
10
 
12
11
  export type CreditsSubscribeModalCopy = {
@@ -14,6 +13,7 @@ export type CreditsSubscribeModalCopy = {
14
13
  desc?: string
15
14
  placeholder: string
16
15
  policy?: string
16
+ oneankerSubscribeAgree?: string
17
17
  successTips?: string
18
18
  dealsType?: string
19
19
  }
@@ -45,6 +45,7 @@ export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubs
45
45
  const { brand, locale } = useHeadlessContext()
46
46
  const rounded = ROUNDED_BRANDS.includes(brand)
47
47
  const [policy, setPolicy] = useState(false)
48
+ const [oneankerSubscribeAgree, setOneankerSubscribeAgree] = useState(false)
48
49
  const [email, setEmail] = useState('')
49
50
  const [errorMessage, setErrorMessage] = useState('')
50
51
  const [successMessage, setSuccessMessage] = useState('')
@@ -56,69 +57,62 @@ export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubs
56
57
  setEmail(event.target?.value?.trim())
57
58
  }, [])
58
59
 
59
- const handleSubmit = useCallback(async () => {
60
- setErrorMessage('')
61
- if (!email) {
62
- return setErrorMessage('Please fill in your email')
63
- }
60
+ const handleSubmit = useCallback(
61
+ async (event: React.FormEvent<HTMLFormElement>) => {
62
+ event.preventDefault()
63
+ setErrorMessage('')
64
64
 
65
- if (!emailValidate(email)) {
66
- return setErrorMessage('Invalid email address')
67
- }
68
-
69
- if (!policy) {
70
- return setErrorMessage('Please agree to the policy')
71
- }
65
+ setLoading(true)
66
+ const { register_source } = getAdCookie()
72
67
 
73
- setLoading(true)
74
- const { register_source } = getAdCookie()
75
-
76
- const result = await fetcher({
77
- locale,
78
- action: 'subscribe',
79
- url: '/api/multipass/subscribe/subscribe_emails',
80
- method: 'POST',
81
- body: {
82
- email,
83
- register_source,
84
- brand,
85
- deals_type: copy.dealsType || 'vip_subscribe',
86
- },
87
- headers: {},
88
- type: '',
89
- })
90
- setLoading(false)
91
-
92
- if (!result.errors) {
93
- setSuccessMessage(copy.successTips || 'Subscribed successfully!')
94
- if (onSuccess) {
95
- onSuccess()
96
- }
97
- setTimeout(() => {
98
- setSuccessMessage('')
99
- }, 3000)
100
- gaTrack({
101
- subscribe_hashed_email: email ? sha256(email) : '',
102
- })
103
- gaTrack({
104
- event: 'ga4Event',
105
- event_name: 'subscribe',
106
- event_parameters: {
107
- page_group: copy.dealsType || 'vip_subscribe',
108
- position: 'pop_up',
68
+ const result = await fetcher({
69
+ locale,
70
+ action: 'subscribe',
71
+ url: '/api/multipass/subscribe/subscribe_emails',
72
+ method: 'POST',
73
+ body: {
74
+ email,
75
+ register_source,
76
+ brand,
77
+ deals_type: copy.dealsType || 'vip_subscribe',
109
78
  },
79
+ headers: {},
80
+ type: '',
110
81
  })
111
- gaTrack({
112
- event: 'uaEvent',
113
- eventCategory: 'subscribe',
114
- eventAction: copy.dealsType || 'vip_subscribe',
115
- eventLabel: 'pop_up',
116
- nonInteraction: false,
117
- })
118
- } else {
119
- setErrorMessage(result.errors || result.statusText)
120
- }
121
- }, [email, policy, locale, brand, copy.dealsType, copy.successTips, onSuccess])
82
+ setLoading(false)
83
+
84
+ if (!result.errors) {
85
+ setSuccessMessage(copy.successTips || 'Subscribed successfully!')
86
+ if (onSuccess) {
87
+ onSuccess()
88
+ }
89
+ setTimeout(() => {
90
+ setSuccessMessage('')
91
+ }, 3000)
92
+ gaTrack({
93
+ subscribe_hashed_email: email ? sha256(email) : '',
94
+ })
95
+ gaTrack({
96
+ event: 'ga4Event',
97
+ event_name: 'subscribe',
98
+ event_parameters: {
99
+ page_group: copy.dealsType || 'vip_subscribe',
100
+ position: 'pop_up',
101
+ },
102
+ })
103
+ gaTrack({
104
+ event: 'uaEvent',
105
+ eventCategory: 'subscribe',
106
+ eventAction: copy.dealsType || 'vip_subscribe',
107
+ eventLabel: 'pop_up',
108
+ nonInteraction: false,
109
+ })
110
+ } else {
111
+ setErrorMessage(result.errors || result.statusText)
112
+ }
113
+ },
114
+ [email, locale, brand, copy.dealsType, copy.successTips, onSuccess]
115
+ )
122
116
 
123
117
  useEffect(() => {
124
118
  if (profile && profile?.email) {
@@ -135,7 +129,7 @@ export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubs
135
129
  animationClassName="md:translate-y-[100vh]"
136
130
  {...props}
137
131
  >
138
- <div className="min-l:px-[16px] flex flex-col gap-[16px] text-center">
132
+ <form className="min-l:px-[16px] flex flex-col gap-[16px] text-center" onSubmit={handleSubmit} noValidate={false}>
139
133
  <div>
140
134
  <Text className={classNames('mb-[24px] text-[22px] font-bold')} html={copy.title}></Text>
141
135
  </div>
@@ -156,13 +150,15 @@ export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubs
156
150
  'h-full flex-1 rounded-l-[2px] border-[1px] border-[#d8d8d8] px-[14px] py-[17px] text-[16px] font-semibold leading-[1] outline-none placeholder:text-[#999] md:h-[52px] md:rounded-[2px]',
157
151
  !rounded && 'rounded-none'
158
152
  )}
153
+ type="email"
154
+ required
159
155
  placeholder={copy.placeholder}
160
156
  value={email}
161
157
  onChange={handleEmailChange}
162
158
  />
163
159
  <Button
160
+ type="submit"
164
161
  variant="primary"
165
- onClick={handleSubmit}
166
162
  loading={loading}
167
163
  className={classNames(
168
164
  'legacy-headless-ui-primary-button !h-full !min-w-0 !rounded-l-none !rounded-r-[2px] !px-[40px] !py-[13px]',
@@ -176,6 +172,22 @@ export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubs
176
172
  />
177
173
  </Button>
178
174
  </div>
175
+ {copy?.oneankerSubscribeAgree && (
176
+ <div className="flex w-full">
177
+ <Checkbox
178
+ checked={oneankerSubscribeAgree}
179
+ onCheckedChange={() => setOneankerSubscribeAgree(!oneankerSubscribeAgree)}
180
+ required
181
+ className="border-[#1d1d1f]"
182
+ />
183
+ <label
184
+ className="ml-2 text-left text-[14px] font-semibold text-[#777] [&_a]:underline"
185
+ dangerouslySetInnerHTML={{
186
+ __html: copy.oneankerSubscribeAgree,
187
+ }}
188
+ />
189
+ </div>
190
+ )}
179
191
  <div className="flex w-full">
180
192
  <Checkbox checked={policy} onCheckedChange={() => setPolicy(!policy)} required className="border-[#1d1d1f]" />
181
193
  <label
@@ -194,7 +206,7 @@ export function CreditsSubscribeModal({ copy, onSuccess, ...props }: CreditsSubs
194
206
  >
195
207
  <Text html={successMessage || errorMessage || ''} />
196
208
  </div>
197
- </div>
209
+ </form>
198
210
  </CreditsModalContainer>
199
211
  )
200
212
  }
@@ -1,14 +0,0 @@
1
- /**
2
- * LiveChatWidget Storybook Stories
3
- * 展示 LiveChat 组件的各种使用场景和配置
4
- */
5
- import type { Meta, StoryObj } from '@storybook/react';
6
- import { LiveChatWidget } from '../components/LiveChatWidget';
7
- import '../styles/livechat.css';
8
- declare const meta: Meta<typeof LiveChatWidget>;
9
- export default meta;
10
- type Story = StoryObj<typeof LiveChatWidget>;
11
- /**
12
- * 默认配置 - 展示所有功能
13
- */
14
- export declare const Default: Story;
@@ -1,52 +0,0 @@
1
- "use strict";var n=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var g=(t,e)=>{for(var a in e)n(t,a,{get:e[a],enumerable:!0})},m=(t,e,a,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of d(e))!p.call(t,r)&&r!==a&&n(t,r,{get:()=>e[r],enumerable:!(i=c(e,r))||i.enumerable});return t};var u=t=>m(n({},"__esModule",{value:!0}),t);var f={};g(f,{Default:()=>h,default:()=>y});module.exports=u(f);var o=require("react/jsx-runtime"),s=require("../components/LiveChatWidget"),C=require("../styles/livechat.css");const v={title:"Campaign/LiveChatWidget",component:s.LiveChatWidget,parameters:{layout:"fullscreen",docs:{story:{inline:!1,iframeHeight:500},description:{component:`
2
- # LiveChat \u804A\u5929\u7EC4\u4EF6
3
-
4
- \u53EF\u590D\u7528\u7684\u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u7EC4\u4EF6\uFF0C\u652F\u6301 SSE \u6D41\u5F0F\u6D88\u606F\u3001\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u548C\u591A\u79CD\u6D88\u606F\u7C7B\u578B\u3002
5
-
6
- ## \u529F\u80FD\u7279\u6027
7
-
8
- - \u{1F388} **\u6C14\u6CE1\u5F39\u7A97**: \u53EF\u81EA\u5B9A\u4E49\u4F4D\u7F6E\u7684\u60AC\u6D6E\u6C14\u6CE1\u6309\u94AE
9
- - \u{1F4AC} **\u6D41\u5F0F\u6D88\u606F**: \u57FA\u4E8E SSE \u7684\u5B9E\u65F6\u6D41\u5F0F\u54CD\u5E94
10
- - \u{1F4E6} **\u591A\u79CD\u6D88\u606F\u7C7B\u578B**: \u6587\u672C\u3001\u5546\u54C1\u5361\u7247\u3001\u5546\u54C1\u5217\u8868\u3001\u653F\u7B56\u3001\u5FEB\u6377\u56DE\u590D\u7B49
11
- - \u{1F3A8} **\u53EF\u5B9A\u5236**: \u652F\u6301\u81EA\u5B9A\u4E49\u54C1\u724C\u989C\u8272\u3001Logo\u3001\u6E32\u67D3\u5668
12
- - \u{1F4F1} **\u54CD\u5E94\u5F0F**: \u79FB\u52A8\u7AEF\u5168\u5C4F\uFF0C\u684C\u9762\u7AEF\u56FA\u5B9A\u5C3A\u5BF8
13
- - \u{1F4BE} **\u4F1A\u8BDD\u7BA1\u7406**: \u81EA\u52A8\u7BA1\u7406 userId \u548C sessionId
14
- - \u{1F512} **\u5B89\u5168\u9632\u62A4**: \u5185\u7F6E XSS \u9632\u62A4\u548C\u8F93\u5165\u9A8C\u8BC1
15
-
16
- ## \u57FA\u7840\u7528\u6CD5
17
-
18
- \`\`\`tsx
19
- import { LiveChatWidget } from '@anker-in/campaign-ui'
20
- import '@anker-in/campaign-ui/livechat.css'
21
-
22
- function App() {
23
- return (
24
- <LiveChatWidget
25
- apiBaseUrl="https://beta-api-livechat.anker.com"
26
- site="www.eufy.com"
27
- channel_code="web_homepage"
28
- welcomeMessage="\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B"
29
- />
30
- )
31
- }
32
- \`\`\`
33
-
34
- ## \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668
35
-
36
- \`\`\`tsx
37
- const customRenderers = {
38
- video: {
39
- render: (content) => (
40
- <video src={content.url} controls className="w-full rounded" />
41
- )
42
- }
43
- }
44
-
45
- <LiveChatWidget
46
- apiBaseUrl="..."
47
- site="..."
48
- customRenderers={customRenderers}
49
- />
50
- \`\`\`
51
- `}}},tags:["autodocs"],argTypes:{apiBaseUrl:{control:"text",description:"API \u57FA\u7840 URL"},headers:{control:"object",description:"\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\uFF0C\u5C06\u5728\u6240\u6709 API \u8BF7\u6C42\u4E2D\u6DFB\u52A0",table:{defaultValue:{summary:"undefined"}}},recaptchaSitekey:{control:"text",description:"Google reCAPTCHA v3 site key\uFF0C\u63D0\u4F9B\u6B64\u53C2\u6570\u5C06\u81EA\u52A8\u542F\u7528 reCAPTCHA v3 \u9A8C\u8BC1",table:{defaultValue:{summary:"undefined"}}},recaptchaAction:{control:"text",description:"reCAPTCHA action \u540D\u79F0\uFF0C\u7528\u4E8E\u533A\u5206\u4E0D\u540C\u7684\u9A8C\u8BC1\u573A\u666F",table:{defaultValue:{summary:'"activity"'}}},site:{control:"text",description:"Shopify \u5E97\u94FA URL"},channelCode:{control:"text",description:"\u6E20\u9053\u7F16\u7801\uFF0C\u7528\u4E8E\u6807\u8BC6\u6765\u6E90\u6E20\u9053",table:{defaultValue:{summary:"undefined"}}},welcomeMessage:{control:"text",description:"\u6B22\u8FCE\u6D88\u606F",table:{defaultValue:{summary:"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F"}}},logoUrl:{control:"text",description:"Logo URL"},position:{control:"object",description:"\u6C14\u6CE1\u6309\u94AE\u4F4D\u7F6E\u5BF9\u8C61",table:{defaultValue:{summary:'{ bottom: "1.5rem", right: "1.5rem" }'}}}},args:{apiBaseUrl:"http://172.16.38.183:3003",site:"www.eufy.com",loginUserId:"test_test",welcomeMessage:"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F"}};var y=v;const h={args:{loginUserId:"test_test1",apiBaseUrl:"https://beta-api-v2-livechat.anker.com",site:"beta.soundcore.com",channelCode:"dtc",title:"eufy AI Assistant",cartId:"gid://shopify/Cart/hWNAAJweS9IkeRKib4LRy9PY?key=ff1f9b24e74b3f11f5574539e7cbbb2f",accessToken:"47b1aa2c0797043f9baba39388029d70",position:{bottom:"24px",right:"30px"},welcomeMessage:"",quickReplies:[{id:"1",label:"Product Info",value:"Tell me about your products",icon:"\u{1F4E6}"},{id:"2",label:"Track Order",value:"I want to track my order",icon:"\u{1F69A}"},{id:"3",label:"Support",value:"I need help with my device",icon:"\u{1F527}"},{id:"4",label:"Recommendations",value:"Recommend products for me",icon:"\u2B50"}],complianceConfig:{title:"Hi! I'm your eufy AI assistant.",content:"AI-generated responses can be inaccurate. Please verify important info. Do not input sensitive personal data.",checkboxText:`By starting to use "Live Chat", you agree to Anker's <a href="https://www.anker.com/pages/privacy-policy" target="_blank" rel="noopener noreferrer" style="text-decoration: underline;">LIVE CHAT PRIVACY NOTICE</a>.`,agreeButtonText:"Agree"},recaptchaAction:"livechat",showNewSessionButton:!0,commonText:{learnMore:"Learn More",total:"Total"},customRenderers:{video:{render:t=>{const e=t;return(0,o.jsxs)("div",{className:"w-full",children:[(0,o.jsx)("video",{src:e.url,controls:!0,className:"w-full rounded-lg",poster:e.poster,children:"Your browser does not support video playback"}),e.title&&(0,o.jsx)("p",{className:"mt-2 text-sm text-gray-600",children:e.title})]})}},image_gallery:{render:t=>{const a=t.images||[];return(0,o.jsx)("div",{className:"grid grid-cols-2 gap-2",children:a.map((i,r)=>(0,o.jsx)("div",{className:"relative aspect-square",children:(0,o.jsx)("img",{src:i.url,alt:i.alt||`Image ${r+1}`,className:"size-full rounded-lg object-cover"})},r))})}}},productCardRender:(t,e)=>{if(!t)return(0,o.jsx)("div",{style:{padding:"16px",border:"1px dashed #ccc",borderRadius:"8px",textAlign:"center"},children:(0,o.jsxs)("p",{children:["Product loading... (handle: ",e,")"]})});const a=t?.featured_image||"",i=t?.title||"",r=t?.description||"",l=t?.average_rating;return(0,o.jsx)("div",{style:{border:"2px solid #4CAF50",borderRadius:"16px",padding:"16px",margin:"12px 0",backgroundColor:"#f0f9ff",boxShadow:"0 4px 12px rgba(76, 175, 80, 0.15)"},children:(0,o.jsxs)("div",{style:{display:"flex",gap:"12px",alignItems:"center"},children:[a&&(0,o.jsx)("img",{src:a,alt:i,style:{width:"60px",height:"60px",borderRadius:"8px",objectFit:"cover"}}),(0,o.jsxs)("div",{style:{flex:1},children:[(0,o.jsx)("h4",{style:{margin:"0 0 4px 0",fontSize:"16px",fontWeight:"bold"},children:i}),r&&(0,o.jsxs)("p",{style:{margin:0,fontSize:"12px",color:"#666",lineHeight:1.4},children:[r.slice(0,80),"..."]}),l&&(0,o.jsxs)("span",{style:{fontSize:"12px",color:"#FFB800"},children:["Rating: ",l.toFixed(1)]})]}),(0,o.jsx)("button",{style:{padding:"8px 16px",backgroundColor:"#4CAF50",color:"white",borderRadius:"8px",border:"none",cursor:"pointer"},onClick:()=>console.log("View product:",e,t),children:"View"})]})})}},render:t=>(0,o.jsx)(s.LiveChatWidget,{...t,onOpen:()=>console.log("[LiveChat] Chat opened"),onClose:()=>console.log("[LiveChat] Chat closed"),onMessageSend:e=>console.log("[LiveChat] Message sent:",e),onError:e=>console.error("[LiveChat] Error:",e),onTextMessage:()=>console.log("[LiveChat] AI text message received"),onProductList:()=>console.log("[LiveChat] Product list received"),onPromotionList:()=>console.log("[LiveChat] Promotion list received"),onAddToCart:e=>{console.log("[LiveChat] Add to cart:",e),alert(`Added "${e.title}" to cart!`)},onCart:(e,a)=>{console.log("[LiveChat] Cart clicked:",{cartId:e,checkoutUrl:a}),alert(`Cart ID: ${e}`)}})};
52
- //# sourceMappingURL=LiveChatWidget.stories.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/stories/LiveChatWidget.stories.tsx"],
4
- "sourcesContent": ["/**\n * LiveChatWidget Storybook Stories\n * \u5C55\u793A LiveChat \u7EC4\u4EF6\u7684\u5404\u79CD\u4F7F\u7528\u573A\u666F\u548C\u914D\u7F6E\n */\n\nimport type { Meta, StoryObj } from '@storybook/react'\nimport { LiveChatWidget } from '../components/LiveChatWidget'\nimport type { MessageRenderer, MessageContent } from '../components/LiveChatWidget'\nimport '../styles/livechat.css'\n\nconst meta: Meta<typeof LiveChatWidget> = {\n title: 'Campaign/LiveChatWidget',\n component: LiveChatWidget,\n parameters: {\n layout: 'fullscreen',\n docs: {\n story: {\n inline: false,\n iframeHeight: 500,\n },\n description: {\n component: `\n# LiveChat \u804A\u5929\u7EC4\u4EF6\n\n\u53EF\u590D\u7528\u7684\u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u7EC4\u4EF6\uFF0C\u652F\u6301 SSE \u6D41\u5F0F\u6D88\u606F\u3001\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u548C\u591A\u79CD\u6D88\u606F\u7C7B\u578B\u3002\n\n## \u529F\u80FD\u7279\u6027\n\n- \uD83C\uDF88 **\u6C14\u6CE1\u5F39\u7A97**: \u53EF\u81EA\u5B9A\u4E49\u4F4D\u7F6E\u7684\u60AC\u6D6E\u6C14\u6CE1\u6309\u94AE\n- \uD83D\uDCAC **\u6D41\u5F0F\u6D88\u606F**: \u57FA\u4E8E SSE \u7684\u5B9E\u65F6\u6D41\u5F0F\u54CD\u5E94\n- \uD83D\uDCE6 **\u591A\u79CD\u6D88\u606F\u7C7B\u578B**: \u6587\u672C\u3001\u5546\u54C1\u5361\u7247\u3001\u5546\u54C1\u5217\u8868\u3001\u653F\u7B56\u3001\u5FEB\u6377\u56DE\u590D\u7B49\n- \uD83C\uDFA8 **\u53EF\u5B9A\u5236**: \u652F\u6301\u81EA\u5B9A\u4E49\u54C1\u724C\u989C\u8272\u3001Logo\u3001\u6E32\u67D3\u5668\n- \uD83D\uDCF1 **\u54CD\u5E94\u5F0F**: \u79FB\u52A8\u7AEF\u5168\u5C4F\uFF0C\u684C\u9762\u7AEF\u56FA\u5B9A\u5C3A\u5BF8\n- \uD83D\uDCBE **\u4F1A\u8BDD\u7BA1\u7406**: \u81EA\u52A8\u7BA1\u7406 userId \u548C sessionId\n- \uD83D\uDD12 **\u5B89\u5168\u9632\u62A4**: \u5185\u7F6E XSS \u9632\u62A4\u548C\u8F93\u5165\u9A8C\u8BC1\n\n## \u57FA\u7840\u7528\u6CD5\n\n\\`\\`\\`tsx\nimport { LiveChatWidget } from '@anker-in/campaign-ui'\nimport '@anker-in/campaign-ui/livechat.css'\n\nfunction App() {\n return (\n <LiveChatWidget\n apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n site=\"www.eufy.com\"\n channel_code=\"web_homepage\"\n welcomeMessage=\"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\"\n />\n )\n}\n\\`\\`\\`\n\n## \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n\n\\`\\`\\`tsx\nconst customRenderers = {\n video: {\n render: (content) => (\n <video src={content.url} controls className=\"w-full rounded\" />\n )\n }\n}\n\n<LiveChatWidget\n apiBaseUrl=\"...\"\n site=\"...\"\n customRenderers={customRenderers}\n/>\n\\`\\`\\`\n `,\n },\n },\n },\n tags: ['autodocs'],\n argTypes: {\n apiBaseUrl: {\n control: 'text',\n description: 'API \u57FA\u7840 URL',\n },\n headers: {\n control: 'object',\n description: '\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\uFF0C\u5C06\u5728\u6240\u6709 API \u8BF7\u6C42\u4E2D\u6DFB\u52A0',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n recaptchaSitekey: {\n control: 'text',\n description: 'Google reCAPTCHA v3 site key\uFF0C\u63D0\u4F9B\u6B64\u53C2\u6570\u5C06\u81EA\u52A8\u542F\u7528 reCAPTCHA v3 \u9A8C\u8BC1',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n recaptchaAction: {\n control: 'text',\n description: 'reCAPTCHA action \u540D\u79F0\uFF0C\u7528\u4E8E\u533A\u5206\u4E0D\u540C\u7684\u9A8C\u8BC1\u573A\u666F',\n table: {\n defaultValue: { summary: '\"activity\"' },\n },\n },\n site: {\n control: 'text',\n description: 'Shopify \u5E97\u94FA URL',\n },\n channelCode: {\n control: 'text',\n description: '\u6E20\u9053\u7F16\u7801\uFF0C\u7528\u4E8E\u6807\u8BC6\u6765\u6E90\u6E20\u9053',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n welcomeMessage: {\n control: 'text',\n description: '\u6B22\u8FCE\u6D88\u606F',\n table: {\n defaultValue: { summary: '\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F' },\n },\n },\n logoUrl: {\n control: 'text',\n description: 'Logo URL',\n },\n position: {\n control: 'object',\n description: '\u6C14\u6CE1\u6309\u94AE\u4F4D\u7F6E\u5BF9\u8C61',\n table: {\n defaultValue: { summary: '{ bottom: \"1.5rem\", right: \"1.5rem\" }' },\n },\n },\n },\n args: {\n apiBaseUrl: 'http://172.16.38.183:3003',\n site: 'www.eufy.com',\n loginUserId: 'test_test',\n welcomeMessage: '\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F',\n },\n}\n\nexport default meta\ntype Story = StoryObj<typeof LiveChatWidget>\n\n/**\n * \u9ED8\u8BA4\u914D\u7F6E - \u5C55\u793A\u6240\u6709\u529F\u80FD\n */\nexport const Default: Story = {\n args: {\n // \u57FA\u7840\u914D\u7F6E\n loginUserId: 'test_test1',\n apiBaseUrl: 'https://beta-api-v2-livechat.anker.com',\n site: 'beta.soundcore.com',\n channelCode: 'dtc',\n title: 'eufy AI Assistant',\n cartId: 'gid://shopify/Cart/hWNAAJweS9IkeRKib4LRy9PY?key=ff1f9b24e74b3f11f5574539e7cbbb2f',\n accessToken: '47b1aa2c0797043f9baba39388029d70',\n\n // \u81EA\u5B9A\u4E49\u4F4D\u7F6E\n position: { bottom: '24px', right: '30px' },\n\n // \u6B22\u8FCE\u6D88\u606F\n welcomeMessage: '',\n\n // \u5FEB\u6377\u56DE\u590D\n quickReplies: [\n { id: '1', label: 'Product Info', value: 'Tell me about your products', icon: '\uD83D\uDCE6' },\n { id: '2', label: 'Track Order', value: 'I want to track my order', icon: '\uD83D\uDE9A' },\n { id: '3', label: 'Support', value: 'I need help with my device', icon: '\uD83D\uDD27' },\n { id: '4', label: 'Recommendations', value: 'Recommend products for me', icon: '\u2B50' },\n ],\n\n // \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\n complianceConfig: {\n title: \"Hi! I'm your eufy AI assistant.\",\n content:\n 'AI-generated responses can be inaccurate. Please verify important info. Do not input sensitive personal data.',\n checkboxText:\n 'By starting to use \"Live Chat\", you agree to Anker\\'s <a href=\"https://www.anker.com/pages/privacy-policy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">LIVE CHAT PRIVACY NOTICE</a>.',\n agreeButtonText: 'Agree',\n },\n\n // reCAPTCHA \u914D\u7F6E\uFF08\u53D6\u6D88\u6CE8\u91CA\u4EE5\u542F\u7528\uFF09\n // recaptchaSitekey: '6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14',\n recaptchaAction: 'livechat',\n\n // \u663E\u793A\u65B0\u4F1A\u8BDD\u6309\u94AE\n showNewSessionButton: true,\n\n // \u81EA\u5B9A\u4E49\u6587\u6848\n commonText: {\n learnMore: 'Learn More',\n total: 'Total',\n },\n\n // \u81EA\u5B9A\u4E49\u6D88\u606F\u6E32\u67D3\u5668\n customRenderers: {\n video: {\n render: (content: MessageContent) => {\n const videoContent = content as any\n return (\n <div className=\"w-full\">\n <video src={videoContent.url} controls className=\"w-full rounded-lg\" poster={videoContent.poster}>\n Your browser does not support video playback\n </video>\n {videoContent.title && <p className=\"mt-2 text-sm text-gray-600\">{videoContent.title}</p>}\n </div>\n )\n },\n } as MessageRenderer,\n image_gallery: {\n render: (content: MessageContent) => {\n const galleryContent = content as any\n const images = galleryContent.images || []\n return (\n <div className=\"grid grid-cols-2 gap-2\">\n {images.map((image: any, index: number) => (\n <div key={index} className=\"relative aspect-square\">\n <img\n src={image.url}\n alt={image.alt || `Image ${index + 1}`}\n className=\"size-full rounded-lg object-cover\"\n />\n </div>\n ))}\n </div>\n )\n },\n } as MessageRenderer,\n },\n\n // \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\n productCardRender: (product, productHandle) => {\n // product \u53EF\u80FD\u4E3A undefined\uFF0C\u6B64\u65F6\u53EF\u7528 productHandle \u67E5\u8BE2\n if (!product) {\n return (\n <div style={{ padding: '16px', border: '1px dashed #ccc', borderRadius: '8px', textAlign: 'center' }}>\n <p>Product loading... (handle: {productHandle})</p>\n </div>\n )\n }\n\n const imageUrl = product?.featured_image || ''\n const title = product?.title || ''\n const description = product?.description || ''\n const averageRating = product?.average_rating\n\n return (\n <div\n style={{\n border: '2px solid #4CAF50',\n borderRadius: '16px',\n padding: '16px',\n margin: '12px 0',\n backgroundColor: '#f0f9ff',\n boxShadow: '0 4px 12px rgba(76, 175, 80, 0.15)',\n }}\n >\n <div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>\n {imageUrl && (\n <img\n src={imageUrl}\n alt={title}\n style={{ width: '60px', height: '60px', borderRadius: '8px', objectFit: 'cover' }}\n />\n )}\n <div style={{ flex: 1 }}>\n <h4 style={{ margin: '0 0 4px 0', fontSize: '16px', fontWeight: 'bold' }}>{title}</h4>\n {description && (\n <p style={{ margin: 0, fontSize: '12px', color: '#666', lineHeight: 1.4 }}>\n {description.slice(0, 80)}...\n </p>\n )}\n {averageRating && (\n <span style={{ fontSize: '12px', color: '#FFB800' }}>Rating: {averageRating.toFixed(1)}</span>\n )}\n </div>\n <button\n style={{\n padding: '8px 16px',\n backgroundColor: '#4CAF50',\n color: 'white',\n borderRadius: '8px',\n border: 'none',\n cursor: 'pointer',\n }}\n onClick={() => console.log('View product:', productHandle, product)}\n >\n View\n </button>\n </div>\n </div>\n )\n },\n },\n\n render: args => (\n <LiveChatWidget\n {...args}\n // \u6240\u6709\u4E8B\u4EF6\u56DE\u8C03\n onOpen={() => console.log('[LiveChat] Chat opened')}\n onClose={() => console.log('[LiveChat] Chat closed')}\n onMessageSend={(message: string) => console.log('[LiveChat] Message sent:', message)}\n onError={(error: Error) => console.error('[LiveChat] Error:', error)}\n onTextMessage={() => console.log('[LiveChat] AI text message received')}\n onProductList={() => console.log('[LiveChat] Product list received')}\n onPromotionList={() => console.log('[LiveChat] Promotion list received')}\n onAddToCart={(product: any) => {\n console.log('[LiveChat] Add to cart:', product)\n alert(`Added \"${product.title}\" to cart!`)\n }}\n onCart={(cartId: string, checkoutUrl?: string) => {\n console.log('[LiveChat] Cart clicked:', { cartId, checkoutUrl })\n alert(`Cart ID: ${cartId}`)\n }}\n />\n ),\n}\n"],
5
- "mappings": "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",
6
- "names": ["LiveChatWidget_stories_exports", "__export", "Default", "LiveChatWidget_stories_default", "__toCommonJS", "import_jsx_runtime", "import_LiveChatWidget", "import_livechat", "meta", "content", "videoContent", "images", "image", "index", "product", "productHandle", "imageUrl", "title", "description", "averageRating", "args", "message", "error", "cartId", "checkoutUrl"]
7
- }
@@ -1,7 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import { Chat } from '../components/index';
3
- import '../styles/chat.css';
4
- type Story = StoryObj<typeof meta>;
5
- declare const meta: Meta<typeof Chat>;
6
- export default meta;
7
- export declare const Default: Story;
@@ -1,2 +0,0 @@
1
- "use strict";var i=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var n=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var c=(t,o)=>{for(var a in o)i(t,a,{get:o[a],enumerable:!0})},y=(t,o,a,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let e of n(o))!h.call(t,e)&&e!==a&&i(t,e,{get:()=>o[e],enumerable:!(r=m(o,e))||r.enumerable});return t};var l=t=>y(i({},"__esModule",{value:!0}),t);var C={};c(C,{Default:()=>d,default:()=>u});module.exports=l(C);var p=require("react/jsx-runtime"),s=require("../components/index"),D=require("../styles/chat.css");const f={title:"Campaign/Chat",component:s.Chat,args:{title:"DTC Live Chat",runtimeUrl:"https://beta-api-livechat.anker.com",shopifyDomain:"soundcoreusa.myshopify.com"},tags:["autodocs"],decorators:[t=>(0,p.jsx)("div",{style:{height:"700px"},children:(0,p.jsx)(t,{})})]};var u=f;const d={args:{title:"DTC Live Chat",runtimeUrl:"https://beta-api-livechat.anker.com",shopifyDomain:"soundcoreusa.myshopify.com",userId:"arno7",showResponseButton:"follow",lang:{popupTip:"Hi ! Welcome to soundcore Innovations live chat\uFF01",popupTipTimeout:[3e3,6e3]}}};
2
- //# sourceMappingURL=chat.stories.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/stories/chat.stories.tsx"],
4
- "sourcesContent": ["import React from 'react'\nimport type { Meta, StoryObj } from '@storybook/react'\nimport { Chat } from '../components/index'\nimport '../styles/chat.css'\n\ntype Story = StoryObj<typeof meta>\n\n// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export\nconst meta: Meta<typeof Chat> = {\n // \u540C\u65F6\u5B9A\u4E49\u4E86\u9875\u9762\u5DE6\u4FA7\u83DC\u5355\n title: 'Campaign/Chat',\n component: Chat,\n args: {\n title: 'DTC Live Chat',\n runtimeUrl: 'https://beta-api-livechat.anker.com',\n shopifyDomain: 'soundcoreusa.myshopify.com',\n },\n // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs\n tags: ['autodocs'],\n // More on argTypes: https://storybook.js.org/docs/api/argtypes\n decorators: [\n Story => (\n <div style={{ height: '700px' }}>\n <Story />\n </div>\n ),\n ],\n} satisfies Meta<typeof Chat>\n\nexport default meta\n\nexport const Default: Story = {\n args: {\n title: 'DTC Live Chat',\n runtimeUrl: 'https://beta-api-livechat.anker.com',\n shopifyDomain: 'soundcoreusa.myshopify.com',\n userId: 'arno7',\n showResponseButton: 'follow',\n lang: { popupTip: 'Hi ! Welcome to soundcore Innovations live chat\uFF01', popupTipTimeout: [3000, 6000] },\n },\n}\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,YAAAC,IAAA,eAAAC,EAAAJ,GAuBQ,IAAAK,EAAA,6BArBRC,EAAqB,+BACrBC,EAAO,8BAKP,MAAMC,EAA0B,CAE9B,MAAO,gBACP,UAAW,OACX,KAAM,CACJ,MAAO,gBACP,WAAY,sCACZ,cAAe,4BACjB,EAEA,KAAM,CAAC,UAAU,EAEjB,WAAY,CACVC,MACE,OAAC,OAAI,MAAO,CAAE,OAAQ,OAAQ,EAC5B,mBAACA,EAAA,EAAM,EACT,CAEJ,CACF,EAEA,IAAON,EAAQK,EAER,MAAMN,EAAiB,CAC5B,KAAM,CACJ,MAAO,gBACP,WAAY,sCACZ,cAAe,6BACf,OAAQ,QACR,mBAAoB,SACpB,KAAM,CAAE,SAAU,wDAAoD,gBAAiB,CAAC,IAAM,GAAI,CAAE,CACtG,CACF",
6
- "names": ["chat_stories_exports", "__export", "Default", "chat_stories_default", "__toCommonJS", "import_jsx_runtime", "import_components", "import_chat", "meta", "Story"]
7
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * LiveChatWidget Storybook Stories
3
- * 展示 LiveChat 组件的各种使用场景和配置
4
- */
5
- import type { Meta, StoryObj } from '@storybook/react';
6
- import { LiveChatWidget } from '../components/LiveChatWidget';
7
- import '../styles/livechat.css';
8
- declare const meta: Meta<typeof LiveChatWidget>;
9
- export default meta;
10
- type Story = StoryObj<typeof LiveChatWidget>;
11
- /**
12
- * 默认配置 - 展示所有功能
13
- */
14
- export declare const Default: Story;
@@ -1,52 +0,0 @@
1
- import{jsx as o,jsxs as r}from"react/jsx-runtime";import{LiveChatWidget as l}from"../components/LiveChatWidget";import"../styles/livechat.css";const c={title:"Campaign/LiveChatWidget",component:l,parameters:{layout:"fullscreen",docs:{story:{inline:!1,iframeHeight:500},description:{component:`
2
- # LiveChat \u804A\u5929\u7EC4\u4EF6
3
-
4
- \u53EF\u590D\u7528\u7684\u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u7EC4\u4EF6\uFF0C\u652F\u6301 SSE \u6D41\u5F0F\u6D88\u606F\u3001\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u548C\u591A\u79CD\u6D88\u606F\u7C7B\u578B\u3002
5
-
6
- ## \u529F\u80FD\u7279\u6027
7
-
8
- - \u{1F388} **\u6C14\u6CE1\u5F39\u7A97**: \u53EF\u81EA\u5B9A\u4E49\u4F4D\u7F6E\u7684\u60AC\u6D6E\u6C14\u6CE1\u6309\u94AE
9
- - \u{1F4AC} **\u6D41\u5F0F\u6D88\u606F**: \u57FA\u4E8E SSE \u7684\u5B9E\u65F6\u6D41\u5F0F\u54CD\u5E94
10
- - \u{1F4E6} **\u591A\u79CD\u6D88\u606F\u7C7B\u578B**: \u6587\u672C\u3001\u5546\u54C1\u5361\u7247\u3001\u5546\u54C1\u5217\u8868\u3001\u653F\u7B56\u3001\u5FEB\u6377\u56DE\u590D\u7B49
11
- - \u{1F3A8} **\u53EF\u5B9A\u5236**: \u652F\u6301\u81EA\u5B9A\u4E49\u54C1\u724C\u989C\u8272\u3001Logo\u3001\u6E32\u67D3\u5668
12
- - \u{1F4F1} **\u54CD\u5E94\u5F0F**: \u79FB\u52A8\u7AEF\u5168\u5C4F\uFF0C\u684C\u9762\u7AEF\u56FA\u5B9A\u5C3A\u5BF8
13
- - \u{1F4BE} **\u4F1A\u8BDD\u7BA1\u7406**: \u81EA\u52A8\u7BA1\u7406 userId \u548C sessionId
14
- - \u{1F512} **\u5B89\u5168\u9632\u62A4**: \u5185\u7F6E XSS \u9632\u62A4\u548C\u8F93\u5165\u9A8C\u8BC1
15
-
16
- ## \u57FA\u7840\u7528\u6CD5
17
-
18
- \`\`\`tsx
19
- import { LiveChatWidget } from '@anker-in/campaign-ui'
20
- import '@anker-in/campaign-ui/livechat.css'
21
-
22
- function App() {
23
- return (
24
- <LiveChatWidget
25
- apiBaseUrl="https://beta-api-livechat.anker.com"
26
- site="www.eufy.com"
27
- channel_code="web_homepage"
28
- welcomeMessage="\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B"
29
- />
30
- )
31
- }
32
- \`\`\`
33
-
34
- ## \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668
35
-
36
- \`\`\`tsx
37
- const customRenderers = {
38
- video: {
39
- render: (content) => (
40
- <video src={content.url} controls className="w-full rounded" />
41
- )
42
- }
43
- }
44
-
45
- <LiveChatWidget
46
- apiBaseUrl="..."
47
- site="..."
48
- customRenderers={customRenderers}
49
- />
50
- \`\`\`
51
- `}}},tags:["autodocs"],argTypes:{apiBaseUrl:{control:"text",description:"API \u57FA\u7840 URL"},headers:{control:"object",description:"\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\uFF0C\u5C06\u5728\u6240\u6709 API \u8BF7\u6C42\u4E2D\u6DFB\u52A0",table:{defaultValue:{summary:"undefined"}}},recaptchaSitekey:{control:"text",description:"Google reCAPTCHA v3 site key\uFF0C\u63D0\u4F9B\u6B64\u53C2\u6570\u5C06\u81EA\u52A8\u542F\u7528 reCAPTCHA v3 \u9A8C\u8BC1",table:{defaultValue:{summary:"undefined"}}},recaptchaAction:{control:"text",description:"reCAPTCHA action \u540D\u79F0\uFF0C\u7528\u4E8E\u533A\u5206\u4E0D\u540C\u7684\u9A8C\u8BC1\u573A\u666F",table:{defaultValue:{summary:'"activity"'}}},site:{control:"text",description:"Shopify \u5E97\u94FA URL"},channelCode:{control:"text",description:"\u6E20\u9053\u7F16\u7801\uFF0C\u7528\u4E8E\u6807\u8BC6\u6765\u6E90\u6E20\u9053",table:{defaultValue:{summary:"undefined"}}},welcomeMessage:{control:"text",description:"\u6B22\u8FCE\u6D88\u606F",table:{defaultValue:{summary:"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F"}}},logoUrl:{control:"text",description:"Logo URL"},position:{control:"object",description:"\u6C14\u6CE1\u6309\u94AE\u4F4D\u7F6E\u5BF9\u8C61",table:{defaultValue:{summary:'{ bottom: "1.5rem", right: "1.5rem" }'}}}},args:{apiBaseUrl:"http://172.16.38.183:3003",site:"www.eufy.com",loginUserId:"test_test",welcomeMessage:"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F"}};var g=c;const m={args:{loginUserId:"test_test1",apiBaseUrl:"https://beta-api-v2-livechat.anker.com",site:"beta.soundcore.com",channelCode:"dtc",title:"eufy AI Assistant",cartId:"gid://shopify/Cart/hWNAAJweS9IkeRKib4LRy9PY?key=ff1f9b24e74b3f11f5574539e7cbbb2f",accessToken:"47b1aa2c0797043f9baba39388029d70",position:{bottom:"24px",right:"30px"},welcomeMessage:"",quickReplies:[{id:"1",label:"Product Info",value:"Tell me about your products",icon:"\u{1F4E6}"},{id:"2",label:"Track Order",value:"I want to track my order",icon:"\u{1F69A}"},{id:"3",label:"Support",value:"I need help with my device",icon:"\u{1F527}"},{id:"4",label:"Recommendations",value:"Recommend products for me",icon:"\u2B50"}],complianceConfig:{title:"Hi! I'm your eufy AI assistant.",content:"AI-generated responses can be inaccurate. Please verify important info. Do not input sensitive personal data.",checkboxText:`By starting to use "Live Chat", you agree to Anker's <a href="https://www.anker.com/pages/privacy-policy" target="_blank" rel="noopener noreferrer" style="text-decoration: underline;">LIVE CHAT PRIVACY NOTICE</a>.`,agreeButtonText:"Agree"},recaptchaAction:"livechat",showNewSessionButton:!0,commonText:{learnMore:"Learn More",total:"Total"},customRenderers:{video:{render:t=>{const e=t;return r("div",{className:"w-full",children:[o("video",{src:e.url,controls:!0,className:"w-full rounded-lg",poster:e.poster,children:"Your browser does not support video playback"}),e.title&&o("p",{className:"mt-2 text-sm text-gray-600",children:e.title})]})}},image_gallery:{render:t=>{const a=t.images||[];return o("div",{className:"grid grid-cols-2 gap-2",children:a.map((i,n)=>o("div",{className:"relative aspect-square",children:o("img",{src:i.url,alt:i.alt||`Image ${n+1}`,className:"size-full rounded-lg object-cover"})},n))})}}},productCardRender:(t,e)=>{if(!t)return o("div",{style:{padding:"16px",border:"1px dashed #ccc",borderRadius:"8px",textAlign:"center"},children:r("p",{children:["Product loading... (handle: ",e,")"]})});const a=t?.featured_image||"",i=t?.title||"",n=t?.description||"",s=t?.average_rating;return o("div",{style:{border:"2px solid #4CAF50",borderRadius:"16px",padding:"16px",margin:"12px 0",backgroundColor:"#f0f9ff",boxShadow:"0 4px 12px rgba(76, 175, 80, 0.15)"},children:r("div",{style:{display:"flex",gap:"12px",alignItems:"center"},children:[a&&o("img",{src:a,alt:i,style:{width:"60px",height:"60px",borderRadius:"8px",objectFit:"cover"}}),r("div",{style:{flex:1},children:[o("h4",{style:{margin:"0 0 4px 0",fontSize:"16px",fontWeight:"bold"},children:i}),n&&r("p",{style:{margin:0,fontSize:"12px",color:"#666",lineHeight:1.4},children:[n.slice(0,80),"..."]}),s&&r("span",{style:{fontSize:"12px",color:"#FFB800"},children:["Rating: ",s.toFixed(1)]})]}),o("button",{style:{padding:"8px 16px",backgroundColor:"#4CAF50",color:"white",borderRadius:"8px",border:"none",cursor:"pointer"},onClick:()=>console.log("View product:",e,t),children:"View"})]})})}},render:t=>o(l,{...t,onOpen:()=>console.log("[LiveChat] Chat opened"),onClose:()=>console.log("[LiveChat] Chat closed"),onMessageSend:e=>console.log("[LiveChat] Message sent:",e),onError:e=>console.error("[LiveChat] Error:",e),onTextMessage:()=>console.log("[LiveChat] AI text message received"),onProductList:()=>console.log("[LiveChat] Product list received"),onPromotionList:()=>console.log("[LiveChat] Promotion list received"),onAddToCart:e=>{console.log("[LiveChat] Add to cart:",e),alert(`Added "${e.title}" to cart!`)},onCart:(e,a)=>{console.log("[LiveChat] Cart clicked:",{cartId:e,checkoutUrl:a}),alert(`Cart ID: ${e}`)}})};export{m as Default,g as default};
52
- //# sourceMappingURL=LiveChatWidget.stories.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/stories/LiveChatWidget.stories.tsx"],
4
- "sourcesContent": ["/**\n * LiveChatWidget Storybook Stories\n * \u5C55\u793A LiveChat \u7EC4\u4EF6\u7684\u5404\u79CD\u4F7F\u7528\u573A\u666F\u548C\u914D\u7F6E\n */\n\nimport type { Meta, StoryObj } from '@storybook/react'\nimport { LiveChatWidget } from '../components/LiveChatWidget'\nimport type { MessageRenderer, MessageContent } from '../components/LiveChatWidget'\nimport '../styles/livechat.css'\n\nconst meta: Meta<typeof LiveChatWidget> = {\n title: 'Campaign/LiveChatWidget',\n component: LiveChatWidget,\n parameters: {\n layout: 'fullscreen',\n docs: {\n story: {\n inline: false,\n iframeHeight: 500,\n },\n description: {\n component: `\n# LiveChat \u804A\u5929\u7EC4\u4EF6\n\n\u53EF\u590D\u7528\u7684\u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u7EC4\u4EF6\uFF0C\u652F\u6301 SSE \u6D41\u5F0F\u6D88\u606F\u3001\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u548C\u591A\u79CD\u6D88\u606F\u7C7B\u578B\u3002\n\n## \u529F\u80FD\u7279\u6027\n\n- \uD83C\uDF88 **\u6C14\u6CE1\u5F39\u7A97**: \u53EF\u81EA\u5B9A\u4E49\u4F4D\u7F6E\u7684\u60AC\u6D6E\u6C14\u6CE1\u6309\u94AE\n- \uD83D\uDCAC **\u6D41\u5F0F\u6D88\u606F**: \u57FA\u4E8E SSE \u7684\u5B9E\u65F6\u6D41\u5F0F\u54CD\u5E94\n- \uD83D\uDCE6 **\u591A\u79CD\u6D88\u606F\u7C7B\u578B**: \u6587\u672C\u3001\u5546\u54C1\u5361\u7247\u3001\u5546\u54C1\u5217\u8868\u3001\u653F\u7B56\u3001\u5FEB\u6377\u56DE\u590D\u7B49\n- \uD83C\uDFA8 **\u53EF\u5B9A\u5236**: \u652F\u6301\u81EA\u5B9A\u4E49\u54C1\u724C\u989C\u8272\u3001Logo\u3001\u6E32\u67D3\u5668\n- \uD83D\uDCF1 **\u54CD\u5E94\u5F0F**: \u79FB\u52A8\u7AEF\u5168\u5C4F\uFF0C\u684C\u9762\u7AEF\u56FA\u5B9A\u5C3A\u5BF8\n- \uD83D\uDCBE **\u4F1A\u8BDD\u7BA1\u7406**: \u81EA\u52A8\u7BA1\u7406 userId \u548C sessionId\n- \uD83D\uDD12 **\u5B89\u5168\u9632\u62A4**: \u5185\u7F6E XSS \u9632\u62A4\u548C\u8F93\u5165\u9A8C\u8BC1\n\n## \u57FA\u7840\u7528\u6CD5\n\n\\`\\`\\`tsx\nimport { LiveChatWidget } from '@anker-in/campaign-ui'\nimport '@anker-in/campaign-ui/livechat.css'\n\nfunction App() {\n return (\n <LiveChatWidget\n apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n site=\"www.eufy.com\"\n channel_code=\"web_homepage\"\n welcomeMessage=\"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\"\n />\n )\n}\n\\`\\`\\`\n\n## \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n\n\\`\\`\\`tsx\nconst customRenderers = {\n video: {\n render: (content) => (\n <video src={content.url} controls className=\"w-full rounded\" />\n )\n }\n}\n\n<LiveChatWidget\n apiBaseUrl=\"...\"\n site=\"...\"\n customRenderers={customRenderers}\n/>\n\\`\\`\\`\n `,\n },\n },\n },\n tags: ['autodocs'],\n argTypes: {\n apiBaseUrl: {\n control: 'text',\n description: 'API \u57FA\u7840 URL',\n },\n headers: {\n control: 'object',\n description: '\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\uFF0C\u5C06\u5728\u6240\u6709 API \u8BF7\u6C42\u4E2D\u6DFB\u52A0',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n recaptchaSitekey: {\n control: 'text',\n description: 'Google reCAPTCHA v3 site key\uFF0C\u63D0\u4F9B\u6B64\u53C2\u6570\u5C06\u81EA\u52A8\u542F\u7528 reCAPTCHA v3 \u9A8C\u8BC1',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n recaptchaAction: {\n control: 'text',\n description: 'reCAPTCHA action \u540D\u79F0\uFF0C\u7528\u4E8E\u533A\u5206\u4E0D\u540C\u7684\u9A8C\u8BC1\u573A\u666F',\n table: {\n defaultValue: { summary: '\"activity\"' },\n },\n },\n site: {\n control: 'text',\n description: 'Shopify \u5E97\u94FA URL',\n },\n channelCode: {\n control: 'text',\n description: '\u6E20\u9053\u7F16\u7801\uFF0C\u7528\u4E8E\u6807\u8BC6\u6765\u6E90\u6E20\u9053',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n welcomeMessage: {\n control: 'text',\n description: '\u6B22\u8FCE\u6D88\u606F',\n table: {\n defaultValue: { summary: '\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F' },\n },\n },\n logoUrl: {\n control: 'text',\n description: 'Logo URL',\n },\n position: {\n control: 'object',\n description: '\u6C14\u6CE1\u6309\u94AE\u4F4D\u7F6E\u5BF9\u8C61',\n table: {\n defaultValue: { summary: '{ bottom: \"1.5rem\", right: \"1.5rem\" }' },\n },\n },\n },\n args: {\n apiBaseUrl: 'http://172.16.38.183:3003',\n site: 'www.eufy.com',\n loginUserId: 'test_test',\n welcomeMessage: '\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F',\n },\n}\n\nexport default meta\ntype Story = StoryObj<typeof LiveChatWidget>\n\n/**\n * \u9ED8\u8BA4\u914D\u7F6E - \u5C55\u793A\u6240\u6709\u529F\u80FD\n */\nexport const Default: Story = {\n args: {\n // \u57FA\u7840\u914D\u7F6E\n loginUserId: 'test_test1',\n apiBaseUrl: 'https://beta-api-v2-livechat.anker.com',\n site: 'beta.soundcore.com',\n channelCode: 'dtc',\n title: 'eufy AI Assistant',\n cartId: 'gid://shopify/Cart/hWNAAJweS9IkeRKib4LRy9PY?key=ff1f9b24e74b3f11f5574539e7cbbb2f',\n accessToken: '47b1aa2c0797043f9baba39388029d70',\n\n // \u81EA\u5B9A\u4E49\u4F4D\u7F6E\n position: { bottom: '24px', right: '30px' },\n\n // \u6B22\u8FCE\u6D88\u606F\n welcomeMessage: '',\n\n // \u5FEB\u6377\u56DE\u590D\n quickReplies: [\n { id: '1', label: 'Product Info', value: 'Tell me about your products', icon: '\uD83D\uDCE6' },\n { id: '2', label: 'Track Order', value: 'I want to track my order', icon: '\uD83D\uDE9A' },\n { id: '3', label: 'Support', value: 'I need help with my device', icon: '\uD83D\uDD27' },\n { id: '4', label: 'Recommendations', value: 'Recommend products for me', icon: '\u2B50' },\n ],\n\n // \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\n complianceConfig: {\n title: \"Hi! I'm your eufy AI assistant.\",\n content:\n 'AI-generated responses can be inaccurate. Please verify important info. Do not input sensitive personal data.',\n checkboxText:\n 'By starting to use \"Live Chat\", you agree to Anker\\'s <a href=\"https://www.anker.com/pages/privacy-policy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">LIVE CHAT PRIVACY NOTICE</a>.',\n agreeButtonText: 'Agree',\n },\n\n // reCAPTCHA \u914D\u7F6E\uFF08\u53D6\u6D88\u6CE8\u91CA\u4EE5\u542F\u7528\uFF09\n // recaptchaSitekey: '6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14',\n recaptchaAction: 'livechat',\n\n // \u663E\u793A\u65B0\u4F1A\u8BDD\u6309\u94AE\n showNewSessionButton: true,\n\n // \u81EA\u5B9A\u4E49\u6587\u6848\n commonText: {\n learnMore: 'Learn More',\n total: 'Total',\n },\n\n // \u81EA\u5B9A\u4E49\u6D88\u606F\u6E32\u67D3\u5668\n customRenderers: {\n video: {\n render: (content: MessageContent) => {\n const videoContent = content as any\n return (\n <div className=\"w-full\">\n <video src={videoContent.url} controls className=\"w-full rounded-lg\" poster={videoContent.poster}>\n Your browser does not support video playback\n </video>\n {videoContent.title && <p className=\"mt-2 text-sm text-gray-600\">{videoContent.title}</p>}\n </div>\n )\n },\n } as MessageRenderer,\n image_gallery: {\n render: (content: MessageContent) => {\n const galleryContent = content as any\n const images = galleryContent.images || []\n return (\n <div className=\"grid grid-cols-2 gap-2\">\n {images.map((image: any, index: number) => (\n <div key={index} className=\"relative aspect-square\">\n <img\n src={image.url}\n alt={image.alt || `Image ${index + 1}`}\n className=\"size-full rounded-lg object-cover\"\n />\n </div>\n ))}\n </div>\n )\n },\n } as MessageRenderer,\n },\n\n // \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\n productCardRender: (product, productHandle) => {\n // product \u53EF\u80FD\u4E3A undefined\uFF0C\u6B64\u65F6\u53EF\u7528 productHandle \u67E5\u8BE2\n if (!product) {\n return (\n <div style={{ padding: '16px', border: '1px dashed #ccc', borderRadius: '8px', textAlign: 'center' }}>\n <p>Product loading... (handle: {productHandle})</p>\n </div>\n )\n }\n\n const imageUrl = product?.featured_image || ''\n const title = product?.title || ''\n const description = product?.description || ''\n const averageRating = product?.average_rating\n\n return (\n <div\n style={{\n border: '2px solid #4CAF50',\n borderRadius: '16px',\n padding: '16px',\n margin: '12px 0',\n backgroundColor: '#f0f9ff',\n boxShadow: '0 4px 12px rgba(76, 175, 80, 0.15)',\n }}\n >\n <div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>\n {imageUrl && (\n <img\n src={imageUrl}\n alt={title}\n style={{ width: '60px', height: '60px', borderRadius: '8px', objectFit: 'cover' }}\n />\n )}\n <div style={{ flex: 1 }}>\n <h4 style={{ margin: '0 0 4px 0', fontSize: '16px', fontWeight: 'bold' }}>{title}</h4>\n {description && (\n <p style={{ margin: 0, fontSize: '12px', color: '#666', lineHeight: 1.4 }}>\n {description.slice(0, 80)}...\n </p>\n )}\n {averageRating && (\n <span style={{ fontSize: '12px', color: '#FFB800' }}>Rating: {averageRating.toFixed(1)}</span>\n )}\n </div>\n <button\n style={{\n padding: '8px 16px',\n backgroundColor: '#4CAF50',\n color: 'white',\n borderRadius: '8px',\n border: 'none',\n cursor: 'pointer',\n }}\n onClick={() => console.log('View product:', productHandle, product)}\n >\n View\n </button>\n </div>\n </div>\n )\n },\n },\n\n render: args => (\n <LiveChatWidget\n {...args}\n // \u6240\u6709\u4E8B\u4EF6\u56DE\u8C03\n onOpen={() => console.log('[LiveChat] Chat opened')}\n onClose={() => console.log('[LiveChat] Chat closed')}\n onMessageSend={(message: string) => console.log('[LiveChat] Message sent:', message)}\n onError={(error: Error) => console.error('[LiveChat] Error:', error)}\n onTextMessage={() => console.log('[LiveChat] AI text message received')}\n onProductList={() => console.log('[LiveChat] Product list received')}\n onPromotionList={() => console.log('[LiveChat] Promotion list received')}\n onAddToCart={(product: any) => {\n console.log('[LiveChat] Add to cart:', product)\n alert(`Added \"${product.title}\" to cart!`)\n }}\n onCart={(cartId: string, checkoutUrl?: string) => {\n console.log('[LiveChat] Cart clicked:', { cartId, checkoutUrl })\n alert(`Cart ID: ${cartId}`)\n }}\n />\n ),\n}\n"],
5
- "mappings": "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",
6
- "names": ["jsx", "jsxs", "LiveChatWidget", "meta", "LiveChatWidget_stories_default", "Default", "content", "videoContent", "images", "image", "index", "product", "productHandle", "imageUrl", "title", "description", "averageRating", "args", "message", "error", "cartId", "checkoutUrl"]
7
- }
@@ -1,7 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import { Chat } from '../components/index';
3
- import '../styles/chat.css';
4
- type Story = StoryObj<typeof meta>;
5
- declare const meta: Meta<typeof Chat>;
6
- export default meta;
7
- export declare const Default: Story;
@@ -1,2 +0,0 @@
1
- import{jsx as t}from"react/jsx-runtime";import{Chat as e}from"../components/index";import"../styles/chat.css";const a={title:"Campaign/Chat",component:e,args:{title:"DTC Live Chat",runtimeUrl:"https://beta-api-livechat.anker.com",shopifyDomain:"soundcoreusa.myshopify.com"},tags:["autodocs"],decorators:[o=>t("div",{style:{height:"700px"},children:t(o,{})})]};var m=a;const n={args:{title:"DTC Live Chat",runtimeUrl:"https://beta-api-livechat.anker.com",shopifyDomain:"soundcoreusa.myshopify.com",userId:"arno7",showResponseButton:"follow",lang:{popupTip:"Hi ! Welcome to soundcore Innovations live chat\uFF01",popupTipTimeout:[3e3,6e3]}}};export{n as Default,m as default};
2
- //# sourceMappingURL=chat.stories.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/stories/chat.stories.tsx"],
4
- "sourcesContent": ["import React from 'react'\nimport type { Meta, StoryObj } from '@storybook/react'\nimport { Chat } from '../components/index'\nimport '../styles/chat.css'\n\ntype Story = StoryObj<typeof meta>\n\n// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export\nconst meta: Meta<typeof Chat> = {\n // \u540C\u65F6\u5B9A\u4E49\u4E86\u9875\u9762\u5DE6\u4FA7\u83DC\u5355\n title: 'Campaign/Chat',\n component: Chat,\n args: {\n title: 'DTC Live Chat',\n runtimeUrl: 'https://beta-api-livechat.anker.com',\n shopifyDomain: 'soundcoreusa.myshopify.com',\n },\n // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs\n tags: ['autodocs'],\n // More on argTypes: https://storybook.js.org/docs/api/argtypes\n decorators: [\n Story => (\n <div style={{ height: '700px' }}>\n <Story />\n </div>\n ),\n ],\n} satisfies Meta<typeof Chat>\n\nexport default meta\n\nexport const Default: Story = {\n args: {\n title: 'DTC Live Chat',\n runtimeUrl: 'https://beta-api-livechat.anker.com',\n shopifyDomain: 'soundcoreusa.myshopify.com',\n userId: 'arno7',\n showResponseButton: 'follow',\n lang: { popupTip: 'Hi ! Welcome to soundcore Innovations live chat\uFF01', popupTipTimeout: [3000, 6000] },\n },\n}\n"],
5
- "mappings": "AAuBQ,cAAAA,MAAA,oBArBR,OAAS,QAAAC,MAAY,sBACrB,MAAO,qBAKP,MAAMC,EAA0B,CAE9B,MAAO,gBACP,UAAWD,EACX,KAAM,CACJ,MAAO,gBACP,WAAY,sCACZ,cAAe,4BACjB,EAEA,KAAM,CAAC,UAAU,EAEjB,WAAY,CACVE,GACEH,EAAC,OAAI,MAAO,CAAE,OAAQ,OAAQ,EAC5B,SAAAA,EAACG,EAAA,EAAM,EACT,CAEJ,CACF,EAEA,IAAOC,EAAQF,EAER,MAAMG,EAAiB,CAC5B,KAAM,CACJ,MAAO,gBACP,WAAY,sCACZ,cAAe,6BACf,OAAQ,QACR,mBAAoB,SACpB,KAAM,CAAE,SAAU,wDAAoD,gBAAiB,CAAC,IAAM,GAAI,CAAE,CACtG,CACF",
6
- "names": ["jsx", "Chat", "meta", "Story", "chat_stories_default", "Default"]
7
- }
@@ -1,317 +0,0 @@
1
- /**
2
- * LiveChatWidget Storybook Stories
3
- * 展示 LiveChat 组件的各种使用场景和配置
4
- */
5
-
6
- import type { Meta, StoryObj } from '@storybook/react'
7
- import { LiveChatWidget } from '../components/LiveChatWidget'
8
- import type { MessageRenderer, MessageContent } from '../components/LiveChatWidget'
9
- import '../styles/livechat.css'
10
-
11
- const meta: Meta<typeof LiveChatWidget> = {
12
- title: 'Campaign/LiveChatWidget',
13
- component: LiveChatWidget,
14
- parameters: {
15
- layout: 'fullscreen',
16
- docs: {
17
- story: {
18
- inline: false,
19
- iframeHeight: 500,
20
- },
21
- description: {
22
- component: `
23
- # LiveChat 聊天组件
24
-
25
- 可复用的气泡弹窗聊天组件,支持 SSE 流式消息、自定义渲染器和多种消息类型。
26
-
27
- ## 功能特性
28
-
29
- - 🎈 **气泡弹窗**: 可自定义位置的悬浮气泡按钮
30
- - 💬 **流式消息**: 基于 SSE 的实时流式响应
31
- - 📦 **多种消息类型**: 文本、商品卡片、商品列表、政策、快捷回复等
32
- - 🎨 **可定制**: 支持自定义品牌颜色、Logo、渲染器
33
- - 📱 **响应式**: 移动端全屏,桌面端固定尺寸
34
- - 💾 **会话管理**: 自动管理 userId 和 sessionId
35
- - 🔒 **安全防护**: 内置 XSS 防护和输入验证
36
-
37
- ## 基础用法
38
-
39
- \`\`\`tsx
40
- import { LiveChatWidget } from '@anker-in/campaign-ui'
41
- import '@anker-in/campaign-ui/livechat.css'
42
-
43
- function App() {
44
- return (
45
- <LiveChatWidget
46
- apiBaseUrl="https://beta-api-livechat.anker.com"
47
- site="www.eufy.com"
48
- channel_code="web_homepage"
49
- welcomeMessage="你好!我是 AI 助手"
50
- />
51
- )
52
- }
53
- \`\`\`
54
-
55
- ## 自定义渲染器
56
-
57
- \`\`\`tsx
58
- const customRenderers = {
59
- video: {
60
- render: (content) => (
61
- <video src={content.url} controls className="w-full rounded" />
62
- )
63
- }
64
- }
65
-
66
- <LiveChatWidget
67
- apiBaseUrl="..."
68
- site="..."
69
- customRenderers={customRenderers}
70
- />
71
- \`\`\`
72
- `,
73
- },
74
- },
75
- },
76
- tags: ['autodocs'],
77
- argTypes: {
78
- apiBaseUrl: {
79
- control: 'text',
80
- description: 'API 基础 URL',
81
- },
82
- headers: {
83
- control: 'object',
84
- description: '自定义请求头,将在所有 API 请求中添加',
85
- table: {
86
- defaultValue: { summary: 'undefined' },
87
- },
88
- },
89
- recaptchaSitekey: {
90
- control: 'text',
91
- description: 'Google reCAPTCHA v3 site key,提供此参数将自动启用 reCAPTCHA v3 验证',
92
- table: {
93
- defaultValue: { summary: 'undefined' },
94
- },
95
- },
96
- recaptchaAction: {
97
- control: 'text',
98
- description: 'reCAPTCHA action 名称,用于区分不同的验证场景',
99
- table: {
100
- defaultValue: { summary: '"activity"' },
101
- },
102
- },
103
- site: {
104
- control: 'text',
105
- description: 'Shopify 店铺 URL',
106
- },
107
- channelCode: {
108
- control: 'text',
109
- description: '渠道编码,用于标识来源渠道',
110
- table: {
111
- defaultValue: { summary: 'undefined' },
112
- },
113
- },
114
- welcomeMessage: {
115
- control: 'text',
116
- description: '欢迎消息',
117
- table: {
118
- defaultValue: { summary: '你好!我是 AI 助手,有什么可以帮助你的吗?' },
119
- },
120
- },
121
- logoUrl: {
122
- control: 'text',
123
- description: 'Logo URL',
124
- },
125
- position: {
126
- control: 'object',
127
- description: '气泡按钮位置对象',
128
- table: {
129
- defaultValue: { summary: '{ bottom: "1.5rem", right: "1.5rem" }' },
130
- },
131
- },
132
- },
133
- args: {
134
- apiBaseUrl: 'http://172.16.38.183:3003',
135
- site: 'www.eufy.com',
136
- loginUserId: 'test_test',
137
- welcomeMessage: '你好!我是 AI 助手,有什么可以帮助你的吗?',
138
- },
139
- }
140
-
141
- export default meta
142
- type Story = StoryObj<typeof LiveChatWidget>
143
-
144
- /**
145
- * 默认配置 - 展示所有功能
146
- */
147
- export const Default: Story = {
148
- args: {
149
- // 基础配置
150
- loginUserId: 'test_test1',
151
- apiBaseUrl: 'https://beta-api-v2-livechat.anker.com',
152
- site: 'beta.soundcore.com',
153
- channelCode: 'dtc',
154
- title: 'eufy AI Assistant',
155
- cartId: 'gid://shopify/Cart/hWNAAJweS9IkeRKib4LRy9PY?key=ff1f9b24e74b3f11f5574539e7cbbb2f',
156
- accessToken: '47b1aa2c0797043f9baba39388029d70',
157
-
158
- // 自定义位置
159
- position: { bottom: '24px', right: '30px' },
160
-
161
- // 欢迎消息
162
- welcomeMessage: '',
163
-
164
- // 快捷回复
165
- quickReplies: [
166
- { id: '1', label: 'Product Info', value: 'Tell me about your products', icon: '📦' },
167
- { id: '2', label: 'Track Order', value: 'I want to track my order', icon: '🚚' },
168
- { id: '3', label: 'Support', value: 'I need help with my device', icon: '🔧' },
169
- { id: '4', label: 'Recommendations', value: 'Recommend products for me', icon: '⭐' },
170
- ],
171
-
172
- // 法规协议弹窗
173
- complianceConfig: {
174
- title: "Hi! I'm your eufy AI assistant.",
175
- content:
176
- 'AI-generated responses can be inaccurate. Please verify important info. Do not input sensitive personal data.',
177
- checkboxText:
178
- 'By starting to use "Live Chat", you agree to Anker\'s <a href="https://www.anker.com/pages/privacy-policy" target="_blank" rel="noopener noreferrer" style="text-decoration: underline;">LIVE CHAT PRIVACY NOTICE</a>.',
179
- agreeButtonText: 'Agree',
180
- },
181
-
182
- // reCAPTCHA 配置(取消注释以启用)
183
- // recaptchaSitekey: '6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14',
184
- recaptchaAction: 'livechat',
185
-
186
- // 显示新会话按钮
187
- showNewSessionButton: true,
188
-
189
- // 自定义文案
190
- commonText: {
191
- learnMore: 'Learn More',
192
- total: 'Total',
193
- },
194
-
195
- // 自定义消息渲染器
196
- customRenderers: {
197
- video: {
198
- render: (content: MessageContent) => {
199
- const videoContent = content as any
200
- return (
201
- <div className="w-full">
202
- <video src={videoContent.url} controls className="w-full rounded-lg" poster={videoContent.poster}>
203
- Your browser does not support video playback
204
- </video>
205
- {videoContent.title && <p className="mt-2 text-sm text-gray-600">{videoContent.title}</p>}
206
- </div>
207
- )
208
- },
209
- } as MessageRenderer,
210
- image_gallery: {
211
- render: (content: MessageContent) => {
212
- const galleryContent = content as any
213
- const images = galleryContent.images || []
214
- return (
215
- <div className="grid grid-cols-2 gap-2">
216
- {images.map((image: any, index: number) => (
217
- <div key={index} className="relative aspect-square">
218
- <img
219
- src={image.url}
220
- alt={image.alt || `Image ${index + 1}`}
221
- className="size-full rounded-lg object-cover"
222
- />
223
- </div>
224
- ))}
225
- </div>
226
- )
227
- },
228
- } as MessageRenderer,
229
- },
230
-
231
- // 自定义产品卡片渲染
232
- productCardRender: (product, productHandle) => {
233
- // product 可能为 undefined,此时可用 productHandle 查询
234
- if (!product) {
235
- return (
236
- <div style={{ padding: '16px', border: '1px dashed #ccc', borderRadius: '8px', textAlign: 'center' }}>
237
- <p>Product loading... (handle: {productHandle})</p>
238
- </div>
239
- )
240
- }
241
-
242
- const imageUrl = product?.featured_image || ''
243
- const title = product?.title || ''
244
- const description = product?.description || ''
245
- const averageRating = product?.average_rating
246
-
247
- return (
248
- <div
249
- style={{
250
- border: '2px solid #4CAF50',
251
- borderRadius: '16px',
252
- padding: '16px',
253
- margin: '12px 0',
254
- backgroundColor: '#f0f9ff',
255
- boxShadow: '0 4px 12px rgba(76, 175, 80, 0.15)',
256
- }}
257
- >
258
- <div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
259
- {imageUrl && (
260
- <img
261
- src={imageUrl}
262
- alt={title}
263
- style={{ width: '60px', height: '60px', borderRadius: '8px', objectFit: 'cover' }}
264
- />
265
- )}
266
- <div style={{ flex: 1 }}>
267
- <h4 style={{ margin: '0 0 4px 0', fontSize: '16px', fontWeight: 'bold' }}>{title}</h4>
268
- {description && (
269
- <p style={{ margin: 0, fontSize: '12px', color: '#666', lineHeight: 1.4 }}>
270
- {description.slice(0, 80)}...
271
- </p>
272
- )}
273
- {averageRating && (
274
- <span style={{ fontSize: '12px', color: '#FFB800' }}>Rating: {averageRating.toFixed(1)}</span>
275
- )}
276
- </div>
277
- <button
278
- style={{
279
- padding: '8px 16px',
280
- backgroundColor: '#4CAF50',
281
- color: 'white',
282
- borderRadius: '8px',
283
- border: 'none',
284
- cursor: 'pointer',
285
- }}
286
- onClick={() => console.log('View product:', productHandle, product)}
287
- >
288
- View
289
- </button>
290
- </div>
291
- </div>
292
- )
293
- },
294
- },
295
-
296
- render: args => (
297
- <LiveChatWidget
298
- {...args}
299
- // 所有事件回调
300
- onOpen={() => console.log('[LiveChat] Chat opened')}
301
- onClose={() => console.log('[LiveChat] Chat closed')}
302
- onMessageSend={(message: string) => console.log('[LiveChat] Message sent:', message)}
303
- onError={(error: Error) => console.error('[LiveChat] Error:', error)}
304
- onTextMessage={() => console.log('[LiveChat] AI text message received')}
305
- onProductList={() => console.log('[LiveChat] Product list received')}
306
- onPromotionList={() => console.log('[LiveChat] Promotion list received')}
307
- onAddToCart={(product: any) => {
308
- console.log('[LiveChat] Add to cart:', product)
309
- alert(`Added "${product.title}" to cart!`)
310
- }}
311
- onCart={(cartId: string, checkoutUrl?: string) => {
312
- console.log('[LiveChat] Cart clicked:', { cartId, checkoutUrl })
313
- alert(`Cart ID: ${cartId}`)
314
- }}
315
- />
316
- ),
317
- }
@@ -1,41 +0,0 @@
1
- import React from 'react'
2
- import type { Meta, StoryObj } from '@storybook/react'
3
- import { Chat } from '../components/index'
4
- import '../styles/chat.css'
5
-
6
- type Story = StoryObj<typeof meta>
7
-
8
- // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
9
- const meta: Meta<typeof Chat> = {
10
- // 同时定义了页面左侧菜单
11
- title: 'Campaign/Chat',
12
- component: Chat,
13
- args: {
14
- title: 'DTC Live Chat',
15
- runtimeUrl: 'https://beta-api-livechat.anker.com',
16
- shopifyDomain: 'soundcoreusa.myshopify.com',
17
- },
18
- // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
19
- tags: ['autodocs'],
20
- // More on argTypes: https://storybook.js.org/docs/api/argtypes
21
- decorators: [
22
- Story => (
23
- <div style={{ height: '700px' }}>
24
- <Story />
25
- </div>
26
- ),
27
- ],
28
- } satisfies Meta<typeof Chat>
29
-
30
- export default meta
31
-
32
- export const Default: Story = {
33
- args: {
34
- title: 'DTC Live Chat',
35
- runtimeUrl: 'https://beta-api-livechat.anker.com',
36
- shopifyDomain: 'soundcoreusa.myshopify.com',
37
- userId: 'arno7',
38
- showResponseButton: 'follow',
39
- lang: { popupTip: 'Hi ! Welcome to soundcore Innovations live chat!', popupTipTimeout: [3000, 6000] },
40
- },
41
- }