From 0cf313f6b2f717acb5ae9389ddca50e9ed6fe5dc Mon Sep 17 00:00:00 2001 From: Gero Posmyk-Leinemann Date: Mon, 18 Jul 2022 17:15:31 +0000 Subject: [PATCH] [dashboard] BlockedRepo UI --- components/dashboard/src/App.tsx | 4 +- .../src/admin/BlockedRepositories.tsx | 300 ++++++++++++++++++ .../src/admin/BlockedRepositorySettings.tsx | 15 - 3 files changed, 302 insertions(+), 17 deletions(-) create mode 100644 components/dashboard/src/admin/BlockedRepositories.tsx delete mode 100644 components/dashboard/src/admin/BlockedRepositorySettings.tsx diff --git a/components/dashboard/src/App.tsx b/components/dashboard/src/App.tsx index f917da2864a83b..346dbf3541e8f1 100644 --- a/components/dashboard/src/App.tsx +++ b/components/dashboard/src/App.tsx @@ -48,7 +48,7 @@ import { parseProps } from "./start/StartWorkspace"; import SelectIDEModal from "./settings/SelectIDEModal"; import { StartPage, StartPhase } from "./start/StartPage"; import { isGitpodIo } from "./utils"; -import { BlockedRepositorySettings } from "./admin/BlockedRepositorySettings"; +import { BlockedRepositories } from "./admin/BlockedRepositories"; const Setup = React.lazy(() => import(/* webpackPrefetch: true */ "./Setup")); const Workspaces = React.lazy(() => import(/* webpackPrefetch: true */ "./workspaces/Workspaces")); @@ -366,7 +366,7 @@ function App() { - + diff --git a/components/dashboard/src/admin/BlockedRepositories.tsx b/components/dashboard/src/admin/BlockedRepositories.tsx new file mode 100644 index 00000000000000..1bc1fd0580cb0f --- /dev/null +++ b/components/dashboard/src/admin/BlockedRepositories.tsx @@ -0,0 +1,300 @@ +/** + * 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 { AdminGetListResult } from "@gitpod/gitpod-protocol"; +import { useEffect, useRef, useState } from "react"; +import { getGitpodService } from "../service/service"; +import { PageWithAdminSubMenu } from "./PageWithAdminSubMenu"; +import { BlockedRepository } from "@gitpod/gitpod-protocol/lib/blocked-repositories-protocol"; +import ConfirmationModal from "../components/ConfirmationModal"; +import Modal from "../components/Modal"; +import CheckBox from "../components/CheckBox"; +import { ItemFieldContextMenu } from "../components/ItemsList"; +import { ContextMenuEntry } from "../components/ContextMenu"; + +export function BlockedRepositories() { + return ( + + + + ); +} + +type NewBlockedRepository = Pick; +type ExistingBlockedRepository = Pick; + +interface Props {} + +export function BlockedRepositoriesList(props: Props) { + const [searchResult, setSearchResult] = useState>({ rows: [], total: 0 }); + const [queryTerm, setQueryTerm] = useState(""); + const [searching, setSearching] = useState(false); + + const [isAddModalVisible, setAddModalVisible] = useState(false); + const [isDeleteModalVisible, setDeleteModalVisible] = useState(false); + + const [currentBlockedRepository, setCurrentBlockedRepository] = useState({ + id: 0, + urlRegexp: "", + blockUser: false, + }); + + const search = async () => { + setSearching(true); + try { + const result = await getGitpodService().server.adminGetBlockedRepositories({ + limit: 100, + orderBy: "urlRegexp", + offset: 0, + orderDir: "asc", + searchTerm: queryTerm, + }); + setSearchResult(result); + } finally { + setSearching(false); + } + }; + useEffect(() => { + search(); // Initial list + }, []); + + const add = () => { + setCurrentBlockedRepository({ + id: 0, + urlRegexp: "", + blockUser: false, + }); + setAddModalVisible(true); + }; + + const save = async (blockedRepository: NewBlockedRepository) => { + await getGitpodService().server.adminCreateBlockedRepository( + blockedRepository.urlRegexp, + blockedRepository.blockUser, + ); + setAddModalVisible(false); + search(); + }; + + const validate = (blockedRepository: NewBlockedRepository): string | undefined => { + if (blockedRepository.urlRegexp === "") { + return "Empty RegEx!"; + } + }; + + const deleteBlockedRepository = async (blockedRepository: ExistingBlockedRepository) => { + await getGitpodService().server.adminDeleteBlockedRepository(blockedRepository.id); + search(); + }; + + const confirmDeleteBlockedRepository = (blockedRepository: ExistingBlockedRepository) => { + setCurrentBlockedRepository(blockedRepository); + setAddModalVisible(false); + setDeleteModalVisible(true); + }; + + return ( + <> + {isAddModalVisible && ( + setAddModalVisible(false)} + /> + )} + {isDeleteModalVisible && ( + deleteBlockedRepository(currentBlockedRepository)} + onClose={() => setDeleteModalVisible(false)} + /> + )} +
+
+
+
+ + + +
+ ke.key === "Enter" && search()} + onChange={(v) => { + setQueryTerm(v.target.value.trim()); + }} + /> +
+
+ +
+
+
+
+
+
Repository URL Regex
+
Block user
+
Delete
+
+ {searchResult.rows.map((br) => ( + + ))} +
+ + ); +} + +function BlockedRepositoryEntry(props: { br: BlockedRepository; confirmedDelete: (br: BlockedRepository) => void }) { + const menuEntries: ContextMenuEntry[] = [ + { + title: "Delete", + onClick: () => props.confirmedDelete(props.br), + }, + ]; + return ( +
+
+ {props.br.urlRegexp} +
+
+ +
+
+ +
+
+ ); +} + +interface AddBlockedRepositoryModalProps { + blockedRepository: NewBlockedRepository; + validate: (blockedRepository: NewBlockedRepository) => string | undefined; + save: (br: NewBlockedRepository) => void; + onClose: () => void; +} + +function AddBlockedRepositoryModal(p: AddBlockedRepositoryModalProps) { + const [br, setBr] = useState({ ...p.blockedRepository }); + const [error, setError] = useState(""); + const ref = useRef(br); + + const update = (previous: Partial) => { + const newEnv = { ...ref.current, ...previous }; + setBr(newEnv); + ref.current = newEnv; + }; + + useEffect(() => { + setBr({ ...p.blockedRepository }); + setError(""); + }, [p.blockedRepository]); + + let save = (): boolean => { + const v = ref.current; + const newError = p.validate(v); + if (!!newError) { + setError(newError); + return false; + } + + p.save(v); + p.onClose(); + return true; + }; + + return ( + + Cancel + , + , + ]} + > +
+ + ); +} + +function DeleteBlockedRepositoryModal(props: { + blockedRepository: ExistingBlockedRepository; + deleteBlockedRepository: () => void; + onClose: () => void; +}) { + return ( + { + props.deleteBlockedRepository(); + props.onClose(); + }} + > +
+ + ); +} + +function Details(props: { + br: NewBlockedRepository; + error?: string; + update?: (pev: Partial) => void; +}) { + return ( +
+ {props.error ? ( +
{props.error}
+ ) : null} +
+

Repository URL RegEx

+ { + if (!!props.update) { + props.update({ urlRegexp: v.target.value }); + } + }} + /> +
+ { + if (!!props.update) { + props.update({ blockUser: v.target.checked }); + } + }} + /> +
+ ); +} diff --git a/components/dashboard/src/admin/BlockedRepositorySettings.tsx b/components/dashboard/src/admin/BlockedRepositorySettings.tsx deleted file mode 100644 index 4c7170e29fd027..00000000000000 --- a/components/dashboard/src/admin/BlockedRepositorySettings.tsx +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 { PageWithAdminSubMenu } from "./PageWithAdminSubMenu"; - -export function BlockedRepositorySettings() { - return ( - -
-
- ); -}