Skip to content

Commit 200b5bc

Browse files
satellite/admin-ui: add delete user functionality
This change adds functionality to delete a user account. Issue: #7619 Change-Id: I298f2d0602c7f6690d0225f729483e31d36f73f1
1 parent 4077f08 commit 200b5bc

File tree

6 files changed

+110
-84
lines changed

6 files changed

+110
-84
lines changed

satellite/admin/back-office/ui/src/components/AccountActionsMenu.vue

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,22 @@
6262
</v-list-item-title>
6363
</v-list-item>
6464

65-
<v-list-item v-if="featureFlags.account.delete" density="comfortable" link rounded="lg" base-color="error">
66-
<v-list-item-title class="text-body-2 font-weight-medium">
65+
<v-list-item
66+
v-if="featureFlags.account.delete"
67+
density="comfortable"
68+
rounded="lg" link
69+
base-color="error"
70+
@click="user.hasUnpaidInvoices || hasActiveProjects ? ()=>{} : emit('delete', user)"
71+
>
72+
<v-list-item-title
73+
v-if="cantDeleteText"
74+
v-tooltip="cantDeleteText"
75+
class="text-body-2 font-weight-medium text-disabled"
76+
>
77+
Delete
78+
</v-list-item-title>
79+
<v-list-item-title v-else class="text-body-2 font-weight-medium">
6780
Delete
68-
<AccountDeleteDialog />
6981
</v-list-item-title>
7082
</v-list-item>
7183
</v-list>
@@ -82,7 +94,6 @@ import { useAppStore } from '@/store/app';
8294
import { ROUTES } from '@/router';
8395
8496
import AccountResetMFADialog from '@/components/AccountResetMFADialog.vue';
85-
import AccountDeleteDialog from '@/components/AccountDeleteDialog.vue';
8697
import AccountNewProjectDialog from '@/components/AccountNewProjectDialog.vue';
8798
import AccountGeofenceDialog from '@/components/AccountGeofenceDialog.vue';
8899
@@ -107,10 +118,26 @@ const isCurrentRouteViewAccount = computed(() => {
107118
return router.currentRoute.value.name === ROUTES.Account.name;
108119
});
109120
121+
const hasActiveProjects = computed(() => props.user.projects?.some(p => p.active) ?? false);
122+
123+
const cantDeleteText = computed(() => {
124+
if (props.user.hasUnpaidInvoices && hasActiveProjects.value) {
125+
return 'User has unpaid invoices and active projects so cannot be deleted';
126+
}
127+
if (props.user.hasUnpaidInvoices) {
128+
return 'User has unpaid invoices so cannot be deleted';
129+
}
130+
if (hasActiveProjects.value) {
131+
return 'User has active projects so cannot be deleted';
132+
}
133+
return '';
134+
});
135+
110136
const emit = defineEmits<{
111137
(e: 'toggleFreeze', user: UserAccount): void;
112138
(e: 'update', user: UserAccount): void;
113139
(e: 'updateLimits', user: UserAccount): void;
140+
(e: 'delete', user: UserAccount): void;
114141
}>();
115142
116143
function viewAccount() {

satellite/admin/back-office/ui/src/components/AccountDeleteDialog.vue

Lines changed: 53 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,116 +2,97 @@
22
// See LICENSE for copying information.
33

44
<template>
5-
<v-dialog v-model="dialog" activator="parent" width="auto" transition="fade-transition">
5+
<v-dialog v-model="model" width="auto" transition="fade-transition">
66
<v-card rounded="xlg">
7-
<v-sheet>
8-
<v-card-item class="pl-7 py-4">
9-
<template #prepend>
10-
<v-card-title class="font-weight-bold">
11-
Delete Account
12-
</v-card-title>
13-
</template>
14-
15-
<template #append>
16-
<v-btn icon="$close" variant="text" size="small" color="default" @click="dialog = false" />
17-
</template>
18-
</v-card-item>
19-
</v-sheet>
20-
21-
<v-divider />
22-
23-
<v-form class="pa-7">
24-
<v-row>
25-
<v-col cols="12">
26-
<p>Please enter the reason for deleting this account.</p>
27-
</v-col>
28-
</v-row>
29-
30-
<v-row>
31-
<v-col cols="12">
32-
<v-select
33-
label="Deleting reason" placeholder="Select one or more reasons"
34-
:items="['Reason 1', 'Reason 2', 'Reason 3', 'Other']" multiple variant="outlined" autofocus required
35-
hide-details="auto"
36-
/>
37-
</v-col>
38-
</v-row>
7+
<template #title>
8+
Delete Account
9+
</template>
10+
<template #append>
11+
<v-btn
12+
:icon="X" :disabled="isLoading"
13+
variant="text" size="small" color="default" @click="model = false"
14+
/>
15+
</template>
3916

17+
<div class="pa-6">
4018
<v-row>
4119
<v-col cols="12">
4220
<v-text-field
43-
model-value="41" label="Account ID" variant="solo-filled" flat readonly
21+
:model-value="account.id" label="Account ID" variant="solo-filled" flat readonly
4422
hide-details="auto"
4523
/>
4624
</v-col>
4725
<v-col cols="12">
4826
<v-text-field
49-
model-value="itacker@gmail.com" label="Account Email" variant="solo-filled" flat readonly
27+
:model-value="account.email" label="Account Email" variant="solo-filled" flat readonly
5028
hide-details="auto"
5129
/>
5230
</v-col>
5331
</v-row>
5432

55-
<v-row>
56-
<v-col>
57-
<v-alert variant="tonal" color="error" rounded="lg">
58-
This will delete the account, data, and account
59-
information.
60-
</v-alert>
61-
</v-col>
62-
</v-row>
63-
</v-form>
64-
65-
<v-divider />
33+
<v-alert class="mt-6" title="Warning" variant="tonal" color="error" rounded="lg">
34+
This will delete the account, data, and account
35+
information.
36+
</v-alert>
37+
</div>
6638

67-
<v-card-actions class="pa-7">
39+
<v-card-actions class="pa-6">
6840
<v-row>
6941
<v-col>
70-
<v-btn variant="outlined" color="default" block @click="dialog = false">Cancel</v-btn>
42+
<v-btn variant="outlined" color="default" block @click="model = false">Cancel</v-btn>
7143
</v-col>
7244
<v-col>
73-
<v-btn color="error" variant="flat" block @click="onButtonClick">Delete Account</v-btn>
45+
<v-btn color="error" variant="flat" block :loading="isLoading" @click="deleteAccount">Delete Account</v-btn>
7446
</v-col>
7547
</v-row>
7648
</v-card-actions>
7749
</v-card>
7850
</v-dialog>
79-
80-
<v-snackbar v-model="snackbar" :timeout="7000" color="success">
81-
The account was deleted successfully.
82-
<template #actions>
83-
<v-btn color="default" variant="text" @click="snackbar = false">
84-
Close
85-
</v-btn>
86-
</template>
87-
</v-snackbar>
8851
</template>
8952

9053
<script setup lang="ts">
91-
import { ref } from 'vue';
9254
import {
9355
VDialog,
9456
VCard,
95-
VSheet,
96-
VCardItem,
97-
VCardTitle,
9857
VBtn,
99-
VDivider,
100-
VForm,
10158
VRow,
10259
VCol,
10360
VTextField,
10461
VCardActions,
105-
VSnackbar,
106-
VSelect,
10762
VAlert,
10863
} from 'vuetify/components';
64+
import { X } from 'lucide-vue-next';
65+
import { useRouter } from 'vue-router';
66+
67+
import { useLoading } from '@/composables/useLoading';
68+
import { UserAccount } from '@/api/client.gen';
69+
import { useUsersStore } from '@/store/users';
70+
import { useNotify } from '@/composables/useNotify';
71+
import { ROUTES } from '@/router';
72+
73+
const notify = useNotify();
74+
const usersStore = useUsersStore();
75+
const router = useRouter();
76+
const { isLoading, withLoading } = useLoading();
77+
78+
const model = defineModel<boolean>({ required: true });
79+
80+
const props = defineProps<{
81+
account: UserAccount;
82+
}>();
10983
110-
const snackbar = ref<boolean>(false);
111-
const dialog = ref<boolean>(false);
84+
function deleteAccount() {
85+
withLoading(async () => {
86+
try {
87+
await usersStore.deleteUser(props.account.id);
88+
notify.success('Account deleted successfully');
11289
113-
function onButtonClick() {
114-
snackbar.value = true;
115-
dialog.value = false;
90+
model.value = false;
91+
await new Promise((resolve) => setTimeout(resolve, 200)); // wait for dialog to close
92+
router.push({ name: ROUTES.AccountSearch.name });
93+
} catch (e) {
94+
notify.error(`Failed to delete account. ${e.message}`);
95+
}
96+
});
11697
}
117-
</script>
98+
</script>

satellite/admin/back-office/ui/src/layouts/default/AppBar.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,17 @@
139139

140140
<v-list-item link rounded="lg">
141141
<!-- <template v-slot:prepend>
142-
<img src="@/assets/icon-check-color.svg" alt="Selected Project">
143-
</template> -->
142+
<img src="@/assets/icon-check-color.svg" alt="Selected Project">
143+
</template> -->
144144
<v-list-item-title class="text-body-2 ml-7">
145145
Europe (EU1)
146146
</v-list-item-title>
147147
</v-list-item>
148148

149149
<v-list-item link rounded="lg">
150150
<!-- <template v-slot:prepend>
151-
<img src="@/assets/icon-check-color.svg" alt="Selected Project">
152-
</template> -->
151+
<img src="@/assets/icon-check-color.svg" alt="Selected Project">
152+
</template> -->
153153
<v-list-item-title class="text-body-2 ml-7">
154154
Asia-Pacific (AP1)
155155
</v-list-item-title>

satellite/admin/back-office/ui/src/store/users.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,16 @@ export const useUsersStore = defineStore('users', () => {
3535
}
3636

3737
// Update the current user stored in the state.
38-
async function updateCurrentUser(user: string | UserAccount): Promise<UserAccount> {
38+
async function updateCurrentUser(user: string | UserAccount): Promise<void> {
3939
if (typeof user === 'string') {
4040
state.currentAccount = await getUser(user);
41-
return state.currentAccount;
41+
return;
4242
}
4343
state.currentAccount = user;
44-
return state.currentAccount;
44+
}
45+
46+
function clearCurrentUser(): void {
47+
state.currentAccount = null;
4548
}
4649

4750
async function getAccountFreezeTypes(): Promise<void> {
@@ -77,15 +80,21 @@ export const useUsersStore = defineStore('users', () => {
7780
return await userApi.updateUser(request, userID);
7881
}
7982

83+
async function deleteUser(userID: string): Promise<void> {
84+
await userApi.deleteUser(userID);
85+
}
86+
8087
return {
8188
state,
8289
getUserByEmail,
8390
updateCurrentUser,
91+
clearCurrentUser,
8492
getAccountFreezeTypes,
8593
freezeUser,
8694
unfreezeUser,
8795
getUserKinds,
8896
getUserStatuses,
8997
updateUser,
98+
deleteUser,
9099
};
91100
});

satellite/admin/back-office/ui/src/views/AccountDetails.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
:user="userAccount"
3838
@update="updateAccountDialogEnabled = true"
3939
@update-limits="updateLimitsDialogEnabled = true"
40+
@delete="deleteAccountDialogEnabled = true"
4041
@toggle-freeze="toggleFreeze"
4142
/>
4243
</v-btn>
@@ -75,6 +76,7 @@
7576
:user="userAccount"
7677
@update="updateAccountDialogEnabled = true"
7778
@update-limits="updateLimitsDialogEnabled = true"
79+
@delete="deleteAccountDialogEnabled = true"
7880
@toggle-freeze="toggleFreeze"
7981
/>
8082
</v-btn>
@@ -217,6 +219,7 @@
217219
<AccountFreezeDialog v-if="userAccount" v-model="freezeDialogEnabled" :account="userAccount" />
218220
<AccountUpdateDialog v-if="userAccount" v-model="updateAccountDialogEnabled" :account="userAccount" />
219221
<AccountUpdateLimitsDialog v-if="userAccount" v-model="updateLimitsDialogEnabled" :account="userAccount" />
222+
<AccountDeleteDialog v-if="userAccount" v-model="deleteAccountDialogEnabled" :account="userAccount" />
220223
</template>
221224

222225
<script setup lang="ts">
@@ -257,6 +260,7 @@ import CardStatsComponent from '@/components/CardStatsComponent.vue';
257260
import AccountFreezeDialog from '@/components/AccountFreezeDialog.vue';
258261
import AccountUpdateDialog from '@/components/AccountUpdateDialog.vue';
259262
import AccountUpdateLimitsDialog from '@/components/AccountUpdateLimitsDialog.vue';
263+
import AccountDeleteDialog from '@/components/AccountDeleteDialog.vue';
260264
261265
const MS_PER_DAY = 1000 * 60 * 60 * 24;
262266
@@ -272,6 +276,7 @@ const unfreezing = ref<boolean>(false);
272276
const freezeDialogEnabled = ref<boolean>(false);
273277
const updateAccountDialogEnabled = ref<boolean>(false);
274278
const updateLimitsDialogEnabled = ref<boolean>(false);
279+
const deleteAccountDialogEnabled = ref<boolean>(false);
275280
276281
const statusColor = computed(() => {
277282
if (!userAccount.value) {

satellite/admin/back-office/ui/src/views/AccountSearch.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
</template>
5353

5454
<script setup lang="ts">
55-
import { computed, ref, watch } from 'vue';
55+
import { computed, onMounted, ref, watch } from 'vue';
5656
import { useRouter } from 'vue-router';
5757
import { VContainer, VRow, VCol, VBtn, VCard, VCardText, VForm, VTextField } from 'vuetify/components';
5858
@@ -119,4 +119,8 @@ async function goToUser(): Promise<void> {
119119
}
120120
121121
watch(email, () => notFoundError.value = false);
122+
123+
onMounted(() => {
124+
usersStore.clearCurrentUser();
125+
});
122126
</script>

0 commit comments

Comments
 (0)