@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 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
@@ -19,7 +19,7 @@ RUN ionic cordova build browser
19
19
 
20
20
  ### STAGE 2: Setup ###
21
21
 
22
- FROM nginx:1.14.1-alpine
22
+ FROM nginx:1.14.1-alpine as setup
23
23
 
24
24
  ## Copy our default nginx config
25
25
  COPY nginx.conf /etc/nginx/nginx.conf
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.12",
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 NOT CANNED ', canned.text)
271
+ this.logger.log('[CANNED] THERE IS CANNED ', canned.text)
272
+ this.onClickCanned.emit(canned)
276
273
  }
277
274
  }
278
275
 
@@ -1,29 +1,41 @@
1
1
  <ion-grid>
2
2
  <div class="footer-options-container">
3
- <!-- CHAT-OPTION -->
4
- <div class="section-option" id="chat" tooltip="{{translationMap?.get('LABEL_CHAT')}}" placement="top">
5
- <ion-button fill="clear" (click)="onOpenSection('chat')" [class.active]="section==='chat'">
6
- <ion-icon name="chatbubbles"></ion-icon>
7
- {{translationMap.get('LABEL_CHAT')}}
8
- </ion-button>
9
- </div>
10
- <!-- EMAIL-OPTION -->
11
- <div *ngIf="emailSection" class="section-option" id="email" tooltip="{{translationMap?.get('EMAIL.LABEL_TOOLTIP')}}" placement="top">
12
- <ion-button fill="clear" [class.active]="section==='email'" (click)="onOpenSection('email')" [disabled]="channelType === 'direct' || channel == CHANNEL_TYPE.WHATSAPP || channel == CHANNEL_TYPE.MESSENGER">
13
- <ion-icon name="mail-open"></ion-icon>
14
- {{translationMap.get('LABEL_EMAIL')}}
15
- </ion-button>
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 *ngIf="whatsappTemplatesSection" class="section-option" id="template" tooltip="{{translationMap?.get('WHATSAPP.LABEL_TOOLTIP')}}" placement="top">
19
- <ion-button fill="clear" [class.active]="section==='templates'" (click)="onOpenSection('templates');onOpenTemplateModal()" [disabled]="channel !== CHANNEL_TYPE.WHATSAPP">
20
- <ion-icon name="logo-whatsapp"></ion-icon>
21
- {{translationMap?.get('WHATSAPP.LABEL_TEMPLATES')}}
22
- </ion-button>
23
- </div>
24
-
25
- <div *ngIf="offlineMsgEmail && section==='chat' && channel !== CHANNEL_TYPE.WHATSAPP && messageString && (leadInfo?.presence['status']==='offline' && leadInfo?.hasEmail)" class="section-option offline-lead-tip" >
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
 
@@ -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: 12px;
45
+ font-size: 9px;
39
46
  width: 100%;
40
47
  display: block;
41
48
  text-align: right;
@@ -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="!HIDE_CANNED_RESPONSES"></div>
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="HIDE_CANNED_RESPONSES === false"
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"
@@ -152,7 +152,8 @@ ion-content {
152
152
  }
153
153
  }
154
154
 
155
- #canned {
155
+ #canned,
156
+ #copilot {
156
157
  z-index: 99999;
157
158
  width: 100%;
158
159
  // background: rgba(237,240,244,.9);
@@ -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 tagsCannedCount: number;
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.HIDE_CANNED_RESPONSES = true
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.HIDE_CANNED_RESPONSES = true
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.HIDE_CANNED_RESPONSES = false
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.HIDE_CANNED_RESPONSES = false
1546
+ this.SHOW_CANNED_RESPONSES = true
1556
1547
  } else if (beforeSlash[beforeSlash.length - 1].indexOf(' ') < 0 && afterSlash === '') {
1557
- this.HIDE_CANNED_RESPONSES = true
1548
+ this.SHOW_CANNED_RESPONSES = false
1558
1549
  } else if (beforeSlash[beforeSlash.length - 1].indexOf(' ') >= 0 && afterSlash === ' ') {
1559
- this.HIDE_CANNED_RESPONSES = true
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.HIDE_CANNED_RESPONSES = !this.HIDE_CANNED_RESPONSES
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 (!this.HIDE_CANNED_RESPONSES) {
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
+ }
@@ -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
  }
@@ -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>