Skip to content

Commit 4481a1a

Browse files
author
Nicola Lanzilotto
committed
Merge branch 'features/X-twilio-voice'
2 parents 412e76d + d619dfb commit 4481a1a

File tree

5 files changed

+200
-36
lines changed

5 files changed

+200
-36
lines changed

src/app/integrations/integrations.component.html

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,29 +175,74 @@
175175
</ext-integration>
176176

177177
</div>
178-
179-
<div *ngIf="integrationLocked === true" style="width: 100%;">
178+
179+
<div *ngIf="integrationLocked === true && ((profileType === 'payment' && subscriptionIsActive === true) || (profileType === 'free' && trialExpired === false))" style="width: 100%;">
180180
<div class="int-lock-container">
181181
<img src="assets/img/int/upgrade-icon.svg" alt="Upgrade Plan">
182182

183+
<p *ngIf="integrationSelectedName === INT_KEYS.TWILIO_VOICE" class="upgrade-title">
184+
{{ 'ContactUs' | translate }}!</p>
185+
186+
<p *ngIf="integrationSelectedName === INT_KEYS.TWILIO_VOICE" class="upgrade-subtitle">
187+
<span style="display: block;"> Voice brings real-time, human interaction into your workflows. </span>
188+
<span style="display: block;">With Twilio Voice, you can handle calls, automate voice interactions, and seamlessly switch between chat and voice.</span>
189+
</p>
190+
183191
<p *ngIf="integrationSelectedName === INT_KEYS.OPENAI" class="upgrade-title">
184192
{{ 'Integration.UpgradePlan' | translate }}</p>
193+
185194
<p *ngIf="integrationSelectedName === INT_KEYS.OPENAI" class="upgrade-subtitle">
186195
{{'Integration.OpenaiSubtitle1' | translate: translateparams }}</p>
196+
187197
<p *ngIf="integrationSelectedName === INT_KEYS.OPENAI" class="upgrade-subtitle">
188198
{{'Integration.OpenaiSubtitle2' | translate }}</p>
199+
189200
<p *ngIf="integrationSelectedName === INT_KEYS.OPENAI" class="upgrade-subtitle"
190201
[innerHTML]="'Integration.AvailableFromPlanShort' | translate: { plan_require: plan_require }">
191202
</p>
192203

193-
<p *ngIf="integrationSelectedName !== INT_KEYS.OPENAI" class="upgrade-title">
204+
<p *ngIf="integrationSelectedName !== INT_KEYS.OPENAI && integrationSelectedName !== INT_KEYS.TWILIO_VOICE" class="upgrade-title">
194205
{{'Integration.UpgradePlan' | translate }}</p>
195-
<p *ngIf="integrationSelectedName !== INT_KEYS.OPENAI" class="upgrade-subtitle"
206+
<p *ngIf="integrationSelectedName !== INT_KEYS.OPENAI && integrationSelectedName !== INT_KEYS.TWILIO_VOICE" class="upgrade-subtitle"
196207
[innerHTML]="'Integration.AvailableFromPlan' | translate: { plan_require: plan_require }">
197208
</p>
198209

199-
<button class="custom-upgrade-primary-button" (click)="goToPricing()">
200-
{{'Integration.UpgradeNow' | translate }}</button>
210+
211+
212+
<button *ngIf="integrationSelectedName !== INT_KEYS.TWILIO_VOICE"
213+
class="custom-upgrade-primary-button"
214+
(click)="goToPricing()">
215+
{{'Integration.UpgradeNow' | translate }}
216+
</button>
217+
<button *ngIf="integrationSelectedName === INT_KEYS.TWILIO_VOICE"
218+
class="custom-upgrade-primary-button"
219+
(click)="contactUs()">
220+
{{'ContactUs' | translate }}
221+
</button>
222+
</div>
223+
</div>
224+
225+
<div *ngIf="integrationLocked === true && ((profileType === 'free' && trialExpired === true) || (profileType === 'payment' && subscriptionIsActive === false))" style="width: 100%;">
226+
<div class="int-lock-container">
227+
<img src="assets/img/int/upgrade-icon.svg" alt="Upgrade Plan">
228+
229+
<p class="upgrade-title">
230+
{{ 'Pricing.SubscriptionPaymentProblem' | translate }}</p>
231+
232+
<p class="upgrade-subtitle">
233+
<span style="display: block;">
234+
{{ 'Pricing.WeWereUnableToAutomaticallyRenewYourSubscription' | translate }}.
235+
</span>
236+
<span style="display: block;">
237+
{{ 'Pricing.PleaseContactUs' | translate }} {{ 'Pricing.ToUpdateYourPaymentInformation' | translate }}.
238+
</span>
239+
</p>
240+
241+
<button
242+
class="custom-upgrade-primary-button"
243+
(click)="contactUsToUpgradePlan()">
244+
{{'ContactUs' | translate }}
245+
</button>
201246
</div>
202247
</div>
203248

