@chat21/chat21-ionic 3.4.26 → 3.4.27-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
@@ -8,6 +8,13 @@
8
8
  ### **Copyrigth**:
9
9
  *Tiledesk SRL*
10
10
 
11
+ # 3.4.27-rc2
12
+ - **bug-fixed**: cannede responses role
13
+
14
+ # 3.4.27-rc1
15
+ - **added**: managed canned responses with roles
16
+ - **changed**: name in info mesage
17
+
11
18
  # 3.4.26 in PROD
12
19
 
13
20
  # 3.4.26-rc2
@@ -16,6 +23,7 @@
16
23
  # 3.4.26-rc1
17
24
  - **added**: tiledesk_projectID query param to manage user status
18
25
  - **added**: token to managane ticket feature
26
+ - **added**: getOsCode login into utils.ts
19
27
 
20
28
  # 3.4.25 in PROD
21
29
  - **changed**: pipe marked to support malicious text input
@@ -38,6 +46,24 @@
38
46
  # 3.4.22 in PROD
39
47
  - **added**: managed allowed_upload_extentions from project settings
40
48
 
49
+ # 3.4.21-rc6
50
+ - **added**: managed allowed_upload_extentions from project settings
51
+
52
+ # 3.4.21-rc5
53
+ - **added**: setConversation as read when agent click on it
54
+
55
+ # 3.4.21-rc4
56
+ - **added**: ability to init and decrement new conversation count badge
57
+
58
+ # 3.4.21-rc3
59
+ - **changed**: badge notification for agentDesktop
60
+
61
+ # 3.4.21-rc2
62
+ - **added**: count in newConversation handler event
63
+
64
+ # 3.4.21-rc1
65
+ - **added**: implement badge notification for agentDesktop sw when new conversation is assigned to logged agent
66
+
41
67
  # 3.4.21 in PROD
42
68
 
43
69
  # 3.4.20 in PROD
@@ -51,8 +77,8 @@
51
77
  - **bug-fixed**: minor fix on ion-texarea element with allowed url domain
52
78
 
53
79
  # 3.4.19-rc1
54
- - added: ability to check for if emoji is allowd to be sent in message textarea
55
- - added: ability to check for if url domain is allowd to be sent in message textarea
80
+ - **added**: ability to check for if emoji is allowd to be sent in message textarea
81
+ - **added**: ability to check for if url domain is allowd to be sent in message textarea
56
82
 
57
83
  # 3.4.18 in PROD
58
84
 
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.26",
4
+ "version": "3.4.27-rc2",
5
5
  "license": "MIT License",
6
6
  "homepage": "https://tiledesk.com/",
