ajaxter-chat 3.0.4 → 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.
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ChatMessage, ChatUser, WidgetConfig } from '../../types';
2
+ import { ChatMessage, ChatUser, WidgetConfig, UserListContext } from '../../types';
3
3
  interface ChatScreenProps {
4
4
  activeUser: ChatUser;
5
5
  messages: ChatMessage[];
@@ -14,6 +14,8 @@ interface ChatScreenProps {
14
14
  onReport: () => void;
15
15
  onBlock: () => void;
16
16
  onStartCall: (withVideo: boolean) => void;
17
+ /** Navigate to support list, colleague list, or tickets (from slide menu) */
18
+ onNavAction: (ctx: UserListContext | 'ticket') => void;
17
19
  }
18
20
  export declare const ChatScreen: React.FC<ChatScreenProps>;
19
21
  export {};
@@ -2,10 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
3
  import { avatarColor, initials, formatTime, formatDate, generateTranscript, downloadText } from '../../utils/chat';
4
4
  import { EmojiPicker } from '../EmojiPicker';
5
- export const ChatScreen = ({ activeUser, messages, config, isPaused, isReported, isBlocked, onSend, onBack, onClose, onTogglePause, onReport, onBlock, onStartCall, }) => {
5
+ import { SlideNavMenu } from '../SlideNavMenu';
6
+ export const ChatScreen = ({ activeUser, messages, config, isPaused, isReported, isBlocked, onSend, onBack, onClose, onTogglePause, onReport, onBlock, onStartCall, onNavAction, }) => {
6
7
  const [text, setText] = useState('');
7
8
  const [showEmoji, setShowEmoji] = useState(false);
8
9
  const [showMenu, setShowMenu] = useState(false);
10
+ const [slideMenuOpen, setSlideMenuOpen] = useState(false);
9
11
  const [isRecording, setIsRecording] = useState(false);
10
12
  const [recordSec, setRecordSec] = useState(0);
11
13
  const [showConfirm, setShowConfirm] = useState(null);
@@ -14,6 +16,7 @@ export const ChatScreen = ({ activeUser, messages, config, isPaused, isReported,
14
16
  const fileRef = useRef(null);
15
17
  const recordTimer = useRef(null);
16
18
  const mediaRecorder = useRef(null);
19
+ const recordChunks = useRef([]);
17
20
  useEffect(() => { var _a; (_a = endRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth' }); }, [messages]);
18
21
  const handleSend = useCallback(() => {
19
22
  var _a;
@@ -29,22 +32,29 @@ export const ChatScreen = ({ activeUser, messages, config, isPaused, isReported,
29
32
  handleSend();
30
33
  }
31
34
  };
32
- // Voice recording
33
35
  const startRecording = async () => {
34
36
  if (isPaused || isBlocked)
35
37
  return;
36
38
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
39
+ recordChunks.current = [];
37
40
  const mr = new MediaRecorder(stream);
38
41
  mediaRecorder.current = mr;
39
- const chunks = [];
40
- mr.ondataavailable = e => chunks.push(e.data);
42
+ mr.ondataavailable = e => { if (e.data.size)
43
+ recordChunks.current.push(e.data); };
41
44
  mr.onstop = () => {
42
45
  stream.getTracks().forEach(t => t.stop());
43
- // In production: upload blob, get URL, then send as voice message
44
- onSend('[Voice Message]', 'voice', { voiceDuration: recordSec });
46
+ const chunks = recordChunks.current;
47
+ if (!chunks.length) {
48
+ setRecordSec(0);
49
+ return;
50
+ }
51
+ const blob = new Blob(chunks, { type: chunks[0] instanceof Blob ? chunks[0].type : 'audio/webm' });
52
+ const voiceUrl = URL.createObjectURL(blob);
53
+ const dur = Math.max(1, recordSec);
54
+ onSend('Voice message', 'voice', { voiceDuration: dur, voiceUrl });
45
55
  setRecordSec(0);
46
56
  };
47
- mr.start();
57
+ mr.start(200);
48
58
  setIsRecording(true);
49
59
  recordTimer.current = setInterval(() => setRecordSec(s => s + 1), 1000);
50
60
  };
@@ -55,25 +65,24 @@ export const ChatScreen = ({ activeUser, messages, config, isPaused, isReported,
55
65
  clearInterval(recordTimer.current);
56
66
  setIsRecording(false);
57
67
  };
58
- // Attachment
59
68
  const handleFileChange = (e) => {
60
69
  var _a;
61
70
  const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
62
71
  if (!file || isPaused || isBlocked)
63
72
  return;
64
- onSend(`[Attachment: ${file.name}]`, 'attachment', {
73
+ const attachmentUrl = URL.createObjectURL(file);
74
+ onSend(file.name, 'attachment', {
65
75
  attachmentName: file.name,
66
76
  attachmentSize: `${(file.size / 1024).toFixed(1)} KB`,
77
+ attachmentUrl,
67
78
  });
68
79
  e.target.value = '';
69
80
  };
70
- // Download transcript
71
81
  const handleTranscript = () => {
72
82
  const content = generateTranscript(messages, activeUser);
73
83
  downloadText(content, `chat-${activeUser.name.replace(/\s+/g, '_')}-${Date.now()}.txt`);
74
84
  setShowMenu(false);
75
85
  };
76
- // Confirm actions
77
86
  const handleConfirm = (action) => {
78
87
  setShowConfirm(null);
79
88
  setShowMenu(false);
@@ -86,50 +95,119 @@ export const ChatScreen = ({ activeUser, messages, config, isPaused, isReported,
86
95
  };
87
96
  const peerAvatar = avatarColor(activeUser.name);
88
97
  const peerInit = initials(activeUser.name);
89
- // Group messages by date
90
98
  const grouped = groupByDate(messages);
91
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideIn 0.22s ease', position: 'relative' }, children: [_jsxs("div", { style: {
99
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideIn 0.22s ease', position: 'relative', overflow: 'hidden' }, children: [_jsx(SlideNavMenu, { open: slideMenuOpen, onClose: () => setSlideMenuOpen(false), primaryColor: config.primaryColor, chatType: config.chatType, onSelect: onNavAction, onBackHome: onBack }), _jsxs("div", { style: {
92
100
  background: `linear-gradient(135deg,${config.primaryColor},${config.primaryColor}cc)`,
93
- padding: '12px 14px', display: 'flex', alignItems: 'center', gap: 10, flexShrink: 0,
94
- }, children: [_jsx("button", { onClick: onBack, style: hdrBtn, children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }), _jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }), _jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" })] }) }), _jsxs("div", { style: { width: 36, height: 36, borderRadius: '50%', backgroundColor: peerAvatar, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontWeight: 700, fontSize: 13, flexShrink: 0, position: 'relative' }, children: [peerInit, _jsx("span", { style: { position: 'absolute', bottom: 0, right: 0, width: 9, height: 9, borderRadius: '50%', border: '2px solid', borderColor: 'transparent', backgroundColor: activeUser.status === 'online' ? '#22c55e' : activeUser.status === 'away' ? '#f59e0b' : '#9ca3af' } })] }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: 14, color: '#fff', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: activeUser.name }), _jsx("div", { style: { fontSize: 11, color: 'rgba(255,255,255,0.8)' }, children: activeUser.designation })] }), _jsx("span", { style: { fontSize: 14, fontWeight: 700, color: '#fff', opacity: 0.9 }, children: "Support" }), config.allowWebCall && (_jsx("button", { onClick: () => onStartCall(false), style: hdrBtn, title: "Voice Call", children: _jsx("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { 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", fill: "#fff" }) }) })), _jsx("button", { style: hdrBtn, title: "Fullscreen", children: _jsx("svg", { width: "16", height: "16", 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", strokeLinecap: "round" }) }) })] }), isPaused && (_jsxs("div", { style: { background: '#fef3c7', padding: '8px 16px', fontSize: 12, fontWeight: 600, color: '#92400e', textAlign: 'center', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6 }, children: ["\u23F8 Chat is paused \u2014 users cannot send messages", _jsx("button", { onClick: onTogglePause, style: { background: '#92400e', color: '#fff', border: 'none', borderRadius: 6, padding: '2px 8px', fontSize: 11, cursor: 'pointer', marginLeft: 4 }, children: "Resume" })] })), isBlocked && (_jsx("div", { style: { background: '#fee2e2', padding: '8px 16px', fontSize: 12, fontWeight: 600, color: '#991b1b', textAlign: 'center', flexShrink: 0 }, children: "\uD83D\uDEAB This user is blocked" })), isReported && (_jsx("div", { style: { background: '#fef3c7', padding: '6px 16px', fontSize: 11, color: '#92400e', textAlign: 'center', flexShrink: 0 }, children: "\u26A0\uFE0F This chat has been reported" })), _jsx("div", { style: { padding: '14px 14px 0', flexShrink: 0 }, children: _jsxs("div", { style: {
95
- background: `linear-gradient(135deg, ${config.primaryColor}, ${config.primaryColor}cc)`,
96
- borderRadius: 12, padding: '14px 16px',
97
- display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer',
98
- }, children: [_jsx("div", { style: { width: 32, height: 32, borderRadius: '50%', background: 'rgba(255,255,255,0.25)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }, children: _jsx("span", { style: { fontSize: 16 }, children: "\u2753" }) }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700, fontSize: 14, color: '#fff' }, children: "Enter your details" }), _jsx("div", { style: { fontSize: 12, color: 'rgba(255,255,255,0.85)' }, children: "Click here to provide your information" })] })] }) }), _jsxs("div", { style: { flex: 1, overflowY: 'auto', padding: '14px', display: 'flex', flexDirection: 'column', gap: 10, background: '#f8f9fc' }, className: "cw-scroll", children: [grouped.map(({ date, msgs }) => (_jsxs(React.Fragment, { children: [_jsx(DateDivider, { label: date }), msgs.map(msg => (_jsx(Bubble, { msg: msg, peer: activeUser, primaryColor: config.primaryColor }, msg.id)))] }, date))), messages.length === 0 && (_jsxs("div", { style: { margin: 'auto', textAlign: 'center', color: '#c4cad4', fontSize: 13 }, children: [_jsx("div", { style: { fontSize: 28, marginBottom: 8 }, children: "\uD83D\uDCAC" }), "Say hello to ", activeUser.name, "!"] })), _jsx("div", { ref: endRef })] }), _jsxs("div", { style: { borderTop: '1px solid #eef0f5', padding: '10px 14px', background: '#fff', flexShrink: 0, position: 'relative' }, children: [showEmoji && config.allowEmoji && (_jsx(EmojiPicker, { primaryColor: config.primaryColor, onSelect: e => setText(t => t + e), onClose: () => setShowEmoji(false) })), isRecording && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8, padding: '6px 12px', background: '#fee2e2', borderRadius: 8 }, children: [_jsx("span", { style: { width: 8, height: 8, borderRadius: '50%', background: '#ef4444', display: 'inline-block', animation: 'cw-pulse 1s infinite' } }), _jsxs("span", { style: { fontSize: 13, color: '#991b1b', fontWeight: 600 }, children: ["Recording ", recordSec, "s"] }), _jsx("button", { onClick: stopRecording, style: { marginLeft: 'auto', background: '#ef4444', color: '#fff', border: 'none', borderRadius: 6, padding: '3px 10px', fontSize: 12, cursor: 'pointer' }, children: "Stop" })] })), _jsxs("div", { style: { display: 'flex', alignItems: 'flex-end', gap: 8 }, children: [_jsx("textarea", { ref: inputRef, value: text, onChange: e => setText(e.target.value), onKeyDown: handleKey, placeholder: isPaused || isBlocked ? 'Chat is unavailable' : 'Message...', disabled: isPaused || isBlocked || isRecording, rows: 1, style: {
99
- flex: 1, resize: 'none', border: '1.5px solid #e5e7eb',
100
- borderRadius: 22, padding: '9px 14px',
101
- fontSize: 14, outline: 'none', lineHeight: 1.5,
102
- maxHeight: 80, overflowY: 'auto', color: '#1a2332',
103
- background: isPaused || isBlocked ? '#f9fafb' : '#fff',
104
- transition: 'border-color 0.2s',
101
+ padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0,
102
+ }, children: [_jsx("button", { type: "button", onClick: () => setSlideMenuOpen(true), style: hdrBtn, "aria-label": "Open menu", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }), _jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }), _jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" })] }) }), _jsxs("div", { style: { width: 36, height: 36, borderRadius: '50%', backgroundColor: peerAvatar, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontWeight: 700, fontSize: 13, flexShrink: 0, position: 'relative' }, children: [peerInit, _jsx("span", { style: { position: 'absolute', bottom: 0, right: 0, width: 9, height: 9, borderRadius: '50%', border: '2px solid', borderColor: 'transparent', backgroundColor: activeUser.status === 'online' ? '#22c55e' : activeUser.status === 'away' ? '#f59e0b' : '#9ca3af' } })] }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: 14, color: '#fff', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: activeUser.name }), _jsx("div", { style: { fontSize: 11, color: 'rgba(255,255,255,0.8)' }, children: activeUser.designation })] }), _jsx("span", { style: { fontSize: 13, fontWeight: 700, color: '#fff', opacity: 0.95, flexShrink: 0 }, children: "Support" }), config.allowWebCall && (_jsx("button", { type: "button", onClick: () => onStartCall(false), style: hdrBtn, title: "Voice Call", children: _jsx("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { 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", fill: "#fff" }) }) })), _jsx("button", { type: "button", onClick: () => setShowMenu(v => !v), style: Object.assign(Object.assign({}, hdrBtn), { background: 'rgba(255,255,255,0.2)' }), title: "More options", "aria-expanded": showMenu, children: _jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "5", r: "1.5", fill: "#fff" }), _jsx("circle", { cx: "12", cy: "12", r: "1.5", fill: "#fff" }), _jsx("circle", { cx: "12", cy: "19", r: "1.5", fill: "#fff" })] }) })] }), showMenu && (_jsxs("div", { style: { position: 'absolute', top: 52, right: 12, zIndex: 120, background: '#fff', borderRadius: 12, boxShadow: '0 8px 30px rgba(0,0,0,0.16)', padding: '6px', minWidth: 200, animation: 'cw-fadeUp 0.18s ease' }, children: [config.allowTranscriptDownload && (_jsx(MenuItem, { icon: "\uD83D\uDCE5", label: "Download Transcript", onClick: handleTranscript })), _jsx(MenuItem, { icon: isPaused ? '▶️' : '⏸', label: isPaused ? 'Resume Chat' : 'Pause Chat', onClick: () => { setShowMenu(false); setShowConfirm('pause'); } }), config.allowReport && !isReported && (_jsx(MenuItem, { icon: "\u26A0\uFE0F", label: "Report Chat", onClick: () => { setShowMenu(false); setShowConfirm('report'); } })), config.allowBlock && activeUser.type === 'user' && !isBlocked && (_jsx(MenuItem, { icon: "\uD83D\uDEAB", label: "Block User", onClick: () => { setShowMenu(false); setShowConfirm('block'); }, danger: true })), _jsx("div", { style: { borderTop: '1px solid #f0f2f5', margin: '4px 0' } }), _jsx(MenuItem, { icon: "\u2715", label: "Close Chat", onClick: onClose })] })), isPaused && (_jsxs("div", { style: { background: '#fef3c7', padding: '8px 16px', fontSize: 12, fontWeight: 600, color: '#92400e', textAlign: 'center', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6 }, children: ["\u23F8 Chat is paused \u2014 users cannot send messages", _jsx("button", { type: "button", onClick: onTogglePause, style: { background: '#92400e', color: '#fff', border: 'none', borderRadius: 6, padding: '2px 8px', fontSize: 11, cursor: 'pointer', marginLeft: 4 }, children: "Resume" })] })), isBlocked && (_jsx("div", { style: { background: '#fee2e2', padding: '8px 16px', fontSize: 12, fontWeight: 600, color: '#991b1b', textAlign: 'center', flexShrink: 0 }, children: "\uD83D\uDEAB This user is blocked" })), isReported && (_jsx("div", { style: { background: '#fef3c7', padding: '6px 16px', fontSize: 11, color: '#92400e', textAlign: 'center', flexShrink: 0 }, children: "\u26A0\uFE0F This chat has been reported" })), _jsxs("div", { style: { flex: 1, overflowY: 'auto', padding: '14px', display: 'flex', flexDirection: 'column', gap: 10, background: '#f8f9fc' }, className: "cw-scroll", children: [grouped.map(({ date, msgs }) => (_jsxs(React.Fragment, { children: [_jsx(DateDivider, { label: date }), msgs.map(msg => (_jsx(Bubble, { msg: msg, primaryColor: config.primaryColor }, msg.id)))] }, date))), messages.length === 0 && (_jsxs("div", { style: { margin: 'auto', textAlign: 'center', color: '#c4cad4', fontSize: 13 }, children: [_jsx("div", { style: { fontSize: 28, marginBottom: 8 }, children: "\uD83D\uDCAC" }), "Say hello to ", activeUser.name, "!"] })), _jsx("div", { ref: endRef })] }), _jsxs("div", { style: { borderTop: '1px solid #eef0f5', padding: '10px 12px 8px', background: '#fff', flexShrink: 0, position: 'relative' }, children: [showEmoji && config.allowEmoji && (_jsx(EmojiPicker, { primaryColor: config.primaryColor, onSelect: e => setText(t => t + e), onClose: () => setShowEmoji(false) })), isRecording && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8, padding: '8px 12px', background: '#fee2e2', borderRadius: 10 }, children: [_jsx("span", { style: { width: 8, height: 8, borderRadius: '50%', background: '#ef4444', display: 'inline-block', animation: 'cw-pulse 1s infinite' } }), _jsxs("span", { style: { fontSize: 13, color: '#991b1b', fontWeight: 600 }, children: ["Recording ", recordSec, "s"] }), _jsx("button", { type: "button", onClick: stopRecording, style: { marginLeft: 'auto', background: '#ef4444', color: '#fff', border: 'none', borderRadius: 6, padding: '4px 12px', fontSize: 12, cursor: 'pointer', fontWeight: 600 }, children: "Stop & send" })] })), _jsxs("div", { style: {
103
+ border: `1.5px solid ${isPaused || isBlocked ? '#e5e7eb' : '#bfdbfe'}`,
104
+ borderRadius: 16,
105
+ padding: '10px 12px 8px',
106
+ background: isPaused || isBlocked ? '#f9fafb' : '#fff',
107
+ }, children: [_jsx("textarea", { ref: inputRef, value: text, onChange: e => setText(e.target.value), onKeyDown: handleKey, placeholder: isPaused || isBlocked ? 'Chat is unavailable' : 'Compose your message…', disabled: isPaused || isBlocked || isRecording, rows: 2, style: {
108
+ width: '100%',
109
+ resize: 'none',
110
+ border: 'none',
111
+ outline: 'none',
112
+ fontSize: 14,
113
+ lineHeight: 1.45,
114
+ color: '#1a2332',
115
+ background: 'transparent',
116
+ maxHeight: 88,
117
+ overflowY: 'auto',
105
118
  fontFamily: 'inherit',
106
- }, onFocus: e => (e.target.style.borderColor = config.primaryColor), onBlur: e => (e.target.style.borderColor = '#e5e7eb') }), config.allowEmoji && (_jsx(ActionBtn, { onClick: () => setShowEmoji(v => !v), title: "Emoji", children: _jsxs("svg", { width: "19", height: "19", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#9ca3af", strokeWidth: "1.8" }), _jsx("path", { d: "M8 14s1.5 2 4 2 4-2 4-2", stroke: "#9ca3af", strokeWidth: "1.8", strokeLinecap: "round" }), _jsx("circle", { cx: "9", cy: "9", r: "1", fill: "#9ca3af" }), _jsx("circle", { cx: "15", cy: "9", r: "1", fill: "#9ca3af" })] }) })), config.allowAttachment && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileRef, type: "file", style: { display: 'none' }, onChange: handleFileChange }), _jsx(ActionBtn, { onClick: () => { var _a; return (_a = fileRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, title: "Attach file", children: _jsx("svg", { width: "19", height: "19", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66L9.41 17.41a2 2 0 01-2.83-2.83l8.49-8.48", stroke: "#9ca3af", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })), config.allowVoiceMessage && !isRecording && (_jsx(ActionBtn, { onClick: startRecording, title: "Voice message", children: _jsxs("svg", { width: "19", height: "19", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z", stroke: "#9ca3af", strokeWidth: "1.8", strokeLinecap: "round" }), _jsx("path", { d: "M19 10v2a7 7 0 01-14 0v-2M12 19v4M8 23h8", stroke: "#9ca3af", strokeWidth: "1.8", strokeLinecap: "round" })] }) })), text.trim() && (_jsx("button", { onClick: handleSend, style: {
107
- width: 36, height: 36, borderRadius: '50%', backgroundColor: config.primaryColor,
108
- border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
109
- transition: 'transform 0.15s',
110
- }, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M22 2L11 13M22 2L15 22L11 13L2 9L22 2Z", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] })] }), _jsxs("div", { style: { position: 'absolute', top: 12, right: 52, zIndex: 50 }, children: [_jsx("button", { onClick: () => setShowMenu(v => !v), style: Object.assign(Object.assign({}, hdrBtn), { background: 'rgba(255,255,255,0.18)' }), title: "More options", children: _jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "5", r: "1.5", fill: "#fff" }), _jsx("circle", { cx: "12", cy: "12", r: "1.5", fill: "#fff" }), _jsx("circle", { cx: "12", cy: "19", r: "1.5", fill: "#fff" })] }) }), showMenu && (_jsxs("div", { style: {
111
- position: 'absolute', top: '100%', right: 0, marginTop: 4,
112
- background: '#fff', borderRadius: 12,
113
- boxShadow: '0 8px 30px rgba(0,0,0,0.16)',
114
- padding: '6px', minWidth: 200, zIndex: 200,
115
- animation: 'cw-fadeUp 0.18s ease',
116
- }, children: [config.allowTranscriptDownload && (_jsx(MenuItem, { icon: "\uD83D\uDCE5", label: "Download Transcript", onClick: handleTranscript })), _jsx(MenuItem, { icon: isPaused ? '▶️' : '⏸', label: isPaused ? 'Resume Chat' : 'Pause Chat', onClick: () => { setShowMenu(false); setShowConfirm('pause'); } }), config.allowReport && !isReported && (_jsx(MenuItem, { icon: "\u26A0\uFE0F", label: "Report Chat", onClick: () => { setShowMenu(false); setShowConfirm('report'); } })), config.allowBlock && activeUser.type === 'user' && !isBlocked && (_jsx(MenuItem, { icon: "\uD83D\uDEAB", label: "Block User", onClick: () => { setShowMenu(false); setShowConfirm('block'); }, danger: true })), _jsx("div", { style: { borderTop: '1px solid #f0f2f5', margin: '4px 0' } }), _jsx(MenuItem, { icon: "\u2715", label: "Close Chat", onClick: onClose })] }))] }), showConfirm && (_jsx("div", { style: {
119
+ marginBottom: 8,
120
+ } }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 2 }, children: [config.allowEmoji && (_jsx(ActionBtn, { onClick: () => setShowEmoji(v => !v), title: "Emoji", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#94a3b8", strokeWidth: "1.8" }), _jsx("path", { d: "M8 14s1.5 2 4 2 4-2 4-2", stroke: "#94a3b8", strokeWidth: "1.8", strokeLinecap: "round" }), _jsx("circle", { cx: "9", cy: "9", r: "1", fill: "#94a3b8" }), _jsx("circle", { cx: "15", cy: "9", r: "1", fill: "#94a3b8" })] }) })), config.allowAttachment && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileRef, type: "file", style: { display: 'none' }, onChange: handleFileChange }), _jsx(ActionBtn, { onClick: () => { var _a; return (_a = fileRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, title: "Attach file", children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66L9.41 17.41a2 2 0 01-2.83-2.83l8.49-8.48", stroke: "#94a3b8", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })), config.allowVoiceMessage && !isRecording && (_jsx(ActionBtn, { onClick: startRecording, title: "Voice message", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z", stroke: "#94a3b8", strokeWidth: "1.8", strokeLinecap: "round" }), _jsx("path", { d: "M19 10v2a7 7 0 01-14 0v-2M12 19v4M8 23h8", stroke: "#94a3b8", strokeWidth: "1.8", strokeLinecap: "round" })] }) }))] }), _jsx("button", { type: "button", onClick: handleSend, disabled: !text.trim() || isPaused || isBlocked || isRecording, style: {
121
+ width: 36,
122
+ height: 36,
123
+ borderRadius: '50%',
124
+ backgroundColor: text.trim() && !isPaused && !isBlocked ? config.primaryColor : '#e2e8f0',
125
+ border: 'none',
126
+ cursor: text.trim() && !isPaused && !isBlocked ? 'pointer' : 'default',
127
+ display: 'flex',
128
+ alignItems: 'center',
129
+ justifyContent: 'center',
130
+ flexShrink: 0,
131
+ transition: 'background 0.15s',
132
+ }, title: "Send", children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M22 2L11 13M22 2L15 22L11 13L2 9L22 2Z", stroke: text.trim() && !isPaused && !isBlocked ? '#fff' : '#94a3b8', strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] }), (config.footerPoweredBy || config.branch) && (_jsxs("p", { style: { margin: '10px 0 0', textAlign: 'center', fontSize: 12, color: '#94a3b8' }, children: [config.footerPoweredBy, config.footerPoweredBy && config.branch ? ' · ' : '', config.branch && _jsx("span", { style: { fontWeight: 600, color: '#64748b' }, children: config.branch })] }))] }), showConfirm && (_jsx("div", { style: {
117
133
  position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.45)',
118
134
  display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 300,
119
135
  borderRadius: 'inherit',
120
- }, children: _jsxs("div", { style: { background: '#fff', borderRadius: 16, padding: '24px 20px', width: 280, boxShadow: '0 16px 48px rgba(0,0,0,0.22)', animation: 'cw-fadeUp 0.2s ease' }, children: [_jsxs("div", { style: { fontWeight: 800, fontSize: 16, color: '#1a2332', marginBottom: 8 }, children: [showConfirm === 'pause' && (isPaused ? 'Resume Chat?' : 'Pause Chat?'), showConfirm === 'report' && 'Report this chat?', showConfirm === 'block' && 'Block this user?'] }), _jsxs("p", { style: { fontSize: 13, color: '#7b8fa1', lineHeight: 1.6, margin: '0 0 18px' }, children: [showConfirm === 'pause' && (isPaused ? 'The user will be able to send messages again.' : 'The user will not be able to send new messages.'), showConfirm === 'report' && 'This chat will be flagged for review by the admin team.', showConfirm === 'block' && 'This user will be blocked and added to your block list. You can unblock them later.'] }), _jsxs("div", { style: { display: 'flex', gap: 10 }, children: [_jsx("button", { onClick: () => setShowConfirm(null), style: { flex: 1, padding: '9px', borderRadius: 10, border: '1.5px solid #e5e7eb', background: '#fff', cursor: 'pointer', fontSize: 13, fontWeight: 600, color: '#374151' }, children: "Cancel" }), _jsx("button", { onClick: () => handleConfirm(showConfirm), style: {
136
+ }, children: _jsxs("div", { style: { background: '#fff', borderRadius: 16, padding: '24px 20px', width: 280, boxShadow: '0 16px 48px rgba(0,0,0,0.22)', animation: 'cw-fadeUp 0.2s ease' }, children: [_jsxs("div", { style: { fontWeight: 800, fontSize: 16, color: '#1a2332', marginBottom: 8 }, children: [showConfirm === 'pause' && (isPaused ? 'Resume Chat?' : 'Pause Chat?'), showConfirm === 'report' && 'Report this chat?', showConfirm === 'block' && 'Block this user?'] }), _jsxs("p", { style: { fontSize: 13, color: '#7b8fa1', lineHeight: 1.6, margin: '0 0 18px' }, children: [showConfirm === 'pause' && (isPaused ? 'The user will be able to send messages again.' : 'The user will not be able to send new messages.'), showConfirm === 'report' && 'This chat will be flagged for review by the admin team.', showConfirm === 'block' && 'This user will be blocked and added to your block list. You can unblock them later.'] }), _jsxs("div", { style: { display: 'flex', gap: 10 }, children: [_jsx("button", { type: "button", onClick: () => setShowConfirm(null), style: { flex: 1, padding: '9px', borderRadius: 10, border: '1.5px solid #e5e7eb', background: '#fff', cursor: 'pointer', fontSize: 13, fontWeight: 600, color: '#374151' }, children: "Cancel" }), _jsx("button", { type: "button", onClick: () => handleConfirm(showConfirm), style: {
121
137
  flex: 1, padding: '9px', borderRadius: 10, border: 'none',
122
138
  background: showConfirm === 'block' ? '#ef4444' : config.primaryColor,
123
139
  color: '#fff', cursor: 'pointer', fontSize: 13, fontWeight: 700,
124
140
  }, children: "Confirm" })] })] }) }))] }));
125
141
  };
126
- // ── Sub-components ─────────────────────────────────────────────────────────────
127
- const Bubble = ({ msg, peer, primaryColor }) => {
142
+ const VoiceRow = ({ msg, isMe, primaryColor }) => {
128
143
  var _a;
144
+ const audioRef = useRef(null);
145
+ const [playing, setPlaying] = useState(false);
146
+ const [current, setCurrent] = useState(0);
147
+ const [dur, setDur] = useState((_a = msg.voiceDuration) !== null && _a !== void 0 ? _a : 0);
148
+ const url = msg.voiceUrl;
149
+ useEffect(() => {
150
+ const a = audioRef.current;
151
+ if (!a || !url)
152
+ return;
153
+ const onMeta = () => setDur(a.duration || msg.voiceDuration || 0);
154
+ const onTime = () => setCurrent(a.currentTime);
155
+ a.addEventListener('loadedmetadata', onMeta);
156
+ a.addEventListener('timeupdate', onTime);
157
+ return () => {
158
+ a.removeEventListener('loadedmetadata', onMeta);
159
+ a.removeEventListener('timeupdate', onTime);
160
+ };
161
+ }, [url, msg.voiceDuration]);
162
+ const toggle = () => {
163
+ const a = audioRef.current;
164
+ if (!a)
165
+ return;
166
+ if (playing) {
167
+ a.pause();
168
+ setPlaying(false);
169
+ }
170
+ else {
171
+ void a.play().then(() => setPlaying(true)).catch(() => { });
172
+ }
173
+ };
174
+ const pct = dur > 0 ? Math.min(100, (current / dur) * 100) : 0;
175
+ const timeLabel = fmtTime(Math.floor(current)) + ' / ' + fmtTime(Math.floor(dur || msg.voiceDuration || 0));
176
+ if (!url) {
177
+ return (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { fontSize: 13 }, children: "\uD83C\uDFA4" }), _jsxs("span", { style: { fontSize: 13 }, children: ["Voice message", msg.voiceDuration ? ` · ${msg.voiceDuration}s` : ''] })] }));
178
+ }
179
+ return (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10, minWidth: 200 }, children: [url && (_jsx("audio", { ref: audioRef, src: url, preload: "metadata", onPlay: () => setPlaying(true), onPause: () => setPlaying(false), onEnded: () => { setPlaying(false); setCurrent(0); } })), _jsx("button", { type: "button", onClick: toggle, style: {
180
+ width: 36,
181
+ height: 36,
182
+ borderRadius: '50%',
183
+ border: 'none',
184
+ background: isMe ? 'rgba(255,255,255,0.95)' : '#fff',
185
+ color: isMe ? primaryColor : primaryColor,
186
+ cursor: 'pointer',
187
+ display: 'flex',
188
+ alignItems: 'center',
189
+ justifyContent: 'center',
190
+ flexShrink: 0,
191
+ boxShadow: '0 1px 4px rgba(0,0,0,0.12)',
192
+ }, "aria-label": playing ? 'Pause' : 'Play', children: playing ? (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: [_jsx("rect", { x: "6", y: "4", width: "4", height: "16" }), _jsx("rect", { x: "14", y: "4", width: "4", height: "16" })] })) : (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M8 5v14l11-7z" }) })) }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { height: 4, borderRadius: 2, background: isMe ? 'rgba(255,255,255,0.35)' : '#e2e8f0', overflow: 'hidden' }, children: _jsx("div", { style: { width: `${pct}%`, height: '100%', background: isMe ? '#fff' : primaryColor, borderRadius: 2, transition: 'width 0.1s linear' } }) }), _jsx("div", { style: { fontSize: 11, marginTop: 4, opacity: 0.9 }, children: timeLabel })] })] }));
193
+ };
194
+ const AttachmentRow = ({ msg, isMe, primaryColor }) => {
195
+ var _a;
196
+ const name = (_a = msg.attachmentName) !== null && _a !== void 0 ? _a : 'File';
197
+ const href = msg.attachmentUrl;
198
+ return (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }, children: [_jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: _jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66L9.41 17.41a2 2 0 01-2.83-2.83l8.49-8.48", stroke: isMe ? '#fff' : '#334155', strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: 14, wordBreak: 'break-word' }, children: name }), msg.attachmentSize && _jsx("div", { style: { fontSize: 11, opacity: 0.8 }, children: msg.attachmentSize })] }), href && (_jsx("a", { href: href, download: name, style: {
199
+ fontSize: 12,
200
+ fontWeight: 700,
201
+ color: isMe ? '#fff' : primaryColor,
202
+ textDecoration: 'underline',
203
+ whiteSpace: 'nowrap',
204
+ }, children: "Download" }))] }));
205
+ };
206
+ const Bubble = ({ msg, primaryColor }) => {
129
207
  const isMe = msg.senderId === 'me';
130
- const content = msg.type === 'voice' ? (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z", stroke: isMe ? '#fff' : '#374151', strokeWidth: "2", strokeLinecap: "round" }), _jsx("path", { d: "M19 10v2a7 7 0 01-14 0v-2M12 19v4", stroke: isMe ? '#fff' : '#374151', strokeWidth: "2", strokeLinecap: "round" })] }), _jsxs("span", { children: ["Voice Message ", msg.voiceDuration ? `· ${msg.voiceDuration}s` : ''] })] })) : msg.type === 'attachment' ? (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66L9.41 17.41a2 2 0 01-2.83-2.83l8.49-8.48", stroke: isMe ? '#fff' : '#374151', strokeWidth: "2", strokeLinecap: "round" }) }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 600 }, children: (_a = msg.attachmentName) !== null && _a !== void 0 ? _a : 'File' }), msg.attachmentSize && _jsx("div", { style: { fontSize: 11, opacity: 0.75 }, children: msg.attachmentSize })] })] })) : _jsx("span", { children: msg.text });
208
+ const content = msg.type === 'voice' ? (_jsx(VoiceRow, { msg: msg, isMe: isMe, primaryColor: primaryColor })) : msg.type === 'attachment' ? (_jsx(AttachmentRow, { msg: msg, isMe: isMe, primaryColor: primaryColor })) : (_jsx("span", { children: msg.text }));
131
209
  return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: isMe ? 'flex-end' : 'flex-start', gap: 3 }, children: [_jsx("div", { style: {
132
- maxWidth: '76%', padding: '10px 13px',
210
+ maxWidth: '85%', padding: '10px 13px',
133
211
  borderRadius: isMe ? '18px 18px 4px 18px' : '18px 18px 18px 4px',
134
212
  backgroundColor: isMe ? primaryColor : '#fff',
135
213
  color: isMe ? '#fff' : '#1a2332',
@@ -138,24 +216,23 @@ const Bubble = ({ msg, peer, primaryColor }) => {
138
216
  wordBreak: 'break-word',
139
217
  }, children: content }), _jsx("span", { style: { fontSize: 11, color: '#b0bec5', padding: '0 4px' }, children: formatTime(msg.timestamp) })] }));
