mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
* feat: add admin config module for user configured config, uses it for ffmpeg * feat: add api endpoint to retrieve admin config settings and values * feat: add settings panel to admin page on web (wip) * feat: add api endpoint to update the admin config * chore: re-generate openapi spec after rebase * refactor: move from admin config to system config naming * chore: move away from UseGuards to new @Authenticated decorator * style: dark mode styling for lists and fix conflicting colors * wip: 2 column design, no edit button * refactor: system config * chore: generate open api * chore: rm broken test * chore: cleanup types * refactor: config module names Co-authored-by: Zack Pollard <zackpollard@ymail.com> Co-authored-by: Zack Pollard <zack.pollard@moonpig.com>
97 lines
2.9 KiB
Svelte
97 lines
2.9 KiB
Svelte
<script lang="ts">
|
|
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
|
import {
|
|
notificationController,
|
|
NotificationType
|
|
} from '$lib/components/shared-components/notification/notification';
|
|
import { api, SystemConfigResponseItem } from '@api';
|
|
import { onMount } from 'svelte';
|
|
|
|
let isSaving = false;
|
|
let items: Array<SystemConfigResponseItem & { originalValue: string }> = [];
|
|
|
|
const refreshConfig = async () => {
|
|
const { data: systemConfig } = await api.systemConfigApi.getConfig();
|
|
items = systemConfig.config.map((item) => ({ ...item, originalValue: item.value }));
|
|
};
|
|
|
|
onMount(() => refreshConfig());
|
|
|
|
const handleSave = async () => {
|
|
try {
|
|
isSaving = true;
|
|
const updates = items
|
|
.filter((item) => item.value !== item.originalValue)
|
|
.map(({ key, value }) => ({ key, value: value || null }));
|
|
if (updates.length > 0) {
|
|
await api.systemConfigApi.updateConfig({ config: updates });
|
|
refreshConfig();
|
|
}
|
|
|
|
notificationController.show({
|
|
message: `Saved settings`,
|
|
type: NotificationType.Info
|
|
});
|
|
} catch (e) {
|
|
console.error('Error [updateSystemConfig]', e);
|
|
notificationController.show({
|
|
message: `Unable to save changes.`,
|
|
type: NotificationType.Error
|
|
});
|
|
} finally {
|
|
isSaving = false;
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<section>
|
|
<table class="text-left my-4 w-full">
|
|
<thead
|
|
class="border rounded-md mb-4 bg-gray-50 flex text-immich-primary h-12 dark:bg-immich-dark-gray dark:text-immich-dark-primary dark:border-immich-dark-gray"
|
|
>
|
|
<tr class="flex w-full place-items-center">
|
|
<th class="text-center w-1/2 font-medium text-sm">Setting</th>
|
|
<th class="text-center w-1/2 font-medium text-sm">Value</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="rounded-md block border dark:border-immich-dark-gray">
|
|
{#each items as item, i}
|
|
<tr
|
|
class={`text-center flex place-items-center w-full h-[80px] dark:text-immich-dark-fg ${
|
|
i % 2 == 0 ? 'bg-slate-50 dark:bg-[#181818]' : 'bg-immich-bg dark:bg-immich-dark-bg'
|
|
}`}
|
|
>
|
|
<td class="text-sm px-4 w-1/2 text-ellipsis">
|
|
{item.name}
|
|
</td>
|
|
<td class="text-sm px-4 w-1/2 text-ellipsis">
|
|
<input
|
|
style="text-align: center"
|
|
class="immich-form-input"
|
|
id={item.key}
|
|
disabled={isSaving}
|
|
name={item.key}
|
|
type="text"
|
|
bind:value={item.value}
|
|
placeholder={item.defaultValue + ''}
|
|
/>
|
|
</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="flex justify-end">
|
|
<button
|
|
on:click={handleSave}
|
|
class="px-6 py-3 text-sm bg-immich-primary dark:bg-immich-dark-primary font-medium rounded-2xl hover:bg-immich-primary/50 transition-all hover:cursor-pointer disabled:cursor-not-allowed shadow-sm text-immich-bg dark:text-immich-dark-gray"
|
|
disabled={isSaving}
|
|
>
|
|
{#if isSaving}
|
|
<LoadingSpinner />
|
|
{:else}
|
|
Save
|
|
{/if}
|
|
</button>
|
|
</div>
|
|
</section>
|