1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 00:27:49 +01:00

Add target error badge if target is missing (#30352)

* Add target error badge if target is missing

* Don't show for newly added items
This commit is contained in:
Bram Kragten
2026-03-26 15:58:22 +01:00
parent 1132cdb364
commit d466ab63bd
7 changed files with 103 additions and 37 deletions

View File

@@ -185,6 +185,8 @@ export default class HaAutomationActionRow extends LitElement {
@state() private _collapsed = true;
@state() private _isNew = false;
@state() private _warnings?: string[];
@query("ha-automation-action-editor")
@@ -237,12 +239,13 @@ export default class HaAutomationActionRow extends LitElement {
private _renderRow() {
const type = getAutomationActionType(this.action);
const target =
type === "service" && "target" in this.action
? (this.action as ServiceAction).target
: type === "device_id" && (this.action as DeviceAction).device_id
? { device_id: (this.action as DeviceAction).device_id }
: undefined;
const actionHasTarget = type === "service" && "target" in this.action;
const target = actionHasTarget
? (this.action as ServiceAction).target
: type === "device_id" && (this.action as DeviceAction).device_id
? { device_id: (this.action as DeviceAction).device_id }
: undefined;
return html`
${type === "service" && "action" in this.action && this.action.action
@@ -265,7 +268,9 @@ export default class HaAutomationActionRow extends LitElement {
${capitalizeFirstLetter(
describeAction(this.hass, this._entityReg, this.action)
)}
${target ? this._renderTargets(target) : nothing}
${target !== undefined || (actionHasTarget && !this._isNew)
? this._renderTargets(target, actionHasTarget && !this._isNew)
: nothing}
${type !== "condition" &&
(this.action as NonConditionAction).continue_on_error === true
? html`<ha-svg-icon
@@ -575,10 +580,11 @@ export default class HaAutomationActionRow extends LitElement {
}
private _renderTargets = memoizeOne(
(target?: HassServiceTarget) =>
(target?: HassServiceTarget, targetRequired = false) =>
html`<ha-automation-row-targets
.hass=${this.hass}
.target=${target}
.targetRequired=${targetRequired}
></ha-automation-row-targets>`
);
@@ -812,6 +818,10 @@ export default class HaAutomationActionRow extends LitElement {
this.openSidebar();
}
public markAsNew(): void {
this._isNew = true;
}
public openSidebar(action?: Action): void {
const sidebarAction = action ?? this.action;
const actionType = getAutomationActionType(sidebarAction);
@@ -822,6 +832,7 @@ export default class HaAutomationActionRow extends LitElement {
},
close: (focus?: boolean) => {
this._selected = false;
this._isNew = false;
fireEvent(this, "close-sidebar");
if (focus) {
this.focus();
@@ -901,6 +912,9 @@ export default class HaAutomationActionRow extends LitElement {
);
private _toggleCollapse() {
if (!this._collapsed) {
this._isNew = false;
}
this._collapsed = !this._collapsed;
}

View File

@@ -161,6 +161,7 @@ export default class HaAutomationAction extends AutomationSortableListMixin<Acti
if (mode === "new") {
row.expand();
row.markAsNew();
}
if (!this.optionsInSidebar) {

View File

@@ -107,6 +107,8 @@ export default class HaAutomationConditionRow extends LitElement {
@state() private _collapsed = true;
@state() private _isNew = false;
@state() private _warnings?: string[];
@property({ attribute: false })
@@ -160,13 +162,15 @@ export default class HaAutomationConditionRow extends LitElement {
}
private _renderRow() {
const target =
"target" in (this.conditionDescriptions[this.condition.condition] || {})
? (this.condition as PlatformCondition).target
: "device_id" in this.condition &&
(this.condition as DeviceCondition).device_id
? { device_id: [(this.condition as DeviceCondition).device_id] }
: undefined;
const descriptionHasTarget =
"target" in (this.conditionDescriptions[this.condition.condition] || {});
const target = descriptionHasTarget
? (this.condition as PlatformCondition).target
: "device_id" in this.condition &&
(this.condition as DeviceCondition).device_id
? { device_id: [(this.condition as DeviceCondition).device_id] }
: undefined;
return html`
<ha-condition-icon
@@ -178,7 +182,9 @@ export default class HaAutomationConditionRow extends LitElement {
${capitalizeFirstLetter(
describeCondition(this.condition, this.hass, this._entityReg)
)}
${target ? this._renderTargets(target) : nothing}
${target !== undefined || (descriptionHasTarget && !this._isNew)
? this._renderTargets(target, descriptionHasTarget && !this._isNew)
: nothing}
</h3>
<slot name="icons" slot="icons"></slot>
@@ -464,10 +470,11 @@ export default class HaAutomationConditionRow extends LitElement {
}
private _renderTargets = memoizeOne(
(target?: HassServiceTarget) =>
(target?: HassServiceTarget, targetRequired = false) =>
html`<ha-automation-row-targets
.hass=${this.hass}
.target=${target}
.targetRequired=${targetRequired}
></ha-automation-row-targets>`
);
@@ -743,6 +750,10 @@ export default class HaAutomationConditionRow extends LitElement {
this.openSidebar();
}
public markAsNew(): void {
this._isNew = true;
}
public openSidebar(condition?: Condition): void {
const sidebarCondition = condition || this.condition;
fireEvent(this, "open-sidebar", {
@@ -751,6 +762,7 @@ export default class HaAutomationConditionRow extends LitElement {
},
close: (focus?: boolean) => {
this._selected = false;
this._isNew = false;
fireEvent(this, "close-sidebar");
if (focus) {
this.focus();
@@ -806,6 +818,9 @@ export default class HaAutomationConditionRow extends LitElement {
);
private _toggleCollapse() {
if (!this._collapsed) {
this._isNew = false;
}
this._collapsed = !this._collapsed;
}

View File

@@ -169,6 +169,7 @@ export default class HaAutomationCondition extends AutomationSortableListMixin<C
if (mode === "new") {
row.expand();
row.markAsNew();
}
if (!this.optionsInSidebar) {

View File

@@ -1,13 +1,15 @@
import { consume } from "@lit/context";
import {
mdiAlert,
mdiAlertOctagon,
mdiCodeBraces,
mdiFormatListBulleted,
mdiShape,
} from "@mdi/js";
import type { HassServiceTarget } from "home-assistant-js-websocket";
import { css, html, LitElement, type nothing, type TemplateResult } from "lit";
import { css, html, LitElement, nothing, type TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ensureArray } from "../../../../common/array/ensure-array";
import { transform } from "../../../../common/decorators/transform";
import { isTemplate } from "../../../../common/string/has-template";
@@ -35,6 +37,9 @@ export class HaAutomationRowTargets extends LitElement {
@property({ attribute: false })
public target?: HassServiceTarget;
@property({ attribute: false })
public targetRequired = false;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: HomeAssistant["localize"];
@@ -73,13 +78,16 @@ export class HaAutomationRowTargets extends LitElement {
protected render() {
const length = Object.keys(this.target || {}).length;
if (!length) {
return html`<span class="target">
<div class="label">
${this.localize(
"ui.panel.config.automation.editor.target_summary.no_target"
)}
</div>
</span>`;
return this._renderTargetBadge(
this.targetRequired
? html`<ha-svg-icon .path=${mdiAlertOctagon}></ha-svg-icon>`
: nothing,
this.localize(
"ui.panel.config.automation.editor.target_summary.no_target"
),
false,
this.targetRequired
);
}
const totalLength = Object.values(this.target || {}).reduce(
(acc, val) => acc + ensureArray(val).length,
@@ -154,9 +162,10 @@ export class HaAutomationRowTargets extends LitElement {
private _renderTargetBadge(
icon: TemplateResult | typeof nothing,
label: string,
alert = false
warning = false,
error = false
) {
return html`<div class="target ${alert ? "alert" : ""}">
return html`<div class=${classMap({ target: true, warning, error })}>
${icon}
<div class="label">${label}</div>
</div>`;
@@ -230,10 +239,14 @@ export class HaAutomationRowTargets extends LitElement {
overflow: hidden;
height: 32px;
}
.target.alert {
.target.warning {
background: var(--ha-color-fill-warning-normal-resting);
color: var(--ha-color-on-warning-normal);
}
.target.error {
background: var(--ha-color-fill-danger-normal-resting);
color: var(--ha-color-on-danger-normal);
}
.target .label {
overflow: hidden;
text-overflow: ellipsis;

View File

@@ -145,6 +145,8 @@ export default class HaAutomationTriggerRow extends LitElement {
@state() private _selected = false;
@state() private _isNew = false;
@state() private _warnings?: string[];
@property({ attribute: false })
@@ -197,14 +199,16 @@ export default class HaAutomationTriggerRow extends LitElement {
const yamlMode = this._yamlMode || !supported;
const target =
const descriptionHasTarget =
type === "platform" &&
"target" in
this.triggerDescriptions[(this.trigger as PlatformTrigger).trigger]
? (this.trigger as PlatformTrigger).target
: type === "device" && (this.trigger as DeviceTrigger).device_id
? { device_id: (this.trigger as DeviceTrigger).device_id }
: undefined;
this.triggerDescriptions[(this.trigger as PlatformTrigger).trigger];
const target = descriptionHasTarget
? (this.trigger as PlatformTrigger).target
: type === "device" && (this.trigger as DeviceTrigger).device_id
? { device_id: (this.trigger as DeviceTrigger).device_id }
: undefined;
return html`
${type === "list"
@@ -220,7 +224,9 @@ export default class HaAutomationTriggerRow extends LitElement {
></ha-trigger-icon>`}
<h3 slot="header">
${describeTrigger(this.trigger, this.hass, this._entityReg)}
${target ? this._renderTargets(target) : nothing}
${target !== undefined || (descriptionHasTarget && !this._isNew)
? this._renderTargets(target, descriptionHasTarget && !this._isNew)
: nothing}
</h3>
<slot name="icons" slot="icons"></slot>
@@ -446,7 +452,10 @@ export default class HaAutomationTriggerRow extends LitElement {
: nothing}${this._renderRow()}</ha-automation-row
>`
: html`
<ha-expansion-panel left-chevron>
<ha-expansion-panel
left-chevron
@expanded-changed=${this._expansionPanelChanged}
>
${this._renderRow()}
</ha-expansion-panel>
`}
@@ -467,10 +476,11 @@ export default class HaAutomationTriggerRow extends LitElement {
}
private _renderTargets = memoizeOne(
(target?: HassServiceTarget) =>
(target?: HassServiceTarget, targetRequired = false) =>
html`<ha-automation-row-targets
.hass=${this.hass}
.target=${target}
.targetRequired=${targetRequired}
></ha-automation-row-targets>`
);
@@ -576,6 +586,16 @@ export default class HaAutomationTriggerRow extends LitElement {
this.openSidebar();
}
public markAsNew(): void {
this._isNew = true;
}
private _expansionPanelChanged(ev: CustomEvent) {
if (!ev.detail.expanded) {
this._isNew = false;
}
}
public openSidebar(trigger?: Trigger): void {
trigger = trigger || this.trigger;
fireEvent(this, "open-sidebar", {
@@ -584,6 +604,7 @@ export default class HaAutomationTriggerRow extends LitElement {
},
close: (focus?: boolean) => {
this._selected = false;
this._isNew = false;
fireEvent(this, "close-sidebar");
if (focus) {
this.focus();

View File

@@ -252,6 +252,7 @@ export default class HaAutomationTrigger extends AutomationSortableListMixin<Tri
row.expand();
row.focus();
}
row.markAsNew();
});
}
}