diff --git a/src/panels/config/backup/ha-config-backup-dashboard.ts b/src/panels/config/backup/ha-config-backup-dashboard.ts index 5b6ae60a17..72700deb65 100644 --- a/src/panels/config/backup/ha-config-backup-dashboard.ts +++ b/src/panels/config/backup/ha-config-backup-dashboard.ts @@ -1,4 +1,4 @@ -import { mdiDelete, mdiPlus } from "@mdi/js"; +import { mdiDelete, mdiDownload, mdiPlus } from "@mdi/js"; import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; @@ -22,6 +22,7 @@ import "../../../components/ha-icon-overflow-menu"; import "../../../components/ha-svg-icon"; import { fetchBackupInfo, + getBackupDownloadUrl, removeBackup, type BackupContent, } from "../../../data/backup"; @@ -38,6 +39,8 @@ import type { HomeAssistant, Route } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; import "./components/ha-backup-summary-card"; import { showGenerateBackupDialog } from "./dialogs/show-dialog-generate-backup"; +import { getSignedPath } from "../../../data/auth"; +import { fileDownload } from "../../../util/file_download"; @customElement("ha-config-backup-dashboard") class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { @@ -115,6 +118,11 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { .hass=${this.hass} narrow .items=${[ + { + label: this.hass.localize("ui.common.download"), + path: mdiDownload, + action: () => this._downloadBackup(backup), + }, { label: this.hass.localize("ui.common.delete"), path: mdiDelete, @@ -266,6 +274,14 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) { navigate(`/config/backup/details/${id}`); } + private async _downloadBackup(backup: BackupContent): Promise { + const signedUrl = await getSignedPath( + this.hass, + getBackupDownloadUrl(backup.backup_id) + ); + fileDownload(signedUrl.path); + } + private async _deleteBackup(backup: BackupContent): Promise { const confirm = await showConfirmationDialog(this, { title: "Delete backup", diff --git a/src/panels/config/backup/ha-config-backup-details.ts b/src/panels/config/backup/ha-config-backup-details.ts index 45414d0db9..56aeef6af4 100644 --- a/src/panels/config/backup/ha-config-backup-details.ts +++ b/src/panels/config/backup/ha-config-backup-details.ts @@ -1,17 +1,30 @@ -import type { TemplateResult } from "lit"; -import { css, html, LitElement } from "lit"; +import { mdiDelete, mdiDotsVertical, mdiDownload } from "@mdi/js"; +import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import "../../../layouts/hass-subpage"; - +import type { ActionDetail } from "@material/mwc-list"; +import { formatDateTime } from "../../../common/datetime/format_date_time"; +import { navigate } from "../../../common/navigate"; import "../../../components/ha-alert"; +import "../../../components/ha-button-menu"; import "../../../components/ha-card"; import "../../../components/ha-circular-progress"; -import "../../../components/ha-relative-time"; -import "../../../components/ha-settings-row"; - +import "../../../components/ha-icon-button"; +import "../../../components/ha-list-item"; +import "../../../components/ha-md-list"; +import "../../../components/ha-md-list-item"; +import { getSignedPath } from "../../../data/auth"; import type { BackupContent } from "../../../data/backup"; -import { fetchBackupDetails } from "../../../data/backup"; +import { + fetchBackupDetails, + getBackupDownloadUrl, + removeBackup, +} from "../../../data/backup"; +import { domainToName } from "../../../data/integration"; +import "../../../layouts/hass-subpage"; import type { HomeAssistant } from "../../../types"; +import { brandsUrl } from "../../../util/brands-url"; +import { fileDownload } from "../../../util/file_download"; +import { showConfirmationDialog } from "../../lovelace/custom-card-helpers"; @customElement("ha-config-backup-details") class HaConfigBackupDetails extends LitElement { @@ -35,10 +48,11 @@ class HaConfigBackupDetails extends LitElement { } } - protected render(): TemplateResult { + protected render() { if (!this.hass) { - return html`:(`; + return nothing; } + return html` + + + + + ${this.hass.localize("ui.common.download")} + + + + ${this.hass.localize("ui.common.delete")} + +
${this._error && html`${this._error}`} @@ -58,27 +87,53 @@ class HaConfigBackupDetails extends LitElement { : html`
- - Partial - Type - + + + + ${Math.ceil(this._backup.size * 10) / 10 + " MB"} + + Size + + + ${formatDateTime( + new Date(this._backup.date), + this.hass.locale, + this.hass.config + )} + Created + + +
+
+ +
+ + ${this._backup.agent_ids?.map((agent) => { + const [domain, name] = agent.split("."); + const domainName = domainToName( + this.hass.localize, + domain + ); - - - ${Math.ceil(this._backup.size * 10) / 10 + " MB"} - - Size - - - - - Created - + return html` + + +
${domainName}: ${name}
+
+ `; + })} +
`} @@ -96,6 +151,41 @@ class HaConfigBackupDetails extends LitElement { } } + private _handleAction(ev: CustomEvent) { + switch (ev.detail.index) { + case 0: + this._downloadBackup(); + break; + case 1: + this._deleteBackup(); + break; + } + } + + private async _downloadBackup(): Promise { + const signedUrl = await getSignedPath( + this.hass, + getBackupDownloadUrl(this._backup!.backup_id) + ); + fileDownload(signedUrl.path); + } + + private async _deleteBackup(): Promise { + const confirm = await showConfirmationDialog(this, { + title: "Delete backup", + text: "This backup will be permanently deleted.", + confirmText: this.hass.localize("ui.common.delete"), + destructive: true, + }); + + if (!confirm) { + return; + } + + await removeBackup(this.hass, this._backup!.backup_id); + navigate("/config/backup"); + } + static styles = css` .content { padding: 28px 20px 0; @@ -104,6 +194,26 @@ class HaConfigBackupDetails extends LitElement { gap: 24px; display: grid; } + .card-content { + padding: 0 20px 8px 20px; + } + ha-md-list { + background: none; + padding: 0; + } + ha-md-list-item { + --md-list-item-leading-space: 0; + --md-list-item-trailing-space: 0; + } + ha-md-list-item img { + width: 48px; + } + .warning { + color: var(--error-color); + } + .warning ha-svg-icon { + color: var(--error-color); + } `; } diff --git a/src/panels/config/backup/ha-config-backup-locations.ts b/src/panels/config/backup/ha-config-backup-locations.ts index a6623b1324..80312f30c2 100644 --- a/src/panels/config/backup/ha-config-backup-locations.ts +++ b/src/panels/config/backup/ha-config-backup-locations.ts @@ -119,10 +119,10 @@ class HaConfigBackupLocations extends LitElement { margin: 0; } - .agents ha-md-list { + ha-md-list { background: none; } - .agents ha-md-list-item img { + ha-md-list-item img { width: 48px; } .card-content {