Skip to content

Commit 550bb79

Browse files
committed
[dashboard] Standardize PendingChangesDropdown component in workspaces list (replaces Modal) and stopped page (unchanged)
1 parent 477d7f8 commit 550bb79

File tree

3 files changed

+50
-105
lines changed

3 files changed

+50
-105
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { WorkspaceInstance } from "@gitpod/gitpod-protocol";
8+
import ContextMenu, { ContextMenuEntry } from "./ContextMenu";
9+
import CaretDown from "../icons/CaretDown.svg";
10+
11+
export default function PendingChangesDropdown(props: { workspaceInstance?: WorkspaceInstance }) {
12+
const repo = props.workspaceInstance?.status?.repo;
13+
const headingStyle = 'text-gray-500 text-left';
14+
const itemStyle = 'text-gray-400 text-left -mt-5';
15+
const menuEntries: ContextMenuEntry[] = [];
16+
let totalChanges = 0;
17+
if (repo) {
18+
if ((repo.totalUntrackedFiles || 0) > 0) {
19+
totalChanges += repo.totalUntrackedFiles || 0;
20+
menuEntries.push({ title: 'Untracked Files', customFontStyle: headingStyle });
21+
(repo.untrackedFiles || []).forEach(item => menuEntries.push({ title: item, customFontStyle: itemStyle }));
22+
}
23+
if ((repo.totalUncommitedFiles || 0) > 0) {
24+
totalChanges += repo.totalUncommitedFiles || 0;
25+
menuEntries.push({ title: 'Uncommitted Files', customFontStyle: headingStyle });
26+
(repo.uncommitedFiles || []).forEach(item => menuEntries.push({ title: item, customFontStyle: itemStyle }));
27+
}
28+
if ((repo.totalUnpushedCommits || 0) > 0) {
29+
totalChanges += repo.totalUnpushedCommits || 0;
30+
menuEntries.push({ title: 'Unpushed Commits', customFontStyle: headingStyle });
31+
(repo.unpushedCommits || []).forEach(item => menuEntries.push({ title: item, customFontStyle: itemStyle }));
32+
}
33+
}
34+
if (totalChanges <= 0) {
35+
return <p>No Changes</p>;
36+
}
37+
return <ContextMenu menuEntries={menuEntries} width="w-64 max-h-48 overflow-scroll mx-auto left-0 right-0">
38+
<p className="flex justify-center text-gitpod-red">
39+
<span>{totalChanges} Change{totalChanges === 1 ? '' : 's'}</span>
40+
<img className="m-2" src={CaretDown}/>
41+
</p>
42+
</ContextMenu>;
43+
}

components/dashboard/src/start/StartWorkspace.tsx

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ import React, { useEffect, Suspense } from "react";
99
import { DisposableCollection, WorkspaceInstance, WorkspaceImageBuild, Workspace, WithPrebuild } from "@gitpod/gitpod-protocol";
1010
import { HeadlessLogEvent } from "@gitpod/gitpod-protocol/lib/headless-workspace-log";
1111
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
12-
import ContextMenu, { ContextMenuEntry } from "../components/ContextMenu";
13-
import CaretDown from "../icons/CaretDown.svg";
12+
import PendingChangesDropdown from "../components/PendingChangesDropdown";
1413
import { getGitpodService, gitpodHostUrl } from "../service/service";
1514
import { StartPage, StartPhase, StartWorkspaceError } from "./StartPage";
1615

@@ -274,7 +273,7 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
274273
title = 'Timed Out';
275274
}
276275
statusMessage = <div>
277-
<div className="flex space-x-3 items-center text-left rounded-xl m-auto px-4 h-16 w-72 mt-4 bg-gray-100 dark:bg-gray-800">
276+
<div className="flex space-x-3 items-center text-left rounded-xl m-auto px-4 h-16 w-72 mt-4 mb-2 bg-gray-100 dark:bg-gray-800">
278277
<div className="rounded-full w-3 h-3 text-sm bg-gray-300">&nbsp;</div>
279278
<div>
280279
<p className="text-gray-700 dark:text-gray-200 font-semibold">{this.state.workspaceInstance.workspaceId}</p>
@@ -296,40 +295,6 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
296295
}
297296
}
298297

