mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
refactor(web): admin page layout (#25281)
* refactor(web): admin page layout * chore: remove unused props
This commit is contained in:
parent
256d62e22d
commit
843d563178
7 changed files with 92 additions and 103 deletions
|
|
@ -1,21 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import BottomInfo from '$lib/components/shared-components/side-bar/bottom-info.svelte';
|
|
||||||
import { AppRoute } from '$lib/constants';
|
|
||||||
import { NavbarItem } from '@immich/ui';
|
|
||||||
import { mdiAccountMultipleOutline, mdiBookshelf, mdiCog, mdiServer, mdiTrayFull } from '@mdi/js';
|
|
||||||
import { t } from 'svelte-i18n';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="h-full flex flex-col justify-between gap-2">
|
|
||||||
<div class="flex flex-col pt-8 pe-4 gap-1">
|
|
||||||
<NavbarItem title={$t('users')} href={AppRoute.ADMIN_USERS} icon={mdiAccountMultipleOutline} />
|
|
||||||
<NavbarItem title={$t('external_libraries')} href={AppRoute.ADMIN_LIBRARIES} icon={mdiBookshelf} />
|
|
||||||
<NavbarItem title={$t('admin.queues')} href={AppRoute.ADMIN_QUEUES} icon={mdiTrayFull} />
|
|
||||||
<NavbarItem title={$t('settings')} href={AppRoute.ADMIN_SETTINGS} icon={mdiCog} />
|
|
||||||
<NavbarItem title={$t('server_stats')} href={AppRoute.ADMIN_STATS} icon={mdiServer} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-2 me-4">
|
|
||||||
<BottomInfo />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
61
web/src/lib/components/BreadcrumbActionPage.svelte
Normal file
61
web/src/lib/components/BreadcrumbActionPage.svelte
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HeaderButtonActionItem } from '$lib/types';
|
||||||
|
import {
|
||||||
|
Breadcrumbs,
|
||||||
|
Button,
|
||||||
|
Container,
|
||||||
|
ContextMenuButton,
|
||||||
|
HStack,
|
||||||
|
MenuItemType,
|
||||||
|
Scrollable,
|
||||||
|
isMenuItemType,
|
||||||
|
type BreadcrumbItem,
|
||||||
|
} from '@immich/ui';
|
||||||
|
import { mdiSlashForward } from '@mdi/js';
|
||||||
|
import type { Snippet } from 'svelte';
|
||||||
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
breadcrumbs?: BreadcrumbItem[];
|
||||||
|
actions?: Array<HeaderButtonActionItem | MenuItemType>;
|
||||||
|
children?: Snippet;
|
||||||
|
};
|
||||||
|
|
||||||
|
let { breadcrumbs = [], actions = [], children }: Props = $props();
|
||||||
|
|
||||||
|
const enabledActions = $derived(
|
||||||
|
actions
|
||||||
|
.filter((action): action is HeaderButtonActionItem => !isMenuItemType(action))
|
||||||
|
.filter((action) => action.$if?.() ?? true),
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="h-full flex flex-col">
|
||||||
|
<div class="flex h-16 w-full justify-between items-center border-b py-2 px-4 md:px-2">
|
||||||
|
<Breadcrumbs items={breadcrumbs} separator={mdiSlashForward} />
|
||||||
|
|
||||||
|
{#if enabledActions.length > 0}
|
||||||
|
<div class="hidden md:block">
|
||||||
|
<HStack gap={0}>
|
||||||
|
{#each enabledActions as action, i (i)}
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="small"
|
||||||
|
color={action.color ?? 'secondary'}
|
||||||
|
leadingIcon={action.icon}
|
||||||
|
onclick={() => action.onAction(action)}
|
||||||
|
title={action.data?.title}
|
||||||
|
>
|
||||||
|
{action.title}
|
||||||
|
</Button>
|
||||||
|
{/each}
|
||||||
|
</HStack>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ContextMenuButton aria-label={$t('open')} items={actions} class="md:hidden" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<Scrollable class="grow">
|
||||||
|
<Container class="p-2 pb-16" {children} />
|
||||||
|
</Scrollable>
|
||||||
|
</div>
|
||||||
|
|
@ -1,23 +1,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AdminSidebar from '$lib/components/AdminSidebar.svelte';
|
import BreadcrumbActionPage from '$lib/components/BreadcrumbActionPage.svelte';
|
||||||
import PageContent from '$lib/components/layouts/PageContent.svelte';
|
|
||||||
import NavigationBar from '$lib/components/shared-components/navigation-bar/navigation-bar.svelte';
|
import NavigationBar from '$lib/components/shared-components/navigation-bar/navigation-bar.svelte';
|
||||||
|
import BottomInfo from '$lib/components/shared-components/side-bar/bottom-info.svelte';
|
||||||
|
import { AppRoute } from '$lib/constants';
|
||||||
import { sidebarStore } from '$lib/stores/sidebar.svelte';
|
import { sidebarStore } from '$lib/stores/sidebar.svelte';
|
||||||
import type { HeaderButtonActionItem } from '$lib/types';
|
import type { HeaderButtonActionItem } from '$lib/types';
|
||||||
import {
|
import { AppShell, AppShellHeader, AppShellSidebar, MenuItemType, NavbarItem, type BreadcrumbItem } from '@immich/ui';
|
||||||
AppShell,
|
import { mdiAccountMultipleOutline, mdiBookshelf, mdiCog, mdiServer, mdiTrayFull } from '@mdi/js';
|
||||||
AppShellHeader,
|
|
||||||
AppShellSidebar,
|
|
||||||
Breadcrumbs,
|
|
||||||
Button,
|
|
||||||
ContextMenuButton,
|
|
||||||
HStack,
|
|
||||||
MenuItemType,
|
|
||||||
Scrollable,
|
|
||||||
isMenuItemType,
|
|
||||||
type BreadcrumbItem,
|
|
||||||
} from '@immich/ui';
|
|
||||||
import { mdiSlashForward } from '@mdi/js';
|
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
|
@ -27,52 +16,31 @@
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
};
|
};
|
||||||
|
|
||||||
let { breadcrumbs, actions = [], children }: Props = $props();
|
let { breadcrumbs, actions, children }: Props = $props();
|
||||||
|
|
||||||
const enabledActions = $derived(
|
|
||||||
actions
|
|
||||||
.filter((action): action is HeaderButtonActionItem => !isMenuItemType(action))
|
|
||||||
.filter((action) => action.$if?.() ?? true),
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AppShell>
|
<AppShell>
|
||||||
<AppShellHeader>
|
<AppShellHeader>
|
||||||
<NavigationBar showUploadButton={false} noBorder />
|
<NavigationBar noBorder />
|
||||||
</AppShellHeader>
|
</AppShellHeader>
|
||||||
<AppShellSidebar bind:open={sidebarStore.isOpen} class="border-none shadow-none">
|
<AppShellSidebar
|
||||||
<AdminSidebar />
|
bind:open={sidebarStore.isOpen}
|
||||||
|
class="border-none shadow-none h-full flex flex-col justify-between gap-2"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col pt-8 pe-4 gap-1">
|
||||||
|
<NavbarItem title={$t('users')} href={AppRoute.ADMIN_USERS} icon={mdiAccountMultipleOutline} />
|
||||||
|
<NavbarItem title={$t('external_libraries')} href={AppRoute.ADMIN_LIBRARIES} icon={mdiBookshelf} />
|
||||||
|
<NavbarItem title={$t('admin.queues')} href={AppRoute.ADMIN_QUEUES} icon={mdiTrayFull} />
|
||||||
|
<NavbarItem title={$t('settings')} href={AppRoute.ADMIN_SETTINGS} icon={mdiCog} />
|
||||||
|
<NavbarItem title={$t('server_stats')} href={AppRoute.ADMIN_STATS} icon={mdiServer} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-2 me-4">
|
||||||
|
<BottomInfo />
|
||||||
|
</div>
|
||||||
</AppShellSidebar>
|
</AppShellSidebar>
|
||||||
|
|
||||||
<div class="h-full flex flex-col">
|
<BreadcrumbActionPage {breadcrumbs} {actions}>
|
||||||
<div class="flex h-16 w-full justify-between items-center border-b py-2 px-4 md:px-2">
|
{@render children?.()}
|
||||||
<Breadcrumbs items={breadcrumbs} separator={mdiSlashForward} />
|
</BreadcrumbActionPage>
|
||||||
|
|
||||||
{#if enabledActions.length > 0}
|
|
||||||
<div class="hidden md:block">
|
|
||||||
<HStack gap={0}>
|
|
||||||
{#each enabledActions as action, i (i)}
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="small"
|
|
||||||
color={action.color ?? 'secondary'}
|
|
||||||
leadingIcon={action.icon}
|
|
||||||
onclick={() => action.onAction(action)}
|
|
||||||
title={action.data?.title}
|
|
||||||
>
|
|
||||||
{action.title}
|
|
||||||
</Button>
|
|
||||||
{/each}
|
|
||||||
</HStack>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ContextMenuButton aria-label={$t('open')} items={actions} class="md:hidden" />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<Scrollable class="grow">
|
|
||||||
<PageContent>
|
|
||||||
{@render children?.()}
|
|
||||||
</PageContent>
|
|
||||||
</Scrollable>
|
|
||||||
</div>
|
|
||||||
</AppShell>
|
</AppShell>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { Container } from '@immich/ui';
|
|
||||||
import type { Snippet } from 'svelte';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
children?: Snippet;
|
|
||||||
};
|
|
||||||
|
|
||||||
const { children }: Props = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Container class="p-2 pb-16" {children} />
|
|
||||||
|
|
@ -14,13 +14,11 @@
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
hideNavbar?: boolean;
|
hideNavbar?: boolean;
|
||||||
showUploadButton?: boolean;
|
|
||||||
title?: string | undefined;
|
title?: string | undefined;
|
||||||
description?: string | undefined;
|
description?: string | undefined;
|
||||||
scrollbar?: boolean;
|
scrollbar?: boolean;
|
||||||
use?: ActionArray;
|
use?: ActionArray;
|
||||||
actions?: Array<HeaderButtonActionItem | MenuItemType>;
|
actions?: Array<HeaderButtonActionItem | MenuItemType>;
|
||||||
header?: Snippet;
|
|
||||||
sidebar?: Snippet;
|
sidebar?: Snippet;
|
||||||
buttons?: Snippet;
|
buttons?: Snippet;
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
|
|
@ -28,13 +26,11 @@
|
||||||
|
|
||||||
let {
|
let {
|
||||||
hideNavbar = false,
|
hideNavbar = false,
|
||||||
showUploadButton = false,
|
|
||||||
title = undefined,
|
title = undefined,
|
||||||
description = undefined,
|
description = undefined,
|
||||||
scrollbar = true,
|
scrollbar = true,
|
||||||
use = [],
|
use = [],
|
||||||
actions = [],
|
actions = [],
|
||||||
header,
|
|
||||||
sidebar,
|
sidebar,
|
||||||
buttons,
|
buttons,
|
||||||
children,
|
children,
|
||||||
|
|
@ -52,10 +48,8 @@
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
{#if !hideNavbar}
|
{#if !hideNavbar}
|
||||||
<NavigationBar {showUploadButton} onUploadClick={() => openFileUploadDialog()} />
|
<NavigationBar onUploadClick={() => openFileUploadDialog()} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{@render header?.()}
|
|
||||||
</header>
|
</header>
|
||||||
<div
|
<div
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,13 @@
|
||||||
import UserAvatar from '../user-avatar.svelte';
|
import UserAvatar from '../user-avatar.svelte';
|
||||||
import AccountInfoPanel from './account-info-panel.svelte';
|
import AccountInfoPanel from './account-info-panel.svelte';
|
||||||
|
|
||||||
interface Props {
|
type Props = {
|
||||||
showUploadButton?: boolean;
|
|
||||||
onUploadClick?: () => void;
|
onUploadClick?: () => void;
|
||||||
// TODO: remove once this is only used in <AppShellHeader>
|
// TODO: remove once this is only used in <AppShellHeader>
|
||||||
noBorder?: boolean;
|
noBorder?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
let { showUploadButton = true, onUploadClick, noBorder = false }: Props = $props();
|
let { onUploadClick, noBorder = false }: Props = $props();
|
||||||
|
|
||||||
let shouldShowAccountInfoPanel = $state(false);
|
let shouldShowAccountInfoPanel = $state(false);
|
||||||
let shouldShowNotificationPanel = $state(false);
|
let shouldShowNotificationPanel = $state(false);
|
||||||
|
|
@ -105,7 +104,7 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !page.url.pathname.includes('/admin') && showUploadButton && onUploadClick}
|
{#if !page.url.pathname.includes('/admin') && onUploadClick}
|
||||||
<Button
|
<Button
|
||||||
leadingIcon={mdiTrayArrowUp}
|
leadingIcon={mdiTrayArrowUp}
|
||||||
onclick={onUploadClick}
|
onclick={onUploadClick}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} showUploadButton scrollbar={false}>
|
<UserPageLayout hideNavbar={assetInteraction.selectionActive} scrollbar={false}>
|
||||||
<Timeline
|
<Timeline
|
||||||
enableRouting={true}
|
enableRouting={true}
|
||||||
bind:timelineManager
|
bind:timelineManager
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue