Path: blob/main/components/dashboard/src/admin/TeamsSearch.tsx
2500 views
/**1* Copyright (c) 2022 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import dayjs from "dayjs";7import { useState, useEffect } from "react";89import TeamDetail from "./TeamDetail";10import { useLocation } from "react-router";11import { Link } from "react-router-dom";12import { getGitpodService } from "../service/service";13import { AdminGetListResult, Team } from "@gitpod/gitpod-protocol";14import Label from "./Label";15import { AdminPageHeader } from "./AdminPageHeader";16import Pagination from "../Pagination/Pagination";17import { SpinnerLoader } from "../components/Loader";18import searchIcon from "../icons/search.svg";1920export default function TeamsSearchPage() {21return (22<AdminPageHeader title="Admin" subtitle="Configure and manage instance settings.">23<div className="app-container">24<TeamsSearch />25</div>26</AdminPageHeader>27);28}2930export function TeamsSearch() {31const location = useLocation();32const [searching, setSearching] = useState(false);33const [searchTerm, setSearchTerm] = useState("");34const [currentTeam, setCurrentTeam] = useState<Team | undefined>(undefined);35const [searchResult, setSearchResult] = useState<AdminGetListResult<Team>>({ total: 0, rows: [] });36const pageLength = 50;37const [currentPage, setCurrentPage] = useState(1);3839useEffect(() => {40const teamId = location.pathname.split("/")[3];41if (teamId && searchResult) {42let foundTeam = searchResult.rows.find((team) => team.id === teamId);43if (foundTeam) {44setCurrentTeam(foundTeam);45} else {46getGitpodService()47.server.adminGetTeamById(teamId)48.then((team) => setCurrentTeam(team))49.catch((e) => console.error(e));50}51} else {52setCurrentTeam(undefined);53}54// eslint-disable-next-line react-hooks/exhaustive-deps55}, [location]);5657if (currentTeam) {58return <TeamDetail team={currentTeam} />;59}6061const search = async (page: number = 1) => {62setSearching(true);63try {64const result = await getGitpodService().server.adminGetTeams({65searchTerm,66limit: pageLength,67orderBy: "creationTime",68offset: (page - 1) * pageLength,69orderDir: "desc",70});71setCurrentPage(page);72setSearchResult(result);73} finally {74setSearching(false);75}76};77return (78<>79<div className="mb-3 mt-3 flex">80<div className="flex justify-between w-full">81<div className="flex relative h-10 my-auto">82{searching ? (83<span className="filter-grayscale absolute top-3 left-3">84<SpinnerLoader small={true} />85</span>86) : (87<img88src={searchIcon}89title="Search"90className="filter-grayscale absolute top-3 left-3"91alt="search icon"92/>93)}94<input95className="w-64 pl-9 border-0"96type="search"97placeholder="Search Organizations"98onKeyDown={(k) => k.key === "Enter" && search()}99onChange={(v) => {100setSearchTerm(v.target.value.trim());101}}102/>103</div>104</div>105</div>106<div className="flex flex-col space-y-2">107<div className="px-6 py-3 flex justify-between text-sm text-gray-400 border-t border-b border-gray-200 dark:border-gray-800 mb-2">108<div className="w-7/12">Name</div>109<div className="w-5/12 flex items-center">110<span>Created</span>111<svg xmlns="http://www.w3.org/2000/svg" fill="none" className="h-4 w-4" viewBox="0 0 16 16">112<path113fill="#A8A29E"114fillRule="evenodd"115d="M13.366 8.234a.8.8 0 010 1.132l-4.8 4.8a.8.8 0 01-1.132 0l-4.8-4.8a.8.8 0 111.132-1.132L7.2 11.67V2.4a.8.8 0 111.6 0v9.269l3.434-3.435a.8.8 0 011.132 0z"116clipRule="evenodd"117/>118</svg>119</div>120</div>121{searchResult.rows.map((team) => (122<TeamResultItem team={team} />123))}124</div>125<Pagination126currentPage={currentPage}127setPage={search}128totalNumberOfPages={Math.ceil(searchResult.total / pageLength)}129/>130</>131);132133function TeamResultItem(props: { team: Team }) {134return (135<Link136key={"pr-" + props.team.name}137to={"/admin/orgs/" + props.team.id}138data-analytics='{"button_type":"sidebar_menu"}'139>140<div className="rounded-xl whitespace-nowrap flex py-6 px-6 w-full justify-between hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-kumquat-light group">141<div className="flex flex-col w-7/12 truncate">142<div className="font-medium text-gray-800 dark:text-gray-100 truncate max-w-sm">143{props.team.name}144{props.team.markedDeleted && <Label text="Deleted" color="red" />}145</div>146</div>147<div className="flex w-5/12 self-center">148<div className="text-sm w-full text-gray-400 truncate">149{dayjs(props.team.creationTime).format("MMM D, YYYY")}150</div>151</div>152</div>153</Link>154);155}156}157158159