shopify-chatbot-widget 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +69 -0
  2. package/eslint.config.js +23 -0
  3. package/index.html +13 -0
  4. package/package-lock 2.json +4493 -0
  5. package/package.json +43 -0
  6. package/public/vite.svg +1 -0
  7. package/src/App.css +42 -0
  8. package/src/App.tsx +13 -0
  9. package/src/Chatbot/Chatbot.css +154 -0
  10. package/src/Chatbot/Chatbot.tsx +207 -0
  11. package/src/Chatbot/components/ChatInput.css +127 -0
  12. package/src/Chatbot/components/ChatInput.tsx +262 -0
  13. package/src/Chatbot/components/Messages/Addcart.tsx +70 -0
  14. package/src/Chatbot/components/Messages/AgentStatus.tsx +37 -0
  15. package/src/Chatbot/components/Messages/AssistantMesage.tsx +316 -0
  16. package/src/Chatbot/components/Messages/AudioMessage.tsx +123 -0
  17. package/src/Chatbot/components/Messages/CartWidget.tsx +116 -0
  18. package/src/Chatbot/components/Messages/ChatImage.tsx +46 -0
  19. package/src/Chatbot/components/Messages/ChatImageText.tsx +74 -0
  20. package/src/Chatbot/components/Messages/ChatImageTextAssis.tsx +72 -0
  21. package/src/Chatbot/components/Messages/ChatMessages.css +5 -0
  22. package/src/Chatbot/components/Messages/ChatMessages.tsx +284 -0
  23. package/src/Chatbot/components/Messages/Support.tsx +177 -0
  24. package/src/Chatbot/components/Messages/Typing.css +27 -0
  25. package/src/Chatbot/components/Messages/Typing.tsx +14 -0
  26. package/src/Chatbot/components/Messages/ZidAssistantMesage.tsx +142 -0
  27. package/src/Chatbot/components/Messenger/Messenger.tsx +521 -0
  28. package/src/Chatbot/components/Notification.tsx +27 -0
  29. package/src/Chatbot/components/Powered.css +16 -0
  30. package/src/Chatbot/components/Powered.tsx +13 -0
  31. package/src/Chatbot/components/Sessions/CreateSession.tsx +139 -0
  32. package/src/Chatbot/components/Sessions/RenderList.tsx +202 -0
  33. package/src/Chatbot/components/Suggestions.css +34 -0
  34. package/src/Chatbot/components/Suggestions.tsx +28 -0
  35. package/src/assets/react.svg +1 -0
  36. package/src/chatbot-widget/index.tsx +14 -0
  37. package/src/hooks/config.tsx +13 -0
  38. package/src/hooks/useCart.tsx +99 -0
  39. package/src/hooks/useChatbotAPI.tsx +160 -0
  40. package/src/hooks/useChatbotData.tsx +129 -0
  41. package/src/hooks/useSessionMessages.tsx +51 -0
  42. package/src/hooks/useWebsocke.tsx +45 -0
  43. package/src/i18n.tsx +30 -0
  44. package/src/index.css +1 -0
  45. package/src/main.tsx +10 -0
  46. package/src/types/chatbot.ts +38 -0
  47. package/src/utils/cookies.tsx +14 -0
  48. package/src/vite-env.d.ts +1 -0
  49. package/tsconfig.app.json +27 -0
  50. package/tsconfig.json +7 -0
  51. package/tsconfig.node.json +25 -0
  52. package/vite.config.ts +18 -0
@@ -0,0 +1,123 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+ import WaveSurfer from "wavesurfer.js";
3
+ import {
4
+ PlayCircleOutlined,
5
+ PauseCircleOutlined
6
+ } from "@ant-design/icons";
7
+
8
+ interface AudioVisualizerProps {
9
+ link: string;
10
+ colorCode: string;
11
+ transcription?: string;
12
+ }
13
+
14
+ const AudioVisualizer: React.FC<AudioVisualizerProps> = ({
15
+ link,
16
+ colorCode,
17
+ transcription,
18
+ }) => {
19
+ const audioRef = useRef<HTMLDivElement | null>(null);
20
+ const wavesurferRef = useRef<WaveSurfer | null>(null);
21
+ const [isPlaying, setIsPlaying] = useState(false);
22
+ const [showTranscription, setShowTranscription] = useState(false);
23
+
24
+ useEffect(() => {
25
+ if (!audioRef.current) return;
26
+
27
+ wavesurferRef.current?.destroy();
28
+
29
+ wavesurferRef.current = WaveSurfer.create({
30
+ container: audioRef.current,
31
+ waveColor: "white",
32
+ progressColor: "white",
33
+ cursorWidth: 0,
34
+ barWidth: 3,
35
+ barHeight: 1.5,
36
+ barGap: 2,
37
+ height: 40,
38
+ interact: true,
39
+ });
40
+
41
+ wavesurferRef.current.load(link);
42
+ wavesurferRef.current.on("finish", () => setIsPlaying(false));
43
+
44
+ return () => {
45
+ wavesurferRef.current?.destroy();
46
+ };
47
+ }, [link, colorCode]);
48
+
49
+ const togglePlay = () => {
50
+ if (wavesurferRef.current) {
51
+ wavesurferRef.current.playPause();
52
+ setIsPlaying((prev) => !prev);
53
+ }
54
+ };
55
+
56
+ return (
57
+ <div
58
+ style={{
59
+ backgroundColor:colorCode,
60
+ padding: "12px",
61
+ borderRadius: "20px",
62
+ color: "white",
63
+ maxWidth: "350px",
64
+ marginBottom: "16px",
65
+ }}
66
+ >
67
+ <div style={{ display: "flex", alignItems: "center" }}>
68
+ <button
69
+ onClick={togglePlay}
70
+ style={{
71
+ background: "transparent",
72
+ border: "none",
73
+ color: "white",
74
+ marginRight: "12px",
75
+ cursor: "pointer",
76
+ }}
77
+ >
78
+ {isPlaying ? (
79
+ <PauseCircleOutlined style={{ fontSize: "24px" }} />
80
+ ) : (
81
+ <PlayCircleOutlined style={{ fontSize: "24px" }} />
82
+ )}
83
+ </button>
84
+
85
+ <div
86
+ ref={audioRef}
87
+ style={{
88
+ width: "130px",
89
+ height: "40px",
90
+ overflow: "hidden",
91
+ color:'white'
92
+ }}
93
+ />
94
+
95
+ </div>
96
+
97
+ {transcription && (
98
+ <div style={{ marginTop: "12px", fontSize: "14px", color: "rgba(255,255,255,0.85)" }}>
99
+ <button
100
+ onClick={() => setShowTranscription(!showTranscription)}
101
+ style={{
102
+ background: "none",
103
+ border: "none",
104
+ color: "white",
105
+ textDecoration: "underline",
106
+ fontSize: "13px",
107
+ cursor: "pointer",
108
+ padding: 0,
109
+ }}
110
+ >
111
+ {showTranscription ? "Hide transcript" : "Show transcript"}
112
+ </button>
113
+
114
+ {showTranscription && (
115
+ <p style={{ marginTop: "6px", lineHeight: "1.4" }}>{transcription}</p>
116
+ )}
117
+ </div>
118
+ )}
119
+ </div>
120
+ );
121
+ };
122
+
123
+ export default AudioVisualizer;
@@ -0,0 +1,116 @@
1
+ // src/components/Messages/CartWidget.tsx
2
+ import { useState, useRef, useEffect } from 'react';
3
+ import type { CartItem } from '../../../hooks/useCart';
4
+ import { ShoppingCartOutlined, CloseOutlined } from '@ant-design/icons';
5
+ import { Button } from 'antd';
6
+
7
+ interface Props {
8
+ items: CartItem[];
9
+ colorCode:string;
10
+ loading: boolean;
11
+ onRefresh: () => void;
12
+ onRemove: (variantId: string, qty: number) => void;
13
+ sendMessage: (message: string) => Promise<void>;
14
+
15
+ }
16
+
17
+ export default function CartWidget({
18
+ items,
19
+ loading,
20
+ colorCode,
21
+ onRefresh,
22
+ onRemove,
23
+ sendMessage
24
+ }: Props) {
25
+ const [open, setOpen] = useState(false);
26
+ const containerRef = useRef<HTMLDivElement>(null);
27
+
28
+ useEffect(() => {
29
+ const handleClickOutside = (e: MouseEvent) => {
30
+ if (
31
+ containerRef.current &&
32
+ !containerRef.current.contains(e.target as Node)
33
+ ) {
34
+ setOpen(false);
35
+ }
36
+ };
37
+ document.addEventListener('mousedown', handleClickOutside);
38
+ return () => document.removeEventListener('mousedown', handleClickOutside);
39
+ }, []);
40
+
41
+ const totalCount = items.reduce((sum, i) => sum + i.quantity, 0);
42
+
43
+ return (
44
+ <div className="relative inline-block" ref={containerRef}>
45
+ <button
46
+ onClick={() => setOpen(o => !o)}
47
+ className="flex items-center space-x-1 px-3 py-1 hover:bg-gray-100 rounded-md"
48
+ >
49
+ <ShoppingCartOutlined className="text-xl" />
50
+ <span className="font-semibold">Cart ({totalCount})</span>
51
+ </button>
52
+
53
+ {open && (
54
+ <div className="absolute right-0 mt-4 w-80 bg-white border border-gray-200 rounded-lg shadow-lg z-50">
55
+ {/* Header */}
56
+ <div className="flex items-center justify-between px-4 py-2 border-b border-gray-100">
57
+ <span className="text-lg font-medium">Your Cart</span>
58
+ <button
59
+ onClick={onRefresh}
60
+ disabled={loading}
61
+ className="text-sm text-blue-600 hover:underline disabled:opacity-50"
62
+ >
63
+ {loading ? 'Refreshing…' : 'Refresh'}
64
+ </button>
65
+ </div>
66
+
67
+ {/* Concatenated-name summary */}
68
+
69
+ {/* Items list */}
70
+ {items.length === 0 ? (
71
+ <div className="p-4 text-center text-gray-500">
72
+ Your cart is empty.
73
+ </div>
74
+ ) : (
75
+ <ul className="max-h-60 overflow-auto">
76
+ {items.map(i => (
77
+ <li
78
+ key={i.variantId}
79
+ className="flex items-center justify-between px-4 py-2 hover:bg-gray-50"
80
+ >
81
+ {/* Fixed-width, truncated name */}
82
+ <span className="inline-block w-40 text-sm font-medium truncate">
83
+ {i.name}
84
+ </span>
85
+
86
+ <div className="flex items-center space-x-2">
87
+ <span className="text-sm text-gray-700">×{i.quantity}</span>
88
+ <CloseOutlined
89
+ onClick={() => onRemove(i.variantId, i.quantity)}
90
+ className="text-red-500 hover:text-red-700 cursor-pointer"
91
+ />
92
+ </div>
93
+ </li>
94
+ ))}
95
+ </ul>
96
+ )}
97
+
98
+ {/* Footer */}
99
+ {items.length > 0 && (
100
+ <div className="px-4 py-3 border-t border-gray-100">
101
+ <Button
102
+ type="primary"
103
+ style={{backgroundColor:colorCode}}
104
+ block
105
+ disabled={totalCount === 0}
106
+ onClick={() => sendMessage('I want to Checkout')}
107
+ >
108
+ Checkout ({totalCount})
109
+ </Button>
110
+ </div>
111
+ )}
112
+ </div>
113
+ )}
114
+ </div>
115
+ );
116
+ }
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+
3
+ type MessagePart = {
4
+ type: string;
5
+ text?: string;
6
+ image_url?: {
7
+ url?: string;
8
+ };
9
+ };
10
+
11
+ type ChatImageProps = {
12
+ message: MessagePart[] & { isBusiness?: boolean };
13
+
14
+ };
15
+
16
+ const ChatImage: React.FC<ChatImageProps> = ({ message }) => {
17
+ const imgPart = message.find(part => part.type === 'image_url')?.image_url?.url;
18
+ const isBusiness = (message as any)?.isBusiness;
19
+
20
+
21
+ return (
22
+ <div>
23
+ <div>
24
+ {imgPart && (
25
+ <div className="mb-2 bg-transparent text-white ">
26
+ <img
27
+ src={imgPart}
28
+ alt="Chat image"
29
+ style={{ width: 200 }}
30
+ className={`rounded-md bg-transparent mt-2 max-w-xs ${
31
+ isBusiness ? 'border border-gray-300 shadow-sm' : ''
32
+ }`}
33
+ />
34
+ </div>
35
+ )}
36
+
37
+
38
+ </div>
39
+
40
+ </div>
41
+
42
+
43
+ );
44
+ };
45
+
46
+ export default ChatImage;
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+
3
+ type MessagePart = {
4
+ type: string;
5
+ text?: string;
6
+ image_url?: {
7
+ url?: string;
8
+ };
9
+ };
10
+
11
+ type ChatImageProps = {
12
+ message: MessagePart[] & { isBusiness?: boolean };
13
+ colorCode:string;
14
+ };
15
+
16
+ const ChatImageText: React.FC<ChatImageProps> = ({ message ,colorCode}) => {
17
+ const textPart = message.find(part => part.type === 'text')?.text;
18
+
19
+
20
+ const formatTextWithLinks = (text: string): React.ReactNode[] => {
21
+ const urlRegex = /(https?:\/\/[^\s]+)/g;
22
+ return text.split(urlRegex).map((part, i) =>
23
+ urlRegex.test(part) ? (
24
+ <a
25
+ key={i}
26
+ href={part.trim()}
27
+ style={{ color: colorCode }}
28
+ target="_blank"
29
+ rel="noopener noreferrer"
30
+ className="underline break-words"
31
+ >
32
+ {part}
33
+ </a>
34
+ ) : (
35
+ <span key={i}>{part}</span>
36
+ )
37
+ );
38
+ };
39
+
40
+ return (
41
+ <>
42
+ {textPart && (
43
+ <div
44
+ style={{
45
+ color:'white',
46
+ border:1,
47
+ backgroundColor:colorCode,
48
+ paddingTop:10,
49
+ paddingBottom:10,
50
+ paddingInline:14,
51
+ maxWidth:'85%',
52
+ lineHeight:1.4,
53
+ borderRadius:'16px'
54
+ ,wordBreak:'break-word'
55
+
56
+ }}
57
+ >
58
+
59
+ <p >
60
+ {formatTextWithLinks(textPart)}
61
+ </p>
62
+
63
+ </div>
64
+ )}
65
+ </>
66
+
67
+
68
+
69
+
70
+
71
+ );
72
+ };
73
+
74
+ export default ChatImageText;
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+
3
+ type MessagePart = {
4
+ type: string;
5
+ text?: string;
6
+ image_url?: {
7
+ url?: string;
8
+ };
9
+ };
10
+
11
+ type ChatImageProps = {
12
+ message: MessagePart[] & { isBusiness?: boolean };
13
+ };
14
+
15
+ const ChatImageTextAssist: React.FC<ChatImageProps> = ({ message }) => {
16
+ const textPart = message.find(part => part.type === 'text')?.text;
17
+
18
+
19
+ const formatTextWithLinks = (text: string): React.ReactNode[] => {
20
+ const urlRegex = /(https?:\/\/[^\s]+)/g;
21
+ return text.split(urlRegex).map((part, i) =>
22
+ urlRegex.test(part) ? (
23
+ <a
24
+ key={i}
25
+ href={part.trim()}
26
+ style={{ color: 'black' }}
27
+ target="_blank"
28
+ rel="noopener noreferrer"
29
+ className="underline break-words"
30
+ >
31
+ {part}
32
+ </a>
33
+ ) : (
34
+ <span key={i}>{part}</span>
35
+ )
36
+ );
37
+ };
38
+
39
+ return (
40
+ <>
41
+ {textPart && (
42
+ <div
43
+ style={{
44
+ color:'black',
45
+ border:1,
46
+ paddingTop:10,
47
+ paddingBottom:10,
48
+ paddingInline:14,
49
+ maxWidth:'85%',
50
+ lineHeight:1.4,
51
+ borderRadius:'16px'
52
+ ,wordBreak:'break-word',
53
+ backgroundColor:'#f0f0f0'
54
+ }}
55
+ >
56
+
57
+ <p >
58
+ {formatTextWithLinks(textPart)}
59
+ </p>
60
+
61
+ </div>
62
+ )}
63
+ </>
64
+
65
+
66
+
67
+
68
+
69
+ );
70
+ };
71
+
72
+ export default ChatImageTextAssist;
@@ -0,0 +1,5 @@
1
+ .chat-image-wrapper {
2
+ padding: 4px 12px;
3
+
4
+ }
5
+