Skip to content

[pat] List view with tokens #14934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 29 additions & 12 deletions components/dashboard/src/settings/PersonalAccessTokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Timestamp } from "@bufbuild/protobuf";
import Alert from "../components/Alert";
import { InputWithCopy } from "../components/InputWithCopy";
import { copyToClipboard } from "../utils";
import TokenEntry from "./TokenEntry";

function PersonalAccessTokens() {
const { enablePersonalAccessTokens } = useContext(FeatureFlagContext);
Expand Down Expand Up @@ -197,14 +198,16 @@ function ListAccessTokensView() {

return (
<>
<div className="flex items-center sm:justify-between mb-2">
<div className="flex items-center sm:justify-between mb-4">
<div>
<h3>Personal Access Tokens</h3>
<h2 className="text-gray-500">Create or regenerate active personal access tokens.</h2>
</div>
<Link to={settingsPathPersonalAccessTokenCreate}>
<button>New Personal Access Token</button>
</Link>
{tokens.length > 0 && (
<Link to={settingsPathPersonalAccessTokenCreate}>
<button>New Personal Access Token</button>
</Link>
)}
</div>
<>
{tokenInfo && (
Expand Down Expand Up @@ -251,16 +254,30 @@ function ListAccessTokensView() {
</>
)}
</>
{tokens.length > 0 && (
<ul>
{tokens.length === 0 ? (
<div className="bg-gray-100 dark:bg-gray-800 rounded-xl w-full py-28 flex flex-col items-center">
<h3 className="text-center pb-3 text-gray-500 dark:text-gray-400">
No Personal Access Tokens (PAT)
</h3>
<p className="text-center pb-6 text-gray-500 text-base w-96">
Generate a personal access token (PAT) for applications that need access to the Gitpod API.{" "}
</p>
<Link to={settingsPathPersonalAccessTokenCreate}>
<button>New Personal Access Token</button>
</Link>
</div>
) : (
<>
<div className="px-6 py-3 flex justify-between space-x-2 text-sm text-gray-400 mb-2 bg-gray-100 rounded-xl">
<h2 className="w-3/12">Token Name</h2>
<h2 className="w-3/12">Permissions</h2>
<h2 className="w-3/12">Expires</h2>
<div className="w-3/12"></div>
</div>
{tokens.map((t: PersonalAccessToken) => {
return (
<li>
{t.id} - {t.name} - {t.value}
</li>
);
return <TokenEntry token={t} />;
})}
</ul>
</>
)}
</>
);
Expand Down
75 changes: 75 additions & 0 deletions components/dashboard/src/settings/TokenEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License-AGPL.txt in the project root for license information.
*/

import { PersonalAccessToken } from "@gitpod/public-api/lib/gitpod/experimental/v1/tokens_pb";
import { ContextMenuEntry } from "../components/ContextMenu";
import { ItemFieldContextMenu } from "../components/ItemsList";

const menuEntries: ContextMenuEntry[] = [
{
title: "Edit",
href: "",
onClick: () => {},
},
{
title: "Regenerate",
href: "",
onClick: () => {},
},
{
title: "Delete",
href: "",
onClick: () => {},
},
];

function TokenEntry(t: { token: PersonalAccessToken }) {
if (!t) {
return <></>;
}

const getDate = () => {
if (!t.token.expirationTime) {
return "";
}
const date = t.token.expirationTime?.toDate();
return date.toDateString();
};

const defaultAllScope = ["function:*", "resource:default"];

const getScopes = () => {
if (!t.token.scopes) {
return "";
}
if (t.token.scopes.every((v) => defaultAllScope.includes(v))) {
return "Access the user's API";
} else {
return "No access";
}
};

return (
<>
<div className="rounded-xl whitespace-nowrap flex space-x-2 py-4 px-4 w-full justify-between hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-gitpod-kumquat-light group">
<div className="pr-3 self-center w-3/12">
<span>{t.token.name || ""}</span>
</div>
<div className="flex flex-col w-3/12 text-gray-400 font-medium">
<span>{getScopes()}</span>
</div>
<div className="flex flex-col w-3/12 text-gray-400">
<span>{getDate()}</span>
</div>
<div className="flex flex-col w-3/12">
<ItemFieldContextMenu menuEntries={menuEntries} />
</div>
</div>
</>
);
}

export default TokenEntry;