@elqnt/chat 2.0.7 → 3.0.0
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 +386 -0
- package/dist/api/index.d.mts +308 -0
- package/dist/api/index.d.ts +308 -0
- package/dist/api/index.js +220 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/index.mjs +183 -0
- package/dist/api/index.mjs.map +1 -0
- package/dist/hooks/index.d.mts +78 -0
- package/dist/hooks/index.d.ts +78 -0
- package/dist/hooks/index.js +709 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +683 -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 +699 -3607
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +690 -3584
- 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 +875 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/index.mjs +843 -0
- package/dist/transport/index.mjs.map +1 -0
- package/dist/types-BB5nRdZs.d.mts +222 -0
- package/dist/types-CNvuxtcv.d.ts +222 -0
- package/package.json +32 -40
- 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.mjs
CHANGED
|
@@ -1,15 +1,682 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
// hooks/use-chat.ts
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
|
+
|
|
6
|
+
// transport/types.ts
|
|
7
|
+
function createLogger(debug = false) {
|
|
8
|
+
return {
|
|
9
|
+
debug: debug ? console.log.bind(console, "[chat]") : () => {
|
|
10
|
+
},
|
|
11
|
+
info: console.info.bind(console, "[chat]"),
|
|
12
|
+
warn: console.warn.bind(console, "[chat]"),
|
|
13
|
+
error: console.error.bind(console, "[chat]")
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
17
|
+
maxRetries: 10,
|
|
18
|
+
intervals: [1e3, 2e3, 5e3],
|
|
19
|
+
backoffMultiplier: 1.5,
|
|
20
|
+
maxBackoffTime: 3e4
|
|
21
|
+
};
|
|
22
|
+
function calculateRetryInterval(retryCount, config = DEFAULT_RETRY_CONFIG) {
|
|
23
|
+
const {
|
|
24
|
+
intervals = DEFAULT_RETRY_CONFIG.intervals,
|
|
25
|
+
backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,
|
|
26
|
+
maxBackoffTime = DEFAULT_RETRY_CONFIG.maxBackoffTime
|
|
27
|
+
} = config;
|
|
28
|
+
if (retryCount < intervals.length) {
|
|
29
|
+
return intervals[retryCount];
|
|
30
|
+
}
|
|
31
|
+
const baseInterval = intervals[intervals.length - 1] || 5e3;
|
|
32
|
+
const backoffTime = baseInterval * Math.pow(backoffMultiplier, retryCount - intervals.length + 1);
|
|
33
|
+
return Math.min(backoffTime, maxBackoffTime);
|
|
34
|
+
}
|
|
7
35
|
|
|
8
|
-
//
|
|
9
|
-
|
|
36
|
+
// transport/sse.ts
|
|
37
|
+
function createSSETransport(options = {}) {
|
|
38
|
+
const {
|
|
39
|
+
retryConfig = DEFAULT_RETRY_CONFIG,
|
|
40
|
+
debug = false,
|
|
41
|
+
logger = createLogger(debug)
|
|
42
|
+
} = options;
|
|
43
|
+
let eventSource;
|
|
44
|
+
let config;
|
|
45
|
+
let state = "disconnected";
|
|
46
|
+
let error;
|
|
47
|
+
let retryCount = 0;
|
|
48
|
+
let reconnectTimeout;
|
|
49
|
+
let intentionalDisconnect = false;
|
|
50
|
+
const metrics = {
|
|
51
|
+
latency: 0,
|
|
52
|
+
messagesSent: 0,
|
|
53
|
+
messagesReceived: 0,
|
|
54
|
+
messagesQueued: 0,
|
|
55
|
+
reconnectCount: 0,
|
|
56
|
+
transportType: "sse"
|
|
57
|
+
};
|
|
58
|
+
const globalHandlers = /* @__PURE__ */ new Set();
|
|
59
|
+
const typeHandlers = /* @__PURE__ */ new Map();
|
|
60
|
+
function emit(event) {
|
|
61
|
+
metrics.messagesReceived++;
|
|
62
|
+
metrics.lastMessageAt = Date.now();
|
|
63
|
+
globalHandlers.forEach((handler) => {
|
|
64
|
+
try {
|
|
65
|
+
handler(event);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
logger.error("Error in message handler:", err);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const handlers = typeHandlers.get(event.type);
|
|
71
|
+
if (handlers) {
|
|
72
|
+
handlers.forEach((handler) => {
|
|
73
|
+
try {
|
|
74
|
+
handler(event);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
logger.error(`Error in ${event.type} handler:`, err);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function sendRest(endpoint, body) {
|
|
82
|
+
if (!config) {
|
|
83
|
+
throw new Error("Transport not connected");
|
|
84
|
+
}
|
|
85
|
+
const url = `${config.baseUrl}/${endpoint}`;
|
|
86
|
+
logger.debug(`POST ${endpoint}`, body);
|
|
87
|
+
const response = await fetch(url, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: { "Content-Type": "application/json" },
|
|
90
|
+
body: JSON.stringify(body)
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
const errorText = await response.text();
|
|
94
|
+
throw new Error(`API error: ${response.status} - ${errorText}`);
|
|
95
|
+
}
|
|
96
|
+
return response.json();
|
|
97
|
+
}
|
|
98
|
+
function handleMessage(event) {
|
|
99
|
+
if (!event.data || event.data === "") return;
|
|
100
|
+
try {
|
|
101
|
+
const data = JSON.parse(event.data);
|
|
102
|
+
logger.debug("Received:", data.type);
|
|
103
|
+
emit(data);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
logger.error("Failed to parse SSE message:", err);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function setupEventListeners(es) {
|
|
109
|
+
es.addEventListener("message", handleMessage);
|
|
110
|
+
const eventTypes = [
|
|
111
|
+
"reconnected",
|
|
112
|
+
"typing",
|
|
113
|
+
"stopped_typing",
|
|
114
|
+
"waiting",
|
|
115
|
+
"waiting_for_agent",
|
|
116
|
+
"human_agent_joined",
|
|
117
|
+
"human_agent_left",
|
|
118
|
+
"chat_ended",
|
|
119
|
+
"chat_updated",
|
|
120
|
+
"load_chat_response",
|
|
121
|
+
"new_chat_created",
|
|
122
|
+
"show_csat_survey",
|
|
123
|
+
"csat_response",
|
|
124
|
+
"user_suggested_actions",
|
|
125
|
+
"agent_execution_started",
|
|
126
|
+
"agent_execution_ended",
|
|
127
|
+
"agent_context_update",
|
|
128
|
+
"plan_pending_approval",
|
|
129
|
+
"step_started",
|
|
130
|
+
"step_completed",
|
|
131
|
+
"step_failed",
|
|
132
|
+
"plan_completed",
|
|
133
|
+
"skills_changed",
|
|
134
|
+
"summary_update"
|
|
135
|
+
];
|
|
136
|
+
eventTypes.forEach((type) => {
|
|
137
|
+
es.addEventListener(type, handleMessage);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
function scheduleReconnect() {
|
|
141
|
+
if (intentionalDisconnect || !config) return;
|
|
142
|
+
const maxRetries = retryConfig.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries;
|
|
143
|
+
if (retryCount >= maxRetries) {
|
|
144
|
+
logger.error(`Max retries (${maxRetries}) exceeded`);
|
|
145
|
+
error = {
|
|
146
|
+
code: "CONNECTION_FAILED",
|
|
147
|
+
message: `Max retries (${maxRetries}) exceeded`,
|
|
148
|
+
retryable: false,
|
|
149
|
+
timestamp: Date.now()
|
|
150
|
+
};
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const interval = calculateRetryInterval(retryCount, retryConfig);
|
|
154
|
+
retryCount++;
|
|
155
|
+
metrics.reconnectCount++;
|
|
156
|
+
logger.info(`Reconnecting in ${interval}ms (attempt ${retryCount})`);
|
|
157
|
+
state = "reconnecting";
|
|
158
|
+
reconnectTimeout = setTimeout(() => {
|
|
159
|
+
if (config) {
|
|
160
|
+
transport.connect(config).catch((err) => {
|
|
161
|
+
logger.error("Reconnect failed:", err);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}, interval);
|
|
165
|
+
}
|
|
166
|
+
const transport = {
|
|
167
|
+
async connect(cfg) {
|
|
168
|
+
config = cfg;
|
|
169
|
+
intentionalDisconnect = false;
|
|
170
|
+
if (eventSource) {
|
|
171
|
+
eventSource.close();
|
|
172
|
+
eventSource = void 0;
|
|
173
|
+
}
|
|
174
|
+
if (reconnectTimeout) {
|
|
175
|
+
clearTimeout(reconnectTimeout);
|
|
176
|
+
reconnectTimeout = void 0;
|
|
177
|
+
}
|
|
178
|
+
state = retryCount > 0 ? "reconnecting" : "connecting";
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
const connectionStart = Date.now();
|
|
181
|
+
const url = `${cfg.baseUrl}/stream?orgId=${cfg.orgId}&userId=${cfg.userId}&clientType=${cfg.clientType}${cfg.chatKey ? `&chatId=${cfg.chatKey}` : ""}`;
|
|
182
|
+
logger.debug("Connecting to:", url);
|
|
183
|
+
const es = new EventSource(url);
|
|
184
|
+
es.onopen = () => {
|
|
185
|
+
const connectionTime = Date.now() - connectionStart;
|
|
186
|
+
logger.info(`Connected in ${connectionTime}ms`);
|
|
187
|
+
state = "connected";
|
|
188
|
+
error = void 0;
|
|
189
|
+
retryCount = 0;
|
|
190
|
+
metrics.connectedAt = Date.now();
|
|
191
|
+
metrics.latency = connectionTime;
|
|
192
|
+
setupEventListeners(es);
|
|
193
|
+
resolve();
|
|
194
|
+
};
|
|
195
|
+
es.onerror = () => {
|
|
196
|
+
if (es.readyState === EventSource.CLOSED) {
|
|
197
|
+
const sseError = {
|
|
198
|
+
code: "CONNECTION_FAILED",
|
|
199
|
+
message: "SSE connection failed",
|
|
200
|
+
retryable: true,
|
|
201
|
+
timestamp: Date.now()
|
|
202
|
+
};
|
|
203
|
+
error = sseError;
|
|
204
|
+
metrics.lastError = sseError;
|
|
205
|
+
state = "disconnected";
|
|
206
|
+
if (!intentionalDisconnect) {
|
|
207
|
+
scheduleReconnect();
|
|
208
|
+
}
|
|
209
|
+
if (retryCount === 0) {
|
|
210
|
+
reject(sseError);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
eventSource = es;
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
disconnect(intentional = true) {
|
|
218
|
+
logger.info("Disconnecting", { intentional });
|
|
219
|
+
intentionalDisconnect = intentional;
|
|
220
|
+
if (reconnectTimeout) {
|
|
221
|
+
clearTimeout(reconnectTimeout);
|
|
222
|
+
reconnectTimeout = void 0;
|
|
223
|
+
}
|
|
224
|
+
if (eventSource) {
|
|
225
|
+
eventSource.close();
|
|
226
|
+
eventSource = void 0;
|
|
227
|
+
}
|
|
228
|
+
state = "disconnected";
|
|
229
|
+
retryCount = 0;
|
|
230
|
+
},
|
|
231
|
+
async send(event) {
|
|
232
|
+
if (!config) {
|
|
233
|
+
throw new Error("Transport not connected");
|
|
234
|
+
}
|
|
235
|
+
switch (event.type) {
|
|
236
|
+
case "message":
|
|
237
|
+
await sendRest("send", {
|
|
238
|
+
orgId: event.orgId,
|
|
239
|
+
chatKey: event.chatKey,
|
|
240
|
+
userId: event.userId,
|
|
241
|
+
message: event.message
|
|
242
|
+
});
|
|
243
|
+
break;
|
|
244
|
+
case "typing":
|
|
245
|
+
await sendRest("typing", {
|
|
246
|
+
orgId: event.orgId,
|
|
247
|
+
chatKey: event.chatKey,
|
|
248
|
+
userId: event.userId,
|
|
249
|
+
typing: true
|
|
250
|
+
});
|
|
251
|
+
break;
|
|
252
|
+
case "stopped_typing":
|
|
253
|
+
await sendRest("typing", {
|
|
254
|
+
orgId: event.orgId,
|
|
255
|
+
chatKey: event.chatKey,
|
|
256
|
+
userId: event.userId,
|
|
257
|
+
typing: false
|
|
258
|
+
});
|
|
259
|
+
break;
|
|
260
|
+
case "load_chat":
|
|
261
|
+
await sendRest("load", {
|
|
262
|
+
orgId: event.orgId,
|
|
263
|
+
chatKey: event.chatKey,
|
|
264
|
+
userId: event.userId
|
|
265
|
+
});
|
|
266
|
+
break;
|
|
267
|
+
case "new_chat":
|
|
268
|
+
await sendRest("create", {
|
|
269
|
+
orgId: event.orgId,
|
|
270
|
+
userId: event.userId,
|
|
271
|
+
metadata: event.data
|
|
272
|
+
});
|
|
273
|
+
break;
|
|
274
|
+
case "end_chat":
|
|
275
|
+
await sendRest("end", {
|
|
276
|
+
orgId: event.orgId,
|
|
277
|
+
chatKey: event.chatKey,
|
|
278
|
+
userId: event.userId,
|
|
279
|
+
data: event.data
|
|
280
|
+
});
|
|
281
|
+
break;
|
|
282
|
+
default:
|
|
283
|
+
await sendRest("event", {
|
|
284
|
+
type: event.type,
|
|
285
|
+
orgId: event.orgId,
|
|
286
|
+
chatKey: event.chatKey,
|
|
287
|
+
userId: event.userId,
|
|
288
|
+
data: event.data
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
metrics.messagesSent++;
|
|
292
|
+
},
|
|
293
|
+
async sendMessage(message) {
|
|
294
|
+
if (!config) {
|
|
295
|
+
throw new Error("Transport not connected");
|
|
296
|
+
}
|
|
297
|
+
await sendRest("send", {
|
|
298
|
+
orgId: config.orgId,
|
|
299
|
+
chatKey: config.chatKey,
|
|
300
|
+
userId: config.userId,
|
|
301
|
+
message
|
|
302
|
+
});
|
|
303
|
+
metrics.messagesSent++;
|
|
304
|
+
},
|
|
305
|
+
onMessage(handler) {
|
|
306
|
+
globalHandlers.add(handler);
|
|
307
|
+
return () => globalHandlers.delete(handler);
|
|
308
|
+
},
|
|
309
|
+
on(eventType, handler) {
|
|
310
|
+
if (!typeHandlers.has(eventType)) {
|
|
311
|
+
typeHandlers.set(eventType, /* @__PURE__ */ new Set());
|
|
312
|
+
}
|
|
313
|
+
typeHandlers.get(eventType).add(handler);
|
|
314
|
+
return () => {
|
|
315
|
+
const handlers = typeHandlers.get(eventType);
|
|
316
|
+
if (handlers) {
|
|
317
|
+
handlers.delete(handler);
|
|
318
|
+
if (handlers.size === 0) {
|
|
319
|
+
typeHandlers.delete(eventType);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
},
|
|
324
|
+
getState() {
|
|
325
|
+
return state;
|
|
326
|
+
},
|
|
327
|
+
getMetrics() {
|
|
328
|
+
return { ...metrics };
|
|
329
|
+
},
|
|
330
|
+
getError() {
|
|
331
|
+
return error;
|
|
332
|
+
},
|
|
333
|
+
clearError() {
|
|
334
|
+
error = void 0;
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
return transport;
|
|
338
|
+
}
|
|
10
339
|
|
|
11
|
-
// hooks/use-
|
|
12
|
-
|
|
340
|
+
// hooks/use-chat.ts
|
|
341
|
+
function getDefaultTransport(type, debug) {
|
|
342
|
+
return createSSETransport({ debug });
|
|
343
|
+
}
|
|
344
|
+
function useChat(options) {
|
|
345
|
+
const {
|
|
346
|
+
baseUrl,
|
|
347
|
+
orgId,
|
|
348
|
+
userId,
|
|
349
|
+
clientType = "customer",
|
|
350
|
+
transport: transportOption,
|
|
351
|
+
onMessage,
|
|
352
|
+
onError,
|
|
353
|
+
onConnectionChange,
|
|
354
|
+
autoConnect = false,
|
|
355
|
+
retryConfig,
|
|
356
|
+
debug = false
|
|
357
|
+
} = options;
|
|
358
|
+
const [connectionState, setConnectionState] = useState("disconnected");
|
|
359
|
+
const [currentChat, setCurrentChat] = useState(null);
|
|
360
|
+
const [chatKey, setChatKey] = useState(null);
|
|
361
|
+
const [messages, setMessages] = useState([]);
|
|
362
|
+
const [error, setError] = useState(null);
|
|
363
|
+
const [metrics, setMetrics] = useState({
|
|
364
|
+
latency: 0,
|
|
365
|
+
messagesSent: 0,
|
|
366
|
+
messagesReceived: 0,
|
|
367
|
+
messagesQueued: 0,
|
|
368
|
+
reconnectCount: 0
|
|
369
|
+
});
|
|
370
|
+
const transportRef = useRef(null);
|
|
371
|
+
const mountedRef = useRef(false);
|
|
372
|
+
const onMessageRef = useRef(onMessage);
|
|
373
|
+
const onErrorRef = useRef(onError);
|
|
374
|
+
const chatCreationResolverRef = useRef(null);
|
|
375
|
+
const typingTimeoutRef = useRef(null);
|
|
376
|
+
useEffect(() => {
|
|
377
|
+
onMessageRef.current = onMessage;
|
|
378
|
+
onErrorRef.current = onError;
|
|
379
|
+
}, [onMessage, onError]);
|
|
380
|
+
useEffect(() => {
|
|
381
|
+
if (typeof transportOption === "object") {
|
|
382
|
+
transportRef.current = transportOption;
|
|
383
|
+
} else {
|
|
384
|
+
transportRef.current = getDefaultTransport(
|
|
385
|
+
transportOption,
|
|
386
|
+
debug
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
}, [transportOption, debug]);
|
|
390
|
+
const handleEvent = useCallback((event) => {
|
|
391
|
+
if (!mountedRef.current) return;
|
|
392
|
+
setMetrics((prev) => ({
|
|
393
|
+
...prev,
|
|
394
|
+
messagesReceived: prev.messagesReceived + 1,
|
|
395
|
+
lastMessageAt: Date.now()
|
|
396
|
+
}));
|
|
397
|
+
switch (event.type) {
|
|
398
|
+
case "new_chat_created":
|
|
399
|
+
const newChatKey = event.data?.chatKey;
|
|
400
|
+
if (newChatKey) {
|
|
401
|
+
setChatKey(newChatKey);
|
|
402
|
+
if (chatCreationResolverRef.current) {
|
|
403
|
+
chatCreationResolverRef.current.resolve(newChatKey);
|
|
404
|
+
chatCreationResolverRef.current = null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
case "load_chat_response":
|
|
409
|
+
const chat = event.data?.chat;
|
|
410
|
+
if (chat) {
|
|
411
|
+
setCurrentChat(chat);
|
|
412
|
+
setChatKey(chat.key);
|
|
413
|
+
setMessages(chat.messages || []);
|
|
414
|
+
}
|
|
415
|
+
break;
|
|
416
|
+
case "message":
|
|
417
|
+
if (event.message) {
|
|
418
|
+
setMessages((prev) => [...prev, event.message]);
|
|
419
|
+
}
|
|
420
|
+
break;
|
|
421
|
+
case "chat_ended":
|
|
422
|
+
setCurrentChat(null);
|
|
423
|
+
setChatKey(null);
|
|
424
|
+
break;
|
|
425
|
+
case "error":
|
|
426
|
+
const errorMsg = event.data?.message;
|
|
427
|
+
if (errorMsg) {
|
|
428
|
+
const transportError = {
|
|
429
|
+
code: "NETWORK_ERROR",
|
|
430
|
+
message: errorMsg,
|
|
431
|
+
retryable: true,
|
|
432
|
+
timestamp: Date.now()
|
|
433
|
+
};
|
|
434
|
+
setError(transportError);
|
|
435
|
+
onErrorRef.current?.(transportError);
|
|
436
|
+
}
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
onMessageRef.current?.(event);
|
|
440
|
+
}, []);
|
|
441
|
+
const connect = useCallback(async () => {
|
|
442
|
+
const transport = transportRef.current;
|
|
443
|
+
if (!transport) {
|
|
444
|
+
throw new Error("Transport not initialized");
|
|
445
|
+
}
|
|
446
|
+
if (!orgId) {
|
|
447
|
+
const err = {
|
|
448
|
+
code: "CONNECTION_FAILED",
|
|
449
|
+
message: "orgId is required",
|
|
450
|
+
retryable: false,
|
|
451
|
+
timestamp: Date.now()
|
|
452
|
+
};
|
|
453
|
+
setError(err);
|
|
454
|
+
throw err;
|
|
455
|
+
}
|
|
456
|
+
setConnectionState("connecting");
|
|
457
|
+
try {
|
|
458
|
+
const unsubscribe = transport.onMessage(handleEvent);
|
|
459
|
+
await transport.connect({
|
|
460
|
+
baseUrl,
|
|
461
|
+
orgId,
|
|
462
|
+
userId,
|
|
463
|
+
clientType,
|
|
464
|
+
chatKey: chatKey || void 0,
|
|
465
|
+
debug
|
|
466
|
+
});
|
|
467
|
+
setConnectionState("connected");
|
|
468
|
+
setError(null);
|
|
469
|
+
setMetrics(transport.getMetrics());
|
|
470
|
+
onConnectionChange?.("connected");
|
|
471
|
+
return;
|
|
472
|
+
} catch (err) {
|
|
473
|
+
const transportError = err;
|
|
474
|
+
setConnectionState("disconnected");
|
|
475
|
+
setError(transportError);
|
|
476
|
+
onErrorRef.current?.(transportError);
|
|
477
|
+
throw err;
|
|
478
|
+
}
|
|
479
|
+
}, [baseUrl, orgId, userId, clientType, chatKey, debug, handleEvent, onConnectionChange]);
|
|
480
|
+
const disconnect = useCallback(() => {
|
|
481
|
+
const transport = transportRef.current;
|
|
482
|
+
if (transport) {
|
|
483
|
+
transport.disconnect(true);
|
|
484
|
+
}
|
|
485
|
+
setConnectionState("disconnected");
|
|
486
|
+
onConnectionChange?.("disconnected");
|
|
487
|
+
}, [onConnectionChange]);
|
|
488
|
+
const startChat = useCallback(
|
|
489
|
+
async (metadata) => {
|
|
490
|
+
const transport = transportRef.current;
|
|
491
|
+
if (!transport) {
|
|
492
|
+
throw new Error("Transport not initialized");
|
|
493
|
+
}
|
|
494
|
+
return new Promise((resolve, reject) => {
|
|
495
|
+
chatCreationResolverRef.current = { resolve, reject };
|
|
496
|
+
transport.send({
|
|
497
|
+
type: "new_chat",
|
|
498
|
+
orgId,
|
|
499
|
+
chatKey: "",
|
|
500
|
+
userId,
|
|
501
|
+
timestamp: Date.now(),
|
|
502
|
+
data: metadata
|
|
503
|
+
}).catch((err) => {
|
|
504
|
+
chatCreationResolverRef.current = null;
|
|
505
|
+
reject(err);
|
|
506
|
+
});
|
|
507
|
+
setTimeout(() => {
|
|
508
|
+
if (chatCreationResolverRef.current) {
|
|
509
|
+
chatCreationResolverRef.current = null;
|
|
510
|
+
reject(new Error("Chat creation timed out"));
|
|
511
|
+
}
|
|
512
|
+
}, 3e4);
|
|
513
|
+
});
|
|
514
|
+
},
|
|
515
|
+
[orgId, userId]
|
|
516
|
+
);
|
|
517
|
+
const loadChat = useCallback(
|
|
518
|
+
async (key) => {
|
|
519
|
+
const transport = transportRef.current;
|
|
520
|
+
if (!transport) {
|
|
521
|
+
throw new Error("Transport not initialized");
|
|
522
|
+
}
|
|
523
|
+
setChatKey(key);
|
|
524
|
+
await transport.send({
|
|
525
|
+
type: "load_chat",
|
|
526
|
+
orgId,
|
|
527
|
+
chatKey: key,
|
|
528
|
+
userId,
|
|
529
|
+
timestamp: Date.now()
|
|
530
|
+
});
|
|
531
|
+
},
|
|
532
|
+
[orgId, userId]
|
|
533
|
+
);
|
|
534
|
+
const sendMessage = useCallback(
|
|
535
|
+
async (content, attachments) => {
|
|
536
|
+
const transport = transportRef.current;
|
|
537
|
+
if (!transport) {
|
|
538
|
+
throw new Error("Transport not initialized");
|
|
539
|
+
}
|
|
540
|
+
if (!chatKey) {
|
|
541
|
+
throw new Error("No active chat");
|
|
542
|
+
}
|
|
543
|
+
const message = {
|
|
544
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
545
|
+
role: "user",
|
|
546
|
+
content,
|
|
547
|
+
time: Date.now(),
|
|
548
|
+
status: "sending",
|
|
549
|
+
senderId: userId,
|
|
550
|
+
createdAt: Date.now(),
|
|
551
|
+
attachments
|
|
552
|
+
};
|
|
553
|
+
setMessages((prev) => [...prev, message]);
|
|
554
|
+
await transport.send({
|
|
555
|
+
type: "message",
|
|
556
|
+
orgId,
|
|
557
|
+
chatKey,
|
|
558
|
+
userId,
|
|
559
|
+
timestamp: Date.now(),
|
|
560
|
+
message
|
|
561
|
+
});
|
|
562
|
+
setMetrics((prev) => ({
|
|
563
|
+
...prev,
|
|
564
|
+
messagesSent: prev.messagesSent + 1
|
|
565
|
+
}));
|
|
566
|
+
},
|
|
567
|
+
[orgId, chatKey, userId]
|
|
568
|
+
);
|
|
569
|
+
const endChat = useCallback(
|
|
570
|
+
async (reason) => {
|
|
571
|
+
const transport = transportRef.current;
|
|
572
|
+
if (!transport) {
|
|
573
|
+
throw new Error("Transport not initialized");
|
|
574
|
+
}
|
|
575
|
+
if (!chatKey) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
await transport.send({
|
|
579
|
+
type: "end_chat",
|
|
580
|
+
orgId,
|
|
581
|
+
chatKey,
|
|
582
|
+
userId,
|
|
583
|
+
timestamp: Date.now(),
|
|
584
|
+
data: reason ? { reason } : void 0
|
|
585
|
+
});
|
|
586
|
+
setCurrentChat(null);
|
|
587
|
+
setChatKey(null);
|
|
588
|
+
},
|
|
589
|
+
[orgId, chatKey, userId]
|
|
590
|
+
);
|
|
591
|
+
const startTyping = useCallback(() => {
|
|
592
|
+
const transport = transportRef.current;
|
|
593
|
+
if (!transport || !chatKey) return;
|
|
594
|
+
if (typingTimeoutRef.current) {
|
|
595
|
+
clearTimeout(typingTimeoutRef.current);
|
|
596
|
+
}
|
|
597
|
+
transport.send({
|
|
598
|
+
type: "typing",
|
|
599
|
+
orgId,
|
|
600
|
+
chatKey,
|
|
601
|
+
userId,
|
|
602
|
+
timestamp: Date.now()
|
|
603
|
+
}).catch(() => {
|
|
604
|
+
});
|
|
605
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
606
|
+
stopTyping();
|
|
607
|
+
}, 3e3);
|
|
608
|
+
}, [orgId, chatKey, userId]);
|
|
609
|
+
const stopTyping = useCallback(() => {
|
|
610
|
+
const transport = transportRef.current;
|
|
611
|
+
if (!transport || !chatKey) return;
|
|
612
|
+
if (typingTimeoutRef.current) {
|
|
613
|
+
clearTimeout(typingTimeoutRef.current);
|
|
614
|
+
typingTimeoutRef.current = null;
|
|
615
|
+
}
|
|
616
|
+
transport.send({
|
|
617
|
+
type: "stopped_typing",
|
|
618
|
+
orgId,
|
|
619
|
+
chatKey,
|
|
620
|
+
userId,
|
|
621
|
+
timestamp: Date.now()
|
|
622
|
+
}).catch(() => {
|
|
623
|
+
});
|
|
624
|
+
}, [orgId, chatKey, userId]);
|
|
625
|
+
const on = useCallback(
|
|
626
|
+
(eventType, handler) => {
|
|
627
|
+
const transport = transportRef.current;
|
|
628
|
+
if (!transport) {
|
|
629
|
+
return () => {
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
return transport.on(eventType, handler);
|
|
633
|
+
},
|
|
634
|
+
[]
|
|
635
|
+
);
|
|
636
|
+
const clearError = useCallback(() => {
|
|
637
|
+
setError(null);
|
|
638
|
+
transportRef.current?.clearError();
|
|
639
|
+
}, []);
|
|
640
|
+
useEffect(() => {
|
|
641
|
+
mountedRef.current = true;
|
|
642
|
+
if (autoConnect) {
|
|
643
|
+
connect().catch(() => {
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
return () => {
|
|
647
|
+
mountedRef.current = false;
|
|
648
|
+
if (typingTimeoutRef.current) {
|
|
649
|
+
clearTimeout(typingTimeoutRef.current);
|
|
650
|
+
}
|
|
651
|
+
disconnect();
|
|
652
|
+
};
|
|
653
|
+
}, []);
|
|
654
|
+
const isConnected = connectionState === "connected";
|
|
655
|
+
return {
|
|
656
|
+
// Connection
|
|
657
|
+
connect,
|
|
658
|
+
disconnect,
|
|
659
|
+
connectionState,
|
|
660
|
+
isConnected,
|
|
661
|
+
// Chat operations
|
|
662
|
+
startChat,
|
|
663
|
+
loadChat,
|
|
664
|
+
sendMessage,
|
|
665
|
+
endChat,
|
|
666
|
+
// Typing
|
|
667
|
+
startTyping,
|
|
668
|
+
stopTyping,
|
|
669
|
+
// State
|
|
670
|
+
currentChat,
|
|
671
|
+
chatKey,
|
|
672
|
+
messages,
|
|
673
|
+
error,
|
|
674
|
+
metrics,
|
|
675
|
+
// Events
|
|
676
|
+
on,
|
|
677
|
+
clearError
|
|
678
|
+
};
|
|
679
|
+
}
|
|
13
680
|
|
|
14
681
|
// models/chat-models.ts
|
|
15
682
|
var ChatStatusActive = "active";
|
|
@@ -128,6 +795,11 @@ var ChatEventTypePong = "pong";
|
|
|
128
795
|
var ChatEventTypeSkillActivate = "skill_activate";
|
|
129
796
|
var ChatEventTypeSkillDeactivate = "skill_deactivate";
|
|
130
797
|
var ChatEventTypeSkillsChanged = "skills_changed";
|
|
798
|
+
var ChatEventTypeAttachmentProcessingStarted = "attachment_processing_started";
|
|
799
|
+
var ChatEventTypeAttachmentProcessingProgress = "attachment_processing_progress";
|
|
800
|
+
var ChatEventTypeAttachmentProcessingComplete = "attachment_processing_complete";
|
|
801
|
+
var ChatEventTypeAttachmentProcessingError = "attachment_processing_error";
|
|
802
|
+
var ChatEventTypeRetryAttachment = "retry_attachment";
|
|
131
803
|
var MessageStatusSending = "sending";
|
|
132
804
|
var MessageStatusSent = "sent";
|
|
133
805
|
var MessageStatusDelivered = "delivered";
|
|
@@ -149,6 +821,8 @@ var AttachmentTypeSticker = "sticker";
|
|
|
149
821
|
var AttachmentTypeData = "data";
|
|
150
822
|
var AttachmentTypeKGNodes = "kgNodes";
|
|
151
823
|
var AttachmentTypeDocumentSources = "document_sources";
|
|
824
|
+
var AttachmentTypeSpreadsheet = "spreadsheet";
|
|
825
|
+
var AttachmentTypeDataFile = "data_file";
|
|
152
826
|
var ChatSessionStatusActive = "active";
|
|
153
827
|
var ChatSessionStatusIdle = "idle";
|
|
154
828
|
var ChatSessionStatusExpired = "expired";
|
|
@@ -193,3567 +867,7 @@ var UpdateUserStatusSubject = "chat.user.status.update";
|
|
|
193
867
|
var GetOnlineUsersSubject = "chat.users.online.get";
|
|
194
868
|
var TriggerAnalyticsScanSubject = "chat.analytics.trigger-scan";
|
|
195
869
|
var SetupOrgSubject = "chat.org.setup";
|
|
196
|
-
|
|
197
|
-
// hooks/use-websocket-chat-base.ts
|
|
198
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
199
|
-
var createDefaultLogger = (debug) => ({
|
|
200
|
-
debug: debug ? console.log : () => {
|
|
201
|
-
},
|
|
202
|
-
info: console.info,
|
|
203
|
-
warn: console.warn,
|
|
204
|
-
error: console.error
|
|
205
|
-
});
|
|
206
|
-
var DEFAULT_RETRY_CONFIG = {
|
|
207
|
-
maxRetries: 10,
|
|
208
|
-
intervals: [1e3, 2e3, 5e3],
|
|
209
|
-
backoffMultiplier: 1.5,
|
|
210
|
-
maxBackoffTime: 3e4
|
|
211
|
-
};
|
|
212
|
-
var DEFAULT_QUEUE_CONFIG = {
|
|
213
|
-
maxSize: 100,
|
|
214
|
-
dropStrategy: "oldest"
|
|
215
|
-
};
|
|
216
|
-
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
217
|
-
var DEFAULT_HEARTBEAT_TIMEOUT = 5e3;
|
|
218
|
-
var DEFAULT_TRANSPORT = "websocket";
|
|
219
|
-
function isChatEvent(data) {
|
|
220
|
-
return data && typeof data === "object" && (typeof data.type === "string" || data.message);
|
|
221
|
-
}
|
|
222
|
-
var useWebSocketChatBase = ({
|
|
223
|
-
serverBaseUrl,
|
|
224
|
-
orgId,
|
|
225
|
-
clientType,
|
|
226
|
-
product,
|
|
227
|
-
onMessage,
|
|
228
|
-
retryConfig = DEFAULT_RETRY_CONFIG,
|
|
229
|
-
queueConfig = DEFAULT_QUEUE_CONFIG,
|
|
230
|
-
debug = false,
|
|
231
|
-
logger = createDefaultLogger(debug),
|
|
232
|
-
heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL,
|
|
233
|
-
heartbeatTimeout = DEFAULT_HEARTBEAT_TIMEOUT,
|
|
234
|
-
transport = DEFAULT_TRANSPORT
|
|
235
|
-
}) => {
|
|
236
|
-
const [connectionState, setConnectionState] = useState("disconnected");
|
|
237
|
-
const [error, setError] = useState(void 0);
|
|
238
|
-
const [startTime, setStartTime] = useState(void 0);
|
|
239
|
-
const [metrics, setMetrics] = useState({
|
|
240
|
-
latency: 0,
|
|
241
|
-
messagesSent: 0,
|
|
242
|
-
messagesReceived: 0,
|
|
243
|
-
messagesQueued: 0,
|
|
244
|
-
reconnectCount: 0,
|
|
245
|
-
transportType: transport
|
|
246
|
-
});
|
|
247
|
-
const wsRef = useRef(void 0);
|
|
248
|
-
const sseRef = useRef(void 0);
|
|
249
|
-
const transportRef = useRef(transport);
|
|
250
|
-
useEffect(() => {
|
|
251
|
-
transportRef.current = transport;
|
|
252
|
-
}, [transport]);
|
|
253
|
-
const reconnectTimeoutRef = useRef(void 0);
|
|
254
|
-
const retryCountRef = useRef(0);
|
|
255
|
-
const messageQueueRef = useRef([]);
|
|
256
|
-
const mountedRef = useRef(false);
|
|
257
|
-
const currentChatKeyRef = useRef(void 0);
|
|
258
|
-
const currentUserIdRef = useRef(void 0);
|
|
259
|
-
const intentionalDisconnectRef = useRef(false);
|
|
260
|
-
const eventHandlersRef = useRef(
|
|
261
|
-
/* @__PURE__ */ new Map()
|
|
262
|
-
);
|
|
263
|
-
const onMessageRef = useRef(onMessage);
|
|
264
|
-
const pendingMessagesRef = useRef(/* @__PURE__ */ new Map());
|
|
265
|
-
const heartbeatIntervalRef = useRef(void 0);
|
|
266
|
-
const heartbeatTimeoutRef = useRef(void 0);
|
|
267
|
-
const lastPongRef = useRef(Date.now());
|
|
268
|
-
const chatCreationPromiseRef = useRef(null);
|
|
269
|
-
const loadChatRetryMapRef = useRef(/* @__PURE__ */ new Map());
|
|
270
|
-
useEffect(() => {
|
|
271
|
-
onMessageRef.current = onMessage;
|
|
272
|
-
}, [onMessage]);
|
|
273
|
-
const isConnected = connectionState === "connected";
|
|
274
|
-
const on = useCallback((eventType, handler) => {
|
|
275
|
-
if (!eventHandlersRef.current.has(eventType)) {
|
|
276
|
-
eventHandlersRef.current.set(eventType, /* @__PURE__ */ new Set());
|
|
277
|
-
}
|
|
278
|
-
eventHandlersRef.current.get(eventType).add(handler);
|
|
279
|
-
return () => off(eventType, handler);
|
|
280
|
-
}, []);
|
|
281
|
-
const off = useCallback((eventType, handler) => {
|
|
282
|
-
const handlers = eventHandlersRef.current.get(eventType);
|
|
283
|
-
if (handlers) {
|
|
284
|
-
handlers.delete(handler);
|
|
285
|
-
if (handlers.size === 0) {
|
|
286
|
-
eventHandlersRef.current.delete(eventType);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}, []);
|
|
290
|
-
const emit = useCallback(
|
|
291
|
-
(eventType, data) => {
|
|
292
|
-
const handlers = eventHandlersRef.current.get(eventType);
|
|
293
|
-
if (handlers) {
|
|
294
|
-
handlers.forEach((handler) => {
|
|
295
|
-
try {
|
|
296
|
-
handler(data);
|
|
297
|
-
} catch (error2) {
|
|
298
|
-
logger.error(`Error in event handler for ${eventType}:`, error2);
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
[logger]
|
|
304
|
-
);
|
|
305
|
-
const updateMetrics = useCallback((updates) => {
|
|
306
|
-
setMetrics((prev) => ({ ...prev, ...updates }));
|
|
307
|
-
}, []);
|
|
308
|
-
const addToQueue = useCallback(
|
|
309
|
-
(event) => {
|
|
310
|
-
const currentQueueSize = messageQueueRef.current.length;
|
|
311
|
-
const maxSize = queueConfig.maxSize || DEFAULT_QUEUE_CONFIG.maxSize;
|
|
312
|
-
if (currentQueueSize >= maxSize) {
|
|
313
|
-
if (queueConfig.dropStrategy === "newest") {
|
|
314
|
-
logger.warn("Message queue full, dropping new message");
|
|
315
|
-
return false;
|
|
316
|
-
} else {
|
|
317
|
-
const dropped = messageQueueRef.current.shift();
|
|
318
|
-
logger.warn("Message queue full, dropped oldest message", dropped);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
messageQueueRef.current.push(event);
|
|
322
|
-
updateMetrics({ messagesQueued: messageQueueRef.current.length });
|
|
323
|
-
return true;
|
|
324
|
-
},
|
|
325
|
-
[queueConfig, logger, updateMetrics]
|
|
326
|
-
);
|
|
327
|
-
const calculateRetryInterval = useCallback(
|
|
328
|
-
(retryCount) => {
|
|
329
|
-
const config = { ...DEFAULT_RETRY_CONFIG, ...retryConfig };
|
|
330
|
-
const {
|
|
331
|
-
intervals = [],
|
|
332
|
-
backoffMultiplier = 1.5,
|
|
333
|
-
maxBackoffTime = 3e4
|
|
334
|
-
} = config;
|
|
335
|
-
if (retryCount < intervals.length) {
|
|
336
|
-
return intervals[retryCount];
|
|
337
|
-
}
|
|
338
|
-
const baseInterval = intervals[intervals.length - 1] || 5e3;
|
|
339
|
-
const backoffTime = baseInterval * Math.pow(backoffMultiplier, retryCount - intervals.length + 1);
|
|
340
|
-
return Math.min(backoffTime, maxBackoffTime);
|
|
341
|
-
},
|
|
342
|
-
[retryConfig]
|
|
343
|
-
);
|
|
344
|
-
const startHeartbeat = useCallback(() => {
|
|
345
|
-
if (!heartbeatInterval || heartbeatInterval <= 0) return;
|
|
346
|
-
const sendPing = () => {
|
|
347
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
348
|
-
const pingTime = Date.now();
|
|
349
|
-
wsRef.current.send(
|
|
350
|
-
JSON.stringify({ type: "ping", timestamp: pingTime })
|
|
351
|
-
);
|
|
352
|
-
heartbeatTimeoutRef.current = setTimeout(() => {
|
|
353
|
-
logger.warn("Heartbeat timeout - no pong received");
|
|
354
|
-
if (wsRef.current) {
|
|
355
|
-
wsRef.current.close(4e3, "Heartbeat timeout");
|
|
356
|
-
}
|
|
357
|
-
}, heartbeatTimeout);
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
if (heartbeatIntervalRef.current) {
|
|
361
|
-
clearInterval(heartbeatIntervalRef.current);
|
|
362
|
-
}
|
|
363
|
-
heartbeatIntervalRef.current = setInterval(sendPing, heartbeatInterval);
|
|
364
|
-
logger.debug("Heartbeat started");
|
|
365
|
-
}, [heartbeatInterval, heartbeatTimeout, logger]);
|
|
366
|
-
const stopHeartbeat = useCallback(() => {
|
|
367
|
-
if (heartbeatIntervalRef.current) {
|
|
368
|
-
clearInterval(heartbeatIntervalRef.current);
|
|
369
|
-
heartbeatIntervalRef.current = void 0;
|
|
370
|
-
}
|
|
371
|
-
if (heartbeatTimeoutRef.current) {
|
|
372
|
-
clearTimeout(heartbeatTimeoutRef.current);
|
|
373
|
-
heartbeatTimeoutRef.current = void 0;
|
|
374
|
-
}
|
|
375
|
-
logger.debug("Heartbeat stopped");
|
|
376
|
-
}, [logger]);
|
|
377
|
-
const cleanup = useCallback(() => {
|
|
378
|
-
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
|
379
|
-
wsRef.current.close(1e3, "Cleanup");
|
|
380
|
-
}
|
|
381
|
-
wsRef.current = void 0;
|
|
382
|
-
if (sseRef.current) {
|
|
383
|
-
sseRef.current.close();
|
|
384
|
-
sseRef.current = void 0;
|
|
385
|
-
}
|
|
386
|
-
if (reconnectTimeoutRef.current) {
|
|
387
|
-
clearTimeout(reconnectTimeoutRef.current);
|
|
388
|
-
reconnectTimeoutRef.current = void 0;
|
|
389
|
-
}
|
|
390
|
-
stopHeartbeat();
|
|
391
|
-
pendingMessagesRef.current.forEach(({ reject, timeout }) => {
|
|
392
|
-
clearTimeout(timeout);
|
|
393
|
-
reject(new Error("Connection closed"));
|
|
394
|
-
});
|
|
395
|
-
pendingMessagesRef.current.clear();
|
|
396
|
-
loadChatRetryMapRef.current.forEach((retryState) => {
|
|
397
|
-
if (retryState.timeoutId) {
|
|
398
|
-
clearTimeout(retryState.timeoutId);
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
loadChatRetryMapRef.current.clear();
|
|
402
|
-
}, [stopHeartbeat]);
|
|
403
|
-
const getRestApiUrl = useCallback((endpoint) => {
|
|
404
|
-
return `${serverBaseUrl}/${endpoint}`;
|
|
405
|
-
}, [serverBaseUrl]);
|
|
406
|
-
const sendRestMessage = useCallback(
|
|
407
|
-
async (endpoint, body) => {
|
|
408
|
-
const url = getRestApiUrl(endpoint);
|
|
409
|
-
logger.debug(`SSE REST API call: POST ${endpoint}`, body);
|
|
410
|
-
try {
|
|
411
|
-
const response = await fetch(url, {
|
|
412
|
-
method: "POST",
|
|
413
|
-
headers: {
|
|
414
|
-
"Content-Type": "application/json"
|
|
415
|
-
},
|
|
416
|
-
body: JSON.stringify(body)
|
|
417
|
-
});
|
|
418
|
-
if (!response.ok) {
|
|
419
|
-
const errorText = await response.text();
|
|
420
|
-
throw new Error(`REST API error: ${response.status} - ${errorText}`);
|
|
421
|
-
}
|
|
422
|
-
const data = await response.json();
|
|
423
|
-
return data;
|
|
424
|
-
} catch (error2) {
|
|
425
|
-
logger.error(`SSE REST API error for ${endpoint}:`, error2);
|
|
426
|
-
throw error2;
|
|
427
|
-
}
|
|
428
|
-
},
|
|
429
|
-
[getRestApiUrl, logger]
|
|
430
|
-
);
|
|
431
|
-
const connect = useCallback(
|
|
432
|
-
async (userId) => {
|
|
433
|
-
if (!mountedRef.current) {
|
|
434
|
-
mountedRef.current = true;
|
|
435
|
-
}
|
|
436
|
-
if (!orgId) {
|
|
437
|
-
const error2 = {
|
|
438
|
-
code: "CONNECTION_FAILED",
|
|
439
|
-
message: "Cannot connect: orgId is undefined",
|
|
440
|
-
retryable: false,
|
|
441
|
-
timestamp: Date.now()
|
|
442
|
-
};
|
|
443
|
-
logger.error("Cannot connect: orgId is undefined");
|
|
444
|
-
setError(error2);
|
|
445
|
-
return Promise.reject(error2);
|
|
446
|
-
}
|
|
447
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
448
|
-
logger.debug("Already connected (WebSocket)");
|
|
449
|
-
return Promise.resolve();
|
|
450
|
-
}
|
|
451
|
-
if (sseRef.current?.readyState === EventSource.OPEN) {
|
|
452
|
-
logger.debug("Already connected (SSE)");
|
|
453
|
-
return Promise.resolve();
|
|
454
|
-
}
|
|
455
|
-
if (connectionState === "connecting" || connectionState === "reconnecting") {
|
|
456
|
-
logger.debug("Connection already in progress");
|
|
457
|
-
return Promise.resolve();
|
|
458
|
-
}
|
|
459
|
-
const maxRetries = retryConfig.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries;
|
|
460
|
-
if (retryCountRef.current >= maxRetries && !intentionalDisconnectRef.current) {
|
|
461
|
-
const error2 = {
|
|
462
|
-
code: "CONNECTION_FAILED",
|
|
463
|
-
message: `Max retries (${maxRetries}) exceeded`,
|
|
464
|
-
retryable: false,
|
|
465
|
-
timestamp: Date.now()
|
|
466
|
-
};
|
|
467
|
-
setError(error2);
|
|
468
|
-
updateMetrics({ lastError: error2 });
|
|
469
|
-
return Promise.reject(error2);
|
|
470
|
-
}
|
|
471
|
-
cleanup();
|
|
472
|
-
setConnectionState(
|
|
473
|
-
retryCountRef.current > 0 ? "reconnecting" : "connecting"
|
|
474
|
-
);
|
|
475
|
-
intentionalDisconnectRef.current = false;
|
|
476
|
-
return new Promise((resolve, reject) => {
|
|
477
|
-
try {
|
|
478
|
-
const connectionStartTime = Date.now();
|
|
479
|
-
logger.info(`\u{1F504} Connecting with transport: ${transportRef.current}`);
|
|
480
|
-
if (transportRef.current === "sse") {
|
|
481
|
-
const sseUrl = getRestApiUrl(`stream?orgId=${orgId}&userId=${userId}&clientType=${clientType}&chatId=${currentChatKeyRef.current || ""}`);
|
|
482
|
-
logger.debug("Connecting to SSE:", sseUrl);
|
|
483
|
-
console.log(`\u23F3 Initiating SSE connection to ${sseUrl}...`);
|
|
484
|
-
const eventSource = new EventSource(sseUrl);
|
|
485
|
-
eventSource.onopen = () => {
|
|
486
|
-
if (!mountedRef.current) {
|
|
487
|
-
eventSource.close();
|
|
488
|
-
reject(new Error("Component unmounted"));
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
const connectionTimeMs = Date.now() - connectionStartTime;
|
|
492
|
-
const connectionTimeSec = (connectionTimeMs / 1e3).toFixed(2);
|
|
493
|
-
logger.info("\u2705 SSE connected", {
|
|
494
|
-
userId,
|
|
495
|
-
retryCount: retryCountRef.current,
|
|
496
|
-
connectionTime: `${connectionTimeSec}s (${connectionTimeMs}ms)`
|
|
497
|
-
});
|
|
498
|
-
console.log(`\u{1F50C} SSE connection established in ${connectionTimeSec} seconds`);
|
|
499
|
-
setConnectionState("connected");
|
|
500
|
-
setError(void 0);
|
|
501
|
-
const wasReconnecting = retryCountRef.current > 0;
|
|
502
|
-
retryCountRef.current = 0;
|
|
503
|
-
updateMetrics({
|
|
504
|
-
connectedAt: Date.now(),
|
|
505
|
-
latency: connectionTimeMs,
|
|
506
|
-
transportType: "sse",
|
|
507
|
-
reconnectCount: wasReconnecting ? metrics.reconnectCount + 1 : metrics.reconnectCount
|
|
508
|
-
});
|
|
509
|
-
currentUserIdRef.current = userId;
|
|
510
|
-
if (currentChatKeyRef.current) {
|
|
511
|
-
logger.info("Loading chat after SSE reconnection:", currentChatKeyRef.current);
|
|
512
|
-
sendRestMessage("load", {
|
|
513
|
-
orgId,
|
|
514
|
-
chatKey: currentChatKeyRef.current,
|
|
515
|
-
userId
|
|
516
|
-
}).then((response) => {
|
|
517
|
-
if (response.success && response.data?.chat) {
|
|
518
|
-
const chatEvent = {
|
|
519
|
-
type: "load_chat_response",
|
|
520
|
-
orgId,
|
|
521
|
-
chatKey: currentChatKeyRef.current,
|
|
522
|
-
userId,
|
|
523
|
-
timestamp: Date.now(),
|
|
524
|
-
data: response.data
|
|
525
|
-
};
|
|
526
|
-
emit("load_chat_response", chatEvent);
|
|
527
|
-
if (onMessageRef.current) {
|
|
528
|
-
onMessageRef.current(chatEvent);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}).catch((err) => {
|
|
532
|
-
logger.error("Failed to load chat after SSE reconnection:", err);
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
emit("connected", { userId, wasReconnecting, transport: "sse" });
|
|
536
|
-
resolve();
|
|
537
|
-
};
|
|
538
|
-
const handleSSEMessage = (event) => {
|
|
539
|
-
if (!mountedRef.current) return;
|
|
540
|
-
if (!event.data || event.data === "") {
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
try {
|
|
544
|
-
const data = JSON.parse(event.data);
|
|
545
|
-
if (!isChatEvent(data)) {
|
|
546
|
-
logger.warn("Received invalid SSE message format:", data);
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
const chatEvent = data;
|
|
550
|
-
logger.debug("SSE message received:", chatEvent.type);
|
|
551
|
-
updateMetrics({
|
|
552
|
-
messagesReceived: metrics.messagesReceived + 1,
|
|
553
|
-
lastMessageAt: Date.now()
|
|
554
|
-
});
|
|
555
|
-
switch (chatEvent.type) {
|
|
556
|
-
case "new_chat_created":
|
|
557
|
-
const newChatKey = chatEvent.data?.chatKey;
|
|
558
|
-
if (newChatKey) {
|
|
559
|
-
logger.info("New chat created with key:", newChatKey);
|
|
560
|
-
currentChatKeyRef.current = newChatKey;
|
|
561
|
-
if (chatCreationPromiseRef.current) {
|
|
562
|
-
chatCreationPromiseRef.current.resolve(newChatKey);
|
|
563
|
-
chatCreationPromiseRef.current = null;
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
break;
|
|
567
|
-
case "load_chat_response":
|
|
568
|
-
const chat = chatEvent.data?.chat;
|
|
569
|
-
if (chat && chat.key) {
|
|
570
|
-
logger.info("Chat loaded with key:", chat.key);
|
|
571
|
-
currentChatKeyRef.current = chat.key;
|
|
572
|
-
}
|
|
573
|
-
break;
|
|
574
|
-
case "chat_ended":
|
|
575
|
-
logger.info("Chat ended, clearing key");
|
|
576
|
-
currentChatKeyRef.current = void 0;
|
|
577
|
-
break;
|
|
578
|
-
}
|
|
579
|
-
emit(chatEvent.type || "message", chatEvent);
|
|
580
|
-
if (onMessageRef.current) {
|
|
581
|
-
onMessageRef.current(chatEvent);
|
|
582
|
-
}
|
|
583
|
-
} catch (error2) {
|
|
584
|
-
logger.error("Failed to parse SSE message:", error2);
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
eventSource.addEventListener("message", handleSSEMessage);
|
|
588
|
-
eventSource.addEventListener("reconnected", handleSSEMessage);
|
|
589
|
-
eventSource.addEventListener("typing", handleSSEMessage);
|
|
590
|
-
eventSource.addEventListener("stopped_typing", handleSSEMessage);
|
|
591
|
-
eventSource.addEventListener("waiting", handleSSEMessage);
|
|
592
|
-
eventSource.addEventListener("waiting_for_agent", handleSSEMessage);
|
|
593
|
-
eventSource.addEventListener("human_agent_joined", handleSSEMessage);
|
|
594
|
-
eventSource.addEventListener("human_agent_left", handleSSEMessage);
|
|
595
|
-
eventSource.addEventListener("chat_ended", handleSSEMessage);
|
|
596
|
-
eventSource.addEventListener("chat_updated", handleSSEMessage);
|
|
597
|
-
eventSource.addEventListener("load_chat_response", handleSSEMessage);
|
|
598
|
-
eventSource.addEventListener("new_chat_created", handleSSEMessage);
|
|
599
|
-
eventSource.addEventListener("show_csat_survey", handleSSEMessage);
|
|
600
|
-
eventSource.addEventListener("csat_response", handleSSEMessage);
|
|
601
|
-
eventSource.addEventListener("user_suggested_actions", handleSSEMessage);
|
|
602
|
-
eventSource.addEventListener("agent_execution_started", handleSSEMessage);
|
|
603
|
-
eventSource.addEventListener("agent_execution_ended", handleSSEMessage);
|
|
604
|
-
eventSource.addEventListener("agent_context_update", handleSSEMessage);
|
|
605
|
-
eventSource.addEventListener("plan_pending_approval", handleSSEMessage);
|
|
606
|
-
eventSource.addEventListener("step_started", handleSSEMessage);
|
|
607
|
-
eventSource.addEventListener("step_completed", handleSSEMessage);
|
|
608
|
-
eventSource.addEventListener("step_failed", handleSSEMessage);
|
|
609
|
-
eventSource.addEventListener("plan_completed", handleSSEMessage);
|
|
610
|
-
eventSource.addEventListener("skills_changed", handleSSEMessage);
|
|
611
|
-
eventSource.addEventListener("summary_update", handleSSEMessage);
|
|
612
|
-
eventSource.onerror = (error2) => {
|
|
613
|
-
logger.error("SSE error:", error2);
|
|
614
|
-
if (!mountedRef.current) return;
|
|
615
|
-
if (eventSource.readyState === EventSource.CLOSED) {
|
|
616
|
-
const sseError = {
|
|
617
|
-
code: "CONNECTION_FAILED",
|
|
618
|
-
message: "SSE connection failed",
|
|
619
|
-
retryable: true,
|
|
620
|
-
timestamp: Date.now()
|
|
621
|
-
};
|
|
622
|
-
setError(sseError);
|
|
623
|
-
updateMetrics({ lastError: sseError });
|
|
624
|
-
setConnectionState("disconnected");
|
|
625
|
-
emit("disconnected", { reason: "SSE error" });
|
|
626
|
-
if (!intentionalDisconnectRef.current && mountedRef.current) {
|
|
627
|
-
const retryInterval = calculateRetryInterval(retryCountRef.current);
|
|
628
|
-
retryCountRef.current++;
|
|
629
|
-
logger.info(`SSE reconnecting in ${retryInterval}ms (attempt ${retryCountRef.current})`);
|
|
630
|
-
if (reconnectTimeoutRef.current) {
|
|
631
|
-
clearTimeout(reconnectTimeoutRef.current);
|
|
632
|
-
}
|
|
633
|
-
reconnectTimeoutRef.current = setTimeout(() => {
|
|
634
|
-
connect(userId);
|
|
635
|
-
}, retryInterval);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
};
|
|
639
|
-
sseRef.current = eventSource;
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
const wsUrl = `${serverBaseUrl}?orgId=${orgId}&userId=${userId}&clientType=${clientType}&product=${product}`;
|
|
643
|
-
logger.debug("Connecting to WebSocket:", wsUrl);
|
|
644
|
-
console.log(`\u23F3 Initiating WebSocket connection to ${serverBaseUrl}...`);
|
|
645
|
-
const ws = new WebSocket(wsUrl);
|
|
646
|
-
ws.onopen = () => {
|
|
647
|
-
if (!mountedRef.current) {
|
|
648
|
-
ws.close(1e3, "Component unmounted");
|
|
649
|
-
reject(new Error("Component unmounted"));
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
const connectionTimeMs = Date.now() - connectionStartTime;
|
|
653
|
-
const connectionTimeSec = (connectionTimeMs / 1e3).toFixed(2);
|
|
654
|
-
logger.info("\u2705 WebSocket connected", {
|
|
655
|
-
userId,
|
|
656
|
-
retryCount: retryCountRef.current,
|
|
657
|
-
connectionTime: `${connectionTimeSec}s (${connectionTimeMs}ms)`
|
|
658
|
-
});
|
|
659
|
-
console.log(`\u{1F50C} WebSocket connection established in ${connectionTimeSec} seconds`);
|
|
660
|
-
setConnectionState("connected");
|
|
661
|
-
setError(void 0);
|
|
662
|
-
const wasReconnecting = retryCountRef.current > 0;
|
|
663
|
-
retryCountRef.current = 0;
|
|
664
|
-
updateMetrics({
|
|
665
|
-
connectedAt: Date.now(),
|
|
666
|
-
latency: connectionTimeMs,
|
|
667
|
-
reconnectCount: wasReconnecting ? metrics.reconnectCount + 1 : metrics.reconnectCount
|
|
668
|
-
});
|
|
669
|
-
while (messageQueueRef.current.length > 0 && ws.readyState === WebSocket.OPEN) {
|
|
670
|
-
const event = messageQueueRef.current.shift();
|
|
671
|
-
if (event) {
|
|
672
|
-
ws.send(JSON.stringify({ ...event, timestamp: Date.now() }));
|
|
673
|
-
updateMetrics({
|
|
674
|
-
messagesSent: metrics.messagesSent + 1,
|
|
675
|
-
messagesQueued: messageQueueRef.current.length
|
|
676
|
-
});
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
currentUserIdRef.current = userId;
|
|
680
|
-
if (currentChatKeyRef.current) {
|
|
681
|
-
if (!orgId) {
|
|
682
|
-
logger.error("Cannot resubscribe to chat: orgId is undefined");
|
|
683
|
-
} else {
|
|
684
|
-
logger.info(
|
|
685
|
-
"Resubscribing to chat after reconnection:",
|
|
686
|
-
currentChatKeyRef.current
|
|
687
|
-
);
|
|
688
|
-
const resubscribeEvent = {
|
|
689
|
-
type: "load_chat",
|
|
690
|
-
orgId,
|
|
691
|
-
chatKey: currentChatKeyRef.current,
|
|
692
|
-
userId,
|
|
693
|
-
timestamp: Date.now(),
|
|
694
|
-
data: {}
|
|
695
|
-
};
|
|
696
|
-
ws.send(JSON.stringify(resubscribeEvent));
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
startHeartbeat();
|
|
700
|
-
emit("connected", { userId, wasReconnecting });
|
|
701
|
-
resolve();
|
|
702
|
-
};
|
|
703
|
-
ws.onmessage = (event) => {
|
|
704
|
-
if (!mountedRef.current) return;
|
|
705
|
-
try {
|
|
706
|
-
const data = JSON.parse(event.data);
|
|
707
|
-
if (!isChatEvent(data)) {
|
|
708
|
-
logger.warn("Received invalid message format:", data);
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
const chatEvent = data;
|
|
712
|
-
logger.debug("Message received:", chatEvent.type);
|
|
713
|
-
updateMetrics({
|
|
714
|
-
messagesReceived: metrics.messagesReceived + 1,
|
|
715
|
-
lastMessageAt: Date.now()
|
|
716
|
-
});
|
|
717
|
-
if (chatEvent.type === "pong") {
|
|
718
|
-
if (heartbeatTimeoutRef.current) {
|
|
719
|
-
clearTimeout(heartbeatTimeoutRef.current);
|
|
720
|
-
heartbeatTimeoutRef.current = void 0;
|
|
721
|
-
}
|
|
722
|
-
const latency = Date.now() - (chatEvent.timestamp || Date.now());
|
|
723
|
-
lastPongRef.current = Date.now();
|
|
724
|
-
updateMetrics({ latency });
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
switch (chatEvent.type) {
|
|
728
|
-
case "new_chat_created":
|
|
729
|
-
const newChatKey = chatEvent.data?.chatKey;
|
|
730
|
-
if (newChatKey) {
|
|
731
|
-
logger.info("New chat created with key:", newChatKey);
|
|
732
|
-
currentChatKeyRef.current = newChatKey;
|
|
733
|
-
if (chatCreationPromiseRef.current) {
|
|
734
|
-
chatCreationPromiseRef.current.resolve(newChatKey);
|
|
735
|
-
chatCreationPromiseRef.current = null;
|
|
736
|
-
}
|
|
737
|
-
if (!orgId) {
|
|
738
|
-
logger.error("Cannot send load_chat: orgId is undefined");
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
const loadChatEvent = {
|
|
742
|
-
type: "load_chat",
|
|
743
|
-
orgId,
|
|
744
|
-
chatKey: newChatKey,
|
|
745
|
-
userId: currentUserIdRef.current || userId,
|
|
746
|
-
timestamp: Date.now(),
|
|
747
|
-
data: {}
|
|
748
|
-
};
|
|
749
|
-
ws.send(JSON.stringify(loadChatEvent));
|
|
750
|
-
}
|
|
751
|
-
break;
|
|
752
|
-
case "load_chat_response":
|
|
753
|
-
const chat = chatEvent.data?.chat;
|
|
754
|
-
if (chat && chat.key) {
|
|
755
|
-
logger.info("Chat loaded with key:", chat.key);
|
|
756
|
-
currentChatKeyRef.current = chat.key;
|
|
757
|
-
const retryState = loadChatRetryMapRef.current.get(chat.key);
|
|
758
|
-
if (retryState) {
|
|
759
|
-
if (retryState.timeoutId) {
|
|
760
|
-
clearTimeout(retryState.timeoutId);
|
|
761
|
-
}
|
|
762
|
-
loadChatRetryMapRef.current.delete(chat.key);
|
|
763
|
-
}
|
|
764
|
-
} else if (!chat) {
|
|
765
|
-
logger.warn("Chat load failed, clearing key");
|
|
766
|
-
currentChatKeyRef.current = void 0;
|
|
767
|
-
}
|
|
768
|
-
break;
|
|
769
|
-
case "chat_ended":
|
|
770
|
-
logger.info("Chat ended, clearing key");
|
|
771
|
-
currentChatKeyRef.current = void 0;
|
|
772
|
-
break;
|
|
773
|
-
case "error":
|
|
774
|
-
const errorMessage = chatEvent.data?.message || "Unknown error";
|
|
775
|
-
logger.error("Received error from server:", errorMessage);
|
|
776
|
-
if (errorMessage.includes("nats: key not found") || errorMessage.includes("Failed to load chat")) {
|
|
777
|
-
const chatKeyFromError = currentChatKeyRef.current;
|
|
778
|
-
if (chatKeyFromError) {
|
|
779
|
-
const maxRetries2 = 5;
|
|
780
|
-
let retryState = loadChatRetryMapRef.current.get(chatKeyFromError);
|
|
781
|
-
if (!retryState) {
|
|
782
|
-
retryState = { retryCount: 0, timeoutId: null };
|
|
783
|
-
loadChatRetryMapRef.current.set(chatKeyFromError, retryState);
|
|
784
|
-
}
|
|
785
|
-
if (retryState.retryCount < maxRetries2) {
|
|
786
|
-
const delay = 200 * Math.pow(2, retryState.retryCount);
|
|
787
|
-
retryState.retryCount++;
|
|
788
|
-
logger.info(
|
|
789
|
-
`Chat load failed, retrying (${retryState.retryCount}/${maxRetries2}) in ${delay}ms...`,
|
|
790
|
-
chatKeyFromError
|
|
791
|
-
);
|
|
792
|
-
retryState.timeoutId = setTimeout(() => {
|
|
793
|
-
if (!wsRef.current || !mountedRef.current) return;
|
|
794
|
-
if (!orgId) {
|
|
795
|
-
logger.error("Cannot retry load_chat: orgId is undefined", chatKeyFromError);
|
|
796
|
-
loadChatRetryMapRef.current.delete(chatKeyFromError);
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
logger.debug("Retrying load_chat:", chatKeyFromError);
|
|
800
|
-
const retryLoadEvent = {
|
|
801
|
-
type: "load_chat",
|
|
802
|
-
orgId,
|
|
803
|
-
chatKey: chatKeyFromError,
|
|
804
|
-
userId: currentUserIdRef.current || "",
|
|
805
|
-
timestamp: Date.now(),
|
|
806
|
-
data: {}
|
|
807
|
-
};
|
|
808
|
-
ws.send(JSON.stringify(retryLoadEvent));
|
|
809
|
-
}, delay);
|
|
810
|
-
} else {
|
|
811
|
-
logger.error("Max retries reached for loading chat:", chatKeyFromError);
|
|
812
|
-
loadChatRetryMapRef.current.delete(chatKeyFromError);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
const wsError = {
|
|
817
|
-
code: "NETWORK_ERROR",
|
|
818
|
-
message: errorMessage,
|
|
819
|
-
retryable: true,
|
|
820
|
-
timestamp: Date.now()
|
|
821
|
-
};
|
|
822
|
-
setError(wsError);
|
|
823
|
-
updateMetrics({ lastError: wsError });
|
|
824
|
-
break;
|
|
825
|
-
}
|
|
826
|
-
emit(chatEvent.type || "message", chatEvent);
|
|
827
|
-
if (onMessageRef.current) {
|
|
828
|
-
onMessageRef.current(chatEvent);
|
|
829
|
-
}
|
|
830
|
-
} catch (error2) {
|
|
831
|
-
logger.error("Failed to parse WebSocket message:", error2);
|
|
832
|
-
const parseError = {
|
|
833
|
-
code: "PARSE_ERROR",
|
|
834
|
-
message: "Failed to parse message",
|
|
835
|
-
retryable: false,
|
|
836
|
-
timestamp: Date.now()
|
|
837
|
-
};
|
|
838
|
-
setError(parseError);
|
|
839
|
-
updateMetrics({ lastError: parseError });
|
|
840
|
-
}
|
|
841
|
-
};
|
|
842
|
-
ws.onclose = (event) => {
|
|
843
|
-
if (!mountedRef.current) return;
|
|
844
|
-
logger.info("WebSocket closed", {
|
|
845
|
-
code: event.code,
|
|
846
|
-
reason: event.reason
|
|
847
|
-
});
|
|
848
|
-
setConnectionState("disconnected");
|
|
849
|
-
wsRef.current = void 0;
|
|
850
|
-
stopHeartbeat();
|
|
851
|
-
emit("disconnected", { code: event.code, reason: event.reason });
|
|
852
|
-
if (event.code !== 1e3 && !intentionalDisconnectRef.current && mountedRef.current) {
|
|
853
|
-
const retryInterval = calculateRetryInterval(
|
|
854
|
-
retryCountRef.current
|
|
855
|
-
);
|
|
856
|
-
retryCountRef.current++;
|
|
857
|
-
logger.info(
|
|
858
|
-
`Reconnecting in ${retryInterval}ms (attempt ${retryCountRef.current})`
|
|
859
|
-
);
|
|
860
|
-
if (reconnectTimeoutRef.current) {
|
|
861
|
-
clearTimeout(reconnectTimeoutRef.current);
|
|
862
|
-
}
|
|
863
|
-
reconnectTimeoutRef.current = setTimeout(() => {
|
|
864
|
-
connect(userId);
|
|
865
|
-
}, retryInterval);
|
|
866
|
-
}
|
|
867
|
-
};
|
|
868
|
-
ws.onerror = (error2) => {
|
|
869
|
-
logger.error("WebSocket error:", error2);
|
|
870
|
-
if (!mountedRef.current) return;
|
|
871
|
-
const wsError = {
|
|
872
|
-
code: "CONNECTION_FAILED",
|
|
873
|
-
message: "WebSocket connection failed",
|
|
874
|
-
retryable: true,
|
|
875
|
-
timestamp: Date.now()
|
|
876
|
-
};
|
|
877
|
-
setError(wsError);
|
|
878
|
-
updateMetrics({ lastError: wsError });
|
|
879
|
-
reject(wsError);
|
|
880
|
-
};
|
|
881
|
-
wsRef.current = ws;
|
|
882
|
-
} catch (error2) {
|
|
883
|
-
logger.error("Failed to create WebSocket:", error2);
|
|
884
|
-
const wsError = {
|
|
885
|
-
code: "CONNECTION_FAILED",
|
|
886
|
-
message: error2 instanceof Error ? error2.message : "Failed to create connection",
|
|
887
|
-
retryable: true,
|
|
888
|
-
timestamp: Date.now()
|
|
889
|
-
};
|
|
890
|
-
setError(wsError);
|
|
891
|
-
updateMetrics({ lastError: wsError });
|
|
892
|
-
reject(wsError);
|
|
893
|
-
}
|
|
894
|
-
});
|
|
895
|
-
},
|
|
896
|
-
[
|
|
897
|
-
serverBaseUrl,
|
|
898
|
-
orgId,
|
|
899
|
-
clientType,
|
|
900
|
-
product,
|
|
901
|
-
connectionState,
|
|
902
|
-
logger,
|
|
903
|
-
retryConfig,
|
|
904
|
-
metrics,
|
|
905
|
-
updateMetrics,
|
|
906
|
-
cleanup,
|
|
907
|
-
calculateRetryInterval,
|
|
908
|
-
startHeartbeat,
|
|
909
|
-
emit
|
|
910
|
-
]
|
|
911
|
-
);
|
|
912
|
-
const sendMessage = useCallback(
|
|
913
|
-
(event, overrideUserId) => {
|
|
914
|
-
return new Promise(async (resolve, reject) => {
|
|
915
|
-
if (!mountedRef.current) {
|
|
916
|
-
reject(new Error("Component not mounted"));
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
const fullEvent = {
|
|
920
|
-
...event,
|
|
921
|
-
timestamp: Date.now()
|
|
922
|
-
};
|
|
923
|
-
logger.debug("Sending message:", fullEvent.type);
|
|
924
|
-
if (transportRef.current === "sse") {
|
|
925
|
-
if (!sseRef.current || sseRef.current.readyState !== EventSource.OPEN) {
|
|
926
|
-
logger.debug("SSE not connected, attempting to connect");
|
|
927
|
-
if (connectionState === "disconnected" && overrideUserId) {
|
|
928
|
-
try {
|
|
929
|
-
await connect(overrideUserId);
|
|
930
|
-
} catch (error2) {
|
|
931
|
-
reject(error2);
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
} else {
|
|
935
|
-
reject(new Error("SSE not connected"));
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
try {
|
|
940
|
-
switch (fullEvent.type) {
|
|
941
|
-
case "message":
|
|
942
|
-
await sendRestMessage("send", {
|
|
943
|
-
orgId: fullEvent.orgId || orgId,
|
|
944
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
945
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
946
|
-
message: fullEvent.message
|
|
947
|
-
});
|
|
948
|
-
break;
|
|
949
|
-
case "typing":
|
|
950
|
-
await sendRestMessage("typing", {
|
|
951
|
-
orgId: fullEvent.orgId || orgId,
|
|
952
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
953
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
954
|
-
typing: true
|
|
955
|
-
});
|
|
956
|
-
break;
|
|
957
|
-
case "stopped_typing":
|
|
958
|
-
await sendRestMessage("typing", {
|
|
959
|
-
orgId: fullEvent.orgId || orgId,
|
|
960
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
961
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
962
|
-
typing: false
|
|
963
|
-
});
|
|
964
|
-
break;
|
|
965
|
-
case "load_chat":
|
|
966
|
-
const loadResponse = await sendRestMessage("load", {
|
|
967
|
-
orgId: fullEvent.orgId || orgId,
|
|
968
|
-
chatKey: fullEvent.chatKey,
|
|
969
|
-
userId: fullEvent.userId || currentUserIdRef.current
|
|
970
|
-
});
|
|
971
|
-
if (loadResponse.success && loadResponse.data?.chat) {
|
|
972
|
-
currentChatKeyRef.current = loadResponse.data.chat.key;
|
|
973
|
-
const chatEvent = {
|
|
974
|
-
type: "load_chat_response",
|
|
975
|
-
orgId: fullEvent.orgId,
|
|
976
|
-
chatKey: loadResponse.data.chat.key,
|
|
977
|
-
userId: fullEvent.userId,
|
|
978
|
-
timestamp: Date.now(),
|
|
979
|
-
data: loadResponse.data
|
|
980
|
-
};
|
|
981
|
-
emit("load_chat_response", chatEvent);
|
|
982
|
-
if (onMessageRef.current) {
|
|
983
|
-
onMessageRef.current(chatEvent);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
break;
|
|
987
|
-
case "new_chat":
|
|
988
|
-
const createResponse = await sendRestMessage("create", {
|
|
989
|
-
orgId: fullEvent.orgId || orgId,
|
|
990
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
991
|
-
metadata: fullEvent.data
|
|
992
|
-
});
|
|
993
|
-
if (createResponse.success && createResponse.data?.chatKey) {
|
|
994
|
-
currentChatKeyRef.current = createResponse.data.chatKey;
|
|
995
|
-
const newChatEvent = {
|
|
996
|
-
type: "new_chat_created",
|
|
997
|
-
orgId: fullEvent.orgId,
|
|
998
|
-
chatKey: createResponse.data.chatKey,
|
|
999
|
-
userId: fullEvent.userId,
|
|
1000
|
-
timestamp: Date.now(),
|
|
1001
|
-
data: { chatKey: createResponse.data.chatKey }
|
|
1002
|
-
};
|
|
1003
|
-
emit("new_chat_created", newChatEvent);
|
|
1004
|
-
if (onMessageRef.current) {
|
|
1005
|
-
onMessageRef.current(newChatEvent);
|
|
1006
|
-
}
|
|
1007
|
-
if (chatCreationPromiseRef.current) {
|
|
1008
|
-
chatCreationPromiseRef.current.resolve(createResponse.data.chatKey);
|
|
1009
|
-
chatCreationPromiseRef.current = null;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
break;
|
|
1013
|
-
case "end_chat":
|
|
1014
|
-
await sendRestMessage("end", {
|
|
1015
|
-
orgId: fullEvent.orgId || orgId,
|
|
1016
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1017
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1018
|
-
data: fullEvent.data
|
|
1019
|
-
});
|
|
1020
|
-
break;
|
|
1021
|
-
case "human_agent_joined":
|
|
1022
|
-
await sendRestMessage("agent-join", {
|
|
1023
|
-
orgId: fullEvent.orgId || orgId,
|
|
1024
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1025
|
-
user: fullEvent.data?.user
|
|
1026
|
-
});
|
|
1027
|
-
break;
|
|
1028
|
-
case "human_agent_left":
|
|
1029
|
-
await sendRestMessage("agent-leave", {
|
|
1030
|
-
orgId: fullEvent.orgId || orgId,
|
|
1031
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1032
|
-
user: fullEvent.data?.user
|
|
1033
|
-
});
|
|
1034
|
-
break;
|
|
1035
|
-
// Event types that use the generic /event endpoint
|
|
1036
|
-
case "load_agent_context":
|
|
1037
|
-
case "skill_activate":
|
|
1038
|
-
case "skill_deactivate":
|
|
1039
|
-
case "sync_metadata":
|
|
1040
|
-
case "sync_user_session":
|
|
1041
|
-
case "csat_response":
|
|
1042
|
-
case "plan_approved":
|
|
1043
|
-
case "plan_rejected":
|
|
1044
|
-
await sendRestMessage("event", {
|
|
1045
|
-
type: fullEvent.type,
|
|
1046
|
-
orgId: fullEvent.orgId || orgId,
|
|
1047
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1048
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1049
|
-
data: fullEvent.data
|
|
1050
|
-
});
|
|
1051
|
-
break;
|
|
1052
|
-
default:
|
|
1053
|
-
logger.warn("Sending unrecognized event type via generic endpoint:", fullEvent.type);
|
|
1054
|
-
await sendRestMessage("event", {
|
|
1055
|
-
type: fullEvent.type,
|
|
1056
|
-
orgId: fullEvent.orgId || orgId,
|
|
1057
|
-
chatKey: fullEvent.chatKey || currentChatKeyRef.current,
|
|
1058
|
-
userId: fullEvent.userId || currentUserIdRef.current,
|
|
1059
|
-
data: fullEvent.data
|
|
1060
|
-
});
|
|
1061
|
-
break;
|
|
1062
|
-
}
|
|
1063
|
-
updateMetrics({ messagesSent: metrics.messagesSent + 1 });
|
|
1064
|
-
logger.debug("SSE REST message sent successfully");
|
|
1065
|
-
resolve();
|
|
1066
|
-
} catch (error2) {
|
|
1067
|
-
logger.error("Failed to send SSE REST message:", error2);
|
|
1068
|
-
const sendError = {
|
|
1069
|
-
code: "SEND_FAILED",
|
|
1070
|
-
message: error2 instanceof Error ? error2.message : "Failed to send message",
|
|
1071
|
-
retryable: true,
|
|
1072
|
-
timestamp: Date.now()
|
|
1073
|
-
};
|
|
1074
|
-
setError(sendError);
|
|
1075
|
-
updateMetrics({ lastError: sendError });
|
|
1076
|
-
reject(sendError);
|
|
1077
|
-
}
|
|
1078
|
-
return;
|
|
1079
|
-
}
|
|
1080
|
-
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
|
|
1081
|
-
if (addToQueue(fullEvent)) {
|
|
1082
|
-
logger.debug("Message queued, attempting to connect");
|
|
1083
|
-
if (connectionState === "disconnected" && overrideUserId) {
|
|
1084
|
-
connect(overrideUserId).then(() => resolve()).catch(reject);
|
|
1085
|
-
} else {
|
|
1086
|
-
resolve();
|
|
1087
|
-
}
|
|
1088
|
-
} else {
|
|
1089
|
-
reject(new Error("Message queue full"));
|
|
1090
|
-
}
|
|
1091
|
-
return;
|
|
1092
|
-
}
|
|
1093
|
-
try {
|
|
1094
|
-
wsRef.current.send(JSON.stringify(fullEvent));
|
|
1095
|
-
updateMetrics({ messagesSent: metrics.messagesSent + 1 });
|
|
1096
|
-
logger.debug("Message sent successfully");
|
|
1097
|
-
resolve();
|
|
1098
|
-
} catch (error2) {
|
|
1099
|
-
logger.error("Failed to send message:", error2);
|
|
1100
|
-
if (addToQueue(fullEvent)) {
|
|
1101
|
-
resolve();
|
|
1102
|
-
} else {
|
|
1103
|
-
const sendError = {
|
|
1104
|
-
code: "SEND_FAILED",
|
|
1105
|
-
message: error2 instanceof Error ? error2.message : "Failed to send message",
|
|
1106
|
-
retryable: true,
|
|
1107
|
-
timestamp: Date.now()
|
|
1108
|
-
};
|
|
1109
|
-
setError(sendError);
|
|
1110
|
-
updateMetrics({ lastError: sendError });
|
|
1111
|
-
reject(sendError);
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
});
|
|
1115
|
-
},
|
|
1116
|
-
[connectionState, connect, addToQueue, logger, metrics, updateMetrics, sendRestMessage, emit, orgId]
|
|
1117
|
-
);
|
|
1118
|
-
const startNewChat = useCallback(
|
|
1119
|
-
(userId, data) => {
|
|
1120
|
-
return new Promise((resolve, reject) => {
|
|
1121
|
-
if (!userId) {
|
|
1122
|
-
reject(new Error("User ID is required"));
|
|
1123
|
-
return;
|
|
1124
|
-
}
|
|
1125
|
-
logger.info("Requesting new chat from server with userId:", userId);
|
|
1126
|
-
setStartTime(/* @__PURE__ */ new Date());
|
|
1127
|
-
chatCreationPromiseRef.current = { resolve, reject };
|
|
1128
|
-
sendMessage(
|
|
1129
|
-
{
|
|
1130
|
-
type: "new_chat",
|
|
1131
|
-
orgId,
|
|
1132
|
-
chatKey: "",
|
|
1133
|
-
// Server will generate
|
|
1134
|
-
userId,
|
|
1135
|
-
data: data ?? {}
|
|
1136
|
-
},
|
|
1137
|
-
userId
|
|
1138
|
-
).catch((error2) => {
|
|
1139
|
-
chatCreationPromiseRef.current = null;
|
|
1140
|
-
reject(error2);
|
|
1141
|
-
});
|
|
1142
|
-
setTimeout(() => {
|
|
1143
|
-
if (chatCreationPromiseRef.current) {
|
|
1144
|
-
chatCreationPromiseRef.current = null;
|
|
1145
|
-
reject(new Error("Chat creation timed out"));
|
|
1146
|
-
}
|
|
1147
|
-
}, 3e4);
|
|
1148
|
-
});
|
|
1149
|
-
},
|
|
1150
|
-
[sendMessage, orgId, logger]
|
|
1151
|
-
);
|
|
1152
|
-
const disconnect = useCallback(
|
|
1153
|
-
(intentional = true) => {
|
|
1154
|
-
logger.info("Disconnecting", { intentional, transport: transportRef.current });
|
|
1155
|
-
intentionalDisconnectRef.current = intentional;
|
|
1156
|
-
cleanup();
|
|
1157
|
-
setConnectionState("disconnected");
|
|
1158
|
-
messageQueueRef.current = [];
|
|
1159
|
-
retryCountRef.current = 0;
|
|
1160
|
-
mountedRef.current = false;
|
|
1161
|
-
currentChatKeyRef.current = void 0;
|
|
1162
|
-
currentUserIdRef.current = void 0;
|
|
1163
|
-
},
|
|
1164
|
-
[cleanup, logger]
|
|
1165
|
-
);
|
|
1166
|
-
const clearError = useCallback(() => {
|
|
1167
|
-
setError(void 0);
|
|
1168
|
-
}, []);
|
|
1169
|
-
useEffect(() => {
|
|
1170
|
-
mountedRef.current = true;
|
|
1171
|
-
return () => {
|
|
1172
|
-
mountedRef.current = false;
|
|
1173
|
-
disconnect(true);
|
|
1174
|
-
};
|
|
1175
|
-
}, []);
|
|
1176
|
-
return {
|
|
1177
|
-
connectionState,
|
|
1178
|
-
isConnected,
|
|
1179
|
-
sendMessage,
|
|
1180
|
-
error,
|
|
1181
|
-
connect,
|
|
1182
|
-
startNewChat,
|
|
1183
|
-
startTime,
|
|
1184
|
-
disconnect,
|
|
1185
|
-
metrics,
|
|
1186
|
-
on,
|
|
1187
|
-
off,
|
|
1188
|
-
clearError
|
|
1189
|
-
};
|
|
1190
|
-
};
|
|
1191
|
-
|
|
1192
|
-
// hooks/use-websocket-chat-admin.ts
|
|
1193
|
-
var useWebSocketChatAdmin = ({
|
|
1194
|
-
serverBaseUrl,
|
|
1195
|
-
orgId,
|
|
1196
|
-
product
|
|
1197
|
-
}) => {
|
|
1198
|
-
const [chats, setChats] = useState2(/* @__PURE__ */ new Map());
|
|
1199
|
-
const [selectedChat, setSelectedChat] = useState2(void 0);
|
|
1200
|
-
const handleMessage = useCallback2(
|
|
1201
|
-
(chatEvent) => {
|
|
1202
|
-
switch (chatEvent.type) {
|
|
1203
|
-
case "message":
|
|
1204
|
-
if (!selectedChat || !chatEvent.message) return;
|
|
1205
|
-
setChats((prev) => {
|
|
1206
|
-
const newMap = new Map(prev);
|
|
1207
|
-
newMap.set(selectedChat.key, {
|
|
1208
|
-
...selectedChat,
|
|
1209
|
-
messages: [...selectedChat.messages, chatEvent.message]
|
|
1210
|
-
});
|
|
1211
|
-
return newMap;
|
|
1212
|
-
});
|
|
1213
|
-
setSelectedChat((prev) => {
|
|
1214
|
-
if (!prev) return prev;
|
|
1215
|
-
return {
|
|
1216
|
-
...prev,
|
|
1217
|
-
messages: [...prev.messages, chatEvent.message]
|
|
1218
|
-
};
|
|
1219
|
-
});
|
|
1220
|
-
break;
|
|
1221
|
-
case "list_chats":
|
|
1222
|
-
const chatList = chatEvent.data?.chats;
|
|
1223
|
-
if (!chatList) return;
|
|
1224
|
-
setChats(new Map(chatList.map((chat) => [chat.key, chat])));
|
|
1225
|
-
break;
|
|
1226
|
-
case "chat_updated":
|
|
1227
|
-
const updatedChat = chatEvent.data?.chat?.value;
|
|
1228
|
-
if (updatedChat) {
|
|
1229
|
-
setChats((prev) => new Map(prev).set(updatedChat.key, updatedChat));
|
|
1230
|
-
}
|
|
1231
|
-
break;
|
|
1232
|
-
case "chat_removed":
|
|
1233
|
-
const chatKey = chatEvent.data?.chatKey?.value;
|
|
1234
|
-
if (chatKey) {
|
|
1235
|
-
setChats((prev) => {
|
|
1236
|
-
const newMap = new Map(prev);
|
|
1237
|
-
newMap.delete(chatKey);
|
|
1238
|
-
return newMap;
|
|
1239
|
-
});
|
|
1240
|
-
}
|
|
1241
|
-
break;
|
|
1242
|
-
case "load_chat":
|
|
1243
|
-
const history = chatEvent.data?.chat?.value;
|
|
1244
|
-
if (history) {
|
|
1245
|
-
setChats((prev) => {
|
|
1246
|
-
const existingChat = prev.get(history.key);
|
|
1247
|
-
if (!existingChat) return prev;
|
|
1248
|
-
return new Map(prev).set(history.key, {
|
|
1249
|
-
...existingChat,
|
|
1250
|
-
messages: history.messages
|
|
1251
|
-
});
|
|
1252
|
-
});
|
|
1253
|
-
}
|
|
1254
|
-
break;
|
|
1255
|
-
}
|
|
1256
|
-
},
|
|
1257
|
-
[selectedChat]
|
|
1258
|
-
);
|
|
1259
|
-
const base = useWebSocketChatBase({
|
|
1260
|
-
serverBaseUrl,
|
|
1261
|
-
orgId,
|
|
1262
|
-
clientType: "humanAgent",
|
|
1263
|
-
onMessage: handleMessage,
|
|
1264
|
-
product
|
|
1265
|
-
});
|
|
1266
|
-
const selectChat = useCallback2(
|
|
1267
|
-
(chatKey) => {
|
|
1268
|
-
const chat = chats.get(chatKey);
|
|
1269
|
-
if (!chat) {
|
|
1270
|
-
console.error("Unable to select chat", chatKey);
|
|
1271
|
-
return;
|
|
1272
|
-
}
|
|
1273
|
-
setSelectedChat(chat);
|
|
1274
|
-
},
|
|
1275
|
-
[chats, orgId, base.sendMessage]
|
|
1276
|
-
);
|
|
1277
|
-
const addUserToChat = useCallback2(
|
|
1278
|
-
(orgId2, chatKey, user) => {
|
|
1279
|
-
base.sendMessage({
|
|
1280
|
-
type: "human_agent_joined",
|
|
1281
|
-
orgId: orgId2,
|
|
1282
|
-
chatKey,
|
|
1283
|
-
userId: user.email,
|
|
1284
|
-
data: { user }
|
|
1285
|
-
});
|
|
1286
|
-
setChats((prev) => {
|
|
1287
|
-
const newMap = new Map(prev);
|
|
1288
|
-
const chat = newMap.get(chatKey);
|
|
1289
|
-
if (!chat) return prev;
|
|
1290
|
-
newMap.set(chatKey, {
|
|
1291
|
-
...chat,
|
|
1292
|
-
humanAgentEngaged: user.role === ChatRoleHumanAgent,
|
|
1293
|
-
users: [...chat?.users ?? [], user]
|
|
1294
|
-
});
|
|
1295
|
-
return newMap;
|
|
1296
|
-
});
|
|
1297
|
-
setSelectedChat((prev) => {
|
|
1298
|
-
if (!prev) return prev;
|
|
1299
|
-
return {
|
|
1300
|
-
...prev,
|
|
1301
|
-
humanAgentEngaged: user.role === ChatRoleHumanAgent,
|
|
1302
|
-
users: [...prev.users ?? [], user]
|
|
1303
|
-
};
|
|
1304
|
-
});
|
|
1305
|
-
},
|
|
1306
|
-
[base.sendMessage]
|
|
1307
|
-
);
|
|
1308
|
-
const removeUserFromChat = useCallback2(
|
|
1309
|
-
(orgId2, chatKey, userId) => {
|
|
1310
|
-
const chat = chats.get(chatKey);
|
|
1311
|
-
if (!chat) return;
|
|
1312
|
-
const user = chat.users?.find((u) => u.id === userId);
|
|
1313
|
-
if (!user) return;
|
|
1314
|
-
base.sendMessage({
|
|
1315
|
-
type: "human_agent_left",
|
|
1316
|
-
orgId: orgId2,
|
|
1317
|
-
chatKey,
|
|
1318
|
-
userId,
|
|
1319
|
-
data: { user }
|
|
1320
|
-
});
|
|
1321
|
-
setChats((prev) => {
|
|
1322
|
-
const newMap = new Map(prev);
|
|
1323
|
-
const chat2 = newMap.get(chatKey);
|
|
1324
|
-
if (!chat2) return prev;
|
|
1325
|
-
newMap.set(chatKey, {
|
|
1326
|
-
...chat2,
|
|
1327
|
-
humanAgentEngaged: false,
|
|
1328
|
-
users: chat2.users?.filter((u) => u.id !== userId)
|
|
1329
|
-
});
|
|
1330
|
-
return newMap;
|
|
1331
|
-
});
|
|
1332
|
-
},
|
|
1333
|
-
[chats, base.sendMessage]
|
|
1334
|
-
);
|
|
1335
|
-
const blockUser = useCallback2(
|
|
1336
|
-
(orgId2, chatKey, userId) => {
|
|
1337
|
-
base.sendMessage({
|
|
1338
|
-
type: "block_user",
|
|
1339
|
-
orgId: orgId2,
|
|
1340
|
-
chatKey,
|
|
1341
|
-
userId
|
|
1342
|
-
});
|
|
1343
|
-
},
|
|
1344
|
-
[base.sendMessage]
|
|
1345
|
-
);
|
|
1346
|
-
return {
|
|
1347
|
-
...base,
|
|
1348
|
-
chats,
|
|
1349
|
-
selectedChat,
|
|
1350
|
-
selectChat,
|
|
1351
|
-
addUserToChat,
|
|
1352
|
-
removeUserFromChat,
|
|
1353
|
-
blockUser
|
|
1354
|
-
};
|
|
1355
|
-
};
|
|
1356
|
-
|
|
1357
|
-
// hooks/use-websocket-chat-customer.ts
|
|
1358
|
-
import { useCallback as useCallback3, useState as useState3 } from "react";
|
|
1359
|
-
var useWebSocketChatCustomer = ({
|
|
1360
|
-
serverBaseUrl,
|
|
1361
|
-
orgId,
|
|
1362
|
-
chatKey,
|
|
1363
|
-
product
|
|
1364
|
-
}) => {
|
|
1365
|
-
const [currentChat, setCurrentChat] = useState3(void 0);
|
|
1366
|
-
const handleMessage = useCallback3((chatEvent) => {
|
|
1367
|
-
console.log("Received event:", chatEvent.type);
|
|
1368
|
-
switch (chatEvent.type) {
|
|
1369
|
-
case "message":
|
|
1370
|
-
if (!chatEvent.message) return;
|
|
1371
|
-
console.log(
|
|
1372
|
-
"got message:",
|
|
1373
|
-
chatEvent.message.role,
|
|
1374
|
-
":",
|
|
1375
|
-
chatEvent.message.content
|
|
1376
|
-
);
|
|
1377
|
-
setCurrentChat((prev) => {
|
|
1378
|
-
if (!prev) return prev;
|
|
1379
|
-
return {
|
|
1380
|
-
...prev,
|
|
1381
|
-
messages: [...prev.messages, chatEvent.message]
|
|
1382
|
-
};
|
|
1383
|
-
});
|
|
1384
|
-
break;
|
|
1385
|
-
case "chat_updated":
|
|
1386
|
-
const chat = chatEvent.data?.chat?.value;
|
|
1387
|
-
if (chat) {
|
|
1388
|
-
setCurrentChat(chat);
|
|
1389
|
-
}
|
|
1390
|
-
break;
|
|
1391
|
-
case "load_chat":
|
|
1392
|
-
const history = chatEvent.data?.chat;
|
|
1393
|
-
if (!history) return;
|
|
1394
|
-
setCurrentChat(history);
|
|
1395
|
-
break;
|
|
1396
|
-
default:
|
|
1397
|
-
break;
|
|
1398
|
-
}
|
|
1399
|
-
}, []);
|
|
1400
|
-
const base = useWebSocketChatBase({
|
|
1401
|
-
serverBaseUrl,
|
|
1402
|
-
orgId,
|
|
1403
|
-
clientType: "customer",
|
|
1404
|
-
onMessage: handleMessage,
|
|
1405
|
-
product
|
|
1406
|
-
});
|
|
1407
|
-
return {
|
|
1408
|
-
...base,
|
|
1409
|
-
chatKey,
|
|
1410
|
-
title: currentChat?.title,
|
|
1411
|
-
messages: currentChat?.messages ?? [],
|
|
1412
|
-
users: currentChat?.users ?? [],
|
|
1413
|
-
isWaiting: currentChat?.isWaiting ?? false,
|
|
1414
|
-
isWaitingForAgent: currentChat?.isWaitingForAgent ?? false,
|
|
1415
|
-
aiEngaged: currentChat?.aiEngaged ?? false,
|
|
1416
|
-
humanAgentEngaged: currentChat?.humanAgentEngaged ?? false,
|
|
1417
|
-
metadata: currentChat?.metadata ?? {},
|
|
1418
|
-
status: currentChat?.status
|
|
1419
|
-
};
|
|
1420
|
-
};
|
|
1421
|
-
|
|
1422
|
-
// context/websocket-chat-admin-context.tsx
|
|
1423
|
-
import { jsx } from "react/jsx-runtime";
|
|
1424
|
-
var WebSocketChatAdminContext = createContext(void 0);
|
|
1425
|
-
function WebSocketChatAdminProvider(props) {
|
|
1426
|
-
const { children, serverBaseUrl, orgId, userId, product } = props;
|
|
1427
|
-
const webSocket = useWebSocketChatAdmin({
|
|
1428
|
-
serverBaseUrl,
|
|
1429
|
-
orgId,
|
|
1430
|
-
product
|
|
1431
|
-
});
|
|
1432
|
-
return /* @__PURE__ */ jsx(WebSocketChatAdminContext.Provider, { value: webSocket, children });
|
|
1433
|
-
}
|
|
1434
|
-
var useWebSocketChatAdminContext = () => {
|
|
1435
|
-
const context = useContext(WebSocketChatAdminContext);
|
|
1436
|
-
if (context === void 0) {
|
|
1437
|
-
throw new Error(
|
|
1438
|
-
"useWebSocketChatAdminContext must be used within a WebSocketChatAdminProvider"
|
|
1439
|
-
);
|
|
1440
|
-
}
|
|
1441
|
-
return context;
|
|
1442
|
-
};
|
|
1443
|
-
|
|
1444
|
-
// components/admin/admin-chat-input.tsx
|
|
1445
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1446
|
-
var AdminChatInput = ({
|
|
1447
|
-
userId,
|
|
1448
|
-
orgId,
|
|
1449
|
-
onFileUpload,
|
|
1450
|
-
chatKey
|
|
1451
|
-
}) => {
|
|
1452
|
-
const { sendMessage } = useWebSocketChatAdminContext();
|
|
1453
|
-
const [input, setInput] = useState4("");
|
|
1454
|
-
const [isUploading, setIsUploading] = useState4(false);
|
|
1455
|
-
const [isSending, setIsSending] = useState4(false);
|
|
1456
|
-
const fileInputRef = useRef2(null);
|
|
1457
|
-
const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
1458
|
-
const handleSend = async () => {
|
|
1459
|
-
if (!input.trim() && !isUploading || isSending) return;
|
|
1460
|
-
if (!chatKey || !orgId || !userId) {
|
|
1461
|
-
console.error("chatKey, orgId, or userId is not defined");
|
|
1462
|
-
return;
|
|
1463
|
-
}
|
|
1464
|
-
try {
|
|
1465
|
-
setIsSending(true);
|
|
1466
|
-
sendMessage({
|
|
1467
|
-
type: "message",
|
|
1468
|
-
chatKey,
|
|
1469
|
-
orgId,
|
|
1470
|
-
userId,
|
|
1471
|
-
message: {
|
|
1472
|
-
id: KSUID.randomSync().string,
|
|
1473
|
-
content: input,
|
|
1474
|
-
role: "user",
|
|
1475
|
-
senderId: userId,
|
|
1476
|
-
time: Date.now(),
|
|
1477
|
-
status: "sending",
|
|
1478
|
-
createdAt: Date.now()
|
|
1479
|
-
}
|
|
1480
|
-
});
|
|
1481
|
-
setInput("");
|
|
1482
|
-
} finally {
|
|
1483
|
-
setIsSending(false);
|
|
1484
|
-
}
|
|
1485
|
-
};
|
|
1486
|
-
const handleFileUpload = async (event) => {
|
|
1487
|
-
const files = event.target.files;
|
|
1488
|
-
if (!files || !onFileUpload) return;
|
|
1489
|
-
try {
|
|
1490
|
-
setIsUploading(true);
|
|
1491
|
-
const uploadedUrls = await Promise.all(
|
|
1492
|
-
Array.from(files).map(async (file) => {
|
|
1493
|
-
if (file.size > MAX_FILE_SIZE) {
|
|
1494
|
-
throw new Error(
|
|
1495
|
-
`File ${file.name} is too large. Maximum size is 5MB.`
|
|
1496
|
-
);
|
|
1497
|
-
}
|
|
1498
|
-
const url = await onFileUpload(file);
|
|
1499
|
-
return {
|
|
1500
|
-
type: file.type,
|
|
1501
|
-
url,
|
|
1502
|
-
name: file.name
|
|
1503
|
-
};
|
|
1504
|
-
})
|
|
1505
|
-
);
|
|
1506
|
-
const attachments = uploadedUrls.map((file) => ({
|
|
1507
|
-
type: "image",
|
|
1508
|
-
url: file.url,
|
|
1509
|
-
title: file.name
|
|
1510
|
-
}));
|
|
1511
|
-
if (!chatKey || !orgId || !userId) {
|
|
1512
|
-
console.error("chatKey, orgId, or userId is not defined");
|
|
1513
|
-
return;
|
|
1514
|
-
}
|
|
1515
|
-
} finally {
|
|
1516
|
-
setIsUploading(false);
|
|
1517
|
-
if (fileInputRef.current) {
|
|
1518
|
-
fileInputRef.current.value = "";
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
};
|
|
1522
|
-
return /* @__PURE__ */ jsxs("div", { className: "p-4 border-t bg-white rounded-b-lg", children: [
|
|
1523
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
1524
|
-
/* @__PURE__ */ jsx2(
|
|
1525
|
-
"input",
|
|
1526
|
-
{
|
|
1527
|
-
type: "file",
|
|
1528
|
-
ref: fileInputRef,
|
|
1529
|
-
onChange: handleFileUpload,
|
|
1530
|
-
className: "hidden",
|
|
1531
|
-
multiple: true,
|
|
1532
|
-
accept: "image/*,.pdf,.doc,.docx,.txt"
|
|
1533
|
-
}
|
|
1534
|
-
),
|
|
1535
|
-
/* @__PURE__ */ jsx2(
|
|
1536
|
-
"button",
|
|
1537
|
-
{
|
|
1538
|
-
onClick: () => fileInputRef.current?.click(),
|
|
1539
|
-
className: "p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100 transition-colors",
|
|
1540
|
-
disabled: isUploading,
|
|
1541
|
-
"aria-label": "Attach file",
|
|
1542
|
-
children: isUploading ? /* @__PURE__ */ jsx2("span", { className: "w-5 h-5 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" }) : /* @__PURE__ */ jsx2(Paperclip, { className: "w-5 h-5" })
|
|
1543
|
-
}
|
|
1544
|
-
),
|
|
1545
|
-
/* @__PURE__ */ jsx2("div", { className: "flex-1 relative", children: /* @__PURE__ */ jsx2(
|
|
1546
|
-
"textarea",
|
|
1547
|
-
{
|
|
1548
|
-
value: input,
|
|
1549
|
-
onChange: (e) => setInput(e.target.value),
|
|
1550
|
-
onKeyDown: (e) => {
|
|
1551
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
1552
|
-
e.preventDefault();
|
|
1553
|
-
handleSend();
|
|
1554
|
-
}
|
|
1555
|
-
},
|
|
1556
|
-
className: "w-full p-3 border rounded-2xl focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none",
|
|
1557
|
-
placeholder: "Type a message...",
|
|
1558
|
-
rows: 1,
|
|
1559
|
-
style: {
|
|
1560
|
-
minHeight: "44px",
|
|
1561
|
-
maxHeight: "200px"
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
) }),
|
|
1565
|
-
/* @__PURE__ */ jsx2(
|
|
1566
|
-
"button",
|
|
1567
|
-
{
|
|
1568
|
-
onClick: handleSend,
|
|
1569
|
-
disabled: !input.trim() && !isUploading || isSending,
|
|
1570
|
-
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"}`,
|
|
1571
|
-
"aria-label": "Send message",
|
|
1572
|
-
children: isSending ? /* @__PURE__ */ jsx2("span", { className: "w-5 h-5 animate-spin rounded-full border-2 border-gray-300 border-t-white" }) : /* @__PURE__ */ jsx2(Send, { className: "w-5 h-5" })
|
|
1573
|
-
}
|
|
1574
|
-
)
|
|
1575
|
-
] }),
|
|
1576
|
-
isUploading && /* @__PURE__ */ jsx2("div", { className: "mt-2", children: /* @__PURE__ */ jsx2("div", { className: "w-full bg-gray-200 rounded-full h-1", children: /* @__PURE__ */ jsx2("div", { className: "bg-blue-500 h-1 rounded-full animate-pulse" }) }) })
|
|
1577
|
-
] });
|
|
1578
|
-
};
|
|
1579
|
-
|
|
1580
|
-
// components/admin/chat-human-agent-actions.tsx
|
|
1581
|
-
import { LogOut, Phone, Plus, User as UserIcon, XCircle } from "lucide-react";
|
|
1582
|
-
|
|
1583
|
-
// context/websocket-chat-customer-context.tsx
|
|
1584
|
-
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
1585
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1586
|
-
var WebSocketChatCustomerContext = createContext2(void 0);
|
|
1587
|
-
function WebSocketChatCustomerProvider(props) {
|
|
1588
|
-
const { children, serverBaseUrl, orgId, userId, chatKey, product } = props;
|
|
1589
|
-
const webSocket = useWebSocketChatCustomer({
|
|
1590
|
-
serverBaseUrl,
|
|
1591
|
-
orgId,
|
|
1592
|
-
chatKey,
|
|
1593
|
-
product
|
|
1594
|
-
});
|
|
1595
|
-
return /* @__PURE__ */ jsx3(WebSocketChatCustomerContext.Provider, { value: webSocket, children });
|
|
1596
|
-
}
|
|
1597
|
-
var useWebSocketChatCustomerContext = () => {
|
|
1598
|
-
const context = useContext2(WebSocketChatCustomerContext);
|
|
1599
|
-
if (context === void 0) {
|
|
1600
|
-
throw new Error(
|
|
1601
|
-
"useWebSocketChatCustomerContext must be used within a WebSocketChatCustomerProvider"
|
|
1602
|
-
);
|
|
1603
|
-
}
|
|
1604
|
-
return context;
|
|
1605
|
-
};
|
|
1606
|
-
|
|
1607
|
-
// components/admin/chat-human-agent-actions.tsx
|
|
1608
|
-
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1609
|
-
var ChatHumanAgentActions = ({ user, orgId }) => {
|
|
1610
|
-
const { selectedChat, addUserToChat, removeUserFromChat, blockUser } = useWebSocketChatAdminContext();
|
|
1611
|
-
const handleJoinChat = () => {
|
|
1612
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1613
|
-
addUserToChat(orgId, selectedChat.key, {
|
|
1614
|
-
id: user.id,
|
|
1615
|
-
role: "humanAgent",
|
|
1616
|
-
name: [user.firstName, user.lastName].filter(Boolean).join(" ") || user.email,
|
|
1617
|
-
email: user.email,
|
|
1618
|
-
authProvider: "tbd",
|
|
1619
|
-
authToken: "tbd"
|
|
1620
|
-
});
|
|
1621
|
-
};
|
|
1622
|
-
const handleLeaveChat = () => {
|
|
1623
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1624
|
-
removeUserFromChat(orgId, selectedChat.key, user.id);
|
|
1625
|
-
};
|
|
1626
|
-
const handleEndChat = () => {
|
|
1627
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1628
|
-
};
|
|
1629
|
-
const handleStartCall = () => {
|
|
1630
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1631
|
-
};
|
|
1632
|
-
const handleCreateCase = () => {
|
|
1633
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1634
|
-
};
|
|
1635
|
-
const handleBlockUser = () => {
|
|
1636
|
-
if (!selectedChat || !user || !orgId) return;
|
|
1637
|
-
blockUser(orgId, selectedChat.key, user.id);
|
|
1638
|
-
};
|
|
1639
|
-
if (!selectedChat) return null;
|
|
1640
|
-
return /* @__PURE__ */ jsxs2("div", { className: "p-4 border-b bg-white flex items-center justify-between", children: [
|
|
1641
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center space-x-4", children: [
|
|
1642
|
-
/* @__PURE__ */ jsx4("h2", { className: "font-semibold text-lg", children: selectedChat.title || `Chat ${selectedChat.key}` }),
|
|
1643
|
-
selectedChat.humanAgentEngaged && /* @__PURE__ */ jsxs2("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: [
|
|
1644
|
-
/* @__PURE__ */ jsx4(UserIcon, { className: "mr-1 h-4 w-4" }),
|
|
1645
|
-
"Agent Engaged"
|
|
1646
|
-
] })
|
|
1647
|
-
] }),
|
|
1648
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center space-x-2", children: [
|
|
1649
|
-
!selectedChat.humanAgentEngaged ? /* @__PURE__ */ jsxs2(
|
|
1650
|
-
"button",
|
|
1651
|
-
{
|
|
1652
|
-
onClick: handleJoinChat,
|
|
1653
|
-
className: "bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1654
|
-
children: [
|
|
1655
|
-
/* @__PURE__ */ jsx4(UserIcon, { className: "h-4 w-4" }),
|
|
1656
|
-
/* @__PURE__ */ jsx4("span", { children: "Join Chat" })
|
|
1657
|
-
]
|
|
1658
|
-
}
|
|
1659
|
-
) : /* @__PURE__ */ jsxs2(
|
|
1660
|
-
"button",
|
|
1661
|
-
{
|
|
1662
|
-
onClick: handleLeaveChat,
|
|
1663
|
-
className: "bg-orange-500 hover:bg-orange-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1664
|
-
children: [
|
|
1665
|
-
/* @__PURE__ */ jsx4(LogOut, { className: "h-4 w-4" }),
|
|
1666
|
-
/* @__PURE__ */ jsx4("span", { children: "Leave Chat" })
|
|
1667
|
-
]
|
|
1668
|
-
}
|
|
1669
|
-
),
|
|
1670
|
-
/* @__PURE__ */ jsxs2(
|
|
1671
|
-
"button",
|
|
1672
|
-
{
|
|
1673
|
-
onClick: handleEndChat,
|
|
1674
|
-
className: "bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1675
|
-
children: [
|
|
1676
|
-
/* @__PURE__ */ jsx4(XCircle, { className: "h-4 w-4" }),
|
|
1677
|
-
/* @__PURE__ */ jsx4("span", { children: "End Chat" })
|
|
1678
|
-
]
|
|
1679
|
-
}
|
|
1680
|
-
),
|
|
1681
|
-
/* @__PURE__ */ jsxs2(
|
|
1682
|
-
"button",
|
|
1683
|
-
{
|
|
1684
|
-
onClick: handleBlockUser,
|
|
1685
|
-
className: "bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1686
|
-
children: [
|
|
1687
|
-
/* @__PURE__ */ jsx4(XCircle, { className: "h-4 w-4" }),
|
|
1688
|
-
/* @__PURE__ */ jsx4("span", { children: "Block User" })
|
|
1689
|
-
]
|
|
1690
|
-
}
|
|
1691
|
-
),
|
|
1692
|
-
/* @__PURE__ */ jsxs2(
|
|
1693
|
-
"button",
|
|
1694
|
-
{
|
|
1695
|
-
onClick: handleStartCall,
|
|
1696
|
-
disabled: !selectedChat.humanAgentEngaged,
|
|
1697
|
-
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",
|
|
1698
|
-
children: [
|
|
1699
|
-
/* @__PURE__ */ jsx4(Phone, { className: "h-4 w-4" }),
|
|
1700
|
-
/* @__PURE__ */ jsx4("span", { children: "Start Call" })
|
|
1701
|
-
]
|
|
1702
|
-
}
|
|
1703
|
-
),
|
|
1704
|
-
/* @__PURE__ */ jsxs2(
|
|
1705
|
-
"button",
|
|
1706
|
-
{
|
|
1707
|
-
onClick: handleCreateCase,
|
|
1708
|
-
className: "bg-purple-500 hover:bg-purple-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors",
|
|
1709
|
-
children: [
|
|
1710
|
-
/* @__PURE__ */ jsx4(Plus, { className: "h-4 w-4" }),
|
|
1711
|
-
/* @__PURE__ */ jsx4("span", { children: "Create Case" })
|
|
1712
|
-
]
|
|
1713
|
-
}
|
|
1714
|
-
)
|
|
1715
|
-
] })
|
|
1716
|
-
] });
|
|
1717
|
-
};
|
|
1718
|
-
|
|
1719
|
-
// components/admin/admin-chat-list.tsx
|
|
1720
|
-
import { AlertCircle, Bot, Loader2, UserCheck } from "lucide-react";
|
|
1721
|
-
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1722
|
-
var ScrollArea = ({ className, children }) => /* @__PURE__ */ jsx5("div", { className: `overflow-auto ${className || ""}`, children });
|
|
1723
|
-
var AdminChatList = ({ className }) => {
|
|
1724
|
-
const { chats, selectedChat, selectChat } = useWebSocketChatAdminContext();
|
|
1725
|
-
const sortedChats = Array.from(chats.values()).sort(
|
|
1726
|
-
(a, b) => b.lastUpdated - a.lastUpdated
|
|
1727
|
-
);
|
|
1728
|
-
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col h-full", children: [
|
|
1729
|
-
/* @__PURE__ */ jsx5("div", { className: "p-4 border-b", children: /* @__PURE__ */ jsx5("h2", { className: "font-semibold text-lg", children: "Active Chats" }) }),
|
|
1730
|
-
/* @__PURE__ */ jsx5("div", { className: "flex-1 overflow-y-auto", children: sortedChats.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "p-4 text-center text-gray-500", children: "No active chats" }) : /* @__PURE__ */ jsx5("div", { className: "divide-y", children: /* @__PURE__ */ jsx5(ScrollArea, { className: "h-[calc(100vh-15rem)]", children: sortedChats.map((chat, index) => /* @__PURE__ */ jsx5(
|
|
1731
|
-
AdminChatListItem,
|
|
1732
|
-
{
|
|
1733
|
-
chat,
|
|
1734
|
-
isSelected: chat.key === selectedChat?.key,
|
|
1735
|
-
onSelect: () => selectChat(chat.key)
|
|
1736
|
-
},
|
|
1737
|
-
index
|
|
1738
|
-
)) }) }) })
|
|
1739
|
-
] });
|
|
1740
|
-
};
|
|
1741
|
-
var AdminChatListItem = ({
|
|
1742
|
-
chat,
|
|
1743
|
-
isSelected,
|
|
1744
|
-
onSelect
|
|
1745
|
-
}) => {
|
|
1746
|
-
const lastMessage = chat.messages[chat.messages.length - 1];
|
|
1747
|
-
return /* @__PURE__ */ jsx5(
|
|
1748
|
-
"button",
|
|
1749
|
-
{
|
|
1750
|
-
onClick: onSelect,
|
|
1751
|
-
className: `w-full p-4 text-left hover:bg-gray-50 focus:outline-none focus:bg-gray-50 ${isSelected ? "bg-blue-50" : ""}`,
|
|
1752
|
-
children: /* @__PURE__ */ jsxs3("div", { className: "flex items-start space-x-3", children: [
|
|
1753
|
-
/* @__PURE__ */ jsxs3("div", { className: "shrink-0", children: [
|
|
1754
|
-
chat.aiEngaged && /* @__PURE__ */ jsx5(Bot, { className: "w-6 h-6 text-blue-500" }),
|
|
1755
|
-
chat.isWaitingForAgent && /* @__PURE__ */ jsx5(Loader2, { className: "w-6 h-6 text-blue-500 animate-spin text-orange-500" }),
|
|
1756
|
-
chat.humanAgentEngaged && /* @__PURE__ */ jsx5(UserCheck, { className: "w-6 h-6 text-blue-500 text-green-500" })
|
|
1757
|
-
] }),
|
|
1758
|
-
/* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [
|
|
1759
|
-
/* @__PURE__ */ jsx5("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx5("p", { className: "text-sm font-medium text-gray-900 truncate", children: chat.title || `Chat ${chat.key}` }) }),
|
|
1760
|
-
lastMessage && /* @__PURE__ */ jsx5("p", { className: "mt-1 text-sm text-gray-500 truncate", children: lastMessage.content }),
|
|
1761
|
-
/* @__PURE__ */ jsxs3("div", { className: "mt-1 flex items-center space-x-2", children: [
|
|
1762
|
-
chat.isWaiting && /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800", children: [
|
|
1763
|
-
/* @__PURE__ */ jsx5(AlertCircle, { className: "mr-1 h-3 w-3" }),
|
|
1764
|
-
"Waiting"
|
|
1765
|
-
] }),
|
|
1766
|
-
chat.humanAgentEngaged && /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800", children: [
|
|
1767
|
-
"Agent Engaged:",
|
|
1768
|
-
" ",
|
|
1769
|
-
chat.users?.find((u) => u.role === ChatRoleHumanAgent)?.name
|
|
1770
|
-
] }),
|
|
1771
|
-
chat.isWaitingForAgent && /* @__PURE__ */ jsx5("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" })
|
|
1772
|
-
] })
|
|
1773
|
-
] })
|
|
1774
|
-
] })
|
|
1775
|
-
}
|
|
1776
|
-
);
|
|
1777
|
-
};
|
|
1778
|
-
|
|
1779
|
-
// components/admin/admin-chat-header.tsx
|
|
1780
|
-
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1781
|
-
var AdminChatHeader = ({
|
|
1782
|
-
className
|
|
1783
|
-
}) => {
|
|
1784
|
-
const { isConnected, chats } = useWebSocketChatAdminContext();
|
|
1785
|
-
return /* @__PURE__ */ jsx6("div", { className: "p-4 border-b", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center space-x-2", children: [
|
|
1786
|
-
/* @__PURE__ */ jsx6(
|
|
1787
|
-
"div",
|
|
1788
|
-
{
|
|
1789
|
-
className: `w-2 h-2 rounded-full ${isConnected ? "bg-green-500" : "bg-red-500"}`
|
|
1790
|
-
}
|
|
1791
|
-
),
|
|
1792
|
-
/* @__PURE__ */ jsxs4("span", { className: "text-sm text-gray-600", children: [
|
|
1793
|
-
"Active Chats: ",
|
|
1794
|
-
/* @__PURE__ */ jsx6("span", { className: "font-bold", children: chats.size })
|
|
1795
|
-
] }),
|
|
1796
|
-
/* @__PURE__ */ jsx6("span", { className: "text-sm text-gray-500", children: "|" }),
|
|
1797
|
-
/* @__PURE__ */ jsxs4("span", { className: "text-sm text-gray-600", children: [
|
|
1798
|
-
"Waiting For Agent:",
|
|
1799
|
-
" ",
|
|
1800
|
-
/* @__PURE__ */ jsx6("span", { className: "font-bold", children: Array.from(chats.values()).filter(
|
|
1801
|
-
(chat) => chat.isWaitingForAgent
|
|
1802
|
-
).length })
|
|
1803
|
-
] })
|
|
1804
|
-
] }) });
|
|
1805
|
-
};
|
|
1806
|
-
|
|
1807
|
-
// components/customer/chat-status-customer-ui.tsx
|
|
1808
|
-
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1809
|
-
var ChatStatusCustomerUI = ({ isConnected }) => {
|
|
1810
|
-
return /* @__PURE__ */ jsxs5("div", { className: "flex items-center space-x-2", children: [
|
|
1811
|
-
/* @__PURE__ */ jsx7(
|
|
1812
|
-
"span",
|
|
1813
|
-
{
|
|
1814
|
-
className: `w-3 h-3 rounded-full ${isConnected ? "bg-green-500" : "bg-red-500"}`,
|
|
1815
|
-
role: "status",
|
|
1816
|
-
"aria-label": isConnected ? "Connected" : "Disconnected"
|
|
1817
|
-
}
|
|
1818
|
-
),
|
|
1819
|
-
/* @__PURE__ */ jsx7("span", { className: "text-sm text-gray-500", children: isConnected ? "Connected" : "Reconnecting..." })
|
|
1820
|
-
] });
|
|
1821
|
-
};
|
|
1822
|
-
|
|
1823
|
-
// components/customer/chat-header.tsx
|
|
1824
|
-
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1825
|
-
var ChatHeader = () => {
|
|
1826
|
-
const { isConnected, title } = useWebSocketChatCustomerContext();
|
|
1827
|
-
return /* @__PURE__ */ jsx8("div", { className: "p-4 border-b bg-white rounded-t-lg", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
|
|
1828
|
-
/* @__PURE__ */ jsx8("h2", { className: "font-bold text-lg", children: title || "Chat" }),
|
|
1829
|
-
/* @__PURE__ */ jsx8(ChatStatusCustomerUI, { isConnected })
|
|
1830
|
-
] }) });
|
|
1831
|
-
};
|
|
1832
|
-
|
|
1833
|
-
// components/customer/chat-input.tsx
|
|
1834
|
-
import KSUID2 from "ksuid";
|
|
1835
|
-
import { Paperclip as Paperclip2, Send as Send2 } from "lucide-react";
|
|
1836
|
-
import { useRef as useRef3, useState as useState5 } from "react";
|
|
1837
|
-
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1838
|
-
var ChatInput = ({ userId, orgId, onFileUpload }) => {
|
|
1839
|
-
const { sendMessage, chatKey } = useWebSocketChatCustomerContext();
|
|
1840
|
-
const [input, setInput] = useState5("");
|
|
1841
|
-
const [isUploading, setIsUploading] = useState5(false);
|
|
1842
|
-
const [isSending, setIsSending] = useState5(false);
|
|
1843
|
-
const fileInputRef = useRef3(null);
|
|
1844
|
-
const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
1845
|
-
const handleSend = async () => {
|
|
1846
|
-
if (!input.trim() && !isUploading || isSending) return;
|
|
1847
|
-
if (!chatKey || !orgId || !userId) {
|
|
1848
|
-
console.error("chatKey, orgId, or userId is not defined");
|
|
1849
|
-
return;
|
|
1850
|
-
}
|
|
1851
|
-
try {
|
|
1852
|
-
setIsSending(true);
|
|
1853
|
-
sendMessage({
|
|
1854
|
-
type: "message",
|
|
1855
|
-
chatKey,
|
|
1856
|
-
orgId,
|
|
1857
|
-
userId,
|
|
1858
|
-
message: {
|
|
1859
|
-
id: KSUID2.randomSync().string,
|
|
1860
|
-
content: input,
|
|
1861
|
-
role: "user",
|
|
1862
|
-
senderId: userId,
|
|
1863
|
-
time: Date.now(),
|
|
1864
|
-
status: "sending",
|
|
1865
|
-
createdAt: Date.now()
|
|
1866
|
-
}
|
|
1867
|
-
});
|
|
1868
|
-
setInput("");
|
|
1869
|
-
} finally {
|
|
1870
|
-
setIsSending(false);
|
|
1871
|
-
}
|
|
1872
|
-
};
|
|
1873
|
-
const handleFileUpload = async (event) => {
|
|
1874
|
-
const files = event.target.files;
|
|
1875
|
-
if (!files || !onFileUpload) return;
|
|
1876
|
-
try {
|
|
1877
|
-
setIsUploading(true);
|
|
1878
|
-
const uploadedUrls = await Promise.all(
|
|
1879
|
-
Array.from(files).map(async (file) => {
|
|
1880
|
-
if (file.size > MAX_FILE_SIZE) {
|
|
1881
|
-
throw new Error(
|
|
1882
|
-
`File ${file.name} is too large. Maximum size is 5MB.`
|
|
1883
|
-
);
|
|
1884
|
-
}
|
|
1885
|
-
const url = await onFileUpload(file);
|
|
1886
|
-
return {
|
|
1887
|
-
type: file.type,
|
|
1888
|
-
url,
|
|
1889
|
-
name: file.name
|
|
1890
|
-
};
|
|
1891
|
-
})
|
|
1892
|
-
);
|
|
1893
|
-
const attachments = uploadedUrls.map((file) => ({
|
|
1894
|
-
type: "image",
|
|
1895
|
-
url: file.url,
|
|
1896
|
-
title: file.name
|
|
1897
|
-
}));
|
|
1898
|
-
if (!chatKey || !orgId || !userId) {
|
|
1899
|
-
console.error("chatKey, orgId, or userId is not defined");
|
|
1900
|
-
return;
|
|
1901
|
-
}
|
|
1902
|
-
} finally {
|
|
1903
|
-
setIsUploading(false);
|
|
1904
|
-
if (fileInputRef.current) {
|
|
1905
|
-
fileInputRef.current.value = "";
|
|
1906
|
-
}
|
|
1907
|
-
}
|
|
1908
|
-
};
|
|
1909
|
-
return /* @__PURE__ */ jsxs7("div", { className: "p-4 border-t bg-white rounded-b-lg", children: [
|
|
1910
|
-
/* @__PURE__ */ jsxs7("div", { className: "flex items-center space-x-2", children: [
|
|
1911
|
-
/* @__PURE__ */ jsx9(
|
|
1912
|
-
"input",
|
|
1913
|
-
{
|
|
1914
|
-
type: "file",
|
|
1915
|
-
ref: fileInputRef,
|
|
1916
|
-
onChange: handleFileUpload,
|
|
1917
|
-
className: "hidden",
|
|
1918
|
-
multiple: true,
|
|
1919
|
-
accept: "image/*,.pdf,.doc,.docx,.txt"
|
|
1920
|
-
}
|
|
1921
|
-
),
|
|
1922
|
-
/* @__PURE__ */ jsx9(
|
|
1923
|
-
"button",
|
|
1924
|
-
{
|
|
1925
|
-
onClick: () => fileInputRef.current?.click(),
|
|
1926
|
-
className: "p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100 transition-colors",
|
|
1927
|
-
disabled: isUploading,
|
|
1928
|
-
"aria-label": "Attach file",
|
|
1929
|
-
children: isUploading ? /* @__PURE__ */ jsx9("span", { className: "w-5 h-5 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" }) : /* @__PURE__ */ jsx9(Paperclip2, { className: "w-5 h-5" })
|
|
1930
|
-
}
|
|
1931
|
-
),
|
|
1932
|
-
/* @__PURE__ */ jsx9("div", { className: "flex-1 relative", children: /* @__PURE__ */ jsx9(
|
|
1933
|
-
"textarea",
|
|
1934
|
-
{
|
|
1935
|
-
value: input,
|
|
1936
|
-
onChange: (e) => setInput(e.target.value),
|
|
1937
|
-
onKeyDown: (e) => {
|
|
1938
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
1939
|
-
e.preventDefault();
|
|
1940
|
-
handleSend();
|
|
1941
|
-
}
|
|
1942
|
-
},
|
|
1943
|
-
className: "w-full p-3 border rounded-2xl focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none",
|
|
1944
|
-
placeholder: "Type a message...",
|
|
1945
|
-
rows: 1,
|
|
1946
|
-
style: {
|
|
1947
|
-
minHeight: "44px",
|
|
1948
|
-
maxHeight: "200px"
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
) }),
|
|
1952
|
-
/* @__PURE__ */ jsx9(
|
|
1953
|
-
"button",
|
|
1954
|
-
{
|
|
1955
|
-
onClick: handleSend,
|
|
1956
|
-
disabled: !input.trim() && !isUploading || isSending,
|
|
1957
|
-
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"}`,
|
|
1958
|
-
"aria-label": "Send message",
|
|
1959
|
-
children: isSending ? /* @__PURE__ */ jsx9("span", { className: "w-5 h-5 animate-spin rounded-full border-2 border-gray-300 border-t-white" }) : /* @__PURE__ */ jsx9(Send2, { className: "w-5 h-5" })
|
|
1960
|
-
}
|
|
1961
|
-
)
|
|
1962
|
-
] }),
|
|
1963
|
-
isUploading && /* @__PURE__ */ jsx9("div", { className: "mt-2", children: /* @__PURE__ */ jsx9("div", { className: "w-full bg-gray-200 rounded-full h-1", children: /* @__PURE__ */ jsx9("div", { className: "bg-blue-500 h-1 rounded-full animate-pulse" }) }) })
|
|
1964
|
-
] });
|
|
1965
|
-
};
|
|
1966
|
-
|
|
1967
|
-
// components/customer/chat-messages.tsx
|
|
1968
|
-
import { useEffect as useEffect2, useRef as useRef4 } from "react";
|
|
1969
|
-
|
|
1970
|
-
// ../../node_modules/date-fns/constants.js
|
|
1971
|
-
var daysInYear = 365.2425;
|
|
1972
|
-
var maxTime = Math.pow(10, 8) * 24 * 60 * 60 * 1e3;
|
|
1973
|
-
var minTime = -maxTime;
|
|
1974
|
-
var millisecondsInWeek = 6048e5;
|
|
1975
|
-
var millisecondsInDay = 864e5;
|
|
1976
|
-
var secondsInHour = 3600;
|
|
1977
|
-
var secondsInDay = secondsInHour * 24;
|
|
1978
|
-
var secondsInWeek = secondsInDay * 7;
|
|
1979
|
-
var secondsInYear = secondsInDay * daysInYear;
|
|
1980
|
-
var secondsInMonth = secondsInYear / 12;
|
|
1981
|
-
var secondsInQuarter = secondsInMonth * 3;
|
|
1982
|
-
var constructFromSymbol = /* @__PURE__ */ Symbol.for("constructDateFrom");
|
|
1983
|
-
|
|
1984
|
-
// ../../node_modules/date-fns/constructFrom.js
|
|
1985
|
-
function constructFrom(date, value) {
|
|
1986
|
-
if (typeof date === "function") return date(value);
|
|
1987
|
-
if (date && typeof date === "object" && constructFromSymbol in date)
|
|
1988
|
-
return date[constructFromSymbol](value);
|
|
1989
|
-
if (date instanceof Date) return new date.constructor(value);
|
|
1990
|
-
return new Date(value);
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
// ../../node_modules/date-fns/toDate.js
|
|
1994
|
-
function toDate(argument, context) {
|
|
1995
|
-
return constructFrom(context || argument, argument);
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
// ../../node_modules/date-fns/_lib/defaultOptions.js
|
|
1999
|
-
var defaultOptions = {};
|
|
2000
|
-
function getDefaultOptions() {
|
|
2001
|
-
return defaultOptions;
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
// ../../node_modules/date-fns/startOfWeek.js
|
|
2005
|
-
function startOfWeek(date, options) {
|
|
2006
|
-
const defaultOptions2 = getDefaultOptions();
|
|
2007
|
-
const weekStartsOn = options?.weekStartsOn ?? options?.locale?.options?.weekStartsOn ?? defaultOptions2.weekStartsOn ?? defaultOptions2.locale?.options?.weekStartsOn ?? 0;
|
|
2008
|
-
const _date = toDate(date, options?.in);
|
|
2009
|
-
const day = _date.getDay();
|
|
2010
|
-
const diff = (day < weekStartsOn ? 7 : 0) + day - weekStartsOn;
|
|
2011
|
-
_date.setDate(_date.getDate() - diff);
|
|
2012
|
-
_date.setHours(0, 0, 0, 0);
|
|
2013
|
-
return _date;
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
// ../../node_modules/date-fns/startOfISOWeek.js
|
|
2017
|
-
function startOfISOWeek(date, options) {
|
|
2018
|
-
return startOfWeek(date, { ...options, weekStartsOn: 1 });
|
|
2019
|
-
}
|
|
2020
|
-
|
|
2021
|
-
// ../../node_modules/date-fns/getISOWeekYear.js
|
|
2022
|
-
function getISOWeekYear(date, options) {
|
|
2023
|
-
const _date = toDate(date, options?.in);
|
|
2024
|
-
const year = _date.getFullYear();
|
|
2025
|
-
const fourthOfJanuaryOfNextYear = constructFrom(_date, 0);
|
|
2026
|
-
fourthOfJanuaryOfNextYear.setFullYear(year + 1, 0, 4);
|
|
2027
|
-
fourthOfJanuaryOfNextYear.setHours(0, 0, 0, 0);
|
|
2028
|
-
const startOfNextYear = startOfISOWeek(fourthOfJanuaryOfNextYear);
|
|
2029
|
-
const fourthOfJanuaryOfThisYear = constructFrom(_date, 0);
|
|
2030
|
-
fourthOfJanuaryOfThisYear.setFullYear(year, 0, 4);
|
|
2031
|
-
fourthOfJanuaryOfThisYear.setHours(0, 0, 0, 0);
|
|
2032
|
-
const startOfThisYear = startOfISOWeek(fourthOfJanuaryOfThisYear);
|
|
2033
|
-
if (_date.getTime() >= startOfNextYear.getTime()) {
|
|
2034
|
-
return year + 1;
|
|
2035
|
-
} else if (_date.getTime() >= startOfThisYear.getTime()) {
|
|
2036
|
-
return year;
|
|
2037
|
-
} else {
|
|
2038
|
-
return year - 1;
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
|
-
// ../../node_modules/date-fns/_lib/getTimezoneOffsetInMilliseconds.js
|
|
2043
|
-
function getTimezoneOffsetInMilliseconds(date) {
|
|
2044
|
-
const _date = toDate(date);
|
|
2045
|
-
const utcDate = new Date(
|
|
2046
|
-
Date.UTC(
|
|
2047
|
-
_date.getFullYear(),
|
|
2048
|
-
_date.getMonth(),
|
|
2049
|
-
_date.getDate(),
|
|
2050
|
-
_date.getHours(),
|
|
2051
|
-
_date.getMinutes(),
|
|
2052
|
-
_date.getSeconds(),
|
|
2053
|
-
_date.getMilliseconds()
|
|
2054
|
-
)
|
|
2055
|
-
);
|
|
2056
|
-
utcDate.setUTCFullYear(_date.getFullYear());
|
|
2057
|
-
return +date - +utcDate;
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
// ../../node_modules/date-fns/_lib/normalizeDates.js
|
|
2061
|
-
function normalizeDates(context, ...dates) {
|
|
2062
|
-
const normalize = constructFrom.bind(
|
|
2063
|
-
null,
|
|
2064
|
-
context || dates.find((date) => typeof date === "object")
|
|
2065
|
-
);
|
|
2066
|
-
return dates.map(normalize);
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2069
|
-
// ../../node_modules/date-fns/startOfDay.js
|
|
2070
|
-
function startOfDay(date, options) {
|
|
2071
|
-
const _date = toDate(date, options?.in);
|
|
2072
|
-
_date.setHours(0, 0, 0, 0);
|
|
2073
|
-
return _date;
|
|
2074
|
-
}
|
|
2075
|
-
|
|
2076
|
-
// ../../node_modules/date-fns/differenceInCalendarDays.js
|
|
2077
|
-
function differenceInCalendarDays(laterDate, earlierDate, options) {
|
|
2078
|
-
const [laterDate_, earlierDate_] = normalizeDates(
|
|
2079
|
-
options?.in,
|
|
2080
|
-
laterDate,
|
|
2081
|
-
earlierDate
|
|
2082
|
-
);
|
|
2083
|
-
const laterStartOfDay = startOfDay(laterDate_);
|
|
2084
|
-
const earlierStartOfDay = startOfDay(earlierDate_);
|
|
2085
|
-
const laterTimestamp = +laterStartOfDay - getTimezoneOffsetInMilliseconds(laterStartOfDay);
|
|
2086
|
-
const earlierTimestamp = +earlierStartOfDay - getTimezoneOffsetInMilliseconds(earlierStartOfDay);
|
|
2087
|
-
return Math.round((laterTimestamp - earlierTimestamp) / millisecondsInDay);
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
// ../../node_modules/date-fns/startOfISOWeekYear.js
|
|
2091
|
-
function startOfISOWeekYear(date, options) {
|
|
2092
|
-
const year = getISOWeekYear(date, options);
|
|
2093
|
-
const fourthOfJanuary = constructFrom(options?.in || date, 0);
|
|
2094
|
-
fourthOfJanuary.setFullYear(year, 0, 4);
|
|
2095
|
-
fourthOfJanuary.setHours(0, 0, 0, 0);
|
|
2096
|
-
return startOfISOWeek(fourthOfJanuary);
|
|
2097
|
-
}
|
|
2098
|
-
|
|
2099
|
-
// ../../node_modules/date-fns/isDate.js
|
|
2100
|
-
function isDate(value) {
|
|
2101
|
-
return value instanceof Date || typeof value === "object" && Object.prototype.toString.call(value) === "[object Date]";
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
// ../../node_modules/date-fns/isValid.js
|
|
2105
|
-
function isValid(date) {
|
|
2106
|
-
return !(!isDate(date) && typeof date !== "number" || isNaN(+toDate(date)));
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2109
|
-
// ../../node_modules/date-fns/startOfYear.js
|
|
2110
|
-
function startOfYear(date, options) {
|
|
2111
|
-
const date_ = toDate(date, options?.in);
|
|
2112
|
-
date_.setFullYear(date_.getFullYear(), 0, 1);
|
|
2113
|
-
date_.setHours(0, 0, 0, 0);
|
|
2114
|
-
return date_;
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
// ../../node_modules/date-fns/locale/en-US/_lib/formatDistance.js
|
|
2118
|
-
var formatDistanceLocale = {
|
|
2119
|
-
lessThanXSeconds: {
|
|
2120
|
-
one: "less than a second",
|
|
2121
|
-
other: "less than {{count}} seconds"
|
|
2122
|
-
},
|
|
2123
|
-
xSeconds: {
|
|
2124
|
-
one: "1 second",
|
|
2125
|
-
other: "{{count}} seconds"
|
|
2126
|
-
},
|
|
2127
|
-
halfAMinute: "half a minute",
|
|
2128
|
-
lessThanXMinutes: {
|
|
2129
|
-
one: "less than a minute",
|
|
2130
|
-
other: "less than {{count}} minutes"
|
|
2131
|
-
},
|
|
2132
|
-
xMinutes: {
|
|
2133
|
-
one: "1 minute",
|
|
2134
|
-
other: "{{count}} minutes"
|
|
2135
|
-
},
|
|
2136
|
-
aboutXHours: {
|
|
2137
|
-
one: "about 1 hour",
|
|
2138
|
-
other: "about {{count}} hours"
|
|
2139
|
-
},
|
|
2140
|
-
xHours: {
|
|
2141
|
-
one: "1 hour",
|
|
2142
|
-
other: "{{count}} hours"
|
|
2143
|
-
},
|
|
2144
|
-
xDays: {
|
|
2145
|
-
one: "1 day",
|
|
2146
|
-
other: "{{count}} days"
|
|
2147
|
-
},
|
|
2148
|
-
aboutXWeeks: {
|
|
2149
|
-
one: "about 1 week",
|
|
2150
|
-
other: "about {{count}} weeks"
|
|
2151
|
-
},
|
|
2152
|
-
xWeeks: {
|
|
2153
|
-
one: "1 week",
|
|
2154
|
-
other: "{{count}} weeks"
|
|
2155
|
-
},
|
|
2156
|
-
aboutXMonths: {
|
|
2157
|
-
one: "about 1 month",
|
|
2158
|
-
other: "about {{count}} months"
|
|
2159
|
-
},
|
|
2160
|
-
xMonths: {
|
|
2161
|
-
one: "1 month",
|
|
2162
|
-
other: "{{count}} months"
|
|
2163
|
-
},
|
|
2164
|
-
aboutXYears: {
|
|
2165
|
-
one: "about 1 year",
|
|
2166
|
-
other: "about {{count}} years"
|
|
2167
|
-
},
|
|
2168
|
-
xYears: {
|
|
2169
|
-
one: "1 year",
|
|
2170
|
-
other: "{{count}} years"
|
|
2171
|
-
},
|
|
2172
|
-
overXYears: {
|
|
2173
|
-
one: "over 1 year",
|
|
2174
|
-
other: "over {{count}} years"
|
|
2175
|
-
},
|
|
2176
|
-
almostXYears: {
|
|
2177
|
-
one: "almost 1 year",
|
|
2178
|
-
other: "almost {{count}} years"
|
|
2179
|
-
}
|
|
2180
|
-
};
|
|
2181
|
-
var formatDistance = (token, count, options) => {
|
|
2182
|
-
let result;
|
|
2183
|
-
const tokenValue = formatDistanceLocale[token];
|
|
2184
|
-
if (typeof tokenValue === "string") {
|
|
2185
|
-
result = tokenValue;
|
|
2186
|
-
} else if (count === 1) {
|
|
2187
|
-
result = tokenValue.one;
|
|
2188
|
-
} else {
|
|
2189
|
-
result = tokenValue.other.replace("{{count}}", count.toString());
|
|
2190
|
-
}
|
|
2191
|
-
if (options?.addSuffix) {
|
|
2192
|
-
if (options.comparison && options.comparison > 0) {
|
|
2193
|
-
return "in " + result;
|
|
2194
|
-
} else {
|
|
2195
|
-
return result + " ago";
|
|
2196
|
-
}
|
|
2197
|
-
}
|
|
2198
|
-
return result;
|
|
2199
|
-
};
|
|
2200
|
-
|
|
2201
|
-
// ../../node_modules/date-fns/locale/_lib/buildFormatLongFn.js
|
|
2202
|
-
function buildFormatLongFn(args) {
|
|
2203
|
-
return (options = {}) => {
|
|
2204
|
-
const width = options.width ? String(options.width) : args.defaultWidth;
|
|
2205
|
-
const format2 = args.formats[width] || args.formats[args.defaultWidth];
|
|
2206
|
-
return format2;
|
|
2207
|
-
};
|
|
2208
|
-
}
|
|
2209
|
-
|
|
2210
|
-
// ../../node_modules/date-fns/locale/en-US/_lib/formatLong.js
|
|
2211
|
-
var dateFormats = {
|
|
2212
|
-
full: "EEEE, MMMM do, y",
|
|
2213
|
-
long: "MMMM do, y",
|
|
2214
|
-
medium: "MMM d, y",
|
|
2215
|
-
short: "MM/dd/yyyy"
|
|
2216
|
-
};
|
|
2217
|
-
var timeFormats = {
|
|
2218
|
-
full: "h:mm:ss a zzzz",
|
|
2219
|
-
long: "h:mm:ss a z",
|
|
2220
|
-
medium: "h:mm:ss a",
|
|
2221
|
-
short: "h:mm a"
|
|
2222
|
-
};
|
|
2223
|
-
var dateTimeFormats = {
|
|
2224
|
-
full: "{{date}} 'at' {{time}}",
|
|
2225
|
-
long: "{{date}} 'at' {{time}}",
|
|
2226
|
-
medium: "{{date}}, {{time}}",
|
|
2227
|
-
short: "{{date}}, {{time}}"
|
|
2228
|
-
};
|
|
2229
|
-
var formatLong = {
|
|
2230
|
-
date: buildFormatLongFn({
|
|
2231
|
-
formats: dateFormats,
|
|
2232
|
-
defaultWidth: "full"
|
|
2233
|
-
}),
|
|
2234
|
-
time: buildFormatLongFn({
|
|
2235
|
-
formats: timeFormats,
|
|
2236
|
-
defaultWidth: "full"
|
|
2237
|
-
}),
|
|
2238
|
-
dateTime: buildFormatLongFn({
|
|
2239
|
-
formats: dateTimeFormats,
|
|
2240
|
-
defaultWidth: "full"
|
|
2241
|
-
})
|
|
2242
|
-
};
|
|
2243
|
-
|
|
2244
|
-
// ../../node_modules/date-fns/locale/en-US/_lib/formatRelative.js
|
|
2245
|
-
var formatRelativeLocale = {
|
|
2246
|
-
lastWeek: "'last' eeee 'at' p",
|
|
2247
|
-
yesterday: "'yesterday at' p",
|
|
2248
|
-
today: "'today at' p",
|
|
2249
|
-
tomorrow: "'tomorrow at' p",
|
|
2250
|
-
nextWeek: "eeee 'at' p",
|
|
2251
|
-
other: "P"
|
|
2252
|
-
};
|
|
2253
|
-
var formatRelative = (token, _date, _baseDate, _options) => formatRelativeLocale[token];
|
|
2254
|
-
|
|
2255
|
-
// ../../node_modules/date-fns/locale/_lib/buildLocalizeFn.js
|
|
2256
|
-
function buildLocalizeFn(args) {
|
|
2257
|
-
return (value, options) => {
|
|
2258
|
-
const context = options?.context ? String(options.context) : "standalone";
|
|
2259
|
-
let valuesArray;
|
|
2260
|
-
if (context === "formatting" && args.formattingValues) {
|
|
2261
|
-
const defaultWidth = args.defaultFormattingWidth || args.defaultWidth;
|
|
2262
|
-
const width = options?.width ? String(options.width) : defaultWidth;
|
|
2263
|
-
valuesArray = args.formattingValues[width] || args.formattingValues[defaultWidth];
|
|
2264
|
-
} else {
|
|
2265
|
-
const defaultWidth = args.defaultWidth;
|
|
2266
|
-
const width = options?.width ? String(options.width) : args.defaultWidth;
|
|
2267
|
-
valuesArray = args.values[width] || args.values[defaultWidth];
|
|
2268
|
-
}
|
|
2269
|
-
const index = args.argumentCallback ? args.argumentCallback(value) : value;
|
|
2270
|
-
return valuesArray[index];
|
|
2271
|
-
};
|
|
2272
|
-
}
|
|
2273
|
-
|
|
2274
|
-
// ../../node_modules/date-fns/locale/en-US/_lib/localize.js
|
|
2275
|
-
var eraValues = {
|
|
2276
|
-
narrow: ["B", "A"],
|
|
2277
|
-
abbreviated: ["BC", "AD"],
|
|
2278
|
-
wide: ["Before Christ", "Anno Domini"]
|
|
2279
|
-
};
|
|
2280
|
-
var quarterValues = {
|
|
2281
|
-
narrow: ["1", "2", "3", "4"],
|
|
2282
|
-
abbreviated: ["Q1", "Q2", "Q3", "Q4"],
|
|
2283
|
-
wide: ["1st quarter", "2nd quarter", "3rd quarter", "4th quarter"]
|
|
2284
|
-
};
|
|
2285
|
-
var monthValues = {
|
|
2286
|
-
narrow: ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
|
|
2287
|
-
abbreviated: [
|
|
2288
|
-
"Jan",
|
|
2289
|
-
"Feb",
|
|
2290
|
-
"Mar",
|
|
2291
|
-
"Apr",
|
|
2292
|
-
"May",
|
|
2293
|
-
"Jun",
|
|
2294
|
-
"Jul",
|
|
2295
|
-
"Aug",
|
|
2296
|
-
"Sep",
|
|
2297
|
-
"Oct",
|
|
2298
|
-
"Nov",
|
|
2299
|
-
"Dec"
|
|
2300
|
-
],
|
|
2301
|
-
wide: [
|
|
2302
|
-
"January",
|
|
2303
|
-
"February",
|
|
2304
|
-
"March",
|
|
2305
|
-
"April",
|
|
2306
|
-
"May",
|
|
2307
|
-
"June",
|
|
2308
|
-
"July",
|
|
2309
|
-
"August",
|
|
2310
|
-
"September",
|
|
2311
|
-
"October",
|
|
2312
|
-
"November",
|
|
2313
|
-
"December"
|
|
2314
|
-
]
|
|
2315
|
-
};
|
|
2316
|
-
var dayValues = {
|
|
2317
|
-
narrow: ["S", "M", "T", "W", "T", "F", "S"],
|
|
2318
|
-
short: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
|
|
2319
|
-
abbreviated: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
2320
|
-
wide: [
|
|
2321
|
-
"Sunday",
|
|
2322
|
-
"Monday",
|
|
2323
|
-
"Tuesday",
|
|
2324
|
-
"Wednesday",
|
|
2325
|
-
"Thursday",
|
|
2326
|
-
"Friday",
|
|
2327
|
-
"Saturday"
|
|
2328
|
-
]
|
|
2329
|
-
};
|
|
2330
|
-
var dayPeriodValues = {
|
|
2331
|
-
narrow: {
|
|
2332
|
-
am: "a",
|
|
2333
|
-
pm: "p",
|
|
2334
|
-
midnight: "mi",
|
|
2335
|
-
noon: "n",
|
|
2336
|
-
morning: "morning",
|
|
2337
|
-
afternoon: "afternoon",
|
|
2338
|
-
evening: "evening",
|
|
2339
|
-
night: "night"
|
|
2340
|
-
},
|
|
2341
|
-
abbreviated: {
|
|
2342
|
-
am: "AM",
|
|
2343
|
-
pm: "PM",
|
|
2344
|
-
midnight: "midnight",
|
|
2345
|
-
noon: "noon",
|
|
2346
|
-
morning: "morning",
|
|
2347
|
-
afternoon: "afternoon",
|
|
2348
|
-
evening: "evening",
|
|
2349
|
-
night: "night"
|
|
2350
|
-
},
|
|
2351
|
-
wide: {
|
|
2352
|
-
am: "a.m.",
|
|
2353
|
-
pm: "p.m.",
|
|
2354
|
-
midnight: "midnight",
|
|
2355
|
-
noon: "noon",
|
|
2356
|
-
morning: "morning",
|
|
2357
|
-
afternoon: "afternoon",
|
|
2358
|
-
evening: "evening",
|
|
2359
|
-
night: "night"
|
|
2360
|
-
}
|
|
2361
|
-
};
|
|
2362
|
-
var formattingDayPeriodValues = {
|
|
2363
|
-
narrow: {
|
|
2364
|
-
am: "a",
|
|
2365
|
-
pm: "p",
|
|
2366
|
-
midnight: "mi",
|
|
2367
|
-
noon: "n",
|
|
2368
|
-
morning: "in the morning",
|
|
2369
|
-
afternoon: "in the afternoon",
|
|
2370
|
-
evening: "in the evening",
|
|
2371
|
-
night: "at night"
|
|
2372
|
-
},
|
|
2373
|
-
abbreviated: {
|
|
2374
|
-
am: "AM",
|
|
2375
|
-
pm: "PM",
|
|
2376
|
-
midnight: "midnight",
|
|
2377
|
-
noon: "noon",
|
|
2378
|
-
morning: "in the morning",
|
|
2379
|
-
afternoon: "in the afternoon",
|
|
2380
|
-
evening: "in the evening",
|
|
2381
|
-
night: "at night"
|
|
2382
|
-
},
|
|
2383
|
-
wide: {
|
|
2384
|
-
am: "a.m.",
|
|
2385
|
-
pm: "p.m.",
|
|
2386
|
-
midnight: "midnight",
|
|
2387
|
-
noon: "noon",
|
|
2388
|
-
morning: "in the morning",
|
|
2389
|
-
afternoon: "in the afternoon",
|
|
2390
|
-
evening: "in the evening",
|
|
2391
|
-
night: "at night"
|
|
2392
|
-
}
|
|
2393
|
-
};
|
|
2394
|
-
var ordinalNumber = (dirtyNumber, _options) => {
|
|
2395
|
-
const number = Number(dirtyNumber);
|
|
2396
|
-
const rem100 = number % 100;
|
|
2397
|
-
if (rem100 > 20 || rem100 < 10) {
|
|
2398
|
-
switch (rem100 % 10) {
|
|
2399
|
-
case 1:
|
|
2400
|
-
return number + "st";
|
|
2401
|
-
case 2:
|
|
2402
|
-
return number + "nd";
|
|
2403
|
-
case 3:
|
|
2404
|
-
return number + "rd";
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
return number + "th";
|
|
2408
|
-
};
|
|
2409
|
-
var localize = {
|
|
2410
|
-
ordinalNumber,
|
|
2411
|
-
era: buildLocalizeFn({
|
|
2412
|
-
values: eraValues,
|
|
2413
|
-
defaultWidth: "wide"
|
|
2414
|
-
}),
|
|
2415
|
-
quarter: buildLocalizeFn({
|
|
2416
|
-
values: quarterValues,
|
|
2417
|
-
defaultWidth: "wide",
|
|
2418
|
-
argumentCallback: (quarter) => quarter - 1
|
|
2419
|
-
}),
|
|
2420
|
-
month: buildLocalizeFn({
|
|
2421
|
-
values: monthValues,
|
|
2422
|
-
defaultWidth: "wide"
|
|
2423
|
-
}),
|
|
2424
|
-
day: buildLocalizeFn({
|
|
2425
|
-
values: dayValues,
|
|
2426
|
-
defaultWidth: "wide"
|
|
2427
|
-
}),
|
|
2428
|
-
dayPeriod: buildLocalizeFn({
|
|
2429
|
-
values: dayPeriodValues,
|
|
2430
|
-
defaultWidth: "wide",
|
|
2431
|
-
formattingValues: formattingDayPeriodValues,
|
|
2432
|
-
defaultFormattingWidth: "wide"
|
|
2433
|
-
})
|
|
2434
|
-
};
|
|
2435
|
-
|
|
2436
|
-
// ../../node_modules/date-fns/locale/_lib/buildMatchFn.js
|
|
2437
|
-
function buildMatchFn(args) {
|
|
2438
|
-
return (string, options = {}) => {
|
|
2439
|
-
const width = options.width;
|
|
2440
|
-
const matchPattern = width && args.matchPatterns[width] || args.matchPatterns[args.defaultMatchWidth];
|
|
2441
|
-
const matchResult = string.match(matchPattern);
|
|
2442
|
-
if (!matchResult) {
|
|
2443
|
-
return null;
|
|
2444
|
-
}
|
|
2445
|
-
const matchedString = matchResult[0];
|
|
2446
|
-
const parsePatterns = width && args.parsePatterns[width] || args.parsePatterns[args.defaultParseWidth];
|
|
2447
|
-
const key = Array.isArray(parsePatterns) ? findIndex(parsePatterns, (pattern) => pattern.test(matchedString)) : (
|
|
2448
|
-
// [TODO] -- I challenge you to fix the type
|
|
2449
|
-
findKey(parsePatterns, (pattern) => pattern.test(matchedString))
|
|
2450
|
-
);
|
|
2451
|
-
let value;
|
|
2452
|
-
value = args.valueCallback ? args.valueCallback(key) : key;
|
|
2453
|
-
value = options.valueCallback ? (
|
|
2454
|
-
// [TODO] -- I challenge you to fix the type
|
|
2455
|
-
options.valueCallback(value)
|
|
2456
|
-
) : value;
|
|
2457
|
-
const rest = string.slice(matchedString.length);
|
|
2458
|
-
return { value, rest };
|
|
2459
|
-
};
|
|
2460
|
-
}
|
|
2461
|
-
function findKey(object, predicate) {
|
|
2462
|
-
for (const key in object) {
|
|
2463
|
-
if (Object.prototype.hasOwnProperty.call(object, key) && predicate(object[key])) {
|
|
2464
|
-
return key;
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
return void 0;
|
|
2468
|
-
}
|
|
2469
|
-
function findIndex(array, predicate) {
|
|
2470
|
-
for (let key = 0; key < array.length; key++) {
|
|
2471
|
-
if (predicate(array[key])) {
|
|
2472
|
-
return key;
|
|
2473
|
-
}
|
|
2474
|
-
}
|
|
2475
|
-
return void 0;
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2478
|
-
// ../../node_modules/date-fns/locale/_lib/buildMatchPatternFn.js
|
|
2479
|
-
function buildMatchPatternFn(args) {
|
|
2480
|
-
return (string, options = {}) => {
|
|
2481
|
-
const matchResult = string.match(args.matchPattern);
|
|
2482
|
-
if (!matchResult) return null;
|
|
2483
|
-
const matchedString = matchResult[0];
|
|
2484
|
-
const parseResult = string.match(args.parsePattern);
|
|
2485
|
-
if (!parseResult) return null;
|
|
2486
|
-
let value = args.valueCallback ? args.valueCallback(parseResult[0]) : parseResult[0];
|
|
2487
|
-
value = options.valueCallback ? options.valueCallback(value) : value;
|
|
2488
|
-
const rest = string.slice(matchedString.length);
|
|
2489
|
-
return { value, rest };
|
|
2490
|
-
};
|
|
2491
|
-
}
|
|
2492
|
-
|
|
2493
|
-
// ../../node_modules/date-fns/locale/en-US/_lib/match.js
|
|
2494
|
-
var matchOrdinalNumberPattern = /^(\d+)(th|st|nd|rd)?/i;
|
|
2495
|
-
var parseOrdinalNumberPattern = /\d+/i;
|
|
2496
|
-
var matchEraPatterns = {
|
|
2497
|
-
narrow: /^(b|a)/i,
|
|
2498
|
-
abbreviated: /^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,
|
|
2499
|
-
wide: /^(before christ|before common era|anno domini|common era)/i
|
|
2500
|
-
};
|
|
2501
|
-
var parseEraPatterns = {
|
|
2502
|
-
any: [/^b/i, /^(a|c)/i]
|
|
2503
|
-
};
|
|
2504
|
-
var matchQuarterPatterns = {
|
|
2505
|
-
narrow: /^[1234]/i,
|
|
2506
|
-
abbreviated: /^q[1234]/i,
|
|
2507
|
-
wide: /^[1234](th|st|nd|rd)? quarter/i
|
|
2508
|
-
};
|
|
2509
|
-
var parseQuarterPatterns = {
|
|
2510
|
-
any: [/1/i, /2/i, /3/i, /4/i]
|
|
2511
|
-
};
|
|
2512
|
-
var matchMonthPatterns = {
|
|
2513
|
-
narrow: /^[jfmasond]/i,
|
|
2514
|
-
abbreviated: /^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,
|
|
2515
|
-
wide: /^(january|february|march|april|may|june|july|august|september|october|november|december)/i
|
|
2516
|
-
};
|
|
2517
|
-
var parseMonthPatterns = {
|
|
2518
|
-
narrow: [
|
|
2519
|
-
/^j/i,
|
|
2520
|
-
/^f/i,
|
|
2521
|
-
/^m/i,
|
|
2522
|
-
/^a/i,
|
|
2523
|
-
/^m/i,
|
|
2524
|
-
/^j/i,
|
|
2525
|
-
/^j/i,
|
|
2526
|
-
/^a/i,
|
|
2527
|
-
/^s/i,
|
|
2528
|
-
/^o/i,
|
|
2529
|
-
/^n/i,
|
|
2530
|
-
/^d/i
|
|
2531
|
-
],
|
|
2532
|
-
any: [
|
|
2533
|
-
/^ja/i,
|
|
2534
|
-
/^f/i,
|
|
2535
|
-
/^mar/i,
|
|
2536
|
-
/^ap/i,
|
|
2537
|
-
/^may/i,
|
|
2538
|
-
/^jun/i,
|
|
2539
|
-
/^jul/i,
|
|
2540
|
-
/^au/i,
|
|
2541
|
-
/^s/i,
|
|
2542
|
-
/^o/i,
|
|
2543
|
-
/^n/i,
|
|
2544
|
-
/^d/i
|
|
2545
|
-
]
|
|
2546
|
-
};
|
|
2547
|
-
var matchDayPatterns = {
|
|
2548
|
-
narrow: /^[smtwf]/i,
|
|
2549
|
-
short: /^(su|mo|tu|we|th|fr|sa)/i,
|
|
2550
|
-
abbreviated: /^(sun|mon|tue|wed|thu|fri|sat)/i,
|
|
2551
|
-
wide: /^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i
|
|
2552
|
-
};
|
|
2553
|
-
var parseDayPatterns = {
|
|
2554
|
-
narrow: [/^s/i, /^m/i, /^t/i, /^w/i, /^t/i, /^f/i, /^s/i],
|
|
2555
|
-
any: [/^su/i, /^m/i, /^tu/i, /^w/i, /^th/i, /^f/i, /^sa/i]
|
|
2556
|
-
};
|
|
2557
|
-
var matchDayPeriodPatterns = {
|
|
2558
|
-
narrow: /^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,
|
|
2559
|
-
any: /^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i
|
|
2560
|
-
};
|
|
2561
|
-
var parseDayPeriodPatterns = {
|
|
2562
|
-
any: {
|
|
2563
|
-
am: /^a/i,
|
|
2564
|
-
pm: /^p/i,
|
|
2565
|
-
midnight: /^mi/i,
|
|
2566
|
-
noon: /^no/i,
|
|
2567
|
-
morning: /morning/i,
|
|
2568
|
-
afternoon: /afternoon/i,
|
|
2569
|
-
evening: /evening/i,
|
|
2570
|
-
night: /night/i
|
|
2571
|
-
}
|
|
2572
|
-
};
|
|
2573
|
-
var match = {
|
|
2574
|
-
ordinalNumber: buildMatchPatternFn({
|
|
2575
|
-
matchPattern: matchOrdinalNumberPattern,
|
|
2576
|
-
parsePattern: parseOrdinalNumberPattern,
|
|
2577
|
-
valueCallback: (value) => parseInt(value, 10)
|
|
2578
|
-
}),
|
|
2579
|
-
era: buildMatchFn({
|
|
2580
|
-
matchPatterns: matchEraPatterns,
|
|
2581
|
-
defaultMatchWidth: "wide",
|
|
2582
|
-
parsePatterns: parseEraPatterns,
|
|
2583
|
-
defaultParseWidth: "any"
|
|
2584
|
-
}),
|
|
2585
|
-
quarter: buildMatchFn({
|
|
2586
|
-
matchPatterns: matchQuarterPatterns,
|
|
2587
|
-
defaultMatchWidth: "wide",
|
|
2588
|
-
parsePatterns: parseQuarterPatterns,
|
|
2589
|
-
defaultParseWidth: "any",
|
|
2590
|
-
valueCallback: (index) => index + 1
|
|
2591
|
-
}),
|
|
2592
|
-
month: buildMatchFn({
|
|
2593
|
-
matchPatterns: matchMonthPatterns,
|
|
2594
|
-
defaultMatchWidth: "wide",
|
|
2595
|
-
parsePatterns: parseMonthPatterns,
|
|
2596
|
-
defaultParseWidth: "any"
|
|
2597
|
-
}),
|
|
2598
|
-
day: buildMatchFn({
|
|
2599
|
-
matchPatterns: matchDayPatterns,
|
|
2600
|
-
defaultMatchWidth: "wide",
|
|
2601
|
-
parsePatterns: parseDayPatterns,
|
|
2602
|
-
defaultParseWidth: "any"
|
|
2603
|
-
}),
|
|
2604
|
-
dayPeriod: buildMatchFn({
|
|
2605
|
-
matchPatterns: matchDayPeriodPatterns,
|
|
2606
|
-
defaultMatchWidth: "any",
|
|
2607
|
-
parsePatterns: parseDayPeriodPatterns,
|
|
2608
|
-
defaultParseWidth: "any"
|
|
2609
|
-
})
|
|
2610
|
-
};
|
|
2611
|
-
|
|
2612
|
-
// ../../node_modules/date-fns/locale/en-US.js
|
|
2613
|
-
var enUS = {
|
|
2614
|
-
code: "en-US",
|
|
2615
|
-
formatDistance,
|
|
2616
|
-
formatLong,
|
|
2617
|
-
formatRelative,
|
|
2618
|
-
localize,
|
|
2619
|
-
match,
|
|
2620
|
-
options: {
|
|
2621
|
-
weekStartsOn: 0,
|
|
2622
|
-
firstWeekContainsDate: 1
|
|
2623
|
-
}
|
|
2624
|
-
};
|
|
2625
|
-
|
|
2626
|
-
// ../../node_modules/date-fns/getDayOfYear.js
|
|
2627
|
-
function getDayOfYear(date, options) {
|
|
2628
|
-
const _date = toDate(date, options?.in);
|
|
2629
|
-
const diff = differenceInCalendarDays(_date, startOfYear(_date));
|
|
2630
|
-
const dayOfYear = diff + 1;
|
|
2631
|
-
return dayOfYear;
|
|
2632
|
-
}
|
|
2633
|
-
|
|
2634
|
-
// ../../node_modules/date-fns/getISOWeek.js
|
|
2635
|
-
function getISOWeek(date, options) {
|
|
2636
|
-
const _date = toDate(date, options?.in);
|
|
2637
|
-
const diff = +startOfISOWeek(_date) - +startOfISOWeekYear(_date);
|
|
2638
|
-
return Math.round(diff / millisecondsInWeek) + 1;
|
|
2639
|
-
}
|
|
2640
|
-
|
|
2641
|
-
// ../../node_modules/date-fns/getWeekYear.js
|
|
2642
|
-
function getWeekYear(date, options) {
|
|
2643
|
-
const _date = toDate(date, options?.in);
|
|
2644
|
-
const year = _date.getFullYear();
|
|
2645
|
-
const defaultOptions2 = getDefaultOptions();
|
|
2646
|
-
const firstWeekContainsDate = options?.firstWeekContainsDate ?? options?.locale?.options?.firstWeekContainsDate ?? defaultOptions2.firstWeekContainsDate ?? defaultOptions2.locale?.options?.firstWeekContainsDate ?? 1;
|
|
2647
|
-
const firstWeekOfNextYear = constructFrom(options?.in || date, 0);
|
|
2648
|
-
firstWeekOfNextYear.setFullYear(year + 1, 0, firstWeekContainsDate);
|
|
2649
|
-
firstWeekOfNextYear.setHours(0, 0, 0, 0);
|
|
2650
|
-
const startOfNextYear = startOfWeek(firstWeekOfNextYear, options);
|
|
2651
|
-
const firstWeekOfThisYear = constructFrom(options?.in || date, 0);
|
|
2652
|
-
firstWeekOfThisYear.setFullYear(year, 0, firstWeekContainsDate);
|
|
2653
|
-
firstWeekOfThisYear.setHours(0, 0, 0, 0);
|
|
2654
|
-
const startOfThisYear = startOfWeek(firstWeekOfThisYear, options);
|
|
2655
|
-
if (+_date >= +startOfNextYear) {
|
|
2656
|
-
return year + 1;
|
|
2657
|
-
} else if (+_date >= +startOfThisYear) {
|
|
2658
|
-
return year;
|
|
2659
|
-
} else {
|
|
2660
|
-
return year - 1;
|
|
2661
|
-
}
|
|
2662
|
-
}
|
|
2663
|
-
|
|
2664
|
-
// ../../node_modules/date-fns/startOfWeekYear.js
|
|
2665
|
-
function startOfWeekYear(date, options) {
|
|
2666
|
-
const defaultOptions2 = getDefaultOptions();
|
|
2667
|
-
const firstWeekContainsDate = options?.firstWeekContainsDate ?? options?.locale?.options?.firstWeekContainsDate ?? defaultOptions2.firstWeekContainsDate ?? defaultOptions2.locale?.options?.firstWeekContainsDate ?? 1;
|
|
2668
|
-
const year = getWeekYear(date, options);
|
|
2669
|
-
const firstWeek = constructFrom(options?.in || date, 0);
|
|
2670
|
-
firstWeek.setFullYear(year, 0, firstWeekContainsDate);
|
|
2671
|
-
firstWeek.setHours(0, 0, 0, 0);
|
|
2672
|
-
const _date = startOfWeek(firstWeek, options);
|
|
2673
|
-
return _date;
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
// ../../node_modules/date-fns/getWeek.js
|
|
2677
|
-
function getWeek(date, options) {
|
|
2678
|
-
const _date = toDate(date, options?.in);
|
|
2679
|
-
const diff = +startOfWeek(_date, options) - +startOfWeekYear(_date, options);
|
|
2680
|
-
return Math.round(diff / millisecondsInWeek) + 1;
|
|
2681
|
-
}
|
|
2682
|
-
|
|
2683
|
-
// ../../node_modules/date-fns/_lib/addLeadingZeros.js
|
|
2684
|
-
function addLeadingZeros(number, targetLength) {
|
|
2685
|
-
const sign = number < 0 ? "-" : "";
|
|
2686
|
-
const output = Math.abs(number).toString().padStart(targetLength, "0");
|
|
2687
|
-
return sign + output;
|
|
2688
|
-
}
|
|
2689
|
-
|
|
2690
|
-
// ../../node_modules/date-fns/_lib/format/lightFormatters.js
|
|
2691
|
-
var lightFormatters = {
|
|
2692
|
-
// Year
|
|
2693
|
-
y(date, token) {
|
|
2694
|
-
const signedYear = date.getFullYear();
|
|
2695
|
-
const year = signedYear > 0 ? signedYear : 1 - signedYear;
|
|
2696
|
-
return addLeadingZeros(token === "yy" ? year % 100 : year, token.length);
|
|
2697
|
-
},
|
|
2698
|
-
// Month
|
|
2699
|
-
M(date, token) {
|
|
2700
|
-
const month = date.getMonth();
|
|
2701
|
-
return token === "M" ? String(month + 1) : addLeadingZeros(month + 1, 2);
|
|
2702
|
-
},
|
|
2703
|
-
// Day of the month
|
|
2704
|
-
d(date, token) {
|
|
2705
|
-
return addLeadingZeros(date.getDate(), token.length);
|
|
2706
|
-
},
|
|
2707
|
-
// AM or PM
|
|
2708
|
-
a(date, token) {
|
|
2709
|
-
const dayPeriodEnumValue = date.getHours() / 12 >= 1 ? "pm" : "am";
|
|
2710
|
-
switch (token) {
|
|
2711
|
-
case "a":
|
|
2712
|
-
case "aa":
|
|
2713
|
-
return dayPeriodEnumValue.toUpperCase();
|
|
2714
|
-
case "aaa":
|
|
2715
|
-
return dayPeriodEnumValue;
|
|
2716
|
-
case "aaaaa":
|
|
2717
|
-
return dayPeriodEnumValue[0];
|
|
2718
|
-
case "aaaa":
|
|
2719
|
-
default:
|
|
2720
|
-
return dayPeriodEnumValue === "am" ? "a.m." : "p.m.";
|
|
2721
|
-
}
|
|
2722
|
-
},
|
|
2723
|
-
// Hour [1-12]
|
|
2724
|
-
h(date, token) {
|
|
2725
|
-
return addLeadingZeros(date.getHours() % 12 || 12, token.length);
|
|
2726
|
-
},
|
|
2727
|
-
// Hour [0-23]
|
|
2728
|
-
H(date, token) {
|
|
2729
|
-
return addLeadingZeros(date.getHours(), token.length);
|
|
2730
|
-
},
|
|
2731
|
-
// Minute
|
|
2732
|
-
m(date, token) {
|
|
2733
|
-
return addLeadingZeros(date.getMinutes(), token.length);
|
|
2734
|
-
},
|
|
2735
|
-
// Second
|
|
2736
|
-
s(date, token) {
|
|
2737
|
-
return addLeadingZeros(date.getSeconds(), token.length);
|
|
2738
|
-
},
|
|
2739
|
-
// Fraction of second
|
|
2740
|
-
S(date, token) {
|
|
2741
|
-
const numberOfDigits = token.length;
|
|
2742
|
-
const milliseconds = date.getMilliseconds();
|
|
2743
|
-
const fractionalSeconds = Math.trunc(
|
|
2744
|
-
milliseconds * Math.pow(10, numberOfDigits - 3)
|
|
2745
|
-
);
|
|
2746
|
-
return addLeadingZeros(fractionalSeconds, token.length);
|
|
2747
|
-
}
|
|
2748
|
-
};
|
|
2749
|
-
|
|
2750
|
-
// ../../node_modules/date-fns/_lib/format/formatters.js
|
|
2751
|
-
var dayPeriodEnum = {
|
|
2752
|
-
am: "am",
|
|
2753
|
-
pm: "pm",
|
|
2754
|
-
midnight: "midnight",
|
|
2755
|
-
noon: "noon",
|
|
2756
|
-
morning: "morning",
|
|
2757
|
-
afternoon: "afternoon",
|
|
2758
|
-
evening: "evening",
|
|
2759
|
-
night: "night"
|
|
2760
|
-
};
|
|
2761
|
-
var formatters = {
|
|
2762
|
-
// Era
|
|
2763
|
-
G: function(date, token, localize2) {
|
|
2764
|
-
const era = date.getFullYear() > 0 ? 1 : 0;
|
|
2765
|
-
switch (token) {
|
|
2766
|
-
// AD, BC
|
|
2767
|
-
case "G":
|
|
2768
|
-
case "GG":
|
|
2769
|
-
case "GGG":
|
|
2770
|
-
return localize2.era(era, { width: "abbreviated" });
|
|
2771
|
-
// A, B
|
|
2772
|
-
case "GGGGG":
|
|
2773
|
-
return localize2.era(era, { width: "narrow" });
|
|
2774
|
-
// Anno Domini, Before Christ
|
|
2775
|
-
case "GGGG":
|
|
2776
|
-
default:
|
|
2777
|
-
return localize2.era(era, { width: "wide" });
|
|
2778
|
-
}
|
|
2779
|
-
},
|
|
2780
|
-
// Year
|
|
2781
|
-
y: function(date, token, localize2) {
|
|
2782
|
-
if (token === "yo") {
|
|
2783
|
-
const signedYear = date.getFullYear();
|
|
2784
|
-
const year = signedYear > 0 ? signedYear : 1 - signedYear;
|
|
2785
|
-
return localize2.ordinalNumber(year, { unit: "year" });
|
|
2786
|
-
}
|
|
2787
|
-
return lightFormatters.y(date, token);
|
|
2788
|
-
},
|
|
2789
|
-
// Local week-numbering year
|
|
2790
|
-
Y: function(date, token, localize2, options) {
|
|
2791
|
-
const signedWeekYear = getWeekYear(date, options);
|
|
2792
|
-
const weekYear = signedWeekYear > 0 ? signedWeekYear : 1 - signedWeekYear;
|
|
2793
|
-
if (token === "YY") {
|
|
2794
|
-
const twoDigitYear = weekYear % 100;
|
|
2795
|
-
return addLeadingZeros(twoDigitYear, 2);
|
|
2796
|
-
}
|
|
2797
|
-
if (token === "Yo") {
|
|
2798
|
-
return localize2.ordinalNumber(weekYear, { unit: "year" });
|
|
2799
|
-
}
|
|
2800
|
-
return addLeadingZeros(weekYear, token.length);
|
|
2801
|
-
},
|
|
2802
|
-
// ISO week-numbering year
|
|
2803
|
-
R: function(date, token) {
|
|
2804
|
-
const isoWeekYear = getISOWeekYear(date);
|
|
2805
|
-
return addLeadingZeros(isoWeekYear, token.length);
|
|
2806
|
-
},
|
|
2807
|
-
// Extended year. This is a single number designating the year of this calendar system.
|
|
2808
|
-
// The main difference between `y` and `u` localizers are B.C. years:
|
|
2809
|
-
// | Year | `y` | `u` |
|
|
2810
|
-
// |------|-----|-----|
|
|
2811
|
-
// | AC 1 | 1 | 1 |
|
|
2812
|
-
// | BC 1 | 1 | 0 |
|
|
2813
|
-
// | BC 2 | 2 | -1 |
|
|
2814
|
-
// Also `yy` always returns the last two digits of a year,
|
|
2815
|
-
// while `uu` pads single digit years to 2 characters and returns other years unchanged.
|
|
2816
|
-
u: function(date, token) {
|
|
2817
|
-
const year = date.getFullYear();
|
|
2818
|
-
return addLeadingZeros(year, token.length);
|
|
2819
|
-
},
|
|
2820
|
-
// Quarter
|
|
2821
|
-
Q: function(date, token, localize2) {
|
|
2822
|
-
const quarter = Math.ceil((date.getMonth() + 1) / 3);
|
|
2823
|
-
switch (token) {
|
|
2824
|
-
// 1, 2, 3, 4
|
|
2825
|
-
case "Q":
|
|
2826
|
-
return String(quarter);
|
|
2827
|
-
// 01, 02, 03, 04
|
|
2828
|
-
case "QQ":
|
|
2829
|
-
return addLeadingZeros(quarter, 2);
|
|
2830
|
-
// 1st, 2nd, 3rd, 4th
|
|
2831
|
-
case "Qo":
|
|
2832
|
-
return localize2.ordinalNumber(quarter, { unit: "quarter" });
|
|
2833
|
-
// Q1, Q2, Q3, Q4
|
|
2834
|
-
case "QQQ":
|
|
2835
|
-
return localize2.quarter(quarter, {
|
|
2836
|
-
width: "abbreviated",
|
|
2837
|
-
context: "formatting"
|
|
2838
|
-
});
|
|
2839
|
-
// 1, 2, 3, 4 (narrow quarter; could be not numerical)
|
|
2840
|
-
case "QQQQQ":
|
|
2841
|
-
return localize2.quarter(quarter, {
|
|
2842
|
-
width: "narrow",
|
|
2843
|
-
context: "formatting"
|
|
2844
|
-
});
|
|
2845
|
-
// 1st quarter, 2nd quarter, ...
|
|
2846
|
-
case "QQQQ":
|
|
2847
|
-
default:
|
|
2848
|
-
return localize2.quarter(quarter, {
|
|
2849
|
-
width: "wide",
|
|
2850
|
-
context: "formatting"
|
|
2851
|
-
});
|
|
2852
|
-
}
|
|
2853
|
-
},
|
|
2854
|
-
// Stand-alone quarter
|
|
2855
|
-
q: function(date, token, localize2) {
|
|
2856
|
-
const quarter = Math.ceil((date.getMonth() + 1) / 3);
|
|
2857
|
-
switch (token) {
|
|
2858
|
-
// 1, 2, 3, 4
|
|
2859
|
-
case "q":
|
|
2860
|
-
return String(quarter);
|
|
2861
|
-
// 01, 02, 03, 04
|
|
2862
|
-
case "qq":
|
|
2863
|
-
return addLeadingZeros(quarter, 2);
|
|
2864
|
-
// 1st, 2nd, 3rd, 4th
|
|
2865
|
-
case "qo":
|
|
2866
|
-
return localize2.ordinalNumber(quarter, { unit: "quarter" });
|
|
2867
|
-
// Q1, Q2, Q3, Q4
|
|
2868
|
-
case "qqq":
|
|
2869
|
-
return localize2.quarter(quarter, {
|
|
2870
|
-
width: "abbreviated",
|
|
2871
|
-
context: "standalone"
|
|
2872
|
-
});
|
|
2873
|
-
// 1, 2, 3, 4 (narrow quarter; could be not numerical)
|
|
2874
|
-
case "qqqqq":
|
|
2875
|
-
return localize2.quarter(quarter, {
|
|
2876
|
-
width: "narrow",
|
|
2877
|
-
context: "standalone"
|
|
2878
|
-
});
|
|
2879
|
-
// 1st quarter, 2nd quarter, ...
|
|
2880
|
-
case "qqqq":
|
|
2881
|
-
default:
|
|
2882
|
-
return localize2.quarter(quarter, {
|
|
2883
|
-
width: "wide",
|
|
2884
|
-
context: "standalone"
|
|
2885
|
-
});
|
|
2886
|
-
}
|
|
2887
|
-
},
|
|
2888
|
-
// Month
|
|
2889
|
-
M: function(date, token, localize2) {
|
|
2890
|
-
const month = date.getMonth();
|
|
2891
|
-
switch (token) {
|
|
2892
|
-
case "M":
|
|
2893
|
-
case "MM":
|
|
2894
|
-
return lightFormatters.M(date, token);
|
|
2895
|
-
// 1st, 2nd, ..., 12th
|
|
2896
|
-
case "Mo":
|
|
2897
|
-
return localize2.ordinalNumber(month + 1, { unit: "month" });
|
|
2898
|
-
// Jan, Feb, ..., Dec
|
|
2899
|
-
case "MMM":
|
|
2900
|
-
return localize2.month(month, {
|
|
2901
|
-
width: "abbreviated",
|
|
2902
|
-
context: "formatting"
|
|
2903
|
-
});
|
|
2904
|
-
// J, F, ..., D
|
|
2905
|
-
case "MMMMM":
|
|
2906
|
-
return localize2.month(month, {
|
|
2907
|
-
width: "narrow",
|
|
2908
|
-
context: "formatting"
|
|
2909
|
-
});
|
|
2910
|
-
// January, February, ..., December
|
|
2911
|
-
case "MMMM":
|
|
2912
|
-
default:
|
|
2913
|
-
return localize2.month(month, { width: "wide", context: "formatting" });
|
|
2914
|
-
}
|
|
2915
|
-
},
|
|
2916
|
-
// Stand-alone month
|
|
2917
|
-
L: function(date, token, localize2) {
|
|
2918
|
-
const month = date.getMonth();
|
|
2919
|
-
switch (token) {
|
|
2920
|
-
// 1, 2, ..., 12
|
|
2921
|
-
case "L":
|
|
2922
|
-
return String(month + 1);
|
|
2923
|
-
// 01, 02, ..., 12
|
|
2924
|
-
case "LL":
|
|
2925
|
-
return addLeadingZeros(month + 1, 2);
|
|
2926
|
-
// 1st, 2nd, ..., 12th
|
|
2927
|
-
case "Lo":
|
|
2928
|
-
return localize2.ordinalNumber(month + 1, { unit: "month" });
|
|
2929
|
-
// Jan, Feb, ..., Dec
|
|
2930
|
-
case "LLL":
|
|
2931
|
-
return localize2.month(month, {
|
|
2932
|
-
width: "abbreviated",
|
|
2933
|
-
context: "standalone"
|
|
2934
|
-
});
|
|
2935
|
-
// J, F, ..., D
|
|
2936
|
-
case "LLLLL":
|
|
2937
|
-
return localize2.month(month, {
|
|
2938
|
-
width: "narrow",
|
|
2939
|
-
context: "standalone"
|
|
2940
|
-
});
|
|
2941
|
-
// January, February, ..., December
|
|
2942
|
-
case "LLLL":
|
|
2943
|
-
default:
|
|
2944
|
-
return localize2.month(month, { width: "wide", context: "standalone" });
|
|
2945
|
-
}
|
|
2946
|
-
},
|
|
2947
|
-
// Local week of year
|
|
2948
|
-
w: function(date, token, localize2, options) {
|
|
2949
|
-
const week = getWeek(date, options);
|
|
2950
|
-
if (token === "wo") {
|
|
2951
|
-
return localize2.ordinalNumber(week, { unit: "week" });
|
|
2952
|
-
}
|
|
2953
|
-
return addLeadingZeros(week, token.length);
|
|
2954
|
-
},
|
|
2955
|
-
// ISO week of year
|
|
2956
|
-
I: function(date, token, localize2) {
|
|
2957
|
-
const isoWeek = getISOWeek(date);
|
|
2958
|
-
if (token === "Io") {
|
|
2959
|
-
return localize2.ordinalNumber(isoWeek, { unit: "week" });
|
|
2960
|
-
}
|
|
2961
|
-
return addLeadingZeros(isoWeek, token.length);
|
|
2962
|
-
},
|
|
2963
|
-
// Day of the month
|
|
2964
|
-
d: function(date, token, localize2) {
|
|
2965
|
-
if (token === "do") {
|
|
2966
|
-
return localize2.ordinalNumber(date.getDate(), { unit: "date" });
|
|
2967
|
-
}
|
|
2968
|
-
return lightFormatters.d(date, token);
|
|
2969
|
-
},
|
|
2970
|
-
// Day of year
|
|
2971
|
-
D: function(date, token, localize2) {
|
|
2972
|
-
const dayOfYear = getDayOfYear(date);
|
|
2973
|
-
if (token === "Do") {
|
|
2974
|
-
return localize2.ordinalNumber(dayOfYear, { unit: "dayOfYear" });
|
|
2975
|
-
}
|
|
2976
|
-
return addLeadingZeros(dayOfYear, token.length);
|
|
2977
|
-
},
|
|
2978
|
-
// Day of week
|
|
2979
|
-
E: function(date, token, localize2) {
|
|
2980
|
-
const dayOfWeek = date.getDay();
|
|
2981
|
-
switch (token) {
|
|
2982
|
-
// Tue
|
|
2983
|
-
case "E":
|
|
2984
|
-
case "EE":
|
|
2985
|
-
case "EEE":
|
|
2986
|
-
return localize2.day(dayOfWeek, {
|
|
2987
|
-
width: "abbreviated",
|
|
2988
|
-
context: "formatting"
|
|
2989
|
-
});
|
|
2990
|
-
// T
|
|
2991
|
-
case "EEEEE":
|
|
2992
|
-
return localize2.day(dayOfWeek, {
|
|
2993
|
-
width: "narrow",
|
|
2994
|
-
context: "formatting"
|
|
2995
|
-
});
|
|
2996
|
-
// Tu
|
|
2997
|
-
case "EEEEEE":
|
|
2998
|
-
return localize2.day(dayOfWeek, {
|
|
2999
|
-
width: "short",
|
|
3000
|
-
context: "formatting"
|
|
3001
|
-
});
|
|
3002
|
-
// Tuesday
|
|
3003
|
-
case "EEEE":
|
|
3004
|
-
default:
|
|
3005
|
-
return localize2.day(dayOfWeek, {
|
|
3006
|
-
width: "wide",
|
|
3007
|
-
context: "formatting"
|
|
3008
|
-
});
|
|
3009
|
-
}
|
|
3010
|
-
},
|
|
3011
|
-
// Local day of week
|
|
3012
|
-
e: function(date, token, localize2, options) {
|
|
3013
|
-
const dayOfWeek = date.getDay();
|
|
3014
|
-
const localDayOfWeek = (dayOfWeek - options.weekStartsOn + 8) % 7 || 7;
|
|
3015
|
-
switch (token) {
|
|
3016
|
-
// Numerical value (Nth day of week with current locale or weekStartsOn)
|
|
3017
|
-
case "e":
|
|
3018
|
-
return String(localDayOfWeek);
|
|
3019
|
-
// Padded numerical value
|
|
3020
|
-
case "ee":
|
|
3021
|
-
return addLeadingZeros(localDayOfWeek, 2);
|
|
3022
|
-
// 1st, 2nd, ..., 7th
|
|
3023
|
-
case "eo":
|
|
3024
|
-
return localize2.ordinalNumber(localDayOfWeek, { unit: "day" });
|
|
3025
|
-
case "eee":
|
|
3026
|
-
return localize2.day(dayOfWeek, {
|
|
3027
|
-
width: "abbreviated",
|
|
3028
|
-
context: "formatting"
|
|
3029
|
-
});
|
|
3030
|
-
// T
|
|
3031
|
-
case "eeeee":
|
|
3032
|
-
return localize2.day(dayOfWeek, {
|
|
3033
|
-
width: "narrow",
|
|
3034
|
-
context: "formatting"
|
|
3035
|
-
});
|
|
3036
|
-
// Tu
|
|
3037
|
-
case "eeeeee":
|
|
3038
|
-
return localize2.day(dayOfWeek, {
|
|
3039
|
-
width: "short",
|
|
3040
|
-
context: "formatting"
|
|
3041
|
-
});
|
|
3042
|
-
// Tuesday
|
|
3043
|
-
case "eeee":
|
|
3044
|
-
default:
|
|
3045
|
-
return localize2.day(dayOfWeek, {
|
|
3046
|
-
width: "wide",
|
|
3047
|
-
context: "formatting"
|
|
3048
|
-
});
|
|
3049
|
-
}
|
|
3050
|
-
},
|
|
3051
|
-
// Stand-alone local day of week
|
|
3052
|
-
c: function(date, token, localize2, options) {
|
|
3053
|
-
const dayOfWeek = date.getDay();
|
|
3054
|
-
const localDayOfWeek = (dayOfWeek - options.weekStartsOn + 8) % 7 || 7;
|
|
3055
|
-
switch (token) {
|
|
3056
|
-
// Numerical value (same as in `e`)
|
|
3057
|
-
case "c":
|
|
3058
|
-
return String(localDayOfWeek);
|
|
3059
|
-
// Padded numerical value
|
|
3060
|
-
case "cc":
|
|
3061
|
-
return addLeadingZeros(localDayOfWeek, token.length);
|
|
3062
|
-
// 1st, 2nd, ..., 7th
|
|
3063
|
-
case "co":
|
|
3064
|
-
return localize2.ordinalNumber(localDayOfWeek, { unit: "day" });
|
|
3065
|
-
case "ccc":
|
|
3066
|
-
return localize2.day(dayOfWeek, {
|
|
3067
|
-
width: "abbreviated",
|
|
3068
|
-
context: "standalone"
|
|
3069
|
-
});
|
|
3070
|
-
// T
|
|
3071
|
-
case "ccccc":
|
|
3072
|
-
return localize2.day(dayOfWeek, {
|
|
3073
|
-
width: "narrow",
|
|
3074
|
-
context: "standalone"
|
|
3075
|
-
});
|
|
3076
|
-
// Tu
|
|
3077
|
-
case "cccccc":
|
|
3078
|
-
return localize2.day(dayOfWeek, {
|
|
3079
|
-
width: "short",
|
|
3080
|
-
context: "standalone"
|
|
3081
|
-
});
|
|
3082
|
-
// Tuesday
|
|
3083
|
-
case "cccc":
|
|
3084
|
-
default:
|
|
3085
|
-
return localize2.day(dayOfWeek, {
|
|
3086
|
-
width: "wide",
|
|
3087
|
-
context: "standalone"
|
|
3088
|
-
});
|
|
3089
|
-
}
|
|
3090
|
-
},
|
|
3091
|
-
// ISO day of week
|
|
3092
|
-
i: function(date, token, localize2) {
|
|
3093
|
-
const dayOfWeek = date.getDay();
|
|
3094
|
-
const isoDayOfWeek = dayOfWeek === 0 ? 7 : dayOfWeek;
|
|
3095
|
-
switch (token) {
|
|
3096
|
-
// 2
|
|
3097
|
-
case "i":
|
|
3098
|
-
return String(isoDayOfWeek);
|
|
3099
|
-
// 02
|
|
3100
|
-
case "ii":
|
|
3101
|
-
return addLeadingZeros(isoDayOfWeek, token.length);
|
|
3102
|
-
// 2nd
|
|
3103
|
-
case "io":
|
|
3104
|
-
return localize2.ordinalNumber(isoDayOfWeek, { unit: "day" });
|
|
3105
|
-
// Tue
|
|
3106
|
-
case "iii":
|
|
3107
|
-
return localize2.day(dayOfWeek, {
|
|
3108
|
-
width: "abbreviated",
|
|
3109
|
-
context: "formatting"
|
|
3110
|
-
});
|
|
3111
|
-
// T
|
|
3112
|
-
case "iiiii":
|
|
3113
|
-
return localize2.day(dayOfWeek, {
|
|
3114
|
-
width: "narrow",
|
|
3115
|
-
context: "formatting"
|
|
3116
|
-
});
|
|
3117
|
-
// Tu
|
|
3118
|
-
case "iiiiii":
|
|
3119
|
-
return localize2.day(dayOfWeek, {
|
|
3120
|
-
width: "short",
|
|
3121
|
-
context: "formatting"
|
|
3122
|
-
});
|
|
3123
|
-
// Tuesday
|
|
3124
|
-
case "iiii":
|
|
3125
|
-
default:
|
|
3126
|
-
return localize2.day(dayOfWeek, {
|
|
3127
|
-
width: "wide",
|
|
3128
|
-
context: "formatting"
|
|
3129
|
-
});
|
|
3130
|
-
}
|
|
3131
|
-
},
|
|
3132
|
-
// AM or PM
|
|
3133
|
-
a: function(date, token, localize2) {
|
|
3134
|
-
const hours = date.getHours();
|
|
3135
|
-
const dayPeriodEnumValue = hours / 12 >= 1 ? "pm" : "am";
|
|
3136
|
-
switch (token) {
|
|
3137
|
-
case "a":
|
|
3138
|
-
case "aa":
|
|
3139
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3140
|
-
width: "abbreviated",
|
|
3141
|
-
context: "formatting"
|
|
3142
|
-
});
|
|
3143
|
-
case "aaa":
|
|
3144
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3145
|
-
width: "abbreviated",
|
|
3146
|
-
context: "formatting"
|
|
3147
|
-
}).toLowerCase();
|
|
3148
|
-
case "aaaaa":
|
|
3149
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3150
|
-
width: "narrow",
|
|
3151
|
-
context: "formatting"
|
|
3152
|
-
});
|
|
3153
|
-
case "aaaa":
|
|
3154
|
-
default:
|
|
3155
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3156
|
-
width: "wide",
|
|
3157
|
-
context: "formatting"
|
|
3158
|
-
});
|
|
3159
|
-
}
|
|
3160
|
-
},
|
|
3161
|
-
// AM, PM, midnight, noon
|
|
3162
|
-
b: function(date, token, localize2) {
|
|
3163
|
-
const hours = date.getHours();
|
|
3164
|
-
let dayPeriodEnumValue;
|
|
3165
|
-
if (hours === 12) {
|
|
3166
|
-
dayPeriodEnumValue = dayPeriodEnum.noon;
|
|
3167
|
-
} else if (hours === 0) {
|
|
3168
|
-
dayPeriodEnumValue = dayPeriodEnum.midnight;
|
|
3169
|
-
} else {
|
|
3170
|
-
dayPeriodEnumValue = hours / 12 >= 1 ? "pm" : "am";
|
|
3171
|
-
}
|
|
3172
|
-
switch (token) {
|
|
3173
|
-
case "b":
|
|
3174
|
-
case "bb":
|
|
3175
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3176
|
-
width: "abbreviated",
|
|
3177
|
-
context: "formatting"
|
|
3178
|
-
});
|
|
3179
|
-
case "bbb":
|
|
3180
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3181
|
-
width: "abbreviated",
|
|
3182
|
-
context: "formatting"
|
|
3183
|
-
}).toLowerCase();
|
|
3184
|
-
case "bbbbb":
|
|
3185
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3186
|
-
width: "narrow",
|
|
3187
|
-
context: "formatting"
|
|
3188
|
-
});
|
|
3189
|
-
case "bbbb":
|
|
3190
|
-
default:
|
|
3191
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3192
|
-
width: "wide",
|
|
3193
|
-
context: "formatting"
|
|
3194
|
-
});
|
|
3195
|
-
}
|
|
3196
|
-
},
|
|
3197
|
-
// in the morning, in the afternoon, in the evening, at night
|
|
3198
|
-
B: function(date, token, localize2) {
|
|
3199
|
-
const hours = date.getHours();
|
|
3200
|
-
let dayPeriodEnumValue;
|
|
3201
|
-
if (hours >= 17) {
|
|
3202
|
-
dayPeriodEnumValue = dayPeriodEnum.evening;
|
|
3203
|
-
} else if (hours >= 12) {
|
|
3204
|
-
dayPeriodEnumValue = dayPeriodEnum.afternoon;
|
|
3205
|
-
} else if (hours >= 4) {
|
|
3206
|
-
dayPeriodEnumValue = dayPeriodEnum.morning;
|
|
3207
|
-
} else {
|
|
3208
|
-
dayPeriodEnumValue = dayPeriodEnum.night;
|
|
3209
|
-
}
|
|
3210
|
-
switch (token) {
|
|
3211
|
-
case "B":
|
|
3212
|
-
case "BB":
|
|
3213
|
-
case "BBB":
|
|
3214
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3215
|
-
width: "abbreviated",
|
|
3216
|
-
context: "formatting"
|
|
3217
|
-
});
|
|
3218
|
-
case "BBBBB":
|
|
3219
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3220
|
-
width: "narrow",
|
|
3221
|
-
context: "formatting"
|
|
3222
|
-
});
|
|
3223
|
-
case "BBBB":
|
|
3224
|
-
default:
|
|
3225
|
-
return localize2.dayPeriod(dayPeriodEnumValue, {
|
|
3226
|
-
width: "wide",
|
|
3227
|
-
context: "formatting"
|
|
3228
|
-
});
|
|
3229
|
-
}
|
|
3230
|
-
},
|
|
3231
|
-
// Hour [1-12]
|
|
3232
|
-
h: function(date, token, localize2) {
|
|
3233
|
-
if (token === "ho") {
|
|
3234
|
-
let hours = date.getHours() % 12;
|
|
3235
|
-
if (hours === 0) hours = 12;
|
|
3236
|
-
return localize2.ordinalNumber(hours, { unit: "hour" });
|
|
3237
|
-
}
|
|
3238
|
-
return lightFormatters.h(date, token);
|
|
3239
|
-
},
|
|
3240
|
-
// Hour [0-23]
|
|
3241
|
-
H: function(date, token, localize2) {
|
|
3242
|
-
if (token === "Ho") {
|
|
3243
|
-
return localize2.ordinalNumber(date.getHours(), { unit: "hour" });
|
|
3244
|
-
}
|
|
3245
|
-
return lightFormatters.H(date, token);
|
|
3246
|
-
},
|
|
3247
|
-
// Hour [0-11]
|
|
3248
|
-
K: function(date, token, localize2) {
|
|
3249
|
-
const hours = date.getHours() % 12;
|
|
3250
|
-
if (token === "Ko") {
|
|
3251
|
-
return localize2.ordinalNumber(hours, { unit: "hour" });
|
|
3252
|
-
}
|
|
3253
|
-
return addLeadingZeros(hours, token.length);
|
|
3254
|
-
},
|
|
3255
|
-
// Hour [1-24]
|
|
3256
|
-
k: function(date, token, localize2) {
|
|
3257
|
-
let hours = date.getHours();
|
|
3258
|
-
if (hours === 0) hours = 24;
|
|
3259
|
-
if (token === "ko") {
|
|
3260
|
-
return localize2.ordinalNumber(hours, { unit: "hour" });
|
|
3261
|
-
}
|
|
3262
|
-
return addLeadingZeros(hours, token.length);
|
|
3263
|
-
},
|
|
3264
|
-
// Minute
|
|
3265
|
-
m: function(date, token, localize2) {
|
|
3266
|
-
if (token === "mo") {
|
|
3267
|
-
return localize2.ordinalNumber(date.getMinutes(), { unit: "minute" });
|
|
3268
|
-
}
|
|
3269
|
-
return lightFormatters.m(date, token);
|
|
3270
|
-
},
|
|
3271
|
-
// Second
|
|
3272
|
-
s: function(date, token, localize2) {
|
|
3273
|
-
if (token === "so") {
|
|
3274
|
-
return localize2.ordinalNumber(date.getSeconds(), { unit: "second" });
|
|
3275
|
-
}
|
|
3276
|
-
return lightFormatters.s(date, token);
|
|
3277
|
-
},
|
|
3278
|
-
// Fraction of second
|
|
3279
|
-
S: function(date, token) {
|
|
3280
|
-
return lightFormatters.S(date, token);
|
|
3281
|
-
},
|
|
3282
|
-
// Timezone (ISO-8601. If offset is 0, output is always `'Z'`)
|
|
3283
|
-
X: function(date, token, _localize) {
|
|
3284
|
-
const timezoneOffset = date.getTimezoneOffset();
|
|
3285
|
-
if (timezoneOffset === 0) {
|
|
3286
|
-
return "Z";
|
|
3287
|
-
}
|
|
3288
|
-
switch (token) {
|
|
3289
|
-
// Hours and optional minutes
|
|
3290
|
-
case "X":
|
|
3291
|
-
return formatTimezoneWithOptionalMinutes(timezoneOffset);
|
|
3292
|
-
// Hours, minutes and optional seconds without `:` delimiter
|
|
3293
|
-
// Note: neither ISO-8601 nor JavaScript supports seconds in timezone offsets
|
|
3294
|
-
// so this token always has the same output as `XX`
|
|
3295
|
-
case "XXXX":
|
|
3296
|
-
case "XX":
|
|
3297
|
-
return formatTimezone(timezoneOffset);
|
|
3298
|
-
// Hours, minutes and optional seconds with `:` delimiter
|
|
3299
|
-
// Note: neither ISO-8601 nor JavaScript supports seconds in timezone offsets
|
|
3300
|
-
// so this token always has the same output as `XXX`
|
|
3301
|
-
case "XXXXX":
|
|
3302
|
-
case "XXX":
|
|
3303
|
-
// Hours and minutes with `:` delimiter
|
|
3304
|
-
default:
|
|
3305
|
-
return formatTimezone(timezoneOffset, ":");
|
|
3306
|
-
}
|
|
3307
|
-
},
|
|
3308
|
-
// Timezone (ISO-8601. If offset is 0, output is `'+00:00'` or equivalent)
|
|
3309
|
-
x: function(date, token, _localize) {
|
|
3310
|
-
const timezoneOffset = date.getTimezoneOffset();
|
|
3311
|
-
switch (token) {
|
|
3312
|
-
// Hours and optional minutes
|
|
3313
|
-
case "x":
|
|
3314
|
-
return formatTimezoneWithOptionalMinutes(timezoneOffset);
|
|
3315
|
-
// Hours, minutes and optional seconds without `:` delimiter
|
|
3316
|
-
// Note: neither ISO-8601 nor JavaScript supports seconds in timezone offsets
|
|
3317
|
-
// so this token always has the same output as `xx`
|
|
3318
|
-
case "xxxx":
|
|
3319
|
-
case "xx":
|
|
3320
|
-
return formatTimezone(timezoneOffset);
|
|
3321
|
-
// Hours, minutes and optional seconds with `:` delimiter
|
|
3322
|
-
// Note: neither ISO-8601 nor JavaScript supports seconds in timezone offsets
|
|
3323
|
-
// so this token always has the same output as `xxx`
|
|
3324
|
-
case "xxxxx":
|
|
3325
|
-
case "xxx":
|
|
3326
|
-
// Hours and minutes with `:` delimiter
|
|
3327
|
-
default:
|
|
3328
|
-
return formatTimezone(timezoneOffset, ":");
|
|
3329
|
-
}
|
|
3330
|
-
},
|
|
3331
|
-
// Timezone (GMT)
|
|
3332
|
-
O: function(date, token, _localize) {
|
|
3333
|
-
const timezoneOffset = date.getTimezoneOffset();
|
|
3334
|
-
switch (token) {
|
|
3335
|
-
// Short
|
|
3336
|
-
case "O":
|
|
3337
|
-
case "OO":
|
|
3338
|
-
case "OOO":
|
|
3339
|
-
return "GMT" + formatTimezoneShort(timezoneOffset, ":");
|
|
3340
|
-
// Long
|
|
3341
|
-
case "OOOO":
|
|
3342
|
-
default:
|
|
3343
|
-
return "GMT" + formatTimezone(timezoneOffset, ":");
|
|
3344
|
-
}
|
|
3345
|
-
},
|
|
3346
|
-
// Timezone (specific non-location)
|
|
3347
|
-
z: function(date, token, _localize) {
|
|
3348
|
-
const timezoneOffset = date.getTimezoneOffset();
|
|
3349
|
-
switch (token) {
|
|
3350
|
-
// Short
|
|
3351
|
-
case "z":
|
|
3352
|
-
case "zz":
|
|
3353
|
-
case "zzz":
|
|
3354
|
-
return "GMT" + formatTimezoneShort(timezoneOffset, ":");
|
|
3355
|
-
// Long
|
|
3356
|
-
case "zzzz":
|
|
3357
|
-
default:
|
|
3358
|
-
return "GMT" + formatTimezone(timezoneOffset, ":");
|
|
3359
|
-
}
|
|
3360
|
-
},
|
|
3361
|
-
// Seconds timestamp
|
|
3362
|
-
t: function(date, token, _localize) {
|
|
3363
|
-
const timestamp = Math.trunc(+date / 1e3);
|
|
3364
|
-
return addLeadingZeros(timestamp, token.length);
|
|
3365
|
-
},
|
|
3366
|
-
// Milliseconds timestamp
|
|
3367
|
-
T: function(date, token, _localize) {
|
|
3368
|
-
return addLeadingZeros(+date, token.length);
|
|
3369
|
-
}
|
|
3370
|
-
};
|
|
3371
|
-
function formatTimezoneShort(offset, delimiter = "") {
|
|
3372
|
-
const sign = offset > 0 ? "-" : "+";
|
|
3373
|
-
const absOffset = Math.abs(offset);
|
|
3374
|
-
const hours = Math.trunc(absOffset / 60);
|
|
3375
|
-
const minutes = absOffset % 60;
|
|
3376
|
-
if (minutes === 0) {
|
|
3377
|
-
return sign + String(hours);
|
|
3378
|
-
}
|
|
3379
|
-
return sign + String(hours) + delimiter + addLeadingZeros(minutes, 2);
|
|
3380
|
-
}
|
|
3381
|
-
function formatTimezoneWithOptionalMinutes(offset, delimiter) {
|
|
3382
|
-
if (offset % 60 === 0) {
|
|
3383
|
-
const sign = offset > 0 ? "-" : "+";
|
|
3384
|
-
return sign + addLeadingZeros(Math.abs(offset) / 60, 2);
|
|
3385
|
-
}
|
|
3386
|
-
return formatTimezone(offset, delimiter);
|
|
3387
|
-
}
|
|
3388
|
-
function formatTimezone(offset, delimiter = "") {
|
|
3389
|
-
const sign = offset > 0 ? "-" : "+";
|
|
3390
|
-
const absOffset = Math.abs(offset);
|
|
3391
|
-
const hours = addLeadingZeros(Math.trunc(absOffset / 60), 2);
|
|
3392
|
-
const minutes = addLeadingZeros(absOffset % 60, 2);
|
|
3393
|
-
return sign + hours + delimiter + minutes;
|
|
3394
|
-
}
|
|
3395
|
-
|
|
3396
|
-
// ../../node_modules/date-fns/_lib/format/longFormatters.js
|
|
3397
|
-
var dateLongFormatter = (pattern, formatLong2) => {
|
|
3398
|
-
switch (pattern) {
|
|
3399
|
-
case "P":
|
|
3400
|
-
return formatLong2.date({ width: "short" });
|
|
3401
|
-
case "PP":
|
|
3402
|
-
return formatLong2.date({ width: "medium" });
|
|
3403
|
-
case "PPP":
|
|
3404
|
-
return formatLong2.date({ width: "long" });
|
|
3405
|
-
case "PPPP":
|
|
3406
|
-
default:
|
|
3407
|
-
return formatLong2.date({ width: "full" });
|
|
3408
|
-
}
|
|
3409
|
-
};
|
|
3410
|
-
var timeLongFormatter = (pattern, formatLong2) => {
|
|
3411
|
-
switch (pattern) {
|
|
3412
|
-
case "p":
|
|
3413
|
-
return formatLong2.time({ width: "short" });
|
|
3414
|
-
case "pp":
|
|
3415
|
-
return formatLong2.time({ width: "medium" });
|
|
3416
|
-
case "ppp":
|
|
3417
|
-
return formatLong2.time({ width: "long" });
|
|
3418
|
-
case "pppp":
|
|
3419
|
-
default:
|
|
3420
|
-
return formatLong2.time({ width: "full" });
|
|
3421
|
-
}
|
|
3422
|
-
};
|
|
3423
|
-
var dateTimeLongFormatter = (pattern, formatLong2) => {
|
|
3424
|
-
const matchResult = pattern.match(/(P+)(p+)?/) || [];
|
|
3425
|
-
const datePattern = matchResult[1];
|
|
3426
|
-
const timePattern = matchResult[2];
|
|
3427
|
-
if (!timePattern) {
|
|
3428
|
-
return dateLongFormatter(pattern, formatLong2);
|
|
3429
|
-
}
|
|
3430
|
-
let dateTimeFormat;
|
|
3431
|
-
switch (datePattern) {
|
|
3432
|
-
case "P":
|
|
3433
|
-
dateTimeFormat = formatLong2.dateTime({ width: "short" });
|
|
3434
|
-
break;
|
|
3435
|
-
case "PP":
|
|
3436
|
-
dateTimeFormat = formatLong2.dateTime({ width: "medium" });
|
|
3437
|
-
break;
|
|
3438
|
-
case "PPP":
|
|
3439
|
-
dateTimeFormat = formatLong2.dateTime({ width: "long" });
|
|
3440
|
-
break;
|
|
3441
|
-
case "PPPP":
|
|
3442
|
-
default:
|
|
3443
|
-
dateTimeFormat = formatLong2.dateTime({ width: "full" });
|
|
3444
|
-
break;
|
|
3445
|
-
}
|
|
3446
|
-
return dateTimeFormat.replace("{{date}}", dateLongFormatter(datePattern, formatLong2)).replace("{{time}}", timeLongFormatter(timePattern, formatLong2));
|
|
3447
|
-
};
|
|
3448
|
-
var longFormatters = {
|
|
3449
|
-
p: timeLongFormatter,
|
|
3450
|
-
P: dateTimeLongFormatter
|
|
3451
|
-
};
|
|
3452
|
-
|
|
3453
|
-
// ../../node_modules/date-fns/_lib/protectedTokens.js
|
|
3454
|
-
var dayOfYearTokenRE = /^D+$/;
|
|
3455
|
-
var weekYearTokenRE = /^Y+$/;
|
|
3456
|
-
var throwTokens = ["D", "DD", "YY", "YYYY"];
|
|
3457
|
-
function isProtectedDayOfYearToken(token) {
|
|
3458
|
-
return dayOfYearTokenRE.test(token);
|
|
3459
|
-
}
|
|
3460
|
-
function isProtectedWeekYearToken(token) {
|
|
3461
|
-
return weekYearTokenRE.test(token);
|
|
3462
|
-
}
|
|
3463
|
-
function warnOrThrowProtectedError(token, format2, input) {
|
|
3464
|
-
const _message = message(token, format2, input);
|
|
3465
|
-
console.warn(_message);
|
|
3466
|
-
if (throwTokens.includes(token)) throw new RangeError(_message);
|
|
3467
|
-
}
|
|
3468
|
-
function message(token, format2, input) {
|
|
3469
|
-
const subject = token[0] === "Y" ? "years" : "days of the month";
|
|
3470
|
-
return `Use \`${token.toLowerCase()}\` instead of \`${token}\` (in \`${format2}\`) for formatting ${subject} to the input \`${input}\`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md`;
|
|
3471
|
-
}
|
|
3472
|
-
|
|
3473
|
-
// ../../node_modules/date-fns/format.js
|
|
3474
|
-
var formattingTokensRegExp = /[yYQqMLwIdDecihHKkms]o|(\w)\1*|''|'(''|[^'])+('|$)|./g;
|
|
3475
|
-
var longFormattingTokensRegExp = /P+p+|P+|p+|''|'(''|[^'])+('|$)|./g;
|
|
3476
|
-
var escapedStringRegExp = /^'([^]*?)'?$/;
|
|
3477
|
-
var doubleQuoteRegExp = /''/g;
|
|
3478
|
-
var unescapedLatinCharacterRegExp = /[a-zA-Z]/;
|
|
3479
|
-
function format(date, formatStr, options) {
|
|
3480
|
-
const defaultOptions2 = getDefaultOptions();
|
|
3481
|
-
const locale = options?.locale ?? defaultOptions2.locale ?? enUS;
|
|
3482
|
-
const firstWeekContainsDate = options?.firstWeekContainsDate ?? options?.locale?.options?.firstWeekContainsDate ?? defaultOptions2.firstWeekContainsDate ?? defaultOptions2.locale?.options?.firstWeekContainsDate ?? 1;
|
|
3483
|
-
const weekStartsOn = options?.weekStartsOn ?? options?.locale?.options?.weekStartsOn ?? defaultOptions2.weekStartsOn ?? defaultOptions2.locale?.options?.weekStartsOn ?? 0;
|
|
3484
|
-
const originalDate = toDate(date, options?.in);
|
|
3485
|
-
if (!isValid(originalDate)) {
|
|
3486
|
-
throw new RangeError("Invalid time value");
|
|
3487
|
-
}
|
|
3488
|
-
let parts = formatStr.match(longFormattingTokensRegExp).map((substring) => {
|
|
3489
|
-
const firstCharacter = substring[0];
|
|
3490
|
-
if (firstCharacter === "p" || firstCharacter === "P") {
|
|
3491
|
-
const longFormatter = longFormatters[firstCharacter];
|
|
3492
|
-
return longFormatter(substring, locale.formatLong);
|
|
3493
|
-
}
|
|
3494
|
-
return substring;
|
|
3495
|
-
}).join("").match(formattingTokensRegExp).map((substring) => {
|
|
3496
|
-
if (substring === "''") {
|
|
3497
|
-
return { isToken: false, value: "'" };
|
|
3498
|
-
}
|
|
3499
|
-
const firstCharacter = substring[0];
|
|
3500
|
-
if (firstCharacter === "'") {
|
|
3501
|
-
return { isToken: false, value: cleanEscapedString(substring) };
|
|
3502
|
-
}
|
|
3503
|
-
if (formatters[firstCharacter]) {
|
|
3504
|
-
return { isToken: true, value: substring };
|
|
3505
|
-
}
|
|
3506
|
-
if (firstCharacter.match(unescapedLatinCharacterRegExp)) {
|
|
3507
|
-
throw new RangeError(
|
|
3508
|
-
"Format string contains an unescaped latin alphabet character `" + firstCharacter + "`"
|
|
3509
|
-
);
|
|
3510
|
-
}
|
|
3511
|
-
return { isToken: false, value: substring };
|
|
3512
|
-
});
|
|
3513
|
-
if (locale.localize.preprocessor) {
|
|
3514
|
-
parts = locale.localize.preprocessor(originalDate, parts);
|
|
3515
|
-
}
|
|
3516
|
-
const formatterOptions = {
|
|
3517
|
-
firstWeekContainsDate,
|
|
3518
|
-
weekStartsOn,
|
|
3519
|
-
locale
|
|
3520
|
-
};
|
|
3521
|
-
return parts.map((part) => {
|
|
3522
|
-
if (!part.isToken) return part.value;
|
|
3523
|
-
const token = part.value;
|
|
3524
|
-
if (!options?.useAdditionalWeekYearTokens && isProtectedWeekYearToken(token) || !options?.useAdditionalDayOfYearTokens && isProtectedDayOfYearToken(token)) {
|
|
3525
|
-
warnOrThrowProtectedError(token, formatStr, String(date));
|
|
3526
|
-
}
|
|
3527
|
-
const formatter = formatters[token[0]];
|
|
3528
|
-
return formatter(originalDate, token, locale.localize, formatterOptions);
|
|
3529
|
-
}).join("");
|
|
3530
|
-
}
|
|
3531
|
-
function cleanEscapedString(input) {
|
|
3532
|
-
const matched = input.match(escapedStringRegExp);
|
|
3533
|
-
if (!matched) {
|
|
3534
|
-
return input;
|
|
3535
|
-
}
|
|
3536
|
-
return matched[1].replace(doubleQuoteRegExp, "'");
|
|
3537
|
-
}
|
|
3538
|
-
|
|
3539
|
-
// components/customer/chat-message.tsx
|
|
3540
|
-
import {
|
|
3541
|
-
AlertCircle as AlertCircle2,
|
|
3542
|
-
Bot as Bot2,
|
|
3543
|
-
Check,
|
|
3544
|
-
CheckCheck,
|
|
3545
|
-
Paperclip as Paperclip3,
|
|
3546
|
-
User,
|
|
3547
|
-
UserCog
|
|
3548
|
-
} from "lucide-react";
|
|
3549
|
-
|
|
3550
|
-
// components/markdown-renderer.tsx
|
|
3551
|
-
import ReactMarkdown from "react-markdown";
|
|
3552
|
-
import remarkGfm from "remark-gfm";
|
|
3553
|
-
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
3554
|
-
function MarkdownRenderer({ content }) {
|
|
3555
|
-
return /* @__PURE__ */ jsx10(
|
|
3556
|
-
ReactMarkdown,
|
|
3557
|
-
{
|
|
3558
|
-
remarkPlugins: [remarkGfm],
|
|
3559
|
-
components: {
|
|
3560
|
-
a: ({ node, ...props }) => /* @__PURE__ */ jsx10(
|
|
3561
|
-
"a",
|
|
3562
|
-
{
|
|
3563
|
-
target: "_blank",
|
|
3564
|
-
rel: "noopener noreferrer",
|
|
3565
|
-
className: "text-blue-600 hover:underline",
|
|
3566
|
-
...props
|
|
3567
|
-
}
|
|
3568
|
-
),
|
|
3569
|
-
h1: ({ node, ...props }) => /* @__PURE__ */ jsx10("h1", { className: "text-2xl font-bold mt-6 mb-4", ...props }),
|
|
3570
|
-
h2: ({ node, ...props }) => /* @__PURE__ */ jsx10("h2", { className: "text-xl font-semibold mt-5 mb-3", ...props }),
|
|
3571
|
-
h3: ({ node, ...props }) => /* @__PURE__ */ jsx10("h3", { className: "text-lg font-semibold mt-4 mb-2", ...props }),
|
|
3572
|
-
code: ({ node, ...props }) => /* @__PURE__ */ jsx10(
|
|
3573
|
-
"code",
|
|
3574
|
-
{
|
|
3575
|
-
className: "block bg-gray-100 p-3 rounded font-mono",
|
|
3576
|
-
...props
|
|
3577
|
-
}
|
|
3578
|
-
),
|
|
3579
|
-
ul: ({ node, ...props }) => /* @__PURE__ */ jsx10("ul", { className: "list-disc pl-6 my-4", ...props }),
|
|
3580
|
-
ol: ({ node, ...props }) => /* @__PURE__ */ jsx10("ol", { className: "list-decimal pl-6 my-4", ...props }),
|
|
3581
|
-
p: ({ node, ...props }) => /* @__PURE__ */ jsx10("p", { className: "my-4", ...props }),
|
|
3582
|
-
blockquote: ({ node, ...props }) => /* @__PURE__ */ jsx10(
|
|
3583
|
-
"blockquote",
|
|
3584
|
-
{
|
|
3585
|
-
className: "border-l-4 border-gray-200 pl-4 my-4 italic",
|
|
3586
|
-
...props
|
|
3587
|
-
}
|
|
3588
|
-
)
|
|
3589
|
-
},
|
|
3590
|
-
children: content
|
|
3591
|
-
}
|
|
3592
|
-
);
|
|
3593
|
-
}
|
|
3594
|
-
|
|
3595
|
-
// components/customer/chat-message.tsx
|
|
3596
|
-
import { Fragment, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3597
|
-
var Message = ({
|
|
3598
|
-
message: message2,
|
|
3599
|
-
isConsecutive,
|
|
3600
|
-
onRetry
|
|
3601
|
-
}) => {
|
|
3602
|
-
const renderAttachment = (attachment, index) => /* @__PURE__ */ jsx11("div", { className: "mt-2", children: attachment.type === "image" ? /* @__PURE__ */ jsx11(
|
|
3603
|
-
"img",
|
|
3604
|
-
{
|
|
3605
|
-
src: attachment.url,
|
|
3606
|
-
alt: attachment.title,
|
|
3607
|
-
className: "max-w-full rounded",
|
|
3608
|
-
loading: "lazy"
|
|
3609
|
-
}
|
|
3610
|
-
) : /* @__PURE__ */ jsxs8(
|
|
3611
|
-
"a",
|
|
3612
|
-
{
|
|
3613
|
-
href: attachment.url,
|
|
3614
|
-
className: "flex items-center space-x-2 text-sm hover:underline",
|
|
3615
|
-
target: "_blank",
|
|
3616
|
-
rel: "noopener noreferrer",
|
|
3617
|
-
children: [
|
|
3618
|
-
/* @__PURE__ */ jsx11(Paperclip3, { className: "w-4 h-4" }),
|
|
3619
|
-
/* @__PURE__ */ jsx11("span", { children: attachment.title })
|
|
3620
|
-
]
|
|
3621
|
-
}
|
|
3622
|
-
) }, index);
|
|
3623
|
-
return /* @__PURE__ */ jsxs8(
|
|
3624
|
-
"div",
|
|
3625
|
-
{
|
|
3626
|
-
className: "flex justify-start gap-2",
|
|
3627
|
-
"data-message-id": message2.id,
|
|
3628
|
-
role: "article",
|
|
3629
|
-
"aria-label": `Message from ${message2.senderId}`,
|
|
3630
|
-
children: [
|
|
3631
|
-
/* @__PURE__ */ jsx11("div", { className: "flex pt-10 w-10", children: /* @__PURE__ */ jsxs8("div", { className: "w-8 h-8 rounded-full flex justify-center", children: [
|
|
3632
|
-
message2.role === "user" && /* @__PURE__ */ jsx11(User, {}),
|
|
3633
|
-
message2.role === "ai" && /* @__PURE__ */ jsx11(Bot2, {}),
|
|
3634
|
-
message2.role === "humanAgent" && /* @__PURE__ */ jsx11(UserCog, {})
|
|
3635
|
-
] }) }),
|
|
3636
|
-
/* @__PURE__ */ jsxs8(
|
|
3637
|
-
"div",
|
|
3638
|
-
{
|
|
3639
|
-
className: `p-3 rounded-lg max-w-[80%] bg-gray-100 text-primary-900 ${isConsecutive ? "mt-1" : "mt-4"}`,
|
|
3640
|
-
children: [
|
|
3641
|
-
/* @__PURE__ */ jsx11("div", { className: "break-words", children: /* @__PURE__ */ jsx11(MarkdownRenderer, { content: message2.content }) }),
|
|
3642
|
-
message2.attachments?.map(
|
|
3643
|
-
(attachment, i) => renderAttachment(attachment, i)
|
|
3644
|
-
),
|
|
3645
|
-
/* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-end space-x-1 mt-1", children: [
|
|
3646
|
-
/* @__PURE__ */ jsx11("span", { className: "text-xs opacity-70", children: format(message2.time, "HH:mm") }),
|
|
3647
|
-
message2.role === "user" && /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
3648
|
-
message2.status === "sending" && /* @__PURE__ */ jsx11("span", { className: "w-4 h-4 animate-spin rounded-full border-2 border-gray-300 border-t-white" }),
|
|
3649
|
-
message2.status === "sent" && /* @__PURE__ */ jsx11(Check, { className: "w-4 h-4" }),
|
|
3650
|
-
message2.status === "delivered" && /* @__PURE__ */ jsx11(CheckCheck, { className: "w-4 h-4" }),
|
|
3651
|
-
message2.status === "failed" && /* @__PURE__ */ jsx11(
|
|
3652
|
-
"button",
|
|
3653
|
-
{
|
|
3654
|
-
onClick: () => onRetry(message2.id),
|
|
3655
|
-
className: "text-red-300 hover:text-red-400",
|
|
3656
|
-
"aria-label": "Retry sending message",
|
|
3657
|
-
children: /* @__PURE__ */ jsx11(AlertCircle2, { className: "w-4 h-4" })
|
|
3658
|
-
}
|
|
3659
|
-
)
|
|
3660
|
-
] })
|
|
3661
|
-
] })
|
|
3662
|
-
]
|
|
3663
|
-
}
|
|
3664
|
-
)
|
|
3665
|
-
]
|
|
3666
|
-
}
|
|
3667
|
-
);
|
|
3668
|
-
};
|
|
3669
|
-
|
|
3670
|
-
// components/customer/chat-messages.tsx
|
|
3671
|
-
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3672
|
-
var ChatMessages = ({
|
|
3673
|
-
onRetry,
|
|
3674
|
-
messages
|
|
3675
|
-
}) => {
|
|
3676
|
-
const messageEndRef = useRef4(null);
|
|
3677
|
-
const scrollToBottom = () => {
|
|
3678
|
-
messageEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
3679
|
-
};
|
|
3680
|
-
useEffect2(() => {
|
|
3681
|
-
scrollToBottom();
|
|
3682
|
-
}, [messages]);
|
|
3683
|
-
return /* @__PURE__ */ jsxs9(
|
|
3684
|
-
"div",
|
|
3685
|
-
{
|
|
3686
|
-
className: "flex-1 overflow-y-auto p-4 space-y-4 bg-white min-h-[calc(100vh-290px)]",
|
|
3687
|
-
role: "log",
|
|
3688
|
-
"aria-label": "Chat messages",
|
|
3689
|
-
children: [
|
|
3690
|
-
messages.filter(
|
|
3691
|
-
(msg) => (!msg.toolCalls?.length || msg.toolCalls.length === 0) && !msg.toolCallId
|
|
3692
|
-
).map((msg, index) => {
|
|
3693
|
-
const isConsecutive = index > 0 && messages[index - 1].senderId === msg.senderId && msg.time - messages[index - 1].time < 3e5;
|
|
3694
|
-
return /* @__PURE__ */ jsx12(
|
|
3695
|
-
Message,
|
|
3696
|
-
{
|
|
3697
|
-
message: msg,
|
|
3698
|
-
isConsecutive,
|
|
3699
|
-
onRetry
|
|
3700
|
-
},
|
|
3701
|
-
msg.id
|
|
3702
|
-
);
|
|
3703
|
-
}),
|
|
3704
|
-
/* @__PURE__ */ jsx12("div", { ref: messageEndRef })
|
|
3705
|
-
]
|
|
3706
|
-
}
|
|
3707
|
-
);
|
|
3708
|
-
};
|
|
3709
|
-
|
|
3710
|
-
// components/customer/chat-typing-indicator.tsx
|
|
3711
|
-
import { jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3712
|
-
var ChatTypingIndicator = ({
|
|
3713
|
-
typingUsers
|
|
3714
|
-
}) => {
|
|
3715
|
-
if (typingUsers.size === 0) return null;
|
|
3716
|
-
return /* @__PURE__ */ jsxs10("div", { className: "px-4 py-2 text-sm text-gray-500 italic", role: "status", children: [
|
|
3717
|
-
Array.from(typingUsers).join(", "),
|
|
3718
|
-
" typing..."
|
|
3719
|
-
] });
|
|
3720
|
-
};
|
|
3721
|
-
|
|
3722
|
-
// components/customer/generic-chat-widget.tsx
|
|
3723
|
-
import { useState as useState6 } from "react";
|
|
3724
|
-
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3725
|
-
var Alert = ({ title, description }) => /* @__PURE__ */ jsxs11("div", { className: "p-4 bg-red-50 border border-red-200 rounded-lg", role: "alert", children: [
|
|
3726
|
-
/* @__PURE__ */ jsx13("p", { className: "font-medium text-red-800", children: title }),
|
|
3727
|
-
/* @__PURE__ */ jsx13("p", { className: "text-sm text-red-600", children: description })
|
|
3728
|
-
] });
|
|
3729
|
-
var GenericChatWidget = ({
|
|
3730
|
-
onFileUpload
|
|
3731
|
-
}) => {
|
|
3732
|
-
const [typingUsers] = useState6(/* @__PURE__ */ new Set());
|
|
3733
|
-
const { error, messages } = useWebSocketChatCustomerContext();
|
|
3734
|
-
const handleRetry = (messageId) => {
|
|
3735
|
-
};
|
|
3736
|
-
return /* @__PURE__ */ jsxs11(
|
|
3737
|
-
"div",
|
|
3738
|
-
{
|
|
3739
|
-
className: "flex flex-col w-full max-w-lg border rounded-lg shadow-lg",
|
|
3740
|
-
role: "region",
|
|
3741
|
-
"aria-label": "Chat interface",
|
|
3742
|
-
children: [
|
|
3743
|
-
/* @__PURE__ */ jsx13(ChatHeader, {}),
|
|
3744
|
-
error && /* @__PURE__ */ jsx13(Alert, { title: "Error", description: error.message }),
|
|
3745
|
-
/* @__PURE__ */ jsx13(ChatMessages, { onRetry: handleRetry, messages }),
|
|
3746
|
-
/* @__PURE__ */ jsx13(ChatTypingIndicator, { typingUsers }),
|
|
3747
|
-
/* @__PURE__ */ jsx13(ChatInput, { onFileUpload })
|
|
3748
|
-
]
|
|
3749
|
-
}
|
|
3750
|
-
);
|
|
3751
|
-
};
|
|
3752
870
|
export {
|
|
3753
|
-
AdminChatHeader,
|
|
3754
|
-
AdminChatInput,
|
|
3755
|
-
AdminChatList,
|
|
3756
|
-
AdminChatListItem,
|
|
3757
871
|
AgentStatusAway,
|
|
3758
872
|
AgentStatusBusy,
|
|
3759
873
|
AgentStatusOffline,
|
|
@@ -3764,6 +878,7 @@ export {
|
|
|
3764
878
|
AttachmentTypeAudio,
|
|
3765
879
|
AttachmentTypeBullets,
|
|
3766
880
|
AttachmentTypeData,
|
|
881
|
+
AttachmentTypeDataFile,
|
|
3767
882
|
AttachmentTypeDocument,
|
|
3768
883
|
AttachmentTypeDocumentAnalysis,
|
|
3769
884
|
AttachmentTypeDocumentSources,
|
|
@@ -3772,6 +887,7 @@ export {
|
|
|
3772
887
|
AttachmentTypeLocation,
|
|
3773
888
|
AttachmentTypeRecords,
|
|
3774
889
|
AttachmentTypeReferences,
|
|
890
|
+
AttachmentTypeSpreadsheet,
|
|
3775
891
|
AttachmentTypeSticker,
|
|
3776
892
|
AttachmentTypeSubsections,
|
|
3777
893
|
AttachmentTypeVideo,
|
|
@@ -3785,6 +901,10 @@ export {
|
|
|
3785
901
|
ChatEventTypeAgentSessionEnd,
|
|
3786
902
|
ChatEventTypeAgentSessionStart,
|
|
3787
903
|
ChatEventTypeAgentStatusChange,
|
|
904
|
+
ChatEventTypeAttachmentProcessingComplete,
|
|
905
|
+
ChatEventTypeAttachmentProcessingError,
|
|
906
|
+
ChatEventTypeAttachmentProcessingProgress,
|
|
907
|
+
ChatEventTypeAttachmentProcessingStarted,
|
|
3788
908
|
ChatEventTypeBlockUser,
|
|
3789
909
|
ChatEventTypeCSATRequest,
|
|
3790
910
|
ChatEventTypeCSATResponse,
|
|
@@ -3836,6 +956,7 @@ export {
|
|
|
3836
956
|
ChatEventTypePong,
|
|
3837
957
|
ChatEventTypeRead,
|
|
3838
958
|
ChatEventTypeReconnected,
|
|
959
|
+
ChatEventTypeRetryAttachment,
|
|
3839
960
|
ChatEventTypeRoomCreated,
|
|
3840
961
|
ChatEventTypeRoomDeleted,
|
|
3841
962
|
ChatEventTypeRoomUpdated,
|
|
@@ -3870,10 +991,6 @@ export {
|
|
|
3870
991
|
ChatEventTypeUserSuggestedActions,
|
|
3871
992
|
ChatEventTypeWaiting,
|
|
3872
993
|
ChatEventTypeWaitingForAgent,
|
|
3873
|
-
ChatHeader,
|
|
3874
|
-
ChatHumanAgentActions,
|
|
3875
|
-
ChatInput,
|
|
3876
|
-
ChatMessages,
|
|
3877
994
|
ChatRoleAI,
|
|
3878
995
|
ChatRoleDataQuery,
|
|
3879
996
|
ChatRoleEvent,
|
|
@@ -3889,19 +1006,16 @@ export {
|
|
|
3889
1006
|
ChatStatusActive,
|
|
3890
1007
|
ChatStatusArchived,
|
|
3891
1008
|
ChatStatusClosed,
|
|
3892
|
-
ChatStatusCustomerUI,
|
|
3893
1009
|
ChatStatusDisconnected,
|
|
3894
1010
|
ChatTypeCustomerSupport,
|
|
3895
1011
|
ChatTypeDirect,
|
|
3896
1012
|
ChatTypeGroup,
|
|
3897
1013
|
ChatTypePrivateRoom,
|
|
3898
1014
|
ChatTypePublicRoom,
|
|
3899
|
-
ChatTypingIndicator,
|
|
3900
1015
|
CompleteChatByAgentSubject,
|
|
3901
1016
|
CreateAgentQueueSubject,
|
|
3902
1017
|
DeleteAgentQueueSubject,
|
|
3903
1018
|
EndAgentSessionSubject,
|
|
3904
|
-
GenericChatWidget,
|
|
3905
1019
|
GetActiveChatCountSubject,
|
|
3906
1020
|
GetActiveChatsSubject,
|
|
3907
1021
|
GetAgentQueuesSubject,
|
|
@@ -3937,14 +1051,6 @@ export {
|
|
|
3937
1051
|
UserStatusBusy,
|
|
3938
1052
|
UserStatusOffline,
|
|
3939
1053
|
UserStatusOnline,
|
|
3940
|
-
|
|
3941
|
-
WebSocketChatAdminProvider,
|
|
3942
|
-
WebSocketChatCustomerContext,
|
|
3943
|
-
WebSocketChatCustomerProvider,
|
|
3944
|
-
useWebSocketChatAdmin,
|
|
3945
|
-
useWebSocketChatAdminContext,
|
|
3946
|
-
useWebSocketChatBase,
|
|
3947
|
-
useWebSocketChatCustomer,
|
|
3948
|
-
useWebSocketChatCustomerContext
|
|
1054
|
+
useChat
|
|
3949
1055
|
};
|
|
3950
1056
|
//# sourceMappingURL=index.mjs.map
|