ajaxter-chat 3.0.8 → 3.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/components/ChatScreen/index.d.ts +2 -0
  2. package/dist/components/ChatScreen/index.js +36 -9
  3. package/dist/components/ChatWidget.js +111 -15
  4. package/dist/components/HomeScreen/index.d.ts +2 -0
  5. package/dist/components/HomeScreen/index.js +2 -2
  6. package/dist/components/Tabs/BottomTabs.d.ts +0 -1
  7. package/dist/components/Tabs/BottomTabs.js +13 -20
  8. package/dist/components/TicketDetailScreen/index.d.ts +9 -0
  9. package/dist/components/TicketDetailScreen/index.js +46 -0
  10. package/dist/components/TicketFormScreen/index.d.ts +9 -0
  11. package/dist/components/TicketFormScreen/index.js +70 -0
  12. package/dist/components/TicketScreen/index.d.ts +2 -1
  13. package/dist/components/TicketScreen/index.js +8 -35
  14. package/dist/components/UserListScreen/index.d.ts +2 -0
  15. package/dist/components/UserListScreen/index.js +15 -2
  16. package/dist/types/index.d.ts +1 -1
  17. package/dist/utils/fileName.d.ts +2 -0
  18. package/dist/utils/fileName.js +7 -0
  19. package/dist/utils/messageSound.d.ts +4 -0
  20. package/dist/utils/messageSound.js +51 -0
  21. package/dist/utils/widgetSession.d.ts +13 -0
  22. package/dist/utils/widgetSession.js +24 -0
  23. package/package.json +1 -1
  24. package/src/components/ChatScreen/index.tsx +70 -16
  25. package/src/components/ChatWidget.tsx +139 -17
  26. package/src/components/HomeScreen/index.tsx +4 -2
  27. package/src/components/Tabs/BottomTabs.tsx +2 -22
  28. package/src/components/TicketDetailScreen/index.tsx +111 -0
  29. package/src/components/TicketFormScreen/index.tsx +140 -0
  30. package/src/components/TicketScreen/index.tsx +18 -58
  31. package/src/components/UserListScreen/index.tsx +32 -3
  32. package/src/types/index.ts +2 -0
  33. package/src/utils/fileName.ts +6 -0
  34. package/src/utils/messageSound.ts +47 -0
  35. package/src/utils/widgetSession.ts +34 -0
@@ -19,6 +19,8 @@ interface ChatScreenProps {
19
19
  /** Other devs (excl. viewer) — for transfer when staff chats with a customer */
20
20
  otherDevelopers?: ChatUser[];
21
21
  onTransferToDeveloper?: (dev: ChatUser) => void;
22
+ messageSoundEnabled?: boolean;
23
+ onToggleMessageSound?: (enabled: boolean) => void;
22
24
  }
23
25
  export declare const ChatScreen: React.FC<ChatScreenProps>;
24
26
  export {};
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
3
  import { avatarColor, initials, formatTime, formatDate, generateTranscript, downloadText } from '../../utils/chat';
4
+ import { shortAttachmentLabel } from '../../utils/fileName';
4
5
  import { shouldShowPrivacyNotice, dismissPrivacyNotice } from '../../utils/privacyConsent';
5
6
  import { EmojiPicker } from '../EmojiPicker';
6
7
  import { SlideNavMenu } from '../SlideNavMenu';
