@chat21/chat21-ionic 3.4.28 → 3.4.29-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/CHANGELOG.md +111 -5
  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 +71 -10
  6. package/src/app/components/canned-response/canned-response.component.html +26 -23
  7. package/src/app/components/canned-response/canned-response.component.scss +0 -2
  8. package/src/app/components/canned-response/canned-response.component.ts +3 -1
  9. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.html +24 -1
  10. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.scss +30 -0
  11. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.ts +29 -7
  12. package/src/app/components/conversation-info/info-content/info-content.component.ts +2 -2
  13. package/src/app/components/conversation-info/info-group/info-group.component.ts +23 -21
  14. package/src/app/components/conversations-list/header-conversations-list/header-conversations-list.component.html +1 -1
  15. package/src/app/components/conversations-list/header-conversations-list/header-conversations-list.component.ts +5 -1
  16. package/src/app/components/navbar/navbar.component.html +3 -3
  17. package/src/app/components/navbar/navbar.component.ts +29 -38
  18. package/src/app/components/project-item/project-item.component.ts +11 -11
  19. package/src/app/components/sidebar/sidebar.component.html +65 -45
  20. package/src/app/components/sidebar/sidebar.component.ts +110 -117
  21. package/src/app/components/sidebar-user-details/sidebar-user-details.component.html +2 -2
  22. package/src/app/components/sidebar-user-details/sidebar-user-details.component.ts +10 -7
  23. package/src/app/modals/create-ticket/create-ticket.page.ts +4 -2
  24. package/src/app/pages/contacts-directory/contacts-directory.page.scss +30 -25
  25. package/src/app/pages/conversation-detail/conversation-detail.page.html +7 -3
  26. package/src/app/pages/conversation-detail/conversation-detail.page.ts +89 -5
  27. package/src/app/pages/conversations-list/conversations-list.page.html +2 -0
  28. package/src/app/pages/conversations-list/conversations-list.page.scss +15 -4
  29. package/src/app/pages/conversations-list/conversations-list.page.ts +40 -2
  30. package/src/app/pages/unassigned-conversations/unassigned-conversations.page.scss +15 -5
  31. package/src/app/services/global-settings/global-settings.service.ts +11 -3
  32. package/src/app/services/nav-proxy.service.ts +0 -1
  33. package/src/app/services/project_users/project-users.service.spec.ts +16 -0
  34. package/src/app/services/project_users/project-users.service.ts +63 -0
  35. package/src/app/services/tiledesk/tiledesk.service.ts +0 -16
  36. package/src/app/services/triggerEvents/triggerEvents.ts +28 -0
  37. package/src/app/services/websocket/websocket-js.ts +59 -534
  38. package/src/app/services/websocket/websocket-js_old.ts +578 -0
  39. package/src/app/services/websocket/websocket.service.ts +9 -10
  40. package/src/app/services/websocket/websocket.worker.ts +242 -0
  41. package/src/app/shared/shared.module.ts +11 -2
  42. package/src/app/utils/globals.ts +2 -0
  43. package/src/app/utils/permissions.constants.ts +138 -0
  44. package/src/app/utils/project-utils.ts +2 -2
  45. package/src/app/utils/utils.ts +18 -1
  46. package/src/assets/i18n/ar.json +11 -1
  47. package/src/assets/i18n/az.json +11 -1
  48. package/src/assets/i18n/de.json +11 -1
  49. package/src/assets/i18n/en.json +11 -1
  50. package/src/assets/i18n/es.json +11 -1
  51. package/src/assets/i18n/fr.json +11 -1
  52. package/src/assets/i18n/it.json +13 -3
  53. package/src/assets/i18n/kk.json +11 -1
  54. package/src/assets/i18n/pt.json +11 -1
  55. package/src/assets/i18n/ru.json +11 -1
  56. package/src/assets/i18n/sr.json +11 -1
  57. package/src/assets/i18n/sv.json +11 -1
  58. package/src/assets/i18n/tr.json +11 -1
  59. package/src/assets/i18n/uk.json +11 -1
  60. package/src/assets/i18n/uz.json +12 -1
  61. package/src/assets/js/agentDesktop-sdk.js +55 -0
  62. package/src/assets/js/chat21client.js +36 -0
  63. package/src/assets/js/mqtt-keepalive-worker.js +53 -0
  64. package/src/assets/test.html +5 -2
  65. package/src/chat-config-template.json +1 -0
  66. package/src/chat-config.json +1 -0
  67. package/src/chat21-core/models/projectUsers.ts +19 -0
  68. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +1 -1
  69. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +1 -1
  70. package/src/chat21-core/providers/tiledesk/tiledesk-auth.service.ts +3 -0
  71. package/src/chat21-core/utils/utils.ts +16 -2
