@chat21/chat21-ionic 3.4.31 → 3.4.32-rc2

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 (82) hide show
  1. package/CHANGELOG.md +133 -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 +64 -0
  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 +3 -3
  26. package/src/app/components/sidebar-user-details/sidebar-user-details.component.ts +15 -22
  27. package/src/app/modals/create-ticket/create-ticket.page.ts +4 -2
  28. package/src/app/pages/conversation-detail/conversation-detail.page.html +7 -3
  29. package/src/app/pages/conversation-detail/conversation-detail.page.ts +89 -5
  30. package/src/app/pages/conversations-list/conversations-list.module.ts +3 -5
  31. package/src/app/pages/conversations-list/conversations-list.page.html +2 -0
  32. package/src/app/pages/conversations-list/conversations-list.page.ts +51 -11
  33. package/src/app/pages/unassigned-conversations/unassigned-conversations.module.ts +16 -4
  34. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.html +43 -17
  35. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.scss +25 -1
  36. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.ts +114 -9
  37. package/src/app/services/global-settings/global-settings.service.ts +11 -3
  38. package/src/app/services/nav-proxy.service.ts +0 -1
  39. package/src/app/services/project_users/project-users.service.spec.ts +16 -0
  40. package/src/app/services/project_users/project-users.service.ts +63 -0
  41. package/src/app/services/projects/project.service.ts +2 -1
  42. package/src/app/services/tiledesk/tiledesk.service.ts +21 -16
  43. package/src/app/services/triggerEvents/triggerEvents.ts +28 -0
  44. package/src/app/services/websocket/websocket-js.ts +59 -534
  45. package/src/app/services/websocket/websocket-js_old.ts +578 -0
  46. package/src/app/services/websocket/websocket.service.ts +67 -14
  47. package/src/app/services/websocket/websocket.worker.ts +242 -0
  48. package/src/app/shared/shared.module.ts +11 -2
  49. package/src/app/utils/globals.ts +2 -0
  50. package/src/app/utils/permissions.constants.ts +138 -0
  51. package/src/app/utils/project-utils.ts +2 -2
  52. package/src/app/utils/utils.ts +18 -1
  53. package/src/assets/i18n/ar.json +11 -1
  54. package/src/assets/i18n/az.json +11 -1
  55. package/src/assets/i18n/de.json +11 -1
  56. package/src/assets/i18n/en.json +11 -1
  57. package/src/assets/i18n/es.json +11 -1
  58. package/src/assets/i18n/fr.json +11 -1
  59. package/src/assets/i18n/it.json +13 -3
  60. package/src/assets/i18n/kk.json +11 -1
  61. package/src/assets/i18n/pt.json +11 -1
  62. package/src/assets/i18n/ru.json +11 -1
  63. package/src/assets/i18n/sr.json +11 -1
  64. package/src/assets/i18n/sv.json +11 -1
  65. package/src/assets/i18n/tr.json +11 -1
  66. package/src/assets/i18n/uk.json +11 -1
  67. package/src/assets/i18n/uz.json +12 -1
  68. package/src/assets/js/agentDesktop-sdk.js +55 -0
  69. package/src/assets/js/chat21client.js +36 -0
  70. package/src/assets/js/mqtt-keepalive-worker.js +53 -0
  71. package/src/assets/test.html +5 -2
  72. package/src/chat-config-template.json +1 -0
  73. package/src/chat-config.json +1 -0
  74. package/src/chat21-core/models/projectUsers.ts +19 -0
  75. package/src/chat21-core/models/project_user.ts +2 -1
  76. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +1 -1
  77. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +1 -1
  78. package/src/chat21-core/providers/tiledesk/tiledesk-auth.service.ts +3 -0
  79. package/src/chat21-core/utils/constants.ts +5 -0
  80. package/src/chat21-core/utils/convertRequestToConversation.ts +1 -1
  81. package/src/chat21-core/utils/utils.ts +53 -3
  82. package/src/variables.scss +3 -0
