Edit user on the web (#458)

* Added dispatch event for edit user

* Fixed import location

* solve merge conflict

* Fixed issue not admin user can access admin page

* Implemented edit user and password reset
This commit is contained in:
Alex
2022-08-12 14:25:19 -05:00
committed by GitHub
parent 5b7236f6ad
commit 4b9187928c
8 changed files with 545 additions and 365 deletions

View File

@@ -1,114 +1,181 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
import { api, UserResponseDto } from '@api';
import type { Load } from '@sveltejs/kit';
import { api, UserResponseDto } from '@api';
import { browser } from '$app/env';
export const load: Load = async ({ fetch, session }) => {
if (!browser && !session.user) {
return {
status: 302,
redirect: '/auth/login'
};
}
export const load: Load = async ({fetch, session}) => {
if (!browser && !session.user) {
return {
status: 302,
redirect: '/auth/login'
};
}
try {
const [user, allUsers] = await Promise.all([
fetch('/data/user/get-my-user-info').then((r) => r.json()),
fetch('/data/user/get-all-users?isAll=false').then((r) => r.json())
]);
try {
return {
status: 200,
props: {
user: user,
allUsers: allUsers
}
};
} catch (e) {
return {
status: 302,
redirect: '/auth/login'
};
}
};
const user: UserResponseDto = await fetch('/data/user/get-my-user-info').then((r) => r.json());
const allUsers: UserResponseDto[] = await fetch<UserResponseDto[]>('/data/user/get-all-users?isAll=false').then((r) => r.json());
if (!user.isAdmin) {
return {
status: 302,
redirect: '/photos'
};
}
return {
status: 200,
props: {
user: user,
allUsers: allUsers
}
};
} catch (e) {
return {
status: 302,
redirect: '/auth/login'
};
}
};
</script>
<script lang="ts">
import { onMount } from 'svelte';
import { onMount } from 'svelte';
import type { ImmichUser } from '$lib/models/immich-user';
import { AdminSideBarSelection } from '$lib/models/admin-sidebar-selection';
import SideBarButton from '$lib/components/shared-components/side-bar/side-bar-button.svelte';
import AccountMultipleOutline from 'svelte-material-icons/AccountMultipleOutline.svelte';
import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte';
import UserManagement from '$lib/components/admin-page/user-management.svelte';
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
import CreateUserForm from '$lib/components/forms/create-user-form.svelte';
import StatusBox from '$lib/components/shared-components/status-box.svelte';
import { browser } from '$app/env';
import { AdminSideBarSelection } from '$lib/models/admin-sidebar-selection';
import SideBarButton from '$lib/components/shared-components/side-bar/side-bar-button.svelte';
import AccountMultipleOutline from 'svelte-material-icons/AccountMultipleOutline.svelte';
import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte';
import UserManagement from '$lib/components/admin-page/user-management.svelte';
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
import CreateUserForm from '$lib/components/forms/create-user-form.svelte';
import EditUserForm from '$lib/components/forms/edit-user-form.svelte';
import StatusBox from '$lib/components/shared-components/status-box.svelte';
let selectedAction: AdminSideBarSelection = AdminSideBarSelection.USER_MANAGEMENT;
export let user: ImmichUser;
export let allUsers: UserResponseDto[];
let selectedAction: AdminSideBarSelection = AdminSideBarSelection.USER_MANAGEMENT;
let shouldShowCreateUserForm: boolean;
export let user: UserResponseDto;
export let allUsers: UserResponseDto[];
const onButtonClicked = (buttonType: CustomEvent) => {
selectedAction = buttonType.detail['actionType'] as AdminSideBarSelection;
};
let editUser: UserResponseDto;
onMount(() => {
selectedAction = AdminSideBarSelection.USER_MANAGEMENT;
});
let shouldShowEditUserForm = false;
let shouldShowCreateUserForm = false;
let shouldShowInfoPanel = false;
const onUserCreated = async () => {
const { data } = await api.userApi.getAllUsers(false);
allUsers = data;
const onButtonClicked = (buttonType: CustomEvent) => {
selectedAction = buttonType.detail['actionType'] as AdminSideBarSelection;
};
shouldShowCreateUserForm = false;
};
onMount(() => {
selectedAction = AdminSideBarSelection.USER_MANAGEMENT;
});
const onUserCreated = async () => {
const {data} = await api.userApi.getAllUsers(false);
allUsers = data;
shouldShowCreateUserForm = false;
};
const editUserHandler = async (event: CustomEvent) => {
const {user} = event.detail;
editUser = user;
shouldShowEditUserForm = true;
};
const onEditUserSuccess = async () => {
const {data} = await api.userApi.getAllUsers(false);
allUsers = data;
shouldShowEditUserForm = false;
}
const onEditPasswordSuccess = async () => {
const {data} = await api.userApi.getAllUsers(false);
allUsers = data;
shouldShowEditUserForm = false;
shouldShowInfoPanel = true;
}
</script>
<svelte:head>
<title>Administration - Immich</title>
<title>Administration - Immich</title>
</svelte:head>
<NavigationBar {user} />
<NavigationBar {user}/>
{#if shouldShowCreateUserForm}
<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}>
<div>
<CreateUserForm on:user-created={onUserCreated} />
</div>
</FullScreenModal>
<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}>
<CreateUserForm on:user-created={onUserCreated}/>
</FullScreenModal>
{/if}
{#if shouldShowEditUserForm}
<FullScreenModal on:clickOutside={() => (shouldShowEditUserForm = false)}>
<EditUserForm user={editUser} on:edit-success={onEditUserSuccess}
on:reset-password-success={onEditPasswordSuccess}/>
</FullScreenModal>
{/if}
{#if shouldShowInfoPanel}
<FullScreenModal on:clickOutside={() => (shouldShowInfoPanel = false)}>
<div class="border bg-white shadow-sm w-[500px] rounded-3xl p-8 text-sm">
<h1 class="font-bold text-immich-primary text-lg mb-4">Password reset success</h1>
<p>
The user's password has been reset to the default <code
class="font-bold bg-gray-200 px-2 py-1 rounded-md text-immich-primary">password</code>
<br>
Please inform the user, and they will need to change the password at the next log-on.
</p>
<div class="flex w-full">
<button
on:click={() => shouldShowInfoPanel = false}
class="mt-6 bg-immich-primary hover:bg-immich-primary/75 px-6 py-3 text-white rounded-full shadow-md w-full font-medium"
>Done
</button
>
</div>
</div>
</FullScreenModal>
{/if}
<section class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen">
<section id="admin-sidebar" class="pt-8 pr-6 flex flex-col">
<SideBarButton
title="User"
logo={AccountMultipleOutline}
actionType={AdminSideBarSelection.USER_MANAGEMENT}
isSelected={selectedAction === AdminSideBarSelection.USER_MANAGEMENT}
on:selected={onButtonClicked}
/>
<section id="admin-sidebar" class="pt-8 pr-6 flex flex-col">
<SideBarButton
title="User"
logo={AccountMultipleOutline}
actionType={AdminSideBarSelection.USER_MANAGEMENT}
isSelected={selectedAction === AdminSideBarSelection.USER_MANAGEMENT}
on:selected={onButtonClicked}
/>
<div class="mb-6 mt-auto">
<StatusBox />
</div>
</section>
<section class="overflow-y-auto relative">
<div id="setting-title" class="pt-10 fixed w-full z-50 bg-immich-bg">
<h1 class="text-lg ml-8 mb-4 text-immich-primary font-medium">{selectedAction}</h1>
<hr />
</div>
<div class="mb-6 mt-auto">
<StatusBox/>
</div>
</section>
<section class="overflow-y-auto relative">
<div id="setting-title" class="pt-10 fixed w-full z-50 bg-immich-bg">
<h1 class="text-lg ml-8 mb-4 text-immich-primary font-medium">{selectedAction}</h1>
<hr/>
</div>
<section id="setting-content" class="relative pt-[85px] flex place-content-center">
<section class="w-[800px] pt-4">
{#if selectedAction === AdminSideBarSelection.USER_MANAGEMENT}
<UserManagement
{allUsers}
on:create-user={() => (shouldShowCreateUserForm = true)}
on:edit-user={editUserHandler}
/>
{/if}
</section>
</section>
</section>
<section id="setting-content" class="relative pt-[85px] flex place-content-center">
<section class="w-[800px] pt-4">
{#if selectedAction === AdminSideBarSelection.USER_MANAGEMENT}
<UserManagement {allUsers} on:createUser={() => (shouldShowCreateUserForm = true)} />
{/if}
</section>
</section>
</section>
</section>

View File

@@ -1,56 +1,58 @@
<script context="module" lang="ts">
export const prerender = false;
import type { Load } from '@sveltejs/kit';
import { api } from '@api';
export const prerender = false;
import type { Load } from '@sveltejs/kit';
import { api } from '@api';
import { browser } from '$app/env';
export const load: Load = async () => {
if (browser) {
try {
const { data: user } = await api.userApi.getMyUserInfo();
export const load: Load = async () => {
if (browser) {
try {
const {data: user} = await api.userApi.getMyUserInfo();
return {
status: 302,
redirect: '/photos'
};
} catch (e) {}
return {
status: 302,
redirect: '/photos'
};
} catch (e) {
}
const { data } = await api.userApi.getUserCount();
const {data} = await api.userApi.getUserCount();
return {
status: 200,
props: {
isAdminUserExist: data.userCount == 0 ? false : true
}
};
}
};
return {
status: 200,
props: {
isAdminUserExist: data.userCount != 0
}
};
}
};
</script>
<script lang="ts">
import { goto } from '$app/navigation';
import { browser } from '$app/env';
import { goto } from '$app/navigation';
export let isAdminUserExist: boolean;
export let isAdminUserExist: boolean;
async function onGettingStartedClicked() {
isAdminUserExist ? goto('/auth/login') : goto('/auth/register');
}
async function onGettingStartedClicked() {
isAdminUserExist ? await goto('/auth/login') : await goto('/auth/register');
}
</script>
<svelte:head>
<title>Welcome 🎉 - Immich</title>
<meta name="description" content="Immich Web Interface" />
<title>Welcome 🎉 - Immich</title>
<meta name="description" content="Immich Web Interface"/>
</svelte:head>
<section class="h-screen w-screen flex place-items-center place-content-center">
<div class="flex flex-col place-items-center gap-8 text-center max-w-[350px]">
<div class="flex place-items-center place-content-center ">
<img class="text-center" src="immich-logo.svg" height="200" width="200" alt="immich-logo" />
</div>
<h1 class="text-4xl text-immich-primary font-bold font-immich-title">Welcome to IMMICH Web</h1>
<button
class="border px-4 py-2 rounded-md bg-immich-primary hover:bg-immich-primary/75 text-white font-bold w-[200px]"
on:click={onGettingStartedClicked}>Getting Started</button
>
</div>
<div class="flex flex-col place-items-center gap-8 text-center max-w-[350px]">
<div class="flex place-items-center place-content-center ">
<img class="text-center" src="immich-logo.svg" height="200" width="200" alt="immich-logo"/>
</div>
<h1 class="text-4xl text-immich-primary font-bold font-immich-title">Welcome to IMMICH Web</h1>
<button
class="border px-4 py-2 rounded-md bg-immich-primary hover:bg-immich-primary/75 text-white font-bold w-[200px]"
on:click={onGettingStartedClicked}>Getting Started
</button
>
</div>
</section>

View File

@@ -1,119 +1,120 @@
<script context="module" lang="ts">
export const prerender = false;
export const prerender = false;
import type { Load } from '@sveltejs/kit';
import { AlbumResponseDto, api, UserResponseDto } from '@api';
import type { Load } from '@sveltejs/kit';
import { AlbumResponseDto, api, UserResponseDto } from '@api';
import { browser } from '$app/env';
export const load: Load = async ({ fetch, session }) => {
if (!browser && !session.user) {
return {
status: 302,
redirect: '/auth/login'
};
}
export const load: Load = async ({fetch, session}) => {
if (!browser && !session.user) {
return {
status: 302,
redirect: '/auth/login'
};
}
try {
const [user, sharedAlbums] = await Promise.all([
fetch('/data/user/get-my-user-info').then((r) => r.json()),
fetch('/data/album/get-all-albums?isShared=true').then((r) => r.json())
]);
try {
const [user, sharedAlbums] = await Promise.all([
fetch('/data/user/get-my-user-info').then((r) => r.json()),
fetch('/data/album/get-all-albums?isShared=true').then((r) => r.json())
]);
return {
status: 200,
props: {
user: user,
sharedAlbums: sharedAlbums
}
};
} catch (e) {
return {
status: 302,
redirect: '/auth/login'
};
}
};
return {
status: 200,
props: {
user: user,
sharedAlbums: sharedAlbums
}
};
} catch (e) {
return {
status: 302,
redirect: '/auth/login'
};
}
};
</script>
<script lang="ts">
import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte';
import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
import SharedAlbumListTile from '$lib/components/sharing-page/shared-album-list-tile.svelte';
import { goto } from '$app/navigation';
import { browser } from '$app/env';
import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte';
import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
import SharedAlbumListTile from '$lib/components/sharing-page/shared-album-list-tile.svelte';
import { goto } from '$app/navigation';
export let user: UserResponseDto;
export let sharedAlbums: AlbumResponseDto[];
export let user: UserResponseDto;
export let sharedAlbums: AlbumResponseDto[];
const createSharedAlbum = async () => {
try {
const { data: newAlbum } = await api.albumApi.createAlbum({
albumName: 'Untitled'
});
const createSharedAlbum = async () => {
try {
const {data: newAlbum} = await api.albumApi.createAlbum({
albumName: 'Untitled'
});
goto('/albums/' + newAlbum.id);
} catch (e) {
console.log('Error [createAlbum] ', e);
}
};
goto('/albums/' + newAlbum.id);
} catch (e) {
console.log('Error [createAlbum] ', e);
}
};
</script>
<svelte:head>
<title>Albums - Immich</title>
<title>Albums - Immich</title>
</svelte:head>
<section>
<NavigationBar {user} on:uploadClicked={() => {}} />
<NavigationBar {user} on:uploadClicked={() => {}}/>
</section>
<section class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg">
<SideBar />
<SideBar/>
<section class="overflow-y-auto relative">
<section id="album-content" class="relative pt-8 pl-4 mb-12 bg-immich-bg">
<!-- Main Section -->
<div class="px-4 flex justify-between place-items-center">
<div>
<p class="font-medium">Sharing</p>
</div>
<section class="overflow-y-auto relative">
<section id="album-content" class="relative pt-8 pl-4 mb-12 bg-immich-bg">
<!-- Main Section -->
<div class="px-4 flex justify-between place-items-center">
<div>
<p class="font-medium">Sharing</p>
</div>
<div>
<button
on:click={createSharedAlbum}
class="flex place-items-center gap-1 text-sm hover:bg-immich-primary/5 p-2 rounded-lg font-medium hover:text-gray-700"
>
<div>
<button
on:click={createSharedAlbum}
class="flex place-items-center gap-1 text-sm hover:bg-immich-primary/5 p-2 rounded-lg font-medium hover:text-gray-700"
>
<span>
<PlusBoxOutline size="18" />
<PlusBoxOutline size="18"/>
</span>
<p>Create shared album</p>
</button>
</div>
</div>
<p>Create shared album</p>
</button>
</div>
</div>
<div class="my-4">
<hr />
</div>
<div class="my-4">
<hr/>
</div>
<!-- Share Album List -->
<div class="w-full flex flex-col place-items-center">
{#each sharedAlbums as album}
<a sveltekit:prefetch href={`albums/${album.id}`}>
<SharedAlbumListTile {album} {user} /></a
>
{/each}
</div>
<!-- Share Album List -->
<div class="w-full flex flex-col place-items-center">
{#each sharedAlbums as album}
<a sveltekit:prefetch href={`albums/${album.id}`}>
<SharedAlbumListTile {album} {user}/>
</a
>
{/each}
</div>
<!-- Empty List -->
{#if sharedAlbums.length === 0}
<div
class="border p-5 w-[50%] m-auto mt-10 bg-gray-50 rounded-3xl flex flex-col place-content-center place-items-center"
>
<img src="/empty-2.svg" alt="Empty shared album" width="500" />
<p class="text-center text-immich-text-gray-500">
Create a shared album to share photos and videos with people in your network
</p>
</div>
{/if}
</section>
</section>
<!-- Empty List -->
{#if sharedAlbums.length === 0}
<div
class="border p-5 w-[50%] m-auto mt-10 bg-gray-50 rounded-3xl flex flex-col place-content-center place-items-center"
>
<img src="/empty-2.svg" alt="Empty shared album" width="500"/>
<p class="text-center text-immich-text-gray-500">
Create a shared album to share photos and videos with people in your network
</p>
</div>
{/if}
</section>
</section>
</section>