mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
refactor: modals (#25163)
This commit is contained in:
parent
88327fb872
commit
1e4af9731d
18 changed files with 258 additions and 357 deletions
|
|
@ -56,7 +56,7 @@ test.describe('User Administration', () => {
|
||||||
await expect(page.getByLabel('Admin User')).not.toBeChecked();
|
await expect(page.getByLabel('Admin User')).not.toBeChecked();
|
||||||
await page.getByLabel('Admin User').click();
|
await page.getByLabel('Admin User').click();
|
||||||
await expect(page.getByLabel('Admin User')).toBeChecked();
|
await expect(page.getByLabel('Admin User')).toBeChecked();
|
||||||
await page.getByRole('button', { name: 'Confirm' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
|
||||||
await expect
|
await expect
|
||||||
.poll(async () => {
|
.poll(async () => {
|
||||||
|
|
@ -85,7 +85,7 @@ test.describe('User Administration', () => {
|
||||||
await expect(page.getByLabel('Admin User')).toBeChecked();
|
await expect(page.getByLabel('Admin User')).toBeChecked();
|
||||||
await page.getByLabel('Admin User').click();
|
await page.getByLabel('Admin User').click();
|
||||||
await expect(page.getByLabel('Admin User')).not.toBeChecked();
|
await expect(page.getByLabel('Admin User')).not.toBeChecked();
|
||||||
await page.getByRole('button', { name: 'Confirm' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
|
||||||
await expect
|
await expect
|
||||||
.poll(async () => {
|
.poll(async () => {
|
||||||
|
|
|
||||||
|
|
@ -735,8 +735,8 @@ importers:
|
||||||
specifier: file:../open-api/typescript-sdk
|
specifier: file:../open-api/typescript-sdk
|
||||||
version: link:../open-api/typescript-sdk
|
version: link:../open-api/typescript-sdk
|
||||||
'@immich/ui':
|
'@immich/ui':
|
||||||
specifier: ^0.54.0
|
specifier: ^0.56.1
|
||||||
version: 0.54.0(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.46.1)
|
version: 0.56.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.46.1)
|
||||||
'@mapbox/mapbox-gl-rtl-text':
|
'@mapbox/mapbox-gl-rtl-text':
|
||||||
specifier: 0.2.3
|
specifier: 0.2.3
|
||||||
version: 0.2.3(mapbox-gl@1.13.3)
|
version: 0.2.3(mapbox-gl@1.13.3)
|
||||||
|
|
@ -3084,8 +3084,8 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^5.0.0
|
svelte: ^5.0.0
|
||||||
|
|
||||||
'@immich/ui@0.54.0':
|
'@immich/ui@0.56.1':
|
||||||
resolution: {integrity: sha512-6jvkvKhgsZ7LvspaJkbht/f8W5IRm+vjYkcZecShFAPaxaowbm7io9sO15MpJdIQfPdXg7vwLI527PV3vlBc6A==}
|
resolution: {integrity: sha512-W4uEQn9pxVKRvIV7sl9p6dU2r7xlVsMFxBeClxtXzSsiJEoE10uZwBIm0L9q17c4TQ/+lk9e/w1e4jNSvFqFwQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^5.0.0
|
svelte: ^5.0.0
|
||||||
|
|
||||||
|
|
@ -15098,7 +15098,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.46.1
|
svelte: 5.46.1
|
||||||
|
|
||||||
'@immich/ui@0.54.0(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.46.1)':
|
'@immich/ui@0.56.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.46.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@immich/svelte-markdown-preprocess': 0.1.0(svelte@5.46.1)
|
'@immich/svelte-markdown-preprocess': 0.1.0(svelte@5.46.1)
|
||||||
'@internationalized/date': 3.10.0
|
'@internationalized/date': 3.10.0
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
"@formatjs/icu-messageformat-parser": "^3.0.0",
|
"@formatjs/icu-messageformat-parser": "^3.0.0",
|
||||||
"@immich/justified-layout-wasm": "^0.4.3",
|
"@immich/justified-layout-wasm": "^0.4.3",
|
||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@immich/ui": "^0.54.0",
|
"@immich/ui": "^0.56.1",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@photo-sphere-viewer/core": "^5.14.0",
|
"@photo-sphere-viewer/core": "^5.14.0",
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@
|
||||||
const allMethodsDisabled = !configToEdit.oauth.enabled && !configToEdit.passwordLogin.enabled;
|
const allMethodsDisabled = !configToEdit.oauth.enabled && !configToEdit.passwordLogin.enabled;
|
||||||
|
|
||||||
if (allMethodsDisabled) {
|
if (allMethodsDisabled) {
|
||||||
const isConfirmed = await modalManager.show(AuthDisableLoginConfirmModal);
|
const confirmed = await modalManager.show(AuthDisableLoginConfirmModal);
|
||||||
if (!isConfirmed) {
|
if (!confirmed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@
|
||||||
size="medium"
|
size="medium"
|
||||||
onClose={handleConfirm}
|
onClose={handleConfirm}
|
||||||
>
|
>
|
||||||
{#snippet promptSnippet()}
|
{#snippet prompt()}
|
||||||
<div class="flex flex-col w-full h-full gap-2">
|
<div class="flex flex-col w-full h-full gap-2">
|
||||||
<div class="relative w-64 sm:w-96 z-1">
|
<div class="relative w-64 sm:w-96 z-1">
|
||||||
{#if suggestionContainer}
|
{#if suggestionContainer}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
import { mdiKeyVariant } from '@mdi/js';
|
import { mdiKeyVariant } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
interface Props {
|
type Props = {
|
||||||
secret?: string;
|
secret?: string;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
let { secret = '', onClose }: Props = $props();
|
let { secret = '', onClose }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
icon={mdiDeleteForeverOutline}
|
icon={mdiDeleteForeverOutline}
|
||||||
{onClose}
|
{onClose}
|
||||||
>
|
>
|
||||||
{#snippet promptSnippet()}
|
{#snippet prompt()}
|
||||||
<p>
|
<p>
|
||||||
<FormatMessage key="permanently_delete_assets_prompt" values={{ count: size }}>
|
<FormatMessage key="permanently_delete_assets_prompt" values={{ count: size }}>
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ConfirmModal, Field, Textarea } from '@immich/ui';
|
import { Field, FormModal, Textarea } from '@immich/ui';
|
||||||
import { mdiText } from '@mdi/js';
|
import { mdiText } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
|
@ -11,16 +11,8 @@
|
||||||
let description = $state('');
|
let description = $state('');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ConfirmModal
|
<FormModal title={$t('edit_description')} icon={mdiText} {onClose} onSubmit={() => onClose(description)}>
|
||||||
confirmColor="primary"
|
<Field label={$t('description')}>
|
||||||
title={$t('edit_description')}
|
<Textarea bind:value={description} grow />
|
||||||
icon={mdiText}
|
</Field>
|
||||||
prompt={$t('edit_description_prompt')}
|
</FormModal>
|
||||||
onClose={(confirmed) => (confirmed ? onClose(description) : onClose())}
|
|
||||||
>
|
|
||||||
{#snippet promptSnippet()}
|
|
||||||
<Field label={$t('description')}>
|
|
||||||
<Textarea bind:value={description} grow />
|
|
||||||
</Field>
|
|
||||||
{/snippet}
|
|
||||||
</ConfirmModal>
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
import { Button, HStack, Modal, ModalBody, ModalFooter } from '@immich/ui';
|
import { ConfirmModal } from '@immich/ui';
|
||||||
import { mdiCancel } from '@mdi/js';
|
import { mdiCancel } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
interface Props {
|
type Props = {
|
||||||
onClose: (confirmed?: boolean) => void;
|
onClose: (confirmed?: boolean) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
let { onClose }: Props = $props();
|
let { onClose }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title={$t('admin.disable_login')} icon={mdiCancel} size="small" {onClose}>
|
<ConfirmModal title={$t('admin.disable_login')} icon={mdiCancel} size="small" {onClose}>
|
||||||
<ModalBody>
|
{#snippet prompt()}
|
||||||
<div class="flex flex-col gap-4 text-center">
|
<div class="flex flex-col gap-4 text-center">
|
||||||
<p>{$t('admin.authentication_settings_disable_all')}</p>
|
<p>{$t('admin.authentication_settings_disable_all')}</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -30,15 +30,5 @@
|
||||||
</FormatMessage>
|
</FormatMessage>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
{/snippet}
|
||||||
<ModalFooter>
|
</ConfirmModal>
|
||||||
<HStack fullWidth>
|
|
||||||
<Button shape="round" color="secondary" fullWidth onclick={() => onClose(false)}>
|
|
||||||
{$t('cancel')}
|
|
||||||
</Button>
|
|
||||||
<Button shape="round" color="danger" fullWidth onclick={() => onClose(true)}>
|
|
||||||
{$t('confirm')}
|
|
||||||
</Button>
|
|
||||||
</HStack>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,20 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Button, HStack, Modal, ModalBody, ModalFooter } from '@immich/ui';
|
import { ConfirmModal } from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
location: { latitude: number | undefined; longitude: number | undefined };
|
location: { latitude: number | undefined; longitude: number | undefined };
|
||||||
assetCount: number;
|
assetCount: number;
|
||||||
onClose: (confirm?: true) => void;
|
onClose: (confirm: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { location, assetCount, onClose }: Props = $props();
|
let { location, assetCount, onClose }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title={$t('confirm')} size="small" {onClose}>
|
<ConfirmModal title={$t('confirm')} size="small" confirmColor="primary" {onClose}>
|
||||||
<ModalBody>
|
{#snippet prompt()}
|
||||||
<p>
|
<p>{$t('update_location_action_prompt', { values: { count: assetCount } })}</p>
|
||||||
{$t('update_location_action_prompt', {
|
|
||||||
values: {
|
|
||||||
count: assetCount,
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>- {$t('latitude')}: {location.latitude}</p>
|
<p>- {$t('latitude')}: {location.latitude}</p>
|
||||||
<p>- {$t('longitude')}: {location.longitude}</p>
|
<p>- {$t('longitude')}: {location.longitude}</p>
|
||||||
</ModalBody>
|
{/snippet}
|
||||||
<ModalFooter>
|
</ConfirmModal>
|
||||||
<HStack fullWidth>
|
|
||||||
<Button shape="round" color="secondary" fullWidth onclick={() => onClose()}>{$t('cancel')}</Button>
|
|
||||||
<Button shape="round" type="submit" fullWidth onclick={() => onClose(true)}>{$t('confirm')}</Button>
|
|
||||||
</HStack>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { copyToClipboard } from '$lib/utils';
|
import { copyToClipboard } from '$lib/utils';
|
||||||
import { Button, Code, HStack, IconButton, Modal, ModalBody, ModalFooter, Text } from '@immich/ui';
|
import { BasicModal, Code, IconButton, Text } from '@immich/ui';
|
||||||
import { mdiCheck, mdiContentCopy } from '@mdi/js';
|
import { mdiCheck, mdiContentCopy } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
|
@ -12,33 +12,23 @@
|
||||||
const { onClose, newPassword }: Props = $props();
|
const { onClose, newPassword }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title={$t('password_reset_success')} icon={mdiCheck} onClose={() => onClose()} size="small">
|
<BasicModal title={$t('password_reset_success')} icon={mdiCheck} {onClose} size="small" closeText={$t('done')}>
|
||||||
<ModalBody>
|
<div class="flex flex-col gap-4">
|
||||||
<div class="flex flex-col gap-4">
|
<Text>{$t('admin.user_password_has_been_reset')}</Text>
|
||||||
<Text>{$t('admin.user_password_has_been_reset')}</Text>
|
|
||||||
|
|
||||||
<div class="flex justify-center gap-2 items-center">
|
<div class="flex justify-center gap-2 items-center">
|
||||||
<Code color="primary">{newPassword}</Code>
|
<Code color="primary">{newPassword}</Code>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={mdiContentCopy}
|
icon={mdiContentCopy}
|
||||||
shape="round"
|
shape="round"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onclick={() => copyToClipboard(newPassword)}
|
onclick={() => copyToClipboard(newPassword)}
|
||||||
title={$t('copy_password')}
|
title={$t('copy_password')}
|
||||||
aria-label={$t('copy_password')}
|
aria-label={$t('copy_password')}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<Text>{$t('admin.user_password_reset_description')}</Text>
|
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
<Text>{$t('admin.user_password_reset_description')}</Text>
|
||||||
<HStack fullWidth>
|
</div>
|
||||||
<Button shape="round" color="primary" fullWidth onclick={() => onClose()}>
|
</BasicModal>
|
||||||
{$t('done')}
|
|
||||||
</Button>
|
|
||||||
</HStack>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,18 @@
|
||||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { mergePerson, type PersonResponseDto } from '@immich/sdk';
|
import { mergePerson, type PersonResponseDto } from '@immich/sdk';
|
||||||
import { Button, HStack, Icon, IconButton, Modal, ModalBody, ModalFooter, toastManager } from '@immich/ui';
|
import { FormModal, Icon, IconButton, toastManager } from '@immich/ui';
|
||||||
import { mdiArrowLeft, mdiCallMerge, mdiSwapHorizontal } from '@mdi/js';
|
import { mdiArrowLeft, mdiCallMerge, mdiSwapHorizontal } from '@mdi/js';
|
||||||
import { onMount, tick } from 'svelte';
|
import { onMount, tick } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import ImageThumbnail from '../components/assets/thumbnail/image-thumbnail.svelte';
|
import ImageThumbnail from '../components/assets/thumbnail/image-thumbnail.svelte';
|
||||||
|
|
||||||
interface Props {
|
type Props = {
|
||||||
personToMerge: PersonResponseDto;
|
personToMerge: PersonResponseDto;
|
||||||
personToBeMergedInto: PersonResponseDto;
|
personToBeMergedInto: PersonResponseDto;
|
||||||
potentialMergePeople: PersonResponseDto[];
|
potentialMergePeople: PersonResponseDto[];
|
||||||
onClose: (people?: [PersonResponseDto, PersonResponseDto]) => void;
|
onClose: (people?: [PersonResponseDto, PersonResponseDto]) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
let {
|
let {
|
||||||
personToMerge = $bindable(),
|
personToMerge = $bindable(),
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
choosePersonToMerge = false;
|
choosePersonToMerge = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMergePerson = async () => {
|
const onSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
await mergePerson({
|
await mergePerson({
|
||||||
id: personToBeMergedInto.id,
|
id: personToBeMergedInto.id,
|
||||||
|
|
@ -51,99 +51,95 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title="{$t('merge_people')} - {title}" {onClose}>
|
<FormModal
|
||||||
<ModalBody>
|
title="{$t('merge_people')} - {title}"
|
||||||
<div class="flex items-center justify-center gap-2 py-4 md:h-36">
|
submitColor="primary"
|
||||||
{#if !choosePersonToMerge}
|
submitText={$t('yes')}
|
||||||
<div class="flex h-20 w-20 items-center px-1 md:h-24 md:w-24 md:px-2">
|
cancelText={$t('no')}
|
||||||
<ImageThumbnail
|
{onClose}
|
||||||
circle
|
{onSubmit}
|
||||||
shadow
|
>
|
||||||
url={getPeopleThumbnailUrl(personToMerge)}
|
<div class="flex items-center justify-center gap-2 py-4 md:h-36">
|
||||||
altText={personToMerge.name}
|
{#if !choosePersonToMerge}
|
||||||
widthStyle="100%"
|
<div class="flex h-20 w-20 items-center px-1 md:h-24 md:w-24 md:px-2">
|
||||||
|
<ImageThumbnail
|
||||||
|
circle
|
||||||
|
shadow
|
||||||
|
url={getPeopleThumbnailUrl(personToMerge)}
|
||||||
|
altText={personToMerge.name}
|
||||||
|
widthStyle="100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-rows-3">
|
||||||
|
<div></div>
|
||||||
|
<div class="flex flex-col h-full items-center justify-center">
|
||||||
|
<div class="flex h-full items-center justify-center">
|
||||||
|
<Icon icon={mdiCallMerge} size="48" class="rotate-90 dark:text-white" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<IconButton
|
||||||
|
shape="round"
|
||||||
|
color="secondary"
|
||||||
|
variant="ghost"
|
||||||
|
aria-label={$t('swap_merge_direction')}
|
||||||
|
icon={mdiSwapHorizontal}
|
||||||
|
onclick={() => ([personToMerge, personToBeMergedInto] = [personToBeMergedInto, personToMerge])}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-rows-3">
|
<button
|
||||||
<div></div>
|
type="button"
|
||||||
<div class="flex flex-col h-full items-center justify-center">
|
disabled={potentialMergePeople.length === 0}
|
||||||
<div class="flex h-full items-center justify-center">
|
class="flex h-28 w-28 items-center rounded-full border-2 border-immich-primary px-1 dark:border-immich-dark-primary md:h-32 md:w-32 md:px-2"
|
||||||
<Icon icon={mdiCallMerge} size="48" class="rotate-90 dark:text-white" />
|
onclick={() => {
|
||||||
</div>
|
if (potentialMergePeople.length > 0) {
|
||||||
</div>
|
choosePersonToMerge = !choosePersonToMerge;
|
||||||
<div>
|
}
|
||||||
<IconButton
|
}}
|
||||||
shape="round"
|
>
|
||||||
color="secondary"
|
<ImageThumbnail
|
||||||
variant="ghost"
|
border={potentialMergePeople.length > 0}
|
||||||
aria-label={$t('swap_merge_direction')}
|
circle
|
||||||
icon={mdiSwapHorizontal}
|
shadow
|
||||||
onclick={() => ([personToMerge, personToBeMergedInto] = [personToBeMergedInto, personToMerge])}
|
url={getPeopleThumbnailUrl(personToBeMergedInto)}
|
||||||
/>
|
altText={personToBeMergedInto.name}
|
||||||
|
widthStyle="100%"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<div class="grid w-full grid-cols-1 gap-2">
|
||||||
|
<div class="px-2">
|
||||||
|
<button type="button" onclick={() => (choosePersonToMerge = false)}> <Icon icon={mdiArrowLeft} /></button>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<div class="flex flex-wrap justify-center md:grid md:grid-cols-{potentialMergePeople.length}">
|
||||||
|
{#each potentialMergePeople as person (person.id)}
|
||||||
|
<div class="h-24 w-24 md:h-28 md:w-28">
|
||||||
|
<button type="button" class="p-2 w-full" onclick={() => changePersonToMerge(person)}>
|
||||||
|
<ImageThumbnail
|
||||||
|
border={true}
|
||||||
|
circle
|
||||||
|
shadow
|
||||||
|
url={getPeopleThumbnailUrl(person)}
|
||||||
|
altText={person.name}
|
||||||
|
widthStyle="100%"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<div class="flex px-4 md:pt-4">
|
||||||
type="button"
|
<h1 class="text-xl text-gray-500 dark:text-gray-300">{$t('are_these_the_same_person')}</h1>
|
||||||
disabled={potentialMergePeople.length === 0}
|
</div>
|
||||||
class="flex h-28 w-28 items-center rounded-full border-2 border-immich-primary px-1 dark:border-immich-dark-primary md:h-32 md:w-32 md:px-2"
|
<div class="flex px-4 pt-2">
|
||||||
onclick={() => {
|
<p class="text-sm text-gray-500 dark:text-gray-300">{$t('they_will_be_merged_together')}</p>
|
||||||
if (potentialMergePeople.length > 0) {
|
</div>
|
||||||
choosePersonToMerge = !choosePersonToMerge;
|
</FormModal>
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ImageThumbnail
|
|
||||||
border={potentialMergePeople.length > 0}
|
|
||||||
circle
|
|
||||||
shadow
|
|
||||||
url={getPeopleThumbnailUrl(personToBeMergedInto)}
|
|
||||||
altText={personToBeMergedInto.name}
|
|
||||||
widthStyle="100%"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<div class="grid w-full grid-cols-1 gap-2">
|
|
||||||
<div class="px-2">
|
|
||||||
<button type="button" onclick={() => (choosePersonToMerge = false)}> <Icon icon={mdiArrowLeft} /></button>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center justify-center">
|
|
||||||
<div class="flex flex-wrap justify-center md:grid md:grid-cols-{potentialMergePeople.length}">
|
|
||||||
{#each potentialMergePeople as person (person.id)}
|
|
||||||
<div class="h-24 w-24 md:h-28 md:w-28">
|
|
||||||
<button type="button" class="p-2 w-full" onclick={() => changePersonToMerge(person)}>
|
|
||||||
<ImageThumbnail
|
|
||||||
border={true}
|
|
||||||
circle
|
|
||||||
shadow
|
|
||||||
url={getPeopleThumbnailUrl(person)}
|
|
||||||
altText={person.name}
|
|
||||||
widthStyle="100%"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex px-4 md:pt-4">
|
|
||||||
<h1 class="text-xl text-gray-500 dark:text-gray-300">{$t('are_these_the_same_person')}</h1>
|
|
||||||
</div>
|
|
||||||
<div class="flex px-4 pt-2">
|
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-300">{$t('they_will_be_merged_together')}</p>
|
|
||||||
</div>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<HStack fullWidth>
|
|
||||||
<Button fullWidth shape="round" color="secondary" onclick={() => onClose()}>{$t('no')}</Button>
|
|
||||||
<Button id="merge-confirm-button" fullWidth shape="round" onclick={handleMergePerson}>
|
|
||||||
{$t('yes')}
|
|
||||||
</Button>
|
|
||||||
</HStack>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { createProfileImage, type AssetResponseDto } from '@immich/sdk';
|
import { createProfileImage, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { Button, Modal, ModalBody, ModalFooter, toastManager } from '@immich/ui';
|
import { FormModal, toastManager } from '@immich/ui';
|
||||||
import domtoimage from 'dom-to-image';
|
import domtoimage from 'dom-to-image';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetProfilePicture = async () => {
|
const onSubmit = async () => {
|
||||||
if (!imgElement) {
|
if (!imgElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -72,24 +72,20 @@
|
||||||
toastManager.success($t('profile_picture_set'));
|
toastManager.success($t('profile_picture_set'));
|
||||||
$user.profileImagePath = profileImagePath;
|
$user.profileImagePath = profileImagePath;
|
||||||
$user.profileChangedAt = profileChangedAt;
|
$user.profileChangedAt = profileChangedAt;
|
||||||
|
|
||||||
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_set_profile_picture'));
|
handleError(error, $t('errors.unable_to_set_profile_picture'));
|
||||||
}
|
}
|
||||||
onClose();
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal size="small" title={$t('set_profile_picture')} {onClose}>
|
<FormModal size="small" title={$t('set_profile_picture')} {onClose} {onSubmit}>
|
||||||
<ModalBody>
|
<div class="flex place-items-center items-center justify-center">
|
||||||
<div class="flex place-items-center items-center justify-center">
|
<div
|
||||||
<div
|
class="relative flex aspect-square w-62.5 overflow-hidden rounded-full border-4 border-immich-primary bg-immich-dark-primary dark:border-immich-dark-primary dark:bg-immich-primary"
|
||||||
class="relative flex aspect-square w-62.5 overflow-hidden rounded-full border-4 border-immich-primary bg-immich-dark-primary dark:border-immich-dark-primary dark:bg-immich-primary"
|
>
|
||||||
>
|
<PhotoViewer bind:element={imgElement} cursor={{ current: asset }} />
|
||||||
<PhotoViewer bind:element={imgElement} cursor={{ current: asset }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</div>
|
||||||
<ModalFooter>
|
</FormModal>
|
||||||
<Button fullWidth shape="round" onclick={handleSetProfilePicture}>{$t('set_as_profile_picture')}</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
{disabled}
|
{disabled}
|
||||||
>
|
>
|
||||||
{#snippet promptSnippet()}
|
{#snippet prompt()}
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<Text>
|
<Text>
|
||||||
{#if force}
|
{#if force}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
size="small"
|
size="small"
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
{#snippet promptSnippet()}
|
{#snippet prompt()}
|
||||||
<p>
|
<p>
|
||||||
<FormatMessage key="admin.user_restore_description" values={{ user: user.name }}>
|
<FormatMessage key="admin.user_restore_description" values={{ user: user.name }}>
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
import { Button, Modal, ModalBody, ModalFooter } from '@immich/ui';
|
import { BasicModal } from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -12,33 +12,33 @@
|
||||||
const { serverVersion, releaseVersion, onClose }: Props = $props();
|
const { serverVersion, releaseVersion, onClose }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal size="small" title="🎉 {$t('new_version_available')}" {onClose} icon={false}>
|
<BasicModal
|
||||||
<ModalBody>
|
size="small"
|
||||||
<div>
|
title="🎉 {$t('new_version_available')}"
|
||||||
<FormatMessage key="version_announcement_message">
|
closeText={$t('acknowledge')}
|
||||||
{#snippet children({ tag, message })}
|
closeColor="primary"
|
||||||
{#if tag === 'link'}
|
{onClose}
|
||||||
<span class="font-medium underline">
|
icon={false}
|
||||||
<a href="https://github.com/immich-app/immich/releases/latest" target="_blank" rel="noopener noreferrer">
|
>
|
||||||
{message}
|
<FormatMessage key="version_announcement_message">
|
||||||
</a>
|
{#snippet children({ tag, message })}
|
||||||
</span>
|
{#if tag === 'link'}
|
||||||
{:else if tag === 'code'}
|
<span class="font-medium underline">
|
||||||
<code>{message}</code>
|
<a href="https://github.com/immich-app/immich/releases/latest" target="_blank" rel="noopener noreferrer">
|
||||||
{/if}
|
{message}
|
||||||
{/snippet}
|
</a>
|
||||||
</FormatMessage>
|
</span>
|
||||||
</div>
|
{:else if tag === 'code'}
|
||||||
|
<code>{message}</code>
|
||||||
|
{/if}
|
||||||
|
{/snippet}
|
||||||
|
</FormatMessage>
|
||||||
|
|
||||||
<div class="mt-4 font-medium">{$t('version_announcement_closing')}</div>
|
<div class="mt-4 font-medium">{$t('version_announcement_closing')}</div>
|
||||||
|
|
||||||
<div class="font-sm mt-8">
|
<div class="font-sm mt-8">
|
||||||
<code>{$t('server_version')}: {serverVersion}</code>
|
<code>{$t('server_version')}: {serverVersion}</code>
|
||||||
<br />
|
<br />
|
||||||
<code>{$t('latest_version')}: {releaseVersion}</code>
|
<code>{$t('latest_version')}: {releaseVersion}</code>
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</BasicModal>
|
||||||
<ModalFooter>
|
|
||||||
<Button fullWidth shape="round" onclick={onClose}>{$t('acknowledge')}</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,7 @@
|
||||||
import { handleCreateUserAdmin } from '$lib/services/user-admin.service';
|
import { handleCreateUserAdmin } from '$lib/services/user-admin.service';
|
||||||
import { userInteraction } from '$lib/stores/user.svelte';
|
import { userInteraction } from '$lib/stores/user.svelte';
|
||||||
import { ByteUnit, convertToBytes } from '$lib/utils/byte-units';
|
import { ByteUnit, convertToBytes } from '$lib/utils/byte-units';
|
||||||
import {
|
import { Field, FormModal, HelperText, Input, PasswordInput, Stack, Switch } from '@immich/ui';
|
||||||
Button,
|
|
||||||
Field,
|
|
||||||
HelperText,
|
|
||||||
HStack,
|
|
||||||
Input,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalFooter,
|
|
||||||
PasswordInput,
|
|
||||||
Stack,
|
|
||||||
Switch,
|
|
||||||
} from '@immich/ui';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
let success = $state(false);
|
let success = $state(false);
|
||||||
|
|
@ -73,61 +61,48 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title={$t('create_new_user')} {onClose} size="small">
|
<FormModal title={$t('create_new_user')} size="small" disabled={!valid} submitText={$t('create')} {onClose} {onSubmit}>
|
||||||
<ModalBody>
|
{#if success}
|
||||||
<form onsubmit={onSubmit} autocomplete="off" id="create-new-user-form">
|
<p class="text-sm text-immich-primary">{$t('new_user_created')}</p>
|
||||||
{#if success}
|
{/if}
|
||||||
<p class="text-sm text-immich-primary">{$t('new_user_created')}</p>
|
|
||||||
|
<Stack gap={4}>
|
||||||
|
<Field label={$t('email')} required>
|
||||||
|
<Input bind:value={email} type="email" />
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
{#if featureFlagsManager.value.email}
|
||||||
|
<Field label={$t('admin.send_welcome_email')}>
|
||||||
|
<Switch id="send-welcome-email" bind:checked={notify} class="text-sm" />
|
||||||
|
</Field>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<Field label={$t('password')} required={!featureFlagsManager.value.oauth}>
|
||||||
|
<PasswordInput id="password" bind:value={password} autocomplete="new-password" />
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label={$t('confirm_password')} required={!featureFlagsManager.value.oauth}>
|
||||||
|
<PasswordInput id="confirmPassword" bind:value={passwordConfirm} autocomplete="new-password" />
|
||||||
|
<HelperText color="danger">{passwordMismatchMessage}</HelperText>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label={$t('admin.require_password_change_on_login')}>
|
||||||
|
<Switch id="require-password-change" bind:checked={shouldChangePassword} class="text-sm text-start" />
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label={$t('name')} required>
|
||||||
|
<Input bind:value={name} />
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label={$t('admin.quota_size_gib')}>
|
||||||
|
<Input bind:value={quotaSize} type="number" placeholder={$t('unlimited')} min="0" step="1" />
|
||||||
|
{#if quotaSizeWarning}
|
||||||
|
<HelperText color="danger">{$t('errors.quota_higher_than_disk_size')}</HelperText>
|
||||||
{/if}
|
{/if}
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Stack gap={4}>
|
<Field label={$t('admin.admin_user')}>
|
||||||
<Field label={$t('email')} required>
|
<Switch bind:checked={isAdmin} />
|
||||||
<Input bind:value={email} type="email" />
|
</Field>
|
||||||
</Field>
|
</Stack>
|
||||||
|
</FormModal>
|
||||||
{#if featureFlagsManager.value.email}
|
|
||||||
<Field label={$t('admin.send_welcome_email')}>
|
|
||||||
<Switch id="send-welcome-email" bind:checked={notify} class="text-sm" />
|
|
||||||
</Field>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<Field label={$t('password')} required={!featureFlagsManager.value.oauth}>
|
|
||||||
<PasswordInput id="password" bind:value={password} autocomplete="new-password" />
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label={$t('confirm_password')} required={!featureFlagsManager.value.oauth}>
|
|
||||||
<PasswordInput id="confirmPassword" bind:value={passwordConfirm} autocomplete="new-password" />
|
|
||||||
<HelperText color="danger">{passwordMismatchMessage}</HelperText>
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label={$t('admin.require_password_change_on_login')}>
|
|
||||||
<Switch id="require-password-change" bind:checked={shouldChangePassword} class="text-sm text-start" />
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label={$t('name')} required>
|
|
||||||
<Input bind:value={name} />
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label={$t('admin.quota_size_gib')}>
|
|
||||||
<Input bind:value={quotaSize} type="number" placeholder={$t('unlimited')} min="0" step="1" />
|
|
||||||
{#if quotaSizeWarning}
|
|
||||||
<HelperText color="danger">{$t('errors.quota_higher_than_disk_size')}</HelperText>
|
|
||||||
{/if}
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label={$t('admin.admin_user')}>
|
|
||||||
<Switch bind:checked={isAdmin} />
|
|
||||||
</Field>
|
|
||||||
</Stack>
|
|
||||||
</form>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<HStack fullWidth>
|
|
||||||
<Button color="secondary" fullWidth onclick={() => onClose()} shape="round">{$t('cancel')}</Button>
|
|
||||||
<Button type="submit" disabled={!valid} fullWidth shape="round" form="create-new-user-form"
|
|
||||||
>{$t('create')}
|
|
||||||
</Button>
|
|
||||||
</HStack>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,7 @@
|
||||||
import { user as authUser } from '$lib/stores/user.store';
|
import { user as authUser } from '$lib/stores/user.store';
|
||||||
import { userInteraction } from '$lib/stores/user.svelte';
|
import { userInteraction } from '$lib/stores/user.svelte';
|
||||||
import { ByteUnit, convertFromBytes, convertToBytes } from '$lib/utils/byte-units';
|
import { ByteUnit, convertFromBytes, convertToBytes } from '$lib/utils/byte-units';
|
||||||
import {
|
import { Field, FormModal, Input, Link, NumberInput, Switch, Text } from '@immich/ui';
|
||||||
Button,
|
|
||||||
Field,
|
|
||||||
HStack,
|
|
||||||
Input,
|
|
||||||
Link,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalFooter,
|
|
||||||
NumberInput,
|
|
||||||
Switch,
|
|
||||||
Text,
|
|
||||||
} from '@immich/ui';
|
|
||||||
import { mdiAccountEditOutline } from '@mdi/js';
|
import { mdiAccountEditOutline } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
@ -69,49 +57,36 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title={$t('edit_user')} size="small" icon={mdiAccountEditOutline} {onClose}>
|
<FormModal title={$t('edit_user')} size="small" icon={mdiAccountEditOutline} {onClose} {onSubmit}>
|
||||||
<ModalBody>
|
<Field label={$t('email')} required>
|
||||||
<form onsubmit={onSubmit} autocomplete="off" id="edit-user-form">
|
<Input type="email" bind:value={email} />
|
||||||
<Field label={$t('email')} required>
|
</Field>
|
||||||
<Input type="email" bind:value={email} />
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label={$t('name')} required class="mt-4">
|
<Field label={$t('name')} required class="mt-4">
|
||||||
<Input bind:value={name} />
|
<Input bind:value={name} />
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Field label={$t('admin.quota_size_gib')} class="mt-4">
|
<Field label={$t('admin.quota_size_gib')} class="mt-4">
|
||||||
<NumberInput bind:value={quotaSize} min="0" step="1" placeholder={$t('unlimited')} />
|
<NumberInput bind:value={quotaSize} min="0" step="1" placeholder={$t('unlimited')} />
|
||||||
{#if quotaSizeWarning}
|
{#if quotaSizeWarning}
|
||||||
<Text size="small" color="danger">{$t('errors.quota_higher_than_disk_size')}</Text>
|
<Text size="small" color="danger">{$t('errors.quota_higher_than_disk_size')}</Text>
|
||||||
{/if}
|
{/if}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Field label={$t('storage_label')} class="mt-4">
|
<Field label={$t('storage_label')} class="mt-4">
|
||||||
<Input bind:value={storageLabel} />
|
<Input bind:value={storageLabel} />
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Text size="small" class="mt-2" color="muted">
|
<Text size="small" class="mt-2" color="muted">
|
||||||
{$t('admin.note_apply_storage_label_previous_assets')}
|
{$t('admin.note_apply_storage_label_previous_assets')}
|
||||||
<Link href={AppRoute.ADMIN_QUEUES}>
|
<Link href={AppRoute.ADMIN_QUEUES}>
|
||||||
{$t('admin.storage_template_migration_job')}
|
{$t('admin.storage_template_migration_job')}
|
||||||
</Link>
|
</Link>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{#if user.id !== $authUser.id}
|
{#if user.id !== $authUser.id}
|
||||||
<Field label={$t('admin.admin_user')}>
|
<Field label={$t('admin.admin_user')}>
|
||||||
<Switch bind:checked={isAdmin} class="mt-4" />
|
<Switch bind:checked={isAdmin} class="mt-4" />
|
||||||
</Field>
|
</Field>
|
||||||
{/if}
|
{/if}
|
||||||
</form>
|
</FormModal>
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<HStack fullWidth>
|
|
||||||
<Button shape="round" color="secondary" fullWidth form="edit-user-form" onclick={() => onClose()}
|
|
||||||
>{$t('cancel')}</Button
|
|
||||||
>
|
|
||||||
<Button type="submit" shape="round" fullWidth form="edit-user-form">{$t('confirm')}</Button>
|
|
||||||
</HStack>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue