4
4
* See License-AGPL.txt in the project root for license information.
5
5
*/
6
6
7
- import { useContext } from "react" ;
7
+ import { Team } from "@gitpod/gitpod-protocol" ;
8
+ import { useContext , useEffect , useState } from "react" ;
8
9
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" ;
9
13
import { PageWithSubMenu } from "../components/PageWithSubMenu" ;
10
14
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" ;
12
18
13
19
export default function Billing ( ) {
20
+ const { user } = useContext ( UserContext ) ;
14
21
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
+ } ;
15
49
16
50
return (
17
51
< PageWithSubMenu
@@ -21,13 +55,45 @@ export default function Billing() {
21
55
>
22
56
< h3 > Usage-Based Billing</ h3 >
23
57
< 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 >
31
97
</ PageWithSubMenu >
32
98
) ;
33
99
}
0 commit comments