140
218
  };
141
- const DateDivider = ({ label }) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10, margin: '4px 0' }, children: [_jsx("div", { style: { flex: 1, height: 1, background: '#e5e7eb' } }), _jsx("span", { style: { fontSize: 11, fontWeight: 600, color: '#9ca3af', whiteSpace: 'nowrap' }, children: label }), _jsx("div", { style: { flex: 1, height: 1, background: '#e5e7eb' } })] }));
142
- const MenuItem = ({ icon, label, onClick, danger }) => (_jsxs("button", { onClick: onClick, style: {
219
+ const DateDivider = ({ label }) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10, margin: '4px 0' }, children: [_jsx("div", { style: { flex: 1, height: 1, background: '#e5e7eb' } }), _jsx("span", { style: { fontSize: 11, fontWeight: 600, color: '#64748b', whiteSpace: 'nowrap' }, children: label }), _jsx("div", { style: { flex: 1, height: 1, background: '#e5e7eb' } })] }));
220
+ const MenuItem = ({ icon, label, onClick, danger }) => (_jsxs("button", { type: "button", onClick: onClick, style: {
143
221
  display: 'flex', alignItems: 'center', gap: 10, width: '100%', padding: '9px 12px',
144
222
  background: 'none', border: 'none', borderRadius: 8, cursor: 'pointer', textAlign: 'left',
145
223
  fontSize: 13, fontWeight: 600, color: danger ? '#ef4444' : '#374151',
146
224
  transition: 'background 0.12s',
147
225
  }, onMouseEnter: e => e.currentTarget.style.background = danger ? '#fee2e2' : '#f3f4f6', onMouseLeave: e => e.currentTarget.style.background = 'none', children: [_jsx("span", { children: icon }), " ", label] }));
