44 * See License.AGPL.txt in the project root for license information.
55 */
66
7- import { forwardRef , useCallback , useEffect , useState } from "react" ;
8- import { getGitpodService , gitpodHostUrl } from "../service/service" ;
9- import {
10- ListUsageRequest ,
11- Ordering ,
12- ListUsageResponse ,
13- WorkspaceInstanceUsageData ,
14- Usage ,
15- } from "@gitpod/gitpod-protocol/lib/usage" ;
7+ import { WorkspaceType } from "@gitpod/gitpod-protocol" ;
168import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution" ;
17- import { Item , ItemField , ItemsList } from "../components/ItemsList" ;
18- import Pagination from "../Pagination/Pagination" ;
19- import Header from "../components/Header" ;
209import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error" ;
10+ import { ListUsageRequest , Ordering , Usage , WorkspaceInstanceUsageData } from "@gitpod/gitpod-protocol/lib/usage" ;
11+ import dayjs from "dayjs" ;
12+ import { forwardRef , useEffect , useMemo , useState } from "react" ;
13+ import DatePicker from "react-datepicker" ;
14+ import "react-datepicker/dist/react-datepicker.css" ;
15+ import { useLocation } from "react-router" ;
16+ import Header from "../components/Header" ;
17+ import { Item , ItemField , ItemsList } from "../components/ItemsList" ;
18+ import { useListUsage } from "../data/usage/usage-query" ;
19+ import { useWorkspaceClasses } from "../data/workspaces/workspace-classes-query" ;
2120import Spinner from "../icons/Spinner.svg" ;
2221import { ReactComponent as UsageIcon } from "../images/usage-default.svg" ;
22+ import Pagination from "../Pagination/Pagination" ;
2323import { toRemoteURL } from "../projects/render-utils" ;
24- import { WorkspaceType } from "@gitpod/gitpod-protocol" ;
25- import DatePicker from "react-datepicker" ;
26- import "react-datepicker/dist/react-datepicker.css" ;
24+ import { gitpodHostUrl } from "../service/service" ;
2725import "./react-datepicker.css" ;
28- import { useLocation } from "react-router" ;
29- import dayjs from "dayjs" ;
3026import { Heading1 , Heading2 , Subheading } from "./typography/headings" ;
31- import { useWorkspaceClasses } from "../data/workspaces/workspace-classes-query" ;
3227
3328interface UsageViewProps {
3429 attributionId : AttributionId ;
3530}
3631
3732function UsageView ( { attributionId } : UsageViewProps ) {
38- const [ usagePage , setUsagePage ] = useState < ListUsageResponse | undefined > ( undefined ) ;
33+ const [ page , setPage ] = useState ( 1 ) ;
3934 const [ errorMessage , setErrorMessage ] = useState ( "" ) ;
4035 const startOfCurrentMonth = dayjs ( ) . startOf ( "month" ) ;
4136 const [ startDate , setStartDate ] = useState ( startOfCurrentMonth ) ;
4237 const [ endDate , setEndDate ] = useState ( dayjs ( ) ) ;
43- const [ totalCreditsUsed , setTotalCreditsUsed ] = useState < number > ( 0 ) ;
44- const [ isLoading , setIsLoading ] = useState < boolean > ( true ) ;
4538 const supportedClasses = useWorkspaceClasses ( ) ;
46-
4739 const location = useLocation ( ) ;
4840 useEffect ( ( ) => {
4941 const match = / # ( \d { 4 } - \d { 2 } - \d { 2 } ) : ( \d { 4 } - \d { 2 } - \d { 2 } ) / . exec ( location . hash ) ;
@@ -56,39 +48,29 @@ function UsageView({ attributionId }: UsageViewProps) {
5648 }
5749 }
5850 } , [ location ] ) ;
51+ const request = useMemo ( ( ) => {
52+ const request : ListUsageRequest = {
53+ attributionId : AttributionId . render ( attributionId ) ,
54+ from : startDate . startOf ( "day" ) . valueOf ( ) ,
55+ to : endDate . endOf ( "day" ) . valueOf ( ) ,
56+ order : Ordering . ORDERING_DESCENDING ,
57+ pagination : {
58+ perPage : 50 ,
59+ page,
60+ } ,
61+ } ;
62+ return request ;
63+ } , [ attributionId , endDate , page , startDate ] ) ;
64+ const usagePage = useListUsage ( request ) ;
65+
66+ if ( usagePage . error ) {
67+ if ( ( usagePage . error as any ) . code === ErrorCodes . PERMISSION_DENIED ) {
68+ setErrorMessage ( "Access to usage details is restricted to team owners." ) ;
69+ } else {
70+ setErrorMessage ( `Error: ${ usagePage . error ?. message } ` ) ;
71+ }
72+ }
5973
60- const loadPage = useCallback (
61- async ( page : number = 1 ) => {
62- if ( usagePage === undefined ) {
63- setIsLoading ( true ) ;
64- setTotalCreditsUsed ( 0 ) ;
65- }
66- const request : ListUsageRequest = {
67- attributionId : AttributionId . render ( attributionId ) ,
68- from : startDate . startOf ( "day" ) . valueOf ( ) ,
69- to : endDate . endOf ( "day" ) . valueOf ( ) ,
70- order : Ordering . ORDERING_DESCENDING ,
71- pagination : {
72- perPage : 50 ,
73- page,
74- } ,
75- } ;
76- try {
77- const page = await getGitpodService ( ) . server . listUsage ( request ) ;
78- setUsagePage ( page ) ;
79- setTotalCreditsUsed ( page . creditsUsed ) ;
80- } catch ( error ) {
81- if ( error . code === ErrorCodes . PERMISSION_DENIED ) {
82- setErrorMessage ( "Access to usage details is restricted to team owners." ) ;
83- } else {
84- setErrorMessage ( `Error: ${ error ?. message } ` ) ;
85- }
86- } finally {
87- setIsLoading ( false ) ;
88- }
89- } ,
90- [ attributionId , endDate , startDate , usagePage ] ,
91- ) ;
9274 useEffect ( ( ) => {
9375 if ( startDate . isAfter ( endDate ) ) {
9476 setErrorMessage ( "The start date needs to be before the end date." ) ;
@@ -99,8 +81,8 @@ function UsageView({ attributionId }: UsageViewProps) {
9981 return ;
10082 }
10183 setErrorMessage ( "" ) ;
102- loadPage ( 1 ) ;
103- } , [ startDate , endDate , loadPage ] ) ;
84+ setPage ( 1 ) ;
85+ } , [ startDate , endDate , setPage ] ) ;
10486
10587 const getType = ( type : WorkspaceType ) => {
10688 if ( type === "regular" ) {
@@ -172,7 +154,8 @@ function UsageView({ attributionId }: UsageViewProps) {
172154 return new Date ( time ) . toLocaleDateString ( undefined , options ) . replace ( "at " , "" ) ;
173155 } ;
174156
175- const currentPaginatedResults = usagePage ?. usageEntriesList . filter ( ( u ) => u . kind === "workspaceinstance" ) ?? [ ] ;
157+ const currentPaginatedResults =
158+ usagePage . data ?. usageEntriesList . filter ( ( u ) => u . kind === "workspaceinstance" ) ?? [ ] ;
176159 const DateDisplay = forwardRef ( ( arg : any , ref : any ) => (
177160 < div
178161 className = "px-2 py-0.5 text-gray-500 bg-gray-50 dark:text-gray-400 dark:bg-gray-800 rounded-md cursor-pointer flex items-center hover:bg-gray-100 dark:hover:bg-gray-700"
@@ -253,21 +236,21 @@ function UsageView({ attributionId }: UsageViewProps) {
253236 < div className = "text-base text-gray-500 truncate" > Previous Months</ div >
254237 { getBillingHistory ( ) }
255238 </ div >
256- { ! isLoading && (
239+ { ! usagePage . isLoading && (
257240 < div >
258241 < div className = "flex flex-col truncate" >
259242 < div className = "text-base text-gray-500" > Credits</ div >
260243 < div className = "flex text-lg text-gray-600 font-semibold" >
261244 < span className = "dark:text-gray-400" >
262- { totalCreditsUsed . toLocaleString ( ) }
245+ { usagePage . data ?. creditsUsed . toLocaleString ( ) }
263246 </ span >
264247 </ div >
265248 </ div >
266249 </ div >
267250 ) }
268251 </ div >
269252 </ div >
270- { ! isLoading &&
253+ { ! usagePage . isLoading &&
271254 ( usagePage === undefined || currentPaginatedResults . length === 0 ) &&
272255 ! errorMessage && (
273256 < div className = "flex flex-col w-full mb-8" >
@@ -282,13 +265,13 @@ function UsageView({ attributionId }: UsageViewProps) {
282265 </ Subheading >
283266 </ div >
284267 ) }
285- { isLoading && (
268+ { usagePage . isLoading && (
286269 < div className = "flex items-center justify-center w-full space-x-2 text-gray-400 text-sm pt-16 pb-40" >
287270 < img alt = "Loading Spinner" className = "h-4 w-4 animate-spin" src = { Spinner } />
288271 < span > Fetching usage...</ span >
289272 </ div >
290273 ) }
291- { ! isLoading && currentPaginatedResults . length > 0 && (
274+ { ! usagePage . isLoading && currentPaginatedResults . length > 0 && (
292275 < div className = "flex flex-col w-full mb-8" >
293276 < ItemsList className = "mt-2 text-gray-400 dark:text-gray-500" >
294277 < Item
@@ -402,13 +385,15 @@ function UsageView({ attributionId }: UsageViewProps) {
402385 ) ;
403386 } ) }
404387 </ ItemsList >
405- { usagePage && usagePage . pagination && usagePage . pagination . totalPages > 1 && (
406- < Pagination
407- currentPage = { usagePage . pagination . page }
408- setPage = { ( page ) => loadPage ( page ) }
409- totalNumberOfPages = { usagePage . pagination . totalPages }
410- />
411- ) }
388+ { usagePage . data &&
389+ usagePage . data . pagination &&
390+ usagePage . data . pagination . totalPages > 1 && (
391+ < Pagination
392+ currentPage = { usagePage . data . pagination . page }
393+ setPage = { setPage }
394+ totalNumberOfPages = { usagePage . data . pagination . totalPages }
395+ />
396+ ) }
412397 </ div >
413398 ) }
414399 </ div >
0 commit comments