Skip to content

Commit

Permalink
Repository Config list page (#19039)
Browse files Browse the repository at this point in the history
* fixing pagination logic

* wip

* flushing out rows

* adding missing creationTime

* adjusting result count copy

* fix offset... again

* Flushing out table UI a bit more

* Flushing out table UI a bit more

* comment for remembering to use search params

* more comments

* fix pages count

* copy/responsive adjustments

* updating search project query & service

* pass org id along and check permission in service

* Extracting table components

* cleanup

* fix menu copy
  • Loading branch information
selfcontained authored Nov 14, 2023
1 parent a5021da commit eaae6cc
Show file tree
Hide file tree
Showing 17 changed files with 480 additions and 81 deletions.
4 changes: 2 additions & 2 deletions components/dashboard/src/components/forms/TextInputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* See License.AGPL.txt in the project root for license information.
*/

import classNames from "classnames";
import { FunctionComponent, memo, ReactNode, useCallback } from "react";
import { useId } from "../../hooks/useId";
import { InputField } from "./InputField";
import { cn } from "@podkit/lib/cn";

type TextInputFieldTypes = "text" | "password" | "email" | "url";

Expand Down Expand Up @@ -97,7 +97,7 @@ export const TextInput: FunctionComponent<TextInputProps> = memo(
return (
<input
id={id}
className={classNames("w-full max-w-lg dark:text-[#A8A29E]", className)}
className={cn("w-full max-w-lg dark:text-[#A8A29E]", className)}
value={value}
type={type}
placeholder={placeholder}
Expand Down
4 changes: 2 additions & 2 deletions components/dashboard/src/components/podkit/buttons/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ export const buttonVariants = cva(
outline: "border border-input bg-transparent hover:bg-kumquat-ripe hover:text-gray-600",
secondary:
"bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-500 dark:text-gray-100 hover:text-gray-600",
ghost: "hover:bg-kumquat-ripe hover:text-gray-600",
ghost: "bg-transparent hover:bg-gray-100 hover:text-gray-600 text-gray-500",
link: "text-gray-500 dark:text-gray-100 underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
icon: "h-9 w-9 p-0 rounded-sm",
},
},
defaultVariants: {
Expand Down
25 changes: 25 additions & 0 deletions components/dashboard/src/components/podkit/layout/PageHeading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2023 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 { Heading1, Subheading } from "@podkit/typography/Headings";
import { FC, ReactNode } from "react";

type PageHeadingProps = {
title: string;
subtitle?: string;
action?: ReactNode;
};
export const PageHeading: FC<PageHeadingProps> = ({ title, subtitle, action }) => {
return (
<div className="flex flex-row flex-wrap justify-between py-8 gap-2">
<div>
<Heading1>{title}</Heading1>
{subtitle && <Subheading>{subtitle}</Subheading>}
</div>
{action && action}
</div>
);
};
68 changes: 68 additions & 0 deletions components/dashboard/src/components/podkit/tables/Table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright (c) 2023 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 { cn } from "@podkit/lib/cn";
import React from "react";

type HideableCellProps = {
hideOnSmallScreen?: boolean;
};

export const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
({ className, ...props }, ref) => {
return (
<div className="relative w-full overflow-auto">
<table ref={ref} className={cn("w-full text-sm text-left", className)} {...props} />
</div>
);
},
);
Table.displayName = "Table";

export const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => {
return (
<thead
ref={ref}
className="[&_th]:p-3 [&_th]:bg-gray-100 dark:[&_th]:bg-gray-800 [&_th:first-child]:rounded-tl-md [&_th:last-child]:rounded-tr-md text-semibold"
{...props}
/>
);
},
);
TableHeader.displayName = "TableHeader";

export const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
({ className, ...props }, ref) => {
return <tr ref={ref} className="border-b dark:border-gray-700" {...props} />;
},
);
TableRow.displayName = "TableRow";

export const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement> & HideableCellProps
>(({ hideOnSmallScreen, className, ...props }, ref) => {
return <th ref={ref} className={cn(hideOnSmallScreen && "hidden md:table-cell", className)} {...props} />;
});
TableHead.displayName = "TableHead";

export const TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => {
return (
<tbody ref={ref} className="[&_td]:p-3 [&_td:last-child]:text-right [&_tr]:hover:bg-muted/5" {...props} />
);
},
);
TableBody.displayName = "TableBody";

