ajaxter-chat 1.0.3 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +124 -241
  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 +19 -0
  7. package/dist/components/ChatScreen/index.js +168 -0
  8. package/dist/components/ChatWidget.d.ts +0 -24
  9. package/dist/components/ChatWidget.js +228 -43
  10. package/dist/components/EmojiPicker/index.d.ts +8 -0
  11. package/dist/components/EmojiPicker/index.js +18 -0
  12. package/dist/components/HomeScreen/index.d.ts +8 -0
  13. package/dist/components/HomeScreen/index.js +55 -0
  14. package/dist/components/MaintenanceView/index.d.ts +0 -1
  15. package/dist/components/MaintenanceView/index.js +13 -52
  16. package/dist/components/RecentChatsScreen/index.d.ts +17 -0
  17. package/dist/components/RecentChatsScreen/index.js +8 -0
  18. package/dist/components/Tabs/BottomTabs.d.ts +10 -0
  19. package/dist/components/Tabs/BottomTabs.js +34 -0
  20. package/dist/components/TicketScreen/index.d.ts +9 -0
  21. package/dist/components/TicketScreen/index.js +54 -0
  22. package/dist/components/UserListScreen/index.d.ts +11 -0
  23. package/dist/components/UserListScreen/index.js +35 -0
  24. package/dist/config/index.d.ts +3 -16
  25. package/dist/config/index.js +20 -103
  26. package/dist/hooks/useChat.d.ts +10 -9
  27. package/dist/hooks/useChat.js +22 -40
  28. package/dist/hooks/useRemoteConfig.d.ts +6 -0
  29. package/dist/hooks/useRemoteConfig.js +22 -0
  30. package/dist/hooks/useWebRTC.d.ts +11 -0
  31. package/dist/hooks/useWebRTC.js +112 -0
  32. package/dist/index.d.ts +16 -11
  33. package/dist/index.js +15 -16
  34. package/dist/types/index.d.ts +66 -38
  35. package/dist/utils/chat.d.ts +13 -0
  36. package/dist/utils/chat.js +62 -0
  37. package/dist/utils/theme.d.ts +3 -2
  38. package/dist/utils/theme.js +13 -21
  39. package/package.json +10 -20
  40. package/public/chatData.json +162 -0
  41. package/src/components/BlockList/index.tsx +94 -0
  42. package/src/components/CallScreen/index.tsx +144 -0
  43. package/src/components/ChatScreen/index.tsx +469 -0
  44. package/src/components/ChatWidget.tsx +471 -0
  45. package/src/components/EmojiPicker/index.tsx +48 -0
  46. package/src/components/HomeScreen/index.tsx +106 -0
  47. package/src/components/MaintenanceView/index.tsx +38 -0
  48. package/src/components/RecentChatsScreen/index.tsx +63 -0
  49. package/src/components/Tabs/BottomTabs.tsx +90 -0
  50. package/src/components/TicketScreen/index.tsx +124 -0
  51. package/src/components/UserListScreen/index.tsx +103 -0
  52. package/src/config/index.ts +40 -0
  53. package/src/hooks/useChat.ts +48 -0
  54. package/src/hooks/useRemoteConfig.ts +20 -0
  55. package/src/hooks/useWebRTC.ts +130 -0
  56. package/src/index.ts +29 -0
  57. package/src/types/index.ts +127 -0
  58. package/src/utils/chat.ts +70 -0
  59. package/src/utils/theme.ts +27 -0
  60. package/dist/components/BottomNav/index.d.ts +0 -10
  61. package/dist/components/BottomNav/index.js +0 -32
  62. package/dist/components/ChatBox/index.d.ts +0 -15
  63. package/dist/components/ChatBox/index.js +0 -228
  64. package/dist/components/ChatButton/index.d.ts +0 -9
  65. package/dist/components/ChatButton/index.js +0 -17
  66. package/dist/components/ChatWindow/index.d.ts +0 -10
  67. package/dist/components/ChatWindow/index.js +0 -286
  68. package/dist/components/HomeView/index.d.ts +0 -12
  69. package/dist/components/HomeView/index.js +0 -51
  70. package/dist/components/UserList/index.d.ts +0 -13
  71. package/dist/components/UserList/index.js +0 -136
  72. package/dist/hooks/useUsers.d.ts +0 -14
  73. package/dist/hooks/useUsers.js +0 -32
  74. package/dist/services/userService.d.ts +0 -7
  75. package/dist/services/userService.js +0 -18
