shuttlepro-shared 1.4.15 → 1.4.16

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(workspaceId, filters = {}) {
197
- return Call.find({ workspaceId, ...filters })
198
- .sort({ createdAt: -1 })
199
- .lean()
200
- .exec();
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(agentId, filters = {}) {
204
- return Call.find({ agentId, ...filters })
205
- .sort({ createdAt: -1 })
206
- .lean()
207
- .exec();
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
- async getAgentStats(workspaceId, from, to) {
240
- return Call.aggregate([
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
- return (
287
- result[0] || {
288
- _id: agentId,
289
- totalCalls: 0,
290
- pending: 0,
291
- abandoned: 0,
292
- ended: 0,
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: { workspaceId, createdAt: { $gte: from, $lte: to } } },
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: { "_id.day": 1 } },
385
+ { $sort: { day: 1 } },
317
386
  ]);
318
387
  }
319
388
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shuttlepro-shared",
3
- "version": "1.4.15",
3
+ "version": "1.4.16",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {