diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 73f272d7fc..1d1606868b 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -23,7 +23,8 @@ export enum AssetAction { export enum AppRoute { ADMIN_USERS = '/admin/users', ADMIN_USERS_NEW = '/admin/users/new', - ADMIN_LIBRARY_MANAGEMENT = '/admin/library-management', + ADMIN_LIBRARIES = '/admin/library-management', + ADMIN_LIBRARIES_NEW = '/admin/library-management/new', ADMIN_SETTINGS = '/admin/system-settings', ADMIN_STATS = '/admin/server-status', ADMIN_QUEUES = '/admin/queues', diff --git a/web/src/lib/modals/LibraryRenameModal.svelte b/web/src/lib/modals/LibraryRenameModal.svelte deleted file mode 100644 index 0a7d675b11..0000000000 --- a/web/src/lib/modals/LibraryRenameModal.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - - - -
- - - -
-
- - - - - - - -
diff --git a/web/src/lib/services/library.service.ts b/web/src/lib/services/library.service.ts index a7f2ba25e7..62371e2512 100644 --- a/web/src/lib/services/library.service.ts +++ b/web/src/lib/services/library.service.ts @@ -1,12 +1,10 @@ import { goto } from '$app/navigation'; import { AppRoute } from '$lib/constants'; import { eventManager } from '$lib/managers/event-manager.svelte'; -import LibraryCreateModal from '$lib/modals/LibraryCreateModal.svelte'; import LibraryExclusionPatternAddModal from '$lib/modals/LibraryExclusionPatternAddModal.svelte'; import LibraryExclusionPatternEditModal from '$lib/modals/LibraryExclusionPatternEditModal.svelte'; import LibraryFolderAddModal from '$lib/modals/LibraryFolderAddModal.svelte'; import LibraryFolderEditModal from '$lib/modals/LibraryFolderEditModal.svelte'; -import LibraryRenameModal from '$lib/modals/LibraryRenameModal.svelte'; import { handleError } from '$lib/utils/handle-error'; import { getFormatter } from '$lib/utils/i18n'; import { @@ -19,6 +17,7 @@ import { updateLibrary, type CreateLibraryDto, type LibraryResponseDto, + type UpdateLibraryDto, } from '@immich/sdk'; import { modalManager, toastManager, type ActionItem } from '@immich/ui'; import { mdiPencilOutline, mdiPlusBoxOutline, mdiSync, mdiTrashCanOutline } from '@mdi/js'; @@ -38,7 +37,7 @@ export const getLibrariesActions = ($t: MessageFormatter, libraries: LibraryResp title: $t('create_library'), type: $t('command'), icon: mdiPlusBoxOutline, - onAction: () => handleShowLibraryCreateModal(), + onAction: () => goto(AppRoute.ADMIN_LIBRARIES_NEW), shortcuts: { shift: true, key: 'n' }, }; @@ -46,11 +45,11 @@ export const getLibrariesActions = ($t: MessageFormatter, libraries: LibraryResp }; export const getLibraryActions = ($t: MessageFormatter, library: LibraryResponseDto) => { - const Rename: ActionItem = { + const Edit: ActionItem = { icon: mdiPencilOutline, type: $t('command'), - title: $t('rename'), - onAction: () => modalManager.show(LibraryRenameModal, { library }), + title: $t('edit'), + onAction: () => goto(`${AppRoute.ADMIN_LIBRARIES}/${library.id}/edit`), shortcuts: { key: 'r' }, }; @@ -85,7 +84,7 @@ export const getLibraryActions = ($t: MessageFormatter, library: LibraryResponse shortcuts: { shift: true, key: 'r' }, }; - return { Rename, Delete, AddFolder, AddExclusionPattern, Scan }; + return { Edit, Delete, AddFolder, AddExclusionPattern, Scan }; }; export const getLibraryFolderActions = ($t: MessageFormatter, library: LibraryResponseDto, folder: string) => { @@ -150,7 +149,7 @@ const handleScanLibrary = async (library: LibraryResponseDto) => { }; export const handleViewLibrary = async (library: LibraryResponseDto) => { - await goto(`${AppRoute.ADMIN_LIBRARY_MANAGEMENT}/${library.id}`); + await goto(`${AppRoute.ADMIN_LIBRARIES}/${library.id}`); }; export const handleCreateLibrary = async (dto: CreateLibraryDto) => { @@ -160,33 +159,24 @@ export const handleCreateLibrary = async (dto: CreateLibraryDto) => { const library = await createLibrary({ createLibraryDto: dto }); eventManager.emit('LibraryCreate', library); toastManager.success($t('admin.library_created', { values: { library: library.name } })); - return true; + return library; } catch (error) { handleError(error, $t('errors.unable_to_create_library')); - return false; } }; -export const handleRenameLibrary = async (library: { id: string }, name?: string) => { +export const handleUpdateLibrary = async (library: LibraryResponseDto, dto: UpdateLibraryDto) => { const $t = await getFormatter(); - if (!name) { - return false; - } - try { - const updatedLibrary = await updateLibrary({ - id: library.id, - updateLibraryDto: { name }, - }); + const updatedLibrary = await updateLibrary({ id: library.id, updateLibraryDto: dto }); eventManager.emit('LibraryUpdate', updatedLibrary); toastManager.success($t('admin.library_updated')); + return true; } catch (error) { handleError(error, $t('errors.unable_to_update_library')); return false; } - - return true; }; const handleDeleteLibrary = async (library: LibraryResponseDto) => { @@ -357,7 +347,3 @@ const handleDeleteExclusionPattern = async (library: LibraryResponseDto, exclusi handleError(error, $t('errors.unable_to_update_library')); } }; - -export const handleShowLibraryCreateModal = async () => { - await modalManager.show(LibraryCreateModal, {}); -}; diff --git a/web/src/lib/services/user-admin.service.ts b/web/src/lib/services/user-admin.service.ts index 557d01bfb0..5fd4ff99fd 100644 --- a/web/src/lib/services/user-admin.service.ts +++ b/web/src/lib/services/user-admin.service.ts @@ -4,7 +4,6 @@ import { eventManager } from '$lib/managers/event-manager.svelte'; import { serverConfigManager } from '$lib/managers/server-config-manager.svelte'; import PasswordResetSuccessModal from '$lib/modals/PasswordResetSuccessModal.svelte'; import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte'; -import UserEditModal from '$lib/modals/UserEditModal.svelte'; import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte'; import { user as authUser } from '$lib/stores/user.store'; import type { HeaderButtonActionItem } from '$lib/types'; @@ -50,7 +49,7 @@ export const getUserAdminActions = ($t: MessageFormatter, user: UserAdminRespons const Update: ActionItem = { icon: mdiPencilOutline, title: $t('edit'), - onAction: () => modalManager.show(UserEditModal, { user }), + onAction: () => goto(`${AppRoute.ADMIN_USERS}/${user.id}/edit`), }; const Delete: ActionItem = { diff --git a/web/src/lib/sidebars/AdminSidebar.svelte b/web/src/lib/sidebars/AdminSidebar.svelte index 919c072527..fa660d7e2f 100644 --- a/web/src/lib/sidebars/AdminSidebar.svelte +++ b/web/src/lib/sidebars/AdminSidebar.svelte @@ -9,7 +9,7 @@
- + diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 77a3d402b2..3a1d4f49f9 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -166,7 +166,7 @@ title: $t('external_libraries'), description: $t('admin.external_libraries_page_description'), icon: mdiBookshelf, - onAction: () => goto(AppRoute.ADMIN_LIBRARY_MANAGEMENT), + onAction: () => goto(AppRoute.ADMIN_LIBRARIES), }, { title: $t('server_stats'), diff --git a/web/src/routes/admin/library-management/+page.svelte b/web/src/routes/admin/library-management/(list)/+layout.svelte similarity index 92% rename from web/src/routes/admin/library-management/+page.svelte rename to web/src/routes/admin/library-management/(list)/+layout.svelte index e61b4433be..3c2c1f9d57 100644 --- a/web/src/routes/admin/library-management/+page.svelte +++ b/web/src/routes/admin/library-management/(list)/+layout.svelte @@ -4,27 +4,29 @@ import OnEvents from '$lib/components/OnEvents.svelte'; import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; import { AppRoute } from '$lib/constants'; - import { getLibrariesActions, handleShowLibraryCreateModal, handleViewLibrary } from '$lib/services/library.service'; + import { getLibrariesActions, handleViewLibrary } from '$lib/services/library.service'; import { locale } from '$lib/stores/preferences.store'; import { getBytesWithUnit } from '$lib/utils/byte-units'; import { getLibrary, getLibraryStatistics, type LibraryResponseDto } from '@immich/sdk'; import { Button, CommandPaletteContext } from '@immich/ui'; + import type { Snippet } from 'svelte'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; import type { PageData } from './$types'; type Props = { + children?: Snippet; data: PageData; }; - let { data }: Props = $props(); + let { children, data }: Props = $props(); let libraries = $state(data.libraries); let statistics = $state(data.statistics); let owners = $state(data.owners); const onLibraryCreate = async (library: LibraryResponseDto) => { - await goto(`${AppRoute.ADMIN_LIBRARY_MANAGEMENT}/${library.id}`); + await goto(`${AppRoute.ADMIN_LIBRARIES}/${library.id}`); }; const onLibraryUpdate = async (library: LibraryResponseDto) => { @@ -100,10 +102,12 @@ {:else} goto(AppRoute.ADMIN_LIBRARIES_NEW)} class="mt-10 mx-auto" /> {/if} + + {@render children?.()}
diff --git a/web/src/routes/admin/library-management/+page.ts b/web/src/routes/admin/library-management/(list)/+layout.ts similarity index 100% rename from web/src/routes/admin/library-management/+page.ts rename to web/src/routes/admin/library-management/(list)/+layout.ts diff --git a/web/src/routes/admin/library-management/(list)/+page.svelte b/web/src/routes/admin/library-management/(list)/+page.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/web/src/lib/modals/LibraryCreateModal.svelte b/web/src/routes/admin/library-management/(list)/new/+page.svelte similarity index 77% rename from web/src/lib/modals/LibraryCreateModal.svelte rename to web/src/routes/admin/library-management/(list)/new/+page.svelte index d2d7f62c02..b236b83ce8 100644 --- a/web/src/lib/modals/LibraryCreateModal.svelte +++ b/web/src/routes/admin/library-management/(list)/new/+page.svelte @@ -1,5 +1,7 @@ diff --git a/web/src/routes/admin/library-management/(list)/new/+page.ts b/web/src/routes/admin/library-management/(list)/new/+page.ts new file mode 100644 index 0000000000..da3756bcf6 --- /dev/null +++ b/web/src/routes/admin/library-management/(list)/new/+page.ts @@ -0,0 +1,14 @@ +import { authenticate } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import type { PageLoad } from './$types'; + +export const load = (async ({ url }) => { + await authenticate(url, { admin: true }); + const $t = await getFormatter(); + + return { + meta: { + title: $t('external_libraries'), + }, + }; +}) satisfies PageLoad; diff --git a/web/src/routes/admin/library-management/[id]/+layout.svelte b/web/src/routes/admin/library-management/[id]/+layout.svelte new file mode 100644 index 0000000000..3e7dd05e14 --- /dev/null +++ b/web/src/routes/admin/library-management/[id]/+layout.svelte @@ -0,0 +1,119 @@ + + + + + + + + +
+ {library.name} +
+ + + +
+ + + {#if library.importPaths.length === 0} + modalManager.show(LibraryFolderAddModal, { library })} + /> + {:else} + + + {#each library.importPaths as folder (folder)} + {@const { Edit, Delete } = getLibraryFolderActions($t, library, folder)} + + + + + {/each} + +
+ {folder} + + + +
+ {/if} +
+ + + + + {#each library.exclusionPatterns as exclusionPattern (exclusionPattern)} + {@const { Edit, Delete } = getLibraryExclusionPatternActions($t, library, exclusionPattern)} + + + + + {/each} + +
+ {exclusionPattern} + + + +
+
+
+ {@render children?.()} +
+
diff --git a/web/src/routes/admin/library-management/[id]/+layout.ts b/web/src/routes/admin/library-management/[id]/+layout.ts new file mode 100644 index 0000000000..242053bcb1 --- /dev/null +++ b/web/src/routes/admin/library-management/[id]/+layout.ts @@ -0,0 +1,30 @@ +import { AppRoute } from '$lib/constants'; +import { authenticate } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import { getLibrary, getLibraryStatistics, type LibraryResponseDto } from '@immich/sdk'; +import { redirect } from '@sveltejs/kit'; +import type { LayoutLoad } from './$types'; + +export const load = (async ({ params: { id }, url }) => { + await authenticate(url, { admin: true }); + + let library: LibraryResponseDto; + + try { + library = await getLibrary({ id }); + console.log(`Fetched latest library: ${library.name}`); + } catch { + redirect(302, AppRoute.ADMIN_LIBRARIES); + } + + const statistics = await getLibraryStatistics({ id }); + const $t = await getFormatter(); + + return { + library, + statistics, + meta: { + title: $t('admin.library_details'), + }, + }; +}) satisfies LayoutLoad; diff --git a/web/src/routes/admin/library-management/[id]/+page.svelte b/web/src/routes/admin/library-management/[id]/+page.svelte index 6bffbe39aa..e69de29bb2 100644 --- a/web/src/routes/admin/library-management/[id]/+page.svelte +++ b/web/src/routes/admin/library-management/[id]/+page.svelte @@ -1,105 +0,0 @@ - - - (library = newLibrary)} - onLibraryDelete={({ id }) => id === library.id && goto(AppRoute.ADMIN_LIBRARY_MANAGEMENT)} -/> - - - - - -
- {library.name} -
- - - -
- - - {#if library.importPaths.length === 0} - modalManager.show(LibraryFolderAddModal, { library })} - /> - {:else} - - - {#each library.importPaths as folder (folder)} - {@const { Edit, Delete } = getLibraryFolderActions($t, library, folder)} - - - - - {/each} - -
- {folder} - - - -
- {/if} -
- - - - - {#each library.exclusionPatterns as exclusionPattern (exclusionPattern)} - {@const { Edit, Delete } = getLibraryExclusionPatternActions($t, library, exclusionPattern)} - - - - - {/each} - -
- {exclusionPattern} - - - -
-
-
-
-
diff --git a/web/src/routes/admin/library-management/[id]/+page.ts b/web/src/routes/admin/library-management/[id]/+page.ts index 77ce1eb1c8..10f0f33e11 100644 --- a/web/src/routes/admin/library-management/[id]/+page.ts +++ b/web/src/routes/admin/library-management/[id]/+page.ts @@ -1,26 +1,13 @@ -import { AppRoute } from '$lib/constants'; import { authenticate } from '$lib/utils/auth'; import { getFormatter } from '$lib/utils/i18n'; -import { getLibrary, getLibraryStatistics, type LibraryResponseDto } from '@immich/sdk'; -import { redirect } from '@sveltejs/kit'; import type { PageLoad } from './$types'; -export const load = (async ({ params: { id }, url }) => { +export const load = (async ({ url }) => { await authenticate(url, { admin: true }); - let library: LibraryResponseDto; - try { - library = await getLibrary({ id }); - } catch { - redirect(302, AppRoute.ADMIN_LIBRARY_MANAGEMENT); - } - - const statistics = await getLibraryStatistics({ id }); const $t = await getFormatter(); return { - library, - statistics, meta: { title: $t('admin.library_details'), }, diff --git a/web/src/routes/admin/library-management/[id]/edit/+page.svelte b/web/src/routes/admin/library-management/[id]/edit/+page.svelte new file mode 100644 index 0000000000..091eb421ab --- /dev/null +++ b/web/src/routes/admin/library-management/[id]/edit/+page.svelte @@ -0,0 +1,35 @@ + + + + + + + diff --git a/web/src/routes/admin/library-management/[id]/edit/+page.ts b/web/src/routes/admin/library-management/[id]/edit/+page.ts new file mode 100644 index 0000000000..10f0f33e11 --- /dev/null +++ b/web/src/routes/admin/library-management/[id]/edit/+page.ts @@ -0,0 +1,15 @@ +import { authenticate } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import type { PageLoad } from './$types'; + +export const load = (async ({ url }) => { + await authenticate(url, { admin: true }); + + const $t = await getFormatter(); + + return { + meta: { + title: $t('admin.library_details'), + }, + }; +}) satisfies PageLoad; diff --git a/web/src/routes/admin/users/(list)/+layout.svelte b/web/src/routes/admin/users/(list)/+layout.svelte index 8f25307e15..bec0e2c316 100644 --- a/web/src/routes/admin/users/(list)/+layout.svelte +++ b/web/src/routes/admin/users/(list)/+layout.svelte @@ -18,19 +18,19 @@ let { children, data }: Props = $props(); - let allUsers: UserAdminResponseDto[] = $state(data.allUsers); + let users: UserAdminResponseDto[] = $state(data.users); const onUpdate = async (user: UserAdminResponseDto) => { - const index = allUsers.findIndex(({ id }) => id === user.id); + const index = users.findIndex(({ id }) => id === user.id); if (index === -1) { - allUsers = await searchUsersAdmin({ withDeleted: true }); + users = await searchUsersAdmin({ withDeleted: true }); } else { - allUsers[index] = user; + users[index] = user; } }; const onUserAdminDeleted = ({ id: userId }: { id: string }) => { - allUsers = allUsers.filter(({ id }) => id !== userId); + users = users.filter(({ id }) => id !== userId); }; const { Create } = $derived(getUserAdminsActions($t)); @@ -62,7 +62,7 @@ - {#each allUsers as user (user.id)} + {#each users as user (user.id)} + import { goto } from '$app/navigation'; + import AdminCard from '$lib/components/AdminCard.svelte'; + import AdminPageLayout from '$lib/components/layouts/AdminPageLayout.svelte'; + import OnEvents from '$lib/components/OnEvents.svelte'; + import ServerStatisticsCard from '$lib/components/server-statistics/ServerStatisticsCard.svelte'; + import UserAvatar from '$lib/components/shared-components/user-avatar.svelte'; + 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 { getUserAdminActions } from '$lib/services/user-admin.service'; + import { locale } from '$lib/stores/preferences.store'; + import { createDateFormatter, findLocale } from '$lib/utils'; + import { getBytesWithUnit } from '$lib/utils/byte-units'; + import { type UserAdminResponseDto } from '@immich/sdk'; + import { + Alert, + Badge, + Code, + CommandPaletteContext, + Container, + getByteUnitString, + Heading, + Icon, + MenuItemType, + Stack, + Text, + } from '@immich/ui'; + import { + mdiAccountOutline, + mdiCameraIris, + mdiChartPie, + mdiChartPieOutline, + mdiCheckCircle, + mdiDevices, + mdiFeatureSearchOutline, + mdiPlayCircle, + mdiTrashCanOutline, + } from '@mdi/js'; + import type { Snippet } from 'svelte'; + import { t } from 'svelte-i18n'; + import type { LayoutData } from './$types'; + + type Props = { + children?: Snippet; + data: LayoutData; + }; + + const { children, data }: Props = $props(); + + let user = $state(data.user); + const userPreferences = $state(data.userPreferences); + const userStatistics = $state(data.userStatistics); + const userSessions = $state(data.userSessions); + const TiB = 1024 ** 4; + const usage = $derived(user.quotaUsageInBytes ?? 0); + let [statsUsage, statsUsageUnit] = $derived(getBytesWithUnit(usage, usage > TiB ? 2 : 0)); + const usedBytes = $derived(user.quotaUsageInBytes ?? 0); + const availableBytes = $derived(user.quotaSizeInBytes ?? 1); + let usedPercentage = $derived(Math.min(Math.round((usedBytes / availableBytes) * 100), 100)); + + let editedLocale = $derived(findLocale($locale).code); + let createAtDate: Date = $derived(new Date(user.createdAt)); + let updatedAtDate: Date = $derived(new Date(user.updatedAt)); + let userCreatedAtDateAndTime: string = $derived(createDateFormatter(editedLocale).formatDateTime(createAtDate)); + let userUpdatedAtDateAndTime: string = $derived(createDateFormatter(editedLocale).formatDateTime(updatedAtDate)); + + const getUsageClass = () => { + if (usedPercentage >= 95) { + return 'bg-red-500'; + } + + if (usedPercentage > 80) { + return 'bg-yellow-500'; + } + + return 'bg-primary'; + }; + + const { ResetPassword, ResetPinCode, Update, Delete, Restore } = $derived(getUserAdminActions($t, user)); + + const onUpdate = (update: UserAdminResponseDto) => { + if (update.id === user.id) { + user = update; + } + }; + + const onUserAdminDeleted = async ({ id }: { id: string }) => { + if (id === user.id) { + await goto(AppRoute.ADMIN_USERS); + } + }; + + + + + + + +
+ + {#if user.deletedAt} + + {/if} + +
+
+
+ + {user.name} +
+ {#if user.isAdmin} +
+ {$t('admin.admin_user')} +
+ {/if} +
+
+
+ + + +
+
+ + + +
+ {$t('name')} + {user.name} +
+
+ {$t('email')} + {user.email} +
+
+ {$t('created_at')} + {userCreatedAtDateAndTime} +
+
+ {$t('updated_at')} + {userUpdatedAtDateAndTime} +
+
+ {$t('id')} + {user.id} +
+
+
+ + + + + + + + + + + + + + + + + {#if user.quotaSizeInBytes !== null && user.quotaSizeInBytes >= 0} + + {$t('storage_usage', { + values: { + used: getByteUnitString(usedBytes, $locale, 3), + available: getByteUnitString(availableBytes, $locale, 3), + }, + })} + + {:else} + + + {$t('unlimited')} + + {/if} + + {#if user.quotaSizeInBytes !== null && user.quotaSizeInBytes >= 0} +
+

{$t('storage')}

+
+
+
+
+ {/if} +
+ + + + {#each userSessions as session (session.id)} + + {:else} + {$t('no_devices')} + {/each} + + +
+ + {@render children?.()} +
+
+
diff --git a/web/src/routes/admin/users/[id]/+layout.ts b/web/src/routes/admin/users/[id]/+layout.ts new file mode 100644 index 0000000000..32c41b0aca --- /dev/null +++ b/web/src/routes/admin/users/[id]/+layout.ts @@ -0,0 +1,38 @@ +import { AppRoute, UUID_REGEX } from '$lib/constants'; +import { authenticate, requestServerInfo } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import { getUserPreferencesAdmin, getUserSessionsAdmin, getUserStatisticsAdmin, searchUsersAdmin } from '@immich/sdk'; +import { redirect } from '@sveltejs/kit'; +import type { LayoutLoad } from './$types'; + +export const load = (async ({ params, url }) => { + await authenticate(url, { admin: true }); + await requestServerInfo(); + + if (!UUID_REGEX.test(params.id)) { + redirect(302, AppRoute.ADMIN_USERS); + } + + const [user] = await searchUsersAdmin({ id: params.id, withDeleted: true }).catch(() => []); + if (!user) { + redirect(302, AppRoute.ADMIN_USERS); + } + + const [userPreferences, userStatistics, userSessions] = await Promise.all([ + getUserPreferencesAdmin({ id: user.id }), + getUserStatisticsAdmin({ id: user.id }), + getUserSessionsAdmin({ id: user.id }), + ]); + + const $t = await getFormatter(); + + return { + user, + userPreferences, + userStatistics, + userSessions, + meta: { + title: $t('admin.user_details'), + }, + }; +}) satisfies LayoutLoad; diff --git a/web/src/routes/admin/users/[id]/+page.svelte b/web/src/routes/admin/users/[id]/+page.svelte index c41afba97c..e69de29bb2 100644 --- a/web/src/routes/admin/users/[id]/+page.svelte +++ b/web/src/routes/admin/users/[id]/+page.svelte @@ -1,218 +0,0 @@ - - - - - - - -
- - {#if user.deletedAt} - - {/if} - -
-
-
- - {user.name} -
- {#if user.isAdmin} -
- {$t('admin.admin_user')} -
- {/if} -
-
-
- - - -
-
- - - -
- {$t('name')} - {user.name} -
-
- {$t('email')} - {user.email} -
-
- {$t('created_at')} - {userCreatedAtDateAndTime} -
-
- {$t('updated_at')} - {userUpdatedAtDateAndTime} -
-
- {$t('id')} - {user.id} -
-
-
- - - - - - - - - - - - - - - - - {#if user.quotaSizeInBytes !== null && user.quotaSizeInBytes >= 0} - - {$t('storage_usage', { - values: { - used: getByteUnitString(usedBytes, $locale, 3), - available: getByteUnitString(availableBytes, $locale, 3), - }, - })} - - {:else} - - - {$t('unlimited')} - - {/if} - - {#if user.quotaSizeInBytes !== null && user.quotaSizeInBytes >= 0} -
-

{$t('storage')}

-
-
-
-
- {/if} -
- - - - {#each userSessions as session (session.id)} - - {:else} - {$t('no_devices')} - {/each} - - -
-
-
-
diff --git a/web/src/routes/admin/users/[id]/+page.ts b/web/src/routes/admin/users/[id]/+page.ts index df1524177e..775f9662d2 100644 --- a/web/src/routes/admin/users/[id]/+page.ts +++ b/web/src/routes/admin/users/[id]/+page.ts @@ -1,36 +1,12 @@ -import { AppRoute, UUID_REGEX } from '$lib/constants'; -import { authenticate, requestServerInfo } from '$lib/utils/auth'; +import { authenticate } from '$lib/utils/auth'; import { getFormatter } from '$lib/utils/i18n'; -import { getUserPreferencesAdmin, getUserSessionsAdmin, getUserStatisticsAdmin, searchUsersAdmin } from '@immich/sdk'; -import { redirect } from '@sveltejs/kit'; import type { PageLoad } from './$types'; -export const load = (async ({ params, url }) => { +export const load = (async ({ url }) => { await authenticate(url, { admin: true }); - await requestServerInfo(); - - if (!UUID_REGEX.test(params.id)) { - redirect(302, AppRoute.ADMIN_USERS); - } - - const [user] = await searchUsersAdmin({ id: params.id, withDeleted: true }).catch(() => []); - if (!user) { - redirect(302, AppRoute.ADMIN_USERS); - } - - const [userPreferences, userStatistics, userSessions] = await Promise.all([ - getUserPreferencesAdmin({ id: user.id }), - getUserStatisticsAdmin({ id: user.id }), - getUserSessionsAdmin({ id: user.id }), - ]); - const $t = await getFormatter(); return { - user, - userPreferences, - userStatistics, - userSessions, meta: { title: $t('admin.user_details'), }, diff --git a/web/src/lib/modals/UserEditModal.svelte b/web/src/routes/admin/users/[id]/edit/+page.svelte similarity index 86% rename from web/src/lib/modals/UserEditModal.svelte rename to web/src/routes/admin/users/[id]/edit/+page.svelte index 4b4878e46d..8a472f9452 100644 --- a/web/src/lib/modals/UserEditModal.svelte +++ b/web/src/routes/admin/users/[id]/edit/+page.svelte @@ -1,10 +1,11 @@ diff --git a/web/src/routes/admin/users/[id]/edit/+page.ts b/web/src/routes/admin/users/[id]/edit/+page.ts new file mode 100644 index 0000000000..64420e91da --- /dev/null +++ b/web/src/routes/admin/users/[id]/edit/+page.ts @@ -0,0 +1,15 @@ +import { authenticate } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import type { PageLoad } from './$types'; + +export const load = (async ({ url }) => { + await authenticate(url, { admin: true }); + + const $t = await getFormatter(); + + return { + meta: { + title: $t('admin.user_details'), + }, + }; +}) satisfies PageLoad;