@@ -0,0 +1,112 @@
1
+ import { useState, useRef, useCallback, useEffect } from 'react';
2
+ const ICE_SERVERS = [
3
+ { urls: 'stun:stun.l.google.com:19302' },
4
+ { urls: 'stun:stun1.l.google.com:19302' },
5
+ ];
6
+ export function useWebRTC() {
7
+ const [session, setSession] = useState({
8
+ state: 'idle',
9
+ peer: null,
10
+ startedAt: null,
11
+ isMuted: false,
12
+ isCameraOn: false,
13
+ });
14
+ const pcRef = useRef(null);
15
+ const localStream = useRef(null);
16
+ const localVideoRef = useRef(null);
17
+ const remoteVideoRef = useRef(null);
18
+ const updateSession = (patch) => setSession(prev => (Object.assign(Object.assign({}, prev), patch)));
19
+ /** Start an outgoing call */
20
+ const startCall = useCallback(async (peer, withVideo = false) => {
21
+ updateSession({ state: 'calling', peer });
22
+ const stream = await navigator.mediaDevices.getUserMedia({
23
+ audio: true,
24
+ video: withVideo,
25
+ });
26
+ localStream.current = stream;
27
+ if (localVideoRef.current)
28
+ localVideoRef.current.srcObject = stream;
29
+ const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });
30
+ pcRef.current = pc;
31
+ stream.getTracks().forEach(t => pc.addTrack(t, stream));
32
+ pc.ontrack = e => {
33
+ if (remoteVideoRef.current)
34
+ remoteVideoRef.current.srcObject = e.streams[0];
35
+ updateSession({ state: 'connected', startedAt: new Date(), isCameraOn: withVideo });
36
+ };
37
+ pc.onicecandidate = e => {
38
+ if (e.candidate) {
39
+ // TODO: socket.emit('ice-candidate', { candidate: e.candidate, to: peer.uid });
40
+ console.log('[WebRTC] ICE candidate ready — send via signalling:', e.candidate);
41
+ }
42
+ };
43
+ const offer = await pc.createOffer();
44
+ await pc.setLocalDescription(offer);
45
+ // TODO: socket.emit('call-offer', { offer, to: peer.uid, from: 'me' });
46
+ console.log('[WebRTC] Offer created — send via signalling server:', offer);
47
+ }, []);
48
+ /** Accept an incoming call offer */
49
+ const acceptCall = useCallback(async (offer, peer, withVideo = false) => {
50
+ updateSession({ state: 'connected', peer, startedAt: new Date(), isCameraOn: withVideo });
51
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: withVideo });
52
+ localStream.current = stream;
53
+ if (localVideoRef.current)
54
+ localVideoRef.current.srcObject = stream;
55
+ const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });
56
+ pcRef.current = pc;
57
+ stream.getTracks().forEach(t => pc.addTrack(t, stream));
58
+ pc.ontrack = e => {
59
+ if (remoteVideoRef.current)
60
+ remoteVideoRef.current.srcObject = e.streams[0];
61
+ };
62
+ pc.onicecandidate = e => {
63
+ if (e.candidate) {
64
+ // TODO: socket.emit('ice-candidate', { candidate: e.candidate, to: peer.uid });
65
+ }
66
+ };
67
+ await pc.setRemoteDescription(offer);
68
+ const answer = await pc.createAnswer();
69
+ await pc.setLocalDescription(answer);
70
+ // TODO: socket.emit('call-answer', { answer, to: peer.uid });
71
+ }, []);
72
+ /** Hang up */
73
+ const endCall = useCallback(() => {
74
+ var _a, _b;
75
+ (_a = localStream.current) === null || _a === void 0 ? void 0 : _a.getTracks().forEach(t => t.stop());
76
+ (_b = pcRef.current) === null || _b === void 0 ? void 0 : _b.close();
77
+ pcRef.current = null;
78
+ localStream.current = null;
79
+ updateSession({ state: 'ended', peer: null, startedAt: null });
80
+ setTimeout(() => updateSession({ state: 'idle' }), 1800);
81
+ }, []);
82
+ const toggleMute = useCallback(() => {
83
+ if (!localStream.current)
84
+ return;
85
+ const enabled = !session.isMuted;
86
+ localStream.current.getAudioTracks().forEach(t => { t.enabled = enabled; });
87
+ updateSession({ isMuted: !session.isMuted });
88
+ }, [session.isMuted]);
89
+ const toggleCamera = useCallback(() => {
90
+ if (!localStream.current)
91
+ return;
92
+ const enabled = !session.isCameraOn;
93
+ localStream.current.getVideoTracks().forEach(t => { t.enabled = enabled; });
94
+ updateSession({ isCameraOn: enabled });
95
+ }, [session.isCameraOn]);
96
+ // Cleanup on unmount
97
+ useEffect(() => () => {
98
+ var _a, _b;
99
+ (_a = localStream.current) === null || _a === void 0 ? void 0 : _a.getTracks().forEach(t => t.stop());
100
+ (_b = pcRef.current) === null || _b === void 0 ? void 0 : _b.close();
101
+ }, []);
102
+ return {
103
+ session,
104
+ localVideoRef,
105
+ remoteVideoRef,
106
+ startCall,
107
+ acceptCall,
108
+ endCall,
109
+ toggleMute,
110
+ toggleCamera,
111
+ };
112
+ }
package/dist/index.d.ts CHANGED
@@ -1,13 +1,18 @@
1
- export { ChatWidget } from './components/ChatWidget';
2
- export { default } from './components/ChatWidget';
3
- export { ChatButton } from './components/ChatButton';
4
- export { ChatWindow } from './components/ChatWindow';
5
- export { UserList } from './components/UserList';
6
- export { ChatBox } from './components/ChatBox';
1
+ export { ChatWidget, default } from './components/ChatWidget';
2
+ export { HomeScreen } from './components/HomeScreen';
3
+ export { UserListScreen } from './components/UserListScreen';
4
+ export { ChatScreen } from './components/ChatScreen';
5
+ export { RecentChatsScreen } from './components/RecentChatsScreen';
6
+ export { TicketScreen } from './components/TicketScreen';
7
+ export { BlockListScreen } from './components/BlockList';
8
+ export { CallScreen } from './components/CallScreen';
7
9
  export { MaintenanceView } from './components/MaintenanceView';
