Compare commits

...

2 Commits

Author SHA1 Message Date
Jason Rasmussen
c55564487e chore: remove unused props 2026-01-15 15:09:50 -05:00
Jason Rasmussen
887033ced3 refactor(web): admin page layout 2026-01-15 14:09:09 -05:00
7 changed files with 92 additions and 103 deletions

View File

@@ -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>

View 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>

View File

@@ -1,23 +1,12 @@
<script lang="ts">
import AdminSidebar from '$lib/components/AdminSidebar.svelte';
import PageContent from '$lib/components/layouts/PageContent.svelte';
import BreadcrumbActionPage from '$lib/components/BreadcrumbActionPage.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 type { HeaderButtonActionItem } from '$lib/types';
import {
AppShell,
AppShellHeader,
AppShellSidebar,
Breadcrumbs,
Button,
ContextMenuButton,
HStack,
MenuItemType,
Scrollable,
isMenuItemType,
type BreadcrumbItem,
} from '@immich/ui';
import { mdiSlashForward } from '@mdi/js';
import { AppShell, AppShellHeader, AppShellSidebar, MenuItemType, NavbarItem, type BreadcrumbItem } from '@immich/ui';
import { mdiAccountMultipleOutline, mdiBookshelf, mdiCog, mdiServer, mdiTrayFull } from '@mdi/js';
import type { Snippet } from 'svelte';
import { t } from 'svelte-i18n';
@@ -27,52 +16,31 @@
children?: Snippet;
};
let { breadcrumbs, actions = [], children }: Props = $props();
const enabledActions = $derived(
actions
.filter((action): action is HeaderButtonActionItem => !isMenuItemType(action))
.filter((action) => action.$if?.() ?? true),
);
let { breadcrumbs, actions, children }: Props = $props();
</script>
<AppShell>
<AppShellHeader>
<NavigationBar showUploadButton={false} noBorder />
<NavigationBar noBorder />
</AppShellHeader>
<AppShellSidebar bind:open={sidebarStore.isOpen} class="border-none shadow-none">
<AdminSidebar />
<AppShellSidebar
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>
<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">
<PageContent>
{@render children?.()}
</PageContent>
</Scrollable>
</div>
<BreadcrumbActionPage {breadcrumbs} {actions}>
{@render children?.()}
</BreadcrumbActionPage>
</AppShell>

View File

@@ -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} />

View File

@@ -14,13 +14,11 @@
interface Props {
hideNavbar?: boolean;
showUploadButton?: boolean;
title?: string | undefined;
description?: string | undefined;
scrollbar?: boolean;
use?: ActionArray;
actions?: Array<HeaderButtonActionItem | MenuItemType>;
header?: Snippet;
sidebar?: Snippet;
buttons?: Snippet;
children?: Snippet;
@@ -28,13 +26,11 @@
let {
hideNavbar = false,
showUploadButton = false,
title = undefined,
description = undefined,
scrollbar = true,
use = [],
actions = [],
header,
sidebar,
buttons,
children,
@@ -52,10 +48,8 @@
<header>
{#if !hideNavbar}
<NavigationBar {showUploadButton} onUploadClick={() => openFileUploadDialog()} />
<NavigationBar onUploadClick={() => openFileUploadDialog()} />
{/if}
{@render header?.()}
</header>
<div
tabindex="-1"

View File

@@ -25,14 +25,13 @@
import UserAvatar from '../user-avatar.svelte';
import AccountInfoPanel from './account-info-panel.svelte';
interface Props {
showUploadButton?: boolean;
type Props = {
onUploadClick?: () => void;
// TODO: remove once this is only used in <AppShellHeader>
noBorder?: boolean;
}
};
let { showUploadButton = true, onUploadClick, noBorder = false }: Props = $props();
let { onUploadClick, noBorder = false }: Props = $props();
let shouldShowAccountInfoPanel = $state(false);
let shouldShowNotificationPanel = $state(false);
@@ -105,7 +104,7 @@
/>
{/if}
{#if !page.url.pathname.includes('/admin') && showUploadButton && onUploadClick}
{#if !page.url.pathname.includes('/admin') && onUploadClick}
<Button
leadingIcon={mdiTrayArrowUp}
onclick={onUploadClick}

View File

@@ -102,7 +102,7 @@
);
</script>
<UserPageLayout hideNavbar={assetInteraction.selectionActive} showUploadButton scrollbar={false}>
<UserPageLayout hideNavbar={assetInteraction.selectionActive} scrollbar={false}>
<Timeline
enableRouting={true}
bind:timelineManager