@chat21/chat21-ionic 3.4.32-rc5 → 3.4.32-rc7

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,12 @@
8
8
  ### **Copyrigth**:
9
9
  *Tiledesk SRL*
10
10
 
11
+ # 3.4.32-rc7
12
+ - **addded**: ability to change availability status for each project the logged-in user in sidebar-user-detail
13
+
14
+ # 3.4.32-rc6
15
+ - **bug-fixed**: convertRequestToConversation timestamp wrong unit
16
+
11
17
  # 3.4.32-rc5
12
18
  - **added**: conversations-list — on init, fetches all projects via `getProjects` and stores them in AppStorageService under `all_projects`; before saving, checks that the key does not already contain each project (avoids duplicates).
13
19
  - **changed**: conversations-list `onConversationLoaded` — project name and id are now resolved from the `all_projects` storage key instead of per-project localStorage entries.
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-rc5",
4
+ "version": "3.4.32-rc7",
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"
@@ -88,6 +89,30 @@
88
89
  <span id="sidebaravatar_{{item.name}}" style="text-transform: capitalize; margin-left:8px"> {{item.label | translate}} </span>
89
90
  </ng-template>
90
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">{{ project?.name || 'Progetti' }}</span>
98
+ <i class="material-icons" style="margin-left: 4px; font-size: 18px;">arrow_drop_down</i>
99
+ </button>
100
+ <div class="ripple-container"></div>
101
+ <div id="projects_dropdown" class="dropdown-menu" [ngClass]="{'open': openDropdownProjects}">
102
+ <li *ngFor="let prjct of projects?.slice() | slice:0:5; let i=index" style="cursor: pointer">
103
+ <a [ngClass]="{'li-selected' : prjct?.id_project?._id === project?.id_project?.id }"
104
+ class="project-item-row">
105
+ <span class="project-item-name">{{ prjct?.id_project?.name }}</span>
106
+ <span class="project-item-status project-item-status-wrapper"
107
+ [attr.title]="translationsMap?.get(prjct?.teammateStatus?.label) || prjct?.teammateStatus?.name"
108
+ (click)="toggleStatusDropdown($event, prjct)">
109
+ <img style="width: 15px; height: 15px; position: relative; top: 1px; cursor: pointer;" height="15" width="15" [src]="prjct?.teammateStatus?.avatar" />
110
+ </span>
111
+ </a>
112
+ </li>
113
+ </div>
114
+ </div>
115
+ </ng-container>
91
116
  </section>
92
117
 
93
118
  <hr class="first-divider">
@@ -140,4 +165,14 @@
140
165
  <div class="chat-version"> ver {{version}}</div>
141
166
  </section>
142
167
 
168
+ </div>
169
+
170
+ <!-- Status dropdown fuori da #user-details per evitare clipping da overflow -->
171
+ <div class="status-dropdown status-dropdown-fixed status-dropdown-outside" *ngIf="openStatusDropdownProjectId && selectedProjectForStatus" (click)="$event.stopPropagation()"
172
+ [style.top.px]="statusDropdownPosition.top" [style.right.px]="statusDropdownPosition.right">
173
+ <div class="status-dropdown-option" *ngFor="let status of TEAMMATE_STATUS"
174
+ (click)="$event.stopPropagation(); onChangeProjectStatus(selectedProjectForStatus, status.id)">
175
+ <img style="width: 15px; height: 15px; margin-right: 6px;" [src]="status.avatar" />
176
+ <span>{{ translationsMap?.get(status.label) || status.name }}</span>
177
+ </div>
143
178
  </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,7 +162,7 @@
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
167
  width: 192px;
181
168
  border-radius: 4px;
@@ -184,6 +171,288 @@
184
171
  left: 50%;
185
172
  margin: auto;
186
173
  position: absolute;
174
+ z-index: 2;
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
+ /* projects-dropdown-wrapper - stile come ng-select .ng-select-container (teammate-status-in-drawer) */
217
+ .projects-dropdown-wrapper {
218
+ position: relative;
219
+ min-height: 36px;
220
+ padding: 6px 12px;
221
+ background-color: var(--sidebar-user-detail-select-background);
222
+ color: var(--sidebar-user-detail-select-color);
223
+ border: 1px solid var(--sidebar-user-detail-select-background);
224
+ border-radius: 4px;
225
+ cursor: pointer;
226
+ display: flex;
227
+ align-items: center;
228
+ text-align: left;
229
+ font-size: 14px;
230
+ line-height: 1.4;
231
+
232
+ .projects-dropdown-toggle {
233
+ background-color: transparent !important;
234
+ border: none !important;
235
+ padding: 0 !important;
236
+ min-height: auto;
237
+ flex: 1;
238
+ justify-content: flex-start;
239
+ color: inherit;
240
+ font-size: inherit;
241
+ }
242
+
243
+ .projects-dropdown-toggle-label {
244
+ text-transform: capitalize;
245
+ overflow: hidden;
246
+ text-overflow: ellipsis;
247
+ white-space: nowrap;
248
+ }
249
+
250
+ .dropdown-menu {
251
+ left: 0;
252
+ right: 0;
253
+ min-width: 100%;
254
+ }
255
+ }
256
+
257
+ li{
258
+ position: relative;
259
+ display: block
260
+ }
261
+
262
+ /* projects_dropdown - stessi colori ng-select (.ng-dropdown-panel) */
263
+ .dropdown-menu {
264
+ border-radius: 4px;
265
+ border: 1px solid var(--sidebar-user-detail-select-background);
266
+ box-shadow: 0 2px 5px 0 rgb(0, 0, 0, 0.26);
267
+
268
+ position: absolute;
269
+ top: 100%;
270
+ right: 0;
271
+ left: auto;
272
+ z-index: 1000;
273
+ display: none;
274
+ float: left;
275
+ min-width: 160px;
276
+ padding: 5px 0;
277
+ margin: 8px 0 0;
278
+ font-size: 14px;
279
+ text-align: left;
280
+ list-style: none;
281
+ background-color: var(--sidebar-user-detail-select-background);
282
+ -webkit-background-clip: padding-box;
283
+ background-clip: padding-box;
284
+ color: var(--sidebar-user-detail-select-color);
285
+ max-height: 240px;
286
+ overflow-y: auto;
287
+ }
288
+
289
+ .dropdown-menu:not(.open){
290
+ margin-top: -20px;
291
+ opacity: 0;
292
+ visibility: hidden;
293
+ display: block;
294
+ }
295
+
296
+ .dropdown-menu.open{
297
+ margin-top: 12px; //0;
298
+ opacity: 1;
299
+ visibility: visible;
300
+ display: block;
301
+ transition: all 150ms linear;
302
+ }
303
+
304
+ .dropdown-menu li {
305
+ position: relative;
306
+
307
+ .li-selected{
308
+ color: var(--sidebar-user-detail-select-color);
309
+ background-color: var(--sidebar-user-detail-select-hover-background);
310
+ }
311
+
312
+ &.all-projects,
313
+ &.add-project{
314
+ color: var(--sidebar-user-detail-select-color);
315
+ i{
316
+ padding-right: 5px;
317
+ margin-bottom: 2px;
318
+ font-size: 20px
319
+ }
320
+ }
321
+
322
+ &.section-title{
323
+ padding-left: 24px;
324
+ font-size: 12px;
325
+ color: var(--sidebar-user-detail-color);
326
+ font-weight: 400
327
+ }
328
+ }
329
+
330
+ .dropdown-menu .divider{
331
+ background-color: rgba(255, 255, 255, 0.2);
332
+ margin: 5px 0;
333
+
334
+ height: 1px;
335
+ overflow: hidden;
336
+ }
337
+
338
+ .dropdown-menu li>a{
339
+ font-size: 13px;
340
+ padding: 10px 20px;
341
+ border-radius: 2px;
342
+ transition: all 150ms linear;
343
+
344
+ display: block;
345
+ clear: both;
346
+ font-weight: 400;
347
+ line-height: 1.42857143;
348
+ color: var(--sidebar-user-detail-select-color);
349
+ white-space: nowrap;
350
+ text-decoration: none;
351
+ cursor: pointer;
352
+
353
+ .material-icons {
354
+ vertical-align: middle;
355
+ }
356
+
357
+ &:hover{
358
+ background-color: var(--sidebar-user-detail-select-hover-background);
359
+ color: var(--sidebar-user-detail-select-color);
360
+ box-shadow: none;
361
+ }
362
+
363
+ &.project-item-row {
364
+ display: flex;
365
+ align-items: center;
366
+ justify-content: space-between;
367
+ gap: 8px;
368
+
369
+ .project-item-name {
370
+ flex: 1;
371
+ min-width: 0;
372
+ text-align: left;
373
+ overflow: hidden;
374
+ text-overflow: ellipsis;
375
+ }
376
+
377
+ .project-item-status {
378
+ flex: 0 0 10%;
379
+ display: flex;
380
+ justify-content: flex-end;
381
+ align-items: center;
382
+
383
+ &.project-item-status-wrapper {
384
+ position: relative;
385
+ }
386
+
387
+ .status-dropdown {
388
+ position: absolute;
389
+ right: 100%;
390
+ top: 50%;
391
+ transform: translateY(-50%);
392
+ margin-right: 4px;
393
+ min-width: 140px;
394
+ padding: 4px 0;
395
+ background-color: var(--sidebar-user-detail-select-background);
396
+ border: 1px solid var(--sidebar-user-detail-select-background);
397
+ border-radius: 4px;
398
+ box-shadow: 0 2px 5px 0 rgb(0, 0, 0, 0.26);
399
+ z-index: 1001;
400
+ list-style: none;
401
+
402
+ &.status-dropdown-fixed {
403
+ right: auto;
404
+ left: auto;
405
+ margin-right: 0;
406
+ transform: translateY(-50%);
407
+ }
408
+
409
+ .status-dropdown-option {
410
+ display: flex;
411
+ align-items: center;
412
+ padding: 8px 16px;
413
+ font-size: 13px;
414
+ color: var(--sidebar-user-detail-select-color);
415
+ cursor: pointer;
416
+ white-space: nowrap;
417
+
418
+ &:hover {
419
+ background-color: var(--sidebar-user-detail-select-hover-background);
420
+ color: var(--sidebar-user-detail-select-color);
421
+ }
422
+ }
423
+ }
424
+ }
425
+ }
426
+ }
427
+ }
428
+
429
+ /* Status dropdown fuori da #user-details - stessi colori ng-select */
430
+ .status-dropdown-outside {
431
+ position: fixed !important;
432
+ transform: translateY(-50%);
433
+ z-index: 1100;
434
+ min-width: 140px;
435
+ padding: 4px 0;
436
+ background-color: var(--sidebar-user-detail-select-background);
437
+ border: 1px solid var(--sidebar-user-detail-select-background);
438
+ border-radius: 4px;
439
+ box-shadow: 0 2px 5px 0 rgb(0, 0, 0, 0.26);
440
+ list-style: none;
441
+
442
+ .status-dropdown-option {
443
+ display: flex;
444
+ align-items: center;
445
+ padding: 8px 16px;
446
+ font-size: 13px;
447
+ color: var(--sidebar-user-detail-select-color);
448
+ cursor: pointer;
449
+ white-space: nowrap;
450
+
451
+ &:hover {
452
+ background-color: var(--sidebar-user-detail-select-hover-background);
453
+ color: var(--sidebar-user-detail-select-color);
454
+ }
455
+ }
187
456
  }
