shuttlepro-shared 1.4.1 → 1.4.3

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.
@@ -1,5 +1,4 @@
1
1
  const Call = require("../../models/Call");
2
- const mongoose = require("mongoose");
3
2
 
4
3
  class CallRepository {
5
4
  constructor() {
@@ -9,39 +8,39 @@ class CallRepository {
9
8
  };
10
9
  }
11
10
 
12
- // Create a new call with transaction support
11
+ // Create a new call
13
12
  async createCall(callData) {
14
- const session = await mongoose.startSession();
15
-
16
13
  try {
17
- return await session.withTransaction(async () => {
18
- const call = new Call({
19
- callId: callData.callId,
20
- callerName: callData.callerName,
21
- callerNumber: callData.callerNumber,
22
- whatsappSdp: callData.whatsappSdp,
23
- status: callData.status || "pending",
24
- autoAccepted: callData.autoAccepted || false,
25
- metadata: callData.metadata || {},
26
- tags: callData.tags || [],
27
- priority: callData.priority || 0,
28
- platformId: callData.platformId || null,
29
- workspaceId: callData.workspaceId || null,
30
- profileId: callData.profileId || null,
31
- });
32
-
33
- call.addToHistory("created", null, "Call created in system");
34
- await call.save({ session });
35
-
36
- await this._updateCallMetrics("created", session);
37
-
38
- return call;
14
+ const historyEntry = {
15
+ timestamp: new Date(),
16
+ action: "created",
17
+ agentId: null,
18
+ details: "Call created in system",
19
+ };
20
+
21
+ const call = await Call.create({
22
+ callId: callData.callId,
23
+ callerName: callData.callerName,
24
+ callerNumber: callData.callerNumber,
25
+ whatsappSdp: callData.whatsappSdp,
26
+ status: callData.status || "pending",
27
+ autoAccepted: callData.autoAccepted || false,
28
+ metadata: callData.metadata || {},
29
+ tags: callData.tags || [],
30
+ priority: callData.priority || 0,
31
+ platformId: callData.platformId || null,
32
+ workspaceId: callData.workspaceId || null,
33
+ profileId: callData.profileId || null,
34
+ platformTimestamp: callData.platformTimestamp || "",
35
+ receiver: callData.receiver || "",
36
+ callHistory: [historyEntry],
39
37
  });
38
+
39
+ await this._updateCallMetrics("created");
40
+ return call;
40
41
  } catch (error) {
41
42
  console.error(`Failed to create call ${callData.callId}:`, error);
42
- throw error;
43
- } finally {
44
- await session.endSession();
43
+ return null;
45
44
  }
46
45
  }
47
46
 
@@ -52,7 +51,6 @@ class CallRepository {
52
51
 
53
52
  if (options.lean) query.lean();
54
53
 
55
- // Auto-populate unless explicitly disabled
56
54
  if (options.populate !== false) {
57
55
  query.populate([
58
56
  { path: "platformId", model: "Integration" },
@@ -68,11 +66,11 @@ class CallRepository {
68
66
  return await query.exec();
69
67
  } catch (error) {
70
68
  console.error(`Failed to get call ${callId}:`, error);
71
- throw error;
69
+ return null;
72
70
  }
73
71
  }
74
72
 
75
- // Bulk get calls with advanced filtering
73
+ // Bulk get calls
76
74
  async getCalls(filters = {}, options = {}) {
77
75
  try {
78
76
  const {
@@ -90,7 +88,6 @@ class CallRepository {
90
88
  } = filters;
91
89
 
92
90
  const query = {};
93
-
94
91
  if (status)
95
92
  query.status = Array.isArray(status) ? { $in: status } : status;
96
93
  if (agentId) query.agentId = agentId;
@@ -106,7 +103,7 @@ class CallRepository {
106
103
  }
107
104
 
108
105
  if (priority !== undefined) query.priority = { $gte: priority };
109
- if (tags && tags.length > 0) query.tags = { $in: tags };
106
+ if (tags?.length > 0) query.tags = { $in: tags };
110
107
 
111
108
  const skip = (page - 1) * limit;
112
109
 
@@ -140,132 +137,127 @@ class CallRepository {
140
137
  };
141
138
  } catch (error) {
142
139
  console.error("Failed to get calls:", error);
143
- throw error;
140
+ return {
141
+ calls: [],
142
+ pagination: { page: 1, limit: 50, total: 0, pages: 0 },
143
+ };
144
144
  }
145
145
  }
146
146
 
147
- // Update call with optimistic locking
148
- async updateCall(callId, updateData, options = {}) {
149
- const session = options.session || (await mongoose.startSession());
150
- const shouldCommitSession = !options.session;
151
-
147
+ // Update call
148
+ async updateCall(callId, updateData) {
152
149
  try {
153
- return await session.withTransaction(async () => {
154
- const call = await Call.findOne({ callId }).session(session);
155
- if (!call) throw new Error(`Call ${callId} not found`);
150
+ const allowedFields = [
151
+ "status",
152
+ "agentId",
153
+ "callerName",
154
+ "callerNumber",
155
+ "priority",
156
+ "tags",
157
+ "metadata",
158
+ "isOnHold",
159
+ "platformId",
160
+ "workspaceId",
161
+ "profileId",
162
+ ];
156
163
 
157
- if (updateData.__v !== undefined && call.__v !== updateData.__v) {
158
- throw new Error(`Concurrent update detected for call ${callId}`);
164
+ const filteredUpdate = {};
165
+ Object.keys(updateData).forEach((key) => {
166
+ if (allowedFields.includes(key)) {
167
+ filteredUpdate[key] = updateData[key];
159
168
  }
169
+ });
160
170
 
161
- const allowedFields = [
162
- "status",
163
- "agentId",
164
- "callerName",
165
- "callerNumber",
166
- "priority",
167
- "tags",
168
- "metadata",
169
- "isOnHold",
170
- "platformId",
171
- "workspaceId",
172
- "profileId",
173
- ];
174
-
175
- Object.keys(updateData).forEach((key) => {
176
- if (allowedFields.includes(key)) {
177
- call[key] = updateData[key];
178
- }
179
- });
180
-
181
- if (updateData.status && updateData.status !== call.status) {
182
- call.addToHistory(
183
- updateData.status,
184
- updateData.agentId || call.agentId,
185
- updateData.reason || `Status changed to ${updateData.status}`
186
- );
187
- }
171
+ const updateOps = {
172
+ $set: {
173
+ ...filteredUpdate,
174
+ lastHeartbeat: new Date(),
175
+ },
176
+ };
188
177
 
189
- call.updateHeartbeat();
190
- await call.save({ session });
178
+ if (updateData.status) {
179
+ updateOps.$push = {
180
+ callHistory: {
181
+ timestamp: new Date(),
182
+ action: updateData.status,
183
+ agentId: updateData.agentId || null,
184
+ details:
185
+ updateData.reason || `Status changed to ${updateData.status}`,
186
+ },
187
+ };
188
+ }
191
189
 
192
- return call;
190
+ const call = await Call.findOneAndUpdate({ callId }, updateOps, {
191
+ new: true,
193
192
  });
193
+ return call || null;
194
194
  } catch (error) {
195
195
  console.error(`Failed to update call ${callId}:`, error);
196
- throw error;
197
- } finally {
198
- if (shouldCommitSession) await session.endSession();
196
+ return null;
199
197
  }
200
198
  }
201
199
 
202
200
  // Assign call to agent
203
- async assignCallToAgent(callId, agentId, options = {}) {
204
- const session = await mongoose.startSession();
205
-
201
+ async assignCallToAgent(callId, agentId) {
206
202
  try {
207
- return await session.withTransaction(async () => {
208
- const call = await Call.findOne({ callId }).session(session);
209
- if (!call) throw new Error(`Call ${callId} not found`);
210
-
211
- if (call.status !== "pending" && call.status !== "hold") {
212
- throw new Error(`Call ${callId} is not available for assignment`);
213
- }
214
-
215
- call.agentId = agentId;
216
- call.status = "active";
217
- call.startTime = new Date();
218
- call.isOnHold = false;
219
- call.addToHistory("answered", agentId, "Call answered by agent");
220
- call.updateHeartbeat();
221
-
222
- await call.save({ session });
223
- return call;
224
- });
203
+ const call = await Call.findOneAndUpdate(
204
+ { callId, status: { $in: ["pending", "hold"] } },
205
+ {
206
+ $set: {
207
+ agentId,
208
+ status: "active",
209
+ startTime: new Date(),
210
+ isOnHold: false,
211
+ lastHeartbeat: new Date(),
212
+ },
213
+ $push: {
214
+ callHistory: {
215
+ timestamp: new Date(),
216
+ action: "answered",
217
+ agentId,
218
+ details: "Call answered by agent",
219
+ },
220
+ },
221
+ },
222
+ { new: true }
223
+ );
224
+ return call || null;
225
225
  } catch (error) {
226
226
  console.error(
227
227
  `Failed to assign call ${callId} to agent ${agentId}:`,
228
228
  error
229
229
  );
230
- throw error;
231
- } finally {
232
- await session.endSession();
230
+ return null;
233
231
  }
234
232
  }
235
233
 
236
- // Bulk operations for call management
234
+ // Bulk update calls
237
235
  async bulkUpdateCalls(operations) {
238
- const session = await mongoose.startSession();
239
-
240
236
  try {
241
- return await session.withTransaction(async () => {
242
- const bulkOps = operations.map((op) => ({
243
- updateOne: {
244
- filter: { callId: op.callId },
245
- update: {
246
- $set: {
247
- ...op.updateData,
248
- lastHeartbeat: new Date(),
249
- },
250
- $push: {
251
- callHistory: {
252
- timestamp: new Date(),
253
- action: op.action || "bulk_update",
254
- agentId: op.agentId,
255
- details: op.details || "Bulk operation",
256
- },
237
+ const bulkOps = operations.map((op) => ({
238
+ updateOne: {
239
+ filter: { callId: op.callId },
240
+ update: {
241
+ $set: {
242
+ ...op.updateData,
243
+ lastHeartbeat: new Date(),
244
+ },
245
+ $push: {
246
+ callHistory: {
247
+ timestamp: new Date(),
248
+ action: op.action || "bulk_update",
249
+ agentId: op.agentId,
250
+ details: op.details || "Bulk operation",
257
251
  },
258
252
  },
259
253
  },
260
- }));
254
+ },
255
+ }));
261
256
 
262
- return await Call.bulkWrite(bulkOps, { session });
263
- });
257
+ return await Call.bulkWrite(bulkOps);
264
258
  } catch (error) {
265
259
  console.error("Failed to perform bulk update:", error);
266
- throw error;
267
- } finally {
268
- await session.endSession();
260
+ return [];
269
261
  }
270
262
  }
271
263
 
@@ -292,7 +284,7 @@ class CallRepository {
292
284
  return await Call.aggregate(pipeline);
293
285
  } catch (error) {
294
286
  console.error("Failed to get hold queue:", error);
295
- throw error;
287
+ return [];
296
288
  }
297
289
  }
298
290
 
@@ -308,7 +300,7 @@ class CallRepository {
308
300
  .lean(options.lean !== false);
309
301
  } catch (error) {
310
302
  console.error("Failed to get available calls:", error);
311
- throw error;
303
+ return [];
312
304
  }
313
305
  }
314
306
 
@@ -368,49 +360,110 @@ class CallRepository {
368
360
  return result[0] || this._getEmptyStats();
369
361
  } catch (error) {
370
362
  console.error("Failed to get advanced statistics:", error);
371
- throw error;
363
+ return this._getEmptyStats();
372
364
  }
373
365
  }
374
366
 
375
367
  // Cleanup stale connections
376
368
  async cleanupStaleConnections(timeoutMinutes = 5) {
377
- const session = await mongoose.startSession();
378
-
379
369
  try {
380
- return await session.withTransaction(async () => {
381
- const cutoffTime = new Date(Date.now() - timeoutMinutes * 60 * 1000);
370
+ const cutoffTime = new Date(Date.now() - timeoutMinutes * 60 * 1000);
382
371
 
383
- const staleCalls = await Call.find({
384
- status: { $in: ["active", "hold", "pending"] },
385
- lastHeartbeat: { $lt: cutoffTime },
386
- }).session(session);
372
+ const staleCalls = await Call.find({
373
+ status: { $in: ["active", "hold", "pending"] },
374
+ lastHeartbeat: { $lt: cutoffTime },
375
+ });
387
376
 
388
- const results = [];
377
+ const results = [];
389
378
 
390
- for (const call of staleCalls) {
391
- if (call.agentId) {
392
- await this.moveCallToHoldQueue(call.callId, "Connection timeout", {
393
- session,
394
- });
379
+ for (const call of staleCalls) {
380
+ if (call.agentId) {
381
+ const moved = await this.moveCallToHoldQueue(
382
+ call.callId,
383
+ "Connection timeout"
384
+ );
385
+ if (moved)
395
386
  results.push({ callId: call.callId, action: "moved_to_hold" });
396
- } else {
397
- await this.endCall(call.callId, null, { session });
398
- results.push({ callId: call.callId, action: "ended" });
399
- }
387
+ } else {
388
+ const ended = await this.endCall(call.callId);
389
+ if (ended) results.push({ callId: call.callId, action: "ended" });
400
390
  }
391
+ }
401
392
 
402
- console.log(`Cleaned up ${results.length} stale connections`);
403
- return results;
404
- });
393
+ console.log(`Cleaned up ${results.length} stale connections`);
394
+ return results;
405
395
  } catch (error) {
406
396
  console.error("Failed to cleanup stale connections:", error);
407
- throw error;
408
- } finally {
409
- await session.endSession();
397
+ return [];
398
+ }
399
+ }
400
+
401
+ async moveCallToHoldQueue(callId, reason = "Agent disconnected") {
402
+ try {
403
+ const call = await Call.findOneAndUpdate(
404
+ { callId },
405
+ {
406
+ $set: {
407
+ agentId: null,
408
+ status: "hold",
409
+ isOnHold: true,
410
+ autoAccepted: true,
411
+ isHoldConnection: true,
412
+ lastHeartbeat: new Date(),
413
+ },
414
+ $inc: { priority: 1 },
415
+ $push: {
416
+ callHistory: {
417
+ timestamp: new Date(),
418
+ action: "disconnected",
419
+ agentId: null,
420
+ details: reason,
421
+ },
422
+ },
423
+ },
424
+ { new: true }
425
+ );
426
+
427
+ if (call) {
428
+ console.log(
429
+ `Call ${callId} moved back to hold queue due to: ${reason}`
430
+ );
431
+ }
432
+ return call || null;
433
+ } catch (error) {
434
+ console.error(`Failed to move call ${callId} to hold queue:`, error);
435
+ return null;
436
+ }
437
+ }
438
+
439
+ async endCall(callId, agentId = null) {
440
+ try {
441
+ const call = await Call.findOneAndUpdate(
442
+ { callId },
443
+ {
444
+ $set: {
445
+ status: "ended",
446
+ endTime: new Date(),
447
+ lastHeartbeat: new Date(),
448
+ },
449
+ $push: {
450
+ callHistory: {
451
+ timestamp: new Date(),
452
+ action: "ended",
453
+ agentId,
454
+ details: "Call ended",
455
+ },
456
+ },
457
+ },
458
+ { new: true }
459
+ );
460
+ return call || null;
461
+ } catch (error) {
462
+ console.error(`Failed to end call ${callId}:`, error);
463
+ return null;
410
464
  }
411
465
  }
412
466
 
413
- // Helper methods
414
467
  _buildDateFilter(timeframe) {
415
468
  const now = new Date();
416
469
  switch (timeframe) {
@@ -464,77 +517,13 @@ class CallRepository {
464
517
  };
465
518
  }
466
519
 
467
- async _updateCallMetrics(action, session = null) {
520
+ async _updateCallMetrics(action) {
468
521
  try {
469
522
  console.log(`Updating metrics for action: ${action}`);
470
523
  } catch (error) {
471
524
  console.error("Failed to update metrics:", error);
472
525
  }
473
526
  }
474
-
475
- async moveCallToHoldQueue(
476
- callId,
477
- reason = "Agent disconnected",
478
- options = {}
479
- ) {
480
- const session = options.session || (await mongoose.startSession());
481
- const shouldCommitSession = !options.session;
482
-
483
- try {
484
- return await session.withTransaction(async () => {
485
- const call = await Call.findOne({ callId }).session(session);
486
- if (!call) throw new Error(`Call ${callId} not found`);
487
-
488
- const previousAgentId = call.agentId;
489
-
490
- call.agentId = null;
491
- call.status = "hold";
492
- call.isOnHold = true;
493
- call.autoAccepted = true;
494
- call.isHoldConnection = true;
495
- call.priority = Math.min(call.priority + 1, 10);
496
- call.addToHistory("disconnected", previousAgentId, reason);
497
- call.updateHeartbeat();
498
-
499
- await call.save({ session });
500
-
501
- console.log(
502
- `Call ${callId} moved back to hold queue due to: ${reason}`
503
- );
504
- return call;
505
- });
506
- } catch (error) {
507
- console.error(`Failed to move call ${callId} to hold queue:`, error);
508
- throw error;
509
- } finally {
510
- if (shouldCommitSession) await session.endSession();
511
- }
512
- }
513
-
514
- async endCall(callId, agentId = null, options = {}) {
515
- const session = options.session || (await mongoose.startSession());
516
- const shouldCommitSession = !options.session;
517
-
518
- try {
519
- return await session.withTransaction(async () => {
520
- const call = await Call.findOne({ callId }).session(session);
521
- if (!call) throw new Error(`Call ${callId} not found`);
522
-
523
- call.status = "ended";
524
- call.endTime = new Date();
525
- call.duration = call.currentDuration;
526
- call.addToHistory("ended", agentId, "Call ended");
527
-
528
- await call.save({ session });
529
- return call;
530
- });
531
- } catch (error) {
532
- console.error(`Failed to end call ${callId}:`, error);
533
- throw error;
534
- } finally {
535
- if (shouldCommitSession) await session.endSession();
536
- }
537
- }
538
527
  }
539
528
 
540
529
  module.exports = new CallRepository();
@@ -16,6 +16,7 @@ const customerProfileRepository = require("./customerProfile.repository");
16
16
  const customerTimelineRepository = require("./customerTimeline.repository");
17
17
  const shopRepository = require("./shop.repository");
18
18
  const callRepository = require("./call.repository");
19
+ const interactiveRepository = require("./interactive.repository");
19
20
 
20
21
  exports.module = {
21
22
  workspaceRepository,
@@ -36,4 +37,5 @@ exports.module = {
36
37
  customerTimelineRepository,
37
38
  shopRepository,
38
39
  callRepository,
40
+ interactiveRepository,
39
41
  };
@@ -0,0 +1,124 @@
1
+ // repositories/interactive.repository.js
2
+ const Interactive = require("../../models/Interactive");
3
+ const { getRedisData, setRedisData } = require("../../config/redis");
4
+
5
+ const CACHE_KEY_ALL = "interactive_all";
6
+
7
+ /**
8
+ * Get cached interactive for all workspaces.
9
+ */
10
+ const getCachedAllInteractive = async () => {
11
+ let interactive = await getRedisData(CACHE_KEY_ALL);
12
+ if (!interactive) {
13
+ interactive = await Interactive.find({}).lean().exec();
14
+ await setRedisData(CACHE_KEY_ALL, interactive);
15
+ }
16
+ return interactive;
17
+ };
18
+
19
+ /**
20
+ * Update cached interactive for all workspaces.
21
+ */
22
+ const updateCachedAllInteractive = async () => {
23
+ const interactive = await Interactive.find({}).lean().exec();
24
+ await setRedisData(CACHE_KEY_ALL, interactive);
25
+ };
26
+
27
+ /**
28
+ * Get interactive for a specific workspace from cache.
29
+ */
30
+ const getWorkspaceInteractive = async (workspaceId) => {
31
+ if (!workspaceId) return [];
32
+ const allInteractive = await getCachedAllInteractive();
33
+ return allInteractive.filter(
34
+ (item) => item.workspaceId?.toString() === workspaceId.toString()
35
+ );
36
+ };
37
+ /**
38
+ * Get interactive by ID (cache first).
39
+ */
40
+ const getInteractiveById = async (id) => {
41
+ if (!id) return null;
42
+
43
+ const allInteractive = await getCachedAllInteractive();
44
+ let interactive = allInteractive.find(
45
+ (item) => item._id?.toString() === id.toString()
46
+ );
47
+
48
+ // If not found in cache, fetch from DB and update cache
49
+ if (!interactive) {
50
+ interactive = await Interactive.findById(id).lean().exec();
51
+ if (interactive) {
52
+ await updateCachedAllInteractive();
53
+ }
54
+ }
55
+
56
+ return interactive || null;
57
+ };
58
+
59
+ /**
60
+ * Create new interactive and refresh cache.
61
+ */
62
+ const createInteractive = async (data) => {
63
+ const newInteractive = await Interactive.create(data);
64
+ if (newInteractive) {
65
+ await updateCachedAllInteractive();
66
+ }
67
+ return newInteractive;
68
+ };
69
+
70
+ /**
71
+ * Update interactive by ID and refresh cache.
72
+ */
73
+ const updateInteractiveById = async (id, data) => {
74
+ const updated = await Interactive.findByIdAndUpdate(id, data, {
75
+ new: true,
76
+ }).exec();
77
+ if (updated) {
78
+ await updateCachedAllInteractive();
79
+ }
80
+ return updated;
81
+ };
82
+
83
+ /**
84
+ * Update interactive by filter and refresh cache.
85
+ */
86
+ const updateSingleInteractiveByFilter = async (filter, data) => {
87
+ const updated = await Interactive.findOneAndUpdate(filter, data, {
88
+ new: true,
89
+ }).exec();
90
+ if (updated) {
91
+ await updateCachedAllInteractive();
92
+ }
93
+ return updated;
94
+ };
95
+
96
+ /**
97
+ * Delete interactive by ID and refresh cache.
98
+ */
99
+ const deleteInteractiveById = async (id) => {
100
+ const deleted = await Interactive.findByIdAndDelete(id).exec();
101
+ if (deleted) {
102
+ await updateCachedAllInteractive();
103
+ }
104
+ return deleted;
105
+ };
106
+
107
+ /**
108
+ * Delete interactive by filter and refresh cache.
109
+ */
110
+ const deleteInteractiveByFilter = async (filter) => {
111
+ await Interactive.deleteMany(filter).exec();
112
+ await updateCachedAllInteractive();
113
+ };
114
+
115
+ module.exports = {
116
+ updateCachedAllInteractive,
117
+ getWorkspaceInteractive,
118
+ createInteractive,
119
+ updateInteractiveById,
120
+ updateSingleInteractiveByFilter,
121
+ deleteInteractiveById,
122
+ deleteInteractiveByFilter,
123
+ getInteractiveById,
124
+ };
@@ -61,6 +61,9 @@ const AutomationAction = new Schema({
61
61
  deleteComment: {
62
62
  enabled: { type: Boolean, default: false },
63
63
  },
64
+ hideComment: {
65
+ enabled: { type: Boolean, default: false },
66
+ },
64
67
  closeChat: {
65
68
  enabled: { type: Boolean, default: false },
66
69
  },
@@ -96,6 +99,11 @@ const AutomationAction = new Schema({
96
99
  default: null,
97
100
  },
98
101
  members: [userJoin],
102
+ templateFormId: {
103
+ type: mongoose.Schema.Types.ObjectId,
104
+ ref: "FormTemplate",
105
+ default: null,
106
+ },
99
107
  },
100
108
  escalation: {
101
109
  enabled: { type: Boolean, default: false },
@@ -160,6 +168,7 @@ const AutomationCondition = new Schema({
160
168
  "post",
161
169
  "shift",
162
170
  "newCommentPost",
171
+ "profile",
163
172
  ],
164
173
  },
165
174
  keyValue: {
@@ -183,6 +192,7 @@ const AutomationCondition = new Schema({
183
192
  "orderConfirmation",
184
193
  "orderPublish",
185
194
  "newCommentPost",
195
+ "profile",
186
196
  ],
187
197
  },
188
198
  subKeyValue: {
package/models/Call.js CHANGED
@@ -55,7 +55,7 @@ const callSchema = new mongoose.Schema(
55
55
  default: null,
56
56
  },
57
57
  duration: {
58
- type: Number, // in seconds
58
+ type: Number,
59
59
  default: 0,
60
60
  },
61
61
  whatsappSdp: {
@@ -104,6 +104,14 @@ const callSchema = new mongoose.Schema(
104
104
  details: String,
105
105
  },
106
106
  ],
107
+ platformTimestamp: {
108
+ type: String,
109
+ default: "",
110
+ },
111
+ receiver: {
112
+ type: String,
113
+ default: "",
114
+ },
107
115
  platformId: {
108
116
  type: Schema.Types.ObjectId,
109
117
  ref: "Integration",
package/models/Card.js CHANGED
@@ -156,6 +156,11 @@ const cardSchema = new mongoose.Schema(
156
156
  type: mongoose.Schema.Types.Mixed,
157
157
  default: {},
158
158
  },
159
+ templateFormId: {
160
+ type: mongoose.Schema.Types.ObjectId,
161
+ ref: "FormTemplate",
162
+ default: null,
163
+ },
159
164
  },
160
165
  { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }
161
166
  );
package/models/Chatbot.js CHANGED
@@ -30,6 +30,7 @@ const ChatbotSchema = new Schema(
30
30
  },
31
31
  events: [
32
32
  {
33
+ type: { type: String, default: "" },
33
34
  value: { type: String, default: "" },
34
35
  },
35
36
  ],
@@ -0,0 +1,70 @@
1
+ const { Schema, Types, model } = require("mongoose");
2
+
3
+ const ActionSchema = new Schema({
4
+ type: {
5
+ type: String,
6
+ default: "",
7
+ },
8
+ interactiveId: { type: Types.ObjectId, ref: "Interactive", default: null },
9
+ value: { type: Schema.Types.Mixed, default: "" },
10
+ });
11
+ const InteractiveSchema = new Schema({
12
+ name: { type: String, default: "" },
13
+ workspace: { type: Types.ObjectId, ref: "Workspace", default: null },
14
+ type: {
15
+ type: String,
16
+ enum: ["button", "list"],
17
+ default: "list",
18
+ },
19
+ header: {
20
+ type: {
21
+ type: String,
22
+ enum: ["text", "image", "video"],
23
+ default: "text",
24
+ },
25
+ value: { type: String, default: "" },
26
+ },
27
+ body: {
28
+ type: {
29
+ type: String,
30
+ default: "text",
31
+ },
32
+ value: { type: String, default: "" },
33
+ },
34
+ footer: {
35
+ type: {
36
+ type: String,
37
+ default: "text",
38
+ },
39
+ value: { type: String, default: "" },
40
+ },
41
+ buttonTitle: {
42
+ type: String,
43
+ default: "Menu",
44
+ },
45
+ sections: [
46
+ {
47
+ title: { type: String, default: "" },
48
+ rows: [
49
+ {
50
+ id: { type: String, default: "" },
51
+ title: { type: String, default: "" },
52
+ description: { type: String, default: "" },
53
+ action: ActionSchema,
54
+ },
55
+ ],
56
+ },
57
+ ],
58
+ buttons: [
59
+ {
60
+ type: { type: String, default: "reply" },
61
+ reply: {
62
+ id: { type: String, default: "" },
63
+ title: { type: String, default: "" },
64
+ action: ActionSchema,
65
+ },
66
+ },
67
+ ],
68
+ });
69
+
70
+ module.exports = model("Interactive", InteractiveSchema);
package/models.js CHANGED
@@ -43,6 +43,7 @@ const FormTemplate = require("./models/FormTemplate");
43
43
  const GoogleClientInfo = require("./models/GoogleClientInfo");
44
44
  const GoogleCredentials = require("./models/GoogleCredentials");
45
45
  const Integration = require("./models/Integration");
46
+ const Interactive = require("./models/Interactive");
46
47
  const InternalComments = require("./models/InternalComments");
47
48
  const InternalThreads = require("./models/InternalThreads");
48
49
  const JobDesign = require("./models/JobDesign");
@@ -152,6 +153,7 @@ module.exports = {
152
153
  GoogleClientInfo,
153
154
  GoogleCredentials,
154
155
  Integration,
156
+ Interactive,
155
157
  InternalComments,
156
158
  InternalThreads,
157
159
  JobDesign,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shuttlepro-shared",
3
- "version": "1.4.01",
3
+ "version": "1.4.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {