@talkpilot/core-db 1.3.51 → 1.3.52

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.
Files changed (151) hide show
  1. package/README.md +21 -2
  2. package/dist/talkpilot/auditLogs/auditLogs.getters.d.ts +8 -0
  3. package/dist/talkpilot/auditLogs/auditLogs.getters.d.ts.map +1 -0
  4. package/dist/talkpilot/auditLogs/auditLogs.getters.js +26 -0
  5. package/dist/talkpilot/auditLogs/auditLogs.getters.js.map +1 -0
  6. package/dist/talkpilot/auditLogs/auditLogs.setters.d.ts +3 -0
  7. package/dist/talkpilot/auditLogs/auditLogs.setters.d.ts.map +1 -0
  8. package/dist/talkpilot/auditLogs/auditLogs.setters.js +9 -0
  9. package/dist/talkpilot/auditLogs/auditLogs.setters.js.map +1 -0
  10. package/dist/talkpilot/auditLogs/auditLogs.types.d.ts +12 -0
  11. package/dist/talkpilot/auditLogs/auditLogs.types.d.ts.map +1 -0
  12. package/dist/talkpilot/auditLogs/auditLogs.types.js +3 -0
  13. package/dist/talkpilot/auditLogs/auditLogs.types.js.map +1 -0
  14. package/dist/talkpilot/auditLogs/index.d.ts +3 -0
  15. package/dist/talkpilot/auditLogs/index.d.ts.map +1 -0
  16. package/dist/talkpilot/auditLogs/index.js +19 -0
  17. package/dist/talkpilot/auditLogs/index.js.map +1 -0
  18. package/dist/talkpilot/calls/calls.constants.d.ts +1 -1
  19. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.getters.d.ts +4 -0
  20. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.getters.d.ts.map +1 -0
  21. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.getters.js +9 -0
  22. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.getters.js.map +1 -0
  23. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.setters.d.ts +3 -0
  24. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.setters.d.ts.map +1 -0
  25. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.setters.js +9 -0
  26. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.setters.js.map +1 -0
  27. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.types.d.ts +10 -0
  28. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.types.d.ts.map +1 -0
  29. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.types.js +3 -0
  30. package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.types.js.map +1 -0
  31. package/dist/talkpilot/clientDeletionLogs/index.d.ts +4 -0
  32. package/dist/talkpilot/clientDeletionLogs/index.d.ts.map +1 -0
  33. package/dist/talkpilot/clientDeletionLogs/index.js +20 -0
  34. package/dist/talkpilot/clientDeletionLogs/index.js.map +1 -0
  35. package/dist/talkpilot/clients/clients.constants.d.ts +3 -0
  36. package/dist/talkpilot/clients/clients.constants.d.ts.map +1 -0
  37. package/dist/talkpilot/clients/clients.constants.js +6 -0
  38. package/dist/talkpilot/clients/clients.constants.js.map +1 -0
  39. package/dist/talkpilot/clients/clients.getters.d.ts +1 -0
  40. package/dist/talkpilot/clients/clients.getters.d.ts.map +1 -1
  41. package/dist/talkpilot/clients/clients.getters.js +8 -1
  42. package/dist/talkpilot/clients/clients.getters.js.map +1 -1
  43. package/dist/talkpilot/clients/clients.lifecycle.d.ts +10 -0
  44. package/dist/talkpilot/clients/clients.lifecycle.d.ts.map +1 -0
  45. package/dist/talkpilot/clients/clients.lifecycle.js +95 -0
  46. package/dist/talkpilot/clients/clients.lifecycle.js.map +1 -0
  47. package/dist/talkpilot/clients/clients.types.d.ts +21 -0
  48. package/dist/talkpilot/clients/clients.types.d.ts.map +1 -1
  49. package/dist/talkpilot/clients/index.d.ts +2 -0
  50. package/dist/talkpilot/clients/index.d.ts.map +1 -1
  51. package/dist/talkpilot/clients/index.js +2 -0
  52. package/dist/talkpilot/clients/index.js.map +1 -1
  53. package/dist/talkpilot/flows/flows.getter.d.ts +1 -4
  54. package/dist/talkpilot/flows/flows.getter.d.ts.map +1 -1
  55. package/dist/talkpilot/flows/flows.getter.js +1 -12
  56. package/dist/talkpilot/flows/flows.getter.js.map +1 -1
  57. package/dist/talkpilot/flows/flows.schema.d.ts +0 -31
  58. package/dist/talkpilot/flows/flows.schema.d.ts.map +1 -1
  59. package/dist/talkpilot/flows/flows.schema.js +0 -25
  60. package/dist/talkpilot/flows/flows.schema.js.map +1 -1
  61. package/dist/talkpilot/flows/flows.types.d.ts +0 -14
  62. package/dist/talkpilot/flows/flows.types.d.ts.map +1 -1
  63. package/dist/talkpilot/flows/index.d.ts +1 -1
  64. package/dist/talkpilot/flows/index.d.ts.map +1 -1
  65. package/dist/talkpilot/flows/index.js +1 -3
  66. package/dist/talkpilot/flows/index.js.map +1 -1
  67. package/dist/talkpilot/index.d.ts +1 -0
  68. package/dist/talkpilot/index.d.ts.map +1 -1
  69. package/dist/talkpilot/index.js +1 -0
  70. package/dist/talkpilot/index.js.map +1 -1
  71. package/dist/talkpilot/phone_numbers/phone_numbers.getter.d.ts +11 -0
  72. package/dist/talkpilot/phone_numbers/phone_numbers.getter.d.ts.map +1 -1
  73. package/dist/talkpilot/phone_numbers/phone_numbers.getter.js +60 -1
  74. package/dist/talkpilot/phone_numbers/phone_numbers.getter.js.map +1 -1
  75. package/dist/talkpilot/results/index.d.ts +3 -0
  76. package/dist/talkpilot/results/index.d.ts.map +1 -1
  77. package/dist/talkpilot/results/index.js +22 -1
  78. package/dist/talkpilot/results/index.js.map +1 -1
  79. package/dist/talkpilot/results/results.constants.d.ts +9 -0
  80. package/dist/talkpilot/results/results.constants.d.ts.map +1 -0
  81. package/dist/talkpilot/results/results.constants.js +12 -0
  82. package/dist/talkpilot/results/results.constants.js.map +1 -0
  83. package/dist/talkpilot/results/results.getter.d.ts.map +1 -1
  84. package/dist/talkpilot/results/results.getter.js +3 -2
  85. package/dist/talkpilot/results/results.getter.js.map +1 -1
  86. package/dist/talkpilot/results/results.statistics.getters.d.ts +7 -0
  87. package/dist/talkpilot/results/results.statistics.getters.d.ts.map +1 -0
  88. package/dist/talkpilot/results/results.statistics.getters.js +244 -0
  89. package/dist/talkpilot/results/results.statistics.getters.js.map +1 -0
  90. package/dist/talkpilot/results/results.statistics.types.d.ts +42 -0
  91. package/dist/talkpilot/results/results.statistics.types.d.ts.map +1 -0
  92. package/dist/talkpilot/results/results.statistics.types.js +3 -0
  93. package/dist/talkpilot/results/results.statistics.types.js.map +1 -0
  94. package/dist/test-utils/factories/index.d.ts +2 -0
  95. package/dist/test-utils/factories/index.d.ts.map +1 -1
  96. package/dist/test-utils/factories/index.js +2 -0
  97. package/dist/test-utils/factories/index.js.map +1 -1
  98. package/dist/test-utils/factories/talkpilot/clients.d.ts +14 -0
  99. package/dist/test-utils/factories/talkpilot/clients.d.ts.map +1 -0
  100. package/dist/test-utils/factories/talkpilot/clients.js +19 -0
  101. package/dist/test-utils/factories/talkpilot/clients.js.map +1 -0
  102. package/dist/test-utils/factories/talkpilot/results.d.ts +16 -0
  103. package/dist/test-utils/factories/talkpilot/results.d.ts.map +1 -0
  104. package/dist/test-utils/factories/talkpilot/results.js +40 -0
  105. package/dist/test-utils/factories/talkpilot/results.js.map +1 -0
  106. package/dist/websitalk/scanResources/scanResources.getters.d.ts +0 -1
  107. package/dist/websitalk/scanResources/scanResources.getters.d.ts.map +1 -1
  108. package/dist/websitalk/scanResources/scanResources.getters.js +1 -6
  109. package/dist/websitalk/scanResources/scanResources.getters.js.map +1 -1
  110. package/dist/websitalk/scans/scans.constants.d.ts +0 -1
  111. package/dist/websitalk/scans/scans.constants.d.ts.map +1 -1
  112. package/dist/websitalk/scans/scans.constants.js +0 -1
  113. package/dist/websitalk/scans/scans.constants.js.map +1 -1
  114. package/dist/websitalk/scans/scans.getters.d.ts +0 -1
  115. package/dist/websitalk/scans/scans.getters.d.ts.map +1 -1
  116. package/dist/websitalk/scans/scans.getters.js +1 -6
  117. package/dist/websitalk/scans/scans.getters.js.map +1 -1
  118. package/package.json +1 -1
  119. package/src/talkpilot/auditLogs/__tests__/auditLogs.spec.ts +92 -0
  120. package/src/talkpilot/auditLogs/auditLogs.getters.ts +26 -0
  121. package/src/talkpilot/auditLogs/auditLogs.types.ts +13 -0
  122. package/src/talkpilot/auditLogs/index.ts +2 -0
  123. package/src/talkpilot/clients/__tests__/clients.getters.spec.ts +33 -0
  124. package/src/talkpilot/clients/__tests__/clients.lifecycle.spec.ts +166 -0
  125. package/src/talkpilot/clients/clients.constants.ts +2 -0
  126. package/src/talkpilot/clients/clients.getters.ts +7 -0
  127. package/src/talkpilot/clients/clients.lifecycle.ts +116 -0
  128. package/src/talkpilot/clients/clients.types.ts +19 -0
  129. package/src/talkpilot/clients/index.ts +2 -0
  130. package/src/talkpilot/flows/__tests__/flows.schema.spec.ts +0 -25
  131. package/src/talkpilot/flows/flows.getter.ts +2 -22
  132. package/src/talkpilot/flows/flows.schema.ts +0 -25
  133. package/src/talkpilot/flows/flows.types.ts +0 -22
  134. package/src/talkpilot/flows/index.ts +1 -6
  135. package/src/talkpilot/index.ts +1 -0
  136. package/src/talkpilot/phone_numbers/__tests__/phone_numbers.spec.ts +227 -13
  137. package/src/talkpilot/phone_numbers/phone_numbers.getter.ts +76 -1
  138. package/src/talkpilot/results/__tests__/results.statistics.spec.ts +226 -0
  139. package/src/talkpilot/results/index.ts +9 -0
  140. package/src/talkpilot/results/results.constants.ts +11 -0
  141. package/src/talkpilot/results/results.getter.ts +3 -2
  142. package/src/talkpilot/results/results.statistics.getters.ts +329 -0
  143. package/src/talkpilot/results/results.statistics.types.ts +45 -0
  144. package/src/test-utils/factories/index.ts +2 -0
  145. package/src/test-utils/factories/talkpilot/clients.ts +21 -0
  146. package/src/test-utils/factories/talkpilot/results.ts +43 -0
  147. package/src/websitalk/scanResources/__tests__/scanResources.spec.ts +0 -19
  148. package/src/websitalk/scanResources/scanResources.getters.ts +0 -7
  149. package/src/websitalk/scans/__tests__/scans.spec.ts +0 -17
  150. package/src/websitalk/scans/scans.constants.ts +0 -1
  151. package/src/websitalk/scans/scans.getters.ts +0 -5
@@ -0,0 +1,329 @@
1
+ import { getResultsCollection } from "./results.getter";
2
+ import {
3
+ formatYmdInTz,
4
+ startOfCalendarDayInTz,
5
+ endOfCalendarDayInTz,
6
+ } from "../../utils/date.utils";
7
+ import {
8
+ ACTIVE_STATUSES,
9
+ ACTIVE_CALLS_WINDOW_MS,
10
+ SESSION_STATS_LIMIT,
11
+ INCOMING_SESSION_ID,
12
+ } from "./results.constants";
13
+ import type {
14
+ StatsDateRangeParams,
15
+ ActiveCallsSummary,
16
+ TodayCallsSummary,
17
+ ClientCallStats,
18
+ SessionCallStats,
19
+ } from "./results.statistics.types";
20
+
21
+ const dateRangeFilter = (
22
+ dateFrom: string | undefined,
23
+ dateTo: string | undefined,
24
+ timezone: string,
25
+ ): { start: Date; end: Date } => {
26
+ const today = formatYmdInTz(new Date(), timezone);
27
+ return {
28
+ start: startOfCalendarDayInTz(dateFrom ?? today, timezone),
29
+ end: endOfCalendarDayInTz(dateTo ?? today, timezone),
30
+ };
31
+ };
32
+
33
+ const buildDateRangeMatch = (
34
+ start: Date,
35
+ end: Date,
36
+ clientId: string | undefined,
37
+ ) => {
38
+ const clientFilter = clientId ? { clientId } : {};
39
+ return { ...clientFilter, "calls.date": { $gte: start, $lte: end } };
40
+ };
41
+
42
+ // Filters on `clientId` (a document-level field) before `$unwind` explodes `calls[]`,
43
+ // so Mongo never has to unwind documents belonging to other clients.
44
+ const clientPreMatchStage = (clientId?: string) =>
45
+ clientId ? [{ $match: { clientId } }] : [];
46
+
47
+ // Converts a duration string like "120 seconds" into a number (120) that can be summed inside an aggregation.
48
+ const durationExpr = {
49
+ $convert: {
50
+ input: {
51
+ $arrayElemAt: [
52
+ { $split: [{ $ifNull: ["$calls.callLength", "0 seconds"] }, " "] },
53
+ 0,
54
+ ],
55
+ },
56
+ to: "double",
57
+ onError: 0,
58
+ onNull: 0,
59
+ },
60
+ };
61
+
62
+ const statusFields = {
63
+ totalCalls: { $sum: 1 },
64
+ completed: {
65
+ $sum: { $cond: [{ $eq: ["$calls.status", "completed"] }, 1, 0] },
66
+ },
67
+ failed: { $sum: { $cond: [{ $eq: ["$calls.status", "failed"] }, 1, 0] } },
68
+ noAnswer: {
69
+ $sum: { $cond: [{ $eq: ["$calls.status", "no-answer"] }, 1, 0] },
70
+ },
71
+ busy: { $sum: { $cond: [{ $eq: ["$calls.status", "busy"] }, 1, 0] } },
72
+ inProgress: {
73
+ $sum: { $cond: [{ $eq: ["$calls.status", "in-progress"] }, 1, 0] },
74
+ },
75
+ };
76
+
77
+ export const aggregateActiveCallsSummary = async (
78
+ clientId?: string,
79
+ ): Promise<ActiveCallsSummary> => {
80
+ const coll = getResultsCollection();
81
+ const twoHoursAgo = new Date(Date.now() - ACTIVE_CALLS_WINDOW_MS);
82
+
83
+ const activeMatch = {
84
+ "calls.status": { $in: ACTIVE_STATUSES },
85
+ "calls.date": { $gte: twoHoursAgo },
86
+ };
87
+
88
+ const facetResult = await coll
89
+ .aggregate<{
90
+ counts: { total: number; incoming: number; outgoing: number }[];
91
+ activeClients: { _id: string }[];
92
+ }>([
93
+ ...clientPreMatchStage(clientId),
94
+ { $match: { "calls.date": { $gte: twoHoursAgo } } },
95
+ { $unwind: "$calls" },
96
+ { $match: activeMatch },
97
+ {
98
+ $facet: {
99
+ counts: [
100
+ {
101
+ $group: {
102
+ _id: null,
103
+ total: { $addToSet: "$calls.callSid" },
104
+ incoming: {
105
+ $addToSet: {
106
+ $cond: [
107
+ { $eq: ["$sessionId", INCOMING_SESSION_ID] },
108
+ "$calls.callSid",
109
+ null,
110
+ ],
111
+ },
112
+ },
113
+ outgoing: {
114
+ $addToSet: {
115
+ $cond: [
116
+ { $ne: ["$sessionId", INCOMING_SESSION_ID] },
117
+ "$calls.callSid",
118
+ null,
119
+ ],
120
+ },
121
+ },
122
+ },
123
+ },
124
+ {
125
+ $project: {
126
+ _id: 0,
127
+ total: { $size: "$total" },
128
+ incoming: {
129
+ $size: { $setDifference: ["$incoming", [null]] },
130
+ },
131
+ outgoing: {
132
+ $size: { $setDifference: ["$outgoing", [null]] },
133
+ },
134
+ },
135
+ },
136
+ ],
137
+ activeClients: [{ $group: { _id: "$clientId" } }],
138
+ },
139
+ },
140
+ ])
141
+ .next();
142
+
143
+ const counts = facetResult?.counts?.[0];
144
+
145
+ return {
146
+ total: counts?.total ?? 0,
147
+ incoming: counts?.incoming ?? 0,
148
+ outgoing: counts?.outgoing ?? 0,
149
+ activeClientIds: (facetResult?.activeClients ?? []).map(
150
+ (d) => d._id || "Unknown",
151
+ ),
152
+ };
153
+ };
154
+
155
+ export const aggregateTodayCallsSummary = async ({
156
+ dateFrom,
157
+ dateTo,
158
+ clientId,
159
+ timezone = "UTC",
160
+ }: StatsDateRangeParams): Promise<TodayCallsSummary> => {
161
+ const coll = getResultsCollection();
162
+ const { start, end } = dateRangeFilter(dateFrom, dateTo, timezone);
163
+ const match = buildDateRangeMatch(start, end, clientId);
164
+
165
+ const facetResult = await coll
166
+ .aggregate<{
167
+ byStatus: { _id: string; count: number }[];
168
+ byType: { _id: string; count: number }[];
169
+ total: { count: number }[];
170
+ totalSeconds: { seconds: number }[];
171
+ }>([
172
+ ...clientPreMatchStage(clientId),
173
+ { $match: { "calls.date": { $gte: start, $lte: end } } },
174
+ { $unwind: "$calls" },
175
+ { $match: match },
176
+ {
177
+ $facet: {
178
+ byStatus: [{ $group: { _id: "$calls.status", count: { $sum: 1 } } }],
179
+ byType: [
180
+ {
181
+ $group: {
182
+ _id: {
183
+ $cond: [
184
+ { $eq: ["$sessionId", INCOMING_SESSION_ID] },
185
+ "incoming",
186
+ "outgoing",
187
+ ],
188
+ },
189
+ count: { $sum: 1 },
190
+ },
191
+ },
192
+ ],
193
+ total: [{ $group: { _id: null, count: { $sum: 1 } } }],
194
+ totalSeconds: [
195
+ { $group: { _id: null, seconds: { $sum: durationExpr } } },
196
+ ],
197
+ },
198
+ },
199
+ ])
200
+ .next();
201
+
202
+ const byStatus: Record<string, number> = {};
203
+ (facetResult?.byStatus ?? []).forEach((s) => {
204
+ byStatus[s._id] = s.count;
205
+ });
206
+ const byType: Record<string, number> = {};
207
+ (facetResult?.byType ?? []).forEach((t) => {
208
+ byType[t._id] = t.count;
209
+ });
210
+
211
+ const totalSeconds = facetResult?.totalSeconds?.[0]?.seconds ?? 0;
212
+
213
+ return {
214
+ total: facetResult?.total?.[0]?.count ?? 0,
215
+ incoming: byType.incoming ?? 0,
216
+ outgoing: byType.outgoing ?? 0,
217
+ completed: byStatus.completed ?? 0,
218
+ failed: byStatus.failed ?? 0,
219
+ busy: byStatus.busy ?? 0,
220
+ noAnswer: byStatus["no-answer"] ?? 0,
221
+ totalMinutes: Math.ceil(totalSeconds / 60),
222
+ };
223
+ };
224
+
225
+ export const aggregateClientCallStats = async ({
226
+ dateFrom,
227
+ dateTo,
228
+ clientId,
229
+ timezone = "UTC",
230
+ }: StatsDateRangeParams): Promise<ClientCallStats[]> => {
231
+ const coll = getResultsCollection();
232
+ const { start, end } = dateRangeFilter(dateFrom, dateTo, timezone);
233
+ const match = buildDateRangeMatch(start, end, clientId);
234
+
235
+ const rows = await coll
236
+ .aggregate<{
237
+ _id: string;
238
+ totalCalls: number;
239
+ completed: number;
240
+ failed: number;
241
+ noAnswer: number;
242
+ busy: number;
243
+ inProgress: number;
244
+ totalSeconds: number;
245
+ }>([
246
+ ...clientPreMatchStage(clientId),
247
+ { $match: { "calls.date": { $gte: start, $lte: end } } },
248
+ { $unwind: "$calls" },
249
+ { $match: match },
250
+ {
251
+ $group: {
252
+ _id: "$clientId",
253
+ ...statusFields,
254
+ totalSeconds: { $sum: durationExpr },
255
+ },
256
+ },
257
+ { $sort: { totalCalls: -1 } },
258
+ ])
259
+ .toArray();
260
+
261
+ return rows.map((r) => ({
262
+ clientId: r._id || "Unknown",
263
+ totalCalls: r.totalCalls,
264
+ completed: r.completed,
265
+ failed: r.failed,
266
+ noAnswer: r.noAnswer,
267
+ busy: r.busy,
268
+ inProgress: r.inProgress,
269
+ totalMinutes: Math.ceil((r.totalSeconds || 0) / 60),
270
+ }));
271
+ };
272
+
273
+ export const aggregateSessionCallStats = async ({
274
+ dateFrom,
275
+ dateTo,
276
+ clientId,
277
+ timezone = "UTC",
278
+ }: StatsDateRangeParams): Promise<SessionCallStats[]> => {
279
+ const coll = getResultsCollection();
280
+ const { start, end } = dateRangeFilter(dateFrom, dateTo, timezone);
281
+ const match = buildDateRangeMatch(start, end, clientId);
282
+
283
+ const rows = await coll
284
+ .aggregate<{
285
+ _id: string;
286
+ totalCalls: number;
287
+ completed: number;
288
+ failed: number;
289
+ noAnswer: number;
290
+ busy: number;
291
+ inProgress: number;
292
+ }>([
293
+ ...clientPreMatchStage(clientId),
294
+ { $match: { "calls.date": { $gte: start, $lte: end } } },
295
+ { $unwind: "$calls" },
296
+ { $match: match },
297
+ { $group: { _id: "$sessionName", ...statusFields } },
298
+ { $sort: { totalCalls: -1 } },
299
+ { $limit: SESSION_STATS_LIMIT },
300
+ ])
301
+ .toArray();
302
+
303
+ return rows.map((r) => ({
304
+ sessionName: r._id || "Unknown",
305
+ totalCalls: r.totalCalls,
306
+ completed: r.completed,
307
+ failed: r.failed,
308
+ noAnswer: r.noAnswer,
309
+ busy: r.busy,
310
+ inProgress: r.inProgress,
311
+ }));
312
+ };
313
+
314
+ export const findClientIdsByCallSids = async (
315
+ callSids: string[],
316
+ ): Promise<Map<string, string>> => {
317
+ if (callSids.length === 0) return new Map();
318
+
319
+ const docs = await getResultsCollection()
320
+ .aggregate<{ clientId: string; callSid: string }>([
321
+ { $match: { "calls.callSid": { $in: callSids } } },
322
+ { $unwind: "$calls" },
323
+ { $match: { "calls.callSid": { $in: callSids } } },
324
+ { $project: { _id: 0, clientId: 1, callSid: "$calls.callSid" } },
325
+ ])
326
+ .toArray();
327
+
328
+ return new Map(docs.map((d) => [d.callSid, d.clientId]));
329
+ };
@@ -0,0 +1,45 @@
1
+ export type StatsDateRangeParams = {
2
+ dateFrom?: string;
3
+ dateTo?: string;
4
+ clientId?: string;
5
+ timezone?: string;
6
+ };
7
+
8
+ export type ActiveCallsSummary = {
9
+ total: number;
10
+ incoming: number;
11
+ outgoing: number;
12
+ activeClientIds: string[];
13
+ };
14
+
15
+ export type TodayCallsSummary = {
16
+ total: number;
17
+ incoming: number;
18
+ outgoing: number;
19
+ completed: number;
20
+ failed: number;
21
+ busy: number;
22
+ noAnswer: number;
23
+ totalMinutes: number;
24
+ };
25
+
26
+ export type ClientCallStats = {
27
+ clientId: string;
28
+ totalCalls: number;
29
+ completed: number;
30
+ failed: number;
31
+ noAnswer: number;
32
+ busy: number;
33
+ inProgress: number;
34
+ totalMinutes: number;
35
+ };
36
+
37
+ export type SessionCallStats = {
38
+ sessionName: string;
39
+ totalCalls: number;
40
+ completed: number;
41
+ failed: number;
42
+ noAnswer: number;
43
+ busy: number;
44
+ inProgress: number;
45
+ };
@@ -1,4 +1,5 @@
1
1
  export * from "./talkpilot/agents";