package/CHANGELOG.md CHANGED
@@ -8,11 +8,98 @@
8
8
  ### **Copyrigth**:
9
9
  *Tiledesk SRL*
10
10
 
11
- # 3.4.28 in PROD
11
+ # 3.4.29-rc1
12
+ - **bug-fixed**: web (Chrome >= 144) `ion-content` stopped scrolling on some pages (conversation list / contacts directory / unassigned); removed the forced `--overflow: hidden` and handled scrolling on Ionic’s internal scroll container via `ion-content::part(scroll)`
13
+
14
+ # 3.4.28-rc1
12
15
  - **bug-fixed**: cannot do project subscription if last_project object is not a project_user obj
13
16
 
14
- # 3.4.27 in PROD
15
- - **bug-fixed**: cannot find route if userFullname contains /
17
+ # 3.4.27-rc26
18
+ - **bug-fixed**: wss push requests twice
19
+
20
+ # 3.4.27-rc25
21
+ - **bug-fixed**: projectUserService is not initialized
22
+
23
+ # 3.4.27-rc24
24
+ - **added**: implementation of multiple message in wss onmessage
25
+
26
+ # 3.4.27-rc23
27
+ - **added**: keepAlive worker for MQTT connection
28
+
29
+ # 3.4.27-rc22
30
+ - **added**: new WsWorker to manage iframe chrome throttling while tab is in background or hidden
31
+
32
+ # 3.4.27-rc21
33
+ - **changed**: new wss reconnect and timeout keepalive
34
+ - **bug-fixed**: cannot route if senderFullaname contains /
35
+
36
+ # 3.4.27-rc20
37
+ - **added**: onOpenTicketExternally event in triggerEvents service
38
+
39
+ # 3.4.27-rc19
40
+ - **added**: window.parent['openTicketOnHDA']
41
+
42
+ # 3.4.27-rc18
43
+ - **added**: triggerOnUpdateNewConversationBadge to update conversation badge count in parent component
44
+
45
+ # 3.4.27-rc17
46
+ - **bug-fixed**: setNotification not called when click on a conversation
47
+
48
+ # 3.4.27-rc16
49
+ - **bug-fixed**: setNotification not called when resolve a conversation
50
+
51
+ # 3.4.27-rc15
52
+ - **changed**: /images with /img in assets folder
53
+
54
+ # 3.4.27-rc14
55
+ - **added**: DISPLAY_EDIT_PROFILE brand variable
56
+ - **bug-fixed**: emojii is sent also if is not allowed
57
+
58
+ # 3.4.27-rc13
59
+ - **added**: ability to mantain logout parameter when redirect to dashboard urls from sidebar component
60
+
61
+ # 3.4.27-rc12
62
+ - **added**: ability to manage logOut option in sidebar-user-detail with tiledesk_logOut url query params
63
+
64
+ # 3.4.27-rc11
65
+ - **bug-fixed**: fixed infinite loading in contact list
66
+
67
+ # 3.4.27-rc10
68
+ - **added**: ability to manage header-conversation-list with roles
69
+ - **bug-fixed**: members in group list not loaded
70
+
71
+ # 3.4.27-rc9
72
+ - **bug-fixed**: Scrolling to the last message when opening a conversation
73
+ - **bug-fixed**: Loading in the conversation list disabled when removing the last conversation
74
+
75
+ # 3.4.27-rc8
76
+ - **added**: ability to open ticket to external service
77
+ - **added**: ticketSection env var
78
+
79
+ # 3.4.27-rc7
80
+ - **bug-fixed**: Scrolling to the last message when opening a conversation
81
+ - **bug-fixed**: Loading in the conversation list disabled when removing the last conversation
82
+
83
+ # 3.4.27-rc6
84
+ - **bug-fixed**: user for dashboard app is incorrect
85
+
86
+ # 3.4.27-rc5
87
+ - **added**: managed roles in sidebar e navbar
88
+ - **bug-fixed**: projectId and supportMode url is not saved in localstorage
89
+
90
+ # 3.4.27-rc4
91
+ - **bug-fixed**: extractUrls function is not able to detect url start with www or without https/http
92
+ - **bug-fixed**: if message is sent with keydown, error on domain check is not showed
93
+
94
+ # 3.4.27-rc3
95
+ - **bug-fixed**: cannot set user availability if supportMode is enabled and tiledesk_projectID url params is set
96
+
97
+ # 3.4.27-rc2
98
+ - **bug-fixed**: cannede responses role
99
+
100
+ # 3.4.27-rc1
101
+ - **added**: managed canned responses with roles
102
+ - **changed**: name in info mesage
16
103
 