7
7
  "repository": {
@@ -44,6 +44,7 @@ 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 { ProjectUsersService } from './services/project_users/project-users.service';
47
48
 
48
49
  @Component({
49
50
  selector: 'app-root',
@@ -142,6 +143,7 @@ export class AppComponent implements OnInit {
142
143
  /**TILEDESK SERVICES */
143
144
  private tiledeskService: TiledeskService,
144
145
  private projectService: ProjectService,
146
+ private projectUsersService: ProjectUsersService,
145
147
  private contactsService: ContactsService
146
148
  ) {
147
149
 
@@ -168,6 +170,7 @@ export class AppComponent implements OnInit {
168
170
  }, { capture: true });
169
171
  }
170
172
 
173
+
171
174
  listenChatAlreadyOpenWithoutParamsInMobileMode() {
172
175
  this.events.subscribe('noparams:mobile', (isAlreadyOpenInMobileMode) => {
173
176
  // console.log('[APP-COMP] Chat is Already Open In Mobile Mode ', isAlreadyOpenInMobileMode)
@@ -332,7 +335,7 @@ export class AppComponent implements OnInit {
332
335
 
333
336
  listenToPostMsgs() {
334
337
  window.addEventListener("message", (event) => {
335
- this.logger.log("[APP-COMP] message event ", event);
338
+ // this.logger.log("[APP-COMP] message event ", event);
336
339
 
337
340
  if (event && event.data && event.data.action && event.data.parameter) {
338
341
  if (event.data.action === 'openJoinConversationModal') {
@@ -1110,6 +1113,7 @@ export class AppComponent implements OnInit {
1110
1113
  if (conversation && conversation.is_new === true && this.isInitialized) {
1111
1114
  this.manageTabNotification('conv_added', conversation.sound)
1112
1115
  this.manageEventNewConversation(conversation)
1116
+ this.setNotification();
1113
1117
  }
1114
1118
  if(conversation) this.updateConversationsOnStorage()
1115
1119
  });
@@ -1174,6 +1178,7 @@ export class AppComponent implements OnInit {
1174
1178
 
1175
1179
  this.tiledeskService.initialize(serverBaseURL)
1176
1180
  this.projectService.initialize(serverBaseURL)
1181
+ this.projectUsersService.initialize(serverBaseURL)
1177
1182
  this.contactsService.initialize(serverBaseURL)
1178
1183
  // this.chatManager.startApp();
1179
1184
 
@@ -1344,7 +1349,10 @@ export class AppComponent implements OnInit {
1344
1349
 
1345
1350
  subscribeConversationSelected= (conversation: ConversationModel) => {
1346
1351
  if(conversation && conversation.is_new){
1347
- this.audio_NewConv.pause()
1352
+ this.audio_NewConv.pause();
1353
+ this.conversationsHandlerService.setConversationRead(conversation.uid)
1354
+ //UPDATE NOTIFICATION FOR NEW CONVERSATION COUNT
1355
+ this.setNotification();
1348
1356
  }
1349
1357
  }
1350
1358
 
@@ -1420,6 +1428,9 @@ export class AppComponent implements OnInit {
1420
1428
  this.logger.debug('[APP-COMP]-CONVS - INIT CONV CONVS 2', conversations)
1421
1429
  this.events.publish('appcompSubscribeToConvs:loadingIsActive', false);
1422
1430
  }
1431
+
1432
+ //INIT NOTIFICATION FOR NEW CONVERSATION COUNT
1433
+ this.setNotification();
1423
1434
  });
1424
1435
 
1425
1436
  }
@@ -1636,6 +1647,14 @@ export class AppComponent implements OnInit {
1636
1647
  this.triggerEvents.triggerOnNewConversationInit(conversation)
1637
1648
  }
1638
1649
 
1650
+ private setNotification() {
1651
+ this.logger.log('[APP-COMP] setNotification for NEW CONVERSATION');
1652
+ if(window['AGENTDESKTOP']){
1653
+ this.logger.log('[APP-COMP] manageNotification AGENTDESKTOP exist', window['AGENTDESKTOP']);
1654
+ window['AGENTDESKTOP']['TAB'].Badge(this.conversationsHandlerService.countIsNew().toString())
1655
+ }
1656
+ }
1657
+
1639
1658
 
1640
1659
  @HostListener('document:visibilitychange', [])
1641
1660
  visibilitychange() {
@@ -1,20 +1,30 @@
1
1
  <div>
2
- <div class="canned-list" *ngIf="tagsCannedFilter.length > 0">
3
- <ion-item button="true" [ngClass]="{'is_active_item': i == arrowkeyLocation}" lines="none"
4
- class="canned-item no-ripple border" id="{{'canned-item_'+ i }}"
5
- *ngFor="let canned of tagsCannedFilter; let i = index;"
6
- (click)="onClickCannedFN(canned, $event)">
7
- <div class="cannedContent">
8
- <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.title" class="title" id="{{'titleCanned_'+canned._id}}" #title></ion-input>
9
- <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.text" class="text truncate"></ion-input>
2
+ <div class="canned-list" *ngIf="!showLoading">
3
+ <span *ngIf="tagsCannedFilter.length > 0">
4
+ <ion-item button="true" [ngClass]="{'is_active_item': i == arrowkeyLocation}" lines="none"
5
+ class="canned-item no-ripple border" id="{{'canned-item_'+ i }}"
6
+ *ngFor="let canned of tagsCannedFilter; let i = index;"
7
+ (click)="onClickCannedFN(canned, $event)">
8
+ <div class="cannedContent">
9
+ <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.title" class="title" id="{{'titleCanned_'+canned._id}}" #title></ion-input>
10
+ <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.text" class="text truncate"></ion-input>
11
+ </div>
12
+ <!-- <ion-icon class="canned-item-icon" name="pin" src="assets/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
- <!-- <ion-icon class="canned-item-icon" name="pin" src="assets/images/pin.svg" slot=end *ngIf="canned.pinned" (click)="onPinCanned(canned, $event)"></ion-icon>
12
- <ion-icon class="canned-item-icon" name="pin" src="assets/images/pinned.svg" slot=end (click)="onUnPinCanned(canned, $event)"></ion-icon> -->
13
- <ion-icon class="canned-item-icon" name="checkmark-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && !canned.disabled" (click)="onConfirmEditCanned(canned, $event)"></ion-icon>
14
- <ion-icon class="canned-item-icon" name="pencil-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && canned.disabled" (click)="onEditCanned(canned, $event)"></ion-icon>
15
- <ion-icon class="canned-item-icon" name="trash-bin-outline" slot=end *ngIf="canned.createdBy === loggedUser.uid" (click)="onDeleteCanned(canned, $event)"></ion-icon>
16
- </ion-item>
17
- <ion-item class="canned-item add-canned-response-wpr" button="true" lines="none" (click)="onClickAddCannedResponseFN()">
26
+ </div>
27
+ <ion-item *ngIf="roles[PERMISSIONS.CANNED_RESPONSES_CREATE]" class="canned-item add-canned-response-wpr" button="true" lines="none" (click)="onClickAddCannedResponseFN()">
18
28
  <ion-icon class="add-canned-response-icon" name="flash-outline"></ion-icon>
19
29
  <span class="add-canned-response-add-icon">+</span>
20
30
  <label class="add-canned-response-label" >{{translationMap?.get('AddNewCannedResponse')}}</label>
@@ -33,12 +43,5 @@
33
43
  <div class="label">{{translationMap.get('LABEL_LOADING')}}</div>
34
44
  </div>
35
45
  </div>
36
- <div class="no-data" *ngIf="tagsCannedFilter.length === 0 && !showLoading">
37
- <div class="container">
38
- <ion-item button="false" lines="none" class="canned-item no-ripple border">
39
- <ion-icon name="cloud-offline" slot="start"></ion-icon>
40
- <ion-label>{{translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE')}}</ion-label>
41
- </ion-item>
42
- </div>
43
- </div>
46
+
44
47
  </div>
@@ -9,6 +9,7 @@ import { TiledeskAuthService } from 'src/chat21-core/providers/tiledesk/tiledesk
9
9
  import { compareValues, htmlEntities } from 'src/chat21-core/utils/utils';
10
10
  import { getProjectIdSelectedConversation } from 'src/chat21-core/utils/utils-message';
11
11
  import { PLAN_NAME } from 'src/chat21-core/utils/constants';
12
+ import { PERMISSIONS } from 'src/app/utils/permissions.constants';
12
13
 
13
14
  @Component({
14
15
  selector: 'app-canned-response',
@@ -21,7 +22,7 @@ export class CannedResponseComponent implements OnInit {
21
22
  @Input() conversationWith: string;
22
23
  @Input() conversationWithFullname: string;
23
24
  @Input() currentString: string;
24
- @Input() canShowCanned: boolean = true;
25
+ @Input() roles: Array<string>;
25
26
  @Input() stylesMap: Map<string, string>;
26
27
  @Input() translationMap: Map<string, string>;
27
28
  @Output() onLoadedCannedResponses = new EventEmitter<[any]>();
@@ -36,6 +37,7 @@ export class CannedResponseComponent implements OnInit {
36
37
 
37
38
  public arrowkeyLocation = -1
38
39
 
40
+ PERMISSIONS = PERMISSIONS
39
41
 
40
42
  private logger: LoggerService = LoggerInstance.getInstance();
41
43
  constructor(
@@ -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">
@@ -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;
@@ -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.tiledeskService.getProjectUsersByProjectId(projctid)
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.tiledeskService.getProjectUsersByProjectId( projctid)
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
- [canShowCanned]="canShowCanned"
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 { getOSCode } from 'src/app/utils/utils';
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.canShowCanned = this.projectPlanUtils.checkPlanIsExpired(project)
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
@@ -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);
@@ -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
@@ -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 fullname, so use hes useruid)
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 fullname, so use hes useruid)
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;