@chat21/chat21-ionic 3.4.31 → 3.4.32-rc10

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 (88) hide show
  1. package/CHANGELOG.md +167 -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 -11
  6. package/src/app/chatlib/list-conversations-component/ion-list-conversations/ion-list-conversations.component.html +14 -2
  7. package/src/app/chatlib/list-conversations-component/ion-list-conversations/ion-list-conversations.component.scss +39 -2
  8. package/src/app/chatlib/list-conversations-component/list-conversations.module.ts +14 -0
  9. package/src/app/components/canned-response/canned-response.component.html +26 -23
  10. package/src/app/components/canned-response/canned-response.component.scss +0 -2
  11. package/src/app/components/canned-response/canned-response.component.ts +3 -1
  12. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.html +24 -1
  13. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.scss +30 -0
  14. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.ts +29 -7
  15. package/src/app/components/conversation-info/info-content/info-content.component.ts +2 -2
  16. package/src/app/components/conversation-info/info-group/info-group.component.ts +23 -21
  17. package/src/app/components/conversations-list/header-conversations-list/header-conversations-list.component.html +1 -1
  18. package/src/app/components/conversations-list/header-conversations-list/header-conversations-list.component.ts +5 -1
  19. package/src/app/components/navbar/navbar.component.html +35 -9
  20. package/src/app/components/navbar/navbar.component.scss +71 -1
  21. package/src/app/components/navbar/navbar.component.ts +100 -42
  22. package/src/app/components/project-item/project-item.component.ts +79 -52
  23. package/src/app/components/sidebar/sidebar.component.html +65 -45
  24. package/src/app/components/sidebar/sidebar.component.ts +110 -117
  25. package/src/app/components/sidebar-user-details/sidebar-user-details.component.html +52 -11
  26. package/src/app/components/sidebar-user-details/sidebar-user-details.component.scss +304 -17
  27. package/src/app/components/sidebar-user-details/sidebar-user-details.component.ts +217 -27
  28. package/src/app/modals/create-ticket/create-ticket.page.ts +4 -2
  29. package/src/app/pages/conversation-detail/conversation-detail.page.html +7 -3
  30. package/src/app/pages/conversation-detail/conversation-detail.page.ts +89 -5
  31. package/src/app/pages/conversations-list/conversations-list.module.ts +3 -5
  32. package/src/app/pages/conversations-list/conversations-list.page.html +2 -0
  33. package/src/app/pages/conversations-list/conversations-list.page.ts +120 -26
  34. package/src/app/pages/unassigned-conversations/unassigned-conversations.module.ts +16 -4
  35. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.html +43 -17
  36. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.scss +25 -1
  37. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.ts +279 -13
  38. package/src/app/pipe/filter.pipe.spec.ts +8 -0
  39. package/src/app/pipe/filter.pipe.ts +15 -0
  40. package/src/app/pipe/find.pipe.spec.ts +8 -0
  41. package/src/app/pipe/find.pipe.ts +15 -0
  42. package/src/app/services/global-settings/global-settings.service.ts +11 -3
  43. package/src/app/services/nav-proxy.service.ts +0 -1
  44. package/src/app/services/project_users/project-users.service.spec.ts +16 -0
  45. package/src/app/services/project_users/project-users.service.ts +63 -0
  46. package/src/app/services/projects/project.service.ts +2 -1
  47. package/src/app/services/tiledesk/tiledesk.service.ts +24 -0
  48. package/src/app/services/triggerEvents/triggerEvents.ts +40 -0
  49. package/src/app/services/websocket/websocket-js.ts +59 -534
  50. package/src/app/services/websocket/websocket-js_old.ts +578 -0
  51. package/src/app/services/websocket/websocket.service.ts +67 -14
  52. package/src/app/services/websocket/websocket.worker.ts +242 -0
  53. package/src/app/shared/shared.module.ts +26 -10
  54. package/src/app/utils/globals.ts +2 -0
  55. package/src/app/utils/permissions.constants.ts +138 -0
  56. package/src/app/utils/project-utils.ts +2 -2
  57. package/src/app/utils/utils.ts +18 -1
  58. package/src/assets/i18n/ar.json +11 -1
  59. package/src/assets/i18n/az.json +11 -1
  60. package/src/assets/i18n/de.json +11 -1
  61. package/src/assets/i18n/en.json +11 -1
  62. package/src/assets/i18n/es.json +11 -1
  63. package/src/assets/i18n/fr.json +11 -1
  64. package/src/assets/i18n/it.json +13 -3
  65. package/src/assets/i18n/kk.json +11 -1
  66. package/src/assets/i18n/pt.json +11 -1
  67. package/src/assets/i18n/ru.json +11 -1
  68. package/src/assets/i18n/sr.json +11 -1
  69. package/src/assets/i18n/sv.json +11 -1
  70. package/src/assets/i18n/tr.json +11 -1
  71. package/src/assets/i18n/uk.json +11 -1
  72. package/src/assets/i18n/uz.json +12 -1
  73. package/src/assets/js/agentDesktop-sdk.js +55 -0
  74. package/src/assets/js/chat21client.js +36 -0
  75. package/src/assets/js/mqtt-keepalive-worker.js +53 -0
  76. package/src/assets/test.html +5 -2
  77. package/src/chat-config-template.json +1 -0
  78. package/src/chat-config.json +1 -0
  79. package/src/chat21-core/models/projectUsers.ts +19 -0
  80. package/src/chat21-core/models/project_user.ts +2 -1
  81. package/src/chat21-core/models/projects.ts +1 -0
  82. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +1 -1
  83. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +1 -1
  84. package/src/chat21-core/providers/tiledesk/tiledesk-auth.service.ts +3 -0
  85. package/src/chat21-core/utils/constants.ts +6 -0
  86. package/src/chat21-core/utils/convertRequestToConversation.ts +2 -2
  87. package/src/chat21-core/utils/utils.ts +53 -3
  88. package/src/variables.scss +3 -0
