ajaxter-chat 2.0.1 → 3.0.3

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 (94) hide show
  1. package/README.md +119 -128
  2. package/dist/components/BlockList/index.d.ts +10 -0
  3. package/dist/components/BlockList/index.js +33 -0
  4. package/dist/components/CallScreen/index.d.ts +13 -0
  5. package/dist/components/CallScreen/index.js +48 -0
  6. package/dist/components/ChatScreen/index.d.ts +10 -3
  7. package/dist/components/ChatScreen/index.js +142 -57
  8. package/dist/components/ChatWidget.js +192 -98
  9. package/dist/components/EmojiPicker/index.d.ts +8 -0
  10. package/dist/components/EmojiPicker/index.js +18 -0
  11. package/dist/components/HomeScreen/index.d.ts +2 -3
  12. package/dist/components/HomeScreen/index.js +25 -41
  13. package/dist/components/MaintenanceView/index.d.ts +0 -1
  14. package/dist/components/MaintenanceView/index.js +4 -6
  15. package/dist/components/RecentChatsScreen/index.d.ts +4 -3
  16. package/dist/components/RecentChatsScreen/index.js +7 -37
  17. package/dist/components/Tabs/BottomTabs.d.ts +1 -1
  18. package/dist/components/Tabs/BottomTabs.js +25 -20
  19. package/dist/components/TicketScreen/index.d.ts +3 -3
  20. package/dist/components/TicketScreen/index.js +39 -56
  21. package/dist/components/UserListScreen/index.d.ts +2 -4
  22. package/dist/components/UserListScreen/index.js +33 -62
  23. package/dist/config/index.d.ts +7 -3
  24. package/dist/config/index.js +28 -25
  25. package/dist/hooks/useChat.d.ts +8 -3
  26. package/dist/hooks/useChat.js +22 -18
  27. package/dist/hooks/useRemoteConfig.d.ts +6 -0
  28. package/dist/hooks/useRemoteConfig.js +26 -0
  29. package/dist/hooks/useWebRTC.d.ts +11 -0
  30. package/dist/hooks/useWebRTC.js +112 -0
  31. package/dist/index.d.ts +9 -5
  32. package/dist/index.js +8 -4
  33. package/dist/types/index.d.ts +62 -21
  34. package/dist/utils/chat.d.ts +13 -0
  35. package/dist/utils/chat.js +62 -0
  36. package/dist/utils/theme.d.ts +3 -1
  37. package/dist/utils/theme.js +14 -7
  38. package/package.json +4 -4
  39. package/public/chatData.json +162 -0
  40. package/src/components/BlockList/index.tsx +94 -0
  41. package/src/components/CallScreen/index.tsx +144 -0
  42. package/src/components/ChatScreen/index.tsx +403 -139
  43. package/src/components/ChatWidget.tsx +394 -250
  44. package/src/components/EmojiPicker/index.tsx +48 -0
  45. package/src/components/HomeScreen/index.tsx +58 -82
  46. package/src/components/MaintenanceView/index.tsx +6 -9
  47. package/src/components/RecentChatsScreen/index.tsx +51 -96
  48. package/src/components/Tabs/BottomTabs.tsx +45 -37
  49. package/src/components/TicketScreen/index.tsx +87 -133
  50. package/src/components/UserListScreen/index.tsx +75 -153
  51. package/src/config/index.ts +32 -26
  52. package/src/hooks/useChat.ts +31 -14
  53. package/src/hooks/useRemoteConfig.ts +26 -0
  54. package/src/hooks/useWebRTC.ts +130 -0
  55. package/src/index.ts +26 -15
  56. package/src/types/index.ts +85 -40
  57. package/src/utils/chat.ts +70 -0
  58. package/src/utils/theme.ts +18 -7
  59. package/dist/hooks/useUsers.d.ts +0 -7
  60. package/dist/hooks/useUsers.js +0 -26
  61. package/dist/services/userService.d.ts +0 -2
  62. package/dist/services/userService.js +0 -9
  63. package/dist/src/components/ChatScreen/index.d.ts +0 -12
  64. package/dist/src/components/ChatScreen/index.js +0 -83
  65. package/dist/src/components/ChatWidget.d.ts +0 -4
  66. package/dist/src/components/ChatWidget.js +0 -141
  67. package/dist/src/components/HomeScreen/index.d.ts +0 -9
  68. package/dist/src/components/HomeScreen/index.js +0 -71
  69. package/dist/src/components/MaintenanceView/index.d.ts +0 -7
  70. package/dist/src/components/MaintenanceView/index.js +0 -16
  71. package/dist/src/components/RecentChatsScreen/index.d.ts +0 -16
  72. package/dist/src/components/RecentChatsScreen/index.js +0 -38
  73. package/dist/src/components/Tabs/BottomTabs.d.ts +0 -10
  74. package/dist/src/components/Tabs/BottomTabs.js +0 -29
  75. package/dist/src/components/TicketScreen/index.d.ts +0 -9
  76. package/dist/src/components/TicketScreen/index.js +0 -71
  77. package/dist/src/components/UserListScreen/index.d.ts +0 -13
  78. package/dist/src/components/UserListScreen/index.js +0 -64
  79. package/dist/src/config/index.d.ts +0 -3
  80. package/dist/src/config/index.js +0 -38
  81. package/dist/src/hooks/useChat.d.ts +0 -8
  82. package/dist/src/hooks/useChat.js +0 -26
  83. package/dist/src/hooks/useUsers.d.ts +0 -7
  84. package/dist/src/hooks/useUsers.js +0 -26
  85. package/dist/src/index.d.ts +0 -14
  86. package/dist/src/index.js +0 -13
  87. package/dist/src/services/userService.d.ts +0 -2
  88. package/dist/src/services/userService.js +0 -9
  89. package/dist/src/types/index.d.ts +0 -59
  90. package/dist/src/types/index.js +0 -1
  91. package/dist/src/utils/theme.d.ts +0 -3
  92. package/dist/src/utils/theme.js +0 -13
  93. package/src/hooks/useUsers.ts +0 -27
  94. package/src/services/userService.ts +0 -9
