ajaxter-chat 3.0.3 β 3.0.5
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/components/ChatScreen/index.d.ts +3 -1
- package/dist/components/ChatScreen/index.js +129 -47
- package/dist/components/ChatWidget.js +12 -1
- package/dist/components/HomeScreen/index.d.ts +2 -1
- package/dist/components/HomeScreen/index.js +128 -51
- package/dist/components/SlideNavMenu.d.ts +12 -0
- package/dist/components/SlideNavMenu.js +73 -0
- package/dist/config/index.js +5 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/index.d.ts +10 -0
- package/package.json +1 -1
- package/public/chatData.json +3 -0
- package/src/components/ChatScreen/index.tsx +276 -172
- package/src/components/ChatWidget.tsx +13 -1
- package/src/components/HomeScreen/index.tsx +240 -80
- package/src/components/SlideNavMenu.tsx +138 -0
- package/src/config/index.ts +5 -1
- package/src/index.ts +1 -0
- package/src/types/index.ts +10 -0
|
@@ -115,6 +115,17 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
|
|
|
115
115
|
}
|
|
116
116
|
}, []);
|
|
117
117
|
|
|
118
|
+
const handleNavFromMenu = useCallback((ctx: UserListContext | 'ticket') => {
|
|
119
|
+
clearChat();
|
|
120
|
+
if (ctx === 'ticket') {
|
|
121
|
+
setActiveTab('tickets');
|
|
122
|
+
setScreen('tickets');
|
|
123
|
+
} else {
|
|
124
|
+
setUserListCtx(ctx);
|
|
125
|
+
setScreen('user-list');
|
|
126
|
+
}
|
|
127
|
+
}, [clearChat]);
|
|
128
|
+
|
|
118
129
|
const handleSelectUser = useCallback((user: ChatUser) => {
|
|
119
130
|
// Load history from sample chats if available
|
|
120
131
|
const history = data?.sampleChats[user.uid] ?? [];
|
|
@@ -366,7 +377,7 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
|
|
|
366
377
|
<div className="cw-scroll" style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
|
|
367
378
|
|
|
368
379
|
{screen === 'home' && (
|
|
369
|
-
<HomeScreen config={widgetConfig} onNavigate={handleCardClick} />
|
|
380
|
+
<HomeScreen config={widgetConfig} onNavigate={handleCardClick} tickets={tickets} />
|
|
370
381
|
)}
|
|
371
382
|
|
|
372
383
|
{screen === 'user-list' && (
|
|
@@ -394,6 +405,7 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
|
|
|
394
405
|
onReport={reportChat}
|
|
395
406
|
onBlock={handleBlock}
|
|
396
407
|
onStartCall={handleStartCall}
|
|
408
|
+
onNavAction={handleNavFromMenu}
|
|
397
409
|
/>
|
|
398
410
|
)}
|
|
399
411
|
|
|
@@ -1,106 +1,266 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { WidgetConfig, UserListContext } from '../../types';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { WidgetConfig, UserListContext, Ticket } from '../../types';
|
|
3
|
+
import { SlideNavMenu } from '../SlideNavMenu';
|
|
3
4
|
|
|
4
5
|
interface HomeScreenProps {
|
|
5
6
|
config: WidgetConfig;
|
|
6
7
|
onNavigate: (ctx: UserListContext | 'ticket') => void;
|
|
8
|
+
tickets: Ticket[];
|
|
7
9
|
}
|
|
8
10
|
|
|
9
|
-
export const HomeScreen: React.FC<HomeScreenProps> = ({ config, onNavigate }) => {
|
|
11
|
+
export const HomeScreen: React.FC<HomeScreenProps> = ({ config, onNavigate, tickets }) => {
|
|
12
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
10
13
|
const showSupport = config.chatType === 'SUPPORT' || config.chatType === 'BOTH';
|
|
11
|
-
const showChat
|
|
14
|
+
const showChat = config.chatType === 'CHAT' || config.chatType === 'BOTH';
|
|
12
15
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
showChat && {
|
|
22
|
-
key: 'conversation' as UserListContext,
|
|
23
|
-
icon: 'π¬',
|
|
24
|
-
title: 'New Conversation',
|
|
25
|
-
subtitle: 'With your colleague',
|
|
26
|
-
onClick: () => onNavigate('conversation'),
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
key: 'ticket',
|
|
30
|
-
icon: 'π«',
|
|
31
|
-
title: 'Raise Ticket',
|
|
32
|
-
subtitle: 'For major changes',
|
|
33
|
-
onClick: () => onNavigate('ticket'),
|
|
34
|
-
},
|
|
35
|
-
].filter(Boolean) as Array<{ key: string; icon: string; title: string; subtitle: string; onClick: () => void }>;
|
|
16
|
+
const continueItems = tickets.slice(0, 2);
|
|
17
|
+
|
|
18
|
+
const handleCallUs = () => {
|
|
19
|
+
const raw = config.supportPhone?.trim();
|
|
20
|
+
if (!raw) return;
|
|
21
|
+
window.location.href = `tel:${raw.replace(/\s/g, '')}`;
|
|
22
|
+
};
|
|
36
23
|
|
|
37
24
|
return (
|
|
38
|
-
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
25
|
+
<div style={{ display: 'flex', flexDirection: 'column', height: '100%', position: 'relative', overflow: 'hidden', background: '#fafbfc' }}>
|
|
26
|
+
<SlideNavMenu
|
|
27
|
+
open={menuOpen}
|
|
28
|
+
onClose={() => setMenuOpen(false)}
|
|
29
|
+
primaryColor={config.primaryColor}
|
|
30
|
+
chatType={config.chatType}
|
|
31
|
+
onSelect={onNavigate}
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
{/* Top bar β burger left */}
|
|
35
|
+
<div
|
|
36
|
+
style={{
|
|
37
|
+
flexShrink: 0,
|
|
38
|
+
padding: '14px 16px 10px',
|
|
39
|
+
display: 'flex',
|
|
40
|
+
alignItems: 'center',
|
|
41
|
+
gap: 12,
|
|
42
|
+
background: '#fff',
|
|
43
|
+
borderBottom: '1px solid #eef0f5',
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
aria-label="Open menu"
|
|
49
|
+
onClick={() => setMenuOpen(true)}
|
|
50
|
+
style={{
|
|
51
|
+
width: 40,
|
|
52
|
+
height: 40,
|
|
53
|
+
borderRadius: 10,
|
|
54
|
+
border: 'none',
|
|
55
|
+
background: '#f1f5f9',
|
|
56
|
+
cursor: 'pointer',
|
|
57
|
+
display: 'flex',
|
|
58
|
+
flexDirection: 'column',
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
justifyContent: 'center',
|
|
61
|
+
gap: 5,
|
|
62
|
+
flexShrink: 0,
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<span style={{ width: 18, height: 2, background: '#334155', borderRadius: 1 }} />
|
|
66
|
+
<span style={{ width: 18, height: 2, background: '#334155', borderRadius: 1 }} />
|
|
67
|
+
<span style={{ width: 18, height: 2, background: '#334155', borderRadius: 1 }} />
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div className="cw-scroll" style={{ flex: 1, overflowY: 'auto', padding: '20px 18px 28px' }}>
|
|
72
|
+
{/* Title + description */}
|
|
73
|
+
<h1
|
|
74
|
+
style={{
|
|
75
|
+
margin: '0 0 8px',
|
|
76
|
+
fontSize: 24,
|
|
77
|
+
fontWeight: 800,
|
|
78
|
+
color: '#0f172a',
|
|
79
|
+
letterSpacing: '-0.03em',
|
|
80
|
+
lineHeight: 1.2,
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
51
83
|
{config.welcomeTitle}
|
|
52
84
|
</h1>
|
|
53
|
-
<p style={{ margin:0, fontSize:14, color:'
|
|
85
|
+
<p style={{ margin: '0 0 28px', fontSize: 14, color: '#64748b', lineHeight: 1.55 }}>
|
|
54
86
|
{config.welcomeSubtitle}
|
|
55
87
|
</p>
|
|
56
|
-
</div>
|
|
57
88
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
{
|
|
89
|
+
{/* Continue Conversations */}
|
|
90
|
+
<h2 style={{ margin: '0 0 12px', fontSize: 15, fontWeight: 800, color: '#0f172a' }}>Continue Conversations</h2>
|
|
91
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 28 }}>
|
|
92
|
+
{continueItems.length > 0 ? (
|
|
93
|
+
continueItems.map(t => (
|
|
94
|
+
<button
|
|
95
|
+
key={t.id}
|
|
96
|
+
type="button"
|
|
97
|
+
onClick={() => onNavigate('ticket')}
|
|
98
|
+
style={{
|
|
99
|
+
width: '100%',
|
|
100
|
+
textAlign: 'left',
|
|
101
|
+
padding: '14px 16px',
|
|
102
|
+
borderRadius: 14,
|
|
103
|
+
border: 'none',
|
|
104
|
+
background: '#e0f2fe',
|
|
105
|
+
color: '#0369a1',
|
|
106
|
+
fontSize: 14,
|
|
107
|
+
fontWeight: 600,
|
|
108
|
+
cursor: 'pointer',
|
|
109
|
+
boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
|
|
110
|
+
}}
|
|
111
|
+
>
|
|
112
|
+
{t.title}
|
|
113
|
+
</button>
|
|
114
|
+
))
|
|
115
|
+
) : (
|
|
116
|
+
<>
|
|
117
|
+
<div
|
|
118
|
+
style={{
|
|
119
|
+
padding: '14px 16px',
|
|
120
|
+
borderRadius: 14,
|
|
121
|
+
background: '#e0f2fe',
|
|
122
|
+
color: '#64748b',
|
|
123
|
+
fontSize: 14,
|
|
124
|
+
fontWeight: 500,
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
No open tickets yet
|
|
128
|
+
</div>
|
|
129
|
+
<div
|
|
130
|
+
style={{
|
|
131
|
+
padding: '14px 16px',
|
|
132
|
+
borderRadius: 14,
|
|
133
|
+
background: '#e0f2fe',
|
|
134
|
+
color: '#64748b',
|
|
135
|
+
fontSize: 14,
|
|
136
|
+
fontWeight: 500,
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
Start via Raise ticket below
|
|
140
|
+
</div>
|
|
141
|
+
</>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
{/* Talk to our experts */}
|
|
146
|
+
<h2 style={{ margin: '0 0 12px', fontSize: 15, fontWeight: 800, color: '#0f172a' }}>Talk to our experts</h2>
|
|
147
|
+
|
|
148
|
+
{showSupport && (
|
|
61
149
|
<button
|
|
62
|
-
|
|
63
|
-
onClick={
|
|
150
|
+
type="button"
|
|
151
|
+
onClick={() => onNavigate('support')}
|
|
64
152
|
style={{
|
|
65
|
-
width:'100%',
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
153
|
+
width: '100%',
|
|
154
|
+
display: 'flex',
|
|
155
|
+
alignItems: 'center',
|
|
156
|
+
justifyContent: 'center',
|
|
157
|
+
gap: 10,
|
|
158
|
+
padding: '14px 18px',
|
|
159
|
+
marginBottom: showChat ? 10 : 14,
|
|
160
|
+
borderRadius: 14,
|
|
161
|
+
border: 'none',
|
|
162
|
+
background: '#ede9fe',
|
|
163
|
+
color: '#5b21b6',
|
|
164
|
+
fontSize: 15,
|
|
165
|
+
fontWeight: 700,
|
|
166
|
+
cursor: 'pointer',
|
|
167
|
+
boxShadow: '0 2px 8px rgba(91,33,182,0.12)',
|
|
72
168
|
}}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
169
|
+
>
|
|
170
|
+
<span style={{ fontSize: 18 }}>π€</span>
|
|
171
|
+
Support
|
|
172
|
+
</button>
|
|
173
|
+
)}
|
|
174
|
+
|
|
175
|
+
{showChat && showSupport && (
|
|
176
|
+
<button
|
|
177
|
+
type="button"
|
|
178
|
+
onClick={() => onNavigate('conversation')}
|
|
179
|
+
style={{
|
|
180
|
+
width: '100%',
|
|
181
|
+
padding: '12px 16px',
|
|
182
|
+
marginBottom: 14,
|
|
183
|
+
borderRadius: 12,
|
|
184
|
+
border: '1.5px solid #e9d5ff',
|
|
185
|
+
background: '#fff',
|
|
186
|
+
color: '#6d28d9',
|
|
187
|
+
fontSize: 14,
|
|
188
|
+
fontWeight: 600,
|
|
189
|
+
cursor: 'pointer',
|
|
76
190
|
}}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
191
|
+
>
|
|
192
|
+
New Conversation
|
|
193
|
+
</button>
|
|
194
|
+
)}
|
|
195
|
+
|
|
196
|
+
{showChat && !showSupport && (
|
|
197
|
+
<button
|
|
198
|
+
type="button"
|
|
199
|
+
onClick={() => onNavigate('conversation')}
|
|
200
|
+
style={{
|
|
201
|
+
width: '100%',
|
|
202
|
+
display: 'flex',
|
|
203
|
+
alignItems: 'center',
|
|
204
|
+
justifyContent: 'center',
|
|
205
|
+
gap: 10,
|
|
206
|
+
padding: '14px 18px',
|
|
207
|
+
marginBottom: 14,
|
|
208
|
+
borderRadius: 14,
|
|
209
|
+
border: 'none',
|
|
210
|
+
background: '#ede9fe',
|
|
211
|
+
color: '#5b21b6',
|
|
212
|
+
fontSize: 15,
|
|
213
|
+
fontWeight: 700,
|
|
214
|
+
cursor: 'pointer',
|
|
80
215
|
}}
|
|
81
216
|
>
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
width:44, height:44, borderRadius:12,
|
|
85
|
-
backgroundColor:`${config.primaryColor}14`,
|
|
86
|
-
display:'flex', alignItems:'center', justifyContent:'center',
|
|
87
|
-
fontSize:20, flexShrink:0,
|
|
88
|
-
}}>{card.icon}</div>
|
|
89
|
-
<div>
|
|
90
|
-
<div style={{ fontWeight:700, fontSize:15, color:'#1a2332', marginBottom:2 }}>{card.title}</div>
|
|
91
|
-
<div style={{ fontSize:12, color:'#7b8fa1' }}>{card.subtitle}</div>
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
<SendArrow color={config.primaryColor} />
|
|
217
|
+
<span style={{ fontSize: 18 }}>π¬</span>
|
|
218
|
+
New Conversation
|
|
95
219
|
</button>
|
|
96
|
-
)
|
|
220
|
+
)}
|
|
221
|
+
|
|
222
|
+
<div
|
|
223
|
+
style={{
|
|
224
|
+
borderRadius: 18,
|
|
225
|
+
padding: '22px 20px 20px',
|
|
226
|
+
background: 'linear-gradient(145deg, #fce7f3 0%, #e9d5ff 45%, #ddd6fe 100%)',
|
|
227
|
+
position: 'relative',
|
|
228
|
+
overflow: 'hidden',
|
|
229
|
+
}}
|
|
230
|
+
>
|
|
231
|
+
<div style={{ position: 'absolute', top: -20, right: -20, width: 100, height: 100, borderRadius: '50%', background: 'rgba(255,255,255,0.35)' }} />
|
|
232
|
+
<p style={{ margin: '0 0 16px', fontSize: 15, fontWeight: 700, color: '#4c1d95', lineHeight: 1.45, position: 'relative' }}>
|
|
233
|
+
Need specialized help? Our teams are ready to assist you with any questions.
|
|
234
|
+
</p>
|
|
235
|
+
<button
|
|
236
|
+
type="button"
|
|
237
|
+
onClick={handleCallUs}
|
|
238
|
+
disabled={!config.supportPhone}
|
|
239
|
+
style={{
|
|
240
|
+
display: 'inline-flex',
|
|
241
|
+
alignItems: 'center',
|
|
242
|
+
gap: 8,
|
|
243
|
+
padding: '10px 18px',
|
|
244
|
+
borderRadius: 12,
|
|
245
|
+
border: 'none',
|
|
246
|
+
background: config.supportPhone ? config.primaryColor : '#94a3b8',
|
|
247
|
+
color: '#fff',
|
|
248
|
+
fontSize: 14,
|
|
249
|
+
fontWeight: 700,
|
|
250
|
+
cursor: config.supportPhone ? 'pointer' : 'not-allowed',
|
|
251
|
+
position: 'relative',
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
|
255
|
+
<path
|
|
256
|
+
d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 10.8a19.79 19.79 0 01-3.07-8.68A2 2 0 012 0h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L6.09 7.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 14.92v2z"
|
|
257
|
+
fill="#fff"
|
|
258
|
+
/>
|
|
259
|
+
</svg>
|
|
260
|
+
Call Us
|
|
261
|
+
</button>
|
|
262
|
+
</div>
|
|
97
263
|
</div>
|
|
98
264
|
</div>
|
|
99
265
|
);
|
|
100
266
|
};
|
|
101
|
-
|
|
102
|
-
const SendArrow: React.FC<{ color: string }> = ({ color }) => (
|
|
103
|
-
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" style={{ flexShrink:0 }}>
|
|
104
|
-
<path d="M5 12h14M12 5l7 7-7 7" stroke={color} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
105
|
-
</svg>
|
|
106
|
-
);
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChatType, UserListContext } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface SlideNavMenuProps {
|
|
5
|
+
open: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
primaryColor: string;
|
|
8
|
+
chatType: ChatType;
|
|
9
|
+
onSelect: (ctx: UserListContext | 'ticket') => void;
|
|
10
|
+
/** When set, shows βBack to homeβ at the bottom (e.g. chat screen) */
|
|
11
|
+
onBackHome?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const SlideNavMenu: React.FC<SlideNavMenuProps> = ({
|
|
15
|
+
open,
|
|
16
|
+
onClose,
|
|
17
|
+
primaryColor,
|
|
18
|
+
chatType,
|
|
19
|
+
onSelect,
|
|
20
|
+
onBackHome,
|
|
21
|
+
}) => {
|
|
22
|
+
const showSupport = chatType === 'SUPPORT' || chatType === 'BOTH';
|
|
23
|
+
const showChat = chatType === 'CHAT' || chatType === 'BOTH';
|
|
24
|
+
|
|
25
|
+
const items: Array<{ key: UserListContext | 'ticket'; icon: string; title: string } | null> = [
|
|
26
|
+
showSupport ? { key: 'support', icon: 'π ', title: 'Need Support' } : null,
|
|
27
|
+
showChat ? { key: 'conversation', icon: 'π¬', title: 'New Conversation' } : null,
|
|
28
|
+
{ key: 'ticket', icon: 'π«', title: 'Raise ticket' },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
if (!open) return null;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
aria-label="Close menu"
|
|
38
|
+
onClick={onClose}
|
|
39
|
+
style={{
|
|
40
|
+
position: 'absolute',
|
|
41
|
+
inset: 0,
|
|
42
|
+
zIndex: 200,
|
|
43
|
+
background: 'rgba(15,23,42,0.45)',
|
|
44
|
+
border: 'none',
|
|
45
|
+
cursor: 'pointer',
|
|
46
|
+
animation: 'cw-fadeIn 0.2s ease',
|
|
47
|
+
}}
|
|
48
|
+
/>
|
|
49
|
+
<style>{`@keyframes cw-fadeIn { from { opacity: 0; } to { opacity: 1; } }`}</style>
|
|
50
|
+
<nav
|
|
51
|
+
style={{
|
|
52
|
+
position: 'absolute',
|
|
53
|
+
top: 0,
|
|
54
|
+
left: 0,
|
|
55
|
+
bottom: 0,
|
|
56
|
+
width: 'min(300px, 88%)',
|
|
57
|
+
zIndex: 210,
|
|
58
|
+
background: '#fff',
|
|
59
|
+
boxShadow: '8px 0 32px rgba(0,0,0,0.12)',
|
|
60
|
+
display: 'flex',
|
|
61
|
+
flexDirection: 'column',
|
|
62
|
+
padding: '20px 0 16px',
|
|
63
|
+
animation: 'cw-slideNavIn 0.28s cubic-bezier(0.22, 1, 0.36, 1)',
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<style>{`@keyframes cw-slideNavIn { from { transform: translateX(-100%); } to { transform: translateX(0); } }`}</style>
|
|
67
|
+
<div style={{ padding: '0 20px 16px', borderBottom: '1px solid #eef0f5' }}>
|
|
68
|
+
<p style={{ margin: 0, fontSize: 13, fontWeight: 700, color: '#64748b', letterSpacing: '0.04em' }}>Menu</p>
|
|
69
|
+
</div>
|
|
70
|
+
<div style={{ flex: 1, overflowY: 'auto', padding: '12px 12px' }}>
|
|
71
|
+
{items.filter(Boolean).map(item => {
|
|
72
|
+
const it = item!;
|
|
73
|
+
return (
|
|
74
|
+
<button
|
|
75
|
+
key={it.key}
|
|
76
|
+
type="button"
|
|
77
|
+
onClick={() => {
|
|
78
|
+
onSelect(it.key);
|
|
79
|
+
onClose();
|
|
80
|
+
}}
|
|
81
|
+
style={{
|
|
82
|
+
width: '100%',
|
|
83
|
+
display: 'flex',
|
|
84
|
+
alignItems: 'center',
|
|
85
|
+
gap: 12,
|
|
86
|
+
padding: '14px 14px',
|
|
87
|
+
marginBottom: 6,
|
|
88
|
+
border: 'none',
|
|
89
|
+
borderRadius: 12,
|
|
90
|
+
background: '#f8fafc',
|
|
91
|
+
cursor: 'pointer',
|
|
92
|
+
textAlign: 'left',
|
|
93
|
+
fontSize: 15,
|
|
94
|
+
fontWeight: 600,
|
|
95
|
+
color: '#1e293b',
|
|
96
|
+
transition: 'background 0.15s',
|
|
97
|
+
}}
|
|
98
|
+
onMouseEnter={e => {
|
|
99
|
+
(e.currentTarget as HTMLButtonElement).style.background = `${primaryColor}12`;
|
|
100
|
+
}}
|
|
101
|
+
onMouseLeave={e => {
|
|
102
|
+
(e.currentTarget as HTMLButtonElement).style.background = '#f8fafc';
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<span style={{ fontSize: 20 }}>{it.icon}</span>
|
|
106
|
+
{it.title}
|
|
107
|
+
</button>
|
|
108
|
+
);
|
|
109
|
+
})}
|
|
110
|
+
</div>
|
|
111
|
+
{onBackHome && (
|
|
112
|
+
<div style={{ padding: '0 12px', borderTop: '1px solid #eef0f5', paddingTop: 12 }}>
|
|
113
|
+
<button
|
|
114
|
+
type="button"
|
|
115
|
+
onClick={() => {
|
|
116
|
+
onBackHome();
|
|
117
|
+
onClose();
|
|
118
|
+
}}
|
|
119
|
+
style={{
|
|
120
|
+
width: '100%',
|
|
121
|
+
padding: '12px 14px',
|
|
122
|
+
border: '1.5px solid #e2e8f0',
|
|
123
|
+
borderRadius: 12,
|
|
124
|
+
background: '#fff',
|
|
125
|
+
fontSize: 14,
|
|
126
|
+
fontWeight: 600,
|
|
127
|
+
color: '#475569',
|
|
128
|
+
cursor: 'pointer',
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
β Back to home
|
|
132
|
+
</button>
|
|
133
|
+
</div>
|
|
134
|
+
)}
|
|
135
|
+
</nav>
|
|
136
|
+
</>
|
|
137
|
+
);
|
|
138
|
+
};
|
package/src/config/index.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { LocalEnvConfig, RemoteChatData } from '../types';
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Default JSON endpoint. Override with `REACT_APP_CHAT_CONFIG_URL` / `NEXT_PUBLIC_CHAT_CONFIG_URL`.
|
|
5
|
+
* If the remote host does not send CORS headers, set this to a same-origin path (e.g. `/api/chat-config`)
|
|
6
|
+
* and proxy the JSON from your server β see `examples/next-app-router-chat-proxy.ts`.
|
|
7
|
+
*/
|
|
4
8
|
const DEFAULT_CHAT_DATA_BASE = 'https://window.mscorpres.com/TEST/chatData.json';
|
|
5
9
|
const DEMO_API_KEY = 'demo1234';
|
|
6
10
|
const DEMO_WIDGET_ID = 'demo';
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { CallScreen } from './components/CallScreen';
|
|
|
9
9
|
export { MaintenanceView } from './components/MaintenanceView';
|
|
10
10
|
export { BottomTabs } from './components/Tabs/BottomTabs';
|
|
11
11
|
export { EmojiPicker } from './components/EmojiPicker';
|
|
12
|
+
export { SlideNavMenu } from './components/SlideNavMenu';
|
|
12
13
|
|
|
13
14
|
export { useChat } from './hooks/useChat';
|
|
14
15
|
export { useWebRTC } from './hooks/useWebRTC';
|
package/src/types/index.ts
CHANGED
|
@@ -9,6 +9,12 @@ export interface WidgetConfig {
|
|
|
9
9
|
buttonPosition: 'bottom-right' | 'bottom-left';
|
|
10
10
|
welcomeTitle: string;
|
|
11
11
|
welcomeSubtitle: string;
|
|
12
|
+
/** Shown in footer (e.g. branch / location name) */
|
|
13
|
+
branch?: string;
|
|
14
|
+
/** Optional label above branch (e.g. "Answers by") */
|
|
15
|
+
footerPoweredBy?: string;
|
|
16
|
+
/** Shown on home βCall Usβ (tel: link) */
|
|
17
|
+
supportPhone?: string;
|
|
12
18
|
allowVoiceMessage: boolean;
|
|
13
19
|
allowAttachment: boolean;
|
|
14
20
|
allowEmoji: boolean;
|
|
@@ -69,7 +75,11 @@ export interface ChatMessage {
|
|
|
69
75
|
status: 'sent' | 'delivered' | 'read';
|
|
70
76
|
attachmentName?: string;
|
|
71
77
|
attachmentSize?: string;
|
|
78
|
+
/** Blob URL for attachment download (local send) */
|
|
79
|
+
attachmentUrl?: string;
|
|
72
80
|
voiceDuration?: number; // seconds
|
|
81
|
+
/** Blob URL for in-bubble audio playback (local recording) */
|
|
82
|
+
voiceUrl?: string;
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
// βββ Ticket βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|