7
- export const ChatScreen = ({ activeUser, messages, config, isPaused, isReported, isBlocked, onSend, onBack, onClose, onTogglePause, onReport, onBlock, onStartCall, onNavAction, otherDevelopers = [], onTransferToDeveloper, }) => {
8
+ export const ChatScreen = ({ activeUser, messages, config, isPaused, isReported, isBlocked, onSend, onBack, onClose, onTogglePause, onReport, onBlock, onStartCall, onNavAction, otherDevelopers = [], onTransferToDeveloper, messageSoundEnabled = true, onToggleMessageSound, }) => {
8
9
  var _a;
9
10
  const [text, setText] = useState('');
10
11
  const [showEmoji, setShowEmoji] = useState(false);
@@ -125,7 +126,32 @@ export const ChatScreen = ({ activeUser, messages, config, isPaused, isReported,
125
126
  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, viewerType: (_a = config.viewerType) !== null && _a !== void 0 ? _a : 'user', onSelect: onNavAction, onBackHome: onBack }), _jsxs("div", { style: {
126
127
  background: `linear-gradient(135deg,${config.primaryColor},${config.primaryColor}cc)`,
127
128
  padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0,
128
- }, 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: headerRole }), 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 })), viewerIsDev && activeUser.type === 'user' && otherDevelopers.length > 0 && onTransferToDeveloper && (_jsx(MenuItem, { icon: "\uD83D\uDD00", label: "Transfer to developer", onClick: () => { setShowMenu(false); setTransferOpen(true); } })), _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: [privacyEnabled && showPrivacy && (_jsxs("div", { style: {
129
+ }, 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: headerRole }), onToggleMessageSound && (_jsxs("label", { style: {
130
+ display: 'flex',
131
+ alignItems: 'center',
132
+ gap: 6,
133
+ cursor: 'pointer',
134
+ flexShrink: 0,
135
+ marginLeft: 4,
136
+ }, children: [_jsx("span", { style: { fontSize: 10, color: 'rgba(255,255,255,0.85)', fontWeight: 600 }, children: "Sound" }), _jsx("button", { type: "button", role: "switch", "aria-checked": messageSoundEnabled, onClick: () => onToggleMessageSound(!messageSoundEnabled), style: {
137
+ width: 36,
138
+ height: 20,
139
+ borderRadius: 10,
140
+ border: 'none',
141
+ background: messageSoundEnabled ? 'rgba(255,255,255,0.35)' : 'rgba(0,0,0,0.2)',
142
+ position: 'relative',
143
+ cursor: 'pointer',
144
+ padding: 0,
145
+ }, children: _jsx("span", { style: {
146
+ position: 'absolute',
147
+ top: 2,
148
+ left: messageSoundEnabled ? 18 : 2,
149
+ width: 16,
150
+ height: 16,
151
+ borderRadius: '50%',
152
+ background: '#fff',
153
+ transition: 'left 0.15s ease',
154
+ } }) })] })), 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 })), viewerIsDev && activeUser.type === 'user' && otherDevelopers.length > 0 && onTransferToDeveloper && (_jsx(MenuItem, { icon: "\uD83D\uDD00", label: "Transfer to developer", onClick: () => { setShowMenu(false); setTransferOpen(true); } })), _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: [privacyEnabled && showPrivacy && (_jsxs("div", { style: {
129
155
  position: 'relative',
130
156
  marginBottom: 10,
131
157
  padding: '12px 36px 12px 12px',
@@ -289,13 +315,14 @@ const AttachmentRow = ({ msg, isMe, primaryColor }) => {
289
315
  var _a;
290
316
  const name = (_a = msg.attachmentName) !== null && _a !== void 0 ? _a : 'File';
291
317
  const href = msg.attachmentUrl;
292
- 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: {
293
- fontSize: 12,
294
- fontWeight: 700,
295
- color: isMe ? '#fff' : primaryColor,
296
- textDecoration: 'underline',
297
- whiteSpace: 'nowrap',
298
- }, children: "Download" }))] }));
318
+ const label = shortAttachmentLabel(name, 10);
319
+ 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: [href ? (_jsxs("a", { href: href, download: name, title: name, style: {
320
+ fontWeight: 700,
321
+ fontSize: 14,
322
+ wordBreak: 'break-word',
323
+ color: isMe ? '#fff' : primaryColor,
324
+ textDecoration: 'underline',
325
+ }, children: ["[", label, "]"] })) : (_jsxs("div", { style: { fontWeight: 700, fontSize: 14, wordBreak: 'break-word' }, title: name, children: ["[", label, "]"] })), msg.attachmentSize && _jsx("div", { style: { fontSize: 11, opacity: 0.8 }, children: msg.attachmentSize })] })] }));
299
326
  };
300
327
  const Bubble = ({ msg, primaryColor }) => {
301
328
  const isMe = msg.senderId === 'me';
@@ -1,22 +1,26 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useEffect, useCallback } from 'react';
3
+ import { useState, useEffect, useCallback, useRef } from 'react';
4
4
  import { loadLocalConfig } from '../config';
5
5
  import { mergeTheme } from '../utils/theme';
6
6
  import { useRemoteConfig } from '../hooks/useRemoteConfig';
7
7
  import { useChat } from '../hooks/useChat';
8
8
  import { useWebRTC } from '../hooks/useWebRTC';
9
+ import { saveSession, loadSession } from '../utils/widgetSession';
10
+ import { playMessageSound, getMessageSoundEnabled, setMessageSoundEnabled } from '../utils/messageSound';
9
11
  import { HomeScreen } from './HomeScreen';
10
12
  import { UserListScreen } from './UserListScreen';
11
13
  import { ChatScreen } from './ChatScreen';
12
14
  import { RecentChatsScreen } from './RecentChatsScreen';
13
15
  import { TicketScreen } from './TicketScreen';
16
+ import { TicketDetailScreen } from './TicketDetailScreen';
17
+ import { TicketFormScreen } from './TicketFormScreen';
14
18
  import { BlockListScreen } from './BlockList';
15
19
  import { CallScreen } from './CallScreen';
16
20
  import { MaintenanceView } from './MaintenanceView';
17
21
  import { BottomTabs } from './Tabs/BottomTabs';