2
+ export * from "./talkpilot/clients";
2
3
  export * from "./talkpilot/calls";
3
4
  export * from "./talkpilot/clientAudioBuffers";
4
5
  export * from "./talkpilot/clientsConfig";
@@ -7,6 +8,7 @@ export * from "./talkpilot/contextNotes";
7
8
  export * from "./talkpilot/groups";
8
9
  export * from "./talkpilot/phone_numbers";
9
10
  export * from "./talkpilot/sessions";
11
+ export * from "./talkpilot/results";
10
12
  export * from "./municipal/cities";
11
13
  export * from "./municipal/departmentsSubjects";
12
14
  export * from "./municipal/muniIssues";
@@ -0,0 +1,21 @@
1
+ import { Factory } from "fishery";
2
+ import { ObjectId } from "mongodb";
3
+ import { faker } from "@faker-js/faker";
4
+ import type { Client } from "../../../talkpilot";
5
+
6
+ export const clientFactory = Factory.define<Client & { _id?: ObjectId }>(
7
+ () => ({
8
+ _id: new ObjectId(),
9
+ clientId: faker.string.uuid(),
10
+ isActive: true,
11
+ credits: faker.number.int({ min: 0, max: 1000 }),
12
+ createdAt: new Date(),
13
+ updatedAt: new Date(),
14
+ }),
15
+ );
16
+
17
+ export function createClient(
18
+ overrides?: Partial<Client>,
19
+ ): Client & { _id?: ObjectId } {
20
+ return clientFactory.build(overrides);
21
+ }
@@ -0,0 +1,43 @@
1
+ import { Factory } from "fishery";
2
+ import { ObjectId } from "mongodb";
3
+ import { faker } from "@faker-js/faker";
4
+ import type { Result, CallResult } from "../../../talkpilot";
5
+
6
+ export const callResultFactory = Factory.define<CallResult>(() => ({
7
+ callSid: `CA${faker.string.alphanumeric(32)}`,
8
+ status: "completed",
9
+ date: new Date(),
10
+ statuses: ["completed"],
11
+ userAnswer: null,
12
+ color: null,
13
+ name: null,
14
+ callLength: "60 seconds",
15
+ answeredBy: null,
16
+ to: faker.phone.number(),
17
+ from: faker.phone.number(),
18
+ transcription: [],
19
+ flowId: new ObjectId().toString(),
20
+ recordingUrl: null,
21
+ }));
22
+
23
+ export function createCallResult(overrides?: Partial<CallResult>): CallResult {
24
+ return callResultFactory.build(overrides);
25
+ }
26
+
27
+ export const resultFactory = Factory.define<Result & { _id?: ObjectId }>(
28
+ () => ({
29
+ _id: new ObjectId(),
30
+ calls: [],
31
+ clientId: faker.string.uuid(),
32
+ date: new Date(),
33
+ runId: `run_${new ObjectId().toString()}`,
34
+ sessionId: "incomingCall",
35
+ sessionName: "incomingCall",
36
+ }),
37
+ );
38
+
39
+ export function createResult(
40
+ overrides?: Partial<Result>,
41
+ ): Result & { _id?: ObjectId } {
42
+ return resultFactory.build(overrides);
43
+ }
@@ -5,7 +5,6 @@ import {
5
5
  getScanPages,
6
6
  getScanResourceById,
7
7
  getScanResourceCountsByScanIds,
8
- deleteScanResourcesByScanId,
9
8
  } from "../scanResources.getters";