@@ -1,38 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { mergeTheme } from '../../utils/theme';
3
- export const RecentChatsScreen = ({ chats, theme, onSelectChat }) => {
4
- const t = mergeTheme(theme);
5
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: {
6
- backgroundColor: t.primaryColor,
7
- padding: '20px 20px 24px',
8
- flexShrink: 0,
9
- }, children: [_jsx("h2", { style: { margin: 0, fontSize: '20px', fontWeight: 800, color: '#fff', fontFamily: t.fontFamily, letterSpacing: '-0.02em' }, children: "Recent Chats" }), _jsx("p", { style: { margin: '4px 0 0', fontSize: '13px', color: 'rgba(255,255,255,0.8)', fontFamily: t.fontFamily }, children: "Your conversation history" })] }), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: chats.length === 0 ? (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center', fontFamily: t.fontFamily }, children: [_jsx("div", { style: { fontSize: '36px', marginBottom: 12 }, children: "\uD83D\uDCAC" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No chats yet" }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1' }, children: "Start a conversation from the home tab" })] })) : (chats.map((chat, i) => {
10
- const initials = chat.user.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
11
- const avatarColors = ['#1aaa96', '#2563EB', '#7C3AED', '#D97706', '#DC2626'];
12
- const bg = avatarColors[chat.user.name.charCodeAt(0) % avatarColors.length];
13
- const timeStr = new Date(chat.lastTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
14
- return (_jsxs("button", { onClick: () => onSelectChat(chat.user), style: {
15
- width: '100%', padding: '14px 20px',
16
- display: 'flex', alignItems: 'center', gap: '14px',
17
- background: 'transparent', border: 'none',
18
- borderBottom: '1px solid #f3f4f6',
19
- cursor: 'pointer', textAlign: 'left',
20
- fontFamily: t.fontFamily,
21
- animation: `cw-fadeUp 0.3s ease both`,
22
- animationDelay: `${i * 0.05}s`,
23
- transition: 'background 0.15s',
24
- }, onMouseEnter: e => e.currentTarget.style.background = '#f8fdfc', onMouseLeave: e => e.currentTarget.style.background = 'transparent', children: [_jsxs("div", { style: {
25
- width: 46, height: 46, borderRadius: '50%', backgroundColor: bg,
26
- display: 'flex', alignItems: 'center', justifyContent: 'center',
27
- color: '#fff', fontWeight: 700, fontSize: '15px', flexShrink: 0,
28
- position: 'relative',
29
- }, children: [initials, chat.unread > 0 && (_jsx("span", { style: {
30
- position: 'absolute', top: -2, right: -2,
31
- width: 18, height: 18, borderRadius: '50%',
32
- backgroundColor: '#ff4757', color: '#fff',
33
- fontSize: '10px', fontWeight: 700,
34
- display: 'flex', alignItems: 'center', justifyContent: 'center',
35
- border: '2px solid #fff',
36
- }, children: chat.unread }))] }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 3 }, children: [_jsx("span", { style: { fontWeight: 700, fontSize: '14px', color: '#1a2332' }, children: chat.user.name }), _jsx("span", { style: { fontSize: '11px', color: '#b0bec5' }, children: timeStr })] }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: chat.lastMessage })] })] }, chat.id));
37
- })) })] }));
38
- };
2
+ import { avatarColor, initials, formatTime } from '../../utils/chat';
3
+ export const RecentChatsScreen = ({ chats, config, onSelectChat }) => (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: { background: `linear-gradient(135deg,${config.primaryColor},${config.primaryColor}cc)`, padding: '18px 18px 22px', flexShrink: 0 }, children: [_jsx("h2", { style: { margin: 0, fontSize: 20, fontWeight: 800, color: '#fff', letterSpacing: '-0.02em' }, children: "Recent Chats" }), _jsx("p", { style: { margin: '3px 0 0', fontSize: 12, color: 'rgba(255,255,255,0.8)' }, children: "Your conversation history" })] }), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: chats.length === 0 ? (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 36, marginBottom: 10 }, children: "\uD83D\uDCAC" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No chats yet" }), _jsx("div", { style: { fontSize: 13, color: '#7b8fa1' }, children: "Start a conversation from home" })] })) : chats.map((chat, i) => (_jsxs("button", { onClick: () => onSelectChat(chat.user), style: {
4
+ width: '100%', padding: '13px 16px', display: 'flex', alignItems: 'center', gap: 13,
5
+ background: 'transparent', border: 'none', borderBottom: '1px solid #f0f2f5',
6
+ cursor: 'pointer', textAlign: 'left', animation: `cw-fadeUp 0.28s ease both`, animationDelay: `${i * 0.05}s`,
7
+ transition: 'background 0.14s',
8
+ }, onMouseEnter: e => e.currentTarget.style.background = '#f8faff', onMouseLeave: e => e.currentTarget.style.background = 'transparent', children: [_jsxs("div", { style: { position: 'relative', flexShrink: 0 }, children: [_jsx("div", { style: { width: 46, height: 46, borderRadius: '50%', backgroundColor: avatarColor(chat.user.name), display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontWeight: 700, fontSize: 15 }, children: initials(chat.user.name) }), chat.unread > 0 && (_jsx("span", { style: { position: 'absolute', top: -2, right: -2, width: 18, height: 18, borderRadius: '50%', background: '#ef4444', color: '#fff', fontSize: 10, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', border: '2px solid #fff' }, children: chat.unread }))] }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 3 }, children: [_jsx("span", { style: { fontWeight: 700, fontSize: 14, color: '#1a2332' }, children: chat.user.name }), _jsx("span", { style: { fontSize: 11, color: '#b0bec5' }, children: formatTime(chat.lastTime) })] }), _jsxs("div", { style: { fontSize: 13, color: '#7b8fa1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 5 }, children: [chat.isPaused && _jsx("span", { style: { fontSize: 10, background: '#fef3c7', color: '#92400e', padding: '1px 5px', borderRadius: 4, fontWeight: 700 }, children: "PAUSED" }), chat.lastMessage] })] })] }, chat.id))) })] }));
@@ -4,7 +4,7 @@ interface BottomTabsProps {
4
4
  active: BottomTab;
5
5
  onChange: (tab: BottomTab) => void;
6
6
  primaryColor: string;
7
- fontFamily: string;
7
+ onBlockList: () => void;
8
8
  }
9
9
  export declare const BottomTabs: React.FC<BottomTabsProps>;
10
10
  export {};
@@ -1,29 +1,34 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export const BottomTabs = ({ active, onChange, primaryColor, fontFamily }) => {
2
+ export const BottomTabs = ({ active, onChange, primaryColor, onBlockList }) => {
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 (_jsx("div", { style: {
9
- display: 'flex',
10
- borderTop: '1px solid #eef0f5',
11
- backgroundColor: '#fff',
12
- flexShrink: 0,
13
- }, children: tabs.map(tab => {
14
- const isActive = active === tab.key;
15
- return (_jsxs("button", { onClick: () => onChange(tab.key), style: {
16
- flex: 1, padding: '10px 0 8px',
8
+ return (_jsxs("div", { style: {
9
+ display: 'flex', borderTop: '1px solid #eef0f5',
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',
17
25
  display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
18
26
  background: 'transparent', border: 'none', cursor: 'pointer',
19
- fontFamily, fontSize: '10px', fontWeight: isActive ? 700 : 500,
20
- color: isActive ? primaryColor : '#9aa3af',
21
- transition: 'color 0.15s',
22
- borderTop: isActive ? `2px solid ${primaryColor}` : '2px solid transparent',
23
- }, children: [_jsx(tab.Icon, { active: isActive, color: isActive ? primaryColor : '#b0bec5' }), tab.label] }, tab.key));
24
- }) }));
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"] })] }));
25
31
  };
26
- // ── Icons ─────────────────────────────────────────────────────────────────────
27
- const HomeIcon = ({ active, color }) => (_jsxs("svg", { width: "22", height: "22", 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: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round", fill: active ? `${color}20` : 'none' }), _jsx("path", { d: "M9 21V12h6v9", stroke: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round" })] }));
28
- const ChatsIcon = ({ active, color }) => (_jsx("svg", { width: "22", height: "22", 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: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round", fill: active ? `${color}20` : 'none' }) }));
29
- const TicketsIcon = ({ active, color }) => (_jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M15 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V9l-4-4z", stroke: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round", fill: active ? `${color}20` : 'none' }), _jsx("path", { d: "M15 5v4h4M9 13h6M9 17h4", stroke: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round" })] }));
32
+ 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
+ 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" }) }));
34
+ const TicketsIcon = ({ a, c }) => (_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M15 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V9l-4-4z", stroke: c, strokeWidth: a ? 2.2 : 1.8, fill: a ? `${c}20` : 'none', strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M15 5v4h4M9 13h6M9 17h4", stroke: c, strokeWidth: a ? 2.2 : 1.8, strokeLinecap: "round" })] }));
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
- import { ChatWidgetTheme, Ticket } from '../../types';
2
+ import { Ticket, WidgetConfig } from '../../types';
3
3
  interface TicketScreenProps {
4
4
  tickets: Ticket[];
5
- theme?: ChatWidgetTheme;
6
- onRaiseTicket: (title: string, description: string) => void;
5
+ config: WidgetConfig;
6
+ onRaiseTicket: (title: string, desc: string, priority: Ticket['priority']) => void;
7
7
  }
8
8
  export declare const TicketScreen: React.FC<TicketScreenProps>;
9
9
  export {};
@@ -1,71 +1,54 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
- import { mergeTheme } from '../../utils/theme';
4
- export const TicketScreen = ({ tickets, theme, onRaiseTicket }) => {
5
- const t = mergeTheme(theme);
3
+ export const TicketScreen = ({ tickets, config, onRaiseTicket }) => {
6
4
  const [showForm, setShowForm] = useState(false);
7
5
  const [title, setTitle] = useState('');
8
6
  const [desc, setDesc] = useState('');
7
+ const [priority, setPriority] = useState('medium');
9
8
  const handleSubmit = () => {
10
9
  if (!title.trim())
11
10
  return;
12
- onRaiseTicket(title.trim(), desc.trim());
11
+ onRaiseTicket(title.trim(), desc.trim(), priority);
13
12
  setTitle('');
14
13
  setDesc('');
14
+ setPriority('medium');
15
15
  setShowForm(false);
16
16
  };
17
- const statusMeta = {
18
- 'open': { label: 'Open', bg: '#e6faf8', color: t.primaryColor },
19
- 'in-progress': { label: 'In Progress', bg: '#fffbeb', color: '#d97706' },
20
- 'resolved': { label: 'Resolved', bg: '#f0fdf4', color: '#16a34a' },
21
- 'closed': { label: 'Closed', bg: '#f3f4f6', color: '#6b7280' },
17
+ const sm = {
18
+ open: { label: 'Open', bg: `${config.primaryColor}14`, color: config.primaryColor },
19
+ 'in-progress': { label: 'In Progress', bg: '#fef3c7', color: '#d97706' },
20
+ resolved: { label: 'Resolved', bg: '#f0fdf4', color: '#16a34a' },
21
+ closed: { label: 'Closed', bg: '#f3f4f6', color: '#6b7280' },
22
22
  };
23
- const priorityMeta = {
24
- low: { label: 'Low', color: '#6b7280' },
25
- medium: { label: 'Medium', color: '#d97706' },
26
- high: { label: 'High', color: '#dc2626' },
23
+ const pm = {
24
+ low: { label: 'Low', color: '#6b7280' },
25
+ medium: { label: 'Medium', color: '#d97706' },
26
+ high: { label: 'High', color: '#ef4444' },
27
27
  };
28
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsx("div", { style: { backgroundColor: t.primaryColor, padding: '20px 20px 24px', flexShrink: 0 }, children: _jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { children: [_jsx("h2", { style: { margin: 0, fontSize: '20px', fontWeight: 800, color: '#fff', fontFamily: t.fontFamily, letterSpacing: '-0.02em' }, children: "Tickets" }), _jsxs("p", { style: { margin: '4px 0 0', fontSize: '13px', color: 'rgba(255,255,255,0.8)', fontFamily: t.fontFamily }, children: [tickets.length, " ticket", tickets.length !== 1 ? 's' : '', " raised"] })] }), _jsx("button", { onClick: () => setShowForm(v => !v), style: {
29
- background: 'rgba(255,255,255,0.25)', border: 'none', borderRadius: '20px',
30
- padding: '8px 16px', color: '#fff', fontWeight: 700, fontSize: '13px',
31
- cursor: 'pointer', fontFamily: t.fontFamily, display: 'flex', alignItems: 'center', gap: 5,
32
- }, children: showForm ? '✕ Cancel' : '+ New Ticket' })] }) }), showForm && (_jsxs("div", { style: {
33
- padding: '16px 20px',
34
- borderBottom: '1px solid #eef0f5',
35
- backgroundColor: '#fafcff',
36
- animation: 'cw-fadeUp 0.2s ease',
37
- flexShrink: 0,
38
- }, children: [_jsx("input", { placeholder: "Ticket title *", value: title, onChange: e => setTitle(e.target.value), style: {
39
- width: '100%', padding: '10px 14px', borderRadius: 10,
40
- border: '1.5px solid #e2e8f0', outline: 'none',
41
- fontFamily: t.fontFamily, fontSize: '14px', color: '#1a2332',
42
- marginBottom: 10, boxSizing: 'border-box',
43
- }, onFocus: e => (e.target.style.borderColor = t.primaryColor), onBlur: e => (e.target.style.borderColor = '#e2e8f0') }), _jsx("textarea", { placeholder: "Describe the issue (optional)", value: desc, onChange: e => setDesc(e.target.value), rows: 3, style: {
44
- width: '100%', padding: '10px 14px', borderRadius: 10,
45
- border: '1.5px solid #e2e8f0', outline: 'none', resize: 'none',
46
- fontFamily: t.fontFamily, fontSize: '14px', color: '#1a2332',
47
- marginBottom: 10, boxSizing: 'border-box',
48
- }, onFocus: e => (e.target.style.borderColor = t.primaryColor), onBlur: e => (e.target.style.borderColor = '#e2e8f0') }), _jsx("button", { onClick: handleSubmit, disabled: !title.trim(), style: {
49
- width: '100%', padding: '11px', borderRadius: 10,
50
- backgroundColor: title.trim() ? t.primaryColor : '#e2e8f0',
51
- color: title.trim() ? '#fff' : '#9aa3af',
52
- border: 'none', cursor: title.trim() ? 'pointer' : 'not-allowed',
53
- fontWeight: 700, fontSize: '14px', fontFamily: t.fontFamily,
54
- transition: 'background 0.2s',
55
- }, children: "Submit Ticket" })] })), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: tickets.length === 0 ? (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center', fontFamily: t.fontFamily }, children: [_jsx("div", { style: { fontSize: '36px', marginBottom: 12 }, children: "\uD83C\uDFAB" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No tickets yet" }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1' }, children: "Raise a ticket for major issues or changes" })] })) : (tickets.map((ticket, i) => {
56
- const sm = statusMeta[ticket.status];
57
- const pm = priorityMeta[ticket.priority];
58
- const date = new Date(ticket.createdAt).toLocaleDateString([], { month: 'short', day: 'numeric' });
59
- return (_jsxs("div", { style: {
60
- padding: '14px 20px',
61
- borderBottom: '1px solid #f3f4f6',
62
- fontFamily: t.fontFamily,
63
- animation: `cw-fadeUp 0.3s ease both`,
64
- animationDelay: `${i * 0.05}s`,
65
- }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 6 }, children: [_jsx("span", { style: { fontWeight: 700, fontSize: '14px', color: '#1a2332', flex: 1, paddingRight: 12 }, children: ticket.title }), _jsx("span", { style: {
66
- fontSize: '10px', fontWeight: 700, padding: '3px 9px', borderRadius: 20,
67
- backgroundColor: sm.bg, color: sm.color, whiteSpace: 'nowrap',
68
- textTransform: 'uppercase', letterSpacing: '0.04em',
69
- }, children: sm.label })] }), ticket.description && (_jsx("div", { style: { fontSize: '13px', color: '#7b8fa1', marginBottom: 8, lineHeight: 1.5 }, children: ticket.description })), _jsxs("div", { style: { display: 'flex', gap: 12, fontSize: '11px', color: '#b0bec5' }, children: [_jsx("span", { style: { color: pm.color, fontWeight: 600 }, children: pm.label }), _jsxs("span", { children: ["#", ticket.id.slice(-6).toUpperCase()] }), _jsx("span", { children: date })] })] }, ticket.id));
70
- })) })] }));
28
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsx("div", { style: {
29
+ background: `linear-gradient(135deg,${config.primaryColor},${config.primaryColor}cc)`,
30
+ padding: '18px 18px 22px', flexShrink: 0, position: 'relative',
31
+ }, children: _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }, children: [_jsxs("div", { children: [_jsx("h2", { style: { margin: 0, fontSize: 20, fontWeight: 800, color: '#fff', letterSpacing: '-0.02em' }, children: "Tickets" }), _jsxs("p", { style: { margin: '3px 0 0', fontSize: 12, color: 'rgba(255,255,255,0.8)' }, children: [tickets.length, " ticket", tickets.length !== 1 ? 's' : '', " raised"] })] }), _jsx("button", { onClick: () => setShowForm(v => !v), style: {
32
+ background: 'rgba(255,255,255,0.22)', border: 'none', borderRadius: 20,
33
+ padding: '7px 14px', color: '#fff', fontWeight: 700, fontSize: 13,
34
+ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 5,
35
+ }, children: showForm ? '✕ Cancel' : '+ New' })] }) }), showForm && (_jsxs("div", { style: { padding: '16px', borderBottom: '1px solid #eef0f5', background: '#fafcff', flexShrink: 0, animation: 'cw-fadeUp 0.2s ease' }, 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: 3, style: Object.assign(Object.assign({}, inputStyle(config.primaryColor)), { resize: 'none', marginTop: 8 }), onFocus: e => (e.target.style.borderColor = config.primaryColor), onBlur: e => (e.target.style.borderColor = '#e5e7eb') }), _jsx("div", { style: { display: 'flex', gap: 8, marginTop: 8, marginBottom: 12 }, children: ['low', 'medium', 'high'].map(p => (_jsx("button", { onClick: () => setPriority(p), style: {
36
+ flex: 1, padding: '6px', border: `1.5px solid ${priority === p ? pm[p].color : '#e5e7eb'}`,
37
+ borderRadius: 8, background: priority === p ? pm[p].color + '15' : '#fff',
38
+ color: pm[p].color, fontWeight: 700, fontSize: 12, cursor: 'pointer', textTransform: 'capitalize',
39
+ transition: 'all 0.15s',
40
+ }, children: pm[p].label }, p))) }), _jsx("button", { onClick: handleSubmit, disabled: !title.trim(), style: {
41
+ width: '100%', padding: '10px', borderRadius: 10, border: 'none',
42
+ background: title.trim() ? config.primaryColor : '#e5e7eb',
43
+ color: title.trim() ? '#fff' : '#9ca3af',
44
+ fontWeight: 700, fontSize: 14, cursor: title.trim() ? 'pointer' : 'not-allowed',
45
+ }, children: "Submit Ticket" })] })), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: tickets.length === 0 ? (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 36, marginBottom: 10 }, children: "\uD83C\uDFAB" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No tickets yet" }), _jsx("div", { style: { fontSize: 13, color: '#7b8fa1' }, children: "Raise a ticket for major issues" })] })) : tickets.map((t, i) => (_jsxs("div", { style: { padding: '14px 16px', borderBottom: '1px solid #f0f2f5', animation: `cw-fadeUp 0.3s ease both`, animationDelay: `${i * 0.05}s` }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 5 }, children: [_jsx("span", { style: { fontWeight: 700, fontSize: 14, color: '#1a2332', flex: 1, paddingRight: 10 }, children: t.title }), _jsx("span", { style: { fontSize: 10, fontWeight: 700, padding: '3px 9px', borderRadius: 20, backgroundColor: sm[t.status].bg, color: sm[t.status].color, whiteSpace: 'nowrap', textTransform: 'uppercase', letterSpacing: '0.04em', flexShrink: 0 }, children: sm[t.status].label })] }), t.description && _jsx("p", { style: { margin: '0 0 7px', fontSize: 13, color: '#7b8fa1', lineHeight: 1.5 }, children: t.description }), _jsxs("div", { style: { display: 'flex', gap: 10, fontSize: 11, color: '#b0bec5' }, children: [_jsxs("span", { style: { color: pm[t.priority].color, fontWeight: 700 }, children: ["\u25CF ", pm[t.priority].label] }), _jsxs("span", { children: ["#", t.id] }), _jsx("span", { children: new Date(t.createdAt).toLocaleDateString([], { month: 'short', day: 'numeric' }) })] })] }, t.id))) })] }));
71
46
  };
