Skip to content

Commit 60cd9d5

Browse files
Laurie T. Malauroboquat
Laurie T. Malau
authored andcommitted
View without data or access
1 parent 0674341 commit 60cd9d5

File tree

2 files changed

+150
-119
lines changed

2 files changed

+150
-119
lines changed

components/dashboard/src/teams/TeamUsage.tsx

Lines changed: 146 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ import { useContext, useEffect, useState } from "react";
88
import { Redirect, useLocation } from "react-router";
99
import { getCurrentTeam, TeamsContext } from "./teams-context";
1010
import { PaymentContext } from "../payment-context";
11-
import { getGitpodService } from "../service/service";
11+
import { getGitpodService, gitpodHostUrl } from "../service/service";
1212
import { BillableSession, BillableWorkspaceType } from "@gitpod/gitpod-protocol/lib/usage";
1313
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
1414
import { Item, ItemField, ItemsList } from "../components/ItemsList";
1515
import moment from "moment";
1616
import Pagination from "../components/Pagination";
1717
import Header from "../components/Header";
18+
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
1819

1920
function TeamUsage() {
2021
const { teams } = useContext(TeamsContext);
@@ -24,15 +25,22 @@ function TeamUsage() {
2425
const [billedUsage, setBilledUsage] = useState<BillableSession[]>([]);
2526
const [currentPage, setCurrentPage] = useState(1);
2627
const [resultsPerPage] = useState(10);
28+
const [errorMessage, setErrorMessage] = useState("");
2729

2830
useEffect(() => {
2931
if (!team) {
3032
return;
3133
}
3234
(async () => {
3335
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+
}
3644
})();
3745
}, [team]);
3846

@@ -75,128 +83,147 @@ function TeamUsage() {
7583
<>
7684
<Header title="Usage" subtitle="Manage team usage." />
7785
<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>
116127
</div>
117128
</div>
118129
</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"
156165
>
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>
197224
)}
198225
</div>
199-
</div>
226+
)}
200227
</div>
201228
</>
202229
);

components/gitpod-protocol/src/util/gitpod-host-url.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ export class GitpodHostUrl {
8585
return this.with((url) => ({ protocol: url.protocol === "https:" ? "wss:" : "ws:" }));
8686
}
8787

88+
asWorkspacePage(): GitpodHostUrl {
89+
return this.with((url) => ({ pathname: "/workspaces" }));
90+
}
91+
8892
asDashboard(): GitpodHostUrl {
8993
return this.with((url) => ({ pathname: "/" }));
9094
}

0 commit comments

Comments
 (0)