ajaxter-chat 2.0.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -128
- package/dist/components/BlockList/index.d.ts +10 -0
- package/dist/components/BlockList/index.js +33 -0
- package/dist/components/CallScreen/index.d.ts +13 -0
- package/dist/components/CallScreen/index.js +48 -0
- package/dist/components/ChatScreen/index.d.ts +10 -3
- package/dist/components/ChatScreen/index.js +142 -57
- package/dist/components/ChatWidget.js +192 -98
- package/dist/components/EmojiPicker/index.d.ts +8 -0
- package/dist/components/EmojiPicker/index.js +18 -0
- package/dist/components/HomeScreen/index.d.ts +2 -3
- package/dist/components/HomeScreen/index.js +25 -41
- package/dist/components/MaintenanceView/index.d.ts +0 -1
- package/dist/components/MaintenanceView/index.js +4 -6
- package/dist/components/RecentChatsScreen/index.d.ts +4 -3
- package/dist/components/RecentChatsScreen/index.js +7 -37
- package/dist/components/Tabs/BottomTabs.d.ts +1 -1
- package/dist/components/Tabs/BottomTabs.js +25 -20
- package/dist/components/TicketScreen/index.d.ts +3 -3
- package/dist/components/TicketScreen/index.js +39 -56
- package/dist/components/UserListScreen/index.d.ts +2 -4
- package/dist/components/UserListScreen/index.js +33 -62
- package/dist/config/index.d.ts +3 -3
- package/dist/config/index.js +18 -26
- package/dist/hooks/useChat.d.ts +8 -3
- package/dist/hooks/useChat.js +22 -18
- package/dist/hooks/useRemoteConfig.d.ts +6 -0
- package/dist/hooks/useRemoteConfig.js +22 -0
- package/dist/hooks/useWebRTC.d.ts +11 -0
- package/dist/hooks/useWebRTC.js +112 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.js +8 -4
- package/dist/types/index.d.ts +62 -21
- package/dist/utils/chat.d.ts +13 -0
- package/dist/utils/chat.js +62 -0
- package/dist/utils/theme.d.ts +3 -1
- package/dist/utils/theme.js +14 -7
- package/package.json +4 -4
- package/public/chatData.json +162 -0
- package/src/components/BlockList/index.tsx +94 -0
- package/src/components/CallScreen/index.tsx +144 -0
- package/src/components/ChatScreen/index.tsx +403 -139
- package/src/components/ChatWidget.tsx +394 -250
- package/src/components/EmojiPicker/index.tsx +48 -0
- package/src/components/HomeScreen/index.tsx +58 -82
- package/src/components/MaintenanceView/index.tsx +6 -9
- package/src/components/RecentChatsScreen/index.tsx +51 -96
- package/src/components/Tabs/BottomTabs.tsx +45 -37
- package/src/components/TicketScreen/index.tsx +87 -133
- package/src/components/UserListScreen/index.tsx +75 -153
- package/src/config/index.ts +22 -28
- package/src/hooks/useChat.ts +31 -14
- package/src/hooks/useRemoteConfig.ts +20 -0
- package/src/hooks/useWebRTC.ts +130 -0
- package/src/index.ts +26 -15
- package/src/types/index.ts +85 -40
- package/src/utils/chat.ts +70 -0
- package/src/utils/theme.ts +18 -7
- package/dist/hooks/useUsers.d.ts +0 -7
- package/dist/hooks/useUsers.js +0 -26
- package/dist/services/userService.d.ts +0 -2
- package/dist/services/userService.js +0 -9
- package/dist/src/components/ChatScreen/index.d.ts +0 -12
- package/dist/src/components/ChatScreen/index.js +0 -83
- package/dist/src/components/ChatWidget.d.ts +0 -4
- package/dist/src/components/ChatWidget.js +0 -141
- package/dist/src/components/HomeScreen/index.d.ts +0 -9
- package/dist/src/components/HomeScreen/index.js +0 -71
- package/dist/src/components/MaintenanceView/index.d.ts +0 -7
- package/dist/src/components/MaintenanceView/index.js +0 -16
- package/dist/src/components/RecentChatsScreen/index.d.ts +0 -16
- package/dist/src/components/RecentChatsScreen/index.js +0 -38
- package/dist/src/components/Tabs/BottomTabs.d.ts +0 -10
- package/dist/src/components/Tabs/BottomTabs.js +0 -29
- package/dist/src/components/TicketScreen/index.d.ts +0 -9
- package/dist/src/components/TicketScreen/index.js +0 -71
- package/dist/src/components/UserListScreen/index.d.ts +0 -13
- package/dist/src/components/UserListScreen/index.js +0 -64
- package/dist/src/config/index.d.ts +0 -3
- package/dist/src/config/index.js +0 -38
- package/dist/src/hooks/useChat.d.ts +0 -8
- package/dist/src/hooks/useChat.js +0 -26
- package/dist/src/hooks/useUsers.d.ts +0 -7
- package/dist/src/hooks/useUsers.js +0 -26
- package/dist/src/index.d.ts +0 -14
- package/dist/src/index.js +0 -13
- package/dist/src/services/userService.d.ts +0 -2
- package/dist/src/services/userService.js +0 -9
- package/dist/src/types/index.d.ts +0 -59
- package/dist/src/types/index.js +0 -1
- package/dist/src/utils/theme.d.ts +0 -3
- package/dist/src/utils/theme.js +0 -13
- package/src/hooks/useUsers.ts +0 -27
- package/src/services/userService.ts +0 -9
|
@@ -1,49 +1,91 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useCallback } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { loadLocalConfig } from '../config';
|
|
5
5
|
import { mergeTheme } from '../utils/theme';
|
|
6
|
-
import {
|
|
6
|
+
import { useRemoteConfig } from '../hooks/useRemoteConfig';
|
|
7
7
|
import { useChat } from '../hooks/useChat';
|
|
8
|
-
|
|
8
|
+
import { useWebRTC } from '../hooks/useWebRTC';
|
|
9
9
|
import { HomeScreen } from './HomeScreen';
|
|
10
10
|
import { UserListScreen } from './UserListScreen';
|
|
11
11
|
import { ChatScreen } from './ChatScreen';
|
|
12
12
|
import { RecentChatsScreen } from './RecentChatsScreen';
|
|
13
13
|
import { TicketScreen } from './TicketScreen';
|
|
14
|
+
import { BlockListScreen } from './BlockList';
|
|
15
|
+
import { CallScreen } from './CallScreen';
|
|
14
16
|
import { MaintenanceView } from './MaintenanceView';
|
|
15
17
|
import { BottomTabs } from './Tabs/BottomTabs';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
/* ─── Drawer width ─────────────────────────────────────────────────────────── */
|
|
19
|
+
const DRAWER_W_NORMAL = 380;
|
|
20
|
+
const DRAWER_W_MAX = 480;
|
|
21
|
+
export const ChatWidget = ({ theme: localTheme }) => {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
/* SSR guard */
|
|
24
|
+
const [mounted, setMounted] = useState(false);
|
|
25
|
+
useEffect(() => { setMounted(true); }, []);
|
|
26
|
+
/* Env config */
|
|
27
|
+
const { apiKey, widgetId } = loadLocalConfig();
|
|
28
|
+
/* Remote config */
|
|
29
|
+
const { data, loading: cfgLoading, error: cfgError } = useRemoteConfig(apiKey, widgetId);
|
|
30
|
+
/* Merged theme — remote config overrides defaults, local prop overrides both */
|
|
31
|
+
const theme = mergeTheme((data === null || data === void 0 ? void 0 : data.widget) ? { primaryColor: data.widget.primaryColor, buttonLabel: data.widget.buttonLabel, buttonPosition: data.widget.buttonPosition } : undefined, localTheme);
|
|
32
|
+
/* Drawer open state */
|
|
18
33
|
const [isOpen, setIsOpen] = useState(false);
|
|
19
34
|
const [isMaximized, setIsMaximized] = useState(false);
|
|
35
|
+
const [closing, setClosing] = useState(false); // for slide-out animation
|
|
36
|
+
/* Navigation */
|
|
20
37
|
const [activeTab, setActiveTab] = useState('home');
|
|
21
38
|
const [screen, setScreen] = useState('home');
|
|
22
39
|
const [userListCtx, setUserListCtx] = useState('support');
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
/* App state */
|
|
41
|
+
const [tickets, setTickets] = useState((_a = data === null || data === void 0 ? void 0 : data.sampleTickets) !== null && _a !== void 0 ? _a : []);
|
|
42
|
+
const [recentChats, setRecentChats] = useState([]);
|
|
43
|
+
const [blockedUids, setBlockedUids] = useState((_b = data === null || data === void 0 ? void 0 : data.blockedUsers) !== null && _b !== void 0 ? _b : []);
|
|
44
|
+
/* Sync remote data into local state once loaded */
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
if (data) {
|
|
48
|
+
setTickets(data.sampleTickets);
|
|
49
|
+
setBlockedUids(data.blockedUsers);
|
|
50
|
+
// Seed recent chats from sample chats
|
|
51
|
+
const all = [...((_a = data.developers) !== null && _a !== void 0 ? _a : []), ...((_b = data.users) !== null && _b !== void 0 ? _b : [])];
|
|
52
|
+
const recents = Object.entries(data.sampleChats).map(([uid, msgs]) => {
|
|
53
|
+
const user = all.find(u => u.uid === uid);
|
|
54
|
+
if (!user || msgs.length === 0)
|
|
55
|
+
return null;
|
|
56
|
+
const last = msgs[msgs.length - 1];
|
|
57
|
+
return {
|
|
58
|
+
id: `rc_${uid}`,
|
|
59
|
+
user,
|
|
60
|
+
lastMessage: last.text,
|
|
61
|
+
lastTime: last.timestamp,
|
|
62
|
+
unread: Math.floor(Math.random() * 3),
|
|
63
|
+
isPaused: false,
|
|
64
|
+
};
|
|
65
|
+
}).filter(Boolean);
|
|
66
|
+
setRecentChats(recents);
|
|
45
67
|
}
|
|
46
|
-
}, []);
|
|
68
|
+
}, [data]);
|
|
69
|
+
/* Chat hook */
|
|
70
|
+
const { messages, activeUser, isPaused, isReported, selectUser, sendMessage, togglePause, reportChat, clearChat, setMessages, } = useChat();
|
|
71
|
+
/* WebRTC hook */
|
|
72
|
+
const { session: callSession, localVideoRef, remoteVideoRef, startCall, endCall, toggleMute, toggleCamera } = useWebRTC();
|
|
73
|
+
/* ── Drawer open/close with slide animation ───────────────────────────── */
|
|
74
|
+
const openDrawer = () => {
|
|
75
|
+
setClosing(false);
|
|
76
|
+
setIsOpen(true);
|
|
77
|
+
};
|
|
78
|
+
const closeDrawer = useCallback(() => {
|
|
79
|
+
setClosing(true);
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
setIsOpen(false);
|
|
82
|
+
setClosing(false);
|
|
83
|
+
setScreen('home');
|
|
84
|
+
setActiveTab('home');
|
|
85
|
+
clearChat();
|
|
86
|
+
}, 300);
|
|
87
|
+
}, [clearChat]);
|
|
88
|
+
/* ── Navigation ──────────────────────────────────────────────────────── */
|
|
47
89
|
const handleCardClick = useCallback((ctx) => {
|
|
48
90
|
if (ctx === 'ticket') {
|
|
49
91
|
setActiveTab('tickets');
|
|
@@ -55,87 +97,139 @@ export const ChatWidget = ({ theme }) => {
|
|
|
55
97
|
}
|
|
56
98
|
}, []);
|
|
57
99
|
const handleSelectUser = useCallback((user) => {
|
|
58
|
-
|
|
100
|
+
var _a;
|
|
101
|
+
// Load history from sample chats if available
|
|
102
|
+
const history = (_a = data === null || data === void 0 ? void 0 : data.sampleChats[user.uid]) !== null && _a !== void 0 ? _a : [];
|
|
103
|
+
selectUser(user, history);
|
|
59
104
|
setScreen('chat');
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
105
|
+
// Update recent chats
|
|
106
|
+
setRecentChats(prev => {
|
|
107
|
+
const exists = prev.find(r => r.user.uid === user.uid);
|
|
108
|
+
if (exists)
|
|
109
|
+
return prev;
|
|
110
|
+
return [{ id: `rc_${user.uid}`, user, lastMessage: '', lastTime: new Date().toISOString(), unread: 0, isPaused: false }, ...prev];
|
|
111
|
+
});
|
|
112
|
+
}, [data, selectUser]);
|
|
113
|
+
const handleTabChange = useCallback((tab) => {
|
|
114
|
+
setActiveTab(tab);
|
|
115
|
+
setScreen(tab === 'home' ? 'home' : tab === 'chats' ? 'recent-chats' : 'tickets');
|
|
63
116
|
}, []);
|
|
64
|
-
|
|
117
|
+
/* ── Block/Unblock ───────────────────────────────────────────────────── */
|
|
118
|
+
const handleBlock = useCallback(() => {
|
|
119
|
+
if (!activeUser)
|
|
120
|
+
return;
|
|
121
|
+
setBlockedUids(prev => [...prev, activeUser.uid]);
|
|
65
122
|
clearChat();
|
|
66
|
-
setScreen('
|
|
67
|
-
|
|
68
|
-
|
|
123
|
+
setScreen('home');
|
|
124
|
+
setActiveTab('home');
|
|
125
|
+
}, [activeUser, clearChat]);
|
|
126
|
+
const handleUnblock = useCallback((uid) => {
|
|
127
|
+
setBlockedUids(prev => prev.filter(id => id !== uid));
|
|
128
|
+
}, []);
|
|
129
|
+
/* ── Tickets ─────────────────────────────────────────────────────────── */
|
|
130
|
+
const handleRaiseTicket = useCallback((title, desc, priority) => {
|
|
69
131
|
const t = {
|
|
70
|
-
id: `
|
|
71
|
-
title,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
createdAt: new Date(),
|
|
76
|
-
updatedAt: new Date(),
|
|
132
|
+
id: `TKT-${String(Date.now()).slice(-4)}`,
|
|
133
|
+
title, description: desc, status: 'open', priority,
|
|
134
|
+
createdAt: new Date().toISOString(),
|
|
135
|
+
updatedAt: new Date().toISOString(),
|
|
136
|
+
assignedTo: null,
|
|
77
137
|
};
|
|
78
138
|
setTickets(prev => [t, ...prev]);
|
|
79
139
|
}, []);
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
140
|
+
/* ── Pause sync back into recent chats ──────────────────────────────── */
|
|
141
|
+
const handleTogglePause = useCallback(() => {
|
|
142
|
+
togglePause();
|
|
143
|
+
if (activeUser) {
|
|
144
|
+
setRecentChats(prev => prev.map(r => r.user.uid === activeUser.uid ? Object.assign(Object.assign({}, r), { isPaused: !isPaused }) : r));
|
|
145
|
+
}
|
|
146
|
+
}, [togglePause, activeUser, isPaused]);
|
|
147
|
+
/* ── Call ────────────────────────────────────────────────────────────── */
|
|
148
|
+
const handleStartCall = useCallback((withVideo) => {
|
|
149
|
+
if (!activeUser)
|
|
150
|
+
return;
|
|
151
|
+
startCall(activeUser, withVideo);
|
|
152
|
+
setScreen('call');
|
|
153
|
+
}, [activeUser, startCall]);
|
|
154
|
+
const handleEndCall = useCallback(() => {
|
|
155
|
+
endCall();
|
|
156
|
+
setScreen('chat');
|
|
157
|
+
}, [endCall]);
|
|
158
|
+
/* ── Derived ─────────────────────────────────────────────────────────── */
|
|
159
|
+
const isBlocked = activeUser ? blockedUids.includes(activeUser.uid) : false;
|
|
160
|
+
const drawerW = isMaximized ? DRAWER_W_MAX : DRAWER_W_NORMAL;
|
|
161
|
+
const widgetConfig = data === null || data === void 0 ? void 0 : data.widget;
|
|
162
|
+
const primaryColor = theme.primaryColor;
|
|
163
|
+
const allUsers = data ? [...data.developers, ...data.users] : [];
|
|
164
|
+
const filteredUsers = screen === 'user-list'
|
|
165
|
+
? allUsers.filter(u => userListCtx === 'support' ? u.type === 'developer' : u.type === 'user')
|
|
166
|
+
: [];
|
|
167
|
+
const blockedUsers = allUsers.filter(u => blockedUids.includes(u.uid));
|
|
168
|
+
/* Position */
|
|
169
|
+
const posStyle = theme.buttonPosition === 'bottom-left'
|
|
88
170
|
? { left: 24, right: 'auto' }
|
|
89
171
|
: { right: 24, left: 'auto' };
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
172
|
+
const drawerPosStyle = theme.buttonPosition === 'bottom-left'
|
|
173
|
+
? { left: 0, borderRadius: '0 16px 16px 0' }
|
|
174
|
+
: { right: 0, borderRadius: '16px 0 0 16px' };
|
|
175
|
+
/* ── Don't render until mounted (SSR safe) ──────────────────────────── */
|
|
176
|
+
if (!mounted)
|
|
93
177
|
return null;
|
|
94
178
|
return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
|
|
95
179
|
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap');
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
@keyframes cw-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
@keyframes cw-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
.cw-
|
|
109
|
-
.cw-
|
|
110
|
-
.cw-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
180
|
+
|
|
181
|
+
.cw-root * { box-sizing: border-box; font-family: 'DM Sans', 'Segoe UI', sans-serif; }
|
|
182
|
+
|
|
183
|
+
@keyframes cw-slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
|
184
|
+
@keyframes cw-slideOutRight { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }
|
|
185
|
+
@keyframes cw-slideInLeft { from { transform: translateX(-100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
|
186
|
+
@keyframes cw-slideOutLeft { from { transform: translateX(0); opacity: 1; } to { transform: translateX(-100%); opacity: 0; } }
|
|
187
|
+
@keyframes cw-fadeUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
|
188
|
+
@keyframes cw-slideIn { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: translateX(0); } }
|
|
189
|
+
@keyframes cw-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
190
|
+
@keyframes cw-btnPop { from { transform: scale(0.8); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
|
191
|
+
|
|
192
|
+
.cw-scroll::-webkit-scrollbar { width: 4px; }
|
|
193
|
+
.cw-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
194
|
+
.cw-scroll::-webkit-scrollbar-thumb { background: #e0e0e0; border-radius: 4px; }
|
|
195
|
+
|
|
196
|
+
.cw-drawer-enter { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideInLeft' : 'cw-slideInRight'} 0.32s cubic-bezier(0.22,1,0.36,1) both; }
|
|
197
|
+
.cw-drawer-exit { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideOutLeft' : 'cw-slideOutRight'} 0.28s cubic-bezier(0.55,0,1,0.45) both; }
|
|
198
|
+
` }), !isOpen && (_jsxs("button", { className: "cw-root", onClick: openDrawer, "aria-label": theme.buttonLabel, style: Object.assign(Object.assign({ position: 'fixed', bottom: 24, zIndex: 9999 }, posStyle), { display: 'flex', alignItems: 'center', gap: 10, padding: '13px 22px', backgroundColor: theme.buttonColor, color: theme.buttonTextColor, border: 'none', borderRadius: 50, cursor: 'pointer', fontSize: 15, fontWeight: 700, boxShadow: `0 8px 28px ${theme.buttonColor}55`, animation: 'cw-btnPop 0.4s cubic-bezier(0.34,1.56,0.64,1)', transition: 'transform 0.2s, box-shadow 0.2s' }), onMouseEnter: e => {
|
|
199
|
+
e.currentTarget.style.transform = 'scale(1.06) translateY(-2px)';
|
|
200
|
+
e.currentTarget.style.boxShadow = `0 14px 36px ${theme.buttonColor}66`;
|
|
116
201
|
}, onMouseLeave: e => {
|
|
117
|
-
e.currentTarget.style.transform =
|
|
118
|
-
e.currentTarget.style.boxShadow = `0 8px 28px ${
|
|
119
|
-
}, children: isOpen
|
|
120
|
-
|
|
121
|
-
:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
202
|
+
e.currentTarget.style.transform = 'scale(1)';
|
|
203
|
+
e.currentTarget.style.boxShadow = `0 8px 28px ${theme.buttonColor}55`;
|
|
204
|
+
}, children: [_jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z", stroke: theme.buttonTextColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }), _jsx("span", { children: theme.buttonLabel })] })), isOpen && (_jsx("div", { onClick: closeDrawer, style: {
|
|
205
|
+
position: 'fixed', inset: 0, zIndex: 9997,
|
|
206
|
+
backgroundColor: 'rgba(0,0,0,0.35)',
|
|
207
|
+
opacity: closing ? 0 : 1,
|
|
208
|
+
transition: 'opacity 0.3s',
|
|
209
|
+
} })), isOpen && (_jsxs("div", { className: `cw-root ${closing ? 'cw-drawer-exit' : 'cw-drawer-enter'}`, style: Object.assign(Object.assign({ position: 'fixed', top: 0, bottom: 0 }, drawerPosStyle), { zIndex: 9998, width: drawerW, maxWidth: '100vw', backgroundColor: '#fff', boxShadow: theme.buttonPosition === 'bottom-left'
|
|
210
|
+
? '4px 0 40px rgba(0,0,0,0.18)'
|
|
211
|
+
: '-4px 0 40px rgba(0,0,0,0.18)', display: 'flex', flexDirection: 'column', overflow: 'hidden', transition: 'width 0.28s ease' }), children: [cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 16 }, children: [_jsx("div", { style: {
|
|
212
|
+
width: 40, height: 40, borderRadius: '50%',
|
|
213
|
+
border: `3px solid ${primaryColor}30`,
|
|
214
|
+
borderTopColor: primaryColor,
|
|
215
|
+
animation: 'spin 0.8s linear infinite',
|
|
216
|
+
} }), _jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` }), _jsx("p", { style: { fontSize: 14, color: '#7b8fa1' }, children: "Loading chat\u2026" })] })), cfgError && !cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 12, padding: 32, textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\u26A0\uFE0F" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Could not load chat configuration" }), _jsx("p", { style: { fontSize: 13, color: '#7b8fa1', lineHeight: 1.6 }, children: cfgError }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), !cfgLoading && !cfgError && widgetConfig && (_jsxs(_Fragment, { children: [screen !== 'chat' && screen !== 'call' && (_jsxs("div", { style: {
|
|
217
|
+
position: 'absolute', top: 12,
|
|
218
|
+
right: theme.buttonPosition === 'bottom-left' ? 'auto' : 12,
|
|
219
|
+
left: theme.buttonPosition === 'bottom-left' ? 12 : 'auto',
|
|
220
|
+
zIndex: 20, display: 'flex', gap: 6,
|
|
221
|
+
}, children: [_jsx(CornerBtn, { onClick: () => setIsMaximized(m => !m), title: isMaximized ? 'Minimize' : 'Maximize', children: isMaximized
|
|
222
|
+
? _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M8 3v5H3M21 8h-5V3M3 16h5v5M16 21v-5h5", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }) })
|
|
223
|
+
: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M8 3H5a2 2 0 00-2 2v3M21 8V5a2 2 0 00-2-2h-3M3 16v3a2 2 0 002 2h3M16 21h3a2 2 0 002-2v-3", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }) }) }), _jsx(CornerBtn, { onClick: closeDrawer, title: "Close", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }) }) })] })), widgetConfig.status === 'MAINTENANCE' && (_jsx(MaintenanceView, { primaryColor: primaryColor })), widgetConfig.status === 'DISABLE' && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', padding: 32, textAlign: 'center', gap: 12 }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\uD83D\uDD12" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Chat is disabled" }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), widgetConfig.status === 'ACTIVE' && (_jsxs("div", { className: "cw-scroll", style: { flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [screen === 'home' && (_jsx(HomeScreen, { config: widgetConfig, onNavigate: handleCardClick })), screen === 'user-list' && (_jsx(UserListScreen, { context: userListCtx, users: filteredUsers, primaryColor: primaryColor, onBack: () => setScreen('home'), onSelectUser: handleSelectUser })), screen === 'chat' && activeUser && (_jsx(ChatScreen, { activeUser: activeUser, messages: messages, config: widgetConfig, isPaused: isPaused, isReported: isReported, isBlocked: isBlocked, onSend: sendMessage, onBack: () => { clearChat(); setScreen('home'); setActiveTab('home'); }, onClose: closeDrawer, onTogglePause: handleTogglePause, onReport: reportChat, onBlock: handleBlock, onStartCall: handleStartCall })), screen === 'call' && callSession.peer && (_jsx(CallScreen, { session: callSession, localVideoRef: localVideoRef, remoteVideoRef: remoteVideoRef, onEnd: handleEndCall, onToggleMute: toggleMute, onToggleCamera: toggleCamera, primaryColor: primaryColor })), screen === 'recent-chats' && (_jsx(RecentChatsScreen, { chats: recentChats, config: widgetConfig, onSelectChat: handleSelectUser })), screen === 'tickets' && (_jsx(TicketScreen, { tickets: tickets, config: widgetConfig, onRaiseTicket: handleRaiseTicket })), screen === 'block-list' && (_jsx(BlockListScreen, { blockedUsers: blockedUsers, config: widgetConfig, onUnblock: handleUnblock, onBack: () => { setScreen('home'); setActiveTab('home'); } }))] })), widgetConfig.status === 'ACTIVE' &&
|
|
224
|
+
screen !== 'chat' &&
|
|
225
|
+
screen !== 'call' &&
|
|
226
|
+
screen !== 'user-list' &&
|
|
227
|
+
screen !== 'block-list' && (_jsx(BottomTabs, { active: activeTab, onChange: handleTabChange, primaryColor: primaryColor, onBlockList: () => setScreen('block-list') }))] }))] }))] }));
|
|
135
228
|
};
|
|
136
229
|
export default ChatWidget;
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
230
|
+
/* ── Tiny corner button ────────────────────────────────────────────────────── */
|
|
231
|
+
const CornerBtn = ({ onClick, title, children }) => (_jsx("button", { onClick: onClick, title: title, style: {
|
|
232
|
+
width: 26, height: 26, borderRadius: '50%',
|
|
233
|
+
background: 'rgba(0,0,0,0.25)', border: 'none',
|
|
234
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
|
|
235
|
+
}, children: children }));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const EMOJIS = [
|
|
3
|
+
'😀', '😂', '😊', '😍', '🤔', '😎', '😢', '😡',
|
|
4
|
+
'👍', '👎', '👏', '🙏', '🎉', '❤️', '🔥', '✅',
|
|
5
|
+
'🚀', '💡', '⚠️', '🎫',
|
|
6
|
+
];
|
|
7
|
+
export const EmojiPicker = ({ onSelect, onClose, primaryColor }) => (_jsxs("div", { style: {
|
|
8
|
+
position: 'absolute', bottom: '100%', right: 0,
|
|
9
|
+
background: '#fff', borderRadius: 14,
|
|
10
|
+
boxShadow: '0 8px 32px rgba(0,0,0,0.18)',
|
|
11
|
+
padding: '12px', zIndex: 100,
|
|
12
|
+
animation: 'cw-fadeUp 0.18s ease',
|
|
13
|
+
marginBottom: 8,
|
|
14
|
+
}, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }, children: [_jsx("span", { style: { fontSize: 11, fontWeight: 700, color: '#7b8fa1', textTransform: 'uppercase', letterSpacing: '0.06em' }, children: "Emojis" }), _jsx("button", { onClick: onClose, style: { background: 'none', border: 'none', cursor: 'pointer', padding: 2, color: '#7b8fa1', fontSize: 14 }, children: "\u2715" })] }), _jsx("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 4, width: 200 }, children: EMOJIS.map(e => (_jsx("button", { onClick: () => { onSelect(e); onClose(); }, style: {
|
|
15
|
+
background: 'none', border: 'none', cursor: 'pointer',
|
|
16
|
+
fontSize: 22, padding: '6px', borderRadius: 8,
|
|
17
|
+
transition: 'background 0.12s',
|
|
18
|
+
}, onMouseEnter: el => el.currentTarget.style.background = `${primaryColor}15`, onMouseLeave: el => el.currentTarget.style.background = 'none', children: e }, e))) })] }));
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { WidgetConfig, UserListContext } from '../../types';
|
|
3
3
|
interface HomeScreenProps {
|
|
4
|
-
config:
|
|
5
|
-
theme?: ChatWidgetTheme;
|
|
4
|
+
config: WidgetConfig;
|
|
6
5
|
onNavigate: (ctx: UserListContext | 'ticket') => void;
|
|
7
6
|
}
|
|
8
7
|
export declare const HomeScreen: React.FC<HomeScreenProps>;
|
|
@@ -1,71 +1,55 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
export const HomeScreen = ({ config, theme, onNavigate }) => {
|
|
4
|
-
const t = mergeTheme(theme);
|
|
2
|
+
export const HomeScreen = ({ config, onNavigate }) => {
|
|
5
3
|
const showSupport = config.chatType === 'SUPPORT' || config.chatType === 'BOTH';
|
|
6
|
-
const
|
|
4
|
+
const showChat = config.chatType === 'CHAT' || config.chatType === 'BOTH';
|
|
7
5
|
const cards = [
|
|
8
6
|
showSupport && {
|
|
9
7
|
key: 'support',
|
|
8
|
+
icon: '🛠',
|
|
10
9
|
title: 'Need Support',
|
|
11
10
|
subtitle: 'We typically reply in a few minutes',
|
|
12
11
|
onClick: () => onNavigate('support'),
|
|
13
12
|
},
|
|
14
|
-
|
|
13
|
+
showChat && {
|
|
15
14
|
key: 'conversation',
|
|
15
|
+
icon: '💬',
|
|
16
16
|
title: 'New Conversation',
|
|
17
17
|
subtitle: 'With your colleague',
|
|
18
18
|
onClick: () => onNavigate('conversation'),
|
|
19
19
|
},
|
|
20
|
-
// Raise Ticket is always shown
|
|
21
20
|
{
|
|
22
21
|
key: 'ticket',
|
|
22
|
+
icon: '🎫',
|
|
23
23
|
title: 'Raise Ticket',
|
|
24
24
|
subtitle: 'For major changes',
|
|
25
25
|
onClick: () => onNavigate('ticket'),
|
|
26
26
|
},
|
|
27
27
|
].filter(Boolean);
|
|
28
28
|
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: {
|
|
29
|
-
|
|
30
|
-
padding: '
|
|
29
|
+
background: `linear-gradient(145deg, ${config.primaryColor}, ${config.primaryColor}dd)`,
|
|
30
|
+
padding: '36px 24px 52px',
|
|
31
31
|
flexShrink: 0,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}, children: "Hi there \uD83D\uDC4B" }), _jsx("p", { style: { margin: 0, fontSize: '15px', color: 'rgba(255,255,255,0.85)', fontFamily: t.fontFamily }, children: "Need help? start a conversation:" })] }), _jsx("div", { style: {
|
|
40
|
-
flex: 1,
|
|
41
|
-
overflowY: 'auto',
|
|
42
|
-
padding: '0 16px 16px',
|
|
43
|
-
marginTop: '-24px',
|
|
44
|
-
display: 'flex',
|
|
45
|
-
flexDirection: 'column',
|
|
46
|
-
gap: '10px',
|
|
47
|
-
}, children: cards.map((card, i) => (_jsxs("button", { onClick: card.onClick, style: {
|
|
48
|
-
width: '100%',
|
|
49
|
-
background: '#fff',
|
|
50
|
-
border: 'none',
|
|
51
|
-
borderRadius: '14px',
|
|
52
|
-
padding: '18px 20px',
|
|
53
|
-
display: 'flex',
|
|
54
|
-
alignItems: 'center',
|
|
55
|
-
justifyContent: 'space-between',
|
|
56
|
-
cursor: 'pointer',
|
|
57
|
-
textAlign: 'left',
|
|
58
|
-
boxShadow: '0 2px 12px rgba(0,0,0,0.09)',
|
|
59
|
-
transition: 'transform 0.15s ease, box-shadow 0.15s ease',
|
|
32
|
+
position: 'relative',
|
|
33
|
+
overflow: 'hidden',
|
|
34
|
+
}, children: [_jsx("div", { style: { position: 'absolute', top: -40, right: -40, width: 140, height: 140, borderRadius: '50%', background: 'rgba(255,255,255,0.07)' } }), _jsx("div", { style: { position: 'absolute', bottom: -20, left: -20, width: 90, height: 90, borderRadius: '50%', background: 'rgba(255,255,255,0.05)' } }), _jsx("h1", { style: { margin: '0 0 8px', fontSize: 26, fontWeight: 800, color: '#fff', letterSpacing: '-0.03em' }, children: config.welcomeTitle }), _jsx("p", { style: { margin: 0, fontSize: 14, color: 'rgba(255,255,255,0.85)', lineHeight: 1.6 }, children: config.welcomeSubtitle })] }), _jsx("div", { style: { flex: 1, overflowY: 'auto', padding: '0 16px 20px', marginTop: -28, display: 'flex', flexDirection: 'column', gap: 10 }, children: cards.map((card, i) => (_jsxs("button", { onClick: card.onClick, style: {
|
|
35
|
+
width: '100%', background: '#fff', border: 'none', borderRadius: 14,
|
|
36
|
+
padding: '18px 20px', display: 'flex', alignItems: 'center',
|
|
37
|
+
justifyContent: 'space-between', cursor: 'pointer', textAlign: 'left',
|
|
38
|
+
boxShadow: '0 2px 14px rgba(0,0,0,0.10)',
|
|
60
39
|
animation: `cw-fadeUp 0.35s ease both`,
|
|
61
40
|
animationDelay: `${i * 0.08}s`,
|
|
62
|
-
|
|
41
|
+
transition: 'transform 0.15s, box-shadow 0.15s',
|
|
63
42
|
}, onMouseEnter: e => {
|
|
64
43
|
e.currentTarget.style.transform = 'translateY(-2px)';
|
|
65
|
-
e.currentTarget.style.boxShadow = '0
|
|
44
|
+
e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.14)';
|
|
66
45
|
}, onMouseLeave: e => {
|
|
67
46
|
e.currentTarget.style.transform = 'translateY(0)';
|
|
68
|
-
e.currentTarget.style.boxShadow = '0 2px
|
|
69
|
-
}, children: [_jsxs("div", {
|
|
47
|
+
e.currentTarget.style.boxShadow = '0 2px 14px rgba(0,0,0,0.10)';
|
|
48
|
+
}, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 14 }, children: [_jsx("div", { style: {
|
|
49
|
+
width: 44, height: 44, borderRadius: 12,
|
|
50
|
+
backgroundColor: `${config.primaryColor}14`,
|
|
51
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
52
|
+
fontSize: 20, flexShrink: 0,
|
|
53
|
+
}, children: card.icon }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700, fontSize: 15, color: '#1a2332', marginBottom: 2 }, children: card.title }), _jsx("div", { style: { fontSize: 12, color: '#7b8fa1' }, children: card.subtitle })] })] }), _jsx(SendArrow, { color: config.primaryColor })] }, card.key))) })] }));
|
|
70
54
|
};
|
|
71
|
-
const SendArrow = ({ color }) => (_jsx("svg", { width: "
|
|
55
|
+
const SendArrow = ({ color }) => (_jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: _jsx("path", { d: "M5 12h14M12 5l7 7-7 7", stroke: color, strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
export const MaintenanceView = ({ primaryColor
|
|
2
|
+
export const MaintenanceView = ({ primaryColor }) => (_jsxs("div", { style: {
|
|
3
3
|
display: 'flex', flexDirection: 'column', alignItems: 'center',
|
|
4
|
-
justifyContent: 'center', height: '100%', padding: '32px',
|
|
5
|
-
fontFamily, textAlign: 'center', gap: 16,
|
|
4
|
+
justifyContent: 'center', height: '100%', padding: '32px', textAlign: 'center', gap: 16,
|
|
6
5
|
}, children: [_jsx("div", { style: {
|
|
7
6
|
width: 72, height: 72, borderRadius: '50%',
|
|
8
7
|
backgroundColor: `${primaryColor}15`,
|
|
9
8
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
10
|
-
}, children: _jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M12 9v4M12 17h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z", stroke: primaryColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx("h3", { style: { margin: 0, fontSize:
|
|
9
|
+
}, children: _jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M12 9v4M12 17h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z", stroke: primaryColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx("h3", { style: { margin: 0, fontSize: 17, fontWeight: 800, color: '#1a2332', letterSpacing: '-0.02em' }, children: "Under Maintenance" }), _jsx("p", { style: { margin: 0, fontSize: 14, color: '#7b8fa1', lineHeight: 1.6, maxWidth: 220 }, children: "Chat is under maintenance. We'll be back shortly!" }), _jsxs("span", { style: {
|
|
11
10
|
display: 'inline-flex', alignItems: 'center', gap: 6,
|
|
12
11
|
padding: '6px 14px', borderRadius: 20,
|
|
13
12
|
backgroundColor: '#fff3cd', color: '#856404',
|
|
14
|
-
fontSize:
|
|
15
|
-
border: '1px solid #ffc10730',
|
|
13
|
+
fontSize: 12, fontWeight: 700, border: '1px solid #ffc10730',
|
|
16
14
|
}, children: [_jsx("span", { style: { width: 6, height: 6, borderRadius: '50%', backgroundColor: '#ffc107', display: 'inline-block' } }), "Temporarily Unavailable"] })] }));
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ChatUser,
|
|
2
|
+
import { ChatUser, WidgetConfig } from '../../types';
|
|
3
3
|
interface RecentChat {
|
|
4
4
|
id: string;
|
|
5
5
|
user: ChatUser;
|
|
6
6
|
lastMessage: string;
|
|
7
|
-
lastTime:
|
|
7
|
+
lastTime: string;
|
|
8
8
|
unread: number;
|
|
9
|
+
isPaused: boolean;
|
|
9
10
|
}
|
|
10
11
|
interface RecentChatsScreenProps {
|
|
11
12
|
chats: RecentChat[];
|
|
12
|
-
|
|
13
|
+
config: WidgetConfig;
|
|
13
14
|
onSelectChat: (user: ChatUser) => void;
|
|
14
15
|
}
|
|
15
16
|
export declare const RecentChatsScreen: React.FC<RecentChatsScreenProps>;
|