17
104
  # 3.4.26 in PROD
18
105
 
@@ -22,6 +109,7 @@
22
109
  # 3.4.26-rc1
23
110
  - **added**: tiledesk_projectID query param to manage user status
24
111
  - **added**: token to managane ticket feature
112
+ - **added**: getOsCode login into utils.ts
25
113
 
26
114
  # 3.4.25 in PROD
27
115
  - **changed**: pipe marked to support malicious text input
@@ -44,6 +132,24 @@
44
132
  # 3.4.22 in PROD
45
133
  - **added**: managed allowed_upload_extentions from project settings
46
134
 
135
+ # 3.4.21-rc6
136
+ - **added**: managed allowed_upload_extentions from project settings
137
+
138
+ # 3.4.21-rc5
139
+ - **added**: setConversation as read when agent click on it
140
+
141
+ # 3.4.21-rc4
142
+ - **added**: ability to init and decrement new conversation count badge
143
+
144
+ # 3.4.21-rc3
145
+ - **changed**: badge notification for agentDesktop
146
+
147
+ # 3.4.21-rc2
148
+ - **added**: count in newConversation handler event
149
+
150
+ # 3.4.21-rc1
151
+ - **added**: implement badge notification for agentDesktop sw when new conversation is assigned to logged agent
152
+
47
153
  # 3.4.21 in PROD
48
154
 
49
155
  # 3.4.20 in PROD
@@ -57,8 +163,8 @@
57
163
  - **bug-fixed**: minor fix on ion-texarea element with allowed url domain
58
164
 
59
165
  # 3.4.19-rc1
60
- - added: ability to check for if emoji is allowd to be sent in message textarea
61
- - added: ability to check for if url domain is allowd to be sent in message textarea
166
+ - **added**: ability to check for if emoji is allowd to be sent in message textarea
167
+ - **added**: ability to check for if url domain is allowd to be sent in message textarea
62
168
 
63
169
  # 3.4.18 in PROD
64
170
 
package/angular.json CHANGED
@@ -32,6 +32,7 @@
32
32
  "src/chat-config-template.json",
33
33
  "src/chat-config.json",
34
34
  "src/chat-config-dev.json",
