Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/usage/UsageEntry.tsx
2499 views
1
/**
2
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
3
* Licensed under the GNU Affero General Public License (AGPL).
4
* See License.AGPL.txt in the project root for license information.
5
*/
6
7
import { WorkspaceType } from "@gitpod/gitpod-protocol";
8
import { Usage, WorkspaceInstanceUsageData } from "@gitpod/gitpod-protocol/lib/usage";
9
import { FC } from "react";
10
import { useWorkspaceClasses } from "../data/workspaces/workspace-classes-query";
11
import { ReactComponent as UsageIcon } from "../images/usage-default.svg";
12
import { toRemoteURL } from "../projects/render-utils";
13
// TODO: shift these into a DatePicker component that wraps react-datepicker
14
import "react-datepicker/dist/react-datepicker.css";
15
import "../components/react-datepicker.css";
16
17
type Props = {
18
usage: Usage;
19
};
20
export const UsageEntry: FC<Props> = ({ usage }) => {
21
// We shouldn't be delivering these to the client, but just in case, don't try to render them
22
if (usage.kind !== "workspaceinstance") {
23
return null;
24
}
25
26
const metadata = usage.metadata as WorkspaceInstanceUsageData;
27
28
return (
29
<div
30
key={usage.workspaceInstanceId}
31
className="flex p-3 grid grid-cols-12 gap-x-3 justify-between transition ease-in-out rounded-xl"
32
>
33
<div className="flex flex-col col-span-2 my-auto">
34
<span className="text-gray-600 dark:text-gray-100 text-md font-medium">
35
{getType(metadata.workspaceType)}
36
</span>
37
<span className="text-sm text-gray-400 dark:text-gray-500">
38
<DisplayName workspaceClass={metadata.workspaceClass} />
39
</span>
40
</div>
41
<div className="flex flex-col col-span-5 my-auto">
42
<div className="flex">
43
{isRunning(usage) && (
44
<div
45
className="rounded-full w-2 h-2 text-sm align-middle bg-green-500 my-auto mx-1"
46
title="Still running"
47
/>
48
)}
49
<span className="truncate text-gray-600 dark:text-gray-100 text-md font-medium">
50
{metadata.workspaceId}
51
</span>
52
</div>
53
<span className="text-sm truncate text-gray-400 dark:text-gray-500">
54
{metadata.contextURL && toRemoteURL(metadata.contextURL)}
55
</span>
56
</div>
57
<div className="flex flex-col my-auto">
58
<span className="text-right text-gray-500 dark:text-gray-400 font-medium">{usage.credits}</span>
59
<span className="text-right text-sm text-gray-400 dark:text-gray-500">{getMinutes(usage)}</span>
60
</div>
61
<div className="my-auto" />
62
<div className="flex flex-col col-span-3 my-auto">
63
<span className="text-gray-400 dark:text-gray-500 truncate font-medium">
64
{displayTime(usage.effectiveTime!)}
65
</span>
66
<div className="flex">
67
{metadata.workspaceType === "prebuild" ? <UsageIcon className="my-auto w-4 h-4 mr-1" /> : ""}
68
{metadata.workspaceType === "prebuild" ? (
69
<span className="text-sm text-gray-400 dark:text-gray-500">Gitpod</span>
70
) : (
71
<div className="flex">
72
<img
73
className="my-auto rounded-full w-4 h-4 inline-block align-text-bottom mr-1 overflow-hidden"
74
src={metadata.userAvatarURL || ""}
75
alt="user avatar"
76
/>
77
<span className="text-sm text-gray-400 dark:text-gray-500">{metadata.userName || ""}</span>
78
</div>
79
)}
80
</div>
81
</div>
82
</div>
83
);
84
};
85
86
export const DisplayName: FC<{ workspaceClass: string }> = ({ workspaceClass }) => {
87
const supportedClasses = useWorkspaceClasses();
88
89
const workspaceDisplayName = supportedClasses.data?.find((wc) => wc.id === workspaceClass)?.displayName;
90
91
return <span>{workspaceDisplayName || workspaceClass}</span>;
92
};
93
94
const getType = (type: WorkspaceType) => {
95
if (type === "regular") {
96
return "Workspace";
97
}
98
return "Prebuild";
99
};
100
101
const isRunning = (usage: Usage) => {
102
if (usage.kind !== "workspaceinstance") {
103
return false;
104
}
105
const metaData = usage.metadata as WorkspaceInstanceUsageData;
106
return metaData.endTime === "" || metaData.endTime === undefined;
107
};
108
109
const getMinutes = (usage: Usage) => {
110
if (usage.kind !== "workspaceinstance") {
111
return "";
112
}
113
const metaData = usage.metadata as WorkspaceInstanceUsageData;
114
const end = metaData.endTime ? new Date(metaData.endTime).getTime() : Date.now();
115
const start = new Date(metaData.startTime).getTime();
116
const lengthOfUsage = Math.floor(end - start);
117
const inMinutes = (lengthOfUsage / (1000 * 60)).toFixed(1);
118
return inMinutes + " min";
119
};
120
121
export const displayTime = (time: string | number) => {
122
const options: Intl.DateTimeFormatOptions = {
123
day: "numeric",
124
month: "short",
125
year: "numeric",
126
hour: "numeric",
127
minute: "numeric",
128
};
129
return new Date(time).toLocaleDateString(undefined, options).replace("at ", "");
130
};
131
132