-
- {#if canResetPassword}
-
- {/if}
-
-
-
-
-
-
-
+
+
+
diff --git a/web/src/routes/admin/+page.ts b/web/src/routes/admin/+page.ts
index 0d53c4ef2b..bab3c1ea66 100644
--- a/web/src/routes/admin/+page.ts
+++ b/web/src/routes/admin/+page.ts
@@ -3,5 +3,5 @@ import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load = (() => {
- redirect(302, AppRoute.ADMIN_USER_MANAGEMENT);
+ redirect(302, AppRoute.ADMIN_USERS);
}) satisfies PageLoad;
diff --git a/web/src/routes/admin/user-management/+page.ts b/web/src/routes/admin/user-management/+page.ts
index 0a6af40c69..6ff068a1fb 100644
--- a/web/src/routes/admin/user-management/+page.ts
+++ b/web/src/routes/admin/user-management/+page.ts
@@ -1,18 +1,5 @@
-import { authenticate, requestServerInfo } from '$lib/utils/auth';
-import { getFormatter } from '$lib/utils/i18n';
-import { searchUsersAdmin } from '@immich/sdk';
+import { AppRoute } from '$lib/constants';
+import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types';
-export const load = (async () => {
- await authenticate({ admin: true });
- await requestServerInfo();
- const allUsers = await searchUsersAdmin({ withDeleted: true });
- const $t = await getFormatter();
-
- return {
- allUsers,
- meta: {
- title: $t('admin.user_management'),
- },
- };
-}) satisfies PageLoad;
+export const load = (() => redirect(307, AppRoute.ADMIN_USERS)) satisfies PageLoad;
diff --git a/web/src/routes/admin/user-management/+page.svelte b/web/src/routes/admin/users/+page.svelte
similarity index 91%
rename from web/src/routes/admin/user-management/+page.svelte
rename to web/src/routes/admin/users/+page.svelte
index 5b6246be8c..75b35491f6 100644
--- a/web/src/routes/admin/user-management/+page.svelte
+++ b/web/src/routes/admin/users/+page.svelte
@@ -6,7 +6,7 @@
NotificationType,
notificationController,
} from '$lib/components/shared-components/notification/notification';
- import PasswordResetSuccess from '$lib/forms/password-reset-success.svelte';
+ import { AppRoute } from '$lib/constants';
import { modalManager } from '$lib/managers/modal-manager.svelte';
import UserCreateModal from '$lib/modals/UserCreateModal.svelte';
import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte';
@@ -18,7 +18,7 @@
import { websocketEvents } from '$lib/stores/websocket';
import { getByteUnitString } from '$lib/utils/byte-units';
import { UserStatus, searchUsersAdmin, type UserAdminResponseDto } from '@immich/sdk';
- import { Button, IconButton } from '@immich/ui';
+ import { Button, IconButton, Link } from '@immich/ui';
import { mdiDeleteRestore, mdiInfinity, mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js';
import { DateTime } from 'luxon';
import { onMount } from 'svelte';
@@ -64,20 +64,9 @@
};
const handleEdit = async (dto: UserAdminResponseDto) => {
- const result = await modalManager.show(UserEditModal, { user: dto, canResetPassword: dto.id !== $user.id });
- switch (result?.action) {
- case 'resetPassword': {
- await modalManager.show(PasswordResetSuccess, { newPassword: result.data });
- break;
- }
- case 'update': {
- await refresh();
- break;
- }
- case 'resetPinCode': {
- notificationController.show({ type: NotificationType.Info, message: $t('pin_code_reset_successfully') });
- break;
- }
+ const result = await modalManager.show(UserEditModal, { user: dto });
+ if (result) {
+ await refresh();
}
};
@@ -123,7 +112,7 @@
: 'bg-immich-bg dark:bg-immich-dark-gray/50'}"
>
{immichUser.email} | {immichUser.email}
{immichUser.name} |
diff --git a/web/src/routes/admin/users/+page.ts b/web/src/routes/admin/users/+page.ts
new file mode 100644
index 0000000000..0a6af40c69
--- /dev/null
+++ b/web/src/routes/admin/users/+page.ts
@@ -0,0 +1,18 @@
+import { authenticate, requestServerInfo } from '$lib/utils/auth';
+import { getFormatter } from '$lib/utils/i18n';
+import { searchUsersAdmin } from '@immich/sdk';
+import type { PageLoad } from './$types';
+
+export const load = (async () => {
+ await authenticate({ admin: true });
+ await requestServerInfo();
+ const allUsers = await searchUsersAdmin({ withDeleted: true });
+ const $t = await getFormatter();
+
+ return {
+ allUsers,
+ meta: {
+ title: $t('admin.user_management'),
+ },
+ };
+}) satisfies PageLoad;
diff --git a/web/src/routes/admin/users/[id]/+page.svelte b/web/src/routes/admin/users/[id]/+page.svelte
new file mode 100644
index 0000000000..b0a9327fcc
--- /dev/null
+++ b/web/src/routes/admin/users/[id]/+page.svelte
@@ -0,0 +1,343 @@
+
+
+
+ {#snippet buttons()}
+
+ {#if canResetPassword}
+
+ {/if}
+
+
+
+
+
+ {/snippet}
+
+
+
+
+
+ {user.name}
+
+
+
+
+
+
+
+ {$t('profile')}
+
+
+
+
+
+ {$t('name')}
+ {user.name}
+
+
+ {$t('email')}
+ {user.email}
+
+
+ {$t('created_at')}
+ {user.createdAt}
+
+
+ {$t('updated_at')}
+ {user.updatedAt}
+
+
+ {$t('id')}
+ {user.id}
+
+
+
+
+
+
+
+
+
+ {$t('features')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {$t('storage_quota')}
+
+
+
+
+ {#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}
+
+ {/if}
+
+
+
+
+
+
diff --git a/web/src/routes/admin/users/[id]/+page.ts b/web/src/routes/admin/users/[id]/+page.ts
new file mode 100644
index 0000000000..ddf3ddbef7
--- /dev/null
+++ b/web/src/routes/admin/users/[id]/+page.ts
@@ -0,0 +1,31 @@
+import { AppRoute } from '$lib/constants';
+import { authenticate, requestServerInfo } from '$lib/utils/auth';
+import { getFormatter } from '$lib/utils/i18n';
+import { getUserPreferencesAdmin, getUserStatisticsAdmin, searchUsersAdmin } from '@immich/sdk';
+import { redirect } from '@sveltejs/kit';
+import type { PageLoad } from './$types';
+
+export const load = (async ({ params }) => {
+ await authenticate({ admin: true });
+ await requestServerInfo();
+ const [user] = await searchUsersAdmin({ id: params.id }).catch(() => []);
+ if (!user) {
+ redirect(302, AppRoute.ADMIN_USERS);
+ }
+
+ const [userPreferences, userStatistics] = await Promise.all([
+ getUserPreferencesAdmin({ id: user.id }),
+ getUserStatisticsAdmin({ id: user.id }),
+ ]);
+
+ const $t = await getFormatter();
+
+ return {
+ user,
+ userPreferences,
+ userStatistics,
+ meta: {
+ title: $t('admin.user_details'),
+ },
+ };
+}) satisfies PageLoad;
From 7544a678ec2a68df6163a9c5213c8e749b27a14a Mon Sep 17 00:00:00 2001
From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
Date: Mon, 12 May 2025 23:17:01 +0200
Subject: [PATCH 4/8] refactor: remove unnecessary bg-color attributes and move
to ui lib vars (#18234)
---
.../server-stats/server-stats-panel.svelte | 4 +---
.../components/album-page/album-title.svelte | 6 ++---
.../components/album-page/album-viewer.svelte | 24 +++++++++----------
.../album-page/albums-table-row.svelte | 2 +-
.../asset-viewer/asset-viewer.svelte | 6 ++---
.../asset-viewer/download-panel.svelte | 2 +-
.../faces-page/assign-face-side-panel.svelte | 20 ++++++++--------
.../manage-people-visibility.svelte | 4 ++--
.../faces-page/merge-face-selector.svelte | 8 +++----
.../faces-page/people-search.svelte | 2 +-
.../faces-page/person-side-panel.svelte | 20 ++++++++--------
.../faces-page/unmerge-face-selector.svelte | 6 ++---
.../forms/library-import-paths-form.svelte | 8 ++-----
.../forms/library-scan-settings-form.svelte | 8 ++-----
.../lib/components/layouts/ErrorLayout.svelte | 6 ++---
.../layouts/user-page-layout.svelte | 2 +-
.../onboarding-page/onboarding-theme.svelte | 4 ++--
.../photos-page/asset-date-group.svelte | 9 ++++---
.../components/photos-page/skeleton.svelte | 2 +-
.../individual-shared-viewer.svelte | 2 +-
.../album-selection-modal.svelte | 16 ++++++-------
.../shared-components/control-app-bar.svelte | 4 ++--
.../navigation-bar/navigation-bar.svelte | 2 +-
.../scrubber/scrubber.svelte | 6 ++---
.../side-bar/side-bar-section.svelte | 2 +-
.../upload-asset-preview.svelte | 6 +----
.../user-api-key-list.svelte | 6 ++---
.../user-usage-statistic.svelte | 8 +++----
.../[[assetId=id]]/+page.svelte | 8 +++----
.../[[assetId=id]]/+page.svelte | 2 +-
web/src/routes/(user)/people/+page.svelte | 2 +-
.../[[assetId=id]]/+page.svelte | 2 +-
.../[[assetId=id]]/+page.svelte | 5 +---
.../[[assetId=id]]/+page.svelte | 18 +++++++-------
.../admin/library-management/+page.svelte | 4 +---
web/src/routes/admin/users/+page.svelte | 6 ++---
36 files changed, 106 insertions(+), 136 deletions(-)
diff --git a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte
index 3dcd3b4594..2f8d391954 100644
--- a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte
+++ b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte
@@ -103,9 +103,7 @@
class="block max-h-[320px] w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg"
>
{#each stats.usageByUser as user (user.userId)}
- |
+
{user.userName} |
{user.photos.toLocaleString($locale)} ({getByteUnitString(user.usagePhotos, $locale, 0)}) |
- import { updateAlbumInfo } from '@immich/sdk';
- import { handleError } from '$lib/utils/handle-error';
import { shortcut } from '$lib/actions/shortcut';
+ import { handleError } from '$lib/utils/handle-error';
+ import { updateAlbumInfo } from '@immich/sdk';
import { t } from 'svelte-i18n';
interface Props {
@@ -40,7 +40,7 @@
onblur={handleUpdateName}
class="w-[99%] mb-2 border-b-2 border-transparent text-2xl md:text-4xl lg:text-6xl text-immich-primary outline-none transition-all dark:text-immich-dark-primary {isOwned
? 'hover:border-gray-400'
- : 'hover:border-transparent'} bg-immich-bg focus:border-b-2 focus:border-immich-primary focus:outline-none dark:bg-immich-dark-bg dark:focus:border-immich-dark-primary dark:focus:bg-immich-dark-gray"
+ : 'hover:border-transparent'} focus:border-b-2 focus:border-immich-primary focus:outline-none bg-light dark:focus:border-immich-dark-primary dark:focus:bg-immich-dark-gray"
type="text"
bind:value={newAlbumName}
disabled={!isOwned}
diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte
index 1f15e22d9e..f3688e780c 100644
--- a/web/src/lib/components/album-page/album-viewer.svelte
+++ b/web/src/lib/components/album-page/album-viewer.svelte
@@ -1,11 +1,18 @@
goto(`${AppRoute.ALBUMS}/${album.id}`)}
{oncontextmenu}
>
diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte
index 5606da31a9..1e3c6135f0 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte
@@ -529,7 +529,7 @@
($isShowDetail = false)} />
@@ -540,7 +540,7 @@
@@ -589,7 +589,7 @@
{$t('downloading').toUpperCase()}
diff --git a/web/src/lib/components/faces-page/assign-face-side-panel.svelte b/web/src/lib/components/faces-page/assign-face-side-panel.svelte
index 19a99fdb94..c1906b70e0 100644
--- a/web/src/lib/components/faces-page/assign-face-side-panel.svelte
+++ b/web/src/lib/components/faces-page/assign-face-side-panel.svelte
@@ -1,20 +1,20 @@
- {#each keys as key, index (key.id)}
+ {#each keys as key (key.id)}
{key.name} |
| {viewName} |
{stats.images.toLocaleString($locale)} |
@@ -95,7 +95,7 @@
{$t('owned')} |
@@ -103,9 +103,7 @@
-
+
{albumStats.owned.toLocaleString($locale)} |
{albumStats.shared.toLocaleString($locale)} |
diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
index 88840b382d..d84e3bda63 100644
--- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
@@ -588,9 +588,7 @@
{/if}
{/if}
-
+
(viewMode = AlbumPageViewMode.SELECT_ASSETS)}
- class="mt-5 flex w-full place-items-center gap-6 rounded-md border bg-immich-bg px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
+ class="mt-5 bg-subtle flex w-full place-items-center gap-6 rounded-md border px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
>
@@ -709,7 +707,7 @@
-
+
{#if assetInteraction.selectionActive}
{
diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte
index dc03a2ae70..447712177e 100644
--- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte
@@ -291,10 +291,7 @@
{:else}
goto(previousRoute)} backIcon={mdiArrowLeft}>
-
+
diff --git a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte
index 7e6057696a..33e8f43cf5 100644
--- a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte
@@ -4,17 +4,17 @@
import IndividualSharedViewer from '$lib/components/share-page/individual-shared-viewer.svelte';
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
import ImmichLogoSmallLink from '$lib/components/shared-components/immich-logo-small-link.svelte';
- import ThemeButton from '$lib/components/shared-components/theme-button.svelte';
import PasswordField from '$lib/components/shared-components/password-field.svelte';
- import { user } from '$lib/stores/user.store';
- import { handleError } from '$lib/utils/handle-error';
- import { getMySharedLink, SharedLinkType } from '@immich/sdk';
- import type { PageData } from './$types';
- import { setSharedLink } from '$lib/utils';
- import { t } from 'svelte-i18n';
- import { navigate } from '$lib/utils/navigation';
+ import ThemeButton from '$lib/components/shared-components/theme-button.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
+ import { user } from '$lib/stores/user.store';
+ import { setSharedLink } from '$lib/utils';
+ import { handleError } from '$lib/utils/handle-error';
+ import { navigate } from '$lib/utils/navigation';
+ import { getMySharedLink, SharedLinkType } from '@immich/sdk';
import { tick } from 'svelte';
+ import { t } from 'svelte-i18n';
+ import type { PageData } from './$types';
interface Props {
data: PageData;
@@ -70,7 +70,7 @@
{$t('password_required')}
diff --git a/web/src/routes/admin/library-management/+page.svelte b/web/src/routes/admin/library-management/+page.svelte
index b0be2cb3ce..10865628a8 100644
--- a/web/src/routes/admin/library-management/+page.svelte
+++ b/web/src/routes/admin/library-management/+page.svelte
@@ -298,9 +298,7 @@
{#each libraries as library, index (library.id)}
{library.name} |
diff --git a/web/src/routes/admin/users/+page.svelte b/web/src/routes/admin/users/+page.svelte
index 75b35491f6..dc21be6c24 100644
--- a/web/src/routes/admin/users/+page.svelte
+++ b/web/src/routes/admin/users/+page.svelte
@@ -103,13 +103,11 @@
|
{#if allUsers}
- {#each allUsers as immichUser, index (immichUser.id)}
+ {#each allUsers as immichUser (immichUser.id)}
{immichUser.email} |
Date: Mon, 12 May 2025 17:48:05 -0400
Subject: [PATCH 5/8] refactor: dialog controller (#18235)
---
.../admin-page/jobs/jobs-panel.svelte | 6 +--
.../actions/keep-this-delete-others.svelte | 6 +--
.../face-editor/face-editor.svelte | 7 +---
.../faces-page/merge-face-selector.svelte | 7 +---
.../faces-page/person-side-panel.svelte | 5 +--
.../actions/remove-from-album.svelte | 6 +--
.../actions/remove-from-shared-link.svelte | 4 +-
.../dialog/dialog-wrapper.svelte | 9 ----
.../shared-components/dialog/dialog.ts | 42 -------------------
.../user-settings-page/device-list.svelte | 13 +++---
.../partner-settings.svelte | 3 +-
.../user-api-key-list.svelte | 3 +-
.../user-purchase-settings.svelte | 24 +++++------
web/src/lib/managers/modal-manager.svelte.ts | 2 +-
web/src/lib/utils/album-utils.ts | 4 +-
.../shared-links/[[id=id]]/+page.svelte | 4 +-
.../[[assetId=id]]/+page.svelte | 6 +--
.../[[assetId=id]]/+page.svelte | 12 ++----
.../[[assetId=id]]/+page.svelte | 3 +-
web/src/routes/+layout.svelte | 2 -
.../admin/library-management/+page.svelte | 7 ++--
web/src/routes/admin/users/[id]/+page.svelte | 4 +-
22 files changed, 53 insertions(+), 126 deletions(-)
delete mode 100644 web/src/lib/components/shared-components/dialog/dialog-wrapper.svelte
delete mode 100644 web/src/lib/components/shared-components/dialog/dialog.ts
diff --git a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte
index 2c59f59416..73dfb30908 100644
--- a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte
+++ b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte
@@ -3,6 +3,7 @@
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
+ import { modalManager } from '$lib/managers/modal-manager.svelte';
import { featureFlags } from '$lib/stores/server-config.store';
import { getJobName } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
@@ -20,10 +21,9 @@
mdiVideo,
} from '@mdi/js';
import type { Component } from 'svelte';
+ import { t } from 'svelte-i18n';
import JobTile from './job-tile.svelte';
import StorageMigrationDescription from './storage-migration-description.svelte';
- import { dialogController } from '$lib/components/shared-components/dialog/dialog';
- import { t } from 'svelte-i18n';
interface Props {
jobs: AllJobStatusResponseDto;
@@ -45,7 +45,7 @@
const handleConfirmCommand = async (jobId: JobName, dto: JobCommandDto) => {
if (dto.force) {
- const isConfirmed = await dialogController.show({
+ const isConfirmed = await modalManager.showDialog({
prompt: $t('admin.confirm_reprocess_all_faces'),
});
diff --git a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte
index 8705476d8d..090e87f4a9 100644
--- a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte
+++ b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte
@@ -1,12 +1,12 @@
-
-{#if $dialog}
-
-{/if}
diff --git a/web/src/lib/components/shared-components/dialog/dialog.ts b/web/src/lib/components/shared-components/dialog/dialog.ts
deleted file mode 100644
index 69a64aad21..0000000000
--- a/web/src/lib/components/shared-components/dialog/dialog.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { writable } from 'svelte/store';
-
-type DialogActions = {
- onClose: (confirmed: boolean) => void;
-};
-
-type DialogOptions = {
- title?: string;
- prompt?: string;
- confirmText?: string;
- cancelText?: string;
- hideCancelButton?: boolean;
- disable?: boolean;
- width?: 'wide' | 'narrow' | undefined;
-};
-
-export type Dialog = DialogOptions & DialogActions;
-
-function createDialogWrapper() {
- const dialog = writable