Skip to content

Commit baeeaeb

Browse files
author
Laurie T. Malau
committed
[dashboard] Allow workspace renaming
Fixes #3946
1 parent 82df304 commit baeeaeb

File tree

1 file changed

+55
-9
lines changed

1 file changed

+55
-9
lines changed

components/dashboard/src/workspaces/WorkspaceEntry.tsx

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
import { CommitContext, Workspace, WorkspaceInfo, WorkspaceInstance, WorkspaceInstanceConditions, WorkspaceInstancePhase } from '@gitpod/gitpod-protocol';
88
import { GitpodHostUrl } from '@gitpod/gitpod-protocol/lib/util/gitpod-host-url';
99
import moment from 'moment';
10-
import React, { useState } from 'react';
10+
import React, { useRef, useState } from 'react';
1111
import ConfirmationModal from '../components/ConfirmationModal';
12+
import Modal from '../components/Modal';
1213
import { ContextMenuEntry } from '../components/ContextMenu';
1314
import { Item, ItemField, ItemFieldContextMenu, ItemFieldIcon } from '../components/ItemsList';
1415
import PendingChangesDropdown from '../components/PendingChangesDropdown';
1516
import Tooltip from '../components/Tooltip';
1617
import { WorkspaceModel } from './workspace-model';
18+
import { getGitpodService } from '../service/service';
1719

1820
function getLabel(state: WorkspaceInstancePhase, conditions?: WorkspaceInstanceConditions) {
1921
if (conditions?.failed) {
@@ -30,10 +32,14 @@ interface Props {
3032
}
3133

3234
export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
33-
const [isModalVisible, setModalVisible] = useState(false);
35+
const [isDeleteModalVisible, setDeleteModalVisible] = useState(false);
36+
const [isRenameModalVisible, setRenameModalVisible] = useState(false);
37+
const renameInputRef = useRef<HTMLInputElement>(null);
3438
const state: WorkspaceInstancePhase = desc.latestInstance?.status?.phase || 'stopped';
3539
const currentBranch = desc.latestInstance?.status.repo?.branch || Workspace.getBranchName(desc.workspace) || '<unknown>';
3640
const ws = desc.workspace;
41+
const [workspaceDescription, setWsDescription] = useState(ws.description);
42+
3743
const startUrl = new GitpodHostUrl(window.location.href).with({
3844
pathname: '/start/',
3945
hash: '#' + ws.id
@@ -45,7 +51,16 @@ export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
4551
{
4652
title: 'Open',
4753
href: startUrl.toString()
48-
}];
54+
},
55+
{
56+
title: 'Rename',
57+
href: "",
58+
onClick: () => {
59+
setRenameModalVisible(true);
60+
}
61+
},
62+
63+
];
4964
if (state === 'running') {
5065
menuEntries.push({
5166
title: 'Stop',
@@ -78,13 +93,30 @@ export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
7893
title: 'Delete',
7994
customFontStyle: 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300',
8095
onClick: () => {
81-
setModalVisible(true);
96+
setDeleteModalVisible(true);
8297
}
8398
}
8499
);
85100
}
86101
const project = getProject(ws);
87102

103+
const updateWorkspaceDescription = async () => {
104+
// Need this check because ref is called twice
105+
// https://reactjs.org/docs/refs-and-the-dom.html#caveats-with-callback-refs
106+
if (!renameInputRef.current) {
107+
return;
108+
}
109+
try {
110+
setWsDescription(renameInputRef.current!.value);
111+
await getGitpodService().server.setWorkspaceDescription(ws.id, renameInputRef.current!.value);
112+
} catch (error) {
113+
// A simple alert for now because there is an open issue to introduce Toast
114+
console.error(error);
115+
window.alert("Something went wrong. Please try renaming again.");
116+
}
117+
setRenameModalVisible(false);
118+
}
119+
88120
return <Item className="whitespace-nowrap py-6 px-6">
89121
<ItemFieldIcon>
90122
<WorkspaceStatusIndicator instance={desc?.latestInstance} />
@@ -96,7 +128,7 @@ export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
96128
</Tooltip>
97129
</ItemField>
98130
<ItemField className="w-4/12 flex flex-col">
99-
<div className="text-gray-500 dark:text-gray-400 overflow-ellipsis truncate">{ws.description}</div>
131+
<div className="text-gray-500 dark:text-gray-400 overflow-ellipsis truncate">{workspaceDescription}</div>
100132
<a href={ws.contextURL}>
101133
<div className="text-sm text-gray-400 dark:text-gray-500 overflow-ellipsis truncate hover:text-blue-600 dark:hover:text-blue-400">{ws.contextURL}</div>
102134
</a>
@@ -111,18 +143,32 @@ export function WorkspaceEntry({ desc, model, isAdmin, stopWorkspace }: Props) {
111143
</Tooltip>
112144
</ItemField>
113145
<ItemFieldContextMenu menuEntries={menuEntries} />
114-
{isModalVisible && <ConfirmationModal
146+
{isDeleteModalVisible && <ConfirmationModal
115147
title="Delete Workspace"
116148
areYouSureText="Are you sure you want to delete this workspace?"
117149
children={{
118150
name: ws.id,
119151
description: ws.description,
120152
}}
121153
buttonText="Delete Workspace"
122-
visible={isModalVisible}
123-
onClose={() => setModalVisible(false)}
154+
visible={isDeleteModalVisible}
155+
onClose={() => setDeleteModalVisible(false)}
124156
onConfirm={() => model.deleteWorkspace(ws.id)}
125157
/>}
158+
<Modal visible={isRenameModalVisible} onClose={() => setRenameModalVisible(false)} onEnter={() => { updateWorkspaceDescription(); return true }}>
159+
<h3 className="mb-4">Rename Workspace Description</h3>
160+
<div className="border-t border-b border-gray-200 dark:border-gray-800 -mx-6 px-6 py-4 space-y-2">
161+
<input className="w-full" type="text" defaultValue={workspaceDescription} ref={renameInputRef} />
162+
<div className="mt-1">
163+
<p className="text-gray-500">You can use a friendlier name for this workspace description.</p>
164+
<p className="text-gray-500"> Workspace URLs and endpoints will remain the same.</p>
165+
</div>
166+
</div>
167+
<div className="flex justify-end mt-6">
168+
<button className="secondary" onClick={() => setRenameModalVisible(false)}>Cancel</button>
169+
<button className="ml-2" type="submit" onClick={updateWorkspaceDescription}>Rename</button>
170+
</div>
171+
</Modal>
126172
</Item>;
127173
}
128174

@@ -134,7 +180,7 @@ export function getProject(ws: Workspace) {
134180
}
135181
}
136182

137-
export function WorkspaceStatusIndicator({instance}: {instance?: WorkspaceInstance}) {
183+
export function WorkspaceStatusIndicator({ instance }: { instance?: WorkspaceInstance }) {
138184
const state: WorkspaceInstancePhase = instance?.status?.phase || 'stopped';
139185
const conditions = instance?.status?.conditions;
140186
let stateClassName = 'rounded-full w-3 h-3 text-sm align-middle';

0 commit comments

Comments
 (0)