18
22
  export const ChatWidget = ({ theme: localTheme }) => {
19
- var _a, _b, _c, _d;
23
+ var _a, _b, _c, _d, _e;
20
24
  /* SSR guard */
21
25
  const [mounted, setMounted] = useState(false);
22
26
  useEffect(() => { setMounted(true); }, []);
@@ -33,6 +37,9 @@ export const ChatWidget = ({ theme: localTheme }) => {
33
37
  const [activeTab, setActiveTab] = useState('home');
34
38
  const [screen, setScreen] = useState('home');
35
39
  const [userListCtx, setUserListCtx] = useState('support');
40
+ const [chatReturnCtx, setChatReturnCtx] = useState('conversation');
41
+ const [viewingTicketId, setViewingTicketId] = useState(null);
42
+ const [messageSoundEnabled, setMessageSoundEnabledState] = useState(true);
36
43
  /* App state */
37
44
  const [tickets, setTickets] = useState((_a = data === null || data === void 0 ? void 0 : data.sampleTickets) !== null && _a !== void 0 ? _a : []);
38
45
  const [recentChats, setRecentChats] = useState([]);
@@ -71,16 +78,83 @@ export const ChatWidget = ({ theme: localTheme }) => {
71
78
  setClosing(false);
72
79
  setIsOpen(true);
73
80
  };
81
+ const persistWidgetState = useCallback(() => {
82
+ var _a;
83
+ const w = data === null || data === void 0 ? void 0 : data.widget;
84
+ if (!w)
85
+ return;
86
+ saveSession(w.id, {
87
+ screen,
88
+ activeTab,
89
+ userListCtx,
90
+ activeUserUid: (_a = activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid) !== null && _a !== void 0 ? _a : null,
91
+ messages,
92
+ viewingTicketId,
93
+ chatReturnCtx,
94
+ });
95
+ }, [data === null || data === void 0 ? void 0 : data.widget, screen, activeTab, userListCtx, activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid, messages, viewingTicketId, chatReturnCtx]);
74
96
  const closeDrawer = useCallback(() => {
97
+ persistWidgetState();
75
98
  setClosing(true);
76
99
  setTimeout(() => {
77
100
  setIsOpen(false);
78
101
  setClosing(false);
79
- setScreen('home');
80
- setActiveTab('home');
81
- clearChat();
82
102
  }, 300);
83
- }, [clearChat]);
103
+ }, [persistWidgetState]);
104
+ const restoredRef = useRef(false);
105
+ useEffect(() => {
106
+ var _a, _b, _c;
107
+ if (!(data === null || data === void 0 ? void 0 : data.widget) || restoredRef.current)
108
+ return;
109
+ const w = data.widget;
110
+ setMessageSoundEnabledState(getMessageSoundEnabled(w.id));
111
+ const p = loadSession(w.id);
112
+ if (p) {
113
+ setScreen(p.screen);
114
+ setActiveTab(p.activeTab);
115
+ setUserListCtx(p.userListCtx);
116
+ setViewingTicketId((_a = p.viewingTicketId) !== null && _a !== void 0 ? _a : null);
117
+ setChatReturnCtx((_b = p.chatReturnCtx) !== null && _b !== void 0 ? _b : 'conversation');
118
+ if (p.activeUserUid) {
119
+ const u = [...data.developers, ...data.users].find(x => x.uid === p.activeUserUid);
120
+ if (u) {
121
+ const hist = Array.isArray(p.messages) && p.messages.length
122
+ ? p.messages
123
+ : ((_c = data.sampleChats[u.uid]) !== null && _c !== void 0 ? _c : []);
124
+ selectUser(u, hist);
125
+ }
126
+ }
127
+ }
128
+ restoredRef.current = true;
129
+ }, [data, selectUser]);
130
+ useEffect(() => {
131
+ if (!(data === null || data === void 0 ? void 0 : data.widget))
132
+ return;
133
+ persistWidgetState();
134
+ }, [(_c = data === null || data === void 0 ? void 0 : data.widget) === null || _c === void 0 ? void 0 : _c.id, screen, activeTab, userListCtx, activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid, messages, viewingTicketId, chatReturnCtx, persistWidgetState]);
135
+ const incomingSoundRef = useRef(0);
136
+ useEffect(() => {
137
+ incomingSoundRef.current = messages.length;
138
+ }, [activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid]);
139
+ useEffect(() => {
140
+ if (!messageSoundEnabled || !activeUser || !(data === null || data === void 0 ? void 0 : data.widget))
141
+ return;
142
+ if (messages.length < incomingSoundRef.current) {
143
+ incomingSoundRef.current = messages.length;
144
+ return;
145
+ }
146
+ const added = messages.slice(incomingSoundRef.current);
147
+ incomingSoundRef.current = messages.length;
148
+ if (added.some(m => m.senderId !== 'me'))
149
+ playMessageSound();
150
+ }, [messages, messageSoundEnabled, activeUser, data === null || data === void 0 ? void 0 : data.widget]);
151
+ const toggleMessageSound = useCallback((enabled) => {
152
+ const w = data === null || data === void 0 ? void 0 : data.widget;
153
+ if (!w)
154
+ return;
155
+ setMessageSoundEnabled(w.id, enabled);
156
+ setMessageSoundEnabledState(enabled);
157
+ }, [data === null || data === void 0 ? void 0 : data.widget]);
84
158
  /* ── Navigation ──────────────────────────────────────────────────────── */
