Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fc07e1f

Browse files
committedJul 20, 2021
WIP projects services
1 parent ece324b commit fc07e1f

20 files changed

+396
-144
lines changed
 

‎components/dashboard/src/projects/Prebuilds.tsx

+51-51
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ export default function () {
3737
(async () => {
3838
const projects = await getGitpodService().server.getProjects(team.id);
3939

40-
const project = projects.find(p => p.name === projectName);
40+
const project = projectName && projects.find(p => p.name === projectName);
4141
if (project) {
4242
setProject(project);
43-
setPrebuilds(await getGitpodService().server.getPrebuilds(team.id, project.id));
43+
setPrebuilds(await getGitpodService().server.getPrebuilds(team.id, projectName!));
4444
}
4545
})();
4646
}, [team]);
@@ -49,58 +49,21 @@ export default function () {
4949
const entries: ContextMenuEntry[] = [];
5050
entries.push({
5151
title: "View Prebuild",
52-
onClick: () => {}
52+
onClick: () => { }
5353
});
5454
entries.push({
5555
title: "Trigger Prebuild",
56-
onClick: () => {},
56+
onClick: () => { },
5757
separator: true
5858
});
5959
entries.push({
6060
title: "Cancel Prebuild",
6161
customFontStyle: 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300',
62-
onClick: () => {}
62+
onClick: () => { }
6363
})
6464
return entries;
6565
}
6666