148
- const ActionBtn = ({ onClick, title, children }) => (_jsx("button", { onClick: onClick, title: title, style: {
149
- background: 'none', border: 'none', cursor: 'pointer', padding: '7px',
226
+ const ActionBtn = ({ onClick, title, children }) => (_jsx("button", { type: "button", onClick: onClick, title: title, style: {
227
+ background: 'none', border: 'none', cursor: 'pointer', padding: '8px',
150
228
  borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
151
229
  transition: 'background 0.13s',
152
- }, onMouseEnter: e => e.currentTarget.style.background = '#f3f4f6', onMouseLeave: e => e.currentTarget.style.background = 'none', children: children }));
230
+ }, onMouseEnter: e => e.currentTarget.style.background = '#f1f5f9', onMouseLeave: e => e.currentTarget.style.background = 'none', children: children }));
153
231
  const hdrBtn = {
154
232
  background: 'rgba(255,255,255,0.2)', border: 'none', borderRadius: '50%',
155
233
  width: 32, height: 32, display: 'flex', alignItems: 'center', justifyContent: 'center',
156
234
  cursor: 'pointer', flexShrink: 0,
157
235
  };
158
- // Group messages by date
159
236
  function groupByDate(messages) {
160
237
  const map = new Map();
161
238
  messages.forEach(m => {
@@ -166,3 +243,8 @@ function groupByDate(messages) {
166
243
  });
167
244
  return Array.from(map.entries()).map(([date, msgs]) => ({ date, msgs }));
168
245
  }
246
+ function fmtTime(sec) {
247
+ const m = Math.floor(sec / 60);
248
+ const s = Math.max(0, sec % 60);
249
+ return `${m}:${String(s).padStart(2, '0')}`;
250
+ }
@@ -96,6 +96,17 @@ export const ChatWidget = ({ theme: localTheme }) => {
96
96
  setScreen('user-list');
97
97
  }
98
98
  }, []);
