Skip to content

[dashboard] In /workspaces, indicate when you have active workspaces in your teams #6103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions components/dashboard/src/projects/Prebuilds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getGitpodService } from "../service/service";
import { TeamsContext, getCurrentTeam } from "../teams/teams-context";
import { ContextMenuEntry } from "../components/ContextMenu";
import { shortCommitMessage } from "./render-utils";
import { Link } from "react-router-dom";

export default function () {
const location = useLocation();
Expand Down Expand Up @@ -170,13 +171,13 @@ export default function () {
</Item>
{prebuilds.filter(filter).sort(prebuildSorter).map((p, index) => <Item key={`prebuild-${p.info.id}`} className="grid grid-cols-3">
<ItemField className="flex items-center">
<a href={`/${!!team ? 't/'+team.slug : 'projects'}/${projectName}/${p.info.id}`} className="cursor-pointer">
<Link to={`/${!!team ? 't/'+team.slug : 'projects'}/${projectName}/${p.info.id}`} className="cursor-pointer">
<div className="text-base text-gray-900 dark:text-gray-50 font-medium uppercase mb-1">
<div className="inline-block align-text-bottom mr-2 w-4 h-4">{prebuildStatusIcon(p)}</div>
{prebuildStatusLabel(p)}
</div>
<p>{p.info.startedByAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={p.info.startedByAvatar || ''} alt={p.info.startedBy} />}Triggered {formatDate(p.info.startedAt)}</p>
</a>
</Link>
</ItemField>
<ItemField className="flex items-center">
<div>
Expand Down
57 changes: 46 additions & 11 deletions components/dashboard/src/workspaces/Workspaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
*/

import { useContext, useEffect, useState } from "react";
import { Project, WhitelistedRepository, Workspace, WorkspaceInfo } from "@gitpod/gitpod-protocol";
import { Project, Team, WhitelistedRepository, Workspace, WorkspaceInfo } from "@gitpod/gitpod-protocol";
import Header from "../components/Header";
import DropDown from "../components/DropDown";
import { WorkspaceModel } from "./workspace-model";
import { WorkspaceEntry } from "./WorkspaceEntry";
import { getGitpodService, gitpodHostUrl } from "../service/service";
import { StartWorkspaceModal, WsStartEntry } from "./StartWorkspaceModal";
import { Item, ItemField, ItemsList } from "../components/ItemsList";
import { ItemsList } from "../components/ItemsList";
import { getCurrentTeam, TeamsContext } from "../teams/teams-context";
import { useLocation, useRouteMatch } from "react-router";
import { toRemoteURL } from "../projects/render-utils";
import { useHistory } from "react-router-dom";
import { Link, useHistory } from "react-router-dom";

export interface WorkspacesProps {
}
Expand All @@ -41,12 +41,22 @@ export default function () {
const [repos, setRepos] = useState<WhitelistedRepository[]>([]);
const [isTemplateModelOpen, setIsTemplateModelOpen] = useState<boolean>(false);
const [workspaceModel, setWorkspaceModel] = useState<WorkspaceModel>();
const [teamsProjects, setTeamsProjects] = useState<Project[]>([]);
const [teamsWorkspaceModel, setTeamsWorkspaceModel] = useState<WorkspaceModel|undefined>();
const [teamsActiveWorkspaces, setTeamsActiveWorkspaces] = useState<WorkspaceInfo[]>([]);

const newProjectUrl = !!team ? `/new?team=${team.slug}` : '/new';
const onNewProject = () => {
history.push(newProjectUrl);
}

const fetchTeamsProjects = async () => {
const projectsPerTeam = await Promise.all((teams || []).map(t => getGitpodService().server.getTeamProjects(t.id)));
const allTeamsProjects = projectsPerTeam.flat(1);
setTeamsProjects(allTeamsProjects);
return allTeamsProjects;
}

useEffect(() => {
// only show example repos on the global user context
if (!team && !projectName) {
Expand All @@ -73,6 +83,9 @@ export default function () {
workspaceModel = new WorkspaceModel(setActiveWorkspaces, setInactiveWorkspaces, getGitpodService().server.getTeamProjects(team?.id).then(projects => projects.map(p => p.id)), false);
} else {
workspaceModel = new WorkspaceModel(setActiveWorkspaces, setInactiveWorkspaces, getGitpodService().server.getUserProjects().then(projects => projects.map(p => p.id)), true);
// Don't await
const teamsProjectIdsPromise = fetchTeamsProjects().then(tp => tp.map(p => p.id));
setTeamsWorkspaceModel(new WorkspaceModel(setTeamsActiveWorkspaces, () => {}, teamsProjectIdsPromise, false));
}
setWorkspaceModel(workspaceModel);
})();
Expand Down Expand Up @@ -156,6 +169,9 @@ export default function () {
</div>
<ItemsList className="lg:px-28 px-10">
<div className="border-t border-gray-200 dark:border-gray-800"></div>
{
teamsWorkspaceModel?.initialized && <ActiveTeamWorkspaces teams={teams} teamProjects={teamsProjects} teamWorkspaces={teamsActiveWorkspaces} />
}
{
activeWorkspaces.map(e => {
return <WorkspaceEntry key={e.workspace.id} desc={e} model={workspaceModel} stopWorkspace={wsId => getGitpodService().server.stopWorkspace(wsId)} />
Expand All @@ -165,12 +181,7 @@ export default function () {
activeWorkspaces.length > 0 && <div className="py-6"></div>
}
{
inactiveWorkspaces.length === 0 ? null :
<Item className="w-full bg-gray-50 py-3 px-3 hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-800">
<ItemField className=" flex flex-col">
<div className="text-gray-400 text-sm text-center">Unpinned workspaces that have been inactive for more than 14 days will be automatically deleted. <a className="gp-link" href="https://www.gitpod.io/docs/life-of-workspace/#garbage-collection">Learn more</a></div>
</ItemField>
</Item>
inactiveWorkspaces.length > 0 && <div className="p-3 text-gray-400 bg-gray-50 dark:bg-gray-800 rounded-xl text-sm text-center">Unpinned workspaces that have been inactive for more than 14 days will be automatically deleted. <a className="gp-link" href="https://www.gitpod.io/docs/life-of-workspace/#garbage-collection">Learn more</a></div>
}
{
inactiveWorkspaces.map(e => {
Expand All @@ -181,8 +192,9 @@ export default function () {
</>
:
<div className="lg:px-28 px-10 flex flex-col space-y-2">
<div className="px-6 py-3 flex justify-between space-x-2 text-gray-400 border-t border-gray-200 dark:border-gray-800 h-96">
<div className="flex flex-col items-center w-96 m-auto">
<div className="px-6 py-3 flex flex-col text-gray-400 border-t border-gray-200 dark:border-gray-800">
{teamsWorkspaceModel?.initialized && <ActiveTeamWorkspaces teams={teams} teamProjects={teamsProjects} teamWorkspaces={teamsActiveWorkspaces} />}
<div className="flex flex-col items-center justify-center h-96 w-96 mx-auto">
{!!team && projects.length === 0
?<>
<h3 className="text-center pb-3 text-gray-500 dark:text-gray-400">No Projects</h3>
Expand Down Expand Up @@ -216,3 +228,26 @@ export default function () {
</>;

}

function ActiveTeamWorkspaces(props: { teams?: Team[], teamProjects: Project[], teamWorkspaces: WorkspaceInfo[] }) {
if (!props.teams || props.teamWorkspaces.length === 0) {
return <></>;
}
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">
<div className="mr-2 rounded-full w-3 h-3 bg-green-500" />
<span>There are currently more active workspaces in the following teams:</span>
<span>{
props.teams
.map(t => {
const projects = props.teamProjects.filter(p => p.teamId === t.id);
const count = props.teamWorkspaces.filter(w => projects.some(p => p.id === w.workspace.projectId)).length;
if (count < 1) {
return undefined;
}
return <Link className="gp-link" to={`/t/${t.slug}/workspaces`}>{t.name}</Link>;
})
.filter(t => !!t)
.map((t, i) => <>{i > 0 && <span>, </span>}{t}</>)
}</span>
</div>;
}