@@ -83,8 +83,12 @@ import { WebsocketService } from 'src/app/services/websocket/websocket.service';
83
83
  import { Project } from 'src/chat21-core/models/projects';
84
84
  import { Globals } from 'src/app/utils/globals';
85
85
  import { ProjectService } from 'src/app/services/projects/project.service';
86
- import { getOSCode } from 'src/app/utils/utils';
87
86
  import { UploadService } from 'src/chat21-core/providers/abstract/upload.service';
87
+ import { ProjectUsersService } from 'src/app/services/project_users/project-users.service';
88
+ import { ProjectUser } from 'src/chat21-core/models/projectUsers';
89
+ import { getOSCode, hasRole } from 'src/app/utils/utils';
90
+ import { PERMISSIONS } from 'src/app/utils/permissions.constants';
91
+ import { TriggerEvents } from 'src/app/services/triggerEvents/triggerEvents';
88
92
 
89
93
  @Component({
90
94
  selector: 'app-conversation-detail',
@@ -109,6 +113,7 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
109
113
  private subscriptions: Array<any>
110
114
  public tenant: string;
111
115
  public loggedUser: UserModel
116
+ public projectUser: ProjectUser;
112
117
  public conversationWith: string
113
118
  public conversationWithFullname: string
114
119
  public messages: Array<MessageModel> = []
@@ -138,6 +143,7 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
138
143
  public tagsCannedFilter: Array<any> = [];
139
144
  public SHOW_CANNED_RESPONSES: boolean = false
140
145
  public canShowCanned: boolean = true
146
+ public rolesCanned: { [key: string]: boolean }
141
147
 
142
148
  public SHOW_COPILOT_SUGGESTIONS: boolean = false;
143
149
 
@@ -172,6 +178,10 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
172
178
  copilotQuestion: string = '';
173
179
  /**COPILOT : end */
174
180
 
181
+ /** TICKET: start */
182
+ isTicketEnabled: boolean = false;
183
+ /** TICKET: end */
184
+
175
185
  isMine = isMine
176
186
  isInfo = isInfo
177
187
  isFirstMessage = isFirstMessage
@@ -242,18 +252,20 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
242
252
  public toastController: ToastController,
243
253
  public tiledeskService: TiledeskService,
244
254
  public projectService: ProjectService,
255
+ public projectUsersService: ProjectUsersService,
245
256
  private networkService: NetworkService,
246
257
  private events: EventsService,
247
258
  private webSocketService: WebsocketService,
248
259
  public projectPlanUtils: ProjectPlanUtils,
260
+ public triggerEvents: TriggerEvents,
249
261
  private g: Globals,
250
262
  ) {
251
263
  // Change list on date change
252
264
  this.route.paramMap.subscribe((params) => {
253
265
  this.logger.log('[CONVS-DETAIL] - constructor -> params: ', params)
254
266
  this.conversationWith = params.get('IDConv')
255
- this.conversationWithFullname = params.get('FullNameConv')
256
- this.conv_type = params.get('Convtype')
267
+ this.conversationWithFullname = decodeURIComponent(params.get('FullNameConv'))
268
+ this.conv_type = decodeURIComponent(params.get('Convtype'))
257
269
 
258
270
  this.events.publish('supportconvid:haschanged', this.conversationWith)
259
271
  })
@@ -420,6 +432,8 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
420
432
  ionViewDidEnter() {
421
433
  this.logger.log('[CONVS-DETAIL] > ionViewDidEnter')
422
434
  // this.info_content_child_enabled = true;
435
+ // Scroll to bottom to show the last message without animation
436
+ this.scrollToLastMessage()
423
437
  }
424
438
 
425
439
  // Unsubscibe when new page transition end
@@ -479,6 +493,7 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
479
493
  this.messages = [] // list messages of conversation
480
494
  this.isFileSelected = false // indicates if a file has been selected (image to upload)
481
495
  this.isEmailEnabled = (this.appConfigProvider.getConfig().emailSection === 'true' || this.appConfigProvider.getConfig().emailSection === true) ? true : false;
496
+ this.isTicketEnabled = (this.appConfigProvider.getConfig().ticketSection === 'true' || this.appConfigProvider.getConfig().ticketSection === true) ? true : false;
482
497
  this.isWhatsappTemplatesEnabled = (this.appConfigProvider.getConfig().whatsappTemplatesSection === 'true' || this.appConfigProvider.getConfig().whatsappTemplatesSection === true) ? true : false;
483
498
  this.fileUploadAccept = this.appConfigProvider.getConfig().fileUploadAccept
484
499
 
@@ -538,7 +553,6 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
538
553
  this.logger.log('[CONVS-DETAIL] - GET PROJECTID BY CONV RECIPIENT * COMPLETE *',)
539
554
  })
