Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/payments/next/app/[locale]/subscriptions/manage/en.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
subscription-management-page-banner-warning-title-no-payment-method = No payment method added
subscription-management-page-banner-warning-link-no-payment-method = Add a payment method
subscription-management-subscriptions-heading = Subscriptions
subscription-management-free-trial-heading = Free trials
subscription-management-your-free-trials-aria = Your free trials
Comment on lines +6 to +7
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


# Heading for mobile only quick links menu
subscription-management-jump-to-heading = Jump to
subscription-management-nav-free-trials = Free trials
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, potentially update based on https://github.com/mozilla/fxa/pull/20282/changes#r3010922866

subscription-management-nav-payment-details = Payment details
subscription-management-nav-active-subscriptions = Active subscriptions
subscription-management-payment-details-heading = Payment details
Expand Down
113 changes: 112 additions & 1 deletion apps/payments/next/app/[locale]/subscriptions/manage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Banner,
BannerVariant,
formatPlanInterval,
FreeTrialContent,
getCardIcon,
GleanPageView,
ManageParams,
Expand Down Expand Up @@ -76,6 +77,7 @@ export default async function Manage({
subscriptions,
appleIapSubscriptions,
googleIapSubscriptions,
trialSubscriptions,
} = await getSubManPageContentAction(
session.user?.id,
{ ...resolvedParams },
Expand Down Expand Up @@ -192,7 +194,8 @@ export default async function Manage({

{(subscriptions.length > 0 ||
appleIapSubscriptions.length > 0 ||
googleIapSubscriptions.length > 0) && (
googleIapSubscriptions.length > 0 ||
trialSubscriptions.length > 0) && (
<nav
className="px-4 tablet:hidden"
aria-labelledby="mobile-quick-links-menu"
Expand All @@ -204,6 +207,26 @@ export default async function Manage({
)}
</h2>
<ul className="flex flex-col gap-6">
{trialSubscriptions.length > 0 && (
<li>
<Link
className="flex items-center justify-between text-blue-500 hover:text-blue-600 cursor-pointer underline"
href="#free-trial"
>
{l10n.getString(
'subscription-management-nav-free-trials',
'Free trials'
)}
<Image
src={arrowDownIcon}
alt=""
width={12}
height={12}
aria-hidden="true"
/>
</Link>
</li>
)}
<li>
<Link
className="flex items-center justify-between text-blue-500 hover:text-blue-600 cursor-pointer underline"
Expand Down Expand Up @@ -248,6 +271,94 @@ export default async function Manage({
</nav>
)}

{trialSubscriptions.length > 0 && (
<section
id="free-trial"
className="scroll-mt-16"
aria-labelledby="free-trial-heading"
>
<h2
id="free-trial-heading"
className="font-bold px-4 pt-8 pb-4 text-lg tablet:px-6"
>
{l10n.getString(
'subscription-management-free-trial-heading',
'Free trials'
Copy link
Copy Markdown
Contributor Author

@elizabeth-ilina elizabeth-ilina Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To match the rest of the page, where headings are plural like Payment details, Active subscriptions, I wrote "Free trials" (with -s at the end), while Figma has "Free trial". Which is better?

)}
</h2>
<ul
aria-label={l10n.getString(
'subscription-management-your-free-trials-aria',
'Your free trials'
)}
>
{trialSubscriptions.map((trial, index: number) => (
<li
key={`${trial.productName}-${index}`}
aria-labelledby={`${trial.productName}-trial-information`}
className="leading-6 pb-4 last:pb-0"
>
<div className="w-full py-6 text-grey-600 bg-white rounded-xl border border-grey-200 opacity-100 shadow-[0_0_16px_0_rgba(0,0,0,0.08)] tablet:px-6 tablet:py-8">
<div className="flex flex-col px-4 tablet:px-0 tablet:flex-row tablet:items-start">
<div className="tablet:min-w-[160px]">
<Image
src={trial.webIcon}
alt={trial.productName}
height={64}
width={64}
/>
</div>
<div className="flex flex-col gap-4 w-full">
<div className="flex items-start justify-between mt-4 tablet:mt-0">
<div>
<h3
id={`${trial.productName}-trial-information`}
className="font-bold text-lg"
>
{trial.productName}
</h3>
<p className="text-grey-500">
{trial.interval &&
formatPlanInterval(trial.interval)}
</p>
</div>
<LinkExternal
href={trial.supportUrl}
className="text-blue-500 hover:text-blue-600 cursor-pointer overflow-hidden text-ellipsis underline whitespace-nowrap"
aria-label={l10n.getString(
'subscription-management-button-support-aria',
{ productName: trial.productName },
`Get help for ${trial.productName}`
)}
data-testid={`link-external-support-${trial.productName}`}
>
<span>
{l10n.getString(
'subscription-management-button-support',
'Get help'
)}
</span>
</LinkExternal>
</div>
<FreeTrialContent
trial={trial}
locale={locale}
userId={userId}
updatePaymentUrl={
type === SubPlatPaymentMethodType.PayPal
? `${config.paymentsNextHostedUrl}/${locale}/subscriptions/payments/paypal`
: `${config.paymentsNextHostedUrl}/${locale}/subscriptions/payments/stripe`
}
/>
</div>
</div>
</div>
</li>
))}
</ul>
</section>
)}

<section
id="payment-details"
className="scroll-mt-16"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
GoogleIapPurchaseResult,
GoogleIapSubscriptionContent,
SubscriptionContent,
TrialSubscriptionContent,
} from '../types';

export const AppleIapPurchaseFactory = (
Expand Down Expand Up @@ -95,3 +96,26 @@ export const SubscriptionContentFactory = (
churnStaySubscribedCtaMessage: faker.string.sample(),
...override,
});

export const TrialSubscriptionContentFactory = (
override?: Partial<TrialSubscriptionContent>
): TrialSubscriptionContent => ({
id: `sub_${faker.string.alphanumeric({ length: 24 })}`,
productName: faker.string.sample(),
offeringApiIdentifier: faker.string.sample(),
supportUrl: faker.internet.url(),
webIcon: faker.internet.url(),
currency: faker.finance.currencyCode().toLowerCase(),
interval: faker.helpers.enumValue(SubplatInterval),
cancelAtPeriodEnd: false,
trialEnd: Math.floor(faker.date.future().getTime() / 1000),
trialStart: Math.floor(faker.date.past().getTime() / 1000),
nextInvoiceTotal: faker.number.int({ min: 1, max: 1000 }),
nextInvoiceTax: faker.number.int({ min: 1, max: 1000 }),
conversionStatus: 'active',
failedInvoiceDate: undefined,
failedInvoiceTotal: undefined,
failedInvoiceTax: undefined,
failedInvoiceUrl: undefined,
...override,
});
Loading
Loading