Skip to content

Commit 07a3da0

Browse files
luannmoreiragustavosbarreto
authored andcommitted
feat(ui): delete account on cloud instance
This commit adds the hability to remove the users own account only on cloud instances
1 parent 2257ae0 commit 07a3da0

File tree

15 files changed

+313
-16
lines changed

15 files changed

+313
-16
lines changed

ui/src/components/Namespace/NamespaceInstructions.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<v-dialog v-model="showNoNamespace" :retain-focus="false" persistent max-width="650px">
2+
<v-dialog v-model="showNoNamespace" :retain-focus="false" max-width="650px">
33
<v-card
44
v-model="showNoNamespace"
55
class="bg-v-theme-surface"

ui/src/components/Setting/SettingDrawer.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const { lgAndUp } = useDisplay();
3333
const permanent = computed(() => lgAndUp.value);
3434
const showNavigationDrawer = ref(true);
3535
const store = useStore();
36-
const currentInANamespace = computed(() => localStorage.getItem("tenant") !== "");
36+
const namespacedInstance = computed(() => localStorage.getItem("tenant") !== "");
3737
const hasNamespace = computed(() => store.getters["namespaces/getNumberNamespaces"] !== 0);
3838
3939
const items = computed(() => [
@@ -44,7 +44,7 @@ const items = computed(() => [
4444
{
4545
title: "Namespace",
4646
path: "/settings/namespace",
47-
hidden: !currentInANamespace.value,
47+
hidden: !namespacedInstance.value,
4848
},
4949
{
5050
title: "Private Keys",
@@ -53,6 +53,7 @@ const items = computed(() => [
5353
{
5454
title: "Tags",
5555
path: "/settings/tags",
56+
hidden: !namespacedInstance.value,
5657
},
5758
{
5859
title: "Billing",

ui/src/components/Setting/SettingProfile.vue

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,18 @@
161161
</v-col>
162162
</v-row>
163163

164-
<div class="mt-4 pl-4 pr-4 pb-4 mb-4">
165-
<p class="mb-4">Multi-factor authentication (MFA) requires users to enter a one-time verification code sent
166-
using your favorite TOPT Provider in order to access your ShellHub account.</p>
167-
<div v-if="mfaEnabled === 'true'">
164+
<v-row>
165+
<v-col>
166+
<p class="mb-4">Multi-factor authentication (MFA) requires users to enter a one-time verification code sent
167+
using your favorite TOPT Provider in order to access your ShellHub account.</p>
168+
</v-col>
169+
<v-col md="auto" v-if="mfaEnabled === 'true'">
168170
<mfa-disable @success="() => mfaEnabled = 'false'" />
169-
</div>
170-
<div v-else>
171+
</v-col>
172+
<v-col md="auto" v-else>
171173
<mfa-settings @enabled="showCongratulationsModal" @reset-form="setRecoveryEmail()" />
172-
</div>
173-
</div>
174+
</v-col>
175+
</v-row>
174176
<v-row justify="center">
175177
<v-dialog v-model="dialog" width="auto" scrollable transition="dialog-bottom-transition" data-test="dialog">
176178
<v-card class="bg-v-theme-surface content" width="650" data-test="card-first-page">
@@ -216,6 +218,25 @@
216218
</v-dialog>
217219
</v-row>
218220
</div>
221+
222+
<div class="mt-6 mb-4" data-test="deleteOperation-div" v-if="envVariables.isCloud">
223+
<h3 class="mb-2">Danger Zone</h3>
224+
<v-row class="mt-2 mb-2">
225+
<v-col class="ml-3">
226+
<h4>Delete this account</h4>
227+
<div class="ml-2">
228+
<p>
229+
Deleting your account is permanent. This action will erase all your data and cannot be undone.
230+
Please be sure before proceeding.
231+
</p>
232+
</div>
233+
</v-col>
234+
235+
<v-col md="auto" class="ml-auto">
236+
<UserDelete />
237+
</v-col>
238+
</v-row>
239+
</div>
219240
</v-col>
220241
</v-row>
221242
</v-container>
@@ -232,6 +253,7 @@ import { INotificationsSuccess } from "../../interfaces/INotifications";
232253
import handleError from "@/utils/handleError";
233254
import MfaSettings from "../AuthMFA/MfaSettings.vue";
234255
import MfaDisable from "../AuthMFA/MfaDisable.vue";
256+
import UserDelete from "../User/UserDelete.vue";
235257
import { envVariables } from "../../envVariables";
236258
237259
const store = useStore();
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<template>
2+
<v-tooltip location="bottom" class="text-center" :disabled="!hasNamespace">
3+
<template v-slot:activator="{ props }">
4+
<div v-bind="props">
5+
<v-btn
6+
:disabled="hasNamespace"
7+
color="red darken-1"
8+
variant="outlined"
9+
data-test="delete-dialog-btn"
10+
@click="open()"
11+
>
12+
Delete Account
13+
</v-btn>
14+
</div>
15+
</template>
16+
<span> All namespaces must be deleted before you can delete your account. </span>
17+
</v-tooltip>
18+
19+
<v-dialog v-model="dialog" max-width="540">
20+
<v-card data-test="user-delete-dialog" class="bg-v-theme-surface">
21+
<v-card-title class="text-h5 pa-4 bg-primary" data-test=title>
22+
Confirm Account Deletion
23+
</v-card-title>
24+
25+
<v-card-text class="mt-4 mb-3 pb-1" data-test="subtitle">
26+
<div>
27+
<p class="mb-2">
28+
Are you sure you want to delete your account? This action cannot be undone.
29+
</p>
30+
</div>
31+
</v-card-text>
32+
33+
<v-card-actions>
34+
<v-spacer />
35+
36+
<v-btn variant="text" data-test="close-btn" @click="dialog = !dialog">
37+
Cancel
38+
</v-btn>
39+
40+
<v-btn
41+
color="red darken-1"
42+
variant="text"
43+
data-test="delete-user-btn"
44+
@click="deleteAccount()"
45+
>
46+
Delete Account
47+
</v-btn>
48+
</v-card-actions>
49+
</v-card>
50+
</v-dialog>
51+
</template>
52+
53+
<script setup lang="ts">
54+
import { ref, computed } from "vue";
55+
import { useRouter } from "vue-router";
56+
import { useStore } from "../../store";
57+
import {
58+
INotificationsError,
59+
INotificationsSuccess,
60+
} from "@/interfaces/INotifications";
61+
import handleError from "@/utils/handleError";
62+
63+
const store = useStore();
64+
const router = useRouter();
65+
const dialog = ref(false);
66+
67+
const hasNamespace = computed(() => store.getters["namespaces/getNumberNamespaces"] > 0);
68+
69+
const open = () => {
70+
dialog.value = true;
71+
};
72+
73+
const deleteAccount = async () => {
74+
try {
75+
dialog.value = !dialog.value;
76+
77+
await store.dispatch("auth/deleteUser");
78+
79+
store.dispatch(
80+
"snackbar/showSnackbarSuccessAction",
81+
INotificationsSuccess.deleteAccount,
82+
);
83+
84+
router.push({ name: "Login" });
85+
} catch (error: unknown) {
86+
store.dispatch(
87+
"snackbar/showSnackbarErrorAction",
88+
INotificationsError.deleteAccount,
89+
);
90+
handleError(error);
91+
}
92+
};
93+
</script>

ui/src/components/User/UserWarning.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
v-model:show="showDeviceWarning"
3434
@update="showDeviceWarning = false"
3535
data-test="DeviceAcceptWarning-component"
36-
3736
/>
3837

3938
<RecoveryHelper

ui/src/interfaces/INotifications.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export enum INotificationsSuccess {
2727
namespaceRemoveUser = "removing member",
2828
namespaceReload = "reloading namespace",
2929
addUser = "creating account",
30+
deleteAccount = "deleting your account",
3031
resendEmail = "resend email",
3132
recoverPassword = "sending email",
3233
recoveryHelper = "disabling Multi-Factor Authentication",
@@ -92,6 +93,7 @@ export enum INotificationsError {
9293
resendEmail = "resend email",
9394
loginFailed = "login",
9495
logoutFailed = "logout",
96+
deleteAccount = "deleting your account",
9597
recoverPassword = "sending email",
9698
validationAccount = "validation account",
9799
updatingAccount = "updating account",

ui/src/layouts/AppLayout.vue

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@
3232
<Namespace data-test="namespace-component" />
3333
</div>
3434

35+
<div class="d-flex justify-center" v-else-if="envVariables.isCloud">
36+
<v-btn
37+
color="primary"
38+
@click="showNamespaceAdd = true"
39+
data-test="save-btn">
40+
Add Namespace
41+
</v-btn>
42+
<NamespaceAdd
43+
v-model="showNamespaceAdd"
44+
enableSwitchIn
45+
data-test="namespaceAdd-component"
46+
/>
47+
</div>
48+
3549
<v-list density="compact" class="bg-v-theme-surface" data-test="list">
3650
<v-list-item
3751
v-for="item in visibleItems"
@@ -113,10 +127,12 @@ import UserWarning from "../components/User/UserWarning.vue";
113127
import Namespace from "../../src/components/Namespace/Namespace.vue";
114128
import AppBar from "../components/AppBar/AppBar.vue";
115129
import QuickConnection from "../components/QuickConnection/QuickConnection.vue";
130+
import NamespaceAdd from "@/components/Namespace/NamespaceAdd.vue";
116131
117132
const router = useRouter();
118133
const store = useStore();
119134
const currentRoute = computed(() => router.currentRoute);
135+
const showNamespaceAdd = ref(false);
120136
const hasNamespaces = computed(
121137
() => store.getters["namespaces/getNumberNamespaces"] !== 0,
122138
);
@@ -138,7 +154,7 @@ onMounted(() => {
138154
store.dispatch("privateKey/fetch");
139155
});
140156
141-
const disableItem = (item: string) => !hasNamespaces.value && item !== "Home";
157+
const disableItem = (item: string) => !hasNamespaces.value && item !== "Settings";
142158
const showConnector = computed(() => (envVariables.isCommunity && !envVariables.premiumPaywall) || !envVariables.hasConnector);
143159
const showFirewall = computed(() => envVariables.isCommunity && !envVariables.premiumPaywall);
144160
const items = [

ui/src/store/api/auth.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,6 @@ export const resetMfa = async (validation: MfaReset) => mfaApi.resetMFA(validati
3737
recovery_email_code: validation.recovery_email_code,
3838
});
3939

40+
export const deleteUser = async () => usersApi.deleteUser();
41+
4042
export const info = async () => usersApi.getUserInfo();

ui/src/store/modules/auth.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ export const auth: Module<AuthState, State> = {
328328
context.commit("authSuccess", resp.data);
329329
},
330330

331+
async deleteUser(context) {
332+
await apiAuth.deleteUser();
333+
context.dispatch("logout");
334+
},
335+
331336
logout(context) {
332337
context.commit("logout");
333338
localStorage.removeItem("token");

ui/tests/components/Setting/__snapshots__/SettingProfile.spec.ts.snap

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,28 @@ exports[`Settings Namespace > Renders the component 1`] = `
353353
<hr data-v-b3fa301d=\\"\\" class=\\"v-divider v-theme--light mt-6\\" aria-orientation=\\"horizontal\\" role=\\"separator\\">
354354
<hr data-v-b3fa301d=\\"\\" class=\\"v-divider v-theme--light mb-6\\" aria-orientation=\\"horizontal\\" role=\\"separator\\">
355355
<!--v-if-->
356+
<div data-v-b3fa301d=\\"\\" class=\\"mt-6 mb-4\\" data-test=\\"deleteOperation-div\\">
357+
<h3 data-v-b3fa301d=\\"\\" class=\\"mb-2\\">Danger Zone</h3>
358+
<div data-v-b3fa301d=\\"\\" class=\\"v-row mt-2 mb-2\\">
359+
<div data-v-b3fa301d=\\"\\" class=\\"v-col ml-3\\">
360+
<h4 data-v-b3fa301d=\\"\\">Delete this account</h4>
361+
<div data-v-b3fa301d=\\"\\" class=\\"ml-2\\">
362+
<p data-v-b3fa301d=\\"\\"> Deleting your account is permanent. This action will erase all your data and cannot be undone. Please be sure before proceeding. </p>
363+
</div>
364+
</div>
365+
<div data-v-b3fa301d=\\"\\" class=\\"v-col-md-auto v-col ml-auto\\">
366+
<div aria-describedby=\\"v-tooltip-16\\"><button type=\\"button\\" class=\\"v-btn v-theme--light text-red darken-1 v-btn--density-default v-btn--size-default v-btn--variant-outlined\\" data-test=\\"delete-dialog-btn\\"><span class=\\"v-btn__overlay\\"></span><span class=\\"v-btn__underlay\\"></span>
367+
<!----><span class=\\"v-btn__content\\" data-no-activator=\\"\\"> Delete Account </span>
368+
<!---->
369+
<!---->
370+
</button></div>
371+
<!--teleport start-->
372+
<!--teleport end-->
373+
<!---->
374+
<!---->
375+
</div>
376+
</div>
377+
</div>
356378
</div>
357379
</div>
358380
</div>"

0 commit comments

Comments
 (0)