shopify-chatbot-widget 0.7.9 → 0.8.2

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 (58) hide show
  1. package/dist/jaweb-chatbot.css +1 -1
  2. package/dist/jaweb-chatbot.iife.js +3 -3
  3. package/package.json +1 -1
  4. package/src/App.css +42 -0
  5. package/src/App.tsx +13 -0
  6. package/src/Chatbot/Chatbot.css +270 -0
  7. package/src/Chatbot/Chatbot.tsx +214 -0
  8. package/src/Chatbot/ChatbotTransition.css +19 -0
  9. package/src/Chatbot/OLDCHAT.tsx +259 -0
  10. package/src/Chatbot/components/ChatInput.css +127 -0
  11. package/src/Chatbot/components/ChatInput.tsx +354 -0
  12. package/src/Chatbot/components/Messages/Addcart.tsx +70 -0
  13. package/src/Chatbot/components/Messages/AgentStatus.tsx +37 -0
  14. package/src/Chatbot/components/Messages/AssistantMesage.tsx +206 -0
  15. package/src/Chatbot/components/Messages/AudioMessage.tsx +123 -0
  16. package/src/Chatbot/components/Messages/CalendlyEmbeds.tsx +66 -0
  17. package/src/Chatbot/components/Messages/CardSwiper.tsx +194 -0
  18. package/src/Chatbot/components/Messages/Carousel/CardSwiper.tsx +264 -0
  19. package/src/Chatbot/components/Messages/Carousel/CardSwiperSalla.tsx +208 -0
  20. package/src/Chatbot/components/Messages/Carousel/CardSwiperZid.tsx +208 -0
  21. package/src/Chatbot/components/Messages/Carousel/SallaSwiper.css +245 -0
  22. package/src/Chatbot/components/Messages/Carousel/TryOn.tsx +111 -0
  23. package/src/Chatbot/components/Messages/CartWidget.tsx +151 -0
  24. package/src/Chatbot/components/Messages/ChatImage.tsx +46 -0
  25. package/src/Chatbot/components/Messages/ChatImageText.tsx +74 -0
  26. package/src/Chatbot/components/Messages/ChatImageTextAssis.tsx +72 -0
  27. package/src/Chatbot/components/Messages/ChatMessages.css +31 -0
  28. package/src/Chatbot/components/Messages/ChatMessages.tsx +334 -0
  29. package/src/Chatbot/components/Messages/ProductCard.css +149 -0
  30. package/src/Chatbot/components/Messages/SallaAssistantMessage.tsx +196 -0
  31. package/src/Chatbot/components/Messages/Support.tsx +177 -0
  32. package/src/Chatbot/components/Messages/Typing.css +31 -0
  33. package/src/Chatbot/components/Messages/Typing.tsx +19 -0
  34. package/src/Chatbot/components/Messages/ZidAssistantMesage.tsx +196 -0
  35. package/src/Chatbot/components/Messages/cartwidget.css +123 -0
  36. package/src/Chatbot/components/Messenger/Messenger.tsx +531 -0
  37. package/src/Chatbot/components/Notification.tsx +28 -0
  38. package/src/Chatbot/components/Powered.css +29 -0
  39. package/src/Chatbot/components/Powered.tsx +12 -0
  40. package/src/Chatbot/components/Sessions/CreateSession.tsx +201 -0
  41. package/src/Chatbot/components/Sessions/RenderList.tsx +212 -0
  42. package/src/Chatbot/components/Suggestions.css +35 -0
  43. package/src/Chatbot/components/Suggestions.tsx +57 -0
  44. package/src/Chatbot/notification.css +17 -0
  45. package/src/assets/react.svg +1 -0
  46. package/src/chatbot-widget/index.tsx +14 -0
  47. package/src/hooks/config.tsx +13 -0
  48. package/src/hooks/useCart.tsx +107 -0
  49. package/src/hooks/useChatbotAPI.tsx +167 -0
  50. package/src/hooks/useChatbotData.tsx +132 -0
  51. package/src/hooks/useSessionMessages.tsx +50 -0
  52. package/src/hooks/useWebsocke.tsx +45 -0
  53. package/src/i18n.tsx +30 -0
  54. package/src/index.css +2 -0
  55. package/src/main.tsx +10 -0
  56. package/src/types/chatbot.ts +40 -0
  57. package/src/utils/cookies.tsx +14 -0
  58. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,167 @@
