Skip to content

Commit 932a33a

Browse files
svenefftingeroboquat
authored andcommitted
[dashboard] single workspaces list
reverts the multiple workspaces lists under teams and projects reintroduces a single workspaces list that shows all my workspaces
1 parent 972dfeb commit 932a33a

File tree

4 files changed

+15
-134
lines changed

4 files changed

+15
-134
lines changed

components/dashboard/src/App.tsx

+1-7
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ function App() {
277277
<Route path="/admin/workspaces" component={WorkspacesSearch} />
278278

279279
<Route path={["/", "/login"]} exact>
280-
<Redirect to="/projects" />
280+
<Redirect to="/workspaces" />
281281
</Route>
282282
<Route path={["/settings"]} exact>
283283
<Redirect to="/account" />
@@ -307,9 +307,6 @@ function App() {
307307
if (resourceOrPrebuild === "configure") {
308308
return <ConfigureProject />;
309309
}
310-
if (resourceOrPrebuild === "workspaces") {
311-
return <Workspaces />;
312-
}
313310
if (resourceOrPrebuild === "prebuilds") {
314311
return <Prebuilds />;
315312
}
@@ -346,9 +343,6 @@ function App() {
346343
if (resourceOrPrebuild === "settings") {
347344
return <ProjectSettings />;
348345
}
349-
if (resourceOrPrebuild === "workspaces") {
350-
return <Workspaces />;
351-
}
352346
if (resourceOrPrebuild === "prebuilds") {
353347
return <Prebuilds />;
354348
}

components/dashboard/src/Menu.tsx

+6-21
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,6 @@ export default function Menu() {
9797
title: 'Branches',
9898
link: `${teamOrUserSlug}/${projectSlug}`,
9999
},
100-
{
101-
title: 'Workspaces',
102-
link: `${teamOrUserSlug}/${projectSlug}/workspaces`,
103-
},
104100
{
105101
title: 'Prebuilds',
106102
link: `${teamOrUserSlug}/${projectSlug}/prebuilds`,
@@ -120,11 +116,7 @@ export default function Menu() {
120116
{
121117
title: 'Projects',
122118
link: `/t/${team.slug}/projects`,
123-
},
124-
{
125-
title: 'Workspaces',
126-
link: `/t/${team.slug}/workspaces`,
127-
alternatives: [`/t/${team.slug}`]
119+
alternatives: ([] as string[])
128120
},
129121
{
130122
title: 'Members',
@@ -143,15 +135,15 @@ export default function Menu() {
143135
}
144136
// User menu
145137
return [
146-
{
147-
title: 'Projects',
148-
link: '/projects'
149-
},
150138
{
151139
title: 'Workspaces',
152140
link: '/workspaces',
153141
alternatives: ['/']
154142
},
143+
{
144+
title: 'Projects',
145+
link: '/projects'
146+
},
155147
{
156148
title: 'Settings',
157149
link: '/settings',
@@ -240,18 +232,11 @@ export default function Menu() {
240232
)
241233
}
242234

243-
const gitpodIconUrl = () => {
244-
if (team) {
245-
return `/t/${team.slug}`;
246-
}
247-
return "/"
248-
}
249-
250235
return <>
251236
<header className={`app-container flex flex-col pt-4 space-y-4 ${isMinimalUI || !!prebuildId ? 'pb-4' : ''}`} data-analytics='{"button_type":"menu"}'>
252237
<div className="flex h-10">
253238
<div className="flex justify-between items-center pr-3">
254-
<Link to={gitpodIconUrl()}>
239+
<Link to="/">
255240
<img src={gitpodIcon} className="h-6" alt="Gitpod's logo" />
256241
</Link>
257242
{!isMinimalUI && <div className="ml-2 text-base">

components/dashboard/src/workspaces/Workspaces.tsx

+4-90
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import { useContext, useEffect, useState } from "react";
8-
import { Project, Team, WhitelistedRepository, Workspace, WorkspaceInfo } from "@gitpod/gitpod-protocol";
8+
import { WhitelistedRepository, Workspace, WorkspaceInfo } from "@gitpod/gitpod-protocol";
99
import Header from "../components/Header";
1010
import DropDown from "../components/DropDown";
1111
import { WorkspaceModel } from "./workspace-model";
@@ -15,8 +15,6 @@ import { StartWorkspaceModal, WsStartEntry } from "./StartWorkspaceModal";
1515
import { ItemsList } from "../components/ItemsList";
1616
import { getCurrentTeam, TeamsContext } from "../teams/teams-context";
1717
import { useLocation, useRouteMatch } from "react-router";
18-
import { toRemoteURL } from "../projects/render-utils";
19-
import { Link, useHistory } from "react-router-dom";
2018

2119
export interface WorkspacesProps {
2220
}
@@ -29,64 +27,24 @@ export interface WorkspacesState {
2927

3028
export default function () {
3129
const location = useLocation();
32-
const history = useHistory();
3330

3431
const { teams } = useContext(TeamsContext);
3532
const team = getCurrentTeam(location, teams);
3633
const match = useRouteMatch<{ team: string, resource: string }>("/(t/)?:team/:resource");
3734
const projectSlug = match?.params?.resource !== 'workspaces' ? match?.params?.resource : undefined;
38-
const [projects, setProjects] = useState<Project[]>([]);
3935
const [activeWorkspaces, setActiveWorkspaces] = useState<WorkspaceInfo[]>([]);
4036
const [inactiveWorkspaces, setInactiveWorkspaces] = useState<WorkspaceInfo[]>([]);
4137
const [repos, setRepos] = useState<WhitelistedRepository[]>([]);
4238
const [isTemplateModelOpen, setIsTemplateModelOpen] = useState<boolean>(false);
4339
const [workspaceModel, setWorkspaceModel] = useState<WorkspaceModel>();
44-
const [teamsProjects, setTeamsProjects] = useState<Project[]>([]);
45-
const [teamsWorkspaceModel, setTeamsWorkspaceModel] = useState<WorkspaceModel|undefined>();
46-
const [teamsActiveWorkspaces, setTeamsActiveWorkspaces] = useState<WorkspaceInfo[]>([]);
47-
48-
const newProjectUrl = !!team ? `/new?team=${team.slug}` : '/new?user=1';
49-
const onNewProject = () => {
50-
history.push(newProjectUrl);
51-
}
52-
53-
const fetchTeamsProjects = async () => {
54-
const projectsPerTeam = await Promise.all((teams || []).map(t => getGitpodService().server.getTeamProjects(t.id)));
55-
const allTeamsProjects = projectsPerTeam.flat(1);
56-
setTeamsProjects(allTeamsProjects);
57-
return allTeamsProjects;
58-
}
5940

6041
useEffect(() => {
6142
// only show example repos on the global user context
6243
if (!team && !projectSlug) {
6344
getGitpodService().server.getFeaturedRepositories().then(setRepos);
6445
}
6546
(async () => {
66-
const projects = (!!team
67-
? await getGitpodService().server.getTeamProjects(team.id)
68-
: await getGitpodService().server.getUserProjects());
69-
70-
let project: Project | undefined = undefined;
71-
if (projectSlug) {
72-
project = projects.find(p => p.slug ? p.slug === projectSlug : p.name === projectSlug);
73-
if (project) {
74-
setProjects([project]);
75-
}
76-
} else {
77-
setProjects(projects);
78-
}
79-
let workspaceModel;
80-
if (!!project) {
81-
workspaceModel = new WorkspaceModel(setActiveWorkspaces, setInactiveWorkspaces, Promise.resolve([project.id]), false);
82-
} else if (!!team) {
83-
workspaceModel = new WorkspaceModel(setActiveWorkspaces, setInactiveWorkspaces, getGitpodService().server.getTeamProjects(team?.id).then(projects => projects.map(p => p.id)), false);
84-
} else {
85-
workspaceModel = new WorkspaceModel(setActiveWorkspaces, setInactiveWorkspaces, getGitpodService().server.getUserProjects().then(projects => projects.map(p => p.id)), true);
86-
// Don't await
87-
const teamsProjectIdsPromise = fetchTeamsProjects().then(tp => tp.map(p => p.id));
88-
setTeamsWorkspaceModel(new WorkspaceModel(setTeamsActiveWorkspaces, () => {}, teamsProjectIdsPromise, false));
89-
}
47+
const workspaceModel = new WorkspaceModel(setActiveWorkspaces, setInactiveWorkspaces);
9048
setWorkspaceModel(workspaceModel);
9149
})();
9250
}, [teams, location]);
@@ -95,16 +53,6 @@ export default function () {
9553
const hideStartWSModal = () => setIsTemplateModelOpen(false);
9654

9755
const getRecentSuggestions: () => WsStartEntry[] = () => {
98-
if (projectSlug || team) {
99-
return projects.map(p => {
100-
const remoteUrl = toRemoteURL(p.cloneUrl);
101-
return {
102-
title: (team ? team.name + '/' : '') + p.name,
103-
description: remoteUrl,
104-
startUrl: gitpodHostUrl.withContext(remoteUrl).toString()
105-
};
106-
});
107-
}
10856
if (workspaceModel) {
10957
const all = workspaceModel.getAllFetchedWorkspaces();
11058
if (all && all.size > 0) {
@@ -169,9 +117,6 @@ export default function () {
169117
</div>
170118
<ItemsList className="app-container pb-40">
171119
<div className="border-t border-gray-200 dark:border-gray-800"></div>
172-
{
173-
teamsWorkspaceModel?.initialized && <ActiveTeamWorkspaces teams={teams} teamProjects={teamsProjects} teamWorkspaces={teamsActiveWorkspaces} />
174-
}
175120
{
176121
activeWorkspaces.map(e => {
177122
return <WorkspaceEntry key={e.workspace.id} desc={e} model={workspaceModel} stopWorkspace={wsId => getGitpodService().server.stopWorkspace(wsId)} />
@@ -193,23 +138,14 @@ export default function () {
193138
:
194139
<div className="app-container flex flex-col space-y-2">
195140
<div className="px-6 py-3 flex flex-col text-gray-400 border-t border-gray-200 dark:border-gray-800">
196-
{teamsWorkspaceModel?.initialized && <ActiveTeamWorkspaces teams={teams} teamProjects={teamsProjects} teamWorkspaces={teamsActiveWorkspaces} />}
197141
<div className="flex flex-col items-center justify-center h-96 w-96 mx-auto">
198-
{!!team && projects.length === 0
199-
?<>
200-
<h3 className="text-center pb-3 text-gray-500 dark:text-gray-400">No Projects</h3>
201-
<div className="text-center pb-6 text-gray-500">This team doesn't have any projects, yet.</div>
202-
<span>
203-
<button onClick={onNewProject}>New Project</button>
204-
</span>
205-
</>
206-
:<>
142+
<>
207143
<h3 className="text-center pb-3 text-gray-500 dark:text-gray-400">No Workspaces</h3>
208144
<div className="text-center pb-6 text-gray-500">Prefix any Git repository URL with {window.location.host}/# or create a new workspace for a recently used project. <a className="gp-link" href="https://www.gitpod.io/docs/getting-started/">Learn more</a></div>
209145
<span>
210146
<button onClick={showStartWSModal}>New Workspace</button>
211147
</span>
212-
</>}
148+
</>
213149
</div>
214150
</div>
215151
</div>
@@ -229,25 +165,3 @@ export default function () {
229165

230166
}
231167

232-
function ActiveTeamWorkspaces(props: { teams?: Team[], teamProjects: Project[], teamWorkspaces: WorkspaceInfo[] }) {
233-
if (!props.teams || props.teamWorkspaces.length === 0) {
234-
return <></>;
235-
}
236-
return <div className="p-3 text-gray-400 bg-gray-50 dark:bg-gray-800 rounded-xl text-sm flex items-center justify-center space-x-1">
237-
<div className="mr-2 rounded-full w-3 h-3 bg-green-500" />
238-
<span>There are currently more active workspaces in the following teams:</span>
239-
<span>{
240-
props.teams
241-
.map(t => {
242-
const projects = props.teamProjects.filter(p => p.teamId === t.id);
243-
const count = props.teamWorkspaces.filter(w => projects.some(p => p.id === w.workspace.projectId)).length;
244-
if (count < 1) {
245-
return undefined;
246-
}
247-
return <Link className="gp-link" to={`/t/${t.slug}/workspaces`}>{t.name}</Link>;
248-
})
249-
.filter(t => !!t)
250-
.map((t, i) => <>{i > 0 && <span>, </span>}{t}</>)
251-
}</span>
252-
</div>;
253-
}

components/dashboard/src/workspaces/workspace-model.ts

+4-16
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ export class WorkspaceModel implements Disposable, Partial<GitpodClient> {
2626

2727
constructor(
2828
protected setActiveWorkspaces: (ws: WorkspaceInfo[]) => void,
29-
protected setInActiveWorkspaces: (ws: WorkspaceInfo[]) => void,
30-
protected projectIds: Promise<string[]>,
31-
protected includeWithoutProject?: boolean) {
29+
protected setInActiveWorkspaces: (ws: WorkspaceInfo[]) => void) {
3230
this.internalRefetch();
3331
}
3432

@@ -38,14 +36,12 @@ export class WorkspaceModel implements Disposable, Partial<GitpodClient> {
3836
const [infos, pinned] = await Promise.all([
3937
getGitpodService().server.getWorkspaces({
4038
limit: this.internalLimit,
41-
projectId: await this.projectIds,
42-
includeWithoutProject: !!this.includeWithoutProject
39+
includeWithoutProject: true
4340
}),
4441
getGitpodService().server.getWorkspaces({
4542
limit: this.internalLimit,
4643
pinnedOnly: true,
47-
projectId: await this.projectIds,
48-
includeWithoutProject: !!this.includeWithoutProject
44+
includeWithoutProject: true
4945
})
5046
]);
5147

@@ -63,14 +59,6 @@ export class WorkspaceModel implements Disposable, Partial<GitpodClient> {
6359
}
6460
}
6561

66-
protected async isIncluded(info: WorkspaceInfo): Promise<boolean> {
67-
if (info.workspace.projectId) {
68-
return (await this.projectIds).some(id => id === info.workspace.projectId);
69-
} else {
70-
return !!this.includeWithoutProject;
71-
}
72-
}
73-
7462
dispose(): void {
7563
this.disposables.dispose();
7664
}
@@ -87,7 +75,7 @@ export class WorkspaceModel implements Disposable, Partial<GitpodClient> {
8775
try {
8876
this.currentlyFetching.add(instance.workspaceId);
8977
const info = await getGitpodService().server.getWorkspace(instance.workspaceId);
90-
if (info.workspace.type === 'regular' && await this.isIncluded(info)) {
78+
if (info.workspace.type === 'regular') {
9179
this.workspaces.set(instance.workspaceId, info);
9280
this.notifyWorkpaces();
9381
}

0 commit comments

Comments
 (0)