8
- export { useUsers } from './hooks/useUsers';
10
+ export { BottomTabs } from './components/Tabs/BottomTabs';
11
+ export { EmojiPicker } from './components/EmojiPicker';
9
12
  export { useChat } from './hooks/useChat';
10
- export { loadChatConfig, buildUserListUrl } from './config';
11
- export { fetchUsers } from './services/userService';
12
- export type { ChatUser, ChatMessage, ChatConfig, ChatWidgetTheme, ChatWidgetProps, ChatStatus, ChatType, UserType, TabType, BottomNavTab, HomeFlow, RaisedTicket, RecentChat, } from './types';
13
- export { defaultTheme, mergeTheme } from './utils/theme';
13
+ export { useWebRTC } from './hooks/useWebRTC';
14
+ export { useRemoteConfig } from './hooks/useRemoteConfig';
15
+ export { loadLocalConfig, fetchRemoteChatData } from './config';
16
+ export { mergeTheme, darken } from './utils/theme';
17
+ export { avatarColor, initials, formatTime, formatDate, generateTranscript, downloadText } from './utils/chat';
18
+ export type { ChatWidgetProps, ChatWidgetTheme, WidgetConfig, RemoteChatData, ChatUser, ChatMessage, Ticket, RecentChat, CallSession, CallState, ChatStatus, ChatType, UserType, OnlineStatus, Screen, BottomTab, UserListContext, MessageType, LocalEnvConfig, } from './types';
package/dist/index.js CHANGED
@@ -1,18 +1,17 @@
1
- // Main component
2
- export { ChatWidget } from './components/ChatWidget';
3
- export { default } from './components/ChatWidget';
4
- // Sub-components (for advanced customization)
5
- export { ChatButton } from './components/ChatButton';
6
- export { ChatWindow } from './components/ChatWindow';
7
- export { UserList } from './components/UserList';
8
- export { ChatBox } from './components/ChatBox';
1
+ export { ChatWidget, default } from './components/ChatWidget';
2
+ export { HomeScreen } from './components/HomeScreen';
3
+ export { UserListScreen } from './components/UserListScreen';
4
+ export { ChatScreen } from './components/ChatScreen';
5
+ export { RecentChatsScreen } from './components/RecentChatsScreen';
6
+ export { TicketScreen } from './components/TicketScreen';
7
+ export { BlockListScreen } from './components/BlockList';
8
+ export { CallScreen } from './components/CallScreen';
9
9
  export { MaintenanceView } from './components/MaintenanceView';