35
+ "src/chat-config-native-ar.json",
35
36
  {
36
37
  "glob": "**/*",
37
38
  "input": "src/assets",
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.28",
4
+ "version": "3.4.29-rc1",
5
5
  "license": "MIT License",
6
6
  "homepage": "https://tiledesk.com/",
7
7
  "repository": {
@@ -21,7 +21,9 @@
21
21
  </div> -->
22
22
 
23
23
  <div class="user-details-sidebar" [ngClass]="{'hide-sidebar': IS_ONLINE === false || IS_ON_MOBILE_DEVICE || SUPPORT_MODE === false}">
24
- <app-sidebar-user-details> </app-sidebar-user-details>
24
+ <app-sidebar-user-details
25
+ [logOut]="g?.logOut">
26
+ </app-sidebar-user-details>
25
27
  </div>
26
28
 
27
29
  <ion-split-pane when="md" contentId="main" [ngClass]="{'mobile': IS_ON_MOBILE_DEVICE, 'sidebar-hidden': IS_ON_MOBILE_DEVICE || SUPPORT_MODE === false}">
@@ -44,6 +44,9 @@ import { conversationToMessage } from 'src/chat21-core/utils/utils-message';
44
44
  import { ProjectService } from './services/projects/project.service';
45
45
  import { ContactsService } from './services/contacts/contacts.service';
46
46
  import { TiledeskService } from './services/tiledesk/tiledesk.service';
47
+ import { Project } from 'src/chat21-core/models/projects';
48
+ import { BRAND_BASE_INFO } from './utils/utils-resources';
49
+ import { ProjectUsersService } from './services/project_users/project-users.service';
47
50
 
48
51
  @Component({
49
52
  selector: 'app-root',
@@ -142,6 +145,7 @@ export class AppComponent implements OnInit {
142
145
  /**TILEDESK SERVICES */
143
146
  private tiledeskService: TiledeskService,
144
147
  private projectService: ProjectService,
148
+ private projectUsersService: ProjectUsersService,
145
149
  private contactsService: ContactsService
146
150
  ) {
147
151
 
@@ -168,6 +172,7 @@ export class AppComponent implements OnInit {
168
172
  }, { capture: true });
169
173
  }
170
174
 
175
+
171
176
  listenChatAlreadyOpenWithoutParamsInMobileMode() {
172
177
  this.events.subscribe('noparams:mobile', (isAlreadyOpenInMobileMode) => {
173
178
  // console.log('[APP-COMP] Chat is Already Open In Mobile Mode ', isAlreadyOpenInMobileMode)
@@ -296,6 +301,10 @@ export class AppComponent implements OnInit {
296
301
  this.zone = new NgZone({}); // a cosa serve?
297
302
 
298
303
  this.SUPPORT_MODE = this.g.supportMode
304
+ this.logger.info('[APP-COMP] this.SUPPORT_MODE', this.SUPPORT_MODE)
305
+
306
+ BRAND_BASE_INFO['LOGOUT_ENABLED'] = this.g.logOut
307
+ this.logger.info('[APP-COMP] this.logOut', BRAND_BASE_INFO['LOGOUT_ENABLED'])
299
308
  }
300
309
 
301
310
  });
@@ -332,7 +341,7 @@ export class AppComponent implements OnInit {
332
341
 
333
342
  listenToPostMsgs() {
334
343
  window.addEventListener("message", (event) => {
335
- this.logger.log("[APP-COMP] message event ", event);
344
+ // this.logger.log("[APP-COMP] message event ", event);
336
345
 
337
346
  if (event && event.data && event.data.action && event.data.parameter) {
338
347
  if (event.data.action === 'openJoinConversationModal') {
@@ -534,10 +543,10 @@ export class AppComponent implements OnInit {
534
543
  this.statusBar.styleLightContent();
535
544
  this.navService.init(this.sidebarNav, this.detailNav);
536
545
  this.tiledeskAuthService.initialize(this.appConfigProvider.getConfig().apiUrl);
537
- this.messagingAuthService.initialize();
538
-
546
+
539
547
  // this.currentUserService.initialize();
540
548
  this.chatManager.initialize();
549
+ this.messagingAuthService.initialize();
541
550
  this.presenceService.initialize(this.tenant);
542
551
  this.typingService.initialize(this.tenant);
543
552
 
@@ -864,7 +873,8 @@ export class AppComponent implements OnInit {
864
873
  // console.log('[APP-COMP] PLATFORM', PLATFORM_MOBILE, 'route.snapshot', this.route.snapshot);
865
874
  if (!IDConv) {
866
875
  this.logger.log('[APP-COMP] navigateByUrl -- conversations-list');
867
- this.router.navigateByUrl('conversations-list')
876
+ const queryString = window.location.search; // restituisce ad es. "?jwt=...&tiledesk_supportMode=false"
877
+ this.router.navigateByUrl('conversations-list' + queryString);
868
878
  }
869
879
  // this.router.navigateByUrl(pageUrl);
870
880
  // this.navService.setRoot(ConversationListPage, {});
@@ -1115,13 +1125,19 @@ export class AppComponent implements OnInit {
1115
1125
  if (conversation && conversation.is_new === true && this.isInitialized) {
1116
1126
  this.manageTabNotification('conv_added', conversation.sound)
1117
1127
  this.manageEventNewConversation(conversation)
1128
+ //UPDATE NOTIFICATION FOR NEW CONVERSATION COUNT
1129
+ this.triggerOnUpdateNewConversationBadge(this.conversationsHandlerService.countIsNew());
1118
1130
  }
1119
1131
  if(conversation) this.updateConversationsOnStorage()
1120
1132
  });
1121
1133
 
1122
1134
  this.conversationsHandlerService.conversationChanged.subscribe((conversation: ConversationModel) => {
1123
1135
  // console.log('[APP-COMP] ***** subscribeConversationChanged conversation: ', conversation);
1124
- if(conversation) this.updateConversationsOnStorage();
1136
+ if(conversation){
1137
+ this.updateConversationsOnStorage();
1138
+ //UPDATE NOTIFICATION FOR NEW CONVERSATION COUNT
1139
+ this.triggerOnUpdateNewConversationBadge(this.conversationsHandlerService.countIsNew());
1140
+ }
1125
1141
  });
1126
1142
 
1127
1143
  this.conversationsHandlerService.conversationChangedDetailed.subscribe((changes: {value: ConversationModel, previousValue: ConversationModel}) => {
@@ -1145,6 +1161,8 @@ export class AppComponent implements OnInit {
1145
1161
  if(conversation) {
1146
1162
  this.updateConversationsOnStorage();
1147
1163
  this.segmentResolved(conversation);
1164
+ //UPDATE NOTIFICATION FOR NEW CONVERSATION COUNT
1165
+ this.triggerOnUpdateNewConversationBadge(this.conversationsHandlerService.countIsNew());
1148
1166
  this.router.navigateByUrl('conversation-detail/'); //redirect to basePage
1149
1167
  }
1150
1168
  });
@@ -1178,6 +1196,7 @@ export class AppComponent implements OnInit {
1178
1196
  this.chatManager.setCurrentUser(currentUser);
1179
1197
 
1180
1198
  this.tiledeskService.initialize(serverBaseURL)
1199
+ this.projectUsersService.initialize(serverBaseURL)
1181
1200
  this.projectService.initialize(serverBaseURL)
1182
1201
  this.contactsService.initialize(serverBaseURL)
1183
1202
 
@@ -1185,11 +1204,14 @@ export class AppComponent implements OnInit {
1185
1204
  this.events.publish('go:online', true);
1186
1205
  // this.chatManager.startApp();
1187
1206
 
1207
+
1208
+ //INIT WEBSOCKET
1209
+ this.connetWebsocket(tiledeskToken)
1210
+
1188
1211
  // ----------------------------------------------
1189
1212
  // PUSH NOTIFICATIONS
1190
1213
  // ----------------------------------------------
1191
1214
  const pushEngine = this.appConfigProvider.getConfig().pushEngine
1192
-
1193
1215
  if (currentUser) {
1194
1216
  if (pushEngine && pushEngine !== 'none') {
1195
1217
  this.notificationsService.getNotificationPermissionAndSaveToken(currentUser.uid);
@@ -1211,6 +1233,24 @@ export class AppComponent implements OnInit {
1211
1233
  } catch (err) {
1212
1234
  this.logger.error('[APP-COMP] -> error:', err);
1213
1235
  }
1236
+
1237
+ // ----------------------------------------------
1238
+ // LAST PROJECT FROM URL
1239
+ // ----------------------------------------------
1240
+ if(this.g.projectID){
1241
+ this.projectService.getProjects().subscribe({ next: (projects: Project[]) => {
1242
+ const project = projects.find(prjct => prjct.id_project._id === this.g.projectID)
1243
+ if(project){
1244
+ this.logger.log('[APP-COMP] - GET PROJECT - project found with this.projectID', project);
1245
+ localStorage.setItem('last_project', JSON.stringify(project))
1246
+ this.events.publish('storage:last_project', project)
1247
+ }
1248
+ }, error: (error) => {
1249
+ this.logger.log('[APP-COMP] - GET PROJECT - project NOT found with this.projectID', this.g.projectID, error);
1250
+ }, complete: () => {
1251
+
1252
+ }});
1253
+ }
1214
1254
  }
1215
1255
 
1216
1256
 
@@ -1251,9 +1291,21 @@ export class AppComponent implements OnInit {
1251
1291
  }
1252
1292
 
1253
1293
  goToDashboardLogin(){
1254
- let DASHBOARD_URL = this.appConfigProvider.getConfig().dashboardUrl + '#/login'
1255
- const myWindow = window.open(DASHBOARD_URL, '_self');
1256
- myWindow.focus();
1294
+ // let DASHBOARD_URL = this.appConfigProvider.getConfig().dashboardUrl + '#/login'
1295
+ // const myWindow = window.open(DASHBOARD_URL, '_self');
1296
+ // myWindow.focus();
1297
+ }
1298
+
1299
+ connetWebsocket(tiledeskToken) {
1300
+
1301
+ this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] tiledeskToken ', tiledeskToken)
1302
+ const appconfig = this.appConfigProvider.getConfig();
1303
+ this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', appconfig.wsUrl)
1304
+ const WS_URL = appconfig.wsUrl + '?token=' + tiledeskToken
1305
+ this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', WS_URL)
1306
+ this.webSocketJs.init(
1307
+ WS_URL
1308
+ );
1257
1309
  }
1258
1310
 
1259
1311
 
@@ -1352,7 +1404,8 @@ export class AppComponent implements OnInit {
1352
1404
 
1353
1405
  subscribeConversationSelected= (conversation: ConversationModel) => {
1354
1406
  if(conversation && conversation.is_new){
1355
- this.audio_NewConv.pause()
1407
+ this.audio_NewConv.pause();
1408
+ this.conversationsHandlerService.setConversationRead(conversation.uid)
1356
1409
  }
1357
1410
  }
1358
1411
 
@@ -1428,6 +1481,9 @@ export class AppComponent implements OnInit {
1428
1481
  this.logger.debug('[APP-COMP]-CONVS - INIT CONV CONVS 2', conversations)
1429
1482
  this.events.publish('appcompSubscribeToConvs:loadingIsActive', false);
1430
1483
  }
1484
+
1485
+ //INIT NOTIFICATION FOR NEW CONVERSATION COUNT
1486
+ this.triggerOnUpdateNewConversationBadge(this.conversationsHandlerService.countIsNew());
1431
1487
  });
1432
1488
 
1433
1489
  }
@@ -1710,6 +1766,11 @@ export class AppComponent implements OnInit {
1710
1766
  this.triggerEvents.triggerOnInit(detailOBJ)
1711
1767
  }
1712
1768
 
1769
+ private triggerOnUpdateNewConversationBadge(count: number){
1770
+ const detailOBJ = { event: 'onUpdateNewConversationBadge', count: count.toString() }
1771
+ this.triggerEvents.triggerOnUpdateNewConversationBadge(detailOBJ)
1772
+ }
1773
+
1713
1774
 
1714
1775
  // @HostListener('mouseenter', ['$event'])
1715
1776
  // onMouseEnter(event: any) {
@@ -1,20 +1,30 @@
1
1
  <div>
2
- <div class="canned-list" *ngIf="tagsCannedFilter.length > 0">
3
- <ion-item button="true" [ngClass]="{'is_active_item': i == arrowkeyLocation}" lines="none"
4
- class="canned-item no-ripple border" id="{{'canned-item_'+ i }}"
5
- *ngFor="let canned of tagsCannedFilter; let i = index;"
6
- (click)="onClickCannedFN(canned, $event)">
7
- <div class="cannedContent">
8
- <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.title" class="title" id="{{'titleCanned_'+canned._id}}" #title></ion-input>
9
- <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.text" class="text truncate"></ion-input>
2
+ <div class="canned-list" *ngIf="!showLoading">
3
+ <span *ngIf="tagsCannedFilter.length > 0">
4
+ <ion-item button="true" [ngClass]="{'is_active_item': i == arrowkeyLocation}" lines="none"
5
+ class="canned-item no-ripple border" id="{{'canned-item_'+ i }}"
6
+ *ngFor="let canned of tagsCannedFilter; let i = index;"
7
+ (click)="onClickCannedFN(canned, $event)">
8
+ <div class="cannedContent">
9
+ <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.title" class="title" id="{{'titleCanned_'+canned._id}}" #title></ion-input>
10
+ <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.text" class="text truncate"></ion-input>
11
+ </div>
12
+ <!-- <ion-icon class="canned-item-icon" name="pin" src="assets/img/pin.svg" slot=end *ngIf="canned.pinned" (click)="onPinCanned(canned, $event)"></ion-icon>
13
+ <ion-icon class="canned-item-icon" name="pin" src="assets/img/pinned.svg" slot=end (click)="onUnPinCanned(canned, $event)"></ion-icon> -->
14
+ <ion-icon class="canned-item-icon" name="checkmark-sharp" slot=end *ngIf="(canned.createdBy === loggedUser.uid && !canned.disabled) && roles[PERMISSIONS.CANNED_RESPONSES_UPDATE]" (click)="onConfirmEditCanned(canned, $event)"></ion-icon>
15
+ <ion-icon class="canned-item-icon" name="pencil-sharp" slot=end *ngIf="(canned.createdBy === loggedUser.uid && canned.disabled) && roles[PERMISSIONS.CANNED_RESPONSES_UPDATE]" (click)="onEditCanned(canned, $event)"></ion-icon>
16
+ <ion-icon class="canned-item-icon" name="trash-bin-outline" slot=end *ngIf="(canned.createdBy === loggedUser.uid) && roles[PERMISSIONS.CANNED_RESPONSES_DELETE]" (click)="onDeleteCanned(canned, $event)"></ion-icon>
17
+ </ion-item>
18
+ </span>
19
+ <div class="no-data" *ngIf="tagsCannedFilter.length === 0">
20
+ <div class="container">
21
+ <ion-item button="false" lines="none" class="canned-item no-ripple border">
22
+ <ion-icon name="cloud-offline" slot="start"></ion-icon>
23
+ <ion-label>{{translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE')}}</ion-label>
24
+ </ion-item>
10
25
  </div>
11
- <!-- <ion-icon class="canned-item-icon" name="pin" src="assets/img/pin.svg" slot=end *ngIf="canned.pinned" (click)="onPinCanned(canned, $event)"></ion-icon>
12
- <ion-icon class="canned-item-icon" name="pin" src="assets/img/pinned.svg" slot=end (click)="onUnPinCanned(canned, $event)"></ion-icon> -->
13
- <ion-icon class="canned-item-icon" name="checkmark-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && !canned.disabled" (click)="onConfirmEditCanned(canned, $event)"></ion-icon>
14
- <ion-icon class="canned-item-icon" name="pencil-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && canned.disabled" (click)="onEditCanned(canned, $event)"></ion-icon>
15
- <ion-icon class="canned-item-icon" name="trash-bin-outline" slot=end *ngIf="canned.createdBy === loggedUser.uid" (click)="onDeleteCanned(canned, $event)"></ion-icon>
16
- </ion-item>
17
- <ion-item class="canned-item add-canned-response-wpr" button="true" lines="none" (click)="onClickAddCannedResponseFN()">
26
+ </div>
27
+ <ion-item *ngIf="roles[PERMISSIONS.CANNED_RESPONSES_CREATE]" class="canned-item add-canned-response-wpr" button="true" lines="none" (click)="onClickAddCannedResponseFN()">
18
28
  <ion-icon class="add-canned-response-icon" name="flash-outline"></ion-icon>
19
29
  <span class="add-canned-response-add-icon">+</span>
20
30
  <label class="add-canned-response-label" >{{translationMap?.get('AddNewCannedResponse')}}</label>
@@ -33,12 +43,5 @@
33
43
  <div class="label">{{translationMap.get('LABEL_LOADING')}}</div>
34
44
  </div>
35
45
  </div>
36
- <div class="no-data" *ngIf="tagsCannedFilter.length === 0 && !showLoading">
37
- <div class="container">
38
- <ion-item button="false" lines="none" class="canned-item no-ripple border">
39
- <ion-icon name="cloud-offline" slot="start"></ion-icon>
40
- <ion-label>{{translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE')}}</ion-label>
41
- </ion-item>
42
- </div>
43
- </div>
46
+
44
47
  </div>
@@ -278,7 +278,5 @@ ion-item {
278
278
  display: flex;
279
279
  justify-content: center;
280
280
  flex-direction: column;
281
- align-items: center;
282
-
283
281
  }
284
282
  }
@@ -9,6 +9,7 @@ import { TiledeskAuthService } from 'src/chat21-core/providers/tiledesk/tiledesk
9
9
  import { compareValues, htmlEntities } from 'src/chat21-core/utils/utils';
10
10
  import { getProjectIdSelectedConversation } from 'src/chat21-core/utils/utils-message';
11
11
  import { PLAN_NAME } from 'src/chat21-core/utils/constants';
12
+ import { PERMISSIONS } from 'src/app/utils/permissions.constants';
12
13
 
13
14
  @Component({
14
15
  selector: 'app-canned-response',
@@ -21,7 +22,7 @@ export class CannedResponseComponent implements OnInit {
21
22
  @Input() conversationWith: string;
22
23
  @Input() conversationWithFullname: string;
23
24
  @Input() currentString: string;
24
- @Input() canShowCanned: boolean = true;
25
+ @Input() roles: Array<string>;
25
26
  @Input() stylesMap: Map<string, string>;
26
27
  @Input() translationMap: Map<string, string>;
27
28
  @Output() onLoadedCannedResponses = new EventEmitter<[any]>();
@@ -36,6 +37,7 @@ export class CannedResponseComponent implements OnInit {
36
37
 
37
38
  public arrowkeyLocation = -1
38
39
 
40
+ PERMISSIONS = PERMISSIONS
39
41
 
40
42
  private logger: LoggerService = LoggerInstance.getInstance();
41
43
  constructor(
@@ -22,6 +22,13 @@
22
22
  {{translationMap?.get('WHATSAPP.LABEL_TEMPLATES')}}
23
23
  </ion-button>
24
24
  </div>
25
+ <!-- OPEN TICKET -->
26
+ <div *ngIf="ticketSection" class="section-option" id="template" tooltip="{{translationMap?.get('TICKET.OPEN_TICKET')}}" placement="top">
27
+ <ion-button fill="clear" [class.active]="section==='ticket'" (click)="onOpenSection('ticket')" [disabled]="channelType === 'direct'">
28
+ <ion-icon name="ticket"></ion-icon>
29
+ {{translationMap?.get('TICKET.OPEN_TICKET')}}
30
+ </ion-button>
31
+ </div>
25
32
  </div>
26
33
 
27
34
  <div class="footerContainerAlert">
@@ -50,6 +57,22 @@
50
57
  </ion-col>
51
58
  </ion-row>
52
59
 
60
+ <ion-row id="ticket" [style.display]="section==='ticket'? 'flex': 'none'">
61
+ <ion-col col-auto>
62
+ <div class="placeholder">{{translationMap.get('TICKET.DESCRIPTION')}}</div>
63
+ <div class="buttons-container">
64
+ <ion-button name="add" size="small" (click)="onClickTicket('open')">
65
+ <ion-icon name="add"></ion-icon>
66
+ {{translationMap?.get('TICKET.CONFIRM')}}
67
+ </ion-button>
68
+ <ion-button size="small" color="danger" (click)="onClickTicket('close')">
69
+ <ion-icon name="close"></ion-icon>
70
+ {{translationMap?.get('TICKET.CLOSE')}}
71
+ </ion-button>
72
+ </div>
73
+ </ion-col>
74
+ </ion-row>
75
+
53
76
  <ion-row id="message-text-area" [style.display]="section==='chat' || section ==='templates' || section ==='copilot'? 'flex': 'none'">
54
77
 
55
78
  <ion-col col-auto style="display: flex;">
@@ -57,7 +80,7 @@
57
80
  <div class="buttons-left">
58
81
 
59
82
  <!-- CANNED RESPONSES -->
60
- <ng-container *ngIf="areVisibleCAR && supportMode">
83
+ <ng-container *ngIf="areVisibleCAR && supportMode && cannedSection">
61
84
  <div class="canned-responses-btn-wpr" tooltip="{{translationMap?.get('CANNED_RESPONSES')}}" placement="top">
62
85
  <ion-button ion-button fill="clear" class="canned-responses-btn" (click)="openCannedResponses()"
63
86
  [disabled]="!conversationWith?.startsWith(CHANNEL_TYPE.SUPPORT_GROUP) || disableTextarea">
@@ -183,6 +183,36 @@
183
183
  }
184
184
  }
185
185
 
186
+ #ticket{
187
+ text-align: center;
188
+ font-size: 12px;
189
+
190
+ .buttons-container{
191
+ display: flex;
192
+ justify-content: center;
193
+ gap: 10px;
194
+
195
+ ion-button{
196
+ font-size: 12px;
197
+ --padding-top: 4px;
198
+ --padding-bottom: 4px;
199
+ --padding-start: 6px;
200
+ --padding-end: 6px;
201
+ --ripple-color: transparent;
202
+ text-transform: unset;
203
+ height: auto;
204
+
205
+ ion-icon{
206
+ margin-right: 4px;
207
+ }
208
+
209
+ &[name="add"]{
210
+ --background: var(--basic-blue);
211
+ }
212
+ }
213
+ }
214
+ }
215
+
186
216
  #fileInput {
187
217
  position: absolute;
188
218
  opacity: 0;
@@ -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;
@@ -288,6 +294,17 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
288
294
  this.prensentTemplateModal();
289
295
  }
290
296
 
297
+ onClickTicket(option: "open" | "close"){
298
+ this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] - onClickTicket', option);
299
+ switch(option){
300
+ case "open":
301
+ this.onOpenTicket.emit();
302
+ case "close":
303
+ this.section = 'chat'
304
+ }
305
+
306
+ }
307
+
291
308
 
292
309
  /**
293
310
  *
@@ -567,8 +584,10 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
567
584
  if (!text.includes("/")) {
568
585
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 1 message: ', message);
569
586
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 1 message: ", message);
570
- this.messageString = '';
587
+
571
588
  this.sendMessage(text);
589
+ // this.messageString = '';
590
+
572
591
  this.countClicks = 0
573
592
  } else if (text.includes("/") && pos === 0 && this.countClicks > 1 && this.tagsCannedFilter.length > 0) {
574
593
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 2: ', this.tagsCannedFilter.length);
@@ -580,9 +599,10 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
580
599
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
581
600
  this.logger.log("[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
582
601
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 2 message: ', message);
583
- this.messageString = '';
584
-
602
+
585
603
  this.sendMessage(text);
604
+ // this.messageString = '';
605
+
586
606
  this.countClicks = 0
587
607
  } else if (text.includes("/") && pos > 0 && this.countClicks > 1 && this.tagsCannedFilter.length > 0 && text.substr(-1) !== '/') {
588
608
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 3: ', this.tagsCannedFilter.length);
@@ -594,17 +614,19 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
594
614
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
595
615
  this.logger.log("[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
596
616
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 2 message: ', message);
597
- this.messageString = '';
598
-
617
+
599
618
  this.sendMessage(text);
619
+ // this.messageString = '';
620
+
600
621
  this.countClicks = 0
601
622
  } else if (text.includes("/") && this.tagsCannedFilter.length === 0) {
602
623
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 3: ', this.tagsCannedFilter.length);
603
624
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 3 message: ', message);
604
625
  this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 3 message: ", message);
605
- this.messageString = '';
606
-
626
+
607
627
  this.sendMessage(text);
628
+ // this.messageString = '';
629
+
608
630
  this.countClicks = 0
609
631
 
610
632
  }