@weni/unnnic-system 3.25.5 → 3.25.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weni/unnnic-system",
3
- "version": "3.25.5",
3
+ "version": "3.25.7",
4
4
  "type": "commonjs",
5
5
  "files": [
6
6
  "dist",
@@ -67,6 +67,13 @@
67
67
  </p>
68
68
  <template v-else-if="lastMessageMedia.isMedia">
69
69
  <section class="chats-contact__infos__media">
70
+ <p
71
+ v-if="lastMessage.isFromUser"
72
+ class="chats-contact__infos__media__sender-prefix"
73
+ data-testid="media-sender-prefix"
74
+ >
75
+ {{ i18n('you_message_prefix') }}
76
+ </p>
70
77
  <UnnnicIcon
71
78
  :icon="lastMessageMedia.mediaIcon"
72
79
  scheme="fg-base"
@@ -106,6 +113,20 @@
106
113
  <section
107
114
  class="chats-contact__infos__unread-messages-container__pin-container"
108
115
  >
116
+ <UnnnicTooltip
117
+ v-if="pendingResponse"
118
+ class="chats-contact__infos__pending-response"
119
+ :enabled="!!pendingResponseTooltip"
120
+ :text="pendingResponseTooltip"
121
+ side="top"
122
+ >
123
+ <UnnnicIcon
124
+ icon="reply"
125
+ size="sm"
126
+ scheme="fg-info"
127
+ data-testid="pending-response-icon"
128
+ />
129
+ </UnnnicTooltip>
109
130
  <button
110
131
  v-if="isRenderPin"
111
132
  data-testid="pin-button"
@@ -121,6 +142,18 @@
121
142
  :scheme="schemePin"
122
143
  />
123
144
  </button>
145
+ <UnnnicTooltip
146
+ v-else-if="newMessageIndicator"
147
+ class="chats-contact__infos__new-message-indicator-tooltip"
148
+ :enabled="!!newMessageIndicatorTooltip"
149
+ :text="newMessageIndicatorTooltip"
150
+ side="top"
151
+ >
152
+ <span
153
+ class="chats-contact__infos__new-message-indicator"
154
+ data-testid="new-message-indicator"
155
+ />
156
+ </UnnnicTooltip>
124
157
  <p
125
158
  v-else-if="(unreadMessages && !selected) || forceShowUnreadMessages"
126
159
  class="chats-contact__infos__unread-messages"
@@ -148,6 +181,7 @@ import UnnnicIcon from '../Icon.vue';
148
181
  import TransitionRipple from '../TransitionRipple/TransitionRipple.vue';
149
182
  import UnnnicI18n from '../../mixins/i18n';
150
183
  import Checkbox from '../Checkbox/Checkbox.vue';
184
+ import UnnnicTooltip from '../ToolTip/ToolTip.vue';
151
185
 
152
186
  import('moment/dist/locale/es.js');
153
187
  import('moment/dist/locale/pt-br.js');
@@ -162,6 +196,7 @@ export default {
162
196
  UnnnicIcon,
163
197
  TransitionRipple,
164
198
  Checkbox,
199
+ UnnnicTooltip,
165
200
  },
166
201
 
167
202
  mixins: [UnnnicI18n],
@@ -235,6 +270,22 @@ export default {
235
270
  type: String,
236
271
  default: ' fg-base',
237
272
  },
273
+ pendingResponse: {
274
+ type: Boolean,
275
+ default: false,
276
+ },
277
+ pendingResponseTooltip: {
278
+ type: String,
279
+ default: '',
280
+ },
281
+ newMessageIndicator: {
282
+ type: Boolean,
283
+ default: false,
284
+ },
285
+ newMessageIndicatorTooltip: {
286
+ type: String,
287
+ default: '',
288
+ },
238
289
  },
239
290
 
240
291
  emits: ['click', 'clickPin'],
@@ -285,12 +336,18 @@ export default {
285
336
  en: 'Video',
286
337
  es: 'Video',
287
338
  },
339
+ you_message_prefix: {
340
+ 'pt-br': 'Você:',
341
+ en: 'You:',
342
+ es: 'Tú:',
343
+ },
288
344
  },
289
345
  };
290
346
  },
291
347
 
