@chat21/chat21-ionic 3.4.30 → 3.4.32-rc1

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 (90) hide show
  1. package/CHANGELOG.md +131 -2
  2. package/angular.json +1 -0
  3. package/package.json +1 -1
  4. package/src/app/app.component.html +3 -1
  5. package/src/app/app.component.ts +72 -13
  6. package/src/app/chatlib/conversation-detail/message/image/image.component.html +1 -0
  7. package/src/app/chatlib/conversation-detail/message/image/image.component.ts +19 -0
  8. package/src/app/chatlib/list-conversations-component/ion-list-conversations/ion-list-conversations.component.html +14 -2
  9. package/src/app/chatlib/list-conversations-component/ion-list-conversations/ion-list-conversations.component.scss +39 -2
  10. package/src/app/chatlib/list-conversations-component/list-conversations.module.ts +14 -0
  11. package/src/app/components/canned-response/canned-response.component.html +26 -23
  12. package/src/app/components/canned-response/canned-response.component.scss +0 -2
  13. package/src/app/components/canned-response/canned-response.component.ts +3 -1
  14. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.html +24 -1
  15. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.scss +30 -0
  16. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.ts +39 -9
  17. package/src/app/components/conversation-info/info-content/info-content.component.ts +2 -2
  18. package/src/app/components/conversation-info/info-group/info-group.component.ts +23 -21
  19. package/src/app/components/conversations-list/header-conversations-list/header-conversations-list.component.html +1 -1
  20. package/src/app/components/conversations-list/header-conversations-list/header-conversations-list.component.ts +5 -1
  21. package/src/app/components/navbar/navbar.component.html +35 -9
  22. package/src/app/components/navbar/navbar.component.scss +64 -0
  23. package/src/app/components/navbar/navbar.component.ts +100 -42
  24. package/src/app/components/project-item/project-item.component.ts +79 -51
  25. package/src/app/components/sidebar/sidebar.component.html +65 -45
  26. package/src/app/components/sidebar/sidebar.component.ts +110 -117
  27. package/src/app/components/sidebar-user-details/sidebar-user-details.component.html +3 -3
  28. package/src/app/components/sidebar-user-details/sidebar-user-details.component.ts +15 -22
  29. package/src/app/directives/html-entities-encode.pipe.ts +20 -5
  30. package/src/app/modals/create-ticket/create-ticket.page.ts +4 -2
  31. package/src/app/pages/conversation-detail/conversation-detail.page.html +7 -3
  32. package/src/app/pages/conversation-detail/conversation-detail.page.ts +95 -7
  33. package/src/app/pages/conversations-list/conversations-list.module.ts +3 -5
  34. package/src/app/pages/conversations-list/conversations-list.page.html +2 -0
  35. package/src/app/pages/conversations-list/conversations-list.page.ts +51 -11
  36. package/src/app/pages/unassigned-conversations/unassigned-conversations.module.ts +16 -4
  37. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.html +41 -17
  38. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.scss +10 -1
  39. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.ts +114 -9
  40. package/src/app/services/global-settings/global-settings.service.ts +11 -3
  41. package/src/app/services/nav-proxy.service.ts +0 -1
  42. package/src/app/services/project_users/project-users.service.spec.ts +16 -0
  43. package/src/app/services/project_users/project-users.service.ts +63 -0
  44. package/src/app/services/projects/project.service.ts +2 -1
  45. package/src/app/services/tiledesk/tiledesk.service.ts +21 -16
  46. package/src/app/services/triggerEvents/triggerEvents.ts +28 -0
  47. package/src/app/services/websocket/websocket-js.ts +59 -534
  48. package/src/app/services/websocket/websocket-js_old.ts +578 -0
  49. package/src/app/services/websocket/websocket.service.ts +59 -10
  50. package/src/app/services/websocket/websocket.worker.ts +242 -0
  51. package/src/app/shared/shared.module.ts +11 -2
  52. package/src/app/utils/globals.ts +2 -0
  53. package/src/app/utils/permissions.constants.ts +138 -0
  54. package/src/app/utils/project-utils.ts +2 -2
  55. package/src/app/utils/utils.ts +18 -1
  56. package/src/assets/i18n/ar.json +11 -1
  57. package/src/assets/i18n/az.json +11 -1
  58. package/src/assets/i18n/de.json +11 -1
  59. package/src/assets/i18n/en.json +11 -1
  60. package/src/assets/i18n/es.json +11 -1
  61. package/src/assets/i18n/fr.json +11 -1
  62. package/src/assets/i18n/it.json +13 -3
  63. package/src/assets/i18n/kk.json +11 -1
  64. package/src/assets/i18n/pt.json +11 -1
  65. package/src/assets/i18n/ru.json +11 -1
  66. package/src/assets/i18n/sr.json +11 -1
  67. package/src/assets/i18n/sv.json +11 -1
  68. package/src/assets/i18n/tr.json +11 -1
  69. package/src/assets/i18n/uk.json +11 -1
  70. package/src/assets/i18n/uz.json +12 -1
  71. package/src/assets/img/no_data_found.png +0 -0
  72. package/src/assets/js/agentDesktop-sdk.js +55 -0
  73. package/src/assets/js/chat21client.js +36 -0
  74. package/src/assets/js/mqtt-keepalive-worker.js +53 -0
  75. package/src/assets/test.html +5 -2
  76. package/src/chat-config-template.json +1 -0
  77. package/src/chat-config.json +1 -0
  78. package/src/chat21-core/models/projectUsers.ts +19 -0
  79. package/src/chat21-core/models/project_user.ts +2 -1
  80. package/src/chat21-core/providers/abstract/upload.service.ts +5 -1
  81. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +1 -1
  82. package/src/chat21-core/providers/firebase/firebase-upload.service.ts +136 -9
  83. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +1 -1
  84. package/src/chat21-core/providers/native/native-image-repo.ts +1 -1
  85. package/src/chat21-core/providers/native/native-upload-service.ts +143 -45
  86. package/src/chat21-core/providers/tiledesk/tiledesk-auth.service.ts +3 -0
  87. package/src/chat21-core/utils/constants.ts +5 -0
  88. package/src/chat21-core/utils/convertRequestToConversation.ts +1 -1
  89. package/src/chat21-core/utils/utils.ts +53 -3
  90. package/src/variables.scss +3 -0
