chore(web): migration svelte 5 syntax (#13883)

This commit is contained in:
Alex 2024-11-14 08:43:25 -06:00 committed by GitHub
parent 9203a61709
commit 0b3742cf13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
310 changed files with 6435 additions and 4176 deletions

View file

@ -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}

View file

@ -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}
/>

View file

@ -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

View file

@ -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}

View file

@ -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}

View file

@ -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')}

View file

@ -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>

View file

@ -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')}

View file

@ -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}

View file

@ -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')}

View file

@ -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')}

View file

@ -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')}

View file

@ -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}

View file

@ -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}
/>

View file

@ -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'] })}

View file

@ -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);

View file

@ -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}

View file

@ -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}

View file

@ -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}
/>