@peopl-health/nexus 2.5.10 → 2.6.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.
@@ -14,6 +14,7 @@ const Monitoreo_ID = require('./runtimeConfig').get('AIRTABLE_MONITOREO_ID') ||
14
14
  const Programa_Juntas_ID = require('./runtimeConfig').get('AIRTABLE_PROGRAMA_JUNTAS_ID') || 'appKFWzkcDEWlrXBE';
15
15
  const Symptoms_ID = require('./runtimeConfig').get('AIRTABLE_SYMPTOMS_ID') || 'appQRhZlQ9tMfYZWJ';
16
16
  const Webinars_Leads_ID = require('./runtimeConfig').get('AIRTABLE_WEBINARS_LEADS_ID') || 'appzjpVXTI0TgqGPq';
17
+ const Product_ID = require('./runtimeConfig').get('AIRTABLE_PRODUCT_ID') || 'appu2YDW2pKDYLL5H';
17
18
 
18
19
  // Initialize Airtable only if API key is provided
19
20
  let airtable = null;
@@ -29,7 +30,8 @@ const BASE_MAP = {
29
30
  monitoreo: Monitoreo_ID,
30
31
  programa: Programa_Juntas_ID,
31
32
  symptoms: Symptoms_ID,
32
- webinars: Webinars_Leads_ID
33
+ webinars: Webinars_Leads_ID,
34
+ product: Product_ID
33
35
  };
34
36
 