540
555
  }else {
541
- this.canShowCanned = false;
542
556
  this.offlineMsgEmail = false;
543
557
  }
544
558
 
@@ -549,10 +563,13 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
549
563
  this.logger.log('[CONVS-DETAIL] - GET PROJECTID BY CONV RECIPIENT RES', project)
550
564
  if (project) {
551
565
  const projectId = project.id_project
552
- this.canShowCanned = this.projectPlanUtils.checkPlanIsExpired(project)
566
+ this.projectUser = await this.projectUsersService.getProjectUserByProjectId(project._id)
553
567
  this.offlineMsgEmail = this.checkOfflineMsgEmailIsEnabled(project)
554
568
  this.isCopilotEnabled = this.projectPlanUtils.checkProjectProfileFeature(project, 'copilot');
555
569
  this.fileUploadAccept = this.checkAcceptedUploadFile(project)
570
+ this.rolesCanned = this.checkCannedResponsesRoles()
571
+ this.canShowCanned = this.checkCannedResponses(project)
572
+ this.logger.log('[CONVS-DETAIL] this.rolesCanned ', this.canShowCanned)
556
573
  }
557
574
  }, (error) => {
558
575
  this.logger.error('[CONVS-DETAIL] - GET PROJECTID BY CONV RECIPIENT - ERROR ', error)
@@ -590,6 +607,40 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
590
607
  return this.appConfigProvider.getConfig().fileUploadAccept
591
608
  }
592
609
 
610
+ checkCannedResponses(project: Project): boolean {
611
+ let expires = this.projectPlanUtils.checkPlanIsExpired(project)
612
+ this.logger.log('[CONVS-DETAIL] checkCannedResponses expires ', expires)
613
+ if(expires){
614
+ return false
615
+ }
616
+
617
+ let hasRoleToShowCanned = this.rolesCanned[PERMISSIONS.CANNED_RESPONSES_READ]
618
+ this.logger.log('[CONVS-DETAIL] checkCannedResponses hasRoleToShowCanned ', hasRoleToShowCanned)
619
+ if(!hasRoleToShowCanned){
620
+ return false
621
+ }
622
+
623
+ return true
624
+ }
625
+
626
+ checkCannedResponsesRoles(): { [key: string]: boolean } {
627
+ const permissionKeys = [
628
+ 'CANNED_RESPONSES_CREATE',
629
+ 'CANNED_RESPONSES_READ',
630
+ 'CANNED_RESPONSES_UPDATE',
631
+ 'CANNED_RESPONSES_DELETE',
632
+ ] as const;
633
+
634
+ const roles: { [key: string]: boolean } = {};
635
+ for (const key of permissionKeys) {
636
+ const permission = PERMISSIONS[key];
637
+ roles[permission] = hasRole(this.projectUser, permission);
638
+ }
639
+
640
+ return roles;
641
+
642
+ }
643
+
593
644
  // getProjectIdSelectedConversation(conversationWith: string): string{
