shuttlepro-shared 1.4.15 → 1.4.17
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.
|
@@ -35,13 +35,21 @@ class CallRepository {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
async getCall(callId) {
|
|
39
|
+
try {
|
|
40
|
+
return await Call.findOne({ callId }).lean().exec();
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error(`❌ Failed to end call ${callId}:`, error);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
38
47
|
async endCall(callId, agentId = null, updateObj = {}, callHistory = {}) {
|
|
39
48
|
try {
|
|
40
49
|
return await Call.findOneAndUpdate(
|
|
41
50
|
{ callId },
|
|
42
51
|
{
|
|
43
52
|
$set: {
|
|
44
|
-
...(updateObj?.status === "ended" ? { endTime: new Date() } : {}),
|
|
45
53
|
...updateObj,
|
|
46
54
|
},
|
|
47
55
|
$push: {
|
|
@@ -193,18 +201,54 @@ class CallRepository {
|
|
|
193
201
|
|
|
194
202
|
/* ----------------- Query Helpers ----------------- */
|
|
195
203
|
|
|
196
|
-
async getCallsByWorkspace(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
204
|
+
async getCallsByWorkspace(
|
|
205
|
+
workspaceId,
|
|
206
|
+
{
|
|
207
|
+
filters = {},
|
|
208
|
+
select = null,
|
|
209
|
+
sortBy = { createdAt: -1 },
|
|
210
|
+
limit = null,
|
|
211
|
+
skip = null,
|
|
212
|
+
} = {}
|
|
213
|
+
) {
|
|
214
|
+
let query = Call.find({ workspaceId, ...filters })
|
|
215
|
+
.select(select || {}) // if null → select all fields
|
|
216
|
+
.sort(sortBy) // default newest first
|
|
217
|
+
.lean();
|
|
218
|
+
|
|
219
|
+
if (limit !== null) {
|
|
220
|
+
query = query.limit(limit);
|
|
221
|
+
}
|
|
222
|
+
if (skip !== null) {
|
|
223
|
+
query = query.skip(skip);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return query.exec();
|
|
201
227
|
}
|
|
202
228
|
|
|
203
|
-
async getCallsByAgent(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
229
|
+
async getCallsByAgent(
|
|
230
|
+
agentId,
|
|
231
|
+
{
|
|
232
|
+
filters = {},
|
|
233
|
+
select = null,
|
|
234
|
+
sortBy = { createdAt: -1 },
|
|
235
|
+
limit = null,
|
|
236
|
+
skip = null,
|
|
237
|
+
} = {}
|
|
238
|
+
) {
|
|
239
|
+
let query = Call.find({ agentId, ...filters })
|
|
240
|
+
.select(select || {}) // if null → select all fields
|
|
241
|
+
.sort(sortBy) // default newest first
|
|
242
|
+
.lean();
|
|
243
|
+
|
|
244
|
+
if (limit !== null) {
|
|
245
|
+
query = query.limit(limit);
|
|
246
|
+
}
|
|
247
|
+
if (skip !== null) {
|
|
248
|
+
query = query.skip(skip);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return query.exec();
|
|
208
252
|
}
|
|
209
253
|
|
|
210
254
|
async getCallsByReceiver(receiver, filters = {}) {
|
|
@@ -236,35 +280,13 @@ class CallRepository {
|
|
|
236
280
|
/* ----------------- Reporting & Analytics ----------------- */
|
|
237
281
|
|
|
238
282
|
// Stats grouped by agent
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
{ $match: { workspaceId, createdAt: { $gte: from, $lte: to } } },
|
|
242
|
-
{
|
|
243
|
-
$group: {
|
|
244
|
-
_id: "$agentId",
|
|
245
|
-
totalCalls: { $sum: 1 },
|
|
246
|
-
abandoned: {
|
|
247
|
-
$sum: { $cond: [{ $eq: ["$status", "pending"] }, 1, 0] },
|
|
248
|
-
},
|
|
249
|
-
abandoned: {
|
|
250
|
-
$sum: { $cond: [{ $eq: ["$status", "abandoned"] }, 1, 0] },
|
|
251
|
-
},
|
|
252
|
-
ended: {
|
|
253
|
-
$sum: { $cond: [{ $eq: ["$status", "ended"] }, 1, 0] },
|
|
254
|
-
},
|
|
255
|
-
avgDuration: { $avg: "$duration" },
|
|
256
|
-
},
|
|
257
|
-
},
|
|
258
|
-
]);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
async getAgentStatsByAgentId(workspaceId, agentId, from, to) {
|
|
283
|
+
// Stats grouped by agent
|
|
284
|
+
async getAgentStats({ from, to }, filters = {}) {
|
|
262
285
|
const result = await Call.aggregate([
|
|
263
286
|
{
|
|
264
287
|
$match: {
|
|
265
|
-
workspaceId,
|
|
266
|
-
agentId,
|
|
267
288
|
createdAt: { $gte: from, $lte: to },
|
|
289
|
+
...filters,
|
|
268
290
|
},
|
|
269
291
|
},
|
|
270
292
|
{
|
|
@@ -282,38 +304,85 @@ class CallRepository {
|
|
|
282
304
|
},
|
|
283
305
|
},
|
|
284
306
|
},
|
|
307
|
+
{
|
|
308
|
+
$project: {
|
|
309
|
+
_id: 0,
|
|
310
|
+
agentId: "$_id",
|
|
311
|
+
totalCalls: 1,
|
|
312
|
+
pending: 1,
|
|
313
|
+
abandoned: 1,
|
|
314
|
+
ended: 1,
|
|
315
|
+
},
|
|
316
|
+
},
|
|
285
317
|
]);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
318
|
+
|
|
319
|
+
if (filters.agentId) {
|
|
320
|
+
// single agent case
|
|
321
|
+
return (
|
|
322
|
+
result.find((r) => r.agentId === filters.agentId) || {
|
|
323
|
+
agentId: filters.agentId,
|
|
324
|
+
totalCalls: 0,
|
|
325
|
+
pending: 0,
|
|
326
|
+
abandoned: 0,
|
|
327
|
+
ended: 0,
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// multi-agent case
|
|
333
|
+
if (result.length === 0) {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return result;
|
|
295
338
|
}
|
|
296
339
|
|
|
297
340
|
// Daily call summary
|
|
298
|
-
async getDailySummary(workspaceId, from, to) {
|
|
341
|
+
async getDailySummary(workspaceId, from, to, filters = {}, groupBy = null) {
|
|
342
|
+
const matchStage = {
|
|
343
|
+
workspaceId,
|
|
344
|
+
createdAt: { $gte: from, $lte: to },
|
|
345
|
+
...filters,
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const groupId = {
|
|
349
|
+
day: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } },
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// If admin wants to group by agentId / receiver / platformId
|
|
353
|
+
if (groupBy && ["agentId", "receiver", "platformId"].includes(groupBy)) {
|
|
354
|
+
groupId[groupBy] = `$${groupBy}`;
|
|
355
|
+
}
|
|
356
|
+
|
|
299
357
|
return Call.aggregate([
|
|
300
|
-
{ $match:
|
|
358
|
+
{ $match: matchStage },
|
|
301
359
|
{
|
|
302
360
|
$group: {
|
|
303
|
-
_id:
|
|
304
|
-
day: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } },
|
|
305
|
-
},
|
|
361
|
+
_id: groupId,
|
|
306
362
|
total: { $sum: 1 },
|
|
307
|
-
ended: {
|
|
308
|
-
$sum: { $cond: [{ $eq: ["$status", "ended"] }, 1, 0] },
|
|
309
|
-
},
|
|
363
|
+
ended: { $sum: { $cond: [{ $eq: ["$status", "ended"] }, 1, 0] } },
|
|
310
364
|
abandoned: {
|
|
311
365
|
$sum: { $cond: [{ $eq: ["$status", "abandoned"] }, 1, 0] },
|
|
312
366
|
},
|
|
313
367
|
avgDuration: { $avg: "$duration" },
|
|
368
|
+
minDuration: { $min: "$duration" },
|
|
369
|
+
maxDuration: { $max: "$duration" },
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
$project: {
|
|
374
|
+
_id: 0,
|
|
375
|
+
day: "$_id.day",
|
|
376
|
+
groupBy: groupBy ? `$_id.${groupBy}` : null,
|
|
377
|
+
total: 1,
|
|
378
|
+
ended: 1,
|
|
379
|
+
abandoned: 1,
|
|
380
|
+
avgDuration: { $round: ["$avgDuration", 2] }, // round to 2 decimals
|
|
381
|
+
minDuration: 1,
|
|
382
|
+
maxDuration: 1,
|
|
314
383
|
},
|
|
315
384
|
},
|
|
316
|
-
{ $sort: {
|
|
385
|
+
{ $sort: { day: 1 } },
|
|
317
386
|
]);
|
|
318
387
|
}
|
|
319
388
|
|