chore: maintenance page design tweaks (#29087)

This commit is contained in:
Daniel Dietzler 2026-06-16 16:15:54 +02:00 committed by GitHub
parent 9ee412110f
commit 805ca1a2bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 54 additions and 33 deletions

View file

@ -189,18 +189,23 @@
"machine_learning_smart_search_enabled": "Enable smart search", "machine_learning_smart_search_enabled": "Enable smart search",
"machine_learning_smart_search_enabled_description": "If disabled, images will not be encoded for smart search.", "machine_learning_smart_search_enabled_description": "If disabled, images will not be encoded for smart search.",
"machine_learning_url_description": "The URL of the machine learning server. If more than one URL is provided, each server will be attempted one-at-a-time until one responds successfully, in order from first to last. Servers that don't respond will be temporarily ignored until they come back online.", "machine_learning_url_description": "The URL of the machine learning server. If more than one URL is provided, each server will be attempted one-at-a-time until one responds successfully, in order from first to last. Servers that don't respond will be temporarily ignored until they come back online.",
"maintenance_backup_management": "Backup management",
"maintenance_delete_backup": "Delete Backup", "maintenance_delete_backup": "Delete Backup",
"maintenance_delete_backup_description": "This file will be irrevocably deleted.", "maintenance_delete_backup_description": "This file will be irrevocably deleted.",
"maintenance_delete_error": "Failed to delete backup.", "maintenance_delete_error": "Failed to delete backup.",
"maintenance_integrity_check": "Check",
"maintenance_integrity_check_all": "Check All", "maintenance_integrity_check_all": "Check All",
"maintenance_integrity_checksum_mismatch": "Checksum Mismatch", "maintenance_integrity_checksum_mismatch": "Checksum Mismatch",
"maintenance_integrity_checksum_mismatch_description": "Files whose on-disk checksum mismatches the checksum Immich has stored in its database.",
"maintenance_integrity_checksum_mismatch_job": "Check for checksum mismatches", "maintenance_integrity_checksum_mismatch_job": "Check for checksum mismatches",
"maintenance_integrity_checksum_mismatch_refresh_job": "Refresh checksum mismatch reports", "maintenance_integrity_checksum_mismatch_refresh_job": "Refresh checksum mismatch reports",
"maintenance_integrity_missing_file": "Missing Files", "maintenance_integrity_missing_file": "Missing Files",
"maintenance_integrity_missing_file_description": "Files that Immich has tracked in its database but do not exist on the file system.",
"maintenance_integrity_missing_file_job": "Check for missing files", "maintenance_integrity_missing_file_job": "Check for missing files",
"maintenance_integrity_missing_file_refresh_job": "Refresh missing file reports", "maintenance_integrity_missing_file_refresh_job": "Refresh missing file reports",
"maintenance_integrity_report": "Integrity Report", "maintenance_integrity_report": "Integrity Report",
"maintenance_integrity_untracked_file": "Untracked Files", "maintenance_integrity_untracked_file": "Untracked Files",
"maintenance_integrity_untracked_file_description": "Files in Immich's directories that Immich does not have any record of.",
"maintenance_integrity_untracked_file_job": "Check for untracked files", "maintenance_integrity_untracked_file_job": "Check for untracked files",
"maintenance_integrity_untracked_file_refresh_job": "Refresh untracked file reports", "maintenance_integrity_untracked_file_refresh_job": "Refresh untracked file reports",
"maintenance_restore_backup": "Restore Backup", "maintenance_restore_backup": "Restore Backup",

View file

@ -12,10 +12,11 @@
icon?: string; icon?: string;
title: string; title: string;
valuePromise: MaybePromise<ValueData>; valuePromise: MaybePromise<ValueData>;
tooltip?: string;
footer?: Snippet; footer?: Snippet;
} }
let { icon, title, valuePromise, footer }: Props = $props(); let { icon, title, valuePromise, tooltip, footer }: Props = $props();
const zeros = (data?: ValueData) => { const zeros = (data?: ValueData) => {
let length = 13; let length = 13;
if (data) { if (data) {
@ -32,7 +33,7 @@
{#if icon} {#if icon}
<Icon {icon} size="40" /> <Icon {icon} size="40" />
{/if} {/if}
<Text size="giant" fontWeight="medium">{title}</Text> <Text size="giant" fontWeight="medium" title={tooltip}>{title}</Text>
</div> </div>
{#await valuePromise} {#await valuePromise}

View file

@ -21,6 +21,7 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { t, type Translations } from 'svelte-i18n'; import { t, type Translations } from 'svelte-i18n';
import type { PageData } from './$types'; import type { PageData } from './$types';
import { SvelteSet } from 'svelte/reactivity';
type Props = { type Props = {
data: PageData; data: PageData;
@ -50,7 +51,7 @@
}; };
let jobs: QueuesResponseLegacyDto | undefined = $state(); let jobs: QueuesResponseLegacyDto | undefined = $state();
let expectingUpdate: boolean = $state(false); const activeJobs = new SvelteSet<ManualJobName>();
const getReportTypeTranslation = (report: IntegrityReport): Translations => { const getReportTypeTranslation = (report: IntegrityReport): Translations => {
switch (report) { switch (report) {
@ -66,13 +67,27 @@
} }
}; };
const getReportTypeDescriptionKey = (report: IntegrityReport): Translations => {
switch (report) {
case IntegrityReport.UntrackedFile: {
return 'admin.maintenance_integrity_untracked_file_description';
}
case IntegrityReport.MissingFile: {
return 'admin.maintenance_integrity_missing_file_description';
}
case IntegrityReport.ChecksumMismatch: {
return 'admin.maintenance_integrity_checksum_mismatch_description';
}
}
};
const updateReports = async () => { const updateReports = async () => {
jobs = await getQueuesLegacy(); jobs = await getQueuesLegacy();
if (jobs.integrityCheck.queueStatus.isActive) { if (jobs.integrityCheck.queueStatus.isActive) {
expectingUpdate = true; activeJobs.add(ManualJobName.IntegrityUntrackedFilesRefresh);
} else if (expectingUpdate) { } else if (activeJobs.size > 0) {
activeJobs.clear();
integrityReport = await getIntegrityReportSummary(); integrityReport = await getIntegrityReportSummary();
expectingUpdate = false;
} }
}; };
@ -84,7 +99,7 @@
const onJobCreate = ({ dto }: { dto: JobCreateDto }) => { const onJobCreate = ({ dto }: { dto: JobCreateDto }) => {
if ((Object.values(jobNames).includes(dto.name) || Object.values(refreshJobNames).includes(dto.name)) && jobs) { if ((Object.values(jobNames).includes(dto.name) || Object.values(refreshJobNames).includes(dto.name)) && jobs) {
expectingUpdate = true; activeJobs.add(dto.name);
jobs.integrityCheck.queueStatus.isActive = true; jobs.integrityCheck.queueStatus.isActive = true;
} }
}; };
@ -106,7 +121,7 @@
} }
}} }}
class="mt-1 self-end" class="mt-1 self-end"
disabled={expectingUpdate}>{$t('admin.maintenance_integrity_check_all')}</Button disabled={activeJobs.size > 0}>{$t('admin.maintenance_integrity_check_all')}</Button
> >
<Button <Button
size="tiny" size="tiny"
@ -117,7 +132,7 @@
} }
}} }}
class="mt-1 self-end" class="mt-1 self-end"
disabled={expectingUpdate}>{$t('refresh')}</Button disabled={activeJobs.size > 0}>{$t('refresh')}</Button
></HStack ></HStack
> >
@ -125,10 +140,12 @@
{#each reportTypes as reportType (reportType)} {#each reportTypes as reportType (reportType)}
<ServerStatisticsCard <ServerStatisticsCard
title={$t(getReportTypeTranslation(reportType))} title={$t(getReportTypeTranslation(reportType))}
tooltip={$t(getReportTypeDescriptionKey(reportType))}
valuePromise={{ value: integrityReport[reportType] }} valuePromise={{ value: integrityReport[reportType] }}
> >
{#snippet footer()} {#snippet footer()}
<HStack gap={1} class="justify-end"> <HStack gap={1} class="justify-between">
<HStack gap={0}>
<Button <Button
onclick={() => onclick={() =>
handleCreateJob({ handleCreateJob({
@ -136,8 +153,7 @@
})} })}
size="tiny" size="tiny"
variant="ghost" variant="ghost"
class="mt-1 self-end" disabled={activeJobs.has(jobNames[reportType])}>{$t('admin.maintenance_integrity_check')}</Button
disabled={expectingUpdate}>{$t('admin.maintenance_integrity_check_all')}</Button
> >
<Button <Button
onclick={() => onclick={() =>
@ -146,15 +162,14 @@
})} })}
size="tiny" size="tiny"
variant="ghost" variant="ghost"
class="mt-1 self-end" disabled={activeJobs.has(refreshJobNames[reportType])}>{$t('refresh')}</Button
disabled={expectingUpdate}>{$t('refresh')}</Button
> >
</HStack>
<Button <Button
href={`${Route.systemMaintenanceIntegrityReport({ href={`${Route.systemMaintenanceIntegrityReport({
reportType, reportType,
})}`} })}`}
size="tiny" size="tiny">{$t('view')}</Button
class="mt-1 self-end">{$t('view')}</Button
> >
</HStack> </HStack>
{/snippet} {/snippet}
@ -166,7 +181,7 @@
<section id="setting-content" class="flex place-content-center sm:mx-4"> <section id="setting-content" class="flex place-content-center sm:mx-4">
<section class="w-full pb-28 sm:w-5/6 md:w-212.5"> <section class="w-full pb-28 sm:w-5/6 md:w-212.5">
<Text size="small">{$t('admin.maintenance_settings')}</Text> <Text size="small">{$t('admin.maintenance_backup_management')}</Text>
<SettingAccordion <SettingAccordion
title={$t('admin.maintenance_restore_database_backup')} title={$t('admin.maintenance_restore_database_backup')}