594
645
  // const conversationWith_segments = conversationWith.split('-')
595
646
  // // Removes the last element of the array if is = to the separator
@@ -673,6 +724,11 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
673
724
  "WHATSAPP.ERROR_WHATSAPP_NOT_INSTALLED",
674
725
  "WHATSAPP.ERROR_WHATSAPP_GENERIC_ERROR",
675
726
 
727
+ "TICKET.OPEN_TICKET",
728
+ "TICKET.DESCRIPTION",
729
+ "TICKET.CONFIRM",
730
+ "TICKET.CLOSE",
731
+
676
732
  "COPILOT.ASK_AI",
677
733
  "COPILOT.NO_SUGGESTIONS_PRESENT",
678
734
 
@@ -1888,6 +1944,11 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
1888
1944
  }
1889
1945
 
1890
1946
 
1947
+ onOpenTicket(event) {
1948
+ this.logger.debug('[CONVS-DETAIL] openTicketOnExternalService - conversationWith ', this.conversationWith)
1949
+ const detailOBJ = { event: 'onOpenTicketExternally', request_id: this.conversationWith, conversation: this.conversation }
1950
+ this.triggerEvents.triggerOnOpenTicketExternally(detailOBJ)
1951
+ }
1891
1952
  // -------------- START SCROLL/RESIZE -------------- //
1892
1953
  /** */
1893
1954
  resizeTextArea() {
@@ -1931,6 +1992,29 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
1931
1992
  }
1932
1993
  }
1933
1994
 
1995
+ /**
1996
+ * Scroll to last message without animation using requestAnimationFrame
1997
+ * This is a best practice alternative to setTimeout
1998
+ */
1999
+ private scrollToLastMessage() {
2000
+ this.showIonContent = true
2001
+ if (this.ionContentChatArea) {
2002
+ // Use requestAnimationFrame for better performance
2003
+ requestAnimationFrame(() => {
2004
+ requestAnimationFrame(() => {
2005
+ // Double RAF ensures DOM is fully rendered
2006
+ this.ionContentChatArea.scrollToBottom(0).then(() => {
2007
+ this.logger.log('[CONVS-DETAIL] scroll posizionato all\'ultimo messaggio')
2008
+ }).catch((error) => {
2009
+ this.logger.error('[CONVS-DETAIL] errore durante lo scroll:', error)
2010
+ })
2011
+ })
2012
+ })
2013
+ } else {
2014
+ this.logger.warn('[CONVS-DETAIL] ionContentChatArea non disponibile')
2015
+ }
2016
+ }
2017
+
1934
2018
  /**
1935
2019
  * detectBottom
1936
2020
  */
@@ -19,8 +19,7 @@ import { ProfileInfoPageModule } from '../profile-info/profile-info.module';
19
19
  // import { ConversationDetailPageModule } from '../conversation-detail/conversation-detail.module';
20
20
  import { SharedModule } from 'src/app/shared/shared.module';
21
21
  import { ScrollbarThemeModule } from '../../utils/scrollbar-theme.directive';
22
- import { ListConversationsComponent } from 'src/app/chatlib/list-conversations-component/list-conversations/list-conversations.component';
23
- import { IonListConversationsComponent } from 'src/app/chatlib/list-conversations-component/ion-list-conversations/ion-list-conversations.component';
22
+ import { ListConversationsModule } from 'src/app/chatlib/list-conversations-component/list-conversations.module';
24
23
  import { HeaderConversationsList } from 'src/app/components/conversations-list/header-conversations-list/header-conversations-list.component';
25
24
  import { HeaderConversationsListArchived } from 'src/app/components/conversations-list/header-conversations-list-archived/header-conversations-list-archived.component';
26
25
  import { HeaderConversationsListUnassigned } from 'src/app/components/conversations-list/header-conversations-list-unassigned/header-conversations-list-unassigned.component';
@@ -46,14 +45,13 @@ import { MomentModule } from 'ngx-moment';
46
45
  }