10
- // Hooks
11
- export { useUsers } from './hooks/useUsers';
10
+ export { BottomTabs } from './components/Tabs/BottomTabs';
11
+ export { EmojiPicker } from './components/EmojiPicker';
12
12
  export { useChat } from './hooks/useChat';
13
- // Config utilities
14
- export { loadChatConfig, buildUserListUrl } from './config';
15
- // Services
16
- export { fetchUsers } from './services/userService';
17
- // Theme utilities
18
- export { defaultTheme, mergeTheme } from './utils/theme';
13
+ export { useWebRTC } from './hooks/useWebRTC';
14
+ export { useRemoteConfig } from './hooks/useRemoteConfig';
15
+ export { loadLocalConfig, fetchRemoteChatData } from './config';
16
+ export { mergeTheme, darken } from './utils/theme';
17
+ export { avatarColor, initials, formatTime, formatDate, generateTranscript, downloadText } from './utils/chat';
@@ -1,71 +1,99 @@
1
+ export interface WidgetConfig {
2
+ id: string;
3
+ apiKey: string;
4
+ status: ChatStatus;
5
+ chatType: ChatType;
6
+ primaryColor: string;
7
+ buttonLabel: string;
8
+ buttonPosition: 'bottom-right' | 'bottom-left';
9
+ welcomeTitle: string;
10
+ welcomeSubtitle: string;
11
+ allowVoiceMessage: boolean;
12
+ allowAttachment: boolean;
13
+ allowEmoji: boolean;
14
+ allowWebCall: boolean;
15
+ maxEmojiCount: number;
16
+ allowTranscriptDownload: boolean;
17
+ allowReport: boolean;
18
+ allowBlock: boolean;
19
+ }
20
+ export interface RemoteChatData {
21
+ widget: WidgetConfig;
22
+ developers: ChatUser[];
23
+ users: ChatUser[];
24
+ sampleChats: Record<string, ChatMessage[]>;
25
+ sampleTickets: Ticket[];
26
+ blockedUsers: string[];
27
+ }
1
28
  export type ChatStatus = 'ACTIVE' | 'DISABLE' | 'MAINTENANCE';
2
29
  export type ChatType = 'SUPPORT' | 'CHAT' | 'BOTH';
3
30
  export type UserType = 'developer' | 'user';
4
- export type TabType = 'developers' | 'users';
5
- export type BottomNavTab = 'home' | 'chats' | 'tickets';
6
- export type HomeFlow = 'home' | 'pickUser' | 'chat' | 'raiseTicket';
31
+ export type OnlineStatus = 'online' | 'away' | 'offline';
32
+ export type BottomTab = 'home' | 'chats' | 'tickets';
33
+ export type Screen = 'home' | 'user-list' | 'chat' | 'recent-chats' | 'tickets' | 'block-list' | 'call';
34
+ export type UserListContext = 'support' | 'conversation';
35
+ export type MessageType = 'text' | 'voice' | 'attachment' | 'emoji';
7
36
  export interface ChatUser {
8
- name: string;
9
37
  uid: string;
38
+ name: string;
10
39
  email: string;
11
40
  mobile: string;
12
41
  project: string;
13
42
  type: UserType;
43
+ avatar: string | null;
44
+ status: OnlineStatus;
45
+ designation: string;
14
46
  }
15
47
  export interface ChatMessage {
16
48
  id: string;
17
49
  senderId: string;
18
50
  receiverId: string;
19
51
  text: string;
20
- timestamp: Date;
52
+ timestamp: string;
53
+ type: MessageType;
21
54
  status: 'sent' | 'delivered' | 'read';
55
+ attachmentName?: string;
56
+ attachmentSize?: string;
57
+ voiceDuration?: number;
22
58
  }
