329 files

This commit is contained in:
Home Assistant Version Control
2026-08-06 13:56:25 +00:00
parent 0df89406fa
commit 7afe7add1d
330 changed files with 13098 additions and 5942 deletions
@@ -12,7 +12,7 @@
import { html, nothing } from "lit";
import { isSafeHttpUrl } from "../helpers/url";
import { t, formatDate, formatDateTime, formatRecurrence } from "../styles";
import type { AdvancedFeatures, HomeAssistant, MaintenanceTask } from "../types";
import type { AdvancedFeatures, HomeAssistant, MaintenanceTask, ManualDocRef } from "../types";
import { renderTriggerSection, type SparklineContext } from "./sparkline";
import { renderPredictionSection } from "./prediction";
import { renderWeibullSection } from "./weibull";
@@ -32,6 +32,13 @@ export interface TaskDetailContext {
objectName: string;
/** Parent object's documentation_url (raw; sanitised here). */
objectDocUrl: string | null | undefined;
/** Parent object's manual-tagged documents — the fallback for the manual
* row when documentation_url is empty (same rule as the objects table). */
objectManualDocs: ManualDocRef[];
/** Opens a manual document (signed path / weblink) — panel-owned. */
openManualDoc: (doc: ManualDocRef) => void;
/** #73: persist one checklist tick (panel sends the full state via WS). */
setChecklistItem: (item: string, done: boolean) => void;
isOperator: boolean;
actionLoading: boolean;
moreMenuOpen: boolean;
@@ -178,22 +185,38 @@ function collapsible(key: string, titleKey: string, body: unknown, ctx: TaskDeta
`;
}
/** Read-only preview of the configured checklist steps so users can see
* the steps without having to open the Edit or Complete dialog. Only
* rendered when the Checklists feature is enabled and steps are set. */
/** Interactive checklist (#73): steps can be ticked off DURING the cycle
* without completing the task — progress persists server-side (survives
* reloads, prefills the complete dialog) and resets when the task is
* completed or skipped. Only rendered when the Checklists feature is
* enabled and steps are set. */
function renderChecklistCard(task: MaintenanceTask, ctx: TaskDetailContext) {
if (!ctx.features.checklists) return nothing;
const items = task.checklist || [];
if (items.length === 0) return nothing;
const L = ctx.lang;
const progress = task.checklist_progress || {};
const done = items.filter((item) => progress[item]).length;
return html`
<div class="checklist-preview-card">
<div class="checklist-preview-header">
<ha-icon icon="mdi:format-list-checks"></ha-icon>
<span>${t("checklist", L)} (${items.length})</span>
<span>${t("checklist", L)} (${done}/${items.length})</span>
</div>
<ol class="checklist-preview-list">
${items.map((item) => html`<li>${item}</li>`)}
${items.map((item) => html`
<li class=${progress[item] ? "checked" : ""}>
<label>
<input
type="checkbox"
.checked=${!!progress[item]}
@change=${(e: Event) =>
ctx.setChecklistItem(item, (e.target as HTMLInputElement).checked)}
/>
<span>${item}</span>
</label>
</li>
`)}
</ol>
</div>
`;
@@ -206,7 +229,10 @@ function renderTaskMeta(task: MaintenanceTask, ctx: TaskDetailContext) {
const safeTaskUrl = isSafeHttpUrl(task.documentation_url)
? task.documentation_url : null;
const safeObjUrl = isSafeHttpUrl(ctx.objectDocUrl) ? ctx.objectDocUrl : null;
if (!task.notes && !safeTaskUrl && !safeObjUrl) return nothing;
// Same fallback rule as the objects table: an UPLOADED manual (category
// "manual") stands in when the object's URL field is empty.
const manualDoc = safeObjUrl ? null : (ctx.objectManualDocs || [])[0];
if (!task.notes && !safeTaskUrl && !safeObjUrl && !manualDoc) return nothing;
const L = ctx.lang;
return html`
<div class="task-meta-card">
@@ -227,6 +253,13 @@ function renderTaskMeta(task: MaintenanceTask, ctx: TaskDetailContext) {
<ha-icon icon="mdi:book-open-variant"></ha-icon>
<a href="${safeObjUrl}" target="_blank" rel="noopener noreferrer">${t("documentation_url_label", L)} (${ctx.objectName})</a>
</div>
` : manualDoc ? html`
<div class="task-meta-row task-meta-link">
<ha-icon icon="mdi:book-open-variant"></ha-icon>
<a href="#" title=${manualDoc.title}
@click=${(e: Event) => { e.preventDefault(); ctx.openManualDoc(manualDoc); }}
>${t("documentation_url_label", L)} (${ctx.objectName})</a>
</div>
` : nothing}
</div>
`;