mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-24 04:39:01 +00:00
Show blueprint usage count in overview (#28054)
* Show blueprint usage count in overview * feat(blueprint): add usage count functionality and update table rendering * feat(blueprint): enhance usage count display with interactive chip * fix(blueprint): convert _handleUsageClick to an arrow function for consistent context binding * Update src/panels/config/blueprint/ha-blueprint-overview.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -43,6 +43,7 @@ import {
|
|||||||
} from "../../../data/blueprint";
|
} from "../../../data/blueprint";
|
||||||
import { showScriptEditor } from "../../../data/script";
|
import { showScriptEditor } from "../../../data/script";
|
||||||
import { findRelated } from "../../../data/search";
|
import { findRelated } from "../../../data/search";
|
||||||
|
import "../../../components/chips/ha-assist-chip";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
@@ -60,6 +61,7 @@ type BlueprintMetaDataPath = BlueprintMetaData & {
|
|||||||
error: boolean;
|
error: boolean;
|
||||||
type: "automation" | "script";
|
type: "automation" | "script";
|
||||||
fullpath: string;
|
fullpath: string;
|
||||||
|
usageCount?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createNewFunctions = {
|
const createNewFunctions = {
|
||||||
@@ -128,14 +130,20 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
})
|
})
|
||||||
private _filter = "";
|
private _filter = "";
|
||||||
|
|
||||||
|
@state() private _usageCounts: Record<string, number> = {};
|
||||||
|
|
||||||
|
private _usageCountRequest = 0;
|
||||||
|
|
||||||
private _processedBlueprints = memoizeOne(
|
private _processedBlueprints = memoizeOne(
|
||||||
(
|
(
|
||||||
blueprints: Record<string, Blueprints>,
|
blueprints: Record<string, Blueprints>,
|
||||||
localize: LocalizeFunc
|
localize: LocalizeFunc,
|
||||||
|
usageCounts: Record<string, number>
|
||||||
): BlueprintMetaDataPath[] => {
|
): BlueprintMetaDataPath[] => {
|
||||||
const result: any[] = [];
|
const result: any[] = [];
|
||||||
Object.entries(blueprints).forEach(([type, typeBlueprints]) =>
|
Object.entries(blueprints).forEach(([type, typeBlueprints]) =>
|
||||||
Object.entries(typeBlueprints).forEach(([path, blueprint]) => {
|
Object.entries(typeBlueprints).forEach(([path, blueprint]) => {
|
||||||
|
const fullpath = `${type}/${path}`;
|
||||||
if ("error" in blueprint) {
|
if ("error" in blueprint) {
|
||||||
result.push({
|
result.push({
|
||||||
name: blueprint.error,
|
name: blueprint.error,
|
||||||
@@ -145,7 +153,8 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
),
|
),
|
||||||
error: true,
|
error: true,
|
||||||
path,
|
path,
|
||||||
fullpath: `${type}/${path}`,
|
fullpath,
|
||||||
|
usageCount: 0,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
result.push({
|
result.push({
|
||||||
@@ -156,7 +165,8 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
),
|
),
|
||||||
error: false,
|
error: false,
|
||||||
path,
|
path,
|
||||||
fullpath: `${type}/${path}`,
|
fullpath,
|
||||||
|
usageCount: usageCounts[fullpath] || 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -189,6 +199,34 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
flex: 2,
|
flex: 2,
|
||||||
},
|
},
|
||||||
|
usage_count: {
|
||||||
|
title: localize(
|
||||||
|
"ui.panel.config.blueprint.overview.headers.usage_count"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
valueColumn: "usageCount",
|
||||||
|
type: "numeric",
|
||||||
|
minWidth: "100px",
|
||||||
|
maxWidth: "120px",
|
||||||
|
template: (blueprint) => {
|
||||||
|
const count = blueprint.usageCount ?? 0;
|
||||||
|
return html`
|
||||||
|
<ha-assist-chip
|
||||||
|
filled
|
||||||
|
.active=${count > 0}
|
||||||
|
label=${String(count)}
|
||||||
|
title=${blueprint.error
|
||||||
|
? String(count)
|
||||||
|
: this.hass.localize(
|
||||||
|
`ui.panel.config.blueprint.overview.view_${blueprint.type}`
|
||||||
|
)}
|
||||||
|
?disabled=${blueprint.error}
|
||||||
|
data-fullpath=${blueprint.fullpath}
|
||||||
|
@click=${this._handleUsageClick}
|
||||||
|
></ha-assist-chip>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
fullpath: {
|
fullpath: {
|
||||||
title: "fullpath",
|
title: "fullpath",
|
||||||
hidden: true,
|
hidden: true,
|
||||||
@@ -266,6 +304,7 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
|
this._loadUsageCounts();
|
||||||
if (this.route.path === "/import") {
|
if (this.route.path === "/import") {
|
||||||
const url = extractSearchParam("blueprint_url");
|
const url = extractSearchParam("blueprint_url");
|
||||||
navigate("/config/blueprint/dashboard", { replace: true });
|
navigate("/config/blueprint/dashboard", { replace: true });
|
||||||
@@ -275,6 +314,13 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
if (changedProps.has("blueprints")) {
|
||||||
|
this._loadUsageCounts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
@@ -284,7 +330,11 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.automations}
|
.tabs=${configSections.automations}
|
||||||
.columns=${this._columns(this.hass.localize)}
|
.columns=${this._columns(this.hass.localize)}
|
||||||
.data=${this._processedBlueprints(this.blueprints, this.hass.localize)}
|
.data=${this._processedBlueprints(
|
||||||
|
this.blueprints,
|
||||||
|
this.hass.localize,
|
||||||
|
this._usageCounts
|
||||||
|
)}
|
||||||
id="fullpath"
|
id="fullpath"
|
||||||
.noDataText=${this.hass.localize(
|
.noDataText=${this.hass.localize(
|
||||||
"ui.panel.config.blueprint.overview.no_blueprints"
|
"ui.panel.config.blueprint.overview.no_blueprints"
|
||||||
@@ -380,10 +430,51 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
fireEvent(this, "reload-blueprints");
|
fireEvent(this, "reload-blueprints");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _loadUsageCounts() {
|
||||||
|
if (!this.blueprints) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = ++this._usageCountRequest;
|
||||||
|
const usageCounts: Record<string, number> = {};
|
||||||
|
|
||||||
|
const blueprintList = this._processedBlueprints(
|
||||||
|
this.blueprints,
|
||||||
|
this.hass.localize,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
blueprintList.map(async (blueprint) => {
|
||||||
|
if (blueprint.error) {
|
||||||
|
usageCounts[blueprint.fullpath] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const related = await findRelated(
|
||||||
|
this.hass,
|
||||||
|
`${blueprint.domain}_blueprint`,
|
||||||
|
blueprint.path
|
||||||
|
);
|
||||||
|
const count =
|
||||||
|
(related.automation?.length || 0) + (related.script?.length || 0);
|
||||||
|
usageCounts[blueprint.fullpath] = count;
|
||||||
|
} catch (_err) {
|
||||||
|
usageCounts[blueprint.fullpath] = 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (request === this._usageCountRequest) {
|
||||||
|
this._usageCounts = usageCounts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
||||||
const blueprint = this._processedBlueprints(
|
const blueprint = this._processedBlueprints(
|
||||||
this.blueprints,
|
this.blueprints,
|
||||||
this.hass.localize
|
this.hass.localize,
|
||||||
|
this._usageCounts
|
||||||
).find((b) => b.fullpath === ev.detail.id)!;
|
).find((b) => b.fullpath === ev.detail.id)!;
|
||||||
if (blueprint.error) {
|
if (blueprint.error) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
@@ -397,6 +488,25 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
this._createNew(blueprint);
|
this._createNew(blueprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleUsageClick = (ev: Event) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
const target = ev.currentTarget as HTMLElement | null;
|
||||||
|
const fullpath = target?.dataset.fullpath;
|
||||||
|
if (!fullpath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const blueprint = this._processedBlueprints(
|
||||||
|
this.blueprints,
|
||||||
|
this.hass.localize,
|
||||||
|
this._usageCounts
|
||||||
|
).find((item) => item.fullpath === fullpath);
|
||||||
|
if (!blueprint || blueprint.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._showUsed(blueprint);
|
||||||
|
};
|
||||||
|
|
||||||
private _showUsed = (blueprint: BlueprintMetaDataPath) => {
|
private _showUsed = (blueprint: BlueprintMetaDataPath) => {
|
||||||
navigate(
|
navigate(
|
||||||
`/config/${blueprint.domain}/dashboard?blueprint=${encodeURIComponent(
|
`/config/${blueprint.domain}/dashboard?blueprint=${encodeURIComponent(
|
||||||
|
|||||||
@@ -4796,7 +4796,8 @@
|
|||||||
"headers": {
|
"headers": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"file_name": "File name"
|
"file_name": "File name",
|
||||||
|
"usage_count": "In use"
|
||||||
},
|
},
|
||||||
"types": {
|
"types": {
|
||||||
"automation": "Automation",
|
"automation": "Automation",
|
||||||
|
|||||||
Reference in New Issue
Block a user