From 32aa1bbe79c883ef91ec8f8dd32988495d734a19 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Wed, 15 Nov 2023 07:35:53 -0700 Subject: [PATCH] adding import repo modal (#19073) * adding import repo modal * fix copy * remove name requirement as it's defaulted * upadte query cache after create --- .../configurations/configuration-queries.ts | 37 ++++++++ .../create/ImportRepositoryModal.tsx | 91 +++++++++++++++++++ .../src/repositories/list/RepositoryList.tsx | 15 +-- .../src/api/configuration-service-api.ts | 3 - 4 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 components/dashboard/src/repositories/create/ImportRepositoryModal.tsx diff --git a/components/dashboard/src/data/configurations/configuration-queries.ts b/components/dashboard/src/data/configurations/configuration-queries.ts index 34307cb1482a3e..a82b8f1834827e 100644 --- a/components/dashboard/src/data/configurations/configuration-queries.ts +++ b/components/dashboard/src/data/configurations/configuration-queries.ts @@ -88,3 +88,40 @@ export const getConfigurationQueryKey = (configurationId: string) => { return key; }; + +export type CreateConfigurationArgs = { + name: string; + cloneUrl: string; +}; + +export const useCreateConfiguration = () => { + const { data: org } = useCurrentOrg(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ name, cloneUrl }) => { + if (!org) { + throw new Error("No org currently selected"); + } + + // TODO: Should we push this into the api? + // ensure a .git suffix + const normalizedCloneURL = cloneUrl.endsWith(".git") ? cloneUrl : `${cloneUrl}.git`; + + const response = await configurationClient.createConfiguration({ + name, + cloneUrl: normalizedCloneURL, + organizationId: org.id, + }); + if (!response.configuration) { + throw new Error("Failed to create configuration"); + } + + return response.configuration; + }, + onSuccess: (configuration) => { + queryClient.setQueryData(getConfigurationQueryKey(configuration.id), configuration); + queryClient.invalidateQueries({ queryKey: ["configurations", "list"] }); + }, + }); +}; diff --git a/components/dashboard/src/repositories/create/ImportRepositoryModal.tsx b/components/dashboard/src/repositories/create/ImportRepositoryModal.tsx new file mode 100644 index 00000000000000..7777aa6df8d3af --- /dev/null +++ b/components/dashboard/src/repositories/create/ImportRepositoryModal.tsx @@ -0,0 +1,91 @@ +/** + * 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 { FC, useCallback, useState } from "react"; +import Modal, { ModalBody, ModalFooter, ModalFooterAlert, ModalHeader } from "../../components/Modal"; +import { SuggestedRepository } from "@gitpod/gitpod-protocol"; +import RepositoryFinder from "../../components/RepositoryFinder"; +import { InputField } from "../../components/forms/InputField"; +import { AuthorizeGit, useNeedsGitAuthorization } from "../../components/AuthorizeGit"; +import { useTemporaryState } from "../../hooks/use-temporary-value"; +import { CreateConfigurationArgs, useCreateConfiguration } from "../../data/configurations/configuration-queries"; +import type { Configuration } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb"; +import { LoadingButton } from "@podkit/buttons/LoadingButton"; +import { Button } from "@podkit/buttons/Button"; + +type Props = { + onCreated: (configuration: Configuration) => void; + onClose: () => void; +}; + +export const ImportRepositoryModal: FC = ({ onClose, onCreated }) => { + const needsGitAuth = useNeedsGitAuthorization(); + const [selectedRepo, setSelectedRepo] = useState(); + const createConfiguration = useCreateConfiguration(); + const [createErrorMsg, setCreateErrorMsg] = useTemporaryState("", 3000); + + const handleSubmit = useCallback(() => { + if (!selectedRepo) { + setCreateErrorMsg("Please select a repository"); + return; + } + + const newProjectArgs: CreateConfigurationArgs = { + // leave the name empty to let the backend generate the name + name: "", + cloneUrl: selectedRepo.url, + }; + + createConfiguration.mutate(newProjectArgs, { + onSuccess: onCreated, + }); + }, [createConfiguration, onCreated, selectedRepo, setCreateErrorMsg]); + + const errorMessage = + createErrorMsg || + (createConfiguration.isError && + (createConfiguration.error?.message ?? "There was a problem importing your repository")); + + return ( + + Import repository + +
+ {needsGitAuth ? ( + + ) : ( + <> + + + + + )} +
+
+ setCreateErrorMsg("")}> + {errorMessage} + + ) + } + > + + + Import + + +
+ ); +}; diff --git a/components/dashboard/src/repositories/list/RepositoryList.tsx b/components/dashboard/src/repositories/list/RepositoryList.tsx index 578c65c6145097..0d839a62ad0b70 100644 --- a/components/dashboard/src/repositories/list/RepositoryList.tsx +++ b/components/dashboard/src/repositories/list/RepositoryList.tsx @@ -7,8 +7,6 @@ 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"; import { RepositoryListItem } from "./RepoListItem"; import { useListConfigurations } from "../../data/configurations/configuration-queries"; import { useStateWithDebounce } from "../../hooks/use-state-with-debounce"; @@ -19,6 +17,8 @@ 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"; +import { ImportRepositoryModal } from "../create/ImportRepositoryModal"; +import type { Configuration } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb"; const RepositoryListPage: FC = () => { useDocumentTitle("Imported repositories"); @@ -51,9 +51,9 @@ const RepositoryListPage: FC = () => { const totalRows = data?.pagination?.total ?? 0; const totalPages = Math.ceil(totalRows / pageSize); - const handleProjectCreated = useCallback( - (project: Project) => { - history.push(`/repositories/${project.id}`); + const handleRepoImported = useCallback( + (configuration: Configuration) => { + history.push(`/repositories/${configuration.id}`); }, [history], ); @@ -145,7 +145,10 @@ const RepositoryListPage: FC = () => { {showCreateProjectModal && ( - setShowCreateProjectModal(false)} onCreated={handleProjectCreated} /> + setShowCreateProjectModal(false)} + onCreated={handleRepoImported} + /> )} ); diff --git a/components/server/src/api/configuration-service-api.ts b/components/server/src/api/configuration-service-api.ts index 09457e15106612..6658d352f4b661 100644 --- a/components/server/src/api/configuration-service-api.ts +++ b/components/server/src/api/configuration-service-api.ts @@ -40,9 +40,6 @@ export class ConfigurationServiceAPI implements ServiceImpl