188
457
 
189
458
  .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() {
@@ -270,7 +312,6 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
270
312
  isActiveSubscription: projectObjct['id_project']['isActiveSubscription'],
271
313
  trialExpired: projectObjct['id_project']['trialExpired']
272
314
  }
273
-
274
315
  if (this.project.profile.type === 'free') {
275
316
 
276
317
  if (this.project.trialExpired === false) {
@@ -285,11 +326,17 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
285
326
  }
286
327
 
287
328
  this.wsService.subscriptionToWsCurrentProjectUserAvailability(this.project._id, projectObjct._id);
329
+ if (this.tiledeskToken) {
330
+ this.getProjects();
331
+ }
288
332
  }
289
333
  })
290
334
 
291
335
  try {
292
336
  this.tiledeskToken = this.appStorageService.getItem('tiledeskToken');
337
+ if (this.tiledeskToken) {
338
+ this.getProjects();
339
+ }
293
340
  // this.logger.log('[SIDEBAR-USER-DETAILS] - GET STORED TOKEN ', this.tiledeskToken)
294
341
  } catch (err) {
295
342
  this.logger.error('[SIDEBAR-USER-DETAILS] - GET STORED TOKEN ', err)
@@ -350,6 +397,106 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
350
397
  });
351
398
  }
352
399
 