85
159
  const handleCardClick = useCallback((ctx) => {
86
160
  if (ctx === 'ticket') {
@@ -103,20 +177,34 @@ export const ChatWidget = ({ theme: localTheme }) => {
103
177
  setScreen('user-list');
104
178
  }
105
179
  }, [clearChat]);
106
- const handleSelectUser = useCallback((user) => {
180
+ const listCtxForUser = useCallback((user, viewerIsDev) => {
181
+ if (viewerIsDev)
182
+ return user.type === 'user' ? 'support' : 'conversation';
183
+ return user.type === 'developer' ? 'support' : 'conversation';
184
+ }, []);
185
+ const handleSelectUser = useCallback((user, returnCtxOverride) => {
107
186
  var _a;
108
- // Load history from sample chats if available
187
+ setChatReturnCtx(returnCtxOverride !== null && returnCtxOverride !== void 0 ? returnCtxOverride : userListCtx);
109
188
  const history = (_a = data === null || data === void 0 ? void 0 : data.sampleChats[user.uid]) !== null && _a !== void 0 ? _a : [];
110
189
  selectUser(user, history);
111
190
  setScreen('chat');
112
- // Update recent chats
113
191
  setRecentChats(prev => {
114
192
  const exists = prev.find(r => r.user.uid === user.uid);
115
193
  if (exists)
116
194
  return prev;
117
195
  return [{ id: `rc_${user.uid}`, user, lastMessage: '', lastTime: new Date().toISOString(), unread: 0, isPaused: false }, ...prev];
118
196
  });
119
- }, [data, selectUser]);
197
+ }, [data, selectUser, userListCtx]);
198
+ const handleBackFromChat = useCallback(() => {
199
+ clearChat();
200
+ setUserListCtx(chatReturnCtx);
201
+ setScreen('user-list');
202
+ }, [clearChat, chatReturnCtx]);
203
+ const handleOpenTicket = useCallback((id) => {
204
+ setViewingTicketId(id);
205
+ setScreen('ticket-detail');
206
+ setActiveTab('tickets');
207
+ }, []);
120
208
  const handleTabChange = useCallback((tab) => {
121
209
  setActiveTab(tab);
122
210
  setScreen(tab === 'home' ? 'home' : tab === 'chats' ? 'recent-chats' : 'tickets');
@@ -127,7 +215,7 @@ export const ChatWidget = ({ theme: localTheme }) => {
127
215
  return;
128
216
  setBlockedUids(prev => [...prev, activeUser.uid]);
129
217
  clearChat();
130
- setScreen('home');
218
+ setScreen('block-list');
131
219
  setActiveTab('home');
132
220
  }, [activeUser, clearChat]);
133
221
  const handleUnblock = useCallback((uid) => {
@@ -142,7 +230,10 @@ export const ChatWidget = ({ theme: localTheme }) => {
142
230
  updatedAt: new Date().toISOString(),
143
231
  assignedTo: null,
144
232
  };
145
- setTickets(prev => [t, ...prev]);
233
+ setTickets(prev => [...prev, t]);
234
+ setViewingTicketId(t.id);
235
+ setScreen('ticket-detail');
236
+ setActiveTab('tickets');
146
237
  }, []);
147
238
  /* ── Pause sync back into recent chats ──────────────────────────────── */
148
239
  const handleTogglePause = useCallback(() => {
@@ -182,7 +273,7 @@ export const ChatWidget = ({ theme: localTheme }) => {
182
273
  return u.type === 'user';
183
274
  })
184
275
  : [];
185
- const otherDevelopers = (_c = data === null || data === void 0 ? void 0 : data.developers.filter(d => d.uid !== viewerUid)) !== null && _c !== void 0 ? _c : [];
276
+ const otherDevelopers = (_d = data === null || data === void 0 ? void 0 : data.developers.filter(d => d.uid !== viewerUid)) !== null && _d !== void 0 ? _d : [];
186
277
  const blockedUsers = allUsers.filter(u => blockedUids.includes(u.uid));
187
278
  const handleTransferToDeveloper = useCallback((dev) => {
188
279
  var _a;
@@ -273,11 +364,16 @@ export const ChatWidget = ({ theme: localTheme }) => {
273
364
  right: theme.buttonPosition === 'bottom-left' ? 'auto' : 12,
274
365
  left: theme.buttonPosition === 'bottom-left' ? 12 : 'auto',
275
366
  zIndex: 20, display: 'flex', gap: 6,
276
- }, children: _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, viewerType: (_d = widgetConfig.viewerType) !== null && _d !== void 0 ? _d : 'user', 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, otherDevelopers: otherDevelopers, onTransferToDeveloper: handleTransferToDeveloper })), 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' &&
367
+ }, children: _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, onOpenTicket: handleOpenTicket, tickets: tickets })), screen === 'user-list' && (_jsx(UserListScreen, { context: userListCtx, users: filteredUsers, primaryColor: primaryColor, viewerType: (_e = widgetConfig.viewerType) !== null && _e !== void 0 ? _e : 'user', onBack: () => setScreen('home'), onSelectUser: handleSelectUser, onBlockList: userListCtx === 'conversation' ? () => setScreen('block-list') : undefined })), screen === 'chat' && activeUser && (_jsx(ChatScreen, { activeUser: activeUser, messages: messages, config: widgetConfig, isPaused: isPaused, isReported: isReported, isBlocked: isBlocked, onSend: sendMessage, onBack: handleBackFromChat, onClose: closeDrawer, onTogglePause: handleTogglePause, onReport: reportChat, onBlock: handleBlock, onStartCall: handleStartCall, onNavAction: handleNavFromMenu, otherDevelopers: otherDevelopers, onTransferToDeveloper: handleTransferToDeveloper, messageSoundEnabled: messageSoundEnabled, onToggleMessageSound: toggleMessageSound })), 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: u => handleSelectUser(u, listCtxForUser(u, viewerIsDev)) })), screen === 'tickets' && (_jsx(TicketScreen, { tickets: tickets, config: widgetConfig, onNewTicket: () => setScreen('ticket-new'), onSelectTicket: id => { setViewingTicketId(id); setScreen('ticket-detail'); } })), screen === 'ticket-new' && (_jsx(TicketFormScreen, { config: widgetConfig, onSubmit: handleRaiseTicket, onCancel: () => setScreen('tickets') })), screen === 'ticket-detail' && viewingTicketId && ((() => {
368
+ const t = tickets.find(x => x.id === viewingTicketId);
369
+ return t ? (_jsx(TicketDetailScreen, { ticket: t, config: widgetConfig, onBack: () => { setViewingTicketId(null); setScreen('tickets'); } })) : null;
370
+ })()), screen === 'block-list' && (_jsx(BlockListScreen, { blockedUsers: blockedUsers, config: widgetConfig, onUnblock: handleUnblock, onBack: () => { setScreen('home'); setActiveTab('home'); } }))] })), widgetConfig.status === 'ACTIVE' &&
277
371
  screen !== 'chat' &&