47
+ function inputStyle(primaryColor) {
48
+ return {
49
+ width: '100%', padding: '9px 13px', borderRadius: 10,
50
+ border: '1.5px solid #e5e7eb', outline: 'none',
51
+ fontSize: 14, color: '#1a2332', boxSizing: 'border-box',
52
+ fontFamily: 'inherit', transition: 'border-color 0.2s',
53
+ };
54
+ }
@@ -1,11 +1,9 @@
1
1
  import React from 'react';
2
- import { ChatUser, ChatWidgetTheme, UserListContext } from '../../types';
2
+ import { ChatUser, UserListContext } from '../../types';
3
3
  interface UserListScreenProps {
4
4
  context: UserListContext;
5
5
  users: ChatUser[];
6
- loading: boolean;
7
- error: string | null;
8
- theme?: ChatWidgetTheme;
6
+ primaryColor: string;
9
7
  onBack: () => void;
10
8
  onSelectUser: (user: ChatUser) => void;
11
9
  }
@@ -1,64 +1,35 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { mergeTheme } from '../../utils/theme';
3
- export const UserListScreen = ({ context, users, loading, error, theme, onBack, onSelectUser, }) => {
4
- const t = mergeTheme(theme);
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { avatarColor, initials } from '../../utils/chat';
3
+ export const UserListScreen = ({ context, users, primaryColor, onBack, onSelectUser, }) => {
5
4
  const title = context === 'support' ? 'Need Support' : 'New Conversation';
6
- const subtitle = context === 'support'
7
- ? 'Choose a support agent'
8
- : 'Choose a colleague to chat with';
9
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideInRight 0.25s ease' }, children: [_jsxs("div", { style: {
10
- backgroundColor: t.primaryColor,
11
- padding: '16px 20px',
12
- display: 'flex',
13
- alignItems: 'center',
14
- gap: '12px',
15
- flexShrink: 0,
16
- }, children: [_jsx("button", { onClick: onBack, style: {
17
- background: 'rgba(255,255,255,0.2)',
18
- border: 'none',
19
- borderRadius: '50%',
20
- width: '34px',
21
- height: '34px',
22
- display: 'flex',
23
- alignItems: 'center',
24
- justifyContent: 'center',
25
- cursor: 'pointer',
26
- flexShrink: 0,
27
- }, 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.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700, fontSize: '16px', color: '#fff', fontFamily: t.fontFamily }, children: title }), _jsx("div", { style: { fontSize: '12px', color: 'rgba(255,255,255,0.8)', fontFamily: t.fontFamily }, children: subtitle })] })] }), _jsxs("div", { style: { flex: 1, overflowY: 'auto', padding: '8px 0' }, children: [loading && _jsx(UserListSkeleton, {}), error && _jsx(ErrorState, { message: error, color: t.primaryColor, font: t.fontFamily }), !loading && !error && users.length === 0 && (_jsx(EmptyState, { color: t.primaryColor, font: t.fontFamily })), !loading && !error && users.map((user, i) => (_jsx(UserRow, { user: user, index: i, primaryColor: t.primaryColor, fontFamily: t.fontFamily, onClick: () => onSelectUser(user) }, user.uid)))] })] }));
5
+ const subtitle = context === 'support' ? 'Choose a support agent' : 'Choose a colleague';
6
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideIn 0.22s ease' }, children: [_jsxs("div", { style: { background: `linear-gradient(135deg,${primaryColor},${primaryColor}cc)`, padding: '14px 18px', display: 'flex', alignItems: 'center', gap: 12, flexShrink: 0 }, children: [_jsx(BackBtn, { onClick: onBack }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700, fontSize: 16, color: '#fff' }, children: title }), _jsx("div", { style: { fontSize: 12, color: 'rgba(255,255,255,0.8)' }, children: subtitle })] })] }), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: users.length === 0 ? (_jsx(Empty, {})) : users.map((u, i) => (_jsxs("button", { onClick: () => onSelectUser(u), style: {
7
+ width: '100%', padding: '13px 18px', display: 'flex',
8
+ alignItems: 'center', gap: 13, background: 'transparent',
9
+ border: 'none', borderBottom: '1px solid #f0f2f5',
10
+ cursor: 'pointer', textAlign: 'left',
11
+ animation: `cw-fadeUp 0.28s ease both`, animationDelay: `${i * 0.05}s`,
12
+ transition: 'background 0.14s',
13
+ }, onMouseEnter: e => e.currentTarget.style.background = '#f8faff', onMouseLeave: e => e.currentTarget.style.background = 'transparent', children: [_jsxs("div", { style: { position: 'relative', flexShrink: 0 }, children: [_jsx("div", { style: {
14
+ width: 44, height: 44, borderRadius: '50%',
15
+ backgroundColor: avatarColor(u.name),
16
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
17
+ color: '#fff', fontWeight: 700, fontSize: 14,
18
+ }, children: initials(u.name) }), _jsx("span", { style: {
19
+ position: 'absolute', bottom: 1, right: 1,
20
+ width: 11, height: 11, borderRadius: '50%', border: '2px solid #fff',
21
+ backgroundColor: u.status === 'online' ? '#22c55e' : u.status === 'away' ? '#f59e0b' : '#d1d5db',
22
+ } })] }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: 14, color: '#1a2332', marginBottom: 2 }, children: u.name }), _jsxs("div", { style: { fontSize: 12, color: '#7b8fa1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: [u.designation, " \u00B7 ", u.project] })] }), _jsx("span", { style: {
23
+ fontSize: 10, fontWeight: 700, padding: '3px 9px', borderRadius: 20,
24
+ textTransform: 'uppercase', letterSpacing: '0.05em', flexShrink: 0,
25
+ background: u.type === 'developer' ? `${primaryColor}15` : '#f0fdf4',
26
+ color: u.type === 'developer' ? primaryColor : '#16a34a',
27
+ border: `1px solid ${u.type === 'developer' ? primaryColor + '30' : '#16a34a30'}`,
28
+ }, children: u.type === 'developer' ? 'Dev' : 'User' })] }, u.uid))) })] }));
28
29
  };
