@anker-in/campaign-ui 0.2.11-beta.40 → 0.2.11-beta.42
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/dist/cjs/components/LiveChatWidget/LiveChatWidget.js +1 -1
- package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/api/chat.js +2 -2
- package/dist/cjs/components/LiveChatWidget/api/chat.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.js +6 -6
- package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageList.js +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageList.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.d.ts +4 -2
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js +1 -1
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/types.d.ts +18 -11
- package/dist/cjs/components/LiveChatWidget/types.js.map +1 -1
- package/dist/cjs/components/index.d.ts +1 -1
- package/dist/cjs/components/index.js +1 -1
- package/dist/cjs/components/index.js.map +2 -2
- package/dist/cjs/stories/LiveChatWidget.stories.d.ts +1 -109
- package/dist/cjs/stories/LiveChatWidget.stories.js +7 -166
- package/dist/cjs/stories/LiveChatWidget.stories.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.js +1 -1
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/api/chat.js +2 -2
- package/dist/esm/components/LiveChatWidget/api/chat.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js +7 -7
- package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageList.js +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageList.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.d.ts +4 -2
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.js +1 -1
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/types.d.ts +18 -11
- package/dist/esm/components/index.d.ts +1 -1
- package/dist/esm/components/index.js +1 -1
- package/dist/esm/components/index.js.map +2 -2
- package/dist/esm/stories/LiveChatWidget.stories.d.ts +1 -109
- package/dist/esm/stories/LiveChatWidget.stories.js +7 -166
- package/dist/esm/stories/LiveChatWidget.stories.js.map +3 -3
- package/package.json +1 -1
- package/src/components/LiveChatWidget/LiveChatWidget.tsx +65 -23
- package/src/components/LiveChatWidget/api/chat.ts +8 -7
- package/src/components/LiveChatWidget/components/ComplianceDialog.tsx +1 -5
- package/src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx +17 -8
- package/src/components/LiveChatWidget/components/MessageList.tsx +11 -17
- package/src/components/LiveChatWidget/hooks/useChatState.ts +64 -98
- package/src/components/LiveChatWidget/types.ts +18 -11
- package/src/components/index.ts +2 -1
- package/src/stories/LiveChatWidget.stories.tsx +95 -660
- package/src/styles/livechat.css +29 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{jsx as o,jsxs as
|
|
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
2
|
# LiveChat \u804A\u5929\u7EC4\u4EF6
|
|
3
3
|
|
|
4
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
|
|
@@ -48,171 +48,12 @@ const customRenderers = {
|
|
|
48
48
|
customRenderers={customRenderers}
|
|
49
49
|
/>
|
|
50
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
|
|
52
|
-
\u{1F44B} \u60A8\u597D\uFF01\u6B22\u8FCE\u6765\u5230 Soundcore \u5B98\u65B9\u5546\u57CE\u3002
|
|
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 u={args:{loginUserId:"test_test",apiBaseUrl:"http://172.16.38.183:3003",site:"beta.eufy.com",channelCode:"dtc",title:"eufy AI Assistant",cartId:"gid://shopify/Cart/hWN7wB3Pa12gh78d8hPOAUBI?key=0e73db1d3fb5ac21da19099c45033253",accessToken:"47b1aa2c0797043f9baba39388029d70",position:{bottom:"24px",right:"30px"},welcomeMessage:`Welcome to eufy AI Assistant!
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
- \u{1F4AC} \u89E3\u7B54\u7591\u95EE
|
|
53
|
+
I can help you with:
|
|
54
|
+
- Product recommendations
|
|
55
|
+
- Order tracking
|
|
56
|
+
- FAQs and support
|
|
59
57
|
|
|
60
|
-
\
|
|
61
|
-
`.trim()}},v={args:{title:"",showNewSessionButton:!0,position:{bottom:"24px",right:"30px"},chatBubbleIcon:"https://cdn.shopify.com/s/files/1/0504/7094/4954/files/Rectangle_400770314.png?v=1768894153",headers:void 0,site:"beta.eufy.com"},render:t=>o(s,{...t,onOpen:()=>{console.log("[Story] Chat opened")},onClose:()=>{console.log("[Story] Chat closed")},onMessageSend:e=>{console.log("[Story] Message sent:",e)},onError:e=>{console.error("[Story] Error occurred:",e)},onTextMessage:()=>{console.log("\u{1F4DD} [Story] AI started replying with text message")},onProductList:()=>{console.log("\u{1F4E6} [Story] AI replied with product list card")},onPromotionList:()=>{console.log("\u{1F389} [Story] AI replied with promotion list card")},onAddToCart:e=>{console.log("\u{1F6D2} [Story] Add to Cart clicked!"),console.log("\u{1F4E6} Product Info:",{id:e.shopifyId,title:e.title,price:e.price,imageUrl:e.imageUrl,productUrl:e.productUrl}),console.log("\u{1F4CB} Full Product Object:",e),alert(`\u2705 "${e.title}" \u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\uFF01
|
|
62
|
-
|
|
63
|
-
\u4EF7\u683C: ${e.price.currency} ${e.price.amount}`)},onCart:(e,r)=>{console.log("\u{1F6D2} [Story] Cart button clicked!"),console.log("\u{1F4CB} Cart ID:",e),console.log("\u{1F517} Checkout URL:",r),alert(`\u8D2D\u7269\u8F66\u6309\u94AE\u88AB\u70B9\u51FB\uFF01
|
|
64
|
-
|
|
65
|
-
Cart ID: ${e}
|
|
66
|
-
Checkout URL: ${r||"\u65E0"}`)}})},b={args:{customRenderers:{video:{render:t=>{const e=t;return a("div",{className:"w-full",children:[o("video",{src:e.url,controls:!0,className:"w-full rounded-lg",poster:e.poster,children:"\u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u89C6\u9891\u64AD\u653E"}),e.title&&o("p",{className:"mt-2 text-sm text-gray-600",children:e.title})]})}}}}},A={args:{customRenderers:{image_gallery:{render:t=>{const r=t.images||[];return o("div",{className:"grid grid-cols-2 gap-2",children:r.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))})}}}}},S={parameters:{viewport:{defaultViewport:"mobile1"}},args:{}},w={parameters:{viewport:{defaultViewport:"tablet"}},args:{}},k={render:()=>a("div",{className:"relative h-screen w-full bg-gray-50",children:[o("div",{className:"absolute inset-0 flex items-center justify-center",children:o("p",{className:"text-gray-500",children:"\u9875\u9762\u5185\u5BB9\u533A\u57DF"})}),o("div",{className:"absolute bottom-4 right-4 text-xs text-gray-400",children:"\u9ED8\u8BA4\u4F4D\u7F6E (\u53F3\u4E0B\u89D2)"}),o(s,{apiBaseUrl:"https://beta-api-livechat.anker.com",site:"www.eufy.com"})]})},I={render:t=>o(s,{...t,onAddToCart:e=>{console.log("\u{1F6D2} [ProductComparison Story] Add to Cart clicked!"),console.log("\u{1F4E6} Product Info:",{id:e.shopifyId,title:e.title,price:e.price,imageUrl:e.imageUrl,productUrl:e.productUrl}),console.log("\u{1F4CB} Full Product Object:",e),alert(`\u2705 "${e.title}" \u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\uFF01
|
|
67
|
-
|
|
68
|
-
\u4EF7\u683C: ${e.price.currency} ${e.price.amount}`)}}),args:{welcomeMessage:"\u4EE5\u4E0B\u662F\u60A8\u5173\u6CE8\u7684\u4EA7\u54C1\u5BF9\u6BD4\u4FE1\u606F\uFF1A"},parameters:{docs:{description:{story:`
|
|
69
|
-
\u4EA7\u54C1\u5BF9\u6BD4\u6D88\u606F\u652F\u6301\u4EE5\u4E0B\u7EF4\u5EA6\uFF1A
|
|
70
|
-
|
|
71
|
-
- **\u4EF7\u683C\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u7684\u4EF7\u683C\u533A\u95F4\u548C\u4FC3\u9500\u6807\u7B7E
|
|
72
|
-
- **\u4F1A\u5458\u4EF7\u5BF9\u6BD4**: \u663E\u793A\u4F1A\u5458\u4E13\u4EAB\u4EF7\u683C\uFF08\u5982\u679C\u53EF\u7528\uFF09
|
|
73
|
-
- **\u53D8\u4F53\u6570\u91CF\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u7684\u53EF\u9009\u53D8\u4F53\u6570\u91CF
|
|
74
|
-
- **\u6298\u6263\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u662F\u5426\u6709\u6298\u6263\u6D3B\u52A8
|
|
75
|
-
|
|
76
|
-
\u7EC4\u4EF6\u4F1A\u81EA\u52A8\u5904\u7406\u4ECE\u540E\u7AEF\u8FD4\u56DE\u7684\u4EA7\u54C1\u5BF9\u6BD4\u6570\u636E\uFF0C\u5305\u62EC\uFF1A
|
|
77
|
-
- \u4EA7\u54C1\u57FA\u672C\u4FE1\u606F\uFF08\u56FE\u7247\u3001\u6807\u9898\u3001\u4EF7\u683C\u7B49\uFF09
|
|
78
|
-
- \u591A\u7EF4\u5EA6\u5BF9\u6BD4\u6570\u636E\uFF08\u4EF7\u683C\u3001\u4F1A\u5458\u4EF7\u3001\u53D8\u4F53\u3001\u6298\u6263\uFF09
|
|
79
|
-
- \u54CD\u5E94\u5F0F\u5E03\u5C40\uFF08\u79FB\u52A8\u7AEF\u53EF\u6A2A\u5411\u6EDA\u52A8\uFF09
|
|
80
|
-
|
|
81
|
-
**\u6D4B\u8BD5 Add to Cart \u529F\u80FD\uFF1A**
|
|
82
|
-
- \u70B9\u51FB\u4EA7\u54C1\u5BF9\u6BD4\u5361\u7247\u4E2D\u7684 "Add to Cart" \u6309\u94AE
|
|
83
|
-
- \u67E5\u770B\u63A7\u5236\u53F0\u8F93\u51FA\u7684\u4EA7\u54C1\u4FE1\u606F
|
|
84
|
-
- \u4F1A\u5F39\u51FA\u786E\u8BA4\u63D0\u793A\u6846\u663E\u793A\u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66
|
|
85
|
-
`}}}},T={args:{welcomeMessage:"\u4EE5\u4E0B\u662F\u4E0E\u60A8\u7684\u95EE\u9898\u76F8\u5173\u7684\u5E38\u89C1\u89E3\u7B54\uFF1A"},parameters:{docs:{description:{story:`
|
|
86
|
-
FAQ \u5217\u8868\u6D88\u606F\u652F\u6301\uFF1A
|
|
87
|
-
|
|
88
|
-
- **\u53EF\u6298\u53E0\u95EE\u9898**: \u70B9\u51FB\u95EE\u9898\u6807\u9898\u5C55\u5F00/\u6298\u53E0\u7B54\u6848
|
|
89
|
-
- **Markdown \u7B54\u6848**: \u7B54\u6848\u652F\u6301 Markdown \u683C\u5F0F\uFF0C\u53EF\u663E\u793A\u5BCC\u6587\u672C
|
|
90
|
-
- **\u76F8\u5173\u95EE\u9898**: \u5C55\u5F00\u7B54\u6848\u540E\u663E\u793A\u76F8\u5173\u95EE\u9898\uFF0C\u70B9\u51FB\u53EF\u89E6\u53D1\u65B0\u641C\u7D22
|
|
91
|
-
- **\u5206\u7C7B\u6807\u8BB0**: \u6839\u636E\u95EE\u9898\u5206\u7C7B\u663E\u793A\uFF08\u914D\u9001\u3001\u9000\u8D27\u3001\u4EA7\u54C1\u3001\u652F\u4ED8\u3001\u901A\u7528\uFF09
|
|
92
|
-
- **\u641C\u7D22\u7ED3\u679C\u7EDF\u8BA1**: \u663E\u793A\u627E\u5230\u7684\u95EE\u9898\u6570\u91CF
|
|
93
|
-
|
|
94
|
-
\u4F7F\u7528\u573A\u666F\uFF1A
|
|
95
|
-
- \u7528\u6237\u8BE2\u95EE\u5E38\u89C1\u95EE\u9898\u65F6\u8FD4\u56DE FAQ \u5217\u8868
|
|
96
|
-
- \u4EA7\u54C1\u4E13\u5C5E\u95EE\u9898\u67E5\u8BE2
|
|
97
|
-
- \u6309\u5206\u7C7B\u67E5\u8BE2 FAQ
|
|
98
|
-
`}}}},U={args:{recaptchaSitekey:"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14",recaptchaAction:"livechat"},parameters:{docs:{description:{story:`
|
|
99
|
-
### reCAPTCHA v3 \u914D\u7F6E
|
|
100
|
-
|
|
101
|
-
\u542F\u7528 reCAPTCHA v3 \u53EF\u4EE5\u4FDD\u62A4\u4F60\u7684 API \u514D\u53D7\u673A\u5668\u4EBA\u548C\u6EE5\u7528\u884C\u4E3A\u7684\u653B\u51FB\u3002
|
|
102
|
-
|
|
103
|
-
**\u914D\u7F6E\u6B65\u9AA4\uFF1A**
|
|
104
|
-
|
|
105
|
-
1. \u5728 [Google reCAPTCHA](https://www.google.com/recaptcha/admin) \u521B\u5EFA v3 \u5BC6\u94A5
|
|
106
|
-
2. \u5728\u9875\u9762\u4E2D\u52A0\u8F7D reCAPTCHA \u811A\u672C\uFF1A
|
|
107
|
-
\`\`\`html
|
|
108
|
-
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"><\/script>
|
|
109
|
-
\`\`\`
|
|
110
|
-
3. \u914D\u7F6E LiveChatWidget \u7EC4\u4EF6\uFF08\u63D0\u4F9B sitekey \u5373\u81EA\u52A8\u542F\u7528\uFF09\uFF1A
|
|
111
|
-
\`\`\`tsx
|
|
112
|
-
<LiveChatWidget
|
|
113
|
-
recaptchaSitekey="YOUR_SITE_KEY"
|
|
114
|
-
recaptchaAction="livechat"
|
|
115
|
-
/>
|
|
116
|
-
\`\`\`
|
|
117
|
-
|
|
118
|
-
**\u5DE5\u4F5C\u539F\u7406\uFF1A**
|
|
119
|
-
|
|
120
|
-
- \u63D0\u4F9B recaptchaSitekey \u53C2\u6570\u5373\u81EA\u52A8\u542F\u7528 reCAPTCHA \u9A8C\u8BC1
|
|
121
|
-
- \u6BCF\u6B21\u53D1\u9001\u6D88\u606F\u6216\u521B\u5EFA\u4F1A\u8BDD\u65F6\uFF0C\u4F1A\u81EA\u52A8\u83B7\u53D6 reCAPTCHA token
|
|
122
|
-
- token \u4F1A\u901A\u8FC7 \`X-Recaptcha-Token\` header \u53D1\u9001\u5230\u540E\u7AEF
|
|
123
|
-
- \u540E\u7AEF\u9700\u8981\u9A8C\u8BC1 token \u7684\u6709\u6548\u6027
|
|
124
|
-
|
|
125
|
-
**\u6CE8\u610F\u4E8B\u9879\uFF1A**
|
|
126
|
-
|
|
127
|
-
- reCAPTCHA v3 \u5728\u540E\u53F0\u8FD0\u884C\uFF0C\u4E0D\u4F1A\u6253\u65AD\u7528\u6237\u4F53\u9A8C
|
|
128
|
-
- \u5EFA\u8BAE\u4E3A\u4E0D\u540C\u7684\u64CD\u4F5C\u4F7F\u7528\u4E0D\u540C\u7684 \`recaptchaAction\` \u4EE5\u4FBF\u5206\u6790
|
|
129
|
-
- \u786E\u4FDD\u540E\u7AEF\u6B63\u786E\u9A8C\u8BC1 reCAPTCHA token
|
|
130
|
-
- \u4E0D\u63D0\u4F9B recaptchaSitekey \u5219\u4E0D\u542F\u7528\u9A8C\u8BC1\uFF08\u4EC5\u7528\u4E8E\u5F00\u53D1\u73AF\u5883\uFF09
|
|
131
|
-
`}}}},L={args:{loginUserId:"test_test",apiBaseUrl:"http://172.16.38.183:3003",site:"beta.eufy.com",channelCode:"dtc",title:"eufy AI Assistant",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"}},parameters:{docs:{description:{story:`
|
|
132
|
-
### \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\u529F\u80FD
|
|
133
|
-
|
|
134
|
-
\u4F7F\u7528 \`complianceConfig\` \u5C5E\u6027\u53EF\u4EE5\u5728\u7528\u6237\u9996\u6B21\u70B9\u51FB\u804A\u5929\u6C14\u6CE1\u65F6\u663E\u793A\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\u3002
|
|
135
|
-
|
|
136
|
-
**\u529F\u80FD\u7279\u6027\uFF1A**
|
|
137
|
-
|
|
138
|
-
- \u2705 \u7528\u6237\u5FC5\u987B\u52FE\u9009\u590D\u9009\u6846\u624D\u80FD\u70B9\u51FB"\u540C\u610F"\u6309\u94AE
|
|
139
|
-
- \u2705 \u652F\u6301\u5728\u52FE\u9009\u6846\u6587\u672C\u4E2D\u5D4C\u5165\u94FE\u63A5\uFF08\u4F7F\u7528 \`{link}\` \u5360\u4F4D\u7B26\uFF09
|
|
140
|
-
- \u2705 \u652F\u6301 HTML \u683C\u5F0F\u7684\u5185\u5BB9\u6587\u672C
|
|
141
|
-
- \u2705 \u540C\u610F\u540E\u81EA\u52A8\u6253\u5F00\u804A\u5929\u7A97\u53E3
|
|
142
|
-
- \u2705 \u5173\u95ED\u5F39\u7A97\u4E0D\u4F1A\u6253\u5F00\u804A\u5929\u7A97\u53E3
|
|
143
|
-
- \u2705 \u540C\u610F\u540E\u518D\u6B21\u70B9\u51FB\u6C14\u6CE1\u76F4\u63A5\u6253\u5F00\u804A\u5929\uFF08\u65E0\u9700\u91CD\u590D\u540C\u610F\uFF09
|
|
144
|
-
- \u2705 \u54CD\u5E94\u5F0F\u8BBE\u8BA1\uFF0C\u79FB\u52A8\u7AEF\u53CB\u597D
|
|
145
|
-
- \u2705 **\u4E0E\u804A\u5929\u7A97\u53E3\u4F4D\u7F6E\u4E00\u81F4**\uFF08\u53F3\u4E0B\u89D2\u6216\u5E95\u90E8\u56FA\u5B9A\uFF09
|
|
146
|
-
- \u2705 **\u65E0\u906E\u7F69\u5C42\u8BBE\u8BA1**\uFF0C\u4E0D\u906E\u6321\u9875\u9762\u5185\u5BB9
|
|
147
|
-
|
|
148
|
-
**\u6D4B\u8BD5\u6B65\u9AA4\uFF1A**
|
|
149
|
-
|
|
150
|
-
1. \u70B9\u51FB\u804A\u5929\u6C14\u6CE1\u6309\u94AE
|
|
151
|
-
2. \u67E5\u770B\u5F39\u51FA\u7684\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97
|
|
152
|
-
3. \u5C1D\u8BD5\u4E0D\u52FE\u9009\u590D\u9009\u6846\u65F6\u70B9\u51FB"Agree"\u6309\u94AE\uFF08\u6309\u94AE\u5E94\u8BE5\u662F\u7981\u7528\u72B6\u6001\uFF09
|
|
153
|
-
4. \u52FE\u9009\u590D\u9009\u6846\u540E\u70B9\u51FB"Agree"
|
|
154
|
-
5. \u89C2\u5BDF\u5F39\u7A97\u5173\u95ED\u5E76\u81EA\u52A8\u6253\u5F00\u804A\u5929\u7A97\u53E3
|
|
155
|
-
6. \u5173\u95ED\u804A\u5929\u7A97\u53E3\u540E\u518D\u6B21\u70B9\u51FB\u6C14\u6CE1\uFF08\u5E94\u8BE5\u76F4\u63A5\u6253\u5F00\u804A\u5929\uFF0C\u4E0D\u518D\u663E\u793A\u6CD5\u89C4\u5F39\u7A97\uFF09
|
|
156
|
-
|
|
157
|
-
**\u914D\u7F6E\u9879\u8BF4\u660E\uFF1A**
|
|
158
|
-
|
|
159
|
-
\`\`\`typescript
|
|
160
|
-
complianceConfig: {
|
|
161
|
-
title: string // \u5F39\u7A97\u6807\u9898
|
|
162
|
-
content: string // \u5185\u5BB9\u6587\u672C\uFF08\u652F\u6301 HTML\uFF09
|
|
163
|
-
checkboxText: string // \u52FE\u9009\u6846\u6587\u672C\uFF08\u652F\u6301\u5B8C\u6574 HTML\uFF0C\u5305\u62EC <a> \u6807\u7B7E\uFF09
|
|
164
|
-
agreeButtonText?: string // \u540C\u610F\u6309\u94AE\u6587\u6848\uFF08\u9ED8\u8BA4 "Agree"\uFF09
|
|
165
|
-
}
|
|
166
|
-
\`\`\`
|
|
167
|
-
|
|
168
|
-
**\u793A\u4F8B\uFF1A**
|
|
169
|
-
|
|
170
|
-
\`\`\`tsx
|
|
171
|
-
checkboxText: 'By starting to use "Live Chat", you agree to <a href="https://example.com/privacy" target="_blank">PRIVACY NOTICE</a>.'
|
|
172
|
-
\`\`\`
|
|
173
|
-
|
|
174
|
-
**\u6837\u5F0F\u5B9A\u5236\uFF1A**
|
|
175
|
-
|
|
176
|
-
\u6240\u6709\u6837\u5F0F\u901A\u8FC7 CSS \u7C7B\u540D\u66B4\u9732\uFF0C\u53EF\u4EE5\u5168\u5C40\u8986\u76D6\uFF1A
|
|
177
|
-
|
|
178
|
-
\`\`\`css
|
|
179
|
-
.livechat-compliance-content { /* \u5F39\u7A97\u4E3B\u4F53 */ }
|
|
180
|
-
.livechat-compliance-title { /* \u6807\u9898 */ }
|
|
181
|
-
.livechat-compliance-text { /* \u5185\u5BB9\u6587\u672C */ }
|
|
182
|
-
.livechat-compliance-checkbox { /* \u590D\u9009\u6846 */ }
|
|
183
|
-
.livechat-compliance-link { /* \u94FE\u63A5 */ }
|
|
184
|
-
.livechat-compliance-button { /* \u540C\u610F\u6309\u94AE */ }
|
|
185
|
-
\`\`\`
|
|
186
|
-
`}}}},M={args:{loginUserId:"test_test",apiBaseUrl:"http://172.16.38.183:3003",site:"beta.eufy.com",channelCode:"dtc",title:"AI Assistant",complianceConfig:{title:"Welcome to AI Chat Support",content:"By using this service, you agree to our terms of service and privacy policy. AI responses may contain errors. Please verify important information.",checkboxText:"I have read and agree to the terms and conditions",agreeButtonText:"Continue"}},parameters:{docs:{description:{story:`
|
|
187
|
-
### \u7B80\u5316\u7248\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97
|
|
188
|
-
|
|
189
|
-
\u5982\u679C\u4E0D\u9700\u8981\u5728\u52FE\u9009\u6846\u4E2D\u663E\u793A\u94FE\u63A5\uFF0C\u53EF\u4EE5\u7701\u7565 \`linkText\` \u548C \`linkUrl\` \u5C5E\u6027\u3002
|
|
190
|
-
|
|
191
|
-
\u8FD9\u4E2A\u7248\u672C\u9002\u7528\u4E8E\uFF1A
|
|
192
|
-
- \u7B80\u5355\u7684\u7528\u6237\u534F\u8BAE\u786E\u8BA4
|
|
193
|
-
- \u514D\u8D23\u58F0\u660E
|
|
194
|
-
- \u670D\u52A1\u6761\u6B3E\u540C\u610F
|
|
195
|
-
|
|
196
|
-
**\u4E0E\u5B8C\u6574\u7248\u7684\u533A\u522B\uFF1A**
|
|
197
|
-
- \u4E0D\u5305\u542B\u53EF\u70B9\u51FB\u7684\u94FE\u63A5
|
|
198
|
-
- \u52FE\u9009\u6846\u6587\u672C\u4E3A\u7EAF\u6587\u672C
|
|
199
|
-
- \u914D\u7F6E\u66F4\u7B80\u5355
|
|
200
|
-
`}}}},P={args:{loginUserId:"test_test",apiBaseUrl:"http://172.16.38.183:3003",cartId:"gid://shopify/Cart/hWN7wB3Pa12gh78d8hPOAUBI?key=0e73db1d3fb5ac21da19099c45033253",accessToken:"47b1aa2c0797043f9baba39388029d70",title:"eufy",site:"beta.eufy.com",channelCode:"dtc",welcomeMessage:'\u4F60\u597D\uFF01\u6211\u53EF\u4EE5\u4E3A\u4F60\u63A8\u8350\u4EA7\u54C1\uFF0C\u8BD5\u8BD5\u53D1\u9001 "\u63A8\u8350\u4EA7\u54C1" \u6765\u67E5\u770B\u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6548\u679C\u3002',productCardRender:t=>{const e=t.featured_image||"",r=t.title||"",i=t.description||"",n=t.price_range?.min||t.variants?.[0]?.price||0,p=t.price_range?.currency||t.variants?.[0]?.currency||"USD",l=t.average_rating;return o("div",{style:{border:"2px solid #4CAF50",borderRadius:"16px",padding:"20px",margin:"12px 0",backgroundColor:"#f0f9ff",boxShadow:"0 4px 12px rgba(76, 175, 80, 0.15)",transition:"transform 0.2s"},onMouseEnter:c=>{c.currentTarget.style.transform="scale(1.02)"},onMouseLeave:c=>{c.currentTarget.style.transform="scale(1)"},children:a("div",{style:{display:"flex",gap:"16px",alignItems:"center"},children:[o("div",{style:{width:"70px",height:"auto",flexShrink:0,borderRadius:"12px",overflow:"hidden",border:"2px solid #e0e0e0"},children:o("img",{src:e,alt:r,style:{width:"100%",height:"100%",objectFit:"cover"}})}),a("div",{style:{flex:1},children:[o("h3",{style:{margin:"0 0 8px 0",fontSize:"18px",fontWeight:"bold",color:"#1a1a1a",lineHeight:"1.4"},children:r}),i&&o("p",{style:{margin:"0 0 12px 0",fontSize:"14px",color:"#666",lineHeight:"1.5",display:"-webkit-box",WebkitLineClamp:2,WebkitBoxOrient:"vertical",overflow:"hidden"},children:i}),a("div",{style:{display:"flex",alignItems:"center",gap:"12px"},children:[o("div",{style:{fontSize:"24px",fontWeight:"bold",color:"#4CAF50"},children:"'test price'"}),l!==void 0&&a("div",{style:{display:"flex",alignItems:"center",gap:"4px",fontSize:"14px",color:"#666"},children:[o("span",{style:{color:"#FFB800"},children:"\u2B50"}),o("span",{children:l.toFixed(1)})]}),o("button",{style:{marginLeft:"auto",padding:"8px 20px",backgroundColor:"#4CAF50",color:"white",borderRadius:"8px",textDecoration:"none",fontSize:"14px",fontWeight:"bold",transition:"background-color 0.2s"},onClick:()=>{console.log("View Details",t)},children:"View Details"})]})]})]})})},onAddToCart:t=>{console.log("Add to cart:",t),alert(`Added ${t.title} to cart!`)}},parameters:{docs:{description:{story:`
|
|
201
|
-
### \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3
|
|
202
|
-
|
|
203
|
-
\u4F7F\u7528 \`productCardRender\` \u5C5E\u6027\u53EF\u4EE5\u5B8C\u5168\u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u7684\u5C55\u793A\u6837\u5F0F\u3002
|
|
204
|
-
|
|
205
|
-
**\u529F\u80FD\u8BF4\u660E\uFF1A**
|
|
206
|
-
|
|
207
|
-
- \u5F53\u540E\u7AEF\u8FD4\u56DE\u5E26\u6709\u4EA7\u54C1\u5360\u4F4D\u7B26 \`{{product:xxx}}\` \u7684\u6587\u672C\u6D41\u65F6\uFF0C\u4F1A\u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570
|
|
208
|
-
- \u81EA\u5B9A\u4E49\u51FD\u6570\u63A5\u6536\u5B8C\u6574\u7684\u4EA7\u54C1\u5BF9\u8C61\u4F5C\u4E3A\u53C2\u6570
|
|
209
|
-
- \u8FD4\u56DE React \u8282\u70B9\uFF0C\u53EF\u4EE5\u662F\u4EFB\u4F55\u5408\u6CD5\u7684 JSX
|
|
210
|
-
- \u5982\u679C\u4E0D\u63D0\u4F9B \`productCardRender\`\uFF0C\u5219\u4F7F\u7528\u9ED8\u8BA4\u7684\u4EA7\u54C1\u5361\u7247\u6837\u5F0F
|
|
211
|
-
|
|
212
|
-
**\u6D4B\u8BD5\u6B65\u9AA4\uFF1A**
|
|
213
|
-
|
|
214
|
-
1. \u6253\u5F00\u804A\u5929\u7A97\u53E3
|
|
215
|
-
2. \u53D1\u9001 "\u63A8\u8350\u4EA7\u54C1" \u6216\u4EFB\u4F55\u4F1A\u89E6\u53D1\u4EA7\u54C1\u63A8\u8350\u7684\u6D88\u606F
|
|
216
|
-
3. \u89C2\u5BDF\u8FD4\u56DE\u7684\u4EA7\u54C1\u5361\u7247\u4F7F\u7528\u7684\u662F\u81EA\u5B9A\u4E49\u6837\u5F0F\uFF08\u7EFF\u8272\u8FB9\u6846\u3001\u60AC\u505C\u653E\u5927\u6548\u679C\uFF09
|
|
217
|
-
`}}}};export{C as CustomBranding,A as CustomImageGalleryRenderer,h as CustomPosition,P as CustomProductCardRender,b as CustomVideoRenderer,x as CustomWelcomeMessage,u as Default,k as DefaultPositionDemo,T as FAQListMessage,S as MobileView,I as ProductComparisonMessage,w as TabletView,L as WithComplianceDialog,M as WithComplianceDialogNoLink,v as WithEventCallbacks,f as WithQuickReplies,U as WithRecaptcha,y as default};
|
|
58
|
+
How can I assist you today?`,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{u as Default,g as default};
|
|
218
59
|
//# sourceMappingURL=LiveChatWidget.stories.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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\n *\n * \u6700\u7B80\u5355\u7684\u4F7F\u7528\u65B9\u5F0F\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E\u3002\n */\nexport const Default: Story = {\n args: {\n loginUserId: 'test_test',\n apiBaseUrl: 'http://172.16.38.183:3003',\n cartId: 'gid://shopify/Cart/hWN7wB3Pa12gh78d8hPOAUBI?key=0e73db1d3fb5ac21da19099c45033253',\n accessToken: '47b1aa2c0797043f9baba39388029d70',\n title: 'eufy',\n\n commonText: {\n learnMore: 'Learn More',\n total: 'Total',\n },\n\n site: 'beta.eufy.com',\n channelCode: 'dtc',\n\n // reCAPTCHA \u914D\u7F6E\uFF08\u63D0\u4F9B sitekey \u5373\u81EA\u52A8\u542F\u7528\uFF09\n // recaptchaSitekey: '6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14',\n recaptchaAction: 'chat',\n\n welcomeMessage: '',\n bottomTips: '',\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u4F4D\u7F6E\n *\n * \u4F7F\u7528\u81EA\u5B9A\u4E49\u4F4D\u7F6E\u5BF9\u8C61\u6765\u63A7\u5236\u6C14\u6CE1\u6309\u94AE\u7684\u4F4D\u7F6E\u3002\n */\nexport const CustomPosition: Story = {\n args: {\n position: { bottom: '1.5rem', left: '1.5rem' },\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u54C1\u724C\u6837\u5F0F\n *\n * \u81EA\u5B9A\u4E49 Logo \u548C\u6B22\u8FCE\u6D88\u606F\u3002\n */\nexport const CustomBranding: Story = {\n args: {\n logoUrl: 'https://images.unsplash.com/photo-1599305445671-ac291c95aaa9?w=100&h=100&fit=crop',\n welcomeMessage: '\u6B22\u8FCE\u6765\u5230 Soundcore\uFF01\u6211\u662F\u60A8\u7684\u4E13\u5C5E AI \u52A9\u624B \uD83C\uDFA7',\n },\n}\n\n/**\n * \u5E26\u5FEB\u6377\u56DE\u590D\n *\n * \u5728\u6B22\u8FCE\u6D88\u606F\u540E\u663E\u793A\u5FEB\u6377\u56DE\u590D\u6309\u94AE\u3002\n */\nexport const WithQuickReplies: Story = {\n args: {\n quickReplies: [\n {\n id: '1',\n label: '\u67E5\u8BE2\u4EF7\u683C',\n value: '\u6211\u60F3\u4E86\u89E3\u4EA7\u54C1\u4EF7\u683C',\n icon: '\uD83D\uDCB0',\n },\n {\n id: '2',\n label: '\u67E5\u8BE2\u7269\u6D41',\n value: '\u6211\u60F3\u67E5\u8BE2\u8BA2\u5355\u7269\u6D41',\n icon: '\uD83D\uDCE6',\n },\n {\n id: '3',\n label: '\u552E\u540E\u670D\u52A1',\n value: '\u6211\u9700\u8981\u552E\u540E\u5E2E\u52A9',\n icon: '\uD83D\uDD27',\n },\n {\n id: '4',\n label: '\u4EA7\u54C1\u63A8\u8350',\n value: '\u8BF7\u63A8\u8350\u9002\u5408\u6211\u7684\u4EA7\u54C1',\n icon: '\u2B50',\n },\n ],\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u6B22\u8FCE\u6D88\u606F\n *\n * \u81EA\u5B9A\u4E49\u9996\u6B21\u6253\u5F00\u804A\u5929\u7A97\u53E3\u65F6\u7684\u6B22\u8FCE\u6D88\u606F\u3002\n */\nexport const CustomWelcomeMessage: Story = {\n args: {\n welcomeMessage: `\n\uD83D\uDC4B \u60A8\u597D\uFF01\u6B22\u8FCE\u6765\u5230 Soundcore \u5B98\u65B9\u5546\u57CE\u3002\n\n\u6211\u662F\u60A8\u7684 AI \u8D2D\u7269\u52A9\u624B\uFF0C\u53EF\u4EE5\u5E2E\u60A8\uFF1A\n- \uD83D\uDD0D \u67E5\u627E\u4EA7\u54C1\n- \uD83D\uDCB0 \u6BD4\u8F83\u4EF7\u683C\n- \uD83D\uDCE6 \u8DDF\u8E2A\u8BA2\u5355\n- \uD83D\uDCAC \u89E3\u7B54\u7591\u95EE\n\n\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u52A9\u60A8\u7684\u5417\uFF1F\n `.trim(),\n },\n}\n\n/**\n * \u4E8B\u4EF6\u56DE\u8C03\n *\n * \u76D1\u542C\u7EC4\u4EF6\u4E8B\u4EF6\u8FDB\u884C\u81EA\u5B9A\u4E49\u5904\u7406\u3002\n */\nexport const WithEventCallbacks: Story = {\n args: {\n title: '',\n showNewSessionButton: true,\n\n position: {\n bottom: '24px',\n right: '30px',\n },\n\n chatBubbleIcon: 'https://cdn.shopify.com/s/files/1/0504/7094/4954/files/Rectangle_400770314.png?v=1768894153',\n\n // \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\u793A\u4F8B\n headers: undefined,\n\n site: 'beta.eufy.com',\n },\n\n render: args => (\n <LiveChatWidget\n {...args}\n onOpen={() => {\n console.log('[Story] Chat opened')\n }}\n onClose={() => {\n console.log('[Story] Chat closed')\n }}\n onMessageSend={(message: string) => {\n console.log('[Story] Message sent:', message)\n }}\n onError={(error: Error) => {\n console.error('[Story] Error occurred:', error)\n }}\n onTextMessage={() => {\n console.log('\uD83D\uDCDD [Story] AI started replying with text message')\n }}\n onProductList={() => {\n console.log('\uD83D\uDCE6 [Story] AI replied with product list card')\n }}\n onPromotionList={() => {\n console.log('\uD83C\uDF89 [Story] AI replied with promotion list card')\n }}\n onAddToCart={(product: any) => {\n console.log('\uD83D\uDED2 [Story] Add to Cart clicked!')\n console.log('\uD83D\uDCE6 Product Info:', {\n id: product.shopifyId,\n title: product.title,\n price: product.price,\n imageUrl: product.imageUrl,\n productUrl: product.productUrl,\n })\n console.log('\uD83D\uDCCB Full Product Object:', product)\n\n // \u6A21\u62DF\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u6210\u529F\n alert(`\u2705 \"${product.title}\" \u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\uFF01\\n\\n\u4EF7\u683C: ${product.price.currency} ${product.price.amount}`)\n }}\n onCart={(cartId: string, checkoutUrl?: string) => {\n console.log('\uD83D\uDED2 [Story] Cart button clicked!')\n console.log('\uD83D\uDCCB Cart ID:', cartId)\n console.log('\uD83D\uDD17 Checkout URL:', checkoutUrl)\n\n // \u6A21\u62DF\u8D2D\u7269\u8F66\u6309\u94AE\u70B9\u51FB\u6210\u529F\n alert(`\u8D2D\u7269\u8F66\u6309\u94AE\u88AB\u70B9\u51FB\uFF01\\n\\nCart ID: ${cartId}\\nCheckout URL: ${checkoutUrl || '\u65E0'}`)\n }}\n />\n ),\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u89C6\u9891\u6D88\u606F\u6E32\u67D3\u5668\n *\n * \u6269\u5C55\u6D88\u606F\u7C7B\u578B\uFF0C\u6DFB\u52A0\u89C6\u9891\u6D88\u606F\u652F\u6301\u3002\n */\nexport const CustomVideoRenderer: Story = {\n args: {\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 \u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u89C6\u9891\u64AD\u653E\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 },\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u56FE\u7247\u753B\u5ECA\u6E32\u67D3\u5668\n *\n * \u6269\u5C55\u6D88\u606F\u7C7B\u578B\uFF0C\u6DFB\u52A0\u56FE\u7247\u753B\u5ECA\u652F\u6301\u3002\n */\nexport const CustomImageGalleryRenderer: Story = {\n args: {\n customRenderers: {\n image_gallery: {\n render: (content: MessageContent) => {\n const galleryContent = content as any\n const images = galleryContent.images || []\n\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}\n\n/**\n * \u79FB\u52A8\u7AEF\u9884\u89C8\n *\n * \u5728\u79FB\u52A8\u7AEF\u5C3A\u5BF8\u4E0B\u67E5\u770B\u6548\u679C\uFF08\u5168\u5C4F\u663E\u793A\uFF09\u3002\n */\nexport const MobileView: Story = {\n parameters: {\n viewport: {\n defaultViewport: 'mobile1',\n },\n },\n args: {},\n}\n\n/**\n * \u5E73\u677F\u7AEF\u9884\u89C8\n *\n * \u5728\u5E73\u677F\u7AEF\u5C3A\u5BF8\u4E0B\u67E5\u770B\u6548\u679C\u3002\n */\nexport const TabletView: Story = {\n parameters: {\n viewport: {\n defaultViewport: 'tablet',\n },\n },\n args: {},\n}\n\n/**\n * \u9ED8\u8BA4\u4F4D\u7F6E\u5C55\u793A\n *\n * \u5C55\u793A\u6C14\u6CE1\u6309\u94AE\u7684\u9ED8\u8BA4\u4F4D\u7F6E\uFF08\u53F3\u4E0B\u89D2\uFF09\u3002\n */\nexport const DefaultPositionDemo: Story = {\n render: () => (\n <div className=\"relative h-screen w-full bg-gray-50\">\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <p className=\"text-gray-500\">\u9875\u9762\u5185\u5BB9\u533A\u57DF</p>\n </div>\n\n {/* \u6F14\u793A\u9ED8\u8BA4\u4F4D\u7F6E */}\n <div className=\"absolute bottom-4 right-4 text-xs text-gray-400\">\u9ED8\u8BA4\u4F4D\u7F6E (\u53F3\u4E0B\u89D2)</div>\n\n <LiveChatWidget apiBaseUrl=\"https://beta-api-livechat.anker.com\" site=\"www.eufy.com\" />\n </div>\n ),\n}\n\n/**\n * \u4EA7\u54C1\u5BF9\u6BD4\u6D88\u606F\n *\n * \u5C55\u793A\u4EA7\u54C1\u5BF9\u6BD4\u7C7B\u578B\u7684\u6D88\u606F\u6E32\u67D3\u6548\u679C\uFF0C\u5305\u62EC\u4EF7\u683C\u3001\u4F1A\u5458\u4EF7\u3001\u53D8\u4F53\u6570\u91CF\u3001\u6298\u6263\u7B49\u591A\u4E2A\u7EF4\u5EA6\u7684\u5BF9\u6BD4\u3002\n *\n * \u4EA7\u54C1\u5BF9\u6BD4\u7EC4\u4EF6\u4F1A\u4EE5\u7F51\u683C\u5E03\u5C40\u5C55\u793A\u591A\u4E2A\u4EA7\u54C1\u7684\u57FA\u672C\u4FE1\u606F\uFF0C\u5E76\u5728\u4E0B\u65B9\u663E\u793A\u5404\u7EF4\u5EA6\u7684\u5BF9\u6BD4\u6570\u636E\u3002\n */\nexport const ProductComparisonMessage: Story = {\n render: args => (\n <LiveChatWidget\n {...args}\n onAddToCart={(product: any) => {\n console.log('\uD83D\uDED2 [ProductComparison Story] Add to Cart clicked!')\n console.log('\uD83D\uDCE6 Product Info:', {\n id: product.shopifyId,\n title: product.title,\n price: product.price,\n imageUrl: product.imageUrl,\n productUrl: product.productUrl,\n })\n console.log('\uD83D\uDCCB Full Product Object:', product)\n\n // \u6A21\u62DF\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u6210\u529F\n alert(`\u2705 \"${product.title}\" \u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\uFF01\\n\\n\u4EF7\u683C: ${product.price.currency} ${product.price.amount}`)\n }}\n />\n ),\n args: {\n welcomeMessage: '\u4EE5\u4E0B\u662F\u60A8\u5173\u6CE8\u7684\u4EA7\u54C1\u5BF9\u6BD4\u4FE1\u606F\uFF1A',\n },\n parameters: {\n docs: {\n description: {\n story: `\n\u4EA7\u54C1\u5BF9\u6BD4\u6D88\u606F\u652F\u6301\u4EE5\u4E0B\u7EF4\u5EA6\uFF1A\n\n- **\u4EF7\u683C\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u7684\u4EF7\u683C\u533A\u95F4\u548C\u4FC3\u9500\u6807\u7B7E\n- **\u4F1A\u5458\u4EF7\u5BF9\u6BD4**: \u663E\u793A\u4F1A\u5458\u4E13\u4EAB\u4EF7\u683C\uFF08\u5982\u679C\u53EF\u7528\uFF09\n- **\u53D8\u4F53\u6570\u91CF\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u7684\u53EF\u9009\u53D8\u4F53\u6570\u91CF\n- **\u6298\u6263\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u662F\u5426\u6709\u6298\u6263\u6D3B\u52A8\n\n\u7EC4\u4EF6\u4F1A\u81EA\u52A8\u5904\u7406\u4ECE\u540E\u7AEF\u8FD4\u56DE\u7684\u4EA7\u54C1\u5BF9\u6BD4\u6570\u636E\uFF0C\u5305\u62EC\uFF1A\n- \u4EA7\u54C1\u57FA\u672C\u4FE1\u606F\uFF08\u56FE\u7247\u3001\u6807\u9898\u3001\u4EF7\u683C\u7B49\uFF09\n- \u591A\u7EF4\u5EA6\u5BF9\u6BD4\u6570\u636E\uFF08\u4EF7\u683C\u3001\u4F1A\u5458\u4EF7\u3001\u53D8\u4F53\u3001\u6298\u6263\uFF09\n- \u54CD\u5E94\u5F0F\u5E03\u5C40\uFF08\u79FB\u52A8\u7AEF\u53EF\u6A2A\u5411\u6EDA\u52A8\uFF09\n\n**\u6D4B\u8BD5 Add to Cart \u529F\u80FD\uFF1A**\n- \u70B9\u51FB\u4EA7\u54C1\u5BF9\u6BD4\u5361\u7247\u4E2D\u7684 \"Add to Cart\" \u6309\u94AE\n- \u67E5\u770B\u63A7\u5236\u53F0\u8F93\u51FA\u7684\u4EA7\u54C1\u4FE1\u606F\n- \u4F1A\u5F39\u51FA\u786E\u8BA4\u63D0\u793A\u6846\u663E\u793A\u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\n `,\n },\n },\n },\n}\n\n/**\n * FAQ \u5217\u8868\u6D88\u606F\n *\n * \u5C55\u793A\u5E38\u89C1\u95EE\u9898\u5217\u8868\uFF0C\u652F\u6301\u6298\u53E0/\u5C55\u5F00\u3002\n *\n * FAQ \u7EC4\u4EF6\u4F1A\u663E\u793A\u641C\u7D22\u7ED3\u679C\u7684\u5E38\u89C1\u95EE\u9898\uFF0C\u6BCF\u4E2A\u95EE\u9898\u53EF\u4EE5\u70B9\u51FB\u5C55\u5F00\u67E5\u770B\u7B54\u6848\u3002\n */\nexport const FAQListMessage: Story = {\n args: {\n welcomeMessage: '\u4EE5\u4E0B\u662F\u4E0E\u60A8\u7684\u95EE\u9898\u76F8\u5173\u7684\u5E38\u89C1\u89E3\u7B54\uFF1A',\n },\n parameters: {\n docs: {\n description: {\n story: `\nFAQ \u5217\u8868\u6D88\u606F\u652F\u6301\uFF1A\n\n- **\u53EF\u6298\u53E0\u95EE\u9898**: \u70B9\u51FB\u95EE\u9898\u6807\u9898\u5C55\u5F00/\u6298\u53E0\u7B54\u6848\n- **Markdown \u7B54\u6848**: \u7B54\u6848\u652F\u6301 Markdown \u683C\u5F0F\uFF0C\u53EF\u663E\u793A\u5BCC\u6587\u672C\n- **\u76F8\u5173\u95EE\u9898**: \u5C55\u5F00\u7B54\u6848\u540E\u663E\u793A\u76F8\u5173\u95EE\u9898\uFF0C\u70B9\u51FB\u53EF\u89E6\u53D1\u65B0\u641C\u7D22\n- **\u5206\u7C7B\u6807\u8BB0**: \u6839\u636E\u95EE\u9898\u5206\u7C7B\u663E\u793A\uFF08\u914D\u9001\u3001\u9000\u8D27\u3001\u4EA7\u54C1\u3001\u652F\u4ED8\u3001\u901A\u7528\uFF09\n- **\u641C\u7D22\u7ED3\u679C\u7EDF\u8BA1**: \u663E\u793A\u627E\u5230\u7684\u95EE\u9898\u6570\u91CF\n\n\u4F7F\u7528\u573A\u666F\uFF1A\n- \u7528\u6237\u8BE2\u95EE\u5E38\u89C1\u95EE\u9898\u65F6\u8FD4\u56DE FAQ \u5217\u8868\n- \u4EA7\u54C1\u4E13\u5C5E\u95EE\u9898\u67E5\u8BE2\n- \u6309\u5206\u7C7B\u67E5\u8BE2 FAQ\n `,\n },\n },\n },\n}\n\n/**\n * \u5E26 reCAPTCHA \u914D\u7F6E\n *\n * \u5C55\u793A\u5982\u4F55\u914D\u7F6E Google reCAPTCHA v3 \u9A8C\u8BC1\uFF0C\u4FDD\u62A4 API \u8BF7\u6C42\u514D\u53D7\u6EE5\u7528\u3002\n *\n * reCAPTCHA \u4F1A\u5728\u540E\u53F0\u81EA\u52A8\u9A8C\u8BC1\u7528\u6237\u884C\u4E3A\uFF0C\u65E0\u9700\u7528\u6237\u624B\u52A8\u64CD\u4F5C\u3002\n */\nexport const WithRecaptcha: Story = {\n args: {\n // \u914D\u7F6E\u4F60\u7684 reCAPTCHA site key\uFF08\u63D0\u4F9B\u5373\u81EA\u52A8\u542F\u7528\uFF09\n recaptchaSitekey: '6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14',\n // \u53EF\u9009\uFF1A\u81EA\u5B9A\u4E49 action \u540D\u79F0\n recaptchaAction: 'livechat',\n },\n parameters: {\n docs: {\n description: {\n story: `\n### reCAPTCHA v3 \u914D\u7F6E\n\n\u542F\u7528 reCAPTCHA v3 \u53EF\u4EE5\u4FDD\u62A4\u4F60\u7684 API \u514D\u53D7\u673A\u5668\u4EBA\u548C\u6EE5\u7528\u884C\u4E3A\u7684\u653B\u51FB\u3002\n\n**\u914D\u7F6E\u6B65\u9AA4\uFF1A**\n\n1. \u5728 [Google reCAPTCHA](https://www.google.com/recaptcha/admin) \u521B\u5EFA v3 \u5BC6\u94A5\n2. \u5728\u9875\u9762\u4E2D\u52A0\u8F7D reCAPTCHA \u811A\u672C\uFF1A\n \\`\\`\\`html\n <script src=\"https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY\"></script>\n \\`\\`\\`\n3. \u914D\u7F6E LiveChatWidget \u7EC4\u4EF6\uFF08\u63D0\u4F9B sitekey \u5373\u81EA\u52A8\u542F\u7528\uFF09\uFF1A\n \\`\\`\\`tsx\n <LiveChatWidget\n recaptchaSitekey=\"YOUR_SITE_KEY\"\n recaptchaAction=\"livechat\"\n />\n \\`\\`\\`\n\n**\u5DE5\u4F5C\u539F\u7406\uFF1A**\n\n- \u63D0\u4F9B recaptchaSitekey \u53C2\u6570\u5373\u81EA\u52A8\u542F\u7528 reCAPTCHA \u9A8C\u8BC1\n- \u6BCF\u6B21\u53D1\u9001\u6D88\u606F\u6216\u521B\u5EFA\u4F1A\u8BDD\u65F6\uFF0C\u4F1A\u81EA\u52A8\u83B7\u53D6 reCAPTCHA token\n- token \u4F1A\u901A\u8FC7 \\`X-Recaptcha-Token\\` header \u53D1\u9001\u5230\u540E\u7AEF\n- \u540E\u7AEF\u9700\u8981\u9A8C\u8BC1 token \u7684\u6709\u6548\u6027\n\n**\u6CE8\u610F\u4E8B\u9879\uFF1A**\n\n- reCAPTCHA v3 \u5728\u540E\u53F0\u8FD0\u884C\uFF0C\u4E0D\u4F1A\u6253\u65AD\u7528\u6237\u4F53\u9A8C\n- \u5EFA\u8BAE\u4E3A\u4E0D\u540C\u7684\u64CD\u4F5C\u4F7F\u7528\u4E0D\u540C\u7684 \\`recaptchaAction\\` \u4EE5\u4FBF\u5206\u6790\n- \u786E\u4FDD\u540E\u7AEF\u6B63\u786E\u9A8C\u8BC1 reCAPTCHA token\n- \u4E0D\u63D0\u4F9B recaptchaSitekey \u5219\u4E0D\u542F\u7528\u9A8C\u8BC1\uFF08\u4EC5\u7528\u4E8E\u5F00\u53D1\u73AF\u5883\uFF09\n `,\n },\n },\n },\n}\n\n/**\n * \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\n *\n * \u6F14\u793A\u5982\u4F55\u914D\u7F6E\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\uFF0C\u5728\u7528\u6237\u9996\u6B21\u70B9\u51FB\u804A\u5929\u6C14\u6CE1\u65F6\u663E\u793A\u3002\n *\n * \u7528\u6237\u5FC5\u987B\u540C\u610F\u534F\u8BAE\u540E\u624D\u80FD\u6253\u5F00\u804A\u5929\u7A97\u53E3\u3002\n */\nexport const WithComplianceDialog: Story = {\n args: {\n loginUserId: 'test_test',\n apiBaseUrl: 'http://172.16.38.183:3003',\n site: 'beta.eufy.com',\n channelCode: 'dtc',\n title: 'eufy AI Assistant',\n\n // \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\u914D\u7F6E\n complianceConfig: {\n title: \"Hi! I'm your eufy AI assistant.\",\n content: \"AI-generated responses can be inaccurate. Please verify important info. Do not input sensitive personal data\",\n 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>.',\n agreeButtonText: \"Agree\"\n },\n },\n parameters: {\n docs: {\n description: {\n story: `\n### \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\u529F\u80FD\n\n\u4F7F\u7528 \\`complianceConfig\\` \u5C5E\u6027\u53EF\u4EE5\u5728\u7528\u6237\u9996\u6B21\u70B9\u51FB\u804A\u5929\u6C14\u6CE1\u65F6\u663E\u793A\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\u3002\n\n**\u529F\u80FD\u7279\u6027\uFF1A**\n\n- \u2705 \u7528\u6237\u5FC5\u987B\u52FE\u9009\u590D\u9009\u6846\u624D\u80FD\u70B9\u51FB\"\u540C\u610F\"\u6309\u94AE\n- \u2705 \u652F\u6301\u5728\u52FE\u9009\u6846\u6587\u672C\u4E2D\u5D4C\u5165\u94FE\u63A5\uFF08\u4F7F\u7528 \\`{link}\\` \u5360\u4F4D\u7B26\uFF09\n- \u2705 \u652F\u6301 HTML \u683C\u5F0F\u7684\u5185\u5BB9\u6587\u672C\n- \u2705 \u540C\u610F\u540E\u81EA\u52A8\u6253\u5F00\u804A\u5929\u7A97\u53E3\n- \u2705 \u5173\u95ED\u5F39\u7A97\u4E0D\u4F1A\u6253\u5F00\u804A\u5929\u7A97\u53E3\n- \u2705 \u540C\u610F\u540E\u518D\u6B21\u70B9\u51FB\u6C14\u6CE1\u76F4\u63A5\u6253\u5F00\u804A\u5929\uFF08\u65E0\u9700\u91CD\u590D\u540C\u610F\uFF09\n- \u2705 \u54CD\u5E94\u5F0F\u8BBE\u8BA1\uFF0C\u79FB\u52A8\u7AEF\u53CB\u597D\n- \u2705 **\u4E0E\u804A\u5929\u7A97\u53E3\u4F4D\u7F6E\u4E00\u81F4**\uFF08\u53F3\u4E0B\u89D2\u6216\u5E95\u90E8\u56FA\u5B9A\uFF09\n- \u2705 **\u65E0\u906E\u7F69\u5C42\u8BBE\u8BA1**\uFF0C\u4E0D\u906E\u6321\u9875\u9762\u5185\u5BB9\n\n**\u6D4B\u8BD5\u6B65\u9AA4\uFF1A**\n\n1. \u70B9\u51FB\u804A\u5929\u6C14\u6CE1\u6309\u94AE\n2. \u67E5\u770B\u5F39\u51FA\u7684\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\n3. \u5C1D\u8BD5\u4E0D\u52FE\u9009\u590D\u9009\u6846\u65F6\u70B9\u51FB\"Agree\"\u6309\u94AE\uFF08\u6309\u94AE\u5E94\u8BE5\u662F\u7981\u7528\u72B6\u6001\uFF09\n4. \u52FE\u9009\u590D\u9009\u6846\u540E\u70B9\u51FB\"Agree\"\n5. \u89C2\u5BDF\u5F39\u7A97\u5173\u95ED\u5E76\u81EA\u52A8\u6253\u5F00\u804A\u5929\u7A97\u53E3\n6. \u5173\u95ED\u804A\u5929\u7A97\u53E3\u540E\u518D\u6B21\u70B9\u51FB\u6C14\u6CE1\uFF08\u5E94\u8BE5\u76F4\u63A5\u6253\u5F00\u804A\u5929\uFF0C\u4E0D\u518D\u663E\u793A\u6CD5\u89C4\u5F39\u7A97\uFF09\n\n**\u914D\u7F6E\u9879\u8BF4\u660E\uFF1A**\n\n\\`\\`\\`typescript\ncomplianceConfig: {\n title: string // \u5F39\u7A97\u6807\u9898\n content: string // \u5185\u5BB9\u6587\u672C\uFF08\u652F\u6301 HTML\uFF09\n checkboxText: string // \u52FE\u9009\u6846\u6587\u672C\uFF08\u652F\u6301\u5B8C\u6574 HTML\uFF0C\u5305\u62EC <a> \u6807\u7B7E\uFF09\n agreeButtonText?: string // \u540C\u610F\u6309\u94AE\u6587\u6848\uFF08\u9ED8\u8BA4 \"Agree\"\uFF09\n}\n\\`\\`\\`\n\n**\u793A\u4F8B\uFF1A**\n\n\\`\\`\\`tsx\ncheckboxText: 'By starting to use \"Live Chat\", you agree to <a href=\"https://example.com/privacy\" target=\"_blank\">PRIVACY NOTICE</a>.'\n\\`\\`\\`\n\n**\u6837\u5F0F\u5B9A\u5236\uFF1A**\n\n\u6240\u6709\u6837\u5F0F\u901A\u8FC7 CSS \u7C7B\u540D\u66B4\u9732\uFF0C\u53EF\u4EE5\u5168\u5C40\u8986\u76D6\uFF1A\n\n\\`\\`\\`css\n.livechat-compliance-content { /* \u5F39\u7A97\u4E3B\u4F53 */ }\n.livechat-compliance-title { /* \u6807\u9898 */ }\n.livechat-compliance-text { /* \u5185\u5BB9\u6587\u672C */ }\n.livechat-compliance-checkbox { /* \u590D\u9009\u6846 */ }\n.livechat-compliance-link { /* \u94FE\u63A5 */ }\n.livechat-compliance-button { /* \u540C\u610F\u6309\u94AE */ }\n\\`\\`\\`\n `,\n },\n },\n },\n}\n\n/**\n * \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97 - \u65E0\u94FE\u63A5\u7248\u672C\n *\n * \u6F14\u793A\u4E0D\u5305\u542B\u94FE\u63A5\u7684\u7B80\u5316\u7248\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\u3002\n */\nexport const WithComplianceDialogNoLink: Story = {\n args: {\n loginUserId: 'test_test',\n apiBaseUrl: 'http://172.16.38.183:3003',\n site: 'beta.eufy.com',\n channelCode: 'dtc',\n title: 'AI Assistant',\n\n // \u7B80\u5316\u7248\u6CD5\u89C4\u534F\u8BAE\uFF08\u4E0D\u5305\u542B\u94FE\u63A5\uFF09\n complianceConfig: {\n title: \"Welcome to AI Chat Support\",\n content: \"By using this service, you agree to our terms of service and privacy policy. AI responses may contain errors. Please verify important information.\",\n checkboxText: \"I have read and agree to the terms and conditions\",\n agreeButtonText: \"Continue\"\n },\n },\n parameters: {\n docs: {\n description: {\n story: `\n### \u7B80\u5316\u7248\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\n\n\u5982\u679C\u4E0D\u9700\u8981\u5728\u52FE\u9009\u6846\u4E2D\u663E\u793A\u94FE\u63A5\uFF0C\u53EF\u4EE5\u7701\u7565 \\`linkText\\` \u548C \\`linkUrl\\` \u5C5E\u6027\u3002\n\n\u8FD9\u4E2A\u7248\u672C\u9002\u7528\u4E8E\uFF1A\n- \u7B80\u5355\u7684\u7528\u6237\u534F\u8BAE\u786E\u8BA4\n- \u514D\u8D23\u58F0\u660E\n- \u670D\u52A1\u6761\u6B3E\u540C\u610F\n\n**\u4E0E\u5B8C\u6574\u7248\u7684\u533A\u522B\uFF1A**\n- \u4E0D\u5305\u542B\u53EF\u70B9\u51FB\u7684\u94FE\u63A5\n- \u52FE\u9009\u6846\u6587\u672C\u4E3A\u7EAF\u6587\u672C\n- \u914D\u7F6E\u66F4\u7B80\u5355\n `,\n },\n },\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\n *\n * \u6F14\u793A\u5982\u4F55\u4F7F\u7528 productCardRender \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u7684\u6E32\u67D3\u6837\u5F0F\u3002\n *\n * \u5F53\u540E\u7AEF\u8FD4\u56DE\u4EA7\u54C1\u5360\u4F4D\u7B26 {{product:xxx}} \u65F6\uFF0C\u4F1A\u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\u6765\u5C55\u793A\u4EA7\u54C1\u5361\u7247\u3002\n */\nexport const CustomProductCardRender: Story = {\n args: {\n loginUserId: 'test_test',\n apiBaseUrl: 'http://172.16.38.183:3003',\n cartId: 'gid://shopify/Cart/hWN7wB3Pa12gh78d8hPOAUBI?key=0e73db1d3fb5ac21da19099c45033253',\n accessToken: '47b1aa2c0797043f9baba39388029d70',\n title: 'eufy',\n site: 'beta.eufy.com',\n channelCode: 'dtc',\n welcomeMessage: '\u4F60\u597D\uFF01\u6211\u53EF\u4EE5\u4E3A\u4F60\u63A8\u8350\u4EA7\u54C1\uFF0C\u8BD5\u8BD5\u53D1\u9001 \"\u63A8\u8350\u4EA7\u54C1\" \u6765\u67E5\u770B\u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6548\u679C\u3002',\n\n // \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\u51FD\u6570\n // \u6CE8\u610F\uFF1Aproduct \u53C2\u6570\u662F\u539F\u59CB\u540E\u7AEF\u6570\u636E\uFF08\u672A\u7ECF\u8F6C\u6362\uFF09\n productCardRender: product => {\n // \u539F\u59CB\u540E\u7AEF\u6570\u636E\u4F7F\u7528 snake_case \u5B57\u6BB5\u540D\n const imageUrl = product.featured_image || ''\n const title = product.title || ''\n const description = product.description || ''\n const price = product.price_range?.min || product.variants?.[0]?.price || 0\n const currency = product.price_range?.currency || product.variants?.[0]?.currency || 'USD'\n const averageRating = product.average_rating\n\n return (\n <div\n style={{\n border: '2px solid #4CAF50',\n borderRadius: '16px',\n padding: '20px',\n margin: '12px 0',\n backgroundColor: '#f0f9ff',\n boxShadow: '0 4px 12px rgba(76, 175, 80, 0.15)',\n transition: 'transform 0.2s',\n }}\n onMouseEnter={e => {\n e.currentTarget.style.transform = 'scale(1.02)'\n }}\n onMouseLeave={e => {\n e.currentTarget.style.transform = 'scale(1)'\n }}\n >\n <div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>\n <div\n style={{\n width: '70px',\n height: 'auto',\n flexShrink: 0,\n borderRadius: '12px',\n overflow: 'hidden',\n border: '2px solid #e0e0e0',\n }}\n >\n <img\n src={imageUrl}\n alt={title}\n style={{\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n }}\n />\n </div>\n\n <div style={{ flex: 1 }}>\n <h3\n style={{\n margin: '0 0 8px 0',\n fontSize: '18px',\n fontWeight: 'bold',\n color: '#1a1a1a',\n lineHeight: '1.4',\n }}\n >\n {title}\n </h3>\n\n {description && (\n <p\n style={{\n margin: '0 0 12px 0',\n fontSize: '14px',\n color: '#666',\n lineHeight: '1.5',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n }}\n >\n {description}\n </p>\n )}\n\n <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>\n <div\n style={{\n fontSize: '24px',\n fontWeight: 'bold',\n color: '#4CAF50',\n }}\n >\n 'test price'\n </div>\n\n {averageRating !== undefined && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n fontSize: '14px',\n color: '#666',\n }}\n >\n <span style={{ color: '#FFB800' }}>\u2B50</span>\n <span>{averageRating.toFixed(1)}</span>\n </div>\n )}\n\n <button\n style={{\n marginLeft: 'auto',\n padding: '8px 20px',\n backgroundColor: '#4CAF50',\n color: 'white',\n borderRadius: '8px',\n textDecoration: 'none',\n fontSize: '14px',\n fontWeight: 'bold',\n transition: 'background-color 0.2s',\n }}\n onClick={() => {\n console.log('View Details', product)\n }}\n >\n View Details\n </button>\n </div>\n </div>\n </div>\n </div>\n )\n },\n\n onAddToCart: product => {\n console.log('Add to cart:', product)\n alert(`Added ${product.title} to cart!`)\n },\n },\n parameters: {\n docs: {\n description: {\n story: `\n### \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\n\n\u4F7F\u7528 \\`productCardRender\\` \u5C5E\u6027\u53EF\u4EE5\u5B8C\u5168\u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u7684\u5C55\u793A\u6837\u5F0F\u3002\n\n**\u529F\u80FD\u8BF4\u660E\uFF1A**\n\n- \u5F53\u540E\u7AEF\u8FD4\u56DE\u5E26\u6709\u4EA7\u54C1\u5360\u4F4D\u7B26 \\`{{product:xxx}}\\` \u7684\u6587\u672C\u6D41\u65F6\uFF0C\u4F1A\u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\n- \u81EA\u5B9A\u4E49\u51FD\u6570\u63A5\u6536\u5B8C\u6574\u7684\u4EA7\u54C1\u5BF9\u8C61\u4F5C\u4E3A\u53C2\u6570\n- \u8FD4\u56DE React \u8282\u70B9\uFF0C\u53EF\u4EE5\u662F\u4EFB\u4F55\u5408\u6CD5\u7684 JSX\n- \u5982\u679C\u4E0D\u63D0\u4F9B \\`productCardRender\\`\uFF0C\u5219\u4F7F\u7528\u9ED8\u8BA4\u7684\u4EA7\u54C1\u5361\u7247\u6837\u5F0F\n\n**\u6D4B\u8BD5\u6B65\u9AA4\uFF1A**\n\n1. \u6253\u5F00\u804A\u5929\u7A97\u53E3\n2. \u53D1\u9001 \"\u63A8\u8350\u4EA7\u54C1\" \u6216\u4EFB\u4F55\u4F1A\u89E6\u53D1\u4EA7\u54C1\u63A8\u8350\u7684\u6D88\u606F\n3. \u89C2\u5BDF\u8FD4\u56DE\u7684\u4EA7\u54C1\u5361\u7247\u4F7F\u7528\u7684\u662F\u81EA\u5B9A\u4E49\u6837\u5F0F\uFF08\u7EFF\u8272\u8FB9\u6846\u3001\u60AC\u505C\u653E\u5927\u6548\u679C\uFF09\n `,\n },\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["jsx", "jsxs", "LiveChatWidget", "meta", "LiveChatWidget_stories_default", "Default", "
|
|
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_test',\n apiBaseUrl: 'http://172.16.38.183:3003',\n site: 'beta.eufy.com',\n channelCode: 'dtc',\n title: 'eufy AI Assistant',\n cartId: 'gid://shopify/Cart/hWN7wB3Pa12gh78d8hPOAUBI?key=0e73db1d3fb5ac21da19099c45033253',\n accessToken: '47b1aa2c0797043f9baba39388029d70',\n\n // \u81EA\u5B9A\u4E49\u4F4D\u7F6E\n position: { bottom: '24px', right: '30px' },\n\n // \u6B22\u8FCE\u6D88\u606F\n welcomeMessage: `Welcome to eufy AI Assistant!\n\nI can help you with:\n- Product recommendations\n- Order tracking\n- FAQs and support\n\nHow can I assist you today?`,\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: \"AI-generated responses can be inaccurate. Please verify important info. Do not input sensitive personal data.\",\n 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>.',\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
7
|
}
|
package/package.json
CHANGED
|
@@ -430,6 +430,9 @@ export const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({
|
|
|
430
430
|
console.error('[LiveChatWidget] Failed to create new session:', error)
|
|
431
431
|
onError?.(error as Error)
|
|
432
432
|
|
|
433
|
+
// Check if it's a reCAPTCHA error
|
|
434
|
+
const isRecaptcha = (error as any)?.type === 'GoogleRecaptchaError'
|
|
435
|
+
|
|
433
436
|
// 添加错误消息到界面
|
|
434
437
|
addMessage({
|
|
435
438
|
id: `error-${Date.now()}`,
|
|
@@ -438,8 +441,10 @@ export const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({
|
|
|
438
441
|
{
|
|
439
442
|
type: 'error',
|
|
440
443
|
data: {
|
|
441
|
-
message:
|
|
442
|
-
|
|
444
|
+
message: isRecaptcha
|
|
445
|
+
? 'Your session has expired. Please refresh the page and try again.'
|
|
446
|
+
: 'Failed to create session. Please refresh the page and try again.',
|
|
447
|
+
code: isRecaptcha ? 'RECAPTCHA_ERROR' : 'SESSION_CREATE_ERROR',
|
|
443
448
|
},
|
|
444
449
|
},
|
|
445
450
|
],
|
|
@@ -545,25 +550,46 @@ export const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({
|
|
|
545
550
|
} catch (error) {
|
|
546
551
|
console.error('[LiveChatWidget] Failed to resume session:', error)
|
|
547
552
|
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
553
|
+
// Check if it's a reCAPTCHA error
|
|
554
|
+
const isRecaptcha = (error as any)?.type === 'GoogleRecaptchaError'
|
|
555
|
+
|
|
556
|
+
if (isRecaptcha) {
|
|
557
|
+
// reCAPTCHA error - show refresh page message, don't retry
|
|
558
|
+
addMessage({
|
|
559
|
+
id: `error-${Date.now()}`,
|
|
560
|
+
role: 'system',
|
|
561
|
+
content: [
|
|
562
|
+
{
|
|
563
|
+
type: 'error',
|
|
564
|
+
data: {
|
|
565
|
+
message: 'Your session has expired. Please refresh the page and try again.',
|
|
566
|
+
code: 'RECAPTCHA_ERROR',
|
|
567
|
+
},
|
|
558
568
|
},
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
569
|
+
],
|
|
570
|
+
timestamp: Date.now(),
|
|
571
|
+
})
|
|
572
|
+
} else {
|
|
573
|
+
// Other errors - show message and try to create new session
|
|
574
|
+
addMessage({
|
|
575
|
+
id: `error-${Date.now()}`,
|
|
576
|
+
role: 'system',
|
|
577
|
+
content: [
|
|
578
|
+
{
|
|
579
|
+
type: 'error',
|
|
580
|
+
data: {
|
|
581
|
+
message: 'Failed to resume session. Creating a new session...',
|
|
582
|
+
code: 'SESSION_RESUME_ERROR',
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
],
|
|
586
|
+
timestamp: Date.now(),
|
|
587
|
+
})
|
|
563
588
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
589
|
+
// 恢复失败,清空会话并创建新的
|
|
590
|
+
clearSession()
|
|
591
|
+
handleCreateNewSession()
|
|
592
|
+
}
|
|
567
593
|
}
|
|
568
594
|
},
|
|
569
595
|
[
|
|
@@ -703,6 +729,24 @@ export const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({
|
|
|
703
729
|
console.error('[LiveChatWidget] Failed to send message:', error)
|
|
704
730
|
onError?.(error as Error)
|
|
705
731
|
|
|
732
|
+
// Check if it's a reCAPTCHA error
|
|
733
|
+
const isRecaptcha = (error as any)?.type === 'GoogleRecaptchaError'
|
|
734
|
+
|
|
735
|
+
// Determine error message based on error type
|
|
736
|
+
let errorMessage: string
|
|
737
|
+
let errorCode: string
|
|
738
|
+
|
|
739
|
+
if (isRecaptcha) {
|
|
740
|
+
errorMessage = 'Your session has expired. Please refresh the page and try again.'
|
|
741
|
+
errorCode = 'RECAPTCHA_ERROR'
|
|
742
|
+
} else if (sessionExpiredDetected) {
|
|
743
|
+
errorMessage = 'Your session has expired. We tried to reconnect but failed. Please try again.'
|
|
744
|
+
errorCode = 'SESSION_EXPIRED'
|
|
745
|
+
} else {
|
|
746
|
+
errorMessage = 'Failed to send message. Please check your network connection and try again.'
|
|
747
|
+
errorCode = 'NETWORK_ERROR'
|
|
748
|
+
}
|
|
749
|
+
|
|
706
750
|
// 移除刚才添加的 thinking 消息,并添加错误消息
|
|
707
751
|
const messagesWithoutThinking = messages.filter(msg => msg.id !== thinkingMessage.id)
|
|
708
752
|
setMessages([
|
|
@@ -714,10 +758,8 @@ export const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({
|
|
|
714
758
|
{
|
|
715
759
|
type: 'error',
|
|
716
760
|
data: {
|
|
717
|
-
message:
|
|
718
|
-
|
|
719
|
-
: 'Failed to send message. Please check your network connection and try again.',
|
|
720
|
-
code: sessionExpiredDetected ? 'SESSION_EXPIRED' : 'NETWORK_ERROR',
|
|
761
|
+
message: errorMessage,
|
|
762
|
+
code: errorCode,
|
|
721
763
|
},
|
|
722
764
|
},
|
|
723
765
|
],
|
|
@@ -59,7 +59,10 @@ export async function sendMessage(
|
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
if (!response.ok) {
|
|
62
|
-
|
|
62
|
+
const errorData = await response.json().catch(() => null)
|
|
63
|
+
const error = new Error(errorData?.message || `HTTP ${response.status}`) as Error & { type?: string }
|
|
64
|
+
error.type = errorData?.type
|
|
65
|
+
throw error
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
const reader = response.body?.getReader()
|
|
@@ -162,12 +165,10 @@ export async function createNewSession(
|
|
|
162
165
|
})
|
|
163
166
|
|
|
164
167
|
if (!response.ok) {
|
|
165
|
-
const errorData
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}))
|
|
170
|
-
throw new Error(errorData.error || 'Failed to create session')
|
|
168
|
+
const errorData = await response.json().catch(() => null)
|
|
169
|
+
const error = new Error(errorData?.message || `HTTP ${response.status}`) as Error & { type?: string }
|
|
170
|
+
error.type = errorData?.type
|
|
171
|
+
throw error
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
return response.json()
|
|
@@ -61,21 +61,17 @@ export const ComplianceDialog: React.FC<ComplianceDialogProps> = ({
|
|
|
61
61
|
onAgree,
|
|
62
62
|
onClose,
|
|
63
63
|
}) => {
|
|
64
|
-
const [isChecked, setIsChecked] = useState(
|
|
64
|
+
const [isChecked, setIsChecked] = useState(true)
|
|
65
65
|
|
|
66
66
|
const handleAgree = useCallback(() => {
|
|
67
67
|
if (isChecked) {
|
|
68
68
|
onAgree()
|
|
69
|
-
// 重置勾选状态,以便下次打开时重新勾选
|
|
70
|
-
setIsChecked(false)
|
|
71
69
|
}
|
|
72
70
|
}, [isChecked, onAgree])
|
|
73
71
|
|
|
74
72
|
const handleOpenChange = useCallback((open: boolean) => {
|
|
75
73
|
if (!open) {
|
|
76
74
|
onClose()
|
|
77
|
-
// 重置勾选状态
|
|
78
|
-
setIsChecked(false)
|
|
79
75
|
}
|
|
80
76
|
}, [onClose])
|
|
81
77
|
|
|
@@ -192,7 +192,7 @@ const CompactProductCard: React.FC<{
|
|
|
192
192
|
* data: {
|
|
193
193
|
* product: { ... },
|
|
194
194
|
* onAddToCart: (product) => { ... },
|
|
195
|
-
* productCardRender: (product) => <CustomCard product={product} />
|
|
195
|
+
* productCardRender: (product, productHandle) => <CustomCard product={product} handle={productHandle} />
|
|
196
196
|
* }
|
|
197
197
|
* }
|
|
198
198
|
* <ProductCard.render(content, false, false) />
|
|
@@ -201,18 +201,27 @@ const CompactProductCard: React.FC<{
|
|
|
201
201
|
export const ProductCard: MessageRenderer = {
|
|
202
202
|
render: (content, isUser, isSystem) => {
|
|
203
203
|
const productContent = content as ProductCardContent
|
|
204
|
-
const { product, rawProduct, onAddToCart, productCardRender } = productContent.data
|
|
204
|
+
const { product, rawProduct, productHandle, onAddToCart, productCardRender } = productContent.data
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
console.log('[ProductCard] 渲染产品卡片:', {
|
|
207
|
+
productHandle,
|
|
208
|
+
hasProduct: !!product,
|
|
209
|
+
hasRawProduct: !!rawProduct,
|
|
210
|
+
hasCustomRender: !!productCardRender,
|
|
211
|
+
})
|
|
209
212
|
|
|
210
|
-
//
|
|
213
|
+
// 如果提供了自定义渲染函数,使用自定义渲染(传入原始后端数据和 productHandle)
|
|
214
|
+
// 即使 product 为空,也调用自定义渲染函数,让应用层可以用 productHandle 查询产品
|
|
211
215
|
if (productCardRender) {
|
|
212
|
-
|
|
216
|
+
console.log('[ProductCard] 使用自定义渲染, productHandle:', productHandle)
|
|
217
|
+
return <>{productCardRender(rawProduct || product, productHandle)}</>
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 默认渲染:如果没有产品数据则不渲染
|
|
221
|
+
if (!product) {
|
|
222
|
+
return null
|
|
213
223
|
}
|
|
214
224
|
|
|
215
|
-
// 否则使用默认渲染
|
|
216
225
|
return <CompactProductCard product={product} onAddToCart={onAddToCart} />
|
|
217
226
|
},
|
|
218
227
|
}
|