mirror of
https://github.com/immich-app/immich.git
synced 2025-11-27 19:29:55 +09:00
chore(web): migration svelte 5 syntax (#13883)
This commit is contained in:
@@ -8,15 +8,15 @@
|
||||
import { t } from 'svelte-i18n';
|
||||
import { retrieveServerConfig } from '$lib/stores/server-config.store';
|
||||
|
||||
let email = '';
|
||||
let password = '';
|
||||
let confirmPassword = '';
|
||||
let name = '';
|
||||
let email = $state('');
|
||||
let password = $state('');
|
||||
let confirmPassword = $state('');
|
||||
let name = $state('');
|
||||
|
||||
let errorMessage: string;
|
||||
let canRegister = false;
|
||||
let errorMessage: string = $state('');
|
||||
let canRegister = $state(false);
|
||||
|
||||
$: {
|
||||
$effect(() => {
|
||||
if (password !== confirmPassword && confirmPassword.length > 0) {
|
||||
errorMessage = $t('password_does_not_match');
|
||||
canRegister = false;
|
||||
@@ -24,7 +24,7 @@
|
||||
errorMessage = '';
|
||||
canRegister = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function registerAdmin() {
|
||||
if (canRegister) {
|
||||
@@ -40,9 +40,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onsubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await registerAdmin();
|
||||
};
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={registerAdmin} method="post" class="mt-5 flex flex-col gap-5">
|
||||
<form {onsubmit} method="post" class="mt-5 flex flex-col gap-5">
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">{$t('admin_email')}</label>
|
||||
<input class="immich-form-input" id="email" bind:value={email} type="email" autocomplete="email" required />
|
||||
|
||||
@@ -5,13 +5,23 @@
|
||||
import FullScreenModal from '../shared-components/full-screen-modal.svelte';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
|
||||
export let apiKey: { name: string };
|
||||
export let title: string;
|
||||
export let cancelText = $t('cancel');
|
||||
export let submitText = $t('save');
|
||||
interface Props {
|
||||
apiKey: { name: string };
|
||||
title: string;
|
||||
cancelText?: string;
|
||||
submitText?: string;
|
||||
onSubmit: (apiKey: { name: string }) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export let onSubmit: (apiKey: { name: string }) => void;
|
||||
export let onCancel: () => void;
|
||||
let {
|
||||
apiKey = $bindable(),
|
||||
title,
|
||||
cancelText = $t('cancel'),
|
||||
submitText = $t('save'),
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: Props = $props();
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (apiKey.name) {
|
||||
@@ -23,17 +33,23 @@
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
handleSubmit();
|
||||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal {title} icon={mdiKeyVariant} onClose={() => onCancel()}>
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off" id="api-key-form">
|
||||
<form {onsubmit} autocomplete="off" id="api-key-form">
|
||||
<div class="mb-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="name">{$t('name')}</label>
|
||||
<input class="immich-form-input" id="name" name="name" type="text" bind:value={apiKey.name} />
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={() => onCancel()}>{cancelText}</Button>
|
||||
|
||||
{#snippet stickyBottom()}
|
||||
<Button color="gray" fullwidth onclick={() => onCancel()}>{cancelText}</Button>
|
||||
<Button type="submit" fullwidth form="api-key-form">{submitText}</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
import FullScreenModal from '../shared-components/full-screen-modal.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let secret = '';
|
||||
export let onDone: () => void;
|
||||
interface Props {
|
||||
secret?: string;
|
||||
onDone: () => void;
|
||||
}
|
||||
|
||||
let { secret = '', onDone }: Props = $props();
|
||||
</script>
|
||||
|
||||
<FullScreenModal title={$t('api_key')} icon={mdiKeyVariant} onClose={onDone}>
|
||||
@@ -21,8 +25,8 @@
|
||||
<textarea class="immich-form-input" id="secret" name="secret" readonly={true} value={secret}></textarea>
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button on:click={() => copyToClipboard(secret)} fullwidth>{$t('copy_to_clipboard')}</Button>
|
||||
<Button on:click={onDone} fullwidth>{$t('done')}</Button>
|
||||
</svelte:fragment>
|
||||
{#snippet stickyBottom()}
|
||||
<Button onclick={() => copyToClipboard(secret)} fullwidth>{$t('copy_to_clipboard')}</Button>
|
||||
<Button onclick={onDone} fullwidth>{$t('done')}</Button>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
|
||||
@@ -4,17 +4,21 @@
|
||||
import { updateMyUser } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let onSuccess: () => void;
|
||||
interface Props {
|
||||
onSuccess: () => void;
|
||||
}
|
||||
|
||||
let errorMessage: string;
|
||||
let { onSuccess }: Props = $props();
|
||||
|
||||
let errorMessage: string = $state('');
|
||||
let success: string;
|
||||
|
||||
let password = '';
|
||||
let passwordConfirm = '';
|
||||
let password = $state('');
|
||||
let passwordConfirm = $state('');
|
||||
|
||||
let valid = false;
|
||||
let valid = $state(false);
|
||||
|
||||
$: {
|
||||
$effect(() => {
|
||||
if (password !== passwordConfirm && passwordConfirm.length > 0) {
|
||||
errorMessage = $t('password_does_not_match');
|
||||
valid = false;
|
||||
@@ -22,7 +26,7 @@
|
||||
errorMessage = '';
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function changePassword() {
|
||||
if (valid) {
|
||||
@@ -33,9 +37,14 @@
|
||||
onSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
const onsubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await changePassword();
|
||||
};
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={changePassword} method="post" class="mt-5 flex flex-col gap-5">
|
||||
<form {onsubmit} method="post" class="mt-5 flex flex-col gap-5">
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="password">{$t('new_password')}</label>
|
||||
<PasswordField id="password" bind:password autocomplete="new-password" />
|
||||
|
||||
@@ -10,29 +10,33 @@
|
||||
import Slider from '../elements/slider.svelte';
|
||||
import PasswordField from '../shared-components/password-field.svelte';
|
||||
|
||||
export let onClose: () => void;
|
||||
export let onSubmit: () => void;
|
||||
export let onCancel: () => void;
|
||||
export let oauthEnabled = false;
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
onSubmit: () => void;
|
||||
onCancel: () => void;
|
||||
oauthEnabled?: boolean;
|
||||
}
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
let { onClose, onSubmit, onCancel, oauthEnabled = false }: Props = $props();
|
||||
|
||||
let email = '';
|
||||
let password = '';
|
||||
let confirmPassword = '';
|
||||
let name = '';
|
||||
let shouldChangePassword = true;
|
||||
let notify = true;
|
||||
let error = $state('');
|
||||
let success = $state('');
|
||||
|
||||
let canCreateUser = false;
|
||||
let quotaSize: number | undefined;
|
||||
let isCreatingUser = false;
|
||||
let email = $state('');
|
||||
let password = $state('');
|
||||
let confirmPassword = $state('');
|
||||
let name = $state('');
|
||||
let shouldChangePassword = $state(true);
|
||||
let notify = $state(true);
|
||||
|
||||
$: quotaSizeInBytes = quotaSize ? convertToBytes(quotaSize, ByteUnit.GiB) : null;
|
||||
$: quotaSizeWarning = quotaSizeInBytes && quotaSizeInBytes > $serverInfo.diskSizeRaw;
|
||||
let canCreateUser = $state(false);
|
||||
let quotaSize: number | undefined = $state();
|
||||
let isCreatingUser = $state(false);
|
||||
|
||||
$: {
|
||||
let quotaSizeInBytes = $derived(quotaSize ? convertToBytes(quotaSize, ByteUnit.GiB) : null);
|
||||
let quotaSizeWarning = $derived(quotaSizeInBytes && quotaSizeInBytes > $serverInfo.diskSizeRaw);
|
||||
|
||||
$effect(() => {
|
||||
if (password !== confirmPassword && confirmPassword.length > 0) {
|
||||
error = $t('password_does_not_match');
|
||||
canCreateUser = false;
|
||||
@@ -40,7 +44,7 @@
|
||||
error = '';
|
||||
canCreateUser = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function registerUser() {
|
||||
if (canCreateUser && !isCreatingUser) {
|
||||
@@ -71,10 +75,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onsubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await registerUser();
|
||||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal title={$t('create_new_user')} showLogo {onClose}>
|
||||
<form on:submit|preventDefault={registerUser} autocomplete="off" id="create-new-user-form">
|
||||
<form {onsubmit} autocomplete="off" id="create-new-user-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">{$t('email')}</label>
|
||||
<input class="immich-form-input" id="email" bind:value={email} type="email" required />
|
||||
@@ -134,8 +143,9 @@
|
||||
<p class="text-sm text-immich-primary">{success}</p>
|
||||
{/if}
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={onCancel}>{$t('cancel')}</Button>
|
||||
|
||||
{#snippet stickyBottom()}
|
||||
<Button color="gray" fullwidth onclick={onCancel}>{$t('cancel')}</Button>
|
||||
<Button type="submit" disabled={isCreatingUser} fullwidth form="create-new-user-form">{$t('create')}</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
|
||||
@@ -6,15 +6,19 @@
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
export let onEditSuccess: ((album: AlbumResponseDto) => unknown) | undefined = undefined;
|
||||
export let onCancel: (() => unknown) | undefined = undefined;
|
||||
export let onClose: () => void;
|
||||
interface Props {
|
||||
album: AlbumResponseDto;
|
||||
onEditSuccess?: ((album: AlbumResponseDto) => unknown) | undefined;
|
||||
onCancel?: (() => unknown) | undefined;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
let albumName = album.albumName;
|
||||
let description = album.description;
|
||||
let { album = $bindable(), onEditSuccess = undefined, onCancel = undefined, onClose }: Props = $props();
|
||||
|
||||
let isSubmitting = false;
|
||||
let albumName = $state(album.albumName);
|
||||
let description = $state(album.description);
|
||||
|
||||
let isSubmitting = $state(false);
|
||||
|
||||
const handleUpdateAlbumInfo = async () => {
|
||||
isSubmitting = true;
|
||||
@@ -35,10 +39,15 @@
|
||||
isSubmitting = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onsubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await handleUpdateAlbumInfo();
|
||||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal title={$t('edit_album')} width="wide" {onClose}>
|
||||
<form on:submit|preventDefault={handleUpdateAlbumInfo} autocomplete="off" id="edit-album-form">
|
||||
<form {onsubmit} autocomplete="off" id="edit-album-form">
|
||||
<div class="flex items-center">
|
||||
<div class="hidden sm:flex">
|
||||
<AlbumCover {album} class="h-[200px] w-[200px] m-4 shadow-lg" />
|
||||
@@ -57,8 +66,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={() => onCancel?.()}>{$t('cancel')}</Button>
|
||||
|
||||
{#snippet stickyBottom()}
|
||||
<Button color="gray" fullwidth onclick={() => onCancel?.()}>{$t('cancel')}</Button>
|
||||
<Button type="submit" fullwidth disabled={isSubmitting} form="edit-album-form">{$t('ok')}</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
|
||||
@@ -10,23 +10,35 @@
|
||||
import { t } from 'svelte-i18n';
|
||||
import { ByteUnit, convertFromBytes, convertToBytes } from '$lib/utils/byte-units';
|
||||
|
||||
export let user: UserAdminResponseDto;
|
||||
export let canResetPassword = true;
|
||||
export let newPassword: string;
|
||||
export let onClose: () => void;
|
||||
export let onResetPasswordSuccess: () => void;
|
||||
export let onEditSuccess: () => void;
|
||||
interface Props {
|
||||
user: UserAdminResponseDto;
|
||||
canResetPassword?: boolean;
|
||||
newPassword: string;
|
||||
onClose: () => void;
|
||||
onResetPasswordSuccess: () => void;
|
||||
onEditSuccess: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
user,
|
||||
canResetPassword = true,
|
||||
newPassword = $bindable(),
|
||||
onClose,
|
||||
onResetPasswordSuccess,
|
||||
onEditSuccess,
|
||||
}: Props = $props();
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
let quotaSize = user.quotaSizeInBytes ? convertFromBytes(user.quotaSizeInBytes, ByteUnit.GiB) : null;
|
||||
let quotaSize = $state(user.quotaSizeInBytes ? convertFromBytes(user.quotaSizeInBytes, ByteUnit.GiB) : null);
|
||||
|
||||
const previousQutoa = user.quotaSizeInBytes;
|
||||
|
||||
$: quotaSizeWarning =
|
||||
let quotaSizeWarning = $derived(
|
||||
previousQutoa !== convertToBytes(Number(quotaSize), ByteUnit.GiB) &&
|
||||
!!quotaSize &&
|
||||
convertToBytes(Number(quotaSize), ByteUnit.GiB) > $serverInfo.diskSizeRaw;
|
||||
!!quotaSize &&
|
||||
convertToBytes(Number(quotaSize), ByteUnit.GiB) > $serverInfo.diskSizeRaw,
|
||||
);
|
||||
|
||||
const editUser = async () => {
|
||||
try {
|
||||
@@ -89,10 +101,15 @@
|
||||
|
||||
return generatedPassword;
|
||||
}
|
||||
|
||||
const onSubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await editUser();
|
||||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal title={$t('edit_user')} icon={mdiAccountEditOutline} {onClose}>
|
||||
<form on:submit|preventDefault={editUser} autocomplete="off" id="edit-user-form">
|
||||
<form onsubmit={onSubmit} autocomplete="off" id="edit-user-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">{$t('email')}</label>
|
||||
<input class="immich-form-input" id="email" name="email" type="email" bind:value={user.email} />
|
||||
@@ -140,10 +157,11 @@
|
||||
<p class="ml-4 text-sm text-immich-primary">{success}</p>
|
||||
{/if}
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
|
||||
{#snippet stickyBottom()}
|
||||
{#if canResetPassword}
|
||||
<Button color="light-red" fullwidth on:click={resetPassword}>{$t('reset_password')}</Button>
|
||||
<Button color="light-red" fullwidth onclick={resetPassword}>{$t('reset_password')}</Button>
|
||||
{/if}
|
||||
<Button type="submit" fullwidth form="edit-user-form">{$t('confirm')}</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
|
||||
@@ -5,13 +5,25 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let exclusionPattern: string;
|
||||
export let exclusionPatterns: string[] = [];
|
||||
export let isEditing = false;
|
||||
export let submitText = $t('submit');
|
||||
export let onCancel: () => void;
|
||||
export let onSubmit: (exclusionPattern: string) => void;
|
||||
export let onDelete: () => void = () => {};
|
||||
interface Props {
|
||||
exclusionPattern: string;
|
||||
exclusionPatterns?: string[];
|
||||
isEditing?: boolean;
|
||||
submitText?: string;
|
||||
onCancel: () => void;
|
||||
onSubmit: (exclusionPattern: string) => void;
|
||||
onDelete?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
exclusionPattern = $bindable(),
|
||||
exclusionPatterns = $bindable([]),
|
||||
isEditing = false,
|
||||
submitText = $t('submit'),
|
||||
onCancel,
|
||||
onSubmit,
|
||||
onDelete,
|
||||
}: Props = $props();
|
||||
|
||||
onMount(() => {
|
||||
if (isEditing) {
|
||||
@@ -19,12 +31,19 @@
|
||||
}
|
||||
});
|
||||
|
||||
$: isDuplicate = exclusionPattern !== null && exclusionPatterns.includes(exclusionPattern);
|
||||
$: canSubmit = exclusionPattern && !exclusionPatterns.includes(exclusionPattern);
|
||||
let isDuplicate = $derived(exclusionPattern !== null && exclusionPatterns.includes(exclusionPattern));
|
||||
let canSubmit = $derived(exclusionPattern && !exclusionPatterns.includes(exclusionPattern));
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
if (canSubmit) {
|
||||
onSubmit(exclusionPattern);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal title={$t('add_exclusion_pattern')} icon={mdiFolderRemove} onClose={onCancel}>
|
||||
<form on:submit|preventDefault={() => onSubmit(exclusionPattern)} autocomplete="off" id="add-exclusion-pattern-form">
|
||||
<form {onsubmit} autocomplete="off" id="add-exclusion-pattern-form">
|
||||
<p class="py-5 text-sm">
|
||||
{$t('admin.exclusion_pattern_description')}
|
||||
<br /><br />
|
||||
@@ -46,11 +65,12 @@
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={onCancel}>{$t('cancel')}</Button>
|
||||
|
||||
{#snippet stickyBottom()}
|
||||
<Button color="gray" fullwidth onclick={onCancel}>{$t('cancel')}</Button>
|
||||
{#if isEditing}
|
||||
<Button color="red" fullwidth on:click={onDelete}>{$t('delete')}</Button>
|
||||
<Button color="red" fullwidth onclick={onDelete}>{$t('delete')}</Button>
|
||||
{/if}
|
||||
<Button type="submit" disabled={!canSubmit} fullwidth form="add-exclusion-pattern-form">{submitText}</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
|
||||
@@ -5,15 +5,29 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let importPath: string | null;
|
||||
export let importPaths: string[] = [];
|
||||
export let title = $t('import_path');
|
||||
export let cancelText = $t('cancel');
|
||||
export let submitText = $t('save');
|
||||
export let isEditing = false;
|
||||
export let onCancel: () => void;
|
||||
export let onSubmit: (importPath: string | null) => void;
|
||||
export let onDelete: () => void = () => {};
|
||||
interface Props {
|
||||
importPath: string | null;
|
||||
importPaths?: string[];
|
||||
title?: string;
|
||||
cancelText?: string;
|
||||
submitText?: string;
|
||||
isEditing?: boolean;
|
||||
onCancel: () => void;
|
||||
onSubmit: (importPath: string | null) => void;
|
||||
onDelete?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
importPath = $bindable(),
|
||||
importPaths = $bindable([]),
|
||||
title = $t('import_path'),
|
||||
cancelText = $t('cancel'),
|
||||
submitText = $t('save'),
|
||||
isEditing = false,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
onDelete,
|
||||
}: Props = $props();
|
||||
|
||||
onMount(() => {
|
||||
if (isEditing) {
|
||||
@@ -21,12 +35,19 @@
|
||||
}
|
||||
});
|
||||
|
||||
$: isDuplicate = importPath !== null && importPaths.includes(importPath);
|
||||
$: canSubmit = importPath !== '' && importPath !== null && !importPaths.includes(importPath);
|
||||
let isDuplicate = $derived(importPath !== null && importPaths.includes(importPath));
|
||||
let canSubmit = $derived(importPath !== '' && importPath !== null && !importPaths.includes(importPath));
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
if (canSubmit) {
|
||||
onSubmit(importPath);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal {title} icon={mdiFolderSync} onClose={onCancel}>
|
||||
<form on:submit|preventDefault={() => onSubmit(importPath)} autocomplete="off" id="library-import-path-form">
|
||||
<form {onsubmit} autocomplete="off" id="library-import-path-form">
|
||||
<p class="py-5 text-sm">{$t('admin.library_import_path_description')}</p>
|
||||
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
@@ -40,11 +61,12 @@
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={onCancel}>{cancelText}</Button>
|
||||
|
||||
{#snippet stickyBottom()}
|
||||
<Button color="gray" fullwidth onclick={onCancel}>{cancelText}</Button>
|
||||
{#if isEditing}
|
||||
<Button color="red" fullwidth on:click={onDelete}>{$t('delete')}</Button>
|
||||
<Button color="red" fullwidth onclick={onDelete}>{$t('delete')}</Button>
|
||||
{/if}
|
||||
<Button type="submit" disabled={!canSubmit} fullwidth form="library-import-path-form">{submitText}</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
|
||||
@@ -11,19 +11,23 @@
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let library: LibraryResponseDto;
|
||||
export let onCancel: () => void;
|
||||
export let onSubmit: (library: LibraryResponseDto) => void;
|
||||
interface Props {
|
||||
library: LibraryResponseDto;
|
||||
onCancel: () => void;
|
||||
onSubmit: (library: LibraryResponseDto) => void;
|
||||
}
|
||||
|
||||
let addImportPath = false;
|
||||
let editImportPath: number | null = null;
|
||||
let { library = $bindable(), onCancel, onSubmit }: Props = $props();
|
||||
|
||||
let importPathToAdd: string | null = null;
|
||||
let editedImportPath: string;
|
||||
let addImportPath = $state(false);
|
||||
let editImportPath: number | null = $state(null);
|
||||
|
||||
let validatedPaths: ValidateLibraryImportPathResponseDto[] = [];
|
||||
let importPathToAdd: string | null = $state(null);
|
||||
let editedImportPath: string = $state('');
|
||||
|
||||
$: importPaths = validatedPaths.map((validatedPath) => validatedPath.importPath);
|
||||
let validatedPaths: ValidateLibraryImportPathResponseDto[] = $state([]);
|
||||
|
||||
let importPaths = $derived(validatedPaths.map((validatedPath) => validatedPath.importPath));
|
||||
|
||||
onMount(async () => {
|
||||
if (library.importPaths) {
|
||||
@@ -134,6 +138,11 @@
|
||||
editImportPath = null;
|
||||
}
|
||||
};
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
onSubmit({ ...library });
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if addImportPath}
|
||||
@@ -163,7 +172,7 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<form on:submit|preventDefault={() => onSubmit({ ...library })} autocomplete="off" class="m-4 flex flex-col gap-4">
|
||||
<form {onsubmit} autocomplete="off" class="m-4 flex flex-col gap-4">
|
||||
<table class="text-left">
|
||||
<tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray">
|
||||
{#each validatedPaths as validatedPath, listIndex}
|
||||
@@ -199,7 +208,7 @@
|
||||
icon={mdiPencilOutline}
|
||||
title={$t('edit_import_path')}
|
||||
size="16"
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
editImportPath = listIndex;
|
||||
editedImportPath = validatedPath.importPath;
|
||||
}}
|
||||
@@ -223,7 +232,7 @@
|
||||
><Button
|
||||
type="button"
|
||||
size="sm"
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
addImportPath = true;
|
||||
}}>{$t('add_path')}</Button
|
||||
></td
|
||||
@@ -233,12 +242,12 @@
|
||||
</table>
|
||||
<div class="flex justify-between w-full">
|
||||
<div class="justify-end gap-2">
|
||||
<Button size="sm" color="gray" on:click={() => revalidate()}
|
||||
<Button size="sm" color="gray" onclick={() => revalidate()}
|
||||
><Icon path={mdiRefresh} size={20} />{$t('validate')}</Button
|
||||
>
|
||||
</div>
|
||||
<div class="justify-end gap-2">
|
||||
<Button size="sm" color="gray" on:click={onCancel}>{$t('cancel')}</Button>
|
||||
<Button size="sm" color="gray" onclick={onCancel}>{$t('cancel')}</Button>
|
||||
<Button size="sm" type="submit">{$t('save')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,18 +3,27 @@
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let library: Partial<LibraryResponseDto>;
|
||||
export let onCancel: () => void;
|
||||
export let onSubmit: (library: Partial<LibraryResponseDto>) => void;
|
||||
interface Props {
|
||||
library: Partial<LibraryResponseDto>;
|
||||
onCancel: () => void;
|
||||
onSubmit: (library: Partial<LibraryResponseDto>) => void;
|
||||
}
|
||||
|
||||
let { library = $bindable(), onCancel, onSubmit }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
onSubmit({ ...library });
|
||||
};
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={() => onSubmit({ ...library })} autocomplete="off" class="m-4 flex flex-col gap-2">
|
||||
<form {onsubmit} autocomplete="off" class="m-4 flex flex-col gap-2">
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="path">{$t('name')}</label>
|
||||
<input class="immich-form-input" id="name" name="name" type="text" bind:value={library.name} />
|
||||
</div>
|
||||
<div class="flex w-full justify-end gap-2 pt-2">
|
||||
<Button size="sm" color="gray" on:click={onCancel}>{$t('cancel')}</Button>
|
||||
<Button size="sm" color="gray" onclick={onCancel}>{$t('cancel')}</Button>
|
||||
<Button size="sm" type="submit">{$t('save')}</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -8,17 +8,21 @@
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let library: Partial<LibraryResponseDto>;
|
||||
export let onCancel: () => void;
|
||||
export let onSubmit: (library: Partial<LibraryResponseDto>) => void;
|
||||
interface Props {
|
||||
library: Partial<LibraryResponseDto>;
|
||||
onCancel: () => void;
|
||||
onSubmit: (library: Partial<LibraryResponseDto>) => void;
|
||||
}
|
||||
|
||||
let addExclusionPattern = false;
|
||||
let editExclusionPattern: number | null = null;
|
||||
let { library = $bindable(), onCancel, onSubmit }: Props = $props();
|
||||
|
||||
let exclusionPatternToAdd: string;
|
||||
let editedExclusionPattern: string;
|
||||
let addExclusionPattern = $state(false);
|
||||
let editExclusionPattern: number | null = $state(null);
|
||||
|
||||
let exclusionPatterns: string[] = [];
|
||||
let exclusionPatternToAdd: string = $state('');
|
||||
let editedExclusionPattern: string = $state('');
|
||||
|
||||
let exclusionPatterns: string[] = $state([]);
|
||||
|
||||
onMount(() => {
|
||||
if (library.exclusionPatterns) {
|
||||
@@ -89,6 +93,11 @@
|
||||
editExclusionPattern = null;
|
||||
}
|
||||
};
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
onSubmit(library);
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if addExclusionPattern}
|
||||
@@ -113,7 +122,7 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<form on:submit|preventDefault={() => onSubmit(library)} autocomplete="off" class="m-4 flex flex-col gap-4">
|
||||
<form {onsubmit} autocomplete="off" class="m-4 flex flex-col gap-4">
|
||||
<table class="w-full text-left">
|
||||
<tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray">
|
||||
{#each exclusionPatterns as exclusionPattern, listIndex}
|
||||
@@ -131,7 +140,7 @@
|
||||
icon={mdiPencilOutline}
|
||||
title={$t('edit_exclusion_pattern')}
|
||||
size="16"
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
editExclusionPattern = listIndex;
|
||||
editedExclusionPattern = exclusionPattern;
|
||||
}}
|
||||
@@ -154,7 +163,7 @@
|
||||
<td class="w-1/4 text-ellipsis px-4 text-sm"
|
||||
><Button
|
||||
size="sm"
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
addExclusionPattern = true;
|
||||
}}>{$t('add_exclusion_pattern')}</Button
|
||||
></td
|
||||
@@ -164,7 +173,7 @@
|
||||
</table>
|
||||
|
||||
<div class="flex w-full justify-end gap-4">
|
||||
<Button size="sm" color="gray" on:click={onCancel}>{$t('cancel')}</Button>
|
||||
<Button size="sm" color="gray" onclick={onCancel}>{$t('cancel')}</Button>
|
||||
<Button size="sm" type="submit">{$t('save')}</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -8,27 +8,37 @@
|
||||
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let onCancel: () => void;
|
||||
export let onSubmit: (ownerId: string) => void;
|
||||
interface Props {
|
||||
onCancel: () => void;
|
||||
onSubmit: (ownerId: string) => void;
|
||||
}
|
||||
|
||||
let ownerId: string = $user.id;
|
||||
let { onCancel, onSubmit }: Props = $props();
|
||||
|
||||
let userOptions: { value: string; text: string }[] = [];
|
||||
let ownerId: string = $state($user.id);
|
||||
|
||||
let userOptions: { value: string; text: string }[] = $state([]);
|
||||
|
||||
onMount(async () => {
|
||||
const users = await searchUsersAdmin({});
|
||||
userOptions = users.map((user) => ({ value: user.id, text: user.name }));
|
||||
});
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
onSubmit(ownerId);
|
||||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal title={$t('select_library_owner')} icon={mdiFolderSync} onClose={onCancel}>
|
||||
<form on:submit|preventDefault={() => onSubmit(ownerId)} autocomplete="off" id="select-library-owner-form">
|
||||
<form {onsubmit} autocomplete="off" id="select-library-owner-form">
|
||||
<p class="p-5 text-sm">{$t('admin.note_cannot_be_changed_later')}</p>
|
||||
|
||||
<SettingSelect bind:value={ownerId} options={userOptions} name="user" />
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={onCancel}>{$t('cancel')}</Button>
|
||||
|
||||
{#snippet stickyBottom()}
|
||||
<Button color="gray" fullwidth onclick={onCancel}>{$t('cancel')}</Button>
|
||||
<Button type="submit" fullwidth form="select-library-owner-form">{$t('create')}</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
|
||||
@@ -12,16 +12,20 @@
|
||||
import PasswordField from '../shared-components/password-field.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let onSuccess: () => unknown | Promise<unknown>;
|
||||
export let onFirstLogin: () => unknown | Promise<unknown>;
|
||||
export let onOnboarding: () => unknown | Promise<unknown>;
|
||||
interface Props {
|
||||
onSuccess: () => unknown | Promise<unknown>;
|
||||
onFirstLogin: () => unknown | Promise<unknown>;
|
||||
onOnboarding: () => unknown | Promise<unknown>;
|
||||
}
|
||||
|
||||
let errorMessage: string;
|
||||
let email = '';
|
||||
let password = '';
|
||||
let oauthError = '';
|
||||
let loading = false;
|
||||
let oauthLoading = true;
|
||||
let { onSuccess, onFirstLogin, onOnboarding }: Props = $props();
|
||||
|
||||
let errorMessage: string = $state('');
|
||||
let email = $state('');
|
||||
let password = $state('');
|
||||
let oauthError = $state('');
|
||||
let loading = $state(false);
|
||||
let oauthLoading = $state(true);
|
||||
|
||||
onMount(async () => {
|
||||
if (!$featureFlags.oauth) {
|
||||
@@ -87,10 +91,15 @@
|
||||
oauthError = $t('errors.unable_to_login_with_oauth');
|
||||
}
|
||||
};
|
||||
|
||||
const onsubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await handleLogin();
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if !oauthLoading && $featureFlags.passwordLogin}
|
||||
<form on:submit|preventDefault={handleLogin} class="mt-5 flex flex-col gap-5">
|
||||
<form {onsubmit} class="mt-5 flex flex-col gap-5">
|
||||
{#if errorMessage}
|
||||
<p class="text-red-400" transition:fade>
|
||||
{errorMessage}
|
||||
@@ -150,7 +159,7 @@
|
||||
size="lg"
|
||||
fullwidth
|
||||
color={$featureFlags.passwordLogin ? 'secondary' : 'primary'}
|
||||
on:click={handleOAuthLogin}
|
||||
onclick={handleOAuthLogin}
|
||||
>
|
||||
{#if oauthLoading}
|
||||
<span class="h-6">
|
||||
|
||||
@@ -9,14 +9,19 @@
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
import { SvelteSet } from 'svelte/reactivity';
|
||||
|
||||
export let onTag: (tagIds: string[]) => void;
|
||||
export let onCancel: () => void;
|
||||
interface Props {
|
||||
onTag: (tagIds: string[]) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
let allTags: TagResponseDto[] = [];
|
||||
$: tagMap = Object.fromEntries(allTags.map((tag) => [tag.id, tag]));
|
||||
let selectedIds = new Set<string>();
|
||||
$: disabled = selectedIds.size === 0;
|
||||
let { onTag, onCancel }: Props = $props();
|
||||
|
||||
let allTags: TagResponseDto[] = $state([]);
|
||||
let tagMap = $derived(Object.fromEntries(allTags.map((tag) => [tag.id, tag])));
|
||||
let selectedIds = $state(new SvelteSet<string>());
|
||||
let disabled = $derived(selectedIds.size === 0);
|
||||
|
||||
onMount(async () => {
|
||||
allTags = await getAllTags();
|
||||
@@ -37,19 +42,26 @@
|
||||
selectedIds.delete(tag);
|
||||
selectedIds = selectedIds;
|
||||
};
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
handleSubmit();
|
||||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal title={$t('tag_assets')} icon={mdiTag} onClose={onCancel}>
|
||||
<div class="text-sm">
|
||||
<p>
|
||||
<FormatMessage key="tag_not_found_question" let:message>
|
||||
<a href={AppRoute.TAGS} class="text-immich-primary dark:text-immich-dark-primary underline">
|
||||
{message}
|
||||
</a>
|
||||
<FormatMessage key="tag_not_found_question">
|
||||
{#snippet children({ message })}
|
||||
<a href={AppRoute.TAGS} class="text-immich-primary dark:text-immich-dark-primary underline">
|
||||
{message}
|
||||
</a>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</div>
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off" id="create-tag-form">
|
||||
<form {onsubmit} autocomplete="off" id="create-tag-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<Combobox
|
||||
onSelect={handleSelect}
|
||||
@@ -77,7 +89,7 @@
|
||||
type="button"
|
||||
class="text-gray-100 dark:text-immich-dark-gray bg-immich-primary/95 dark:bg-immich-dark-primary/95 rounded-tr-full rounded-br-full place-items-center place-content-center pr-2 pl-1 py-1 hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all"
|
||||
title="Remove tag"
|
||||
on:click={() => handleRemove(tagId)}
|
||||
onclick={() => handleRemove(tagId)}
|
||||
>
|
||||
<Icon path={mdiClose} />
|
||||
</button>
|
||||
@@ -86,8 +98,8 @@
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={onCancel}>{$t('cancel')}</Button>
|
||||
{#snippet stickyBottom()}
|
||||
<Button color="gray" fullwidth onclick={onCancel}>{$t('cancel')}</Button>
|
||||
<Button type="submit" fullwidth form="create-tag-form" {disabled}>{$t('tag_assets')}</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
|
||||
Reference in New Issue
Block a user