@chat21/chat21-ionic 3.4.32-rc6 → 3.4.32-rc8

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/CHANGELOG.md CHANGED
@@ -8,6 +8,19 @@
8
8
  ### **Copyrigth**:
9
9
  *Tiledesk SRL*
10
10
 
11
+ # 3.4.32-rc8
12
+ - **bug-fixed**: sidebar-user-details — status dropdown not visible when clicking first/last project; moved outside #user-details container to avoid overflow clipping.
13
+ - **bug-fixed**: sidebar-user-details — replaced `transform` on #user-details with `left` animation to fix `position: fixed` containing block (dropdown positioning).
14
+ - **changed**: sidebar-user-details — projects_dropdown_container and status-dropdown now use same colors as ng-select teammate-status-in-drawer.
15
+ - **changed**: sidebar-user-details — projects-dropdown-wrapper styled to match ng-select container.
16
+ - **added**: sidebar-user-details — MPA feature flag in featuresToken: if MPA is true show projects_dropdown_container, else show availability_dropdown_container.
17
+ - **bug-fixed**: RouterModule.forRoot() called twice when clicking conversation — SharedModule now imports RouterModule.forChild([]) instead of AppRoutingModule.
18
+ - **changed**: FindPipe and FilterPipe moved from AppModule to SharedModule for app-wide availability.
19
+ - **added**: conversations-list — postMessage to hosting app on conversation selection (event: `onConversationChanged`, data: full conversation object).
20
+
21
+ # 3.4.32-rc7
22
+ - **addded**: ability to change availability status for each project the logged-in user in sidebar-user-detail
23
+
11
24
  # 3.4.32-rc6
12
25
  - **bug-fixed**: convertRequestToConversation timestamp wrong unit
13
26
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chat21/chat21-ionic",
3
3
  "author": "Tiledesk SRL",
4
- "version": "3.4.32-rc6",
4
+ "version": "3.4.32-rc8",
5
5
  "license": "MIT License",
6
6
  "homepage": "https://tiledesk.com/",
