fix: Handle trial ending without cancelation (#2651)

Resolves https://github.com/webrecorder/browsertrix/issues/2650

## Changes

Differentials between `trialing` and `trialing_canceled` when displaying
messages:
- No changes to messages if `trialing_canceled`.
- If `trialing`, show messaging that subscription will automatically
continue.

---------

Co-authored-by: Ilya Kreymer <ikreymer@gmail.com>
Co-authored-by: Ilya Kreymer <ikreymer@users.noreply.github.com>
This commit is contained in:
sua yoo 2025-06-10 15:20:57 -07:00 committed by GitHub
parent 223221c31e
commit 40ebbd11d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 98 additions and 50 deletions

View File

@ -1,7 +1,9 @@
import { localized, msg, str } from "@lit/localize";
import type { SlAlert } from "@shoelace-style/shoelace";
import { differenceInHours } from "date-fns/fp";
import { html, type TemplateResult } from "lit";
import { customElement } from "lit/decorators.js";
import { when } from "lit/directives/when.js";
import { BtrixElement } from "@/classes/BtrixElement";
import { SubscriptionStatus } from "@/types/billing";
@ -9,6 +11,7 @@ import { OrgReadOnlyReason } from "@/types/org";
type Alert = {
test: () => boolean;
variant?: SlAlert["variant"];
content: () => {
title: string | TemplateResult;
detail: string | TemplateResult;
@ -30,7 +33,7 @@ export class OrgStatusBanner extends BtrixElement {
return html`
<div id="banner" class="border-b bg-slate-100 py-5">
<div class="mx-auto box-border w-full max-w-screen-desktop px-3">
<sl-alert variant="danger" open>
<sl-alert variant=${alert.variant || "danger"} open>
<sl-icon slot="icon" name="exclamation-triangle-fill"></sl-icon>
<strong class="block font-semibold">${content.title}</strong>
${content.detail}
@ -56,11 +59,12 @@ export class OrgStatusBanner extends BtrixElement {
const {
readOnly,
readOnlyReason,
readOnlyOnCancel,
subscription,
storageQuotaReached,
execMinutesQuotaReached,
} = this.org;
const readOnlyOnCancel =
subscription?.readOnlyOnCancel ?? this.org.readOnlyOnCancel;
let hoursDiff = 0;
let daysDiff = 0;
@ -69,7 +73,7 @@ export class OrgStatusBanner extends BtrixElement {
if (futureCancelDate) {
hoursDiff = differenceInHours(new Date(), new Date(futureCancelDate));
daysDiff = Math.trunc(hoursDiff / 24);
daysDiff = Math.ceil(hoursDiff / 24);
dateStr = this.localize.date(futureCancelDate, {
month: "long",
@ -80,14 +84,13 @@ export class OrgStatusBanner extends BtrixElement {
});
}
const isTrialingCanceled =
const isCancelingTrial =
subscription?.status == SubscriptionStatus.TrialingCanceled;
const isTrial =
subscription?.status === SubscriptionStatus.Trialing ||
isTrialingCanceled;
subscription?.status === SubscriptionStatus.Trialing || isCancelingTrial;
// show banner if < this many days of trial is left
const MAX_TRIAL_DAYS_SHOW_BANNER = 4;
const MAX_TRIAL_DAYS_SHOW_BANNER = 8;
return [
{
@ -125,8 +128,8 @@ export class OrgStatusBanner extends BtrixElement {
!readOnlyOnCancel &&
!!futureCancelDate &&
((isTrial && daysDiff < MAX_TRIAL_DAYS_SHOW_BANNER) ||
isTrialingCanceled),
isCancelingTrial),
variant: isCancelingTrial ? "danger" : "warning",
content: () => {
return {
title:
@ -138,21 +141,32 @@ export class OrgStatusBanner extends BtrixElement {
str`You have ${daysDiff} days left of your Browsertrix trial`,
),
detail: html`
<p>
${msg(
html`Your free trial ends on ${dateStr}. To continue using
Browsertrix, select <strong>Subscribe Now</strong> in
${billingTabLink}.`,
)}
detail: html`<p>
${msg(str`Your free trial ends on ${dateStr}.`)}
${isCancelingTrial
? msg(
html`To continue using Browsertrix, select
<strong>Subscribe Now</strong> in ${billingTabLink}.`,
)
: html`${msg(
"Afterwards, your subscription will continue automatically.",
)}
${msg(
html`View and manage your subscription in
${billingTabLink}.`,
)}`}
</p>
<p>
${msg(
str`Your web archives are always yours — you can download any archived items you'd like to keep
${when(
isCancelingTrial,
() => html`
<p>
${msg(
str`Your web archives are always yours — you can download any archived items you'd like to keep
before the trial ends!`,
)}
</p>
`,
)}
</p>
`,
)} `,
};
},
},

View File

@ -3,6 +3,7 @@ import { Task } from "@lit/task";
import clsx from "clsx";
import { css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import { choose } from "lit/directives/choose.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { when } from "lit/directives/when.js";
import capitalize from "lodash/fp/capitalize";
@ -38,11 +39,10 @@ export class OrgSettingsBilling extends BtrixElement {
if (!subscription) return;
let label = msg("Manage Billing");
let label = msg("Manage Subscription");
switch (subscription.status) {
case SubscriptionStatus.TrialingCanceled:
case SubscriptionStatus.Trialing: {
case SubscriptionStatus.TrialingCanceled: {
label = msg("Subscribe Now");
break;
}
@ -86,6 +86,10 @@ export class OrgSettingsBilling extends BtrixElement {
});
render() {
const manageSubscriptionMessage = msg(
str`Click “${this.portalUrlLabel}” to view plan details, payment methods, and billing information.`,
);
return html`
<section class="-mt-5">
${columns([
@ -131,6 +135,13 @@ export class OrgSettingsBilling extends BtrixElement {
},
);
const trialMessage = (detail?: string) => html`
<span class="font-medium text-neutral-700">
${msg(str`Your trial ends ${futureCancelDate}`)}
</span>
${when(detail, () => html`&mdash; ${detail}`)}
`;
return html`
<div
class="mb-3 flex items-center gap-2 border-b pb-3 text-neutral-500"
@ -140,22 +151,31 @@ export class OrgSettingsBilling extends BtrixElement {
class="size-4 flex-shrink-0"
></sl-icon>
<div>
${org.subscription.status ===
SubscriptionStatus.Trialing ||
org.subscription.status ===
SubscriptionStatus.TrialingCanceled
? html`
<span class="font-medium text-neutral-700">
${msg(
str`Your trial will end on ${futureCancelDate}`,
)}
</span>
&mdash;
${msg(str`subscribe to keep your account`)}
`
: msg(
${choose(
org.subscription.status,
[
[
SubscriptionStatus.Trialing,
() =>
trialMessage(
msg(
"subscription will automatically continue",
),
),
],
[
SubscriptionStatus.TrialingCanceled,
() =>
trialMessage(
msg("subscribe to keep your account"),
),
],
],
() =>
html`${msg(
str`Your plan will be canceled on ${futureCancelDate}`,
)}
)}`,
)}
</div>
</div>
`;
@ -185,16 +205,30 @@ export class OrgSettingsBilling extends BtrixElement {
${when(this.org, (org) =>
org.subscription
? html` <p class="mb-3 leading-normal">
${org.subscription.status ===
SubscriptionStatus.Trialing ||
org.subscription.status ===
SubscriptionStatus.TrialingCanceled
? msg(
str`To continue using Browsertrix at the end of your trial, click “${this.portalUrlLabel}”.`,
)
: msg(
str`You can view plan details, update payment methods, and update billing information by clicking “${this.portalUrlLabel}”.`,
)}
${choose(
org.subscription.status,
[
[
SubscriptionStatus.Trialing,
() => [
manageSubscriptionMessage,
html`<br /><br />`,
msg(
"You also have the ability to cancel your trial or permanently delete your account from the subscription portal.",
),
],
],
[
SubscriptionStatus.TrialingCanceled,
() => [
msg(
str`To continue using Browsertrix at the end of your trial, click “${this.portalUrlLabel}”.`,
),
],
],
],
() => [manageSubscriptionMessage],
)}
</p>
${this.salesEmail
? html`<p class="leading-normal">