From 5cab5b8ef7f4b23dfb8c2e91bfc6ef9c3a184460 Mon Sep 17 00:00:00 2001 From: Alex Tugarev Date: Wed, 23 Jun 2021 13:25:03 +0000 Subject: [PATCH] Add New Project page and GH App installation --- components/dashboard/src/App.tsx | 25 +- components/dashboard/src/Menu.tsx | 150 ++++-- components/dashboard/src/icons/Plus.svg | 3 + components/dashboard/src/icons/Switch.svg | 4 + components/dashboard/src/icons/search.svg | 3 + .../dashboard/src/projects/NewProject.tsx | 464 ++++++++++++++++++ .../dashboard/src/projects/Prebuilds.tsx | 121 +++++ components/dashboard/src/projects/Project.tsx | 106 ++++ .../dashboard/src/projects/Projects.tsx | 115 ++++- .../dashboard/src/projects/Settings.tsx | 34 ++ components/dashboard/src/provider-utils.tsx | 7 +- components/gitpod-db/src/project-db.ts | 1 + .../src/typeorm/entity/db-project.ts | 3 + .../1622468446118-TeamsAndProjects.ts | 2 +- .../gitpod-db/src/typeorm/project-db-impl.ts | 16 + .../gitpod-protocol/src/gitpod-service.ts | 33 +- components/gitpod-protocol/src/protocol.ts | 18 + components/server/ee/src/container-module.ts | 2 + .../ee/src/github/github-app-support.ts | 151 ++++++ .../server/ee/src/prebuilds/github-app.ts | 29 +- .../ee/src/workspace/gitpod-server-impl.ts | 84 +++- components/server/src/auth/rate-limiter.ts | 5 + .../src/workspace/gitpod-server-impl.ts | 30 +- 23 files changed, 1310 insertions(+), 96 deletions(-) create mode 100644 components/dashboard/src/icons/Plus.svg create mode 100644 components/dashboard/src/icons/Switch.svg create mode 100644 components/dashboard/src/icons/search.svg create mode 100644 components/dashboard/src/projects/NewProject.tsx create mode 100644 components/dashboard/src/projects/Prebuilds.tsx create mode 100644 components/dashboard/src/projects/Project.tsx create mode 100644 components/dashboard/src/projects/Settings.tsx create mode 100644 components/server/ee/src/github/github-app-support.ts diff --git a/components/dashboard/src/App.tsx b/components/dashboard/src/App.tsx index daabf4803b33c3..d367f37360b75f 100644 --- a/components/dashboard/src/App.tsx +++ b/components/dashboard/src/App.tsx @@ -31,7 +31,11 @@ const CreateWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ './s const NewTeam = React.lazy(() => import(/* webpackPrefetch: true */ './teams/NewTeam')); const JoinTeam = React.lazy(() => import(/* webpackPrefetch: true */ './teams/JoinTeam')); const Members = React.lazy(() => import(/* webpackPrefetch: true */ './teams/Members')); +const NewProject = React.lazy(() => import(/* webpackPrefetch: true */ './projects/NewProject')); const Projects = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Projects')); +const Project = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Project')); +const Prebuilds = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Prebuilds')); +const Settings = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Settings')); const InstallGitHubApp = React.lazy(() => import(/* webpackPrefetch: true */ './prebuilds/InstallGitHubApp')); const FromReferrer = React.lazy(() => import(/* webpackPrefetch: true */ './FromReferrer')); const UserSearch = React.lazy(() => import(/* webpackPrefetch: true */ './admin/UserSearch')); @@ -148,6 +152,7 @@ function App() {
+ @@ -190,11 +195,25 @@ function App() { - - + { + const { maybeProject, subResource } = props.match.params; + if (maybeProject === "projects") { + return ; + } + if (maybeProject === "members") { + return ; + } + if (subResource === "prebuilds") { + return ; + } + if (subResource === "settings") { + return ; + } + return ; + }} /> )} { + (_match) => { return isGitpodIo() ? // delegate to our website to handle the request diff --git a/components/dashboard/src/Menu.tsx b/components/dashboard/src/Menu.tsx index 44f460f096a8d3..b5dc4ca00c0daf 100644 --- a/components/dashboard/src/Menu.tsx +++ b/components/dashboard/src/Menu.tsx @@ -7,7 +7,7 @@ import { User, TeamMemberInfo } from "@gitpod/gitpod-protocol"; import { useContext, useEffect, useState } from "react"; import { Link, useHistory } from "react-router-dom"; -import { useLocation } from "react-router"; +import { useLocation, useRouteMatch } from "react-router"; import { Location } from "history"; import gitpodIcon from './icons/gitpod.svg'; import CaretDown from "./icons/CaretDown.svg"; @@ -39,6 +39,14 @@ export default function Menu() { const history = useHistory(); const location = useLocation(); + const match = useRouteMatch<{ team: string, resource: string }>("/:team/:resource"); + const projectName = (() => { + const resource = match?.params?.resource; + if (resource !== "projects" && resource !== "members") { + return resource; + } + })(); + const userFullName = user?.fullName || user?.name || '...'; const showTeamsUI = user?.rolesOrPermissions?.includes('teams-and-projects') || window.location.hostname.endsWith('gitpod-dev.com') || window.location.hostname.endsWith('gitpod-io-dev.com'); const team = getCurrentTeam(location, teams); @@ -58,32 +66,48 @@ export default function Menu() { })(); }, [ teams ]); - const leftMenu = (!!team - ? [ + const leftMenu: Entry[] = (() => { + if (!team) { + return [ + { + title: 'Workspaces', + link: '/workspaces', + alternatives: ['/'] + }, + { + title: 'Settings', + link: '/settings', + alternatives: settingsMenu.flatMap(e => e.link) + } + ]; + } + return projectName ? [ { - title: 'Projects', - link: `/${team.slug}/projects`, + title: 'Overview', + link: `/${team.slug}/${projectName}`, alternatives: [`/${team.slug}`] }, { - title: 'Members', - link: `/${team.slug}/members` + title: 'Prebuilds', + link: `/${team.slug}/${projectName}/prebuilds` + }, + { + title: 'Settings', + link: `/${team.slug}/${projectName}/settings` } - ] - : [ + ] : [ { - title: 'Workspaces', - link: '/workspaces', - alternatives: ['/'] + title: 'Projects', + link: `/${team.slug}/projects`, + alternatives: [`/${team.slug}`] }, { - title: 'Settings', - link: '/settings', - alternatives: settingsMenu.flatMap(e => e.link) + title: 'Members', + link: `/${team.slug}/members` } ] - ); - const rightMenu = [ + })(); + const rightMenu: Entry[] = [ ...(user?.rolesOrPermissions?.includes('admin') ? [{ title: 'Admin', link: '/admin', @@ -99,6 +123,61 @@ export default function Menu() { } ]; + const renderTeamMenu = () => { + return ( +
+
+ + + {team?.name || userFullName} + +
+
+ + {userFullName} + Personal Account +
, + separator: true, + onClick: () => history.push("/"), + }, + ...(teams || []).map(t => ({ + title: t.name, + customContent:
+ {t.name} + {!!teamMembers[t.id] + ? `${teamMembers[t.id].length} member${teamMembers[t.id].length === 1 ? '' : 's'}` + : '...' + } +
, + separator: true, + onClick: () => history.push(`/${t.slug}`), + })).sort((a, b) => a.title.toLowerCase() > b.title.toLowerCase() ? 1 : -1), + { + title: 'Create a new team', + customContent:
+ New Team + +
, + onClick: () => history.push("/new-team"), + } + ]}> +
+ +
+ +
+ { projectName && ( +
+ {projectName} +
+ )} +
+ ) + } + return <>
@@ -108,42 +187,7 @@ export default function Menu() {
{showTeamsUI - ? - {userFullName} - Personal Account -
, - separator: true, - onClick: () => history.push("/"), - }, - ...(teams || []).map(t => ({ - title: t.name, - customContent:
- {t.name} - {!!teamMembers[t.id] - ? `${teamMembers[t.id].length} member${teamMembers[t.id].length === 1 ? '' : 's'}` - : '...' - } -
, - separator: true, - onClick: () => history.push(`/${t.slug}`), - })).sort((a,b) => a.title.toLowerCase() > b.title.toLowerCase() ? 1 : -1), - { - title: 'Create a new team', - customContent:
- New Team - -
, - onClick: () => history.push("/new-team"), - } - ]}> -
- {team?.name || userFullName} - -
- + ? renderTeamMenu() :