mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
chore(web): migration svelte 5 syntax (#13883)
This commit is contained in:
parent
9203a61709
commit
0b3742cf13
310 changed files with 6435 additions and 4176 deletions
|
|
@ -1,5 +1,3 @@
|
|||
<svelte:options accessors />
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
NotificationType,
|
||||
|
|
@ -13,12 +11,17 @@
|
|||
import type { SettingsResetOptions } from './admin-settings';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let config: SystemConfigDto;
|
||||
interface Props {
|
||||
config: SystemConfigDto;
|
||||
children: import('svelte').Snippet<[{ savedConfig: SystemConfigDto; defaultConfig: SystemConfigDto }]>;
|
||||
}
|
||||
|
||||
let savedConfig: SystemConfigDto;
|
||||
let defaultConfig: SystemConfigDto;
|
||||
let { config = $bindable(), children }: Props = $props();
|
||||
|
||||
const handleReset = async (options: SettingsResetOptions) => {
|
||||
let savedConfig: SystemConfigDto | undefined = $state();
|
||||
let defaultConfig: SystemConfigDto | undefined = $state();
|
||||
|
||||
export const handleReset = async (options: SettingsResetOptions) => {
|
||||
await (options.default ? resetToDefault(options.configKeys) : reset(options.configKeys));
|
||||
};
|
||||
|
||||
|
|
@ -26,7 +29,8 @@
|
|||
let systemConfigDto = {
|
||||
...savedConfig,
|
||||
...update,
|
||||
};
|
||||
} as SystemConfigDto;
|
||||
|
||||
if (isEqual(systemConfigDto, savedConfig)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -59,6 +63,10 @@
|
|||
};
|
||||
|
||||
const resetToDefault = (configKeys: Array<keyof SystemConfigDto>) => {
|
||||
if (!defaultConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key of configKeys) {
|
||||
config = { ...config, [key]: defaultConfig[key] };
|
||||
}
|
||||
|
|
@ -75,5 +83,5 @@
|
|||
</script>
|
||||
|
||||
{#if savedConfig && defaultConfig}
|
||||
<slot {handleReset} {handleSave} {savedConfig} {defaultConfig} />
|
||||
{@render children({ savedConfig, defaultConfig })}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@
|
|||
import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte';
|
||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import { type SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
|
@ -12,15 +10,20 @@
|
|||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
import { t } from 'svelte-i18n';
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let isConfirmOpen = false;
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
let isConfirmOpen = $state(false);
|
||||
|
||||
const handleToggleOverride = () => {
|
||||
// click runs before bind
|
||||
|
|
@ -48,29 +51,31 @@
|
|||
onCancel={() => (isConfirmOpen = false)}
|
||||
onConfirm={() => handleSave(true)}
|
||||
>
|
||||
<svelte:fragment slot="prompt">
|
||||
{#snippet promptSnippet()}
|
||||
<div class="flex flex-col gap-4">
|
||||
<p>{$t('admin.authentication_settings_disable_all')}</p>
|
||||
<p>
|
||||
<FormatMessage key="admin.authentication_settings_reenable" let:message>
|
||||
<a
|
||||
href="https://immich.app/docs/administration/server-commands"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
class="underline"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
<FormatMessage key="admin.authentication_settings_reenable">
|
||||
{#snippet children({ message })}
|
||||
<a
|
||||
href="https://immich.app/docs/administration/server-commands"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
class="underline"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</ConfirmDialog>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" onsubmit={(e) => e.preventDefault()}>
|
||||
<div class="ml-4 mt-4 flex flex-col">
|
||||
<SettingAccordion
|
||||
key="oauth"
|
||||
|
|
@ -79,15 +84,17 @@
|
|||
>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
<FormatMessage key="admin.oauth_settings_more_details" let:message>
|
||||
<a
|
||||
href="https://immich.app/docs/administration/oauth"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
<FormatMessage key="admin.oauth_settings_more_details">
|
||||
{#snippet children({ message })}
|
||||
<a
|
||||
href="https://immich.app/docs/administration/oauth"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
|
||||
|
|
@ -147,7 +154,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_profile_signing_algorithm').toUpperCase()}
|
||||
desc={$t('admin.oauth_profile_signing_algorithm_description')}
|
||||
description={$t('admin.oauth_profile_signing_algorithm_description')}
|
||||
bind:value={config.oauth.profileSigningAlgorithm}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
|
|
@ -157,7 +164,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_storage_label_claim').toUpperCase()}
|
||||
desc={$t('admin.oauth_storage_label_claim_description')}
|
||||
description={$t('admin.oauth_storage_label_claim_description')}
|
||||
bind:value={config.oauth.storageLabelClaim}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
|
|
@ -167,7 +174,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_storage_quota_claim').toUpperCase()}
|
||||
desc={$t('admin.oauth_storage_quota_claim_description')}
|
||||
description={$t('admin.oauth_storage_quota_claim_description')}
|
||||
bind:value={config.oauth.storageQuotaClaim}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
|
|
@ -177,7 +184,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.oauth_storage_quota_default').toUpperCase()}
|
||||
desc={$t('admin.oauth_storage_quota_default_description')}
|
||||
description={$t('admin.oauth_storage_quota_default_description')}
|
||||
bind:value={config.oauth.defaultStorageQuota}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
|
|
@ -213,7 +220,7 @@
|
|||
values: { callback: 'app.immich:///oauth-callback' },
|
||||
})}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
on:click={() => handleToggleOverride()}
|
||||
onToggle={() => handleToggleOverride()}
|
||||
bind:checked={config.oauth.mobileOverrideEnabled}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,33 +3,40 @@
|
|||
import { isEqual } from 'lodash-es';
|
||||
import { fade } from 'svelte/transition';
|
||||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
$: cronExpressionOptions = [
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
let cronExpressionOptions = $derived([
|
||||
{ text: $t('interval.night_at_midnight'), value: '0 0 * * *' },
|
||||
{ text: $t('interval.night_at_twoam'), value: '0 02 * * *' },
|
||||
{ text: $t('interval.day_at_onepm'), value: '0 13 * * *' },
|
||||
{ text: $t('interval.hours', { values: { hours: 6 } }), value: '0 */6 * * *' },
|
||||
];
|
||||
]);
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingSwitch
|
||||
title={$t('admin.backup_database_enable_description')}
|
||||
|
|
@ -53,21 +60,23 @@
|
|||
bind:value={config.backup.database.cronExpression}
|
||||
isEdited={config.backup.database.cronExpression !== savedConfig.backup.database.cronExpression}
|
||||
>
|
||||
<svelte:fragment slot="desc">
|
||||
{#snippet descriptionSnippet()}
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
<FormatMessage key="admin.cron_expression_description" let:message>
|
||||
<a
|
||||
href="https://crontab.guru/#{config.backup.database.cronExpression.replaceAll(' ', '_')}"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
<br />
|
||||
</a>
|
||||
<FormatMessage key="admin.cron_expression_description">
|
||||
{#snippet children({ message })}
|
||||
<a
|
||||
href="https://crontab.guru/#{config.backup.database.cronExpression.replaceAll(' ', '_')}"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
<br />
|
||||
</a>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</SettingInputField>
|
||||
|
||||
<SettingInputField
|
||||
|
|
|
|||
|
|
@ -15,44 +15,53 @@
|
|||
import { fade } from 'svelte/transition';
|
||||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import SettingCheckboxes from '$lib/components/shared-components/settings/setting-checkboxes.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
<Icon path={mdiHelpCircleOutline} class="inline" size="15" />
|
||||
<FormatMessage key="admin.transcoding_codecs_learn_more" let:tag let:message>
|
||||
{#if tag === 'h264-link'}
|
||||
<a href="https://trac.ffmpeg.org/wiki/Encode/H.264" class="underline" target="_blank" rel="noreferrer">
|
||||
{message}
|
||||
</a>
|
||||
{:else if tag === 'hevc-link'}
|
||||
<a href="https://trac.ffmpeg.org/wiki/Encode/H.265" class="underline" target="_blank" rel="noreferrer">
|
||||
{message}
|
||||
</a>
|
||||
{:else if tag === 'vp9-link'}
|
||||
<a href="https://trac.ffmpeg.org/wiki/Encode/VP9" class="underline" target="_blank" rel="noreferrer">
|
||||
{message}
|
||||
</a>
|
||||
{/if}
|
||||
<FormatMessage key="admin.transcoding_codecs_learn_more">
|
||||
{#snippet children({ tag, message })}
|
||||
{#if tag === 'h264-link'}
|
||||
<a href="https://trac.ffmpeg.org/wiki/Encode/H.264" class="underline" target="_blank" rel="noreferrer">
|
||||
{message}
|
||||
</a>
|
||||
{:else if tag === 'hevc-link'}
|
||||
<a href="https://trac.ffmpeg.org/wiki/Encode/H.265" class="underline" target="_blank" rel="noreferrer">
|
||||
{message}
|
||||
</a>
|
||||
{:else if tag === 'vp9-link'}
|
||||
<a href="https://trac.ffmpeg.org/wiki/Encode/VP9" class="underline" target="_blank" rel="noreferrer">
|
||||
{message}
|
||||
</a>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
|
||||
|
|
@ -60,7 +69,7 @@
|
|||
inputType={SettingInputFieldType.NUMBER}
|
||||
{disabled}
|
||||
label={$t('admin.transcoding_constant_rate_factor')}
|
||||
desc={$t('admin.transcoding_constant_rate_factor_description')}
|
||||
description={$t('admin.transcoding_constant_rate_factor_description')}
|
||||
bind:value={config.ffmpeg.crf}
|
||||
required={true}
|
||||
isEdited={config.ffmpeg.crf !== savedConfig.ffmpeg.crf}
|
||||
|
|
@ -186,7 +195,7 @@
|
|||
inputType={SettingInputFieldType.TEXT}
|
||||
{disabled}
|
||||
label={$t('admin.transcoding_max_bitrate')}
|
||||
desc={$t('admin.transcoding_max_bitrate_description')}
|
||||
description={$t('admin.transcoding_max_bitrate_description')}
|
||||
bind:value={config.ffmpeg.maxBitrate}
|
||||
isEdited={config.ffmpeg.maxBitrate !== savedConfig.ffmpeg.maxBitrate}
|
||||
/>
|
||||
|
|
@ -195,7 +204,7 @@
|
|||
inputType={SettingInputFieldType.NUMBER}
|
||||
{disabled}
|
||||
label={$t('admin.transcoding_threads')}
|
||||
desc={$t('admin.transcoding_threads_description')}
|
||||
description={$t('admin.transcoding_threads_description')}
|
||||
bind:value={config.ffmpeg.threads}
|
||||
isEdited={config.ffmpeg.threads !== savedConfig.ffmpeg.threads}
|
||||
/>
|
||||
|
|
@ -329,7 +338,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.transcoding_preferred_hardware_device')}
|
||||
desc={$t('admin.transcoding_preferred_hardware_device_description')}
|
||||
description={$t('admin.transcoding_preferred_hardware_device_description')}
|
||||
bind:value={config.ffmpeg.preferredHwDevice}
|
||||
isEdited={config.ffmpeg.preferredHwDevice !== savedConfig.ffmpeg.preferredHwDevice}
|
||||
{disabled}
|
||||
|
|
@ -346,7 +355,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.transcoding_max_b_frames')}
|
||||
desc={$t('admin.transcoding_max_b_frames_description')}
|
||||
description={$t('admin.transcoding_max_b_frames_description')}
|
||||
bind:value={config.ffmpeg.bframes}
|
||||
isEdited={config.ffmpeg.bframes !== savedConfig.ffmpeg.bframes}
|
||||
{disabled}
|
||||
|
|
@ -355,7 +364,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.transcoding_reference_frames')}
|
||||
desc={$t('admin.transcoding_reference_frames_description')}
|
||||
description={$t('admin.transcoding_reference_frames_description')}
|
||||
bind:value={config.ffmpeg.refs}
|
||||
isEdited={config.ffmpeg.refs !== savedConfig.ffmpeg.refs}
|
||||
{disabled}
|
||||
|
|
@ -364,7 +373,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.transcoding_max_keyframe_interval')}
|
||||
desc={$t('admin.transcoding_max_keyframe_interval_description')}
|
||||
description={$t('admin.transcoding_max_keyframe_interval_description')}
|
||||
bind:value={config.ffmpeg.gopSize}
|
||||
isEdited={config.ffmpeg.gopSize !== savedConfig.ffmpeg.gopSize}
|
||||
{disabled}
|
||||
|
|
|
|||
|
|
@ -7,24 +7,39 @@
|
|||
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
export let openByDefault = false;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
openByDefault?: boolean;
|
||||
}
|
||||
|
||||
let {
|
||||
savedConfig,
|
||||
defaultConfig,
|
||||
config = $bindable(),
|
||||
disabled = false,
|
||||
onReset,
|
||||
onSave,
|
||||
openByDefault = false,
|
||||
}: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingAccordion
|
||||
key="thumbnail-settings"
|
||||
|
|
@ -65,7 +80,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.image_quality')}
|
||||
desc={$t('admin.image_thumbnail_quality_description')}
|
||||
description={$t('admin.image_thumbnail_quality_description')}
|
||||
bind:value={config.image.thumbnail.quality}
|
||||
isEdited={config.image.thumbnail.quality !== savedConfig.image.thumbnail.quality}
|
||||
{disabled}
|
||||
|
|
@ -110,7 +125,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.image_quality')}
|
||||
desc={$t('admin.image_preview_quality_description')}
|
||||
description={$t('admin.image_preview_quality_description')}
|
||||
bind:value={config.image.preview.quality}
|
||||
isEdited={config.image.preview.quality !== savedConfig.image.preview.quality}
|
||||
{disabled}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,20 @@
|
|||
import { fade } from 'svelte/transition';
|
||||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const jobNames = [
|
||||
JobName.ThumbnailGeneration,
|
||||
|
|
@ -34,11 +37,15 @@
|
|||
function isSystemConfigJobDto(jobName: any): jobName is keyof SystemConfigJobDto {
|
||||
return jobName in config.job;
|
||||
}
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
{#each jobNames as jobName}
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
{#if isSystemConfigJobDto(jobName)}
|
||||
|
|
@ -46,7 +53,7 @@
|
|||
inputType={SettingInputFieldType.NUMBER}
|
||||
{disabled}
|
||||
label={$t('admin.job_concurrency', { values: { job: $getJobName(jobName) } })}
|
||||
desc=""
|
||||
description=""
|
||||
bind:value={config.job[jobName].concurrency}
|
||||
required={true}
|
||||
isEdited={!(config.job[jobName].concurrency == savedConfig.job[jobName].concurrency)}
|
||||
|
|
@ -55,7 +62,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.job_concurrency', { values: { job: $getJobName(jobName) } })}
|
||||
desc=""
|
||||
description=""
|
||||
value="1"
|
||||
disabled={true}
|
||||
title={$t('admin.job_not_concurrency_safe')}
|
||||
|
|
|
|||
|
|
@ -4,34 +4,49 @@
|
|||
import { fade } from 'svelte/transition';
|
||||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
export let openByDefault = false;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
openByDefault?: boolean;
|
||||
}
|
||||
|
||||
$: cronExpressionOptions = [
|
||||
let {
|
||||
savedConfig,
|
||||
defaultConfig,
|
||||
config = $bindable(),
|
||||
disabled = false,
|
||||
onReset,
|
||||
onSave,
|
||||
openByDefault = false,
|
||||
}: Props = $props();
|
||||
|
||||
let cronExpressionOptions = $derived([
|
||||
{ text: $t('interval.night_at_midnight'), value: '0 0 * * *' },
|
||||
{ text: $t('interval.night_at_twoam'), value: '0 2 * * *' },
|
||||
{ text: $t('interval.day_at_onepm'), value: '0 13 * * *' },
|
||||
{ text: $t('interval.hours', { values: { hours: 6 } }), value: '0 */6 * * *' },
|
||||
];
|
||||
]);
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingAccordion
|
||||
key="library-watching"
|
||||
|
|
@ -77,20 +92,22 @@
|
|||
bind:value={config.library.scan.cronExpression}
|
||||
isEdited={config.library.scan.cronExpression !== savedConfig.library.scan.cronExpression}
|
||||
>
|
||||
<svelte:fragment slot="desc">
|
||||
{#snippet descriptionSnippet()}
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
<FormatMessage key="admin.cron_expression_description" let:message>
|
||||
<a
|
||||
href="https://crontab.guru/#{config.library.scan.cronExpression.replaceAll(' ', '_')}"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
<FormatMessage key="admin.cron_expression_description">
|
||||
{#snippet children({ message })}
|
||||
<a
|
||||
href="https://crontab.guru/#{config.library.scan.cronExpression.replaceAll(' ', '_')}"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</SettingInputField>
|
||||
</div>
|
||||
</SettingAccordion>
|
||||
|
|
|
|||
|
|
@ -8,17 +8,25 @@
|
|||
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingSwitch
|
||||
title={$t('admin.logging_enable_description')}
|
||||
|
|
|
|||
|
|
@ -5,26 +5,33 @@
|
|||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { t } from 'svelte-i18n';
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="mt-2">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault class="mx-4 mt-4">
|
||||
<form autocomplete="off" {onsubmit} class="mx-4 mt-4">
|
||||
<div class="flex flex-col gap-4">
|
||||
<SettingSwitch
|
||||
title={$t('admin.machine_learning_enabled')}
|
||||
|
|
@ -38,7 +45,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('url')}
|
||||
desc={$t('admin.machine_learning_url_description')}
|
||||
description={$t('admin.machine_learning_url_description')}
|
||||
bind:value={config.machineLearning.url}
|
||||
required={true}
|
||||
disabled={disabled || !config.machineLearning.enabled}
|
||||
|
|
@ -69,11 +76,15 @@
|
|||
disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.clip.enabled}
|
||||
isEdited={config.machineLearning.clip.modelName !== savedConfig.machineLearning.clip.modelName}
|
||||
>
|
||||
<p slot="desc" class="immich-form-label pb-2 text-sm">
|
||||
<FormatMessage key="admin.machine_learning_clip_model_description" let:message>
|
||||
<a href="https://huggingface.co/immich-app"><u>{message}</u></a>
|
||||
</FormatMessage>
|
||||
</p>
|
||||
{#snippet descriptionSnippet()}
|
||||
<p class="immich-form-label pb-2 text-sm">
|
||||
<FormatMessage key="admin.machine_learning_clip_model_description">
|
||||
{#snippet children({ message })}
|
||||
<a href="https://huggingface.co/immich-app"><u>{message}</u></a>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
{/snippet}
|
||||
</SettingInputField>
|
||||
</div>
|
||||
</SettingAccordion>
|
||||
|
|
@ -100,7 +111,7 @@
|
|||
step="0.0005"
|
||||
min={0.001}
|
||||
max={0.1}
|
||||
desc={$t('admin.machine_learning_max_detection_distance_description')}
|
||||
description={$t('admin.machine_learning_max_detection_distance_description')}
|
||||
disabled={disabled || !$featureFlags.duplicateDetection}
|
||||
isEdited={config.machineLearning.duplicateDetection.maxDistance !==
|
||||
savedConfig.machineLearning.duplicateDetection.maxDistance}
|
||||
|
|
@ -142,7 +153,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.machine_learning_min_detection_score')}
|
||||
desc={$t('admin.machine_learning_min_detection_score_description')}
|
||||
description={$t('admin.machine_learning_min_detection_score_description')}
|
||||
bind:value={config.machineLearning.facialRecognition.minScore}
|
||||
step="0.1"
|
||||
min={0.1}
|
||||
|
|
@ -155,7 +166,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.machine_learning_max_recognition_distance')}
|
||||
desc={$t('admin.machine_learning_max_recognition_distance_description')}
|
||||
description={$t('admin.machine_learning_max_recognition_distance_description')}
|
||||
bind:value={config.machineLearning.facialRecognition.maxDistance}
|
||||
step="0.1"
|
||||
min={0.1}
|
||||
|
|
@ -168,7 +179,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.machine_learning_min_recognized_faces')}
|
||||
desc={$t('admin.machine_learning_min_recognized_faces_description')}
|
||||
description={$t('admin.machine_learning_min_recognized_faces_description')}
|
||||
bind:value={config.machineLearning.facialRecognition.minFaces}
|
||||
step="1"
|
||||
min={1}
|
||||
|
|
|
|||
|
|
@ -6,23 +6,30 @@
|
|||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="mt-2">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="flex flex-col gap-4">
|
||||
<SettingAccordion key="map" title={$t('admin.map_settings')} subtitle={$t('admin.map_settings_description')}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
|
|
@ -38,7 +45,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.map_light_style')}
|
||||
desc={$t('admin.map_style_description')}
|
||||
description={$t('admin.map_style_description')}
|
||||
bind:value={config.map.lightStyle}
|
||||
disabled={disabled || !config.map.enabled}
|
||||
isEdited={config.map.lightStyle !== savedConfig.map.lightStyle}
|
||||
|
|
@ -46,7 +53,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.map_dark_style')}
|
||||
desc={$t('admin.map_style_description')}
|
||||
description={$t('admin.map_style_description')}
|
||||
bind:value={config.map.darkStyle}
|
||||
disabled={disabled || !config.map.enabled}
|
||||
isEdited={config.map.darkStyle !== savedConfig.map.darkStyle}
|
||||
|
|
@ -55,20 +62,22 @@
|
|||
>
|
||||
|
||||
<SettingAccordion key="reverse-geocoding" title={$t('admin.map_reverse_geocoding_settings')}>
|
||||
<svelte:fragment slot="subtitle">
|
||||
{#snippet subtitleSnippet()}
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
<FormatMessage key="admin.map_manage_reverse_geocoding_settings" let:message>
|
||||
<a
|
||||
href="https://immich.app/docs/features/reverse-geocoding"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
<FormatMessage key="admin.map_manage_reverse_geocoding_settings">
|
||||
{#snippet children({ message })}
|
||||
<a
|
||||
href="https://immich.app/docs/features/reverse-geocoding"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingSwitch
|
||||
title={$t('admin.map_reverse_geocoding_enable_description')}
|
||||
|
|
|
|||
|
|
@ -7,17 +7,25 @@
|
|||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="mt-2">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault class="mx-4 mt-4">
|
||||
<form autocomplete="off" {onsubmit} class="mx-4 mt-4">
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingSwitch
|
||||
title={$t('admin.metadata_faces_import_setting')}
|
||||
|
|
|
|||
|
|
@ -7,17 +7,25 @@
|
|||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="ml-4 mt-4">
|
||||
<SettingSwitch
|
||||
title={$t('admin.version_check_enabled_description')}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@
|
|||
import { isEqual } from 'lodash-es';
|
||||
import { fade } from 'svelte/transition';
|
||||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
|
|
@ -18,15 +16,20 @@
|
|||
import { user } from '$lib/stores/user.store';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let isSending = false;
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
let isSending = $state(false);
|
||||
|
||||
const handleSendTestEmail = async () => {
|
||||
if (isSending) {
|
||||
|
|
@ -65,11 +68,15 @@
|
|||
isSending = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault class="mt-4">
|
||||
<form autocomplete="off" {onsubmit} class="mt-4">
|
||||
<div class="flex flex-col gap-4">
|
||||
<SettingAccordion key="email" title={$t('email')} subtitle={$t('admin.notification_email_setting_description')}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
|
|
@ -85,7 +92,7 @@
|
|||
inputType={SettingInputFieldType.TEXT}
|
||||
required
|
||||
label={$t('host')}
|
||||
desc={$t('admin.notification_email_host_description')}
|
||||
description={$t('admin.notification_email_host_description')}
|
||||
disabled={disabled || !config.notifications.smtp.enabled}
|
||||
bind:value={config.notifications.smtp.transport.host}
|
||||
isEdited={config.notifications.smtp.transport.host !== savedConfig.notifications.smtp.transport.host}
|
||||
|
|
@ -95,7 +102,7 @@
|
|||
inputType={SettingInputFieldType.NUMBER}
|
||||
required
|
||||
label={$t('port')}
|
||||
desc={$t('admin.notification_email_port_description')}
|
||||
description={$t('admin.notification_email_port_description')}
|
||||
disabled={disabled || !config.notifications.smtp.enabled}
|
||||
bind:value={config.notifications.smtp.transport.port}
|
||||
isEdited={config.notifications.smtp.transport.port !== savedConfig.notifications.smtp.transport.port}
|
||||
|
|
@ -104,7 +111,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('username')}
|
||||
desc={$t('admin.notification_email_username_description')}
|
||||
description={$t('admin.notification_email_username_description')}
|
||||
disabled={disabled || !config.notifications.smtp.enabled}
|
||||
bind:value={config.notifications.smtp.transport.username}
|
||||
isEdited={config.notifications.smtp.transport.username !==
|
||||
|
|
@ -114,7 +121,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.PASSWORD}
|
||||
label={$t('password')}
|
||||
desc={$t('admin.notification_email_password_description')}
|
||||
description={$t('admin.notification_email_password_description')}
|
||||
disabled={disabled || !config.notifications.smtp.enabled}
|
||||
bind:value={config.notifications.smtp.transport.password}
|
||||
isEdited={config.notifications.smtp.transport.password !==
|
||||
|
|
@ -134,14 +141,14 @@
|
|||
inputType={SettingInputFieldType.TEXT}
|
||||
required
|
||||
label={$t('admin.notification_email_from_address')}
|
||||
desc={$t('admin.notification_email_from_address_description')}
|
||||
description={$t('admin.notification_email_from_address_description')}
|
||||
disabled={disabled || !config.notifications.smtp.enabled}
|
||||
bind:value={config.notifications.smtp.from}
|
||||
isEdited={config.notifications.smtp.from !== savedConfig.notifications.smtp.from}
|
||||
/>
|
||||
|
||||
<div class="flex gap-2 place-items-center">
|
||||
<Button size="sm" disabled={!config.notifications.smtp.enabled} on:click={handleSendTestEmail}>
|
||||
<Button size="sm" disabled={!config.notifications.smtp.enabled} onclick={handleSendTestEmail}>
|
||||
{#if disabled}
|
||||
{$t('admin.notification_email_test_email')}
|
||||
{:else}
|
||||
|
|
|
|||
|
|
@ -3,28 +3,35 @@
|
|||
import { isEqual } from 'lodash-es';
|
||||
import { fade } from 'svelte/transition';
|
||||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="mt-4 ml-4">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.server_external_domain_settings')}
|
||||
desc={$t('admin.server_external_domain_settings_description')}
|
||||
description={$t('admin.server_external_domain_settings_description')}
|
||||
bind:value={config.server.externalDomain}
|
||||
isEdited={config.server.externalDomain !== savedConfig.server.externalDomain}
|
||||
/>
|
||||
|
|
@ -32,7 +39,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.server_welcome_message')}
|
||||
desc={$t('admin.server_welcome_message_description')}
|
||||
description={$t('admin.server_welcome_message_description')}
|
||||
bind:value={config.server.loginPageMessage}
|
||||
isEdited={config.server.loginPageMessage !== savedConfig.server.loginPageMessage}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { createBubbler, preventDefault } from 'svelte/legacy';
|
||||
|
||||
const bubble = createBubbler();
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { AppRoute, SettingInputFieldType } from '$lib/constants';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import {
|
||||
getStorageTemplateOptions,
|
||||
|
|
@ -15,24 +18,38 @@
|
|||
import SupportedDatetimePanel from './supported-datetime-panel.svelte';
|
||||
import SupportedVariablesPanel from './supported-variables-panel.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let minified = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
export let duration: number = 500;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
minified?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
duration?: number;
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
let templateOptions: SystemConfigTemplateStorageOptionDto;
|
||||
let selectedPreset = '';
|
||||
let {
|
||||
savedConfig,
|
||||
defaultConfig,
|
||||
config = $bindable(),
|
||||
disabled = false,
|
||||
minified = false,
|
||||
onReset,
|
||||
onSave,
|
||||
duration = 500,
|
||||
children,
|
||||
}: Props = $props();
|
||||
|
||||
let templateOptions: SystemConfigTemplateStorageOptionDto | undefined = $state();
|
||||
let selectedPreset = $state('');
|
||||
|
||||
const getTemplateOptions = async () => {
|
||||
templateOptions = await getStorageTemplateOptions();
|
||||
|
|
@ -41,15 +58,11 @@
|
|||
|
||||
const getSupportDateTimeFormat = () => getStorageTemplateOptions();
|
||||
|
||||
$: parsedTemplate = () => {
|
||||
try {
|
||||
return renderTemplate(config.storageTemplate.template);
|
||||
} catch {
|
||||
return 'error';
|
||||
}
|
||||
};
|
||||
|
||||
const renderTemplate = (templateString: string) => {
|
||||
if (!templateOptions) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const template = handlebar.compile(templateString, {
|
||||
knownHelpers: undefined,
|
||||
});
|
||||
|
|
@ -85,31 +98,40 @@
|
|||
const handlePresetSelection = () => {
|
||||
config.storageTemplate.template = selectedPreset;
|
||||
};
|
||||
let parsedTemplate = $derived(() => {
|
||||
try {
|
||||
return renderTemplate(config.storageTemplate.template);
|
||||
} catch {
|
||||
return 'error';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<section class="dark:text-immich-dark-fg mt-2">
|
||||
<div in:fade={{ duration }} class="mx-4 flex flex-col gap-4 py-4">
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
<FormatMessage key="admin.storage_template_more_details" let:tag let:message>
|
||||
{#if tag === 'template-link'}
|
||||
<a
|
||||
href="https://immich.app/docs/administration/storage-template"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
{:else if tag === 'implications-link'}
|
||||
<a
|
||||
href="https://immich.app/docs/administration/backup-and-restore#asset-types-and-storage-locations"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
{/if}
|
||||
<FormatMessage key="admin.storage_template_more_details">
|
||||
{#snippet children({ tag, message })}
|
||||
{#if tag === 'template-link'}
|
||||
<a
|
||||
href="https://immich.app/docs/administration/storage-template"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
{:else if tag === 'implications-link'}
|
||||
<a
|
||||
href="https://immich.app/docs/administration/backup-and-restore#asset-types-and-storage-locations"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -164,19 +186,18 @@
|
|||
<FormatMessage
|
||||
key="admin.storage_template_path_length"
|
||||
values={{ length: parsedTemplate().length + $user.id.length + 'UPLOAD_LOCATION'.length, limit: 260 }}
|
||||
let:message
|
||||
>
|
||||
<span class="font-semibold text-immich-primary dark:text-immich-dark-primary">{message}</span>
|
||||
{#snippet children({ message })}
|
||||
<span class="font-semibold text-immich-primary dark:text-immich-dark-primary">{message}</span>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
|
||||
<p class="text-sm">
|
||||
<FormatMessage
|
||||
key="admin.storage_template_user_label"
|
||||
values={{ label: $user.storageLabel || $user.id }}
|
||||
let:message
|
||||
>
|
||||
<code class="text-immich-primary dark:text-immich-dark-primary">{message}</code>
|
||||
<FormatMessage key="admin.storage_template_user_label" values={{ label: $user.storageLabel || $user.id }}>
|
||||
{#snippet children({ message })}
|
||||
<code class="text-immich-primary dark:text-immich-dark-primary">{message}</code>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
|
||||
|
|
@ -186,24 +207,30 @@
|
|||
>/{parsedTemplate()}.jpg
|
||||
</p>
|
||||
|
||||
<form autocomplete="off" class="flex flex-col" on:submit|preventDefault>
|
||||
<form autocomplete="off" class="flex flex-col" onsubmit={preventDefault(bubble('submit'))}>
|
||||
<div class="flex flex-col my-2">
|
||||
<label class="font-medium text-immich-primary dark:text-immich-dark-primary text-sm" for="preset-select">
|
||||
{$t('preset')}
|
||||
</label>
|
||||
<select
|
||||
class="immich-form-input p-2 mt-2 text-sm rounded-lg bg-slate-200 hover:cursor-pointer dark:bg-gray-600"
|
||||
disabled={disabled || !config.storageTemplate.enabled}
|
||||
name="presets"
|
||||
id="preset-select"
|
||||
bind:value={selectedPreset}
|
||||
on:change={handlePresetSelection}
|
||||
>
|
||||
{#each templateOptions.presetOptions as preset}
|
||||
<option value={preset}>{renderTemplate(preset)}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{#if templateOptions}
|
||||
<label
|
||||
class="font-medium text-immich-primary dark:text-immich-dark-primary text-sm"
|
||||
for="preset-select"
|
||||
>
|
||||
{$t('preset')}
|
||||
</label>
|
||||
<select
|
||||
class="immich-form-input p-2 mt-2 text-sm rounded-lg bg-slate-200 hover:cursor-pointer dark:bg-gray-600"
|
||||
disabled={disabled || !config.storageTemplate.enabled}
|
||||
name="presets"
|
||||
id="preset-select"
|
||||
bind:value={selectedPreset}
|
||||
onchange={handlePresetSelection}
|
||||
>
|
||||
{#each templateOptions.presetOptions as preset}
|
||||
<option value={preset}>{renderTemplate(preset)}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 align-bottom">
|
||||
<SettingInputField
|
||||
label={$t('template')}
|
||||
|
|
@ -232,11 +259,12 @@
|
|||
<FormatMessage
|
||||
key="admin.storage_template_migration_info"
|
||||
values={{ job: $t('admin.storage_template_migration_job') }}
|
||||
let:message
|
||||
>
|
||||
<a href={AppRoute.ADMIN_JOBS} class="text-immich-primary dark:text-immich-dark-primary">
|
||||
{message}
|
||||
</a>
|
||||
{#snippet children({ message })}
|
||||
<a href={AppRoute.ADMIN_JOBS} class="text-immich-primary dark:text-immich-dark-primary">
|
||||
{message}
|
||||
</a>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</section>
|
||||
|
|
@ -247,7 +275,7 @@
|
|||
{/if}
|
||||
|
||||
{#if minified}
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
{:else}
|
||||
<SettingButtonsRow
|
||||
onReset={(options) => onReset({ ...options, configKeys: ['storageTemplate'] })}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@
|
|||
import { DateTime } from 'luxon';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let options: SystemConfigTemplateStorageOptionDto;
|
||||
interface Props {
|
||||
options: SystemConfigTemplateStorageOptionDto;
|
||||
}
|
||||
|
||||
let { options }: Props = $props();
|
||||
|
||||
const getLuxonExample = (format: string) => {
|
||||
return DateTime.fromISO('2022-09-04T20:03:05.250Z', { locale: $locale }).toFormat(format);
|
||||
|
|
|
|||
|
|
@ -7,22 +7,30 @@
|
|||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingTextarea
|
||||
{disabled}
|
||||
label={$t('admin.theme_custom_css_settings')}
|
||||
desc={$t('admin.theme_custom_css_settings_description')}
|
||||
description={$t('admin.theme_custom_css_settings_description')}
|
||||
bind:value={config.theme.customCss}
|
||||
required={true}
|
||||
isEdited={config.theme.customCss !== savedConfig.theme.customCss}
|
||||
|
|
|
|||
|
|
@ -4,23 +4,30 @@
|
|||
import { fade } from 'svelte/transition';
|
||||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingSwitch title={$t('admin.trash_enabled_description')} {disabled} bind:checked={config.trash.enabled} />
|
||||
|
||||
|
|
@ -29,7 +36,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.trash_number_of_days')}
|
||||
desc={$t('admin.trash_number_of_days_description')}
|
||||
description={$t('admin.trash_number_of_days_description')}
|
||||
bind:value={config.trash.days}
|
||||
required={true}
|
||||
disabled={disabled || !config.trash.enabled}
|
||||
|
|
|
|||
|
|
@ -5,28 +5,31 @@
|
|||
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
|
||||
|
||||
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
|
||||
import SettingInputField, {
|
||||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { SettingInputFieldType } from '$lib/constants';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
export let onReset: SettingsResetEvent;
|
||||
export let onSave: SettingsSaveEvent;
|
||||
interface Props {
|
||||
savedConfig: SystemConfigDto;
|
||||
defaultConfig: SystemConfigDto;
|
||||
config: SystemConfigDto;
|
||||
disabled?: boolean;
|
||||
onReset: SettingsResetEvent;
|
||||
onSave: SettingsSaveEvent;
|
||||
}
|
||||
|
||||
let { savedConfig, defaultConfig, config = $bindable(), disabled = false, onReset, onSave }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<form autocomplete="off" onsubmit={(e) => e.preventDefault()}>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
min={1}
|
||||
label={$t('admin.user_delete_delay_settings')}
|
||||
desc={$t('admin.user_delete_delay_settings_description')}
|
||||
description={$t('admin.user_delete_delay_settings_description')}
|
||||
bind:value={config.user.deleteDelay}
|
||||
isEdited={config.user.deleteDelay !== savedConfig.user.deleteDelay}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue