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" ;
89import { 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" ;
913import { PageWithSubMenu } from "../components/PageWithSubMenu" ;
1014import { 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
1319export 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