23
- export interface ChatConfig {
24
- hostUrl: string;
25
- /** When omitted, URLs use `hostUrl` only (no `:port` segment). */
26
- hostPort?: number;
27
- userListEndpoint: string;
28
- status: ChatStatus;
29
- chatType: ChatType;
30
- /** Show “Need Support” card on home (env `CHAT_SHOW_NEED_SUPPORT`). */
31
- showNeedSupport: boolean;
32
- /** Show “New Conversation” card on home (env `CHAT_SHOW_NEW_CONVERSATION`). */
33
- showNewConversation: boolean;
34
- /** Pixel bounds for the resize slider (env `CHAT_WIDGET_*`). */
35
- widgetMinWidth: number;
36
- widgetMaxWidth: number;
37
- widgetMinHeight: number;
38
- widgetMaxHeight: number;
39
- /** Default widget size as ratio 0–1 between min and max. */
40
- widgetDefaultSize: number;
41
- }
42
- export interface RaisedTicket {
59
+ export interface Ticket {
43
60
  id: string;
44
- subject: string;
45
- body: string;
46
- createdAt: Date;
47
- status: 'open' | 'closed';
61
+ title: string;
62
+ description: string;
63
+ status: 'open' | 'in-progress' | 'resolved' | 'closed';
64
+ priority: 'low' | 'medium' | 'high';
65
+ createdAt: string;
66
+ updatedAt: string;
67
+ assignedTo: string | null;
48
68
  }
49
69
  export interface RecentChat {
70
+ id: string;
50
71
  user: ChatUser;
51
72
  lastMessage: string;
52
- updatedAt: Date;
73
+ lastTime: string;
74
+ unread: number;
75
+ isPaused: boolean;
76
+ }
77
+ export type CallState = 'idle' | 'calling' | 'connected' | 'ended';
78
+ export interface CallSession {
79
+ state: CallState;
80
+ peer: ChatUser | null;
81
+ startedAt: Date | null;
82
+ isMuted: boolean;
83
+ isCameraOn: boolean;
84
+ }
85
+ export interface LocalEnvConfig {
86
+ apiKey: string;
87
+ widgetId: string;
53
88
  }
54
89
  export interface ChatWidgetTheme {
55
- fontFamily?: string;
56
90
  primaryColor?: string;
57
- backgroundColor?: string;
91
+ fontFamily?: string;
58
92
  buttonColor?: string;
59
93
  buttonTextColor?: string;
60
94
  buttonLabel?: string;
61
95
  buttonPosition?: 'bottom-right' | 'bottom-left';
62
96
  borderRadius?: string;
63
- /** Override config min width (px). */
64
- widgetMinWidth?: number;
65
- widgetMaxWidth?: number;
66
- widgetMinHeight?: number;
67
- widgetMaxHeight?: number;
68
- widgetDefaultSize?: number;
69
97
  }
70
98
  export interface ChatWidgetProps {
71
99
  theme?: ChatWidgetTheme;
@@ -0,0 +1,13 @@
1
+ import { ChatMessage, ChatUser } from '../types';
2
+ /** Generate a consistent avatar color from a name */
3
+ export declare function avatarColor(name: string): string;
4
+ /** Get initials from full name */
5
+ export declare function initials(name: string): string;
6
+ /** Format timestamp to readable time string */
7
+ export declare function formatTime(ts: string | Date): string;
8
+ /** Format date for chat separators */
9
+ export declare function formatDate(ts: string | Date): string;
10
+ /** Generate a plain-text transcript from messages */
11
+ export declare function generateTranscript(messages: ChatMessage[], peer: ChatUser, myName?: string): string;
12
+ /** Trigger a file download in the browser */
13
+ export declare function downloadText(content: string, filename: string): void;
@@ -0,0 +1,62 @@
1
+ /** Generate a consistent avatar color from a name */
2
+ export function avatarColor(name) {
3
+ const palette = [
4
+ '#2563EB', '#7C3AED', '#059669', '#D97706',
5
+ '#DC2626', '#0891B2', '#4F46E5', '#BE185D',
6
+ ];
7
+ let hash = 0;
8
+ for (let i = 0; i < name.length; i++)
9
+ hash = name.charCodeAt(i) + ((hash << 5) - hash);
10
+ return palette[Math.abs(hash) % palette.length];
11
+ }
12
+ /** Get initials from full name */
13
+ export function initials(name) {
14
+ return name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
15
+ }
16
+ /** Format timestamp to readable time string */
17
+ export function formatTime(ts) {
18
+ return new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
19
+ }
20
+ /** Format date for chat separators */
21
+ export function formatDate(ts) {
22
+ const d = new Date(ts);
23
+ const today = new Date();
24
+ const yesterday = new Date(today);
25
+ yesterday.setDate(today.getDate() - 1);
26
+ if (d.toDateString() === today.toDateString())
27
+ return 'Today';
28
+ if (d.toDateString() === yesterday.toDateString())
29
+ return 'Yesterday';
30
+ return d.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' });
31
+ }
32
+ /** Generate a plain-text transcript from messages */
33
+ export function generateTranscript(messages, peer, myName = 'Me') {
34
+ const header = [
35
+ '═══════════════════════════════════════',
36
+ ' CHAT TRANSCRIPT',
37
+ ` Conversation with: ${peer.name} (${peer.email})`,
38
+ ` Downloaded: ${new Date().toLocaleString()}`,
39
+ '═══════════════════════════════════════',
40
+ '',
41
+ ].join('\n');
42
+ const body = messages.map(m => {
43
+ var _a;
44
+ const sender = m.senderId === 'me' ? myName : peer.name;
45
+ const time = new Date(m.timestamp).toLocaleString();
46
+ const label = m.type === 'voice' ? '[Voice Message]'
47
+ : m.type === 'attachment' ? `[Attachment: ${(_a = m.attachmentName) !== null && _a !== void 0 ? _a : 'file'}]`
48
+ : m.text;
49
+ return `[${time}] ${sender}: ${label}`;
50
+ }).join('\n');
51
+ return header + body;
52
+ }
53
+ /** Trigger a file download in the browser */
54
+ export function downloadText(content, filename) {
55
+ const blob = new Blob([content], { type: 'text/plain' });
56
+ const url = URL.createObjectURL(blob);
57
+ const a = document.createElement('a');
58
+ a.href = url;
59
+ a.download = filename;
60
+ a.click();
61
+ URL.revokeObjectURL(url);
62
+ }
@@ -1,4 +1,5 @@
1
1
  import { ChatWidgetTheme } from '../types';
2
2
  export declare const defaultTheme: Required<ChatWidgetTheme>;
3
- export declare function mergeTheme(custom?: ChatWidgetTheme): Required<ChatWidgetTheme>;
4
- export declare function themeToCSS(theme: Required<ChatWidgetTheme>): string;
3
+ export declare function mergeTheme(remote?: Partial<ChatWidgetTheme>, local?: ChatWidgetTheme): Required<ChatWidgetTheme>;
4
+ /** Darken a hex color by `amount` (0-255) */
5
+ export declare function darken(hex: string, amount?: number): string;
@@ -1,28 +1,20 @@
1
1
  export const defaultTheme = {
2
+ primaryColor: '#2563EB',
2
3
  fontFamily: "'DM Sans', 'Segoe UI', sans-serif",
3
- primaryColor: '#13947e',
4
- backgroundColor: '#ffffff',
5
- buttonColor: '#13947e',
4
+ buttonColor: '#2563EB',
6
5
  buttonTextColor: '#ffffff',
7
- buttonLabel: 'Chat with us',
6
+ buttonLabel: 'Support',
8
7
  buttonPosition: 'bottom-right',
9
- borderRadius: '16px',
10
- widgetMinWidth: 320,
11
- widgetMaxWidth: 720,
12
- widgetMinHeight: 420,
13
- widgetMaxHeight: 720,
14
- widgetDefaultSize: 0.45,
8
+ borderRadius: '0px', // drawer uses 16px only on top-left / bottom-left
15
9
  };
16
- export function mergeTheme(custom) {
17
- return Object.assign(Object.assign({}, defaultTheme), custom);
10
+ export function mergeTheme(remote, local) {
11
+ return Object.assign(Object.assign(Object.assign({}, defaultTheme), remote), local);
18
12
  }
19
- export function themeToCSS(theme) {
20
- return `
21
- --cw-font: ${theme.fontFamily};
22
- --cw-primary: ${theme.primaryColor};
23
- --cw-bg: ${theme.backgroundColor};
24
- --cw-btn-bg: ${theme.buttonColor};
25
- --cw-btn-text: ${theme.buttonTextColor};
26
- --cw-radius: ${theme.borderRadius};
27
- `;
13
+ /** Darken a hex color by `amount` (0-255) */
14
+ export function darken(hex, amount = 20) {
15
+ const n = parseInt(hex.replace('#', ''), 16);
16
+ const r = Math.max(0, (n >> 16) - amount);
17
+ const g = Math.max(0, ((n >> 8) & 0xff) - amount);
18
+ const b = Math.max(0, (n & 0xff) - amount);
19
+ return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
28
20
  }
package/package.json CHANGED
@@ -1,18 +1,14 @@
1
1
  {
2
2
  "name": "ajaxter-chat",
3
- "version": "1.0.3",
4
- "description": "A reusable, configurable chat widget for React.js and Next.js applications.",
3
+ "version": "3.0.1",
4
+ "description": "Drawer-based chat widget with support chat, tickets, WebRTC calling, voice messages, block list, and transcript download.",
5
5
  "main": "dist/index.js",
6
- "module": "dist/index.esm.js",
7
6
  "types": "dist/index.d.ts",
8
- "files": [
9
- "dist"
10
- ],
7
+ "files": ["dist", "src", "public"],
11
8
  "sideEffects": false,
12
9
  "scripts": {
13
10
  "build": "tsc",
14
11
  "dev": "tsc --watch",
15
- "lint": "eslint src --ext .ts,.tsx",
16
12
  "type-check": "tsc --noEmit"
17
13
  },
18
14
  "peerDependencies": {
@@ -20,19 +16,13 @@
20
16
  "react-dom": ">=17.0.0"
21
17
  },
22
18
  "devDependencies": {
23
- "@types/node": "^25.5.0",
24
- "@types/react": "^18.3.28",
25
- "@types/react-dom": "^18.3.7",
26
- "typescript": "^5.9.3"
19
+ "@types/node": "^22.0.0",
20
+ "@types/react": "^18.0.0",
21
+ "@types/react-dom": "^18.0.0",
22
+ "react": "^18.0.0",
23
+ "react-dom": "^18.0.0",
24
+ "typescript": "^5.0.0"
27
25
  },
28
- "keywords": [
29
- "react",
30
- "nextjs",
31
- "chat",
32
- "widget",
33
- "support",
34
- "typescript",
35
- "ajaxter"
36
- ],
26
+ "keywords": ["react","nextjs","chat","widget","webrtc","support","tickets","drawer","typescript"],
37
27
  "license": "MIT"
38
28
  }
@@ -0,0 +1,162 @@
1
+ {
2
+ "widget": {
3
+ "id": "demo",
4
+ "apiKey": "demo1234",
5
+ "status": "ACTIVE",
6
+ "chatType": "BOTH",
7
+ "primaryColor": "#2563EB",
8
+ "buttonLabel": "Support",
9
+ "buttonPosition": "bottom-right",
10
+ "welcomeTitle": "Hi there 👋",
11
+ "welcomeSubtitle": "Need help? Start a conversation:",
12
+ "allowVoiceMessage": true,
13
+ "allowAttachment": true,
14
+ "allowEmoji": true,
15
+ "allowWebCall": true,
16
+ "maxEmojiCount": 20,
17
+ "allowTranscriptDownload": true,
18
+ "allowReport": true,
19
+ "allowBlock": true
20
+ },
21
+ "developers": [
22
+ {
23
+ "uid": "dev_001",
24
+ "name": "Arjun Sharma",
25
+ "email": "arjun.sharma@mscorpres.com",
26
+ "mobile": "+91-9876543210",
27
+ "project": "Platform Engineering",
28
+ "type": "developer",
29
+ "avatar": null,
30
+ "status": "online",
31
+ "designation": "Senior Developer"
32
+ },
33
+ {
34
+ "uid": "dev_002",
35
+ "name": "Priya Nair",
36
+ "email": "priya.nair@mscorpres.com",
37
+ "mobile": "+91-9876543211",
38
+ "project": "Product Support",
39
+ "type": "developer",
40
+ "avatar": null,
41
+ "status": "online",
42
+ "designation": "Support Engineer"
43
+ }
44
+ ],
45
+ "users": [
46
+ {
47
+ "uid": "usr_001",
48
+ "name": "Ravi Kumar",
49
+ "email": "ravi.kumar@client.com",
50
+ "mobile": "+91-9988776655",
51
+ "project": "Client Portal",
52
+ "type": "user",
53
+ "avatar": null,
54
+ "status": "online",
55
+ "designation": "Product Manager"
56
+ },
57
+ {
58
+ "uid": "usr_002",
59
+ "name": "Sneha Patel",
60
+ "email": "sneha.patel@client.com",
61
+ "mobile": "+91-9988776644",
62
+ "project": "Client Portal",
63
+ "type": "user",
64
+ "avatar": null,
65
+ "status": "away",
66
+ "designation": "Business Analyst"
67
+ },
68
+ {
69
+ "uid": "usr_003",
70
+ "name": "Mohan Das",
71
+ "email": "mohan.das@client.com",
72
+ "mobile": "+91-9988776633",
73
+ "project": "Mobile App",
74
+ "type": "user",
75
+ "avatar": null,
76
+ "status": "offline",
77
+ "designation": "QA Engineer"
78
+ }
79
+ ],
80
+ "sampleChats": {
81
+ "dev_001": [
82
+ {
83
+ "id": "msg_001",
84
+ "senderId": "me",
85
+ "receiverId": "dev_001",
86
+ "text": "Hi Arjun, I'm getting a 500 error on the login API.",
87
+ "timestamp": "2025-03-27T09:10:00Z",
88
+ "type": "text",
89
+ "status": "read"
90
+ },
91
+ {
92
+ "id": "msg_002",
93
+ "senderId": "dev_001",
94
+ "receiverId": "me",
95
+ "text": "Hey! Can you share the request payload? I'll check on my end.",
96
+ "timestamp": "2025-03-27T09:11:30Z",
97
+ "type": "text",
98
+ "status": "read"
99
+ },
100
+ {
101
+ "id": "msg_003",
102
+ "senderId": "me",
103
+ "receiverId": "dev_001",
104
+ "text": "Sure, here it is: { \"email\": \"test@demo.com\", \"password\": \"***\" }",
105
+ "timestamp": "2025-03-27T09:12:00Z",
106
+ "type": "text",
107
+ "status": "read"
108
+ },
109
+ {
110
+ "id": "msg_004",
111
+ "senderId": "dev_001",
112
+ "receiverId": "me",
113
+ "text": "Got it. The issue was a DB connection timeout. Fixed in the latest deploy 🚀",
114
+ "timestamp": "2025-03-27T09:20:00Z",
115
+ "type": "text",
116
+ "status": "read"
117
+ },
118
+ {
119
+ "id": "msg_005",
120
+ "senderId": "me",
121
+ "receiverId": "dev_001",
122
+ "text": "Perfect, it's working now. Thanks!",
123
+ "timestamp": "2025-03-27T09:21:00Z",
124
+ "type": "text",
125
+ "status": "read"
126
+ }
127
+ ]
128
+ },
129
+ "sampleTickets": [
130
+ {
131
+ "id": "TKT-0001",
132
+ "title": "Login page 500 error",
133
+ "description": "Getting a server error when logging in with valid credentials.",
134
+ "status": "resolved",
135
+ "priority": "high",
136
+ "createdAt": "2025-03-25T10:00:00Z",
137
+ "updatedAt": "2025-03-27T09:22:00Z",
138
+ "assignedTo": "dev_001"
139
+ },
140
+ {
141
+ "id": "TKT-0002",
142
+ "title": "Export to CSV not working",
143
+ "description": "The export button on the reports page does nothing when clicked.",
144
+ "status": "in-progress",
145
+ "priority": "medium",
146
+ "createdAt": "2025-03-26T14:30:00Z",
147
+ "updatedAt": "2025-03-27T08:00:00Z",
148
+ "assignedTo": "dev_002"
149
+ },
150
+ {
151
+ "id": "TKT-0003",
152
+ "title": "Add dark mode support",
153
+ "description": "Feature request to add a dark mode toggle for the dashboard.",
154
+ "status": "open",
155
+ "priority": "low",
156
+ "createdAt": "2025-03-27T07:00:00Z",
157
+ "updatedAt": "2025-03-27T07:00:00Z",
158
+ "assignedTo": null
159
+ }
160
+ ],
161
+ "blockedUsers": []
162
+ }