Skip to content

Commit 2e7cba9

Browse files
committed
[dashboard] Ask user for confirmation before deleting an environment variable + validate duplicate name and scope
Fixes #3604 Fixes #3942
1 parent f8b389a commit 2e7cba9

File tree

1 file changed

+51
-16
lines changed

1 file changed

+51
-16
lines changed

components/dashboard/src/settings/EnvironmentVariables.tsx

+51-16
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,26 @@ 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+
82102
function sortEnvVars(a: UserEnvVarValue, b: UserEnvVarValue) {
83103
if (a.name === b.name) {
84104
return a.repositoryPattern > b.repositoryPattern ? 1 : -1;
@@ -90,6 +110,7 @@ export default function EnvVars() {
90110
const [envVars, setEnvVars] = useState([] as UserEnvVarValue[]);
91111
const [currentEnvVar, setCurrentEnvVar] = useState({ name: '', value: '', repositoryPattern: '' } as UserEnvVarValue);
92112
const [isAddEnvVarModalVisible, setAddEnvVarModalVisible] = useState(false);
113+
const [isDeleteEnvVarModalVisible, setDeleteEnvVarModalVisible] = useState(false);
93114
const update = async () => {
94115
await getGitpodService().server.getAllEnvVars().then(r => setEnvVars(r.sort(sortEnvVars)));
95116
}
@@ -102,19 +123,27 @@ export default function EnvVars() {
102123
const add = () => {
103124
setCurrentEnvVar({ name: '', value: '', repositoryPattern: '' });
104125
setAddEnvVarModalVisible(true);
126+
setDeleteEnvVarModalVisible(false);
105127
}
106128

107-
const edit = (ev: UserEnvVarValue) => {
108-
setCurrentEnvVar(ev);
129+
const edit = (variable: UserEnvVarValue) => {
130+
setCurrentEnvVar(variable);
109131
setAddEnvVarModalVisible(true);
132+
setDeleteEnvVarModalVisible(false);
133+
}
134+
135+
const confirmDeleteVariable = (variable: UserEnvVarValue) => {
136+
setCurrentEnvVar(variable);
137+
setAddEnvVarModalVisible(false);
138+
setDeleteEnvVarModalVisible(true);
110139
}
111140

112141
const save = async (variable: UserEnvVarValue) => {
113142
await getGitpodService().server.setEnvVar(variable);
114143
await update();
115144
};
116145

117-
const deleteV = async (variable: UserEnvVarValue) => {
146+
const deleteVariable = async (variable: UserEnvVarValue) => {
118147
await getGitpodService().server.deleteEnvVar(variable);
119148
await update();
120149
};
@@ -145,26 +174,32 @@ export default function EnvVars() {
145174
}
146175
}
147176
}
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+
}
148180
return '';
149181
};
150182

151-
return <PageWithSubMenu subMenu={settingsMenu} title='Variables' subtitle='Configure environment variables for all workspaces.'>
152-
{isAddEnvVarModalVisible ? <AddEnvVarModal
183+
return <PageWithSubMenu subMenu={settingsMenu} title='Variables' subtitle='Configure environment variables for all workspaces.'>
184+
{isAddEnvVarModalVisible && <AddEnvVarModal
153185
save={save}
154186
envVar={currentEnvVar}
155187
validate={validate}
156-
onClose={() => setAddEnvVarModalVisible(false)} /> : null}
188+
onClose={() => setAddEnvVarModalVisible(false)} />}
189+
{isDeleteEnvVarModalVisible && <DeleteEnvVarModal
190+
variable={currentEnvVar}
191+
deleteVariable={() => deleteVariable(currentEnvVar)}
192+
onClose={() => setDeleteEnvVarModalVisible(false)} />}
157193
<div className="flex items-start sm:justify-between mb-2">
158194
<div>
159195
<h3>Environment Variables</h3>
160196
<h2 className="text-gray-500">Variables are used to store information like passwords.</h2>
161197
</div>
162198
{envVars.length !== 0
163-
?
164-
<div className="mt-3 flex mt-0">
165-
<button onClick={add} className="ml-2">New Variable</button>
166-
</div>
167-
: null}
199+
? <div className="mt-3 flex mt-0">
200+
<button onClick={add} className="ml-2">New Variable</button>
201+
</div>
202+
: null}
168203
</div>
169204
{envVars.length === 0
170205
? <div className="bg-gray-100 dark:bg-gray-800 rounded-xl w-full h-96">
@@ -183,22 +218,22 @@ export default function EnvVars() {
183218
</div>
184219
</div>
185220
<div className="flex flex-col">
186-
{envVars.map(ev => {
221+
{envVars.map(variable => {
187222
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">
188-
<div className="w-5/12 m-auto">{ev.name}</div>
189-
<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>
190225
<div className="w-2/12 flex justify-end">
191226
<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">
192227
<ContextMenu menuEntries={[
193228
{
194229
title: 'Edit',
195-
onClick: () => edit(ev),
230+
onClick: () => edit(variable),
196231
separator: true
197232
},
198233
{
199234
title: 'Delete',
200235
customFontStyle: 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300',
201-
onClick: () => deleteV(ev)
236+
onClick: () => confirmDeleteVariable(variable)
202237
},
203238
]}>
204239
<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)