Skip to content

Commit 8552d9e

Browse files
committed
Add New Project page and GH App installation
1 parent 71cc5eb commit 8552d9e

23 files changed

+1310
-96
lines changed

components/dashboard/src/App.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ const CreateWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ './s
3131
const NewTeam = React.lazy(() => import(/* webpackPrefetch: true */ './teams/NewTeam'));
3232
const JoinTeam = React.lazy(() => import(/* webpackPrefetch: true */ './teams/JoinTeam'));
3333
const Members = React.lazy(() => import(/* webpackPrefetch: true */ './teams/Members'));
34+
const NewProject = React.lazy(() => import(/* webpackPrefetch: true */ './projects/NewProject'));
3435
const Projects = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Projects'));
36+
const Project = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Project'));
37+
const Prebuilds = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Prebuilds'));
38+
const Settings = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Settings'));
3539
const InstallGitHubApp = React.lazy(() => import(/* webpackPrefetch: true */ './prebuilds/InstallGitHubApp'));
3640
const FromReferrer = React.lazy(() => import(/* webpackPrefetch: true */ './FromReferrer'));
3741
const UserSearch = React.lazy(() => import(/* webpackPrefetch: true */ './admin/UserSearch'));
@@ -148,6 +152,7 @@ function App() {
148152
<div className="container">
149153
<Menu />
150154
<Switch>
155+
<Route path="/new" exact component={NewProject} />
151156
<Route path="/setup" exact component={Setup} />
152157
<Route path="/workspaces" exact component={Workspaces} />
153158
<Route path="/account" exact component={Account} />
@@ -190,11 +195,25 @@ function App() {
190195
<Route exact path={`/${team.slug}`}>
191196
<Redirect to={`/${team.slug}/projects`} />
192197
</Route>
193-
<Route exact path={`/${team.slug}/members`} component={Members} />
194-
<Route exact path={`/${team.slug}/projects`} component={Projects} />
198+
<Route exact path={`/${team.slug}/:maybeProject/:subResource?`} render={(props) => {
199+
const { maybeProject, subResource } = props.match.params;
200+
if (maybeProject === "projects") {
201+
return <Projects />;
202+
}
203+
if (maybeProject === "members") {
204+
return <Members />;
205+
}
206+
if (subResource === "prebuilds") {
207+
return <Prebuilds />;
208+
}
209+
if (subResource === "settings") {
210+
return <Settings />;
211+
}
212+
return <Project />;
213+
}} />
195214
</Route>)}
196215
<Route path="*" render={
197-
(match) => {
216+
(_match) => {
198217

199218
return isGitpodIo() ?
200219
// delegate to our website to handle the request

components/dashboard/src/Menu.tsx

Lines changed: 97 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import { User, TeamMemberInfo } from "@gitpod/gitpod-protocol";
88
import { useContext, useEffect, useState } from "react";
99
import { Link, useHistory } from "react-router-dom";
10-
import { useLocation } from "react-router";
10+
import { useLocation, useRouteMatch } from "react-router";
1111
import { Location } from "history";
1212
import gitpodIcon from './icons/gitpod.svg';
1313
import CaretDown from "./icons/CaretDown.svg";
@@ -39,6 +39,14 @@ export default function Menu() {
3939
const history = useHistory();
4040
const location = useLocation();
4141

42+
const match = useRouteMatch<{ team: string, resource: string }>("/:team/:resource");
43+
const projectName = (() => {
44+
const resource = match?.params?.resource;
45+
if (resource !== "projects" && resource !== "members") {
46+
return resource;
47+
}
48+
})();
49+
4250
const userFullName = user?.fullName || user?.name || '...';
4351
const showTeamsUI = user?.rolesOrPermissions?.includes('teams-and-projects') || window.location.hostname.endsWith('gitpod-dev.com') || window.location.hostname.endsWith('gitpod-io-dev.com');
4452
const team = getCurrentTeam(location, teams);
@@ -58,32 +66,48 @@ export default function Menu() {
5866
})();
5967
}, [ teams ]);
6068

61-
const leftMenu = (!!team
62-
? [
69+
const leftMenu: Entry[] = (() => {
70+
if (!team) {
71+
return [
72+
{
73+
title: 'Workspaces',
74+
link: '/workspaces',
75+
alternatives: ['/']
76+
},
77+
{
78+
title: 'Settings',
79+
link: '/settings',
80+
alternatives: settingsMenu.flatMap(e => e.link)
81+
}
82+
];
83+
}
84+
return projectName ? [
6385
{
64-
title: 'Projects',
65-
link: `/${team.slug}/projects`,
86+
title: 'Overview',
87+
link: `/${team.slug}/${projectName}`,
6688
alternatives: [`/${team.slug}`]
6789
},
6890
{
69-
title: 'Members',
70-
link: `/${team.slug}/members`
91+
title: 'Prebuilds',
92+
link: `/${team.slug}/${projectName}/prebuilds`
93+
},
94+
{
95+
title: 'Settings',
96+
link: `/${team.slug}/${projectName}/settings`
7197
}
72-
]
73-
: [
98+
] : [
7499
{
75-
title: 'Workspaces',
76-
link: '/workspaces',
77-
alternatives: ['/']
100+
title: 'Projects',
101+
link: `/${team.slug}/projects`,
102+
alternatives: [`/${team.slug}`]
78103
},
79104
{
80-
title: 'Settings',
81-
link: '/settings',
82-
alternatives: settingsMenu.flatMap(e => e.link)
105+
title: 'Members',
106+
link: `/${team.slug}/members`
83107
}
84108
]
85-
);
86-
const rightMenu = [
109+
})();
110+
const rightMenu: Entry[] = [
87111
...(user?.rolesOrPermissions?.includes('admin') ? [{
88112
title: 'Admin',
89113
link: '/admin',
@@ -99,6 +123,61 @@ export default function Menu() {
99123
}
100124
];
101125

126+
const renderTeamMenu = () => {
127+
return (
128+
<div className="flex p-1 pl-3 ">
129+
<div className="flex h-full rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 px-2 py-1">
130+
<Link to={team ? `/${team.slug}/projects` : "/workspaces"}>
131+
132+
<span className="text-base text-gray-600 dark:text-gray-400 font-semibold">{team?.name || userFullName}</span>
133+
</Link>
134+
</div>
135+
<div className="flex h-full rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 py-1">
136+
<ContextMenu classes="w-64 left-0" menuEntries={[
137+
{
138+
title: userFullName,
139+
customContent: <div className="w-full text-gray-400 flex flex-col">
140+
<span className="text-gray-800 dark:text-gray-100 text-base font-semibold">{userFullName}</span>
141+
<span className="">Personal Account</span>
142+
</div>,
143+
separator: true,
144+
onClick: () => history.push("/"),
145+
},
146+
...(teams || []).map(t => ({
147+
title: t.name,
148+
customContent: <div className="w-full text-gray-400 flex flex-col">
149+
<span className="text-gray-800 dark:text-gray-300 text-base font-semibold">{t.name}</span>
150+
<span className="">{!!teamMembers[t.id]
151+
? `${teamMembers[t.id].length} member${teamMembers[t.id].length === 1 ? '' : 's'}`
152+
: '...'
153+
}</span>
154+
</div>,
155+
separator: true,
156+
onClick: () => history.push(`/${t.slug}`),
157+
})).sort((a, b) => a.title.toLowerCase() > b.title.toLowerCase() ? 1 : -1),
158+
{
159+
title: 'Create a new team',
160+
customContent: <div className="w-full text-gray-400 flex items-center">
161+
<span className="flex-1 font-semibold">New Team</span>
162+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14" className="w-3.5"><path fill="currentColor" fill-rule="evenodd" d="M7 0a1 1 0 011 1v5h5a1 1 0 110 2H8v5a1 1 0 11-2 0V8H1a1 1 0 010-2h5V1a1 1 0 011-1z" clip-rule="evenodd" /></svg>
163+
</div>,
164+
onClick: () => history.push("/new-team"),
165+
}
166+
]}>
167+
<div className="flex h-full p-2 mt-0.5">
168+
<img className="filter-grayscale m-auto" src={CaretDown} />
169+
</div>
170+
</ContextMenu>
171+
</div>
172+
{ projectName && (
173+
<div className="flex h-full ml-2 py-1">
174+
<span className="text-base text-gray-600 dark:text-gray-400 font-semibold">{projectName}</span>
175+
</div>
176+
)}
177+
</div>
178+
)
179+
}
180+
102181
return <>
103182
<header className="lg:px-28 px-10 flex flex-col pt-4 space-y-4">
104183
<div className="flex">
@@ -108,42 +187,7 @@ export default function Menu() {
108187
</Link>
109188
<div className="ml-2 text-base">
110189
{showTeamsUI
111-
? <ContextMenu classes="w-64 left-0" menuEntries={[
112-
{
113-
title: userFullName,
114-
customContent: <div className="w-full text-gray-400 flex flex-col">
115-
<span className="text-gray-800 dark:text-gray-100 text-base font-semibold">{userFullName}</span>
116-
<span className="">Personal Account</span>
117-
</div>,
118-
separator: true,
119-
onClick: () => history.push("/"),
120-
},
121-
...(teams || []).map(t => ({
122-
title: t.name,
123-
customContent: <div className="w-full text-gray-400 flex flex-col">
124-
<span className="text-gray-800 dark:text-gray-300 text-base font-semibold">{t.name}</span>
125-
<span className="">{!!teamMembers[t.id]
126-
? `${teamMembers[t.id].length} member${teamMembers[t.id].length === 1 ? '' : 's'}`
127-
: '...'
128-
}</span>
129-
</div>,
130-
separator: true,
131-
onClick: () => history.push(`/${t.slug}`),
132-
})).sort((a,b) => a.title.toLowerCase() > b.title.toLowerCase() ? 1 : -1),
133-
{
134-
title: 'Create a new team',
135-
customContent: <div className="w-full text-gray-400 flex items-center">
136-
<span className="flex-1 font-semibold">New Team</span>
137-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14" className="w-3.5"><path fill="currentColor" fill-rule="evenodd" d="M7 0a1 1 0 011 1v5h5a1 1 0 110 2H8v5a1 1 0 11-2 0V8H1a1 1 0 010-2h5V1a1 1 0 011-1z" clip-rule="evenodd"/></svg>
138-
</div>,
139-
onClick: () => history.push("/new-team"),
140-
}
141-
]}>
142-
<div className="flex p-1.5 pl-3 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800">
143-
<span className="text-base text-gray-600 dark:text-gray-400 font-semibold">{team?.name || userFullName}</span>
144-
<img className="m-2 filter-grayscale" src={CaretDown}/>
145-
</div>
146-
</ContextMenu>
190+
? renderTeamMenu()
147191
: <nav className="flex-1">
148192
<ul className="flex flex-1 items-center justify-between text-base text-gray-700 space-x-2">
149193
<li className="flex-1"></li>
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)