mirror of
https://github.com/immich-app/immich.git
synced 2025-12-24 01:11:32 +03:00
Add web interface with admin functionality (#167)
This commit is contained in:
15
web/src/lib/components/shared/click-outside.ts
Normal file
15
web/src/lib/components/shared/click-outside.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export function clickOutside(node: Node) {
|
||||
const handleClick = (event: any) => {
|
||||
if (!node.contains(event.target)) {
|
||||
node.dispatchEvent(new CustomEvent("outclick"));
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("click", handleClick, true);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
document.removeEventListener("click", handleClick, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
17
web/src/lib/components/shared/full-screen-modal.svelte
Normal file
17
web/src/lib/components/shared/full-screen-modal.svelte
Normal file
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { clickOutside } from './click-outside';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
</script>
|
||||
|
||||
<section
|
||||
in:fade={{ duration: 100 }}
|
||||
out:fade={{ duration: 100 }}
|
||||
class="absolute w-full h-full bg-black/40 z-[100] flex place-items-center place-content-center "
|
||||
>
|
||||
<div class="bg-immich-bg z-[9999] rounded-md" use:clickOutside on:outclick={() => dispatch('clickOutside')}>
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
60
web/src/lib/components/shared/navigation-bar.svelte
Normal file
60
web/src/lib/components/shared/navigation-bar.svelte
Normal file
@@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import type { ImmichUser } from '$lib/models/immich-user';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
export let user: ImmichUser;
|
||||
|
||||
let shouldShowAccountInfo = false;
|
||||
|
||||
const getFirstLetter = (text?: string) => {
|
||||
return text?.charAt(0).toUpperCase();
|
||||
};
|
||||
</script>
|
||||
|
||||
<section id="dashboard-navbar" class="fixed w-screen z-[100] bg-immich-bg text-sm">
|
||||
<div class="flex border place-items-center px-6 py-2 ">
|
||||
<a class="flex gap-2 place-items-center hover:cursor-pointer" href="/photos">
|
||||
<img src="/immich-logo.svg" alt="immich logo" height="35" width="35" />
|
||||
<h1 class="font-immich-title text-2xl text-immich-primary">Immich</h1>
|
||||
</a>
|
||||
<div class="flex-1 ml-24">
|
||||
<div class="w-[50%] border rounded-md bg-gray-200 px-8 py-4">Search</div>
|
||||
</div>
|
||||
<section class="flex gap-6 place-items-center">
|
||||
<!-- <div>Upload</div> -->
|
||||
|
||||
{#if user.isAdmin}
|
||||
<a
|
||||
class={`hover:text-immich-primary font-medium ${
|
||||
$page.url.pathname == '/admin' && 'text-immich-primary underline'
|
||||
}`}
|
||||
href="/admin">Administration</a
|
||||
>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
on:mouseover={() => (shouldShowAccountInfo = true)}
|
||||
on:focus={() => (shouldShowAccountInfo = true)}
|
||||
on:mouseleave={() => (shouldShowAccountInfo = false)}
|
||||
>
|
||||
<button
|
||||
class="flex place-items-center place-content-center rounded-full bg-immich-primary/80 h-10 w-10 text-gray-100 hover:bg-immich-primary"
|
||||
>
|
||||
{getFirstLetter(user.firstName)}{getFirstLetter(user.lastName)}
|
||||
</button>
|
||||
|
||||
{#if shouldShowAccountInfo}
|
||||
<div
|
||||
in:fade={{ delay: 500, duration: 150 }}
|
||||
out:fade={{ delay: 200, duration: 150 }}
|
||||
class="absolute -bottom-12 right-5 border bg-gray-500 text-[12px] text-gray-100 p-2 rounded-md shadow-md"
|
||||
>
|
||||
<p>{user.firstName} {user.lastName}</p>
|
||||
<p>{user.email}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
27
web/src/lib/components/shared/side-bar-button.svelte
Normal file
27
web/src/lib/components/shared/side-bar-button.svelte
Normal file
@@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
export let title: string;
|
||||
export let logo: any;
|
||||
export let actionType: AdminSideBarSelection | AppSideBarSelection;
|
||||
export let isSelected: boolean;
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import type { AdminSideBarSelection, AppSideBarSelection } from '../../models/admin-sidebar-selection';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const onButtonClicked = () => {
|
||||
dispatch('selected', {
|
||||
actionType,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<div
|
||||
on:click={onButtonClicked}
|
||||
class={`flex gap-4 place-items-center pl-5 py-3 rounded-tr-xl rounded-br-xl hover:bg-gray-200 hover:text-immich-primary hover:cursor-pointer
|
||||
${isSelected && 'bg-immich-primary/10 text-immich-primary hover:bg-immich-primary/50'}
|
||||
`}
|
||||
>
|
||||
<svelte:component this={logo} size="24" />
|
||||
<p class="font-medium text-sm">{title}</p>
|
||||
</div>
|
||||
Reference in New Issue
Block a user