Add selection to snapshot table for mass deletion (#9284)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Joakim Sørensen 2021-05-28 15:49:40 +02:00 committed by GitHub
parent 959134df02
commit 2d5ae78521
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 154 additions and 5 deletions

View File

@ -1,15 +1,17 @@
import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical, mdiPlus } from "@mdi/js";
import { mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import relativeTime from "../../../src/common/datetime/relative_time";
@ -17,18 +19,25 @@ import { HASSDomEvent } from "../../../src/common/dom/fire_event";
import {
DataTableColumnContainer,
RowClickedEvent,
SelectionChangedEvent,
} from "../../../src/components/data-table/ha-data-table";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-fab";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import {
fetchHassioSnapshots,
friendlyFolderName,
HassioSnapshot,
reloadHassioSnapshots,
removeSnapshot,
} from "../../../src/data/hassio/snapshot";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-tabs-subpage-data-table";
import type { HaTabsSubpageDataTable } from "../../../src/layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
import { showHassioCreateSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-create-snapshot";
@ -49,10 +58,15 @@ export class HassioSnapshots extends LitElement {
@property({ type: Boolean }) public isWide!: boolean;
private _firstUpdatedCalled = false;
@state() private _selectedSnapshots: string[] = [];
@state() private _snapshots?: HassioSnapshot[] = [];
@query("hass-tabs-subpage-data-table", true)
private _dataTable!: HaTabsSubpageDataTable;
private _firstUpdatedCalled = false;
public connectedCallback(): void {
super.connectedCallback();
if (this.hass && this._firstUpdatedCalled) {
@ -153,7 +167,9 @@ export class HassioSnapshots extends LitElement {
.data=${this._snapshotData(this._snapshots || [])}
id="slug"
@row-click=${this._handleRowClicked}
@selection-changed=${this._handleSelectionChanged}
clickable
selectable
hasFab
main-page
supervisor
@ -176,6 +192,45 @@ export class HassioSnapshots extends LitElement {
: ""}
</ha-button-menu>
${this._selectedSnapshots.length
? html`<div
class=${classMap({
"header-toolbar": this.narrow,
"table-header": !this.narrow,
})}
slot="header"
>
<p class="selected-txt">
${this.supervisor.localize("snapshot.selected", {
number: this._selectedSnapshots.length,
})}
</p>
<div class="header-btns">
${!this.narrow
? html`
<mwc-button
@click=${this._deleteSelected}
class="warning"
>
${this.supervisor.localize("snapshot.delete_selected")}
</mwc-button>
`
: html`
<mwc-icon-button
id="delete-btn"
class="warning"
@click=${this._deleteSelected}
>
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
</mwc-icon-button>
<paper-tooltip animation-delay="0" for="delete-btn">
${this.supervisor.localize("snapshot.delete_selected")}
</paper-tooltip>
`}
</div>
</div> `
: ""}
<ha-fab
slot="fab"
@click=${this._createSnapshot}
@ -199,6 +254,12 @@ export class HassioSnapshots extends LitElement {
}
}
private _handleSelectionChanged(
ev: HASSDomEvent<SelectionChangedEvent>
): void {
this._selectedSnapshots = ev.detail.value;
}
private _showUploadSnapshotDialog() {
showSnapshotUploadDialog(this, {
showSnapshot: (slug: string) =>
@ -216,6 +277,35 @@ export class HassioSnapshots extends LitElement {
this._snapshots = await fetchHassioSnapshots(this.hass);
}
private async _deleteSelected() {
const confirm = await showConfirmationDialog(this, {
title: this.supervisor.localize("snapshot.delete_snapshot_title"),
text: this.supervisor.localize("snapshot.delete_snapshot_text", {
number: this._selectedSnapshots.length,
}),
confirmText: this.supervisor.localize("snapshot.delete_snapshot_confirm"),
});
if (!confirm) {
return;
}
try {
await Promise.all(
this._selectedSnapshots.map((slug) => removeSnapshot(this.hass, slug))
);
} catch (err) {
showAlertDialog(this, {
title: this.supervisor.localize("snapshot.failed_to_delete"),
text: extractApiErrorMessage(err),
});
return;
}
await reloadHassioSnapshots(this.hass);
this._snapshots = await fetchHassioSnapshots(this.hass);
this._dataTable.clearSelection();
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const slug = ev.detail.id;
showHassioSnapshotDialog(this, {
@ -244,7 +334,45 @@ export class HassioSnapshots extends LitElement {
}
static get styles(): CSSResultGroup {
return [haStyle, hassioStyle];
return [
haStyle,
hassioStyle,
css`
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 58px;
border-bottom: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
}
.header-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
color: var(--secondary-text-color);
position: relative;
top: -4px;
}
.selected-txt {
font-weight: bold;
padding-left: 16px;
color: var(--primary-text-color);
}
.table-header .selected-txt {
margin-top: 20px;
}
.header-toolbar .selected-txt {
font-size: 16px;
}
.header-toolbar .header-btns {
margin-right: -12px;
}
.header-btns > mwc-button,
.header-btns > mwc-icon-button {
margin: 8px;
}
`,
];
}
}

View File

@ -130,6 +130,21 @@ export const createHassioFullSnapshot = async (
);
};
export const removeSnapshot = async (hass: HomeAssistant, slug: string) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/snapshots/${slug}/remove`,
method: "post",
});
return;
}
await hass.callApi<HassioResponse<void>>(
"POST",
`hassio/snapshots/${slug}/remove`
);
};
export const createHassioPartialSnapshot = async (
hass: HomeAssistant,
data: HassioPartialSnapshotCreateParams

View File

@ -3896,6 +3896,12 @@
"available_snapshots": "Available Snapshots",
"no_snapshots": "You don't have any snapshots yet.",
"create_blocked_not_running": "Creating a snapshot is not possible right now because the system is in {state} state.",
"delete_selected": "Delete selected snapshots",
"delete_snapshot_title": "Delete snapshot",
"delete_snapshot_text": "Do you want to delete {number} {number, plural,\n one {snapshot}\n other {snapshots}\n}?",
"delete_snapshot_confirm": "delete",
"selected": "{number} selected",
"failed_to_delete": "Failed to delete",
"could_not_create": "Could not create snapshot",
"upload_snapshot": "Upload snapshot",
"create_snapshot": "Create snapshot",