278
372
  screen !== 'call' &&
279
373
  screen !== 'user-list' &&
280
- screen !== 'block-list' && (_jsx(BottomTabs, { active: activeTab, onChange: handleTabChange, primaryColor: primaryColor, onBlockList: () => setScreen('block-list') }))] }))] }))] }));
374
+ screen !== 'block-list' &&
375
+ screen !== 'ticket-detail' &&
376
+ screen !== 'ticket-new' && (_jsx(BottomTabs, { active: activeTab, onChange: handleTabChange, primaryColor: primaryColor }))] }))] }))] }));
281
377
  };
282
378
  export default ChatWidget;
283
379
  /* ── Tiny corner button ────────────────────────────────────────────────────── */
@@ -3,6 +3,8 @@ import { WidgetConfig, UserListContext, Ticket } from '../../types';
3
3
  interface HomeScreenProps {
4
4
  config: WidgetConfig;
5
5
  onNavigate: (ctx: UserListContext | 'ticket') => void;
6
+ /** Open a specific pending ticket (full detail) */
7
+ onOpenTicket: (ticketId: string) => void;
6
8
  tickets: Ticket[];
7
9
  }
8
10
  export declare const HomeScreen: React.FC<HomeScreenProps>;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useState, useMemo } from 'react';
3
3
  import { SlideNavMenu } from '../SlideNavMenu';
4
4
  import { truncateWords } from '../../utils/chat';
