From 5fb384ad31cd1db3e4b5f6395a70969e8ee4fd1a Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 23 Dec 2024 15:06:36 +0100 Subject: [PATCH] Backup status banner improvement (#23400) * Improve overdue backups and don't consider backup with failed agents as success * Improve margin and loader * Improve margin * Show agent name if only one off site location is configured --- .../components/ha-backup-summary-card.ts | 5 +- .../components/ha-backup-summary-status.ts | 84 ---------- .../overview/ha-backup-overview-backups.ts | 4 + .../overview/ha-backup-overview-settings.ts | 18 ++- .../overview/ha-backup-overview-summary.ts | 147 ++++++++++++------ .../backup/ha-config-backup-overview.ts | 37 ++--- .../backup/ha-config-backup-settings.ts | 6 + 7 files changed, 141 insertions(+), 160 deletions(-) delete mode 100644 src/panels/config/backup/components/ha-backup-summary-status.ts diff --git a/src/panels/config/backup/components/ha-backup-summary-card.ts b/src/panels/config/backup/components/ha-backup-summary-card.ts index fb894266a7..c03a6733ea 100644 --- a/src/panels/config/backup/components/ha-backup-summary-card.ts +++ b/src/panels/config/backup/components/ha-backup-summary-card.ts @@ -75,6 +75,7 @@ class HaBackupSummaryCard extends LitElement { row-gap: 8px; align-items: center; padding: 16px; + padding-bottom: 8px; width: 100%; box-sizing: border-box; } @@ -145,10 +146,6 @@ class HaBackupSummaryCard extends LitElement { } @media all and (max-width: 550px) { - .summary { - flex-wrap: wrap; - padding: 8px; - } .action { width: 100%; display: flex; diff --git a/src/panels/config/backup/components/ha-backup-summary-status.ts b/src/panels/config/backup/components/ha-backup-summary-status.ts deleted file mode 100644 index 9a5efdd3ba..0000000000 --- a/src/panels/config/backup/components/ha-backup-summary-status.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { differenceInDays } from "date-fns"; -import { html, LitElement } from "lit"; -import { customElement, property } from "lit/decorators"; -import memoizeOne from "memoize-one"; -import { formatShortDateTime } from "../../../../common/datetime/format_date_time"; -import type { BackupContent } from "../../../../data/backup"; -import type { ManagerStateEvent } from "../../../../data/backup_manager"; -import type { HomeAssistant } from "../../../../types"; -import "./ha-backup-summary-card"; - -@customElement("ha-backup-summary-status") -export class HaBackupSummaryProgress extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property({ attribute: false }) public manager!: ManagerStateEvent; - - @property({ attribute: false }) public backups!: BackupContent[]; - - @property({ type: Boolean, attribute: "has-action" }) - public hasAction = false; - - private _lastBackup = memoizeOne((backups: BackupContent[]) => { - const sortedBackups = backups - // eslint-disable-next-line arrow-body-style - .filter((backup) => { - // TODO : only show backups with default flag - return backup.with_automatic_settings; - }) - .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); - - return sortedBackups[0] as BackupContent | undefined; - }); - - protected render() { - const lastBackup = this._lastBackup(this.backups); - - if (!lastBackup) { - return html` - - - - `; - } - - const lastBackupDate = new Date(lastBackup.date); - const numberOfDays = differenceInDays(new Date(), lastBackupDate); - - // TODO : Improve time format - const description = `Last successful backup ${formatShortDateTime(lastBackupDate, this.hass.locale, this.hass.config)} and synced to ${lastBackup.agent_ids?.length} locations`; - if (numberOfDays > 8) { - return html` - - - - `; - } - return html` - - - - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "ha-backup-summary-status": HaBackupSummaryProgress; - } -} diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-backups.ts b/src/panels/config/backup/components/overview/ha-backup-overview-backups.ts index 5c44183678..a667e1a4bc 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-backups.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-backups.ts @@ -109,9 +109,13 @@ class HaBackupOverviewBackups extends LitElement { display: flex; justify-content: flex-end; } + .card-header { + padding-bottom: 8px; + } .card-content { padding-left: 0; padding-right: 0; + padding-bottom: 0; } `, ]; diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-settings.ts b/src/panels/config/backup/components/overview/ha-backup-overview-settings.ts index dc3679f1ba..95ad7f1979 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-settings.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-settings.ts @@ -10,7 +10,11 @@ import "../../../../../components/ha-md-list"; import "../../../../../components/ha-md-list-item"; import "../../../../../components/ha-svg-icon"; import type { BackupConfig } from "../../../../../data/backup"; -import { BackupScheduleState, isLocalAgent } from "../../../../../data/backup"; +import { + BackupScheduleState, + computeBackupAgentName, + isLocalAgent, +} from "../../../../../data/backup"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; @@ -88,6 +92,14 @@ class HaBackupBackupsSummary extends LitElement { ); if (offsiteLocations.length) { + if (offsiteLocations.length === 1) { + const name = computeBackupAgentName( + this.hass.localize, + offsiteLocations[0], + offsiteLocations + ); + return `Upload to ${name}`; + } return `Upload to ${offsiteLocations.length} off-site locations`; } if (hasLocal) { @@ -184,9 +196,13 @@ class HaBackupBackupsSummary extends LitElement { display: flex; justify-content: flex-end; } + .card-header { + padding-bottom: 8px; + } .card-content { padding-left: 0; padding-right: 0; + padding-bottom: 0; } `, ]; diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts b/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts index 5882675bf7..cf09d19114 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts @@ -1,5 +1,5 @@ import { mdiBackupRestore, mdiCalendar } from "@mdi/js"; -import { differenceInDays, setHours, setMinutes } from "date-fns"; +import { addHours, differenceInDays, setHours, setMinutes } from "date-fns"; import type { CSSResultGroup } from "lit"; import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; @@ -17,6 +17,8 @@ import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; import "../ha-backup-summary-card"; +const OVERDUE_MARGIN_HOURS = 3; + @customElement("ha-backup-overview-summary") class HaBackupOverviewBackups extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -25,9 +27,14 @@ class HaBackupOverviewBackups extends LitElement { @property({ attribute: false }) public config!: BackupConfig; + @property({ type: Boolean }) public fetching = false; + private _lastBackup = memoizeOne((backups: BackupContent[]) => { const sortedBackups = backups - .filter((backup) => backup.with_automatic_settings) + .filter( + (backup) => + backup.with_automatic_settings && !backup.failed_agent_ids?.length + ) .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); return sortedBackups[0] as BackupContent | undefined; @@ -60,6 +67,23 @@ class HaBackupOverviewBackups extends LitElement { } protected render() { + if (this.fetching) { + return html` + + + + + + + + + + + + + `; + } + const lastBackup = this._lastBackup(this.backups); if (!lastBackup) { @@ -75,10 +99,9 @@ class HaBackupOverviewBackups extends LitElement { const lastBackupDate = new Date(lastBackup.date); - const numberOfDays = differenceInDays(new Date(), lastBackupDate); const now = new Date(); - const lastBackupDescription = `Last successful backup ${relativeTime(lastBackupDate, this.hass.locale, now, true)} and synced to ${lastBackup.agent_ids?.length} locations.`; + const lastBackupDescription = `Last successful backup ${relativeTime(lastBackupDate, this.hass.locale, now, true)} and stored to ${lastBackup.agent_ids?.length} locations.`; const nextBackupDescription = this._nextBackupDescription( this.config.schedule.state ); @@ -94,51 +117,62 @@ class HaBackupOverviewBackups extends LitElement { heading=${`Last automatic backup failed`} status="error" > - + ${lastBackupDescription} + + `; } - if (numberOfDays > 0) { + const numberOfDays = differenceInDays( + // Subtract a few hours to avoid showing as overdue if it's just a few hours (e.g. daylight saving) + addHours(now, -OVERDUE_MARGIN_HOURS), + lastBackupDate + ); + + const isOverdue = + (numberOfDays >= 1 && + this.config.schedule.state === BackupScheduleState.DAILY) || + numberOfDays >= 7; + + if (isOverdue) { return html` - + ${nextBackupDescription} + + `; } return html` - + ${nextBackupDescription} + + `; } @@ -156,25 +190,6 @@ class HaBackupOverviewBackups extends LitElement { p { margin: 0; } - .list { - display: flex; - flex-direction: column; - gap: 16px; - padding: 8px 24px 24px 24px; - margin: 0; - } - .item { - display: flex; - flex-direction: row; - gap: 16px; - align-items: center; - color: var(--secondary-text-color); - font-size: 14px; - font-style: normal; - font-weight: 400; - line-height: 20px; - letter-spacing: 0.25px; - } ha-svg-icon { flex: none; } @@ -183,6 +198,42 @@ class HaBackupOverviewBackups extends LitElement { justify-content: flex-end; border-top: none; } + ha-md-list { + background: none; + } + ha-md-list-item { + --md-list-item-top-space: 8px; + --md-list-item-bottom-space: 8px; + --md-list-item-one-line-container-height: 40x; + } + span.skeleton { + position: relative; + display: block; + width: 160px; + animation-fill-mode: forwards; + animation-iteration-count: infinite; + animation-name: loading; + animation-timing-function: linear; + animation-duration: 1.2s; + border-radius: 4px; + height: 20px; + background: linear-gradient( + to right, + rgb(247, 249, 250) 8%, + rgb(235, 238, 240) 18%, + rgb(247, 249, 250) 33% + ) + 0% 0% / 936px 104px; + } + + @keyframes loading { + 0% { + background-position: -468px 0; + } + 100% { + background-position: 468px 0; + } + } `, ]; } diff --git a/src/panels/config/backup/ha-config-backup-overview.ts b/src/panels/config/backup/ha-config-backup-overview.ts index b7621ab9f7..40614715a6 100644 --- a/src/panels/config/backup/ha-config-backup-overview.ts +++ b/src/panels/config/backup/ha-config-backup-overview.ts @@ -27,7 +27,6 @@ import "../../../layouts/hass-tabs-subpage-data-table"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, Route } from "../../../types"; import "./components/ha-backup-summary-card"; -import "./components/ha-backup-summary-status"; import "./components/overview/ha-backup-overview-backups"; import "./components/overview/ha-backup-overview-onboarding"; import "./components/overview/ha-backup-overview-progress"; @@ -182,31 +181,23 @@ class HaConfigBackupOverview extends LitElement { > ` - : this.fetching + : this._needsOnboarding ? html` - - + ` - : this._needsOnboarding - ? html` - - - ` - : html` - - - `} + : html` + + + `}