47
46
  }),
48
47
  SharedModule,
49
- MomentModule
48
+ MomentModule,
49
+ ListConversationsModule
50
50
  ],
51
51
  // entryComponents: [DdpHeaderComponent],
52
52
  declarations: [
53
53
  ConversationListPage,
54
54
  //******** COMPONENTS - init ********//
55
- ListConversationsComponent,
56
- IonListConversationsComponent,
57
55
  HeaderConversationsList,
58
56
  HeaderConversationsListArchived,
59
57
  HeaderConversationsListUnassigned,
@@ -7,6 +7,8 @@
7
7
  [sound_btn]="sound_btn"
8
8
  [isMobile]="isMobile"
9
9
  [isVisibleTKT]="isVisibleTKT"
10
+ [isVisibleCNT]="isVisibleCNT"
11
+ [roles]="rolesHeader"
10
12
  (onSoundChange)="onSoundChange($event)"
11
13
  (openContactsDirectory)=openContactsDirectory($event)
12
14
  (openProfileInfo)=openProfileInfo($event)>
@@ -53,7 +53,10 @@ import { Globals } from 'src/app/utils/globals';
53
53
  import { TriggerEvents } from 'src/app/services/triggerEvents/triggerEvents';
54
54
  import { MessageModel } from 'src/chat21-core/models/message';
55
55
  import { Project } from 'src/chat21-core/models/projects';
56
- import { getOSCode } from 'src/app/utils/utils';
56
+ import { getOSCode, hasRole } from 'src/app/utils/utils';
57
+ import { PERMISSIONS } from 'src/app/utils/permissions.constants';
58
+ import { ProjectUser } from 'src/chat21-core/models/projectUsers';
59
+ import { ProjectUsersService } from 'src/app/services/project_users/project-users.service';
57
60
 
58
61
  @Component({
59
62
  selector: 'app-conversations-list',
@@ -82,6 +85,7 @@ export class ConversationListPage implements OnInit {
82
85
  public archived_btn: boolean
83
86
  public sound_btn: string
84
87
  public isVisibleTKT: boolean = true;
88
+ public isVisibleCNT: boolean = true;;
85
89
  public convertMessage = convertMessage
86
90
  private isShowMenuPage = false
87
91
  private logger: LoggerService = LoggerInstance.getInstance()
@@ -104,6 +108,9 @@ export class ConversationListPage implements OnInit {
104
108
  public isMobile: boolean = false;
105
109
  public isInitialized: boolean = false;
106
110
 
111
+ public projectUser: ProjectUser;
112
+ public rolesHeader: { [key: string]: boolean }
113
+
107
114
  // PROJECT AVAILABILITY INFO: start
108
115
  project: Project
109
116
  profile_name_translated: string;
@@ -130,6 +137,7 @@ export class ConversationListPage implements OnInit {
130
137
  private translateService: CustomTranslateService,
131
138
  public tiledeskService: TiledeskService,
132
139
  public tiledeskAuthService: TiledeskAuthService,
140
+ public projectUsersService: ProjectUsersService,
133
141
  public appConfigProvider: AppConfigProvider,
134
142
  public platform: Platform,
135
143
  public wsService: WebsocketService,
@@ -294,17 +302,19 @@ export class ConversationListPage implements OnInit {
294
302
  // ---------------------------------------------------------
295
303
  // Opens the Unassigned Conversations iframe
296
304
  // ---------------------------------------------------------
297
- openUnassignedConversations(IFRAME_URL: string, event) {
305
+ openUnassignedConversations(IFRAME_URL: string, event: { event: string; data: ConversationModel[] }) {
306
+ const unassignedConversations = event?.data ?? [];
307
+ const params = {
308
+ iframe_URL: IFRAME_URL,
309
+ callerBtn: event.event,
310
+ unassignedConversations,
311
+ stylesMap: this.stylesMap,
312
+ translationMapConversation: this.translationMapConversation,
313
+ };
298
314
  if (checkPlatformIsMobile()) {
299
- presentModal(this.modalController, UnassignedConversationsPage, {
300
- iframe_URL: IFRAME_URL,
301
- callerBtn: event,
302
- })
315
+ presentModal(this.modalController, UnassignedConversationsPage, params);
303
316
  } else {
304
- this.navService.push(UnassignedConversationsPage, {
305
- iframe_URL: IFRAME_URL,
306
- callerBtn: event,
307
- })
317
+ this.navService.push(UnassignedConversationsPage, params);
308
318
  }
309
319
  }
310
320
 
@@ -371,6 +381,11 @@ export class ConversationListPage implements OnInit {
371
381
  // save conversationHandler in chatManager
372
382
  this.chatManager.setConversationsHandler(this.conversationsHandlerService)
373
383
  this.showPlaceholder = false
384
+
385
+ // Hide loading spinner if there are no conversations
386
+ if (this.conversations.length === 0) {
387
+ this.loadingIsActive = false
388
+ }
374
389
  }
375
390
 
376
391
  // private manageStoredConversations() {
@@ -471,7 +486,7 @@ export class ConversationListPage implements OnInit {
471
486
  }
472
487
 
473
488
  listenToCurrentStoredProject() {
474
- this.events.subscribe('storage:last_project', projectObjct => {
489
+ this.events.subscribe('storage:last_project', async(projectObjct) => {
475
490
  if (projectObjct && projectObjct !== 'undefined') {
476
491
  this.logger.log('[CONVS-LIST-PAGE] - GET STORED PROJECT ', projectObjct)
477
492
 
@@ -496,6 +511,10 @@ export class ConversationListPage implements OnInit {
496
511
  } else if (this.project.profile.type === 'payment' && this.project.profile.name === 'enterprise') {
497
512
  this.profile_name_translated = this.translationMapHeader.get('PaydPlanNameEnterprise');
498
513
  }
514
+
515
+ this.projectUser = await this.projectUsersService.getProjectUserByProjectId(this.project._id)
516
+ this.rolesHeader = this.checkCannedResponsesRoles();
517
+ this.logger.log('[CONVS-LIST-PAGE] - GET PROJECT USER ROLES ', this.rolesHeader)
499
518
  }
500
519
  })
501
520
  }
@@ -631,8 +650,24 @@ export class ConversationListPage implements OnInit {
631
650
  const public_Key = this.appConfigProvider.getConfig().t2y12PruGU9wUtEGzBJfolMIgK
632
651
  this.logger.log('[CONVS-LIST-PAGE] AppConfigService getAppConfig public_Key', public_Key)
633
652
  this.isVisibleTKT = getOSCode("TKT", public_Key);
653
+ this.isVisibleCNT = getOSCode("CNT", public_Key);
634
654
  }
635
655
 
656
+ checkCannedResponsesRoles(): { [key: string]: boolean } {
657
+ const permissionKeys = [
658
+ 'LEADS_READ',
659
+ ] as const;
660
+
661
+ const roles: { [key: string]: boolean } = {};
662
+ for (const key of permissionKeys) {
663
+ const permission = PERMISSIONS[key];
664
+ roles[permission] = hasRole(this.projectUser, permission);
665
+ }
666
+
667
+ return roles;
668
+
669
+ }
670
+
636
671
  onBackButtonFN(event) {
637
672
  this.conversationType = 'active'
638
673
 
@@ -883,6 +918,10 @@ export class ConversationListPage implements OnInit {
883
918
 
884
919
  this.logger.log('[CONVS-LIST-PAGE] navigateByUrl this.uidConvSelected ', this.uidConvSelected)
885
920
 
921
+ const queryParams = this.route.snapshot.queryParams;
922
+ const queryString = new URLSearchParams(queryParams).toString();
923
+
924
+
886
925
  this.setUidConvSelected(uidConvSelected, converationType)
887
926
  if (checkPlatformIsMobile()) {
888
927
  this.logger.log('[CONVS-LIST-PAGE] checkPlatformIsMobile(): ', checkPlatformIsMobile())
@@ -900,6 +939,7 @@ export class ConversationListPage implements OnInit {
900
939
  if (this.conversationSelected && this.conversationSelected.conversation_with_fullname) {
901
940
  pageUrl = 'conversation-detail/' + this.uidConvSelected + '/' + encodeURIComponent(this.conversationSelected.conversation_with_fullname) + '/' + converationType
902
941
  }
942
+ pageUrl += queryString ? `?${queryString}` : '';
903
943
  this.logger.log('[CONVS-LIST-PAGE] setUidConvSelected navigateByUrl--->: ', pageUrl)
904
944
  // replace(/\(/g, '%28').replace(/\)/g, '%29') -> used for the encoder of any round brackets
905
945
  this.router.navigateByUrl(pageUrl.replace(/\(/g, '%28').replace(/\)/g, '%29'), {replaceUrl: true})
@@ -1,22 +1,34 @@
1
1
  import { NgModule } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { FormsModule } from '@angular/forms';
4
+ import { HttpClient } from '@angular/common/http';
4
5
 
5
6
  import { IonicModule } from '@ionic/angular';
7
+ import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
8
+ import { createTranslateLoader } from 'src/chat21-core/utils/utils';
6
9
 
7
10
  import { UnassignedConversationsPageRoutingModule } from './unassigned-conversations-routing.module';
8
11
 
9
12
  import { UnassignedConversationsPage } from './unassigned-conversations.page';
13
+ import { ListConversationsModule } from 'src/app/chatlib/list-conversations-component/list-conversations.module';
14
+ import { MomentModule } from 'ngx-moment';
10
15
 
11
16
  @NgModule({
12
17
  imports: [
13
18
  CommonModule,
14
19
  FormsModule,
15
20
  IonicModule,
16
- UnassignedConversationsPageRoutingModule
21
+ UnassignedConversationsPageRoutingModule,
22
+ MomentModule,
23
+ ListConversationsModule,
24
+ TranslateModule.forChild({
25
+ loader: {
26
+ provide: TranslateLoader,
27
+ useFactory: createTranslateLoader,
28
+ deps: [HttpClient],
29
+ },
30
+ }),
17
31
  ],
18
- declarations: [
19
- UnassignedConversationsPage,
20
- ]
32
+ declarations: [UnassignedConversationsPage]
21
33
  })
22
34
  export class UnassignedConversationsPageModule {}
@@ -1,27 +1,53 @@
1
- <ion-header>
2
- <ion-toolbar [class.mobile]="isMobile">
3
- <ion-buttons slot="start">
4
- <ion-button ion-button fill="clear" (click)="onClose()">
5
- <ion-icon slot="icon-only" name="close"></ion-icon>
6
- </ion-button>
7
- </ion-buttons>
8
- <ion-title *ngIf="callerBtn !== 'pinbtn'" style="font-size: 16px;">
9
- {{translationMap?.get('UnassignedConversations') }}
10
- </ion-title>
11
- <ion-title *ngIf="callerBtn === 'pinbtn'" style="font-size: 16px;">
12
- {{translationMap?.get('PIN_A_PROJECT') }}
13
- </ion-title>
14
- </ion-toolbar>
15
- </ion-header>
1
+ <ion-toolbar [class.mobile]="isMobile">
2
+ <ion-title *ngIf="callerBtn !== 'pinbtn'" style="font-size: 16px;">
3
+ {{translationMap?.get('UnassignedConversations') }}
4
+ </ion-title>
5
+ <ion-title *ngIf="callerBtn === 'pinbtn'" style="font-size: 16px;">
6
+ {{translationMap?.get('PIN_A_PROJECT') }}
7
+ </ion-title>
8
+
9
+ <ion-buttons slot="end">
10
+ <ion-button ion-button fill="clear" (click)="onClose()">
11
+ <ion-icon slot="icon-only" name="close"></ion-icon>
12
+ </ion-button>
13
+ </ion-buttons>
14
+
15
+ </ion-toolbar>
16
16
 
17
17
  <ion-content overflow-scroll="true" id="iframe-ion-content"
18
18
  [ngClass]="{'ion-content-black-background' : isProjectsForPanel === true}">
19
- <!-- <iframe id="i_frame" style="width:100%; height:99%" frameBorder="0" allowfullscreen [src]="unassigned_convs_url_sanitized"></iframe> -->
20
- <div class="loader-spinner-wpr">
19
+ <div *ngIf="callerBtn === 'pinbtn'" class="loader-spinner-wpr">
21
20
  <div id="loader" class="loader">
22
21
  <svg class="circular" viewBox="25 25 50 50">
23
22
  <circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10" />
24
23
  </svg>
25
24
  </div>
26
25
  </div>
26
+
27
+ <div *ngIf="callerBtn !== 'pinbtn'" class="list-avatar-page">
28
+ <ion-list>
29
+ <ng-container *ngIf="unassignedConversationsList.length > 0; else noConvs">
30
+ <ion-list-conversations
31
+ [listConversations]="unassignedConversationsList"
32
+ [stylesMap]="stylesMap"
33
+ [translationMap]="translationMapConversation"
34
+ [uidConvSelected]="uidConvSelected"
35
+ (onConversationSelected)="onConversationSelected($event)"
36
+ (onCloseConversation)="onCloseConversation($event)"
37
+ (onJoinConversation)="onJoinConversation($event)"
38
+ (onImageLoaded)="onImageLoaded($event)"
39
+ (onConversationLoaded)="onConversationLoaded($event)">
40
+ </ion-list-conversations>
41
+ </ng-container>
42
+ <ng-template #noConvs>
43
+ <div class="no-convs-container">
44
+ <ion-item id="no-convs" class="ion-text-center" lines="none">
45
+ <ion-label class="ion-text-wrap" color="medium">
46
+ {{ 'LABEL_MSG_PUSH_START_CHAT' | translate }}
47
+ </ion-label>
48
+ </ion-item>
49
+ </div>
50
+ </ng-template>
51
+ </ion-list>
52
+ </div>
27
53
  </ion-content>
@@ -1,5 +1,6 @@
1
1
  ion-toolbar {
2
2
  height: var(--header-height);
3
+ --background: transparent;
3
4
  &:not(.mobile){
4
5
  --background: var(--list-bkg-color);
5
6
  border: none;
@@ -20,7 +21,7 @@ ion-content {
20
21
  // Forcing `--overflow: hidden` breaks scrolling (Chrome >= 144 is stricter here).
21
22
  --overflow: auto;
22
23
  &:not(.mobile){
23
- --background: var(--list-bkg-color);
24
+ --background: var(--bacis-white);
24
25
  }
25
26
  }
26
27
 
@@ -224,3 +225,26 @@ ion-content::part(scroll) {
224
225
  .hide-stretchspinner {
225
226
  display: none;
226
227
  }
228
+
229
+ // -------------------------------------------------
230
+ // List avatar page - full viewport height
231
+ // -------------------------------------------------
232
+ .list-avatar-page {
233
+ min-height: calc(100vh - var(--header-height, 56px));
234
+ height: 100%;
235
+ }
236
+
237
+ // -------------------------------------------------
238
+ // Empty state - label centrata nell'altezza disponibile
239
+ // -------------------------------------------------
240
+ .no-convs-container {
241
+ display: flex;
242
+ align-items: center;
243
+ justify-content: center;
244
+ min-height: calc(100vh - var(--header-height, 56px));
245
+ width: 100%;
246
+
247
+ ion-item {
248
+ --background: transparent;
249
+ }
250
+ }