10
9
  import { boardColumn } from "../scanResources.utils";
11
10
  import { createScanResourceFixture } from "../../../test-utils/factories";
@@ -191,22 +190,4 @@ describe("db.websitalk.scanResources", () => {
191
190
  expect(result).toBeNull();
192
191
  });
193
192
  });
194
-
195
- describe("deleteScanResourcesByScanId()", () => {
196
- it("should delete all resources for the scan and return the count", async () => {
197
- const scanId = new ObjectId().toString();
198
- await getScanResourcesCollection().insertMany([
199
- createScanResourceFixture({ scanId }),
200
- createScanResourceFixture({ scanId }),
201
- createScanResourceFixture({ scanId: new ObjectId().toString() }),
202
- ]);
203
-
204
- const deleted = await deleteScanResourcesByScanId(scanId);
205
-
206
- expect(deleted).toBe(2);
207
- expect(
208
- await getScanResourcesCollection().countDocuments({ scanId }),
209
- ).toBe(0);
210
- });
211
- });
212
193
  });
@@ -100,10 +100,3 @@ export const getScanResourceById = async (
100
100
  pageId: string,
101
101
  ): Promise<ScanResourceDoc | null> =>
102
102
  getScanResourcesCollection().findOne({ scanId, _id: new ObjectId(pageId) });