1
+ import { useCallback } from 'react';
2
+ import config from './config';
3
+
4
+ interface Params {
5
+ disableChatbot: boolean;
6
+ disableChatbotGlobally: boolean;
7
+ isChatlogCreated: boolean;
8
+ createNew: boolean;
9
+ disableForm: boolean;
10
+ calendlyLink: string | null;
11
+ socket: WebSocket | null;
12
+ newSession:Boolean;
13
+ setNewSession: (v: boolean) => void;
14
+ setMessages: React.Dispatch<React.SetStateAction<any[]>>;
15
+ setIsChatlogCreated: (v: boolean) => void;
16
+ setChatLogWaiting: (v: boolean) => void;
17
+ setNewUser: (v: boolean) => void;
18
+ setCreateNew: (v: boolean) => void;
19
+ setSessionCreated: (v: boolean) => void;
20
+ }
21
+
22
+ export const useChatbotAPI = (params: Params) => {
23
+ const {
24
+ disableChatbot,
25
+ disableChatbotGlobally,
26
+ isChatlogCreated,
27
+ createNew,
28
+ disableForm,
29
+ calendlyLink,
30
+ socket,
31
+ newSession,
32
+ setNewSession,
33
+ setMessages,
34
+ } = params;
35
+
36
+ const apiCall = useCallback(
37
+ async (
38
+ userMessage: string,
39
+ newSession:boolean,
40
+ sessionId: string,
41
+ finalImageUrl?: string,
42
+ isImage?: boolean
43
+ ) => {
44
+ if (newSession) {
45
+ setMessages((prev) => [
46
+ ...prev,
47
+ { role: 'assistant', content: 'typing', typing: true },
48
+ ]);
49
+
50
+ await fetch(`${config.apiUrl}chatlog-create/`, {
51
+ method: 'POST',
52
+ headers: { 'Content-Type': 'application/json' },
53
+ body: JSON.stringify({ visitor_id: localStorage.getItem('visitor_id') , company_username: localStorage.getItem('company_username'),session_id:sessionId}),
54
+ });
55
+
56
+ setNewSession(false)
57
+
58
+ }
59
+
60
+ if (!disableChatbot && !disableChatbotGlobally) {
61
+ setMessages((prev) => {
62
+ const withoutTyping = prev.filter((msg) => !msg.typing);
63
+ return [...withoutTyping, { role: 'assistant', content: 'typing', typing: true }];
64
+ });
65
+
66
+ }
67
+
68
+ try {
69
+ const res = await fetch(`${config.apiUrl}message-create/`, {
70
+ method: 'POST',
71
+ headers: { 'Content-Type': 'application/json' },
72
+ body: JSON.stringify({
73
+ message: userMessage,
74
+ isBusiness: true,
75
+ session_id: sessionId,
76
+ organization: localStorage.getItem('company_username'),
77
+ isImage,
78
+ image_url: finalImageUrl,
79
+ }),
80
+ });
81
+
82
+ if (res.ok) {
83
+ const data = await res.json();
84
+
85
+ if (data.message_history) {
86
+ const assistantMessage = data.isConnect
87
+ ? {
88
+ role: 'assistant',
89
+ content: 'Your support request is on its way—please stand by!',
90
+ isConnect: true,
91
+ sender: 'Ai Agent',
92
+ isBusiness: false,
93
+ isHtml: data.isHtml,
94
+ }
95
+ : {
96
+ role: 'assistant',
97
+ content: data.message,
98
+ sender: 'Ai Agent',
99
+ isBusiness: false,
100
+ isHtml: data.isHtml,
101
+ };
102
+
103
+ // Send message through WebSocket if available
104
+ if (socket?.readyState === WebSocket.OPEN) {
105
+ socket.send(
106
+ JSON.stringify({
107
+ message: data.message,
108
+ additionalData: {
109
+ isBusiness: false,
110
+ SessionId: sessionId,
111
+ sender: 'AI',
112
+ response: 'from_ai_web',
113
+ },
114
+ })
115
+ );
116
+ }
117
+
118
+
119
+ // Update messages safely
120
+ setMessages((prev) => {
121
+ const filtered = prev.filter((msg) => !msg.typing);
122
+ const updated = [...filtered, assistantMessage];
123
+
124
+ console.log(calendlyLink)
125
+ // Add Calendly message if applicable
126
+ if (
127
+ data.message &&
128
+ calendlyLink &&
129
+ /book\s?(a\s?)?meeting|schedule\s?(a\s?)?(call|meeting)|let\s?('s)?\s?meet|set\s?up\s?(a\s?)?meeting|book\s?(an\s?)?appointment/i.test(
130
+ data.message
131
+ )
132
+ ) {
133
+ console.log("meeting please")
134
+ updated.push({
135
+ role: 'assistant',
136
+ content: 'Scheduling a meeting...',
137
+ isBusiness: false,
138
+ calendly: true,
139
+ });
140
+ }
141
+
142
+ return updated;
143
+ });
144
+ } else if (data.status === 'success') {
145
+ setMessages((prev) => prev.filter((msg) => !msg.typing));
146
+ }
147
+ } else {
148
+ console.error('message-create failed');
149
+ }
150
+ } catch (error) {
151
+ console.error('Message error:', error);
152
+ }
153
+ },
154
+ [
155
+ disableChatbot,
156
+ disableChatbotGlobally,
157
+ isChatlogCreated,
158
+ createNew,
159
+ disableForm,
160
+ calendlyLink,
161
+ socket,
162
+ newSession,
163
+ ]
164
+ );
165
+
166
+ return { apiCall };
167
+ };
@@ -0,0 +1,132 @@
1
+ // hooks/useChatbotData.ts
2
+ import { useEffect, useState } from 'react';
3
+ import axios from 'axios';
4
+ import config from './config';
5
+ import i18n from '../i18n';
6
+ import type { ChatbotDetailsResponse, SuggestionResponse } from '../types/chatbot';
7
+
8
+
9
+ const MAX_RETRIES = 3;
10
+ const RETRY_DELAY = 3000;
11
+
12
+ export const useChatbotData = () => {
13
+
14
+ const [isFetchingDetails, setIsFetchingDetails] = useState(true);
15
+ const [disableForm, setDisableForm] = useState(false);
16
+ const [colorCode, setColorCode] = useState('');
17
+ const [chatbotLogo, setChatbotLogo] = useState('');
18
+ const [disableChatbotGlobally, setDisableChatbotGlobally] = useState(false);
19
+ const [removePoweredByJaweb, setRemovePoweredByJaweb] = useState(false);
20
+ const [initialMessageText, setInitialMessageText] = useState('');
21
+ const [subscription, setSubscription] = useState('');
22
+ const [calendly, setCalendly] = useState<string | null>(null);
23
+ const [merchantPhone, setMerchantPhone] = useState('');
24
+ const [messagesSuggestions, setMessagesSuggestions] = useState<string[]>([]);
25
+ const [restSuggestions, setRestSuggestions] = useState<string[]>([]);
26
+ const [userType,setUserType]=useState('')
27
+ const [disbleCheckout, setDisbleCheckout] = useState(false);
28
+
29
+
30
+ const fetchData = async (retryCount = 0) => {
31
+ const company_name = localStorage.getItem('company_username');
32
+ setIsFetchingDetails(true);
33
+
34
+ const configObj = {
35
+ headers: {
36
+ 'Content-Type': 'application/json',
37
+ Accept: 'application/json',
38
+ },
39
+ params: {
40
+ username: company_name,
41
+ },
42
+ };
43
+
44
+ try {
45
+ const chatbotDetailsResponse = await axios.get<ChatbotDetailsResponse>(
46
+ `${config.apiUrl}chatbot-details/`,
47
+ configObj
48
+ );
49
+
50
+ const data = chatbotDetailsResponse.data.data;
51
+
52
+ if (data.language === 'Arabic') {
53
+ i18n.changeLanguage('ar');
54
+ } else {
55
+ i18n.changeLanguage('en');
56
+ }
57
+
58
+
59
+
60
+ setDisableForm(data.chatbot_form);
61
+ setColorCode(data.chatbot_color);
62
+ setChatbotLogo(data.chatbot_logo);
63
+ setDisableChatbotGlobally(data.disable_chatbot_globally);
64
+ setRemovePoweredByJaweb(data.remove_powered_by_jaweb);
65
+ setInitialMessageText(data.chatbot_initial_msg);
66
+ setSubscription(data.status);
67
+ setUserType(data.mode)
68
+ setCalendly(data.calendly_link ?? null);
69
+ setDisbleCheckout(data.disable_checkout)
70
+
71
+
72
+
73
+ if (data.calendly_link) setCalendly(data.calendly_link);
74
+ if (data.phone_number_id) setMerchantPhone(`+${data.phone_number_id}`);
75
+
76
+ const suggestionsResponse = await axios.get<SuggestionResponse>(
77
+ `${config.apiUrl}chatbot-plugin-suggestions/`,
78
+ configObj
79
+ );
80
+
81
+
82
+ // 💡 Safe fallback logic
83
+ let suggestions: string[] = [];
84
+
85
+ if (Array.isArray(suggestionsResponse.data.data)) {
86
+ suggestions = suggestionsResponse.data.data;
87
+ } else if (Array.isArray((suggestionsResponse.data as any).user_questions)) {
88
+ suggestions = (suggestionsResponse.data as any).user_questions;
89
+ }
90
+
91
+ if (suggestions.length > 0) {
92
+ setMessagesSuggestions(suggestions.slice(0, 3));
93
+ setRestSuggestions(suggestions.slice(3));
94
+ } else {
95
+ setMessagesSuggestions([]);
96
+ setRestSuggestions([]);
97
+ }
98
+
99
+ setIsFetchingDetails(false);
100
+ } catch (error) {
101
+ console.error('Error fetching data:', error);
102
+ if (retryCount < MAX_RETRIES) {
103
+ setTimeout(() => fetchData(retryCount + 1), RETRY_DELAY);
104
+ } else {
105
+ console.error('Max retries reached. Could not fetch data.');
106
+ }
107
+ }
108
+ };
109
+
110
+
111
+
112
+ useEffect(() => {
113
+ fetchData();
114
+ }, []);
115
+
116
+ return {
117
+ isFetchingDetails,
118
+ disableForm,
119
+ colorCode,
120
+ chatbotLogo,
121
+ disableChatbotGlobally,
122
+ removePoweredByJaweb,
123
+ initialMessageText,
124
+ subscription,
125
+ calendly,
126
+ merchantPhone,
127
+ messagesSuggestions,
128
+ restSuggestions,
129
+ userType,
130
+ disbleCheckout
131
+ };
132
+ };
@@ -0,0 +1,50 @@
1
+ import { useEffect, useState } from 'react';
2
+ import axios from 'axios';
3
+ import config from './config';
4
+ import type { ChatMessage } from '../types/chatbot';
5
+
6
+ export const useChatbotMessage = (sessionId: string | null) => {
7
+ const [messages, setMessages] = useState<ChatMessage[]>([]);
8
+ const [isFetchingMessages, setisFetchingMessages] = useState(false);
9
+
10
+
11
+ const loadHistory = async () => {
12
+ const sid = sessionId
13
+ if (!sid) return;
14
+
15
+ setisFetchingMessages(true);
16
+
17
+
18
+ try {
19
+ const res = await axios.get<{ messages: ChatMessage[] }>(
20
+ `${config.apiUrl}chat-message-history/`,
21
+ {
22
+ withCredentials: true,
23
+ params: {
24
+ session_id: sid,
25
+ username: localStorage.getItem('company_username'),
26
+ },
27
+ }
28
+ );
29
+
30
+
31
+ if (res.data?.messages?.length > 0) {
32
+ setMessages(res.data.messages);
33
+ setisFetchingMessages(false)
34
+
35
+ }
36
+ } catch (err) {
37
+ console.error("Couldn't load history", err);
38
+ }
39
+ };
40
+
41
+ useEffect(() => {
42
+ loadHistory();
43
+ }, [sessionId]);
44
+
45
+ return {
46
+ messages,
47
+ setMessages,
48
+ isFetchingMessages
49
+ };
50
+ };
@@ -0,0 +1,45 @@
1
+ // hooks/useChatSocket.ts
2
+ import { useEffect, useRef } from 'react';
3
+ import config from './config';
4
+
5
+ export default function useChatSocket(
6
+ session_id: string,
7
+ onMessage: (data: any) => void
8
+ ) {
9
+ const socketRef = useRef<WebSocket | null>(null);
10
+ // keep a stable ref to the latest onMessage
11
+ const onMessageRef = useRef(onMessage);
12
+ useEffect(() => {
13
+ onMessageRef.current = onMessage;
14
+ }, [onMessage]);
15
+
16
+ useEffect(() => {
17
+ if (!session_id) return;
18
+
19
+ const ws = new WebSocket(`${config.websocketUrl}/chat/${session_id}/`);
20
+ socketRef.current = ws;
21
+
22
+ // precise add/remove so we never leak handlers
23
+ const handle = (event: MessageEvent) => {
24
+ try {
25
+ const data = JSON.parse(event.data);
26
+ onMessageRef.current(data);
27
+ } catch (e) {
28
+ console.error('Invalid JSON', e);
29
+ }
30
+ };
31
+ ws.addEventListener('message', handle);
32
+
33
+ // you can still do onopen/onclose, but tear them down too if you attach them
34
+ const handleClose = () => { /* reconnect logic… */ };
35
+ ws.addEventListener('close', handleClose);
36
+
37
+ return () => {
38
+ ws.removeEventListener('message', handle);
39
+ ws.removeEventListener('close', handleClose);
40
+ ws.close();
41
+ };
42
+ }, [session_id]);
43
+
44
+ return socketRef.current;
45
+ }
package/src/i18n.tsx ADDED
@@ -0,0 +1,30 @@
1
+ // src/i18n.ts
2
+ import i18n from 'i18next';
3
+ import { initReactI18next } from 'react-i18next';
4
+
5
+ const resources = {
6
+ en: {
7
+ translation: {
8
+ welcome: "Hi there, you’re speaking with Jaweb AI Assistant.",
9
+ // Add more English translations here
10
+ },
11
+ },
12
+ ar: {
13
+ translation: {
14
+ welcome: "مرحبًا، أنت تتحدث إلى مساعد Jaweb AI.",
15
+ // Add more Arabic translations here
16
+ },
17
+ },
18
+ };
19
+
20
+ i18n.use(initReactI18next).init({
21
+ resources,
22
+ lng: 'en', // default language
23
+ fallbackLng: 'en',
24
+
25
+ interpolation: {
26
+ escapeValue: false, // React already escapes
27
+ },
28
+ });
29
+
30
+ export default i18n;
package/src/index.css ADDED
@@ -0,0 +1,2 @@
1
+ @import "tailwindcss";
2
+
package/src/main.tsx ADDED
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './index.css'
4
+ import App from './App.tsx'
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ )
@@ -0,0 +1,40 @@
1
+ // types/chatbot.ts
2
+
3
+ export interface ChatbotDetailsResponse {
4
+ data: {
5
+ language: string;
6
+ chatbot_form: boolean;
7
+ chatbot_color: string;
8
+ chatbot_logo: string;
9
+ disable_chatbot_globally: boolean;
10
+ remove_powered_by_jaweb: boolean;
11
+ chatbot_initial_msg: string;
12
+ status: string;
13
+ calendly_link?: string | null;
14
+ phone_number_id?: string | null;
15
+ mode:string;
16
+ disable_checkout:boolean;
17
+ };
18
+ }
19
+
20
+ export interface SuggestionResponse {
21
+ data?: string[]; // when data is returned directly
22
+ user_questions?: string[] | null; // when API sends it here
23
+ }
24
+
25
+
26
+ export interface ChatMessage {
27
+ role?: 'user' | 'bot' | 'assistant' | 'system';
28
+ content?:string;
29
+ isBusiness?:boolean;
30
+ sender?:string;
31
+ typing?: boolean;
32
+ id?:Number;
33
+ image_url?:string;
34
+ status?:string;
35
+ audio_url?: string;
36
+ isConnect?:boolean;
37
+ calendly?:boolean;
38
+
39
+
40
+ }
@@ -0,0 +1,14 @@
1
+ // utils/cookies.ts
2
+ import Cookies from 'js-cookie';
3
+
4
+ export const getCookie = (key: string): string | undefined => {
5
+ return Cookies.get(key);
6
+ };
7
+
8
+ export const setCookie = (key: string, value: string, days = 30): void => {
9
+ Cookies.set(key, value, { expires: days });
10
+ };
11
+
12
+ export const removeCookie = (key: string): void => {
13
+ Cookies.remove(key);
14
+ };
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />