diff --git a/web/src/lib/components/server-statistics/ServerStatisticsCard.svelte b/web/src/lib/components/server-statistics/ServerStatisticsCard.svelte index b374a7696..71f16ba43 100644 --- a/web/src/lib/components/server-statistics/ServerStatisticsCard.svelte +++ b/web/src/lib/components/server-statistics/ServerStatisticsCard.svelte @@ -3,29 +3,17 @@ import { Icon, Text } from '@immich/ui'; import type { Snippet } from 'svelte'; - type ValueData = { - value: number; - unit?: ByteUnit | undefined; - }; - interface Props { icon?: string; title: string; - value?: ValueData; + value?: number; + unit?: ByteUnit; tooltip?: string; footer?: Snippet; } - let { icon, title, value, tooltip, footer }: Props = $props(); - const zeros = (data?: ValueData) => { - let length = 13; - if (data) { - const valueLength = data.value.toString().length; - length -= valueLength; - } - - return '0'.repeat(length); - }; + let { icon, title, value, unit, tooltip, footer }: Props = $props(); + const zeros = (value?: number) => '0'.repeat(13 - (value?.toString().length ?? 0));
@@ -36,11 +24,11 @@ {title}
- {#if value} -
- {zeros(value)}{value.value} - {#if value.unit} - {value.unit} + {#if value !== undefined} +
+ {zeros(value)}{value} + {#if unit} + {unit} {/if}
{:else} diff --git a/web/src/lib/managers/event-manager.svelte.ts b/web/src/lib/managers/event-manager.svelte.ts index d8d07fcb7..8e0ac5adc 100644 --- a/web/src/lib/managers/event-manager.svelte.ts +++ b/web/src/lib/managers/event-manager.svelte.ts @@ -79,12 +79,6 @@ export type Events = { UserPinCodeReset: []; - UserAdminCreate: [UserAdminResponseDto]; - UserAdminUpdate: [UserAdminResponseDto]; - UserAdminRestore: [UserAdminResponseDto]; - // soft deleted - UserAdminDelete: [UserAdminResponseDto]; - // confirmed permanently deleted from server UserAdminDeleted: [{ id: string }]; SessionLocked: []; diff --git a/web/src/lib/modals/UserDeleteConfirmModal.svelte b/web/src/lib/modals/UserDeleteConfirmModal.svelte index 34646076c..03818b550 100644 --- a/web/src/lib/modals/UserDeleteConfirmModal.svelte +++ b/web/src/lib/modals/UserDeleteConfirmModal.svelte @@ -1,7 +1,8 @@ diff --git a/web/src/lib/modals/UserRestoreConfirmModal.svelte b/web/src/lib/modals/UserRestoreConfirmModal.svelte index 71546921a..377b90a9d 100644 --- a/web/src/lib/modals/UserRestoreConfirmModal.svelte +++ b/web/src/lib/modals/UserRestoreConfirmModal.svelte @@ -1,6 +1,7 @@ diff --git a/web/src/lib/queries/library.query.ts b/web/src/lib/queries/library.query.ts index 4949ce557..317d6b84d 100644 --- a/web/src/lib/queries/library.query.ts +++ b/web/src/lib/queries/library.query.ts @@ -10,7 +10,7 @@ import { import { queryOptions } from '@tanstack/svelte-query'; import { asMutation, queryClient } from '$lib/queries/client'; -const findLibrary = (libraries: LibraryResponseDto[], id: string) => { +const findOrFail = (libraries: LibraryResponseDto[], id: string) => { const library = libraries.find((library) => library.id === id); if (!library) { throw new Error(`Library ${id} not found`); @@ -27,7 +27,7 @@ export const getAllLibrariesQuery = () => export const getLibraryQuery = (id: string) => queryOptions({ ...getAllLibrariesQuery(), - select: (libraries) => findLibrary(libraries, id), + select: (libraries) => findOrFail(libraries, id), }); export const getLibraryStatisticsQuery = (id: string) => diff --git a/web/src/lib/queries/user-admin.query.ts b/web/src/lib/queries/user-admin.query.ts index bcf010e57..9cf80cf0f 100644 --- a/web/src/lib/queries/user-admin.query.ts +++ b/web/src/lib/queries/user-admin.query.ts @@ -1,20 +1,92 @@ -import { searchUsersAdmin } from '@immich/sdk'; +import { + CalendarHeatmapType, + createUserAdmin, + deleteUserAdmin, + getUserCalendarHeatmapAdmin, + getUserPreferencesAdmin, + getUserSessionsAdmin, + getUserStatisticsAdmin, + restoreUserAdmin, + searchUsersAdmin, + updateUserAdmin, + type UserAdminResponseDto, +} from '@immich/sdk'; import { queryOptions } from '@tanstack/svelte-query'; +import { getHeatmapRange } from '$lib'; +import { asMutation, queryClient } from '$lib/queries/client'; -export const searchUsersAdminQuery = () => +const findOrFail = (users: UserAdminResponseDto[], id: string) => { + const user = users.find((user) => user.id === id); + if (!user) { + throw new Error(`User ${id} not found`); + } + return user; +}; + +export const searchUsersAdminQuery = ({ withDeleted = false }: { withDeleted?: boolean } = {}) => queryOptions({ queryKey: ['user-admin'], queryFn: ({ signal }) => searchUsersAdmin({ withDeleted: true }, { signal }), + select: (users) => (withDeleted ? users : users.filter((user) => !user.deletedAt)), }); export const getUserAdminQuery = (id: string) => queryOptions({ - ...searchUsersAdminQuery(), - select: (users) => { - const user = users.find((user) => user.id === id); - if (!user) { - throw new Error(`User ${id} not found`); - } - return user; - }, + ...searchUsersAdminQuery({ withDeleted: true }), + select: (users) => findOrFail(users, id), }); + +export const getUserPreferencesAdminQuery = (id: string) => + queryOptions({ + queryKey: ['user-admin', id, 'preferences'], + queryFn: ({ signal }) => getUserPreferencesAdmin({ id }, { signal }), + staleTime: 0, + }); + +export const getUserStatisticsAdminQuery = (id: string) => + queryOptions({ + queryKey: ['user-admin', id, 'statistics'], + queryFn: ({ signal }) => getUserStatisticsAdmin({ id }, { signal }), + staleTime: 0, + }); + +export const getUserSessionsAdminQuery = (id: string) => + queryOptions({ + queryKey: ['user-admin', id, 'sessions'], + queryFn: ({ signal }) => getUserSessionsAdmin({ id }, { signal }), + staleTime: 0, + }); + +export const getUserCalendarHeatmapAdminQuery = (id: string) => + queryOptions({ + queryKey: ['user-admin', id, 'heatmap'], + queryFn: ({ signal }) => + getUserCalendarHeatmapAdmin({ ...getHeatmapRange(), id, $type: CalendarHeatmapType.Upload }, { signal }), + staleTime: 0, + }); + +export const createUserAdminMutation = () => + asMutation(createUserAdmin, { + onSuccess: () => queryClient.invalidateQueries({ queryKey: searchUsersAdminQuery().queryKey, exact: true }), + meta: { error: { toast: 'errors.unable_to_create_user' } }, + }); + +export const updateUserAdminMutation = () => + asMutation(updateUserAdmin, { onSuccess: onUpdateSuccess, meta: { success: { toast: true } } }); + +export const deleteUserAdminMutation = () => + asMutation(deleteUserAdmin, { + onSuccess: onUpdateSuccess, + meta: { success: { toast: true }, error: { toast: 'errors.unable_to_delete_user' } }, + }); + +export const restoreUserAdminMutation = () => + asMutation(restoreUserAdmin, { + onSuccess: onUpdateSuccess, + meta: { success: { toast: true }, error: { toast: 'errors.unable_to_restore_user' } }, + }); + +const onUpdateSuccess = (user: UserAdminResponseDto) => + queryClient.setQueryData(searchUsersAdminQuery().queryKey, (users) => + users?.map((item) => (item.id === user.id ? user : item)), + ); diff --git a/web/src/lib/services/user-admin.service.ts b/web/src/lib/services/user-admin.service.ts index 00807c84b..f266a0c72 100644 --- a/web/src/lib/services/user-admin.service.ts +++ b/web/src/lib/services/user-admin.service.ts @@ -1,15 +1,5 @@ -import { - createUserAdmin, - deleteUserAdmin, - restoreUserAdmin, - updateUserAdmin, - UserStatus, - type UserAdminCreateDto, - type UserAdminDeleteDto, - type UserAdminResponseDto, - type UserAdminUpdateDto, -} from '@immich/sdk'; -import { modalManager, toastManager, type ActionItem } from '@immich/ui'; +import { UserStatus, type UserAdminResponseDto } from '@immich/sdk'; +import { modalManager, type ActionItem } from '@immich/ui'; import { mdiDeleteRestore, mdiInformationOutline, @@ -23,14 +13,14 @@ import { DateTime } from 'luxon'; import type { MessageFormatter } from 'svelte-i18n'; import { goto } from '$app/navigation'; import { authManager } from '$lib/managers/auth-manager.svelte'; -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 UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte'; +import { executeMutation } from '$lib/queries/client'; +import { updateUserAdminMutation } from '$lib/queries/user-admin.query'; import { Route } from '$lib/route'; import type { HeaderButtonActionItem } from '$lib/types'; -import { handleError } from '$lib/utils/handle-error'; import { getFormatter } from '$lib/utils/i18n'; export const getUserAdminsActions = ($t: MessageFormatter) => { @@ -97,60 +87,6 @@ export const getUserAdminActions = ($t: MessageFormatter, user: UserAdminRespons return { Detail, Update, Delete, Restore, ResetPassword, ResetPinCode }; }; -export const handleCreateUserAdmin = async (dto: UserAdminCreateDto) => { - const $t = await getFormatter(); - - try { - const response = await createUserAdmin({ userAdminCreateDto: dto }); - eventManager.emit('UserAdminCreate', response); - toastManager.primary(); - return response; - } catch (error) { - handleError(error, $t('errors.unable_to_create_user')); - } -}; - -export const handleUpdateUserAdmin = async (user: UserAdminResponseDto, dto: UserAdminUpdateDto) => { - const $t = await getFormatter(); - - try { - const response = await updateUserAdmin({ id: user.id, userAdminUpdateDto: dto }); - eventManager.emit('UserAdminUpdate', response); - toastManager.primary(); - return true; - } catch (error) { - handleError(error, $t('errors.unable_to_update_user')); - return false; - } -}; - -export const handleDeleteUserAdmin = async (user: UserAdminResponseDto, dto: UserAdminDeleteDto) => { - const $t = await getFormatter(); - - try { - const result = await deleteUserAdmin({ id: user.id, userAdminDeleteDto: dto }); - eventManager.emit('UserAdminDelete', result); - toastManager.primary(); - return true; - } catch (error) { - handleError(error, $t('errors.unable_to_delete_user')); - } -}; - -export const handleRestoreUserAdmin = async (user: UserAdminResponseDto) => { - const $t = await getFormatter(); - - try { - const response = await restoreUserAdmin({ id: user.id }); - eventManager.emit('UserAdminRestore', response); - toastManager.primary(); - return true; - } catch (error) { - handleError(error, $t('errors.unable_to_restore_user')); - return false; - } -}; - // TODO move password reset server-side const generatePassword = (length: number = 16) => { let generatedPassword = ''; @@ -176,15 +112,12 @@ const handleResetPasswordUserAdmin = async (user: UserAdminResponseDto) => { return; } - try { - const dto = { password: generatePassword(), shouldChangePassword: true }; - const response = await updateUserAdmin({ id: user.id, userAdminUpdateDto: dto }); - eventManager.emit('UserAdminUpdate', response); - toastManager.primary(); - await modalManager.show(PasswordResetSuccessModal, { newPassword: dto.password }); - } catch (error) { - handleError(error, $t('errors.unable_to_reset_password')); - } + const password = generatePassword(); + await executeMutation( + updateUserAdminMutation, + [{ id: user.id, userAdminUpdateDto: { password, shouldChangePassword: true } }], + { onSuccess: () => void modalManager.show(PasswordResetSuccessModal, { newPassword: password }) }, + ); }; const handleResetPinCodeUserAdmin = async (user: UserAdminResponseDto) => { @@ -195,11 +128,5 @@ const handleResetPinCodeUserAdmin = async (user: UserAdminResponseDto) => { return; } - try { - const response = await updateUserAdmin({ id: user.id, userAdminUpdateDto: { pinCode: null } }); - eventManager.emit('UserAdminUpdate', response); - toastManager.primary($t('pin_code_reset_successfully')); - } catch (error) { - handleError(error, $t('errors.unable_to_reset_pin_code')); - } + await executeMutation(updateUserAdminMutation, [{ id: user.id, userAdminUpdateDto: { pinCode: null } }]); }; diff --git a/web/src/lib/stores/websocket.ts b/web/src/lib/stores/websocket.ts index f9832f8c3..301713e89 100644 --- a/web/src/lib/stores/websocket.ts +++ b/web/src/lib/stores/websocket.ts @@ -16,6 +16,7 @@ import { authManager } from '$lib/managers/auth-manager.svelte'; import { eventManager } from '$lib/managers/event-manager.svelte'; import { queryClient } from '$lib/queries/client'; import { getConfigQuery } from '$lib/queries/system-config.query'; +import { searchUsersAdminQuery } from '$lib/queries/user-admin.query'; import { Route } from '$lib/route'; import { maintenanceStore } from '$lib/stores/maintenance.store'; import { notificationManager } from '$lib/stores/notification-manager.svelte'; @@ -93,7 +94,11 @@ websocket }) .on('on_new_release', (event) => eventManager.emit('ReleaseEvent', event)) .on('on_session_delete', () => eventManager.emit('SessionDelete')) - .on('on_user_delete', (id) => eventManager.emit('UserAdminDeleted', { id })) + .on('on_user_delete', (id) => { + queryClient.removeQueries({ queryKey: ['user-admin', id] }); + void queryClient.invalidateQueries({ queryKey: searchUsersAdminQuery().queryKey, exact: true }); + eventManager.emit('UserAdminDeleted', { id }); + }) .on('on_asset_delete', (asset) => eventManager.emit('AssetsDelete', [asset])) .on('on_asset_trash', (assets) => eventManager.emit('AssetsDelete', assets)) .on('on_asset_update', (asset) => eventManager.emit('AssetUpdate', asset)) diff --git a/web/src/routes/admin/library-management/(list)/new/+page.svelte b/web/src/routes/admin/library-management/(list)/new/+page.svelte index b8db1a86f..6dde25581 100644 --- a/web/src/routes/admin/library-management/(list)/new/+page.svelte +++ b/web/src/routes/admin/library-management/(list)/new/+page.svelte @@ -11,7 +11,7 @@ let ownerId: string = $state(authManager.user.id); const query = createPreloadedQuery(searchUsersAdminQuery); - const users = $derived(query.data.filter((user) => !user.deletedAt)); + const users = $derived(query.data); const onClose = async () => { await goto(Route.libraries()); diff --git a/web/src/routes/admin/library-management/[id]/+layout.svelte b/web/src/routes/admin/library-management/[id]/+layout.svelte index 690a01ea1..3c1c1228d 100644 --- a/web/src/routes/admin/library-management/[id]/+layout.svelte +++ b/web/src/routes/admin/library-management/[id]/+layout.svelte @@ -29,15 +29,7 @@ const statisticsQuery = createQuery(() => getLibraryStatisticsQuery(library.id)); const stats = $derived(statisticsQuery.data); - const photos = $derived(stats && { value: stats.photos }); - const videos = $derived(stats && { value: stats.videos }); - const usage = $derived.by(() => { - if (!stats) { - return; - } - const [value, unit] = getBytesWithUnit(stats.usage); - return { value, unit }; - }); + const [usage, usageUnit] = $derived(stats ? getBytesWithUnit(stats.usage) : [undefined, undefined]); const { Edit, Delete, AddFolder, AddExclusionPattern, Scan } = $derived(getLibraryActions($t, library)); @@ -52,9 +44,9 @@
{library.name}
- - - + + +
diff --git a/web/src/routes/admin/library-management/[id]/+layout.ts b/web/src/routes/admin/library-management/[id]/+layout.ts index 017cf68ed..779ee8e06 100644 --- a/web/src/routes/admin/library-management/[id]/+layout.ts +++ b/web/src/routes/admin/library-management/[id]/+layout.ts @@ -1,6 +1,6 @@ import { redirect } from '@sveltejs/kit'; import { queryClient } from '$lib/queries/client'; -import { getAllLibrariesQuery } from '$lib/queries/library.query'; +import { getLibraryQuery } from '$lib/queries/library.query'; import { Route } from '$lib/route'; import { authenticate } from '$lib/utils/auth'; import { getFormatter } from '$lib/utils/i18n'; @@ -9,10 +9,7 @@ import type { LayoutLoad } from './$types'; export const load = (async ({ params: { id }, url }) => { await authenticate(url, { admin: true }); - const libraries = await queryClient.query(getAllLibrariesQuery()); - if (libraries.every((library) => library.id !== id)) { - redirect(307, Route.libraries()); - } + await queryClient.query(getLibraryQuery(id)).catch(() => redirect(307, Route.libraries())); const $t = await getFormatter(); diff --git a/web/src/routes/admin/maintenance/+page.svelte b/web/src/routes/admin/maintenance/+page.svelte index a8163c439..3859d1df4 100644 --- a/web/src/routes/admin/maintenance/+page.svelte +++ b/web/src/routes/admin/maintenance/+page.svelte @@ -143,7 +143,7 @@ {#snippet footer()} diff --git a/web/src/routes/admin/server-status/+page.svelte b/web/src/routes/admin/server-status/+page.svelte index aacfeae60..fe2ac576c 100644 --- a/web/src/routes/admin/server-status/+page.svelte +++ b/web/src/routes/admin/server-status/+page.svelte @@ -13,6 +13,6 @@ - + diff --git a/web/src/routes/admin/server-status/+page.ts b/web/src/routes/admin/server-status/+page.ts index 44a7861db..7c168bfb3 100644 --- a/web/src/routes/admin/server-status/+page.ts +++ b/web/src/routes/admin/server-status/+page.ts @@ -1,7 +1,7 @@ -import { searchUsersAdmin } from '@immich/sdk'; import { noop } from 'lodash-es'; import { queryClient } from '$lib/queries/client'; import { getServerStatisticsQuery } from '$lib/queries/server.query'; +import { searchUsersAdminQuery } from '$lib/queries/user-admin.query'; import { authenticate } from '$lib/utils/auth'; import { getFormatter } from '$lib/utils/i18n'; import type { PageLoad } from './$types'; @@ -9,11 +9,10 @@ import type { PageLoad } from './$types'; export const load = (async ({ url }) => { await authenticate(url, { admin: true }); void queryClient.query(getServerStatisticsQuery()).catch(noop); - const users = await searchUsersAdmin({ withDeleted: false }); + await queryClient.query(searchUsersAdminQuery()); const $t = await getFormatter(); return { - users, meta: { title: $t('server_stats'), }, diff --git a/web/src/routes/admin/server-status/ServerStatisticsPanel.svelte b/web/src/routes/admin/server-status/ServerStatisticsPanel.svelte index 3c93977d2..a0df72fb8 100644 --- a/web/src/routes/admin/server-status/ServerStatisticsPanel.svelte +++ b/web/src/routes/admin/server-status/ServerStatisticsPanel.svelte @@ -1,9 +1,10 @@ - - diff --git a/web/src/routes/admin/users/(list)/+layout.ts b/web/src/routes/admin/users/(list)/+layout.ts index a8941ad8a..ba3812bc3 100644 --- a/web/src/routes/admin/users/(list)/+layout.ts +++ b/web/src/routes/admin/users/(list)/+layout.ts @@ -1,15 +1,15 @@ -import { searchUsersAdmin } from '@immich/sdk'; +import { queryClient } from '$lib/queries/client'; +import { searchUsersAdminQuery } from '$lib/queries/user-admin.query'; import { authenticate } from '$lib/utils/auth'; import { getFormatter } from '$lib/utils/i18n'; import type { LayoutLoad } from './$types'; export const load = (async ({ url }) => { await authenticate(url, { admin: true }); - const users = await searchUsersAdmin({ withDeleted: true }); + await queryClient.query(searchUsersAdminQuery()); const $t = await getFormatter(); return { - users, meta: { title: $t('admin.user_management'), }, diff --git a/web/src/routes/admin/users/(list)/new/+page.svelte b/web/src/routes/admin/users/(list)/new/+page.svelte index e0439c85c..e48646f32 100644 --- a/web/src/routes/admin/users/(list)/new/+page.svelte +++ b/web/src/routes/admin/users/(list)/new/+page.svelte @@ -2,8 +2,9 @@ import { goto } from '$app/navigation'; import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte'; import { Route } from '$lib/route'; - import { handleCreateUserAdmin } from '$lib/services/user-admin.service'; + import { executeMutation } from '$lib/queries/client'; import { getStorageQuery } from '$lib/queries/server.query'; + import { createUserAdminMutation } from '$lib/queries/user-admin.query'; import { ByteUnit, convertToBytes } from '$lib/utils/byte-units'; import { Field, FormModal, HelperText, Input, PasswordInput, Stack, Switch } from '@immich/ui'; import { createQuery } from '@tanstack/svelte-query'; @@ -45,19 +46,11 @@ isCreatingUser = true; - const user = await handleCreateUserAdmin({ - email, - password, - shouldChangePassword, - name, - quotaSizeInBytes, - notify, - isAdmin, - }); - - if (user) { - await goto(Route.viewUser(user), { replaceState: true }); - } + await executeMutation( + createUserAdminMutation, + [{ userAdminCreateDto: { email, password, shouldChangePassword, name, quotaSizeInBytes, notify, isAdmin } }], + { onSuccess: (user) => void goto(Route.viewUser(user), { replaceState: true }) }, + ); isCreatingUser = false; }; diff --git a/web/src/routes/admin/users/[id]/+layout.svelte b/web/src/routes/admin/users/[id]/+layout.svelte index 6726883cb..f60662ac0 100644 --- a/web/src/routes/admin/users/[id]/+layout.svelte +++ b/web/src/routes/admin/users/[id]/+layout.svelte @@ -1,5 +1,5 @@ - + @@ -122,13 +121,9 @@
- - - + + +
@@ -159,15 +154,17 @@ - - - - - - - - - + {#if userPreferences} + + + + + + + + + + {/if} @@ -210,15 +207,15 @@ {$t('uploads')}
- {#await getUserCalendarHeatmapAdmin({ ...getHeatmapRange(), id: user.id, $type: CalendarHeatmapType.Upload })} - - {:then data} + {#if heatmap} $t('upload_day_count', { values: item })} totalLabel={(count) => $t('uploads_count', { values: { count } })} /> - {/await} + {:else} + + {/if} diff --git a/web/src/routes/admin/users/[id]/+layout.ts b/web/src/routes/admin/users/[id]/+layout.ts index 12546452c..5e13c2fed 100644 --- a/web/src/routes/admin/users/[id]/+layout.ts +++ b/web/src/routes/admin/users/[id]/+layout.ts @@ -1,6 +1,7 @@ -import { getUserPreferencesAdmin, getUserSessionsAdmin, getUserStatisticsAdmin, searchUsersAdmin } from '@immich/sdk'; import { redirect } from '@sveltejs/kit'; import { UUID_REGEX } from '$lib/constants'; +import { queryClient } from '$lib/queries/client'; +import { getUserAdminQuery } from '$lib/queries/user-admin.query'; import { Route } from '$lib/route'; import { authenticate } from '$lib/utils/auth'; import { getFormatter } from '$lib/utils/i18n'; @@ -13,24 +14,11 @@ export const load = (async ({ params, url }) => { redirect(307, Route.users()); } - const [user] = await searchUsersAdmin({ id: params.id, withDeleted: true }).catch(() => []); - if (!user) { - redirect(307, Route.users()); - } - - const [userPreferences, userStatistics, userSessions] = await Promise.all([ - getUserPreferencesAdmin({ id: user.id }), - getUserStatisticsAdmin({ id: user.id }), - getUserSessionsAdmin({ id: user.id }), - ]); + await queryClient.query(getUserAdminQuery(params.id)).catch(() => redirect(307, Route.users())); const $t = await getFormatter(); return { - user, - userPreferences, - userStatistics, - userSessions, meta: { title: $t('admin.user_details'), }, diff --git a/web/src/routes/admin/users/[id]/edit/+page.svelte b/web/src/routes/admin/users/[id]/edit/+page.svelte index 322c965f3..28d832649 100644 --- a/web/src/routes/admin/users/[id]/edit/+page.svelte +++ b/web/src/routes/admin/users/[id]/edit/+page.svelte @@ -2,25 +2,23 @@ import { goto } from '$app/navigation'; import { authManager } from '$lib/managers/auth-manager.svelte'; import { Route } from '$lib/route'; - import { handleUpdateUserAdmin } from '$lib/services/user-admin.service'; + import { createPreloadedQuery, executeMutation } from '$lib/queries/client'; import { getStorageQuery } from '$lib/queries/server.query'; + import { getUserAdminQuery, updateUserAdminMutation } from '$lib/queries/user-admin.query'; import { ByteUnit, convertFromBytes, convertToBytes } from '$lib/utils/byte-units'; import { Field, FormModal, Input, Link, NumberInput, Switch, Text } from '@immich/ui'; import { createQuery } from '@tanstack/svelte-query'; import { mdiAccountEditOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; - import type { PageData } from './$types'; + import type { PageProps } from './$types'; - type Props = { - data: PageData; - }; - - let { data }: Props = $props(); + const { params }: PageProps = $props(); const query = createQuery(getStorageQuery); const serverInfo = $derived(query.data); - const user = $derived(data.user); + const userQuery = createPreloadedQuery(() => getUserAdminQuery(params.id)); + const user = $derived(userQuery.data); let { isAdmin, name, email } = $derived(user); let storageLabel = $derived(user.storageLabel || ''); const previousQuota = $derived(user.quotaSizeInBytes); @@ -35,24 +33,16 @@ previousQuota !== quotaSizeBytes && !!quotaSizeBytes && serverInfo && quotaSizeBytes > serverInfo.diskSizeRaw, ); - const onClose = async () => { - await goto(Route.viewUser(user)); - }; + const onClose = () => void goto(Route.viewUser(user)); const onSubmit = async (event: Event) => { event.preventDefault(); - const success = await handleUpdateUserAdmin(user, { - email, - name, - storageLabel, - quotaSizeInBytes: typeof quotaSize === 'number' ? convertToBytes(quotaSize, ByteUnit.GiB) : null, - isAdmin, - }); - - if (success) { - await onClose(); - } + await executeMutation( + updateUserAdminMutation, + [{ id: user.id, userAdminUpdateDto: { email, name, storageLabel, quotaSizeInBytes: quotaSizeBytes, isAdmin } }], + { onSuccess: onClose }, + ); };