99
+ const handleNavFromMenu = useCallback((ctx) => {
100
+ clearChat();
101
+ if (ctx === 'ticket') {
102
+ setActiveTab('tickets');
103
+ setScreen('tickets');
104
+ }
105
+ else {
106
+ setUserListCtx(ctx);
107
+ setScreen('user-list');
108
+ }
109
+ }, [clearChat]);
99
110
  const handleSelectUser = useCallback((user) => {
100
111
  var _a;
101
112
  // Load history from sample chats if available
@@ -220,7 +231,7 @@ export const ChatWidget = ({ theme: localTheme }) => {
220
231
  zIndex: 20, display: 'flex', gap: 6,
221
232
  }, children: [_jsx(CornerBtn, { onClick: () => setIsMaximized(m => !m), title: isMaximized ? 'Minimize' : 'Maximize', children: isMaximized
222
233
  ? _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' &&
234
+ : _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, tickets: tickets })), 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, onNavAction: handleNavFromMenu })), 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
235
  screen !== 'chat' &&
225
236
  screen !== 'call' &&
226
237
  screen !== 'user-list' &&
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
- import { WidgetConfig, UserListContext } from '../../types';
2
+ import { WidgetConfig, UserListContext, Ticket } from '../../types';
3
3
  interface HomeScreenProps {
4
4
  config: WidgetConfig;
5
5
  onNavigate: (ctx: UserListContext | 'ticket') => void;
6
+ tickets: Ticket[];
6
7
  }
