From 2ab867986a13846b06c67b8a2a84c4f158a0a853 Mon Sep 17 00:00:00 2001 From: ildar170975 <71872483+ildar170975@users.noreply.github.com> Date: Tue, 3 Feb 2026 23:37:53 +0300 Subject: [PATCH] Data tables: standardize columns (#29155) * Create data-table-columns.ts * Update data-table-columns.ts * move a code for columns into separate functions * move a code for columns into separate functions * move a code for columns into separate functions * move a code for columns into separate functions * move a code for columns into separate functions * move a code for columns into separate functions * move a code for columns into separate functions * move a code for columns into separate functions * fix a translation key * move commonly used translations to generic * remove a translation for another PR * restore "domain" translation for while * resolving conflicts * resolve conflicts * resolving conflicts * resolving conflicts * resolving conflicts * resolving conflicts * fix conflicts * fix conflicts * fix import * fix import --- .../config/automation/ha-automation-picker.ts | 83 ++----- .../config/common/data-table-columns.ts | 204 ++++++++++++++++++ .../devices/ha-config-devices-dashboard.ts | 68 ++---- .../config/entities/ha-config-entities.ts | 70 ++---- .../config/helpers/ha-config-helpers.ts | 73 ++----- src/panels/config/labels/ha-config-labels.ts | 35 +-- src/panels/config/scene/ha-scene-dashboard.ts | 116 +++------- src/panels/config/script/ha-script-picker.ts | 71 ++---- .../expose/assistants-table-column.ts | 4 +- .../ha-config-voice-assistants-expose.ts | 32 +-- src/translations/en.json | 28 +-- 11 files changed, 335 insertions(+), 449 deletions(-) create mode 100644 src/panels/config/common/data-table-columns.ts diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index 49f5c14ba7..3bda64b46a 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -19,7 +19,6 @@ import { mdiToggleSwitchOffOutline, mdiTransitConnection, } from "@mdi/js"; -import { differenceInDays } from "date-fns"; import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; @@ -28,14 +27,11 @@ import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; import { computeCssColor } from "../../../common/color/compute-color"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; -import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time"; -import { relativeTime } from "../../../common/datetime/relative_time"; import { storage } from "../../../common/decorators/storage"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { navigate } from "../../../common/navigate"; -import { slugify } from "../../../common/string/slugify"; import type { LocalizeFunc } from "../../../common/translations/localize"; import { hasRejectedItems, @@ -115,6 +111,13 @@ import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, Route, ServiceCallResponse } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import { turnOnOffEntity } from "../../lovelace/common/entity/turn-on-off-entity"; +import { + getEntityIdHiddenTableColumn, + getAreaTableColumn, + getCategoryTableColumn, + getLabelsTableColumn, + getTriggeredAtTableColumn, +} from "../common/data-table-columns"; import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail"; import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; @@ -134,7 +137,7 @@ type AutomationItem = AutomationEntity & { last_triggered: string | undefined; formatted_state: string; category: string | undefined; - labels: LabelRegistryEntry[]; + label_entries: LabelRegistryEntry[]; assistants: string[]; assistants_sortable_key: string | undefined; }; @@ -269,7 +272,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { category: category ? categoryReg?.find((cat) => cat.category_id === category)?.name : undefined, - labels: (labels || []).map( + label_entries: (labels || []).map( (lbl) => labelReg!.find((label) => label.label_id === lbl)! ), assistants, @@ -284,7 +287,6 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { ( narrow: boolean, localize: LocalizeFunc, - locale: HomeAssistant["locale"], entitiesToCheck?: any[] ): DataTableColumnContainer => { const columns: DataTableColumnContainer = { @@ -306,11 +308,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { })} >`, }, - entity_id: { - title: "", - hidden: true, - filterable: true, - }, + entity_id: getEntityIdHiddenTableColumn(), name: { title: localize("ui.panel.config.automation.picker.headers.name"), main: true, @@ -319,59 +317,17 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { direction: "asc", flex: 2, extraTemplate: (automation) => - automation.labels.length + automation.label_entries.length ? html`` : nothing, }, - area: { - title: localize("ui.panel.config.automation.picker.headers.area"), - groupable: true, - filterable: true, - sortable: true, - }, - category: { - title: localize("ui.panel.config.automation.picker.headers.category"), - defaultHidden: true, - groupable: true, - filterable: true, - sortable: true, - }, - labels: { - title: "", - hidden: true, - filterable: true, - template: (automation) => - automation.labels.map((lbl) => lbl.name).join(" "), - }, - last_triggered: { - sortable: true, - title: localize("ui.card.automation.last_triggered"), - template: (automation) => { - if (!automation.last_triggered) { - return this.hass.localize("ui.components.relative_time.never"); - } - const date = new Date(automation.last_triggered); - const now = new Date(); - const dayDifference = differenceInDays(now, date); - const formattedTime = formatShortDateTimeWithConditionalYear( - date, - this.hass.locale, - this.hass.config - ); - const elementId = "last-triggered-" + slugify(automation.entity_id); - return html` - ${dayDifference > 3 - ? formattedTime - : html` - ${formattedTime} - ${relativeTime(date, locale)} - `} - `; - }, - }, + area: getAreaTableColumn(localize), + category: getCategoryTableColumn(localize), + labels: getLabelsTableColumn(), + last_triggered: getTriggeredAtTableColumn(localize, this.hass), formatted_state: { minWidth: "82px", maxWidth: "82px", @@ -485,12 +441,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { Array.isArray(val) ? val.length : val ) ).length} - .columns=${this._columns( - this.narrow, - this.hass.localize, - this.hass.locale, - automations - )} + .columns=${this._columns(this.narrow, this.hass.localize, automations)} .initialGroupColumn=${this._activeGrouping ?? "category"} .initialCollapsedGroups=${this._activeCollapsed} .initialSorting=${this._activeSorting} diff --git a/src/panels/config/common/data-table-columns.ts b/src/panels/config/common/data-table-columns.ts new file mode 100644 index 0000000000..22b3927717 --- /dev/null +++ b/src/panels/config/common/data-table-columns.ts @@ -0,0 +1,204 @@ +import { html, nothing } from "lit"; +import { differenceInDays } from "date-fns"; +import { mdiPencilOff } from "@mdi/js"; +import type { HomeAssistant } from "../../../types"; +import type { LocalizeFunc } from "../../../common/translations/localize"; +import type { DataTableColumnData } from "../../../components/data-table/ha-data-table"; +import { slugify } from "../../../common/string/slugify"; +import { relativeTime } from "../../../common/datetime/relative_time"; +import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time"; +import { isUnavailableState } from "../../../data/entity/entity"; +import "../../../components/ha-tooltip"; +import "../../../components/ha-svg-icon"; + +export function getEntityIdHiddenTableColumn(): DataTableColumnData { + return { + title: "", + hidden: true, + filterable: true, + }; +} + +export function getEntityIdTableColumn( + localize: LocalizeFunc, + defaultHidden?: boolean +): DataTableColumnData { + return { + title: localize("ui.panel.config.generic.headers.entity_id"), + defaultHidden: defaultHidden, + filterable: true, + sortable: true, + }; +} + +export function getDomainTableColumn( + localize: LocalizeFunc +): DataTableColumnData { + return { + title: localize("ui.panel.config.generic.headers.domain"), + hidden: true, + groupable: true, + filterable: true, + sortable: false, + }; +} + +export function getAreaTableColumn( + localize: LocalizeFunc +): DataTableColumnData { + return { + title: localize("ui.panel.config.generic.headers.area"), + groupable: true, + filterable: true, + sortable: true, + minWidth: "120px", + }; +} + +export function getFloorTableColumn( + localize: LocalizeFunc +): DataTableColumnData { + return { + title: localize("ui.panel.config.generic.headers.floor"), + defaultHidden: true, + groupable: true, + filterable: true, + sortable: true, + minWidth: "120px", + }; +} + +export function getCategoryTableColumn( + localize: LocalizeFunc +): DataTableColumnData { + return { + title: localize("ui.panel.config.generic.headers.category"), + defaultHidden: true, + groupable: true, + filterable: true, + sortable: true, + }; +} + +export function getEditableTableColumn( + localize: LocalizeFunc, + tooltip: string +): DataTableColumnData { + return { + title: localize("ui.panel.config.generic.headers.editable"), + type: "icon", + showNarrow: true, + sortable: true, + minWidth: "88px", + maxWidth: "88px", + template: (entry: any) => html` + ${!entry.editable + ? html` + + + ${tooltip} + + ` + : nothing} + `, + }; +} + +export function getLabelsTableColumn(): DataTableColumnData { + return { + title: "", + hidden: true, + filterable: true, + template: (entry: any) => + entry.label_entries.map((lbl) => lbl.name).join(" "), + }; +} + +export function getTriggeredAtTableColumn( + localize: LocalizeFunc, + hass: HomeAssistant +): DataTableColumnData { + return { + title: localize("ui.card.automation.last_triggered"), + sortable: true, + template: (entry: any) => + renderRelativeTimeColumn( + entry.last_triggered, + "last-triggered", + entry.entity_id, + localize, + hass + ), + }; +} + +export const renderRelativeTimeColumn = ( + valueRelativeTime: string, + valueName: string, + entity_id: string, + localize: LocalizeFunc, + hass: HomeAssistant +) => { + if (!valueRelativeTime || isUnavailableState(valueRelativeTime)) { + return localize("ui.components.relative_time.never"); + } + const date = new Date(valueRelativeTime); + const now = new Date(); + const dayDifference = differenceInDays(now, date); + const formattedTime = formatShortDateTimeWithConditionalYear( + date, + hass.locale, + hass.config + ); + const elementId = valueName + "-" + slugify(entity_id); + return html` + ${dayDifference > 3 + ? formattedTime + : html` + ${formattedTime} + ${relativeTime(date, hass.locale)} + `} + `; +}; + +export function getCreatedAtTableColumn( + localize: LocalizeFunc, + hass: HomeAssistant +): DataTableColumnData { + return { + title: localize("ui.panel.config.generic.headers.created_at"), + defaultHidden: true, + sortable: true, + minWidth: "128px", + template: (entry: any) => renderDateTimeColumn(entry.created_at, hass), + }; +} + +export function getModifiedAtTableColumn( + localize: LocalizeFunc, + hass: HomeAssistant +): DataTableColumnData { + return { + title: localize("ui.panel.config.generic.headers.modified_at"), + defaultHidden: true, + sortable: true, + minWidth: "128px", + template: (entry: any) => renderDateTimeColumn(entry.modified_at, hass), + }; +} + +const renderDateTimeColumn = (valueDateTime: number, hass: HomeAssistant) => + html`${valueDateTime + ? formatShortDateTimeWithConditionalYear( + new Date(valueDateTime * 1000), + hass.locale, + hass.config + ) + : nothing}`; diff --git a/src/panels/config/devices/ha-config-devices-dashboard.ts b/src/panels/config/devices/ha-config-devices-dashboard.ts index 3b27c43ee7..e9b74b4fb0 100644 --- a/src/panels/config/devices/ha-config-devices-dashboard.ts +++ b/src/panels/config/devices/ha-config-devices-dashboard.ts @@ -16,7 +16,6 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { computeCssColor } from "../../../common/color/compute-color"; -import { formatShortDateTime } from "../../../common/datetime/format_date_time"; import { storage } from "../../../common/decorators/storage"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; @@ -96,6 +95,13 @@ import { configSections } from "../ha-panel-config"; import "../integrations/ha-integration-overflow-menu"; import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; +import { + getAreaTableColumn, + getFloorTableColumn, + getLabelsTableColumn, + getCreatedAtTableColumn, + getModifiedAtTableColumn, +} from "../common/data-table-columns"; import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown"; interface DeviceRowData extends DeviceRegistryEntry { @@ -103,7 +109,7 @@ interface DeviceRowData extends DeviceRegistryEntry { area?: string; integration?: string; battery_entity?: [string | undefined, string | undefined]; - label_entries: EntityRegistryEntry[]; + label_entries: LabelRegistryEntry[]; } @customElement("ha-config-devices-dashboard") @@ -450,7 +456,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { (lbl) => labelReg!.find((label) => label.label_id === lbl)! ); - let floorName = "—"; + let floorName; if ( device.area_id && areas[device.area_id]?.floor_id && @@ -556,22 +562,8 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { : nothing} `, }, - area: { - title: localize("ui.panel.config.devices.data_table.area"), - sortable: true, - filterable: true, - groupable: true, - minWidth: "120px", - template: (device) => device.area || "—", - }, - floor: { - title: localize("ui.panel.config.devices.data_table.floor"), - sortable: true, - filterable: true, - groupable: true, - minWidth: "120px", - defaultHidden: true, - }, + area: getAreaTableColumn(localize), + floor: getFloorTableColumn(localize), integration: { title: localize("ui.panel.config.devices.data_table.integration"), sortable: true, @@ -629,34 +621,8 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { : "—"; }, }, - created_at: { - title: localize("ui.panel.config.generic.headers.created_at"), - defaultHidden: true, - sortable: true, - minWidth: "128px", - template: (entry) => - entry.created_at - ? formatShortDateTime( - new Date(entry.created_at * 1000), - this.hass.locale, - this.hass.config - ) - : "—", - }, - modified_at: { - title: localize("ui.panel.config.generic.headers.modified_at"), - defaultHidden: true, - sortable: true, - minWidth: "128px", - template: (entry) => - entry.modified_at - ? formatShortDateTime( - new Date(entry.modified_at * 1000), - this.hass.locale, - this.hass.config - ) - : "—", - }, + created_at: getCreatedAtTableColumn(localize, this.hass), + modified_at: getModifiedAtTableColumn(localize, this.hass), disabled_by: { title: localize("ui.panel.config.devices.picker.state"), type: "icon", @@ -685,13 +651,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { ` : "—", }, - labels: { - title: "", - hidden: true, - filterable: true, - template: (device) => - device.label_entries.map((lbl) => lbl.name).join(" "), - }, + labels: getLabelsTableColumn(), } as DataTableColumnContainer; }); diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index e0ff7a51bb..1dbf185952 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -24,7 +24,6 @@ import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; import memoize from "memoize-one"; import { computeCssColor } from "../../../common/color/compute-color"; -import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time"; import { storage } from "../../../common/decorators/storage"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; import { computeAreaName } from "../../../common/entity/compute_area_name"; @@ -120,6 +119,14 @@ import { isHelperDomain } from "../helpers/const"; import "../integrations/ha-integration-overflow-menu"; import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; +import { + getEntityIdTableColumn, + getDomainTableColumn, + getAreaTableColumn, + getLabelsTableColumn, + getCreatedAtTableColumn, + getModifiedAtTableColumn, +} from "../common/data-table-columns"; import { getAssistantsSortableKey, getAssistantsTableColumn, @@ -376,32 +383,15 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { groupable: true, hidden: true, }, - area: { - title: localize("ui.panel.config.entities.picker.headers.area"), - sortable: true, - filterable: true, - groupable: true, - template: (entry) => entry.area || "—", - }, - entity_id: { - title: localize("ui.panel.config.entities.picker.headers.entity_id"), - sortable: true, - filterable: true, - defaultHidden: true, - }, + area: getAreaTableColumn(localize), + entity_id: getEntityIdTableColumn(localize, true), localized_platform: { title: localize("ui.panel.config.entities.picker.headers.integration"), sortable: true, groupable: true, filterable: true, }, - domain: { - title: localize("ui.panel.config.entities.picker.headers.domain"), - sortable: false, - hidden: true, - filterable: true, - groupable: true, - }, + domain: getDomainTableColumn(localize), disabled_by: { title: localize("ui.panel.config.entities.picker.headers.disabled_by"), hidden: true, @@ -475,34 +465,8 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { ` : "—", }, - created_at: { - title: localize("ui.panel.config.generic.headers.created_at"), - defaultHidden: true, - sortable: true, - minWidth: "128px", - template: (entry) => - entry.created_at - ? formatShortDateTimeWithConditionalYear( - new Date(entry.created_at * 1000), - this.hass.locale, - this.hass.config - ) - : "—", - }, - modified_at: { - title: localize("ui.panel.config.generic.headers.modified_at"), - defaultHidden: true, - sortable: true, - minWidth: "128px", - template: (entry) => - entry.modified_at - ? formatShortDateTimeWithConditionalYear( - new Date(entry.modified_at * 1000), - this.hass.locale, - this.hass.config - ) - : "—", - }, + created_at: getCreatedAtTableColumn(localize, this.hass), + modified_at: getModifiedAtTableColumn(localize, this.hass), available: { title: localize("ui.panel.config.entities.picker.headers.availability"), sortable: true, @@ -521,13 +485,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { groupable: true, hidden: true, }, - labels: { - title: "", - hidden: true, - filterable: true, - template: (entry) => - entry.label_entries.map((lbl) => lbl.name).join(" "), - }, + labels: getLabelsTableColumn(), assistants: getAssistantsTableColumn( localize, this.hass, diff --git a/src/panels/config/helpers/ha-config-helpers.ts b/src/panels/config/helpers/ha-config-helpers.ts index 42f71058fe..883730828a 100644 --- a/src/panels/config/helpers/ha-config-helpers.ts +++ b/src/panels/config/helpers/ha-config-helpers.ts @@ -9,7 +9,6 @@ import { mdiDotsVertical, mdiDownload, mdiMenuDown, - mdiPencilOff, mdiPlus, mdiProgressHelper, mdiTag, @@ -27,7 +26,6 @@ import type { HASSDomEvent } from "../../../common/dom/fire_event"; import { computeAreaName } from "../../../common/entity/compute_area_name"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { navigate } from "../../../common/navigate"; -import { slugify } from "../../../common/string/slugify"; import type { LocalizeFunc, LocalizeKeys, @@ -118,6 +116,13 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, Route } from "../../../types"; import { fileDownload } from "../../../util/file_download"; +import { + getEntityIdTableColumn, + getAreaTableColumn, + getCategoryTableColumn, + getLabelsTableColumn, + getEditableTableColumn, +} from "../common/data-table-columns"; import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; import { configSections } from "../ha-panel-config"; @@ -360,68 +365,20 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { ` : nothing, }, - entity_id: { - title: localize("ui.panel.config.helpers.picker.headers.entity_id"), - sortable: true, - filterable: true, - }, - category: { - title: localize("ui.panel.config.helpers.picker.headers.category"), - hidden: true, - groupable: true, - filterable: true, - sortable: true, - }, - area: { - title: localize("ui.panel.config.helpers.picker.headers.area"), - sortable: true, - filterable: true, - groupable: true, - template: (helper) => helper.area || "—", - }, - labels: { - title: "", - hidden: true, - filterable: true, - template: (helper) => - helper.label_entries.map((lbl) => lbl.name).join(" "), - }, + entity_id: getEntityIdTableColumn(localize), + category: getCategoryTableColumn(localize), + area: getAreaTableColumn(localize), + labels: getLabelsTableColumn(), localized_type: { title: localize("ui.panel.config.helpers.picker.headers.type"), sortable: true, filterable: true, groupable: true, }, - editable: { - title: localize("ui.panel.config.helpers.picker.headers.editable"), - type: "icon", - sortable: true, - minWidth: "88px", - maxWidth: "88px", - showNarrow: true, - template: (helper) => html` - ${!helper.editable - ? html` -
- - ${this.hass.localize( - "ui.panel.config.entities.picker.status.unmanageable" - )} - -
- ` - : ""} - `, - }, + editable: getEditableTableColumn( + localize, + localize("ui.panel.config.entities.picker.status.unmanageable") + ), actions: { title: "", label: this.hass.localize("ui.panel.config.generic.headers.actions"), diff --git a/src/panels/config/labels/ha-config-labels.ts b/src/panels/config/labels/ha-config-labels.ts index 80b745a284..26ddd16871 100644 --- a/src/panels/config/labels/ha-config-labels.ts +++ b/src/panels/config/labels/ha-config-labels.ts @@ -12,7 +12,6 @@ import type { PropertyValues } from "lit"; import { LitElement, html, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; -import { formatShortDateTime } from "../../../common/datetime/format_date_time"; import { storage } from "../../../common/decorators/storage"; import { navigate } from "../../../common/navigate"; import type { LocalizeFunc } from "../../../common/translations/localize"; @@ -47,6 +46,10 @@ import "../../../layouts/hass-tabs-subpage-data-table"; import type { HomeAssistant, Route } from "../../../types"; import { configSections } from "../ha-panel-config"; import { showLabelDetailDialog } from "./show-dialog-label-detail"; +import { + getCreatedAtTableColumn, + getModifiedAtTableColumn, +} from "../common/data-table-columns"; @customElement("ha-config-labels") export class HaConfigLabels extends LitElement { @@ -135,34 +138,8 @@ export class HaConfigLabels extends LitElement { filterable: true, hideable: true, }, - created_at: { - title: localize("ui.panel.config.generic.headers.created_at"), - defaultHidden: true, - sortable: true, - minWidth: "128px", - template: (label) => - label.created_at - ? formatShortDateTime( - new Date(label.created_at * 1000), - this.hass.locale, - this.hass.config - ) - : "—", - }, - modified_at: { - title: localize("ui.panel.config.generic.headers.modified_at"), - defaultHidden: true, - sortable: true, - minWidth: "128px", - template: (label) => - label.modified_at - ? formatShortDateTime( - new Date(label.modified_at * 1000), - this.hass.locale, - this.hass.config - ) - : "—", - }, + created_at: getCreatedAtTableColumn(localize, this.hass), + modified_at: getModifiedAtTableColumn(localize, this.hass), actions: { title: "", label: localize("ui.panel.config.generic.headers.actions"), diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index 0a9c0b6679..3cf362b9a5 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -12,27 +12,22 @@ import { mdiOpenInNew, mdiPalette, mdiPencil, - mdiPencilOff, mdiPlay, mdiPlus, mdiTag, mdiTextureBox, } from "@mdi/js"; -import { differenceInDays } from "date-fns"; import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { computeCssColor } from "../../../common/color/compute-color"; -import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time"; -import { relativeTime } from "../../../common/datetime/relative_time"; import { storage } from "../../../common/decorators/storage"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { navigate } from "../../../common/navigate"; -import { slugify } from "../../../common/string/slugify"; import type { LocalizeFunc } from "../../../common/translations/localize"; import { hasRejectedItems, @@ -79,7 +74,6 @@ import { isRelatedItemsFilterUsed, serializeFilters, } from "../../../data/data_table_filters"; -import { isUnavailableState } from "../../../data/entity/entity"; import type { EntityRegistryEntry, UpdateEntityRegistryEntryResult, @@ -116,6 +110,13 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; import { configSections } from "../ha-panel-config"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; +import { + getAreaTableColumn, + getCategoryTableColumn, + getLabelsTableColumn, + getEditableTableColumn, + renderRelativeTimeColumn, +} from "../common/data-table-columns"; import { getAssistantsSortableKey, getAssistantsTableColumn, @@ -128,9 +129,10 @@ type SceneItem = SceneEntity & { name: string; area: string | undefined; category: string | undefined; - labels: LabelRegistryEntry[]; + label_entries: LabelRegistryEntry[]; assistants: string[]; assistants_sortable_key: string | undefined; + editable: boolean; }; @customElement("ha-scene-dashboard") @@ -257,12 +259,13 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { category: category ? categoryReg?.find((cat) => cat.category_id === category)?.name : undefined, - labels: (labels || []).map( + label_entries: (labels || []).map( (lbl) => labelReg!.find((label) => label.label_id === lbl)! ), assistants, assistants_sortable_key: getAssistantsSortableKey(assistants), selectable: entityRegEntry !== undefined, + editable: Boolean(scene.attributes.id), }; }); } @@ -295,89 +298,34 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { direction: "asc", flex: 2, extraTemplate: (scene) => - scene.labels.length + scene.label_entries.length ? html`` : nothing, }, - area: { - title: localize("ui.panel.config.scene.picker.headers.area"), - groupable: true, - filterable: true, - sortable: true, - }, - category: { - title: localize("ui.panel.config.scene.picker.headers.category"), - defaultHidden: true, - groupable: true, - filterable: true, - sortable: true, - }, - labels: { - title: "", - hidden: true, - filterable: true, - template: (scene) => scene.labels.map((lbl) => lbl.name).join(" "), - }, + area: getAreaTableColumn(localize), + category: getCategoryTableColumn(localize), + labels: getLabelsTableColumn(), state: { title: localize( "ui.panel.config.scene.picker.headers.last_activated" ), sortable: true, - template: (scene) => { - const lastActivated = scene.state; - if (!lastActivated || isUnavailableState(lastActivated)) { - return localize("ui.components.relative_time.never"); - } - const date = new Date(scene.state); - const now = new Date(); - const dayDifference = differenceInDays(now, date); - const formattedTime = formatShortDateTimeWithConditionalYear( - date, - this.hass.locale, - this.hass.config - ); - const elementId = "last-activated-" + slugify(scene.entity_id); - return html` - ${dayDifference > 3 - ? formattedTime - : html` - ${formattedTime} - ${relativeTime(date, this.hass.locale)} - `} - `; - }, - }, - only_editable: { - title: "", - label: this.hass.localize( - "ui.panel.config.scene.picker.headers.editable" - ), - type: "icon", - showNarrow: true, template: (scene) => - !scene.attributes.id - ? html` - - - ${this.hass.localize( - "ui.panel.config.scene.picker.only_editable" - )} - - ` - : nothing, + renderRelativeTimeColumn( + scene.state, + "last-activated", + scene.entity_id, + localize, + this.hass + ), }, + only_editable: getEditableTableColumn( + localize, + localize("ui.panel.config.scene.picker.only_editable") + ), actions: { title: "", label: this.hass.localize("ui.panel.config.generic.headers.actions"), @@ -424,7 +372,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { "ui.panel.config.scene.editor.rename" ), action: () => this._rename(scene), - disabled: !scene.attributes.id, + disabled: !scene.editable, }, { divider: true, @@ -435,7 +383,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { "ui.panel.config.scene.picker.duplicate" ), action: () => this._duplicate(scene), - disabled: !scene.attributes.id, + disabled: !scene.editable, }, { label: this.hass.localize( @@ -443,8 +391,8 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { ), path: mdiDelete, action: () => this._deleteConfirm(scene), - warning: scene.attributes.id, - disabled: !scene.attributes.id, + warning: scene.editable, + disabled: !scene.editable, }, ]} > @@ -1066,7 +1014,7 @@ ${rejected } } - private async _duplicate(scene) { + private async _duplicate(scene: SceneEntity) { if (scene.attributes.id) { const config = await getSceneConfig(this.hass, scene.attributes.id); const entityRegEntry = this._entityReg.find( diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index d6dff1e632..e44b5d0e76 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -17,7 +17,6 @@ import { mdiTextureBox, mdiTransitConnection, } from "@mdi/js"; -import { differenceInDays } from "date-fns"; import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; @@ -26,14 +25,11 @@ import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; import { computeCssColor } from "../../../common/color/compute-color"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; -import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time"; -import { relativeTime } from "../../../common/datetime/relative_time"; import { storage } from "../../../common/decorators/storage"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { navigate } from "../../../common/navigate"; -import { slugify } from "../../../common/string/slugify"; import type { LocalizeFunc } from "../../../common/translations/localize"; import { hasRejectedItems, @@ -116,6 +112,13 @@ import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry import { showNewAutomationDialog } from "../automation/show-dialog-new-automation"; import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; +import { + getEntityIdHiddenTableColumn, + getAreaTableColumn, + getCategoryTableColumn, + getLabelsTableColumn, + getTriggeredAtTableColumn, +} from "../common/data-table-columns"; import { configSections } from "../ha-panel-config"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; import { @@ -130,7 +133,7 @@ type ScriptItem = ScriptEntity & { area: string | undefined; last_triggered: string | undefined; category: string | undefined; - labels: LabelRegistryEntry[]; + label_entries: LabelRegistryEntry[]; assistants: string[]; assistants_sortable_key: string | undefined; }; @@ -264,7 +267,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { category: category ? categoryReg?.find((cat) => cat.category_id === category)?.name : undefined, - labels: (labels || []).map( + label_entries: (labels || []).map( (lbl) => labelReg!.find((label) => label.label_id === lbl)! ), assistants, @@ -297,6 +300,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { })} >`, }, + entity_id: getEntityIdHiddenTableColumn(), name: { title: localize("ui.panel.config.script.picker.headers.name"), main: true, @@ -305,60 +309,17 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { direction: "asc", flex: 2, extraTemplate: (script) => - script.labels.length + script.label_entries.length ? html`` : nothing, }, - area: { - title: localize("ui.panel.config.script.picker.headers.area"), - groupable: true, - filterable: true, - sortable: true, - }, - category: { - title: localize("ui.panel.config.script.picker.headers.category"), - defaultHidden: true, - groupable: true, - filterable: true, - sortable: true, - }, - labels: { - title: "", - hidden: true, - filterable: true, - template: (script) => script.labels.map((lbl) => lbl.name).join(" "), - }, - last_triggered: { - sortable: true, - title: localize("ui.card.automation.last_triggered"), - template: (script) => { - if (!script.last_triggered) { - return this.hass.localize("ui.components.relative_time.never"); - } - const date = new Date(script.last_triggered); - const now = new Date(); - const dayDifference = differenceInDays(now, date); - const formattedTime = formatShortDateTimeWithConditionalYear( - date, - this.hass.locale, - this.hass.config - ); - const elementId = "last-triggered-" + slugify(script.entity_id); - return html` - ${dayDifference > 3 - ? formattedTime - : html` - ${formattedTime} - ${relativeTime(date, this.hass.locale)} - `} - `; - }, - }, + area: getAreaTableColumn(localize), + category: getCategoryTableColumn(localize), + labels: getLabelsTableColumn(), + last_triggered: getTriggeredAtTableColumn(localize, this.hass), actions: { title: "", label: this.hass.localize("ui.panel.config.generic.headers.actions"), diff --git a/src/panels/config/voice-assistants/expose/assistants-table-column.ts b/src/panels/config/voice-assistants/expose/assistants-table-column.ts index c05263cfa0..77f391c768 100644 --- a/src/panels/config/voice-assistants/expose/assistants-table-column.ts +++ b/src/panels/config/voice-assistants/expose/assistants-table-column.ts @@ -16,9 +16,7 @@ export function getAssistantsTableColumn( visible?: boolean ): DataTableColumnData { return { - title: localize( - "ui.panel.config.voice_assistants.expose.headers.assistants" - ), + title: localize("ui.panel.config.generic.headers.assistants"), type: "flex", defaultHidden: !visible, sortable: true, diff --git a/src/panels/config/voice-assistants/ha-config-voice-assistants-expose.ts b/src/panels/config/voice-assistants/ha-config-voice-assistants-expose.ts index e0e9ec8011..9e4854f4f5 100644 --- a/src/panels/config/voice-assistants/ha-config-voice-assistants-expose.ts +++ b/src/panels/config/voice-assistants/ha-config-voice-assistants-expose.ts @@ -49,6 +49,11 @@ import "../../../layouts/hass-tabs-subpage-data-table"; import type { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-data-table"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, Route } from "../../../types"; +import { + getEntityIdTableColumn, + getDomainTableColumn, + getAreaTableColumn, +} from "../common/data-table-columns"; import "./expose/expose-assistant-icon"; import { getAssistantsTableColumn, @@ -178,30 +183,9 @@ export class VoiceAssistantsExpose extends LitElement { direction: "asc", flex: 2, }, - area: { - title: localize("ui.panel.config.voice_assistants.expose.headers.area"), - sortable: true, - groupable: true, - filterable: true, - template: (entry) => entry.area || "—", - }, - entity_id: { - title: localize( - "ui.panel.config.voice_assistants.expose.headers.entity_id" - ), - sortable: true, - filterable: true, - defaultHidden: true, - }, - domain: { - title: localize( - "ui.panel.config.voice_assistants.expose.headers.domain" - ), - sortable: false, - hidden: true, - filterable: true, - groupable: true, - }, + area: getAreaTableColumn(localize), + entity_id: getEntityIdTableColumn(localize, true), + domain: getDomainTableColumn(localize), assistants: getAssistantsTableColumn( localize, this.hass, diff --git a/src/translations/en.json b/src/translations/en.json index 8398c4356e..454a91fd15 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2329,6 +2329,13 @@ "config": { "generic": { "headers": { + "entity_id": "Entity ID", + "domain": "Domain", + "area": "Area", + "floor": "Floor", + "category": "Category", + "assistants": "Assistants", + "editable": "Editable", "modified_at": "Modified", "created_at": "Created", "actions": "Actions" @@ -3964,11 +3971,7 @@ "headers": { "icon": "Icon", "name": "Name", - "entity_id": "Entity ID", - "type": "Type", - "editable": "Editable", - "category": "Category", - "area": "Area" + "type": "Type" }, "create_helper": "Create helper", "no_helpers": "Looks like you don't have any helpers yet!", @@ -4425,10 +4428,6 @@ "headers": { "icon": "Icon", "name": "Name", - "entity_id": "Entity ID", - "area": "Area", - "domain": "Domain", - "assistants": "Assistants", "aliases": "Aliases", "remove": "[%key:ui::common::remove%]" }, @@ -4598,8 +4597,6 @@ "trigger": "Trigger", "actions": "Actions", "state": "State", - "category": "Category", - "area": "Area", "icon": "Icon" }, "bulk_action": "Action", @@ -5619,8 +5616,6 @@ "headers": { "name": "Name", "state": "State", - "category": "Category", - "area": "Area", "icon": "Icon" }, "edit_category": "[%key:ui::panel::config::automation::picker::edit_category%]", @@ -5746,9 +5741,6 @@ "state": "State", "name": "Name", "last_activated": "Last activated", - "category": "Category", - "editable": "[%key:ui::panel::config::helpers::picker::headers::editable%]", - "area": "Area", "icon": "Icon" }, "edit_category": "[%key:ui::panel::config::automation::picker::edit_category%]", @@ -6142,8 +6134,6 @@ "device": "Device", "manufacturer": "Manufacturer", "model": "Model", - "area": "Area", - "floor": "Floor", "integration": "Integration", "battery": "Battery", "disabled_by": "Disabled", @@ -6201,10 +6191,8 @@ "headers": { "state_icon": "State icon", "entity": "Entity", - "entity_id": "Entity ID", "device": "Device", "integration": "Integration", - "area": "Area", "disabled_by": "Disabled by", "status": "Status", "domain": "Domain",