@@ -8,13 +8,14 @@ import { useContext, useEffect, useState } from "react";
8
8
import { Redirect , useLocation } from "react-router" ;
9
9
import { getCurrentTeam , TeamsContext } from "./teams-context" ;
10
10
import { PaymentContext } from "../payment-context" ;
11
- import { getGitpodService } from "../service/service" ;
11
+ import { getGitpodService , gitpodHostUrl } from "../service/service" ;
12
12
import { BillableSession , BillableWorkspaceType } from "@gitpod/gitpod-protocol/lib/usage" ;
13
13
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution" ;
14
14
import { Item , ItemField , ItemsList } from "../components/ItemsList" ;
15
15
import moment from "moment" ;
16
16
import Pagination from "../components/Pagination" ;
17
17
import Header from "../components/Header" ;
18
+ import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error" ;
18
19
19
20
function TeamUsage ( ) {
20
21
const { teams } = useContext ( TeamsContext ) ;
@@ -24,15 +25,22 @@ function TeamUsage() {
24
25
const [ billedUsage , setBilledUsage ] = useState < BillableSession [ ] > ( [ ] ) ;
25
26
const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
26
27
const [ resultsPerPage ] = useState ( 10 ) ;
28
+ const [ errorMessage , setErrorMessage ] = useState ( "" ) ;
27
29
28
30
useEffect ( ( ) => {
29
31
if ( ! team ) {
30
32
return ;
31
33
}
32
34
( async ( ) => {
33
35
const attributionId = AttributionId . render ( { kind : "team" , teamId : team . id } ) ;
34
- const billedUsageResult = await getGitpodService ( ) . server . listBilledUsage ( attributionId ) ;
35
- setBilledUsage ( billedUsageResult ) ;
36
+ try {
37
+ const billedUsageResult = await getGitpodService ( ) . server . listBilledUsage ( attributionId ) ;
38
+ setBilledUsage ( billedUsageResult ) ;
39
+ } catch ( error ) {
40
+ if ( error . code === ErrorCodes . PERMISSION_DENIED ) {
41
+ setErrorMessage ( "Access to usage details is restricted to team owners." ) ;
42
+ }
43
+ }
36
44
} ) ( ) ;
37
45
} , [ team ] ) ;
38
46
@@ -75,128 +83,147 @@ function TeamUsage() {
75
83
< >
76
84
< Header title = "Usage" subtitle = "Manage team usage." />
77
85
< div className = "app-container pt-9" >
78
- < div className = "flex space-x-16" >
79
- < div className = "flex" >
80
- < div className = "space-y-8 mb-6" style = { { width : "max-content" } } >
81
- < div className = "flex flex-col truncate" >
82
- < div className = "text-base text-gray-500 truncate" > Period</ div >
83
- < div className = "text-lg text-gray-600 font-semibold truncate" > June 2022</ div >
84
- </ div >
85
- < div className = "flex flex-col truncate" >
86
- < div className = "text-base text-gray-500" > Total usage</ div >
87
- < div className = "flex text-lg text-gray-600 font-semibold" >
88
- < svg
89
- className = "my-auto mr-1"
90
- width = "20"
91
- height = "20"
92
- fill = "none"
93
- xmlns = "http://www.w3.org/2000/svg"
94
- >
95
- < path
96
- fill-rule = "evenodd"
97
- clip-rule = "evenodd"
98
- d = "M5 2a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3H5Zm5.2 11.4a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4Z"
99
- fill = "url(#a)"
100
- />
101
- < defs >
102
- < linearGradient
103
- id = "a"
104
- x1 = "4.3"
105
- y1 = "4.3"
106
- x2 = "16.071"
107
- y2 = "17.107"
108
- gradientUnits = "userSpaceOnUse"
109
- >
110
- < stop stop-color = "#FFAD33" />
111
- < stop offset = "1" stop-color = "#FF8A00" />
112
- </ linearGradient >
113
- </ defs >
114
- </ svg >
115
- < span > { calculateTotalUsage ( ) } Total Credits</ span >
86
+ { errorMessage && < p className = "text-base" > { errorMessage } </ p > }
87
+ { ! errorMessage && (
88
+ < div className = "flex space-x-16" >
89
+ < div className = "flex" >
90
+ < div className = "space-y-8 mb-6" style = { { width : "max-content" } } >
91
+ < div className = "flex flex-col truncate" >
92
+ < div className = "text-base text-gray-500 truncate" > Period</ div >
93
+ < div className = "text-lg text-gray-600 font-semibold truncate" > June 2022</ div >
94
+ </ div >
95
+ < div className = "flex flex-col truncate" >
96
+ < div className = "text-base text-gray-500" > Total usage</ div >
97
+ < div className = "flex text-lg text-gray-600 font-semibold" >
98
+ < svg
99
+ className = "my-auto mr-1"
100
+ width = "20"
101
+ height = "20"
102
+ fill = "none"
103
+ xmlns = "http://www.w3.org/2000/svg"
104
+ >
105
+ < path
106
+ fill-rule = "evenodd"
107
+ clip-rule = "evenodd"
108
+ d = "M5 2a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3H5Zm5.2 11.4a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4Z"
109
+ fill = "url(#a)"
110
+ />
111
+ < defs >
112
+ < linearGradient
113
+ id = "a"
114
+ x1 = "4.3"
115
+ y1 = "4.3"
116
+ x2 = "16.071"
117
+ y2 = "17.107"
118
+ gradientUnits = "userSpaceOnUse"
119
+ >
120
+ < stop stop-color = "#FFAD33" />
121
+ < stop offset = "1" stop-color = "#FF8A00" />
122
+ </ linearGradient >
123
+ </ defs >
124
+ </ svg >
125
+ < span > { calculateTotalUsage ( ) } Total Credits</ span >
126
+ </ div >
116
127
</ div >
117
128
</ div >
118
129
</ div >
119
- </ div >
120
- < div className = "flex flex-col w-full mb-8" >
121
- < h3 > All Usage</ h3 >
122
- < span className = "text-gray-500 mb-5" > View usage details of all team members.</ span >
123
- < ItemsList className = "mt-2 text-gray-500" >
124
- < Item header = { false } className = "grid grid-cols-5 bg-gray-100 mb-5" >
125
- < ItemField className = "my-auto" >
126
- < span > Type</ span >
127
- </ ItemField >
128
- < ItemField className = "my-auto" >
129
- < span > Class</ span >
130
- </ ItemField >
131
- < ItemField className = "my-auto" >
132
- < span > Usage</ span >
133
- </ ItemField >
134
- < ItemField className = "flex my-auto" >
135
- < svg
136
- className = "my-auto mr-1"
137
- width = "20"
138
- height = "20"
139
- fill = "none"
140
- xmlns = "http://www.w3.org/2000/svg"
141
- >
142
- < path
143
- fill-rule = "evenodd"
144
- clip-rule = "evenodd"
145
- d = "M5 2a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3H5Zm5.2 11.4a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4Z"
146
- fill = "url(#a)"
147
- />
148
- < defs >
149
- < linearGradient
150
- id = "a"
151
- x1 = "4.3"
152
- y1 = "4.3"
153
- x2 = "16.071"
154
- y2 = "17.107"
155
- gradientUnits = "userSpaceOnUse"
130
+ { billedUsage . length === 0 && ! errorMessage && (
131
+ < div className = "flex flex-col w-full mb-8" >
132
+ < h3 className = "text-center text-gray-500 mt-8" > No sessions found.</ h3 >
133
+ < p className = "text-center text-gray-500 mt-1" >
134
+ Have you started any
135
+ < a className = "gp-link" href = { gitpodHostUrl . asWorkspacePage ( ) . toString ( ) } >
136
+ { " " }
137
+ workspaces
138
+ </ a > { " " }
139
+ or checked your other teams?
140
+ </ p >
141
+ </ div >
142
+ ) }
143
+ { billedUsage . length > 0 && (
144
+ < div className = "flex flex-col w-full mb-8" >
145
+ < h3 > All Usage</ h3 >
146
+ < span className = "text-gray-500 mb-5" > View usage details of all team members.</ span >
147
+ < ItemsList className = "mt-2 text-gray-500" >
148
+ < Item header = { false } className = "grid grid-cols-5 bg-gray-100 mb-5" >
149
+ < ItemField className = "my-auto" >
150
+ < span > Type</ span >
151
+ </ ItemField >
152
+ < ItemField className = "my-auto" >
153
+ < span > Class</ span >
154
+ </ ItemField >
155
+ < ItemField className = "my-auto" >
156
+ < span > Usage</ span >
157
+ </ ItemField >
158
+ < ItemField className = "flex my-auto" >
159
+ < svg
160
+ className = "my-auto mr-1"
161
+ width = "20"
162
+ height = "20"
163
+ fill = "none"
164
+ xmlns = "http://www.w3.org/2000/svg"
156
165
>
157
- < stop stop-color = "#FFAD33" />
158
- < stop offset = "1" stop-color = "#FF8A00" />
159
- </ linearGradient >
160
- </ defs >
161
- </ svg >
162
- < span > Credits</ span >
163
- </ ItemField >
164
- < ItemField className = "my-auto" />
165
- </ Item >
166
- { currentPaginatedResults . map ( ( usage ) => (
167
- < div
168
- key = { usage . instanceId }
169
- className = "flex p-3 grid grid-cols-5 justify-between transition ease-in-out rounded-xl focus:bg-gitpod-kumquat-light"
170
- >
171
- < div className = "my-auto" >
172
- < span > { getType ( usage . workspaceType ) } </ span >
173
- </ div >
174
- < div className = "my-auto" >
175
- < span className = "text-gray-400" > { usage . workspaceClass } </ span >
176
- </ div >
177
- < div className = "my-auto" >
178
- < span className = "text-gray-700" > { getMinutes ( usage ) } </ span >
179
- </ div >
180
- < div className = "my-auto" >
181
- < span className = "text-gray-700" > { usage . credits . toFixed ( 1 ) } </ span >
182
- </ div >
183
- < div className = "my-auto" >
184
- < span className = "text-gray-400" >
185
- { moment ( new Date ( usage . startTime ) . toDateString ( ) ) . fromNow ( ) }
186
- </ span >
187
- </ div >
188
- </ div >
189
- ) ) }
190
- </ ItemsList >
191
- { billedUsage . length > resultsPerPage && (
192
- < Pagination
193
- currentPage = { currentPage }
194
- setCurrentPage = { setCurrentPage }
195
- numberOfPages = { numberOfPages }
196
- />
166
+ < path
167
+ fill-rule = "evenodd"
168
+ clip-rule = "evenodd"
169
+ d = "M5 2a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3H5Zm5.2 11.4a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4Z"
170
+ fill = "url(#a)"
171
+ />
172
+ < defs >
173
+ < linearGradient
174
+ id = "a"
175
+ x1 = "4.3"
176
+ y1 = "4.3"
177
+ x2 = "16.071"
178
+ y2 = "17.107"
179
+ gradientUnits = "userSpaceOnUse"
180
+ >
181
+ < stop stop-color = "#FFAD33" />
182
+ < stop offset = "1" stop-color = "#FF8A00" />
183
+ </ linearGradient >
184
+ </ defs >
185
+ </ svg >
186
+ < span > Credits</ span >
187
+ </ ItemField >
188
+ < ItemField className = "my-auto" />
189
+ </ Item >
190
+ { currentPaginatedResults &&
191
+ currentPaginatedResults . map ( ( usage ) => (
192
+ < div
193
+ key = { usage . instanceId }
194
+ className = "flex p-3 grid grid-cols-5 justify-between transition ease-in-out rounded-xl focus:bg-gitpod-kumquat-light"
195
+ >
196
+ < div className = "my-auto" >
197
+ < span > { getType ( usage . workspaceType ) } </ span >
198
+ </ div >
199
+ < div className = "my-auto" >
200
+ < span className = "text-gray-400" > { usage . workspaceClass } </ span >
201
+ </ div >
202
+ < div className = "my-auto" >
203
+ < span className = "text-gray-700" > { getMinutes ( usage ) } </ span >
204
+ </ div >
205
+ < div className = "my-auto" >
206
+ < span className = "text-gray-700" > { usage . credits . toFixed ( 1 ) } </ span >
207
+ </ div >
208
+ < div className = "my-auto" >
209
+ < span className = "text-gray-400" >
210
+ { moment ( new Date ( usage . startTime ) . toDateString ( ) ) . fromNow ( ) }
211
+ </ span >
212
+ </ div >
213
+ </ div >
214
+ ) ) }
215
+ </ ItemsList >
216
+ { billedUsage . length > resultsPerPage && (
217
+ < Pagination
218
+ currentPage = { currentPage }
219
+ setCurrentPage = { setCurrentPage }
220
+ numberOfPages = { numberOfPages }
221
+ />
222
+ ) }
223
+ </ div >
197
224
) }
198
225
</ div >
199
- </ div >
226
+ ) }
200
227
</ div >
201
228
</ >
202
229
) ;
0 commit comments