mirror of
https://github.com/mozilla/fxa.git
synced 2025-12-13 20:36:41 +01:00
polish(payments-next): Adjust styling and server action for terms
Because: * A few code style and css-style tweaks were needed as a follow to the terms page release This commit: * addresses some of the outcomes from style conversations Closes #N/A
This commit is contained in:
@@ -2,9 +2,8 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import { Breadcrumbs, Header } from '@fxa/payments/ui';
|
import { Header } from '@fxa/payments/ui';
|
||||||
import { auth } from 'apps/payments/next/auth';
|
import { auth } from 'apps/payments/next/auth';
|
||||||
import { config } from 'apps/payments/next/config';
|
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
@@ -16,19 +15,13 @@ export default async function ChurnLayout({
|
|||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-[calc(100vh_-_4rem)] bg-white tablet:bg-grey-10 flex flex-col justify-start">
|
<>
|
||||||
<Header
|
<Header
|
||||||
auth={{
|
auth={{
|
||||||
user: session?.user,
|
user: session?.user,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs
|
<>{children}</>
|
||||||
contentServerUrl={config.contentServerUrl}
|
</>
|
||||||
paymentsNextUrl={config.paymentsNextHostedUrl}
|
|
||||||
/>
|
|
||||||
<div className="flex flex-col tablet:pt-20 items-center flex-1">
|
|
||||||
<div className="flex justify-center max-w-lg">{children}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
loyalty-discount-terms-heading = Terms and Restrictions
|
loyalty-discount-terms-heading = Terms and restrictions
|
||||||
loyalty-discount-terms-support = Contact Support
|
loyalty-discount-terms-support = Contact Support
|
||||||
loyalty-discount-terms-support-aria = Contact Support
|
# $productName (String) - The name of the product to create subscription, e.g. Mozilla VPN
|
||||||
|
loyalty-discount-terms-contact-support-product-aria = Contact Support for { $productName }
|
||||||
|
not-found-page-title-terms = Page not found
|
||||||
|
not-found-page-description-terms = The page you’re looking for does not exist.
|
||||||
|
not-found-page-button-terms-manage-subscriptions = Manage subscriptions
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import { headers } from 'next/headers';
|
||||||
|
import { PageNotFound } from '@fxa/payments/ui';
|
||||||
|
import { getApp } from '@fxa/payments/ui/server';
|
||||||
|
|
||||||
|
export enum PaymentsPage {
|
||||||
|
Subscriptions = 'subscriptions',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
|
const acceptLanguage = headers().get('accept-language');
|
||||||
|
const l10n = getApp().getL10n(acceptLanguage);
|
||||||
|
return (
|
||||||
|
<PageNotFound
|
||||||
|
header={l10n.getString('not-found-page-title-terms', 'Page not found')}
|
||||||
|
description={l10n.getString(
|
||||||
|
'not-found-page-description-terms',
|
||||||
|
'The page you’re looking for does not exist.'
|
||||||
|
)}
|
||||||
|
button={l10n.getString(
|
||||||
|
'not-found-page-button-terms-manage-subscriptions',
|
||||||
|
'Manage subscriptions'
|
||||||
|
)}
|
||||||
|
paymentsPage={PaymentsPage.Subscriptions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import { getApp } from '@fxa/payments/ui/server';
|
|||||||
import { headers } from 'next/headers';
|
import { headers } from 'next/headers';
|
||||||
import { URLSearchParams } from 'url';
|
import { URLSearchParams } from 'url';
|
||||||
import { SubplatInterval } from '@fxa/payments/customer';
|
import { SubplatInterval } from '@fxa/payments/customer';
|
||||||
import { BaseButton, ButtonVariant } from '@fxa/payments/ui';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
export default async function ChurnTerms({
|
export default async function ChurnTerms({
|
||||||
params,
|
params,
|
||||||
@@ -37,45 +37,61 @@ export default async function ChurnTerms({
|
|||||||
selectedLanguage: locale,
|
selectedLanguage: locale,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const content = churnIntervention.churnInterventions.at(0);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!content ||
|
||||||
|
!content.termsHeading ||
|
||||||
|
!Array.isArray(content.termsDetails) ||
|
||||||
|
content.termsDetails.length === 0
|
||||||
|
) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col tablet:bg-white p-8 tablet:rounded-lg tablet:shadow-lg">
|
<section
|
||||||
<h1 className="font-bold text-lg my-1">
|
className="flex tablet:items-center justify-center min-h-[calc(100vh_-_4rem)] tablet:min-h-[calc(100vh_-_5rem)]"
|
||||||
{l10n.getString(
|
aria-labelledby="loyalty-discount-terms"
|
||||||
'loyalty-discount-terms-heading',
|
>
|
||||||
'Terms and Restrictions'
|
<div className="max-w-xl flex flex-col p-6 pt-10 tablet:bg-white tablet:border tablet:border-grey-200 tablet:opacity-100 tablet:p-8 tablet:rounded-xl tablet:shadow-[0_0px_10px_rgba(0,0,0,0.08)]">
|
||||||
)}
|
<h1
|
||||||
</h1>
|
id="loyalty-discount-terms"
|
||||||
<h1 className="font-bold text-lg my-1">
|
className="font-semibold text-xl leading-8"
|
||||||
{`${churnIntervention.churnInterventions[0].termsHeading}`}
|
|
||||||
</h1>
|
|
||||||
<ul className="list-disc ml-5 my-2 marker:text-xs text-sm font-light [&_li]:leading-5">
|
|
||||||
{churnIntervention.churnInterventions[0].termsDetails.map(
|
|
||||||
(term, index) => (
|
|
||||||
<li key={index}>{term}</li>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<div className="flex justify-start">
|
|
||||||
<LinkExternal
|
|
||||||
href={`${churnIntervention.churnInterventions[0].supportUrl}${searchParamsString}`}
|
|
||||||
aria-label={l10n.getString(
|
|
||||||
'loyalty-discount-terms-support-aria',
|
|
||||||
'Contact Support'
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<BaseButton
|
{l10n.getString(
|
||||||
variant={ButtonVariant.SubscriptionManagementSecondary}
|
'loyalty-discount-terms-heading',
|
||||||
className="w-40 mt-4"
|
'Terms and restrictions'
|
||||||
|
)}
|
||||||
|
</h1>
|
||||||
|
<h2 className="font-semibold text-xl leading-8">
|
||||||
|
{`${content.termsHeading}`}
|
||||||
|
</h2>
|
||||||
|
<div className="mt-3 mx-6 mb-6 tablet:mx-10">
|
||||||
|
<ul className="font-light leading-6 list-disc marker:text-sm marker:leading-normal">
|
||||||
|
{content.termsDetails.map((term, index) => (
|
||||||
|
<li key={index}>{term}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="flex">
|
||||||
|
<LinkExternal
|
||||||
|
className="border box-border font-header rounded text-center py-2 px-5 border-grey-200 w-auto bg-grey-10 font-semibold hover:bg-grey-50 text-grey-700"
|
||||||
|
href={`${content.supportUrl}${searchParamsString}`}
|
||||||
|
aria-label={l10n.getString(
|
||||||
|
'loyalty-discount-terms-contact-support-product-aria',
|
||||||
|
{
|
||||||
|
productName: content.productName,
|
||||||
|
},
|
||||||
|
`Contact support for ${content.productName}`
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<span>
|
{l10n.getString(
|
||||||
{l10n.getString(
|
'loyalty-discount-terms-support',
|
||||||
'loyalty-discount-terms-support',
|
'Contact Support'
|
||||||
'Contact Support'
|
)}
|
||||||
)}
|
</LinkExternal>
|
||||||
</span>
|
</div>
|
||||||
</BaseButton>
|
|
||||||
</LinkExternal>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
26
libs/payments/management/src/lib/churn-intervention.error.ts
Normal file
26
libs/payments/management/src/lib/churn-intervention.error.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import { BaseError } from '@fxa/shared/error';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChurnInterventionError is not intended for direct use, except for type-checking errors.
|
||||||
|
* When throwing a new ChurnInterventionError, create a unique extension of the class.
|
||||||
|
*/
|
||||||
|
export class ChurnInterventionError extends BaseError {
|
||||||
|
constructor(message: string, info: Record<string, any>, cause?: Error) {
|
||||||
|
super(message, { info, cause });
|
||||||
|
this.name = 'ChurnInterventionError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChurnInterventionProductIdentifierMissingError extends ChurnInterventionError {
|
||||||
|
constructor() {
|
||||||
|
super(
|
||||||
|
'Either stripeProductId or offeringApiIdentifier must be provided',
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
this.name = 'ChurnInterventionProductIdentifierMissingError';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,14 @@
|
|||||||
import { Inject, Injectable, Logger, type LoggerService } from '@nestjs/common';
|
import { Inject, Injectable, Logger, type LoggerService } from '@nestjs/common';
|
||||||
import { ChurnInterventionManager } from '@fxa/payments/cart';
|
import { ChurnInterventionManager } from '@fxa/payments/cart';
|
||||||
import {
|
import {
|
||||||
|
ChurnInterventionByProductIdResultUtil,
|
||||||
ProductConfigurationManager,
|
ProductConfigurationManager,
|
||||||
} from '@fxa/shared/cms';
|
} from '@fxa/shared/cms';
|
||||||
import { SubscriptionManagementService } from './subscriptionManagement.service';
|
import { SubscriptionManagementService } from './subscriptionManagement.service';
|
||||||
import {
|
import { StatsDService } from '@fxa/shared/metrics/statsd';
|
||||||
StatsDService,
|
|
||||||
} from '@fxa/shared/metrics/statsd';
|
|
||||||
import { StatsD } from 'hot-shots';
|
import { StatsD } from 'hot-shots';
|
||||||
|
import { SubplatInterval } from '@fxa/payments/customer';
|
||||||
|
import { ChurnInterventionProductIdentifierMissingError } from './churn-intervention.error';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ChurnInterventionService {
|
export class ChurnInterventionService {
|
||||||
@@ -20,10 +21,9 @@ export class ChurnInterventionService {
|
|||||||
private churnInterventionManager: ChurnInterventionManager,
|
private churnInterventionManager: ChurnInterventionManager,
|
||||||
private subscriptionManagementService: SubscriptionManagementService,
|
private subscriptionManagementService: SubscriptionManagementService,
|
||||||
@Inject(StatsDService) private statsd: StatsD,
|
@Inject(StatsDService) private statsd: StatsD,
|
||||||
@Inject(Logger) private log: LoggerService,
|
@Inject(Logger) private log: LoggerService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|
||||||
async getChurnInterventionForCustomerId(
|
async getChurnInterventionForCustomerId(
|
||||||
customerId: string,
|
customerId: string,
|
||||||
churnInterventionId: string
|
churnInterventionId: string
|
||||||
@@ -34,11 +34,47 @@ export class ChurnInterventionService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getChurnInterventionForProduct(
|
||||||
|
interval: SubplatInterval,
|
||||||
|
churnType: 'cancel' | 'stay_subscribed',
|
||||||
|
stripeProductId?: string,
|
||||||
|
offeringApiIdentifier?: string,
|
||||||
|
acceptLanguage?: string,
|
||||||
|
selectedLanguage?: string
|
||||||
|
) {
|
||||||
|
let util: ChurnInterventionByProductIdResultUtil;
|
||||||
|
if (stripeProductId) {
|
||||||
|
util = await this.productConfigurationManager.getChurnIntervention(
|
||||||
|
interval,
|
||||||
|
churnType,
|
||||||
|
stripeProductId,
|
||||||
|
null,
|
||||||
|
acceptLanguage,
|
||||||
|
selectedLanguage
|
||||||
|
);
|
||||||
|
} else if (offeringApiIdentifier) {
|
||||||
|
util = await this.productConfigurationManager.getChurnIntervention(
|
||||||
|
interval,
|
||||||
|
churnType,
|
||||||
|
null,
|
||||||
|
offeringApiIdentifier,
|
||||||
|
acceptLanguage,
|
||||||
|
selectedLanguage
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new ChurnInterventionProductIdentifierMissingError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
churnInterventions: util.getTransformedChurnInterventionByProductId(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async determineStaySubscribedEligibility(
|
async determineStaySubscribedEligibility(
|
||||||
uid: string,
|
uid: string,
|
||||||
subscriptionId: string,
|
subscriptionId: string,
|
||||||
acceptLanguage?: string | null,
|
acceptLanguage?: string | null,
|
||||||
selectedLanguage?: string,
|
selectedLanguage?: string
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const cmsChurnResult =
|
const cmsChurnResult =
|
||||||
@@ -49,7 +85,8 @@ export class ChurnInterventionService {
|
|||||||
selectedLanguage
|
selectedLanguage
|
||||||
);
|
);
|
||||||
|
|
||||||
const cmsChurnInterventionEntries = cmsChurnResult.getTransformedChurnInterventionByProductId();
|
const cmsChurnInterventionEntries =
|
||||||
|
cmsChurnResult.getTransformedChurnInterventionByProductId();
|
||||||
if (!cmsChurnInterventionEntries.length) {
|
if (!cmsChurnInterventionEntries.length) {
|
||||||
this.statsd.increment('stay_subscribed_eligibility', {
|
this.statsd.increment('stay_subscribed_eligibility', {
|
||||||
eligibility: 'ineligible',
|
eligibility: 'ineligible',
|
||||||
@@ -59,16 +96,20 @@ export class ChurnInterventionService {
|
|||||||
isEligible: false,
|
isEligible: false,
|
||||||
reason: 'no_churn_intervention_found',
|
reason: 'no_churn_intervention_found',
|
||||||
cmsChurnInterventionEntry: null,
|
cmsChurnInterventionEntry: null,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const cmsChurnInterventionEntry = cmsChurnInterventionEntries[0];
|
const cmsChurnInterventionEntry = cmsChurnInterventionEntries[0];
|
||||||
const redemptionCount = await this.churnInterventionManager.getRedemptionCountForUid(
|
const redemptionCount =
|
||||||
uid,
|
await this.churnInterventionManager.getRedemptionCountForUid(
|
||||||
cmsChurnInterventionEntry.churnInterventionId
|
uid,
|
||||||
);
|
cmsChurnInterventionEntry.churnInterventionId
|
||||||
|
);
|
||||||
|
|
||||||
if (cmsChurnInterventionEntry.redemptionLimit && redemptionCount >= cmsChurnInterventionEntry.redemptionLimit) {
|
if (
|
||||||
|
cmsChurnInterventionEntry.redemptionLimit &&
|
||||||
|
redemptionCount >= cmsChurnInterventionEntry.redemptionLimit
|
||||||
|
) {
|
||||||
this.statsd.increment('stay_subscribed_eligibility', {
|
this.statsd.increment('stay_subscribed_eligibility', {
|
||||||
eligibility: 'ineligible',
|
eligibility: 'ineligible',
|
||||||
reason: 'discount_already_applied',
|
reason: 'discount_already_applied',
|
||||||
@@ -77,13 +118,14 @@ export class ChurnInterventionService {
|
|||||||
isEligible: false,
|
isEligible: false,
|
||||||
reason: 'discount_already_applied',
|
reason: 'discount_already_applied',
|
||||||
cmsChurnInterventionEntry: null,
|
cmsChurnInterventionEntry: null,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscriptionStatus = await this.subscriptionManagementService.getSubscriptionStatus(
|
const subscriptionStatus =
|
||||||
uid,
|
await this.subscriptionManagementService.getSubscriptionStatus(
|
||||||
subscriptionId
|
uid,
|
||||||
);
|
subscriptionId
|
||||||
|
);
|
||||||
if (!subscriptionStatus.active) {
|
if (!subscriptionStatus.active) {
|
||||||
this.statsd.increment('stay_subscribed_eligibility', {
|
this.statsd.increment('stay_subscribed_eligibility', {
|
||||||
eligibility: 'ineligible',
|
eligibility: 'ineligible',
|
||||||
@@ -93,7 +135,7 @@ export class ChurnInterventionService {
|
|||||||
isEligible: false,
|
isEligible: false,
|
||||||
reason: 'subscription_not_active',
|
reason: 'subscription_not_active',
|
||||||
cmsChurnInterventionEntry: null,
|
cmsChurnInterventionEntry: null,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
if (!subscriptionStatus.cancelAtPeriodEnd) {
|
if (!subscriptionStatus.cancelAtPeriodEnd) {
|
||||||
this.statsd.increment('stay_subscribed_eligibility', {
|
this.statsd.increment('stay_subscribed_eligibility', {
|
||||||
@@ -114,14 +156,14 @@ export class ChurnInterventionService {
|
|||||||
isEligible: true,
|
isEligible: true,
|
||||||
reason: 'eligible',
|
reason: 'eligible',
|
||||||
cmsChurnInterventionEntry,
|
cmsChurnInterventionEntry,
|
||||||
}
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.log.error(error);
|
this.log.error(error);
|
||||||
return {
|
return {
|
||||||
isEligible: false,
|
isEligible: false,
|
||||||
reason: 'general_error',
|
reason: 'general_error',
|
||||||
cmsChurnInterventionEntry: null,
|
cmsChurnInterventionEntry: null,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +171,7 @@ export class ChurnInterventionService {
|
|||||||
uid: string,
|
uid: string,
|
||||||
subscriptionId: string,
|
subscriptionId: string,
|
||||||
acceptLanguage?: string | null,
|
acceptLanguage?: string | null,
|
||||||
selectedLanguage?: string,
|
selectedLanguage?: string
|
||||||
) {
|
) {
|
||||||
const eligibilityResult = await this.determineStaySubscribedEligibility(
|
const eligibilityResult = await this.determineStaySubscribedEligibility(
|
||||||
uid,
|
uid,
|
||||||
@@ -138,7 +180,10 @@ export class ChurnInterventionService {
|
|||||||
selectedLanguage
|
selectedLanguage
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!eligibilityResult.isEligible || !eligibilityResult.cmsChurnInterventionEntry) {
|
if (
|
||||||
|
!eligibilityResult.isEligible ||
|
||||||
|
!eligibilityResult.cmsChurnInterventionEntry
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
redeemed: false,
|
redeemed: false,
|
||||||
reason: eligibilityResult.reason,
|
reason: eligibilityResult.reason,
|
||||||
@@ -148,18 +193,26 @@ export class ChurnInterventionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const updatedSubscription = await this.subscriptionManagementService.applyStripeCouponToSubscription({
|
const updatedSubscription =
|
||||||
uid,
|
await this.subscriptionManagementService.applyStripeCouponToSubscription(
|
||||||
subscriptionId,
|
{
|
||||||
stripeCouponId: eligibilityResult.cmsChurnInterventionEntry.stripeCouponId,
|
uid,
|
||||||
setCancelAtPeriodEnd: true,
|
subscriptionId,
|
||||||
});
|
stripeCouponId:
|
||||||
if (!updatedSubscription || updatedSubscription.cancel_at_period_end !== true) {
|
eligibilityResult.cmsChurnInterventionEntry.stripeCouponId,
|
||||||
|
setCancelAtPeriodEnd: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!updatedSubscription ||
|
||||||
|
updatedSubscription.cancel_at_period_end !== true
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
redeemed: false,
|
redeemed: false,
|
||||||
reason: 'stripe_subscription_update_failed',
|
reason: 'stripe_subscription_update_failed',
|
||||||
updatedChurnInterventionEntryData: null,
|
updatedChurnInterventionEntryData: null,
|
||||||
cmsChurnInterventionEntry: eligibilityResult.cmsChurnInterventionEntry,
|
cmsChurnInterventionEntry:
|
||||||
|
eligibilityResult.cmsChurnInterventionEntry,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ import {
|
|||||||
} from '@fxa/payments/cart';
|
} from '@fxa/payments/cart';
|
||||||
import { ContentServerManager } from '@fxa/payments/content-server';
|
import { ContentServerManager } from '@fxa/payments/content-server';
|
||||||
import { CurrencyManager } from '@fxa/payments/currency';
|
import { CurrencyManager } from '@fxa/payments/currency';
|
||||||
import { SubscriptionManagementService, ChurnInterventionService } from '@fxa/payments/management';
|
import {
|
||||||
|
SubscriptionManagementService,
|
||||||
|
ChurnInterventionService,
|
||||||
|
} from '@fxa/payments/management';
|
||||||
import {
|
import {
|
||||||
CheckoutTokenManager,
|
CheckoutTokenManager,
|
||||||
PaypalBillingAgreementManager,
|
PaypalBillingAgreementManager,
|
||||||
} from '@fxa/payments/paypal';
|
} from '@fxa/payments/paypal';
|
||||||
import {
|
import {
|
||||||
ChurnInterventionByProductIdResultUtil,
|
|
||||||
ProductConfigError,
|
ProductConfigError,
|
||||||
ProductConfigurationManager,
|
ProductConfigurationManager,
|
||||||
} from '@fxa/shared/cms';
|
} from '@fxa/shared/cms';
|
||||||
@@ -279,15 +281,12 @@ export class NextJSActionsService {
|
|||||||
args.uid,
|
args.uid,
|
||||||
args.subscriptionId,
|
args.subscriptionId,
|
||||||
args.acceptLanguage,
|
args.acceptLanguage,
|
||||||
args.selectedLanguage,
|
args.selectedLanguage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SanitizeExceptions()
|
@SanitizeExceptions()
|
||||||
@NextIOValidator(
|
@NextIOValidator(RedeemChurnCouponActionArgs, RedeemChurnCouponActionResult)
|
||||||
RedeemChurnCouponActionArgs,
|
|
||||||
RedeemChurnCouponActionResult
|
|
||||||
)
|
|
||||||
@WithTypeCachableAsyncLocalStorage()
|
@WithTypeCachableAsyncLocalStorage()
|
||||||
@CaptureTimingWithStatsD()
|
@CaptureTimingWithStatsD()
|
||||||
async redeemChurnCoupon(args: {
|
async redeemChurnCoupon(args: {
|
||||||
@@ -300,7 +299,7 @@ export class NextJSActionsService {
|
|||||||
args.uid,
|
args.uid,
|
||||||
args.subscriptionId,
|
args.subscriptionId,
|
||||||
args.acceptLanguage,
|
args.acceptLanguage,
|
||||||
args.selectedLanguage,
|
args.selectedLanguage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -903,36 +902,13 @@ export class NextJSActionsService {
|
|||||||
acceptLanguage?: string;
|
acceptLanguage?: string;
|
||||||
selectedLanguage?: string;
|
selectedLanguage?: string;
|
||||||
}) {
|
}) {
|
||||||
let util: ChurnInterventionByProductIdResultUtil;
|
return await this.churnInterventionService.getChurnInterventionForProduct(
|
||||||
if (args.stripeProductId) {
|
args.interval,
|
||||||
util = await this.productConfigurationManager.getChurnIntervention(
|
args.churnType,
|
||||||
args.interval,
|
args.stripeProductId,
|
||||||
args.churnType,
|
args.offeringApiIdentifier,
|
||||||
args.stripeProductId,
|
args.acceptLanguage,
|
||||||
null,
|
args.selectedLanguage
|
||||||
args.acceptLanguage,
|
);
|
||||||
args.selectedLanguage
|
|
||||||
);
|
|
||||||
} else if (args.offeringApiIdentifier) {
|
|
||||||
util = await this.productConfigurationManager.getChurnIntervention(
|
|
||||||
args.interval,
|
|
||||||
args.churnType,
|
|
||||||
null,
|
|
||||||
args.offeringApiIdentifier,
|
|
||||||
args.acceptLanguage,
|
|
||||||
args.selectedLanguage
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
'Either stripeProductId or offeringApiIdentifier must be provided'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const churnInterventions =
|
|
||||||
util.getTransformedChurnInterventionByProductId();
|
|
||||||
|
|
||||||
return {
|
|
||||||
churnInterventions,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
libs/shared/cms/src/__generated__/gql.ts
generated
6
libs/shared/cms/src/__generated__/gql.ts
generated
@@ -16,7 +16,7 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
|
|||||||
type Documents = {
|
type Documents = {
|
||||||
"\n query CancelInterstitialOffer(\n $offeringApiIdentifier: String!\n $currentInterval: String!\n $upgradeInterval: String!\n $locale: String!\n ) {\n cancelInterstitialOffers(\n filters: {\n offeringApiIdentifier: { eq: $offeringApiIdentifier }\n currentInterval: { eq: $currentInterval }\n upgradeInterval: { eq: $upgradeInterval }\n }\n ) {\n offeringApiIdentifier\n currentInterval\n upgradeInterval\n advertisedSavings\n ctaMessage\n modalHeading1\n modalHeading2\n modalMessage\n productPageUrl\n upgradeButtonLabel\n upgradeButtonUrl\n localizations(filters: { locale: { eq: $locale } }) {\n ctaMessage\n modalHeading1\n modalHeading2\n modalMessage\n productPageUrl\n upgradeButtonLabel\n upgradeButtonUrl\n }\n offering {\n stripeProductId\n defaultPurchase {\n purchaseDetails {\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n webIcon\n }\n }\n }\n }\n }\n }\n": typeof types.CancelInterstitialOfferDocument,
|
"\n query CancelInterstitialOffer(\n $offeringApiIdentifier: String!\n $currentInterval: String!\n $upgradeInterval: String!\n $locale: String!\n ) {\n cancelInterstitialOffers(\n filters: {\n offeringApiIdentifier: { eq: $offeringApiIdentifier }\n currentInterval: { eq: $currentInterval }\n upgradeInterval: { eq: $upgradeInterval }\n }\n ) {\n offeringApiIdentifier\n currentInterval\n upgradeInterval\n advertisedSavings\n ctaMessage\n modalHeading1\n modalHeading2\n modalMessage\n productPageUrl\n upgradeButtonLabel\n upgradeButtonUrl\n localizations(filters: { locale: { eq: $locale } }) {\n ctaMessage\n modalHeading1\n modalHeading2\n modalMessage\n productPageUrl\n upgradeButtonLabel\n upgradeButtonUrl\n }\n offering {\n stripeProductId\n defaultPurchase {\n purchaseDetails {\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n webIcon\n }\n }\n }\n }\n }\n }\n": typeof types.CancelInterstitialOfferDocument,
|
||||||
"\n query CapabilityServiceByPlanIds($stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 200 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n offering {\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n capabilities {\n slug\n services {\n oauthClientId\n }\n }\n }\n }\n }\n": typeof types.CapabilityServiceByPlanIdsDocument,
|
"\n query CapabilityServiceByPlanIds($stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 200 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n offering {\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n capabilities {\n slug\n services {\n oauthClientId\n }\n }\n }\n }\n }\n": typeof types.CapabilityServiceByPlanIdsDocument,
|
||||||
"\n query ChurnInterventionByProductId(\n $offeringApiIdentifier: String\n $stripeProductId: String\n $interval: String!\n $locale: String!\n $churnType: String!\n ) {\n offerings(\n filters: {\n or: [\n { stripeProductId: { eq: $stripeProductId } }\n { apiIdentifier: { eq: $offeringApiIdentifier } }\n ]\n }\n pagination: { limit: 200 }\n ) {\n defaultPurchase {\n purchaseDetails {\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n webIcon\n }\n }\n }\n commonContent {\n supportUrl\n }\n churnInterventions(\n filters: { interval: { eq: $interval }, churnType: { eq: $churnType } }\n pagination: { limit: 200 }\n ) {\n localizations(filters: { locale: { eq: $locale } }) {\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n }\n }\n": typeof types.ChurnInterventionByProductIdDocument,
|
"\n query ChurnInterventionByProductId(\n $offeringApiIdentifier: String\n $stripeProductId: String\n $interval: String!\n $locale: String!\n $churnType: String!\n ) {\n offerings(\n filters: {\n or: [\n { stripeProductId: { eq: $stripeProductId } }\n { apiIdentifier: { eq: $offeringApiIdentifier } }\n ]\n }\n pagination: { limit: 200 }\n ) {\n defaultPurchase {\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n }\n commonContent {\n supportUrl\n }\n churnInterventions(\n filters: { interval: { eq: $interval }, churnType: { eq: $churnType } }\n pagination: { limit: 200 }\n ) {\n localizations(filters: { locale: { eq: $locale } }) {\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n }\n }\n": typeof types.ChurnInterventionByProductIdDocument,
|
||||||
"\n query EligibilityContentByOffering($apiIdentifier: String!) {\n offerings(\n filters: { apiIdentifier: { eq: $apiIdentifier } }\n pagination: { limit: 200 }\n ) {\n apiIdentifier\n stripeProductId\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n }\n subGroups {\n groupName\n offerings {\n apiIdentifier\n stripeProductId\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n }\n }\n }\n }\n }\n": typeof types.EligibilityContentByOfferingDocument,
|
"\n query EligibilityContentByOffering($apiIdentifier: String!) {\n offerings(\n filters: { apiIdentifier: { eq: $apiIdentifier } }\n pagination: { limit: 200 }\n ) {\n apiIdentifier\n stripeProductId\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n }\n subGroups {\n groupName\n offerings {\n apiIdentifier\n stripeProductId\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n }\n }\n }\n }\n }\n": typeof types.EligibilityContentByOfferingDocument,
|
||||||
"\n query EligibilityContentByPlanIds($stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 200 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n offering {\n apiIdentifier\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n countries\n subGroups {\n groupName\n offerings {\n apiIdentifier\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n countries\n }\n }\n }\n }\n }\n": typeof types.EligibilityContentByPlanIdsDocument,
|
"\n query EligibilityContentByPlanIds($stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 200 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n offering {\n apiIdentifier\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n countries\n subGroups {\n groupName\n offerings {\n apiIdentifier\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n countries\n }\n }\n }\n }\n }\n": typeof types.EligibilityContentByPlanIdsDocument,
|
||||||
"\n query IapOfferingsByStoreIDs($locale: String!, $storeIDs: [String!]!) {\n iaps(filters: { storeID: { in: $storeIDs } }) {\n storeID\n interval\n offering {\n apiIdentifier\n commonContent {\n supportUrl\n localizations(filters: { locale: { eq: $locale } }) {\n supportUrl\n }\n }\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n }\n subGroups {\n groupName\n offerings {\n apiIdentifier\n }\n }\n }\n }\n }\n": typeof types.IapOfferingsByStoreIDsDocument,
|
"\n query IapOfferingsByStoreIDs($locale: String!, $storeIDs: [String!]!) {\n iaps(filters: { storeID: { in: $storeIDs } }) {\n storeID\n interval\n offering {\n apiIdentifier\n commonContent {\n supportUrl\n localizations(filters: { locale: { eq: $locale } }) {\n supportUrl\n }\n }\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n }\n subGroups {\n groupName\n offerings {\n apiIdentifier\n }\n }\n }\n }\n }\n": typeof types.IapOfferingsByStoreIDsDocument,
|
||||||
@@ -31,7 +31,7 @@ type Documents = {
|
|||||||
const documents: Documents = {
|
const documents: Documents = {
|
||||||
"\n query CancelInterstitialOffer(\n $offeringApiIdentifier: String!\n $currentInterval: String!\n $upgradeInterval: String!\n $locale: String!\n ) {\n cancelInterstitialOffers(\n filters: {\n offeringApiIdentifier: { eq: $offeringApiIdentifier }\n currentInterval: { eq: $currentInterval }\n upgradeInterval: { eq: $upgradeInterval }\n }\n ) {\n offeringApiIdentifier\n currentInterval\n upgradeInterval\n advertisedSavings\n ctaMessage\n modalHeading1\n modalHeading2\n modalMessage\n productPageUrl\n upgradeButtonLabel\n upgradeButtonUrl\n localizations(filters: { locale: { eq: $locale } }) {\n ctaMessage\n modalHeading1\n modalHeading2\n modalMessage\n productPageUrl\n upgradeButtonLabel\n upgradeButtonUrl\n }\n offering {\n stripeProductId\n defaultPurchase {\n purchaseDetails {\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n webIcon\n }\n }\n }\n }\n }\n }\n": types.CancelInterstitialOfferDocument,
|
"\n query CancelInterstitialOffer(\n $offeringApiIdentifier: String!\n $currentInterval: String!\n $upgradeInterval: String!\n $locale: String!\n ) {\n cancelInterstitialOffers(\n filters: {\n offeringApiIdentifier: { eq: $offeringApiIdentifier }\n currentInterval: { eq: $currentInterval }\n upgradeInterval: { eq: $upgradeInterval }\n }\n ) {\n offeringApiIdentifier\n currentInterval\n upgradeInterval\n advertisedSavings\n ctaMessage\n modalHeading1\n modalHeading2\n modalMessage\n productPageUrl\n upgradeButtonLabel\n upgradeButtonUrl\n localizations(filters: { locale: { eq: $locale } }) {\n ctaMessage\n modalHeading1\n modalHeading2\n modalMessage\n productPageUrl\n upgradeButtonLabel\n upgradeButtonUrl\n }\n offering {\n stripeProductId\n defaultPurchase {\n purchaseDetails {\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n webIcon\n }\n }\n }\n }\n }\n }\n": types.CancelInterstitialOfferDocument,
|
||||||
"\n query CapabilityServiceByPlanIds($stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 200 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n offering {\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n capabilities {\n slug\n services {\n oauthClientId\n }\n }\n }\n }\n }\n": types.CapabilityServiceByPlanIdsDocument,
|
"\n query CapabilityServiceByPlanIds($stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 200 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n offering {\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n capabilities {\n slug\n services {\n oauthClientId\n }\n }\n }\n }\n }\n": types.CapabilityServiceByPlanIdsDocument,
|
||||||
"\n query ChurnInterventionByProductId(\n $offeringApiIdentifier: String\n $stripeProductId: String\n $interval: String!\n $locale: String!\n $churnType: String!\n ) {\n offerings(\n filters: {\n or: [\n { stripeProductId: { eq: $stripeProductId } }\n { apiIdentifier: { eq: $offeringApiIdentifier } }\n ]\n }\n pagination: { limit: 200 }\n ) {\n defaultPurchase {\n purchaseDetails {\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n webIcon\n }\n }\n }\n commonContent {\n supportUrl\n }\n churnInterventions(\n filters: { interval: { eq: $interval }, churnType: { eq: $churnType } }\n pagination: { limit: 200 }\n ) {\n localizations(filters: { locale: { eq: $locale } }) {\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n }\n }\n": types.ChurnInterventionByProductIdDocument,
|
"\n query ChurnInterventionByProductId(\n $offeringApiIdentifier: String\n $stripeProductId: String\n $interval: String!\n $locale: String!\n $churnType: String!\n ) {\n offerings(\n filters: {\n or: [\n { stripeProductId: { eq: $stripeProductId } }\n { apiIdentifier: { eq: $offeringApiIdentifier } }\n ]\n }\n pagination: { limit: 200 }\n ) {\n defaultPurchase {\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n }\n commonContent {\n supportUrl\n }\n churnInterventions(\n filters: { interval: { eq: $interval }, churnType: { eq: $churnType } }\n pagination: { limit: 200 }\n ) {\n localizations(filters: { locale: { eq: $locale } }) {\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n }\n }\n": types.ChurnInterventionByProductIdDocument,
|
||||||
"\n query EligibilityContentByOffering($apiIdentifier: String!) {\n offerings(\n filters: { apiIdentifier: { eq: $apiIdentifier } }\n pagination: { limit: 200 }\n ) {\n apiIdentifier\n stripeProductId\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n }\n subGroups {\n groupName\n offerings {\n apiIdentifier\n stripeProductId\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n }\n }\n }\n }\n }\n": types.EligibilityContentByOfferingDocument,
|
"\n query EligibilityContentByOffering($apiIdentifier: String!) {\n offerings(\n filters: { apiIdentifier: { eq: $apiIdentifier } }\n pagination: { limit: 200 }\n ) {\n apiIdentifier\n stripeProductId\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n }\n subGroups {\n groupName\n offerings {\n apiIdentifier\n stripeProductId\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n }\n }\n }\n }\n }\n": types.EligibilityContentByOfferingDocument,
|
||||||
"\n query EligibilityContentByPlanIds($stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 200 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n offering {\n apiIdentifier\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n countries\n subGroups {\n groupName\n offerings {\n apiIdentifier\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n countries\n }\n }\n }\n }\n }\n": types.EligibilityContentByPlanIdsDocument,
|
"\n query EligibilityContentByPlanIds($stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 200 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n offering {\n apiIdentifier\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n countries\n subGroups {\n groupName\n offerings {\n apiIdentifier\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n countries\n }\n }\n }\n }\n }\n": types.EligibilityContentByPlanIdsDocument,
|
||||||
"\n query IapOfferingsByStoreIDs($locale: String!, $storeIDs: [String!]!) {\n iaps(filters: { storeID: { in: $storeIDs } }) {\n storeID\n interval\n offering {\n apiIdentifier\n commonContent {\n supportUrl\n localizations(filters: { locale: { eq: $locale } }) {\n supportUrl\n }\n }\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n }\n subGroups {\n groupName\n offerings {\n apiIdentifier\n }\n }\n }\n }\n }\n": types.IapOfferingsByStoreIDsDocument,
|
"\n query IapOfferingsByStoreIDs($locale: String!, $storeIDs: [String!]!) {\n iaps(filters: { storeID: { in: $storeIDs } }) {\n storeID\n interval\n offering {\n apiIdentifier\n commonContent {\n supportUrl\n localizations(filters: { locale: { eq: $locale } }) {\n supportUrl\n }\n }\n defaultPurchase {\n stripePlanChoices {\n stripePlanChoice\n }\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n }\n subGroups {\n groupName\n offerings {\n apiIdentifier\n }\n }\n }\n }\n }\n": types.IapOfferingsByStoreIDsDocument,
|
||||||
@@ -69,7 +69,7 @@ export function graphql(source: "\n query CapabilityServiceByPlanIds($stripePla
|
|||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query ChurnInterventionByProductId(\n $offeringApiIdentifier: String\n $stripeProductId: String\n $interval: String!\n $locale: String!\n $churnType: String!\n ) {\n offerings(\n filters: {\n or: [\n { stripeProductId: { eq: $stripeProductId } }\n { apiIdentifier: { eq: $offeringApiIdentifier } }\n ]\n }\n pagination: { limit: 200 }\n ) {\n defaultPurchase {\n purchaseDetails {\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n webIcon\n }\n }\n }\n commonContent {\n supportUrl\n }\n churnInterventions(\n filters: { interval: { eq: $interval }, churnType: { eq: $churnType } }\n pagination: { limit: 200 }\n ) {\n localizations(filters: { locale: { eq: $locale } }) {\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n }\n }\n"): (typeof documents)["\n query ChurnInterventionByProductId(\n $offeringApiIdentifier: String\n $stripeProductId: String\n $interval: String!\n $locale: String!\n $churnType: String!\n ) {\n offerings(\n filters: {\n or: [\n { stripeProductId: { eq: $stripeProductId } }\n { apiIdentifier: { eq: $offeringApiIdentifier } }\n ]\n }\n pagination: { limit: 200 }\n ) {\n defaultPurchase {\n purchaseDetails {\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n webIcon\n }\n }\n }\n commonContent {\n supportUrl\n }\n churnInterventions(\n filters: { interval: { eq: $interval }, churnType: { eq: $churnType } }\n pagination: { limit: 200 }\n ) {\n localizations(filters: { locale: { eq: $locale } }) {\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n }\n }\n"];
|
export function graphql(source: "\n query ChurnInterventionByProductId(\n $offeringApiIdentifier: String\n $stripeProductId: String\n $interval: String!\n $locale: String!\n $churnType: String!\n ) {\n offerings(\n filters: {\n or: [\n { stripeProductId: { eq: $stripeProductId } }\n { apiIdentifier: { eq: $offeringApiIdentifier } }\n ]\n }\n pagination: { limit: 200 }\n ) {\n defaultPurchase {\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n }\n commonContent {\n supportUrl\n }\n churnInterventions(\n filters: { interval: { eq: $interval }, churnType: { eq: $churnType } }\n pagination: { limit: 200 }\n ) {\n localizations(filters: { locale: { eq: $locale } }) {\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n }\n }\n"): (typeof documents)["\n query ChurnInterventionByProductId(\n $offeringApiIdentifier: String\n $stripeProductId: String\n $interval: String!\n $locale: String!\n $churnType: String!\n ) {\n offerings(\n filters: {\n or: [\n { stripeProductId: { eq: $stripeProductId } }\n { apiIdentifier: { eq: $offeringApiIdentifier } }\n ]\n }\n pagination: { limit: 200 }\n ) {\n defaultPurchase {\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n }\n commonContent {\n supportUrl\n }\n churnInterventions(\n filters: { interval: { eq: $interval }, churnType: { eq: $churnType } }\n pagination: { limit: 200 }\n ) {\n localizations(filters: { locale: { eq: $locale } }) {\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n churnInterventionId\n churnType\n redemptionLimit\n stripeCouponId\n interval\n discountAmount\n ctaMessage\n modalHeading\n modalMessage\n productPageUrl\n termsHeading\n termsDetails\n }\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|||||||
4
libs/shared/cms/src/__generated__/graphql.ts
generated
4
libs/shared/cms/src/__generated__/graphql.ts
generated
File diff suppressed because one or more lines are too long
@@ -40,9 +40,11 @@ export const ChurnInterventionByProductIdOfferingsResultFactory = (
|
|||||||
): ChurnInterventionByProductIdOfferingResult => ({
|
): ChurnInterventionByProductIdOfferingResult => ({
|
||||||
defaultPurchase: {
|
defaultPurchase: {
|
||||||
purchaseDetails: {
|
purchaseDetails: {
|
||||||
|
productName: faker.string.sample(),
|
||||||
webIcon: faker.image.urlLoremFlickr(),
|
webIcon: faker.image.urlLoremFlickr(),
|
||||||
localizations: [
|
localizations: [
|
||||||
{
|
{
|
||||||
|
productName: faker.string.sample(),
|
||||||
webIcon: faker.image.urlLoremFlickr(),
|
webIcon: faker.image.urlLoremFlickr(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -77,6 +79,7 @@ export const ChurnInterventionByProductIdRawResultFactory = (
|
|||||||
export const ChurnInterventionByProductIdResultFactory = (
|
export const ChurnInterventionByProductIdResultFactory = (
|
||||||
override?: Partial<ChurnInterventionByProductIdResult>
|
override?: Partial<ChurnInterventionByProductIdResult>
|
||||||
): ChurnInterventionByProductIdResult => ({
|
): ChurnInterventionByProductIdResult => ({
|
||||||
|
productName: faker.string.sample(),
|
||||||
webIcon: faker.image.urlLoremFlickr(),
|
webIcon: faker.image.urlLoremFlickr(),
|
||||||
churnInterventionId: faker.string.uuid(),
|
churnInterventionId: faker.string.uuid(),
|
||||||
churnType: faker.helpers.enumValue(Enum_Churnintervention_Churntype),
|
churnType: faker.helpers.enumValue(Enum_Churnintervention_Churntype),
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ export const churnInterventionByProductIdQuery = graphql(`
|
|||||||
) {
|
) {
|
||||||
defaultPurchase {
|
defaultPurchase {
|
||||||
purchaseDetails {
|
purchaseDetails {
|
||||||
|
productName
|
||||||
webIcon
|
webIcon
|
||||||
localizations(filters: { locale: { eq: $locale } }) {
|
localizations(filters: { locale: { eq: $locale } }) {
|
||||||
|
productName
|
||||||
webIcon
|
webIcon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ export interface ChurnInterventionByProductIdChurnInterventionsResult {
|
|||||||
export interface ChurnInterventionByProductIdOfferingResult {
|
export interface ChurnInterventionByProductIdOfferingResult {
|
||||||
defaultPurchase: {
|
defaultPurchase: {
|
||||||
purchaseDetails: {
|
purchaseDetails: {
|
||||||
|
productName: string;
|
||||||
webIcon: string;
|
webIcon: string;
|
||||||
localizations: { webIcon: string }[];
|
localizations: { productName: string; webIcon: string }[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
commonContent: {
|
commonContent: {
|
||||||
@@ -42,6 +43,7 @@ export interface ChurnInterventionByProductIdRawResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ChurnInterventionByProductIdResult {
|
export interface ChurnInterventionByProductIdResult {
|
||||||
|
productName: string;
|
||||||
webIcon: string;
|
webIcon: string;
|
||||||
churnInterventionId: string;
|
churnInterventionId: string;
|
||||||
churnType: Enum_Churnintervention_Churntype;
|
churnType: Enum_Churnintervention_Churntype;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ describe('ChurnInterventionByProductIdResultUtil', () => {
|
|||||||
it('should transform churn intervention by offering', () => {
|
it('should transform churn intervention by offering', () => {
|
||||||
const transformed = util.getTransformedChurnInterventionByProductId()[0];
|
const transformed = util.getTransformedChurnInterventionByProductId()[0];
|
||||||
expect(transformed).toBeDefined();
|
expect(transformed).toBeDefined();
|
||||||
|
expect(transformed?.productName).toBeDefined();
|
||||||
expect(transformed?.webIcon).toBeDefined();
|
expect(transformed?.webIcon).toBeDefined();
|
||||||
expect(transformed?.supportUrl).toBeDefined();
|
expect(transformed?.supportUrl).toBeDefined();
|
||||||
expect(transformed?.churnInterventionId).toBeDefined();
|
expect(transformed?.churnInterventionId).toBeDefined();
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ export class ChurnInterventionByProductIdResultUtil {
|
|||||||
defaultPurchase.purchaseDetails.localizations.length > 0
|
defaultPurchase.purchaseDetails.localizations.length > 0
|
||||||
? defaultPurchase.purchaseDetails.localizations[0].webIcon
|
? defaultPurchase.purchaseDetails.localizations[0].webIcon
|
||||||
: defaultPurchase.purchaseDetails.webIcon,
|
: defaultPurchase.purchaseDetails.webIcon,
|
||||||
|
productName:
|
||||||
|
defaultPurchase.purchaseDetails.localizations.length > 0
|
||||||
|
? defaultPurchase.purchaseDetails.localizations[0].productName
|
||||||
|
: defaultPurchase.purchaseDetails.productName,
|
||||||
supportUrl: commonContent.supportUrl,
|
supportUrl: commonContent.supportUrl,
|
||||||
ctaMessage:
|
ctaMessage:
|
||||||
churnIntervention.localizations.at(0)?.ctaMessage ??
|
churnIntervention.localizations.at(0)?.ctaMessage ??
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface LinkExternalProps {
|
|||||||
rel?: 'noopener noreferrer' | 'author';
|
rel?: 'noopener noreferrer' | 'author';
|
||||||
tabIndex?: number;
|
tabIndex?: number;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
|
'aria-label'?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LinkExternal = ({
|
export const LinkExternal = ({
|
||||||
@@ -26,6 +27,7 @@ export const LinkExternal = ({
|
|||||||
rel = 'noopener noreferrer',
|
rel = 'noopener noreferrer',
|
||||||
tabIndex,
|
tabIndex,
|
||||||
onClick,
|
onClick,
|
||||||
|
'aria-label': ariaLabel,
|
||||||
}: LinkExternalProps) => (
|
}: LinkExternalProps) => (
|
||||||
<a
|
<a
|
||||||
data-testid={testid}
|
data-testid={testid}
|
||||||
@@ -37,6 +39,7 @@ export const LinkExternal = ({
|
|||||||
rel,
|
rel,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
onClick,
|
onClick,
|
||||||
|
'aria-label': ariaLabel,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
Reference in New Issue
Block a user