35
37
  module.exports = {
@@ -43,7 +45,7 @@ module.exports = {
43
45
  Programa_Juntas_ID,
44
46
  Symptoms_ID,
45
47
  Webinars_Leads_ID,
46
-
48
+ Product_ID,
47
49
  // Helper function to get base by ID
48
50
  getBase: (baseKeyOrId = require('./runtimeConfig').get('AIRTABLE_BASE_ID')) => {
49
51
  if (!airtable) {
@@ -0,0 +1,56 @@
1
+ const { addRecord, getRecordByFilter } = require('../services/airtableService');
2
+ const { Product_ID } = require('../config/airtableConfig');
3
+ const { logger } = require('../utils/logger');
4
+
5
+ async function logCaseDocumentationToAirtable(reporter, whatsapp_id, tableName, dynamicFields = {}) {
6
+ try {
7
+ let patientId = null;
8
+ try {
9
+ const patientRecords = await getRecordByFilter(Product_ID, 'estado_general', `{whatsapp_id}='${whatsapp_id}'`);
10
+ if (patientRecords && patientRecords.length > 0) {
11
+ patientId = patientRecords[0].product_record_id;
12
+ }
13
+ } catch (err) {
14
+ logger.warn('Could not find patient in estado_general:', err.message);
15
+ }
16
+
17
+ const airtableData = {
18
+ reporter,
19
+ ...(patientId && { patient_id: [patientId] }),
20
+ ...dynamicFields
21
+ };
22
+
23
+ Object.keys(airtableData).forEach(key => {
24
+ if (airtableData[key] === undefined) {
25
+ delete airtableData[key];
26
+ }
27
+ });
28
+
29
+ await addRecord(Product_ID, tableName, airtableData);
30
+ logger.info(`Case documentation logged to Airtable successfully in table: ${tableName}`);
31
+ } catch (error) {
32
+ logger.error('Error logging case documentation to Airtable:', error);
33
+ throw error;
34
+ }
35
+ }
36
+
37
+ const caseDocumentationController = async (req, res) => {
38
+ try {
39
+ const { reporter, whatsapp_id, table_name, ...dynamicFields } = req.body;
40
+
41
+ if (!reporter) { return res.status(400).json({ success: false, error: 'Reporter username is required' }); }
42
+ if (!whatsapp_id) { return res.status(400).json({ success: false, error: 'WhatsApp ID is required' }); }
43
+ if (!table_name) { return res.status(400).json({ success: false, error: 'Table name is required' }); }
44
+
45
+ logCaseDocumentationToAirtable(reporter, whatsapp_id, table_name, dynamicFields).catch(err =>
46
+ logger.error('Background case documentation logging failed:', err)
47
+ );
48
+
49
+ res.status(201).json({ success: true, message: 'Case documentation submitted successfully' });
50
+ } catch (error) {
51
+ logger.error('Error submitting case documentation:', error);
52
+ res.status(500).json({ success: false, error: error.message });
53
+ }
54
+ };
55
+
56
+ module.exports = { caseDocumentationController };
@@ -13,7 +13,7 @@ const Message = mongoose.models.Message;
13
13
 
14
14
  const getConversationController = async (req, res) => {
15
15
  const startTime = Date.now();
16
- logger.info('Starting getConversationController at', new Date().toISOString());
16
+ logger.info('Starting getConversationController at', { time: new Date().toISOString() });
17
17
 
18
18
  try {
19
19
  // Parse pagination parameters
@@ -34,7 +34,7 @@ const getConversationController = async (req, res) => {
34
34
  }
35
35
 
36
36
  const messageCount = await Message.countDocuments({});
37
- logger.info('Total message count:', messageCount);
37
+ logger.info('Total message count:', { messageCount });
38
38
 
39
39
  if (messageCount === 0) {
40
40
  logger.info('No messages found in database, returning empty conversations list');
@@ -63,7 +63,7 @@ const getConversationController = async (req, res) => {
63
63
  const totalPages = Math.ceil(total / limit);
64
64
  const totalTime = Date.now() - startTime;
65
65
 
66
- logger.info('Number of conversations found:', conversations?.length || 0);
66
+ logger.info('Number of conversations found:', { conversationsCount: conversations?.length || 0 });
67
67
  logger.info(`Total controller execution time: ${totalTime}ms`);
68
68
  logger.info(`Filter: ${filter}, Pagination: ${conversations.length} of ${total} conversations (page ${page}/${totalPages})`);
69
69
 
@@ -83,8 +83,8 @@ const getConversationController = async (req, res) => {
83
83
  logger.info('Response sent successfully!');
84
84
 
85
85
  } catch (error) {
86
- logger.error('Error fetching conversations:', error);
87
- logger.error('Error stack:', error.stack);
86
+ logger.error('Error fetching conversations:', { error });
87
+ logger.error('Error stack:', { error: error.stack });
88
88
  res.status(500).json({
89
89
  success: false,
90
90
  error: error.message || 'Failed to fetch conversations'
@@ -93,10 +93,10 @@ const getConversationController = async (req, res) => {
93
93
  };
94
94
 
95
95
  const getConversationMessagesController = async (req, res) => {
96
- logger.info('Starting getConversationMessagesController at', new Date().toISOString());
96
+ logger.info('Starting getConversationMessagesController at', { time: new Date().toISOString() });
97
97
  try {
98
98
  const { phoneNumber } = req.params;
99
- logger.info('Requested conversation for phone number:', phoneNumber);
99
+ logger.info('Requested conversation for phone number:', { phoneNumber });
100
100
 
101
101
  if (!phoneNumber) {
102
102
  return res.status(400).json({
@@ -113,7 +113,7 @@ const getConversationMessagesController = async (req, res) => {
113
113
  try {
114
114
  query.createdAt = { $lt: new Date(before) };
115
115
  } catch (parseError) {
116
- logger.warn('Invalid date format for before parameter:', before, parseError);
116
+ logger.warn('Invalid date format for before parameter:', { before, parseError });
117
117
  query.createdAt = { $lt: before };
118
118
  }
119
119
  }
@@ -142,12 +142,12 @@ const getConversationMessagesController = async (req, res) => {
142
142
 
143
143
  if (msg.media) {
144
144
  if (!msg.media || typeof msg.media !== 'object') {
145
- logger.warn('Found media message with invalid media data:', msg._id);
145
+ logger.warn('Found media message with invalid media data:', { msgId: msg._id });
146
146
  return true;
147
147
  }
148
148
 
149
149
  if (msg.media && (typeof msg.media.data === 'function' || msg.media.data instanceof Buffer)) {
150
- logger.warn('Found media message with Buffer data that might cause serialization issues:', msg._id);
150
+ logger.warn('Found media message with Buffer data that might cause serialization issues:', { msgId: msg._id });
151
151
  return true;
152
152
  }
153
153
  }
@@ -163,7 +163,7 @@ const getConversationMessagesController = async (req, res) => {
163
163
  logger.info('No messages found for this query');
164
164
  }
165
165
  } catch (err) {
166
- logger.error('Database query error in message retrieval:', err);
166
+ logger.error('Database query error in message retrieval:', { err });
167
167
  messages = [];
168
168
  }
169
169
 
@@ -174,7 +174,7 @@ const getConversationMessagesController = async (req, res) => {
174
174
  JSON.stringify(msg);
175
175
  return msg;
176
176
  } catch (serializationError) {
177
- logger.error(`Found non-serializable message with ID ${msg._id}:`, serializationError);
177
+ logger.error(`Found non-serializable message with ID ${msg._id}:`, { serializationError });
178
178
  return {
179
179
  _id: msg._id?.toString() || 'unknown',
180
180
  numero: msg.numero || phoneNumber,
@@ -197,7 +197,7 @@ const getConversationMessagesController = async (req, res) => {
197
197
  });
198
198
  logger.info('Successfully sent conversation messages response');
199
199
  } catch (error) {
200
- logger.error(`Error fetching conversation for ${req.params?.phoneNumber || 'unknown'}:`, error);
200
+ logger.error(`Error fetching conversation for ${req.params?.phoneNumber || 'unknown'}:`, { error });
201
201
  res.status(500).json({
202
202
  success: false,
203
203
  error: error.message || 'Failed to fetch conversation'
@@ -206,7 +206,7 @@ const getConversationMessagesController = async (req, res) => {
206
206
  };
207
207
 
208
208
  const getConversationReplyController = async (req, res) => {
209
- logger.info('Starting getConversationReplyController at', new Date().toISOString());
209
+ logger.info('Starting getConversationReplyController at', { time: new Date().toISOString() });
210
210
  try {
211
211
  const { phoneNumber, message, mediaData, contentSid, variables } = req.body;
212
212
  logger.info('Reply request params:', {
@@ -225,7 +225,7 @@ const getConversationReplyController = async (req, res) => {
225
225
  }
226
226
 
227
227
  const formattedPhoneNumber = ensureWhatsAppFormat(phoneNumber);
228
- logger.info('Formatted phone number:', formattedPhoneNumber);
228
+ logger.info('Formatted phone number:', { formattedPhoneNumber });
229
229
 
230
230
  const messageData = {
231
231
  code: formattedPhoneNumber,
@@ -235,7 +235,7 @@ const getConversationReplyController = async (req, res) => {
235
235
 
236
236
  // Handle template message (contentSid provided)
237
237
  if (contentSid) {
238
- logger.info('Processing template message with contentSid:', contentSid);
238
+ logger.info('Processing template message with contentSid:', { contentSid });
239
239
  messageData.contentSid = contentSid;
240
240
 
241
241
  if (variables && Object.keys(variables).length > 0) {
@@ -281,10 +281,10 @@ const getConversationReplyController = async (req, res) => {
281
281
  message: 'Reply sent successfully'
282
282
  });
283
283
  } catch (error) {
284
- logger.error('Error sending reply:', error);
284
+ logger.error('Error sending reply:', { error });
285
285
  logger.info('Request body', { body: req.body || {} });
286
286
  const errorMsg = error.message || 'Failed to send reply';
287
- logger.error('Responding with error:', errorMsg);
287
+ logger.error('Responding with error:', { errorMsg });
288
288
  res.status(500).json({
289
289
  success: false,
290
290
  error: errorMsg
@@ -361,7 +361,7 @@ const searchConversationsController = async (req, res) => {
361
361
  batch.map(p => `{whatsapp_id} = "${p}"`).join(', ') +
362
362
  ')';
363
363
  return getRecordByFilter(Historial_Clinico_ID, 'estado_general', formula).catch(error => {
364
- logger.error('Error fetching Airtable batch for search:', error);
364
+ logger.error('Error fetching Airtable batch for search:', { error });
365
365
  return [];
366
366
  });
367
367
  });
@@ -380,7 +380,7 @@ const searchConversationsController = async (req, res) => {
380
380
  });
381
381
  logger.info(`Found ${Object.keys(airtableNameMap).length} names in Airtable for search results (${batches.length} batches)`);
382
382
  } catch (error) {
383
- logger.error('Error fetching names from Airtable for search, falling back to nombre_whatsapp:', error);
383
+ logger.error('Error fetching names from Airtable for search, falling back to nombre_whatsapp:', { error });
384
384
  }
385
385
  }
386
386
 
@@ -445,7 +445,7 @@ const searchConversationsController = async (req, res) => {
445
445
  });
446
446
 
447
447
  } catch (error) {
448
- logger.error('Error searching conversations:', error);
448
+ logger.error('Error searching conversations:', { error });
449
449
  res.status(500).json({
450
450
  success: false,
451
451
  error: error.message || 'Failed to search conversations'
@@ -478,7 +478,7 @@ const getConversationsByNameController = async (req, res) => {
478
478
  }))
479
479
  });
480
480
  } catch (error) {
481
- logger.error('Error fetching conversations by name:', error);
481
+ logger.error('Error fetching conversations by name:', { error });
482
482
  res.status(500).json({
483
483
  success: false,
484
484
  error: error.message || 'Failed to fetch conversations by name'
@@ -524,7 +524,7 @@ const getNewMessagesController = async (req, res) => {
524
524
  messages: messages
525
525
  });
526
526
  } catch (error) {
527
- logger.error(`Error fetching new messages for ${req.params.phoneNumber}:`, error);
527
+ logger.error(`Error fetching new messages for ${req.params.phoneNumber}:`, { error });
528
528
  res.status(500).json({
529
529
  success: false,
530
530
  error: error.message || 'Failed to fetch new messages'
@@ -533,7 +533,7 @@ const getNewMessagesController = async (req, res) => {
533
533
  };
534
534
 
535
535
  const markMessagesAsReadController = async (req, res) => {
536
- logger.info('Starting markMessagesAsReadController at', new Date().toISOString());
536
+ logger.info('Starting markMessagesAsReadController at', { time: new Date().toISOString() });
537
537
  try {
538
538
  const { phoneNumber } = req.params;
539
539
 
@@ -544,7 +544,7 @@ const markMessagesAsReadController = async (req, res) => {
544
544
  });
545
545
  }
546
546
 
547
- logger.info('Marking messages as read for phone number:', phoneNumber);
547
+ logger.info('Marking messages as read for phone number:', { phoneNumber });
548
548
  const result = await Message.updateMany(
549
549
  {
550
550
  numero: phoneNumber,
@@ -563,8 +563,8 @@ const markMessagesAsReadController = async (req, res) => {
563
563
  modifiedCount: result.nModified || result.modifiedCount
564
564
  });
565
565
  } catch (error) {
566
- logger.error(`Error marking messages as read for ${req.params?.phoneNumber || 'unknown'}:`, error);
567
- logger.error('Error stack:', error.stack);
566
+ logger.error(`Error marking messages as read for ${req.params?.phoneNumber || 'unknown'}:`, { error });
567
+ logger.error('Error stack:', { error: error.stack });
568
568
  res.status(500).json({
569
569
  success: false,
570
570
  error: error.message || 'Failed to mark messages as read'
@@ -573,7 +573,7 @@ const markMessagesAsReadController = async (req, res) => {
573
573
  };
574
574
 
575
575
  const sendTemplateToNewNumberController = async (req, res) => {
576
- logger.info('Starting sendTemplateToNewNumberController at', new Date().toISOString());
576
+ logger.info('Starting sendTemplateToNewNumberController at', { time: new Date().toISOString() });
577
577
  try {
578
578
  const { phoneNumber, templateId, variables } = req.body;
579
579
 
@@ -618,7 +618,7 @@ const sendTemplateToNewNumberController = async (req, res) => {
618
618
  messageId: message.sid
619
619
  });
620
620
  } catch (error) {
621
- logger.error('Error sending template to new number:', error);
621
+ logger.error('Error sending template to new number:', { error });
622
622
  res.status(500).json({
623
623
  success: false,
624
624
  error: error.message || 'Failed to send template to new number'
@@ -627,14 +627,14 @@ const sendTemplateToNewNumberController = async (req, res) => {
627
627
  };
628
628
 
629
629
  const getOpenAIThreadMessagesController = async (req, res) => {
630
- logger.info('Starting getOpenAIThreadMessagesController at', new Date().toISOString());
630
+ logger.info('Starting getOpenAIThreadMessagesController at', { time: new Date().toISOString() });
631
631
  try {
632
632
  const { phoneNumber } = req.params;
633
633
  const { limit = 50, order = 'desc', runId } = req.query;
634
634
  const variant = process.env.VARIANT || 'assistants';
635
635
 
636
- logger.info('Fetching OpenAI thread messages for:', phoneNumber);
637
- logger.info('Variant:', variant, 'Limit:', limit, 'Order:', order);
636
+ logger.info('Fetching OpenAI thread messages for:', { phoneNumber });
637
+ logger.info('Variant:', { variant }, 'Limit:', { limit }, 'Order:', { order });
638
638
 
639
639
  if (!phoneNumber) {
640
640
  return res.status(400).json({
@@ -649,7 +649,7 @@ const getOpenAIThreadMessagesController = async (req, res) => {
649
649
  }).sort({ createdAt: -1 });
650
650
 
651
651
  if (!thread) {
652
- logger.info('No active OpenAI thread found for:', phoneNumber);
652
+ logger.info('No active OpenAI thread found for:', { phoneNumber });
653
653
  return res.status(404).json({
654
654
  success: false,
655
655
  error: 'No active OpenAI thread found for this phone number'
@@ -657,14 +657,14 @@ const getOpenAIThreadMessagesController = async (req, res) => {
657
657
  }
658
658
 
659
659
  let conversationId = thread.conversation_id;
660
- logger.info('Thread found - Conversation ID:', conversationId);
660
+ logger.info('Thread found - Conversation ID:', { conversationId });
661
661
 
662
662
  const provider = llmConfig.getOpenAIProvider({ instantiate: true, variant });
663
663
  if (!provider) {
664
664
  throw new Error('OpenAI provider not initialized');
665
665
  }
666
666
 
667
- logger.info('Using provider variant:', provider.getVariant());
667
+ logger.info('Using provider variant:', { variant: provider.getVariant() });
668
668
 
669
669
  const queryParams = {
670
670
  threadId: conversationId,
@@ -679,7 +679,7 @@ const getOpenAIThreadMessagesController = async (req, res) => {
679
679
  let messages;
680
680
  let threadRecreated = false;
681
681
 
682
- logger.info('Calling listMessages with params:', queryParams);
682
+ logger.info('Calling listMessages with params:', { queryParams });
683
683
  messages = await withThreadRecovery(
684
684
  async (currentThread = thread) => {
685
685
  if (currentThread !== thread) {
@@ -712,8 +712,8 @@ const getOpenAIThreadMessagesController = async (req, res) => {
712
712
  });
713
713
 
714
714
  } catch (error) {
715
- logger.error('Error fetching OpenAI thread messages:', error);
716
- logger.error('Error stack:', error.stack);
715
+ logger.error('Error fetching OpenAI thread messages:', { error });
716
+ logger.error('Error stack:', { error: error.stack });
717
717
  res.status(500).json({
718
718
  success: false,
719
719
  error: error.message || 'Failed to fetch OpenAI thread messages'
@@ -23,7 +23,8 @@ const conversationRouteDefinitions = {
23
23
  'POST /reply': 'getConversationReplyController',
24
24
  'POST /send-template': 'sendTemplateToNewNumberController',
25
25
  'POST /:phoneNumber/read': 'markMessagesAsReadController',
26
- 'POST /report-bug': 'reportBugController'
26
+ 'POST /report-bug': 'reportBugController',
27
+ 'POST /case-documentation': 'caseDocumentationController'
27
28
  };
28
29
 
29
30
  const mediaRouteDefinitions = {
@@ -67,8 +68,8 @@ const templateRouteDefinitions = {
67
68
  };
68
69
 
69
70
  const threadRouteDefinitions = {
70
- 'PUT /review/:code': 'updateThreadReviewStatus',
71
71
  'PUT /review/all': 'updateAllThreadsReviewStatus',
72
+ 'PUT /review/:code': 'updateThreadReviewStatus',
72
73
  'GET /review': 'getThreadsByReviewStatus'
73
74
  };
74
75
 
@@ -91,6 +92,7 @@ const createRouter = (routeDefinitions, controllers) => {
91
92
  // Import built-in controllers
92
93
  const assistantController = require('../controllers/assistantController');
93
94
  const bugReportController = require('../controllers/bugReportController');
95
+ const caseDocumentationController = require('../controllers/caseDocumentationController');
94
96
  const conversationController = require('../controllers/conversationController');
95
97
  const interactionController = require('../controllers/interactionController');
96
98
  const mediaController = require('../controllers/mediaController');
@@ -125,6 +127,7 @@ const builtInControllers = {
125
127
  sendTemplateToNewNumberController: conversationController.sendTemplateToNewNumberController,
126
128
  markMessagesAsReadController: conversationController.markMessagesAsReadController,
127
129
  reportBugController: bugReportController.reportBugController,
130
+ caseDocumentationController: caseDocumentationController.caseDocumentationController,
128
131
 
129
132
  // Interaction controllers
130
133
  addInteractionController: interactionController.addInteractionController,
@@ -12,6 +12,7 @@ const fetchConversationData = async (filter, skip, limit) => {
12
12
  unread: { ...baseMatch, ...unreadMatch },
13
13
  recent: { ...baseMatch, createdAt: { $gt: new Date(Date.now() - 24 * 60 * 60 * 1000) } },
14
14
  'no-response': baseMatch,
15
+ 'pending-review': baseMatch,
15
16
  all: baseMatch
16
17
  };
17
18
 
@@ -24,7 +25,22 @@ const fetchConversationData = async (filter, skip, limit) => {
24
25
  { $sort: { createdAt: 1, timestamp: 1 } },
25
26
  { $group: { _id: '$numero', latestMessage: { $last: '$$ROOT' }, messageCount: { $sum: 1 } } },
26
27
  ...(filter === 'no-response' ? [{ $match: { 'latestMessage.from_me': false } }] : []),
27
- { $sort: { 'latestMessage.createdAt': -1 } },
28
+ {
29
+ $lookup: {
30
+ from: 'threads',
31
+ localField: '_id',
32
+ foreignField: 'code',
33
+ as: 'threadInfo'
34
+ }
35
+ },
36
+ {
37
+ $addFields: {
38
+ review: { $arrayElemAt: ['$threadInfo.review', 0] }
39
+ }
40
+ },
41
+ { $project: { threadInfo: 0 } },
42
+ ...(filter === 'pending-review' ? [{ $match: { $or: [{ review: false }, { review: null }] } }] : []),
43
+ { $sort: { 'latestMessage.createdAt': -1, 'latestMessage.timestamp': -1 } },
28
44
  { $skip: skip },
29
45
  { $limit: limit }
30
46
  ];
@@ -34,9 +50,12 @@ const fetchConversationData = async (filter, skip, limit) => {
34
50
  Message.aggregate(pipeline),
35
51
  Message.aggregate([{ $match: { ...baseMatch, from_me: false } }, { $sort: { createdAt: -1 } }, { $group: { _id: '$numero', name: { $first: '$nombre_whatsapp' } } }]),
36
52
  Message.aggregate([{ $match: { ...baseMatch, ...unreadMatch } }, { $group: { _id: '$numero', unreadCount: { $sum: 1 } } }]),
37
- Message.aggregate(filter === 'no-response'
38
- ? [{ $match: baseMatch }, { $project: { numero: 1, from_me: 1, createdAt: 1, timestamp: 1 } }, { $sort: { createdAt: -1 } }, { $group: { _id: '$numero', latestMessage: { $first: '$$ROOT' } } }, { $match: { 'latestMessage.from_me': false } }, { $count: 'total' }]
39
- : [{ $match: filterConditions }, { $group: { _id: '$numero' } }, { $count: 'total' }]
53
+ Message.aggregate(
54
+ filter === 'no-response'
55
+ ? [{ $match: baseMatch }, { $project: { numero: 1, from_me: 1, createdAt: 1, timestamp: 1 } }, { $sort: { createdAt: -1 } }, { $group: { _id: '$numero', latestMessage: { $first: '$$ROOT' } } }, { $match: { 'latestMessage.from_me': false } }, { $count: 'total' }]
56
+ : filter === 'pending-review'
57
+ ? [{ $match: baseMatch }, { $group: { _id: '$numero' } }, { $lookup: { from: 'threads', localField: '_id', foreignField: 'code', as: 'threadInfo' } }, { $addFields: { review: { $arrayElemAt: ['$threadInfo.review', 0] } } }, { $match: { $or: [{ review: false }, { review: null }] } }, { $count: 'total' }]
58
+ : [{ $match: filterConditions }, { $group: { _id: '$numero' } }, { $count: 'total' }]
40
59
  )
41
60
  ]);
42
61
 
@@ -76,7 +95,8 @@ const processConversations = async (conversations, nameMap, unreadMap) => {
76
95
  unreadCount: unreadMap[phoneNumber] || 0,
77
96
  isLastMessageMedia: !!msg?.media,
78
97
  lastMessageType: getMediaType(msg?.media),
79
- lastMessageFromMe: msg?.from_me || false
98
+ lastMessageFromMe: msg?.from_me || false,
99
+ review: conv?.review || false
80
100
  };
81
101
  };
82
102
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "2.5.10",
3
+ "version": "2.6.0",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",