@@ -9,6 +9,7 @@ import { TiledeskAuthService } from 'src/chat21-core/providers/tiledesk/tiledesk
9
9
  import { compareValues, htmlEntities } from 'src/chat21-core/utils/utils';
10
10
  import { getProjectIdSelectedConversation } from 'src/chat21-core/utils/utils-message';
11
11
  import { PLAN_NAME } from 'src/chat21-core/utils/constants';
12
+ import { PERMISSIONS } from 'src/app/utils/permissions.constants';
12
13
 
13
14
  @Component({
14
15
  selector: 'app-canned-response',
@@ -21,7 +22,7 @@ export class CannedResponseComponent implements OnInit {
21
22
  @Input() conversationWith: string;
22
23
  @Input() conversationWithFullname: string;
23
24
  @Input() currentString: string;
24
- @Input() canShowCanned: boolean = true;
25
+ @Input() roles: Array<string>;
25
26
  @Input() stylesMap: Map<string, string>;
26
27
  @Input() translationMap: Map<string, string>;
27
28
  @Output() onLoadedCannedResponses = new EventEmitter<[any]>();
@@ -36,6 +37,7 @@ export class CannedResponseComponent implements OnInit {
36
37
 
37
38
  public arrowkeyLocation = -1
38
39
 
40
+ PERMISSIONS = PERMISSIONS
39
41
 
40
42
  private logger: LoggerService = LoggerInstance.getInstance();
41
43
  constructor(
@@ -22,6 +22,13 @@
22
22
  {{translationMap?.get('WHATSAPP.LABEL_TEMPLATES')}}
23
23
  </ion-button>
24
24
  </div>
25
+ <!-- OPEN TICKET -->
26
+ <div *ngIf="ticketSection" class="section-option" id="template" tooltip="{{translationMap?.get('TICKET.OPEN_TICKET')}}" placement="top">
27
+ <ion-button fill="clear" [class.active]="section==='ticket'" (click)="onOpenSection('ticket')" [disabled]="channelType === 'direct'">
28
+ <ion-icon name="ticket"></ion-icon>
29
+ {{translationMap?.get('TICKET.OPEN_TICKET')}}
30
+ </ion-button>
31
+ </div>
25
32
  </div>
26
33
 
27
34
  <div class="footerContainerAlert">
@@ -50,6 +57,22 @@
50
57
  </ion-col>
51
58
  </ion-row>
52
59
 
60
+ <ion-row id="ticket" [style.display]="section==='ticket'? 'flex': 'none'">
61
+ <ion-col col-auto>
62
+ <div class="placeholder">{{translationMap.get('TICKET.DESCRIPTION')}}</div>
63
+ <div class="buttons-container">
64
+ <ion-button name="add" size="small" (click)="onClickTicket('open')">
65
+ <ion-icon name="add"></ion-icon>
66
+ {{translationMap?.get('TICKET.CONFIRM')}}
67
+ </ion-button>
68
+ <ion-button size="small" color="danger" (click)="onClickTicket('close')">
69
+ <ion-icon name="close"></ion-icon>
70
+ {{translationMap?.get('TICKET.CLOSE')}}
71
+ </ion-button>
72
+ </div>
73
+ </ion-col>
74
+ </ion-row>
75
+
53
76
  <ion-row id="message-text-area" [style.display]="section==='chat' || section ==='templates' || section ==='copilot'? 'flex': 'none'">
54
77
 
55
78
  <ion-col col-auto style="display: flex;">
@@ -57,7 +80,7 @@
57
80
  <div class="buttons-left">
58
81
 
59
82
  <!-- CANNED RESPONSES -->
60
- <ng-container *ngIf="areVisibleCAR && supportMode">
83
+ <ng-container *ngIf="areVisibleCAR && supportMode && cannedSection">
61
84
  <div class="canned-responses-btn-wpr" tooltip="{{translationMap?.get('CANNED_RESPONSES')}}" placement="top">
62
85
  <ion-button ion-button fill="clear" class="canned-responses-btn" (click)="openCannedResponses()"
63
86
  [disabled]="!conversationWith?.startsWith(CHANNEL_TYPE.SUPPORT_GROUP) || disableTextarea">
@@ -183,6 +183,36 @@
183
183
  }
184
184
  }
185
185
 
186
+ #ticket{
187
+ text-align: center;
188
+ font-size: 12px;
189
+
190
+ .buttons-container{
191
+ display: flex;
192
+ justify-content: center;
193
+ gap: 10px;
194
+
195
+ ion-button{
196
+ font-size: 12px;
197
+ --padding-top: 4px;
198
+ --padding-bottom: 4px;
199
+ --padding-start: 6px;
200
+ --padding-end: 6px;
201
+ --ripple-color: transparent;
202
+ text-transform: unset;
203
+ height: auto;
204
+
205
+ ion-icon{
206
+ margin-right: 4px;
207
+ }
208
+
209
+ &[name="add"]{
210
+ --background: var(--basic-blue);
211
+ }
212
+ }
213
+ }
214
+ }
215
+
186
216
  #fileInput {
187
217
  position: absolute;
188
218
  opacity: 0;
@@ -12,7 +12,7 @@ import { LoaderPreviewPage } from 'src/app/modals/loader-preview/loader-preview.
12
12
  // Services
13
13
  import { UploadService } from 'src/chat21-core/providers/abstract/upload.service';
14
14
  // utils
15
- import { TYPE_MSG_EMAIL, TYPE_MSG_TEXT, CHANNEL_TYPE } from 'src/chat21-core/utils/constants';
15
+ import { TYPE_MSG_EMAIL, TYPE_MSG_TEXT, CHANNEL_TYPE, TYPE_DIRECT } from 'src/chat21-core/utils/constants';
16
16
  // Models
17
17
  import { UploadModel } from 'src/chat21-core/models/upload';
18
18
 
@@ -26,6 +26,7 @@ import { CopilotService } from 'src/app/services/copilot/copilot.service';
26
26
  import { BRAND_BASE_INFO } from 'src/app/utils/utils-resources';
27
27
  import { ProjectService } from 'src/app/services/projects/project.service';
28
28
  import { Project } from 'src/chat21-core/models/projects';
29
+ import { ProjectUser } from 'src/chat21-core/models/projectUsers';
29
30
 
30
31
 
31
32
  @Component({
@@ -49,6 +50,7 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
49
50
  @ViewChild('fileInput', { static: false }) fileInput: any;
50
51
 
51
52
  @Input() loggedUser: UserModel;
53
+ @Input() projectUser: ProjectUser;
52
54
  @Input() conversationWith: string;
53
55
  @Input() channelType: string;
54
56
  @Input() channel: string;
@@ -61,16 +63,20 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
61
63
  @Input() emailSection: boolean;
62
64
  @Input() offlineMsgEmail: boolean;
63
65
  @Input() whatsappTemplatesSection: boolean;
66
+ @Input() ticketSection: boolean
64
67
  @Input() isOpenInfoConversation: boolean;
68
+ @Input() cannedSection: boolean;
65
69
  @Input() stylesMap: Map<string, string>;
66
70
  @Input() translationMap: Map<string, string>;
67
71
  @Input() dropEvent: any;
68
72
  @Input() disableTextarea: boolean;
73
+ @Input() roles: Array<string>;
69
74
  @Output() eventChangeTextArea = new EventEmitter<{msg: string, offsetHeight: number}>();
70
75
  @Output() eventSendMessage = new EventEmitter<{msg: string, type: string, metadata?: Object, attributes?: Object}>();
71
76
  @Output() onClickOpenCannedResponses = new EventEmitter<boolean>();
72
77
  @Output() onPresentModalScrollToBottom = new EventEmitter<boolean>();
73
78
  @Output() onOpenFooterSection = new EventEmitter<string>();
79
+ @Output() onOpenTicket = new EventEmitter<boolean>();
74
80
 
75
81
  public project: Project;
76
82
  public conversationEnabled = false;
@@ -162,6 +168,11 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
162
168
 
163
169
  this.project = this.projectService.getProject();
164
170
  this.logger.log('[CONVS-DETAIL] - returnChangeTextArea ngOnChanges in [MSG-TEXT-AREA] project', this.project)
171
+ if (this.channelType === TYPE_DIRECT) {
172
+ this.uploadService.initialize();
173
+ } else {
174
+ this.uploadService.initialize(this.project?._id);
175
+ }
165
176
  // use case drop
166
177
  if (this.dropEvent) {
167
178
  this.presentModal(this.dropEvent)
@@ -288,6 +299,17 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
288
299
  this.prensentTemplateModal();
289
300
  }
290
301
 
302
+ onClickTicket(option: "open" | "close"){
303
+ this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] - onClickTicket', option);
304
+ switch(option){
305
+ case "open":
306
+ this.onOpenTicket.emit();
307
+ case "close":
308
+ this.section = 'chat'
309
+ }
310
+
311
+ }
312
+
291
313
 
292
314
  /**
293
315
  *
@@ -384,7 +406,10 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
384
406
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] FIREBASE-UPLOAD presentModal onDidDismiss currentUpload', currentUpload);
385
407
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] FIREBASE-UPLOAD presentModal onDidDismiss detail.data', detail.data);
386
408
 
387
- that.uploadService.upload(that.loggedUser.uid, currentUpload).then((data) => {
409
+ const uploadPromise = (that.channelType === TYPE_DIRECT)
410
+ ? that.uploadService.upload(that.loggedUser.uid, currentUpload)
411
+ : that.uploadService.uploadFile(that.loggedUser.uid, currentUpload)
412
+ uploadPromise.then((data) => {
388
413
  metadata.src = data.src;
389
414
  metadata.downloadURL = data.downloadURL;
390
415
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] FIREBASE-UPLOAD presentModal invio msg metadata::: ', metadata);
@@ -567,8 +592,10 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
567
592
  if (!text.includes("/")) {
568
593
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 1 message: ', message);
569
594
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 1 message: ", message);
570
- this.messageString = '';
595
+
571
596
  this.sendMessage(text);
597
+ // this.messageString = '';
598
+
572
599
  this.countClicks = 0
573
600
  } else if (text.includes("/") && pos === 0 && this.countClicks > 1 && this.tagsCannedFilter.length > 0) {
574
601
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 2: ', this.tagsCannedFilter.length);
@@ -580,9 +607,10 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
580
607
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
581
608
  this.logger.log("[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
582
609
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 2 message: ', message);
583
- this.messageString = '';
584
-
610
+
585
611
  this.sendMessage(text);
612
+ // this.messageString = '';
613
+
586
614
  this.countClicks = 0
587
615
  } else if (text.includes("/") && pos > 0 && this.countClicks > 1 && this.tagsCannedFilter.length > 0 && text.substr(-1) !== '/') {
588
616
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 3: ', this.tagsCannedFilter.length);
@@ -594,17 +622,19 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
594
622
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
595
623
  this.logger.log("[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
596
624
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 2 message: ', message);
597
- this.messageString = '';
598
-
625
+
599
626
  this.sendMessage(text);
627
+ // this.messageString = '';
628
+
600
629
  this.countClicks = 0
601
630
  } else if (text.includes("/") && this.tagsCannedFilter.length === 0) {
602
631
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 3: ', this.tagsCannedFilter.length);
603
632
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 3 message: ', message);
604
633
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 3 message: ", message);
605
- this.messageString = '';
606
-
634
+
607
635
  this.sendMessage(text);
636
+ // this.messageString = '';
637
+
608
638
  this.countClicks = 0
609
639
 
610
640
  }
@@ -70,8 +70,8 @@ export class InfoContentComponent implements OnInit {
70
70
  this.route.paramMap.subscribe(params => {
71
71
  this.logger.log('[INFO-CONTENT-COMP] initialize params: ', params);
72
72
  this.conversationWith = params.get('IDConv');
73
- this.conversationWithFullname = params.get('FullNameConv');
74
- this.conv_type = params.get('Convtype');
73
+ this.conversationWithFullname = decodeURIComponent(params.get('FullNameConv'));
74
+ this.conv_type = decodeURIComponent(params.get('Convtype'));
75
75
 
76
76
  const conversationWith_segments = this.conversationWith.split('-');
77
77
 
@@ -151,27 +151,29 @@ export class InfoGroupComponent implements OnInit, AfterViewInit, OnChanges {
151
151
  });
152
152
 
153
153
  this.contactsService.loadContactDetail(key).pipe(takeUntil(this.unsubscribe$)).subscribe(user => {
154
- this.logger.log('InfoGroupComponent group detail loadContactDetail RES', user);
155
- // this.logger.log('InfoGroupComponent group detail this.presenceService.BSIsOnline.value()', this.presenceService.BSIsOnline.getValue);
156
-
157
- user.imageurl = this.imageRepoService.getImagePhotoUrl(key)
158
- // this.member_array.push({ userid: user.uid, avatar: user.avatar, color: user.color, email: user.email, fullname: user.fullname, imageurl: user.imageurl, userOnline: isOnline })
159
- var index = this.member_array.findIndex(m => m.userid === user.uid);
160
- this.logger.log('InfoGroupComponent member_array first of push index', index);
161
- this.logger.log('InfoGroupComponent member_array first of push', this.member_array);
162
- if (index === -1) {
163
- this.member_array.push(
164
- {
165
- userid: user.uid,
166
- avatar: user.avatar,
167
- color: user.color,
168
- email: user.email,
169
- fullname: user.fullname,
170
- imageurl: user.imageurl,
171
- userOnline: members_isonline_array[user.uid]['isSignin']
172
- })
173
- } else {
174
- this.logger.log('InfoGroupComponent member already exist in member_array');
154
+ if(user){
155
+ this.logger.log('InfoGroupComponent group detail loadContactDetail RES', user);
156
+ // this.logger.log('InfoGroupComponent group detail this.presenceService.BSIsOnline.value()', this.presenceService.BSIsOnline.getValue);
157
+
158
+ user.imageurl = this.imageRepoService.getImagePhotoUrl(key)
159
+ // this.member_array.push({ userid: user.uid, avatar: user.avatar, color: user.color, email: user.email, fullname: user.fullname, imageurl: user.imageurl, userOnline: isOnline })
160
+ var index = this.member_array.findIndex(m => m.userid === user.uid);
161
+ this.logger.log('InfoGroupComponent member_array first of push index', index);
162
+ this.logger.log('InfoGroupComponent member_array first of push', this.member_array);
163
+ if (index === -1) {
164
+ this.member_array.push(
165
+ {
166
+ userid: user.uid,
167
+ avatar: user.avatar,
168
+ color: user.color,
169
+ email: user.email,
170
+ fullname: user.fullname,
171
+ imageurl: user.imageurl,
172
+ userOnline: members_isonline_array[user.uid]['isSignin']
173
+ })
174
+ } else {
175
+ this.logger.log('InfoGroupComponent member already exist in member_array');
176
+ }
175
177
  }
176
178
  }, (error) => {
177
179
  this.logger.error('InfoGroupComponent group detail loadContactDetail - ERROR ', error);
@@ -34,7 +34,7 @@
34
34
  <!-- <ion-icon name="file-tray-full-outline"></ion-icon> -->
35
35
  </ion-button>
36
36
 
37
- <ion-button *ngIf="writeto_btn" ion-button fill="clear" (click)="onOpenContactsDirectory($event)"
37
+ <ion-button *ngIf="writeto_btn && isVisibleCNT && roles?.[PERMISSIONS.LEADS_READ]" ion-button fill="clear" (click)="onOpenContactsDirectory($event)"
38
38
  tooltip="{{translationMap?.get('ViewContactsList')}}" placement="bottom">
39
39
  <ion-icon slot="icon-only" name="create-outline"></ion-icon>
40
40
  <!-- <ion-icon slot="icon-only" name="people-outline"></ion-icon> -->
@@ -1,3 +1,4 @@
1
+ import { PERMISSIONS } from 'src/app/utils/permissions.constants';
1
2
  import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'
2
3
  import { ModalController } from '@ionic/angular'
3
4
  import { EventsService } from 'src/app/services/events-service'
@@ -17,6 +18,8 @@ export class HeaderConversationsList implements OnInit {
17
18
  @Input() sound_btn: string;
18
19
  @Input() isMobile: boolean;
19
20
  @Input() isVisibleTKT: boolean = true;
21
+ @Input() isVisibleCNT: boolean = true;;
22
+ @Input() roles: Array<string>;
20
23
  @Output() onSoundChange = new EventEmitter<string>()
21
24
  @Output() openContactsDirectory = new EventEmitter()
22
25
  @Output() openProfileInfo = new EventEmitter()
@@ -24,6 +27,7 @@ export class HeaderConversationsList implements OnInit {
24
27
  createTicketModal = null
25
28
  public translationMap: Map<string, string>;
26
29
 
30
+ PERMISSIONS = PERMISSIONS;
27
31
  constructor(
28
32
  public events: EventsService,
29
33
  public modalController: ModalController,
@@ -62,7 +66,7 @@ export class HeaderConversationsList implements OnInit {
62
66
  // }
63
67
 
64
68
  ngOnInit() {
65
- // console.log('DDP HEADER SUPPORT MODE ', this.supportMode)
69
+ console.log('DDP HEADER SUPPORT MODE ', this.roles)
66
70
  }
67
71
 
68
72
  // START @Output() //
@@ -5,7 +5,7 @@
5
5
  </div>
6
6
  <div class="navbar-right">
7
7
  <!-- test site -->
8
- <ng-container *ngIf="project">
8
+ <ng-container *ngIf="project && roles?.[PERMISSIONS.SIMULATE_CONV]">
9
9
  <button class="btn simulate-visitor-btn" (click)="testWidgetPage()">
10
10
  <i class="material-icons">play_arrow</i>
11
11
  <!-- {{translationsMap?.get('NAVBAR.SIMULATE_VISITOR')}} -->
@@ -23,9 +23,9 @@
23
23
  </ng-container>
24
24
 
25
25
  <!-- ------ PROJECTS DROPDOWN ------ -->
26
- <ng-container *ngIf="project">
26
+ <ng-container *ngIf="project && roles?.[PERMISSIONS.CHANGE_PROJECT]">
27
27
  <li>
28
- <button class="btn dropdown-toggle project-dropdown" (click)="openDropdownProjects = !openDropdownProjects">
28
+ <button class="btn dropdown-toggle project-dropdown" (click)="toggleProjectsDropdown()">
29
29
  <span class="project-dropdown" style="text-transform: none"> {{ project?.id_project?.name }} </span>
30
30
  <i class="material-icons" style="margin-right: 3px;">arrow_drop_down</i>
31
31
  </button>
@@ -49,7 +49,7 @@
49
49
  </li>
50
50
 
51
51
  <!-- ADD PROJECT -->
52
- <li id="navbar_create_prjct" *ngIf="MT === true" (click)="onClickDropdownOption('addProject')" class="add-project">
52
+ <li id="navbar_create_prjct" *ngIf="isVisibleMT" (click)="onClickDropdownOption('addProject')" class="add-project">
53
53
  <a>
54
54
  <i class="material-icons">add_circle_outline </i>
55
55
  {{translationsMap?.get('NAVBAR.ADD_PROJECT')}}
@@ -71,9 +71,22 @@
71
71
  <!-- *ngFor="let prjct of projects?.slice().reverse() | slice:0:5; let i=index" -->
72
72
  <li *ngFor="let prjct of projects?.slice() | slice:0:5; let i=index" style="cursor: pointer">
73
73
  <a (click)="goToHome(prjct?.id_project?._id, prjct?.id_project?.name)"
74
- [ngClass]="{'li-selected' : prjct?.id_project?._id === project?.id_project?.id }">
75
-
76
- <span> {{ prjct?.id_project?.name }} </span>
74
+ [ngClass]="{'li-selected' : prjct?.id_project?._id === project?.id_project?.id }"
75
+ class="project-item-row">
76
+ <span class="project-item-name">{{ prjct?.id_project?.name }}</span>
77
+ <span class="project-item-status project-item-status-wrapper"
78
+ [attr.title]="translationsMap?.get(prjct?.teammateStatus?.label) || prjct?.teammateStatus?.name"
79
+ (click)="toggleStatusDropdown($event, prjct)">
80
+ <img style="width: 15px; height: 15px; position: relative; top: 1px; cursor: pointer;" height="15" width="15" [src]="prjct?.teammateStatus?.avatar" />
81
+ <div class="status-dropdown status-dropdown-fixed" *ngIf="openStatusDropdownProjectId === prjct?.id_project?._id" (click)="$event.stopPropagation()"
82
+ [style.top.px]="statusDropdownPosition.top" [style.right.px]="statusDropdownPosition.right">
83
+ <div class="status-dropdown-option" *ngFor="let status of TEAMMATE_STATUS"
84
+ (click)="$event.stopPropagation(); onChangeProjectStatus(prjct, status.id)">
85
+ <img style="width: 15px; height: 15px; margin-right: 6px;" [src]="status.avatar" />
86
+ <span>{{ translationsMap?.get(status.label) || status.name }}</span>
87
+ </div>
88
+ </div>
89
+ </span>
77
90
  </a>
78
91
  </li>
79
92
 
@@ -85,8 +98,21 @@
85
98
 
86
99
  <!-- *ngFor="let prjct of projects?.slice().reverse() | slice:5:10; let i=index" -->
87
100
  <li *ngFor="let prjct of projects?.slice() | slice:5:10; let i=index" style="cursor: pointer">
88
- <a (click)="goToHome(prjct?.id_project?._id, prjct?.id_project?.name)">
89
- {{ prjct?.id_project?.name }}
101
+ <a (click)="goToHome(prjct?.id_project?._id, prjct?.id_project?.name)" class="project-item-row">
102
+ <span class="project-item-name">{{ prjct?.id_project?.name }}</span>
103
+ <span class="project-item-status project-item-status-wrapper"
104
+ [attr.title]="translationsMap?.get(prjct?.teammateStatus?.label) || prjct?.teammateStatus?.name"
105
+ (click)="toggleStatusDropdown($event, prjct)">
106
+ <img style="width: 15px; height: 15px; position: relative; top: 1px; cursor: pointer;" height="15" width="15" [src]="prjct?.teammateStatus?.avatar" />
107
+ <div class="status-dropdown status-dropdown-fixed" *ngIf="openStatusDropdownProjectId === prjct?.id_project?._id" (click)="$event.stopPropagation()"
108
+ [style.top.px]="statusDropdownPosition.top" [style.right.px]="statusDropdownPosition.right">
109
+ <div class="status-dropdown-option" *ngFor="let status of TEAMMATE_STATUS"
110
+ (click)="$event.stopPropagation(); onChangeProjectStatus(prjct, status.id)">
111
+ <img style="width: 15px; height: 15px; margin-right: 6px;" [src]="status.avatar" />
112
+ <span>{{ translationsMap?.get(status.label) || status.name }}</span>
113
+ </div>
114
+ </div>
115
+ </span>
90
116
  </a>
91
117
  </li>
92
118
  </ng-container>
@@ -204,6 +204,70 @@ li{
204
204
  color: var(--dropdown-menu-hover-color);
205
205
  box-shadow: 0 12px 20px -10px rgba(var(--dropdown-menu-hover-color), 0.28), 0 4px 20px 0 rgba(0, 0, 0, 0.12), 0 7px 8px -5px rgba(var(--dropdown-menu-hover-color), 0.2);
206
206
  }
207
+
208
+ &.project-item-row {
209
+ display: flex;
210
+ align-items: center;
211
+ justify-content: space-between;
212
+ gap: 8px;
213
+
214
+ .project-item-name {
215
+ flex: 1;
216
+ min-width: 0;
217
+ text-align: left;
218
+ overflow: hidden;
219
+ text-overflow: ellipsis;
220
+ }
221
+
222
+ .project-item-status {
223
+ flex: 0 0 10%;
224
+ display: flex;
225
+ justify-content: flex-end;
226
+ align-items: center;
227
+
228
+ &.project-item-status-wrapper {
229
+ position: relative;
230
+ }
231
+
232
+ .status-dropdown {
233
+ position: absolute;
234
+ right: 100%;
235
+ top: 50%;
236
+ transform: translateY(-50%);
237
+ margin-right: 4px;
238
+ min-width: 140px;
239
+ padding: 4px 0;
240
+ background-color: var(--dropdown-menu-background);
241
+ border-radius: 3px;
242
+ box-shadow: 0 2px 5px 0 rgb(0, 0, 0, 0.26);
243
+ z-index: 1001;
244
+ list-style: none;
245
+
246
+ &.status-dropdown-fixed {
247
+ position: fixed;
248
+ right: auto;
249
+ left: auto;
250
+ margin-right: 0;
251
+ transform: translateY(-50%);
252
+ }
253
+
254
+ .status-dropdown-option {
255
+ display: flex;
256
+ align-items: center;
257
+ padding: 8px 16px;
258
+ font-size: 13px;
259
+ color: var(--dropdown-menu-color);
260
+ cursor: pointer;
261
+ white-space: nowrap;
262
+
263
+ &:hover {
264
+ background-color: var(--dropdown-menu-hover-background);
265
+ color: var(--dropdown-menu-hover-color);
266
+ }
267
+ }
268
+ }
269
+ }
270
+ }
207
271
  }
208
272
 
209
273