292
348
  computed: {
293
349
  messageInfoAlign() {
350
+ if (this.newMessageIndicator) return 'space-between';
294
351
  return this.unreadMessages && this.selected ? 'center' : 'flex-start';
295
352
  },
296
353
  messageInfoMarginTop() {
@@ -528,6 +585,10 @@ export default {
528
585
  display: flex;
529
586
  align-items: center;
530
587
  gap: $unnnic-spacing-nano;
588
+
589
+ &__sender-prefix {
590
+ color: $unnnic-color-fg-base;
591
+ }
531
592
  }
532
593
 
533
594
  &__title,
@@ -547,6 +608,11 @@ export default {
547
608
  color: $unnnic-color-fg-muted;
548
609
  }
549
610
 
611
+ &__pending-response {
612
+ display: flex;
613
+ align-items: center;
614
+ }
615
+
550
616
  &__unread-messages-container {
551
617
  height: 100%;
552
618
  min-width: max-content;
@@ -560,6 +626,7 @@ export default {
560
626
  &__pin-container {
561
627
  display: flex;
562
628
  align-items: center;
629
+ gap: $unnnic-space-1;
563
630
  position: relative;
564
631
  z-index: 10;
565
632
  }
@@ -617,6 +684,27 @@ export default {
617
684
  color: $unnnic-color-weni-700;
618
685
  }
619
686
 
687
+ &__new-message-indicator {
688
+ display: flex;
689
+ justify-content: center;
690
+ align-items: center;
691
+
692
+ width: $unnnic-space-5;
693
+ min-width: $unnnic-space-5;
694
+ height: $unnnic-space-5;
695
+
696
+ &::before {
697
+ content: '';
698
+ display: block;
699
+
700
+ width: $unnnic-space-2;
701
+ height: $unnnic-space-2;
702
+
703
+ border-radius: 50%;
704
+ background-color: $unnnic-color-bg-blue-strong;
705
+ }
706
+ }
707
+
620
708
  .ellipsis {
621
709
  overflow: hidden;
622
710
  text-overflow: ellipsis;
@@ -1,4 +1,4 @@
1
- import { beforeEach, describe, expect, afterEach, test, vi } from 'vitest';
1
+ import { beforeEach, describe, expect, afterEach, it, vi } from 'vitest';
2
2
  import { mount } from '@vue/test-utils';
3
3
 
4
4
  import UnnnicChatsContact from '../ChatsContact.vue';
@@ -258,4 +258,167 @@ describe('UnnnicChatsContact', () => {
258
258
  expect(pinElement.exists()).toBe(false);
259
259
  });
260
260
  });
261
+
262
+ describe('Media "You:" prefix', () => {
263
+ const imageMedia = {
264
+ media: [
265
+ {
266
+ content_type: 'image/jpeg',
267
+ message: 'a997421d-6238-4bef-912d-e689c1c0db3f',
268
+ url: 'https://example.com/photo.jpg',
269
+ created_on: '2025-04-15T14:58:56.163027-03:00',
270
+ },
271
+ ],
272
+ };
273
+
274
+ it('should not render the prefix on a media message when isFromUser is falsy', async () => {
275
+ await wrapper.setProps({ lastMessage: imageMedia });
276
+
277
+ const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
278
+ expect(prefix.exists()).toBe(false);
279
+ });
280
+
281
+ it('should render the EN "You:" prefix when isFromUser is true', async () => {
282
+ await wrapper.setProps({
283
+ lastMessage: { ...imageMedia, isFromUser: true },
284
+ locale: 'en',
285
+ });
286
+
287
+ const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
288
+ expect(prefix.exists()).toBe(true);
289
+ expect(prefix.text()).toBe('You:');
290
+ });
291
+
292
+ it('should render the PT-BR "Você:" prefix when isFromUser is true', async () => {
293
+ await wrapper.setProps({
294
+ lastMessage: { ...imageMedia, isFromUser: true },
295
+ locale: 'pt-br',
296
+ });
297
+
298
+ const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
299
+ expect(prefix.exists()).toBe(true);
300
+ expect(prefix.text()).toBe('Você:');
301
+ });
302
+
303
+ it('should render the ES "Tú:" prefix when isFromUser is true', async () => {
304
+ await wrapper.setProps({
305
+ lastMessage: { ...imageMedia, isFromUser: true },
306
+ locale: 'es',
307
+ });
308
+
309
+ const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
310
+ expect(prefix.exists()).toBe(true);
311
+ expect(prefix.text()).toBe('Tú:');
312
+ });
313
+
314
+ it('should not render the prefix for non-media messages even when isFromUser is true', async () => {
315
+ await wrapper.setProps({
316
+ lastMessage: { text: 'Hello there', isFromUser: true },
317
+ });
318
+
319
+ const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
320
+ expect(prefix.exists()).toBe(false);
321
+ });
322
+ });
323
+
324
+ describe('New message indicator', () => {
325
+ it('should not render the dot by default', () => {
326
+ const indicator = wrapper.find('[data-testid="new-message-indicator"]');
327
+ expect(indicator.exists()).toBe(false);
328
+ });
329
+
330
+ it('should render the dot when newMessageIndicator is true', async () => {
331
+ await wrapper.setProps({ newMessageIndicator: true });
332
+
333
+ const indicator = wrapper.find('[data-testid="new-message-indicator"]');
334
+ expect(indicator.exists()).toBe(true);
335
+ });
336
+
337
+ it('should hide the unread-count circle when newMessageIndicator and unreadMessages are both set', async () => {
338
+ await wrapper.setProps({
339
+ newMessageIndicator: true,
340
+ unreadMessages: 5,
341
+ });
342
+
343
+ const indicator = wrapper.find('[data-testid="new-message-indicator"]');
344
+ const unreadCount = wrapper.find(
345
+ '.chats-contact__infos__unread-messages',
346
+ );
347
+
348
+ expect(indicator.exists()).toBe(true);
349
+ expect(unreadCount.exists()).toBe(false);
350
+ });
351
+
352
+ it('should keep tooltip disabled when newMessageIndicatorTooltip is empty', async () => {
353
+ await wrapper.setProps({
354
+ newMessageIndicator: true,
355
+ newMessageIndicatorTooltip: '',
356
+ });
357
+
358
+ const tooltip = wrapper.findComponent({ name: 'UnnnicTooltip' });
359
+
360
+ expect(tooltip.exists()).toBe(true);
361
+ expect(tooltip.props('enabled')).toBe(false);
362
+ expect(tooltip.props('text')).toBe('');
363
+ });
364
+
365
+ it('should enable tooltip with the provided text when newMessageIndicatorTooltip is set', async () => {
366
+ const tooltipText = 'New chats received';
367
+
368
+ await wrapper.setProps({
369
+ newMessageIndicator: true,
370
+ newMessageIndicatorTooltip: tooltipText,
371
+ });
372
+
373
+ const tooltip = wrapper.findComponent({ name: 'UnnnicTooltip' });
374
+
375
+ expect(tooltip.exists()).toBe(true);
376
+ expect(tooltip.props('enabled')).toBe(true);
377
+ expect(tooltip.props('text')).toBe(tooltipText);
378
+ });
379
+ });
380
+
381
+ describe('Pending response', () => {
382
+ it('should not render the pending response icon by default', () => {
383
+ const icon = wrapper.find('[data-testid="pending-response-icon"]');
384
+ expect(icon.exists()).toBe(false);
385
+ });
386
+
387
+ it('should render the pending response icon when pendingResponse is true', async () => {
388
+ await wrapper.setProps({ pendingResponse: true });
389
+
390
+ const icon = wrapper.findComponent(
391
+ '[data-testid="pending-response-icon"]',
392
+ );
393
+ expect(icon.exists()).toBe(true);
394
+ expect(icon.props('icon')).toBe('reply');
395
+ expect(icon.props('scheme')).toBe('fg-info');
396
+ });
397
+
398
+ it('should keep tooltip disabled when pendingResponseTooltip is empty', async () => {
399
+ await wrapper.setProps({
400
+ pendingResponse: true,
401
+ pendingResponseTooltip: '',
402
+ });
403
+
404
+ const tooltip = wrapper.findComponent({ name: 'UnnnicTooltip' });
405
+ expect(tooltip.exists()).toBe(true);
406
+ expect(tooltip.props('enabled')).toBe(false);
407
+ expect(tooltip.props('text')).toBe('');
408
+ });
409
+
410
+ it('should enable tooltip with the provided text when pendingResponseTooltip is set', async () => {
411
+ const tooltipText = 'You did not respond to this contact';
412
+
413
+ await wrapper.setProps({
414
+ pendingResponse: true,
415
+ pendingResponseTooltip: tooltipText,
416
+ });
417
+
418
+ const tooltip = wrapper.findComponent({ name: 'UnnnicTooltip' });
419
+ expect(tooltip.exists()).toBe(true);
420
+ expect(tooltip.props('enabled')).toBe(true);
421
+ expect(tooltip.props('text')).toBe(tooltipText);
422
+ });
423
+ });
261
424
  });
@@ -16,6 +16,7 @@ exports[`UnnnicChatsContact > should match snapshot 1`] = `
16
16
  <!--v-if-->
17
17
  <section data-v-8ce6efd5="" class="chats-contact__infos__unread-messages-container__pin-container">
18
18
  <!--v-if-->
19
+ <!--v-if-->
19
20
  </section>
20
21
  </section>
21
22
  <transition-group-stub data-v-17112bed="" data-v-8ce6efd5="" name="grow" tag="div" appear="false" persisted="false" css="true" class="ripples"></transition-group-stub>
@@ -135,6 +135,36 @@ export const Discussion = {
135
135
  },
136
136
  };
137
137
 
138
+ export const PendingResponse = {
139
+ args: {
140
+ ...defaultArgs,
141
+ title: 'Aline',
142
+ projectName: 'Sector name',
143
+ lastMessage: {
144
+ text: 'I’d like to know if powdered dishwasher detergent is available',
145
+ },
146
+ lastInteractionTime: new Date().toISOString(),
147
+ pendingResponse: true,
148
+ pendingResponseTooltip: 'You did not respond to this contact',
149
+ },
150
+ };
151
+
152
+ export const PendingResponsePinned = {
153
+ args: {
154
+ ...defaultArgs,
155
+ title: 'Milena',
156
+ projectName: 'Sector name',
157
+ lastMessage: {
158
+ text: 'Okay, I’ll wait for a response regarding the order status',
159
+ },
160
+ lastInteractionTime: new Date().toISOString(),
161
+ pendingResponse: true,
162
+ pendingResponseTooltip: 'You did not respond to this contact',
163
+ pinned: true,
164
+ activePin: true,
165
+ },
166
+ };
167
+
138
168
  export const ContactList = {
139
169
  args: {
140
170
  ...defaultArgs,
@@ -190,3 +220,56 @@ export const MediaLastMessage = {
190
220
  `,
191
221
  }),
