@chat21/chat21-ionic 3.4.12 → 3.4.13-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.
- package/CHANGELOG.md +7 -0
- package/Dockerfile +1 -1
- package/config.xml +0 -1
- package/package.json +4 -4
- package/src/app/components/canned-response/canned-response.component.ts +3 -6
- package/src/app/components/conversation-detail/message-text-area/message-text-area.component.html +35 -23
- package/src/app/components/conversation-detail/message-text-area/message-text-area.component.scss +8 -1
- package/src/app/components/conversation-detail/message-text-area/message-text-area.component.ts +5 -2
- package/src/app/components/copilot-suggestions/copilot-suggestions.component.html +41 -0
- package/src/app/components/copilot-suggestions/copilot-suggestions.component.scss +232 -0
- package/src/app/components/copilot-suggestions/copilot-suggestions.component.spec.ts +24 -0
- package/src/app/components/copilot-suggestions/copilot-suggestions.component.ts +163 -0
- package/src/app/pages/conversation-detail/conversation-detail.module.ts +2 -0
- package/src/app/pages/conversation-detail/conversation-detail.page.html +14 -43
- package/src/app/pages/conversation-detail/conversation-detail.page.scss +2 -1
- package/src/app/pages/conversation-detail/conversation-detail.page.ts +52 -33
- package/src/app/services/copilot/copilot.service.spec.ts +16 -0
- package/src/app/services/copilot/copilot.service.ts +44 -0
- package/src/assets/i18n/en.json +4 -0
- package/src/assets/i18n/it.json +5 -1
- package/src/assets/images/icons/copilot.svg +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# chat21-ionic ver 3.0
|
|
2
2
|
|
|
3
|
+
### 3.4.13-rc.2
|
|
4
|
+
- added: no-suggestions in copilot component
|
|
5
|
+
- changed: HIDE_CANNED_RESPONSES with SHOW CANNED_RESPONSES
|
|
6
|
+
|
|
7
|
+
### 3.4.13-rc.1
|
|
8
|
+
- added: copilot suggestions for agent responses
|
|
9
|
+
|
|
3
10
|
### 3.4.12 in PROD
|
|
4
11
|
|
|
5
12
|
### 3.4.12-rc.2
|
package/Dockerfile
CHANGED
package/config.xml
CHANGED
|
@@ -106,7 +106,6 @@
|
|
|
106
106
|
<platform name="browser">
|
|
107
107
|
<preference name="ShowSplashScreen" value="false" />
|
|
108
108
|
</platform>
|
|
109
|
-
<engine name="browser" spec="^5.0.4" />
|
|
110
109
|
<plugin name="cordova-android-support-gradle-release" spec="^3.0.1">
|
|
111
110
|
<variable name="ANDROID_SUPPORT_VERSION" value="27.+" />
|
|
112
111
|
</plugin>
|
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.
|
|
4
|
+
"version": "3.4.13-rc2",
|
|
5
5
|
"license": "MIT License",
|
|
6
6
|
"homepage": "https://tiledesk.com/",
|
|
7
7
|
"repository": {
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
"autolinker": "^3.14.2",
|
|
49
49
|
"cordova": "^11.1.0",
|
|
50
50
|
"cordova-android-support-gradle-release": "^3.0.1",
|
|
51
|
-
"cordova-browser": "^5.0.4",
|
|
52
51
|
"cordova-plugin-chooser": "^1.3.2",
|
|
53
52
|
"cordova-plugin-ionic-keyboard": "^2.2.0",
|
|
54
53
|
"cordova-plugin-ionic-webview": "^4.2.1",
|
|
@@ -86,6 +85,7 @@
|
|
|
86
85
|
"@types/node": "^12.20.55",
|
|
87
86
|
"codelyzer": "^6.0.0",
|
|
88
87
|
"cordova-android": "^10.1.2",
|
|
88
|
+
"cordova-browser": "^6.0.0",
|
|
89
89
|
"cordova-ios": "^6.3.0",
|
|
90
90
|
"cordova-plugin-add-swift-support": "^2.0.2",
|
|
91
91
|
"cordova-plugin-fcm-with-dependecy-updated": "^7.8.0",
|
|
@@ -147,9 +147,9 @@
|
|
|
147
147
|
}
|
|
148
148
|
},
|
|
149
149
|
"platforms": [
|
|
150
|
-
"browser",
|
|
151
150
|
"android",
|
|
152
|
-
"ios"
|
|
151
|
+
"ios",
|
|
152
|
+
"browser"
|
|
153
153
|
]
|
|
154
154
|
}
|
|
155
155
|
}
|
|
@@ -31,7 +31,6 @@ export class CannedResponseComponent implements OnInit {
|
|
|
31
31
|
public projectID: string;
|
|
32
32
|
|
|
33
33
|
public tagsCanned: any = []
|
|
34
|
-
public tagsCannedCount: number
|
|
35
34
|
public tagsCannedFilter: any = []
|
|
36
35
|
|
|
37
36
|
public arrowkeyLocation = -1
|
|
@@ -128,8 +127,6 @@ export class CannedResponseComponent implements OnInit {
|
|
|
128
127
|
this.logger.log('[CANNED] - loadTagsCanned getCannedResponses RES', res)
|
|
129
128
|
|
|
130
129
|
this.tagsCanned = res
|
|
131
|
-
this.tagsCannedCount = res.length
|
|
132
|
-
this.logger.log('[CANNED] - loadTagsCanned getCannedResponses tagsCannedCount', this.tagsCannedCount)
|
|
133
130
|
// if (this.HIDE_CANNED_RESPONSES === false) {
|
|
134
131
|
this.showTagsCanned(strSearch)
|
|
135
132
|
// }
|
|
@@ -179,6 +176,7 @@ export class CannedResponseComponent implements OnInit {
|
|
|
179
176
|
// <div class='cannedText no-canned-available-text'>" + this.translationMap.get('AddNewCannedResponse') + '</div>
|
|
180
177
|
title: this.translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE') ,
|
|
181
178
|
text: '',
|
|
179
|
+
disabled: true
|
|
182
180
|
}
|
|
183
181
|
// } else if (this.USER_ROLE === 'agent') {
|
|
184
182
|
// nocanned = {
|
|
@@ -269,10 +267,9 @@ export class CannedResponseComponent implements OnInit {
|
|
|
269
267
|
if(!canned.disabled){
|
|
270
268
|
event.preventDefault();
|
|
271
269
|
event.stopPropagation();
|
|
272
|
-
} else if(this.tagsCannedCount > 0){
|
|
273
|
-
this.onClickCanned.emit(canned)
|
|
274
270
|
} else {
|
|
275
|
-
this.logger.log('[CANNED] THERE IS
|
|
271
|
+
this.logger.log('[CANNED] THERE IS CANNED ', canned.text)
|
|
272
|
+
this.onClickCanned.emit(canned)
|
|
276
273
|
}
|
|
277
274
|
}
|
|
278
275
|
|
package/src/app/components/conversation-detail/message-text-area/message-text-area.component.html
CHANGED
|
@@ -1,29 +1,41 @@
|
|
|
1
1
|
<ion-grid>
|
|
2
2
|
<div class="footer-options-container">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<
|
|
6
|
-
<ion-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<
|
|
13
|
-
<ion-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
<div>
|
|
4
|
+
<!-- CHAT-OPTION -->
|
|
5
|
+
<div class="section-option" id="chat" tooltip="{{translationMap?.get('LABEL_CHAT')}}" placement="top">
|
|
6
|
+
<ion-button fill="clear" (click)="onOpenSection('chat')" [class.active]="section==='chat'">
|
|
7
|
+
<ion-icon name="chatbubbles"></ion-icon>
|
|
8
|
+
{{translationMap.get('LABEL_CHAT')}}
|
|
9
|
+
</ion-button>
|
|
10
|
+
</div>
|
|
11
|
+
<!-- EMAIL-OPTION -->
|
|
12
|
+
<div *ngIf="emailSection" class="section-option" id="email" tooltip="{{translationMap?.get('EMAIL.LABEL_TOOLTIP')}}" placement="top">
|
|
13
|
+
<ion-button fill="clear" [class.active]="section==='email'" (click)="onOpenSection('email')" [disabled]="channelType === 'direct' || channel == CHANNEL_TYPE.WHATSAPP || channel == CHANNEL_TYPE.MESSENGER">
|
|
14
|
+
<ion-icon name="mail-open"></ion-icon>
|
|
15
|
+
{{translationMap.get('LABEL_EMAIL')}}
|
|
16
|
+
</ion-button>
|
|
17
|
+
</div>
|
|
18
|
+
<!-- WHATSAPP TEMPLATE -->
|
|
19
|
+
<div *ngIf="whatsappTemplatesSection" class="section-option" id="template" tooltip="{{translationMap?.get('WHATSAPP.LABEL_TOOLTIP')}}" placement="top">
|
|
20
|
+
<ion-button fill="clear" [class.active]="section==='templates'" (click)="onOpenSection('templates');onOpenTemplateModal()" [disabled]="channel !== CHANNEL_TYPE.WHATSAPP">
|
|
21
|
+
<ion-icon name="logo-whatsapp"></ion-icon>
|
|
22
|
+
{{translationMap?.get('WHATSAPP.LABEL_TEMPLATES')}}
|
|
23
|
+
</ion-button>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div *ngIf="offlineMsgEmail && section==='chat' && channel !== CHANNEL_TYPE.WHATSAPP && messageString && (leadInfo?.presence['status']==='offline' && leadInfo?.hasEmail)" class="section-option offline-lead-tip" >
|
|
27
|
+
{{translationMap.get('EMAIL.EMAIL_OFFLINE_TIP')}}
|
|
28
|
+
</div>
|
|
16
29
|
</div>
|
|
17
30
|
|
|
18
|
-
<div
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{{translationMap.get('EMAIL.EMAIL_OFFLINE_TIP')}}
|
|
31
|
+
<div>
|
|
32
|
+
<!-- COPILOT-OPTION -->
|
|
33
|
+
<div class="section-option" id="copilot" tooltip="{{translationMap?.get('COPILOT.ASK_AI')}}" placement="top">
|
|
34
|
+
<ion-button fill="clear" [class.active]="section ==='copilot'" (click)="onOpenSection('copilot')">
|
|
35
|
+
<ion-icon class="channel-icon" src="assets/images/icons/copilot.svg"></ion-icon>
|
|
36
|
+
{{translationMap?.get('COPILOT.ASK_AI')}}
|
|
37
|
+
</ion-button>
|
|
38
|
+
</div>
|
|
27
39
|
</div>
|
|
28
40
|
|
|
29
41
|
</div>
|
|
@@ -35,7 +47,7 @@
|
|
|
35
47
|
</ion-col>
|
|
36
48
|
</ion-row>
|
|
37
49
|
|
|
38
|
-
<ion-row id="message-text-area" [style.display]="section==='chat' || section ==='templates'? 'flex': 'none'">
|
|
50
|
+
<ion-row id="message-text-area" [style.display]="section==='chat' || section ==='templates' || section ==='copilot'? 'flex': 'none'">
|
|
39
51
|
|
|
40
52
|
<ion-col col-auto style="display: flex;">
|
|
41
53
|
|
package/src/app/components/conversation-detail/message-text-area/message-text-area.component.scss
CHANGED
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
padding: 0px 16px 0px;
|
|
6
6
|
border-bottom: 1px solid var(--border-color-base);
|
|
7
7
|
border-top: 3px solid var(--basic-blue);
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
|
|
10
|
+
& > div{
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
}
|
|
8
14
|
|
|
9
15
|
.section-option{
|
|
10
16
|
line-height: 16px;
|
|
@@ -26,6 +32,7 @@
|
|
|
26
32
|
height: 25px;
|
|
27
33
|
&.active{
|
|
28
34
|
color: var(--basic-blue);
|
|
35
|
+
fill: var(--basic-blue);
|
|
29
36
|
}
|
|
30
37
|
ion-icon{
|
|
31
38
|
margin-right: 4px;
|
|
@@ -35,7 +42,7 @@
|
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
.offline-lead-tip{
|
|
38
|
-
font-size:
|
|
45
|
+
font-size: 9px;
|
|
39
46
|
width: 100%;
|
|
40
47
|
display: block;
|
|
41
48
|
text-align: right;
|
package/src/app/components/conversation-detail/message-text-area/message-text-area.component.ts
CHANGED
|
@@ -22,6 +22,8 @@ import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance'
|
|
|
22
22
|
import { EventsService } from 'src/app/services/events-service';
|
|
23
23
|
import { isOnMobileDevice } from 'src/chat21-core/utils/utils';
|
|
24
24
|
import { checkAcceptedFile } from 'src/chat21-core/utils/utils';
|
|
25
|
+
import { CopilotService } from 'src/app/services/copilot/copilot.service';
|
|
26
|
+
import { BRAND_BASE_INFO } from 'src/app/utils/utils-resources';
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
@Component({
|
|
@@ -49,7 +51,6 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
|
|
|
49
51
|
@Input() channelType: string;
|
|
50
52
|
@Input() channel: string;
|
|
51
53
|
@Input() tagsCannedFilter: any;
|
|
52
|
-
@Input() tagsCannedCount: number;
|
|
53
54
|
@Input() areVisibleCAR: boolean;
|
|
54
55
|
@Input() supportMode: boolean;
|
|
55
56
|
@Input() leadInfo: {lead_id: string, hasEmail: boolean, email: string, projectId: string, presence: {}};
|
|
@@ -106,6 +107,9 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
|
|
|
106
107
|
imageUrl: 'https://tiledesk.com/wp-content/uploads/2022/11/FAQ-Chatbot.png',
|
|
107
108
|
}
|
|
108
109
|
];
|
|
110
|
+
|
|
111
|
+
BRAND_BASE_INFO = BRAND_BASE_INFO
|
|
112
|
+
|
|
109
113
|
/**
|
|
110
114
|
* Constructor
|
|
111
115
|
* @param chooser
|
|
@@ -118,7 +122,6 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
|
|
|
118
122
|
public modalController: ModalController,
|
|
119
123
|
public uploadService: UploadService,
|
|
120
124
|
public toastController: ToastController,
|
|
121
|
-
private renderer: Renderer2,
|
|
122
125
|
public eventsService: EventsService
|
|
123
126
|
) { }
|
|
124
127
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<div>
|
|
2
|
+
<div class="canned-list" *ngIf="suggestions.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 suggestion of suggestions; let i = index;"
|
|
6
|
+
(click)="onClickSuggestionFN(suggestion, $event)">
|
|
7
|
+
<div class="cannedContent">
|
|
8
|
+
<!-- <ion-input [class.readonly]="suggestion?.disabled" [readonly]="suggestion?.disabled" type="text" [(ngModel)]="suggestion.title" class="title" id="{{'titleCanned_'+suggestion._id}}" #title></ion-input> -->
|
|
9
|
+
<!-- <ion-input [class.readonly]="suggestion?.disabled" [readonly]="suggestion?.disabled" type="text" [(ngModel)]="suggestion.text" class="text"></ion-input> -->
|
|
10
|
+
<ion-label [class.readonly]="suggestion?.disabled" class="title" id="{{'titleCanned_'+suggestion._id}}" #title >{{suggestion?.title}}</ion-label>
|
|
11
|
+
<ion-label [class.readonly]="suggestion?.disabled" class="text">{{suggestion?.text}}</ion-label>
|
|
12
|
+
</div>
|
|
13
|
+
</ion-item>
|
|
14
|
+
<!-- <ion-item class="canned-item add-canned-response-wpr" button="true" lines="none" (click)="onClickAddCannedResponseFN()">
|
|
15
|
+
<ion-icon class="add-canned-response-icon" name="flash-outline"></ion-icon>
|
|
16
|
+
<span class="add-canned-response-add-icon">+</span>
|
|
17
|
+
<label class="add-canned-response-label" >{{translationMap?.get('AddNewCannedResponse')}}</label>
|
|
18
|
+
</ion-item> -->
|
|
19
|
+
</div>
|
|
20
|
+
<!-- LOADER -->
|
|
21
|
+
<div class="loader" *ngIf="showLoading">
|
|
22
|
+
<div class="box">
|
|
23
|
+
<!-- <div class="container">
|
|
24
|
+
<span class="circle" [ngStyle]="{'background-color': stylesMap?.get('themeColor')}"></span>
|
|
25
|
+
<span class="circle" [ngStyle]="{'background-color': stylesMap?.get('themeColor')}"></span>
|
|
26
|
+
<span class="circle" [ngStyle]="{'background-color': stylesMap?.get('themeColor')}"></span>
|
|
27
|
+
<span class="circle" [ngStyle]="{'background-color': stylesMap?.get('themeColor')}"></span>
|
|
28
|
+
</div> -->
|
|
29
|
+
<div class="spinner" [ngStyle]="{'border-top-color': stylesMap?.get('themeColor')}"></div>
|
|
30
|
+
<div class="label">{{translationMap.get('LABEL_LOADING')}}</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="no-data" *ngIf="suggestions.length === 0 && !showLoading">
|
|
34
|
+
<div class="container">
|
|
35
|
+
<ion-item button="false" lines="none" class="canned-item no-ripple border">
|
|
36
|
+
<ion-icon name="cloud-offline" slot="start"></ion-icon>
|
|
37
|
+
<ion-label>{{translationMap.get('COPILOT.NO_SUGGESTIONS_PRESENT')}}</ion-label>
|
|
38
|
+
</ion-item>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
.canned-list {
|
|
2
|
+
// position: absolute;
|
|
3
|
+
// bottom: 0;
|
|
4
|
+
background-color: white !important;
|
|
5
|
+
width: 100%;
|
|
6
|
+
max-height: 310px;
|
|
7
|
+
overflow-y: auto;
|
|
8
|
+
// padding: 10px 0;
|
|
9
|
+
// margin: 0;
|
|
10
|
+
margin-bottom: 1px;
|
|
11
|
+
font-size: 14px;
|
|
12
|
+
line-height: 1.42857143;
|
|
13
|
+
color: #080f1a;
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
-webkit-font-smoothing: antialiased;
|
|
16
|
+
// list-style: none;
|
|
17
|
+
z-index: 999999;
|
|
18
|
+
|
|
19
|
+
&::-webkit-scrollbar {
|
|
20
|
+
width: 6px;
|
|
21
|
+
height: 8px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&::-webkit-scrollbar-track {
|
|
25
|
+
background: #f9f9f9;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&::-webkit-scrollbar-thumb {
|
|
29
|
+
background-color: #b9b9b9;
|
|
30
|
+
border-radius: 0px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
34
|
+
background-color: #727272;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.cannedContent{
|
|
38
|
+
width: 100%;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
ion-label{
|
|
43
|
+
white-space: pre-wrap;
|
|
44
|
+
word-wrap: break-word;
|
|
45
|
+
|
|
46
|
+
--padding-bottom: 0px;
|
|
47
|
+
--padding-top: 0px;
|
|
48
|
+
&.text{
|
|
49
|
+
font-style: italic;
|
|
50
|
+
margin-top: 2px;
|
|
51
|
+
}
|
|
52
|
+
&.title {
|
|
53
|
+
font-weight: 500;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&.readonly{
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// .native-input[disabled] {
|
|
62
|
+
// opacity: 10 !important;
|
|
63
|
+
// cursor: pointer;
|
|
64
|
+
// }
|
|
65
|
+
// .native-textarea[disabled] {
|
|
66
|
+
// opacity: 10 !important;
|
|
67
|
+
// cursor: pointer;
|
|
68
|
+
// }
|
|
69
|
+
ion-icon {
|
|
70
|
+
zoom: 0.7;
|
|
71
|
+
color: var(--icon-color);
|
|
72
|
+
|
|
73
|
+
&:hover{
|
|
74
|
+
opacity: 0.8;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
.no-ripple {
|
|
78
|
+
--ripple-color: transparent;
|
|
79
|
+
--background-activated: transparent;
|
|
80
|
+
--background: transparent;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.border{
|
|
84
|
+
border-bottom: 1px dashed rgb(211, 219, 229) !important;
|
|
85
|
+
margin: 0px 4px
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.canned-item {
|
|
92
|
+
-webkit-tap-highlight-color: transparent;
|
|
93
|
+
font-family: Lato, sans-serif;
|
|
94
|
+
font-size: 14px;
|
|
95
|
+
line-height: 1.42857143;
|
|
96
|
+
list-style: none;
|
|
97
|
+
box-sizing: border-box;
|
|
98
|
+
-webkit-font-smoothing: antialiased;
|
|
99
|
+
// margin: 0 10px;
|
|
100
|
+
position: relative;
|
|
101
|
+
outline: none;
|
|
102
|
+
color: #434a54;
|
|
103
|
+
// padding: 10px;
|
|
104
|
+
padding: 5px;
|
|
105
|
+
width: auto;
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
// .item-inner{
|
|
108
|
+
// border: none!important;
|
|
109
|
+
// }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
ion-item {
|
|
113
|
+
--background-hover: var(--canned-hover-background) !important;
|
|
114
|
+
--color-hover: var(--canned-hover-color);
|
|
115
|
+
--background: transparent;
|
|
116
|
+
.nocannedTitle {
|
|
117
|
+
color: #f44336;
|
|
118
|
+
}
|
|
119
|
+
.no-canned-available-text {
|
|
120
|
+
color: #1877f2 !important;
|
|
121
|
+
}
|
|
122
|
+
.no-canned-available-text:hover {
|
|
123
|
+
text-decoration: underline;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.loader {
|
|
128
|
+
height: 310px;
|
|
129
|
+
|
|
130
|
+
.box{
|
|
131
|
+
top: 50%;
|
|
132
|
+
left: 50%;
|
|
133
|
+
transform: translate(-50%, -50%);
|
|
134
|
+
position: absolute;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.container{
|
|
138
|
+
height: 15px;
|
|
139
|
+
width: 105px;
|
|
140
|
+
display: flex;
|
|
141
|
+
position: relative;
|
|
142
|
+
|
|
143
|
+
.circle{
|
|
144
|
+
width: 15px;
|
|
145
|
+
height: 15px;
|
|
146
|
+
border-radius: 50%;
|
|
147
|
+
background-color: var(--basic-blue);
|
|
148
|
+
animation: move 500ms linear 0ms infinite;
|
|
149
|
+
margin-right: 30px;
|
|
150
|
+
|
|
151
|
+
&:first-child{
|
|
152
|
+
position: absolute;
|
|
153
|
+
top:0;
|
|
154
|
+
left:0;
|
|
155
|
+
animation: grow 500ms linear 0ms infinite;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
&:last-child{
|
|
159
|
+
position: absolute;
|
|
160
|
+
top: 0;
|
|
161
|
+
right: 0;
|
|
162
|
+
margin-right: 0;
|
|
163
|
+
animation: grow 500ms linear 0s infinite reverse;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.spinner {
|
|
170
|
+
margin: auto;
|
|
171
|
+
border: 8px solid #EAF0F6;
|
|
172
|
+
border-radius: 50%;
|
|
173
|
+
border-top: 8px solid var(--basic-blue);
|
|
174
|
+
width: 50px;
|
|
175
|
+
height: 50px;
|
|
176
|
+
animation: spinner 1s linear infinite;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@keyframes spinner {
|
|
180
|
+
0% { transform: rotate(0deg); }
|
|
181
|
+
100% { transform: rotate(360deg); }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.label{
|
|
185
|
+
text-align: center;
|
|
186
|
+
margin-top: 10px;
|
|
187
|
+
animation: blinker 1s linear infinite;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@keyframes grow {
|
|
191
|
+
from {transform: scale(0,0); opacity: 0;}
|
|
192
|
+
to {transform: scale(1,1); opacity: 1;}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@keyframes move {
|
|
196
|
+
from {transform: translateX(0px)}
|
|
197
|
+
to {transform: translateX(45px)}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@keyframes blinker {
|
|
201
|
+
50% {
|
|
202
|
+
opacity: 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
.no-data{
|
|
209
|
+
|
|
210
|
+
background-color: white !important;
|
|
211
|
+
width: 100%;
|
|
212
|
+
max-height: 310px;
|
|
213
|
+
overflow-y: auto;
|
|
214
|
+
// padding: 10px 0;
|
|
215
|
+
// margin: 0;
|
|
216
|
+
margin-bottom: 1px;
|
|
217
|
+
font-size: 14px;
|
|
218
|
+
line-height: 1.42857143;
|
|
219
|
+
color: #080f1a;
|
|
220
|
+
box-sizing: border-box;
|
|
221
|
+
-webkit-font-smoothing: antialiased;
|
|
222
|
+
// list-style: none;
|
|
223
|
+
z-index: 999999;
|
|
224
|
+
|
|
225
|
+
.container{
|
|
226
|
+
display: flex;
|
|
227
|
+
justify-content: center;
|
|
228
|
+
flex-direction: column;
|
|
229
|
+
align-items: center;
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|
2
|
+
import { IonicModule } from '@ionic/angular';
|
|
3
|
+
|
|
4
|
+
import { CopilotSuggestionsComponent } from './copilot-suggestions.component';
|
|
5
|
+
|
|
6
|
+
describe('CopilotSuggestionsComponent', () => {
|
|
7
|
+
let component: CopilotSuggestionsComponent;
|
|
8
|
+
let fixture: ComponentFixture<CopilotSuggestionsComponent>;
|
|
9
|
+
|
|
10
|
+
beforeEach(waitForAsync(() => {
|
|
11
|
+
TestBed.configureTestingModule({
|
|
12
|
+
declarations: [ CopilotSuggestionsComponent ],
|
|
13
|
+
imports: [IonicModule.forRoot()]
|
|
14
|
+
}).compileComponents();
|
|
15
|
+
|
|
16
|
+
fixture = TestBed.createComponent(CopilotSuggestionsComponent);
|
|
17
|
+
component = fixture.componentInstance;
|
|
18
|
+
fixture.detectChanges();
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
it('should create', () => {
|
|
22
|
+
expect(component).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { filter } from 'rxjs/operators';
|
|
2
|
+
import { Component, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, SimpleChange } from '@angular/core';
|
|
3
|
+
import { CopilotService } from 'src/app/services/copilot/copilot.service';
|
|
4
|
+
import { TiledeskService } from 'src/app/services/tiledesk/tiledesk.service';
|
|
5
|
+
import { UserModel } from 'src/chat21-core/models/user';
|
|
6
|
+
import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
|
|
7
|
+
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
8
|
+
import { TiledeskAuthService } from 'src/chat21-core/providers/tiledesk/tiledesk-auth.service';
|
|
9
|
+
import { getProjectIdSelectedConversation } from 'src/chat21-core/utils/utils';
|
|
10
|
+
|
|
11
|
+
@Component({
|
|
12
|
+
selector: 'copilot-suggestions',
|
|
13
|
+
templateUrl: './copilot-suggestions.component.html',
|
|
14
|
+
styleUrls: ['./copilot-suggestions.component.scss'],
|
|
15
|
+
})
|
|
16
|
+
export class CopilotSuggestionsComponent implements OnInit {
|
|
17
|
+
|
|
18
|
+
// @Input() tagsCannedFilter: any = []
|
|
19
|
+
@Input() conversationWith: string;
|
|
20
|
+
@Input() conversationWithFullname: string;
|
|
21
|
+
@Input() currentString: string;
|
|
22
|
+
@Input() stylesMap: Map<string, string>;
|
|
23
|
+
@Input() translationMap: Map<string, string>;
|
|
24
|
+
@Output() onLoadedSuggestions = new EventEmitter<[any]>();
|
|
25
|
+
@Output() onClickSuggestion = new EventEmitter<any>();
|
|
26
|
+
|
|
27
|
+
public loggedUser: UserModel
|
|
28
|
+
public projectID: string;
|
|
29
|
+
|
|
30
|
+
public suggestions: any = []
|
|
31
|
+
public showLoading: boolean = false
|
|
32
|
+
|
|
33
|
+
public arrowkeyLocation = -1
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
private logger: LoggerService = LoggerInstance.getInstance();
|
|
37
|
+
constructor(
|
|
38
|
+
public tiledeskAuthService: TiledeskAuthService,
|
|
39
|
+
public tiledeskService: TiledeskService,
|
|
40
|
+
public copilotService: CopilotService,
|
|
41
|
+
public el: ElementRef
|
|
42
|
+
) { }
|
|
43
|
+
|
|
44
|
+
ngOnInit() {
|
|
45
|
+
this.loggedUser = this.tiledeskAuthService.getCurrentUser()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ngOnChanges(changes: SimpleChange){
|
|
49
|
+
this.logger.debug('[COPILOT] - loadTagsCanned strSearch ', this.currentString)
|
|
50
|
+
if(changes && changes['conversationWith'] && (changes['conversationWith'].previousValue !== changes['conversationWith'].currentValue)){
|
|
51
|
+
this.projectID = getProjectIdSelectedConversation(this.conversationWith)
|
|
52
|
+
}
|
|
53
|
+
this.loadSuggestions(this.conversationWith)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
// ----------------------------------------------------------
|
|
58
|
+
// @ CANNED RESPONSES methods
|
|
59
|
+
// ----------------------------------------------------------
|
|
60
|
+
loadSuggestions(conversationWith) {
|
|
61
|
+
this.logger.log('[COPILOT] - loadTagsCanned conversationWith ', conversationWith)
|
|
62
|
+
|
|
63
|
+
if (this.projectID) {
|
|
64
|
+
this.logger.log('[COPILOT] - loadTagsCanned projectId ', this.projectID)
|
|
65
|
+
this.getAndShowSuggestions(this.projectID)
|
|
66
|
+
} else {
|
|
67
|
+
this.getProjectIdByConversationWith(this.conversationWith)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getProjectIdByConversationWith(conversationWith: string) {
|
|
72
|
+
const tiledeskToken = this.tiledeskAuthService.getTiledeskToken()
|
|
73
|
+
|
|
74
|
+
this.tiledeskService.getProjectIdByConvRecipient(tiledeskToken, conversationWith).subscribe((res) => {
|
|
75
|
+
this.logger.log('[COPILOT] - loadTagsCanned - GET PROJECTID BY CONV RECIPIENT RES', res)
|
|
76
|
+
if (res) {
|
|
77
|
+
const projectId = res.id_project
|
|
78
|
+
this.logger.log('[COPILOT] - loadTagsCanned - GET PROJECTID BY CONV RECIPIENT projectId ', projectId)
|
|
79
|
+
if (projectId) {
|
|
80
|
+
this.getAndShowSuggestions(projectId)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}, (error) => {
|
|
84
|
+
this.logger.error('[COPILOT] - loadTagsCanned - GET PROJECTID BY CONV RECIPIENT - ERROR ', error)
|
|
85
|
+
}, () => {
|
|
86
|
+
this.logger.log('[COPILOT] - loadTagsCanned - GET PROJECTID BY CONV RECIPIENT * COMPLETE *')
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
getAndShowSuggestions(projectId) {
|
|
92
|
+
const tiledeskToken = this.tiledeskAuthService.getTiledeskToken()
|
|
93
|
+
this.logger.log('[COPILOT] - loadTagsCanned tagsCanned.length', this.suggestions.length)
|
|
94
|
+
//if(this.tagsCanned.length <= 0 ){
|
|
95
|
+
this.suggestions = []
|
|
96
|
+
this.showLoading = true;
|
|
97
|
+
this.copilotService.getAll(tiledeskToken, projectId, this.conversationWith).subscribe((res) => {
|
|
98
|
+
this.logger.log('[COPILOT] - loadTagsCanned getCannedResponses RES', res)
|
|
99
|
+
this.suggestions = res.map(el => ({ ...el, disabled : true }))
|
|
100
|
+
this.logger.log('[COPILOT] - loadTagsCanned getCannedResponses tagsCannedCount', this.suggestions)
|
|
101
|
+
}, (error) => {
|
|
102
|
+
this.logger.error('[COPILOT] - loadTagsCanned getCannedResponses - ERROR ', error)
|
|
103
|
+
}, () => {
|
|
104
|
+
this.logger.log('[COPILOT] - loadTagsCanned getCannedResponses * COMPLETE *')
|
|
105
|
+
this.showLoading = false
|
|
106
|
+
this.onLoadedSuggestions.emit(this.suggestions)
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
onClickSuggestionFN(suggestion, event){
|
|
112
|
+
if(!suggestion.disabled){
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
event.stopPropagation();
|
|
115
|
+
} else {
|
|
116
|
+
this.logger.log('[CANNED] THERE IS NOT CANNED ', suggestion.text)
|
|
117
|
+
this.onClickSuggestion.emit(suggestion)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@HostListener('document:keydown', ['$event'])
|
|
122
|
+
handleKeyboardEvent(event: KeyboardEvent) {
|
|
123
|
+
// this.logger.log("CONVERSATION-DETAIL handleKeyboardEvent event.key ", event);
|
|
124
|
+
|
|
125
|
+
if (this.suggestions.length > 0) {
|
|
126
|
+
if (event.key === 'ArrowDown') {
|
|
127
|
+
this.arrowkeyLocation++
|
|
128
|
+
if (this.arrowkeyLocation === this.suggestions.length) {
|
|
129
|
+
this.arrowkeyLocation--
|
|
130
|
+
}
|
|
131
|
+
// this.replaceTagInMessage(this.tagsCannedFilter[this.arrowkeyLocation])
|
|
132
|
+
} else if (event.key === 'ArrowUp') {
|
|
133
|
+
if (this.arrowkeyLocation > 0) {
|
|
134
|
+
this.arrowkeyLocation--
|
|
135
|
+
} else if (this.arrowkeyLocation < 0) {
|
|
136
|
+
this.arrowkeyLocation++
|
|
137
|
+
}
|
|
138
|
+
// this.replaceTagInMessage(this.tagsCannedFilter[this.arrowkeyLocation])
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// set the focus on current canned
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
this.el.nativeElement.querySelector('.canned-list').scrollTop = this.arrowkeyLocation * 59 // 59px is the height of the single element
|
|
144
|
+
// this.el.nativeElement.querySelector('#canned-item_'+this.arrowkeyLocation).scrollIntoView({ behavior: 'smooth' })
|
|
145
|
+
}, 0);
|
|
146
|
+
|
|
147
|
+
if (event.key === 'Enter') {
|
|
148
|
+
const canned_selected = this.suggestions[this.arrowkeyLocation]
|
|
149
|
+
this.logger.log('[CONVS-DETAIL] replaceTagInMessage canned_selected ',canned_selected)
|
|
150
|
+
if (canned_selected) {
|
|
151
|
+
this.arrowkeyLocation = -1
|
|
152
|
+
this.onClickSuggestion.emit(canned_selected)
|
|
153
|
+
// event.preventDefault();
|
|
154
|
+
// return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
}
|
|
@@ -43,6 +43,7 @@ import { ReturnReceiptComponent } from 'src/app/chatlib/conversation-detail/mess
|
|
|
43
43
|
import { OptionsComponent } from 'src/app/chatlib/conversation-detail/message/options/options.component';
|
|
44
44
|
import { UserTypingComponent } from 'src/chat21-core/utils/user-typing/user-typing.component';
|
|
45
45
|
import { AvatarProfileComponent } from 'src/app/components/utils/avatar-profile/avatar-profile.component';
|
|
46
|
+
import { CopilotSuggestionsComponent } from 'src/app/components/copilot-suggestions/copilot-suggestions.component';
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
@NgModule({
|
|
@@ -93,6 +94,7 @@ import { AvatarProfileComponent } from 'src/app/components/utils/avatar-profile/
|
|
|
93
94
|
// --------- footer --------- //
|
|
94
95
|
MessageTextAreaComponent,
|
|
95
96
|
CannedResponseComponent,
|
|
97
|
+
CopilotSuggestionsComponent,
|
|
96
98
|
|
|
97
99
|
|
|
98
100
|
TruncatePipe
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
here is ignored
|
|
62
62
|
</span>
|
|
63
63
|
|
|
64
|
-
<div class="overlay" *ngIf="!
|
|
64
|
+
<div class="overlay" *ngIf="SHOW_CANNED_RESPONSES || SHOW_COPILOT_SUGGESTIONS" (click)="this.SHOW_COPILOT_SUGGESTIONS=!this.SHOW_COPILOT_SUGGESTIONS; this.SHOW_CANNED_RESPONSES = !this.SHOW_CANNED_RESPONSES"></div>
|
|
65
65
|
|
|
66
66
|
<ng-template #content_welcome>
|
|
67
67
|
<!-- <div class="messageFirst">
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
<!-- ----------------------------------------------------------- -->
|
|
169
169
|
<!-- Canned responses -->
|
|
170
170
|
<!-- ----------------------------------------------------------- -->
|
|
171
|
-
<app-canned-response *ngIf="
|
|
171
|
+
<app-canned-response *ngIf="SHOW_CANNED_RESPONSES"
|
|
172
172
|
id="canned"
|
|
173
173
|
[canShowCanned]="canShowCanned"
|
|
174
174
|
[conversationWith]="conversationWith"
|
|
@@ -180,47 +180,19 @@
|
|
|
180
180
|
(onClickCanned)="replaceTagInMessage($event)"
|
|
181
181
|
(onClickAddCannedResponse)="presentCreateCannedResponseModal()">
|
|
182
182
|
</app-canned-response>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
<!-- <div id="canned" *ngIf="tagsCannedFilter.length > 0 && HIDE_CANNED_RESPONSES === false">
|
|
186
|
-
<div class="header">
|
|
187
|
-
<ion-item lines="none"> <ion-icon class="canned-response-icon-header" name="flash-outline"></ion-icon> Canned responses</ion-item>
|
|
188
|
-
<ion-item lines="none" (click)="presentCreateCannedResponseModal()">
|
|
189
|
-
<ion-icon class="canned-response-icon-header" name="add" slot="start"></ion-icon>
|
|
190
|
-
New response
|
|
191
|
-
</ion-item>
|
|
192
|
-
<ion-item lines="none" (click)="closeListCannedResponse()"> <ion-icon class="canned-response-icon-header" name="close" ></ion-icon></ion-item>
|
|
193
|
-
</div>
|
|
194
|
-
<ion-list class="canned-list" >
|
|
195
|
-
<ion-item button="true" [ngClass]="{'is_active_item': i == arrowkeyLocation}" lines="none"
|
|
196
|
-
class="canned-item no-ripple border" id="{{'canned-item_'+ i }}"
|
|
197
|
-
*ngFor="let canned of tagsCannedFilter; let i = index;"
|
|
198
|
-
(click)="replaceTagInMessage(canned, $event)">
|
|
199
|
-
<div class="cannedContent">
|
|
200
|
-
<ion-input [(ngModel)]="canned.title" class="title" id="{{'titleCanned_'+canned._id}}" [disabled]="canned.disabled"></ion-input>
|
|
201
|
-
<ion-input [(ngModel)]="canned.text" class="text" [disabled]="canned.disabled"></ion-input>
|
|
202
|
-
</div>
|
|
203
|
-
<ion-icon class="canned-item-icon" name="checkmark-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && !canned.disabled" (click)="onConfirmEditCanned(canned, $event)"></ion-icon>
|
|
204
|
-
<ion-icon class="canned-item-icon" name="pencil-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && canned.disabled" (click)="onEditCanned(canned, $event)"></ion-icon>
|
|
205
|
-
<ion-icon class="canned-item-icon" name="trash-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid" (click)="onDeleteCanned(canned, $event)"></ion-icon>
|
|
206
|
-
</ion-item>
|
|
207
|
-
|
|
208
|
-
<ion-item class="canned-item add-canned-response-wpr" button="true" lines="none" (click)="presentCreateCannedResponseModal()">
|
|
209
|
-
<ion-icon class="add-canned-response-icon" name="flash-outline"></ion-icon>
|
|
210
|
-
<span class="add-canned-response-add-icon">+</span>
|
|
211
|
-
<label class="add-canned-response-label" >{{translationMap?.get('AddNewCannedResponse')}}</label>
|
|
212
|
-
</ion-item>
|
|
213
|
-
</ion-list>
|
|
214
|
-
<!- - <ion-list class="canned-list" *ngIf="tagsCannedFilter.length === 0 && HIDE_CANNED_RESPONSES === false">
|
|
215
|
-
<ion-item button="true" >
|
|
216
|
-
There are no canned responses available
|
|
217
|
-
</ion-item>
|
|
218
|
-
</ion-list> - ->
|
|
219
|
-
</div> -->
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
183
|
|
|
184
|
+
<copilot-suggestions *ngIf="SHOW_COPILOT_SUGGESTIONS"
|
|
185
|
+
id="copilot"
|
|
186
|
+
[conversationWith]="conversationWith"
|
|
187
|
+
[conversationWithFullname]="conversationWithFullname"
|
|
188
|
+
[currentString]="messageStr"
|
|
189
|
+
[stylesMap]="styleMap"
|
|
190
|
+
[translationMap]="translationsMap"
|
|
191
|
+
(onLoadedSuggestions)="onLoadedSuggestions($event)"
|
|
192
|
+
(onClickSuggestion)="replaceSuggestionInMessage($event)">
|
|
193
|
+
</copilot-suggestions>
|
|
194
|
+
|
|
195
|
+
|
|
224
196
|
<!-- (eventReplaceMessageWithCanned)="replaceTagInMessage($event)" -->
|
|
225
197
|
<!-- [tagsCannedFilter]="tagsCannedFilter" -->
|
|
226
198
|
<!-- openInfoConversation {{openInfoConversation}} - isMobile {{isMobile}} -->
|
|
@@ -230,7 +202,6 @@
|
|
|
230
202
|
[channelType]="channelType"
|
|
231
203
|
[channel]="conversation?.attributes?.request_channel"
|
|
232
204
|
[tagsCannedFilter]="tagsCannedFilter"
|
|
233
|
-
[tagsCannedCount]="tagsCannedCount"
|
|
234
205
|
[areVisibleCAR]="areVisibleCAR"
|
|
235
206
|
[supportMode]="supportMode"
|
|
236
207
|
[leadInfo]="leadInfo"
|
|
@@ -6,24 +6,15 @@ import {
|
|
|
6
6
|
AfterViewInit,
|
|
7
7
|
ViewChild,
|
|
8
8
|
ElementRef,
|
|
9
|
-
Directive,
|
|
10
9
|
HostListener,
|
|
11
|
-
ChangeDetectorRef,
|
|
12
|
-
Renderer2,
|
|
13
|
-
isDevMode
|
|
14
10
|
} from '@angular/core'
|
|
15
11
|
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'
|
|
16
12
|
import {
|
|
17
13
|
ModalController,
|
|
18
14
|
ToastController,
|
|
19
|
-
PopoverController,
|
|
20
15
|
Platform,
|
|
21
16
|
ActionSheetController,
|
|
22
|
-
NavController,
|
|
23
17
|
IonContent,
|
|
24
|
-
IonTextarea,
|
|
25
|
-
IonButton,
|
|
26
|
-
IonInput,
|
|
27
18
|
} from '@ionic/angular'
|
|
28
19
|
|
|
29
20
|
// models
|
|
@@ -56,7 +47,6 @@ import {
|
|
|
56
47
|
MESSAGE_TYPE_INFO,
|
|
57
48
|
MESSAGE_TYPE_MINE,
|
|
58
49
|
MESSAGE_TYPE_OTHERS,
|
|
59
|
-
URL_SOUND_LIST_CONVERSATION,
|
|
60
50
|
TYPE_DIRECT,
|
|
61
51
|
TYPE_MSG_EMAIL,
|
|
62
52
|
TYPE_MSG_FORM,
|
|
@@ -88,12 +78,9 @@ import { takeUntil } from 'rxjs/operators'
|
|
|
88
78
|
import { TiledeskService } from '../../services/tiledesk/tiledesk.service'
|
|
89
79
|
import { NetworkService } from '../../services/network-service/network.service'
|
|
90
80
|
import { EventsService } from '../../services/events-service'
|
|
91
|
-
import { ScrollbarThemeDirective } from 'src/app/utils/scrollbar-theme.directive'
|
|
92
81
|
import { WebsocketService } from 'src/app/services/websocket/websocket.service';
|
|
93
82
|
import { Project } from 'src/chat21-core/models/projects';
|
|
94
|
-
import { AppStorageService } from 'src/chat21-core/providers/abstract/app-storage.service';
|
|
95
83
|
import { Globals } from 'src/app/utils/globals';
|
|
96
|
-
import { TriggerEvents } from 'src/app/services/triggerEvents/triggerEvents';
|
|
97
84
|
|
|
98
85
|
@Component({
|
|
99
86
|
selector: 'app-conversation-detail',
|
|
@@ -145,10 +132,11 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
145
132
|
|
|
146
133
|
public messageStr: string;
|
|
147
134
|
public tagsCannedFilter: Array<any> = [];
|
|
148
|
-
public
|
|
149
|
-
public HIDE_CANNED_RESPONSES: boolean = true
|
|
135
|
+
public SHOW_CANNED_RESPONSES: boolean = false
|
|
150
136
|
public canShowCanned: boolean = true
|
|
151
137
|
|
|
138
|
+
public SHOW_COPILOT_SUGGESTIONS: boolean = false;
|
|
139
|
+
|
|
152
140
|
public window: any = window
|
|
153
141
|
public styleMap: Map<string, string> = new Map()
|
|
154
142
|
|
|
@@ -695,7 +683,10 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
695
683
|
"WHATSAPP.LABEL_TOOLTIP",
|
|
696
684
|
"WHATSAPP.SELECT_MESSAGE_TEMPLATE",
|
|
697
685
|
"WHATSAPP.ERROR_WHATSAPP_NOT_INSTALLED",
|
|
698
|
-
"WHATSAPP.ERROR_WHATSAPP_GENERIC_ERROR"
|
|
686
|
+
"WHATSAPP.ERROR_WHATSAPP_GENERIC_ERROR",
|
|
687
|
+
|
|
688
|
+
"COPILOT.ASK_AI",
|
|
689
|
+
"COPILOT.NO_SUGGESTIONS_PRESENT"
|
|
699
690
|
|
|
700
691
|
]
|
|
701
692
|
|
|
@@ -1501,7 +1492,7 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
1501
1492
|
|
|
1502
1493
|
if (pos === -1) {
|
|
1503
1494
|
// this.tagsCannedFilter = []
|
|
1504
|
-
this.
|
|
1495
|
+
this.SHOW_CANNED_RESPONSES = false
|
|
1505
1496
|
}
|
|
1506
1497
|
// test
|
|
1507
1498
|
// var rest = message.substring(0, message.lastIndexOf("/") + 1);
|
|
@@ -1530,12 +1521,12 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
1530
1521
|
|
|
1531
1522
|
var after_slash = message.substring(message.lastIndexOf('/') + 1, message.length)
|
|
1532
1523
|
if (pos === 0 && after_slash.length === 1 && after_slash.trim() === '') {
|
|
1533
|
-
this.logger.log('[CONVS-DETAIL] - returnChangeTextArea after_slash --> there is a white space after ')
|
|
1534
|
-
this.
|
|
1524
|
+
this.logger.log('[CONVS-DETAIL] - returnChangeTextArea after_slash --> there is a white space after ');
|
|
1525
|
+
this.SHOW_CANNED_RESPONSES = false
|
|
1535
1526
|
// this.tagsCannedFilter = []
|
|
1536
1527
|
} else if (pos === 0 && after_slash.length === 0) {
|
|
1537
1528
|
this.logger.log('[CONVS-DETAIL] - returnChangeTextArea after_slash --> there is NOT a white space after')
|
|
1538
|
-
this.
|
|
1529
|
+
this.SHOW_CANNED_RESPONSES = true
|
|
1539
1530
|
}
|
|
1540
1531
|
|
|
1541
1532
|
if (pos > 0) {
|
|
@@ -1552,11 +1543,11 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
1552
1543
|
this.logger.log('[CONVS-DETAIL] - returnChangeTextArea --> afterSlash', afterSlash)
|
|
1553
1544
|
|
|
1554
1545
|
if (beforeSlash[beforeSlash.length - 1].indexOf(' ') >= 0 && afterSlash === '') {
|
|
1555
|
-
this.
|
|
1546
|
+
this.SHOW_CANNED_RESPONSES = true
|
|
1556
1547
|
} else if (beforeSlash[beforeSlash.length - 1].indexOf(' ') < 0 && afterSlash === '') {
|
|
1557
|
-
this.
|
|
1548
|
+
this.SHOW_CANNED_RESPONSES = false
|
|
1558
1549
|
} else if (beforeSlash[beforeSlash.length - 1].indexOf(' ') >= 0 && afterSlash === ' ') {
|
|
1559
|
-
this.
|
|
1550
|
+
this.SHOW_CANNED_RESPONSES = false
|
|
1560
1551
|
// this.tagsCannedFilter = []
|
|
1561
1552
|
}
|
|
1562
1553
|
}
|
|
@@ -1592,7 +1583,6 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
1592
1583
|
this.logger.log('[CONVS-DETAIL] onLoadedCannedResponses --> ', event)
|
|
1593
1584
|
if (event && event.length > 0) {
|
|
1594
1585
|
this.tagsCannedFilter = event
|
|
1595
|
-
this.tagsCannedCount = event.length
|
|
1596
1586
|
}
|
|
1597
1587
|
}
|
|
1598
1588
|
|
|
@@ -1628,13 +1618,6 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
1628
1618
|
|
|
1629
1619
|
}
|
|
1630
1620
|
|
|
1631
|
-
|
|
1632
|
-
closeListCannedResponse() {
|
|
1633
|
-
this.logger.log('[CONVS-DETAIL] close list canned . . . ')
|
|
1634
|
-
this.HIDE_CANNED_RESPONSES = true
|
|
1635
|
-
this.tagsCannedFilter = []
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
1621
|
async presentCreateCannedResponseModal(): Promise<any> {
|
|
1639
1622
|
const elTextArea = this.rowTextArea['el']
|
|
1640
1623
|
const textArea = elTextArea.getElementsByTagName('ion-textarea')[0]
|
|
@@ -1657,11 +1640,11 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
1657
1640
|
|
|
1658
1641
|
onClickOpenCannedResponses($event) {
|
|
1659
1642
|
this.logger.log('[CONVS-DETAIL] - onClickOpenCannedResponses ', $event)
|
|
1660
|
-
this.
|
|
1643
|
+
this.SHOW_CANNED_RESPONSES = !this.SHOW_CANNED_RESPONSES
|
|
1661
1644
|
|
|
1662
1645
|
//HIDE_CANNED_RESPONSES: true --> not show CANNED component
|
|
1663
1646
|
//HIDE_CANNED_RESPONSES: false --> show CANNED component and place '/' char in textarea
|
|
1664
|
-
if (
|
|
1647
|
+
if (this.SHOW_CANNED_RESPONSES) {
|
|
1665
1648
|
const elTextArea = this.rowTextArea['el']
|
|
1666
1649
|
const textArea = elTextArea.getElementsByTagName('ion-textarea')[0]
|
|
1667
1650
|
if (elTextArea) {
|
|
@@ -1676,6 +1659,39 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
1676
1659
|
}
|
|
1677
1660
|
}
|
|
1678
1661
|
|
|
1662
|
+
|
|
1663
|
+
/** COPILOT SUGGGESTIONS : start */
|
|
1664
|
+
onLoadedSuggestions(event){
|
|
1665
|
+
this.logger.log('[CONVS-DETAIL] onLoadedSuggestions --> ', event)
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
replaceSuggestionInMessage(suggestion, event?) {
|
|
1669
|
+
const elTextArea = this.rowTextArea['el']
|
|
1670
|
+
const textArea = elTextArea.getElementsByTagName('ion-textarea')[0] as HTMLInputElement;
|
|
1671
|
+
|
|
1672
|
+
this.logger.log('[CONVS-DETAIL] replaceSuggestionInMessage canned text ', suggestion.text)
|
|
1673
|
+
|
|
1674
|
+
// replace text
|
|
1675
|
+
var strTEMP = textArea.value + suggestion.text
|
|
1676
|
+
strTEMP = this.replacePlaceholderInCanned(strTEMP)
|
|
1677
|
+
this.logger.log('[CONVS-DETAIL] replaceSuggestionInMessage strSearch ', strTEMP)
|
|
1678
|
+
// strTEMP = this.replacePlaceholderInCanned(strTEMP);
|
|
1679
|
+
// textArea.value = '';
|
|
1680
|
+
// that.messageString = strTEMP;
|
|
1681
|
+
textArea.value = strTEMP
|
|
1682
|
+
this.insertAtCursor(textArea, '')
|
|
1683
|
+
this.setCaretPosition(textArea)
|
|
1684
|
+
// setTimeout(() => {
|
|
1685
|
+
// // textArea.focus();
|
|
1686
|
+
// textArea.setFocus()
|
|
1687
|
+
// // this.resizeTextArea()
|
|
1688
|
+
// }, 200)
|
|
1689
|
+
this.SHOW_COPILOT_SUGGESTIONS = !this.SHOW_COPILOT_SUGGESTIONS
|
|
1690
|
+
|
|
1691
|
+
}
|
|
1692
|
+
/** COPILOT SUGGGESTIONS : end */
|
|
1693
|
+
|
|
1694
|
+
|
|
1679
1695
|
setCaretPosition(ctrl) {
|
|
1680
1696
|
ctrl.value.trim()
|
|
1681
1697
|
ctrl.setFocus()
|
|
@@ -1868,6 +1884,9 @@ export class ConversationDetailPage implements OnInit, OnDestroy, AfterViewInit
|
|
|
1868
1884
|
if (event === 'email' || event === 'templates') {
|
|
1869
1885
|
this.getLeadDetail()
|
|
1870
1886
|
}
|
|
1887
|
+
if(event === 'copilot'){
|
|
1888
|
+
this.SHOW_COPILOT_SUGGESTIONS = !this.SHOW_COPILOT_SUGGESTIONS
|
|
1889
|
+
}
|
|
1871
1890
|
}
|
|
1872
1891
|
|
|
1873
1892
|
// -------------- START SCROLL/RESIZE -------------- //
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { CopilotService } from './copilot.service';
|
|
4
|
+
|
|
5
|
+
describe('CopilotService', () => {
|
|
6
|
+
let service: CopilotService;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
TestBed.configureTestingModule({});
|
|
10
|
+
service = TestBed.inject(CopilotService);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should be created', () => {
|
|
14
|
+
expect(service).toBeTruthy();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
2
|
+
import { Injectable } from '@angular/core';
|
|
3
|
+
import { AppConfigProvider } from '../app-config';
|
|
4
|
+
import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
|
|
5
|
+
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
6
|
+
import { map, filter } from 'rxjs/operators';
|
|
7
|
+
import * as uuid from 'uuid';
|
|
8
|
+
|
|
9
|
+
@Injectable({
|
|
10
|
+
providedIn: 'root'
|
|
11
|
+
})
|
|
12
|
+
export class CopilotService {
|
|
13
|
+
|
|
14
|
+
private apiUrl: string;
|
|
15
|
+
private logger: LoggerService = LoggerInstance.getInstance();
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
public http: HttpClient,
|
|
19
|
+
public appConfigProvider: AppConfigProvider
|
|
20
|
+
) {
|
|
21
|
+
this.logger.log('[COPILOT-SERVICE] HELLO !');
|
|
22
|
+
this.apiUrl = appConfigProvider.getConfig().apiUrl;
|
|
23
|
+
this.logger.log('[COPILOT-SERVICE] apiUrl ', this.apiUrl);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public getAll(token: string, projectid: string, conversWith: string) {
|
|
27
|
+
const cannedResponsesURL = this.apiUrl + projectid + "/copilot?request_id=" + conversWith;
|
|
28
|
+
this.logger.log('[COPILOT-SERVICE] getAllSuggestions - URL ', cannedResponsesURL);
|
|
29
|
+
|
|
30
|
+
const httpOptions = {
|
|
31
|
+
headers: new HttpHeaders({
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
Authorization: token
|
|
34
|
+
})
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return this.http.get(cannedResponsesURL, httpOptions).pipe(map((res: [any]) => {
|
|
38
|
+
let suggestions = res.filter(el => el !== null).map(el => ({ ...el, _id: uuid.v4()} ));
|
|
39
|
+
this.logger.log('[COPILOT-SERVICE] getCannedResponses - RES ', suggestions);
|
|
40
|
+
return suggestions
|
|
41
|
+
}))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|
package/src/assets/i18n/en.json
CHANGED
|
@@ -304,5 +304,9 @@
|
|
|
304
304
|
"SEND_EMAIL_ERROR":"⚠️ ATTENTION ⚠️ Error occurred while sending email. Please retry again",
|
|
305
305
|
"SUBJECT_OFFLINE_MESSAGE":"Tiledesk Offline message 💬",
|
|
306
306
|
"SEND_EMAIL_SUCCESS_OFFLINE_MESSAGE":"Message also sent by email 📩"
|
|
307
|
+
},
|
|
308
|
+
"COPILOT": {
|
|
309
|
+
"ASK_AI":"Ask to AI",
|
|
310
|
+
"NO_SUGGESTIONS_PRESENT":"No suggestions found"
|
|
307
311
|
}
|
|
308
312
|
}
|
package/src/assets/i18n/it.json
CHANGED
|
@@ -304,5 +304,9 @@
|
|
|
304
304
|
"SEND_EMAIL_ERROR":"⚠️ ATTENZIONE ⚠️Si è verificato un errore durante l'invio dell'email. Si prega di riprovare",
|
|
305
305
|
"SUBJECT_OFFLINE_MESSAGE":"Messaggio offline di Tiledesk 💬",
|
|
306
306
|
"SEND_EMAIL_SUCCESS_OFFLINE_MESSAGE":"Messaggio inviato anche via mail 📩"
|
|
307
|
-
}
|
|
307
|
+
},
|
|
308
|
+
"COPILOT": {
|
|
309
|
+
"ASK_AI":"Chiedi all'AI",
|
|
310
|
+
"NO_SUGGESTIONS_PRESENT":"Nessun suggerimento trovato"
|
|
311
|
+
}
|
|
308
312
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 15 15" height="15" width="15" fill="currentColor">
|
|
2
|
+
<path d="M8.4,2.8l-.9-2.5-.9,2.5c-.6,1.7-2,3.1-3.7,3.7l-2.5.9,2.5.9c1.7.6,3.1,2,3.7,3.7l.9,2.5.9-2.5c.6-1.7,2-3.1,3.7-3.7l2.5-.9-2.5-.9c-1.7-.6-3.1-2-3.7-3.7Z"/>
|
|
3
|
+
</svg>
|