7
7
  "repository": {
@@ -71,7 +71,8 @@
71
71
  </span>
72
72
  </mat-slide-toggle> -->
73
73
 
74
- <ng-select style="text-align: left;"
74
+ <ng-container *ngIf="!isVisibleMPA" id="availability_dropdown_container">
75
+ <ng-select style="text-align: left;"
75
76
  (change)="changeAvailabilityStateInUserDetailsSidebar(selectedStatus)"
76
77
  [(ngModel)]="selectedStatus"
77
78
  class="teammate-status-in-drawer sidebar"
@@ -79,15 +80,42 @@
79
80
  bindLabel="name" bindValue="id"
80
81
  [clearable]="false"
81
82
  [searchable]="false">
82
- <ng-template ng-label-tmp let-item="item">
83
- <img style="width: 15px;height: 15px;position: relative; top: 1px;" height="15" width="15" [src]="item?.avatar" />
84
- <span id="sidebaravatar_{{item.name}}" style="text-transform: capitalize; margin-left:8px"> {{item.label | translate}} </span>
85
- </ng-template>
86
- <ng-template ng-option-tmp let-item="item" let-index="index">
87
- <img style="width: 15px;height: 15px;position: relative; top: 1px;" height="15" width="15" [src]="item?.avatar" />
88
- <span id="sidebaravatar_{{item.name}}" style="text-transform: capitalize; margin-left:8px"> {{item.label | translate}} </span>
89
- </ng-template>
90
- </ng-select>
83
+ <ng-template ng-label-tmp let-item="item">
84
+ <img style="width: 15px;height: 15px;position: relative; top: 1px;" height="15" width="15" [src]="item?.avatar" />
85
+ <span id="sidebaravatar_{{item.name}}" style="text-transform: capitalize; margin-left:8px"> {{item.label | translate}} </span>
86
+ </ng-template>
87
+ <ng-template ng-option-tmp let-item="item" let-index="index">
88
+ <img style="width: 15px;height: 15px;position: relative; top: 1px;" height="15" width="15" [src]="item?.avatar" />
89
+ <span id="sidebaravatar_{{item.name}}" style="text-transform: capitalize; margin-left:8px"> {{item.label | translate}} </span>
90
+ </ng-template>
91
+ </ng-select>
92
+ </ng-container>
93
+
94
+ <ng-container *ngIf="isVisibleMPA && projects?.length > 0" id="projects_dropdown_container">
95
+ <div class="projects-dropdown-wrapper">
96
+ <button class="btn projects-dropdown-toggle" (click)="toggleProjectsDropdown()">
97
+ <span class="projects-dropdown-toggle-label">
98
+ {{ project?.name || 'Progetti' }}
99
+ <img style="width: 15px; height: 15px; position: relative; top: 1px; cursor: pointer;" height="15" width="15" [src]="project?.teammateStatus?.avatar" />
100
+ </span>
101
+ <i class="material-icons" style="margin-left: 4px; font-size: 18px;">arrow_drop_down</i>
102
+ </button>
103
+ <div class="ripple-container"></div>
104
+ <div id="projects_dropdown" class="dropdown-menu" [ngClass]="{'open': openDropdownProjects}">
105
+ <li *ngFor="let prjct of projects?.slice() | slice:0:5; let i=index" style="cursor: pointer">
106
+ <a [ngClass]="{'li-selected' : prjct?.id_project?._id === project?.id_project?.id }"
107
+ class="project-item-row">
108
+ <span class="project-item-name">{{ prjct?.id_project?.name }}</span>
109
+ <span class="project-item-status project-item-status-wrapper"
110
+ [attr.title]="translationsMap?.get(prjct?.teammateStatus?.label) || prjct?.teammateStatus?.name"
111
+ (click)="toggleStatusDropdown($event, prjct)">
112
+ <img style="width: 15px; height: 15px; position: relative; top: 1px; cursor: pointer;" height="15" width="15" [src]="prjct?.teammateStatus?.avatar" />
113
+ </span>
114
+ </a>
115
+ </li>
116
+ </div>
117
+ </div>
118
+ </ng-container>
91
119
  </section>
92
120
 
93
121
  <hr class="first-divider">
@@ -140,4 +168,14 @@
140
168
  <div class="chat-version"> ver {{version}}</div>
141
169
  </section>
142
170
 
171
+ </div>
172
+
173
+ <!-- Status dropdown fuori da #user-details per evitare clipping da overflow -->
174
+ <div class="status-dropdown status-dropdown-fixed status-dropdown-outside" *ngIf="openStatusDropdownProjectId && selectedProjectForStatus" (click)="$event.stopPropagation()"
175
+ [style.top.px]="statusDropdownPosition.top" [style.right.px]="statusDropdownPosition.right">
176
+ <div class="status-dropdown-option" *ngFor="let status of TEAMMATE_STATUS"
177
+ (click)="$event.stopPropagation(); onChangeProjectStatus(selectedProjectForStatus, status.id)">
178
+ <img style="width: 15px; height: 15px; margin-right: 6px;" [src]="status.avatar" />
179
+ <span>{{ translationsMap?.get(status.label) || status.name }}</span>
180
+ </div>
143
181
  </div>
@@ -2,23 +2,13 @@
2
2
  #user-details {
3
3
  background-color: var(--sidebar-background-color);
4
4
  color: var(--sidebar-color);
5
- // height: calc(100% - 60px);
6
5
  height: 100%;
7
6
  overflow-y: auto;
8
7
  padding: 16px 12px 32px;
9
8
  position: absolute;
10
- // left: -60px;
11
- // top: 60px;
12
- // top: 0px;
13
- // transform: translate(305px);
14
- // transform: translate(60px);
15
- transition: transform 0.5s;
9
+ transition: left 0.5s;
16
10
  width: 305px;
17
11
  left: -245px;
18
- // z-index: 1029;
19
- // transform: translateX(-100%);
20
- // -webkit-transform: translateX(-100%);
21
- // display: none;
22
12
  }
23
13
 
24
14
  // #user-details::-webkit-scrollbar {
@@ -33,11 +23,8 @@
33
23
  // }
34
24
 
35
25
  #user-details.active {
36
- // transform: translate(0);
37
- transform: translate(60px);
38
26
  display: block;
39
- // left: 0px;
40
- left: 10px;
27
+ left: 70px; /* 10px + 60px (equivalente a transform: translate(60px)) - evita containing block per position: fixed */
41
28
  }
42
29
 
