257 files

This commit is contained in:
Home Assistant Version Control
2026-10-02 16:06:24 +00:00
parent 7c55603942
commit c29f5cc088
258 changed files with 24621 additions and 3136 deletions
@@ -18,6 +18,7 @@ import { property, state } from "lit/decorators.js";
import { t, ensureLocale, langOf, formatCost, formatNumber, formatQty } from "../styles";
import { runWs } from "../helpers/ws-run";
import type { HomeAssistant, MaintenancePart } from "../types";
import { restockUnits, unitPrice } from "../helpers/parts-cost";
// Per-part document links (v2.26) — the task-documents component in part mode.
import "./task-documents";
@@ -34,6 +35,7 @@ interface PartForm {
stock: string;
reorder_threshold: string;
restock_quantity: string;
package_size: string;
auto_buy_task: boolean;
notes: string;
}
@@ -50,6 +52,7 @@ const EMPTY_FORM: PartForm = {
stock: "",
reorder_threshold: "",
restock_quantity: "",
package_size: "",
auto_buy_task: true,
notes: "",
};
@@ -111,6 +114,7 @@ export class MaintenancePartsSection extends LitElement {
stock: part.stock != null ? String(part.stock) : "",
reorder_threshold: part.reorder_threshold != null ? String(part.reorder_threshold) : "",
restock_quantity: part.restock_quantity != null ? String(part.restock_quantity) : "",
package_size: part.package_size != null ? String(part.package_size) : "",
auto_buy_task: !!part.auto_buy_task,
notes: part.notes || "",
};
@@ -131,6 +135,7 @@ export class MaintenancePartsSection extends LitElement {
stock: num(f.stock),
reorder_threshold: num(f.reorder_threshold),
restock_quantity: num(f.restock_quantity),
package_size: num(f.package_size),
auto_buy_task: f.auto_buy_task,
notes: f.notes.trim() || null,
};
@@ -221,6 +226,7 @@ export class MaintenancePartsSection extends LitElement {
? html`<span class="loc"><ha-icon icon="mdi:map-marker-outline"></ha-icon>${part.storage_location}</span>`
: nothing}
</div>
${part.notes ? html`<div class="part-notes">${part.notes}</div>` : nothing}
</div>
<ha-icon-button
title=${t("documents", L)}
@@ -254,7 +260,9 @@ export class MaintenancePartsSection extends LitElement {
@click=${() => {
this._restockFor = part.id;
this._restockInvalid = false;
this._restockQty = String(part.restock_quantity || 1);
// The stock counts units: one restock is its
// packages × package size (#98 follow-up).
this._restockQty = String(restockUnits(part));
}}
><ha-icon icon="mdi:plus-minus-variant"></ha-icon
></ha-icon-button>
@@ -315,7 +323,15 @@ export class MaintenancePartsSection extends LitElement {
${this._field(t("part_cost", L), "cost", { type: "number" })}
${this._field(t("part_stock", L), "stock", { type: "number" })}
${this._field(t("part_reorder_threshold", L), "reorder_threshold", { type: "number" })}
${this._field(t("part_restock_quantity", L), "restock_quantity", { type: "number" })}
${this._field(t("part_package_size", L), "package_size", { type: "number" })}
${this._field(
f.package_size.trim() ? t("part_restock_packages", L) : t("part_restock_quantity", L),
"restock_quantity",
{ type: "number" },
)}
${f.package_size.trim()
? html`<div class="form-hint">${t("part_package_hint", L)}</div>`
: nothing}
<label class="form-field checkbox">
<input
type="checkbox"
@@ -326,6 +342,20 @@ export class MaintenancePartsSection extends LitElement {
/>
<span>${t("part_auto_buy", L)}</span>
</label>
<!-- Documented and searched since the parts feature, but the form
never had a field — only the API and imports could set it
(audit 2026-09-28). -->
<label class="form-field notes">
<span>${t("notes_label", L)}</span>
<textarea
rows="2"
maxlength="500"
.value=${f.notes}
@input=${(e: Event) => {
this._editing = { ...this._editing!, notes: (e.target as HTMLTextAreaElement).value };
}}
></textarea>
</label>
</div>
<div class="form-actions">
<ha-button appearance="plain" @click=${() => (this._editing = null)}>${t("cancel", L)}</ha-button>
@@ -343,7 +373,8 @@ export class MaintenancePartsSection extends LitElement {
private _inventoryValue(): number | null {
let sum = 0, any = false;
for (const p of this.parts) {
const cost = typeof p.cost === "number" ? p.cost : null;
// Per unit: a package price spread over its contents (#98 follow-up).
const cost = unitPrice(p);
const stock = typeof p.stock === "number" ? p.stock : null;
if (cost !== null && stock !== null) { sum += cost * stock; any = true; }
}
@@ -475,6 +506,12 @@ export class MaintenancePartsSection extends LitElement {
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 8px 12px;
}
.form-hint {
grid-column: 1 / -1;
font-size: 12px;
color: var(--secondary-text-color);
line-height: 1.4;
}
.form-field {
display: flex;
flex-direction: column;
@@ -490,6 +527,25 @@ export class MaintenancePartsSection extends LitElement {
background: var(--card-background-color);
color: var(--primary-text-color);
}
.form-field.notes {
grid-column: 1 / -1;
}
.form-field textarea {
padding: 6px;
border: 1px solid var(--divider-color);
border-radius: 4px;
background: var(--card-background-color);
color: var(--primary-text-color);
font: inherit;
resize: vertical;
}
.part-notes {
font-size: 12px;
color: var(--secondary-text-color);
white-space: pre-line;
overflow-wrap: anywhere;
margin-top: 2px;
}
.form-field.checkbox {
flex-direction: row;
align-items: center;