103
-
104
- export const deleteScanResourcesByScanId = async (
105
- scanId: string,
106
- ): Promise<number> => {
107
- const result = await getScanResourcesCollection().deleteMany({ scanId });
108
- return result.deletedCount;
109
- };
@@ -12,7 +12,6 @@ import {
12
12
  getInProgressScanByWebsiteUrl,
13
13
  getLatestCompletedScanByWebsiteUrl,
14
14
  deleteScansByWebsiteUrl,
15
- deleteScanById,
16
15
  } from "../scans.getters";
17
16
  import { createScanFixture } from "../../../test-utils/factories";
18
17
  import { ObjectId } from "mongodb";
@@ -311,20 +310,4 @@ describe("db.websitalk.scans", () => {
311
310
  expect(await countScans({ websiteUrlId })).toBe(0);
312
311
  });
313
312
  });
314
-
315
- describe("deleteScanById()", () => {
316
- it("should delete the scan and return true", async () => {
317
- const { insertedId } =
318
- await getScansCollection().insertOne(createScanFixture());
319
-
320
- expect(await deleteScanById(insertedId)).toBe(true);
321
- expect(
322
- await getScansCollection().findOne({ _id: insertedId }),
323
- ).toBeNull();
324
- });
325
-
326
- it("should return false when the scan does not exist", async () => {
327
- expect(await deleteScanById(new ObjectId())).toBe(false);
328
- });
329
- });
330
313
  });
@@ -6,7 +6,6 @@ export const SCAN_STATUSES = {
6
6
  COMPLETED: "COMPLETED",
7
7
  FAILED: "FAILED",
8
8
  OVERWRITTEN: "OVERWRITTEN",
9
- PAUSED: "PAUSED",
10
9
  } as const;
11
10
 
12
11
  export const FINISHED_SCAN_STATUSES = [
@@ -164,8 +164,3 @@ export const deleteScansByWebsiteUrl = async (
164
164
  const result = await getScansCollection().deleteMany({ websiteUrlId });
165
165
  return result.deletedCount;
166
166
  };
167
-
168
- export const deleteScanById = async (scanId: ObjectId): Promise<boolean> => {
169
- const result = await getScansCollection().deleteOne({ _id: scanId });
170
- return result.deletedCount > 0;
171
- };