43
30
  .user-details-btn-close {
@@ -175,15 +162,305 @@
175
162
  .availability-section {
176
163
  top: 320px;
177
164
  text-align: center;
178
- background-color: var(--user-detail-select-background);
165
+ background-color: var(--sidebar-user-detail-select-background);
179
166
  padding: 4px 0px;
180
- width: 192px;
181
167
  border-radius: 4px;
182
168
  -webkit-transform: translateX(-50%);
183
169
  transform: translateX(-50%);
184
170
  left: 50%;
185
171
  margin: auto;
186
172
  position: absolute;
173
+ z-index: 2;
174
+
175
+
176
+
177
+ .btn{
178
+ display: flex;
179
+ position: relative;
180
+ flex-flow: row nowrap;
181
+ align-items: center;
182
+ justify-content: center;
183
+
184
+ border: 1px solid transparent;
185
+ transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out
186
+ }
187
+
188
+ .dropdown-toggle{
189
+ background-color: transparent;
190
+ color: inherit;
191
+ padding: 0px 15px;
192
+ }
193
+
194
+ .project-dropdown,
195
+ .projects-dropdown-toggle {
196
+ // max-width: 180px;
197
+ white-space: nowrap;
198
+ overflow: hidden;
199
+ text-overflow: ellipsis;
200
+ font-family: var(--header-font-family);
201
+
202
+ .material-icons{
203
+ font-size: 20px;
204
+ }
205
+ }
206
+
207
+ button.btn.project-dropdown,
208
+ button.btn.projects-dropdown-toggle {
209
+ padding-top: 4px;
210
+ padding-bottom: 4px;
211
+ background-color: var(--sidebar-user-detail-select-background);
212
+ color: var(--sidebar-user-detail-select-color);
213
+ border: 1px solid var(--sidebar-user-detail-select-background);
214
+ }
215
+
216
+ .teammate-status-in-drawer {
217
+ width: 190px;
218
+ }
219
+
220
+ /* projects-dropdown-wrapper - stile come ng-select .ng-select-container (teammate-status-in-drawer) */
221
+ .projects-dropdown-wrapper {
222
+ position: relative;
223
+ min-height: 36px;
224
+ padding: 6px 12px;
225
+ background-color: var(--sidebar-user-detail-select-background);
226
+ color: var(--sidebar-user-detail-select-color);
227
+ border: 1px solid var(--sidebar-user-detail-select-background);
228
+ border-radius: 4px;
229
+ cursor: pointer;
230
+ display: flex;
231
+ align-items: center;
232
+ text-align: left;
233
+ font-size: 14px;
234
+ line-height: 1.4;
235
+ width: 250px;
236
+
237
+ .projects-dropdown-toggle {
238
+ background-color: transparent !important;
239
+ border: none !important;
240
+ padding: 0 !important;
241
+ min-height: auto;
242
+ flex: 1;
243
+ justify-content: flex-start;
244
+ color: inherit;
245
+ font-size: inherit;
246
+ }
247
+
248
+ .projects-dropdown-toggle-label {
249
+ text-transform: capitalize;
250
+ overflow: hidden;
251
+ text-overflow: ellipsis;
252
+ white-space: nowrap;
253
+ display: flex;
254
+ align-items: center;
255
+ gap: 8px;
256
+ }
257
+
258
+ .dropdown-menu {
259
+ left: 0;
260
+ right: 0;
261
+ min-width: 100%;
262
+ }
263
+ }
264
+
265
+ li{
266
+ position: relative;
267
+ display: block
268
+ }
269
+
270
+ /* projects_dropdown - stessi colori ng-select (.ng-dropdown-panel) */
271
+ .dropdown-menu {
272
+ border-radius: 4px;
273
+ border: 1px solid var(--sidebar-user-detail-select-background);
274
+ box-shadow: 0 2px 5px 0 rgb(0, 0, 0, 0.26);
275
+
276
+ position: absolute;
277
+ top: 100%;
278
+ right: 0;
279
+ left: auto;
280
+ z-index: 1000;
281
+ display: none;
282
+ float: left;
283
+ min-width: 160px;
284
+ padding: 5px 0;
285
+ margin: 8px 0 0;
286
+ font-size: 14px;
287
+ text-align: left;
288
+ list-style: none;
289
+ background-color: var(--sidebar-user-detail-select-background);
290
+ -webkit-background-clip: padding-box;
291
+ background-clip: padding-box;
292
+ color: var(--sidebar-user-detail-select-color);
293
+ max-height: 240px;
294
+ overflow-y: auto;
295
+ }
296
+
297
+ .dropdown-menu:not(.open){
298
+ margin-top: -20px;
299
+ opacity: 0;
300
+ visibility: hidden;
301
+ display: block;
302
+ }
303
+
304
+ .dropdown-menu.open{
305
+ margin-top: 12px; //0;
306
+ opacity: 1;
307
+ visibility: visible;
308
+ display: block;
309
+ transition: all 150ms linear;
310
+ }
311
+
312
+ .dropdown-menu li {
313
+ position: relative;
314
+
315
+ .li-selected{
316
+ color: var(--sidebar-user-detail-select-color);
317
+ background-color: var(--sidebar-user-detail-select-hover-background);
318
+ }
319
+
320
+ &.all-projects,
321
+ &.add-project{
322
+ color: var(--sidebar-user-detail-select-color);
323
+ i{
324
+ padding-right: 5px;
325
+ margin-bottom: 2px;
326
+ font-size: 20px
327
+ }
328
+ }
329
+
330
+ &.section-title{
331
+ padding-left: 24px;
332
+ font-size: 12px;
333
+ color: var(--sidebar-user-detail-color);
334
+ font-weight: 400
335
+ }
336
+ }
337
+
338
+ .dropdown-menu .divider{
339
+ background-color: rgba(255, 255, 255, 0.2);
340
+ margin: 5px 0;
341
+
342
+ height: 1px;
343
+ overflow: hidden;
344
+ }
345
+
346
+ .dropdown-menu li>a{
347
+ font-size: 13px;
348
+ padding: 10px 20px;
349
+ border-radius: 2px;
350
+ transition: all 150ms linear;
351
+
352
+ display: block;
353
+ clear: both;
354
+ font-weight: 400;
355
+ line-height: 1.42857143;
356
+ color: var(--sidebar-user-detail-select-color);
357
+ white-space: nowrap;
358
+ text-decoration: none;
359
+ cursor: pointer;
360
+
361
+ .material-icons {
362
+ vertical-align: middle;
363
+ }
364
+
365
+ &:hover{
366
+ background-color: var(--sidebar-user-detail-select-hover-background);
367
+ color: var(--sidebar-user-detail-select-color);
368
+ box-shadow: none;
369
+ }
370
+
371
+ &.project-item-row {
372
+ display: flex;
373
+ align-items: center;
374
+ justify-content: space-between;
375
+ gap: 8px;
376
+
377
+ .project-item-name {
378
+ flex: 1;
379
+ min-width: 0;
380
+ text-align: left;
381
+ overflow: hidden;
382
+ text-overflow: ellipsis;
383
+ }
384
+
385
+ .project-item-status {
386
+ flex: 0 0 10%;
387
+ display: flex;
388
+ justify-content: flex-end;
389
+ align-items: center;
390
+
391
+ &.project-item-status-wrapper {
392
+ position: relative;
393
+ }
394
+
395
+ .status-dropdown {
396
+ position: absolute;
397
+ right: 100%;
398
+ top: 50%;
399
+ transform: translateY(-50%);
400
+ margin-right: 4px;
401
+ min-width: 140px;
402
+ padding: 4px 0;
403
+ background-color: var(--sidebar-user-detail-select-background);
404
+ border: 1px solid var(--sidebar-user-detail-select-background);
405
+ border-radius: 4px;
406
+ box-shadow: 0 2px 5px 0 rgb(0, 0, 0, 0.26);
407
+ z-index: 1001;
408
+ list-style: none;
409
+
410
+ &.status-dropdown-fixed {
411
+ right: auto;
412
+ left: auto;
413
+ margin-right: 0;
414
+ transform: translateY(-50%);
415
+ }
416
+
417
+ .status-dropdown-option {
418
+ display: flex;
419
+ align-items: center;
420
+ padding: 8px 16px;
421
+ font-size: 13px;
422
+ color: var(--sidebar-user-detail-select-color);
423
+ cursor: pointer;
424
+ white-space: nowrap;
425
+
426
+ &:hover {
427
+ background-color: var(--sidebar-user-detail-select-hover-background);
428
+ color: var(--sidebar-user-detail-select-color);
429
+ }
430
+ }
431
+ }
432
+ }
433
+ }
434
+ }
435
+ }
436
+
437
+ /* Status dropdown fuori da #user-details - stessi colori ng-select */
438
+ .status-dropdown-outside {
439
+ position: fixed !important;
440
+ transform: translateY(-50%);
441
+ z-index: 1100;
442
+ min-width: 140px;
443
+ padding: 4px 0;
444
+ background-color: var(--sidebar-user-detail-select-background);
445
+ border: 1px solid var(--sidebar-user-detail-select-background);
446
+ border-radius: 4px;
447
+ box-shadow: 0 2px 5px 0 rgb(0, 0, 0, 0.26);
448
+ list-style: none;
449
+
450
+ .status-dropdown-option {
451
+ display: flex;
452
+ align-items: center;
453
+ padding: 8px 16px;
454
+ font-size: 13px;
455
+ color: var(--sidebar-user-detail-select-color);
456
+ cursor: pointer;
457
+ white-space: nowrap;
458
+
459
+ &:hover {
460
+ background-color: var(--sidebar-user-detail-select-hover-background);
461
+ color: var(--sidebar-user-detail-select-color);
462
+ }
463
+ }
187
464
  }
188
465
 
189
466
  .first-divider {
@@ -17,6 +17,8 @@ import { Project } from 'src/chat21-core/models/projects';
17
17
  import { BRAND_BASE_INFO } from 'src/app/utils/utils-resources';
18
18
  import { getOSCode } from 'src/app/utils/utils';
19
19
  import { getUserStatusFromProjectUser } from 'src/chat21-core/utils/utils';
20
+ import { ProjectService } from 'src/app/services/projects/project.service';
21
+ import { ProjectUser } from 'src/chat21-core/models/project_user';
20
22
  @Component({
21
23
  selector: 'app-sidebar-user-details',
22
24
  templateUrl: './sidebar-user-details.component.html',
@@ -55,6 +57,13 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
55
57
  selectedStatus: any;
56
58
  TEAMMATE_STATUS = TEAMMATE_STATUS;
57
59
 
60
+ projects: ProjectUser[] = [];
61
+ selectedProjectForStatus: ProjectUser | null = null;
62
+ public openDropdownProjects: boolean = false
63
+ public openStatusDropdownProjectId: string | null = null
64
+ statusDropdownPosition = { top: 0, right: 0 };
65
+ isVisibleMT = false;
66
+ isVisibleMPA = false;
58
67
 
59
68
  translationsMap: Map<string, string> = new Map();
60
69
 
@@ -71,7 +80,7 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
71
80
  public appConfigProvider: AppConfigProvider,
72
81
  public events: EventsService,
73
82
  private eRef: ElementRef,
74
-
83
+ private projectService: ProjectService,
75
84
  ) { }
76
85
 
77
86
  ngOnInit() {
@@ -80,6 +89,7 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
80
89
  this.subcribeToAuthStateChanged();
81
90
  this.listenTocurrentProjectUserUserAvailability$();
82
91
  this.listenToCurrentStoredProject();
92
+ this.listenToUserGoOnline();
83
93
  this.getOSCODE();
84
94
  }
85
95
 
@@ -240,6 +250,8 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
240
250
  .set('LABEL_LOGOUT', text['LABEL_LOGOUT'])
241
251
  .set('SubscriptionPaymentProblem', text['SubscriptionPaymentProblem'])
242
252
  .set('ThePlanHasExpired', text['ThePlanHasExpired'])
253
+ .set('NAVBAR.RECENT_PROJECTS', text['NAVBAR.RECENT_PROJECTS'])
254
+ .set('NAVBAR.OTHER_PROJECTS', text['NAVBAR.OTHER_PROJECTS'])
243
255
 
244
256
  this.TEAMMATE_STATUS.forEach(element => {
245
257
  element.label = this.translationsMap.get(element.label)
@@ -255,6 +267,36 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
255
267
  this.logger.log('[SIDEBAR-USER-DETAILS] AppConfigService getAppConfig', this.appConfigProvider.getConfig());
256
268
 
257
269
  this.isVisiblePAY = getOSCode("PAY", this.public_Key);
270
+ this.isVisibleMT = getOSCode("MTT", this.public_Key);
271
+ this.isVisibleMPA = getOSCode("MPA", this.public_Key);
272
+ }
273
+
274
+ listenToUserGoOnline() {
275
+ this.events.subscribe('go:online', (isOnline: boolean) => {
276
+ this.logger.log('[SIDEBAR-USER-DETAILS] listen to go:online --> ', isOnline);
277
+ if (isOnline) {
278
+ this.tiledeskToken = this.tiledeskAuthService.getTiledeskToken();
279
+ this.getProjects();
280
+ }
281
+ });
282
+ }
283
+
284
+ getProjects() {
285
+ this.logger.log('[SIDEBAR-USER-DETAILS] calling getProjects ... ');
286
+ this.projectService.getProjects().subscribe((projects: ProjectUser[]) => {
287
+ this.logger.log('[SIDEBAR-USER-DETAILS] getProjects PROJECTS ', projects);
288
+ if (projects) {
289
+ this.projects = projects.filter((prj: ProjectUser) => prj?.id_project?.status === 100);
290
+ this.projects.forEach((prj: ProjectUser) => {
291
+ prj.teammateStatus = getUserStatusFromProjectUser(prj as any);
292
+ });
293
+ this.logger.log('[SIDEBAR-USER-DETAILS] getProjects this.projects ', this.projects);
294
+ }
295
+ }, (error) => {
296
+ this.logger.error('[SIDEBAR-USER-DETAILS] getProjects - ERROR ', error);
297
+ }, () => {
298
+ this.logger.log('[SIDEBAR-USER-DETAILS] getProjects - COMPLETE');
299
+ });
258
300
  }
259
301
 
260
302
  listenToCurrentStoredProject() {
@@ -268,9 +310,9 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
268
310
  name: projectObjct['id_project']['name'],
269
311
  profile: projectObjct['id_project']['profile'],
270
312
  isActiveSubscription: projectObjct['id_project']['isActiveSubscription'],
271
- trialExpired: projectObjct['id_project']['trialExpired']
313
+ trialExpired: projectObjct['id_project']['trialExpired'],
314
+ teammateStatus: getUserStatusFromProjectUser(projectObjct as any)
272
315
  }
273
-
274
316
  if (this.project.profile.type === 'free') {
275
317
 
276
318
  if (this.project.trialExpired === false) {
@@ -285,11 +327,17 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
285
327
  }
286
328
 
287
329
  this.wsService.subscriptionToWsCurrentProjectUserAvailability(this.project._id, projectObjct._id);
330
+ if (this.tiledeskToken) {
331
+ this.getProjects();
332
+ }
288
333
  }
289
334
  })
290
335
 
291
336
  try {
292
337
  this.tiledeskToken = this.appStorageService.getItem('tiledeskToken');
338
+ if (this.tiledeskToken) {
339
+ this.getProjects();
340
+ }
293
341
  // this.logger.log('[SIDEBAR-USER-DETAILS] - GET STORED TOKEN ', this.tiledeskToken)
294
342
  } catch (err) {
295
343
  this.logger.error('[SIDEBAR-USER-DETAILS] - GET STORED TOKEN ', err)
@@ -350,6 +398,109 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
350
398
  });
351
399
  }
