feat(server,web): Delete and restore user from the admin portal (#935)

* delete and restore user from admin UI

* addressed review comments and fix e2e test

* added cron job to delete user, and some formatting changes

* addressed review comments

* adding missing queue registration
This commit is contained in:
Zeeshan Khan 2022-11-07 16:53:47 -05:00 committed by GitHub
parent 948ff5530c
commit fe4b307fe6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 804 additions and 59 deletions

View file

@ -0,0 +1,41 @@
<script lang="ts">
import { api, UserResponseDto } from '@api';
import { createEventDispatcher } from 'svelte';
export let user: UserResponseDto;
const dispatch = createEventDispatcher();
const deleteUser = async () => {
const deletedUser = await api.userApi.deleteUser(user.id);
if (deletedUser.data.deletedAt != null) dispatch('user-delete-success');
else dispatch('user-delete-fail');
};
</script>
<div
class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] rounded-3xl py-8 dark:text-immich-dark-fg"
>
<div
class="flex flex-col place-items-center place-content-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
>
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
Confirm User Deletion
</h1>
</div>
<div>
<p class="ml-4 text-md py-5 text-center">
{user.firstName}
{user.lastName} account and assets along will be marked to delete completely after 7 days. are
you sure you want to proceed ?
</p>
<div class="flex w-full px-4 gap-4 mt-8">
<button
on:click={deleteUser}
class="flex-1 transition-colors bg-red-500 hover:bg-red-400 px-6 py-3 text-white rounded-full w-full font-medium"
>Confirm
</button>
</div>
</div>
</div>

View file

@ -0,0 +1,40 @@
<script lang="ts">
import { api, UserResponseDto } from '@api';
import { createEventDispatcher } from 'svelte';
export let user: UserResponseDto;
const dispatch = createEventDispatcher();
const restoreUser = async () => {
const restoredUser = await api.userApi.restoreUser(user.id);
if (restoredUser.data.deletedAt == null) dispatch('user-restore-success');
else dispatch('user-restore-fail');
};
</script>
<div
class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] rounded-3xl py-8 dark:text-immich-dark-fg"
>
<div
class="flex flex-col place-items-center place-content-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
>
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
Restore User
</h1>
</div>
<div>
<p class="ml-4 text-md py-5 text-center">
{user.firstName}
{user.lastName} account will restored
</p>
<div class="flex w-full px-4 gap-4 mt-8">
<button
on:click={restoreUser}
class="flex-1 transition-colors bg-lime-600 hover:bg-lime-500 px-6 py-3 text-white rounded-full w-full font-medium"
>Confirm
</button>
</div>
</div>
</div>

View file

@ -3,9 +3,21 @@
import { createEventDispatcher } from 'svelte';
import PencilOutline from 'svelte-material-icons/PencilOutline.svelte';
import TrashCanOutline from 'svelte-material-icons/TrashCanOutline.svelte';
import DeleteRestore from 'svelte-material-icons/DeleteRestore.svelte';
import moment from 'moment';
export let allUsers: Array<UserResponseDto>;
const dispatch = createEventDispatcher();
const isDeleted = (user: UserResponseDto): boolean => {
return user.deletedAt != null;
};
const getDeleteDate = (user: UserResponseDto): string => {
return moment(user.deletedAt).add(7, 'days').format('LL');
};
</script>
<table class="text-left w-full my-5">
@ -16,7 +28,7 @@
<th class="text-center w-1/4 font-medium text-sm">Email</th>
<th class="text-center w-1/4 font-medium text-sm">First name</th>
<th class="text-center w-1/4 font-medium text-sm">Last name</th>
<th class="text-center w-1/4 font-medium text-sm">Edit</th>
<th class="text-center w-1/4 font-medium text-sm">Action</th>
</tr>
</thead>
<tbody
@ -25,21 +37,44 @@
{#each allUsers as user, i}
<tr
class={`text-center flex place-items-center w-full h-[80px] dark:text-immich-dark-bg ${
i % 2 == 0 ? 'bg-immich-gray dark:bg-[#e5e5e5]' : 'bg-immich-bg dark:bg-[#eeeeee]'
isDeleted(user)
? 'bg-red-50'
: i % 2 == 0
? 'bg-immich-gray dark:bg-[#e5e5e5]'
: 'bg-immich-bg dark:bg-[#eeeeee]'
}`}
>
<td class="text-sm px-4 w-1/4 text-ellipsis">{user.email}</td>
<td class="text-sm px-4 w-1/4 text-ellipsis">{user.firstName}</td>
<td class="text-sm px-4 w-1/4 text-ellipsis">{user.lastName}</td>
<td class="text-sm px-4 w-1/4 text-ellipsis"
><button
on:click={() => {
dispatch('edit-user', { user });
}}
class="bg-immich-primary dark:bg-immich-dark-primary text-gray-100 dark:text-gray-700 rounded-full p-3 transition-all duration-150 hover:bg-immich-primary/75"
><PencilOutline size="20" /></button
></td
>
<td class="text-sm px-4 w-1/4 text-ellipsis">
{#if !isDeleted(user)}
<button
on:click={() => {
dispatch('edit-user', { user });
}}
class="bg-immich-primary dark:bg-immich-dark-primary text-gray-100 dark:text-gray-700 rounded-full p-3 transition-all duration-150 hover:bg-immich-primary/75"
><PencilOutline size="16" /></button
>
<button
on:click={() => {
dispatch('delete-user', { user });
}}
class="bg-immich-primary dark:bg-immich-dark-primary text-gray-100 dark:text-gray-700 rounded-full p-3 transition-all duration-150 hover:bg-immich-primary/75"
><TrashCanOutline size="16" /></button
>
{/if}
{#if isDeleted(user)}
<button
on:click={() => {
dispatch('restore-user', { user });
}}
class="bg-immich-primary dark:bg-immich-dark-primary text-gray-100 dark:text-gray-700 rounded-full p-3 transition-all duration-150 hover:bg-immich-primary/75"
title={`scheduled removal on ${getDeleteDate(user)}`}
><DeleteRestore size="16" /></button
>
{/if}
</td>
</tr>
{/each}
</tbody>