From 4b1b9ec2662c332c8c47f7a67926bf94d16b148f Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Wed, 8 Nov 2023 21:09:52 +0000 Subject: [PATCH 01/17] fixing pagination logic --- components/server/src/api/configuration-service-api.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/server/src/api/configuration-service-api.ts b/components/server/src/api/configuration-service-api.ts index 6d98b59b271e69..997af41e0d125f 100644 --- a/components/server/src/api/configuration-service-api.ts +++ b/components/server/src/api/configuration-service-api.ts @@ -78,7 +78,8 @@ export class ConfigurationServiceAPI implements ServiceImpl 1 ? currentPage * limit : 0; const { rows, total } = await this.projectService.findProjects(context.user.id, { searchTerm: req.searchTerm, From d8d723e7e68d419d6829ccad5905cbb6dd591a51 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Wed, 8 Nov 2023 21:39:43 +0000 Subject: [PATCH 02/17] wip --- .../src/repositories/list/RepoListItem.tsx | 15 +++-- .../src/repositories/list/RepositoryList.tsx | 56 +++++++++++++++---- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/components/dashboard/src/repositories/list/RepoListItem.tsx b/components/dashboard/src/repositories/list/RepoListItem.tsx index 6910a6fbee92f7..5fce4c935fdc83 100644 --- a/components/dashboard/src/repositories/list/RepoListItem.tsx +++ b/components/dashboard/src/repositories/list/RepoListItem.tsx @@ -19,17 +19,24 @@ export const RepositoryListItem: FC = ({ configuration }) => { const url = usePrettyRepoURL(configuration.cloneUrl); return ( -
  • -
    + + {configuration.name} + + {url} -
    + + + {configuration.creationTime + ?.toDate() + .toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })} +
    -
  • + ); }; diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 6eadc7ba29a45b..98a025448868fc 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -15,13 +15,23 @@ import { RepositoryListItem } from "./RepoListItem"; import { useListConfigurations } from "../../data/configurations/configuration-queries"; import { useStateWithDebounce } from "../../hooks/use-state-with-debounce"; import { TextInput } from "../../components/forms/TextInputField"; +import Pagination from "../../Pagination/Pagination"; const RepositoryListPage: FC = () => { const history = useHistory(); + + // TODO: Consider pushing this state into query params + const [currentPage, setCurrentPage] = useState(1); const [searchTerm, setSearchTerm, debouncedSearchTerm] = useStateWithDebounce(""); - const { data, isLoading } = useListConfigurations({ searchTerm: debouncedSearchTerm, page: 0, pageSize: 10 }); + + const pageSize = 10; + + const { data, isLoading } = useListConfigurations({ searchTerm: debouncedSearchTerm, page: currentPage, pageSize }); const [showCreateProjectModal, setShowCreateProjectModal] = useState(false); + // TODO: add this to response payload for pagination + const totalPages = data?.pagination?.total ?? 0 / pageSize; + const handleProjectCreated = useCallback( (project: Project) => { history.push(`/repositories/${project.id}`); @@ -31,25 +41,49 @@ const RepositoryListPage: FC = () => { return ( <> -
    -
    -
    - + {/* TODO: Consider updating Header to have an action button prop */} +
    +
    +
    -
    - + {/* Search/Filter bar */} +
    +
    + + {/* TODO: Add prebuild status filter dropdown */} +
    +
    {/* TODO: Add copy explaining what records we're showing & total records count */}
    {isLoading && } -
      - {!isLoading && - data?.configurations.map((configuration) => ( + + + + + + + + + + + {data?.configurations.map((configuration) => ( ))} - + +
      NameRepository URLCreatedPrebuilds
      + + {/* TODO: Refactor Pagination into podkit or to use podkit components internally */} +
    {showCreateProjectModal && ( From c5e130907e57b8c51e3343991afded2699df1d65 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Wed, 8 Nov 2023 22:00:56 +0000 Subject: [PATCH 03/17] flushing out rows --- .../src/repositories/list/RepoListItem.tsx | 18 +++++++++++------- .../src/repositories/list/RepositoryList.tsx | 2 ++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/components/dashboard/src/repositories/list/RepoListItem.tsx b/components/dashboard/src/repositories/list/RepoListItem.tsx index 5fce4c935fdc83..0980504d3d67c1 100644 --- a/components/dashboard/src/repositories/list/RepoListItem.tsx +++ b/components/dashboard/src/repositories/list/RepoListItem.tsx @@ -8,8 +8,7 @@ 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"; type Props = { @@ -23,20 +22,25 @@ export const RepositoryListItem: FC = ({ configuration }) => { {configuration.name} + {url} + {configuration.creationTime ?.toDate() .toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })} -
    - - - -
    + + {/* TODO: add icon */} + {configuration.prebuildSettings?.enabled ? "Enabled" : "Disabled"} + + + + View + ); }; diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 98a025448868fc..6eee532a710fb4 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -73,6 +73,8 @@ const RepositoryListPage: FC = () => { Repository URL Created Prebuilds + {/* Action column */} + From 6d9545d797dba6bcb5333656505854a0859d3d55 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Wed, 8 Nov 2023 23:50:33 +0000 Subject: [PATCH 04/17] adding missing creationTime --- components/dashboard/src/repositories/list/RepoListItem.tsx | 2 +- components/dashboard/src/repositories/list/RepositoryList.tsx | 2 +- components/gitpod-protocol/src/public-api-converter.spec.ts | 1 + components/gitpod-protocol/src/public-api-converter.ts | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/dashboard/src/repositories/list/RepoListItem.tsx b/components/dashboard/src/repositories/list/RepoListItem.tsx index 0980504d3d67c1..4410a868141805 100644 --- a/components/dashboard/src/repositories/list/RepoListItem.tsx +++ b/components/dashboard/src/repositories/list/RepoListItem.tsx @@ -18,7 +18,7 @@ export const RepositoryListItem: FC = ({ configuration }) => { const url = usePrettyRepoURL(configuration.cloneUrl); return ( - + {configuration.name} diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 6eee532a710fb4..6c0e1c2fc9732b 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -66,7 +66,7 @@ const RepositoryListPage: FC = () => { {isLoading && } - +
    diff --git a/components/gitpod-protocol/src/public-api-converter.spec.ts b/components/gitpod-protocol/src/public-api-converter.spec.ts index fc1a52d1b6335c..5cf57f242391e7 100644 --- a/components/gitpod-protocol/src/public-api-converter.spec.ts +++ b/components/gitpod-protocol/src/public-api-converter.spec.ts @@ -666,6 +666,7 @@ describe("PublicAPIConverter", () => { expect(result.organizationId).to.equal(project.teamId); expect(result.name).to.equal(project.name); expect(result.cloneUrl).to.equal(project.cloneUrl); + expect(result.creationTime).to.deep.equal(Timestamp.fromDate(new Date(project.creationTime))); expect(result.workspaceSettings).to.deep.equal( new WorkspaceSettings({ workspaceClass: project.settings?.workspaceClasses?.regular, diff --git a/components/gitpod-protocol/src/public-api-converter.ts b/components/gitpod-protocol/src/public-api-converter.ts index b205614b8bfa64..5e94f1217475a9 100644 --- a/components/gitpod-protocol/src/public-api-converter.ts +++ b/components/gitpod-protocol/src/public-api-converter.ts @@ -403,6 +403,7 @@ export class PublicAPIConverter { result.organizationId = project.teamId; result.name = project.name; result.cloneUrl = project.cloneUrl; + result.creationTime = Timestamp.fromDate(new Date(project.creationTime)); result.workspaceSettings = this.toWorkspaceSettings(project.settings?.workspaceClasses?.regular); result.prebuildSettings = this.toPrebuildSettings(project.settings?.prebuilds); return result; From 0cfe97bdd66a9437d1fa7275957b83e7fbf1c0b9 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Thu, 9 Nov 2023 00:17:08 +0000 Subject: [PATCH 05/17] adjusting result count copy --- .../dashboard/src/repositories/list/RepositoryList.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 6c0e1c2fc9732b..85201af915cef7 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -29,8 +29,9 @@ const RepositoryListPage: FC = () => { const { data, isLoading } = useListConfigurations({ searchTerm: debouncedSearchTerm, page: currentPage, pageSize }); const [showCreateProjectModal, setShowCreateProjectModal] = useState(false); + const totalRows = data?.pagination?.total ?? 0; // TODO: add this to response payload for pagination - const totalPages = data?.pagination?.total ?? 0 / pageSize; + const totalPages = totalRows / pageSize; const handleProjectCreated = useCallback( (project: Project) => { @@ -52,7 +53,7 @@ const RepositoryListPage: FC = () => { {/* Search/Filter bar */} -
    +
    { /> {/* TODO: Add prebuild status filter dropdown */}
    -
    {/* TODO: Add copy explaining what records we're showing & total records count */}
    + {/* Account for variation of message when totalRows is greater than smallest page size option (20?) */} +
    {totalRows === 1 ? "Showing 1 repo" : `Showing ${totalRows} repos`}
    {isLoading && } From 370d4e27148f4250878c9bc0837f83a50d6d5590 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Thu, 9 Nov 2023 18:50:32 +0000 Subject: [PATCH 06/17] fix offset... again --- components/server/src/api/configuration-service-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/server/src/api/configuration-service-api.ts b/components/server/src/api/configuration-service-api.ts index 997af41e0d125f..44982dea01bfb3 100644 --- a/components/server/src/api/configuration-service-api.ts +++ b/components/server/src/api/configuration-service-api.ts @@ -79,7 +79,7 @@ export class ConfigurationServiceAPI implements ServiceImpl 1 ? currentPage * limit : 0; + const offset = currentPage > 1 ? (currentPage - 1) * limit : 0; const { rows, total } = await this.projectService.findProjects(context.user.id, { searchTerm: req.searchTerm, From 408cf490711605264bb52f9db234eb5b8ed6ce6f Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Thu, 9 Nov 2023 19:03:58 +0000 Subject: [PATCH 07/17] Flushing out table UI a bit more --- .../src/components/forms/TextInputField.tsx | 4 +- .../src/components/podkit/buttons/Button.tsx | 4 +- .../components/podkit/layout/PageHeading.tsx | 25 ++++++ .../components/podkit/typography/Headings.tsx | 2 +- .../src/repositories/list/RepoListItem.tsx | 43 ++++++--- .../src/repositories/list/RepositoryList.tsx | 90 ++++++++++++------- 6 files changed, 119 insertions(+), 49 deletions(-) create mode 100644 components/dashboard/src/components/podkit/layout/PageHeading.tsx diff --git a/components/dashboard/src/components/forms/TextInputField.tsx b/components/dashboard/src/components/forms/TextInputField.tsx index 922b2dfe307dd2..664bb9ec91a0db 100644 --- a/components/dashboard/src/components/forms/TextInputField.tsx +++ b/components/dashboard/src/components/forms/TextInputField.tsx @@ -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"; @@ -97,7 +97,7 @@ export const TextInput: FunctionComponent = memo( return ( = ({ title, subtitle, action }) => { + return ( +
    +
    + {title} + {subtitle && {subtitle}} +
    + {action && action} +
    + ); +}; diff --git a/components/dashboard/src/components/podkit/typography/Headings.tsx b/components/dashboard/src/components/podkit/typography/Headings.tsx index a6e288f6bb20c3..97922be5388ea0 100644 --- a/components/dashboard/src/components/podkit/typography/Headings.tsx +++ b/components/dashboard/src/components/podkit/typography/Headings.tsx @@ -22,7 +22,7 @@ export const Heading1: FC = ({ id, color, tracking, className, chi return ( {children} diff --git a/components/dashboard/src/repositories/list/RepoListItem.tsx b/components/dashboard/src/repositories/list/RepoListItem.tsx index 4410a868141805..44294b7ccc06d8 100644 --- a/components/dashboard/src/repositories/list/RepoListItem.tsx +++ b/components/dashboard/src/repositories/list/RepoListItem.tsx @@ -10,36 +10,53 @@ import { TextMuted } from "@podkit/typography/TextMuted"; import { Text } from "@podkit/typography/Text"; 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"; type Props = { configuration: Configuration; }; export const RepositoryListItem: FC = ({ 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 ( -
    - + - - + - ); diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 85201af915cef7..bf6ca312bc635a 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -5,19 +5,22 @@ */ import { FC, useCallback, useState } from "react"; -import Header from "../../components/Header"; -import { Loader2 } from "lucide-react"; +import { ChevronLeftIcon, ChevronRightIcon, Loader2 } from "lucide-react"; import { useHistory } from "react-router-dom"; import { Project } from "@gitpod/gitpod-protocol"; import { CreateProjectModal } from "../../projects/create-project-modal/CreateProjectModal"; -import { Button } from "../../components/Button"; import { RepositoryListItem } from "./RepoListItem"; import { useListConfigurations } from "../../data/configurations/configuration-queries"; import { useStateWithDebounce } from "../../hooks/use-state-with-debounce"; import { TextInput } from "../../components/forms/TextInputField"; -import Pagination from "../../Pagination/Pagination"; +import { TextMuted } from "@podkit/typography/TextMuted"; +import { PageHeading } from "@podkit/layout/PageHeading"; +import { Button } from "@podkit/buttons/Button"; +import { useDocumentTitle } from "../../hooks/use-document-title"; const RepositoryListPage: FC = () => { + useDocumentTitle("Repository configuration"); + const history = useHistory(); // TODO: Consider pushing this state into query params @@ -44,18 +47,17 @@ const RepositoryListPage: FC = () => { <>
    {/* TODO: Consider updating Header to have an action button prop */} -
    -
    - -
    + setShowCreateProjectModal(true)}>Import Repository} + /> {/* Search/Filter bar */}
    { {/* TODO: Add prebuild status filter dropdown */}
    {/* Account for variation of message when totalRows is greater than smallest page size option (20?) */} -
    {totalRows === 1 ? "Showing 1 repo" : `Showing ${totalRows} repos`}
    +
    + + {totalRows === 1 ? "Showing 1 repo" : `Showing ${totalRows} repos`} + +
    {isLoading && } -
    Name
    - {configuration.name} +
    +
    + {configuration.name} + {url} +
    + {url} - {configuration.creationTime - ?.toDate() - .toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })} - {created} - {/* TODO: add icon */} - {configuration.prebuildSettings?.enabled ? "Enabled" : "Disabled"} + +
    + {prebuildsEnabled ? ( + + ) : ( + + )} + + + {prebuildsEnabled ? "Enabled" : "Disabled"} + +
    - View + + View +
    - - - - - - - {/* Action column */} - - - - - {data?.configurations.map((configuration) => ( - - ))} - -
    NameRepository URLCreatedPrebuilds
    +
    + + + + + + + + {/* Action column */} + + + + + {data?.configurations.map((configuration) => ( + + ))} + +
    NameRepository URLCreatedPrebuilds
    - {/* TODO: Refactor Pagination into podkit or to use podkit components internally */} - + {totalPages > 1 && ( +
    + {/* TODO: Rows per page select */} + {/* TODO: Current records copy, i.e. "1-50 of 95" */} + + +
    + )} +
    {showCreateProjectModal && ( From eff508dbc0b84c5bb25c96ae944302a257c29eab Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Thu, 9 Nov 2023 23:40:59 +0000 Subject: [PATCH 08/17] Flushing out table UI a bit more --- .../configurations/configuration-queries.ts | 6 +- .../repositories/list/PaginationControls.tsx | 81 +++++++++++++++++ .../src/repositories/list/RepoListItem.tsx | 4 +- .../src/repositories/list/RepositoryList.tsx | 86 +++++++++++-------- 4 files changed, 139 insertions(+), 38 deletions(-) create mode 100644 components/dashboard/src/repositories/list/PaginationControls.tsx diff --git a/components/dashboard/src/data/configurations/configuration-queries.ts b/components/dashboard/src/data/configurations/configuration-queries.ts index 0b752d618d1ff6..34307cb1482a3e 100644 --- a/components/dashboard/src/data/configurations/configuration-queries.ts +++ b/components/dashboard/src/data/configurations/configuration-queries.ts @@ -33,10 +33,14 @@ export const useListConfigurations = ({ searchTerm = "", page, pageSize }: ListC pagination: { page, pageSize }, }); - return { configurations, pagination }; + return { + configurations, + pagination, + }; }, { enabled: !!org, + keepPreviousData: true, }, ); }; diff --git a/components/dashboard/src/repositories/list/PaginationControls.tsx b/components/dashboard/src/repositories/list/PaginationControls.tsx new file mode 100644 index 00000000000000..ce5884976a01b6 --- /dev/null +++ b/components/dashboard/src/repositories/list/PaginationControls.tsx @@ -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 = ({ + currentPage, + totalPages, + pageSize, + totalRows, + currentRows, + onPageChanged, +}) => { + const prevPage = useCallback(() => { + onPageChanged(currentPage - 1); + }, [currentPage, onPageChanged]); + + const nextPage = useCallback(() => { + onPageChanged(currentPage + 1); + }, [currentPage, onPageChanged]); + + return ( +
    + {/* TODO: Rows per page select */} + + + +
    + ); +}; + +type PaginationCountTextProps = { + currentPage: number; + pageSize: number; + currentRows: number; + totalRows: number; + className?: string; + includePrefix?: boolean; +}; +export const PaginationCountText: FC = ({ + currentPage, + pageSize, + currentRows, + totalRows, + className, + includePrefix = false, +}) => { + const start = (currentPage - 1) * pageSize + 1; + const end = start + currentRows - 1; + + return ( + + {includePrefix ? `Showing ${start} - ${end} of ${totalRows}` : `${start} - ${end} of ${totalRows}`} + + ); +}; diff --git a/components/dashboard/src/repositories/list/RepoListItem.tsx b/components/dashboard/src/repositories/list/RepoListItem.tsx index 44294b7ccc06d8..ce41f31bfcfff4 100644 --- a/components/dashboard/src/repositories/list/RepoListItem.tsx +++ b/components/dashboard/src/repositories/list/RepoListItem.tsx @@ -42,9 +42,9 @@ export const RepositoryListItem: FC = ({ configuration }) => {
    {prebuildsEnabled ? ( - + ) : ( - + )} diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index bf6ca312bc635a..7c111523a76f67 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -4,8 +4,8 @@ * See License.AGPL.txt in the project root for license information. */ -import { FC, useCallback, useState } from "react"; -import { ChevronLeftIcon, ChevronRightIcon, Loader2 } from "lucide-react"; +import { FC, useCallback, useEffect, useState } from "react"; +import { LoaderIcon } from "lucide-react"; import { useHistory } from "react-router-dom"; import { Project } from "@gitpod/gitpod-protocol"; import { CreateProjectModal } from "../../projects/create-project-modal/CreateProjectModal"; @@ -17,6 +17,7 @@ import { TextMuted } from "@podkit/typography/TextMuted"; import { PageHeading } from "@podkit/layout/PageHeading"; import { Button } from "@podkit/buttons/Button"; import { useDocumentTitle } from "../../hooks/use-document-title"; +import { PaginationControls, PaginationCountText } from "./PaginationControls"; const RepositoryListPage: FC = () => { useDocumentTitle("Repository configuration"); @@ -27,13 +28,25 @@ const RepositoryListPage: FC = () => { const [currentPage, setCurrentPage] = useState(1); const [searchTerm, setSearchTerm, debouncedSearchTerm] = useStateWithDebounce(""); - const pageSize = 10; + // Reset to page 1 when debounced search term changes (when we perform a new search) + useEffect(() => { + setCurrentPage(1); + }, [debouncedSearchTerm]); - const { data, isLoading } = useListConfigurations({ searchTerm: debouncedSearchTerm, page: currentPage, pageSize }); + // TODO: move this into state and add control for changing it + const pageSize = 5; + + const { data, isFetching, isPreviousData } = useListConfigurations({ + searchTerm: debouncedSearchTerm, + page: currentPage, + pageSize, + }); const [showCreateProjectModal, setShowCreateProjectModal] = useState(false); + // TODO: Adding these to response payload to avoid having to calculate them here + // This will fix issues w/ relying on some server provided state and some client state (like current page) + const rowCount = data?.configurations.length ?? 0; const totalRows = data?.pagination?.total ?? 0; - // TODO: add this to response payload for pagination const totalPages = totalRows / pageSize; const handleProjectCreated = useCallback( @@ -46,7 +59,6 @@ const RepositoryListPage: FC = () => { return ( <>
    - {/* TODO: Consider updating Header to have an action button prop */} { {/* Search/Filter bar */}
    -
    +
    + {/* TODO: Add search icon on left and decide on pulling Inputs into podkit */} { {/* Account for variation of message when totalRows is greater than smallest page size option (20?) */}
    - {totalRows === 1 ? "Showing 1 repo" : `Showing ${totalRows} repos`} + {rowCount < totalRows ? ( + + ) : ( + <>{totalRows === 1 ? "Showing 1 repo" : `Showing ${totalRows} repos`} + )}
    - {isLoading && } -
    + {/* TODO: Add sorting controls */} - + - - - {/* Action column */} - + + + {/* Action column, loading status in header */} + @@ -94,26 +122,14 @@ const RepositoryListPage: FC = () => {
    NameName Repository URLCreatedPrebuildsCreatedPrebuilds + {isFetching && isPreviousData && ( +
    + +
    + )} +
    {totalPages > 1 && ( -
    - {/* TODO: Rows per page select */} - {/* TODO: Current records copy, i.e. "1-50 of 95" */} - - -
    + )}
    From f7a609684c3f2249d192465fefd11ef4ff6d9b9d Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Thu, 9 Nov 2023 23:54:34 +0000 Subject: [PATCH 09/17] comment for remembering to use search params --- components/dashboard/src/repositories/list/RepositoryList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 7c111523a76f67..72d0b84e55bd29 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -24,7 +24,7 @@ const RepositoryListPage: FC = () => { const history = useHistory(); - // TODO: Consider pushing this state into query params + // TODO: Move this state into url search params const [currentPage, setCurrentPage] = useState(1); const [searchTerm, setSearchTerm, debouncedSearchTerm] = useStateWithDebounce(""); From 728e34ff0642c86572dbbebfa05d5d9e44029fce Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Thu, 9 Nov 2023 23:55:16 +0000 Subject: [PATCH 10/17] more comments --- components/dashboard/src/repositories/list/RepositoryList.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 72d0b84e55bd29..810337e2201eb6 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -33,6 +33,7 @@ const RepositoryListPage: FC = () => { setCurrentPage(1); }, [debouncedSearchTerm]); + // Have this set to a low value for now to test pagination while we develop this // TODO: move this into state and add control for changing it const pageSize = 5; From aff2533b03ab694e7dec6b430b32666f52ef7ad0 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Fri, 10 Nov 2023 01:47:40 +0000 Subject: [PATCH 11/17] fix pages count --- components/dashboard/src/repositories/list/RepositoryList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 810337e2201eb6..bba71937457f26 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -48,7 +48,7 @@ const RepositoryListPage: FC = () => { // This will fix issues w/ relying on some server provided state and some client state (like current page) const rowCount = data?.configurations.length ?? 0; const totalRows = data?.pagination?.total ?? 0; - const totalPages = totalRows / pageSize; + const totalPages = Math.ceil(totalRows / pageSize); const handleProjectCreated = useCallback( (project: Project) => { From 95c5e4225156d399d1b20bc014e72ae1891b2349 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Mon, 13 Nov 2023 18:09:21 +0000 Subject: [PATCH 12/17] copy/responsive adjustments --- .../src/components/podkit/layout/PageHeading.tsx | 2 +- .../dashboard/src/repositories/list/RepositoryList.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/dashboard/src/components/podkit/layout/PageHeading.tsx b/components/dashboard/src/components/podkit/layout/PageHeading.tsx index d0c4e1c6ca914b..1f461060791c0d 100644 --- a/components/dashboard/src/components/podkit/layout/PageHeading.tsx +++ b/components/dashboard/src/components/podkit/layout/PageHeading.tsx @@ -14,7 +14,7 @@ type PageHeadingProps = { }; export const PageHeading: FC = ({ title, subtitle, action }) => { return ( -
    +
    {title} {subtitle && {subtitle}} diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index bba71937457f26..317086270eeb93 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -20,7 +20,7 @@ import { useDocumentTitle } from "../../hooks/use-document-title"; import { PaginationControls, PaginationCountText } from "./PaginationControls"; const RepositoryListPage: FC = () => { - useDocumentTitle("Repository configuration"); + useDocumentTitle("Imported repositories"); const history = useHistory(); @@ -61,14 +61,14 @@ const RepositoryListPage: FC = () => { <>
    setShowCreateProjectModal(true)}>Import Repository} /> {/* Search/Filter bar */} -
    -
    +
    +
    {/* TODO: Add search icon on left and decide on pulling Inputs into podkit */} { Name - Repository URL + Repository Created Prebuilds {/* Action column, loading status in header */} From aff276c5602b85a09365c4b5ff94c1ad99c17518 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Mon, 13 Nov 2023 18:41:32 +0000 Subject: [PATCH 13/17] updating search project query & service --- .../gitpod-db/src/project-db.spec.db.ts | 119 +++++++++++++++--- components/gitpod-db/src/project-db.ts | 17 +-- .../gitpod-db/src/typeorm/project-db-impl.ts | 21 ++-- .../server/src/projects/projects-service.ts | 17 +-- 4 files changed, 134 insertions(+), 40 deletions(-) diff --git a/components/gitpod-db/src/project-db.spec.db.ts b/components/gitpod-db/src/project-db.spec.db.ts index 8c3be98e03da0d..9b14ffc55f8494 100644 --- a/components/gitpod-db/src/project-db.spec.db.ts +++ b/components/gitpod-db/src/project-db.spec.db.ts @@ -51,26 +51,32 @@ class ProjectDBSpec { }); const searchTerm = "rand"; const storedProject = await this.projectDb.storeProject(project); - const foundProject = await this.projectDb.findProjectsBySearchTerm(0, 10, "creationTime", "DESC", searchTerm); + const foundProject = await this.projectDb.findProjectsBySearchTerm({ + offset: 0, + limit: 10, + orderBy: "creationTime", + orderDir: "DESC", + searchTerm, + }); expect(foundProject.rows[0].id).to.eq(storedProject.id); - const foundProjectByName = await this.projectDb.findProjectsBySearchTerm( - 0, - 10, - "creationTime", - "DESC", - "some-proj", - ); + const foundProjectByName = await this.projectDb.findProjectsBySearchTerm({ + offset: 0, + limit: 10, + orderBy: "creationTime", + orderDir: "DESC", + searchTerm: "some-proj", + }); expect(foundProjectByName.rows[0].id).to.eq(storedProject.id); - const foundProjectEmptySearch = await this.projectDb.findProjectsBySearchTerm( - 0, - 10, - "creationTime", - "DESC", - " ", - ); + const foundProjectEmptySearch = await this.projectDb.findProjectsBySearchTerm({ + offset: 0, + limit: 10, + orderBy: "creationTime", + orderDir: "DESC", + searchTerm: " ", + }); expect(foundProjectEmptySearch.rows[0].id).to.eq(storedProject.id); } @@ -121,7 +127,14 @@ class ProjectDBSpec { const storedProject4 = await this.projectDb.storeProject(project4); const storedProject5 = await this.projectDb.storeProject(project5); - const allResults = await this.projectDb.findProjectsBySearchTerm(0, 10, "name", "ASC", ""); + // TODO: omit searchTerm + const allResults = await this.projectDb.findProjectsBySearchTerm({ + offset: 0, + limit: 10, + orderBy: "name", + orderDir: "ASC", + searchTerm: "", + }); expect(allResults.total).equals(5); expect(allResults.rows.length).equal(5); expect(allResults.rows[0].id).to.eq(storedProject1.id); @@ -131,19 +144,89 @@ class ProjectDBSpec { expect(allResults.rows[4].id).to.eq(storedProject5.id); const pageSize = 3; - const page1 = await this.projectDb.findProjectsBySearchTerm(0, pageSize, "name", "ASC", ""); + // TODO: omit searchTerm + const page1 = await this.projectDb.findProjectsBySearchTerm({ + offset: 0, + limit: pageSize, + orderBy: "name", + orderDir: "ASC", + searchTerm: "", + }); expect(page1.total).equals(5); expect(page1.rows.length).equal(3); expect(page1.rows[0].id).to.eq(storedProject1.id); expect(page1.rows[1].id).to.eq(storedProject2.id); expect(page1.rows[2].id).to.eq(storedProject3.id); - const page2 = await this.projectDb.findProjectsBySearchTerm(pageSize * 1, pageSize, "name", "ASC", ""); + // TODO: omit searchTerm + const page2 = await this.projectDb.findProjectsBySearchTerm({ + offset: pageSize * 1, + limit: pageSize, + orderBy: "name", + orderDir: "ASC", + searchTerm: "", + }); expect(page2.total).equals(5); expect(page2.rows.length).equal(2); expect(page2.rows[0].id).to.eq(storedProject4.id); expect(page2.rows[1].id).to.eq(storedProject5.id); } + + @test() + public async findProjectBySearchTermOrganizationId() { + const user = await this.userDb.newUser(); + user.identities.push({ + authProviderId: "GitHub", + authId: "1234", + authName: "newUser", + primaryEmail: "newuser@git.com", + }); + await this.userDb.storeUser(user); + + const project1 = Project.create({ + name: "some-project", + cloneUrl: "some-random-clone-url", + teamId: "team-1", + appInstallationId: "", + }); + const project2 = Project.create({ + name: "some-project-2", + cloneUrl: "some-random-clone-url-2", + teamId: "team-2", + appInstallationId: "", + }); + const storedProject1 = await this.projectDb.storeProject(project1); + const storedProject2 = await this.projectDb.storeProject(project2); + + const team1Results = await this.projectDb.findProjectsBySearchTerm({ + offset: 0, + limit: 10, + orderBy: "name", + orderDir: "ASC", + organizationId: "team-1", + }); + expect(team1Results.total).equals(1); + expect(team1Results.rows[0].id).to.eq(storedProject1.id); + + const team2Results = await this.projectDb.findProjectsBySearchTerm({ + offset: 0, + limit: 10, + orderBy: "name", + orderDir: "ASC", + organizationId: "team-2", + }); + expect(team2Results.total).equals(1); + expect(team2Results.rows[0].id).to.eq(storedProject2.id); + + const noResults = await this.projectDb.findProjectsBySearchTerm({ + offset: 0, + limit: 10, + orderBy: "name", + orderDir: "ASC", + organizationId: "does-not-exist", + }); + expect(noResults.total).equals(0); + } } module.exports = new ProjectDBSpec(); diff --git a/components/gitpod-db/src/project-db.ts b/components/gitpod-db/src/project-db.ts index 22323b18d17dd9..d38e4721ada5b3 100644 --- a/components/gitpod-db/src/project-db.ts +++ b/components/gitpod-db/src/project-db.ts @@ -12,13 +12,7 @@ export interface ProjectDB extends TransactionalDB { findProjectById(projectId: string): Promise; findProjectsByCloneUrl(cloneUrl: string): Promise; findProjects(orgID: string): Promise; - findProjectsBySearchTerm( - offset: number, - limit: number, - orderBy: keyof Project, - orderDir: "ASC" | "DESC", - searchTerm: string, - ): Promise<{ total: number; rows: Project[] }>; + findProjectsBySearchTerm(args: FindProjectsBySearchTermArgs): Promise<{ total: number; rows: Project[] }>; storeProject(project: Project): Promise; updateProject(partialProject: PartialProject): Promise; markDeleted(projectId: string): Promise; @@ -37,3 +31,12 @@ export interface ProjectDB extends TransactionalDB { getProjectUsage(projectId: string): Promise; updateProjectUsage(projectId: string, usage: Partial): Promise; } + +export type FindProjectsBySearchTermArgs = { + offset: number; + limit: number; + orderBy: keyof Project; + orderDir: "ASC" | "DESC"; + searchTerm?: string; + organizationId?: string; +}; diff --git a/components/gitpod-db/src/typeorm/project-db-impl.ts b/components/gitpod-db/src/typeorm/project-db-impl.ts index 889d84d664cfb6..16262207102d65 100644 --- a/components/gitpod-db/src/typeorm/project-db-impl.ts +++ b/components/gitpod-db/src/typeorm/project-db-impl.ts @@ -9,7 +9,7 @@ import { EncryptionService } from "@gitpod/gitpod-protocol/lib/encryption/encryp import { inject, injectable, optional } from "inversify"; import { Brackets, EntityManager, FindConditions, Repository } from "typeorm"; import { v4 as uuidv4 } from "uuid"; -import { ProjectDB } from "../project-db"; +import { ProjectDB, FindProjectsBySearchTermArgs } from "../project-db"; import { DBProject } from "./entity/db-project"; import { DBProjectEnvVar } from "./entity/db-project-env-vars"; import { DBProjectInfo } from "./entity/db-project-info"; @@ -69,13 +69,14 @@ export class ProjectDBImpl extends TransactionalDBImpl implements Pro return repo.find({ where: { teamId: orgId, markedDeleted: false }, order: { name: "ASC" } }); } - public async findProjectsBySearchTerm( - offset: number, - limit: number, - orderBy: keyof Project, - orderDir: "DESC" | "ASC", - searchTerm?: string, - ): Promise<{ total: number; rows: Project[] }> { + public async findProjectsBySearchTerm({ + offset, + limit, + orderBy, + orderDir, + searchTerm, + organizationId, + }: FindProjectsBySearchTermArgs): Promise<{ total: number; rows: Project[] }> { const projectRepo = await this.getRepo(); const normalizedSearchTerm = searchTerm?.trim(); @@ -86,6 +87,10 @@ export class ProjectDBImpl extends TransactionalDBImpl implements Pro .take(limit) .orderBy(orderBy, orderDir); + if (organizationId) { + queryBuilder.andWhere("project.teamId = :organizationId", { organizationId }); + } + if (normalizedSearchTerm) { queryBuilder.andWhere( new Brackets((qb) => { diff --git a/components/server/src/projects/projects-service.ts b/components/server/src/projects/projects-service.ts index bd4f52c0b8feb8..570f9d0a4f54d9 100644 --- a/components/server/src/projects/projects-service.ts +++ b/components/server/src/projects/projects-service.ts @@ -68,15 +68,18 @@ export class ProjectsService { orderBy?: keyof Project; orderDir?: "ASC" | "DESC"; searchTerm?: string; + organizationId?: string; }, ): Promise<{ total: number; rows: Project[] }> { - const projects = await this.projectDB.findProjectsBySearchTerm( - searchOptions.offset || 0, - searchOptions.limit || 1000, - searchOptions.orderBy || "creationTime", - searchOptions.orderDir || "ASC", - searchOptions.searchTerm || "", - ); + const projects = await this.projectDB.findProjectsBySearchTerm({ + offset: searchOptions.offset || 0, + limit: searchOptions.limit || 1000, + orderBy: searchOptions.orderBy || "creationTime", + orderDir: searchOptions.orderDir || "ASC", + searchTerm: searchOptions.searchTerm || "", + organizationId: searchOptions.organizationId, + }); + // TODO: adjust this to not filter entities, but log errors if any are not accessible for current user const rows = await this.filterByReadAccess(userId, projects.rows); const total = projects.total; return { From 5b9fc4664565f1ca834f0f6998355c9f8b200929 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Mon, 13 Nov 2023 18:47:50 +0000 Subject: [PATCH 14/17] pass org id along and check permission in service --- components/server/src/api/configuration-service-api.ts | 1 + components/server/src/projects/projects-service.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/components/server/src/api/configuration-service-api.ts b/components/server/src/api/configuration-service-api.ts index 44982dea01bfb3..092783202763bf 100644 --- a/components/server/src/api/configuration-service-api.ts +++ b/components/server/src/api/configuration-service-api.ts @@ -82,6 +82,7 @@ export class ConfigurationServiceAPI implements ServiceImpl 1 ? (currentPage - 1) * limit : 0; const { rows, total } = await this.projectService.findProjects(context.user.id, { + organizationId: req.organizationId, searchTerm: req.searchTerm, orderBy: "name", orderDir: "ASC", diff --git a/components/server/src/projects/projects-service.ts b/components/server/src/projects/projects-service.ts index 570f9d0a4f54d9..227a842548e425 100644 --- a/components/server/src/projects/projects-service.ts +++ b/components/server/src/projects/projects-service.ts @@ -71,6 +71,12 @@ export class ProjectsService { organizationId?: string; }, ): Promise<{ total: number; rows: Project[] }> { + if (searchOptions.organizationId) { + await this.auth.checkPermissionOnOrganization(userId, "read_info", searchOptions.organizationId); + } else { + // If no org is provided need to check that user has installation admin scope + } + const projects = await this.projectDB.findProjectsBySearchTerm({ offset: searchOptions.offset || 0, limit: searchOptions.limit || 1000, From 8a3210a00ec287156a3566f5592a91544e791436 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Mon, 13 Nov 2023 23:36:44 +0000 Subject: [PATCH 15/17] Extracting table components --- .../src/components/podkit/tables/Table.tsx | 68 +++++++++++++++++++ .../src/repositories/list/RepoListItem.tsx | 26 +++---- .../src/repositories/list/RepositoryList.tsx | 39 +++++++---- 3 files changed, 106 insertions(+), 27 deletions(-) create mode 100644 components/dashboard/src/components/podkit/tables/Table.tsx diff --git a/components/dashboard/src/components/podkit/tables/Table.tsx b/components/dashboard/src/components/podkit/tables/Table.tsx new file mode 100644 index 00000000000000..e9cf888cc4010a --- /dev/null +++ b/components/dashboard/src/components/podkit/tables/Table.tsx @@ -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>( + ({ className, ...props }, ref) => { + return ( +
    + + + ); + }, +); +Table.displayName = "Table"; + +export const TableHeader = React.forwardRef>( + ({ className, ...props }, ref) => { + return ( + + ); + }, +); +TableHeader.displayName = "TableHeader"; + +export const TableRow = React.forwardRef>( + ({ className, ...props }, ref) => { + return ; + }, +); +TableRow.displayName = "TableRow"; + +export const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes & HideableCellProps +>(({ hideOnSmallScreen, className, ...props }, ref) => { + return + ); + }, +); +TableBody.displayName = "TableBody"; + +export const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes & HideableCellProps +>(({ hideOnSmallScreen, className, ...props }, ref) => { + return - + - + - + {created} - + - - + + ); }; diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 317086270eeb93..578c65c6145097 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -18,6 +18,7 @@ import { PageHeading } from "@podkit/layout/PageHeading"; import { Button } from "@podkit/buttons/Button"; import { useDocumentTitle } from "../../hooks/use-document-title"; import { PaginationControls, PaginationCountText } from "./PaginationControls"; +import { Table, TableBody, TableHead, TableHeader, TableRow } from "@podkit/tables/Table"; const RepositoryListPage: FC = () => { useDocumentTitle("Imported repositories"); @@ -97,30 +98,38 @@ const RepositoryListPage: FC = () => {
    -
    ; +}); +TableHead.displayName = "TableHead"; + +export const TableBody = React.forwardRef>( + ({ className, ...props }, ref) => { + return ( +
    ; +}); +TableCell.displayName = "TableCell"; diff --git a/components/dashboard/src/repositories/list/RepoListItem.tsx b/components/dashboard/src/repositories/list/RepoListItem.tsx index ce41f31bfcfff4..6580e58c079f63 100644 --- a/components/dashboard/src/repositories/list/RepoListItem.tsx +++ b/components/dashboard/src/repositories/list/RepoListItem.tsx @@ -12,6 +12,7 @@ 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; @@ -25,21 +26,22 @@ export const RepositoryListItem: FC = ({ configuration }) => { .toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" }) ?? ""; return ( -
    + +
    {configuration.name} + {/* We show the url on a 2nd line for smaller screens since we hide the column */} {url}
    -
    + {url} - {created} +
    {prebuildsEnabled ? ( @@ -47,17 +49,17 @@ export const RepositoryListItem: FC = ({ configuration }) => { )} - + {prebuildsEnabled ? "Enabled" : "Disabled"}
    -
    + View -
    +
    {/* TODO: Add sorting controls */} - - - - - - + + + Name + Repository + + Created + + + Prebuilds + {/* Action column, loading status in header */} - - - - + + + + {data?.configurations.map((configuration) => ( ))} - -
    NameRepositoryCreatedPrebuilds + {isFetching && isPreviousData && (
    - + {/* TODO: Make a LoadingIcon component */} +
    )} -
    + + {totalPages > 1 && ( Date: Mon, 13 Nov 2023 23:49:35 +0000 Subject: [PATCH 16/17] cleanup --- components/gitpod-db/src/project-db.spec.db.ts | 6 ------ components/server/src/api/configuration-service-api.ts | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/components/gitpod-db/src/project-db.spec.db.ts b/components/gitpod-db/src/project-db.spec.db.ts index 9b14ffc55f8494..e9cf3c3db1eea2 100644 --- a/components/gitpod-db/src/project-db.spec.db.ts +++ b/components/gitpod-db/src/project-db.spec.db.ts @@ -127,13 +127,11 @@ class ProjectDBSpec { const storedProject4 = await this.projectDb.storeProject(project4); const storedProject5 = await this.projectDb.storeProject(project5); - // TODO: omit searchTerm const allResults = await this.projectDb.findProjectsBySearchTerm({ offset: 0, limit: 10, orderBy: "name", orderDir: "ASC", - searchTerm: "", }); expect(allResults.total).equals(5); expect(allResults.rows.length).equal(5); @@ -144,13 +142,11 @@ class ProjectDBSpec { expect(allResults.rows[4].id).to.eq(storedProject5.id); const pageSize = 3; - // TODO: omit searchTerm const page1 = await this.projectDb.findProjectsBySearchTerm({ offset: 0, limit: pageSize, orderBy: "name", orderDir: "ASC", - searchTerm: "", }); expect(page1.total).equals(5); expect(page1.rows.length).equal(3); @@ -158,13 +154,11 @@ class ProjectDBSpec { expect(page1.rows[1].id).to.eq(storedProject2.id); expect(page1.rows[2].id).to.eq(storedProject3.id); - // TODO: omit searchTerm const page2 = await this.projectDb.findProjectsBySearchTerm({ offset: pageSize * 1, limit: pageSize, orderBy: "name", orderDir: "ASC", - searchTerm: "", }); expect(page2.total).equals(5); expect(page2.rows.length).equal(2); diff --git a/components/server/src/api/configuration-service-api.ts b/components/server/src/api/configuration-service-api.ts index 092783202763bf..09457e15106612 100644 --- a/components/server/src/api/configuration-service-api.ts +++ b/components/server/src/api/configuration-service-api.ts @@ -92,6 +92,7 @@ export class ConfigurationServiceAPI implements ServiceImpl this.apiConverter.toConfiguration(project)), + // TODO: add additional pagination metadata to response pagination: new PaginationResponse({ total, }), From fd89c7d5b42ecca4b277acd8bb072d66e1c12033 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Tue, 14 Nov 2023 16:42:48 +0000 Subject: [PATCH 17/17] fix menu copy --- components/dashboard/src/menu/OrganizationSelector.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/dashboard/src/menu/OrganizationSelector.tsx b/components/dashboard/src/menu/OrganizationSelector.tsx index 2bd6bab93044a2..d29638a159d739 100644 --- a/components/dashboard/src/menu/OrganizationSelector.tsx +++ b/components/dashboard/src/menu/OrganizationSelector.tsx @@ -57,8 +57,8 @@ export default function OrganizationSelector() { if (currentOrg.data) { if (repoConfigListAndDetail) { linkEntries.push({ - title: "Configurations", - customContent: Configurations, + title: "Repositories", + customContent: Repositories, active: false, separator: false, link: "/repositories",