ajaxter-chat 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -191
- package/dist/components/ChatScreen/index.d.ts +12 -0
- package/dist/components/ChatScreen/index.js +83 -0
- package/dist/components/ChatWidget.d.ts +0 -24
- package/dist/components/ChatWidget.js +129 -38
- package/dist/components/HomeScreen/index.d.ts +9 -0
- package/dist/components/HomeScreen/index.js +71 -0
- package/dist/components/MaintenanceView/index.d.ts +1 -1
- package/dist/components/MaintenanceView/index.js +15 -52
- package/dist/components/RecentChatsScreen/index.d.ts +16 -0
- package/dist/components/RecentChatsScreen/index.js +38 -0
- package/dist/components/Tabs/BottomTabs.d.ts +10 -0
- package/dist/components/Tabs/BottomTabs.js +29 -0
- package/dist/components/TicketScreen/index.d.ts +9 -0
- package/dist/components/TicketScreen/index.js +71 -0
- package/dist/components/UserListScreen/index.d.ts +13 -0
- package/dist/components/UserListScreen/index.js +64 -0
- package/dist/config/index.js +19 -74
- package/dist/hooks/useChat.d.ts +3 -7
- package/dist/hooks/useChat.js +8 -30
- package/dist/hooks/useUsers.d.ts +3 -10
- package/dist/hooks/useUsers.js +5 -11
- package/dist/index.d.ts +8 -7
- package/dist/index.js +7 -12
- package/dist/services/userService.d.ts +0 -5
- package/dist/services/userService.js +6 -13
- package/dist/src/components/ChatScreen/index.d.ts +12 -0
- package/dist/src/components/ChatScreen/index.js +83 -0
- package/dist/src/components/ChatWidget.d.ts +4 -0
- package/dist/src/components/ChatWidget.js +141 -0
- package/dist/src/components/HomeScreen/index.d.ts +9 -0
- package/dist/src/components/HomeScreen/index.js +71 -0
- package/dist/src/components/MaintenanceView/index.d.ts +7 -0
- package/dist/src/components/MaintenanceView/index.js +16 -0
- package/dist/src/components/RecentChatsScreen/index.d.ts +16 -0
- package/dist/src/components/RecentChatsScreen/index.js +38 -0
- package/dist/src/components/Tabs/BottomTabs.d.ts +10 -0
- package/dist/src/components/Tabs/BottomTabs.js +29 -0
- package/dist/src/components/TicketScreen/index.d.ts +9 -0
- package/dist/src/components/TicketScreen/index.js +71 -0
- package/dist/src/components/UserListScreen/index.d.ts +13 -0
- package/dist/src/components/UserListScreen/index.js +64 -0
- package/dist/src/config/index.d.ts +3 -0
- package/dist/src/config/index.js +38 -0
- package/dist/src/hooks/useChat.d.ts +8 -0
- package/dist/src/hooks/useChat.js +26 -0
- package/dist/src/hooks/useUsers.d.ts +7 -0
- package/dist/src/hooks/useUsers.js +26 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +13 -0
- package/dist/src/services/userService.d.ts +2 -0
- package/dist/src/services/userService.js +9 -0
- package/dist/src/types/index.d.ts +59 -0
- package/dist/src/types/index.js +1 -0
- package/dist/src/utils/theme.d.ts +3 -0
- package/dist/src/utils/theme.js +13 -0
- package/dist/types/index.d.ts +23 -36
- package/dist/utils/theme.d.ts +0 -1
- package/dist/utils/theme.js +3 -18
- package/package.json +10 -20
- package/src/components/ChatScreen/index.tsx +205 -0
- package/src/components/ChatWidget.tsx +327 -0
- package/src/components/HomeScreen/index.tsx +130 -0
- package/src/components/MaintenanceView/index.tsx +41 -0
- package/src/components/RecentChatsScreen/index.tsx +108 -0
- package/src/components/Tabs/BottomTabs.tsx +82 -0
- package/src/components/TicketScreen/index.tsx +170 -0
- package/src/components/UserListScreen/index.tsx +181 -0
- package/src/config/index.ts +46 -0
- package/src/hooks/useChat.ts +31 -0
- package/src/hooks/useUsers.ts +27 -0
- package/src/index.ts +18 -0
- package/src/services/userService.ts +9 -0
- package/src/types/index.ts +82 -0
- package/src/utils/theme.ts +16 -0
- package/dist/components/BottomNav/index.d.ts +0 -10
- package/dist/components/BottomNav/index.js +0 -32
- package/dist/components/ChatBox/index.d.ts +0 -15
- package/dist/components/ChatBox/index.js +0 -228
- package/dist/components/ChatButton/index.d.ts +0 -9
- package/dist/components/ChatButton/index.js +0 -17
- package/dist/components/ChatWindow/index.d.ts +0 -10
- package/dist/components/ChatWindow/index.js +0 -286
- package/dist/components/HomeView/index.d.ts +0 -12
- package/dist/components/HomeView/index.js +0 -51
- package/dist/components/UserList/index.d.ts +0 -13
- package/dist/components/UserList/index.js +0 -136
package/dist/config/index.js
CHANGED
|
@@ -1,93 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
* Safely reads an environment variable.
|
|
3
|
-
* Supports both Next.js (NEXT_PUBLIC_) and React (REACT_APP_) prefixes.
|
|
4
|
-
* Falls back gracefully if running in SSR or non-browser environments.
|
|
5
|
-
*/
|
|
6
|
-
function getEnvVar(key) {
|
|
1
|
+
function getEnv(key) {
|
|
7
2
|
var _a, _b, _c;
|
|
8
|
-
const nextKey = `NEXT_PUBLIC_${key}`;
|
|
9
|
-
const reactKey = `REACT_APP_${key}`;
|
|
10
|
-
// Next.js / Node.js environment
|
|
11
3
|
if (typeof process !== 'undefined' && process.env) {
|
|
12
|
-
return ((_c = (_b = (_a = process.env[
|
|
4
|
+
return ((_c = (_b = (_a = process.env[`NEXT_PUBLIC_${key}`]) !== null && _a !== void 0 ? _a : process.env[`REACT_APP_${key}`]) !== null && _b !== void 0 ? _b : process.env[key]) !== null && _c !== void 0 ? _c : undefined);
|
|
13
5
|
}
|
|
14
6
|
return undefined;
|
|
15
7
|
}
|
|
16
|
-
function validateStatus(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
console.warn(`[ChatWidget] Invalid CHAT_STATUS "${value}". Defaulting to "DISABLE".`);
|
|
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.`);
|
|
22
12
|
return 'DISABLE';
|
|
23
13
|
}
|
|
24
|
-
function validateChatType(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
console.warn(`[ChatWidget] Invalid CHAT_TYPE "${value}". Defaulting to "SUPPORT".`);
|
|
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.`);
|
|
30
18
|
return 'SUPPORT';
|
|
31
19
|
}
|
|
32
|
-
function parseBool(value, defaultValue) {
|
|
33
|
-
if (value === undefined || value === '')
|
|
34
|
-
return defaultValue;
|
|
35
|
-
const v = value.toLowerCase().trim();
|
|
36
|
-
if (v === 'true' || v === '1' || v === 'yes')
|
|
37
|
-
return true;
|
|
38
|
-
if (v === 'false' || v === '0' || v === 'no')
|
|
39
|
-
return false;
|
|
40
|
-
return defaultValue;
|
|
41
|
-
}
|
|
42
|
-
function parsePositiveInt(value, fallback) {
|
|
43
|
-
if (value === undefined || value === '')
|
|
44
|
-
return fallback;
|
|
45
|
-
const n = parseInt(value, 10);
|
|
46
|
-
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
47
|
-
}
|
|
48
|
-
function parseOptionalPort(value) {
|
|
49
|
-
if (value === undefined || value.trim() === '')
|
|
50
|
-
return undefined;
|
|
51
|
-
const n = parseInt(value, 10);
|
|
52
|
-
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
53
|
-
}
|
|
54
|
-
function parseSizeRatio(value, fallback) {
|
|
55
|
-
if (value === undefined || value === '')
|
|
56
|
-
return fallback;
|
|
57
|
-
const n = parseFloat(value);
|
|
58
|
-
if (!Number.isFinite(n))
|
|
59
|
-
return fallback;
|
|
60
|
-
return Math.min(1, Math.max(0, n));
|
|
61
|
-
}
|
|
62
20
|
export function loadChatConfig() {
|
|
63
21
|
var _a, _b;
|
|
64
|
-
const
|
|
65
|
-
const hostPort =
|
|
66
|
-
const userListEndpoint = (_b = getEnvVar('CHAT_USER_LIST')) !== null && _b !== void 0 ? _b : 'api/users';
|
|
67
|
-
const status = validateStatus(getEnvVar('CHAT_STATUS'));
|
|
68
|
-
const chatType = validateChatType(getEnvVar('CHAT_TYPE'));
|
|
69
|
-
const showNeedSupport = parseBool(getEnvVar('CHAT_SHOW_NEED_SUPPORT'), true);
|
|
70
|
-
const showNewConversation = parseBool(getEnvVar('CHAT_SHOW_NEW_CONVERSATION'), true);
|
|
22
|
+
const portStr = getEnv('CHAT_HOST_PORT');
|
|
23
|
+
const hostPort = portStr ? parseInt(portStr, 10) : null;
|
|
71
24
|
return {
|
|
72
|
-
hostUrl,
|
|
25
|
+
hostUrl: (_a = getEnv('CHAT_HOST_URL')) !== null && _a !== void 0 ? _a : 'http://localhost',
|
|
73
26
|
hostPort,
|
|
74
|
-
userListEndpoint,
|
|
75
|
-
status,
|
|
76
|
-
chatType,
|
|
77
|
-
showNeedSupport,
|
|
78
|
-
showNewConversation,
|
|
79
|
-
widgetMinWidth: parsePositiveInt(getEnvVar('CHAT_WIDGET_MIN_WIDTH'), 320),
|
|
80
|
-
widgetMaxWidth: parsePositiveInt(getEnvVar('CHAT_WIDGET_MAX_WIDTH'), 720),
|
|
81
|
-
widgetMinHeight: parsePositiveInt(getEnvVar('CHAT_WIDGET_MIN_HEIGHT'), 420),
|
|
82
|
-
widgetMaxHeight: parsePositiveInt(getEnvVar('CHAT_WIDGET_MAX_HEIGHT'), 720),
|
|
83
|
-
widgetDefaultSize: parseSizeRatio(getEnvVar('CHAT_WIDGET_DEFAULT_SIZE'), 0.45),
|
|
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')),
|
|
84
30
|
};
|
|
85
31
|
}
|
|
86
32
|
export function buildUserListUrl(config) {
|
|
87
33
|
const base = config.hostUrl.replace(/\/$/, '');
|
|
88
34
|
const endpoint = config.userListEndpoint.replace(/^\//, '');
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
return `${base}/${endpoint}`;
|
|
35
|
+
// Port is optional
|
|
36
|
+
const portPart = config.hostPort ? `:${config.hostPort}` : '';
|
|
37
|
+
return `${base}${portPart}/${endpoint}`;
|
|
93
38
|
}
|
package/dist/hooks/useChat.d.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import { ChatMessage, ChatUser
|
|
2
|
-
|
|
1
|
+
import { ChatMessage, ChatUser } from '../types';
|
|
2
|
+
export declare function useChat(): {
|
|
3
3
|
messages: ChatMessage[];
|
|
4
4
|
activeUser: ChatUser | null;
|
|
5
|
-
recentChats: RecentChat[];
|
|
6
5
|
selectUser: (user: ChatUser) => void;
|
|
7
6
|
sendMessage: (text: string) => void;
|
|
8
7
|
clearChat: () => void;
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
export declare function useChat(): UseChatReturn;
|
|
12
|
-
export {};
|
|
8
|
+
};
|
package/dist/hooks/useChat.js
CHANGED
|
@@ -2,47 +2,25 @@ import { useState, useCallback } from 'react';
|
|
|
2
2
|
export function useChat() {
|
|
3
3
|
const [messages, setMessages] = useState([]);
|
|
4
4
|
const [activeUser, setActiveUser] = useState(null);
|
|
5
|
-
const [recentChats, setRecentChats] = useState([]);
|
|
6
|
-
const upsertRecent = useCallback((user, lastMessage) => {
|
|
7
|
-
const updatedAt = new Date();
|
|
8
|
-
setRecentChats((prev) => {
|
|
9
|
-
const rest = prev.filter((r) => r.user.uid !== user.uid);
|
|
10
|
-
return [{ user, lastMessage, updatedAt }, ...rest].slice(0, 50);
|
|
11
|
-
});
|
|
12
|
-
}, []);
|
|
13
5
|
const selectUser = useCallback((user) => {
|
|
14
6
|
setActiveUser(user);
|
|
15
7
|
setMessages([]);
|
|
16
|
-
|
|
17
|
-
const openRecent = useCallback((user) => {
|
|
18
|
-
setActiveUser(user);
|
|
19
|
-
setMessages([]);
|
|
8
|
+
// TODO: socket.emit('join', user.uid); socket.on('message', handler)
|
|
20
9
|
}, []);
|
|
21
10
|
const sendMessage = useCallback((text) => {
|
|
22
11
|
if (!activeUser || !text.trim())
|
|
23
12
|
return;
|
|
24
|
-
const
|
|
25
|
-
id: `msg_${Date.now()}
|
|
13
|
+
const msg = {
|
|
14
|
+
id: `msg_${Date.now()}`,
|
|
26
15
|
senderId: 'me',
|
|
27
16
|
receiverId: activeUser.uid,
|
|
28
17
|
text: text.trim(),
|
|
29
18
|
timestamp: new Date(),
|
|
30
19
|
status: 'sent',
|
|
31
20
|
};
|
|
32
|
-
setMessages(
|
|
33
|
-
|
|
34
|
-
}, [activeUser
|
|
35
|
-
const clearChat = useCallback(() => {
|
|
36
|
-
|
|
37
|
-
setActiveUser(null);
|
|
38
|
-
}, []);
|
|
39
|
-
return {
|
|
40
|
-
messages,
|
|
41
|
-
activeUser,
|
|
42
|
-
recentChats,
|
|
43
|
-
selectUser,
|
|
44
|
-
sendMessage,
|
|
45
|
-
clearChat,
|
|
46
|
-
openRecent,
|
|
47
|
-
};
|
|
21
|
+
setMessages(prev => [...prev, msg]);
|
|
22
|
+
// TODO: socket.emit('message', msg)
|
|
23
|
+
}, [activeUser]);
|
|
24
|
+
const clearChat = useCallback(() => { setMessages([]); setActiveUser(null); }, []);
|
|
25
|
+
return { messages, activeUser, selectUser, sendMessage, clearChat };
|
|
48
26
|
}
|
package/dist/hooks/useUsers.d.ts
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { ChatUser, UserType } from '../types';
|
|
2
|
-
|
|
3
|
-
url: string;
|
|
4
|
-
filterType?: UserType;
|
|
5
|
-
enabled?: boolean;
|
|
6
|
-
}
|
|
7
|
-
interface UseUsersReturn {
|
|
2
|
+
export declare function useUsers(url: string, filterType?: UserType, enabled?: boolean): {
|
|
8
3
|
users: ChatUser[];
|
|
9
4
|
loading: boolean;
|
|
10
5
|
error: string | null;
|
|
11
|
-
refetch: () => void
|
|
12
|
-
}
|
|
13
|
-
export declare function useUsers({ url, filterType, enabled, }: UseUsersOptions): UseUsersReturn;
|
|
14
|
-
export {};
|
|
6
|
+
refetch: () => Promise<void>;
|
|
7
|
+
};
|
package/dist/hooks/useUsers.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import { fetchUsers } from '../services/userService';
|
|
3
|
-
export function useUsers(
|
|
3
|
+
export function useUsers(url, filterType, enabled = true) {
|
|
4
4
|
const [users, setUsers] = useState([]);
|
|
5
5
|
const [loading, setLoading] = useState(false);
|
|
6
6
|
const [error, setError] = useState(null);
|
|
@@ -11,22 +11,16 @@ export function useUsers({ url, filterType, enabled = true, }) {
|
|
|
11
11
|
setError(null);
|
|
12
12
|
try {
|
|
13
13
|
const data = await fetchUsers(url);
|
|
14
|
-
|
|
15
|
-
? data.filter((u) => u.type === filterType)
|
|
16
|
-
: data;
|
|
17
|
-
setUsers(filtered);
|
|
14
|
+
setUsers(filterType ? data.filter(u => u.type === filterType) : data);
|
|
18
15
|
}
|
|
19
|
-
catch (
|
|
20
|
-
|
|
21
|
-
setError(message);
|
|
16
|
+
catch (e) {
|
|
17
|
+
setError(e instanceof Error ? e.message : 'Unknown error');
|
|
22
18
|
setUsers([]);
|
|
23
19
|
}
|
|
24
20
|
finally {
|
|
25
21
|
setLoading(false);
|
|
26
22
|
}
|
|
27
23
|
}, [url, filterType, enabled]);
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
load();
|
|
30
|
-
}, [load]);
|
|
24
|
+
useEffect(() => { load(); }, [load]);
|
|
31
25
|
return { users, loading, error, refetch: load };
|
|
32
26
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
export { ChatWidget } from './components/ChatWidget';
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
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
7
|
export { MaintenanceView } from './components/MaintenanceView';
|
|
8
|
+
export { BottomTabs } from './components/Tabs/BottomTabs';
|
|
8
9
|
export { useUsers } from './hooks/useUsers';
|
|
9
10
|
export { useChat } from './hooks/useChat';
|
|
10
11
|
export { loadChatConfig, buildUserListUrl } from './config';
|
|
11
12
|
export { fetchUsers } from './services/userService';
|
|
12
|
-
export type { ChatUser, ChatMessage, ChatConfig, ChatWidgetTheme, ChatWidgetProps, ChatStatus, ChatType, UserType, TabType, BottomNavTab, HomeFlow, RaisedTicket, RecentChat, } from './types';
|
|
13
13
|
export { defaultTheme, mergeTheme } from './utils/theme';
|
|
14
|
+
export type { ChatUser, ChatMessage, ChatConfig, ChatWidgetTheme, ChatWidgetProps, ChatStatus, ChatType, UserType, Screen, BottomTab, UserListContext, Ticket, } from './types';
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
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';
|
|
9
7
|
export { MaintenanceView } from './components/MaintenanceView';
|
|
10
|
-
|
|
8
|
+
export { BottomTabs } from './components/Tabs/BottomTabs';
|
|
11
9
|
export { useUsers } from './hooks/useUsers';
|
|
12
10
|
export { useChat } from './hooks/useChat';
|
|
13
|
-
// Config utilities
|
|
14
11
|
export { loadChatConfig, buildUserListUrl } from './config';
|
|
15
|
-
// Services
|
|
16
12
|
export { fetchUsers } from './services/userService';
|
|
17
|
-
// Theme utilities
|
|
18
13
|
export { defaultTheme, mergeTheme } from './utils/theme';
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
export async function fetchUsers(url) {
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (!response.ok) {
|
|
9
|
-
throw new Error(`[ChatWidget] Failed to fetch users: ${response.status} ${response.statusText}`);
|
|
10
|
-
}
|
|
11
|
-
const data = await response.json();
|
|
12
|
-
if (!Array.isArray(data)) {
|
|
13
|
-
throw new Error('[ChatWidget] User list API did not return an array.');
|
|
14
|
-
}
|
|
2
|
+
const res = await fetch(url, { headers: { 'Content-Type': 'application/json' } });
|
|
3
|
+
if (!res.ok)
|
|
4
|
+
throw new Error(`Failed to fetch users: ${res.status}`);
|
|
5
|
+
const data = await res.json();
|
|
6
|
+
if (!Array.isArray(data))
|
|
7
|
+
throw new Error('User API did not return an array');
|
|
15
8
|
return data;
|
|
16
9
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChatMessage, ChatUser, ChatWidgetTheme } from '../../types';
|
|
3
|
+
interface ChatScreenProps {
|
|
4
|
+
activeUser: ChatUser;
|
|
5
|
+
messages: ChatMessage[];
|
|
6
|
+
onSend: (text: string) => void;
|
|
7
|
+
onBack: () => void;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
theme?: ChatWidgetTheme;
|
|
10
|
+
}
|
|
11
|
+
export declare const ChatScreen: React.FC<ChatScreenProps>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect } from 'react';
|
|
3
|
+
import { mergeTheme } from '../../utils/theme';
|
|
4
|
+
export const ChatScreen = ({ activeUser, messages, onSend, onBack, onClose, theme, }) => {
|
|
5
|
+
const t = mergeTheme(theme);
|
|
6
|
+
const [text, setText] = useState('');
|
|
7
|
+
const endRef = useRef(null);
|
|
8
|
+
const inputRef = useRef(null);
|
|
9
|
+
useEffect(() => { var _a; (_a = endRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth' }); }, [messages]);
|
|
10
|
+
const handleSend = () => {
|
|
11
|
+
var _a;
|
|
12
|
+
if (!text.trim())
|
|
13
|
+
return;
|
|
14
|
+
onSend(text);
|
|
15
|
+
setText('');
|
|
16
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
17
|
+
};
|
|
18
|
+
const handleKey = (e) => {
|
|
19
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
handleSend();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const initials = activeUser.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
|
|
25
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideInRight 0.25s ease' }, children: [_jsxs("div", { style: {
|
|
26
|
+
backgroundColor: t.primaryColor,
|
|
27
|
+
padding: '14px 18px',
|
|
28
|
+
display: 'flex',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
gap: '10px',
|
|
31
|
+
flexShrink: 0,
|
|
32
|
+
}, children: [_jsx("button", { onClick: onBack, style: iconBtnStyle, 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" }) }) }), _jsx("div", { style: {
|
|
33
|
+
width: 36, height: 36, borderRadius: '50%',
|
|
34
|
+
backgroundColor: 'rgba(255,255,255,0.25)',
|
|
35
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
36
|
+
fontWeight: 700, fontSize: '13px', color: '#fff', flexShrink: 0,
|
|
37
|
+
}, children: initials }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: '14px', color: '#fff', fontFamily: t.fontFamily }, children: activeUser.name }), _jsxs("div", { style: { fontSize: '11px', color: 'rgba(255,255,255,0.8)', display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("span", { style: { width: 6, height: 6, borderRadius: '50%', backgroundColor: '#a8f0c6', display: 'inline-block' } }), "Online"] })] }), _jsx("button", { onClick: onClose, style: iconBtnStyle, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }) }) })] }), _jsxs("div", { style: {
|
|
38
|
+
flex: 1, overflowY: 'auto', padding: '18px 16px',
|
|
39
|
+
display: 'flex', flexDirection: 'column', gap: '10px',
|
|
40
|
+
backgroundColor: '#f7f8fc',
|
|
41
|
+
}, children: [messages.length === 0 && (_jsxs("div", { style: {
|
|
42
|
+
margin: 'auto', textAlign: 'center',
|
|
43
|
+
fontSize: '13px', color: '#b0bec5',
|
|
44
|
+
fontFamily: t.fontFamily,
|
|
45
|
+
}, children: [_jsx("div", { style: { fontSize: '28px', marginBottom: 8 }, children: "\uD83D\uDCAC" }), "Say hi to ", activeUser.name, "!"] })), messages.map(msg => (_jsx(Bubble, { msg: msg, primaryColor: t.primaryColor, font: t.fontFamily }, msg.id))), _jsx("div", { ref: endRef })] }), _jsxs("div", { style: {
|
|
46
|
+
borderTop: '1px solid #eef0f5',
|
|
47
|
+
padding: '10px 14px',
|
|
48
|
+
backgroundColor: '#fff',
|
|
49
|
+
display: 'flex',
|
|
50
|
+
alignItems: 'flex-end',
|
|
51
|
+
gap: '10px',
|
|
52
|
+
flexShrink: 0,
|
|
53
|
+
}, children: [_jsx("textarea", { ref: inputRef, value: text, onChange: e => setText(e.target.value), onKeyDown: handleKey, placeholder: "Type and press [enter]..", rows: 1, style: {
|
|
54
|
+
flex: 1, resize: 'none', border: 'none', outline: 'none',
|
|
55
|
+
fontFamily: t.fontFamily, fontSize: '14px', lineHeight: '1.5',
|
|
56
|
+
maxHeight: '80px', overflowY: 'auto', color: '#1a2332',
|
|
57
|
+
padding: '6px 0', backgroundColor: 'transparent',
|
|
58
|
+
} }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px', flexShrink: 0 }, children: [_jsx(IconBtn, { onClick: () => { }, title: "Reaction", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M14 9h.01M10 9h.01M12 2a10 10 0 100 20A10 10 0 0012 2zm0 14s-4-1.5-4-4", stroke: "#9aa3af", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M8 15s1.5 2 4 2 4-2 4-2", stroke: "#9aa3af", strokeWidth: "1.8", strokeLinecap: "round" })] }) }), _jsx(IconBtn, { onClick: () => { }, title: "Attach", children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66L9.41 17.41a2 2 0 01-2.83-2.83l8.49-8.48", stroke: "#9aa3af", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx(IconBtn, { onClick: () => { }, title: "Emoji", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#9aa3af", strokeWidth: "1.8" }), _jsx("path", { d: "M8 14s1.5 2 4 2 4-2 4-2", stroke: "#9aa3af", strokeWidth: "1.8", strokeLinecap: "round" }), _jsx("line", { x1: "9", y1: "9", x2: "9.01", y2: "9", stroke: "#9aa3af", strokeWidth: "2.5", strokeLinecap: "round" }), _jsx("line", { x1: "15", y1: "9", x2: "15.01", y2: "9", stroke: "#9aa3af", strokeWidth: "2.5", strokeLinecap: "round" })] }) }), text.trim() && (_jsx("button", { onClick: handleSend, style: {
|
|
59
|
+
width: 36, height: 36, borderRadius: '50%',
|
|
60
|
+
backgroundColor: t.primaryColor, border: 'none',
|
|
61
|
+
cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
62
|
+
transition: 'transform 0.15s',
|
|
63
|
+
}, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M22 2L11 13M22 2L15 22L11 13L2 9L22 2Z", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] })] })] }));
|
|
64
|
+
};
|
|
65
|
+
const Bubble = ({ msg, primaryColor, font }) => {
|
|
66
|
+
const isMe = msg.senderId === 'me';
|
|
67
|
+
const time = new Date(msg.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
68
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: isMe ? 'flex-end' : 'flex-start', gap: 3 }, children: [_jsx("div", { style: {
|
|
69
|
+
maxWidth: '75%', padding: '10px 14px',
|
|
70
|
+
borderRadius: isMe ? '18px 18px 4px 18px' : '18px 18px 18px 4px',
|
|
71
|
+
backgroundColor: isMe ? primaryColor : '#fff',
|
|
72
|
+
color: isMe ? '#fff' : '#1a2332',
|
|
73
|
+
fontSize: '14px', lineHeight: '1.5',
|
|
74
|
+
boxShadow: '0 1px 4px rgba(0,0,0,0.07)',
|
|
75
|
+
fontFamily: font, wordBreak: 'break-word',
|
|
76
|
+
}, children: msg.text }), _jsx("span", { style: { fontSize: '11px', color: '#b0bec5', padding: '0 4px' }, children: time })] }));
|
|
77
|
+
};
|
|
78
|
+
const IconBtn = ({ onClick, title, children }) => (_jsx("button", { onClick: onClick, title: title, style: { background: 'none', border: 'none', cursor: 'pointer', padding: '4px', display: 'flex', alignItems: 'center', borderRadius: '6px' }, onMouseEnter: e => e.currentTarget.style.background = '#f3f4f6', onMouseLeave: e => e.currentTarget.style.background = 'none', children: children }));
|
|
79
|
+
const iconBtnStyle = {
|
|
80
|
+
background: 'rgba(255,255,255,0.2)', border: 'none', borderRadius: '50%',
|
|
81
|
+
width: 34, height: 34, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
82
|
+
cursor: 'pointer', flexShrink: 0,
|
|
83
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
4
|
+
import { loadChatConfig, buildUserListUrl } from '../config';
|
|
5
|
+
import { mergeTheme } from '../utils/theme';
|
|
6
|
+
import { useUsers } from '../hooks/useUsers';
|
|
7
|
+
import { useChat } from '../hooks/useChat';
|
|
8
|
+
// Screens
|
|
9
|
+
import { HomeScreen } from './HomeScreen';
|
|
10
|
+
import { UserListScreen } from './UserListScreen';
|
|
11
|
+
import { ChatScreen } from './ChatScreen';
|
|
12
|
+
import { RecentChatsScreen } from './RecentChatsScreen';
|
|
13
|
+
import { TicketScreen } from './TicketScreen';
|
|
14
|
+
import { MaintenanceView } from './MaintenanceView';
|
|
15
|
+
import { BottomTabs } from './Tabs/BottomTabs';
|
|
16
|
+
export const ChatWidget = ({ theme }) => {
|
|
17
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
18
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
19
|
+
const [isMaximized, setIsMaximized] = useState(false);
|
|
20
|
+
const [activeTab, setActiveTab] = useState('home');
|
|
21
|
+
const [screen, setScreen] = useState('home');
|
|
22
|
+
const [userListCtx, setUserListCtx] = useState('support');
|
|
23
|
+
const [tickets, setTickets] = useState([]);
|
|
24
|
+
// SSR guard
|
|
25
|
+
useEffect(() => { setIsMounted(true); }, []);
|
|
26
|
+
const config = loadChatConfig();
|
|
27
|
+
const t = mergeTheme(theme);
|
|
28
|
+
const apiUrl = buildUserListUrl(config);
|
|
29
|
+
// Determine filter based on context
|
|
30
|
+
const filterType = userListCtx === 'support' ? 'developer' : 'user';
|
|
31
|
+
const { users, loading, error } = useUsers(apiUrl, filterType, config.status === 'ACTIVE' && screen === 'user-list');
|
|
32
|
+
const { messages, activeUser, selectUser, sendMessage, clearChat } = useChat();
|
|
33
|
+
// ── Navigation helpers ─────────────────────────────────────────────────────
|
|
34
|
+
const goHome = useCallback(() => { setScreen('home'); setActiveTab('home'); }, []);
|
|
35
|
+
const handleTabChange = useCallback((tab) => {
|
|
36
|
+
setActiveTab(tab);
|
|
37
|
+
if (tab === 'home') {
|
|
38
|
+
setScreen('home');
|
|
39
|
+
}
|
|
40
|
+
if (tab === 'chats') {
|
|
41
|
+
setScreen('recent-chats');
|
|
42
|
+
}
|
|
43
|
+
if (tab === 'tickets') {
|
|
44
|
+
setScreen('tickets');
|
|
45
|
+
}
|
|
46
|
+
}, []);
|
|
47
|
+
const handleCardClick = useCallback((ctx) => {
|
|
48
|
+
if (ctx === 'ticket') {
|
|
49
|
+
setActiveTab('tickets');
|
|
50
|
+
setScreen('tickets');
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
setUserListCtx(ctx);
|
|
54
|
+
setScreen('user-list');
|
|
55
|
+
}
|
|
56
|
+
}, []);
|
|
57
|
+
const handleSelectUser = useCallback((user) => {
|
|
58
|
+
selectUser(user);
|
|
59
|
+
setScreen('chat');
|
|
60
|
+
}, [selectUser]);
|
|
61
|
+
const handleBackFromUserList = useCallback(() => {
|
|
62
|
+
setScreen('home');
|
|
63
|
+
}, []);
|
|
64
|
+
const handleBackFromChat = useCallback(() => {
|
|
65
|
+
clearChat();
|
|
66
|
+
setScreen('user-list');
|
|
67
|
+
}, [clearChat]);
|
|
68
|
+
const handleRaiseTicket = useCallback((title, description) => {
|
|
69
|
+
const t = {
|
|
70
|
+
id: `ticket_${Date.now()}`,
|
|
71
|
+
title,
|
|
72
|
+
description,
|
|
73
|
+
status: 'open',
|
|
74
|
+
priority: 'medium',
|
|
75
|
+
createdAt: new Date(),
|
|
76
|
+
updatedAt: new Date(),
|
|
77
|
+
};
|
|
78
|
+
setTickets(prev => [t, ...prev]);
|
|
79
|
+
}, []);
|
|
80
|
+
// ── Sizing ─────────────────────────────────────────────────────────────────
|
|
81
|
+
const normalW = 380;
|
|
82
|
+
const normalH = 560;
|
|
83
|
+
const maxW = 480;
|
|
84
|
+
const maxH = 720;
|
|
85
|
+
const width = isMaximized ? maxW : normalW;
|
|
86
|
+
const height = isMaximized ? maxH : normalH;
|
|
87
|
+
const posStyle = t.buttonPosition === 'bottom-left'
|
|
88
|
+
? { left: 24, right: 'auto' }
|
|
89
|
+
: { right: 24, left: 'auto' };
|
|
90
|
+
if (!isMounted)
|
|
91
|
+
return null;
|
|
92
|
+
if (config.status === 'DISABLE')
|
|
93
|
+
return null;
|
|
94
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
|
|
95
|
+
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap');
|
|
96
|
+
@keyframes cw-fadeUp {
|
|
97
|
+
from { opacity:0; transform:translateY(10px); }
|
|
98
|
+
to { opacity:1; transform:translateY(0); }
|
|
99
|
+
}
|
|
100
|
+
@keyframes cw-slideInRight {
|
|
101
|
+
from { opacity:0; transform:translateX(18px); }
|
|
102
|
+
to { opacity:1; transform:translateX(0); }
|
|
103
|
+
}
|
|
104
|
+
@keyframes cw-popIn {
|
|
105
|
+
from { opacity:0; transform:scale(0.88) translateY(16px); }
|
|
106
|
+
to { opacity:1; transform:scale(1) translateY(0); }
|
|
107
|
+
}
|
|
108
|
+
.cw-scrollbar::-webkit-scrollbar { width:4px; }
|
|
109
|
+
.cw-scrollbar::-webkit-scrollbar-track { background:transparent; }
|
|
110
|
+
.cw-scrollbar::-webkit-scrollbar-thumb { background:#e0e0e0; border-radius:4px; }
|
|
111
|
+
` }), _jsx("button", { onClick: () => setIsOpen(o => !o), "aria-label": isOpen ? 'Close chat' : t.buttonLabel, style: Object.assign(Object.assign({ position: 'fixed', bottom: 24 }, posStyle), { zIndex: 9999, display: 'flex', alignItems: 'center', gap: 10, padding: isOpen ? '14px' : '13px 22px', backgroundColor: t.buttonColor, color: t.buttonTextColor, border: 'none', borderRadius: 50, cursor: 'pointer', fontFamily: t.fontFamily, fontSize: '15px', fontWeight: 700, boxShadow: `0 8px 28px ${t.buttonColor}55`, transition: 'all 0.3s cubic-bezier(0.34,1.56,0.64,1)', transform: isOpen ? 'scale(0.94)' : 'scale(1)', minWidth: isOpen ? 50 : 'auto', justifyContent: 'center' }), onMouseEnter: e => {
|
|
112
|
+
if (!isOpen) {
|
|
113
|
+
e.currentTarget.style.transform = 'scale(1.06) translateY(-2px)';
|
|
114
|
+
e.currentTarget.style.boxShadow = `0 12px 36px ${t.buttonColor}77`;
|
|
115
|
+
}
|
|
116
|
+
}, onMouseLeave: e => {
|
|
117
|
+
e.currentTarget.style.transform = isOpen ? 'scale(0.94)' : 'scale(1)';
|
|
118
|
+
e.currentTarget.style.boxShadow = `0 8px 28px ${t.buttonColor}55`;
|
|
119
|
+
}, children: isOpen
|
|
120
|
+
? _jsx(CloseIcon, { color: t.buttonTextColor })
|
|
121
|
+
: _jsxs(_Fragment, { children: [_jsx(ChatBubbleIcon, { color: t.buttonTextColor }), _jsx("span", { children: t.buttonLabel })] }) }), isOpen && (_jsxs("div", { style: Object.assign(Object.assign({ position: 'fixed', bottom: 86 }, posStyle), { zIndex: 9998, width,
|
|
122
|
+
height, maxWidth: 'calc(100vw - 32px)', maxHeight: 'calc(100vh - 110px)', backgroundColor: '#fff', borderRadius: t.borderRadius, boxShadow: '0 20px 70px rgba(0,0,0,0.2), 0 6px 20px rgba(0,0,0,0.08)', display: 'flex', flexDirection: 'column', overflow: 'hidden', fontFamily: t.fontFamily, animation: 'cw-popIn 0.3s cubic-bezier(0.34,1.56,0.64,1)', transition: 'width 0.3s ease, height 0.3s ease' }), children: [screen !== 'chat' && (_jsx("button", { onClick: () => setIsMaximized(m => !m), title: isMaximized ? 'Minimize' : 'Maximize', style: {
|
|
123
|
+
position: 'absolute', top: 12, right: 48, zIndex: 10,
|
|
124
|
+
background: 'rgba(255,255,255,0.22)', border: 'none', borderRadius: '50%',
|
|
125
|
+
width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
126
|
+
cursor: 'pointer', transition: 'background 0.15s',
|
|
127
|
+
}, onMouseEnter: e => e.currentTarget.style.background = 'rgba(255,255,255,0.38)', onMouseLeave: e => e.currentTarget.style.background = 'rgba(255,255,255,0.22)', children: isMaximized
|
|
128
|
+
? _jsx(MinimizeIcon, {})
|
|
129
|
+
: _jsx(MaximizeIcon, {}) })), screen !== 'chat' && (_jsx("button", { onClick: () => setIsOpen(false), title: "Close", style: {
|
|
130
|
+
position: 'absolute', top: 12, right: 12, zIndex: 10,
|
|
131
|
+
background: 'rgba(255,255,255,0.22)', border: 'none', borderRadius: '50%',
|
|
132
|
+
width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
133
|
+
cursor: 'pointer',
|
|
134
|
+
}, children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }) }) })), _jsxs("div", { className: "cw-scrollbar", style: { flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }, children: [config.status === 'MAINTENANCE' && (_jsx(MaintenanceView, { primaryColor: t.primaryColor, fontFamily: t.fontFamily })), config.status === 'ACTIVE' && (_jsxs(_Fragment, { children: [screen === 'home' && (_jsx(HomeScreen, { config: config, theme: theme, onNavigate: handleCardClick })), screen === 'user-list' && (_jsx(UserListScreen, { context: userListCtx, users: users, loading: loading, error: error, theme: theme, onBack: handleBackFromUserList, onSelectUser: handleSelectUser })), screen === 'chat' && activeUser && (_jsx(ChatScreen, { activeUser: activeUser, messages: messages, onSend: sendMessage, onBack: handleBackFromChat, onClose: () => setIsOpen(false), theme: theme })), screen === 'recent-chats' && (_jsx(RecentChatsScreen, { chats: [], theme: theme, onSelectChat: handleSelectUser })), screen === 'tickets' && (_jsx(TicketScreen, { tickets: tickets, theme: theme, onRaiseTicket: handleRaiseTicket }))] }))] }), screen !== 'chat' && screen !== 'user-list' && config.status !== 'MAINTENANCE' && (_jsx(BottomTabs, { active: activeTab, onChange: handleTabChange, primaryColor: t.primaryColor, fontFamily: t.fontFamily }))] }))] }));
|
|
135
|
+
};
|
|
136
|
+
export default ChatWidget;
|
|
137
|
+
// ── Tiny SVG icons ─────────────────────────────────────────────────────────────
|
|
138
|
+
const ChatBubbleIcon = ({ color }) => (_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: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
139
|
+
const CloseIcon = ({ color }) => (_jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: color, strokeWidth: "2.5", strokeLinecap: "round" }) }));
|
|
140
|
+
const MaximizeIcon = () => (_jsx("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M8 3H5a2 2 0 00-2 2v3M21 8V5a2 2 0 00-2-2h-3M3 16v3a2 2 0 002 2h3M16 21h3a2 2 0 002-2v-3", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }) }));
|
|
141
|
+
const MinimizeIcon = () => (_jsx("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M8 3v5H3M21 8h-5V3M3 16h5v5M16 21v-5h5", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }) }));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChatConfig, ChatWidgetTheme, UserListContext } from '../../types';
|
|
3
|
+
interface HomeScreenProps {
|
|
4
|
+
config: ChatConfig;
|
|
5
|
+
theme?: ChatWidgetTheme;
|
|
6
|
+
onNavigate: (ctx: UserListContext | 'ticket') => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const HomeScreen: React.FC<HomeScreenProps>;
|
|
9
|
+
export {};
|