400
+ getCurrentStatusAvatar(): string {
401
+ const status = this.TEAMMATE_STATUS?.find(s => s.id === this.selectedStatus);
402
+ return status?.avatar || 'assets/img/teammate-status/avaible.svg';
403
+ }
404
+
405
+ getCurrentStatusLabel(): string {
406
+ const status = this.TEAMMATE_STATUS?.find(s => s.id === this.selectedStatus);
407
+ return status?.label || status?.name || '';
408
+ }
409
+
410
+ toggleProjectsDropdown() {
411
+ this.openDropdownProjects = !this.openDropdownProjects;
412
+ if (!this.openDropdownProjects) {
413
+ this.openStatusDropdownProjectId = null;
414
+ this.selectedProjectForStatus = null;
415
+ }
416
+ }
417
+
418
+ toggleStatusDropdown(event: Event, prjct: any) {
419
+ event.stopPropagation()
420
+ event.preventDefault()
421
+ const projectId = prjct?.id_project?._id
422
+ const isOpening = this.openStatusDropdownProjectId !== projectId
423
+ if (isOpening) {
424
+ const el = event.currentTarget as HTMLElement
425
+ const rect = el.getBoundingClientRect()
426
+ this.statusDropdownPosition = {
427
+ top: rect.top + rect.height / 2,
428
+ right: window.innerWidth - rect.left + 4
429
+ }
430
+ this.selectedProjectForStatus = prjct
431
+ } else {
432
+ this.selectedProjectForStatus = null
433
+ }
434
+ this.openStatusDropdownProjectId = this.openStatusDropdownProjectId === projectId ? null : projectId
435
+ }
436
+
437
+ onChangeProjectStatus(projectUser: ProjectUser, selectedStatusID: any) {
438
+ this.logger.log('[SIDEBAR-USER-DETAILS] onChangeProjectStatus', projectUser, selectedStatusID)
439
+ this.openStatusDropdownProjectId = null
440
+ this.selectedProjectForStatus = null
441
+
442
+ let IS_AVAILABLE = null
443
+ let profilestatus = ''
444
+ if (selectedStatusID === 1) {
445
+ IS_AVAILABLE = true
446
+ } else if (selectedStatusID === 2) {
447
+ IS_AVAILABLE = false
448
+ } else if (selectedStatusID === 3) {
449
+ IS_AVAILABLE = false
450
+ profilestatus = 'inactive'
451
+ }
452
+
453
+ this.wsService.updateCurrentUserAvailability(this.tiledeskToken, projectUser.id_project._id, IS_AVAILABLE, profilestatus).subscribe((projectUserUpdated: any) => {
454
+
455
+ this.logger.log('[NAVBAR] - PROJECT-USER UPDATED ', projectUser)
456
+ this.projects.find(p => p.id_project._id === projectUser.id_project._id).teammateStatus = getUserStatusFromProjectUser(projectUserUpdated as any);
457
+
458
+ }, (error) => {
459
+ this.logger.error('[NAVBAR] - PROJECT-USER UPDATED - ERROR ', error);
460
+
461
+ }, () => {
462
+ this.logger.log('[NAVBAR] - PROJECT-USER UPDATED * COMPLETE *');
463
+
464
+ });
465
+ }
466
+
467
+ onStatusDropdownOptionClick(status: { id: number; name: string; avatar: string; label: string }, projectUser: ProjectUser | null) {
468
+ if (!projectUser) return;
469
+ this.changeProjectStatus(projectUser, status.id);
470
+ this.openStatusDropdownProjectId = null;
471
+ this.selectedProjectForStatus = null;
472
+ if (projectUser?.id_project?._id === this.project?._id) {
473
+ this.selectedStatus = status.id;
474
+ }
475
+ }
476
+
477
+ changeProjectStatus(projectUser: ProjectUser, selectedStatusID: number) {
478
+ this.logger.log('[SIDEBAR-USER-DETAILS] changeProjectStatus projectid', projectUser?.id_project?._id, ' status: ', selectedStatusID);
479
+ let IS_AVAILABLE: boolean | null = null;
480
+ let profilestatus = '';
481
+ if (selectedStatusID === 1) {
482
+ IS_AVAILABLE = true;
483
+ } else if (selectedStatusID === 2) {
484
+ IS_AVAILABLE = false;
485
+ } else if (selectedStatusID === 3) {
486
+ IS_AVAILABLE = false;
487
+ profilestatus = 'inactive';
488
+ }
489
+ this.wsService.updateCurrentUserAvailability(this.tiledeskToken, projectUser.id_project._id, IS_AVAILABLE, profilestatus).subscribe((updated: any) => {
490
+ this.logger.log('[SIDEBAR-USER-DETAILS] - PROJECT-USER UPDATED ', updated);
491
+ const p = this.projects.find(prj => prj?.id_project?._id === projectUser?.id_project?._id);
492
+ if (p) {
493
+ p.teammateStatus = getUserStatusFromProjectUser(updated as any);
494
+ }
495
+ }, (error) => {
496
+ this.logger.error('[SIDEBAR-USER-DETAILS] - PROJECT-USER UPDATED - ERROR ', error);
497
+ });
498
+ }
499
+
353
500
  changeAvailabilityStateInUserDetailsSidebar(selectedStatusID) {
354
501
  this.logger.log('[SIDEBAR-USER-DETAILS] - changeAvailabilityState projectid', this.project._id, ' available 1: ', selectedStatusID);
355
502
 
@@ -57,8 +57,9 @@ import { getOSCode, hasRole } from 'src/app/utils/utils';
57
57
  import { PERMISSIONS } from 'src/app/utils/permissions.constants';
58
58
  import { ProjectUser } from 'src/chat21-core/models/projectUsers';
59
59
  import { ProjectUsersService } from 'src/app/services/project_users/project-users.service';
60
+ import { ProjectService } from 'src/app/services/projects/project.service';
60
61
 
61
- const PROJECTS_STORAGE_KEY = 'all_projects';
62
+ import { PROJECTS_STORAGE_KEY } from 'src/chat21-core/utils/constants';
62
63
 
63
64
  @Component({
64
65
  selector: 'app-conversations-list',
@@ -140,6 +141,7 @@ export class ConversationListPage implements OnInit {
140
141
  public tiledeskService: TiledeskService,
141
142
  public tiledeskAuthService: TiledeskAuthService,
142
143
  public projectUsersService: ProjectUsersService,
144
+ public projectService: ProjectService,
143
145
  public appConfigProvider: AppConfigProvider,
144
146
  public platform: Platform,
145
147
  public wsService: WebsocketService,
@@ -230,7 +232,7 @@ export class ConversationListPage implements OnInit {
230
232
  private loadAndStoreProjects() {
231
233
  const token = this.tiledeskAuthService.getTiledeskToken();
232
234
  if (!token) return;
233
- this.tiledeskService.getProjects(token).subscribe((projects: Project[]) => {
235
+ this.projectService.getProjects().subscribe((projects: Project[]) => {
234
236
  if (!projects?.length) return;
235
237
  let projectsMap: Record<string, Project> = {};
236
238
  const stored = this.appStorageService.getItem(PROJECTS_STORAGE_KEY);
@@ -14,9 +14,8 @@ import { TiledeskService } from 'src/app/services/tiledesk/tiledesk.service';
14
14
  import { getProjectIdSelectedConversation, isGroup } from 'src/chat21-core/utils/utils';
15
15
  import { ImageRepoService } from 'src/chat21-core/providers/abstract/image-repo.service';
16
16
  import { Project } from 'src/chat21-core/models/projects';
17
-
18
- const PROJECTS_STORAGE_KEY = 'all_projects';
19
-
17
+ import { ProjectService } from 'src/app/services/projects/project.service';
18
+ import { PROJECTS_STORAGE_KEY } from 'src/chat21-core/utils/constants';
20
19
 
21
20
  @Component({
22
21
  selector: 'app-unassigned-conversations',
@@ -59,6 +58,7 @@ export class UnassignedConversationsPage implements OnInit, OnChanges {
59
58
  private events: EventsService,
60
59
  private tiledeskAuthService: TiledeskAuthService,
61
60
  private tiledeskService: TiledeskService,
61
+ private projectService: ProjectService,
62
62
  public imageRepoService: ImageRepoService,
63
63
  public appStorageService: AppStorageService,
64
64
  ) {
@@ -125,7 +125,7 @@ export class UnassignedConversationsPage implements OnInit, OnChanges {
125
125
  }
126
126
  const token = this.tiledeskAuthService.getTiledeskToken();
127
127
  if (!token) return;
128
- this.tiledeskService.getProjects(token).subscribe(
128
+ this.projectService.getProjects().subscribe(
129
129
  (projects: Project[]) => {
130
130
  if (!projects?.length) return;
131
131
  let projectsMap: Record<string, Project> = {};
@@ -95,22 +95,6 @@ export class TiledeskService {
95
95
  }))
96
96
  }
97
97
 
98
- public getProjects(token: string): Observable<Project[]> {
99
- const url = this.SERVER_BASE_URL + 'projects/';
100
- this.logger.log('[TILEDESK-SERVICE] - GET PROJECTS URL', url);
101
-
102
- const httpOptions = {
103
- headers: new HttpHeaders({
104
- 'Content-Type': 'application/json',
105
- Authorization: token
106
- })
107
- };
108
-
109
- return this.http.get(url, httpOptions).pipe(map((projects: Project[]) => {
110
- this.logger.log('[TILEDESK-SERVICE] GET PROJECTS - RES ', projects);
111
- return projects
112
- }))
113
- }
114
98
 
115
99
  public getProjectUsersByProjectId(project_id: string) {
116
100
  const url = this.SERVER_BASE_URL + project_id + '/project_users/';
@@ -25,6 +25,7 @@ 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';
28
29
 
29
30
  @NgModule({
30
31
  declarations: [
@@ -124,6 +125,8 @@ import { HttpClient } from '@angular/common/http';
124
125
  HtmlEntitiesEncodePipe,
125
126
  SafeHtmlPipe,
126
127
 
128
+ RouterModule,
129
+
127
130
  //COMMON COMPONENTS
128
131
  AvatarProfileComponent,
129
132
  UserPresenceComponent,
@@ -140,16 +143,15 @@ import { HttpClient } from '@angular/common/http';
140
143
  MomentModule,
141
144
  NgSelectModule,
142
145
  FormsModule,
143
-
144
146
  TranslateModule.forChild({
145
- loader: {
146
- provide: TranslateLoader,
147
- useFactory: (createTranslateLoader),
148
- deps: [HttpClient]
149
- }
150
- })
151
-
152
- ],
147
+ loader: {
148
+ provide: TranslateLoader,
149
+ useFactory: (createTranslateLoader),
150
+ deps: [HttpClient]
151
+ }
152
+ }),
153
+ RouterModule.forChild([])
154
+ ],
153
155
  schemas: [
154
156
  CUSTOM_ELEMENTS_SCHEMA,
155
157
  NO_ERRORS_SCHEMA
@@ -116,6 +116,7 @@ export const PLATFORM_DESKTOP = 'desktop';
116
116
 
117
117
  // STORAGE
118
118
  export const STORAGE_PREFIX = 'tiledesk_widget_';
119
+ export const PROJECTS_STORAGE_KEY = 'all_projects';
119
120
 
120
121
  // links
121
122
  export const FIREBASESTORAGE_BASE_URL_IMAGE = 'https://firebasestorage.googleapis.com/v0/b/' //+ 'chat-v2-dev.appspot.com/o/';
@@ -30,7 +30,7 @@ export class ConvertRequestToConversation {
30
30
  '',
31
31
  request.lead && request.lead.fullname ? request.lead.fullname: null,
32
32
  request.status || '0',
33
- moment(request.createdAt).unix(),
33
+ moment(request.createdAt).valueOf(),
34
34
  getColorBck(request.lead.fullname),
35
35
  avatarPlaceholder(request.lead.fullname),
36
36
  false,