@elqnt/chat 2.0.8 → 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.
- package/README.md +433 -0
- package/dist/api/index.d.mts +250 -8
- package/dist/api/index.d.ts +250 -8
- package/dist/api/index.js +115 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/index.mjs +109 -0
- package/dist/api/index.mjs.map +1 -1
- package/dist/hooks/index.d.mts +80 -0
- package/dist/hooks/index.d.ts +80 -0
- package/dist/hooks/index.js +741 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +715 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/index.d.mts +4 -109
- package/dist/index.d.ts +4 -109
- package/dist/index.js +731 -2039
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +722 -2016
- package/dist/index.mjs.map +1 -1
- package/dist/models/index.d.mts +76 -6
- package/dist/models/index.d.ts +76 -6
- package/dist/models/index.js +21 -0
- package/dist/models/index.js.map +1 -1
- package/dist/models/index.mjs +14 -0
- package/dist/models/index.mjs.map +1 -1
- package/dist/transport/index.d.mts +243 -0
- package/dist/transport/index.d.ts +243 -0
- package/dist/transport/index.js +972 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/index.mjs +940 -0
- package/dist/transport/index.mjs.map +1 -0
- package/dist/types-7UNI1iYv.d.ts +250 -0
- package/dist/types-CQHtUQ6p.d.mts +250 -0
- package/package.json +20 -38
- package/dist/hooks/use-websocket-chat-admin.d.mts +0 -17
- package/dist/hooks/use-websocket-chat-admin.d.ts +0 -17
- package/dist/hooks/use-websocket-chat-admin.js +0 -1196
- package/dist/hooks/use-websocket-chat-admin.js.map +0 -1
- package/dist/hooks/use-websocket-chat-admin.mjs +0 -1172
- package/dist/hooks/use-websocket-chat-admin.mjs.map +0 -1
- package/dist/hooks/use-websocket-chat-base.d.mts +0 -81
- package/dist/hooks/use-websocket-chat-base.d.ts +0 -81
- package/dist/hooks/use-websocket-chat-base.js +0 -1025
- package/dist/hooks/use-websocket-chat-base.js.map +0 -1
- package/dist/hooks/use-websocket-chat-base.mjs +0 -1001
- package/dist/hooks/use-websocket-chat-base.mjs.map +0 -1
- package/dist/hooks/use-websocket-chat-customer.d.mts +0 -24
- package/dist/hooks/use-websocket-chat-customer.d.ts +0 -24
- package/dist/hooks/use-websocket-chat-customer.js +0 -1092
- package/dist/hooks/use-websocket-chat-customer.js.map +0 -1
- package/dist/hooks/use-websocket-chat-customer.mjs +0 -1068
- package/dist/hooks/use-websocket-chat-customer.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use strict";
|
|
3
|
-
var __create = Object.create;
|
|
4
3
|
var __defProp = Object.defineProperty;
|
|
5
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
7
|
var __export = (target, all) => {
|
|
10
8
|
for (var name in all)
|
|
@@ -18,23 +16,11 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
16
|
}
|
|
19
17
|
return to;
|
|
20
18
|
};
|
|
21
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
-
mod
|
|
28
|
-
));
|
|
29
19
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
20
|
|
|
31
|
-
// index.
|
|
21
|
+
// index.ts
|
|
32
22
|
var index_exports = {};
|
|
33
23
|
__export(index_exports, {
|
|
34
|
-
AdminChatHeader: () => AdminChatHeader,
|
|
35
|
-
AdminChatInput: () => AdminChatInput,
|
|
36
|
-
AdminChatList: () => AdminChatList,
|
|
37
|
-
AdminChatListItem: () => AdminChatListItem,
|
|
38
24
|
AgentStatusAway: () => AgentStatusAway,
|
|
39
25
|
AgentStatusBusy: () => AgentStatusBusy,
|
|
40
26
|
AgentStatusOffline: () => AgentStatusOffline,
|
|
@@ -45,6 +31,7 @@ __export(index_exports, {
|
|
|
45
31
|
AttachmentTypeAudio: () => AttachmentTypeAudio,
|
|
46
32
|
AttachmentTypeBullets: () => AttachmentTypeBullets,
|
|
47
33
|
AttachmentTypeData: () => AttachmentTypeData,
|
|
34
|
+
AttachmentTypeDataFile: () => AttachmentTypeDataFile,
|
|
48
35
|
AttachmentTypeDocument: () => AttachmentTypeDocument,
|
|
49
36
|
AttachmentTypeDocumentAnalysis: () => AttachmentTypeDocumentAnalysis,
|
|
50
37
|
AttachmentTypeDocumentSources: () => AttachmentTypeDocumentSources,
|
|
@@ -53,6 +40,7 @@ __export(index_exports, {
|
|
|
53
40
|
AttachmentTypeLocation: () => AttachmentTypeLocation,
|
|
54
41
|
AttachmentTypeRecords: () => AttachmentTypeRecords,
|
|
55
42
|
AttachmentTypeReferences: () => AttachmentTypeReferences,
|
|
43
|
+
AttachmentTypeSpreadsheet: () => AttachmentTypeSpreadsheet,
|
|
56
44
|
AttachmentTypeSticker: () => AttachmentTypeSticker,
|
|
57
45
|
AttachmentTypeSubsections: () => AttachmentTypeSubsections,
|
|
58
46
|
AttachmentTypeVideo: () => AttachmentTypeVideo,
|
|
@@ -66,6 +54,10 @@ __export(index_exports, {
|
|
|
66
54
|
ChatEventTypeAgentSessionEnd: () => ChatEventTypeAgentSessionEnd,
|
|
67
55
|
ChatEventTypeAgentSessionStart: () => ChatEventTypeAgentSessionStart,
|
|
68
56
|
ChatEventTypeAgentStatusChange: () => ChatEventTypeAgentStatusChange,
|
|
57
|
+
ChatEventTypeAttachmentProcessingComplete: () => ChatEventTypeAttachmentProcessingComplete,
|
|
58
|
+
ChatEventTypeAttachmentProcessingError: () => ChatEventTypeAttachmentProcessingError,
|
|
59
|
+
ChatEventTypeAttachmentProcessingProgress: () => ChatEventTypeAttachmentProcessingProgress,
|
|
60
|
+
ChatEventTypeAttachmentProcessingStarted: () => ChatEventTypeAttachmentProcessingStarted,
|
|
69
61
|
ChatEventTypeBlockUser: () => ChatEventTypeBlockUser,
|
|
70
62
|
ChatEventTypeCSATRequest: () => ChatEventTypeCSATRequest,
|
|
71
63
|
ChatEventTypeCSATResponse: () => ChatEventTypeCSATResponse,
|
|
@@ -117,6 +109,7 @@ __export(index_exports, {
|
|
|
117
109
|
ChatEventTypePong: () => ChatEventTypePong,
|
|
118
110
|
ChatEventTypeRead: () => ChatEventTypeRead,
|
|
119
111
|
ChatEventTypeReconnected: () => ChatEventTypeReconnected,
|
|
112
|
+
ChatEventTypeRetryAttachment: () => ChatEventTypeRetryAttachment,
|
|
120
113
|
ChatEventTypeRoomCreated: () => ChatEventTypeRoomCreated,
|
|
121
114
|
ChatEventTypeRoomDeleted: () => ChatEventTypeRoomDeleted,
|
|
122
115
|
ChatEventTypeRoomUpdated: () => ChatEventTypeRoomUpdated,
|
|
@@ -151,10 +144,6 @@ __export(index_exports, {
|
|
|
151
144
|
ChatEventTypeUserSuggestedActions: () => ChatEventTypeUserSuggestedActions,
|
|
152
145
|
ChatEventTypeWaiting: () => ChatEventTypeWaiting,
|
|
153
146
|
ChatEventTypeWaitingForAgent: () => ChatEventTypeWaitingForAgent,
|
|
154
|
-
ChatHeader: () => ChatHeader,
|
|
155
|
-
ChatHumanAgentActions: () => ChatHumanAgentActions,
|
|
156
|
-
ChatInput: () => ChatInput,
|
|
157
|
-
ChatMessages: () => ChatMessages,
|
|
158
147
|
ChatRoleAI: () => ChatRoleAI,
|
|
159
148
|
ChatRoleDataQuery: () => ChatRoleDataQuery,
|
|
160
149
|
ChatRoleEvent: () => ChatRoleEvent,
|
|
@@ -170,19 +159,16 @@ __export(index_exports, {
|
|
|
170
159
|
ChatStatusActive: () => ChatStatusActive,
|
|
171
160
|
ChatStatusArchived: () => ChatStatusArchived,
|
|
172
161
|
ChatStatusClosed: () => ChatStatusClosed,
|
|
173
|
-
ChatStatusCustomerUI: () => ChatStatusCustomerUI,
|
|
174
162
|
ChatStatusDisconnected: () => ChatStatusDisconnected,
|
|
175
163
|
ChatTypeCustomerSupport: () => ChatTypeCustomerSupport,
|
|
176
164
|
ChatTypeDirect: () => ChatTypeDirect,
|
|
177
165
|
ChatTypeGroup: () => ChatTypeGroup,
|
|
178
166
|
ChatTypePrivateRoom: () => ChatTypePrivateRoom,
|
|
179
167
|
ChatTypePublicRoom: () => ChatTypePublicRoom,
|
|
180
|
-
ChatTypingIndicator: () => ChatTypingIndicator,
|
|
181
168
|
CompleteChatByAgentSubject: () => CompleteChatByAgentSubject,
|
|
182
169
|
CreateAgentQueueSubject: () => CreateAgentQueueSubject,
|
|
183
170
|
DeleteAgentQueueSubject: () => DeleteAgentQueueSubject,
|
|
184
171
|
EndAgentSessionSubject: () => EndAgentSessionSubject,
|
|
185
|
-
GenericChatWidget: () => GenericChatWidget,
|
|
186
172
|
GetActiveChatCountSubject: () => GetActiveChatCountSubject,
|
|
187
173
|
GetActiveChatsSubject: () => GetActiveChatsSubject,
|
|
188
174
|
GetAgentQueuesSubject: () => GetAgentQueuesSubject,
|
|
@@ -218,28 +204,719 @@ __export(index_exports, {
|
|
|
218
204
|
UserStatusBusy: () => UserStatusBusy,
|
|
219
205
|
UserStatusOffline: () => UserStatusOffline,
|
|
220
206
|
UserStatusOnline: () => UserStatusOnline,
|
|
221
|
-
|
|
222
|
-
WebSocketChatAdminProvider: () => WebSocketChatAdminProvider,
|
|
223
|
-
WebSocketChatCustomerContext: () => WebSocketChatCustomerContext,
|
|
224
|
-
WebSocketChatCustomerProvider: () => WebSocketChatCustomerProvider,
|
|
225
|
-
useWebSocketChatAdmin: () => useWebSocketChatAdmin,
|
|
226
|
-
useWebSocketChatAdminContext: () => useWebSocketChatAdminContext,
|
|
227
|
-
useWebSocketChatBase: () => useWebSocketChatBase,
|
|
228
|
-
useWebSocketChatCustomer: () => useWebSocketChatCustomer,
|
|
229
|
-
useWebSocketChatCustomerContext: () => useWebSocketChatCustomerContext
|
|
207
|
+
useChat: () => useChat
|
|
230
208
|
});
|
|
231
209
|
module.exports = __toCommonJS(index_exports);
|
|
232
210
|
|
|
233
|
-
//
|
|
234
|
-
var
|
|
235
|
-
|
|
236
|
-
|
|
211
|
+
// hooks/use-chat.ts
|
|
212
|
+
var import_react = require("react");
|
|
213
|
+
|
|
214
|
+
// transport/types.ts
|
|
215
|
+
function createLogger(debug = false) {
|
|
216
|
+
return {
|
|
217
|
+
debug: debug ? console.log.bind(console, "[chat]") : () => {
|
|
218
|
+
},
|
|
219
|
+
info: console.info.bind(console, "[chat]"),
|
|
220
|
+
warn: console.warn.bind(console, "[chat]"),
|
|
221
|
+
error: console.error.bind(console, "[chat]")
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
225
|
+
maxRetries: 10,
|
|
226
|
+
intervals: [1e3, 2e3, 5e3],
|
|
227
|
+
backoffMultiplier: 1.5,
|
|
228
|
+
maxBackoffTime: 3e4
|
|
229
|
+
};
|
|
230
|
+
function calculateRetryInterval(retryCount, config = DEFAULT_RETRY_CONFIG) {
|
|
231
|
+
const {
|
|
232
|
+
intervals = DEFAULT_RETRY_CONFIG.intervals,
|
|
233
|
+
backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,
|
|
234
|
+
maxBackoffTime = DEFAULT_RETRY_CONFIG.maxBackoffTime
|
|
235
|
+
} = config;
|
|
236
|
+
if (retryCount < intervals.length) {
|
|
237
|
+
return intervals[retryCount];
|
|
238
|
+
}
|
|
239
|
+
const baseInterval = intervals[intervals.length - 1] || 5e3;
|
|
240
|
+
const backoffTime = baseInterval * Math.pow(backoffMultiplier, retryCount - intervals.length + 1);
|
|
241
|
+
return Math.min(backoffTime, maxBackoffTime);
|
|
242
|
+
}
|
|
237
243
|
|
|
238
|
-
//
|
|
239
|
-
|
|
244
|
+
// transport/sse.ts
|
|
245
|
+
function createSSETransport(options = {}) {
|
|
246
|
+
const {
|
|
247
|
+
retryConfig = DEFAULT_RETRY_CONFIG,
|
|
248
|
+
debug = false,
|
|
249
|
+
logger = createLogger(debug)
|
|
250
|
+
} = options;
|
|
251
|
+
let eventSource;
|
|
252
|
+
let config;
|
|
253
|
+
let state = "disconnected";
|
|
254
|
+
let error;
|
|
255
|
+
let retryCount = 0;
|
|
256
|
+
let reconnectTimeout;
|
|
257
|
+
let intentionalDisconnect = false;
|
|
258
|
+
const metrics = {
|
|
259
|
+
latency: 0,
|
|
260
|
+
messagesSent: 0,
|
|
261
|
+
messagesReceived: 0,
|
|
262
|
+
messagesQueued: 0,
|
|
263
|
+
reconnectCount: 0,
|
|
264
|
+
transportType: "sse"
|
|
265
|
+
};
|
|
266
|
+
const globalHandlers = /* @__PURE__ */ new Set();
|
|
267
|
+
const typeHandlers = /* @__PURE__ */ new Map();
|
|
268
|
+
function emit(event) {
|
|
269
|
+
metrics.messagesReceived++;
|
|
270
|
+
metrics.lastMessageAt = Date.now();
|
|
271
|
+
globalHandlers.forEach((handler) => {
|
|
272
|
+
try {
|
|
273
|
+
handler(event);
|
|
274
|
+
} catch (err) {
|
|
275
|
+
logger.error("Error in message handler:", err);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
const handlers = typeHandlers.get(event.type);
|
|
279
|
+
if (handlers) {
|
|
280
|
+
handlers.forEach((handler) => {
|
|
281
|
+
try {
|
|
282
|
+
handler(event);
|
|
283
|
+
} catch (err) {
|
|
284
|
+
logger.error(`Error in ${event.type} handler:`, err);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async function sendRest(endpoint, body) {
|
|
290
|
+
if (!config) {
|
|
291
|
+
throw new Error("Transport not connected");
|
|
292
|
+
}
|
|
293
|
+
const url = `${config.baseUrl}/${endpoint}`;
|
|
294
|
+
logger.debug(`POST ${endpoint}`, body);
|
|
295
|
+
const response = await fetch(url, {
|
|
296
|
+
method: "POST",
|
|
297
|
+
headers: { "Content-Type": "application/json" },
|
|
298
|
+
body: JSON.stringify(body)
|
|
299
|
+
});
|
|
300
|
+
if (!response.ok) {
|
|
301
|
+
const errorText = await response.text();
|
|
302
|
+
throw new Error(`API error: ${response.status} - ${errorText}`);
|
|
303
|
+
}
|
|
304
|
+
const json = await response.json();
|
|
305
|
+
if (json && typeof json === "object" && "data" in json) {
|
|
306
|
+
return json.data;
|
|
307
|
+
}
|
|
308
|
+
return json;
|
|
309
|
+
}
|
|
310
|
+
function handleMessage(event) {
|
|
311
|
+
if (!event.data || event.data === "") return;
|
|
312
|
+
try {
|
|
313
|
+
const data = JSON.parse(event.data);
|
|
314
|
+
logger.debug("Received:", data.type);
|
|
315
|
+
emit(data);
|
|
316
|
+
} catch (err) {
|
|
317
|
+
logger.error("Failed to parse SSE message:", err);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function setupEventListeners(es) {
|
|
321
|
+
es.addEventListener("message", handleMessage);
|
|
322
|
+
const eventTypes = [
|
|
323
|
+
"reconnected",
|
|
324
|
+
"typing",
|
|
325
|
+
"stopped_typing",
|
|
326
|
+
"waiting",
|
|
327
|
+
"waiting_for_agent",
|
|
328
|
+
"human_agent_joined",
|
|
329
|
+
"human_agent_left",
|
|
330
|
+
"chat_ended",
|
|
331
|
+
"chat_updated",
|
|
332
|
+
"load_chat_response",
|
|
333
|
+
"new_chat_created",
|
|
334
|
+
"show_csat_survey",
|
|
335
|
+
"csat_response",
|
|
336
|
+
"user_suggested_actions",
|
|
337
|
+
"agent_execution_started",
|
|
338
|
+
"agent_execution_ended",
|
|
339
|
+
"agent_context_update",
|
|
340
|
+
"plan_pending_approval",
|
|
341
|
+
"step_started",
|
|
342
|
+
"step_completed",
|
|
343
|
+
"step_failed",
|
|
344
|
+
"plan_completed",
|
|
345
|
+
"skills_changed",
|
|
346
|
+
"summary_update"
|
|
347
|
+
];
|
|
348
|
+
eventTypes.forEach((type) => {
|
|
349
|
+
es.addEventListener(type, handleMessage);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
function scheduleReconnect() {
|
|
353
|
+
if (intentionalDisconnect || !config) return;
|
|
354
|
+
const maxRetries = retryConfig.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries;
|
|
355
|
+
if (retryCount >= maxRetries) {
|
|
356
|
+
logger.error(`Max retries (${maxRetries}) exceeded`);
|
|
357
|
+
error = {
|
|
358
|
+
code: "CONNECTION_FAILED",
|
|
359
|
+
message: `Max retries (${maxRetries}) exceeded`,
|
|
360
|
+
retryable: false,
|
|
361
|
+
timestamp: Date.now()
|
|
362
|
+
};
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const interval = calculateRetryInterval(retryCount, retryConfig);
|
|
366
|
+
retryCount++;
|
|
367
|
+
metrics.reconnectCount++;
|
|
368
|
+
logger.info(`Reconnecting in ${interval}ms (attempt ${retryCount})`);
|
|
369
|
+
state = "reconnecting";
|
|
370
|
+
reconnectTimeout = setTimeout(() => {
|
|
371
|
+
if (config) {
|
|
372
|
+
transport.connect(config).catch((err) => {
|
|
373
|
+
logger.error("Reconnect failed:", err);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}, interval);
|
|
377
|
+
}
|
|
378
|
+
const transport = {
|
|
379
|
+
async connect(cfg) {
|
|
380
|
+
config = cfg;
|
|
381
|
+
intentionalDisconnect = false;
|
|
382
|
+
if (eventSource) {
|
|
383
|
+
eventSource.close();
|
|
384
|
+
eventSource = void 0;
|
|
385
|
+
}
|
|
386
|
+
if (reconnectTimeout) {
|
|
387
|
+
clearTimeout(reconnectTimeout);
|
|
388
|
+
reconnectTimeout = void 0;
|
|
389
|
+
}
|
|
390
|
+
state = retryCount > 0 ? "reconnecting" : "connecting";
|
|
391
|
+
return new Promise((resolve, reject) => {
|
|
392
|
+
const connectionStart = Date.now();
|
|
393
|
+
const url = `${cfg.baseUrl}/stream?orgId=${cfg.orgId}&userId=${cfg.userId}&clientType=${cfg.clientType}${cfg.chatKey ? `&chatId=${cfg.chatKey}` : ""}`;
|
|
394
|
+
logger.debug("Connecting to:", url);
|
|
395
|
+
const es = new EventSource(url);
|
|
396
|
+
es.onopen = () => {
|
|
397
|
+
const connectionTime = Date.now() - connectionStart;
|
|
398
|
+
logger.info(`Connected in ${connectionTime}ms`);
|
|
399
|
+
state = "connected";
|
|
400
|
+
error = void 0;
|
|
401
|
+
retryCount = 0;
|
|
402
|
+
metrics.connectedAt = Date.now();
|
|
403
|
+
metrics.latency = connectionTime;
|
|
404
|
+
setupEventListeners(es);
|
|
405
|
+
resolve();
|
|
406
|
+
};
|
|
407
|
+
es.onerror = () => {
|
|
408
|
+
if (es.readyState === EventSource.CLOSED) {
|
|
409
|
+
const sseError = {
|
|
410
|
+
code: "CONNECTION_FAILED",
|
|
411
|
+
message: "SSE connection failed",
|
|
412
|
+
retryable: true,
|
|
413
|
+
timestamp: Date.now()
|
|
414
|
+
};
|
|
415
|
+
error = sseError;
|
|
416
|
+
metrics.lastError = sseError;
|
|
417
|
+
state = "disconnected";
|
|
418
|
+
if (!intentionalDisconnect) {
|
|
419
|
+
scheduleReconnect();
|
|
420
|
+
}
|
|
421
|
+
if (retryCount === 0) {
|
|
422
|
+
reject(sseError);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
eventSource = es;
|
|
427
|
+
});
|
|
428
|
+
},
|
|
429
|
+
disconnect(intentional = true) {
|
|
430
|
+
logger.info("Disconnecting", { intentional });
|
|
431
|
+
intentionalDisconnect = intentional;
|
|
432
|
+
if (reconnectTimeout) {
|
|
433
|
+
clearTimeout(reconnectTimeout);
|
|
434
|
+
reconnectTimeout = void 0;
|
|
435
|
+
}
|
|
436
|
+
if (eventSource) {
|
|
437
|
+
eventSource.close();
|
|
438
|
+
eventSource = void 0;
|
|
439
|
+
}
|
|
440
|
+
state = "disconnected";
|
|
441
|
+
retryCount = 0;
|
|
442
|
+
},
|
|
443
|
+
async send(event) {
|
|
444
|
+
if (!config) {
|
|
445
|
+
throw new Error("Transport not connected");
|
|
446
|
+
}
|
|
447
|
+
switch (event.type) {
|
|
448
|
+
case "message":
|
|
449
|
+
await sendRest("send", {
|
|
450
|
+
orgId: event.orgId,
|
|
451
|
+
chatKey: event.chatKey,
|
|
452
|
+
userId: event.userId,
|
|
453
|
+
message: event.message
|
|
454
|
+
});
|
|
455
|
+
break;
|
|
456
|
+
case "typing":
|
|
457
|
+
await sendRest("typing", {
|
|
458
|
+
orgId: event.orgId,
|
|
459
|
+
chatKey: event.chatKey,
|
|
460
|
+
userId: event.userId,
|
|
461
|
+
typing: true
|
|
462
|
+
});
|
|
463
|
+
break;
|
|
464
|
+
case "stopped_typing":
|
|
465
|
+
await sendRest("typing", {
|
|
466
|
+
orgId: event.orgId,
|
|
467
|
+
chatKey: event.chatKey,
|
|
468
|
+
userId: event.userId,
|
|
469
|
+
typing: false
|
|
470
|
+
});
|
|
471
|
+
break;
|
|
472
|
+
case "load_chat":
|
|
473
|
+
await sendRest("load", {
|
|
474
|
+
orgId: event.orgId,
|
|
475
|
+
chatKey: event.chatKey,
|
|
476
|
+
userId: event.userId
|
|
477
|
+
});
|
|
478
|
+
break;
|
|
479
|
+
case "new_chat":
|
|
480
|
+
await sendRest("create", {
|
|
481
|
+
orgId: event.orgId,
|
|
482
|
+
userId: event.userId,
|
|
483
|
+
metadata: event.data
|
|
484
|
+
});
|
|
485
|
+
break;
|
|
486
|
+
case "end_chat":
|
|
487
|
+
await sendRest("end", {
|
|
488
|
+
orgId: event.orgId,
|
|
489
|
+
chatKey: event.chatKey,
|
|
490
|
+
userId: event.userId,
|
|
491
|
+
data: event.data
|
|
492
|
+
});
|
|
493
|
+
break;
|
|
494
|
+
default:
|
|
495
|
+
await sendRest("event", {
|
|
496
|
+
type: event.type,
|
|
497
|
+
orgId: event.orgId,
|
|
498
|
+
chatKey: event.chatKey,
|
|
499
|
+
userId: event.userId,
|
|
500
|
+
data: event.data
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
metrics.messagesSent++;
|
|
504
|
+
},
|
|
505
|
+
async sendMessage(message) {
|
|
506
|
+
if (!config) {
|
|
507
|
+
throw new Error("Transport not connected");
|
|
508
|
+
}
|
|
509
|
+
await sendRest("send", {
|
|
510
|
+
orgId: config.orgId,
|
|
511
|
+
chatKey: config.chatKey,
|
|
512
|
+
userId: config.userId,
|
|
513
|
+
message
|
|
514
|
+
});
|
|
515
|
+
metrics.messagesSent++;
|
|
516
|
+
},
|
|
517
|
+
async createChat(options2) {
|
|
518
|
+
if (!config) {
|
|
519
|
+
throw new Error("Transport not connected");
|
|
520
|
+
}
|
|
521
|
+
const response = await sendRest("create", {
|
|
522
|
+
orgId: options2.orgId,
|
|
523
|
+
userId: options2.userId,
|
|
524
|
+
metadata: options2.metadata
|
|
525
|
+
});
|
|
526
|
+
if (!response?.chatKey) {
|
|
527
|
+
throw new Error("Failed to create chat: no chatKey returned");
|
|
528
|
+
}
|
|
529
|
+
return { chatKey: response.chatKey };
|
|
530
|
+
},
|
|
531
|
+
async loadChatData(options2) {
|
|
532
|
+
if (!config) {
|
|
533
|
+
throw new Error("Transport not connected");
|
|
534
|
+
}
|
|
535
|
+
const response = await sendRest("load", {
|
|
536
|
+
orgId: options2.orgId,
|
|
537
|
+
chatKey: options2.chatKey,
|
|
538
|
+
userId: options2.userId
|
|
539
|
+
});
|
|
540
|
+
if (!response?.chat) {
|
|
541
|
+
throw new Error("Failed to load chat: no chat data returned");
|
|
542
|
+
}
|
|
543
|
+
return {
|
|
544
|
+
chat: response.chat,
|
|
545
|
+
agentId: response.agentId
|
|
546
|
+
};
|
|
547
|
+
},
|
|
548
|
+
onMessage(handler) {
|
|
549
|
+
globalHandlers.add(handler);
|
|
550
|
+
return () => globalHandlers.delete(handler);
|
|
551
|
+
},
|
|
552
|
+
on(eventType, handler) {
|
|
553
|
+
if (!typeHandlers.has(eventType)) {
|
|
554
|
+
typeHandlers.set(eventType, /* @__PURE__ */ new Set());
|
|
555
|
+
}
|
|
556
|
+
typeHandlers.get(eventType).add(handler);
|
|
557
|
+
return () => {
|
|
558
|
+
const handlers = typeHandlers.get(eventType);
|
|
559
|
+
if (handlers) {
|
|
560
|
+
handlers.delete(handler);
|
|
561
|
+
if (handlers.size === 0) {
|
|
562
|
+
typeHandlers.delete(eventType);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
},
|
|
567
|
+
getState() {
|
|
568
|
+
return state;
|
|
569
|
+
},
|
|
570
|
+
getMetrics() {
|
|
571
|
+
return { ...metrics };
|
|
572
|
+
},
|
|
573
|
+
getError() {
|
|
574
|
+
return error;
|
|
575
|
+
},
|
|
576
|
+
clearError() {
|
|
577
|
+
error = void 0;
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
return transport;
|
|
581
|
+
}
|
|
240
582
|
|
|
241
|
-
// hooks/use-
|
|
242
|
-
|
|
583
|
+
// hooks/use-chat.ts
|
|
584
|
+
function getDefaultTransport(type, debug) {
|
|
585
|
+
return createSSETransport({ debug });
|
|
586
|
+
}
|
|
587
|
+
function useChat(options) {
|
|
588
|
+
const {
|
|
589
|
+
baseUrl,
|
|
590
|
+
orgId,
|
|
591
|
+
userId,
|
|
592
|
+
clientType = "customer",
|
|
593
|
+
transport: transportOption,
|
|
594
|
+
onMessage,
|
|
595
|
+
onError,
|
|
596
|
+
onConnectionChange,
|
|
597
|
+
autoConnect = false,
|
|
598
|
+
retryConfig,
|
|
599
|
+
debug = false
|
|
600
|
+
} = options;
|
|
601
|
+
const [connectionState, setConnectionState] = (0, import_react.useState)("disconnected");
|
|
602
|
+
const [currentChat, setCurrentChat] = (0, import_react.useState)(null);
|
|
603
|
+
const [chatKey, setChatKey] = (0, import_react.useState)(null);
|
|
604
|
+
const [messages, setMessages] = (0, import_react.useState)([]);
|
|
605
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
606
|
+
const [metrics, setMetrics] = (0, import_react.useState)({
|
|
607
|
+
latency: 0,
|
|
608
|
+
messagesSent: 0,
|
|
609
|
+
messagesReceived: 0,
|
|
610
|
+
messagesQueued: 0,
|
|
611
|
+
reconnectCount: 0
|
|
612
|
+
});
|
|
613
|
+
const transportRef = (0, import_react.useRef)(null);
|
|
614
|
+
const mountedRef = (0, import_react.useRef)(false);
|
|
615
|
+
const onMessageRef = (0, import_react.useRef)(onMessage);
|
|
616
|
+
const onErrorRef = (0, import_react.useRef)(onError);
|
|
617
|
+
const typingTimeoutRef = (0, import_react.useRef)(null);
|
|
618
|
+
(0, import_react.useEffect)(() => {
|
|
619
|
+
onMessageRef.current = onMessage;
|
|
620
|
+
onErrorRef.current = onError;
|
|
621
|
+
}, [onMessage, onError]);
|
|
622
|
+
(0, import_react.useEffect)(() => {
|
|
623
|
+
if (typeof transportOption === "object") {
|
|
624
|
+
transportRef.current = transportOption;
|
|
625
|
+
} else {
|
|
626
|
+
transportRef.current = getDefaultTransport(
|
|
627
|
+
transportOption,
|
|
628
|
+
debug
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
}, [transportOption, debug]);
|
|
632
|
+
const handleEvent = (0, import_react.useCallback)((event) => {
|
|
633
|
+
if (!mountedRef.current) return;
|
|
634
|
+
setMetrics((prev) => ({
|
|
635
|
+
...prev,
|
|
636
|
+
messagesReceived: prev.messagesReceived + 1,
|
|
637
|
+
lastMessageAt: Date.now()
|
|
638
|
+
}));
|
|
639
|
+
switch (event.type) {
|
|
640
|
+
case "new_chat_created":
|
|
641
|
+
const newChatKey = event.data?.chatKey;
|
|
642
|
+
if (newChatKey) {
|
|
643
|
+
setChatKey(newChatKey);
|
|
644
|
+
}
|
|
645
|
+
break;
|
|
646
|
+
case "load_chat_response":
|
|
647
|
+
const chat = event.data?.chat;
|
|
648
|
+
if (chat) {
|
|
649
|
+
setCurrentChat(chat);
|
|
650
|
+
setChatKey(chat.key);
|
|
651
|
+
setMessages(chat.messages || []);
|
|
652
|
+
}
|
|
653
|
+
break;
|
|
654
|
+
case "message":
|
|
655
|
+
if (event.message) {
|
|
656
|
+
setMessages((prev) => [...prev, event.message]);
|
|
657
|
+
}
|
|
658
|
+
break;
|
|
659
|
+
case "chat_ended":
|
|
660
|
+
setCurrentChat(null);
|
|
661
|
+
setChatKey(null);
|
|
662
|
+
break;
|
|
663
|
+
case "error":
|
|
664
|
+
const errorMsg = event.data?.message;
|
|
665
|
+
if (errorMsg) {
|
|
666
|
+
const transportError = {
|
|
667
|
+
code: "NETWORK_ERROR",
|
|
668
|
+
message: errorMsg,
|
|
669
|
+
retryable: true,
|
|
670
|
+
timestamp: Date.now()
|
|
671
|
+
};
|
|
672
|
+
setError(transportError);
|
|
673
|
+
onErrorRef.current?.(transportError);
|
|
674
|
+
}
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
onMessageRef.current?.(event);
|
|
678
|
+
}, []);
|
|
679
|
+
const connect = (0, import_react.useCallback)(async () => {
|
|
680
|
+
const transport = transportRef.current;
|
|
681
|
+
if (!transport) {
|
|
682
|
+
throw new Error("Transport not initialized");
|
|
683
|
+
}
|
|
684
|
+
if (!orgId) {
|
|
685
|
+
const err = {
|
|
686
|
+
code: "CONNECTION_FAILED",
|
|
687
|
+
message: "orgId is required",
|
|
688
|
+
retryable: false,
|
|
689
|
+
timestamp: Date.now()
|
|
690
|
+
};
|
|
691
|
+
setError(err);
|
|
692
|
+
throw err;
|
|
693
|
+
}
|
|
694
|
+
setConnectionState("connecting");
|
|
695
|
+
try {
|
|
696
|
+
const unsubscribe = transport.onMessage(handleEvent);
|
|
697
|
+
await transport.connect({
|
|
698
|
+
baseUrl,
|
|
699
|
+
orgId,
|
|
700
|
+
userId,
|
|
701
|
+
clientType,
|
|
702
|
+
chatKey: chatKey || void 0,
|
|
703
|
+
debug
|
|
704
|
+
});
|
|
705
|
+
setConnectionState("connected");
|
|
706
|
+
setError(null);
|
|
707
|
+
setMetrics(transport.getMetrics());
|
|
708
|
+
onConnectionChange?.("connected");
|
|
709
|
+
return;
|
|
710
|
+
} catch (err) {
|
|
711
|
+
const transportError = err;
|
|
712
|
+
setConnectionState("disconnected");
|
|
713
|
+
setError(transportError);
|
|
714
|
+
onErrorRef.current?.(transportError);
|
|
715
|
+
throw err;
|
|
716
|
+
}
|
|
717
|
+
}, [baseUrl, orgId, userId, clientType, chatKey, debug, handleEvent, onConnectionChange]);
|
|
718
|
+
const disconnect = (0, import_react.useCallback)(() => {
|
|
719
|
+
const transport = transportRef.current;
|
|
720
|
+
if (transport) {
|
|
721
|
+
transport.disconnect(true);
|
|
722
|
+
}
|
|
723
|
+
setConnectionState("disconnected");
|
|
724
|
+
onConnectionChange?.("disconnected");
|
|
725
|
+
}, [onConnectionChange]);
|
|
726
|
+
const startChat = (0, import_react.useCallback)(
|
|
727
|
+
async (metadata) => {
|
|
728
|
+
const transport = transportRef.current;
|
|
729
|
+
if (!transport) {
|
|
730
|
+
throw new Error("Transport not initialized");
|
|
731
|
+
}
|
|
732
|
+
const response = await transport.createChat({
|
|
733
|
+
orgId,
|
|
734
|
+
userId,
|
|
735
|
+
metadata
|
|
736
|
+
});
|
|
737
|
+
setChatKey(response.chatKey);
|
|
738
|
+
return response.chatKey;
|
|
739
|
+
},
|
|
740
|
+
[orgId, userId]
|
|
741
|
+
);
|
|
742
|
+
const loadChat = (0, import_react.useCallback)(
|
|
743
|
+
async (key) => {
|
|
744
|
+
const transport = transportRef.current;
|
|
745
|
+
if (!transport) {
|
|
746
|
+
throw new Error("Transport not initialized");
|
|
747
|
+
}
|
|
748
|
+
const response = await transport.loadChatData({
|
|
749
|
+
orgId,
|
|
750
|
+
chatKey: key,
|
|
751
|
+
userId
|
|
752
|
+
});
|
|
753
|
+
setCurrentChat(response.chat);
|
|
754
|
+
setChatKey(response.chat.key);
|
|
755
|
+
setMessages(response.chat.messages || []);
|
|
756
|
+
return response.chat;
|
|
757
|
+
},
|
|
758
|
+
[orgId, userId]
|
|
759
|
+
);
|
|
760
|
+
const sendMessage = (0, import_react.useCallback)(
|
|
761
|
+
async (content, attachments) => {
|
|
762
|
+
const transport = transportRef.current;
|
|
763
|
+
if (!transport) {
|
|
764
|
+
throw new Error("Transport not initialized");
|
|
765
|
+
}
|
|
766
|
+
if (!chatKey) {
|
|
767
|
+
throw new Error("No active chat");
|
|
768
|
+
}
|
|
769
|
+
const message = {
|
|
770
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
771
|
+
role: "user",
|
|
772
|
+
content,
|
|
773
|
+
time: Date.now(),
|
|
774
|
+
status: "sending",
|
|
775
|
+
senderId: userId,
|
|
776
|
+
createdAt: Date.now(),
|
|
777
|
+
attachments
|
|
778
|
+
};
|
|
779
|
+
setMessages((prev) => [...prev, message]);
|
|
780
|
+
await transport.send({
|
|
781
|
+
type: "message",
|
|
782
|
+
orgId,
|
|
783
|
+
chatKey,
|
|
784
|
+
userId,
|
|
785
|
+
timestamp: Date.now(),
|
|
786
|
+
message
|
|
787
|
+
});
|
|
788
|
+
setMetrics((prev) => ({
|
|
789
|
+
...prev,
|
|
790
|
+
messagesSent: prev.messagesSent + 1
|
|
791
|
+
}));
|
|
792
|
+
},
|
|
793
|
+
[orgId, chatKey, userId]
|
|
794
|
+
);
|
|
795
|
+
const endChat = (0, import_react.useCallback)(
|
|
796
|
+
async (reason) => {
|
|
797
|
+
const transport = transportRef.current;
|
|
798
|
+
if (!transport) {
|
|
799
|
+
throw new Error("Transport not initialized");
|
|
800
|
+
}
|
|
801
|
+
if (!chatKey) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
await transport.send({
|
|
805
|
+
type: "end_chat",
|
|
806
|
+
orgId,
|
|
807
|
+
chatKey,
|
|
808
|
+
userId,
|
|
809
|
+
timestamp: Date.now(),
|
|
810
|
+
data: reason ? { reason } : void 0
|
|
811
|
+
});
|
|
812
|
+
setCurrentChat(null);
|
|
813
|
+
setChatKey(null);
|
|
814
|
+
},
|
|
815
|
+
[orgId, chatKey, userId]
|
|
816
|
+
);
|
|
817
|
+
const sendEvent = (0, import_react.useCallback)(
|
|
818
|
+
async (event) => {
|
|
819
|
+
const transport = transportRef.current;
|
|
820
|
+
if (!transport) {
|
|
821
|
+
throw new Error("Transport not initialized");
|
|
822
|
+
}
|
|
823
|
+
await transport.send({
|
|
824
|
+
...event,
|
|
825
|
+
timestamp: Date.now()
|
|
826
|
+
});
|
|
827
|
+
},
|
|
828
|
+
[]
|
|
829
|
+
);
|
|
830
|
+
const startTyping = (0, import_react.useCallback)(() => {
|
|
831
|
+
const transport = transportRef.current;
|
|
832
|
+
if (!transport || !chatKey) return;
|
|
833
|
+
if (typingTimeoutRef.current) {
|
|
834
|
+
clearTimeout(typingTimeoutRef.current);
|
|
835
|
+
}
|
|
836
|
+
transport.send({
|
|
837
|
+
type: "typing",
|
|
838
|
+
orgId,
|
|
839
|
+
chatKey,
|
|
840
|
+
userId,
|
|
841
|
+
timestamp: Date.now()
|
|
842
|
+
}).catch(() => {
|
|
843
|
+
});
|
|
844
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
845
|
+
stopTyping();
|
|
846
|
+
}, 3e3);
|
|
847
|
+
}, [orgId, chatKey, userId]);
|
|
848
|
+
const stopTyping = (0, import_react.useCallback)(() => {
|
|
849
|
+
const transport = transportRef.current;
|
|
850
|
+
if (!transport || !chatKey) return;
|
|
851
|
+
if (typingTimeoutRef.current) {
|
|
852
|
+
clearTimeout(typingTimeoutRef.current);
|
|
853
|
+
typingTimeoutRef.current = null;
|
|
854
|
+
}
|
|
855
|
+
transport.send({
|
|
856
|
+
type: "stopped_typing",
|
|
857
|
+
orgId,
|
|
858
|
+
chatKey,
|
|
859
|
+
userId,
|
|
860
|
+
timestamp: Date.now()
|
|
861
|
+
}).catch(() => {
|
|
862
|
+
});
|
|
863
|
+
}, [orgId, chatKey, userId]);
|
|
864
|
+
const on = (0, import_react.useCallback)(
|
|
865
|
+
(eventType, handler) => {
|
|
866
|
+
const transport = transportRef.current;
|
|
867
|
+
if (!transport) {
|
|
868
|
+
return () => {
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
return transport.on(eventType, handler);
|
|
872
|
+
},
|
|
873
|
+
[]
|
|
874
|
+
);
|
|
875
|
+
const clearError = (0, import_react.useCallback)(() => {
|
|
876
|
+
setError(null);
|
|
877
|
+
transportRef.current?.clearError();
|
|
878
|
+
}, []);
|
|
879
|
+
(0, import_react.useEffect)(() => {
|
|
880
|
+
mountedRef.current = true;
|
|
881
|
+
if (autoConnect) {
|
|
882
|
+
connect().catch(() => {
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
return () => {
|
|
886
|
+
mountedRef.current = false;
|
|
887
|
+
if (typingTimeoutRef.current) {
|
|
888
|
+
clearTimeout(typingTimeoutRef.current);
|
|
889
|
+
}
|
|
890
|
+
disconnect();
|
|
891
|
+
};
|
|
892
|
+
}, []);
|
|
893
|
+
const isConnected = connectionState === "connected";
|
|
894
|
+
return {
|
|
895
|
+
// Connection
|
|
896
|
+
connect,
|
|
897
|
+
disconnect,
|
|
898
|
+
connectionState,
|
|
899
|
+
isConnected,
|
|
900
|
+
// Chat operations
|
|
901
|
+
startChat,
|
|
902
|
+
loadChat,
|
|
903
|
+
sendMessage,
|
|
904
|
+
sendEvent,
|
|
905
|
+
endChat,
|
|
906
|
+
// Typing
|
|
907
|
+
startTyping,
|
|
908
|
+
stopTyping,
|
|
909
|
+
// State
|
|
910
|
+
currentChat,
|
|
911
|
+
chatKey,
|
|
912
|
+
messages,
|
|
913
|
+
error,
|
|
914
|
+
metrics,
|
|
915
|
+
// Events
|
|
916
|
+
on,
|
|
917
|
+
clearError
|
|
918
|
+
};
|
|
919
|
+
}
|
|
243
920
|
|
|
244
921
|
// models/chat-models.ts
|
|
245
922
|
var ChatStatusActive = "active";
|
|
@@ -358,6 +1035,11 @@ var ChatEventTypePong = "pong";
|
|
|
358
1035
|
var ChatEventTypeSkillActivate = "skill_activate";
|
|
359
1036
|
var ChatEventTypeSkillDeactivate = "skill_deactivate";
|
|
360
1037
|
var ChatEventTypeSkillsChanged = "skills_changed";
|
|
1038
|
+
var ChatEventTypeAttachmentProcessingStarted = "attachment_processing_started";
|
|
1039
|
+
var ChatEventTypeAttachmentProcessingProgress = "attachment_processing_progress";
|
|
1040
|
+
var ChatEventTypeAttachmentProcessingComplete = "attachment_processing_complete";
|
|
1041
|
+
var ChatEventTypeAttachmentProcessingError = "attachment_processing_error";
|
|
1042
|
+
var ChatEventTypeRetryAttachment = "retry_attachment";
|
|
361
1043
|
var MessageStatusSending = "sending";
|
|
362
1044
|
var MessageStatusSent = "sent";
|
|
363
1045
|
var MessageStatusDelivered = "delivered";
|
|
@@ -379,6 +1061,8 @@ var AttachmentTypeSticker = "sticker";
|
|
|
379
1061
|
var AttachmentTypeData = "data";
|
|
380
1062
|
var AttachmentTypeKGNodes = "kgNodes";
|
|
381
1063
|
var AttachmentTypeDocumentSources = "document_sources";
|
|
1064
|
+
var AttachmentTypeSpreadsheet = "spreadsheet";
|
|
1065
|
+
var AttachmentTypeDataFile = "data_file";
|
|
382
1066
|
var ChatSessionStatusActive = "active";
|
|
383
1067
|
var ChatSessionStatusIdle = "idle";
|
|
384
1068
|
var ChatSessionStatusExpired = "expired";
|
|
@@ -423,1992 +1107,8 @@ var UpdateUserStatusSubject = "chat.user.status.update";
|
|
|
423
1107
|
var GetOnlineUsersSubject = "chat.users.online.get";
|
|
424
1108
|
var TriggerAnalyticsScanSubject = "chat.analytics.trigger-scan";
|
|
425
1109
|
var SetupOrgSubject = "chat.org.setup";
|
|
426
|
-
|
|
427
|
-
// hooks/use-websocket-chat-base.ts
|
|
428
|
-
var import_react = require("react");
|
|
429
|
-
var createDefaultLogger = (debug) => ({
|
|
430
|
-
debug: debug ? console.log : () => {
|
|
431
|
-
},
|
|
432
|
-
info: console.info,
|
|
433
|
-
warn: console.warn,
|
|
434
|
-
error: console.error
|
|
435
|
-
});
|
|
436
|
-
var DEFAULT_RETRY_CONFIG = {
|
|
437
|
-
maxRetries: 10,
|
|
438
|
-
intervals: [1e3, 2e3, 5e3],
|
|
439
|
-
backoffMultiplier: 1.5,
|
|
440
|
-
maxBackoffTime: 3e4
|
|
441
|
-
};
|
|
442
|
-
var DEFAULT_QUEUE_CONFIG = {
|
|
443
|
-
maxSize: 100,
|
|
444
|
-
dropStrategy: "oldest"
|
|
445
|
-
};
|
|
446
|
-
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
447
|
-
var DEFAULT_HEARTBEAT_TIMEOUT = 5e3;
|
|
448
|
-
var DEFAULT_TRANSPORT = "websocket";
|
|
449
|
-
function isChatEvent(data) {
|
|
450
|
-
return data && typeof data === "object" && (typeof data.type === "string" || data.message);
|
|
451
|
-
}
|
|
452
|
-
var useWebSocketChatBase = ({
|
|
453
|
-
serverBaseUrl,
|
|
454
|
-
orgId,
|
|
455
|
-
clientType,
|
|
456
|
-
product,
|
|
457
|
-
onMessage,
|
|
458
|
-
retryConfig = DEFAULT_RETRY_CONFIG,
|
|
459
|
-
queueConfig = DEFAULT_QUEUE_CONFIG,
|
|
460
|
-
debug = false,
|
|
461
|
-
logger = createDefaultLogger(debug),
|
|
462
|
-
heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL,
|
|
463
|
-
heartbeatTimeout = DEFAULT_HEARTBEAT_TIMEOUT,
|
|
464
|
-
transport = DEFAULT_TRANSPORT
|
|
465
|
-
}) => {
|
|
466
|
-
const [connectionState, setConnectionState] = (0, import_react.useState)("disconnected");
|
|
467
|
-
const [error, setError] = (0, import_react.useState)(void 0);
|
|
468
|
-
const [startTime, setStartTime] = (0, import_react.useState)(void 0);
|
|
469
|
-
const [metrics, setMetrics] = (0, import_react.useState)({
|
|
470
|
-
latency: 0,
|
|
471
|
-
messagesSent: 0,
|
|
472
|
-
messagesReceived: 0,
|
|
473
|
-
messagesQueued: 0,
|
|
474
|
-
reconnectCount: 0,
|
|
475
|
-
transportType: transport
|
|
476
|
-
});
|
|
477
|
-
const wsRef = (0, import_react.useRef)(void 0);
|
|
478
|
-
const sseRef = (0, import_react.useRef)(void 0);
|
|
479
|
-
const transportRef = (0, import_react.useRef)(transport);
|
|
480
|
-
(0, import_react.useEffect)(() => {
|
|
481
|
-
transportRef.current = transport;
|
|
482
|
-
}, [transport]);
|
|
483
|
-
const reconnectTimeoutRef = (0, import_react.useRef)(void 0);
|
|
484
|
-
const retryCountRef = (0, import_react.useRef)(0);
|
|
485
|
-
const messageQueueRef = (0, import_react.useRef)([]);
|
|
486
|
-
const mountedRef = (0, import_react.useRef)(false);
|
|
487
|
-
const currentChatKeyRef = (0, import_react.useRef)(void 0);
|
|
488
|
-
const currentUserIdRef = (0, import_react.useRef)(void 0);
|
|
489
|
-
const intentionalDisconnectRef = (0, import_react.useRef)(false);
|
|
490
|
-
const eventHandlersRef = (0, import_react.useRef)(
|
|
491
|
-
/* @__PURE__ */ new Map()
|
|
492
|
-
);
|
|
493
|
-
const onMessageRef = (0, import_react.useRef)(onMessage);
|
|
494
|
-
const pendingMessagesRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
495
|
-
const heartbeatIntervalRef = (0, import_react.useRef)(void 0);
|
|
496
|
-
const heartbeatTimeoutRef = (0, import_react.useRef)(void 0);
|
|
497
|
-
const lastPongRef = (0, import_react.useRef)(Date.now());
|
|
498
|
-
const chatCreationPromiseRef = (0, import_react.useRef)(null);
|
|
499
|
-
const loadChatRetryMapRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
500
|
-
(0, import_react.useEffect)(() => {
|
|
501
|
-
onMessageRef.current = onMessage;
|
|
502
|
-
}, [onMessage]);
|
|
503
|
-
const isConnected = connectionState === "connected";
|
|
504
|
-
const on = (0, import_react.useCallback)((eventType, handler) => {
|
|
505
|
-
if (!eventHandlersRef.current.has(eventType)) {
|
|
506
|
-
eventHandlersRef.current.set(eventType, /* @__PURE__ */ new Set());
|
|
507
|
-
}
|
|
508
|
-
eventHandlersRef.current.get(eventType).add(handler);
|
|
509
|
-
return () => off(eventType, handler);
|
|
510
|
-
}, []);
|
|
511
|
-
const off = (0, import_react.useCallback)((eventType, handler) => {
|
|
512
|
-
const handlers = eventHandlersRef.current.get(eventType);
|
|
513
|
-
if (handlers) {
|
|
514
|
-
handlers.delete(handler);
|
|
515
|
-
if (handlers.size === 0) {
|
|
516
|
-
eventHandlersRef.current.delete(eventType);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}, []);
|
|
520
|
-
const emit = (0, import_react.useCallback)(
|
|
521
|
-
(eventType, data) => {
|
|
522
|
-
const handlers = eventHandlersRef.current.get(eventType);
|
|
523
|
-
if (handlers) {
|
|
524
|
-
handlers.forEach((handler) => {
|
|
525
|
-
try {
|
|
526
|
-
handler(data);
|
|
527
|
-
} catch (error2) {
|
|
528
|
-
logger.error(`Error in event handler for ${eventType}:`, error2);
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
},
|
|
533
|
-
[logger]
|
|
534
|
-
);
|
|
535
|
-
const updateMetrics = (0, import_react.useCallback)((updates) => {
|
|
536
|
-
setMetrics((prev) => ({ ...prev, ...updates }));
|
|
537
|
-
}, []);
|
|
538
|
-
const addToQueue = (0, import_react.useCallback)(
|
|
539
|
-
(event) => {
|
|
540
|
-
const currentQueueSize = messageQueueRef.current.length;
|
|
541
|
-
const maxSize = queueConfig.maxSize || DEFAULT_QUEUE_CONFIG.maxSize;
|
|
542
|
-
if (currentQueueSize >= maxSize) {
|
|
543
|
-
if (queueConfig.dropStrategy === "newest") {
|
|
544
|
-
logger.warn("Message queue full, dropping new message");
|
|
545
|
-
return false;
|
|
546
|
-
} else {
|
|
547
|
-
const dropped = messageQueueRef.current.shift();
|
|
548
|
-
logger.warn("Message queue full, dropped oldest message", dropped);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
messageQueueRef.current.push(event);
|
|
552
|
-
updateMetrics({ messagesQueued: messageQueueRef.current.length });
|
|
553
|
-
return true;
|
|
554
|
-
},
|
|
555
|
-
[queueConfig, logger, updateMetrics]
|
|
556
|
-
);
|
|
557
|
-
const calculateRetryInterval = (0, import_react.useCallback)(
|
|
558
|
-
(retryCount) => {
|
|
559
|
-
const config = { ...DEFAULT_RETRY_CONFIG, ...retryConfig };
|
|
560
|
-
const {
|
|
561
|
-
intervals = [],
|
|
562
|
-
backoffMultiplier = 1.5,
|
|
563
|
-
maxBackoffTime = 3e4
|
|
564
|
-
} = config;
|
|
565
|
-
if (retryCount < intervals.length) {
|
|
566
|
-
return intervals[retryCount];
|
|
567
|
-
}
|
|
568
|
-
const baseInterval = intervals[intervals.length - 1] || 5e3;
|
|
569
|
-
const backoffTime = baseInterval * Math.pow(backoffMultiplier, retryCount - intervals.length + 1);
|
|
570
|
-
return Math.min(backoffTime, maxBackoffTime);
|
|
571
|
-
},
|
|
572
|
-
[retryConfig]
|
|
573
|
-
);
|
|
574
|
-
const startHeartbeat = (0, import_react.useCallback)(() => {
|
|
575
|
-
if (!heartbeatInterval || heartbeatInterval <= 0) return;
|
|
576
|
-
const sendPing = () => {
|
|
577
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
578
|
-
const pingTime = Date.now();
|
|
579
|
-
wsRef.current.send(
|
|
580
|
-
JSON.stringify({ type: "ping", timestamp: pingTime })
|
|
581
|
-
);
|
|
582
|
-
heartbeatTimeoutRef.current = setTimeout(() => {
|
|
583
|
-
logger.warn("Heartbeat timeout - no pong received");
|
|
584
|
-
if (wsRef.current) {
|
|
585
|
-
wsRef.current.close(4e3, "Heartbeat timeout");
|
|
586
|
-
}
|
|
587
|
-
}, heartbeatTimeout);
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
if (heartbeatIntervalRef.current) {
|
|
591
|
-
clearInterval(heartbeatIntervalRef.current);
|
|
592
|
-
}
|
|
593
|
-
heartbeatIntervalRef.current = setInterval(sendPing, heartbeatInterval);
|
|
594
|
-
logger.debug("Heartbeat started");
|
|
595
|
-
}, [heartbeatInterval, heartbeatTimeout, logger]);
|
|
596
|
-
const stopHeartbeat = (0, import_react.useCallback)(() => {
|
|
597
|
-
if (heartbeatIntervalRef.current) {
|
|
598
|
-
clearInterval(heartbeatIntervalRef.current);
|
|
599
|
-
heartbeatIntervalRef.current = void 0;
|
|
600
|
-
}
|
|
601
|
-
if (heartbeatTimeoutRef.current) {
|
|
602
|
-
clearTimeout(heartbeatTimeoutRef.current);
|
|
603
|
-
heartbeatTimeoutRef.current = void 0;
|
|
604
|
-
}
|
|
605
|
-
logger.debug("Heartbeat stopped");
|
|
606
|
-
}, [logger]);
|
|
607
|
-
const cleanup = (0, import_react.useCallback)(() => {
|
|
608
|
-
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
|
609
|
-
wsRef.current.close(1e3, "Cleanup");
|
|
610
|
-
}
|
|
611
|
-
wsRef.current = void 0;
|
|
612
|
-
if (sseRef.current) {
|
|
613
|
-
sseRef.current.close();
|
|
614
|
-
sseRef.current = void 0;
|
|
615
|
-
}
|
|
616
|
-
if (reconnectTimeoutRef.current) {
|
|
617
|
-
clearTimeout(reconnectTimeoutRef.current);
|
|
618
|
-
reconnectTimeoutRef.current = void 0;
|
|
619
|
-
}
|
|
620
|
-
stopHeartbeat();
|
|
621
|
-
pendingMessagesRef.current.forEach(({ reject, timeout }) => {
|
|
622
|
-
clearTimeout(timeout);
|
|
623
|
-
reject(new Error("Connection closed"));
|
|
624
|
-
});
|
|
625
|
-
pendingMessagesRef.current.clear();
|
|
626
|
-
loadChatRetryMapRef.current.forEach((retryState) => {
|
|
627
|
-
if (retryState.timeoutId) {
|
|
628
|
-
clearTimeout(retryState.timeoutId);
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
loadChatRetryMapRef.current.clear();
|
|
632
|
-
}, [stopHeartbeat]);
|
|
633
|
-
const getRestApiUrl = (0, import_react.useCallback)((endpoint) => {
|
|
634
|
-
return `${serverBaseUrl}/${endpoint}`;
|
|
635
|
-
}, [serverBaseUrl]);
|
|
636
|
-
const sendRestMessage = (0, import_react.useCallback)(
|
|
637
|
-
async (endpoint, body) => {
|
|
638
|
-
const url = getRestApiUrl(endpoint);
|
|
639
|
-
logger.debug(`SSE REST API call: POST ${endpoint}`, body);
|
|
640
|
-
try {
|
|
641
|
-
const response = await fetch(url, {
|
|
642
|
-
method: "POST",
|
|
643
|
-
headers: {
|
|
644
|
-
"Content-Type": "application/json"
|
|
645
|
-
},
|
|
646
|
-
body: JSON.stringify(body)
|
|
647
|
-
});
|
|
648
|
-
if (!response.ok) {
|
|
649
|
-
const errorText = await response.text();
|
|
650
|
-
throw new Error(`REST API error: ${response.status} - ${errorText}`);
|
|
651
|
-
}
|
|
652
|
-
const data = await response.json();
|
|
653
|
-
return data;
|
|
654
|
-
} catch (error2) {
|
|
655
|
-
logger.error(`SSE REST API error for ${endpoint}:`, error2);
|
|
656
|
-
throw error2;
|
|
657
|
-
}
|
|
658
|
-
},
|
|
659
|
-
[getRestApiUrl, logger]
|
|
660
|
-
);
|
|
661
|
-
const connect = (0, import_react.useCallback)(
|
|
662
|
-
async (userId) => {
|
|
663
|
-
if (!mountedRef.current) {
|
|
664
|
-
mountedRef.current = true;
|
|
665
|
-
}
|
|
666
|
-
if (!orgId) {
|
|
667
|
-
const error2 = {
|
|
668
|
-
code: "CONNECTION_FAILED",
|
|
669
|
-
message: "Cannot connect: orgId is undefined",
|
|
670
|
-
retryable: false,
|
|
671
|
-
timestamp: Date.now()
|
|
672
|
-
};
|
|
673
|
-
logger.error("Cannot connect: orgId is undefined");
|
|
674
|
-
setError(error2);
|
|
675
|
-
return Promise.reject(error2);
|
|
676
|
-
}
|
|
677
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
678
|
-
logger.debug("Already connected (WebSocket)");
|
|
679
|
-
return Promise.resolve();
|
|
680
|
-
}
|
|
681
|
-
if (sseRef.current?.readyState === EventSource.OPEN) {
|
|
682
|
-
logger.debug("Already connected (SSE)");
|
|
683
|
-
return Promise.resolve();
|
|
684
|
-
}
|
|
685
|
-
if (connectionState === "connecting" || connectionState === "reconnecting") {
|
|
686
|
-
logger.debug("Connection already in progress");
|
|
687
|
-
return Promise.resolve();
|
|
688
|
-
}
|
|
689
|
-
const maxRetries = retryConfig.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries;
|
|
690
|
-
if (retryCountRef.current >= maxRetries && !intentionalDisconnectRef.current) {
|
|
691
|
-
const error2 = {
|
|
692
|
-
code: "CONNECTION_FAILED",
|
|
693
|
-
message: `Max retries (${maxRetries}) exceeded`,
|
|
694
|
-
retryable: false,
|
|
695
|
-
timestamp: Date.now()
|
|
696
|
-
};
|
|
697
|
-
setError(error2);
|
|
698
|
-
updateMetrics({ lastError: error2 });
|
|
699
|
-
return Promise.reject(error2);
|
|
700
|
-
}
|
|
701
|
-
cleanup();
|
|
702
|
-
setConnectionState(
|
|
703
|
-
retryCountRef.current > 0 ? "reconnecting" : "connecting"
|
|
704
|
-
);
|
|
705
|
-
intentionalDisconnectRef.current = false;
|
|
706
|
-
return new Promise((resolve, reject) => {
|
|
707
|
-
try {
|
|
708
|
-
const connectionStartTime = Date.now();
|
|
709
|
-
logger.info(`\u{1F504} Connecting with transport: ${transportRef.current}`);
|
|
710
|
-
if (transportRef.current === "sse") {
|
|
711
|
-
const sseUrl = getRestApiUrl(`stream?orgId=${orgId}&userId=${userId}&clientType=${clientType}&chatId=${currentChatKeyRef.current || ""}`);
|
|
712
|
-
logger.debug("Connecting to SSE:", sseUrl);
|
|
713
|
-
console.log(`\u23F3 Initiating SSE connection to ${sseUrl}...`);
|
|
714
|
-
const eventSource = new EventSource(sseUrl);
|
|
715
|
-
eventSource.onopen = () => {
|
|
716
|
-
if (!mountedRef.current) {
|
|
717
|
-
eventSource.close();
|
|
718
|
-
reject(new Error("Component unmounted"));
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
const connectionTimeMs = Date.now() - connectionStartTime;
|
|
722
|
-
const connectionTimeSec = (connectionTimeMs / 1e3).toFixed(2);
|
|
723
|
-
logger.info("\u2705 SSE connected", {
|
|
724
|
-
userId,
|
|
725
|
-
retryCount: retryCountRef.current,
|
|
726
|
-
connectionTime: `${connectionTimeSec}s (${connectionTimeMs}ms)`
|
|
727
|
-
});
|
|
728
|
-
console.log(`\u{1F50C} SSE connection established in ${connectionTimeSec} seconds`);
|
|
729
|
-
setConnectionState("connected");
|
|
730
|
-
setError(void 0);
|
|
731
|
-
const wasReconnecting = retryCountRef.current > 0;
|
|
732
|
-
retryCountRef.current = 0;
|
|
733
|
-
updateMetrics({
|
|
734
|
-
connectedAt: Date.now(),
|
|
735
|
-
latency: connectionTimeMs,
|
|
736
|
-
transportType: "sse",
|
|
737
|
-
reconnectCount: wasReconnecting ? metrics.reconnectCount + 1 : metrics.reconnectCount
|
|
738
|
-
});
|
|
739
|
-
currentUserIdRef.current = userId;
|
|
740
|
-
if (currentChatKeyRef.current) {
|
|
741
|
-
logger.info("Loading chat after SSE reconnection:", currentChatKeyRef.current);
|
|
742
|
-
sendRestMessage("load", {
|
|
743
|
-
orgId,
|
|
744
|
-
chatKey: currentChatKeyRef.current,
|
|
745
|
-
userId
|
|
746
|
-
}).then((response) => {
|
|
747
|
-
if (response.success && response.data?.chat) {
|
|
748
|
-
const chatEvent = {
|
|
749
|
-
type: "load_chat_response",
|
|
750
|
-
orgId,
|
|
751
|
-
chatKey: currentChatKeyRef.current,
|
|
752
|
-
userId,
|
|
753
|
-
timestamp: Date.now(),
|
|
754
|
-
data: response.data
|
|
755
|
-
};
|
|
756
|
-
emit("load_chat_response", chatEvent);
|
|
757
|
-
if (onMessageRef.current) {
|
|
758
|
-
onMessageRef.current(chatEvent);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
}).catch((err) => {
|
|
762
|
-
logger.error("Failed to load chat after SSE reconnection:", err);
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
emit("connected", { userId, wasReconnecting, transport: "sse" });
|
|
766
|
-
resolve();
|
|
767
|
-
};
|
|
768
|
-
const handleSSEMessage = (event) => {
|
|
769
|
-
if (!mountedRef.current) return;
|
|
770
|
-
if (!event.data || event.data === "") {
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
try {
|
|
774
|
-
const data = JSON.parse(event.data);
|
|
775
|
-
if (!isChatEvent(data)) {
|
|
776
|
-
logger.warn("Received invalid SSE message format:", data);
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
const chatEvent = data;
|
|
780
|
-
logger.debug("SSE message received:", chatEvent.type);
|
|
781
|
-
updateMetrics({
|
|
782
|
-
messagesReceived: metrics.messagesReceived + 1,
|
|
783
|
-
lastMessageAt: Date.now()
|
|
784
|
-
});
|
|
785
|
-
switch (chatEvent.type) {
|
|
786
|
-
case "new_chat_created":
|
|
787
|
-
const newChatKey = chatEvent.data?.chatKey;
|
|
788
|
-
if (newChatKey) {
|
|
789
|
-
logger.info("New chat created with key:", newChatKey);
|
|
790
|
-
currentChatKeyRef.current = newChatKey;
|
|
791
|
-
if (chatCreationPromiseRef.current) {
|
|
792
|
-
chatCreationPromiseRef.current.resolve(newChatKey);
|
|
793
|
-
chatCreationPromiseRef.current = null;
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
break;
|
|
797
|
-
case "load_chat_response":
|
|
798
|
-
const chat = chatEvent.data?.chat;
|
|
799
|
-
if (chat && chat.key) {
|
|
800
|
-
logger.info("Chat loaded with key:", chat.key);
|
|
801
|
-
currentChatKeyRef.current = chat.key;
|
|
802
|
-
}
|
|
803
|
-
break;
|
|
804
|
-
case "chat_ended":
|
|
805
|
-
logger.info("Chat ended, clearing key");
|
|
806
|
-
currentChatKeyRef.current = void 0;
|
|
807
|
-
break;
|
|
808
|
-
}
|
|
809
|
-
emit(chatEvent.type || "message", chatEvent);
|
|
810
|
-
if (onMessageRef.current) {
|
|
811
|
-
onMessageRef.current(chatEvent);
|
|
812
|
-
}
|
|
813
|
-
} catch (error2) {
|
|
814
|
-
logger.error("Failed to parse SSE message:", error2);
|
|
815
|
-
}
|
|
816
|
-
};
|
|
817
|
-
eventSource.addEventListener("message", handleSSEMessage);
|
|
818
|
-
eventSource.addEventListener("reconnected", handleSSEMessage);
|
|
819
|
-
eventSource.addEventListener("typing", handleSSEMessage);
|
|
820
|
-
eventSource.addEventListener("stopped_typing", handleSSEMessage);
|
|
821
|
-
eventSource.addEventListener("waiting", handleSSEMessage);
|
|
822
|
-
eventSource.addEventListener("waiting_for_agent", handleSSEMessage);
|
|
823
|
-
eventSource.addEventListener("human_agent_joined", handleSSEMessage);
|
|
824
|
-
eventSource.addEventListener("human_agent_left", handleSSEMessage);
|
|
825
|
-
eventSource.addEventListener("chat_ended", handleSSEMessage);
|
|
826
|
-
eventSource.addEventListener("chat_updated", handleSSEMessage);
|
|
827
|
-
eventSource.addEventListener("load_chat_response", handleSSEMessage);
|
|
828
|
-
eventSource.addEventListener("new_chat_created", handleSSEMessage);
|
|
829
|
-
eventSource.addEventListener("show_csat_survey", handleSSEMessage);
|
|
830
|
-
eventSource.addEventListener("csat_response", handleSSEMessage);
|
|
831
|
-
eventSource.addEventListener("user_suggested_actions", handleSSEMessage);
|
|
832
|
-
eventSource.addEventListener("agent_execution_started", handleSSEMessage);
|
|
833
|
-
eventSource.addEventListener("agent_execution_ended", handleSSEMessage);
|
|
834
|
-
eventSource.addEventListener("agent_context_update", handleSSEMessage);
|
|
835
|
-
eventSource.addEventListener("plan_pending_approval", handleSSEMessage);
|
|
836
|
-
eventSource.addEventListener("step_started", handleSSEMessage);
|
|
837
|
-
eventSource.addEventListener("step_completed", handleSSEMessage);
|
|
838
|
-
eventSource.addEventListener("step_failed", handleSSEMessage);
|
|
839
|
-
eventSource.addEventListener("plan_completed", handleSSEMessage);
|
|
840
|
-
eventSource.addEventListener("skills_changed", handleSSEMessage);
|
|
841
|
-
eventSource.addEventListener("summary_update", handleSSEMessage);
|
|
842
|
-
eventSource.onerror = (error2) => {
|
|
843
|
-
logger.error("SSE error:", error2);
|
|
844
|
-
if (!mountedRef.current) return;
|
|
845
|
-
if (eventSource.readyState === EventSource.CLOSED) {
|
|
846
|
-
const sseError = {
|
|
847
|
-
code: "CONNECTION_FAILED",
|
|
848
|
-
message: "SSE connection failed",
|
|
849
|
-
retryable: true,
|
|
850
|
-
timestamp: Date.now()
|
|
851
|
-
};
|
|
852
|
-
setError(sseError);
|
|
853
|
-
updateMetrics({ lastError: sseError });
|
|
854
|
-
setConnectionState("disconnected");
|
|
855
|
-
emit("disconnected", { reason: "SSE error" });
|
|
856
|
-
if (!intentionalDisconnectRef.current && mountedRef.current) {
|
|
857
|
-
const retryInterval = calculateRetryInterval(retryCountRef.current);
|
|
858
|
-
retryCountRef.current++;
|
|
859
|
-
logger.info(`SSE reconnecting in ${retryInterval}ms (attempt ${retryCountRef.current})`);
|
|
860
|
-
if (reconnectTimeoutRef.current) {
|
|
861
|
-
clearTimeout(reconnectTimeoutRef.current);
|
|
862
|
-
}
|
|
863
|
-
reconnectTimeoutRef.current = setTimeout(() => {
|
|
864
|
-
connect(userId);
|
|
865
|
-
}, retryInterval);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
};
|
|
869
|
-
sseRef.current = eventSource;
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
const wsUrl = `${serverBaseUrl}?orgId=${orgId}&userId=${userId}&clientType=${clientType}&product=${product}`;
|
|
873
|
-
logger.debug("Connecting to WebSocket:", wsUrl);
|
|
874
|
-
console.log(`\u23F3 Initiating WebSocket connection to ${serverBaseUrl}...`);
|
|
875
|
-
const ws = new WebSocket(wsUrl);
|
|
876
|
-
ws.onopen = () => {
|
|
877
|
-
if (!mountedRef.current) {
|
|
878
|
-
ws.close(1e3, "Component unmounted");
|
|
879
|
-
reject(new Error("Component unmounted"));
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
const connectionTimeMs = Date.now() - connectionStartTime;
|
|
883
|
-
const connectionTimeSec = (connectionTimeMs / 1e3).toFixed(2);
|
|
884
|
-
logger.info("\u2705 WebSocket connected", {
|
|
885
|
-
userId,
|
|
886
|
-
retryCount: retryCountRef.current,
|
|
887
|
-
connectionTime: `${connectionTimeSec}s (${connectionTimeMs}ms)`
|
|
888
|
-
});
|
|
889
|
-
console.log(`\u{1F50C} WebSocket connection established in ${connectionTimeSec} seconds`);
|
|
890
|
-
setConnectionState("connected");
|
|
891
|
-
setError(void 0);
|
|
892
|
-
const wasReconnecting = retryCountRef.current > 0;
|
|
893
|
-
retryCountRef.current = 0;
|
|
894
|
-
updateMetrics({
|
|
895
|
-
connectedAt: Date.now(),
|
|
896
|
-
latency: connectionTimeMs,
|
|
897
|
-
reconnectCount: wasReconnecting ? metrics.reconnectCount + 1 : metrics.reconnectCount
|
|
898
|
-
});
|
|
899
|
-
while (messageQueueRef.current.length > 0 && ws.readyState === WebSocket.OPEN) {
|
|
900
|
-
const event = messageQueueRef.current.shift();
|
|
901
|
-
if (event) {
|
|
902
|
-
ws.send(JSON.stringify({ ...event, timestamp: Date.now() }));
|
|
903
|
-
updateMetrics({
|
|
904
|
-
messagesSent: metrics.messagesSent + 1,
|
|
905
|
-
messagesQueued: messageQueueRef.current.length
|
|
906
|
-
});
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
currentUserIdRef.current = userId;
|
|
910
|
-
if (currentChatKeyRef.current) {
|
|
911
|
-
if (!orgId) {
|
|
912
|
-
logger.error("Cannot resubscribe to chat: orgId is undefined");
|
|
913
|
-
} else {
|
|
914
|
-
logger.info(
|
|
915
|
-
"Resubscribing to chat after reconnection:",
|
|
916
|
-
currentChatKeyRef.current
|
|
917
|
-
);
|
|
918
|
-
const resubscribeEvent = {
|
|
919
|
-
type: "load_chat",
|
|
920
|
-
orgId,
|
|
921
|
-
chatKey: currentChatKeyRef.current,
|
|
922
|
-
userId,
|
|
923
|
-
timestamp: Date.now(),
|
|
924
|
-
data: {}
|
|
925
|
-
};
|
|
926
|
-
ws.send(JSON.stringify(resubscribeEvent));
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
startHeartbeat();
|
|
930
|
-
emit("connected", { userId, wasReconnecting });
|
|
931
|
-
resolve();
|
|
932
|
-
};
|
|
933
|
-
ws.onmessage = (event) => {
|
|
934
|
-
if (!mountedRef.current) return;
|
|
935
|
-
try {
|
|
936
|
-
const data = JSON.parse(event.data);
|
|
937
|
-
if (!isChatEvent(data)) {
|
|
938
|
-
logger.warn("Received invalid message format:", data);
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
941
|
-
const chatEvent = data;
|
|
942
|
-
logger.debug("Message received:", chatEvent.type);
|
|
943
|
-
updateMetrics({
|
|
944
|
-
messagesReceived: metrics.messagesReceived + 1,
|
|
945
|
-
lastMessageAt: Date.now()
|
|
946
|
-
});
|
|
947
|
-
if (chatEvent.type === "pong") {
|
|
948
|
-
if (heartbeatTimeoutRef.current) {
|
|
949
|
-
clearTimeout(heartbeatTimeoutRef.current);
|
|
950
|
-
heartbeatTimeoutRef.current = void 0;
|
|
951
|
-
}
|
|
952
|
-
const latency = Date.now() - (chatEvent.timestamp || Date.now());
|
|
953
|
-
lastPongRef.current = Date.now();
|
|
954
|
-
updateMetrics({ latency });
|
|
955
|
-
return;
|
|
956
|
-
}
|
|
957
|
-
switch (chatEvent.type) {
|
|
958
|
-
case "new_chat_created":
|
|
959
|
-
const newChatKey = chatEvent.data?.chatKey;
|
|
960
|
-
if (newChatKey) {
|
|
961
|
-
logger.info("New chat created with key:", newChatKey);
|
|
962
|
-
currentChatKeyRef.current = newChatKey;
|
|
963
|
-
if (chatCreationPromiseRef.current) {
|
|
964
|
-
chatCreationPromiseRef.current.resolve(newChatKey);
|
|
965
|
-
chatCreationPromiseRef.current = null;
|
|
966
|
-
}
|
|
967
|
-
if (!orgId) {
|
|
968
|
-
logger.error("Cannot send load_chat: orgId is undefined");
|
|
969
|
-
return;
|
|
970
|
-
}
|
|
971
|
-
const loadChatEvent = {
|
|
972
|
-
type: "load_chat",
|
|
973
|
-
orgId,
|
|
974
|
-
chatKey: newChatKey,
|
|
975
|
-
userId: currentUserIdRef.current || userId,
|
|
976
|
-
timestamp: Date.now(),
|
|
977
|
-
data: {}
|
|
978
|
-
};
|
|
979
|
-
ws.send(JSON.stringify(loadChatEvent));
|
|
980
|
-
}
|
|
981
|
-
break;
|
|
982
|
-
case "load_chat_response":
|
|
983
|
-
const chat = chatEvent.data?.chat;
|
|
984
|
-
if (chat && chat.key) {
|
|
985
|
-
logger.info("Chat loaded with key:", chat.key);
|
|
986
|
-
currentChatKeyRef.current = chat.key;
|
|
987
|
-
const retryState = loadChatRetryMapRef.current.get(chat.key);
|
|
988
|
-
if (retryState) {
|
|
989
|
-
if (retryState.timeoutId) {
|
|
990
|
-
clearTimeout(retryState.timeoutId);
|
|
991
|
-
}
|
|
992
|
-
loadChatRetryMapRef.current.delete(chat.key);
|
|
993
|
-
}
|
|
994
|
-
} else if (!chat) {
|
|
995
|
-
logger.warn("Chat load failed, clearing key");
|
|
996
|
-
currentChatKeyRef.current = void 0;
|
|
997
|
-
}
|
|
998
|
-
break;
|
|
999
|
-
case "chat_ended":
|
|
1000
|
-
logger.info("Chat ended, clearing key");
|
|
1001
|
-
currentChatKeyRef.current = void 0;
|
|
1002
|
-
break;
|
|
1003
|
-
case "error":
|
|
1004
|
-
const errorMessage = chatEvent.data?.message || "Unknown error";
|
|
1005
|
-
logger.error("Received error from server:", errorMessage);
|
|
1006
|
-
if (errorMessage.includes("nats: key not found") || errorMessage.includes("Failed to load chat")) {
|
|
1007
|
-
const chatKeyFromError = currentChatKeyRef.current;
|
|
1008
|
-
if (chatKeyFromError) {
|
|
1009
|
-
const maxRetries2 = 5;
|
|
1010
|
-
let retryState = loadChatRetryMapRef.current.get(chatKeyFromError);
|
|
1011
|
-
if (!retryState) {
|
|
1012
|
-
retryState = { retryCount: 0, timeoutId: null };
|
|
1013
|
-
loadChatRetryMapRef.current.set(chatKeyFromError, retryState);
|
|
1014
|
-
}
|
|
1015
|
-
if (retryState.retryCount < maxRetries2) {
|
|
1016
|
-
const delay = 200 * Math.pow(2, retryState.retryCount);
|
|
1017
|
-
retryState.retryCount++;
|
|
1018
|
-
logger.info(
|
|
1019
|
-
`Chat load failed, retrying (${retryState.retryCount}/${maxRetries2}) in ${delay}ms...`,
|
|
1020
|
-
chatKeyFromError
|
|
1021
|
-
);
|
|
1022
|
-
retryState.timeoutId = setTimeout(() => {
|
|
1023
|
-
if (!wsRef.current || !mountedRef.current) return;
|
|
1024
|
-
if (!orgId) {
|
|
1025
|
-
logger.error("Cannot retry load_chat: orgId is undefined", chatKeyFromError);
|
|
1026
|
-
loadChatRetryMapRef.current.delete(chatKeyFromError);
|
|
1027
|
-
return;
|
|
1028
|
-
}
|
|
1029
|
-
logger.debug("Retrying load_chat:", chatKeyFromError);
|
|
1030
|
-
const retryLoadEvent = {
|
|
1031
|
-
type: "load_chat",
|
|
1032
|
-
orgId,
|
|
1033
|
-
chatKey: chatKeyFromError,
|
|
1034
|
-
userId: currentUserIdRef.current || "",
|
|
1035
|
-
timestamp: Date.now(),
|
|
1036
|
-
data: {}
|
|
1037
|
-
};
|
|
1038
|
-
ws.send(JSON.stringify(retryLoadEvent));
|
|
1039
|
-
}, delay);
|
|
1040
|
-
} else {
|
|
1041
|
-
logger.error("Max retries reached for loading chat:", chatKeyFromError);
|
|
1042
|
-
loadChatRetryMapRef.current.delete(chatKeyFromError);
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
const wsError = {
|
|
1047
|
-
code: "NETWORK_ERROR",
|
|
1048
|
-
message: errorMessage,
|
|
1049
|
-
retryable: true,
|
|
1050
|
-
timestamp: Date.now()
|
|
1051
|
-
};
|
|
1052
|
-
setError(wsError);
|
|
1053
|
-
updateMetrics({ lastError: wsError });
|
|
1054
|
-
break;
|
|
1055
|
-
}
|
|
1056
|
-
emit(chatEvent.type || "message", chatEvent);
|
|
1057
|
-
if (onMessageRef.current) {
|
|
1058
|
-
onMessageRef.current(chatEvent);
|
|
1059
|
-
}
|
|
1060
|
-
} catch (error2) {
|
|
1061
|
-
logger.error("Failed to parse WebSocket message:", error2);
|
|
1062
|
-
const parseError = {
|
|
1063
|
-
code: "PARSE_ERROR",
|
|
1064
|
-
message: "Failed to parse message",
|
|
1065
|
-
retryable: false,
|
|
1066
|
-
timestamp: Date.now()
|
|
1067
|
-
};
|
|
1068
|
-
setError(parseError);
|
|
1069
|
-
updateMetrics({ lastError: parseError });
|
|
1070
|
-
}
|
|
1071
|
-
};
|
|
1072
|
-
ws.onclose = (event) => {
|
|
1073
|
-
if (!mountedRef.current) return;
|
|
1074
|
-
logger.info("WebSocket closed", {
|
|
1075
|
-
code: event.code,
|
|
1076
|
-
reason: event.reason
|
|
1077
|
-
});
|
|
1078
|
-
setConnectionState("disconnected");
|
|
1079
|
-
wsRef.current = void 0;
|
|
1080
|
-
stopHeartbeat();
|
|
1081
|
-
emit("disconnected", { code: event.code, reason: event.reason });
|
|
1082
|
-
if (event.code !== 1e3 && !intentionalDisconnectRef.current && mountedRef.current) {
|
|
1083
|
-
const retryInterval = calculateRetryInterval(
|
|
1084
|
-
retryCountRef.current
|
|
1085
|
-
);
|
|
1086
|
-
retryCountRef.current++;
|
|
1087
|
-
logger.info(
|
|
1088
|
-
`Reconnecting in ${retryInterval}ms (attempt ${retryCountRef.current})`
|
|
1089
|
-
);
|
|
1090
|
-
if (reconnectTimeoutRef.current) {
|
|
1091
|
-
clearTimeout(reconnectTimeoutRef.current);
|
|
1092
|
-
}
|
|
1093
|
-
reconnectTimeoutRef.current = setTimeout(() => {
|
|
1094
|
-
connect(userId);
|
|
1095
|
-
}, retryInterval);
|
|
1096
|
-
}
|
|
1097
|
-
};
|
|
1098
|
-
ws.onerror = (error2) => {
|
|
1099
|
-
logger.error("WebSocket error:", error2);
|
|
1100
|
-
if (!mountedRef.current) return;
|
|
1101
|
-
const wsError = {
|
|
1102
|
-
code: "CONNECTION_FAILED",
|
|
1103
|
-
message: "WebSocket connection failed",
|
|
1104
|
-
retryable: true,
|
|
1105
|
-
timestamp: Date.now()
|
|
1106
|
-
};
|
|
1107
|
-
setError(wsError);
|
|
1108
|
-
updateMetrics({ lastError: wsError });
|
|
1109
|
-
reject(wsError);
|
|
1110
|
-
};
|
|
1111
|
-
wsRef.current = ws;
|
|
1112
|
-
} catch (error2) {
|
|
1113
|
-
logger.error("Failed to create WebSocket:", error2);
|
|
1114
|
-
const wsError = {
|
|
1115
|
-
code: "CONNECTION_FAILED",
|
|
1116
|
-
message: error2 instanceof Error ? error2.message : "Failed to create connection",
|
|
1117
|
-
retryable: true,
|
|
1118
|
-
timestamp: Date.now()
|
|
1119
|
-
};
|
|
1120
|
-
setError(wsError);
|
|
1121
|
-
updateMetrics({ lastError: wsError });
|
|
1122
|
-
reject(wsError);
|
|
1123
|
-
}
|
|
1124
|
-
});
|
|
1125
|
-
},
|
|
1126
|
-
[
|
|
1127
|
-
serverBaseUrl,
|
|
1128
|
-
orgId,
|
|
1129
|
-
clientType,
|
|
1130
|
-
product,
|
|
1131
|
-
connectionState,
|
|
1132
|
-
logger,
|
|
1133
|
-
retryConfig,
|
|
1134
|
-
metrics,
|
|
1135
|
-
updateMetrics,
|
|
1136
|
-
cleanup,
|
|
1137
|
-
calculateRetryInterval,
|
|
1138
|
-
startHeartbeat,
|
|
1139
|
-
emit
|
|
1140
|
-
]
|
|
1141
|
-
);
|
|
1142
|
-
const sendMessage = (0, import_react.useCallback)(
|
|
1143
|
-
(event, overrideUserId) => {
|
|
1144
|
-
return new Promise(async (resolve, reject) => {
|
|
1145
|
-
if (!mountedRef.current) {
|
|
1146
|
-
reject(new Error("Component not mounted"));
|
|
1147
|
-
return;
|
|
1148
|
-
}
|
|
1149
|
-
const fullEvent = {
|
|
1150
|
-
...event,
|
|
1151
|
-
timestamp: Date.now()
|
|
1152
|
-
};
|
|
1153
|
-
logger.debug("Sending message:", fullEvent.type);
|
|
1154
|
-
if (transportRef.current === "sse") {
|
|
1155
|
-
if (!sseRef.current || sseRef.current.readyState !== EventSource.OPEN) {
|
|
1156
|
-
logger.debug("SSE not connected, attempting to connect");
|
|
1157
|
-
if (connectionState === "disconnected" && overrideUserId) {
|
|
1158
|
-
try {
|
|
1159
|
-
await connect(overrideUserId);
|
|
1160
|
-
} catch (error2) {
|
|
1161
|
-
reject(error2);
|
|
1162
|
-
return;
|
|
1163
|
-
}
|
|
1164
|
-
} else {
|
|
1165
|
-
reject(new Error("SSE not connected"));
|
|
1166
|
-
return;
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
try {
|
|
1170
|
-
switch (fullEvent.type) {
|
|
1171
|
-
case "message":
|
|
1172
|
-
await sendRestMessage("send", {
|
|
1173
|
-
orgId: fullEvent.orgId || orgId,
|
|
1174
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1175
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1176
|
-
message: fullEvent.message
|
|
1177
|
-
});
|
|
1178
|
-
break;
|
|
1179
|
-
case "typing":
|
|
1180
|
-
await sendRestMessage("typing", {
|
|
1181
|
-
orgId: fullEvent.orgId || orgId,
|
|
1182
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1183
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1184
|
-
typing: true
|
|
1185
|
-
});
|
|
1186
|
-
break;
|
|
1187
|
-
case "stopped_typing":
|
|
1188
|
-
await sendRestMessage("typing", {
|
|
1189
|
-
orgId: fullEvent.orgId || orgId,
|
|
1190
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1191
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1192
|
-
typing: false
|
|
1193
|
-
});
|
|
1194
|
-
break;
|
|
1195
|
-
case "load_chat":
|
|
1196
|
-
const loadResponse = await sendRestMessage("load", {
|
|
1197
|
-
orgId: fullEvent.orgId || orgId,
|
|
1198
|
-
chatKey: fullEvent.chatKey,
|
|
1199
|
-
userId: fullEvent.userId || currentUserIdRef.current
|
|
1200
|
-
});
|
|
1201
|
-
if (loadResponse.success && loadResponse.data?.chat) {
|
|
1202
|
-
currentChatKeyRef.current = loadResponse.data.chat.key;
|
|
1203
|
-
const chatEvent = {
|
|
1204
|
-
type: "load_chat_response",
|
|
1205
|
-
orgId: fullEvent.orgId,
|
|
1206
|
-
chatKey: loadResponse.data.chat.key,
|
|
1207
|
-
userId: fullEvent.userId,
|
|
1208
|
-
timestamp: Date.now(),
|
|
1209
|
-
data: loadResponse.data
|
|
1210
|
-
};
|
|
1211
|
-
emit("load_chat_response", chatEvent);
|
|
1212
|
-
if (onMessageRef.current) {
|
|
1213
|
-
onMessageRef.current(chatEvent);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
break;
|
|
1217
|
-
case "new_chat":
|
|
1218
|
-
const createResponse = await sendRestMessage("create", {
|
|
1219
|
-
orgId: fullEvent.orgId || orgId,
|
|
1220
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1221
|
-
metadata: fullEvent.data
|
|
1222
|
-
});
|
|
1223
|
-
if (createResponse.success && createResponse.data?.chatKey) {
|
|
1224
|
-
currentChatKeyRef.current = createResponse.data.chatKey;
|
|
1225
|
-
const newChatEvent = {
|
|
1226
|
-
type: "new_chat_created",
|
|
1227
|
-
orgId: fullEvent.orgId,
|
|
1228
|
-
chatKey: createResponse.data.chatKey,
|
|
1229
|
-
userId: fullEvent.userId,
|
|
1230
|
-
timestamp: Date.now(),
|
|
1231
|
-
data: { chatKey: createResponse.data.chatKey }
|
|
1232
|
-
};
|
|
1233
|
-
emit("new_chat_created", newChatEvent);
|
|
1234
|
-
if (onMessageRef.current) {
|
|
1235
|
-
onMessageRef.current(newChatEvent);
|
|
1236
|
-
}
|
|
1237
|
-
if (chatCreationPromiseRef.current) {
|
|
1238
|
-
chatCreationPromiseRef.current.resolve(createResponse.data.chatKey);
|
|
1239
|
-
chatCreationPromiseRef.current = null;
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
break;
|
|
1243
|
-
case "end_chat":
|
|
1244
|
-
await sendRestMessage("end", {
|
|
1245
|
-
orgId: fullEvent.orgId || orgId,
|
|
1246
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1247
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1248
|
-
data: fullEvent.data
|
|
1249
|
-
});
|
|
1250
|
-
break;
|
|
1251
|
-
case "human_agent_joined":
|
|
1252
|
-
await sendRestMessage("agent-join", {
|
|
1253
|
-
orgId: fullEvent.orgId || orgId,
|
|
1254
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1255
|
-
user: fullEvent.data?.user
|
|
1256
|
-
});
|
|
1257
|
-
break;
|
|
1258
|
-
case "human_agent_left":
|
|
1259
|
-
await sendRestMessage("agent-leave", {
|
|
1260
|
-
orgId: fullEvent.orgId || orgId,
|
|
1261
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1262
|
-
user: fullEvent.data?.user
|
|
1263
|
-
});
|
|
1264
|
-
break;
|
|
1265
|
-
// Event types that use the generic /event endpoint
|
|
1266
|
-
case "load_agent_context":
|
|
1267
|
-
case "skill_activate":
|
|
1268
|
-
case "skill_deactivate":
|
|
1269
|
-
case "sync_metadata":
|
|
1270
|
-
case "sync_user_session":
|
|
1271
|
-
case "csat_response":
|
|
1272
|
-
case "plan_approved":
|
|
1273
|
-
case "plan_rejected":
|
|
1274
|
-
await sendRestMessage("event", {
|
|
1275
|
-
type: fullEvent.type,
|
|
1276
|
-
orgId: fullEvent.orgId || orgId,
|
|
1277
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1278
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1279
|
-
data: fullEvent.data
|
|
1280
|
-
});
|
|
1281
|
-
break;
|
|
1282
|
-
default:
|
|
1283
|
-
logger.warn("Sending unrecognized event type via generic endpoint:", fullEvent.type);
|
|
1284
|
-
await sendRestMessage("event", {
|
|
1285
|
-
type: fullEvent.type,
|
|
1286
|
-
orgId: fullEvent.orgId || orgId,
|
|
1287
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1288
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1289
|
-
data: fullEvent.data
|
|
1290
|
-
});
|
|
1291
|
-
break;
|
|
1292
|
-
}
|
|
1293
|
-
updateMetrics({ messagesSent: metrics.messagesSent + 1 });
|
|
1294
|
-
logger.debug("SSE REST message sent successfully");
|
|
1295
|
-
resolve();
|
|
1296
|
-
} catch (error2) {
|
|
1297
|
-
logger.error("Failed to send SSE REST message:", error2);
|
|
1298
|
-
const sendError = {
|
|
1299
|
-
code: "SEND_FAILED",
|
|
1300
|
-
message: error2 instanceof Error ? error2.message : "Failed to send message",
|
|
1301
|
-
retryable: true,
|
|
1302
|
-
timestamp: Date.now()
|
|
1303
|
-
};
|
|
1304
|
-
setError(sendError);
|
|
1305
|
-
updateMetrics({ lastError: sendError });
|
|
1306
|
-
reject(sendError);
|
|
1307
|
-
}
|
|
1308
|
-
return;
|
|
1309
|
-
}
|
|
1310
|
-
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
|
|
1311
|
-
if (addToQueue(fullEvent)) {
|
|
1312
|
-
logger.debug("Message queued, attempting to connect");
|
|
1313
|
-
if (connectionState === "disconnected" && overrideUserId) {
|
|
1314
|
-
connect(overrideUserId).then(() => resolve()).catch(reject);
|
|
1315
|
-
} else {
|
|
1316
|
-
resolve();
|
|
1317
|
-
}
|
|
1318
|
-
} else {
|
|
1319
|
-
reject(new Error("Message queue full"));
|
|
1320
|
-
}
|
|
1321
|
-
return;
|
|
1322
|
-
}
|
|
1323
|
-
try {
|
|
1324
|
-
wsRef.current.send(JSON.stringify(fullEvent));
|
|
1325
|
-
updateMetrics({ messagesSent: metrics.messagesSent + 1 });
|
|
1326
|
-
logger.debug("Message sent successfully");
|
|
1327
|
-
resolve();
|
|
1328
|
-
} catch (error2) {
|
|
1329
|
-
logger.error("Failed to send message:", error2);
|
|
1330
|
-
if (addToQueue(fullEvent)) {
|
|
1331
|
-
resolve();
|
|
1332
|
-
} else {
|
|
1333
|
-
const sendError = {
|
|
1334
|
-
code: "SEND_FAILED",
|
|
1335
|
-
message: error2 instanceof Error ? error2.message : "Failed to send message",
|
|
1336
|
-
retryable: true,
|
|
1337
|
-
timestamp: Date.now()
|
|
1338
|
-
};
|
|
1339
|
-
setError(sendError);
|
|
1340
|
-
updateMetrics({ lastError: sendError });
|
|
1341
|
-
reject(sendError);
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
});
|
|
1345
|
-
},
|
|
1346
|
-
[connectionState, connect, addToQueue, logger, metrics, updateMetrics, sendRestMessage, emit, orgId]
|
|
1347
|
-
);
|
|
1348
|
-
const startNewChat = (0, import_react.useCallback)(
|
|
1349
|
-
(userId, data) => {
|
|
1350
|
-
return new Promise((resolve, reject) => {
|
|
1351
|
-
if (!userId) {
|
|
1352
|
-
reject(new Error("User ID is required"));
|
|
1353
|
-
return;
|
|
1354
|
-
}
|
|
1355
|
-
logger.info("Requesting new chat from server with userId:", userId);
|
|
1356
|
-
setStartTime(/* @__PURE__ */ new Date());
|
|
1357
|
-
chatCreationPromiseRef.current = { resolve, reject };
|
|
1358
|
-
sendMessage(
|
|
1359
|
-
{
|
|
1360
|
-
type: "new_chat",
|
|
1361
|
-
orgId,
|
|
1362
|
-
chatKey: "",
|
|
1363
|
-
// Server will generate
|
|
1364
|
-
userId,
|
|
1365
|
-
data: data ?? {}
|
|
1366
|
-
},
|
|
1367
|
-
userId
|
|
1368
|
-
).catch((error2) => {
|
|
1369
|
-
chatCreationPromiseRef.current = null;
|
|
1370
|
-
reject(error2);
|
|
1371
|
-
});
|
|
1372
|
-
setTimeout(() => {
|
|
1373
|
-
if (chatCreationPromiseRef.current) {
|
|
1374
|
-
chatCreationPromiseRef.current = null;
|
|
1375
|
-
reject(new Error("Chat creation timed out"));
|
|
1376
|
-
}
|
|
1377
|
-
}, 3e4);
|
|
1378
|
-
});
|
|
1379
|
-
},
|
|
1380
|
-
[sendMessage, orgId, logger]
|
|
1381
|
-
);
|
|
1382
|
-
const disconnect = (0, import_react.useCallback)(
|
|
1383
|
-
(intentional = true) => {
|
|
1384
|
-
logger.info("Disconnecting", { intentional, transport: transportRef.current });
|
|
1385
|
-
intentionalDisconnectRef.current = intentional;
|
|
1386
|
-
cleanup();
|
|
1387
|
-
setConnectionState("disconnected");
|
|
1388
|
-
messageQueueRef.current = [];
|
|
1389
|
-
retryCountRef.current = 0;
|
|
1390
|
-
mountedRef.current = false;
|
|
1391
|
-
currentChatKeyRef.current = void 0;
|
|
1392
|
-
currentUserIdRef.current = void 0;
|
|
1393
|
-
},
|
|
1394
|
-
[cleanup, logger]
|
|
1395
|
-
);
|
|
1396
|
-
const clearError = (0, import_react.useCallback)(() => {
|
|
1397
|
-
setError(void 0);
|
|
1398
|
-
}, []);
|
|
1399
|
-
(0, import_react.useEffect)(() => {
|
|
1400
|
-
mountedRef.current = true;
|
|
1401
|
-
return () => {
|
|
1402
|
-
mountedRef.current = false;
|
|
1403
|
-
disconnect(true);
|
|
1404
|
-
};
|
|
1405
|
-
}, []);
|
|
1406
|
-
return {
|
|
1407
|
-
connectionState,
|
|
1408
|
-
isConnected,
|
|
1409
|
-
sendMessage,
|
|
1410
|
-
error,
|
|
1411
|
-
connect,
|
|
1412
|
-
startNewChat,
|
|
1413
|
-
startTime,
|
|
1414
|
-
disconnect,
|
|
1415
|
-
metrics,
|
|
1416
|
-
on,
|
|
1417
|
-
off,
|
|
1418
|
-
clearError
|
|
1419
|
-
};
|
|
1420
|
-
};
|
|
1421
|
-
|
|
1422
|
-
// hooks/use-websocket-chat-admin.ts
|
|
1423
|
-
var useWebSocketChatAdmin = ({
|
|
1424
|
-
serverBaseUrl,
|
|
1425
|
-
orgId,
|
|
1426
|
-
product
|
|
1427
|
-
}) => {
|
|
1428
|
-
const [chats, setChats] = (0, import_react2.useState)(/* @__PURE__ */ new Map());
|
|
1429
|
-
const [selectedChat, setSelectedChat] = (0, import_react2.useState)(void 0);
|
|
1430
|
-
const handleMessage = (0, import_react2.useCallback)(
|
|
1431
|
-
(chatEvent) => {
|
|
1432
|
-
switch (chatEvent.type) {
|
|
1433
|
-
case "message":
|
|
1434
|
-
if (!selectedChat || !chatEvent.message) return;
|
|
1435
|
-
setChats((prev) => {
|
|
1436
|
-
const newMap = new Map(prev);
|
|
1437
|
-
newMap.set(selectedChat.key, {
|
|
1438
|
-
...selectedChat,
|
|
1439
|
-
messages: [...selectedChat.messages, chatEvent.message]
|
|
1440
|
-
});
|
|
1441
|
-
return newMap;
|
|
1442
|
-
});
|
|
1443
|
-
setSelectedChat((prev) => {
|
|
1444
|
-
if (!prev) return prev;
|
|
1445
|
-
return {
|
|
1446
|
-
...prev,
|
|
1447
|
-
messages: [...prev.messages, chatEvent.message]
|
|
1448
|
-
};
|
|
1449
|
-
});
|
|
1450
|
-
break;
|
|
1451
|
-
case "list_chats":
|
|
1452
|
-
const chatList = chatEvent.data?.chats;
|
|
1453
|
-
if (!chatList) return;
|
|
1454
|
-
setChats(new Map(chatList.map((chat) => [chat.key, chat])));
|
|
1455
|
-
break;
|
|
1456
|
-
case "chat_updated":
|
|
1457
|
-
const updatedChat = chatEvent.data?.chat?.value;
|
|
1458
|
-
if (updatedChat) {
|
|
1459
|
-
setChats((prev) => new Map(prev).set(updatedChat.key, updatedChat));
|
|
1460
|
-
}
|
|
1461
|
-
break;
|
|
1462
|
-
case "chat_removed":
|
|
1463
|
-
const chatKey = chatEvent.data?.chatKey?.value;
|
|
1464
|
-
if (chatKey) {
|
|
1465
|
-
setChats((prev) => {
|
|
1466
|
-
const newMap = new Map(prev);
|
|
1467
|
-
newMap.delete(chatKey);
|
|
1468
|
-
return newMap;
|
|
1469
|
-
});
|
|
1470
|
-
}
|
|
1471
|
-
break;
|
|
1472
|
-
case "load_chat":
|
|
1473
|
-
const history = chatEvent.data?.chat?.value;
|
|
1474
|
-
if (history) {
|
|
1475
|
-
setChats((prev) => {
|
|
1476
|
-
const existingChat = prev.get(history.key);
|
|
1477
|
-
if (!existingChat) return prev;
|
|
1478
|
-
return new Map(prev).set(history.key, {
|
|
1479
|
-
...existingChat,
|
|
1480
|
-
messages: history.messages
|
|
1481
|
-
});
|
|
1482
|
-
});
|
|
1483
|
-
}
|
|
1484
|
-
break;
|
|
1485
|
-
}
|
|
1486
|
-
},
|
|
1487
|
-
[selectedChat]
|
|
1488
|
-
);
|
|
1489
|
-
const base = useWebSocketChatBase({
|
|
1490
|
-
serverBaseUrl,
|
|
1491
|
-
orgId,
|
|
1492
|
-
clientType: "humanAgent",
|
|
1493
|
-
onMessage: handleMessage,
|
|
1494
|
-
product
|
|
1495
|
-
});
|
|
1496
|
-
const selectChat = (0, import_react2.useCallback)(
|
|
1497
|
-
(chatKey) => {
|
|
1498
|
-
const chat = chats.get(chatKey);
|
|
1499
|
-
if (!chat) {
|
|
1500
|
-
console.error("Unable to select chat", chatKey);
|
|
1501
|
-
return;
|
|
1502
|
-
}
|
|
1503
|
-
setSelectedChat(chat);
|
|
1504
|
-
},
|
|
1505
|
-
[chats, orgId, base.sendMessage]
|
|
1506
|
-
);
|
|
1507
|
-
const addUserToChat = (0, import_react2.useCallback)(
|
|
1508
|
-
(orgId2, chatKey, user) => {
|
|
1509
|
-
base.sendMessage({
|
|
1510
|
-
type: "human_agent_joined",
|
|
1511
|
-
orgId: orgId2,
|
|
1512
|
-
chatKey,
|
|
1513
|
-
userId: user.email,
|
|
1514
|
-
data: { user }
|
|
1515
|
-
});
|
|
1516
|
-
setChats((prev) => {
|
|
1517
|
-
const newMap = new Map(prev);
|
|
1518
|
-
const chat = newMap.get(chatKey);
|
|
1519
|
-
if (!chat) return prev;
|
|
1520
|
-
newMap.set(chatKey, {
|
|
1521
|
-
...chat,
|
|
1522
|
-
humanAgentEngaged: user.role === ChatRoleHumanAgent,
|
|
1523
|
-
users: [...chat?.users ?? [], user]
|
|
1524
|
-
});
|
|
1525
|
-
return newMap;
|
|
1526
|
-
});
|
|
1527
|
-
setSelectedChat((prev) => {
|
|
1528
|
-
if (!prev) return prev;
|
|
1529
|
-
return {
|
|
1530
|
-
...prev,
|
|
1531
|
-
humanAgentEngaged: user.role === ChatRoleHumanAgent,
|
|
1532
|
-
users: [...prev.users ?? [], user]
|
|
1533
|
-
};
|
|
1534
|
-
});
|
|
1535
|
-
},
|
|
1536
|
-
[base.sendMessage]
|
|
1537
|
-
);
|
|
1538
|
-
const removeUserFromChat = (0, import_react2.useCallback)(
|
|
1539
|
-
(orgId2, chatKey, userId) => {
|
|
1540
|
-
const chat = chats.get(chatKey);
|
|
1541
|
-
if (!chat) return;
|
|
1542
|
-
const user = chat.users?.find((u) => u.id === userId);
|
|
1543
|
-
if (!user) return;
|
|
1544
|
-
base.sendMessage({
|
|
1545
|
-
type: "human_agent_left",
|
|
1546
|
-
orgId: orgId2,
|
|
1547
|
-
chatKey,
|
|
1548
|
-
userId,
|
|
1549
|
-
data: { user }
|
|
1550
|
-
});
|
|
1551
|
-
setChats((prev) => {
|
|
1552
|
-
const newMap = new Map(prev);
|
|
1553
|
-
const chat2 = newMap.get(chatKey);
|
|
1554
|
-
if (!chat2) return prev;
|
|
1555
|
-
newMap.set(chatKey, {
|
|
1556
|
-
...chat2,
|
|
1557
|
-
humanAgentEngaged: false,
|
|
1558
|
-
users: chat2.users?.filter((u) => u.id !== userId)
|
|
1559
|
-
});
|
|
1560
|
-
return newMap;
|
|
1561
|
-
});
|
|
1562
|
-
},
|
|
1563
|
-
[chats, base.sendMessage]
|
|
1564
|
-
);
|
|
1565
|
-
const blockUser = (0, import_react2.useCallback)(
|
|
1566
|
-
(orgId2, chatKey, userId) => {
|
|
1567
|
-
base.sendMessage({
|
|
1568
|
-
type: "block_user",
|
|
1569
|
-
orgId: orgId2,
|
|
1570
|
-
chatKey,
|
|
1571
|
-
userId
|
|
1572
|
-
});
|
|
1573
|
-
},
|
|
1574
|
-
[base.sendMessage]
|
|
1575
|
-
);
|
|
1576
|
-
return {
|
|
1577
|
-
...base,
|
|
1578
|
-
chats,
|
|
1579
|
-
selectedChat,
|
|
1580
|
-
selectChat,
|
|
1581
|
-
addUserToChat,
|
|
1582
|
-
removeUserFromChat,
|
|
1583
|
-
blockUser
|
|
1584
|
-
};
|
|
1585
|
-
};
|
|
1586
|
-
|
|
1587
|
-
// hooks/use-websocket-chat-customer.ts
|
|
1588
|
-
var import_react3 = require("react");
|
|
1589
|
-
var useWebSocketChatCustomer = ({
|
|
1590
|
-
serverBaseUrl,
|
|
1591
|
-
orgId,
|
|
1592
|
-
chatKey,
|
|
1593
|
-
product
|
|
1594
|
-
}) => {
|
|
1595
|
-
const [currentChat, setCurrentChat] = (0, import_react3.useState)(void 0);
|
|
1596
|
-
const handleMessage = (0, import_react3.useCallback)((chatEvent) => {
|
|
1597
|
-
console.log("Received event:", chatEvent.type);
|
|
1598
|
-
switch (chatEvent.type) {
|
|
1599
|
-
case "message":
|
|
1600
|
-
if (!chatEvent.message) return;
|
|
1601
|
-
console.log(
|
|
1602
|
-
"got message:",
|
|
1603
|
-
chatEvent.message.role,
|
|
1604
|
-
":",
|
|
1605
|
-
chatEvent.message.content
|
|
1606
|
-
);
|
|
1607
|
-
setCurrentChat((prev) => {
|
|
1608
|
-
if (!prev) return prev;
|
|
1609
|
-
return {
|
|
1610
|
-
...prev,
|
|
1611
|
-
messages: [...prev.messages, chatEvent.message]
|
|
1612
|
-
};
|
|
1613
|
-
});
|
|
1614
|
-
break;
|
|
1615
|
-
case "chat_updated":
|
|
1616
|
-
const chat = chatEvent.data?.chat?.value;
|
|
1617
|
-
if (chat) {
|
|
1618
|
-
setCurrentChat(chat);
|
|
1619
|
-
}
|
|
1620
|
-
break;
|
|
1621
|
-
case "load_chat":
|
|
1622
|
-
const history = chatEvent.data?.chat;
|
|
1623
|
-
if (!history) return;
|
|
1624
|
-
setCurrentChat(history);
|
|
1625
|
-
break;
|
|
1626
|
-
default:
|
|
1627
|
-
break;
|
|
1628
|
-
}
|
|
1629
|
-
}, []);
|
|
1630
|
-
const base = useWebSocketChatBase({
|
|
1631
|
-
serverBaseUrl,
|
|
1632
|
-
orgId,
|
|
1633
|
-
clientType: "customer",
|
|
1634
|
-
onMessage: handleMessage,
|
|
1635
|
-
product
|
|
1636
|
-
});
|
|
1637
|
-
return {
|
|
1638
|
-
...base,
|
|
1639
|
-
chatKey,
|
|
1640
|
-
title: currentChat?.title,
|
|
1641
|
-
messages: currentChat?.messages ?? [],
|
|
1642
|
-
users: currentChat?.users ?? [],
|
|
1643
|
-
isWaiting: currentChat?.isWaiting ?? false,
|
|
1644
|
-
isWaitingForAgent: currentChat?.isWaitingForAgent ?? false,
|
|
1645
|
-
aiEngaged: currentChat?.aiEngaged ?? false,
|
|
1646
|
-
humanAgentEngaged: currentChat?.humanAgentEngaged ?? false,
|
|
1647
|
-
metadata: currentChat?.metadata ?? {},
|
|
1648
|
-
status: currentChat?.status
|
|
1649
|
-
};
|
|
1650
|
-
};
|
|
1651
|
-
|
|
1652
|
-
// context/websocket-chat-admin-context.tsx
|
|
1653
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1654
|
-
var WebSocketChatAdminContext = (0, import_react4.createContext)(void 0);
|
|
1655
|
-
function WebSocketChatAdminProvider(props) {
|
|
1656
|
-
const { children, serverBaseUrl, orgId, userId, product } = props;
|
|
1657
|
-
const webSocket = useWebSocketChatAdmin({
|
|
1658
|
-
serverBaseUrl,
|
|
1659
|
-
orgId,
|
|
1660
|
-
product
|
|
1661
|
-
});
|
|
1662
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WebSocketChatAdminContext.Provider, { value: webSocket, children });
|
|
1663
|
-
}
|
|
1664
|
-
var useWebSocketChatAdminContext = () => {
|
|
1665
|
-
const context = (0, import_react4.useContext)(WebSocketChatAdminContext);
|
|
1666
|
-
if (context === void 0) {
|
|
1667
|
-
throw new Error(
|
|
1668
|
-
"useWebSocketChatAdminContext must be used within a WebSocketChatAdminProvider"
|
|
1669
|
-
);
|
|
1670
|
-
}
|
|
1671
|
-
return context;
|
|
1672
|
-
};
|
|
1673
|
-
|
|
1674
|
-
// components/admin/admin-chat-input.tsx
|
|
1675
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1676
|
-
var AdminChatInput = ({
|
|
1677
|
-
userId,
|
|
1678
|
-
orgId,
|
|
1679
|
-
onFileUpload,
|
|
1680
|
-
chatKey
|
|
1681
|
-
}) => {
|
|
1682
|
-
const { sendMessage } = useWebSocketChatAdminContext();
|
|
1683
|
-
const [input, setInput] = (0, import_react5.useState)("");
|
|
1684
|
-
const [isUploading, setIsUploading] = (0, import_react5.useState)(false);
|
|
1685
|
-
const [isSending, setIsSending] = (0, import_react5.useState)(false);
|
|
1686
|
-
const fileInputRef = (0, import_react5.useRef)(null);
|
|
1687
|
-
const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
1688
|
-
const handleSend = async () => {
|
|
1689
|
-
if (!input.trim() && !isUploading || isSending) return;
|
|
1690
|
-
if (!chatKey || !orgId || !userId) {
|
|
1691
|
-
console.error("chatKey, orgId, or userId is not defined");
|
|
1692
|
-
return;
|
|
1693
|
-
}
|
|
1694
|
-
try {
|
|
1695
|
-
setIsSending(true);
|
|
1696
|
-
sendMessage({
|
|
1697
|
-
type: "message",
|
|
1698
|
-
chatKey,
|
|
1699
|
-
orgId,
|
|
1700
|
-
userId,
|
|
1701
|
-
message: {
|
|
1702
|
-
id: import_ksuid.default.randomSync().string,
|
|
1703
|
-
content: input,
|
|
1704
|
-
role: "user",
|
|
1705
|
-
senderId: userId,
|
|
1706
|
-
time: Date.now(),
|
|
1707
|
-
status: "sending",
|
|
1708
|
-
createdAt: Date.now()
|
|
1709
|
-
}
|
|
1710
|
-
});
|
|
1711
|
-
setInput("");
|
|
1712
|
-
} finally {
|
|
1713
|
-
setIsSending(false);
|
|
1714
|
-
}
|
|
1715
|
-
};
|
|
1716
|
-
const handleFileUpload = async (event) => {
|
|
1717
|
-
const files = event.target.files;
|
|
1718
|
-
if (!files || !onFileUpload) return;
|
|
1719
|
-
try {
|
|
1720
|
-
setIsUploading(true);
|
|
1721
|
-
const uploadedUrls = await Promise.all(
|
|
1722
|
-
Array.from(files).map(async (file) => {
|
|
1723
|
-
if (file.size > MAX_FILE_SIZE) {
|
|
1724
|
-
throw new Error(
|
|
1725
|
-
`File ${file.name} is too large. Maximum size is 5MB.`
|
|
1726
|
-
);
|
|
1727
|
-
}
|
|
1728
|
-
const url = await onFileUpload(file);
|
|
1729
|
-
return {
|
|
1730
|
-
type: file.type,
|
|
1731
|
-
url,
|
|
1732
|
-
name: file.name
|
|
1733
|
-
};
|
|
1734
|
-
})
|
|
1735
|
-
);
|
|
1736
|
-
const attachments = uploadedUrls.map((file) => ({
|
|
1737
|
-
type: "image",
|
|
1738
|
-
url: file.url,
|
|
1739
|
-
title: file.name
|
|
1740
|
-
}));
|
|
1741
|
-
if (!chatKey || !orgId || !userId) {
|
|
1742
|
-
console.error("chatKey, orgId, or userId is not defined");
|
|
1743
|
-
return;
|
|
1744
|
-
}
|
|
1745
|
-
} finally {
|
|
1746
|
-
setIsUploading(false);
|
|
1747
|
-
if (fileInputRef.current) {
|
|
1748
|
-
fileInputRef.current.value = "";
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
};
|
|
1752
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "p-4 border-t bg-white rounded-b-lg", children: [
|
|
1753
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center space-x-2", children: [
|
|
1754
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1755
|
-
"input",
|
|
1756
|
-
{
|
|
1757
|
-
type: "file",
|
|
1758
|
-
ref: fileInputRef,
|
|
1759
|
-
onChange: handleFileUpload,
|
|
1760
|
-
className: "hidden",
|
|
1761
|
-
multiple: true,
|
|
1762
|
-
accept: "image/*,.pdf,.doc,.docx,.txt"
|
|
1763
|
-
}
|
|
1764
|
-
),
|
|
1765
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1766
|
-
"button",
|
|
1767
|
-
{
|
|
1768
|
-
onClick: () => fileInputRef.current?.click(),
|
|
1769
|
-
className: "p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100 transition-colors",
|
|
1770
|
-
disabled: isUploading,
|
|
1771
|
-
"aria-label": "Attach file",
|
|
1772
|
-
children: isUploading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-5 h-5 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Paperclip, { className: "w-5 h-5" })
|
|
1773
|
-
}
|
|
1774
|
-
),
|
|
1775
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex-1 relative", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1776
|
-
"textarea",
|
|
1777
|
-
{
|
|
1778
|
-
value: input,
|
|
1779
|
-
onChange: (e) => setInput(e.target.value),
|
|
1780
|
-
onKeyDown: (e) => {
|
|
1781
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
1782
|
-
e.preventDefault();
|
|
1783
|
-
handleSend();
|
|
1784
|
-
}
|
|
1785
|
-
},
|
|
1786
|
-
className: "w-full p-3 border rounded-2xl focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none",
|
|
1787
|
-
placeholder: "Type a message...",
|
|
1788
|
-
rows: 1,
|
|
1789
|
-
style: {
|
|
1790
|
-
minHeight: "44px",
|
|
1791
|
-
maxHeight: "200px"
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
) }),
|
|
1795
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1796
|
-
"button",
|
|
1797
|
-
{
|
|
1798
|
-
onClick: handleSend,
|
|
1799
|
-
disabled: !input.trim() && !isUploading || isSending,
|
|
1800
|
-
className: `p-2 rounded-full transition-colors ${!input.trim() && !isUploading || isSending ? "text-gray-400 bg-gray-100" : "text-white bg-blue-500 hover:bg-blue-600"}`,
|
|
1801
|
-
"aria-label": "Send message",
|
|
1802
|
-
children: isSending ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-5 h-5 animate-spin rounded-full border-2 border-gray-300 border-t-white" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Send, { className: "w-5 h-5" })
|
|
1803
|
-
}
|
|
1804
|
-
)
|
|
1805
|
-
] }),
|
|
1806
|
-
isUploading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mt-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-full bg-gray-200 rounded-full h-1", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "bg-blue-500 h-1 rounded-full animate-pulse" }) }) })
|
|
1807
|
-
] });
|
|
1808
|
-
};
|
|
1809
|
-
|
|
1810
|
-
// components/admin/chat-human-agent-actions.tsx
|
|
1811
|
-
var import_lucide_react2 = require("lucide-react");
|
|
1812
|
-
|
|
1813
|
-
// context/websocket-chat-customer-context.tsx
|
|
1814
|
-
var import_react6 = require("react");
|
|
1815
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1816
|
-
var WebSocketChatCustomerContext = (0, import_react6.createContext)(void 0);
|
|
1817
|
-
function WebSocketChatCustomerProvider(props) {
|
|
1818
|
-
const { children, serverBaseUrl, orgId, userId, chatKey, product } = props;
|
|
1819
|
-
const webSocket = useWebSocketChatCustomer({
|
|
1820
|
-
serverBaseUrl,
|
|
1821
|
-
orgId,
|
|
1822
|
-
chatKey,
|
|
1823
|
-
product
|
|
1824
|
-
});
|
|
1825
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WebSocketChatCustomerContext.Provider, { value: webSocket, children });
|
|
1826
|
-
}
|
|
1827
|
-
var useWebSocketChatCustomerContext = () => {
|
|
1828
|
-
const context = (0, import_react6.useContext)(WebSocketChatCustomerContext);
|
|
1829
|
-
if (context === void 0) {
|
|
1830
|
-
throw new Error(
|
|
1831
|
-
"useWebSocketChatCustomerContext must be used within a WebSocketChatCustomerProvider"
|
|
1832
|
-
);
|
|
1833
|
-
}
|
|
1834
|
-
return context;
|
|
1835
|
-
};
|
|
1836
|
-
|
|
1837
|
-
// components/admin/chat-human-agent-actions.tsx
|
|
1838
|
-
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1839
|
-
var ChatHumanAgentActions = ({ user, orgId }) => {
|
|
1840
|
-
const { selectedChat, addUserToChat, removeUserFromChat, blockUser } = useWebSocketChatAdminContext();
|
|
1841
|
-
const handleJoinChat = () => {
|
|
1842
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1843
|
-
addUserToChat(orgId, selectedChat.key, {
|
|
1844
|
-
id: user.id,
|
|
1845
|
-
role: "humanAgent",
|
|
1846
|
-
name: [user.firstName, user.lastName].filter(Boolean).join(" ") || user.email,
|
|
1847
|
-
email: user.email,
|
|
1848
|
-
authProvider: "tbd",
|
|
1849
|
-
authToken: "tbd"
|
|
1850
|
-
});
|
|
1851
|
-
};
|
|
1852
|
-
const handleLeaveChat = () => {
|
|
1853
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1854
|
-
removeUserFromChat(orgId, selectedChat.key, user.id);
|
|
1855
|
-
};
|
|
1856
|
-
const handleEndChat = () => {
|
|
1857
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1858
|
-
};
|
|
1859
|
-
const handleStartCall = () => {
|
|
1860
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1861
|
-
};
|
|
1862
|
-
const handleCreateCase = () => {
|
|
1863
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1864
|
-
};
|
|
1865
|
-
const handleBlockUser = () => {
|
|
1866
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1867
|
-
blockUser(orgId, selectedChat.key, user.id);
|
|
1868
|
-
};
|
|
1869
|
-
if (!selectedChat) return null;
|
|
1870
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "p-4 border-b bg-white flex items-center justify-between", children: [
|
|
1871
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center space-x-4", children: [
|
|
1872
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "font-semibold text-lg", children: selectedChat.title || `Chat ${selectedChat.key}` }),
|
|
1873
|
-
selectedChat.humanAgentEngaged && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-sm font-medium text-green-800", children: [
|
|
1874
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.User, { className: "mr-1 h-4 w-4" }),
|
|
1875
|
-
"Agent Engaged"
|
|
1876
|
-
] })
|
|
1877
|
-
] }),
|
|
1878
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center space-x-2", children: [
|
|
1879
|
-
!selectedChat.humanAgentEngaged ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1880
|
-
"button",
|
|
1881
|
-
{
|
|
1882
|
-
onClick: handleJoinChat,
|
|
1883
|
-
className: "bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1884
|
-
children: [
|
|
1885
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.User, { className: "h-4 w-4" }),
|
|
1886
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Join Chat" })
|
|
1887
|
-
]
|
|
1888
|
-
}
|
|
1889
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1890
|
-
"button",
|
|
1891
|
-
{
|
|
1892
|
-
onClick: handleLeaveChat,
|
|
1893
|
-
className: "bg-orange-500 hover:bg-orange-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1894
|
-
children: [
|
|
1895
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.LogOut, { className: "h-4 w-4" }),
|
|
1896
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Leave Chat" })
|
|
1897
|
-
]
|
|
1898
|
-
}
|
|
1899
|
-
),
|
|
1900
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1901
|
-
"button",
|
|
1902
|
-
{
|
|
1903
|
-
onClick: handleEndChat,
|
|
1904
|
-
className: "bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1905
|
-
children: [
|
|
1906
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.XCircle, { className: "h-4 w-4" }),
|
|
1907
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "End Chat" })
|
|
1908
|
-
]
|
|
1909
|
-
}
|
|
1910
|
-
),
|
|
1911
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1912
|
-
"button",
|
|
1913
|
-
{
|
|
1914
|
-
onClick: handleBlockUser,
|
|
1915
|
-
className: "bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1916
|
-
children: [
|
|
1917
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.XCircle, { className: "h-4 w-4" }),
|
|
1918
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Block User" })
|
|
1919
|
-
]
|
|
1920
|
-
}
|
|
1921
|
-
),
|
|
1922
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1923
|
-
"button",
|
|
1924
|
-
{
|
|
1925
|
-
onClick: handleStartCall,
|
|
1926
|
-
disabled: !selectedChat.humanAgentEngaged,
|
|
1927
|
-
className: "bg-green-500 hover:bg-green-600 disabled:bg-gray-300 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1928
|
-
children: [
|
|
1929
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Phone, { className: "h-4 w-4" }),
|
|
1930
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Start Call" })
|
|
1931
|
-
]
|
|
1932
|
-
}
|
|
1933
|
-
),
|
|
1934
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1935
|
-
"button",
|
|
1936
|
-
{
|
|
1937
|
-
onClick: handleCreateCase,
|
|
1938
|
-
className: "bg-purple-500 hover:bg-purple-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1939
|
-
children: [
|
|
1940
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Plus, { className: "h-4 w-4" }),
|
|
1941
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Create Case" })
|
|
1942
|
-
]
|
|
1943
|
-
}
|
|
1944
|
-
)
|
|
1945
|
-
] })
|
|
1946
|
-
] });
|
|
1947
|
-
};
|
|
1948
|
-
|
|
1949
|
-
// components/admin/admin-chat-list.tsx
|
|
1950
|
-
var import_lucide_react3 = require("lucide-react");
|
|
1951
|
-
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1952
|
-
var ScrollArea = ({ className, children }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `overflow-auto ${className || ""}`, children });
|
|
1953
|
-
var AdminChatList = ({ className }) => {
|
|
1954
|
-
const { chats, selectedChat, selectChat } = useWebSocketChatAdminContext();
|
|
1955
|
-
const sortedChats = Array.from(chats.values()).sort(
|
|
1956
|
-
(a, b) => b.lastUpdated - a.lastUpdated
|
|
1957
|
-
);
|
|
1958
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col h-full", children: [
|
|
1959
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "p-4 border-b", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "font-semibold text-lg", children: "Active Chats" }) }),
|
|
1960
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex-1 overflow-y-auto", children: sortedChats.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "p-4 text-center text-gray-500", children: "No active chats" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "divide-y", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ScrollArea, { className: "h-[calc(100vh-15rem)]", children: sortedChats.map((chat, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1961
|
-
AdminChatListItem,
|
|
1962
|
-
{
|
|
1963
|
-
chat,
|
|
1964
|
-
isSelected: chat.key === selectedChat?.key,
|
|
1965
|
-
onSelect: () => selectChat(chat.key)
|
|
1966
|
-
},
|
|
1967
|
-
index
|
|
1968
|
-
)) }) }) })
|
|
1969
|
-
] });
|
|
1970
|
-
};
|
|
1971
|
-
var AdminChatListItem = ({
|
|
1972
|
-
chat,
|
|
1973
|
-
isSelected,
|
|
1974
|
-
onSelect
|
|
1975
|
-
}) => {
|
|
1976
|
-
const lastMessage = chat.messages[chat.messages.length - 1];
|
|
1977
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1978
|
-
"button",
|
|
1979
|
-
{
|
|
1980
|
-
onClick: onSelect,
|
|
1981
|
-
className: `w-full p-4 text-left hover:bg-gray-50 focus:outline-none focus:bg-gray-50 ${isSelected ? "bg-blue-50" : ""}`,
|
|
1982
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-start space-x-3", children: [
|
|
1983
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "shrink-0", children: [
|
|
1984
|
-
chat.aiEngaged && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.Bot, { className: "w-6 h-6 text-blue-500" }),
|
|
1985
|
-
chat.isWaitingForAgent && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.Loader2, { className: "w-6 h-6 text-blue-500 animate-spin text-orange-500" }),
|
|
1986
|
-
chat.humanAgentEngaged && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.UserCheck, { className: "w-6 h-6 text-blue-500 text-green-500" })
|
|
1987
|
-
] }),
|
|
1988
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
1989
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-medium text-gray-900 truncate", children: chat.title || `Chat ${chat.key}` }) }),
|
|
1990
|
-
lastMessage && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mt-1 text-sm text-gray-500 truncate", children: lastMessage.content }),
|
|
1991
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mt-1 flex items-center space-x-2", children: [
|
|
1992
|
-
chat.isWaiting && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800", children: [
|
|
1993
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.AlertCircle, { className: "mr-1 h-3 w-3" }),
|
|
1994
|
-
"Waiting"
|
|
1995
|
-
] }),
|
|
1996
|
-
chat.humanAgentEngaged && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800", children: [
|
|
1997
|
-
"Agent Engaged:",
|
|
1998
|
-
" ",
|
|
1999
|
-
chat.users?.find((u) => u.role === ChatRoleHumanAgent)?.name
|
|
2000
|
-
] }),
|
|
2001
|
-
chat.isWaitingForAgent && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "inline-flex items-center rounded-full bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-800", children: "Waiting For Agent" })
|
|
2002
|
-
] })
|
|
2003
|
-
] })
|
|
2004
|
-
] })
|
|
2005
|
-
}
|
|
2006
|
-
);
|
|
2007
|
-
};
|
|
2008
|
-
|
|
2009
|
-
// components/admin/admin-chat-header.tsx
|
|
2010
|
-
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2011
|
-
var AdminChatHeader = ({
|
|
2012
|
-
className
|
|
2013
|
-
}) => {
|
|
2014
|
-
const { isConnected, chats } = useWebSocketChatAdminContext();
|
|
2015
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "p-4 border-b", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center space-x-2", children: [
|
|
2016
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2017
|
-
"div",
|
|
2018
|
-
{
|
|
2019
|
-
className: `w-2 h-2 rounded-full ${isConnected ? "bg-green-500" : "bg-red-500"}`
|
|
2020
|
-
}
|
|
2021
|
-
),
|
|
2022
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "text-sm text-gray-600", children: [
|
|
2023
|
-
"Active Chats: ",
|
|
2024
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-bold", children: chats.size })
|
|
2025
|
-
] }),
|
|
2026
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-sm text-gray-500", children: "|" }),
|
|
2027
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "text-sm text-gray-600", children: [
|
|
2028
|
-
"Waiting For Agent:",
|
|
2029
|
-
" ",
|
|
2030
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-bold", children: Array.from(chats.values()).filter(
|
|
2031
|
-
(chat) => chat.isWaitingForAgent
|
|
2032
|
-
).length })
|
|
2033
|
-
] })
|
|
2034
|
-
] }) });
|
|
2035
|
-
};
|
|
2036
|
-
|
|
2037
|
-
// components/customer/chat-status-customer-ui.tsx
|
|
2038
|
-
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
2039
|
-
var ChatStatusCustomerUI = ({ isConnected }) => {
|
|
2040
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center space-x-2", children: [
|
|
2041
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2042
|
-
"span",
|
|
2043
|
-
{
|
|
2044
|
-
className: `w-3 h-3 rounded-full ${isConnected ? "bg-green-500" : "bg-red-500"}`,
|
|
2045
|
-
role: "status",
|
|
2046
|
-
"aria-label": isConnected ? "Connected" : "Disconnected"
|
|
2047
|
-
}
|
|
2048
|
-
),
|
|
2049
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-sm text-gray-500", children: isConnected ? "Connected" : "Reconnecting..." })
|
|
2050
|
-
] });
|
|
2051
|
-
};
|
|
2052
|
-
|
|
2053
|
-
// components/customer/chat-header.tsx
|
|
2054
|
-
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2055
|
-
var ChatHeader = () => {
|
|
2056
|
-
const { isConnected, title } = useWebSocketChatCustomerContext();
|
|
2057
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "p-4 border-b bg-white rounded-t-lg", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2058
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "font-bold text-lg", children: title || "Chat" }),
|
|
2059
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChatStatusCustomerUI, { isConnected })
|
|
2060
|
-
] }) });
|
|
2061
|
-
};
|
|
2062
|
-
|
|
2063
|
-
// components/customer/chat-input.tsx
|
|
2064
|
-
var import_ksuid2 = __toESM(require("ksuid"));
|
|
2065
|
-
var import_lucide_react4 = require("lucide-react");
|
|
2066
|
-
var import_react7 = require("react");
|
|
2067
|
-
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2068
|
-
var ChatInput = ({ userId, orgId, onFileUpload }) => {
|
|
2069
|
-
const { sendMessage, chatKey } = useWebSocketChatCustomerContext();
|
|
2070
|
-
const [input, setInput] = (0, import_react7.useState)("");
|
|
2071
|
-
const [isUploading, setIsUploading] = (0, import_react7.useState)(false);
|
|
2072
|
-
const [isSending, setIsSending] = (0, import_react7.useState)(false);
|
|
2073
|
-
const fileInputRef = (0, import_react7.useRef)(null);
|
|
2074
|
-
const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
2075
|
-
const handleSend = async () => {
|
|
2076
|
-
if (!input.trim() && !isUploading || isSending) return;
|
|
2077
|
-
if (!chatKey || !orgId || !userId) {
|
|
2078
|
-
console.error("chatKey, orgId, or userId is not defined");
|
|
2079
|
-
return;
|
|
2080
|
-
}
|
|
2081
|
-
try {
|
|
2082
|
-
setIsSending(true);
|
|
2083
|
-
sendMessage({
|
|
2084
|
-
type: "message",
|
|
2085
|
-
chatKey,
|
|
2086
|
-
orgId,
|
|
2087
|
-
userId,
|
|
2088
|
-
message: {
|
|
2089
|
-
id: import_ksuid2.default.randomSync().string,
|
|
2090
|
-
content: input,
|
|
2091
|
-
role: "user",
|
|
2092
|
-
senderId: userId,
|
|
2093
|
-
time: Date.now(),
|
|
2094
|
-
status: "sending",
|
|
2095
|
-
createdAt: Date.now()
|
|
2096
|
-
}
|
|
2097
|
-
});
|
|
2098
|
-
setInput("");
|
|
2099
|
-
} finally {
|
|
2100
|
-
setIsSending(false);
|
|
2101
|
-
}
|
|
2102
|
-
};
|
|
2103
|
-
const handleFileUpload = async (event) => {
|
|
2104
|
-
const files = event.target.files;
|
|
2105
|
-
if (!files || !onFileUpload) return;
|
|
2106
|
-
try {
|
|
2107
|
-
setIsUploading(true);
|
|
2108
|
-
const uploadedUrls = await Promise.all(
|
|
2109
|
-
Array.from(files).map(async (file) => {
|
|
2110
|
-
if (file.size > MAX_FILE_SIZE) {
|
|
2111
|
-
throw new Error(
|
|
2112
|
-
`File ${file.name} is too large. Maximum size is 5MB.`
|
|
2113
|
-
);
|
|
2114
|
-
}
|
|
2115
|
-
const url = await onFileUpload(file);
|
|
2116
|
-
return {
|
|
2117
|
-
type: file.type,
|
|
2118
|
-
url,
|
|
2119
|
-
name: file.name
|
|
2120
|
-
};
|
|
2121
|
-
})
|
|
2122
|
-
);
|
|
2123
|
-
const attachments = uploadedUrls.map((file) => ({
|
|
2124
|
-
type: "image",
|
|
2125
|
-
url: file.url,
|
|
2126
|
-
title: file.name
|
|
2127
|
-
}));
|
|
2128
|
-
if (!chatKey || !orgId || !userId) {
|
|
2129
|
-
console.error("chatKey, orgId, or userId is not defined");
|
|
2130
|
-
return;
|
|
2131
|
-
}
|
|
2132
|
-
} finally {
|
|
2133
|
-
setIsUploading(false);
|
|
2134
|
-
if (fileInputRef.current) {
|
|
2135
|
-
fileInputRef.current.value = "";
|
|
2136
|
-
}
|
|
2137
|
-
}
|
|
2138
|
-
};
|
|
2139
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "p-4 border-t bg-white rounded-b-lg", children: [
|
|
2140
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center space-x-2", children: [
|
|
2141
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2142
|
-
"input",
|
|
2143
|
-
{
|
|
2144
|
-
type: "file",
|
|
2145
|
-
ref: fileInputRef,
|
|
2146
|
-
onChange: handleFileUpload,
|
|
2147
|
-
className: "hidden",
|
|
2148
|
-
multiple: true,
|
|
2149
|
-
accept: "image/*,.pdf,.doc,.docx,.txt"
|
|
2150
|
-
}
|
|
2151
|
-
),
|
|
2152
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2153
|
-
"button",
|
|
2154
|
-
{
|
|
2155
|
-
onClick: () => fileInputRef.current?.click(),
|
|
2156
|
-
className: "p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100 transition-colors",
|
|
2157
|
-
disabled: isUploading,
|
|
2158
|
-
"aria-label": "Attach file",
|
|
2159
|
-
children: isUploading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "w-5 h-5 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Paperclip, { className: "w-5 h-5" })
|
|
2160
|
-
}
|
|
2161
|
-
),
|
|
2162
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex-1 relative", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2163
|
-
"textarea",
|
|
2164
|
-
{
|
|
2165
|
-
value: input,
|
|
2166
|
-
onChange: (e) => setInput(e.target.value),
|
|
2167
|
-
onKeyDown: (e) => {
|
|
2168
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
2169
|
-
e.preventDefault();
|
|
2170
|
-
handleSend();
|
|
2171
|
-
}
|
|
2172
|
-
},
|
|
2173
|
-
className: "w-full p-3 border rounded-2xl focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none",
|
|
2174
|
-
placeholder: "Type a message...",
|
|
2175
|
-
rows: 1,
|
|
2176
|
-
style: {
|
|
2177
|
-
minHeight: "44px",
|
|
2178
|
-
maxHeight: "200px"
|
|
2179
|
-
}
|
|
2180
|
-
}
|
|
2181
|
-
) }),
|
|
2182
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2183
|
-
"button",
|
|
2184
|
-
{
|
|
2185
|
-
onClick: handleSend,
|
|
2186
|
-
disabled: !input.trim() && !isUploading || isSending,
|
|
2187
|
-
className: `p-2 rounded-full transition-colors ${!input.trim() && !isUploading || isSending ? "text-gray-400 bg-gray-100" : "text-white bg-blue-500 hover:bg-blue-600"}`,
|
|
2188
|
-
"aria-label": "Send message",
|
|
2189
|
-
children: isSending ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "w-5 h-5 animate-spin rounded-full border-2 border-gray-300 border-t-white" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Send, { className: "w-5 h-5" })
|
|
2190
|
-
}
|
|
2191
|
-
)
|
|
2192
|
-
] }),
|
|
2193
|
-
isUploading && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mt-2", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-full bg-gray-200 rounded-full h-1", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bg-blue-500 h-1 rounded-full animate-pulse" }) }) })
|
|
2194
|
-
] });
|
|
2195
|
-
};
|
|
2196
|
-
|
|
2197
|
-
// components/customer/chat-messages.tsx
|
|
2198
|
-
var import_react8 = require("react");
|
|
2199
|
-
|
|
2200
|
-
// components/customer/chat-message.tsx
|
|
2201
|
-
var import_date_fns = require("date-fns");
|
|
2202
|
-
var import_lucide_react5 = require("lucide-react");
|
|
2203
|
-
|
|
2204
|
-
// components/markdown-renderer.tsx
|
|
2205
|
-
var import_react_markdown = __toESM(require("react-markdown"));
|
|
2206
|
-
var import_remark_gfm = __toESM(require("remark-gfm"));
|
|
2207
|
-
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2208
|
-
function MarkdownRenderer({ content }) {
|
|
2209
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2210
|
-
import_react_markdown.default,
|
|
2211
|
-
{
|
|
2212
|
-
remarkPlugins: [import_remark_gfm.default],
|
|
2213
|
-
components: {
|
|
2214
|
-
a: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2215
|
-
"a",
|
|
2216
|
-
{
|
|
2217
|
-
target: "_blank",
|
|
2218
|
-
rel: "noopener noreferrer",
|
|
2219
|
-
className: "text-blue-600 hover:underline",
|
|
2220
|
-
...props
|
|
2221
|
-
}
|
|
2222
|
-
),
|
|
2223
|
-
h1: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-2xl font-bold mt-6 mb-4", ...props }),
|
|
2224
|
-
h2: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "text-xl font-semibold mt-5 mb-3", ...props }),
|
|
2225
|
-
h3: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "text-lg font-semibold mt-4 mb-2", ...props }),
|
|
2226
|
-
code: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2227
|
-
"code",
|
|
2228
|
-
{
|
|
2229
|
-
className: "block bg-gray-100 p-3 rounded font-mono",
|
|
2230
|
-
...props
|
|
2231
|
-
}
|
|
2232
|
-
),
|
|
2233
|
-
ul: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ul", { className: "list-disc pl-6 my-4", ...props }),
|
|
2234
|
-
ol: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ol", { className: "list-decimal pl-6 my-4", ...props }),
|
|
2235
|
-
p: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "my-4", ...props }),
|
|
2236
|
-
blockquote: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2237
|
-
"blockquote",
|
|
2238
|
-
{
|
|
2239
|
-
className: "border-l-4 border-gray-200 pl-4 my-4 italic",
|
|
2240
|
-
...props
|
|
2241
|
-
}
|
|
2242
|
-
)
|
|
2243
|
-
},
|
|
2244
|
-
children: content
|
|
2245
|
-
}
|
|
2246
|
-
);
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
|
-
// components/customer/chat-message.tsx
|
|
2250
|
-
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2251
|
-
var Message = ({
|
|
2252
|
-
message,
|
|
2253
|
-
isConsecutive,
|
|
2254
|
-
onRetry
|
|
2255
|
-
}) => {
|
|
2256
|
-
const renderAttachment = (attachment, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mt-2", children: attachment.type === "image" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2257
|
-
"img",
|
|
2258
|
-
{
|
|
2259
|
-
src: attachment.url,
|
|
2260
|
-
alt: attachment.title,
|
|
2261
|
-
className: "max-w-full rounded",
|
|
2262
|
-
loading: "lazy"
|
|
2263
|
-
}
|
|
2264
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2265
|
-
"a",
|
|
2266
|
-
{
|
|
2267
|
-
href: attachment.url,
|
|
2268
|
-
className: "flex items-center space-x-2 text-sm hover:underline",
|
|
2269
|
-
target: "_blank",
|
|
2270
|
-
rel: "noopener noreferrer",
|
|
2271
|
-
children: [
|
|
2272
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.Paperclip, { className: "w-4 h-4" }),
|
|
2273
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: attachment.title })
|
|
2274
|
-
]
|
|
2275
|
-
}
|
|
2276
|
-
) }, index);
|
|
2277
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2278
|
-
"div",
|
|
2279
|
-
{
|
|
2280
|
-
className: "flex justify-start gap-2",
|
|
2281
|
-
"data-message-id": message.id,
|
|
2282
|
-
role: "article",
|
|
2283
|
-
"aria-label": `Message from ${message.senderId}`,
|
|
2284
|
-
children: [
|
|
2285
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex pt-10 w-10", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "w-8 h-8 rounded-full flex justify-center", children: [
|
|
2286
|
-
message.role === "user" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.User, {}),
|
|
2287
|
-
message.role === "ai" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.Bot, {}),
|
|
2288
|
-
message.role === "humanAgent" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.UserCog, {})
|
|
2289
|
-
] }) }),
|
|
2290
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2291
|
-
"div",
|
|
2292
|
-
{
|
|
2293
|
-
className: `p-3 rounded-lg max-w-[80%] bg-gray-100 text-primary-900 ${isConsecutive ? "mt-1" : "mt-4"}`,
|
|
2294
|
-
children: [
|
|
2295
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "break-words", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MarkdownRenderer, { content: message.content }) }),
|
|
2296
|
-
message.attachments?.map(
|
|
2297
|
-
(attachment, i) => renderAttachment(attachment, i)
|
|
2298
|
-
),
|
|
2299
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-end space-x-1 mt-1", children: [
|
|
2300
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-xs opacity-70", children: (0, import_date_fns.format)(message.time, "HH:mm") }),
|
|
2301
|
-
message.role === "user" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2302
|
-
message.status === "sending" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "w-4 h-4 animate-spin rounded-full border-2 border-gray-300 border-t-white" }),
|
|
2303
|
-
message.status === "sent" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.Check, { className: "w-4 h-4" }),
|
|
2304
|
-
message.status === "delivered" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.CheckCheck, { className: "w-4 h-4" }),
|
|
2305
|
-
message.status === "failed" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2306
|
-
"button",
|
|
2307
|
-
{
|
|
2308
|
-
onClick: () => onRetry(message.id),
|
|
2309
|
-
className: "text-red-300 hover:text-red-400",
|
|
2310
|
-
"aria-label": "Retry sending message",
|
|
2311
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.AlertCircle, { className: "w-4 h-4" })
|
|
2312
|
-
}
|
|
2313
|
-
)
|
|
2314
|
-
] })
|
|
2315
|
-
] })
|
|
2316
|
-
]
|
|
2317
|
-
}
|
|
2318
|
-
)
|
|
2319
|
-
]
|
|
2320
|
-
}
|
|
2321
|
-
);
|
|
2322
|
-
};
|
|
2323
|
-
|
|
2324
|
-
// components/customer/chat-messages.tsx
|
|
2325
|
-
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2326
|
-
var ChatMessages = ({
|
|
2327
|
-
onRetry,
|
|
2328
|
-
messages
|
|
2329
|
-
}) => {
|
|
2330
|
-
const messageEndRef = (0, import_react8.useRef)(null);
|
|
2331
|
-
const scrollToBottom = () => {
|
|
2332
|
-
messageEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
2333
|
-
};
|
|
2334
|
-
(0, import_react8.useEffect)(() => {
|
|
2335
|
-
scrollToBottom();
|
|
2336
|
-
}, [messages]);
|
|
2337
|
-
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2338
|
-
"div",
|
|
2339
|
-
{
|
|
2340
|
-
className: "flex-1 overflow-y-auto p-4 space-y-4 bg-white min-h-[calc(100vh-290px)]",
|
|
2341
|
-
role: "log",
|
|
2342
|
-
"aria-label": "Chat messages",
|
|
2343
|
-
children: [
|
|
2344
|
-
messages.filter(
|
|
2345
|
-
(msg) => (!msg.toolCalls?.length || msg.toolCalls.length === 0) && !msg.toolCallId
|
|
2346
|
-
).map((msg, index) => {
|
|
2347
|
-
const isConsecutive = index > 0 && messages[index - 1].senderId === msg.senderId && msg.time - messages[index - 1].time < 3e5;
|
|
2348
|
-
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2349
|
-
Message,
|
|
2350
|
-
{
|
|
2351
|
-
message: msg,
|
|
2352
|
-
isConsecutive,
|
|
2353
|
-
onRetry
|
|
2354
|
-
},
|
|
2355
|
-
msg.id
|
|
2356
|
-
);
|
|
2357
|
-
}),
|
|
2358
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { ref: messageEndRef })
|
|
2359
|
-
]
|
|
2360
|
-
}
|
|
2361
|
-
);
|
|
2362
|
-
};
|
|
2363
|
-
|
|
2364
|
-
// components/customer/chat-typing-indicator.tsx
|
|
2365
|
-
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2366
|
-
var ChatTypingIndicator = ({
|
|
2367
|
-
typingUsers
|
|
2368
|
-
}) => {
|
|
2369
|
-
if (typingUsers.size === 0) return null;
|
|
2370
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "px-4 py-2 text-sm text-gray-500 italic", role: "status", children: [
|
|
2371
|
-
Array.from(typingUsers).join(", "),
|
|
2372
|
-
" typing..."
|
|
2373
|
-
] });
|
|
2374
|
-
};
|
|
2375
|
-
|
|
2376
|
-
// components/customer/generic-chat-widget.tsx
|
|
2377
|
-
var import_react9 = require("react");
|
|
2378
|
-
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2379
|
-
var Alert = ({ title, description }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "p-4 bg-red-50 border border-red-200 rounded-lg", role: "alert", children: [
|
|
2380
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "font-medium text-red-800", children: title }),
|
|
2381
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-red-600", children: description })
|
|
2382
|
-
] });
|
|
2383
|
-
var GenericChatWidget = ({
|
|
2384
|
-
onFileUpload
|
|
2385
|
-
}) => {
|
|
2386
|
-
const [typingUsers] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
|
|
2387
|
-
const { error, messages } = useWebSocketChatCustomerContext();
|
|
2388
|
-
const handleRetry = (messageId) => {
|
|
2389
|
-
};
|
|
2390
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2391
|
-
"div",
|
|
2392
|
-
{
|
|
2393
|
-
className: "flex flex-col w-full max-w-lg border rounded-lg shadow-lg",
|
|
2394
|
-
role: "region",
|
|
2395
|
-
"aria-label": "Chat interface",
|
|
2396
|
-
children: [
|
|
2397
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChatHeader, {}),
|
|
2398
|
-
error && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Alert, { title: "Error", description: error.message }),
|
|
2399
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChatMessages, { onRetry: handleRetry, messages }),
|
|
2400
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChatTypingIndicator, { typingUsers }),
|
|
2401
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChatInput, { onFileUpload })
|
|
2402
|
-
]
|
|
2403
|
-
}
|
|
2404
|
-
);
|
|
2405
|
-
};
|
|
2406
1110
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2407
1111
|
0 && (module.exports = {
|
|
2408
|
-
AdminChatHeader,
|
|
2409
|
-
AdminChatInput,
|
|
2410
|
-
AdminChatList,
|
|
2411
|
-
AdminChatListItem,
|
|
2412
1112
|
AgentStatusAway,
|
|
2413
1113
|
AgentStatusBusy,
|
|
2414
1114
|
AgentStatusOffline,
|
|
@@ -2419,6 +1119,7 @@ var GenericChatWidget = ({
|
|
|
2419
1119
|
AttachmentTypeAudio,
|
|
2420
1120
|
AttachmentTypeBullets,
|
|
2421
1121
|
AttachmentTypeData,
|
|
1122
|
+
AttachmentTypeDataFile,
|
|
2422
1123
|
AttachmentTypeDocument,
|
|
2423
1124
|
AttachmentTypeDocumentAnalysis,
|
|
2424
1125
|
AttachmentTypeDocumentSources,
|
|
@@ -2427,6 +1128,7 @@ var GenericChatWidget = ({
|
|
|
2427
1128
|
AttachmentTypeLocation,
|
|
2428
1129
|
AttachmentTypeRecords,
|
|
2429
1130
|
AttachmentTypeReferences,
|
|
1131
|
+
AttachmentTypeSpreadsheet,
|
|
2430
1132
|
AttachmentTypeSticker,
|
|
2431
1133
|
AttachmentTypeSubsections,
|
|
2432
1134
|
AttachmentTypeVideo,
|
|
@@ -2440,6 +1142,10 @@ var GenericChatWidget = ({
|
|
|
2440
1142
|
ChatEventTypeAgentSessionEnd,
|
|
2441
1143
|
ChatEventTypeAgentSessionStart,
|
|
2442
1144
|
ChatEventTypeAgentStatusChange,
|
|
1145
|
+
ChatEventTypeAttachmentProcessingComplete,
|
|
1146
|
+
ChatEventTypeAttachmentProcessingError,
|
|
1147
|
+
ChatEventTypeAttachmentProcessingProgress,
|
|
1148
|
+
ChatEventTypeAttachmentProcessingStarted,
|
|
2443
1149
|
ChatEventTypeBlockUser,
|
|
2444
1150
|
ChatEventTypeCSATRequest,
|
|
2445
1151
|
ChatEventTypeCSATResponse,
|
|
@@ -2491,6 +1197,7 @@ var GenericChatWidget = ({
|
|
|
2491
1197
|
ChatEventTypePong,
|
|
2492
1198
|
ChatEventTypeRead,
|
|
2493
1199
|
ChatEventTypeReconnected,
|
|
1200
|
+
ChatEventTypeRetryAttachment,
|
|
2494
1201
|
ChatEventTypeRoomCreated,
|
|
2495
1202
|
ChatEventTypeRoomDeleted,
|
|
2496
1203
|
ChatEventTypeRoomUpdated,
|
|
@@ -2525,10 +1232,6 @@ var GenericChatWidget = ({
|
|
|
2525
1232
|
ChatEventTypeUserSuggestedActions,
|
|
2526
1233
|
ChatEventTypeWaiting,
|
|
2527
1234
|
ChatEventTypeWaitingForAgent,
|
|
2528
|
-
ChatHeader,
|
|
2529
|
-
ChatHumanAgentActions,
|
|
2530
|
-
ChatInput,
|
|
2531
|
-
ChatMessages,
|
|
2532
1235
|
ChatRoleAI,
|
|
2533
1236
|
ChatRoleDataQuery,
|
|
2534
1237
|
ChatRoleEvent,
|
|
@@ -2544,19 +1247,16 @@ var GenericChatWidget = ({
|
|
|
2544
1247
|
ChatStatusActive,
|
|
2545
1248
|
ChatStatusArchived,
|
|
2546
1249
|
ChatStatusClosed,
|
|
2547
|
-
ChatStatusCustomerUI,
|
|
2548
1250
|
ChatStatusDisconnected,
|
|
2549
1251
|
ChatTypeCustomerSupport,
|
|
2550
1252
|
ChatTypeDirect,
|
|
2551
1253
|
ChatTypeGroup,
|
|
2552
1254
|
ChatTypePrivateRoom,
|
|
2553
1255
|
ChatTypePublicRoom,
|
|
2554
|
-
ChatTypingIndicator,
|
|
2555
1256
|
CompleteChatByAgentSubject,
|
|
2556
1257
|
CreateAgentQueueSubject,
|
|
2557
1258
|
DeleteAgentQueueSubject,
|
|
2558
1259
|
EndAgentSessionSubject,
|
|
2559
|
-
GenericChatWidget,
|
|
2560
1260
|
GetActiveChatCountSubject,
|
|
2561
1261
|
GetActiveChatsSubject,
|
|
2562
1262
|
GetAgentQueuesSubject,
|
|
@@ -2592,14 +1292,6 @@ var GenericChatWidget = ({
|
|
|
2592
1292
|
UserStatusBusy,
|
|
2593
1293
|
UserStatusOffline,
|
|
2594
1294
|
UserStatusOnline,
|
|
2595
|
-
|
|
2596
|
-
WebSocketChatAdminProvider,
|
|
2597
|
-
WebSocketChatCustomerContext,
|
|
2598
|
-
WebSocketChatCustomerProvider,
|
|
2599
|
-
useWebSocketChatAdmin,
|
|
2600
|
-
useWebSocketChatAdminContext,
|
|
2601
|
-
useWebSocketChatBase,
|
|
2602
|
-
useWebSocketChatCustomer,
|
|
2603
|
-
useWebSocketChatCustomerContext
|
|
1295
|
+
useChat
|
|
2604
1296
|
});
|
|
2605
1297
|
//# sourceMappingURL=index.js.map
|