Skip to content

Commit 25c8bc8

Browse files
committed
[dashboard] Prevent upgrading to Chargebee once Usage-Based Pricing is enabled
1 parent 93a68d3 commit 25c8bc8

File tree

2 files changed

+87
-113
lines changed

2 files changed

+87
-113
lines changed

components/dashboard/src/settings/Plans.tsx

Lines changed: 79 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { UserContext } from "../user-context";
2525
import Tooltip from "../components/Tooltip";
2626
import { PaymentContext } from "../payment-context";
2727
import { PageWithSettingsSubMenu } from "./PageWithSettingsSubMenu";
28+
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode";
2829

2930
type PlanWithOriginalPrice = Plan & { originalPrice?: number };
3031
type Pending = { pendingSince: number };
@@ -356,6 +357,7 @@ export default function () {
356357
};
357358

358359
const planCards = [];
360+
const canUpgradeToChargebee = !BillingMode.showUsageBasedBilling(userBillingMode);
359361

360362
// Plan card: Free a.k.a. Open Source (or Professional Open Source)
361363
const openSourceFeatures = (
@@ -374,7 +376,7 @@ export default function () {
374376
if (currentPlan.chargebeeId === freePlan.chargebeeId) {
375377
planCards.push(
376378
<PlanCard
377-
isDisabled={!!assignedTs || pendingChargebeeCallback}
379+
isDisabled={!canUpgradeToChargebee || !!assignedTs || pendingChargebeeCallback}
378380
plan={freePlan}
379381
isCurrent={!!accountStatement}
380382
>
@@ -409,7 +411,7 @@ export default function () {
409411
}
410412
planCards.push(
411413
<PlanCard
412-
isDisabled={!!assignedTs || pendingChargebeeCallback}
414+
isDisabled={!canUpgradeToChargebee || !!assignedTs || pendingChargebeeCallback}
413415
plan={targetPlan}
414416
isCurrent={false}
415417
onDowngrade={onDowngrade}
@@ -435,7 +437,7 @@ export default function () {
435437
) : undefined;
436438
planCards.push(
437439
<PlanCard
438-
isDisabled={!!assignedTs || pendingChargebeeCallback}
440+
isDisabled={!canUpgradeToChargebee || !!assignedTs || pendingChargebeeCallback}
439441
plan={applyCoupons(personalPlan, appliedCoupons)}
440442
isCurrent={true}
441443
bottomLabel={bottomLabel}
@@ -474,7 +476,7 @@ export default function () {
474476
}
475477
planCards.push(
476478
<PlanCard
477-
isDisabled={!!assignedTs || pendingChargebeeCallback}
479+
isDisabled={!canUpgradeToChargebee || !!assignedTs || pendingChargebeeCallback}
478480
plan={targetPlan}
479481
isCurrent={false}
480482
onUpgrade={onUpgrade}
@@ -505,7 +507,7 @@ export default function () {
505507
) : undefined;
506508
planCards.push(
507509
<PlanCard
508-
isDisabled={!!assignedTs || pendingChargebeeCallback}
510+
isDisabled={!canUpgradeToChargebee || !!assignedTs || pendingChargebeeCallback}
509511
plan={applyCoupons(professionalPlan, appliedCoupons)}
510512
isCurrent={true}
511513
bottomLabel={bottomLabel}
@@ -545,7 +547,7 @@ export default function () {
545547
}
546548
planCards.push(
547549
<PlanCard
548-
isDisabled={!!assignedTs || pendingChargebeeCallback}
550+
isDisabled={!canUpgradeToChargebee || !!assignedTs || pendingChargebeeCallback}
549551
plan={targetPlan}
550552
isCurrent={!!assignedProfessionalTs}
551553
onUpgrade={onUpgrade}
@@ -583,7 +585,7 @@ export default function () {
583585
) : undefined;
584586
planCards.push(
585587
<PlanCard
586-
isDisabled={!!assignedTs || pendingChargebeeCallback}
588+
isDisabled={!canUpgradeToChargebee || !!assignedTs || pendingChargebeeCallback}
587589
plan={applyCoupons(studentUnleashedPlan, appliedCoupons)}
588590
isCurrent={true}
589591
bottomLabel={bottomLabel}
@@ -599,7 +601,7 @@ export default function () {
599601
) : undefined;
600602
planCards.push(
601603
<PlanCard
602-
isDisabled={!!assignedTs || pendingChargebeeCallback}
604+
isDisabled={!canUpgradeToChargebee || !!assignedTs || pendingChargebeeCallback}
603605
plan={applyCoupons(unleashedPlan, appliedCoupons)}
604606
isCurrent={true}
605607
bottomLabel={bottomLabel}
@@ -618,7 +620,7 @@ export default function () {
618620
}
619621
planCards.push(
620622
<PlanCard
621-
isDisabled={!!assignedTs || pendingChargebeeCallback}
623+
isDisabled={!canUpgradeToChargebee || !!assignedTs || pendingChargebeeCallback}
622624
plan={targetPlan}
623625
isCurrent={!!isUnleashedTsAssigned}
624626
onUpgrade={onUpgrade}
@@ -629,101 +631,87 @@ export default function () {
629631
);
630632
}
631633

632-
const showPlans = userBillingMode && userBillingMode.mode === "chargebee";
633634
return (
634635
<div>
635636
<PageWithSettingsSubMenu title="Plans" subtitle="Manage account usage and billing.">
636-
{showPlans && (
637-
<div className="w-full text-center">
638-
<p className="text-xl text-gray-500">
639-
You are currently using the{" "}
640-
<span className="font-bold">
641-
{Plans.getById(assignedTs?.planId)?.name || currentPlan.name}
642-
</span>{" "}
643-
plan.
637+
<div className="w-full text-center">
638+
<p className="text-xl text-gray-500">
639+
You are currently using the{" "}
640+
<span className="font-bold">{Plans.getById(assignedTs?.planId)?.name || currentPlan.name}</span>{" "}
641+
plan.
642+
</p>
643+
{canUpgradeToChargebee && !assignedTs && (
644+
<p className="text-base w-96 m-auto">
645+
Upgrade your plan to get more hours and more parallel workspaces.
644646
</p>
645-
{!assignedTs && (
646-
<p className="text-base w-96 m-auto">
647-
Upgrade your plan to get more hours and more parallel workspaces.
648-
</p>
649-
)}
650-
<Tooltip
651-
content={`Current billing cycle: ${guessCurrentBillingCycle(currentPlan, accountStatement)
652-
.map((d) => d.toLocaleDateString())
653-
.join(" - ")}`}
647+
)}
648+
<Tooltip
649+
content={`Current billing cycle: ${guessCurrentBillingCycle(currentPlan, accountStatement)
650+
.map((d) => d.toLocaleDateString())
651+
.join(" - ")}`}
652+
>
653+
<p className="mt-2 font-semibold text-gray-500">
654+
Remaining hours:{" "}
655+
{typeof accountStatement?.remainingHours === "number"
656+
? Math.floor(accountStatement.remainingHours * 10) / 10
657+
: accountStatement?.remainingHours}
658+
</p>
659+
</Tooltip>
660+
{typeof accountStatement?.remainingHours === "number" &&
661+
typeof currentPlan.hoursPerMonth === "number" ? (
662+
<progress
663+
value={currentPlan.hoursPerMonth - accountStatement.remainingHours}
664+
max={currentPlan.hoursPerMonth}
665+
/>
666+
) : (
667+
<progress value="0" max="100" />
668+
)}
669+
<p className="text-sm">
670+
<a
671+
className={`gp-link ${isChargebeeCustomer ? "" : "invisible"}`}
672+
href="javascript:void(0)"
673+
onClick={() => {
674+
ChargebeeClient.getOrCreate().then((chargebeeClient) => chargebeeClient.openPortal());
675+
}}
654676
>
655-
<p className="mt-2 font-semibold text-gray-500">
656-
Remaining hours:{" "}
657-
{typeof accountStatement?.remainingHours === "number"
658-
? Math.floor(accountStatement.remainingHours * 10) / 10
659-
: accountStatement?.remainingHours}
660-
</p>
661-
</Tooltip>
662-
{typeof accountStatement?.remainingHours === "number" &&
663-
typeof currentPlan.hoursPerMonth === "number" ? (
664-
<progress
665-
value={currentPlan.hoursPerMonth - accountStatement.remainingHours}
666-
max={currentPlan.hoursPerMonth}
667-
/>
668-
) : (
669-
<progress value="0" max="100" />
677+
Billing
678+
</a>
679+
{!!accountStatement && Plans.isFreePlan(currentPlan.chargebeeId) && (
680+
<span className="pl-6">
681+
{currency === "EUR" ? (
682+
<>
683+
€ /{" "}
684+
<a
685+
className="text-blue-light hover:underline"
686+
href="javascript:void(0)"
687+
onClick={() => setCurrency("USD")}
688+
>
689+
$
690+
</a>
691+
</>
692+
) : (
693+
<>
694+
<a
695+
className="text-blue-light hover:underline"
696+
href="javascript:void(0)"
697+
onClick={() => setCurrency("EUR")}
698+
>
699+
700+
</a>{" "}
701+
/ $
702+
</>
703+
)}
704+
</span>
670705
)}
671-
<p className="text-sm">
672-
<a
673-
className={`gp-link ${isChargebeeCustomer ? "" : "invisible"}`}
674-
href="javascript:void(0)"
675-
onClick={() => {
676-
ChargebeeClient.getOrCreate().then((chargebeeClient) =>
677-
chargebeeClient.openPortal(),
678-
);
679-
}}
680-
>
681-
Billing
682-
</a>
683-
{!!accountStatement && Plans.isFreePlan(currentPlan.chargebeeId) && (
684-
<span className="pl-6">
685-
{currency === "EUR" ? (
686-
<>
687-
€ /{" "}
688-
<a
689-
className="text-blue-light hover:underline"
690-
href="javascript:void(0)"
691-
onClick={() => setCurrency("USD")}
692-
>
693-
$
694-
</a>
695-
</>
696-
) : (
697-
<>
698-
<a
699-
className="text-blue-light hover:underline"
700-
href="javascript:void(0)"
701-
onClick={() => setCurrency("EUR")}
702-
>
703-
704-
</a>{" "}
705-
/ $
706-
</>
707-
)}
708-
</span>
709-
)}
710-
</p>
711-
</div>
712-
)}
706+
</p>
707+
</div>
713708
<div className="mt-4 flex justify-center space-x-3 2xl:space-x-7">{planCards}</div>
714709
{assignedTs && userBillingMode?.mode === "chargebee" && !!userBillingMode.teamNames && (
715710
<Alert type="info" className="mt-10 mx-auto">
716711
<p>Assigned Team Seats</p>
717712
<ul>{userBillingMode.teamNames.join(", ")}</ul>
718713
</Alert>
719714
)}
720-
<InfoBox className="w-2/3 mt-14 mx-auto">
721-
If you are interested in purchasing a plan for a team, purchase a Team plan with one centralized
722-
billing.{" "}
723-
<a className="underline" href="https://www.gitpod.io/docs/teams/">
724-
Learn more
725-
</a>
726-
</InfoBox>
727715
{!!confirmUpgradeToPlan && (
728716
// TODO: Use title and buttons props
729717
<Modal visible={true} onClose={() => setConfirmUpgradeToPlan(undefined)}>

components/dashboard/src/settings/Teams.tsx

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License-AGPL.txt in the project root for license information.
55
*/
66

7-
import React, { useContext, useEffect, useRef, useState } from "react";
7+
import { useContext, useEffect, useRef, useState } from "react";
88
import ContextMenu, { ContextMenuEntry } from "../components/ContextMenu";
99
import { getGitpodService } from "../service/service";
1010
import Alert from "../components/Alert";
@@ -453,8 +453,9 @@ function AllTeams() {
453453
return pendingSlotsPurchase && pendingSlotsPurchase.tsId === ts.id;
454454
};
455455

456-
const renderTeams = () => (
457-
<React.Fragment>
456+
const canUpgradeToChargebee = !BillingMode.showUsageBasedBilling(userBillingMode);
457+
return (
458+
<div>
458459
<div className="flex flex-row">
459460
<div className="flex-grow ">
460461
<h3 className="self-center">All Team Plans</h3>
@@ -469,7 +470,9 @@ function AllTeams() {
469470
{getActiveSubs().length > 0 && (
470471
<button
471472
className="self-end my-auto"
472-
disabled={!!pendingPlanPurchase || getAvailableSubTypes().length === 0}
473+
disabled={
474+
!canUpgradeToChargebee || !!pendingPlanPurchase || getAvailableSubTypes().length === 0
475+
}
473476
onClick={() => showCreateTeamModal()}
474477
>
475478
Create Team Plan
@@ -501,7 +504,7 @@ function AllTeams() {
501504
<AddMembersModal onClose={() => setAddMembersModal(undefined)} onBuy={onBuy} {...addMembersModal} />
502505
)}
503506

504-
{getActiveSubs().length === 0 && !pendingPlanPurchase && (
507+
{getActiveSubs().length === 0 && !pendingPlanPurchase && canUpgradeToChargebee && (
505508
<div className="w-full flex h-80 mt-2 rounded-xl bg-gray-100 dark:bg-gray-900">
506509
<div className="m-auto text-center">
507510
<h3 className="self-center text-gray-500 dark:text-gray-400 mb-4">No Active Team Plans</h3>
@@ -602,23 +605,6 @@ function AllTeams() {
602605
))}
603606
</div>
604607
)}
605-
</React.Fragment>
606-
);
607-
608-
// TOOD(gpl) We might want to reduce visibility of those actions similarly to how we guard access to that API, cmp.: https://github.com/gitpod-io/gitpod/blob/db90aefb9f7dc1d062e9cb73241c1fda2eccee4b/components/server/ee/src/workspace/gitpod-server-impl.ts#L2011
609-
const showTeamPlans = BillingMode.showTeamSubscriptionUI(userBillingMode);
610-
return (
611-
<div>
612-
{showTeamPlans ? (
613-
renderTeams()
614-
) : (
615-
<div className="flex flex-row">
616-
<div className="flex-grow ">
617-
<h3 className="self-center">All Team Plans</h3>
618-
<h2>Manage team plans and team members.</h2>
619-
</div>
620-
</div>
621-
)}
622608
</div>
623609
);
624610
}

0 commit comments

Comments
 (0)