From c814b8e888b2270148d58db7bc8b3a9c20d79ae9 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Sat, 27 Sep 2025 17:07:01 +0200 Subject: [PATCH] Align dashboard data table with other data tables (#27206) * Align dashboard data table with other data tables * Update ha-config-lovelace-dashboards.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update ha-config-lovelace-dashboards.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../ha-config-lovelace-dashboards.ts | 240 ++++++++++-------- src/translations/en.json | 2 + 2 files changed, 141 insertions(+), 101 deletions(-) diff --git a/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts b/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts index 5199a338c6..e1e4de60d5 100644 --- a/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts +++ b/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts @@ -1,8 +1,9 @@ import { mdiCheck, mdiCheckCircleOutline, + mdiDelete, mdiDotsVertical, - mdiOpenInNew, + mdiPencil, mdiPlus, } from "@mdi/js"; import type { PropertyValues } from "lit"; @@ -20,10 +21,11 @@ import type { RowClickedEvent, SortingChangedEvent, } from "../../../../components/data-table/ha-data-table"; +import "../../../../components/ha-button"; import "../../../../components/ha-fab"; import "../../../../components/ha-icon"; -import "../../../../components/ha-button"; import "../../../../components/ha-icon-button"; +import "../../../../components/ha-icon-overflow-menu"; import "../../../../components/ha-md-button-menu"; import "../../../../components/ha-md-list-item"; import "../../../../components/ha-svg-icon"; @@ -224,87 +226,94 @@ export class HaConfigLovelaceDashboards extends LitElement { : html`—`, }; - columns.url_path = { + columns.actions = { title: "", - label: localize( - "ui.panel.config.lovelace.dashboards.picker.headers.url" - ), - filterable: true, + label: this.hass.localize("ui.panel.config.generic.headers.actions"), + type: "overflow-menu", showNarrow: true, - template: (dashboard) => - narrow - ? html` - - ` - : html` - ${this.hass.localize( - "ui.panel.config.lovelace.dashboards.picker.open" - )} - `, + moveable: false, + hideable: false, + template: (dashboard) => html` + this._handleEdit(dashboard), + }, + ...(this._canDelete(dashboard.url_path) + ? [ + { + label: this.hass.localize( + "ui.panel.config.lovelace.dashboards.picker.delete" + ), + path: mdiDelete, + action: () => this._handleDelete(dashboard), + warning: true, + }, + ] + : []), + ]} + > + + `, }; return columns; } ); - private _getItems = memoize((dashboards: LovelaceDashboard[]) => { - const defaultMode = ( - this.hass.panels?.lovelace?.config as LovelacePanelConfig - ).mode; - const defaultUrlPath = this.hass.defaultPanel; - const isDefault = defaultUrlPath === "lovelace"; - const result: DataTableItem[] = [ - { - icon: "hass:view-dashboard", - title: this.hass.localize("panel.states"), - default: isDefault, - show_in_sidebar: isDefault, - require_admin: false, - url_path: "lovelace", - mode: defaultMode, - filename: defaultMode === "yaml" ? "ui-lovelace.yaml" : "", - iconColor: "var(--primary-color)", - }, - ]; - if (isComponentLoaded(this.hass, "energy")) { - result.push({ - icon: "hass:lightning-bolt", - title: this.hass.localize(`ui.panel.config.dashboard.energy.main`), - show_in_sidebar: true, - mode: "storage", - url_path: "energy", - filename: "", - iconColor: "var(--label-badge-yellow)", - default: false, - require_admin: false, - }); - } - - result.push( - ...dashboards - .sort((a, b) => - stringCompare(a.title, b.title, this.hass.locale.language) - ) - .map((dashboard) => ({ + private _getItems = memoize( + (dashboards: LovelaceDashboard[], defaultUrlPath: string) => { + const defaultMode = ( + this.hass.panels?.lovelace?.config as LovelacePanelConfig + ).mode; + const isDefault = defaultUrlPath === "lovelace"; + const result: DataTableItem[] = [ + { + icon: "hass:view-dashboard", + title: this.hass.localize("panel.states"), + default: isDefault, + show_in_sidebar: isDefault, + require_admin: false, + url_path: "lovelace", + mode: defaultMode, + filename: defaultMode === "yaml" ? "ui-lovelace.yaml" : "", + iconColor: "var(--primary-color)", + }, + ]; + if (isComponentLoaded(this.hass, "energy")) { + result.push({ + icon: "hass:lightning-bolt", + title: this.hass.localize(`ui.panel.config.dashboard.energy.main`), + show_in_sidebar: true, + mode: "storage", + url_path: "energy", filename: "", - ...dashboard, - default: defaultUrlPath === dashboard.url_path, - })) - ); - return result; - }); + iconColor: "var(--label-badge-yellow)", + default: false, + require_admin: false, + }); + } + + result.push( + ...dashboards + .sort((a, b) => + stringCompare(a.title, b.title, this.hass.locale.language) + ) + .map((dashboard) => ({ + filename: "", + ...dashboard, + default: defaultUrlPath === dashboard.url_path, + })) + ); + return result; + } + ); protected render() { if (!this.hass || this._dashboards === undefined) { @@ -324,7 +333,7 @@ export class HaConfigLovelaceDashboards extends LitElement { this._dashboards, this.hass.localize )} - .data=${this._getItems(this._dashboards)} + .data=${this._getItems(this._dashboards, this.hass.defaultPanel)} .initialSorting=${this._activeSorting} .columnOrder=${this._activeColumnOrder} .hiddenColumns=${this._activeHiddenColumns} @@ -332,7 +341,7 @@ export class HaConfigLovelaceDashboards extends LitElement { @sorting-changed=${this._handleSortingChanged} .filter=${this._filter} @search-changed=${this._handleSearchChange} - @row-click=${this._editDashboard} + @row-click=${this._handleRowClicked} id="url_path" has-fab clickable @@ -370,13 +379,14 @@ export class HaConfigLovelaceDashboards extends LitElement { this._dashboards = await fetchDashboards(this.hass); } - private _navigate(ev: Event) { + private _handleRowClicked(ev: CustomEvent) { ev.stopPropagation(); - navigate(`/${(ev.target as any).urlPath}`); + const urlPath = (ev.detail as RowClickedEvent).id; + navigate(`/${urlPath}`); } - private _editDashboard(ev: CustomEvent) { - const urlPath = (ev.detail as RowClickedEvent).id; + private _handleEdit(item: DataTableItem) { + const urlPath = item.url_path; if (urlPath === "energy") { navigate("/config/energy"); @@ -386,6 +396,23 @@ export class HaConfigLovelaceDashboards extends LitElement { this._openDetailDialog(dashboard, urlPath); } + private _canDelete(urlPath: string) { + if (urlPath === "lovelace" || urlPath === "energy") { + return false; + } + return true; + } + + private _handleDelete = async (item: DataTableItem) => { + const dashboard = this._dashboards.find( + (res) => res.url_path === item.url_path + ); + if (!dashboard) { + return; + } + this._deleteDashboard(dashboard); + }; + private async _addDashboard() { showNewDashboardDialog(this, { selectConfig: async (config) => { @@ -445,33 +472,44 @@ export class HaConfigLovelaceDashboards extends LitElement { ); }, removeDashboard: async () => { - const confirm = await showConfirmationDialog(this, { - title: this.hass!.localize( - "ui.panel.config.lovelace.dashboards.confirm_delete_title", - { dashboard_title: dashboard!.title } - ), - text: this.hass!.localize( - "ui.panel.config.lovelace.dashboards.confirm_delete_text" - ), - confirmText: this.hass!.localize("ui.common.delete"), - destructive: true, - }); - if (!confirm) { - return false; - } - try { - await deleteDashboard(this.hass!, dashboard!.id); - this._dashboards = this._dashboards!.filter( - (res) => res !== dashboard - ); - return true; - } catch (_err: any) { + if (!dashboard) { return false; } + return this._deleteDashboard(dashboard); }, }); } + private async _deleteDashboard( + dashboard: LovelaceDashboard + ): Promise { + if (!this._canDelete(dashboard.url_path)) { + return false; + } + + const confirm = await showConfirmationDialog(this, { + title: this.hass!.localize( + "ui.panel.config.lovelace.dashboards.confirm_delete_title", + { dashboard_title: dashboard.title } + ), + text: this.hass!.localize( + "ui.panel.config.lovelace.dashboards.confirm_delete_text" + ), + confirmText: this.hass!.localize("ui.common.delete"), + destructive: true, + }); + if (!confirm) { + return false; + } + try { + await deleteDashboard(this.hass!, dashboard.id); + this._dashboards = this._dashboards.filter((res) => res !== dashboard); + return true; + } catch (_err: any) { + return false; + } + } + private _handleSortingChanged(ev: CustomEvent) { this._activeSorting = ev.detail; } diff --git a/src/translations/en.json b/src/translations/en.json index 525e39143b..fb4aba03c8 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3416,6 +3416,8 @@ "url": "Open" }, "open": "Open", + "edit": "Edit", + "delete": "Delete", "add_dashboard": "Add dashboard" }, "confirm_delete_title": "Delete {dashboard_title}?",