192
222
  };
223
+
224
+ const lastMessageMediaFromUser = Object.fromEntries(
225
+ Object.entries(lastMessageMedia).map(([mediaType, message]) => [
226
+ mediaType,
227
+ { ...message, isFromUser: true },
228
+ ]),
229
+ );
230
+
231
+ export const MediaLastMessageFromUser = {
232
+ args: {
233
+ ...defaultArgs,
234
+ },
235
+ render: (args) => ({
236
+ setup() {
237
+ return { args, lastMessageMediaFromUser };
238
+ },
239
+ components: { unnnicChatsContact },
240
+ template: `
241
+ <div style="display: grid;">
242
+ <unnnic-chats-contact v-for="mediaType in ['geo', 'audio', 'image', 'video', 'document']" v-bind="args" :lastMessage="lastMessageMediaFromUser[mediaType]"/>
243
+ </div>
244
+ `,
245
+ }),
246
+ };
247
+
248
+ export const NewMessageIndicator = {
249
+ args: {
250
+ ...defaultArgs,
251
+ title: 'Aline',
252
+ projectName: 'Sector name',
253
+ lastMessage: {
254
+ text: 'I’d like to know if powdered dishwasher detergent is available',
255
+ },
256
+ lastInteractionTime: new Date().toISOString(),
257
+ unreadMessages: 1,
258
+ newMessageIndicator: true,
259
+ },
260
+ };
261
+
262
+ export const NewMessageIndicatorWithTooltip = {
263
+ args: {
264
+ ...defaultArgs,
265
+ title: 'Aline',
266
+ projectName: 'Sector name',
267
+ lastMessage: {
268
+ text: 'I’d like to know if powdered dishwasher detergent is available',
269
+ },
270
+ lastInteractionTime: new Date().toISOString(),
271
+ unreadMessages: 1,
272
+ newMessageIndicator: true,
273
+ newMessageIndicatorTooltip: 'New chats received',
274
+ },
275
+ };