@chat21/chat21-ionic 3.4.26 → 3.4.27-ar2
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 +35 -2
- package/package.json +1 -1
- package/src/app/app.component.ts +67 -4
- package/src/app/components/canned-response/canned-response.component.html +26 -23
- package/src/app/components/canned-response/canned-response.component.ts +3 -1
- package/src/app/components/conversation-detail/message-text-area/message-text-area.component.html +1 -1
- package/src/app/components/conversation-detail/message-text-area/message-text-area.component.ts +15 -7
- package/src/app/components/project-item/project-item.component.ts +11 -11
- package/src/app/components/sidebar-user-details/sidebar-user-details.component.html +2 -2
- package/src/app/components/sidebar-user-details/sidebar-user-details.component.ts +9 -7
- package/src/app/modals/create-ticket/create-ticket.page.ts +4 -2
- package/src/app/pages/conversation-detail/conversation-detail.page.html +4 -2
- package/src/app/pages/conversation-detail/conversation-detail.page.ts +45 -3
- package/src/app/services/global-settings/global-settings.service.ts +4 -2
- package/src/app/services/project_users/project-users.service.spec.ts +16 -0
- package/src/app/services/project_users/project-users.service.ts +63 -0
- package/src/app/services/tiledesk/tiledesk.service.ts +0 -16
- package/src/app/shared/shared.module.ts +11 -0
- package/src/app/utils/permissions.constants.ts +135 -0
- package/src/app/utils/project-utils.ts +2 -2
- package/src/app/utils/utils.ts +18 -1
- package/src/chat21-core/models/projectUsers.ts +19 -0
- package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +1 -1
- package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +1 -1
- package/src/chat21-core/utils/utils.ts +10 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,20 @@
|
|
|
8
8
|
### **Copyrigth**:
|
|
9
9
|
*Tiledesk SRL*
|
|
10
10
|
|
|
11
|
+
# 3.4.27-rc4
|
|
12
|
+
- **bug-fixed**: extractUrls function is not able to detect url start with www or without https/http
|
|
13
|
+
- **bug-fixed**: if message is sent with keydown, error on domain check is not showed
|
|
14
|
+
|
|
15
|
+
# 3.4.27-rc3
|
|
16
|
+
- **bug-fixed**: cannot set user availability if supportMode is enabled and tiledesk_projectID url params is set
|
|
17
|
+
|
|
18
|
+
# 3.4.27-rc2
|
|
19
|
+
- **bug-fixed**: cannede responses role
|
|
20
|
+
|
|
21
|
+
# 3.4.27-rc1
|
|
22
|
+
- **added**: managed canned responses with roles
|
|
23
|
+
- **changed**: name in info mesage
|
|
24
|
+
|
|
11
25
|
# 3.4.26 in PROD
|
|
12
26
|
|
|
13
27
|
# 3.4.26-rc2
|
|
@@ -16,6 +30,7 @@
|
|
|
16
30
|
# 3.4.26-rc1
|
|
17
31
|
- **added**: tiledesk_projectID query param to manage user status
|
|
18
32
|
- **added**: token to managane ticket feature
|
|
33
|
+
- **added**: getOsCode login into utils.ts
|
|
19
34
|
|
|
20
35
|
# 3.4.25 in PROD
|
|
21
36
|
- **changed**: pipe marked to support malicious text input
|
|
@@ -38,6 +53,24 @@
|
|
|
38
53
|
# 3.4.22 in PROD
|
|
39
54
|
- **added**: managed allowed_upload_extentions from project settings
|
|
40
55
|
|
|
56
|
+
# 3.4.21-rc6
|
|
57
|
+
- **added**: managed allowed_upload_extentions from project settings
|
|
58
|
+
|
|
59
|
+
# 3.4.21-rc5
|
|
60
|
+
- **added**: setConversation as read when agent click on it
|
|
61
|
+
|
|
62
|
+
# 3.4.21-rc4
|
|
63
|
+
- **added**: ability to init and decrement new conversation count badge
|
|
64
|
+
|
|
65
|
+
# 3.4.21-rc3
|
|
66
|
+
- **changed**: badge notification for agentDesktop
|
|
67
|
+
|
|
68
|
+
# 3.4.21-rc2
|
|
69
|
+
- **added**: count in newConversation handler event
|
|
70
|
+
|
|
71
|
+
# 3.4.21-rc1
|
|
72
|
+
- **added**: implement badge notification for agentDesktop sw when new conversation is assigned to logged agent
|
|
73
|
+
|
|
41
74
|
# 3.4.21 in PROD
|
|
42
75
|
|
|
43
76
|
# 3.4.20 in PROD
|
|
@@ -51,8 +84,8 @@
|
|
|
51
84
|
- **bug-fixed**: minor fix on ion-texarea element with allowed url domain
|
|
52
85
|
|
|
53
86
|
# 3.4.19-rc1
|
|
54
|
-
- added
|
|
55
|
-
- added
|
|
87
|
+
- **added**: ability to check for if emoji is allowd to be sent in message textarea
|
|
88
|
+
- **added**: ability to check for if url domain is allowd to be sent in message textarea
|
|
56
89
|
|
|
57
90
|
# 3.4.18 in PROD
|
|
58
91
|
|
package/package.json
CHANGED
package/src/app/app.component.ts
CHANGED
|
@@ -44,6 +44,8 @@ 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 { ProjectUsersService } from './services/project_users/project-users.service';
|
|
47
49
|
|
|
48
50
|
@Component({
|
|
49
51
|
selector: 'app-root',
|
|
@@ -142,6 +144,7 @@ export class AppComponent implements OnInit {
|
|
|
142
144
|
/**TILEDESK SERVICES */
|
|
143
145
|
private tiledeskService: TiledeskService,
|
|
144
146
|
private projectService: ProjectService,
|
|
147
|
+
private projectUsersService: ProjectUsersService,
|
|
145
148
|
private contactsService: ContactsService
|
|
146
149
|
) {
|
|
147
150
|
|
|
@@ -168,6 +171,7 @@ export class AppComponent implements OnInit {
|
|
|
168
171
|
}, { capture: true });
|
|
169
172
|
}
|
|
170
173
|
|
|
174
|
+
|
|
171
175
|
listenChatAlreadyOpenWithoutParamsInMobileMode() {
|
|
172
176
|
this.events.subscribe('noparams:mobile', (isAlreadyOpenInMobileMode) => {
|
|
173
177
|
// console.log('[APP-COMP] Chat is Already Open In Mobile Mode ', isAlreadyOpenInMobileMode)
|
|
@@ -296,6 +300,7 @@ export class AppComponent implements OnInit {
|
|
|
296
300
|
this.zone = new NgZone({}); // a cosa serve?
|
|
297
301
|
|
|
298
302
|
this.SUPPORT_MODE = this.g.supportMode
|
|
303
|
+
console.log('[APP-COMP] this.SUPPORT_MODE', this.SUPPORT_MODE)
|
|
299
304
|
}
|
|
300
305
|
|
|
301
306
|
});
|
|
@@ -332,7 +337,7 @@ export class AppComponent implements OnInit {
|
|
|
332
337
|
|
|
333
338
|
listenToPostMsgs() {
|
|
334
339
|
window.addEventListener("message", (event) => {
|
|
335
|
-
this.logger.log("[APP-COMP] message event ", event);
|
|
340
|
+
// this.logger.log("[APP-COMP] message event ", event);
|
|
336
341
|
|
|
337
342
|
if (event && event.data && event.data.action && event.data.parameter) {
|
|
338
343
|
if (event.data.action === 'openJoinConversationModal') {
|
|
@@ -864,7 +869,8 @@ export class AppComponent implements OnInit {
|
|
|
864
869
|
// console.log('[APP-COMP] PLATFORM', PLATFORM_MOBILE, 'route.snapshot', this.route.snapshot);
|
|
865
870
|
if (!IDConv) {
|
|
866
871
|
this.logger.log('[APP-COMP] navigateByUrl -- conversations-list');
|
|
867
|
-
|
|
872
|
+
const queryString = window.location.search; // restituisce ad es. "?jwt=...&tiledesk_supportMode=false"
|
|
873
|
+
this.router.navigateByUrl('conversations-list' + queryString);
|
|
868
874
|
}
|
|
869
875
|
// this.router.navigateByUrl(pageUrl);
|
|
870
876
|
// this.navService.setRoot(ConversationListPage, {});
|
|
@@ -888,6 +894,11 @@ export class AppComponent implements OnInit {
|
|
|
888
894
|
if (IDConv && FullNameConv) {
|
|
889
895
|
pageUrl += IDConv + '/' + FullNameConv + '/' + Convtype
|
|
890
896
|
}
|
|
897
|
+
|
|
898
|
+
const queryParams = this.route.snapshot.queryParams;
|
|
899
|
+
const queryString = new URLSearchParams(queryParams).toString();
|
|
900
|
+
pageUrl += queryString ? `?${queryString}` : '';
|
|
901
|
+
|
|
891
902
|
// replace(/\(/g, '%28').replace(/\)/g, '%29') -> used for the encoder of any round brackets
|
|
892
903
|
this.router.navigateByUrl(pageUrl.replace(/\(/g, '%28').replace(/\)/g, '%29').replace( /#/g, "%23" ));
|
|
893
904
|
|
|
@@ -1110,6 +1121,7 @@ export class AppComponent implements OnInit {
|
|
|
1110
1121
|
if (conversation && conversation.is_new === true && this.isInitialized) {
|
|
1111
1122
|
this.manageTabNotification('conv_added', conversation.sound)
|
|
1112
1123
|
this.manageEventNewConversation(conversation)
|
|
1124
|
+
this.setNotification();
|
|
1113
1125
|
}
|
|
1114
1126
|
if(conversation) this.updateConversationsOnStorage()
|
|
1115
1127
|
});
|
|
@@ -1174,14 +1186,18 @@ export class AppComponent implements OnInit {
|
|
|
1174
1186
|
|
|
1175
1187
|
this.tiledeskService.initialize(serverBaseURL)
|
|
1176
1188
|
this.projectService.initialize(serverBaseURL)
|
|
1189
|
+
this.projectUsersService.initialize(serverBaseURL)
|
|
1177
1190
|
this.contactsService.initialize(serverBaseURL)
|
|
1178
1191
|
// this.chatManager.startApp();
|
|
1179
1192
|
|
|
1193
|
+
|
|
1194
|
+
//INIT WEBSOCKET
|
|
1195
|
+
this.connetWebsocket(tiledeskToken)
|
|
1196
|
+
|
|
1180
1197
|
// ----------------------------------------------
|
|
1181
1198
|
// PUSH NOTIFICATIONS
|
|
1182
1199
|
// ----------------------------------------------
|
|
1183
1200
|
const pushEngine = this.appConfigProvider.getConfig().pushEngine
|
|
1184
|
-
|
|
1185
1201
|
if (currentUser) {
|
|
1186
1202
|
if (pushEngine && pushEngine !== 'none') {
|
|
1187
1203
|
this.notificationsService.getNotificationPermissionAndSaveToken(currentUser.uid);
|
|
@@ -1203,6 +1219,24 @@ export class AppComponent implements OnInit {
|
|
|
1203
1219
|
} catch (err) {
|
|
1204
1220
|
this.logger.error('[APP-COMP] -> error:', err);
|
|
1205
1221
|
}
|
|
1222
|
+
|
|
1223
|
+
// ----------------------------------------------
|
|
1224
|
+
// LAST PROJECT FROM URL
|
|
1225
|
+
// ----------------------------------------------
|
|
1226
|
+
if(this.g.projectID){
|
|
1227
|
+
this.projectService.getProjects().subscribe({ next: (projects: Project[]) => {
|
|
1228
|
+
const project = projects.find(prjct => prjct.id_project._id === this.g.projectID)
|
|
1229
|
+
if(project){
|
|
1230
|
+
this.logger.log('[APP-COMP] - GET PROJECT - project found with this.projectID', project);
|
|
1231
|
+
localStorage.setItem('last_project', JSON.stringify(project))
|
|
1232
|
+
this.events.publish('storage:last_project', project)
|
|
1233
|
+
}
|
|
1234
|
+
}, error: (error) => {
|
|
1235
|
+
this.logger.log('[APP-COMP] - GET PROJECT - project NOT found with this.projectID', this.g.projectID, error);
|
|
1236
|
+
}, complete: () => {
|
|
1237
|
+
|
|
1238
|
+
}});
|
|
1239
|
+
}
|
|
1206
1240
|
}
|
|
1207
1241
|
|
|
1208
1242
|
|
|
@@ -1248,6 +1282,21 @@ export class AppComponent implements OnInit {
|
|
|
1248
1282
|
myWindow.focus();
|
|
1249
1283
|
}
|
|
1250
1284
|
|
|
1285
|
+
connetWebsocket(tiledeskToken) {
|
|
1286
|
+
|
|
1287
|
+
this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] tiledeskToken ', tiledeskToken)
|
|
1288
|
+
const appconfig = this.appConfigProvider.getConfig();
|
|
1289
|
+
this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', appconfig.wsUrl)
|
|
1290
|
+
const WS_URL = appconfig.wsUrl + '?token=' + tiledeskToken
|
|
1291
|
+
this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', WS_URL)
|
|
1292
|
+
this.webSocketJs.init(
|
|
1293
|
+
WS_URL,
|
|
1294
|
+
undefined,
|
|
1295
|
+
undefined,
|
|
1296
|
+
undefined
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1251
1300
|
|
|
1252
1301
|
webSocketClose() {
|
|
1253
1302
|
this.logger.log('[APP-COMP] - GO-OFFLINE - webSocketClose');
|
|
@@ -1344,7 +1393,10 @@ export class AppComponent implements OnInit {
|
|
|
1344
1393
|
|
|
1345
1394
|
subscribeConversationSelected= (conversation: ConversationModel) => {
|
|
1346
1395
|
if(conversation && conversation.is_new){
|
|
1347
|
-
this.audio_NewConv.pause()
|
|
1396
|
+
this.audio_NewConv.pause();
|
|
1397
|
+
this.conversationsHandlerService.setConversationRead(conversation.uid)
|
|
1398
|
+
//UPDATE NOTIFICATION FOR NEW CONVERSATION COUNT
|
|
1399
|
+
this.setNotification();
|
|
1348
1400
|
}
|
|
1349
1401
|
}
|
|
1350
1402
|
|
|
@@ -1420,6 +1472,9 @@ export class AppComponent implements OnInit {
|
|
|
1420
1472
|
this.logger.debug('[APP-COMP]-CONVS - INIT CONV CONVS 2', conversations)
|
|
1421
1473
|
this.events.publish('appcompSubscribeToConvs:loadingIsActive', false);
|
|
1422
1474
|
}
|
|
1475
|
+
|
|
1476
|
+
//INIT NOTIFICATION FOR NEW CONVERSATION COUNT
|
|
1477
|
+
this.setNotification();
|
|
1423
1478
|
});
|
|
1424
1479
|
|
|
1425
1480
|
}
|
|
@@ -1636,6 +1691,14 @@ export class AppComponent implements OnInit {
|
|
|
1636
1691
|
this.triggerEvents.triggerOnNewConversationInit(conversation)
|
|
1637
1692
|
}
|
|
1638
1693
|
|
|
1694
|
+
private setNotification() {
|
|
1695
|
+
this.logger.log('[APP-COMP] setNotification for NEW CONVERSATION');
|
|
1696
|
+
if(window['AGENTDESKTOP']){
|
|
1697
|
+
this.logger.log('[APP-COMP] manageNotification AGENTDESKTOP exist', window['AGENTDESKTOP']);
|
|
1698
|
+
window['AGENTDESKTOP']['TAB'].Badge(this.conversationsHandlerService.countIsNew().toString())
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1639
1702
|
|
|
1640
1703
|
@HostListener('document:visibilitychange', [])
|
|
1641
1704
|
visibilitychange() {
|
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
<div>
|
|
2
|
-
<div class="canned-list" *ngIf="
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<
|
|
9
|
-
|
|
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/images/pin.svg" slot=end *ngIf="canned.pinned" (click)="onPinCanned(canned, $event)"></ion-icon>
|
|
13
|
+
<ion-icon class="canned-item-icon" name="pin" src="assets/images/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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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>
|
|
@@ -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()
|
|
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(
|
package/src/app/components/conversation-detail/message-text-area/message-text-area.component.html
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
<div class="buttons-left">
|
|
58
58
|
|
|
59
59
|
<!-- CANNED RESPONSES -->
|
|
60
|
-
<ng-container *ngIf="areVisibleCAR && supportMode">
|
|
60
|
+
<ng-container *ngIf="areVisibleCAR && supportMode && cannedSection">
|
|
61
61
|
<div class="canned-responses-btn-wpr" tooltip="{{translationMap?.get('CANNED_RESPONSES')}}" placement="top">
|
|
62
62
|
<ion-button ion-button fill="clear" class="canned-responses-btn" (click)="openCannedResponses()"
|
|
63
63
|
[disabled]="!conversationWith?.startsWith(CHANNEL_TYPE.SUPPORT_GROUP) || disableTextarea">
|
package/src/app/components/conversation-detail/message-text-area/message-text-area.component.ts
CHANGED
|
@@ -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;
|
|
@@ -62,6 +64,7 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
|
|
|
62
64
|
@Input() offlineMsgEmail: boolean;
|
|
63
65
|
@Input() whatsappTemplatesSection: boolean;
|
|
64
66
|
@Input() isOpenInfoConversation: boolean;
|
|
67
|
+
@Input() cannedSection: boolean;
|
|
65
68
|
@Input() stylesMap: Map<string, string>;
|
|
66
69
|
@Input() translationMap: Map<string, string>;
|
|
67
70
|
@Input() dropEvent: any;
|
|
@@ -563,8 +566,10 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
|
|
|
563
566
|
if (!text.includes("/")) {
|
|
564
567
|
this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 1 message: ', message);
|
|
565
568
|
this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 1 message: ", message);
|
|
566
|
-
|
|
569
|
+
|
|
567
570
|
this.sendMessage(text);
|
|
571
|
+
// this.messageString = '';
|
|
572
|
+
|
|
568
573
|
this.countClicks = 0
|
|
569
574
|
} else if (text.includes("/") && pos === 0 && this.countClicks > 1 && this.tagsCannedFilter.length > 0) {
|
|
570
575
|
this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 2: ', this.tagsCannedFilter.length);
|
|
@@ -576,9 +581,10 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
|
|
|
576
581
|
this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
|
|
577
582
|
this.logger.log("[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
|
|
578
583
|
this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 2 message: ', message);
|
|
579
|
-
|
|
580
|
-
|
|
584
|
+
|
|
581
585
|
this.sendMessage(text);
|
|
586
|
+
// this.messageString = '';
|
|
587
|
+
|
|
582
588
|
this.countClicks = 0
|
|
583
589
|
} else if (text.includes("/") && pos > 0 && this.countClicks > 1 && this.tagsCannedFilter.length > 0 && text.substr(-1) !== '/') {
|
|
584
590
|
this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 3: ', this.tagsCannedFilter.length);
|
|
@@ -590,17 +596,19 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
|
|
|
590
596
|
this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
|
|
591
597
|
this.logger.log("[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown in msg-texarea SEND MESSAGE 2 this.countClicks: ", this.countClicks);
|
|
592
598
|
this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 2 message: ', message);
|
|
593
|
-
|
|
594
|
-
|
|
599
|
+
|
|
595
600
|
this.sendMessage(text);
|
|
601
|
+
// this.messageString = '';
|
|
602
|
+
|
|
596
603
|
this.countClicks = 0
|
|
597
604
|
} else if (text.includes("/") && this.tagsCannedFilter.length === 0) {
|
|
598
605
|
this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - tagsCannedFilter.length 3: ', this.tagsCannedFilter.length);
|
|
599
606
|
this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] onKeydown - SEND MESSAGE 3 message: ', message);
|
|
600
607
|
this.logger.log("[CONVS-DETAIL] replaceTagInMessage onKeydown in msg-texarea SEND MESSAGE 3 message: ", message);
|
|
601
|
-
|
|
602
|
-
|
|
608
|
+
|
|
603
609
|
this.sendMessage(text);
|
|
610
|
+
// this.messageString = '';
|
|
611
|
+
|
|
604
612
|
this.countClicks = 0
|
|
605
613
|
|
|
606
614
|
}
|
|
@@ -85,17 +85,17 @@ export class ProjectItemComponent implements OnInit {
|
|
|
85
85
|
|
|
86
86
|
connetWebsocket(tiledeskToken) {
|
|
87
87
|
|
|
88
|
-
this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] tiledeskToken ', tiledeskToken)
|
|
89
|
-
const appconfig = this.appConfigProvider.getConfig();
|
|
90
|
-
this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', appconfig.wsUrl)
|
|
91
|
-
const WS_URL = appconfig.wsUrl + '?token=' + tiledeskToken
|
|
92
|
-
this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', WS_URL)
|
|
93
|
-
this.webSocketJs.init(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
);
|
|
88
|
+
// this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] tiledeskToken ', tiledeskToken)
|
|
89
|
+
// const appconfig = this.appConfigProvider.getConfig();
|
|
90
|
+
// this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', appconfig.wsUrl)
|
|
91
|
+
// const WS_URL = appconfig.wsUrl + '?token=' + tiledeskToken
|
|
92
|
+
// this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', WS_URL)
|
|
93
|
+
// this.webSocketJs.init(
|
|
94
|
+
// WS_URL,
|
|
95
|
+
// undefined,
|
|
96
|
+
// undefined,
|
|
97
|
+
// undefined
|
|
98
|
+
// );
|
|
99
99
|
|
|
100
100
|
this.getLastProjectStoredAndSubscToWSAvailabilityAndConversations();
|
|
101
101
|
}
|
|
@@ -81,11 +81,11 @@
|
|
|
81
81
|
[searchable]="false">
|
|
82
82
|
<ng-template ng-label-tmp let-item="item">
|
|
83
83
|
<img style="width: 15px;height: 15px;position: relative; top: 1px;" height="15" width="15" [src]="item?.avatar" />
|
|
84
|
-
<span id="sidebaravatar_{{item.name}}" style="text-transform: capitalize; margin-left:8px"> {{item.label}} </span>
|
|
84
|
+
<span id="sidebaravatar_{{item.name}}" style="text-transform: capitalize; margin-left:8px"> {{item.label | translate}} </span>
|
|
85
85
|
</ng-template>
|
|
86
86
|
<ng-template ng-option-tmp let-item="item" let-index="index">
|
|
87
87
|
<img style="width: 15px;height: 15px;position: relative; top: 1px;" height="15" width="15" [src]="item?.avatar" />
|
|
88
|
-
<span id="sidebaravatar_{{item.name}}" style="text-transform: capitalize; margin-left:8px"> {{item.label}} </span>
|
|
88
|
+
<span id="sidebaravatar_{{item.name}}" style="text-transform: capitalize; margin-left:8px"> {{item.label | translate}} </span>
|
|
89
89
|
</ng-template>
|
|
90
90
|
</ng-select>
|
|
91
91
|
</section>
|
|
@@ -242,9 +242,9 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
|
|
|
242
242
|
.set('SubscriptionPaymentProblem', text['SubscriptionPaymentProblem'])
|
|
243
243
|
.set('ThePlanHasExpired', text['ThePlanHasExpired'])
|
|
244
244
|
|
|
245
|
-
this.teammateStatus.forEach(element => {
|
|
246
|
-
|
|
247
|
-
});
|
|
245
|
+
// this.teammateStatus.forEach(element => {
|
|
246
|
+
// element.label = this.translationsMap.get(element.label)
|
|
247
|
+
// });
|
|
248
248
|
|
|
249
249
|
});
|
|
250
250
|
}
|
|
@@ -261,7 +261,7 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
|
|
|
261
261
|
listenToCurrentStoredProject() {
|
|
262
262
|
this.events.subscribe('storage:last_project', projectObjct => {
|
|
263
263
|
if (projectObjct && projectObjct !== 'undefined') {
|
|
264
|
-
|
|
264
|
+
this.logger.log('[SIDEBAR-USER-DETAILS] - GET STORED PROJECT ', projectObjct)
|
|
265
265
|
|
|
266
266
|
//TODO: recuperare info da root e non da id_project
|
|
267
267
|
this.project = {
|
|
@@ -284,6 +284,8 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
|
|
|
284
284
|
} else if (this.project.profile.type === 'payment' && this.project.profile.name === 'enterprise') {
|
|
285
285
|
this.getEnterprisePlanTranslation();
|
|
286
286
|
}
|
|
287
|
+
|
|
288
|
+
this.wsService.subscriptionToWsCurrentProjectUserAvailability(this.project._id, projectObjct._id);
|
|
287
289
|
}
|
|
288
290
|
})
|
|
289
291
|
|
|
@@ -329,14 +331,14 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
|
|
|
329
331
|
// this.logger.log('teammateStatus ', this.teammateStatus)
|
|
330
332
|
this.selectedStatus = this.teammateStatus[2].id;
|
|
331
333
|
this.logger.debug('[SIDEBAR-USER-DETAILS] - PROFILE_STATUS selected option', this.teammateStatus[2].name);
|
|
332
|
-
this.teammateStatus = this.teammateStatus.slice(0)
|
|
334
|
+
// this.teammateStatus = this.teammateStatus.slice(0)
|
|
333
335
|
} else if (projectUser['user_available'] === false && (projectUser['profileStatus'] === '' || !projectUser['profileStatus'])) {
|
|
334
336
|
this.selectedStatus = this.teammateStatus[1].id;
|
|
335
337
|
this.logger.debug('[SIDEBAR-USER-DETAILS] - PROFILE_STATUS selected option', this.teammateStatus[1].name);
|
|
336
|
-
this.teammateStatus = this.teammateStatus.slice(0)
|
|
338
|
+
// this.teammateStatus = this.teammateStatus.slice(0)
|
|
337
339
|
} else if (projectUser['user_available'] === true && (projectUser['profileStatus'] === '' || !projectUser['profileStatus'])) {
|
|
338
340
|
this.selectedStatus = this.teammateStatus[0].id
|
|
339
|
-
this.teammateStatus = this.teammateStatus.slice(0)
|
|
341
|
+
// this.teammateStatus = this.teammateStatus.slice(0)
|
|
340
342
|
this.logger.debug('[SIDEBAR-USER-DETAILS] - PROFILE_STATUS selected option', this.teammateStatus[0].name);
|
|
341
343
|
}
|
|
342
344
|
this.IS_BUSY = projectUser['isBusy']
|
|
@@ -9,6 +9,7 @@ import * as uuid from 'uuid';
|
|
|
9
9
|
import { EventsService } from 'src/app/services/events-service'
|
|
10
10
|
import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
|
|
11
11
|
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
12
|
+
import { ProjectUsersService } from 'src/app/services/project_users/project-users.service'
|
|
12
13
|
|
|
13
14
|
@Component({
|
|
14
15
|
selector: 'app-create-ticket',
|
|
@@ -64,6 +65,7 @@ export class CreateTicketPage implements OnInit {
|
|
|
64
65
|
logger: LoggerService = LoggerInstance.getInstance();
|
|
65
66
|
constructor(
|
|
66
67
|
public modalController: ModalController,
|
|
68
|
+
public projectUsersService: ProjectUsersService,
|
|
67
69
|
public tiledeskService: TiledeskService,
|
|
68
70
|
public appConfigProvider: AppConfigProvider,
|
|
69
71
|
public events: EventsService
|
|
@@ -104,7 +106,7 @@ export class CreateTicketPage implements OnInit {
|
|
|
104
106
|
// Create the array of the project-users and contacts displayed in the combo box "Requester"
|
|
105
107
|
// -------------------------------------------------------------------------------------------
|
|
106
108
|
getProjectUsersAndContacts(projctid: string) {
|
|
107
|
-
const projectUsers = this.
|
|
109
|
+
const projectUsers = this.projectUsersService.getProjectUsersByProjectId(projctid)
|
|
108
110
|
const leads = this.tiledeskService.getAllLeadsActiveWithLimit(projctid,10000)
|
|
109
111
|
|
|
110
112
|
zip(projectUsers, leads).subscribe(
|
|
@@ -243,7 +245,7 @@ export class CreateTicketPage implements OnInit {
|
|
|
243
245
|
// -------------------------------------------------------------------------------------------------------------------
|
|
244
246
|
getProjectUserBotsAndDepts(projctid: string) {
|
|
245
247
|
// this.loadingAssignee = true;
|
|
246
|
-
const projectUsers = this.
|
|
248
|
+
const projectUsers = this.projectUsersService.getProjectUsersByProjectId( projctid)
|
|
247
249
|
const bots = this.tiledeskService.getAllBotByProjectId(projctid)
|
|
248
250
|
const depts = this.tiledeskService.getDeptsByProjectId(projctid)
|
|
249
251
|
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
<!-- ----------------------------------------------------------- -->
|
|
174
174
|
<app-canned-response *ngIf="SHOW_CANNED_RESPONSES"
|
|
175
175
|
id="canned"
|
|
176
|
-
[
|
|
176
|
+
[roles]="rolesCanned"
|
|
177
177
|
[conversationWith]="conversationWith"
|
|
178
178
|
[conversationWithFullname]="conversationWithFullname"
|
|
179
179
|
[currentString]="messageStr"
|
|
@@ -201,7 +201,8 @@
|
|
|
201
201
|
<!-- [tagsCannedFilter]="tagsCannedFilter" -->
|
|
202
202
|
<!-- openInfoConversation {{openInfoConversation}} - isMobile {{isMobile}} -->
|
|
203
203
|
<app-message-text-area *ngIf="(openInfoConversation === false && isMobile === true) || (openInfoConversation === true && isMobile === false) || (openInfoConversation === false && isMobile === false)"
|
|
204
|
-
[loggedUser]="loggedUser"
|
|
204
|
+
[loggedUser]="loggedUser"
|
|
205
|
+
[projectUser]="projectUser"
|
|
205
206
|
[conversationWith]="conversationWith"
|
|
206
207
|
[channelType]="channelType"
|
|
207
208
|
[channel]="conversation?.attributes?.request_channel"
|
|
@@ -213,6 +214,7 @@
|
|
|
213
214
|
[fileUploadAccept]="fileUploadAccept"
|
|
214
215
|
[emailSection]="isEmailEnabled"
|
|
215
216
|
[offlineMsgEmail]="offlineMsgEmail"
|
|
217
|
+
[cannedSection]="canShowCanned"
|
|
216
218
|
[whatsappTemplatesSection]="isWhatsappTemplatesEnabled"
|
|
217
219
|
[isOpenInfoConversation]="openInfoConversation"
|
|
218
220
|
[stylesMap]="styleMap"
|
|
@@ -83,7 +83,10 @@ 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 {
|
|
86
|
+
import { ProjectUsersService } from 'src/app/services/project_users/project-users.service';
|
|
87
|
+
import { ProjectUser } from 'src/chat21-core/models/projectUsers';
|
|
88
|
+
import { getOSCode, hasRole } from 'src/app/utils/utils';
|
|
89
|
+
import { PERMISSIONS } from 'src/app/utils/permissions.constants';
|
|
87
90
|
|
|
88
91
|
@Component({
|
|
89
92
|
selector: 'app-conversation-detail',
|
|
@@ -108,6 +111,7 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
108
111
|
private subscriptions: Array<any>
|
|
109
112
|
public tenant: string;
|
|
110
113
|
public loggedUser: UserModel
|
|
114
|
+
public projectUser: ProjectUser;
|
|
111
115
|
public conversationWith: string
|
|
112
116
|
public conversationWithFullname: string
|
|
113
117
|
public messages: Array<MessageModel> = []
|
|
@@ -137,6 +141,7 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
137
141
|
public tagsCannedFilter: Array<any> = [];
|
|
138
142
|
public SHOW_CANNED_RESPONSES: boolean = false
|
|
139
143
|
public canShowCanned: boolean = true
|
|
144
|
+
public rolesCanned: { [key: string]: boolean }
|
|
140
145
|
|
|
141
146
|
public SHOW_COPILOT_SUGGESTIONS: boolean = false;
|
|
142
147
|
|
|
@@ -240,6 +245,7 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
240
245
|
public toastController: ToastController,
|
|
241
246
|
public tiledeskService: TiledeskService,
|
|
242
247
|
public projectService: ProjectService,
|
|
248
|
+
public projectUsersService: ProjectUsersService,
|
|
243
249
|
private networkService: NetworkService,
|
|
244
250
|
private events: EventsService,
|
|
245
251
|
private webSocketService: WebsocketService,
|
|
@@ -534,7 +540,6 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
534
540
|
this.logger.log('[CONVS-DETAIL] - GET PROJECTID BY CONV RECIPIENT * COMPLETE *',)
|
|
535
541
|
})
|
|
536
542
|
}else {
|
|
537
|
-
this.canShowCanned = false;
|
|
538
543
|
this.offlineMsgEmail = false;
|
|
539
544
|
}
|
|
540
545
|
|
|
@@ -545,10 +550,13 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
545
550
|
this.logger.log('[CONVS-DETAIL] - GET PROJECTID BY CONV RECIPIENT RES', project)
|
|
546
551
|
if (project) {
|
|
547
552
|
const projectId = project.id_project
|
|
548
|
-
this.
|
|
553
|
+
this.projectUser = await this.projectUsersService.getProjectUserByProjectId(project._id)
|
|
549
554
|
this.offlineMsgEmail = this.checkOfflineMsgEmailIsEnabled(project)
|
|
550
555
|
this.isCopilotEnabled = this.projectPlanUtils.checkProjectProfileFeature(project, 'copilot');
|
|
551
556
|
this.fileUploadAccept = this.checkAcceptedUploadFile(project)
|
|
557
|
+
this.rolesCanned = this.checkCannedResponsesRoles(project)
|
|
558
|
+
this.canShowCanned = this.checkCannedResponses(project)
|
|
559
|
+
this.logger.log('[CONVS-DETAIL] this.rolesCanned ', this.canShowCanned)
|
|
552
560
|
}
|
|
553
561
|
}, (error) => {
|
|
554
562
|
this.logger.error('[CONVS-DETAIL] - GET PROJECTID BY CONV RECIPIENT - ERROR ', error)
|
|
@@ -586,6 +594,40 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
586
594
|
return this.appConfigProvider.getConfig().fileUploadAccept
|
|
587
595
|
}
|
|
588
596
|
|
|
597
|
+
checkCannedResponses(project: Project): boolean {
|
|
598
|
+
let expires = this.projectPlanUtils.checkPlanIsExpired(project)
|
|
599
|
+
this.logger.log('[CONVS-DETAIL] checkCannedResponses expires ', expires)
|
|
600
|
+
if(expires){
|
|
601
|
+
return false
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
let hasRoleToShowCanned = this.rolesCanned[PERMISSIONS.CANNED_RESPONSES_READ]
|
|
605
|
+
this.logger.log('[CONVS-DETAIL] checkCannedResponses hasRoleToShowCanned ', hasRoleToShowCanned)
|
|
606
|
+
if(!hasRoleToShowCanned){
|
|
607
|
+
return false
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return true
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
checkCannedResponsesRoles(project: Project): { [key: string]: boolean } {
|
|
614
|
+
const permissionKeys = [
|
|
615
|
+
'CANNED_RESPONSES_CREATE',
|
|
616
|
+
'CANNED_RESPONSES_READ',
|
|
617
|
+
'CANNED_RESPONSES_UPDATE',
|
|
618
|
+
'CANNED_RESPONSES_DELETE',
|
|
619
|
+
] as const;
|
|
620
|
+
|
|
621
|
+
const roles: { [key: string]: boolean } = {};
|
|
622
|
+
for (const key of permissionKeys) {
|
|
623
|
+
const permission = PERMISSIONS[key];
|
|
624
|
+
roles[permission] = hasRole(this.projectUser, permission);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return roles;
|
|
628
|
+
|
|
629
|
+
}
|
|
630
|
+
|
|
589
631
|
// getProjectIdSelectedConversation(conversationWith: string): string{
|
|
590
632
|
// const conversationWith_segments = conversationWith.split('-')
|
|
591
633
|
// // Removes the last element of the array if is = to the separator
|
|
@@ -88,8 +88,8 @@ export class GlobalSettingsService {
|
|
|
88
88
|
this.logger.debug('[GLOBAL-SET] setVariableFromStorage :::::::: SET VARIABLE ---------->', Object.keys(globals));
|
|
89
89
|
for (const key of Object.keys(globals)) {
|
|
90
90
|
const val = this.appStorageService.getItem(key);
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
this.logger.debug('[GLOBAL-SET] setVariableFromStorage SET globals KEY ---------->', key);
|
|
92
|
+
this.logger.debug('[GLOBAL-SET] setVariableFromStorage SET globals VAL ---------->', val);
|
|
93
93
|
if (val && val !== null) {
|
|
94
94
|
// globals.setParameter(key, val);
|
|
95
95
|
globals[key] = stringToBoolean(val);
|
|
@@ -111,8 +111,10 @@ export class GlobalSettingsService {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
TEMP = getParameterByName(windowContext, 'tiledesk_supportMode');
|
|
114
|
+
console.log('TEMP supportMode', TEMP);
|
|
114
115
|
if (TEMP) {
|
|
115
116
|
globals.supportMode = stringToBoolean(TEMP);
|
|
117
|
+
console.log('globals.supportMode', globals.supportMode);
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
TEMP = getParameterByName(windowContext, 'tiledesk_lang');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { ProjectUsersService } from './project-users.service';
|
|
4
|
+
|
|
5
|
+
describe('ProjectUsersService', () => {
|
|
6
|
+
let service: ProjectUsersService;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
TestBed.configureTestingModule({});
|
|
10
|
+
service = TestBed.inject(ProjectUsersService);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should be created', () => {
|
|
14
|
+
expect(service).toBeTruthy();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
2
|
+
import { Injectable } from '@angular/core';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
import { map } from 'rxjs/operators';
|
|
5
|
+
import { ProjectUser } from 'src/chat21-core/models/projectUsers';
|
|
6
|
+
import { AppStorageService } from 'src/chat21-core/providers/abstract/app-storage.service';
|
|
7
|
+
import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
|
|
8
|
+
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
9
|
+
|
|
10
|
+
@Injectable({
|
|
11
|
+
providedIn: 'root'
|
|
12
|
+
})
|
|
13
|
+
export class ProjectUsersService {
|
|
14
|
+
|
|
15
|
+
private SERVER_BASE_URL: string;
|
|
16
|
+
private tiledeskToken: string;
|
|
17
|
+
|
|
18
|
+
private logger: LoggerService = LoggerInstance.getInstance();
|
|
19
|
+
constructor(
|
|
20
|
+
public http: HttpClient,
|
|
21
|
+
public appStorageService: AppStorageService
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
initialize(serverBaseUrl: string) {
|
|
25
|
+
this.logger.log('[TILEDESK-PROJECT_USERS-SERV] - initialize serverBaseUrl', serverBaseUrl);
|
|
26
|
+
this.SERVER_BASE_URL = serverBaseUrl;
|
|
27
|
+
this.tiledeskToken = this.appStorageService.getItem('tiledeskToken')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public getProjectUsersByProjectId(project_id: string): Observable<ProjectUser[]> {
|
|
31
|
+
const url = this.SERVER_BASE_URL + project_id + '/project_users/';
|
|
32
|
+
this.logger.log('[TILEDESK-SERVICE] - GET PROJECT-USER URL', url);
|
|
33
|
+
|
|
34
|
+
const httpOptions = {
|
|
35
|
+
headers: new HttpHeaders({
|
|
36
|
+
'Content-Type': 'application/json',
|
|
37
|
+
Authorization: this.tiledeskToken
|
|
38
|
+
})
|
|
39
|
+
};
|
|
40
|
+
return this.http.get(url, httpOptions).pipe(map((res: any) => {
|
|
41
|
+
this.logger.log('[TILEDESK-SERVICE] - GET PROJECT-USER RES ', res);
|
|
42
|
+
return res
|
|
43
|
+
}))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public getProjectUserByProjectId(project_id: string): Promise<ProjectUser> {
|
|
47
|
+
const url = this.SERVER_BASE_URL + project_id + '/project_users/me';
|
|
48
|
+
this.logger.log('[TILEDESK-SERVICE]- GET PROJECT-USER BY USER-ID - URL', url);
|
|
49
|
+
|
|
50
|
+
const httpOptions = {
|
|
51
|
+
headers: new HttpHeaders({
|
|
52
|
+
'Content-Type': 'application/json',
|
|
53
|
+
Authorization: this.tiledeskToken
|
|
54
|
+
})
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return this.http.get(url, httpOptions).pipe(map((res: any) => {
|
|
58
|
+
this.logger.log('[TILEDESK-SERVICE] - GET PROJECT-USER RES ', res);
|
|
59
|
+
return res[0]
|
|
60
|
+
})).toPromise();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
@@ -93,22 +93,6 @@ export class TiledeskService {
|
|
|
93
93
|
}))
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
public getProjectUsersByProjectId(project_id: string) {
|
|
97
|
-
const url = this.SERVER_BASE_URL + project_id + '/project_users/';
|
|
98
|
-
this.logger.log('[TILEDESK-SERVICE] - GET PROJECT-USER URL', url);
|
|
99
|
-
|
|
100
|
-
const httpOptions = {
|
|
101
|
-
headers: new HttpHeaders({
|
|
102
|
-
'Content-Type': 'application/json',
|
|
103
|
-
Authorization: this.tiledeskToken
|
|
104
|
-
})
|
|
105
|
-
};
|
|
106
|
-
return this.http.get(url, httpOptions).pipe(map((res: any) => {
|
|
107
|
-
this.logger.log('[TILEDESK-SERVICE] - GET PROJECT-USER RES ', res);
|
|
108
|
-
return res
|
|
109
|
-
}))
|
|
110
|
-
}
|
|
111
|
-
|
|
112
96
|
public getAllLeadsActiveWithLimit(project_id: string, limit: number) {
|
|
113
97
|
const url = this.SERVER_BASE_URL + project_id + '/leads?limit=' + limit + '&with_fullname=true';
|
|
114
98
|
this.logger.log('[TILEDESK-SERVICE] - GET ALL ACTIVE LEADS (LIMIT 10000) - URL', url);
|
|
@@ -22,6 +22,9 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
|
|
22
22
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
23
23
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
24
24
|
import { SafeHtmlPipe } from '../directives/safe-html.pipe';
|
|
25
|
+
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
|
26
|
+
import { createTranslateLoader } from 'src/chat21-core/utils/utils';
|
|
27
|
+
import { HttpClient } from '@angular/common/http';
|
|
25
28
|
|
|
26
29
|
// import { MessageTextAreaComponent } from '../components/conversation-detail/message-text-area/message-text-area.component'; // MessageTextAreaComponent is part of the declarations ConversationDetailPageModule
|
|
27
30
|
|
|
@@ -140,6 +143,14 @@ import { SafeHtmlPipe } from '../directives/safe-html.pipe';
|
|
|
140
143
|
NgSelectModule,
|
|
141
144
|
FormsModule,
|
|
142
145
|
|
|
146
|
+
TranslateModule.forChild({
|
|
147
|
+
loader: {
|
|
148
|
+
provide: TranslateLoader,
|
|
149
|
+
useFactory: (createTranslateLoader),
|
|
150
|
+
deps: [HttpClient]
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
143
154
|
],
|
|
144
155
|
schemas: [
|
|
145
156
|
CUSTOM_ELEMENTS_SCHEMA,
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
|
|
2
|
+
export const PERMISSIONS = {
|
|
3
|
+
REQUEST_READ_ALL: 'request_read_all',
|
|
4
|
+
REQUEST_READ_GROUP: 'request_read_group',
|
|
5
|
+
REQUEST_READ_MY: 'request_read_my',
|
|
6
|
+
|
|
7
|
+
INBOX_READ: 'inbox_read',
|
|
8
|
+
REQUEST_UPDATE: 'request_update',
|
|
9
|
+
REQUEST_SEND: 'request_send',
|
|
10
|
+
REQUEST_CREATE_TICKET: 'request_create_ticket',
|
|
11
|
+
REQUEST_CLOSE: 'request_close',
|
|
12
|
+
REQUEST_JOIN: 'request_join',
|
|
13
|
+
REQUEST_REOPEN: 'request_reopen',
|
|
14
|
+
REQUEST_DELETE: 'request_delete',
|
|
15
|
+
REQUEST_UPDATE_STATUS: 'request_update_status',
|
|
16
|
+
REQUEST_UPDATE_PRIORITY: 'request_update_priority',
|
|
17
|
+
REQUEST_UPDATE_FOLLOWERS: 'request_update_followers',
|
|
18
|
+
REQUEST_UPDATE_SMART_ASSIGNMENT: 'request_update_smart_assignment',
|
|
19
|
+
REQUEST_UPDATE_TAGS: 'request_update_tags',
|
|
20
|
+
REQUEST_UPDATE_NOTES: 'request_update_notes',
|
|
21
|
+
REQUEST_REASSIGN:'request_reassign',
|
|
22
|
+
REQUEST_ADD:'request_add',
|
|
23
|
+
REQUEST_LEFT: 'request_left',
|
|
24
|
+
REQUEST_TRANSCRIPT_SEND: 'request_transcript_send',
|
|
25
|
+
|
|
26
|
+
HISTORY_READ: 'history_read',
|
|
27
|
+
|
|
28
|
+
AUTOMATIONSLOG_READ: "automationslog_read",
|
|
29
|
+
AUTOMATIONSLOG_CREATE: "automationslog_create",
|
|
30
|
+
|
|
31
|
+
KB_READ: 'kb_read',
|
|
32
|
+
KB_CONTENTS_ADD:'kb_contents_add',
|
|
33
|
+
KB_CONTENT_UPDATE: 'kb_content_update',
|
|
34
|
+
KB_CONTENT_REINDEX: 'kb_content_reindex',
|
|
35
|
+
KB_CONTENT_CHECK_STATUS: 'kb_content_check_status',
|
|
36
|
+
KB_NAMESPACE_ADD:'kb_namespace_add',
|
|
37
|
+
KB_SETTINGS_EDIT:'kb_settings_edit',
|
|
38
|
+
KB_DELETE: 'kb_delete',
|
|
39
|
+
KB_CONTENTS_EXPORT: 'kb_contents_export',
|
|
40
|
+
// KB_NAMESPACE_DELETE:'kb_namespace_delete',
|
|
41
|
+
// KB_CONTENTS_DELETE:'kb_contents_delete',
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
FLOWS_READ: 'flows_read',
|
|
46
|
+
FLOW_ADD: 'flow_add',
|
|
47
|
+
FLOW_EDIT: 'flow_edit',
|
|
48
|
+
FLOW_TEST: 'flow_test',
|
|
49
|
+
FLOW_DUPLICATE: 'flow_duplicate',
|
|
50
|
+
FLOW_DELETE: 'flow_delete',
|
|
51
|
+
FLOW_SHARE: 'flow_share',
|
|
52
|
+
FLOW_EXPORT: 'flow_export',
|
|
53
|
+
FLOW_WEBHOOK_COPY:"flow_webhook_copy",
|
|
54
|
+
FLOW_WEBHOOK_EDIT:"flow_webhook_edit",
|
|
55
|
+
FLOW_WEBHOOK_DELETE:"flow_webhook_delete",
|
|
56
|
+
// FLOW_VIEW_MESSAGE_GRAPH: 'flow_view_message_graph',
|
|
57
|
+
|
|
58
|
+
LEADS_READ: 'leads_read',
|
|
59
|
+
LEAD_UPDATE: 'lead_update',
|
|
60
|
+
LEAD_RESTORE: 'lead_restore',
|
|
61
|
+
LEAD_TRASH: 'lead_trash',
|
|
62
|
+
LEAD_DELETE: 'lead_delete',
|
|
63
|
+
LEADS_EXPORT: 'leads_export',
|
|
64
|
+
LEAD_BAN: 'lead_ban',
|
|
65
|
+
LEAD_UNBAN: 'lead_unban',
|
|
66
|
+
|
|
67
|
+
ANALYTICS_READ: 'analytics_read',
|
|
68
|
+
ACTIVITIES_READ: 'activities_read',
|
|
69
|
+
|
|
70
|
+
WIDGETSETUP_READ: 'widgetsetup_read',
|
|
71
|
+
INSTALLATION_READ: 'installation_read',
|
|
72
|
+
TRANSLATIONS_READ: 'translations_read',
|
|
73
|
+
|
|
74
|
+
DEPARTMENTS_LIST_READ: 'department_list_read',
|
|
75
|
+
DEPARTMENT_DETAIL_READ: 'department_detail_read',
|
|
76
|
+
DEPARTMENT_CREATE_READ: 'department_create_read',
|
|
77
|
+
|
|
78
|
+
TEAMMATES_READ: 'teammates_read',
|
|
79
|
+
TEAMMATES_DETAILS_READ: 'teammates_detail_read',
|
|
80
|
+
TEAMMATES_CREATE: 'teammates_create',
|
|
81
|
+
ROLES_READ: 'roles_read',
|
|
82
|
+
GROUPS_READ: 'groups_read',
|
|
83
|
+
|
|
84
|
+
TEAMMATE_STATUS_UPDATE: 'teammate_status_update',
|
|
85
|
+
|
|
86
|
+
EMAIL_TICKETING_READ:'email_ticketing_read',
|
|
87
|
+
EMAIL_TICKETING_UPDATE:'email_ticketing_update',
|
|
88
|
+
|
|
89
|
+
CANNED_RESPONSES_READ:'canned_responses_read',
|
|
90
|
+
CANNED_RESPONSES_UPDATE:'canned_responses_update',
|
|
91
|
+
CANNED_RESPONSES_CREATE:'canned_responses_create',
|
|
92
|
+
CANNED_RESPONSES_DELETE:'canned_responses_delete',
|
|
93
|
+
|
|
94
|
+
TAGS_READ:'tags_read',
|
|
95
|
+
TAG_CREATE:'tag_create',
|
|
96
|
+
TAG_DELETE:'tag_delete',
|
|
97
|
+
TAG_UPDATE:'tag_update',
|
|
98
|
+
|
|
99
|
+
HOURS_READ: 'hours_read',
|
|
100
|
+
HOURS_UPDATE: 'hours_update',
|
|
101
|
+
HOURS_DELETE: 'hours_delete',
|
|
102
|
+
HOURS_CREATE: 'hours_create',
|
|
103
|
+
|
|
104
|
+
INTEGRATIONS_READ: 'integrations_read',
|
|
105
|
+
INTEGRATIONS_UPDATE: 'integrations_update',
|
|
106
|
+
|
|
107
|
+
APPS_READ:'apps_read',
|
|
108
|
+
APPS_UPDATE:'apps_update',
|
|
109
|
+
|
|
110
|
+
PROJECTSETTINGS_GENERAL_READ: 'projectsettings_general_read',
|
|
111
|
+
PROJECTSETTINGS_GENERAL_UPDATE: 'projectsettings_general_update',
|
|
112
|
+
|
|
113
|
+
PROJECTSETTINGS_SUBSCRIPTION_READ: 'projectsettings_subscription_read',
|
|
114
|
+
|
|
115
|
+
PROJECTSETTINGS_DEVELOPER_READ: 'projectsettings_developer_read',
|
|
116
|
+
PROJECTSETTINGS_DEVELOPER_UPDATE: 'projectsettings_developer_update',
|
|
117
|
+
|
|
118
|
+
PROJECTSETTINGS_SMARTASSIGNMENT_READ: 'projectsettings_smartassignment_read',
|
|
119
|
+
PROJECTSETTINGS_SMARTASSIGNMENT_UPDATE: 'projectsettings_smartassignment_update',
|
|
120
|
+
|
|
121
|
+
PROJECTSETTINGS_NOTIFICATION_READ: 'projectsettings_notification_read',
|
|
122
|
+
|
|
123
|
+
PROJECTSETTINGS_SECURITY_READ: 'projectsettings_security_read',
|
|
124
|
+
|
|
125
|
+
PROJECTSETTINGS_BANNED_READ: 'projectsettings_banned_read',
|
|
126
|
+
|
|
127
|
+
PROJECTSETTINGS_ADVANCED_READ: 'projectsettings_advanced_read',
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
ACCESS_LISTS: 'accessLists',
|
|
131
|
+
PROFILE_PAGES: 'profilePages',
|
|
132
|
+
LEAD_DATA: 'leadData',
|
|
133
|
+
IMPORT_DATA: 'importData',
|
|
134
|
+
MANAGE_TAGS: 'manageTags'
|
|
135
|
+
};
|
|
@@ -129,9 +129,9 @@ export class ProjectPlanUtils {
|
|
|
129
129
|
|
|
130
130
|
//case PAYMENT plan
|
|
131
131
|
if(project && project.isActiveSubscription && project.profile.type=== 'payment'){
|
|
132
|
-
check = true
|
|
133
|
-
}else if(project && !project.isActiveSubscription && project.profile.type=== 'payment'){
|
|
134
132
|
check = false
|
|
133
|
+
}else if(project && !project.isActiveSubscription && project.profile.type=== 'payment'){
|
|
134
|
+
check = true
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
return check
|
package/src/app/utils/utils.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { ProjectUser } from "src/chat21-core/models/projectUsers";
|
|
2
|
+
|
|
1
3
|
export function getOSCode(key: string, token: string): boolean {
|
|
2
4
|
|
|
3
5
|
if (token) {
|
|
4
6
|
const keys: String[] = token.split("-");
|
|
5
7
|
|
|
6
8
|
let element = keys.find(el => el.includes(key))
|
|
7
|
-
console.log('keys', keys)
|
|
9
|
+
// console.log('keys', keys)
|
|
8
10
|
if(element){
|
|
9
11
|
element = element.split(":")[1]
|
|
10
12
|
if(element && element === "F"){
|
|
@@ -21,4 +23,19 @@ export function getOSCode(key: string, token: string): boolean {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export function hasRole(projectUser: ProjectUser, role: string ): boolean {
|
|
30
|
+
let roles = ['owner', 'admin', 'agent'];
|
|
31
|
+
if(roles.includes(projectUser.role)){
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if(Array.isArray(projectUser.rolePermissions) && projectUser.rolePermissions.includes(role)){
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return false
|
|
40
|
+
|
|
24
41
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface ProjectUser {
|
|
2
|
+
_id?: string;
|
|
3
|
+
updatedAt?: any;
|
|
4
|
+
createdAt?: any;
|
|
5
|
+
id_project?: string;
|
|
6
|
+
user_available?: boolean;
|
|
7
|
+
role?: string;
|
|
8
|
+
createdBy?: string;
|
|
9
|
+
is_group_member?: boolean;
|
|
10
|
+
// id_user?: string;
|
|
11
|
+
isAuthenticated?: boolean;
|
|
12
|
+
isBusy?: boolean;
|
|
13
|
+
status?: string;
|
|
14
|
+
id_user?: any;
|
|
15
|
+
rolePermissions?: string[];
|
|
16
|
+
profileStatus?: string;
|
|
17
|
+
presence?: { [key: string]: string}
|
|
18
|
+
__v?: any;
|
|
19
|
+
}
|
|
@@ -392,7 +392,7 @@ export class FirebaseConversationHandler extends ConversationHandlerService {
|
|
|
392
392
|
verb = INFO_SUPPORT_USER_ADDED_VERB;
|
|
393
393
|
complement = INFO_SUPPORT_USER_ADDED_COMPLEMENT;
|
|
394
394
|
} else {
|
|
395
|
-
// other user has been added to the group (and he has not a
|
|
395
|
+
// other user has been added to the group (and he has not a firstname, so use hes useruid)
|
|
396
396
|
subject = message.attributes.messagelabel.parameters.member_id;
|
|
397
397
|
verb = INFO_SUPPORT_USER_ADDED_VERB;
|
|
398
398
|
complement = INFO_SUPPORT_USER_ADDED_COMPLEMENT;
|
|
@@ -370,7 +370,7 @@ export class MQTTConversationHandler extends ConversationHandlerService {
|
|
|
370
370
|
verb = INFO_SUPPORT_USER_ADDED_VERB;
|
|
371
371
|
complement = INFO_SUPPORT_USER_ADDED_COMPLEMENT;
|
|
372
372
|
} else {
|
|
373
|
-
// other user has been added to the group (and he has not a
|
|
373
|
+
// other user has been added to the group (and he has not a firstname, so use hes useruid)
|
|
374
374
|
subject = message.attributes.messagelabel.parameters.member_id;
|
|
375
375
|
verb = INFO_SUPPORT_USER_ADDED_VERB;
|
|
376
376
|
complement = INFO_SUPPORT_USER_ADDED_COMPLEMENT;
|
|
@@ -1018,6 +1018,14 @@ export function isAllowedUrlInText(text: string, allowedUrls: string[]) {
|
|
|
1018
1018
|
}
|
|
1019
1019
|
|
|
1020
1020
|
function extractUrls(text: string): string[] {
|
|
1021
|
-
|
|
1022
|
-
|
|
1021
|
+
// Rileva URL con o senza protocollo (http/https)
|
|
1022
|
+
const urlRegex = /\b((https?:\/\/)?(www\.)?[a-z0-9.-]+\.[a-z]{2,})(\/[^\s]*)?/gi;
|
|
1023
|
+
const matches = text.match(urlRegex) || [];
|
|
1024
|
+
// Normalizza: aggiunge https:// se manca, così il parsing con new URL() funziona
|
|
1025
|
+
return matches.map((url) => {
|
|
1026
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
1027
|
+
return 'https://' + url;
|
|
1028
|
+
}
|
|
1029
|
+
return url;
|
|
1030
|
+
});
|
|
1023
1031
|
}
|