@@ -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;
@@ -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;
@@ -293,6 +299,17 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
293
299
  this.prensentTemplateModal();
294
300
  }
295
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
+
296
313
 
297
314
  /**
298
315
  *
@@ -575,8 +592,10 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
575
592
  if (!text.includes("/")) {
576
593
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 1 message: ', message);
577
594
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 1 message: ", message);
578
- this.messageString = '';
595
+
579
596
  this.sendMessage(text);
597
+ // this.messageString = '';
598
+
580
599
  this.countClicks = 0
581
600
  } else if (text.includes("/") && pos === 0 && this.countClicks > 1 && this.tagsCannedFilter.length > 0) {
582
601
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 2: ', this.tagsCannedFilter.length);
@@ -588,9 +607,10 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
588
607
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
589
608
  this.logger.log("[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
590
609
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 2 message: ', message);
591
- this.messageString = '';
592
-
610
+
593
611
  this.sendMessage(text);
612
+ // this.messageString = '';
613
+
594
614
  this.countClicks = 0
595
615
  } else if (text.includes("/") && pos > 0 && this.countClicks > 1 && this.tagsCannedFilter.length > 0 && text.substr(-1) !== '/') {
596
616
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 3: ', this.tagsCannedFilter.length);
@@ -602,17 +622,19 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
602
622
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
603
623
  this.logger.log("[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
604
624
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 2 message: ', message);
605
- this.messageString = '';
606
-
625
+
607
626
  this.sendMessage(text);
627
+ // this.messageString = '';
628
+
608
629
  this.countClicks = 0
609
630
  } else if (text.includes("/") && this.tagsCannedFilter.length === 0) {
610
631
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 3: ', this.tagsCannedFilter.length);
611
632
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 3 message: ', message);
612
633
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 3 message: ", message);
613
- this.messageString = '';
614
-
634
+
615
635
  this.sendMessage(text);
636
+ // this.messageString = '';
637
+
616
638
  this.countClicks = 0
617
639
 
618
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>
@@ -94,12 +94,18 @@ ion-navbar{
94
94
  overflow: hidden;
95
95
  text-overflow: ellipsis;
96
96
  font-family: var(--header-font-family);
97
-
97
+ line-height: 1.4;
98
+
98
99
  .material-icons{
99
100
  font-size: 20px;
100
101
  }
101
102
  }
102
103
 
104
+ button.btn.project-dropdown {
105
+ padding-top: 4px;
106
+ padding-bottom: 4px;
107
+ }
108
+
103
109
  li{
104
110
  position: relative;
105
111
  display: block
@@ -204,6 +210,70 @@ li{
204
210
  color: var(--dropdown-menu-hover-color);
205
211
  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
212
  }
213
+
214
+ &.project-item-row {
215
+ display: flex;
216
+ align-items: center;
217
+ justify-content: space-between;
218
+ gap: 8px;
219
+
220
+ .project-item-name {
221
+ flex: 1;
222
+ min-width: 0;
223
+ text-align: left;
224
+ overflow: hidden;
225
+ text-overflow: ellipsis;
226
+ }
227
+
228
+ .project-item-status {
229
+ flex: 0 0 10%;
230
+ display: flex;
231
+ justify-content: flex-end;
232
+ align-items: center;
233
+
234
+ &.project-item-status-wrapper {
235
+ position: relative;
236
+ }
237
+
238
+ .status-dropdown {
239
+ position: absolute;
240
+ right: 100%;
241
+ top: 50%;
242
+ transform: translateY(-50%);
243
+ margin-right: 4px;
244
+ min-width: 140px;
245
+ padding: 4px 0;
246
+ background-color: var(--dropdown-menu-background);
247
+ border-radius: 3px;
248
+ box-shadow: 0 2px 5px 0 rgb(0, 0, 0, 0.26);
249
+ z-index: 1001;
250
+ list-style: none;
251
+
252
+ &.status-dropdown-fixed {
253
+ position: fixed;
254
+ right: auto;
255
+ left: auto;
256
+ margin-right: 0;
257
+ transform: translateY(-50%);
258
+ }
259
+
260
+ .status-dropdown-option {
261
+ display: flex;
262
+ align-items: center;
263
+ padding: 8px 16px;
264
+ font-size: 13px;
265
+ color: var(--dropdown-menu-color);
266
+ cursor: pointer;
267
+ white-space: nowrap;
268
+
269
+ &:hover {
270
+ background-color: var(--dropdown-menu-hover-background);
271
+ color: var(--dropdown-menu-hover-color);
272
+ }
273
+ }
274
+ }
275
+ }
276
+ }
207
277
  }
208
278
 
209
279