393 files

This commit is contained in:
Home Assistant Version Control committed 2026-09-27 17:16:54 +00:00
1 parent b05b7a897e
commit 39d97ac2db
404 files changed
+54006 -11034

No files matched your search

@@ -26,6 +26,24 @@ import { matchesQuery } from "../helpers/search-match";
* a different concept: what belongs in the object's record.) */
export const EDITABLE_HISTORY_TYPES: readonly HistoryEntryType[] = ["completed", "reset", "skipped"];
/** Newest first BY TIMESTAMP. The store appends, so array order is entry
* order — a backdated completion (#133) lands last although it happened
* before the ones above it, and every "reverse the array" view showed it
* as the newest (bug audit 2026-09-26). Stable: entries with the same (or
* an unparsable) timestamp keep "later appended = newer". */
export function newestFirst<T extends { timestamp?: string | null }>(history: readonly T[] | null | undefined): T[] {
const ms = (e: T): number => {
const raw = e.timestamp ?? "";
// Date-only strings are local days (like formatDate) — not UTC midnight.
const v = Date.parse(raw.length === 10 ? `${raw}T00:00:00` : raw);
return Number.isNaN(v) ? -Infinity : v;
};
return (history ?? [])
.map((entry, index) => ({ entry, index, at: ms(entry) }))
.sort((a, b) => (b.at - a.at) || (b.index - a.index))
.map((x) => x.entry);
}
/** What one history row needs — the panel's HistoryContext is a superset;
* the dialog and the overview card build just this. */
export interface HistoryEntryContext {
@@ -34,7 +52,10 @@ export interface HistoryEntryContext {
hass: HomeAssistant;
/** Currency symbol for cost display (defaults to €). */
currencySymbol: string;
openEdit: (entry: HistoryEntry) => void;
/** Opens the history-edit dialog. Absent = the viewer may not edit
* (task/history/update is WRITE tier): no pencil at all instead of one
* whose Save the server refuses (bug audit 2026-09-26 #2). */
openEdit?: (entry: HistoryEntry) => void;
/** v2.20 (#83): unit + delta-vs-previous for reading-task entries. */
readingUnit?: string | null;
readingDelta?: (entry: HistoryEntry) => number | null;
@@ -119,7 +140,7 @@ export function renderHistoryList(task: MaintenanceTask, ctx: HistoryContext) {
return html`
<div class="history-timeline">
${[...filtered].reverse().map((entry: HistoryEntry) => renderHistoryEntry(entry, ctx))}
${newestFirst(filtered).map((entry: HistoryEntry) => renderHistoryEntry(entry, ctx))}
</div>
`;
}
@@ -168,7 +189,8 @@ function renderHistoryReadings(entry: HistoryEntry, ctx: HistoryEntryContext) {
export function renderHistoryEntry(entry: HistoryEntry, ctx: HistoryEntryContext, opts: HistoryEntryOptions = {}) {
const L = ctx.lang;
const { compact = false, showRef = true, showBadges = true, showEdit = true } = opts;
const editable = showEdit && EDITABLE_HISTORY_TYPES.includes(entry.type);
const openEdit = ctx.openEdit;
const editable = showEdit && !!openEdit && EDITABLE_HISTORY_TYPES.includes(entry.type);
return html`
<div class="history-entry${compact ? " compact" : ""}">
${compact
@@ -185,7 +207,7 @@ export function renderHistoryEntry(entry: HistoryEntry, ctx: HistoryEntryContext
${editable
? html`<button class="history-edit-btn"
title=${t("history_edit_button", L)}
@click=${() => ctx.openEdit(entry)}>
@click=${() => openEdit!(entry)}>
<ha-icon icon="mdi:pencil"></ha-icon>
</button>`
: nothing}