export const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement> & HideableCellProps
>(({ hideOnSmallScreen, className, ...props }, ref) => {
return <td ref={ref} className={cn(hideOnSmallScreen && "hidden md:table-cell", className)} {...props} />;
});
TableCell.displayName = "TableCell";
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const Heading1: FC<HeadingProps> = ({ id, color, tracking, className, chi
return (
<Comp
id={id}
className={cn(getHeadingColor(color), getTracking(tracking), "font-bold text-4xl truncate", className)}
className={cn(getHeadingColor(color), getTracking(tracking), "font-bold text-3xl truncate", className)}
>
{children}
</Comp>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ export const useListConfigurations = ({ searchTerm = "", page, pageSize }: ListC
pagination: { page, pageSize },
});

return { configurations, pagination };
return {
configurations,
pagination,
};
},
{
enabled: !!org,
keepPreviousData: true,
},
);
};
Expand Down
4 changes: 2 additions & 2 deletions components/dashboard/src/menu/OrganizationSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ export default function OrganizationSelector() {
if (currentOrg.data) {
if (repoConfigListAndDetail) {
linkEntries.push({
title: "Configurations",
customContent: <LinkEntry>Configurations</LinkEntry>,
title: "Repositories",
customContent: <LinkEntry>Repositories</LinkEntry>,
active: false,
separator: false,
link: "/repositories",
Expand Down
81 changes: 81 additions & 0 deletions components/dashboard/src/repositories/list/PaginationControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Copyright (c) 2023 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 { Button } from "@podkit/buttons/Button";
import { cn } from "@podkit/lib/cn";
import { TextMuted } from "@podkit/typography/TextMuted";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import { FC, useCallback } from "react";

type Props = {
currentPage: number;
totalPages: number;
pageSize: number;
totalRows: number;
currentRows: number;
onPageChanged: (page: number) => void;
};
export const PaginationControls: FC<Props> = ({
currentPage,
totalPages,
pageSize,
totalRows,
currentRows,
onPageChanged,
}) => {
const prevPage = useCallback(() => {
onPageChanged(currentPage - 1);
}, [currentPage, onPageChanged]);

const nextPage = useCallback(() => {
onPageChanged(currentPage + 1);
}, [currentPage, onPageChanged]);

return (
<div className="flex flex-row justify-center items-center py-2 gap-2 text-sm">
{/* TODO: Rows per page select */}
<PaginationCountText
className="w-24"
currentPage={currentPage}
pageSize={pageSize}
currentRows={currentRows}
totalRows={totalRows}
/>
<Button variant="ghost" size="icon" onClick={prevPage} disabled={currentPage === 1}>
<ChevronLeftIcon size={20} />
</Button>
<Button variant="ghost" size="icon" onClick={nextPage} disabled={currentPage >= totalPages}>
<ChevronRightIcon size={20} />
</Button>
</div>
);
};

type PaginationCountTextProps = {
currentPage: number;
pageSize: number;
currentRows: number;
totalRows: number;
className?: string;
includePrefix?: boolean;
};
export const PaginationCountText: FC<PaginationCountTextProps> = ({
currentPage,
pageSize,
currentRows,
totalRows,
className,
includePrefix = false,
}) => {
const start = (currentPage - 1) * pageSize + 1;
const end = start + currentRows - 1;

return (
<TextMuted className={cn("min-w-max text-right", className)}>
{includePrefix ? `Showing ${start} - ${end} of ${totalRows}` : `${start} - ${end} of ${totalRows}`}
</TextMuted>
);
};
56 changes: 43 additions & 13 deletions components/dashboard/src/repositories/list/RepoListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,58 @@ import { FC } from "react";
import { usePrettyRepoURL } from "../../hooks/use-pretty-repo-url";
import { TextMuted } from "@podkit/typography/TextMuted";
import { Text } from "@podkit/typography/Text";
import { Link } from "react-router-dom";
import { Button } from "../../components/Button";
import { LinkButton } from "@podkit/buttons/LinkButton";
import type { Configuration } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb";
import { cn } from "@podkit/lib/cn";
import { AlertTriangleIcon, CheckCircle2Icon } from "lucide-react";
import { TableCell, TableRow } from "@podkit/tables/Table";

type Props = {
configuration: Configuration;
};
export const RepositoryListItem: FC<Props> = ({ configuration }) => {
const url = usePrettyRepoURL(configuration.cloneUrl);
const prebuildsEnabled = !!configuration.prebuildSettings?.enabled;
const created =
configuration.creationTime
?.toDate()
.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" }) ?? "";

return (
<li key={configuration.id} className="flex flex-row w-full space-between items-center">
<div className="flex flex-col flex-grow gap-1">
<Text className="font-semibold">{configuration.name}</Text>
<TableRow>
<TableCell>
<div className="flex flex-col gap-1">
<Text className="font-semibold">{configuration.name}</Text>
{/* We show the url on a 2nd line for smaller screens since we hide the column */}
<TextMuted className="inline md:hidden text-sm">{url}</TextMuted>
</div>
</TableCell>

<TableCell hideOnSmallScreen>
<TextMuted className="text-sm">{url}</TextMuted>
</div>

<div>
<Link to={`/repositories/${configuration.id}`}>
<Button type="secondary">View</Button>
</Link>
</div>
</li>
</TableCell>

<TableCell hideOnSmallScreen>{created}</TableCell>

<TableCell hideOnSmallScreen>
<div className="flex flex-row gap-1 items-center">
{prebuildsEnabled ? (
<CheckCircle2Icon size={20} className="text-green-500" />
) : (
<AlertTriangleIcon size={20} className="text-red-500" />
)}

<TextMuted className={cn(!prebuildsEnabled && "text-red-500 dark:text-red-500")}>
{prebuildsEnabled ? "Enabled" : "Disabled"}
</TextMuted>
</div>
</TableCell>

<TableCell>
<LinkButton href={`/repositories/${configuration.id}`} variant="secondary">
View
</LinkButton>
</TableCell>
</TableRow>
);
};
Loading

0 comments on commit eaae6cc

Please sign in to comment.