ajaxter-chat 1.0.3 → 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 -204
- 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.d.ts +0 -13
- package/dist/config/index.js +20 -95
- 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 -15
- 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.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
1
|
import { ChatConfig } from '../types';
|
|
2
2
|
export declare function loadChatConfig(): ChatConfig;
|
|
3
|
-
/**
|
|
4
|
-
* Resolves the URL used by `fetchUsers`.
|
|
5
|
-
*
|
|
6
|
-
* **Same-origin / internal proxy (recommended):** If `CHAT_USER_LIST` starts with `/`
|
|
7
|
-
* (e.g. `/api/v1/chat/users`), the browser only requests your app origin. The real
|
|
8
|
-
* backend URL stays on the server (Next.js route, BFF, etc.) and does not appear in
|
|
9
|
-
* the client Network tab as a cross-origin call.
|
|
10
|
-
*
|
|
11
|
-
* **Absolute URL:** If `CHAT_USER_LIST` is a full `http(s)://...` string, it is used as-is
|
|
12
|
-
* (visible in Network as that host).
|
|
13
|
-
*
|
|
14
|
-
* **Legacy:** Otherwise the path is joined with `CHAT_HOST_URL` and optional `CHAT_HOST_PORT`.
|
|
15
|
-
*/
|
|
16
3
|
export declare function buildUserListUrl(config: ChatConfig): string;
|
package/dist/config/index.js
CHANGED
|
@@ -1,113 +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
|
-
/**
|
|
87
|
-
* Resolves the URL used by `fetchUsers`.
|
|
88
|
-
*
|
|
89
|
-
* **Same-origin / internal proxy (recommended):** If `CHAT_USER_LIST` starts with `/`
|
|
90
|
-
* (e.g. `/api/v1/chat/users`), the browser only requests your app origin. The real
|
|
91
|
-
* backend URL stays on the server (Next.js route, BFF, etc.) and does not appear in
|
|
92
|
-
* the client Network tab as a cross-origin call.
|
|
93
|
-
*
|
|
94
|
-
* **Absolute URL:** If `CHAT_USER_LIST` is a full `http(s)://...` string, it is used as-is
|
|
95
|
-
* (visible in Network as that host).
|
|
96
|
-
*
|
|
97
|
-
* **Legacy:** Otherwise the path is joined with `CHAT_HOST_URL` and optional `CHAT_HOST_PORT`.
|
|
98
|
-
*/
|
|
99
32
|
export function buildUserListUrl(config) {
|
|
100
|
-
const raw = config.userListEndpoint.trim();
|
|
101
|
-
if (raw.startsWith('/')) {
|
|
102
|
-
return raw;
|
|
103
|
-
}
|
|
104
|
-
if (/^https?:\/\//i.test(raw)) {
|
|
105
|
-
return raw;
|
|
106
|
-
}
|
|
107
33
|
const base = config.hostUrl.replace(/\/$/, '');
|
|
108
|
-
const endpoint =
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
return `${base}/${endpoint}`;
|
|
34
|
+
const endpoint = config.userListEndpoint.replace(/^\//, '');
|
|
35
|
+
// Port is optional
|
|
36
|
+
const portPart = config.hostPort ? `:${config.hostPort}` : '';
|
|
37
|
+
return `${base}${portPart}/${endpoint}`;
|
|
113
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,18 +1,9 @@
|
|
|
1
1
|
export async function fetchUsers(url) {
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
credentials: sameOriginPath ? 'same-origin' : 'omit',
|
|
9
|
-
});
|
|
10
|
-
if (!response.ok) {
|
|
11
|
-
throw new Error(`[ChatWidget] Failed to fetch users: ${response.status} ${response.statusText}`);
|
|
12
|
-
}
|
|
13
|
-
const data = await response.json();
|
|
14
|
-
if (!Array.isArray(data)) {
|
|
15
|
-
throw new Error('[ChatWidget] User list API did not return an array.');
|
|
16
|
-
}
|
|
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');
|
|
17
8
|
return data;
|
|
18
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 {};
|