fix(web): user admin pages (#24185)

fix: user admin pages
This commit is contained in:
Jason Rasmussen
2025-11-25 16:35:37 -05:00
committed by GitHub
parent 5226898184
commit 2d5ec528d5
23 changed files with 129 additions and 173 deletions

View File

@@ -58,7 +58,7 @@
});
</script>
<AdminPageLayout title={data.meta.title}>
<AdminPageLayout breadcrumbs={[{ title: data.meta.title }]}>
{#snippet buttons()}
<HStack gap={0}>
{#if pausedJobs.length > 0}

View File

@@ -58,7 +58,7 @@
onLibraryDelete={handleDeleteLibrary}
/>
<AdminPageLayout title={data.meta.title}>
<AdminPageLayout breadcrumbs={[{ title: data.meta.title }]}>
{#snippet buttons()}
<div class="flex justify-end gap-2">
{#if libraries.length > 0}

View File

@@ -39,7 +39,12 @@
onLibraryDelete={({ id }) => id === library.id && goto(AppRoute.ADMIN_LIBRARY_MANAGEMENT)}
/>
<AdminPageLayout title={data.meta.title}>
<AdminPageLayout
breadcrumbs={[
{ title: $t('admin.external_library_management'), href: AppRoute.ADMIN_LIBRARY_MANAGEMENT },
{ title: library.name },
]}
>
{#snippet buttons()}
<div class="flex justify-end gap-2">
<HeaderButton action={Scan} />

View File

@@ -24,7 +24,7 @@
});
</script>
<AdminPageLayout title={data.meta.title}>
<AdminPageLayout breadcrumbs={[{ title: data.meta.title }]}>
<section id="setting-content" class="flex place-content-center sm:mx-4">
<section class="w-full pb-28 sm:w-5/6 md:w-212.5">
<ServerStatisticsPanel {stats} />

View File

@@ -215,7 +215,7 @@
);
</script>
<AdminPageLayout title={data.meta.title}>
<AdminPageLayout breadcrumbs={[{ title: data.meta.title }]}>
{#snippet buttons()}
<HStack gap={1}>
<div class="hidden lg:block">

View File

@@ -2,12 +2,11 @@
import HeaderButton from '$lib/components/HeaderButton.svelte';
import AdminPageLayout from '$lib/components/layouts/AdminPageLayout.svelte';
import OnEvents from '$lib/components/OnEvents.svelte';
import TableButton from '$lib/components/TableButton.svelte';
import { getUserAdminActions, getUserAdminsActions } from '$lib/services/user-admin.service';
import { getUserAdminsActions, handleNavigateUserAdmin } from '$lib/services/user-admin.service';
import { locale } from '$lib/stores/preferences.store';
import { getByteUnitString } from '$lib/utils/byte-units';
import { type UserAdminResponseDto } from '@immich/sdk';
import { HStack, Icon } from '@immich/ui';
import { searchUsersAdmin, type UserAdminResponseDto } from '@immich/sdk';
import { Button, HStack, Icon } from '@immich/ui';
import { mdiInfinity } from '@mdi/js';
import { t } from 'svelte-i18n';
import type { PageData } from './$types';
@@ -20,9 +19,11 @@
let allUsers: UserAdminResponseDto[] = $state(data.allUsers);
const onUpdate = (user: UserAdminResponseDto) => {
const onUpdate = async (user: UserAdminResponseDto) => {
const index = allUsers.findIndex(({ id }) => id === user.id);
if (index !== -1) {
if (index === -1) {
allUsers = await searchUsersAdmin({ withDeleted: true });
} else {
allUsers[index] = user;
}
};
@@ -42,7 +43,7 @@
{onUserAdminDeleted}
/>
<AdminPageLayout title={data.meta.title}>
<AdminPageLayout breadcrumbs={[{ title: data.meta.title }]}>
{#snippet buttons()}
<HStack gap={1}>
<HeaderButton action={Create} />
@@ -60,12 +61,10 @@
>
<th class="hidden sm:block w-3/12 text-center text-sm font-medium">{$t('name')}</th>
<th class="hidden xl:block w-3/12 2xl:w-2/12 text-center text-sm font-medium">{$t('has_quota')}</th>
<th class="w-4/12 lg:w-3/12 xl:w-2/12 text-center text-sm font-medium">{$t('action')}</th>
</tr>
</thead>
<tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray">
{#each allUsers as user (user.id)}
{@const { View, ContextMenu } = getUserAdminActions($t, user)}
<tr
class="flex h-20 overflow-hidden w-full place-items-center text-center dark:text-immich-dark-fg {user.deletedAt
? 'bg-red-300 dark:bg-red-900'
@@ -87,8 +86,7 @@
<td
class="flex flex-row flex-wrap justify-center gap-x-2 gap-y-1 w-4/12 lg:w-3/12 xl:w-2/12 text-ellipsis break-all text-sm"
>
<TableButton action={View} />
<TableButton action={ContextMenu} />
<Button onclick={() => handleNavigateUserAdmin(user)}>{$t('view')}</Button>
</td>
</tr>
{/each}

View File

@@ -8,6 +8,7 @@
import DeviceCard from '$lib/components/user-settings-page/device-card.svelte';
import FeatureSetting from '$lib/components/users/FeatureSetting.svelte';
import { AppRoute } from '$lib/constants';
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
import { getUserAdminActions } from '$lib/services/user-admin.service';
import { locale } from '$lib/stores/preferences.store';
import { createDateFormatter, findLocale } from '$lib/utils';
@@ -15,6 +16,7 @@
import { type UserAdminResponseDto } from '@immich/sdk';
import {
Alert,
Badge,
Card,
CardBody,
CardHeader,
@@ -39,6 +41,7 @@
mdiPlayCircle,
mdiTrashCanOutline,
} from '@mdi/js';
import { DateTime } from 'luxon';
import { t } from 'svelte-i18n';
import type { PageData } from './$types';
@@ -77,7 +80,7 @@
return 'bg-primary';
};
const UserAdminActions = $derived(getUserAdminActions($t, user));
const { ResetPassword, ResetPinCode, Update, Delete, Restore } = $derived(getUserAdminActions($t, user));
const onUpdate = (update: UserAdminResponseDto) => {
if (update.id === user.id) {
@@ -90,6 +93,9 @@
await goto(AppRoute.ADMIN_USERS);
}
};
const getDeleteDate = (deletedAt: string): Date =>
DateTime.fromISO(deletedAt).plus({ days: serverConfigManager.value.userDeleteDelay }).toJSDate();
</script>
<OnEvents
@@ -99,14 +105,19 @@
{onUserAdminDeleted}
/>
<AdminPageLayout title={data.meta.title}>
<AdminPageLayout
breadcrumbs={[{ title: $t('admin.user_management'), href: AppRoute.ADMIN_USERS }, { title: user.name }]}
>
{#snippet buttons()}
<HStack gap={0}>
<HeaderButton action={UserAdminActions.ResetPassword} />
<HeaderButton action={UserAdminActions.ResetPinCode} />
<HeaderButton action={UserAdminActions.Update} />
<HeaderButton action={UserAdminActions.Restore} />
<HeaderButton action={UserAdminActions.Delete} />
<HeaderButton action={ResetPassword} />
<HeaderButton action={ResetPinCode} />
<HeaderButton action={Update} />
<HeaderButton
action={Restore}
title={$t('admin.user_restore_scheduled_removal', { values: { date: getDeleteDate(user.deletedAt!) } })}
/>
<HeaderButton action={Delete} />
</HStack>
{/snippet}
<div>
@@ -116,9 +127,16 @@
{/if}
<div class="grid gap-4 grid-cols-1 lg:grid-cols-2 w-full">
<div class="col-span-full flex gap-4 items-center my-4">
<UserAvatar {user} size="md" />
<Heading tag="h1" size="large">{user.name}</Heading>
<div class="col-span-full flex flex-col gap-4 my-4">
<div class="flex items-center gap-4">
<UserAvatar {user} size="md" />
<Heading tag="h1" size="large">{user.name}</Heading>
</div>
{#if user.isAdmin}
<div>
<Badge color="primary" size="small">{$t('admin.admin_user')}</Badge>
</div>
{/if}
</div>
<div class="col-span-full">
<div class="flex flex-col lg:flex-row gap-4 w-full">