352
400
 
401
+ getCurrentStatusAvatar(): string {
402
+ const status = this.TEAMMATE_STATUS?.find(s => s.id === this.selectedStatus);
403
+ return status?.avatar || 'assets/img/teammate-status/avaible.svg';
404
+ }
405
+
406
+ getCurrentStatusLabel(): string {
407
+ const status = this.TEAMMATE_STATUS?.find(s => s.id === this.selectedStatus);
408
+ return status?.label || status?.name || '';
409
+ }
410
+
411
+ toggleProjectsDropdown() {
412
+ this.openDropdownProjects = !this.openDropdownProjects;
413
+ if (!this.openDropdownProjects) {
414
+ this.openStatusDropdownProjectId = null;
415
+ this.selectedProjectForStatus = null;
416
+ }
417
+ }
418
+
419
+ toggleStatusDropdown(event: Event, prjct: any) {
420
+ event.stopPropagation()
421
+ event.preventDefault()
422
+ const projectId = prjct?.id_project?._id
423
+ const isOpening = this.openStatusDropdownProjectId !== projectId
424
+ if (isOpening) {
425
+ const el = event.currentTarget as HTMLElement
426
+ const rect = el.getBoundingClientRect()
427
+ this.statusDropdownPosition = {
428
+ top: rect.top + rect.height / 2,
429
+ right: window.innerWidth - rect.left + 4
430
+ }
431
+ this.selectedProjectForStatus = prjct
432
+ } else {
433
+ this.selectedProjectForStatus = null
434
+ }
435
+ this.openStatusDropdownProjectId = this.openStatusDropdownProjectId === projectId ? null : projectId
436
+ }
437
+
438
+ onChangeProjectStatus(projectUser: ProjectUser, selectedStatusID: any) {
439
+ this.logger.log('[SIDEBAR-USER-DETAILS] onChangeProjectStatus', projectUser, selectedStatusID)
440
+ this.openStatusDropdownProjectId = null
441
+ this.selectedProjectForStatus = null
442
+
443
+ let IS_AVAILABLE = null
444
+ let profilestatus = ''
445
+ if (selectedStatusID === 1) {
446
+ IS_AVAILABLE = true
447
+ } else if (selectedStatusID === 2) {
448
+ IS_AVAILABLE = false
449
+ } else if (selectedStatusID === 3) {
450
+ IS_AVAILABLE = false
451
+ profilestatus = 'inactive'
452
+ }
453
+
454
+ this.wsService.updateCurrentUserAvailability(this.tiledeskToken, projectUser.id_project._id, IS_AVAILABLE, profilestatus).subscribe((projectUserUpdated: any) => {
455
+
456
+ this.logger.log('[NAVBAR] - PROJECT-USER UPDATED ', projectUser)
457
+ this.projects.find(p => p.id_project._id === projectUser.id_project._id).teammateStatus = getUserStatusFromProjectUser(projectUserUpdated as any);
458
+
459
+ if(projectUser.id_project._id === this.project._id) {
460
+ this.project.teammateStatus = getUserStatusFromProjectUser(projectUserUpdated as any);
461
+ }
462
+ }, (error) => {
463
+ this.logger.error('[NAVBAR] - PROJECT-USER UPDATED - ERROR ', error);
464
+
465
+ }, () => {
466
+ this.logger.log('[NAVBAR] - PROJECT-USER UPDATED * COMPLETE *');
467
+
468
+ });
469
+ }
470
+
471
+ onStatusDropdownOptionClick(status: { id: number; name: string; avatar: string; label: string }, projectUser: ProjectUser | null) {
472
+ if (!projectUser) return;
473
+ this.changeProjectStatus(projectUser, status.id);
474
+ this.openStatusDropdownProjectId = null;
475
+ this.selectedProjectForStatus = null;
476
+ if (projectUser?.id_project?._id === this.project?._id) {
477
+ this.selectedStatus = status.id;
478
+ }
479
+ }
480
+
481
+ changeProjectStatus(projectUser: ProjectUser, selectedStatusID: number) {
482
+ this.logger.log('[SIDEBAR-USER-DETAILS] changeProjectStatus projectid', projectUser?.id_project?._id, ' status: ', selectedStatusID);
483
+ let IS_AVAILABLE: boolean | null = null;
484
+ let profilestatus = '';
485
+ if (selectedStatusID === 1) {
486
+ IS_AVAILABLE = true;
487
+ } else if (selectedStatusID === 2) {
488
+ IS_AVAILABLE = false;
489
+ } else if (selectedStatusID === 3) {
490
+ IS_AVAILABLE = false;
491
+ profilestatus = 'inactive';
492
+ }
493
+ this.wsService.updateCurrentUserAvailability(this.tiledeskToken, projectUser.id_project._id, IS_AVAILABLE, profilestatus).subscribe((updated: any) => {
494
+ this.logger.log('[SIDEBAR-USER-DETAILS] - PROJECT-USER UPDATED ', updated);
495
+ const p = this.projects.find(prj => prj?.id_project?._id === projectUser?.id_project?._id);
496
+ if (p) {
497
+ p.teammateStatus = getUserStatusFromProjectUser(updated as any);
498
+ }
499
+ }, (error) => {
500
+ this.logger.error('[SIDEBAR-USER-DETAILS] - PROJECT-USER UPDATED - ERROR ', error);
501
+ });
502
+ }
503
+
353
504
  changeAvailabilityStateInUserDetailsSidebar(selectedStatusID) {
354
505
  this.logger.log('[SIDEBAR-USER-DETAILS] - changeAvailabilityState projectid', this.project._id, ' available 1: ', selectedStatusID);
355
506
 
@@ -147,6 +147,7 @@ export class ConversationListPage implements OnInit {
147
147
  public wsService: WebsocketService,
148
148
  public g: Globals,
149
149
  public appStorageService: AppStorageService,
150
+ private triggerEvents: TriggerEvents,
150
151
  ) {
151
152
  this.checkPlatform();
152
153
  this.translations();
@@ -837,6 +838,7 @@ export class ConversationListPage implements OnInit {
837
838
  this.logger.log('[CONVS-LIST-PAGE] onConversationSelected active conversation.uid ', conversation.uid)
838
839
  this.events.publish('convList:onConversationSelected', conversation)
839
840
  }
841
+ this.triggerEvents.triggerOnConversationChanged(conversation)
840
842
  }
841
843
 
842
844
  onImageLoaded(conversation: any) {
@@ -0,0 +1,8 @@
1
+ import { FilterPipe } from './filter.pipe';
2
+
3
+ describe('FilterPipe', () => {
4
+ it('create an instance', () => {
5
+ const pipe = new FilterPipe();
6
+ expect(pipe).toBeTruthy();
7
+ });
8
+ });
@@ -0,0 +1,15 @@
1
+ import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+ @Pipe({
4
+ name: 'filter'
5
+ })
6
+ export class FilterPipe implements PipeTransform {
7
+
8
+ transform(items: any[], filter: Object): any {
9
+ if (!items || !filter) {
10
+ return items;
11
+ }
12
+ return items.filter(item => item[filter['key']] === filter['value']);
13
+ }
14
+
15
+ }
@@ -0,0 +1,8 @@
1
+ import { FindPipe } from './find.pipe';
2
+
3
+ describe('FindPipe', () => {
4
+ it('create an instance', () => {
5
+ const pipe = new FindPipe();
6
+ expect(pipe).toBeTruthy();
7
+ });
8
+ });
@@ -0,0 +1,15 @@
1
+ import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+ @Pipe({
4
+ name: 'find'
5
+ })
6
+ export class FindPipe implements PipeTransform {
7
+
8
+ transform(items: any[], filter: Object): any {
9
+ if (!items || !filter) {
10
+ return items;
11
+ }
12
+ return items.find(item => item[filter['key']] === filter['value']);
13
+ }
14
+
15
+ }
@@ -131,4 +131,16 @@ export class TriggerEvents {
131
131
  }
132
132
 
133
133
 
134
+ public triggerOnConversationChanged(conversation: ConversationModel) {
135
+ this.logger.debug(' ---------------- triggerOnConversationChanged ---------------- ', conversation);
136
+ try {
137
+ const windowContext = this.windowContext;
138
+ if (windowContext) {
139
+ windowContext.postMessage({ type: 'onConversationChanged', data: conversation }, '*');
140
+ }
141
+ } catch (e) {
142
+ this.logger.error('[TRIGGER-HANDLER] > Error triggerOnConversationChanged:' + e);
143
+ }
144
+ }
145
+
134
146
  }
@@ -25,6 +25,9 @@ import { SafeHtmlPipe } from '../directives/safe-html.pipe';
25
25
  import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
26
26
  import { createTranslateLoader } from 'src/chat21-core/utils/utils';
27
27
  import { HttpClient } from '@angular/common/http';
28
+ import { RouterModule } from '@angular/router';
29
+ import { FindPipe } from '../pipe/find.pipe';
30
+ import { FilterPipe } from '../pipe/filter.pipe';
28
31
 
29
32
  @NgModule({
30
33
  declarations: [
@@ -67,12 +70,13 @@ import { HttpClient } from '@angular/common/http';
67
70
  SidebarUserDetailsComponent,
68
71
 
69
72
  //DIRECTIVES
70
- AutofocusDirective,
71
- TooltipDirective,
72
- MarkedPipe,
73
- HtmlEntitiesEncodePipe,
74
- SafeHtmlPipe,
75
-
73
+ AutofocusDirective,
74
+ TooltipDirective,
75
+ MarkedPipe,
76
+ HtmlEntitiesEncodePipe,
77
+ SafeHtmlPipe,
78
+ FindPipe,
79
+ FilterPipe,
76
80
 
77
81
 
78
82
  AvatarProfileComponent,
@@ -123,6 +127,10 @@ import { HttpClient } from '@angular/common/http';
123
127
  MarkedPipe,
124
128
  HtmlEntitiesEncodePipe,
125
129
  SafeHtmlPipe,
130
+ FindPipe,
131
+ FilterPipe,
132
+
133
+ RouterModule,
126
134
 
127
135
  //COMMON COMPONENTS
128
136
  AvatarProfileComponent,
@@ -140,16 +148,15 @@ import { HttpClient } from '@angular/common/http';
140
148
  MomentModule,
141
149
  NgSelectModule,
142
150
  FormsModule,
143
-
144
151
  TranslateModule.forChild({
145
- loader: {
146
- provide: TranslateLoader,
147
- useFactory: (createTranslateLoader),
148
- deps: [HttpClient]
149
- }
150
- })
151
-
152
- ],
152
+ loader: {
153
+ provide: TranslateLoader,
154
+ useFactory: (createTranslateLoader),
155
+ deps: [HttpClient]
156
+ }
157
+ }),
158
+ RouterModule.forChild([])
159
+ ],
153
160
  schemas: [
154
161
  CUSTOM_ELEMENTS_SCHEMA,
155
162
  NO_ERRORS_SCHEMA
@@ -20,6 +20,7 @@ export interface Project {
20
20
  isActiveSubscription?: boolean;
21
21
  profile?: any;
22
22
  offlineMsgEmail?: boolean;
23
+ teammateStatus?: any;
23
24
  // subscription_end_date?: any;
24
25
  // subscription_id?: any;
25
26
  // subscription_creation_date?: any;
@@ -153,6 +153,6 @@ export const TEAMMATE_STATUS = [
153
153
  { id: 1, name: 'Available', avatar: 'assets/img/teammate-status/avaible.svg', label: "LABEL_AVAILABLE" },
154
154
  { id: 2, name: 'Unavailable', avatar: 'assets/img/teammate-status/unavaible.svg', label: "LABEL_NOT_AVAILABLE" },
155
155
  { id: 3, name: 'Inactive', avatar: 'assets/img/teammate-status/inactive.svg', label: "LABEL_INACTIVE" },
156
- ];
156
+ ];
157
157
 
158
158