Skip to content

Commit 6afe081

Browse files
jankeromnesroboquat
authored andcommitted
[dashboard] When usage-based billing is enabled, allow users to specify a 'billing account' to attribute all their usage to
1 parent 441252f commit 6afe081

File tree

1 file changed

+75
-9
lines changed

1 file changed

+75
-9
lines changed

components/dashboard/src/settings/Billing.tsx

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

7-
import { useContext } from "react";
7+
import { Team } from "@gitpod/gitpod-protocol";
8+
import { useContext, useEffect, useState } from "react";
89
import { Link } from "react-router-dom";
10+
import getSettingsMenu from "./settings-menu";
11+
import { ReactComponent as Spinner } from "../icons/Spinner.svg";
12+
import DropDown from "../components/DropDown";
913
import { PageWithSubMenu } from "../components/PageWithSubMenu";
1014
import { PaymentContext } from "../payment-context";
11-
import getSettingsMenu from "./settings-menu";
15+
import { getGitpodService } from "../service/service";
16+
import { TeamsContext } from "../teams/teams-context";
17+
import { UserContext } from "../user-context";
1218

1319
export default function Billing() {
20+
const { user } = useContext(UserContext);
1421
const { showPaymentUI, showUsageBasedUI } = useContext(PaymentContext);
22+
const { teams } = useContext(TeamsContext);
23+
const [teamsWithBillingEnabled, setTeamsWithBillingEnabled] = useState<Team[] | undefined>();
24+
25+
useEffect(() => {
26+
if (!teams) {
27+
setTeamsWithBillingEnabled(undefined);
28+
return;
29+
}
30+
const teamsWithBilling: Team[] = [];
31+
Promise.all(
32+
teams.map(async (t) => {
33+
const subscriptionId = await getGitpodService().server.findStripeSubscriptionIdForTeam(t.id);
34+
if (subscriptionId) {
35+
teamsWithBilling.push(t);
36+
}
37+
}),
38+
).then(() => setTeamsWithBillingEnabled(teamsWithBilling));
39+
}, [teams]);
40+
41+
const setUsageAttributionTeam = async (team?: Team) => {
42+
if (!user) {
43+
return;
44+
}
45+
const additionalData = user.additionalData || {};
46+
additionalData.usageAttributionId = team ? `team:${team.id}` : `user:${user.id}`;
47+
await getGitpodService().server.updateLoggedInUser({ additionalData });
48+
};
1549

1650
return (
1751
<PageWithSubMenu
@@ -21,13 +55,45 @@ export default function Billing() {
2155
>
2256
<h3>Usage-Based Billing</h3>
2357
<h2 className="text-gray-500">Manage usage-based billing, spending limit, and payment method.</h2>
24-
<p className="mt-8">
25-
Hint:{" "}
26-
<Link className="gp-link" to="/teams/new">
27-
Create a team
28-
</Link>{" "}
29-
to set up usage-based billing.
30-
</p>
58+
<div className="mt-8">
59+
<h3>Billing Account</h3>
60+
{teamsWithBillingEnabled === undefined && <Spinner className="m-2 h-5 w-5 animate-spin" />}
61+
{teamsWithBillingEnabled && teamsWithBillingEnabled.length === 0 && (
62+
<div className="flex space-x-2">
63+
<span>
64+
<Link className="gp-link" to="/teams/new">
65+
Create a team
66+
</Link>{" "}
67+
to set up usage-based billing.
68+
</span>
69+
</div>
70+
)}
71+
{teamsWithBillingEnabled && teamsWithBillingEnabled.length > 0 && (
72+
<div className="flex space-x-2">
73+
<span>Bill all my usage to:</span>
74+
<DropDown
75+
activeEntry={
76+
teamsWithBillingEnabled.find(
77+
(t) => `team:${t.id}` === user?.additionalData?.usageAttributionId,
78+
)?.name
79+
}
80+
customClasses="w-32"
81+
renderAsLink={true}
82+
entries={[
83+
{
84+
title: "(myself)",
85+
onClick: () => setUsageAttributionTeam(undefined),
86+
},
87+
].concat(
88+
teamsWithBillingEnabled.map((t) => ({
89+
title: t.name,
90+
onClick: () => setUsageAttributionTeam(t),
91+
})),
92+
)}
93+
/>
94+
</div>
95+
)}
96+
</div>
3197
</PageWithSubMenu>
3298
);
3399
}

0 commit comments

Comments
 (0)