-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add overview summary for backups (#23343)
- Loading branch information
Showing
13 changed files
with
537 additions
and
410 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
src/panels/config/backup/components/overview/ha-backup-overview-onboarding.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { mdiInformationOutline } from "@mdi/js"; | ||
import type { CSSResultGroup } from "lit"; | ||
import { css, html, LitElement } from "lit"; | ||
import { customElement, property } from "lit/decorators"; | ||
import { fireEvent } from "../../../../../common/dom/fire_event"; | ||
import "../../../../../components/ha-button"; | ||
import "../../../../../components/ha-card"; | ||
import "../../../../../components/ha-svg-icon"; | ||
import { haStyle } from "../../../../../resources/styles"; | ||
import type { HomeAssistant } from "../../../../../types"; | ||
|
||
declare global { | ||
// for fire event | ||
interface HASSDomEvents { | ||
"button-click": undefined; | ||
} | ||
} | ||
|
||
@customElement("ha-backup-overview-onboarding") | ||
class HaBackupOverviewBackups extends LitElement { | ||
@property({ attribute: false }) public hass!: HomeAssistant; | ||
|
||
private async _setup() { | ||
fireEvent(this, "button-click"); | ||
} | ||
|
||
render() { | ||
return html` | ||
<ha-card> | ||
<div class="card-header"> | ||
<div class="icon"> | ||
<ha-svg-icon .path=${mdiInformationOutline}></ha-svg-icon> | ||
</div> | ||
Set up automatic backups | ||
</div> | ||
<div class="card-content"> | ||
<p> | ||
Backups are essential to a reliable smart home. They protect your | ||
setup against failures and allows you to quickly have a working | ||
system again. It is recommended to create a daily backup and keep | ||
copies of the last 3 days on two different locations. And one of | ||
them is off-site. | ||
</p> | ||
</div> | ||
<div class="card-actions"> | ||
<ha-button @click=${this._setup}> | ||
Set up automatic backups | ||
</ha-button> | ||
</div> | ||
</ha-card> | ||
`; | ||
} | ||
|
||
static get styles(): CSSResultGroup { | ||
return [ | ||
haStyle, | ||
css` | ||
.card-header { | ||
display: flex; | ||
flex-direction: row; | ||
align-items: center; | ||
gap: 16px; | ||
} | ||
.icon { | ||
position: relative; | ||
border-radius: 20px; | ||
width: 40px; | ||
height: 40px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
overflow: hidden; | ||
} | ||
.icon::before { | ||
display: block; | ||
content: ""; | ||
position: absolute; | ||
inset: 0; | ||
background-color: var(--primary-color); | ||
opacity: 0.2; | ||
} | ||
.icon ha-svg-icon { | ||
color: var(--primary-color); | ||
width: 24px; | ||
height: 24px; | ||
} | ||
p { | ||
margin: 0; | ||
} | ||
.card-actions { | ||
display: flex; | ||
justify-content: flex-end; | ||
border-top: none; | ||
} | ||
`, | ||
]; | ||
} | ||
} | ||
|
||
declare global { | ||
interface HTMLElementTagNameMap { | ||
"ha-backup-overview-onboarding": HaBackupOverviewBackups; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
195 changes: 195 additions & 0 deletions
195
src/panels/config/backup/components/overview/ha-backup-overview-summary.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
import { mdiBackupRestore, mdiCalendar } from "@mdi/js"; | ||
import { differenceInDays, setHours, setMinutes } from "date-fns"; | ||
import type { CSSResultGroup } from "lit"; | ||
import { css, html, LitElement } from "lit"; | ||
import { customElement, property } from "lit/decorators"; | ||
import memoizeOne from "memoize-one"; | ||
import { formatTime } from "../../../../../common/datetime/format_time"; | ||
import { relativeTime } from "../../../../../common/datetime/relative_time"; | ||
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, BackupContent } from "../../../../../data/backup"; | ||
import { BackupScheduleState } from "../../../../../data/backup"; | ||
import { haStyle } from "../../../../../resources/styles"; | ||
import type { HomeAssistant } from "../../../../../types"; | ||
import "../ha-backup-summary-card"; | ||
|
||
@customElement("ha-backup-overview-summary") | ||
class HaBackupOverviewBackups extends LitElement { | ||
@property({ attribute: false }) public hass!: HomeAssistant; | ||
|
||
@property({ attribute: false }) public backups: BackupContent[] = []; | ||
|
||
@property({ attribute: false }) public config!: BackupConfig; | ||
|
||
private _lastBackup = memoizeOne((backups: BackupContent[]) => { | ||
const sortedBackups = backups | ||
.filter((backup) => backup.with_automatic_settings) | ||
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); | ||
|
||
return sortedBackups[0] as BackupContent | undefined; | ||
}); | ||
|
||
private _nextBackupDescription(schedule: BackupScheduleState) { | ||
const newDate = setMinutes(setHours(new Date(), 4), 45); | ||
const time = formatTime(newDate, this.hass.locale, this.hass.config); | ||
|
||
switch (schedule) { | ||
case BackupScheduleState.DAILY: | ||
return `Next automatic backup tomorrow at ${time}`; | ||
case BackupScheduleState.MONDAY: | ||
return `Next automatic backup next Monday at ${time}`; | ||
case BackupScheduleState.TUESDAY: | ||
return `Next automatic backup next Thuesday at ${time}`; | ||
case BackupScheduleState.WEDNESDAY: | ||
return `Next automatic backup next Wednesday at ${time}`; | ||
case BackupScheduleState.THURSDAY: | ||
return `Next automatic backup next Thursday at ${time}`; | ||
case BackupScheduleState.FRIDAY: | ||
return `Next automatic backup next Friday at ${time}`; | ||
case BackupScheduleState.SATURDAY: | ||
return `Next automatic backup next Saturday at ${time}`; | ||
case BackupScheduleState.SUNDAY: | ||
return `Next automatic backup next Sunday at ${time}`; | ||
default: | ||
return "No automatic backup scheduled"; | ||
} | ||
} | ||
|
||
protected render() { | ||
const lastBackup = this._lastBackup(this.backups); | ||
|
||
if (!lastBackup) { | ||
return html` | ||
<ha-backup-summary-card | ||
heading="No automatic backup available" | ||
description="You have no automatic backups yet." | ||
status="warning" | ||
> | ||
</ha-backup-summary-card> | ||
`; | ||
} | ||
|
||
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 nextBackupDescription = this._nextBackupDescription( | ||
this.config.schedule.state | ||
); | ||
|
||
const lastAttempt = this.config.last_attempted_automatic_backup | ||
? new Date(this.config.last_attempted_automatic_backup) | ||
: undefined; | ||
|
||
if (lastAttempt && lastAttempt > lastBackupDate) { | ||
const lastAttemptDescription = `The last automatic backup trigged ${relativeTime(lastAttempt, this.hass.locale, now, true)} wasn't successful.`; | ||
return html` | ||
<ha-backup-summary-card | ||
heading=${`Last automatic backup failed`} | ||
status="error" | ||
> | ||
<ul class="list"> | ||
<li class="item"> | ||
<ha-svg-icon slot="start" .path=${mdiBackupRestore}></ha-svg-icon> | ||
<span>${lastAttemptDescription}</span> | ||
</li> | ||
<li class="item"> | ||
<ha-svg-icon slot="start" .path=${mdiCalendar}></ha-svg-icon> | ||
<span>${lastBackupDescription}</span> | ||
</li> | ||
</ul> | ||
</ha-backup-summary-card> | ||
`; | ||
} | ||
|
||
if (numberOfDays > 0) { | ||
return html` | ||
<ha-backup-summary-card | ||
heading=${`No backup for ${numberOfDays} days`} | ||
status="warning" | ||
> | ||
<ul class="list"> | ||
<li class="item"> | ||
<ha-svg-icon slot="start" .path=${mdiBackupRestore}></ha-svg-icon> | ||
<span>${lastBackupDescription}</span> | ||
</li> | ||
<li class="item"> | ||
<ha-svg-icon slot="start" .path=${mdiCalendar}></ha-svg-icon> | ||
<span>${nextBackupDescription}</span> | ||
</li> | ||
</ul> | ||
</ha-backup-summary-card> | ||
`; | ||
} | ||
return html` | ||
<ha-backup-summary-card heading=${`Backed up`} status="success"> | ||
<ul class="list"> | ||
<li class="item"> | ||
<ha-svg-icon slot="start" .path=${mdiBackupRestore}></ha-svg-icon> | ||
<span>${lastBackupDescription}</span> | ||
</li> | ||
<li class="item"> | ||
<ha-svg-icon slot="start" .path=${mdiCalendar}></ha-svg-icon> | ||
<span>${nextBackupDescription}</span> | ||
</li> | ||
</ul> | ||
</ha-backup-summary-card> | ||
`; | ||
} | ||
|
||
static get styles(): CSSResultGroup { | ||
return [ | ||
haStyle, | ||
css` | ||
.card-header { | ||
display: flex; | ||
flex-direction: row; | ||
align-items: center; | ||
gap: 16px; | ||
} | ||
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; | ||
} | ||
.card-actions { | ||
display: flex; | ||
justify-content: flex-end; | ||
border-top: none; | ||
} | ||
`, | ||
]; | ||
} | ||
} | ||
|
||
declare global { | ||
interface HTMLElementTagNameMap { | ||
"ha-backup-overview-summary": HaBackupOverviewBackups; | ||
} | ||
} |
Oops, something went wrong.