src/app/integrations/integrations.component.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,10 @@ mat-expansion-panel ::ng-deep {
228228
//background: #3ea9f5;
229229
background: linear-gradient(45deg, #3167e2, #3167e2, #a736ff);
230230
border: none;
231-
border-radius: 6px;
232-
height: 30px;
231+
border-radius: 9999px;
232+
height: 40px;
233233
font-weight: 500;
234-
padding: 0px 8px;
234+
padding: 0px 26px;
235235
opacity: 0.8;
236236

237237
&:hover {

src/app/integrations/integrations.component.ts

Lines changed: 134 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { ProjectPlanService } from 'app/services/project-plan.service';
1414
import { PLAN_NAME } from 'app/utils/util';
1515
import { AppStoreService } from 'app/services/app-store.service';
1616
import { environment } from 'environments/environment';
17+
import { AppConfigService } from 'app/services/app-config.service';
1718

1819

1920
const swal = require('sweetalert');
@@ -70,6 +71,9 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
7071
subscriptionIsActive: boolean;
7172
profileType: string;
7273
user: any;
74+
salesEmail: string;
75+
public_Key: string;
76+
isVisiblePAY: boolean;
7377

7478
constructor(
7579
private auth: AuthService,
@@ -83,18 +87,21 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
8387
private brand: BrandService,
8488
public prjctPlanService: ProjectPlanService,
8589
private projectPlanService: ProjectPlanService,
86-
private appService: AppStoreService
90+
private appService: AppStoreService,
91+
public appConfigService: AppConfigService,
8792
) {
8893
const _brand = this.brand.getBrand();
8994
this.logger.log("[INTEGRATION-COMP] brand: ", _brand);
9095
this.translateparams = _brand;
96+
this.salesEmail = _brand['CONTACT_SALES_EMAIL'];
9197
// this.INTEGRATIONS_CLONE = JSON.parse(JSON.stringify(INTEGRATION_LIST_ARRAY))
9298

9399
}
94100

95101
ngOnInit(): void {
96102
this.getCurrentProject();
97103
this.getLoggedUser()
104+
this.getOSCODE();
98105
this.getProjectPlan();
99106
this.getBrowserVersion();
100107
this.listenSidebarIsOpened();
@@ -111,6 +118,53 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
111118
// UTILS FUNCTIONS - START
112119
// ------------------------------
113120

121+
getOSCODE() {
122+
this.public_Key = this.appConfigService.getConfig().t2y12PruGU9wUtEGzBJfolMIgK;
123+
this.logger.log('[INTEGRATION-COMP] AppConfigService getAppConfig public_Key', this.public_Key);
124+
125+
let keys = this.public_Key.split("-");
126+
this.logger.log('[INTEGRATION-COMP] PUBLIC-KEY - public_Key keys', keys)
127+
128+
keys.forEach(key => {
129+
130+
if (key.includes("PAY")) {
131+
132+
let pay = key.split(":");
133+
134+
if (pay[1] === "F") {
135+
this.isVisiblePAY = false;
136+
this.logger.log('[INTEGRATION-COMP] isVisiblePAY', this.isVisiblePAY)
137+
} else {
138+
this.isVisiblePAY = true;
139+
this.logger.log('[INTEGRATION-COMP] isVisiblePAY', this.isVisiblePAY)
140+
}
141+
}
142+
});
143+
144+
if (!this.public_Key.includes("PAY")) {
145+
this.isVisiblePAY = false;
146+
}
147+
}
148+
149+
getPayValue(): boolean {
150+
this.public_Key = this.appConfigService.getConfig().t2y12PruGU9wUtEGzBJfolMIgK;
151+
let keys = this.public_Key.split("-");
152+
153+
let payKey = keys.find((key) => key.startsWith('PAY'));
154+
if (payKey) {
155+
let payParts = payKey.split(':');
156+
let payValue = payParts[1];
157+
if (payValue === 'F') {
158+
return false;
159+
} else {
160+
return true;
161+
}
162+
}
163+
164+
// If PAY key doesn't exist, return false
165+
return false;
166+
}
167+
114168
getLoggedUser() {
115169
this.auth.user_bs
116170
.pipe(
@@ -156,10 +210,13 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
156210
this.profile_name = projectProfileData.profile_name;
157211
this.logger.log('[INTEGRATION-COMP] INTEGRATIONS profile_name ', this.profile_name)
158212

159-
this.trialExpired = projectProfileData.trial_expired
213+
160214
this.trialExpired = projectProfileData.trial_expired
161215
this.profileType = projectProfileData.profile_type
162216
this.subscriptionIsActive = projectProfileData.subscription_is_active
217+
// console.log('subscriptionIsActive', this.subscriptionIsActive)
218+
// console.log('profileType', this.profileType)
219+
// console.log('trialExpired', this.trialExpired)
163220

164221
this.customization = projectProfileData.customization;
165222

@@ -239,7 +296,7 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
239296

240297
return new Promise((resolve) => {
241298
this.appService.getApps().subscribe((response: any) => {
242-
299+
// this.logger.log("[INTEGRATION-COMP] getApps response ", response)
243300
let whatsappApp = response.apps.find(a => (a.title === APPS_TITLE.WHATSAPP && a.version === "v2"));
244301
if (environment['whatsappConfigUrl']) {
245302
if (whatsappApp) {
@@ -372,7 +429,7 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
372429
this.logger.log("[INTEGRATIONS]- onIntegrationSelect integration", integration)
373430
this.integrationSelectedType = 'none'
374431
this.integrationLocked = false;
375-
this.checkPlan(integration.plan).then(() => {
432+
this.checkPlan(integration, integration.plan).then(() => {
376433
this.integrationSelectedName = integration.key;
377434
this.logger.log("[INTEGRATIONS]- onIntegrationSelect integrationSelectedName", integration.key)
378435
this.logger.log("[INTEGRATIONS]- onIntegrationSelect this.integrations", this.integrations)
@@ -783,10 +840,44 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
783840
this.logger.log('[INTEGRATION-COMP] INTEGRATIONS ', this.INTEGRATIONS)
784841
}
785842

786-
checkPlan(integration_plan) {
787-
this.logger.log("INTEGRATIONS_KEYS checkPlan profile_name: " + this.profile_name + " integration_plan: " + integration_plan);
843+
checkPlan(integration, integration_plan) {
844+
// this.logger.log("INTEGRATIONS_KEYS checkPlan integration: " , integration);
845+
// this.logger.log("INTEGRATIONS_KEYS checkPlan profile_name: " + this.profile_name + " integration_plan: " + integration_plan);
846+
this.logger.log("INTEGRATIONS_KEYS checkPlan isVisiblePAY: " + this.getPayValue() + " integration_plan: " + integration_plan);
788847

789848
return new Promise((resolve, reject) => {
849+
// A = 'Growth',
850+
// B = 'Scale',
851+
// C = 'Plus',
852+
// D = 'Starter',
853+
// E = 'Pro',
854+
// EE = 'Business',
855+
// F = 'Custom'
856+
857+
// TWILIO_VOICE: Check trial/subscription status first, then customization
858+
// Reject if: trial is expired (profileType === 'free' && trialExpired === true),
859+
// or subscription is expired (profileType === 'payment' && subscriptionIsActive === false),
860+
// or getPayValue() is false, or customization doesn't exist, or voice_twilio key doesn't exist, or voice_twilio is false
861+
// Resolve only if trial/subscription is active, getPayValue() is true, and voice_twilio exists and is true
862+
if (integration && integration.key === this.INT_KEYS.TWILIO_VOICE) {
863+
// First check: if trial is expired (free profile with expired trial)
864+
if (this.profileType === 'free' && this.trialExpired === true) {
865+
reject(false);
866+
}
867+
// Second check: if subscription is expired (payment profile with inactive subscription)
868+
else if (this.profileType === 'payment' && this.subscriptionIsActive === false) {
869+
reject(false);
870+
} else if (!this.getPayValue()) {
871+
reject(false);
872+
} else if (!this.customization) {
873+
reject(false);
874+
} else if (!this.customization.hasOwnProperty(this.INT_KEYS.TWILIO_VOICE) || this.customization[this.INT_KEYS.TWILIO_VOICE] === false) {
875+
reject(false);
876+
} else {
877+
resolve(true);
878+
}
879+
return;
880+
}
790881

791882
// FREE or SANDBOX PLAN
792883
// if (this.profile_name === 'free' || this.profile_name === 'Sandbox') {
@@ -870,7 +961,9 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
870961
}
871962

872963
manageAppVisibility(projectProfileData) {
873-
964+
const isVisiblePAY = this.getPayValue();
965+
this.logger.log('[INTEGRATIONS] manageAppVisibility isVisiblePAY ', isVisiblePAY)
966+
874967
if (projectProfileData && projectProfileData.customization) {
875968

876969
if (projectProfileData.customization[this.INT_KEYS.WHATSAPP] === false) {
@@ -889,6 +982,11 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
889982
let index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.TWILIO_SMS);
890983
if (index != -1) { this.INTEGRATIONS.splice(index, 1) };
891984
}
985+
// Remove TWILIO_VOICE if getPayValue() is false or if customization.voice_twilio is false
986+
if (!isVisiblePAY || (projectProfileData.customization[this.INT_KEYS.TWILIO_VOICE] === false)) {
987+
let index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.TWILIO_VOICE);
988+
if (index != -1) { this.INTEGRATIONS.splice(index, 1) };
989+
}
892990

893991
// -----------------------------
894992
// VXML_VOICE
@@ -918,24 +1016,24 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
9181016
// TWILIO_VOICE
9191017
// -----------------------------
9201018
// Removes "Twilio voice" integration if in not activated in customization
921-
if (!projectProfileData.customization[this.INT_KEYS.TWILIO_VOICE] || projectProfileData.customization[this.INT_KEYS.TWILIO_VOICE] === false) {
922-
let index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.TWILIO_VOICE);
923-
if (index != -1) { this.INTEGRATIONS.splice(index, 1) };
924-
}
1019+
// if (!projectProfileData.customization[this.INT_KEYS.TWILIO_VOICE] || projectProfileData.customization[this.INT_KEYS.TWILIO_VOICE] === false) {
1020+
// let index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.TWILIO_VOICE);
1021+
// if (index != -1) { this.INTEGRATIONS.splice(index, 1) };
1022+
// }
9251023

9261024
// Restores the "Twilio voice" integration (use case: it was removed from the Integration array in a project where it was not active)
927-
if (projectProfileData.customization[this.INT_KEYS.TWILIO_VOICE] && projectProfileData.customization[this.INT_KEYS.TWILIO_VOICE] === true) {
928-
this.logger.log('[INTEGRATIONS] manageAppVisibility TWILIO_VOICE ')
929-
let index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.TWILIO_VOICE);
930-
if (index != -1) {
931-
this.logger.log('TWILIO_VOICE index A', index)
932-
} else if (index == -1) {
933-
this.logger.log('TWILIO_VOICE index B', index)
934-
const twilioVoiceObjct = INTEGRATION_LIST_ARRAY_CLONE.find(i => i.key === this.INT_KEYS.TWILIO_VOICE);
935-
this.logger.log('twilioVoiceObjct', twilioVoiceObjct)
936-
this.INTEGRATIONS.push(twilioVoiceObjct)
937-
}
938-
}
1025+
// if (projectProfileData.customization[this.INT_KEYS.TWILIO_VOICE] && projectProfileData.customization[this.INT_KEYS.TWILIO_VOICE] === true) {
1026+
// this.logger.log('[INTEGRATIONS] manageAppVisibility TWILIO_VOICE ')
1027+
// let index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.TWILIO_VOICE);
1028+
// if (index != -1) {
1029+
// this.logger.log('TWILIO_VOICE index A', index)
1030+
// } else if (index == -1) {
1031+
// this.logger.log('TWILIO_VOICE index B', index)
1032+
// const twilioVoiceObjct = INTEGRATION_LIST_ARRAY_CLONE.find(i => i.key === this.INT_KEYS.TWILIO_VOICE);
1033+
// this.logger.log('twilioVoiceObjct', twilioVoiceObjct)
1034+
// this.INTEGRATIONS.push(twilioVoiceObjct)
1035+
// }
1036+
// }
9391037

9401038

9411039
let index = this.INTEGRATIONS.findIndex(i => i.category === INTEGRATIONS_CATEGORIES.CHANNEL);
@@ -950,14 +1048,25 @@ export class IntegrationsComponent implements OnInit, OnDestroy {
9501048
let vxml_voice_index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.VXML_VOICE);
9511049
if (vxml_voice_index != -1) { this.INTEGRATIONS.splice(vxml_voice_index, 1) };
9521050

953-
let twilio_voice_index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.TWILIO_VOICE);
954-
if (twilio_voice_index != -1) { this.INTEGRATIONS.splice(twilio_voice_index, 1) };
1051+
// Remove TWILIO_VOICE if getPayValue() is false (even when customization is not present)
1052+
if (!isVisiblePAY) {
1053+
let twilio_voice_index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.TWILIO_VOICE);
1054+
if (twilio_voice_index != -1) { this.INTEGRATIONS.splice(twilio_voice_index, 1) };
1055+
}
9551056

9561057
}
9571058

9581059
this.integrationListReady = true;
9591060
}
9601061

1062+
contactUs() {
1063+
window.open(`mailto:${this.salesEmail}?subject=Enable Twilio Voice for project id ${this.projectID}`);
1064+
}
1065+
1066+
contactUsToUpgradePlan(){
1067+
window.open(`mailto:${this.salesEmail}?subject=Upgrade plan (subscription expired) for project id ${this.projectID}`);
1068+
}
1069+
9611070
trackSavedIntegration(integrationName, integrationisVerified) {
9621071
this.logger.log('[INTEGRATIONS] trackSavedIntegration integrationName ', integrationName)
9631072
this.logger.log('[INTEGRATIONS] trackSavedIntegration integrationisVerified ', integrationisVerified)

0 commit comments

Comments
 (0)