5
- export const HomeScreen = ({ config, onNavigate, tickets }) => {
5
+ export const HomeScreen = ({ config, onNavigate, onOpenTicket, tickets }) => {
6
6
  var _a, _b, _c, _d;
7
7
  const [menuOpen, setMenuOpen] = useState(false);
8
8
  const showSupport = config.chatType === 'SUPPORT' || config.chatType === 'BOTH';
@@ -51,7 +51,7 @@ export const HomeScreen = ({ config, onNavigate, tickets }) => {
51
51
  color: '#0f172a',
52
52
  letterSpacing: '-0.03em',
53
53
  lineHeight: 1.2,
54
- }, 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: pendingTickets.length > 0 ? (pendingTickets.map(t => (_jsxs("button", { type: "button", onClick: () => onNavigate('ticket'), style: {
54
+ }, 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: pendingTickets.length > 0 ? (pendingTickets.map(t => (_jsxs("button", { type: "button", onClick: () => onOpenTicket(t.id), style: {
55
55
  width: '100%',
56
56
  textAlign: 'left',
57
57
  padding: '14px 16px',
@@ -4,7 +4,6 @@ interface BottomTabsProps {
4
4
  active: BottomTab;
5
5
  onChange: (tab: BottomTab) => void;
6
6
  primaryColor: string;
7
- onBlockList: () => void;
8
7
  }
9
8
  export declare const BottomTabs: React.FC<BottomTabsProps>;
10
9
  export {};
@@ -1,33 +1,26 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export const BottomTabs = ({ active, onChange, primaryColor, onBlockList }) => {
2
+ export const BottomTabs = ({ active, onChange, primaryColor }) => {
3
3
  const tabs = [
4
4
  { key: 'home', label: 'Home', Icon: HomeIcon },
5
5
  { key: 'chats', label: 'Chats', Icon: ChatsIcon },
6
6
  { key: 'tickets', label: 'Tickets', Icon: TicketsIcon },
7
7
  ];
8
- return (_jsxs("div", { style: {
8
+ return (_jsx("div", { style: {
9
9
  display: 'flex', borderTop: '1px solid #eef0f5',
10
10
  backgroundColor: '#fff', flexShrink: 0,
11
- }, children: [tabs.map(tab => {
12
- const isActive = active === tab.key;
13
- return (_jsxs("button", { onClick: () => onChange(tab.key), style: {
14
- flex: 1, padding: '10px 0 8px',
15
- display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
16
- background: 'transparent', border: 'none', cursor: 'pointer',
17
- fontSize: '10px', fontWeight: isActive ? 700 : 500,
18
- color: isActive ? primaryColor : '#9aa3af',
19
- borderTop: isActive ? `2px solid ${primaryColor}` : '2px solid transparent',
20
- transition: 'color 0.15s',
21
- fontFamily: 'inherit',
22
- }, children: [_jsx(tab.Icon, { a: isActive, c: isActive ? primaryColor : '#b0bec5' }), tab.label] }, tab.key));
23
- }), _jsxs("button", { onClick: onBlockList, style: {
24
- padding: '10px 14px 8px',
11
+ }, children: tabs.map(tab => {
12
+ const isActive = active === tab.key;
13
+ return (_jsxs("button", { type: "button", onClick: () => onChange(tab.key), style: {
14
+ flex: 1, padding: '10px 0 8px',
25
15
  display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
26
16
  background: 'transparent', border: 'none', cursor: 'pointer',
27
- fontSize: '10px', fontWeight: 500, color: '#9aa3af',
28
- borderTop: '2px solid transparent',
29
- transition: 'color 0.15s', fontFamily: 'inherit',
30
- }, title: "Block List", children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#b0bec5", strokeWidth: "1.8" }), _jsx("line", { x1: "4.93", y1: "4.93", x2: "19.07", y2: "19.07", stroke: "#b0bec5", strokeWidth: "1.8", strokeLinecap: "round" })] }), "Blocked"] })] }));
17
+ fontSize: '10px', fontWeight: isActive ? 700 : 500,
18
+ color: isActive ? primaryColor : '#9aa3af',
19
+ borderTop: isActive ? `2px solid ${primaryColor}` : '2px solid transparent',
20
+ transition: 'color 0.15s',
21
+ fontFamily: 'inherit',
22
+ }, children: [_jsx(tab.Icon, { a: isActive, c: isActive ? primaryColor : '#b0bec5' }), tab.label] }, tab.key));
23
+ }) }));
31
24
  };
32
25
  const HomeIcon = ({ a, c }) => (_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M3 9.5L12 3l9 6.5V20a1 1 0 01-1 1H4a1 1 0 01-1-1V9.5z", stroke: c, strokeWidth: a ? 2.2 : 1.8, fill: a ? `${c}20` : 'none', strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M9 21V12h6v9", stroke: c, strokeWidth: a ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round" })] }));
33
26
  const ChatsIcon = ({ a, c }) => (_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: c, strokeWidth: a ? 2.2 : 1.8, fill: a ? `${c}20` : 'none', strokeLinecap: "round", strokeLinejoin: "round" }) }));
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { Ticket, WidgetConfig } from '../../types';
3
+ interface TicketDetailScreenProps {
4
+ ticket: Ticket;
5
+ config: WidgetConfig;
6
+ onBack: () => void;
7
+ }
8
+ export declare const TicketDetailScreen: React.FC<TicketDetailScreenProps>;
9
+ export {};
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const sm = {
3
+ open: { label: 'Open', bg: '', color: '' },
4
+ 'in-progress': { label: 'In Progress', bg: '#fef3c7', color: '#d97706' },
5
+ resolved: { label: 'Resolved', bg: '#f0fdf4', color: '#16a34a' },
6
+ closed: { label: 'Closed', bg: '#f3f4f6', color: '#6b7280' },
7
+ };
8
+ const pm = {
9
+ low: { label: 'Low', color: '#6b7280' },
10
+ medium: { label: 'Medium', color: '#d97706' },
11
+ high: { label: 'High', color: '#ef4444' },
12
+ };
13
+ export const TicketDetailScreen = ({ ticket, config, onBack }) => {
14
+ const st = sm[ticket.status];
15
+ const pr = pm[ticket.priority];
16
+ const stBg = ticket.status === 'open' ? `${config.primaryColor}14` : st.bg;
17
+ const stColor = ticket.status === 'open' ? config.primaryColor : st.color;
18
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideIn 0.22s ease' }, children: [_jsxs("div", { style: {
19
+ background: `linear-gradient(135deg,${config.primaryColor},${config.primaryColor}cc)`,
20
+ padding: '14px 18px',
21
+ display: 'flex',
22
+ alignItems: 'center',
23
+ gap: 12,
24
+ flexShrink: 0,
25
+ }, children: [_jsx("button", { type: "button", onClick: onBack, style: {
26
+ background: 'rgba(255,255,255,0.22)',
27
+ border: 'none',
28
+ borderRadius: '50%',
29
+ width: 36,
30
+ height: 36,
31
+ display: 'flex',
32
+ alignItems: 'center',
33
+ justifyContent: 'center',
34
+ cursor: 'pointer',
35
+ flexShrink: 0,
36
+ }, children: _jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M19 12H5M5 12L12 19M5 12L12 5", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 800, fontSize: 16, color: '#fff', lineHeight: 1.25 }, children: ticket.title }), _jsxs("div", { style: { fontSize: 12, color: 'rgba(255,255,255,0.85)', marginTop: 2 }, children: ["#", ticket.id] })] })] }), _jsxs("div", { className: "cw-scroll", style: { flex: 1, overflowY: 'auto', padding: '20px 18px' }, children: [_jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 16 }, children: [_jsx("span", { style: {
37
+ fontSize: 11,
38
+ fontWeight: 700,
39
+ padding: '5px 12px',
40
+ borderRadius: 20,
41
+ backgroundColor: stBg,
42
+ color: stColor,
43
+ textTransform: 'uppercase',
44
+ letterSpacing: '0.04em',
45
+ }, children: st.label }), _jsxs("span", { style: { fontSize: 11, fontWeight: 700, padding: '5px 12px', borderRadius: 20, color: pr.color, background: `${pr.color}15` }, children: ["\u25CF ", pr.label, " priority"] })] }), _jsx("h3", { style: { margin: '0 0 8px', fontSize: 13, fontWeight: 700, color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.06em' }, children: "Description" }), _jsx("p", { style: { margin: '0 0 24px', fontSize: 15, color: '#1e293b', lineHeight: 1.65, whiteSpace: 'pre-wrap' }, children: ticket.description || '—' }), _jsxs("div", { style: { fontSize: 13, color: '#94a3b8', display: 'grid', gap: 8 }, children: [_jsxs("div", { children: [_jsx("strong", { style: { color: '#64748b' }, children: "Created" }), " \u00B7 ", new Date(ticket.createdAt).toLocaleString()] }), _jsxs("div", { children: [_jsx("strong", { style: { color: '#64748b' }, children: "Updated" }), " \u00B7 ", new Date(ticket.updatedAt).toLocaleString()] }), ticket.assignedTo && (_jsxs("div", { children: [_jsx("strong", { style: { color: '#64748b' }, children: "Assigned" }), " \u00B7 ", ticket.assignedTo] }))] })] })] }));
46
+ };
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { Ticket, WidgetConfig } from '../../types';
3
+ interface TicketFormScreenProps {
4
+ config: WidgetConfig;
5
+ onSubmit: (title: string, desc: string, priority: Ticket['priority']) => void;
6
+ onCancel: () => void;
7
+ }
8
+ export declare const TicketFormScreen: React.FC<TicketFormScreenProps>;
9
+ export {};
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ function inputStyle(primaryColor) {
4
+ return {
5
+ width: '100%',
6
+ padding: '11px 14px',
7
+ borderRadius: 10,
8
+ border: '1.5px solid #e5e7eb',
9
+ outline: 'none',
10
+ fontSize: 14,
11
+ color: '#1a2332',
12
+ boxSizing: 'border-box',
13
+ fontFamily: 'inherit',
14
+ transition: 'border-color 0.2s',
15
+ };
16
+ }
17
+ export const TicketFormScreen = ({ config, onSubmit, onCancel }) => {
18
+ const [title, setTitle] = useState('');
19
+ const [desc, setDesc] = useState('');
20
+ const [priority, setPriority] = useState('medium');
21
+ const pm = {
22
+ low: { label: 'Low', color: '#6b7280' },
23
+ medium: { label: 'Medium', color: '#d97706' },
24
+ high: { label: 'High', color: '#ef4444' },
25
+ };
26
+ const handleSubmit = () => {
27
+ if (!title.trim())
28
+ return;
29
+ onSubmit(title.trim(), desc.trim(), priority);
30
+ };
31
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', background: '#fff' }, children: [_jsxs("div", { style: {
32
+ background: `linear-gradient(135deg,${config.primaryColor},${config.primaryColor}cc)`,
33
+ padding: '14px 18px',
34
+ display: 'flex',
35
+ alignItems: 'center',
36
+ gap: 12,
37
+ flexShrink: 0,
38
+ }, children: [_jsx("button", { type: "button", onClick: onCancel, style: {
39
+ background: 'rgba(255,255,255,0.22)',
40
+ border: 'none',
41
+ borderRadius: '50%',
42
+ width: 36,
43
+ height: 36,
44
+ display: 'flex',
45
+ alignItems: 'center',
46
+ justifyContent: 'center',
47
+ cursor: 'pointer',
48
+ }, children: _jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M19 12H5M5 12L12 19M5 12L12 5", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsxs("div", { children: [_jsx("h2", { style: { margin: 0, fontSize: 18, fontWeight: 800, color: '#fff' }, children: "New ticket" }), _jsx("p", { style: { margin: '4px 0 0', fontSize: 12, color: 'rgba(255,255,255,0.85)' }, children: "Describe your issue below" })] })] }), _jsxs("div", { className: "cw-scroll", style: { flex: 1, overflowY: 'auto', padding: '20px 18px' }, children: [_jsx("input", { placeholder: "Title *", value: title, onChange: e => setTitle(e.target.value), style: inputStyle(config.primaryColor), onFocus: e => (e.target.style.borderColor = config.primaryColor), onBlur: e => (e.target.style.borderColor = '#e5e7eb') }), _jsx("textarea", { placeholder: "Describe the issue\u2026", value: desc, onChange: e => setDesc(e.target.value), rows: 5, style: Object.assign(Object.assign({}, inputStyle(config.primaryColor)), { resize: 'none', marginTop: 12 }), onFocus: e => (e.target.style.borderColor = config.primaryColor), onBlur: e => (e.target.style.borderColor = '#e5e7eb') }), _jsx("div", { style: { display: 'flex', gap: 8, marginTop: 12, marginBottom: 20 }, children: ['low', 'medium', 'high'].map(p => (_jsx("button", { type: "button", onClick: () => setPriority(p), style: {
49
+ flex: 1,
50
+ padding: '8px',
51
+ border: `1.5px solid ${priority === p ? pm[p].color : '#e5e7eb'}`,
52
+ borderRadius: 8,
53
+ background: priority === p ? `${pm[p].color}15` : '#fff',
54
+ color: pm[p].color,
55
+ fontWeight: 700,
56
+ fontSize: 12,
57
+ cursor: 'pointer',
58
+ textTransform: 'capitalize',
59
+ }, children: pm[p].label }, p))) }), _jsx("button", { type: "button", onClick: handleSubmit, disabled: !title.trim(), style: {
60
+ width: '100%',
61
+ padding: '12px',
62
+ borderRadius: 10,
63
+ border: 'none',
64
+ background: title.trim() ? config.primaryColor : '#e5e7eb',
65
+ color: title.trim() ? '#fff' : '#9ca3af',
66
+ fontWeight: 700,
67
+ fontSize: 15,
68
+ cursor: title.trim() ? 'pointer' : 'not-allowed',
69
+ }, children: "Submit ticket" })] })] }));
70
+ };
@@ -3,7 +3,8 @@ import { Ticket, WidgetConfig } from '../../types';
3
3
  interface TicketScreenProps {
4
4
  tickets: Ticket[];
5
5
  config: WidgetConfig;
6
- onRaiseTicket: (title: string, desc: string, priority: Ticket['priority']) => void;
6
+ onNewTicket: () => void;
7
+ onSelectTicket: (id: string) => void;
7
8
  }
8
9
  export declare const TicketScreen: React.FC<TicketScreenProps>;
9
10
  export {};