From 95559cbc2a316407faf629b5be7fbcb60ac50f9e Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 19 Dec 2024 14:37:05 +0100 Subject: [PATCH] Setup backup overview page (#23331) * Add overview page * Remove configure button * Reorganize files * Add backups summary * Add settings overview * Fixes * Update wording * Setup onboarding before creating a backup --- src/data/backup.ts | 2 +- .../{ => config}/ha-backup-config-agents.ts | 20 +- .../{ => config}/ha-backup-config-data.ts | 30 +- .../ha-backup-config-encryption-key.ts | 14 +- .../{ => config}/ha-backup-config-schedule.ts | 34 +-- .../overview/ha-backup-overview-backups.ts | 122 ++++++++ .../overview/ha-backup-overview-settings.ts | 188 ++++++++++++ .../dialogs/dialog-backup-onboarding.ts | 14 +- .../backup/dialogs/dialog-generate-backup.ts | 14 +- .../backup/dialogs/dialog-new-backup.ts | 14 +- ...shboard.ts => ha-config-backup-backups.ts} | 121 +------- .../config/backup/ha-config-backup-details.ts | 2 +- .../backup/ha-config-backup-overview.ts | 270 ++++++++++++++++++ .../backup/ha-config-backup-settings.ts | 12 +- src/panels/config/backup/ha-config-backup.ts | 13 +- 15 files changed, 681 insertions(+), 189 deletions(-) rename src/panels/config/backup/components/{ => config}/ha-backup-config-agents.ts (88%) rename src/panels/config/backup/components/{ => config}/ha-backup-config-data.ts (92%) rename src/panels/config/backup/components/{ => config}/ha-backup-config-encryption-key.ts (83%) rename src/panels/config/backup/components/{ => config}/ha-backup-config-schedule.ts (92%) create mode 100644 src/panels/config/backup/components/overview/ha-backup-overview-backups.ts create mode 100644 src/panels/config/backup/components/overview/ha-backup-overview-settings.ts rename src/panels/config/backup/{ha-config-backup-dashboard.ts => ha-config-backup-backups.ts} (81%) create mode 100644 src/panels/config/backup/ha-config-backup-overview.ts diff --git a/src/data/backup.ts b/src/data/backup.ts index 7fa6b1077e..2cda20eb49 100644 --- a/src/data/backup.ts +++ b/src/data/backup.ts @@ -214,7 +214,7 @@ export const getPreferredAgentForDownload = (agents: string[]) => { }; export const CORE_LOCAL_AGENT = "backup.local"; -export const HASSIO_LOCAL_AGENT = "backup.hassio"; +export const HASSIO_LOCAL_AGENT = "hassio.local"; export const CLOUD_AGENT = "cloud.cloud"; export const isLocalAgent = (agentId: string) => diff --git a/src/panels/config/backup/components/ha-backup-config-agents.ts b/src/panels/config/backup/components/config/ha-backup-config-agents.ts similarity index 88% rename from src/panels/config/backup/components/ha-backup-config-agents.ts rename to src/panels/config/backup/components/config/ha-backup-config-agents.ts index 4343689f32..e9895fc505 100644 --- a/src/panels/config/backup/components/ha-backup-config-agents.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-agents.ts @@ -2,22 +2,22 @@ import { mdiDatabase } from "@mdi/js"; import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { fireEvent } from "../../../../common/dom/fire_event"; -import { computeDomain } from "../../../../common/entity/compute_domain"; -import "../../../../components/ha-md-list"; -import "../../../../components/ha-md-list-item"; -import "../../../../components/ha-svg-icon"; -import "../../../../components/ha-switch"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { computeDomain } from "../../../../../common/entity/compute_domain"; +import "../../../../../components/ha-md-list"; +import "../../../../../components/ha-md-list-item"; +import "../../../../../components/ha-svg-icon"; +import "../../../../../components/ha-switch"; import { CLOUD_AGENT, compareAgents, computeBackupAgentName, fetchBackupAgentsInfo, isLocalAgent, -} from "../../../../data/backup"; -import type { CloudStatus } from "../../../../data/cloud"; -import type { HomeAssistant } from "../../../../types"; -import { brandsUrl } from "../../../../util/brands-url"; +} from "../../../../../data/backup"; +import type { CloudStatus } from "../../../../../data/cloud"; +import type { HomeAssistant } from "../../../../../types"; +import { brandsUrl } from "../../../../../util/brands-url"; const DEFAULT_AGENTS = []; diff --git a/src/panels/config/backup/components/ha-backup-config-data.ts b/src/panels/config/backup/components/config/ha-backup-config-data.ts similarity index 92% rename from src/panels/config/backup/components/ha-backup-config-data.ts rename to src/panels/config/backup/components/config/ha-backup-config-data.ts index 8af0ab046c..7f99759141 100644 --- a/src/panels/config/backup/components/ha-backup-config-data.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-data.ts @@ -9,21 +9,21 @@ import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; -import { isComponentLoaded } from "../../../../common/config/is_component_loaded"; -import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/ha-button"; -import "../../../../components/ha-expansion-panel"; -import "../../../../components/ha-md-list"; -import "../../../../components/ha-md-list-item"; -import "../../../../components/ha-md-select"; -import type { HaMdSelect } from "../../../../components/ha-md-select"; -import "../../../../components/ha-md-select-option"; -import "../../../../components/ha-switch"; -import type { HaSwitch } from "../../../../components/ha-switch"; -import { fetchHassioAddonsInfo } from "../../../../data/hassio/addon"; -import type { HomeAssistant } from "../../../../types"; -import "./ha-backup-addons-picker"; -import type { BackupAddonItem } from "./ha-backup-addons-picker"; +import { isComponentLoaded } from "../../../../../common/config/is_component_loaded"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import "../../../../../components/ha-button"; +import "../../../../../components/ha-expansion-panel"; +import "../../../../../components/ha-md-list"; +import "../../../../../components/ha-md-list-item"; +import "../../../../../components/ha-md-select"; +import type { HaMdSelect } from "../../../../../components/ha-md-select"; +import "../../../../../components/ha-md-select-option"; +import "../../../../../components/ha-switch"; +import type { HaSwitch } from "../../../../../components/ha-switch"; +import { fetchHassioAddonsInfo } from "../../../../../data/hassio/addon"; +import type { HomeAssistant } from "../../../../../types"; +import "../ha-backup-addons-picker"; +import type { BackupAddonItem } from "../ha-backup-addons-picker"; export type FormData = { homeassistant: boolean; diff --git a/src/panels/config/backup/components/ha-backup-config-encryption-key.ts b/src/panels/config/backup/components/config/ha-backup-config-encryption-key.ts similarity index 83% rename from src/panels/config/backup/components/ha-backup-config-encryption-key.ts rename to src/panels/config/backup/components/config/ha-backup-config-encryption-key.ts index de775961d8..b2b947fec5 100644 --- a/src/panels/config/backup/components/ha-backup-config-encryption-key.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-encryption-key.ts @@ -1,13 +1,13 @@ import { mdiDownload } from "@mdi/js"; import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/ha-md-list"; -import "../../../../components/ha-md-list-item"; -import type { HomeAssistant } from "../../../../types"; -import { showChangeBackupEncryptionKeyDialog } from "../dialogs/show-dialog-change-backup-encryption-key"; -import { fileDownload } from "../../../../util/file_download"; -import { showSetBackupEncryptionKeyDialog } from "../dialogs/show-dialog-set-backup-encryption-key"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import "../../../../../components/ha-md-list"; +import "../../../../../components/ha-md-list-item"; +import type { HomeAssistant } from "../../../../../types"; +import { showChangeBackupEncryptionKeyDialog } from "../../dialogs/show-dialog-change-backup-encryption-key"; +import { fileDownload } from "../../../../../util/file_download"; +import { showSetBackupEncryptionKeyDialog } from "../../dialogs/show-dialog-set-backup-encryption-key"; @customElement("ha-backup-config-encryption-key") class HaBackupConfigEncryptionKey extends LitElement { diff --git a/src/panels/config/backup/components/ha-backup-config-schedule.ts b/src/panels/config/backup/components/config/ha-backup-config-schedule.ts similarity index 92% rename from src/panels/config/backup/components/ha-backup-config-schedule.ts rename to src/panels/config/backup/components/config/ha-backup-config-schedule.ts index 13724c4551..64922e2c73 100644 --- a/src/panels/config/backup/components/ha-backup-config-schedule.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-schedule.ts @@ -2,19 +2,19 @@ import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; -import { fireEvent } from "../../../../common/dom/fire_event"; -import type { HaCheckbox } from "../../../../components/ha-checkbox"; -import "../../../../components/ha-md-list"; -import "../../../../components/ha-md-list-item"; -import "../../../../components/ha-md-select"; -import "../../../../components/ha-md-textfield"; -import type { HaMdSelect } from "../../../../components/ha-md-select"; -import "../../../../components/ha-md-select-option"; -import "../../../../components/ha-switch"; -import type { BackupConfig } from "../../../../data/backup"; -import { BackupScheduleState } from "../../../../data/backup"; -import type { HomeAssistant } from "../../../../types"; -import { clamp } from "../../../../common/number/clamp"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import type { HaCheckbox } from "../../../../../components/ha-checkbox"; +import "../../../../../components/ha-md-list"; +import "../../../../../components/ha-md-list-item"; +import "../../../../../components/ha-md-select"; +import "../../../../../components/ha-md-textfield"; +import type { HaMdSelect } from "../../../../../components/ha-md-select"; +import "../../../../../components/ha-md-select-option"; +import "../../../../../components/ha-switch"; +import type { BackupConfig } from "../../../../../data/backup"; +import { BackupScheduleState } from "../../../../../data/backup"; +import type { HomeAssistant } from "../../../../../types"; +import { clamp } from "../../../../../common/number/clamp"; export type BackupConfigSchedule = Pick; @@ -24,7 +24,7 @@ const MAX_VALUE = 50; enum RetentionPreset { COPIES_3 = "copies_3", DAYS_7 = "days_7", - FOREOVER = "forever", + FOREVER = "forever", CUSTOM = "custom", } @@ -191,7 +191,7 @@ class HaBackupConfigSchedule extends LitElement {
Keep 7 days
- +
Keep forever
@@ -270,7 +270,9 @@ class HaBackupConfigSchedule extends LitElement { const data = this._getData(this.value); const retention = RETENTION_PRESETS[value]; // Ensure we have at least 1 in defaut value because user can't select 0 - retention.value = Math.max(retention.value, 1); + if (value !== RetentionPreset.FOREVER) { + retention.value = Math.max(retention.value, 1); + } this._setData({ ...data, retention: RETENTION_PRESETS[value], 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 new file mode 100644 index 0000000000..c79a0aeeae --- /dev/null +++ b/src/panels/config/backup/components/overview/ha-backup-overview-backups.ts @@ -0,0 +1,122 @@ +import type { CSSResultGroup } from "lit"; +import { css, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { navigate } from "../../../../../common/navigate"; +import "../../../../../components/ha-button"; +import "../../../../../components/ha-card"; +import "../../../../../components/ha-icon-next"; +import "../../../../../components/ha-md-list"; +import "../../../../../components/ha-md-list-item"; +import type { BackupContent } from "../../../../../data/backup"; +import { haStyle } from "../../../../../resources/styles"; +import type { HomeAssistant } from "../../../../../types"; +import { bytesToString } from "../../../../../util/bytes-to-string"; + +type BackupStats = { + count: number; + size: number; +}; + +const computeBackupStats = (backups: BackupContent[]): BackupStats => + backups.reduce( + (stats, backup) => { + stats.count++; + stats.size += backup.size; + return stats; + }, + { count: 0, size: 0 } + ); + +@customElement("ha-backup-overview-backups") +class HaBackupOverviewBackups extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public backups: BackupContent[] = []; + + private _showAll() { + navigate("/config/backup/backups"); + } + + private _automaticStats = memoizeOne((backups: BackupContent[]) => { + const automaticBackups = backups.filter( + (backup) => backup.with_automatic_settings + ); + return computeBackupStats(automaticBackups); + }); + + private _manualStats = memoizeOne((backups: BackupContent[]) => { + const manualBackups = backups.filter( + (backup) => !backup.with_automatic_settings + ); + return computeBackupStats(manualBackups); + }); + + render() { + const automaticStats = this._automaticStats(this.backups); + const manualStats = this._manualStats(this.backups); + + return html` + +
My backups
+
+ + +
+ ${automaticStats.count} automatic backups +
+
+ ${bytesToString(automaticStats.size, 1)} in total +
+ +
+ +
${manualStats.count} manual backups
+
+ ${bytesToString(manualStats.size, 1)} in total +
+ +
+
+
+
+ + Show all backups + +
+
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + .content { + padding: 28px 20px 0; + max-width: 690px; + margin: 0 auto; + gap: 24px; + display: flex; + flex-direction: column; + margin-bottom: 24px; + margin-bottom: 72px; + } + .card-actions { + display: flex; + justify-content: flex-end; + } + .card-content { + padding-left: 0; + padding-right: 0; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-backup-overview-backups": HaBackupOverviewBackups; + } +} 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 new file mode 100644 index 0000000000..2d7b0589c0 --- /dev/null +++ b/src/panels/config/backup/components/overview/ha-backup-overview-settings.ts @@ -0,0 +1,188 @@ +import { mdiCalendar, mdiCog, mdiPuzzle, mdiUpload } from "@mdi/js"; +import type { CSSResultGroup } from "lit"; +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; +import { navigate } from "../../../../../common/navigate"; +import "../../../../../components/ha-button"; +import "../../../../../components/ha-card"; +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 { haStyle } from "../../../../../resources/styles"; +import type { HomeAssistant } from "../../../../../types"; + +@customElement("ha-backup-overview-settings") +class HaBackupBackupsSummary extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public config!: BackupConfig; + + private _configure() { + navigate("/config/backup/settings"); + } + + private _scheduleDescription(config: BackupConfig): string { + const { copies, days } = config.retention; + const { state: schedule } = config.schedule; + + if (schedule === BackupScheduleState.NEVER) { + return "Automatic backups are disabled"; + } + + let copiesText = "and keep all backups"; + if (copies) { + copiesText = `and keep the latest ${copies} copie(s)`; + } else if (days) { + copiesText = `and keep backups for ${days} day(s)`; + } + + let scheduleText = ""; + if (schedule === BackupScheduleState.DAILY) { + scheduleText = `Daily at 04:45`; + } + if (schedule === BackupScheduleState.MONDAY) { + scheduleText = `Weekly on Mondays at 04:45`; + } + if (schedule === BackupScheduleState.TUESDAY) { + scheduleText = `Weekly on Thuesdays at 04:45`; + } + if (schedule === BackupScheduleState.WEDNESDAY) { + scheduleText = `Weekly on Wednesdays at 04:45`; + } + if (schedule === BackupScheduleState.THURSDAY) { + scheduleText = `Weekly on Thursdays at 04:45`; + } + if (schedule === BackupScheduleState.FRIDAY) { + scheduleText = `Weekly on Fridays at 04:45`; + } + if (schedule === BackupScheduleState.SATURDAY) { + scheduleText = `Weekly on Saturdays at 04:45`; + } + if (schedule === BackupScheduleState.SUNDAY) { + scheduleText = `Weekly on Sundays at 04:45`; + } + + return scheduleText + " " + copiesText; + } + + private _addonsDescription(config: BackupConfig): string { + if (config.create_backup.include_all_addons) { + return "All add-ons, including new"; + } + if (config.create_backup.include_addons?.length) { + return `${config.create_backup.include_addons.length} add-ons`; + } + return "No add-ons"; + } + + private _agentsDescription(config: BackupConfig): string { + const hasLocal = config.create_backup.agent_ids.some((a) => + isLocalAgent(a) + ); + + const offsiteLocations = config.create_backup.agent_ids.filter( + (a) => !isLocalAgent(a) + ); + + if (offsiteLocations.length) { + return `Upload to ${offsiteLocations.length} off-site locations`; + } + if (hasLocal) { + return "Local backup only"; + } + return "No location configured"; + } + + render() { + const isHassio = this.hass.config.components.includes("hassio"); + + return html` + +
Automatic backups
+
+ + + +
+ ${this._scheduleDescription(this.config)} +
+
+ Schedule and number of backups to keep +
+
+ + +
+ ${this.config.create_backup.include_database + ? "Settings and history" + : "Settings only"} +
+
+ Home Assistant data that is included +
+
+ ${isHassio + ? html` + + +
+ ${this._addonsDescription(this.config)} +
+
Add-ons that are included
+
+ + +
+ ${this._agentsDescription(this.config)} +
+
+ Locations where backup is uploaded to +
+
+ ` + : nothing} +
+
+
+ + Configure automatic backups + +
+
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + .content { + padding: 28px 20px 0; + max-width: 690px; + margin: 0 auto; + gap: 24px; + display: flex; + flex-direction: column; + margin-bottom: 24px; + margin-bottom: 72px; + } + .card-actions { + display: flex; + justify-content: flex-end; + } + .card-content { + padding-left: 0; + padding-right: 0; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-backup-overview-settings": HaBackupBackupsSummary; + } +} diff --git a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts index 35e5bcc996..512932468b 100644 --- a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts +++ b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts @@ -33,11 +33,11 @@ import { haStyle, haStyleDialog } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; import { fileDownload } from "../../../../util/file_download"; import { showToast } from "../../../../util/toast"; -import "../components/ha-backup-config-agents"; -import "../components/ha-backup-config-data"; -import type { BackupConfigData } from "../components/ha-backup-config-data"; -import "../components/ha-backup-config-schedule"; -import type { BackupConfigSchedule } from "../components/ha-backup-config-schedule"; +import "../components/config/ha-backup-config-agents"; +import "../components/config/ha-backup-config-data"; +import type { BackupConfigData } from "../components/config/ha-backup-config-data"; +import "../components/config/ha-backup-config-schedule"; +import type { BackupConfigSchedule } from "../components/config/ha-backup-config-schedule"; import type { BackupOnboardingDialogParams } from "./show-dialog-backup_onboarding"; const STEPS = [ @@ -367,8 +367,8 @@ class DialogBackupOnboarding extends LitElement implements HassDialog { case "locations": return html`

- Home Assistant will upload to these locations when this backup - settings are used. You can use all locations for custom backups. + Home Assistant will upload to these locations when an automatic + backup is made. You can use all locations for manual backups.

- Use automatic backups + Automatic backup Create a backup with the data and locations you have configured. - + - Make custom backup + Manual backup - Select specific data and locations for a custom backup. + Select data and locations for a manual backup. @@ -96,12 +96,12 @@ class DialogNewBackup extends LitElement implements HassDialog { `; } - private async _custom() { + private async _manual() { this._params!.submit?.("manual"); this.closeDialog(); } - private async _default() { + private async _automatic() { this._params!.submit?.("automatic"); this.closeDialog(); } diff --git a/src/panels/config/backup/ha-config-backup-dashboard.ts b/src/panels/config/backup/ha-config-backup-backups.ts similarity index 81% rename from src/panels/config/backup/ha-config-backup-dashboard.ts rename to src/panels/config/backup/ha-config-backup-backups.ts index 882046f0be..2d00aed0fa 100644 --- a/src/panels/config/backup/ha-config-backup-dashboard.ts +++ b/src/panels/config/backup/ha-config-backup-backups.ts @@ -11,7 +11,6 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { relativeTime } from "../../../common/datetime/relative_time"; import { storage } from "../../../common/decorators/storage"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; @@ -22,12 +21,12 @@ import { capitalizeFirstLetter } from "../../../common/string/capitalize-first-l import type { LocalizeFunc } from "../../../common/translations/localize"; import type { DataTableColumnContainer, + DataTableRowData, RowClickedEvent, SelectionChangedEvent, } from "../../../components/data-table/ha-data-table"; import "../../../components/ha-button"; import "../../../components/ha-button-menu"; -import "../../../components/ha-card"; import "../../../components/ha-fab"; import "../../../components/ha-icon"; import "../../../components/ha-icon-next"; @@ -68,24 +67,20 @@ import { brandsUrl } from "../../../util/brands-url"; import { bytesToString } from "../../../util/bytes-to-string"; import { fileDownload } from "../../../util/file_download"; import { showToast } from "../../../util/toast"; -import "./components/ha-backup-summary-card"; -import "./components/ha-backup-summary-progress"; -import "./components/ha-backup-summary-status"; -import { showBackupOnboardingDialog } from "./dialogs/show-dialog-backup_onboarding"; import { showGenerateBackupDialog } from "./dialogs/show-dialog-generate-backup"; import { showNewBackupDialog } from "./dialogs/show-dialog-new-backup"; import { showUploadBackupDialog } from "./dialogs/show-dialog-upload-backup"; -interface BackupRow extends BackupContent { +interface BackupRow extends DataTableRowData, BackupContent { formatted_type: string; } -type BackupType = "automatic" | "manual"; +type BackupType = "automatic" | "manual" | "imported"; -const TYPE_ORDER: Array = ["automatic", "manual"]; +const TYPE_ORDER: Array = ["automatic", "manual", "imported"]; -@customElement("ha-config-backup-dashboard") -class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { +@customElement("ha-config-backup-backups") +class HaConfigBackupBackups extends SubscribeMixin(LitElement) { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public cloudStatus!: CloudStatus; @@ -98,8 +93,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { @state() private _backups: BackupContent[] = []; - @state() private _fetching = false; - @state() private _selected: string[] = []; @state() private _config?: BackupConfig; @@ -112,7 +105,7 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { state: false, subscribe: false, }) - private _activeCollapsed?: string; + private _activeCollapsed: string[] = []; private _subscribed?: Promise<() => void>; @@ -271,7 +264,7 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { ]} .hass=${this.hass} .narrow=${this.narrow} - back-path="/config/system" + back-path="/config/backup/overview" clickable id="backup_id" selectable @@ -291,70 +284,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { "ui.panel.config.backup.picker.search" )} > -
- ${this._fetching - ? html` - - - Configure - - - ` - : backupInProgress - ? html` - - - Configure - - - ` - : this._needsOnboarding - ? html` - - - Set up automatic backups - - - ` - : html` - - - Configure - - - `} -
-
@@ -466,10 +393,7 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { protected firstUpdated(changedProps: PropertyValues) { super.firstUpdated(changedProps); - this._fetching = true; - this._fetchBackupInfo().then(() => { - this._fetching = false; - }); + this._fetchBackupInfo(); this._subscribeEvents(); this._fetchBackupConfig(); } @@ -529,12 +453,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { return; } - if (!isComponentLoaded(this.hass, "hassio")) { - delete params.include_folders; - delete params.include_all_addons; - delete params.include_addons; - } - await generateBackup(this.hass, params); await this._fetchBackupInfo(); return; @@ -602,23 +520,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { this._dataTable.clearSelection(); } - private _configureAutomaticBackups() { - navigate("/config/backup/settings"); - } - - private async _setupAutomaticBackups() { - const success = await showBackupOnboardingDialog(this, { - cloudStatus: this.cloudStatus, - }); - if (!success) { - return; - } - - this._fetchBackupConfig(); - await generateBackupWithAutomaticSettings(this.hass); - await this._fetchBackupInfo(); - } - static get styles(): CSSResultGroup { return [ haStyle, @@ -688,6 +589,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { declare global { interface HTMLElementTagNameMap { - "ha-config-backup-dashboard": HaConfigBackupDashboard; + "ha-config-backup-backups": HaConfigBackupBackups; } } diff --git a/src/panels/config/backup/ha-config-backup-details.ts b/src/panels/config/backup/ha-config-backup-details.ts index e9d09555f9..0f37a3bd59 100644 --- a/src/panels/config/backup/ha-config-backup-details.ts +++ b/src/panels/config/backup/ha-config-backup-details.ts @@ -87,7 +87,7 @@ class HaConfigBackupDetails extends LitElement { return html` { + if (this._needsOnboarding) { + this._setupAutomaticBackup(); + return; + } + + if (!this._config) { + return; + } + + const config = this._config; + + const type = await showNewBackupDialog(this, { config }); + + if (!type) { + return; + } + + if (type === "manual") { + const params = await showGenerateBackupDialog(this, {}); + + if (!params) { + return; + } + + await generateBackup(this.hass, params); + await this._fetchBackupInfo(); + return; + } + if (type === "automatic") { + await generateBackupWithAutomaticSettings(this.hass); + await this._fetchBackupInfo(); + } + } + + private get _needsOnboarding() { + return !this._config?.create_backup.password; + } + + protected render(): TemplateResult { + const backupInProgress = + "state" in this._manager && this._manager.state === "in_progress"; + + return html` + +
+ + + + + Upload backup + + +
+
+ ${this._fetching + ? html` + + + ` + : backupInProgress + ? html` + + + ` + : this._needsOnboarding + ? html` + + + Set up automatic backups + + + ` + : html` + + + `} + + + + ${!this._needsOnboarding + ? html` + + ` + : nothing} +
+ + + + +
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + .content { + padding: 28px 20px 0; + max-width: 690px; + margin: 0 auto; + gap: 24px; + display: flex; + flex-direction: column; + margin-bottom: 24px; + margin-bottom: 72px; + } + .card-actions { + display: flex; + justify-content: flex-end; + } + .card-content { + padding-left: 0; + padding-right: 0; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-backup-overview": HaConfigBackupOverview; + } +} diff --git a/src/panels/config/backup/ha-config-backup-settings.ts b/src/panels/config/backup/ha-config-backup-settings.ts index c42281e45d..ffb4c27190 100644 --- a/src/panels/config/backup/ha-config-backup-settings.ts +++ b/src/panels/config/backup/ha-config-backup-settings.ts @@ -15,12 +15,12 @@ import { import type { CloudStatus } from "../../../data/cloud"; import "../../../layouts/hass-subpage"; import type { HomeAssistant } from "../../../types"; -import "./components/ha-backup-config-agents"; -import "./components/ha-backup-config-data"; -import type { BackupConfigData } from "./components/ha-backup-config-data"; -import "./components/ha-backup-config-encryption-key"; -import "./components/ha-backup-config-schedule"; -import type { BackupConfigSchedule } from "./components/ha-backup-config-schedule"; +import "./components/config/ha-backup-config-agents"; +import "./components/config/ha-backup-config-data"; +import type { BackupConfigData } from "./components/config/ha-backup-config-data"; +import "./components/config/ha-backup-config-encryption-key"; +import "./components/config/ha-backup-config-schedule"; +import type { BackupConfigSchedule } from "./components/config/ha-backup-config-schedule"; const INITIAL_BACKUP_CONFIG: BackupConfig = { create_backup: { diff --git a/src/panels/config/backup/ha-config-backup.ts b/src/panels/config/backup/ha-config-backup.ts index f69720c5cd..9bd2736277 100644 --- a/src/panels/config/backup/ha-config-backup.ts +++ b/src/panels/config/backup/ha-config-backup.ts @@ -5,7 +5,8 @@ import type { RouterOptions } from "../../../layouts/hass-router-page"; import { HassRouterPage } from "../../../layouts/hass-router-page"; import "../../../layouts/hass-tabs-subpage-data-table"; import type { HomeAssistant } from "../../../types"; -import "./ha-config-backup-dashboard"; +import "./ha-config-backup-overview"; +import "./ha-config-backup-backups"; @customElement("ha-config-backup") class HaConfigBackup extends HassRouterPage { @@ -16,10 +17,14 @@ class HaConfigBackup extends HassRouterPage { @property({ type: Boolean }) public narrow = false; protected routerOptions: RouterOptions = { - defaultPage: "dashboard", + defaultPage: "overview", routes: { - dashboard: { - tag: "ha-config-backup-dashboard", + overview: { + tag: "ha-config-backup-overview", + cache: true, + }, + backups: { + tag: "ha-config-backup-backups", cache: true, }, details: {