7
8
  export declare const HomeScreen: React.FC<HomeScreenProps>;
8
9
  export {};
@@ -1,55 +1,132 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export const HomeScreen = ({ config, onNavigate }) => {
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { SlideNavMenu } from '../SlideNavMenu';
4
+ export const HomeScreen = ({ config, onNavigate, tickets }) => {
5
+ const [menuOpen, setMenuOpen] = useState(false);
3
6
  const showSupport = config.chatType === 'SUPPORT' || config.chatType === 'BOTH';
4
7
  const showChat = config.chatType === 'CHAT' || config.chatType === 'BOTH';
5
- const cards = [
6
- showSupport && {
7
- key: 'support',
8
- icon: '🛠',
9
- title: 'Need Support',
10
- subtitle: 'We typically reply in a few minutes',
11
- onClick: () => onNavigate('support'),
12
- },
13
- showChat && {
14
- key: 'conversation',
15
- icon: '💬',
16
- title: 'New Conversation',
17
- subtitle: 'With your colleague',
18
- onClick: () => onNavigate('conversation'),
19
- },
20
- {
21
- key: 'ticket',
22
- icon: '🎫',
23
- title: 'Raise Ticket',
24
- subtitle: 'For major changes',
25
- onClick: () => onNavigate('ticket'),
26
- },
27
- ].filter(Boolean);
28
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: {
29
- background: `linear-gradient(145deg, ${config.primaryColor}, ${config.primaryColor}dd)`,
30
- padding: '36px 24px 52px',
8
+ const continueItems = tickets.slice(0, 2);
9
+ const handleCallUs = () => {
10
+ var _a;
11
+ const raw = (_a = config.supportPhone) === null || _a === void 0 ? void 0 : _a.trim();
12
+ if (!raw)
13
+ return;
14
+ window.location.href = `tel:${raw.replace(/\s/g, '')}`;
15
+ };
16
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', position: 'relative', overflow: 'hidden', background: '#fafbfc' }, children: [_jsx(SlideNavMenu, { open: menuOpen, onClose: () => setMenuOpen(false), primaryColor: config.primaryColor, chatType: config.chatType, onSelect: onNavigate }), _jsx("div", { style: {
31
17
  flexShrink: 0,
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)',
39
- animation: `cw-fadeUp 0.35s ease both`,
40
- animationDelay: `${i * 0.08}s`,
41
- transition: 'transform 0.15s, box-shadow 0.15s',
42
- }, onMouseEnter: e => {
43
- e.currentTarget.style.transform = 'translateY(-2px)';
44
- e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.14)';
45
- }, onMouseLeave: e => {
46
- e.currentTarget.style.transform = 'translateY(0)';
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))) })] }));
18
+ padding: '14px 16px 10px',
19
+ display: 'flex',
20
+ alignItems: 'center',
21
+ gap: 12,
22
+ background: '#fff',
23
+ borderBottom: '1px solid #eef0f5',
24
+ }, children: _jsxs("button", { type: "button", "aria-label": "Open menu", onClick: () => setMenuOpen(true), style: {
25
+ width: 40,
26
+ height: 40,
27
+ borderRadius: 10,
28
+ border: 'none',
29
+ background: '#f1f5f9',
30
+ cursor: 'pointer',
31
+ display: 'flex',
32
+ flexDirection: 'column',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ gap: 5,
36
+ flexShrink: 0,
37
+ }, children: [_jsx("span", { style: { width: 18, height: 2, background: '#334155', borderRadius: 1 } }), _jsx("span", { style: { width: 18, height: 2, background: '#334155', borderRadius: 1 } }), _jsx("span", { style: { width: 18, height: 2, background: '#334155', borderRadius: 1 } })] }) }), _jsxs("div", { className: "cw-scroll", style: { flex: 1, overflowY: 'auto', padding: '20px 18px 28px' }, children: [_jsx("h1", { style: {
38
+ margin: '0 0 8px',
39
+ fontSize: 24,
40
+ fontWeight: 800,
41
+ color: '#0f172a',
42
+ letterSpacing: '-0.03em',
43
+ lineHeight: 1.2,
44
+ }, children: config.welcomeTitle }), _jsx("p", { style: { margin: '0 0 28px', fontSize: 14, color: '#64748b', lineHeight: 1.55 }, children: config.welcomeSubtitle }), _jsx("h2", { style: { margin: '0 0 12px', fontSize: 15, fontWeight: 800, color: '#0f172a' }, children: "Continue Conversations" }), _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 28 }, children: continueItems.length > 0 ? (continueItems.map(t => (_jsx("button", { type: "button", onClick: () => onNavigate('ticket'), style: {
45
+ width: '100%',
46
+ textAlign: 'left',
47
+ padding: '14px 16px',
48
+ borderRadius: 14,
49
+ border: 'none',
50
+ background: '#e0f2fe',
51
+ color: '#0369a1',
52
+ fontSize: 14,
53
+ fontWeight: 600,
54
+ cursor: 'pointer',
55
+ boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
56
+ }, children: t.title }, t.id)))) : (_jsxs(_Fragment, { children: [_jsx("div", { style: {
57
+ padding: '14px 16px',
58
+ borderRadius: 14,
59
+ background: '#e0f2fe',
60
+ color: '#64748b',
61
+ fontSize: 14,
62
+ fontWeight: 500,
63
+ }, children: "No open tickets yet" }), _jsx("div", { style: {
64
+ padding: '14px 16px',
65
+ borderRadius: 14,
66
+ background: '#e0f2fe',
67
+ color: '#64748b',
68
+ fontSize: 14,
69
+ fontWeight: 500,
70
+ }, children: "Start via Raise ticket below" })] })) }), _jsx("h2", { style: { margin: '0 0 12px', fontSize: 15, fontWeight: 800, color: '#0f172a' }, children: "Talk to our experts" }), showSupport && (_jsxs("button", { type: "button", onClick: () => onNavigate('support'), style: {
71
+ width: '100%',
72
+ display: 'flex',
73
+ alignItems: 'center',
74
+ justifyContent: 'center',
75
+ gap: 10,
76
+ padding: '14px 18px',
77
+ marginBottom: showChat ? 10 : 14,
78
+ borderRadius: 14,
79
+ border: 'none',
80
+ background: '#ede9fe',
81
+ color: '#5b21b6',
82
+ fontSize: 15,
83
+ fontWeight: 700,
84
+ cursor: 'pointer',
85
+ boxShadow: '0 2px 8px rgba(91,33,182,0.12)',
86
+ }, children: [_jsx("span", { style: { fontSize: 18 }, children: "\uD83D\uDC64" }), "Support"] })), showChat && showSupport && (_jsx("button", { type: "button", onClick: () => onNavigate('conversation'), style: {
87
+ width: '100%',
88
+ padding: '12px 16px',
89
+ marginBottom: 14,
90
+ borderRadius: 12,
91
+ border: '1.5px solid #e9d5ff',
92
+ background: '#fff',
93
+ color: '#6d28d9',
94
+ fontSize: 14,
95
+ fontWeight: 600,
96
+ cursor: 'pointer',
97
+ }, children: "New Conversation" })), showChat && !showSupport && (_jsxs("button", { type: "button", onClick: () => onNavigate('conversation'), style: {
98
+ width: '100%',
99
+ display: 'flex',
100
+ alignItems: 'center',
101
+ justifyContent: 'center',
102
+ gap: 10,
103
+ padding: '14px 18px',
104
+ marginBottom: 14,
105
+ borderRadius: 14,
106
+ border: 'none',
107
+ background: '#ede9fe',
108
+ color: '#5b21b6',
109
+ fontSize: 15,
110
+ fontWeight: 700,
111
+ cursor: 'pointer',
112
+ }, children: [_jsx("span", { style: { fontSize: 18 }, children: "\uD83D\uDCAC" }), "New Conversation"] })), _jsxs("div", { style: {
113
+ borderRadius: 18,
114
+ padding: '22px 20px 20px',
115
+ background: 'linear-gradient(145deg, #fce7f3 0%, #e9d5ff 45%, #ddd6fe 100%)',
116
+ position: 'relative',
117
+ overflow: 'hidden',
118
+ }, children: [_jsx("div", { style: { position: 'absolute', top: -20, right: -20, width: 100, height: 100, borderRadius: '50%', background: 'rgba(255,255,255,0.35)' } }), _jsx("p", { style: { margin: '0 0 16px', fontSize: 15, fontWeight: 700, color: '#4c1d95', lineHeight: 1.45, position: 'relative' }, children: "Need specialized help? Our teams are ready to assist you with any questions." }), _jsxs("button", { type: "button", onClick: handleCallUs, disabled: !config.supportPhone, style: {
119
+ display: 'inline-flex',
120
+ alignItems: 'center',
121
+ gap: 8,
122
+ padding: '10px 18px',
123
+ borderRadius: 12,
124
+ border: 'none',
125
+ background: config.supportPhone ? config.primaryColor : '#94a3b8',
126
+ color: '#fff',
127
+ fontSize: 14,
128
+ fontWeight: 700,
129
+ cursor: config.supportPhone ? 'pointer' : 'not-allowed',
130
+ position: 'relative',
131
+ }, children: [_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { 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", fill: "#fff" }) }), "Call Us"] })] })] })] }));
54
132
  };
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" }) }));