Skip to content

Commit df0eda6

Browse files
jankeromnesMatthewFagan
authored and
MatthewFagan
committed
[dashboard] Ask user for confirmation before deleting an environment variable + validate duplicate name and scope
Fixes gitpod-io#3604 Fixes gitpod-io#3942
1 parent a74ef89 commit df0eda6

File tree

1 file changed

+58
-16
lines changed

1 file changed

+58
-16
lines changed

components/dashboard/src/settings/EnvironmentVariables.tsx

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,38 @@ function AddEnvVarModal(p: EnvVarModalProps) {
7979
</Modal>
8080
}
8181

82+
function DeleteEnvVarModal(p: { variable: UserEnvVarValue, deleteVariable: () => void, onClose: () => void }) {
83+
return <Modal visible={true} onClose={p.onClose}>
84+
<h3 className="mb-4">Delete Variable?</h3>
85+
<div className="border-t border-b border-gray-200 dark:border-gray-800 -mx-6 px-6 py-4 flex flex-col">
86+
<div className="grid grid-cols-2 gap-4 px-3 text-sm text-gray-400">
87+
<span className="truncate">Name</span>
88+
<span className="truncate">Scope</span>
89+
</div>
90+
<div className="grid grid-cols-2 gap-4 p-3 mt-3 text-gray-400 bg-gray-100 dark:bg-gray-800 rounded-xl">
91+
<span className="truncate text-gray-900 dark:text-gray-50">{p.variable.name}</span>
92+
<span className="truncate text-sm">{p.variable.repositoryPattern}</span>
93+
</div>
94+
</div>
95+
<div className="flex justify-end mt-6">
96+
<button className="secondary" onClick={p.onClose}>Cancel</button>
97+
<button className="ml-2 danger" onClick={() => { p.deleteVariable(); p.onClose(); }} >Delete Variable</button>
98+
</div>
99+
</Modal>;
100+
}
101+
102+
function sortEnvVars(a: UserEnvVarValue, b: UserEnvVarValue) {
103+
if (a.name === b.name) {
104+
return a.repositoryPattern > b.repositoryPattern ? 1 : -1;
105+
}
106+
return a.name > b.name ? 1 : -1;
107+
}
108+
82109
export default function EnvVars() {
83110
const [envVars, setEnvVars] = useState([] as UserEnvVarValue[]);
84111
const [currentEnvVar, setCurrentEnvVar] = useState({ name: '', value: '', repositoryPattern: '' } as UserEnvVarValue);
85112
const [isAddEnvVarModalVisible, setAddEnvVarModalVisible] = useState(false);
113+
const [isDeleteEnvVarModalVisible, setDeleteEnvVarModalVisible] = useState(false);
86114
const update = async () => {
87115
await getGitpodService().server.getEnvVars().then(r => setEnvVars(r));
88116
}
@@ -95,19 +123,27 @@ export default function EnvVars() {
95123
const add = () => {
96124
setCurrentEnvVar({ name: '', value: '', repositoryPattern: '' });
97125
setAddEnvVarModalVisible(true);
126+
setDeleteEnvVarModalVisible(false);
98127
}
99128

100-
const edit = (ev: UserEnvVarValue) => {
101-
setCurrentEnvVar(ev);
129+
const edit = (variable: UserEnvVarValue) => {
130+
setCurrentEnvVar(variable);
102131
setAddEnvVarModalVisible(true);
132+
setDeleteEnvVarModalVisible(false);
133+
}
134+
135+
const confirmDeleteVariable = (variable: UserEnvVarValue) => {
136+
setCurrentEnvVar(variable);
137+
setAddEnvVarModalVisible(false);
138+
setDeleteEnvVarModalVisible(true);
103139
}
104140

105141
const save = async (variable: UserEnvVarValue) => {
106142
await getGitpodService().server.setEnvVar(variable);
107143
await update();
108144
};
109145

110-
const deleteV = async (variable: UserEnvVarValue) => {
146+
const deleteVariable = async (variable: UserEnvVarValue) => {
111147
await getGitpodService().server.deleteEnvVar(variable);
112148
await update();
113149
};
@@ -138,26 +174,32 @@ export default function EnvVars() {
138174
}
139175
}
140176
}
177+
if (!variable.id && envVars.some(v => v.name === name && v.repositoryPattern === pattern)) {
178+
return 'A variable with this name and scope already exists';
179+
}
141180
return '';
142181
};
143182

144-
return <PageWithSubMenu subMenu={settingsMenu} title='Variables' subtitle='Configure environment variables for all workspaces.'>
145-
{isAddEnvVarModalVisible ? <AddEnvVarModal
183+
return <PageWithSubMenu subMenu={settingsMenu} title='Variables' subtitle='Configure environment variables for all workspaces.'>
184+
{isAddEnvVarModalVisible && <AddEnvVarModal
146185
save={save}
147186
envVar={currentEnvVar}
148187
validate={validate}
149-
onClose={() => setAddEnvVarModalVisible(false)} /> : null}
188+
onClose={() => setAddEnvVarModalVisible(false)} />}
189+
{isDeleteEnvVarModalVisible && <DeleteEnvVarModal
190+
variable={currentEnvVar}
191+
deleteVariable={() => deleteVariable(currentEnvVar)}
192+
onClose={() => setDeleteEnvVarModalVisible(false)} />}
150193
<div className="flex items-start sm:justify-between mb-2">
151194
<div>
152195
<h3>Environment Variables</h3>
153196
<h2 className="text-gray-500">Variables are used to store information like passwords.</h2>
154197
</div>
155198
{envVars.length !== 0
156-
?
157-
<div className="mt-3 flex mt-0">
158-
<button onClick={add} className="ml-2">New Variable</button>
159-
</div>
160-
: null}
199+
? <div className="mt-3 flex mt-0">
200+
<button onClick={add} className="ml-2">New Variable</button>
201+
</div>
202+
: null}
161203
</div>
162204
{envVars.length === 0
163205
? <div className="bg-gray-100 dark:bg-gray-800 rounded-xl w-full h-96">
@@ -176,22 +218,22 @@ export default function EnvVars() {
176218
</div>
177219
</div>
178220
<div className="flex flex-col">
179-
{envVars.map(ev => {
221+
{envVars.map(variable => {
180222
return <div className="rounded-xl whitespace-nowrap flex space-x-2 py-3 px-3 w-full justify-between hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-gitpod-kumquat-light transition ease-in-out group">
181-
<div className="w-5/12 m-auto">{ev.name}</div>
182-
<div className="w-5/12 m-auto text-sm text-gray-400">{ev.repositoryPattern}</div>
223+
<div className="w-5/12 m-auto">{variable.name}</div>
224+
<div className="w-5/12 m-auto text-sm text-gray-400">{variable.repositoryPattern}</div>
183225
<div className="w-2/12 flex justify-end">
184226
<div className="flex w-8 self-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded-md cursor-pointer opacity-0 group-hover:opacity-100">
185227
<ContextMenu menuEntries={[
186228
{
187229
title: 'Edit',
188-
onClick: () => edit(ev),
230+
onClick: () => edit(variable),
189231
separator: true
190232
},
191233
{
192234
title: 'Delete',
193235
customFontStyle: 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300',
194-
onClick: () => deleteV(ev)
236+
onClick: () => confirmDeleteVariable(variable)
195237
},
196238
]}>
197239
<svg className="w-8 h-8 p-1 text-gray-600 dark:text-gray-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Actions</title><g fill="currentColor" transform="rotate(90 12 12)"><circle cx="1" cy="1" r="2" transform="translate(5 11)"/><circle cx="1" cy="1" r="2" transform="translate(11 11)"/><circle cx="1" cy="1" r="2" transform="translate(17 11)"/></g></svg>

0 commit comments

Comments
 (0)