299-
function PendingChangesDropdown(props: { workspaceInstance?: WorkspaceInstance }) {
300-
const repo = props.workspaceInstance?.status?.repo;
301-
const headingStyle = 'text-gray-500 text-left';
302-
const itemStyle = 'text-gray-400 text-left -mt-5';
303-
const menuEntries: ContextMenuEntry[] = [];
304-
let totalChanges = 0;
305-
if (repo) {
306-
if ((repo.totalUntrackedFiles || 0) > 0) {
307-
totalChanges += repo.totalUntrackedFiles || 0;
308-
menuEntries.push({ title: 'Untracked Files', customFontStyle: headingStyle });
309-
(repo.untrackedFiles || []).forEach(item => menuEntries.push({ title: item, customFontStyle: itemStyle }));
310-
}
311-
if ((repo.totalUncommitedFiles || 0) > 0) {
312-
totalChanges += repo.totalUncommitedFiles || 0;
313-
menuEntries.push({ title: 'Uncommitted Files', customFontStyle: headingStyle });
314-
(repo.uncommitedFiles || []).forEach(item => menuEntries.push({ title: item, customFontStyle: itemStyle }));
315-
}
316-
if ((repo.totalUnpushedCommits || 0) > 0) {
317-
totalChanges += repo.totalUnpushedCommits || 0;
318-
menuEntries.push({ title: 'Unpushed Commits', customFontStyle: headingStyle });
319-
(repo.unpushedCommits || []).forEach(item => menuEntries.push({ title: item, customFontStyle: itemStyle }));
320-
}
321-
}
322-
if (totalChanges <= 0) {
323-
return <p className="mt-2">No Changes</p>;
324-
}
325-
return <ContextMenu menuEntries={menuEntries} width="w-64 max-h-48 overflow-scroll mx-auto left-0 right-0">
326-
<p className="mt-2 flex justify-center text-gitpod-red">
327-
<span>{totalChanges} Change{totalChanges === 1 ? '' : 's'}</span>
328-
<img className="m-2" src={CaretDown}/>
329-
</p>
330-
</ContextMenu>;
331-
}
332-
333298
interface ImageBuildViewProps {
334299
workspaceId: string;
335300
onStartWithDefaultImage: () => void;

components/dashboard/src/workspaces/WorkspaceEntry.tsx

Lines changed: 5 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import { GitpodHostUrl } from '@gitpod/gitpod-protocol/lib/util/gitpod-host-url'
99
import ContextMenu, { ContextMenuEntry } from '../components/ContextMenu';
1010
import moment from 'moment';
1111
import Modal from '../components/Modal';
12-
import { MouseEvent, useState } from 'react';
12+
import { useState } from 'react';
1313
import { WorkspaceModel } from './workspace-model';
14+
import PendingChangesDropdown from '../components/PendingChangesDropdown';
1415
import Tooltip from '../components/Tooltip';
1516

1617
function getLabel(state: WorkspaceInstancePhase) {
@@ -26,16 +27,7 @@ interface Props {
2627

2728
export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
2829
const [isModalVisible, setModalVisible] = useState(false);
29-
const [isChangesModalVisible, setChangesModalVisible] = useState(false);
3030
const state: WorkspaceInstancePhase = desc.latestInstance?.status?.phase || 'stopped';
31-
const pendingChanges = getPendingChanges(desc.latestInstance);
32-
const numberOfChanges = pendingChanges.reduceRight((i, c) => i + c.items.length, 0)
33-
let changesLabel = 'No Changes';
34-
if (numberOfChanges === 1) {
35-
changesLabel = '1 Change';
36-
} else if (numberOfChanges > 1) {
37-
changesLabel = numberOfChanges + ' Changes';
38-
}
3931
const currentBranch = desc.latestInstance?.status.repo?.branch || Workspace.getBranchName(desc.workspace) || '<unknown>';
4032
const ws = desc.workspace;
4133
const startUrl = new GitpodHostUrl(window.location.href).with({
@@ -88,10 +80,6 @@ export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
8880
);
8981
}
9082
const project = getProject(ws);
91-
const showChanges = (event: MouseEvent) => {
92-
event.preventDefault();
93-
setChangesModalVisible(true);
94-
}
9583
return <div>
9684
<div className="rounded-xl whitespace-nowrap flex space-x-2 py-6 px-6 w-full justify-between hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-gitpod-kumquat-light group">
9785
<div className="pr-3 self-center">
@@ -109,16 +97,9 @@ export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
10997
</a>
11098
</div>
11199
</div>
112-
<div className="flex w-2/12 truncate" onClick={numberOfChanges > 0 ? showChanges : undefined}>
113-
<div className="flex flex-col">
114-
<div className="text-gray-500 truncate">{currentBranch}</div>
115-
{
116-
numberOfChanges > 0 ?
117-
<div className={"text-sm text-red-600 truncate cursor-pointer bg-red-50 group-hover:bg-red-100 hover:text-red-800 px-1.5 py-0.5 relative rounded-md -top-0.5"} onClick={showChanges}>{changesLabel}</div>
118-
:
119-
<div className="text-sm text-gray-400 truncate ">No Changes</div>
120-
}
121-
</div>
100+
<div className="flex flex-col items-start w-2/12">
101+
<div className="text-gray-500 truncate">{currentBranch}</div>
102+
<PendingChangesDropdown workspaceInstance={desc.latestInstance} />
122103
</div>
123104
<div className="flex w-2/12 self-center">
124105
<Tooltip content={`Created ${moment(desc.workspace.creationTime).fromNow()}`}>
@@ -131,9 +112,6 @@ export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
131112
</ContextMenu>
132113
</div>
133114
</div>
134-
<Modal visible={isChangesModalVisible} onClose={() => setChangesModalVisible(false)}>
135-
{getChangesPopup(pendingChanges)}
136-
</Modal>
137115
{isModalVisible && <Modal visible={isModalVisible} onClose={() => setModalVisible(false)}>
138116
<div>
139117
<h3 className="pb-2">Delete Workspace</h3>
@@ -155,36 +133,6 @@ export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
155133
</div>;
156134
}
157135

158-
export interface PendingChanges {
159-
message: string, items: string[]
160-
}
161-
162-
export function getPendingChanges(wsi?: WorkspaceInstance): PendingChanges[] {
163-
const pendingChanges: { message: string, items: string[] }[] = [];
164-
const repo = wsi?.status.repo;
165-
if (repo) {
166-
if (repo.totalUncommitedFiles || 0 > 0) {
167-
pendingChanges.push({
168-
message: repo.totalUncommitedFiles === 1 ? 'an uncommited file' : `${repo.totalUncommitedFiles} uncommited files`,
169-
items: repo.uncommitedFiles || []
170-
});
171-
}
172-
if (repo.totalUntrackedFiles || 0 > 0) {
173-
pendingChanges.push({
174-
message: repo.totalUntrackedFiles === 1 ? 'an untracked file' : `${repo.totalUntrackedFiles} untracked files`,
175-
items: repo.untrackedFiles || []
176-
});
177-
}
178-
if (repo.totalUnpushedCommits || 0 > 0) {
179-
pendingChanges.push({
180-
message: repo.totalUnpushedCommits === 1 ? 'an unpushed commit' : `${repo.totalUnpushedCommits} unpushed commits`,
181-
items: repo.unpushedCommits || []
182-
});
183-
}
184-
}
185-
return pendingChanges;
186-
}
187-
188136
export function getProject(ws: Workspace) {
189137
if (CommitContext.is(ws.context)) {
190138
return `${ws.context.repository.host}/${ws.context.repository.owner}/${ws.context.repository.name}`;
@@ -193,17 +141,6 @@ export function getProject(ws: Workspace) {
193141
}
194142
}
195143

196-
export function getChangesPopup(changes: PendingChanges[]) {
197-
return <div className="flex flex-col space-y-4 w-96">
198-
{changes.map(c => {
199-
return <div className="">
200-
<div className="text-gray-500">{c.message}</div>
201-
{c.items.map(i => <div className="text-gray-400 text-xs">{i}</div>)}
202-
</div>;
203-
})}
204-
</div>
205-
}
206-
207144
export function WorkspaceStatusIndicator({instance}: {instance?: WorkspaceInstance}) {
208145
const state: WorkspaceInstancePhase = instance?.status?.phase || 'stopped';
209146
let stateClassName = 'rounded-full w-3 h-3 text-sm align-middle';

0 commit comments

Comments
 (0)