67-
const prebuildStatusLabel = (pbw: PrebuildInfo) => {
68-
switch (pbw.status) {
69-
case "aborted":
70-
return (<span className="font-medium text-red-500 uppercase">failed</span>);
71-
case "available":
72-
return (<span className="font-medium text-green-500 uppercase">ready</span>);
73-
case "building":
74-
return (<span className="font-medium text-blue-500 uppercase">running</span>);
75-
case "queued":
76-
return (<span className="font-medium text-orange-500 uppercase">pending</span>);
77-
default:
78-
break;
79-
}
80-
}
81-
const prebuildStatusIcon = (pbw: PrebuildInfo) => {
82-
switch (pbw.status) {
83-
case "aborted":
84-
return (<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
85-
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM6.70711 5.29289C6.31658 4.90237 5.68342 4.90237 5.29289 5.29289C4.90237 5.68342 4.90237 6.31658 5.29289 6.70711L6.58579 8L5.29289 9.29289C4.90237 9.68342 4.90237 10.3166 5.29289 10.7071C5.68342 11.0976 6.31658 11.0976 6.70711 10.7071L8 9.41421L9.29289 10.7071C9.68342 11.0976 10.3166 11.0976 10.7071 10.7071C11.0976 10.3166 11.0976 9.68342 10.7071 9.29289L9.41421 8L10.7071 6.70711C11.0976 6.31658 11.0976 5.68342 10.7071 5.29289C10.3166 4.90237 9.68342 4.90237 9.29289 5.29289L8 6.58579L6.70711 5.29289Z" fill="#F87171"/>
86-
</svg>)
87-
case "available":
88-
return (<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
89-
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM11.7071 6.70711C12.0976 6.31658 12.0976 5.68342 11.7071 5.29289C11.3166 4.90237 10.6834 4.90237 10.2929 5.29289L7 8.58578L5.7071 7.29289C5.31658 6.90237 4.68342 6.90237 4.29289 7.29289C3.90237 7.68342 3.90237 8.31658 4.29289 8.70711L6.29289 10.7071C6.68342 11.0976 7.31658 11.0976 7.7071 10.7071L11.7071 6.70711Z" fill="#84CC16"/>
90-
</svg>);
91-
case "building":
92-
return (<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
93-
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.99609 16C13.4144 16 16.9961 12.4183 16.9961 8C16.9961 3.58172 13.4144 0 8.99609 0C4.57781 0 0.996094 3.58172 0.996094 8C0.996094 12.4183 4.57781 16 8.99609 16ZM9.99609 4C9.99609 3.44772 9.54837 3 8.99609 3C8.4438 3 7.99609 3.44772 7.99609 4V8C7.99609 8.26522 8.10144 8.51957 8.28898 8.70711L11.1174 11.5355C11.5079 11.9261 12.1411 11.9261 12.5316 11.5355C12.9221 11.145 12.9221 10.5118 12.5316 10.1213L9.99609 7.58579V4Z" fill="#60A5FA"/>
94-
</svg>);
95-
case "queued":
96-
return (<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
97-
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8ZM5 6C5 5.44772 5.44772 5 6 5C6.55228 5 7 5.44772 7 6V10C7 10.5523 6.55228 11 6 11C5.44772 11 5 10.5523 5 10V6ZM10 5C9.44771 5 9 5.44772 9 6V10C9 10.5523 9.44771 11 10 11C10.5523 11 11 10.5523 11 10V6C11 5.44772 10.5523 5 10 5Z" fill="#FBBF24"/>
98-
</svg>);
99-
default:
100-
break;
101-
}
102-
}
103-
10467
const statusFilterEntries = () => {
10568
const entries: DropDownEntry[] = [];
10669
entries.push({
@@ -158,8 +121,8 @@ export default function () {
158121
<ItemField className="flex items-center">
159122
<div>
160123
<div className="text-base text-gray-900 dark:text-gray-50 font-medium uppercase mb-1">
161-
<div className="inline-block align-text-bottom mr-2 w-4 h-4">{prebuildStatusIcon(p)}</div>
162-
{prebuildStatusLabel(p)}
124+
<div className="inline-block align-text-bottom mr-2 w-4 h-4">{prebuildStatusIcon(p.status)}</div>
125+
{prebuildStatusLabel(p.status)}
163126
</div>
164127
<p>{p.startedByAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={p.startedByAvatar || ''} alt={p.startedBy} />}Triggered {moment(p.startedAt).fromNow()}</p>
165128
</div>
@@ -171,16 +134,53 @@ export default function () {
171134
</div>
172135
</ItemField>
173136
<ItemField className="flex items-center">
174-
<div className="flex space-x-2 text-gray-400">
175-
<span className="font-medium">{p.branch}</span>
176-
<span>#{p.branchPrebuildNumber}</span>
177-
</div>
178-
<span className="flex-grow" />
179-
<ItemFieldContextMenu menuEntries={prebuildContextMenu(p)} />
180-
</ItemField>
137+
<div className="flex space-x-2 text-gray-400">
138+
<span className="font-medium">{p.branch}</span>
139+
<span>#{p.branchPrebuildNumber}</span>
140+
</div>
141+
<span className="flex-grow" />
142+
<ItemFieldContextMenu menuEntries={prebuildContextMenu(p)} />
143+
</ItemField>
181144
</Item>)}
182145
</ItemsList>
183146
</div>
184147

185148
</>;
149+
}
150+
151+
export function prebuildStatusLabel(status: PrebuiltWorkspaceState) {
152+
switch (status) {
153+
case "aborted":
154+
return (<span className="font-medium text-red-500 uppercase">failed</span>);
155+
case "available":
156+
return (<span className="font-medium text-green-500 uppercase">ready</span>);
157+
case "building":
158+
return (<span className="font-medium text-blue-500 uppercase">running</span>);
159+
case "queued":
160+
return (<span className="font-medium text-orange-500 uppercase">pending</span>);
161+
default:
162+
break;
163+
}
164+
}
165+
export function prebuildStatusIcon(status: PrebuiltWorkspaceState) {
166+
switch (status) {
167+
case "aborted":
168+
return (<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
169+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM6.70711 5.29289C6.31658 4.90237 5.68342 4.90237 5.29289 5.29289C4.90237 5.68342 4.90237 6.31658 5.29289 6.70711L6.58579 8L5.29289 9.29289C4.90237 9.68342 4.90237 10.3166 5.29289 10.7071C5.68342 11.0976 6.31658 11.0976 6.70711 10.7071L8 9.41421L9.29289 10.7071C9.68342 11.0976 10.3166 11.0976 10.7071 10.7071C11.0976 10.3166 11.0976 9.68342 10.7071 9.29289L9.41421 8L10.7071 6.70711C11.0976 6.31658 11.0976 5.68342 10.7071 5.29289C10.3166 4.90237 9.68342 4.90237 9.29289 5.29289L8 6.58579L6.70711 5.29289Z" fill="#F87171" />
170+
</svg>)
171+
case "available":
172+
return (<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
173+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM11.7071 6.70711C12.0976 6.31658 12.0976 5.68342 11.7071 5.29289C11.3166 4.90237 10.6834 4.90237 10.2929 5.29289L7 8.58578L5.7071 7.29289C5.31658 6.90237 4.68342 6.90237 4.29289 7.29289C3.90237 7.68342 3.90237 8.31658 4.29289 8.70711L6.29289 10.7071C6.68342 11.0976 7.31658 11.0976 7.7071 10.7071L11.7071 6.70711Z" fill="#84CC16" />
174+
</svg>);
175+
case "building":
176+
return (<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
177+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.99609 16C13.4144 16 16.9961 12.4183 16.9961 8C16.9961 3.58172 13.4144 0 8.99609 0C4.57781 0 0.996094 3.58172 0.996094 8C0.996094 12.4183 4.57781 16 8.99609 16ZM9.99609 4C9.99609 3.44772 9.54837 3 8.99609 3C8.4438 3 7.99609 3.44772 7.99609 4V8C7.99609 8.26522 8.10144 8.51957 8.28898 8.70711L11.1174 11.5355C11.5079 11.9261 12.1411 11.9261 12.5316 11.5355C12.9221 11.145 12.9221 10.5118 12.5316 10.1213L9.99609 7.58579V4Z" fill="#60A5FA" />
178+
</svg>);
179+
case "queued":
180+
return (<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
181+
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8ZM5 6C5 5.44772 5.44772 5 6 5C6.55228 5 7 5.44772 7 6V10C7 10.5523 6.55228 11 6 11C5.44772 11 5 10.5523 5 10V6ZM10 5C9.44771 5 9 5.44772 9 6V10C9 10.5523 9.44771 11 10 11C10.5523 11 11 10.5523 11 10V6C11 5.44772 10.5523 5 10 5Z" fill="#FBBF24" />
182+
</svg>);
183+
default:
184+
break;
185+
}
186186
}

‎components/dashboard/src/projects/Project.tsx

+92-39
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
*/
66

77
import moment from "moment";
8-
import { PrebuildInfo, ProjectInfo } from "@gitpod/gitpod-protocol";
8+
import { PrebuiltWorkspaceState, Project, ProjectInfo } from "@gitpod/gitpod-protocol";
99
import { useContext, useEffect, useState } from "react";
1010
import { useLocation, useRouteMatch } from "react-router";
1111
import Header from "../components/Header";
12-
import DropDown from "../components/DropDown";
13-
import { ItemsList, Item, ItemField, ItemFieldContextMenu, ItemFieldIcon } from "../components/ItemsList";
12+
import DropDown, { DropDownEntry } from "../components/DropDown";
13+
import { ItemsList, Item, ItemField, ItemFieldContextMenu } from "../components/ItemsList";
1414
import { getGitpodService } from "../service/service";
1515
import { TeamsContext, getCurrentTeam } from "../teams/teams-context";
16+
import { prebuildStatusIcon, prebuildStatusLabel } from "./Prebuilds";
17+
import { ContextMenuEntry } from "../components/ContextMenu";
1618

1719
export default function () {
1820
const { teams } = useContext(TeamsContext);
@@ -23,7 +25,10 @@ export default function () {
2325

2426
const [project, setProject] = useState<ProjectInfo | undefined>();
2527

26-
const [prebuilds, setPrebuilds] = useState<PrebuildInfo[]>([]);
28+
const [projectDetails, setProjectDetails] = useState<Project.Overview | undefined>(undefined);
29+
30+
const [searchFilter, setSearchFilter] = useState<string | undefined>();
31+
const [statusFilter, setStatusFilter] = useState<PrebuiltWorkspaceState | undefined>();
2732

2833
useEffect(() => {
2934
if (!team) {
@@ -35,7 +40,10 @@ export default function () {
3540
const project = projects.find(p => p.name === projectName);
3641
if (project) {
3742
setProject(project);
38-
setPrebuilds(await getGitpodService().server.getPrebuilds(team.id, project.id));
43+
setProjectDetails(await getGitpodService().server.getProjectOverview(team.id, project.name));
44+
if (projectDetails) {
45+
Object.keys(projectDetails)
46+
}
3947
}
4048
})();
4149
}, [team]);
@@ -44,6 +52,48 @@ export default function () {
4452
return cloneURL.replace("https://", "");
4553
}
4654

55+
const prebuildContextMenu = (branch: Project.BranchDetails) => {
56+
const entries: ContextMenuEntry[] = [];
57+
entries.push({
58+
title: "New Workspace",
59+
onClick: () => { }
60+
});
61+
entries.push({
62+
title: "Trigger Prebuild",
63+
onClick: () => { },
64+
separator: true
65+
});
66+
entries.push({
67+
title: "Cancel Prebuild",
68+
customFontStyle: 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300',
69+
onClick: () => { }
70+
})
71+
return entries;
72+
}
73+
74+
const statusFilterEntries = () => {
75+
const entries: DropDownEntry[] = [];
76+
entries.push({
77+
title: 'All',
78+
onClick: () => setStatusFilter(undefined)
79+
});
80+
entries.push({
81+
title: 'READY',
82+
onClick: () => setStatusFilter("available")
83+
});
84+
return entries;
85+
}
86+
87+
const filter = (branch: Project.BranchDetails) => {
88+
if (statusFilter && branch.lastPrebuild && statusFilter !== branch.lastPrebuild.status) {
89+
return false;
90+
}
91+
if (searchFilter && `${branch.changeTitle} ${branch.name}`.toLowerCase().includes(searchFilter.toLowerCase()) === false) {
92+
return false;
93+
}
94+
return true;
95+
}
96+
4797
return <>
4898
<Header title={project?.name || ""} subtitle={toRemoteURL(project?.cloneUrl || "")} />
4999
<div className="lg:px-28 px-10">
@@ -52,53 +102,56 @@ export default function () {
52102
<div className="py-4">
53103
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="16" height="16"><path fill="#A8A29E" d="M6 2a4 4 0 100 8 4 4 0 000-8zM0 6a6 6 0 1110.89 3.477l4.817 4.816a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 010 6z" /></svg>
54104
</div>
55-
<input type="search" placeholder="Search" onChange={() => { /* TODO */ }} />
105+
<input type="search" placeholder="Search Active Brancehs" onChange={e => setSearchFilter(e.target.value)} />
56106
</div>
57107
<div className="flex-1" />
58108
<div className="py-3 pl-3">
59-
<DropDown prefix="Status: " contextMenuWidth="w-32" activeEntry={'All'} entries={[{
60-
title: 'All',
61-
onClick: () => { /* TODO */ }
62-
}]} />
109+
<DropDown prefix="Prebuild Status: " contextMenuWidth="w-32" entries={statusFilterEntries()} />
63110
</div>
64111
</div>
65112
<ItemsList className="mt-2">
66-
<Item header={true} className="grid grid-cols-4">
67-
<ItemField className="w-5">
68-
</ItemField>
113+
<Item header={true} className="grid grid-cols-3">
69114
<ItemField>
70-
<span>Context</span>
115+
<span>Branch</span>
71116
</ItemField>
72117
<ItemField>
73-
<span>Started</span>
118+
<span>Commit</span>
74119
</ItemField>
75-
<ItemField className="flex items-center">
76-
<span className="flex-grow">Status</span>
120+
<ItemField>
121+
<span>Prebuild</span>
77122
<ItemFieldContextMenu />
78123
</ItemField>
79124
</Item>
80-
{prebuilds.map(p => <Item className="grid grid-cols-4">
81-
<ItemFieldIcon className="w-5">
82-
<div className={"rounded-full w-3 h-3 text-sm align-middle m-auto " + (true ? "bg-green-500" : "bg-gray-400")}>
83-
&nbsp;
84-
</div>
85-
</ItemFieldIcon>
86-
<ItemField className="flex items-center">
87-
<div>
88-
<div className="text-base text-gray-900 dark:text-gray-50 font-medium">{p.branch}</div>
89-
<p>{p.cloneUrl}</p>
90-
</div>
91-
</ItemField>
92-
<ItemField>
93-
<div>
94-
<div className="text-base text-gray-900 dark:text-gray-50 font-medium">{moment(p.startedAt).fromNow()}</div>
95-
<p>{p.startedBy}</p>
96-
</div>
97-
</ItemField>
98-
<ItemField className="flex items-center">
99-
<span className="text-gray-400 flex-grow capitalize">{p.status}</span>
100-
</ItemField>
101-
</Item>)}
125+
{projectDetails && Object.keys(projectDetails).map(branchName => {
126+
const branch = projectDetails[branchName];
127+
if (!filter(branch)) {
128+
return undefined;
129+
}
130+
return <Item className="grid grid-cols-3">
131+
<ItemField className="flex items-center">
132+
<div>
133+
<div className="text-base text-gray-900 dark:text-gray-50 font-medium mb-1">
134+
{branchName}
135+
</div>
136+
<p>Updated _ minutes ago</p>
137+
</div>
138+
</ItemField>
139+
<ItemField className="flex items-center">
140+
<div>
141+
<div className="text-base text-gray-500 dark:text-gray-50 font-medium mb-1">{branch.changeTitle}</div>
142+
<p>{branch.changeAuthorAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={branch.changeAuthorAvatar || ''} alt={branch.changeAuthor} />}Authored {moment(branch.changeDate).fromNow()} · {branch.changeHash}</p>
143+
</div>
144+
</ItemField>
145+
<ItemField className="flex items-center">
146+
<div className="text-base text-gray-900 dark:text-gray-50 font-medium uppercase mb-1">
147+
<div className="inline-block align-text-bottom mr-2 w-4 h-4">{branch.lastPrebuild?.status && prebuildStatusIcon(branch.lastPrebuild.status)}</div>
148+
{branch.lastPrebuild?.status && prebuildStatusLabel(branch.lastPrebuild.status)}
149+
</div>
150+
<span className="flex-grow" />
151+
<ItemFieldContextMenu menuEntries={prebuildContextMenu(branch)} />
152+
</ItemField>
153+
</Item>}
154+
)}
102155
</ItemsList>
103156
</div>
104157

0 commit comments

Comments
 (0)
Please sign in to comment.