29
- // ── Sub-components ─────────────────────────────────────────────────────────────
30
- const UserRow = ({ user, index, primaryColor, fontFamily, onClick }) => {
31
- const initials = user.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
32
- const avatarColors = ['#1aaa96', '#2563EB', '#7C3AED', '#D97706', '#DC2626', '#059669'];
33
- const bg = avatarColors[user.name.charCodeAt(0) % avatarColors.length];
34
- return (_jsxs("button", { onClick: onClick, style: {
35
- width: '100%',
36
- padding: '14px 20px',
37
- display: 'flex',
38
- alignItems: 'center',
39
- gap: '14px',
40
- background: 'transparent',
41
- border: 'none',
42
- borderBottom: '1px solid #f3f4f6',
43
- cursor: 'pointer',
44
- textAlign: 'left',
45
- fontFamily,
46
- animation: 'cw-fadeUp 0.3s ease both',
47
- animationDelay: `${index * 0.05}s`,
48
- transition: 'background 0.15s',
49
- }, onMouseEnter: e => e.currentTarget.style.background = '#f8fdfc', onMouseLeave: e => e.currentTarget.style.background = 'transparent', children: [_jsx("div", { style: {
50
- width: '46px', height: '46px', borderRadius: '50%',
51
- backgroundColor: bg, flexShrink: 0,
52
- display: 'flex', alignItems: 'center', justifyContent: 'center',
53
- color: '#fff', fontWeight: 700, fontSize: '15px',
54
- }, children: initials }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: '14px', color: '#1a2332', marginBottom: '2px' }, children: user.name }), _jsx("div", { style: { fontSize: '12px', color: '#7b8fa1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: user.project || user.email })] }), _jsx("span", { style: {
55
- fontSize: '10px', fontWeight: 700, padding: '3px 9px',
56
- borderRadius: '20px', textTransform: 'uppercase', letterSpacing: '0.05em',
57
- backgroundColor: user.type === 'developer' ? '#e6faf8' : '#eff6ff',
58
- color: user.type === 'developer' ? primaryColor : '#2563EB',
59
- border: `1px solid ${user.type === 'developer' ? primaryColor + '30' : '#2563eb30'}`,
60
- }, children: user.type === 'developer' ? 'Dev' : 'User' })] }));
61
- };
62
- const UserListSkeleton = () => (_jsx(_Fragment, { children: [1, 2, 3, 4].map(i => (_jsxs("div", { style: { padding: '14px 20px', display: 'flex', gap: '14px', alignItems: 'center' }, children: [_jsx("div", { style: { width: 46, height: 46, borderRadius: '50%', background: '#f0f0f0', flexShrink: 0 } }), _jsxs("div", { style: { flex: 1 }, children: [_jsx("div", { style: { height: 13, width: '55%', background: '#f0f0f0', borderRadius: 6, marginBottom: 7 } }), _jsx("div", { style: { height: 11, width: '38%', background: '#f0f0f0', borderRadius: 6 } })] })] }, i))) }));
63
- const ErrorState = ({ message, color, font }) => (_jsxs("div", { style: { padding: '40px 24px', textAlign: 'center', fontFamily: font }, children: [_jsx("div", { style: { fontSize: '32px', marginBottom: '10px' }, children: "\u26A0\uFE0F" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: '6px' }, children: "Could not load users" }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1' }, children: message })] }));
64
- const EmptyState = ({ color, font }) => (_jsxs("div", { style: { padding: '40px 24px', textAlign: 'center', fontFamily: font }, children: [_jsx("div", { style: { fontSize: '32px', marginBottom: '10px' }, children: "\uD83D\uDC65" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: '6px' }, children: "No users available" }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1' }, children: "Check back later" })] }));
30
+ const BackBtn = ({ onClick }) => (_jsx("button", { onClick: onClick, style: {
31
+ background: 'rgba(255,255,255,0.22)', border: 'none', borderRadius: '50%',
32
+ width: 32, height: 32, display: 'flex', alignItems: 'center', justifyContent: 'center',
33
+ cursor: 'pointer', flexShrink: 0,
34
+ }, children: _jsx("svg", { width: "16", height: "16", 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" }) }) }));
35
+ const Empty = () => (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 36, marginBottom: 10 }, children: "\uD83D\uDC65" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No users available" }), _jsx("div", { style: { fontSize: 13, color: '#7b8fa1' }, children: "Check back later" })] }));
@@ -1,3 +1,7 @@
1
- import { ChatConfig } from '../types';
2
- export declare function loadChatConfig(): ChatConfig;
3
- export declare function buildUserListUrl(config: ChatConfig): string;
1
+ import { LocalEnvConfig, RemoteChatData } from '../types';
2
+ /**
3
+ * Loads remote widget config once via GET. Uses query params `key` and `widget` so the
4
+ * server can validate the request without custom headers (avoids CORS preflight failures).
5
+ */
6
+ export declare function fetchRemoteChatData(apiKey: string, widgetId: string): Promise<RemoteChatData>;
7
+ export declare function loadLocalConfig(): LocalEnvConfig;
@@ -1,3 +1,7 @@
1
+ /** Default JSON endpoint; override with REACT_APP_CHAT_CONFIG_URL / NEXT_PUBLIC_CHAT_CONFIG_URL */
2
+ const DEFAULT_CHAT_DATA_BASE = 'https://window.mscorpres.com/TEST/chatData.json';
3
+ const DEMO_API_KEY = 'demo1234';
4
+ const DEMO_WIDGET_ID = 'demo';
1
5
  function getEnv(key) {
2
6
  var _a, _b, _c;
3
7
  if (typeof process !== 'undefined' && process.env) {
@@ -5,34 +9,33 @@ function getEnv(key) {
5
9
  }
6
10
  return undefined;
7
11
  }
8
- function validateStatus(v) {
9
- if (v === 'ACTIVE' || v === 'DISABLE' || v === 'MAINTENANCE')
10
- return v;
11
- console.warn(`[ChatWidget] Invalid CHAT_STATUS "${v}". Defaulting to DISABLE.`);
12
- return 'DISABLE';
12
+ function getChatDataBaseUrl() {
13
+ var _a;
14
+ return ((_a = getEnv('CHAT_CONFIG_URL')) === null || _a === void 0 ? void 0 : _a.trim()) || DEFAULT_CHAT_DATA_BASE;
13
15
  }
14
- function validateChatType(v) {
15
- if (v === 'SUPPORT' || v === 'CHAT' || v === 'BOTH')
16
- return v;
17
- console.warn(`[ChatWidget] Invalid CHAT_TYPE "${v}". Defaulting to SUPPORT.`);
18
- return 'SUPPORT';
16
+ /**
17
+ * Loads remote widget config once via GET. Uses query params `key` and `widget` so the
18
+ * server can validate the request without custom headers (avoids CORS preflight failures).
19
+ */
20
+ export async function fetchRemoteChatData(apiKey, widgetId) {
21
+ const base = getChatDataBaseUrl();
22
+ const url = new URL(base, typeof window !== 'undefined' ? window.location.origin : 'https://localhost');
23
+ url.searchParams.set('key', apiKey);
24
+ url.searchParams.set('widget', widgetId);
25
+ const res = await fetch(url.toString(), {
26
+ method: 'GET',
27
+ credentials: 'omit',
28
+ mode: 'cors',
29
+ headers: { Accept: 'application/json' },
30
+ });
31
+ if (!res.ok)
32
+ throw new Error(`Failed to load chat config: ${res.status}`);
33
+ return res.json();
19
34
  }
20
- export function loadChatConfig() {
35
+ export function loadLocalConfig() {
21
36
  var _a, _b;
22
- const portStr = getEnv('CHAT_HOST_PORT');
23
- const hostPort = portStr ? parseInt(portStr, 10) : null;
24
37
  return {
25
- hostUrl: (_a = getEnv('CHAT_HOST_URL')) !== null && _a !== void 0 ? _a : 'http://localhost',
26
- hostPort,
27
- userListEndpoint: (_b = getEnv('CHAT_USER_LIST')) !== null && _b !== void 0 ? _b : 'api/users',
28
- status: validateStatus(getEnv('CHAT_STATUS')),
29
- chatType: validateChatType(getEnv('CHAT_TYPE')),
38
+ apiKey: (_a = getEnv('CHAT_API_KEY')) !== null && _a !== void 0 ? _a : DEMO_API_KEY,
39
+ widgetId: (_b = getEnv('CHAT_WIDGET_ID')) !== null && _b !== void 0 ? _b : DEMO_WIDGET_ID,
30
40
  };
31
41
  }
32
- export function buildUserListUrl(config) {
33
- const base = config.hostUrl.replace(/\/$/, '');
34
- const endpoint = config.userListEndpoint.replace(/^\//, '');
35
- // Port is optional
36
- const portPart = config.hostPort ? `:${config.hostPort}` : '';
37
- return `${base}${portPart}/${endpoint}`;
38
- }
@@ -1,8 +1,13 @@
1
1
  import { ChatMessage, ChatUser } from '../types';
2
- export declare function useChat(): {
2
+ export declare function useChat(initialMessages?: ChatMessage[]): {
3
3
  messages: ChatMessage[];
4
4
  activeUser: ChatUser | null;
5
- selectUser: (user: ChatUser) => void;
6
- sendMessage: (text: string) => void;
5
+ isPaused: boolean;
6
+ isReported: boolean;
7
+ selectUser: (user: ChatUser, history?: ChatMessage[]) => void;
8
+ sendMessage: (text: string, type?: ChatMessage["type"], extra?: Partial<ChatMessage>) => void;
9
+ togglePause: () => void;
10
+ reportChat: () => void;
7
11
  clearChat: () => void;
12
+ setMessages: import("react").Dispatch<import("react").SetStateAction<ChatMessage[]>>;
8
13
  };
@@ -1,26 +1,30 @@
1
1
  import { useState, useCallback } from 'react';
2
- export function useChat() {
3
- const [messages, setMessages] = useState([]);
2
+ export function useChat(initialMessages = []) {
3
+ const [messages, setMessages] = useState(initialMessages);
4
4
  const [activeUser, setActiveUser] = useState(null);
5
- const selectUser = useCallback((user) => {
5
+ const [isPaused, setIsPaused] = useState(false);
6
+ const [isReported, setIsReported] = useState(false);
7
+ const selectUser = useCallback((user, history = []) => {
6
8
  setActiveUser(user);
7
- setMessages([]);
8
- // TODO: socket.emit('join', user.uid); socket.on('message', handler)
9
+ setMessages(history);
10
+ setIsPaused(false);
11
+ setIsReported(false);
12
+ // TODO: socket.emit('join', { roomId: user.uid });
13
+ // TODO: socket.on('message', msg => setMessages(prev => [...prev, msg]));
9
14
  }, []);
10
- const sendMessage = useCallback((text) => {
11
- if (!activeUser || !text.trim())
15
+ const sendMessage = useCallback((text, type = 'text', extra = {}) => {
16
+ if (!activeUser || isPaused)
12
17
  return;
13
- const msg = {
14
- id: `msg_${Date.now()}`,
15
- senderId: 'me',
16
- receiverId: activeUser.uid,
17
- text: text.trim(),
18
- timestamp: new Date(),
19
- status: 'sent',
20
- };
18
+ const msg = Object.assign({ id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`, senderId: 'me', receiverId: activeUser.uid, text, timestamp: new Date().toISOString(), type, status: 'sent' }, extra);
21
19
  setMessages(prev => [...prev, msg]);
22
- // TODO: socket.emit('message', msg)
23
- }, [activeUser]);
20
+ // TODO: socket.emit('message', msg);
21
+ }, [activeUser, isPaused]);
22
+ const togglePause = useCallback(() => setIsPaused(p => !p), []);
23
+ const reportChat = useCallback(() => { setIsReported(true); /* TODO: API call */ }, []);
24
24
  const clearChat = useCallback(() => { setMessages([]); setActiveUser(null); }, []);
25
- return { messages, activeUser, selectUser, sendMessage, clearChat };
25
+ return {
26
+ messages, activeUser, isPaused, isReported,
27
+ selectUser, sendMessage, togglePause, reportChat, clearChat,
28
+ setMessages,
29
+ };
26
30
  }
@@ -0,0 +1,6 @@
1
+ import { RemoteChatData } from '../types';
2
+ export declare function useRemoteConfig(apiKey: string, widgetId: string): {
3
+ data: RemoteChatData | null;
4
+ loading: boolean;
5
+ error: string | null;
6
+ };
@@ -0,0 +1,26 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { fetchRemoteChatData } from '../config';
3
+ export function useRemoteConfig(apiKey, widgetId) {
4
+ const [data, setData] = useState(null);
5
+ const [loading, setLoading] = useState(true);
6
+ const [error, setError] = useState(null);
7
+ useEffect(() => {
8
+ let cancelled = false;
9
+ setLoading(true);
10
+ fetchRemoteChatData(apiKey, widgetId)
11
+ .then(d => { if (!cancelled) {
12
+ setData(d);
13
+ setError(null);
14
+ setLoading(false);
15
+ } })
16
+ .catch(e => {
17
+ if (!cancelled) {
18
+ const msg = e instanceof Error ? e.message : String(e);
19
+ setError(msg || 'Network error while loading configuration');
20
+ setLoading(false);
21
+ }
22
+ });
23
+ return () => { cancelled = true; };
24
+ }, [apiKey, widgetId]);
25
+ return { data, loading, error };
26
+ }
@@ -0,0 +1,11 @@
1
+ import { CallSession, ChatUser } from '../types';
2
+ export declare function useWebRTC(): {
3
+ session: CallSession;
4
+ localVideoRef: import("react").MutableRefObject<HTMLVideoElement | null>;
5
+ remoteVideoRef: import("react").MutableRefObject<HTMLVideoElement | null>;
6
+ startCall: (peer: ChatUser, withVideo?: boolean) => Promise<void>;
7
+ acceptCall: (offer: RTCSessionDescriptionInit, peer: ChatUser, withVideo?: boolean) => Promise<void>;
8
+ endCall: () => void;
9
+ toggleMute: () => void;
10
+ toggleCamera: () => void;
11
+ };