113 files
This commit is contained in:
+5
-2
@@ -76,8 +76,11 @@ export class MaintenanceBudgetSectionCard extends LitElement {
|
||||
this._busy = true;
|
||||
this._error = "";
|
||||
try {
|
||||
const m = parseFloat(this._localMonthly);
|
||||
const y = parseFloat(this._localYearly);
|
||||
// An emptied field means "remove this budget" and must SEND 0 (the
|
||||
// backend's off-state) — omitting the key kept the old value, so a
|
||||
// budget could never be cleared from this card (bug audit 2026-08-22).
|
||||
const m = this._localMonthly.trim() === "" ? 0 : parseFloat(this._localMonthly);
|
||||
const y = this._localYearly.trim() === "" ? 0 : parseFloat(this._localYearly);
|
||||
const settings: Record<string, number> = {};
|
||||
if (!isNaN(m) && m >= 0) settings.budget_monthly = m;
|
||||
if (!isNaN(y) && y >= 0) settings.budget_yearly = y;
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ export class MaintenanceObjectQuickActionsDialog extends LitElement {
|
||||
private _onAddTask(): void {
|
||||
if (!this._entryId) return;
|
||||
import("../dialog-mount").then(({ openCreateTaskDialog }) => {
|
||||
openCreateTaskDialog();
|
||||
openCreateTaskDialog(this._entryId!);
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
+32
-6
@@ -16,6 +16,8 @@ import { LitElement, html, css, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
import { sharedStyles, t, STATUS_COLORS, formatDate, formatDateTime, formatInterval, formatRecurrence, langOf } from "../styles";
|
||||
import { describeWsError } from "../ws-errors";
|
||||
import { isoDateLocal } from "../helpers/calendar-bucket";
|
||||
import { partsForCompletion } from "../helpers/shared-parts";
|
||||
import { renderWeibullSection } from "../renderers/weibull";
|
||||
import { renderPredictionSection } from "../renderers/prediction";
|
||||
import { renderRecommendationBars } from "../renderers/recommendation";
|
||||
@@ -75,7 +77,9 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
|
||||
this._showReset = false;
|
||||
this._showAdaptive = false;
|
||||
this._skipReason = "";
|
||||
this._resetDate = new Date().toISOString().slice(0, 10);
|
||||
// Local calendar date — toISOString() is UTC and prefills YESTERDAY for
|
||||
// users east of UTC before their morning (bug audit 2026-08-22).
|
||||
this._resetDate = isoDateLocal(new Date());
|
||||
this._open = true;
|
||||
await Promise.all([this._loadTask(), this._loadFeatures()]);
|
||||
}
|
||||
@@ -146,14 +150,36 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
|
||||
|
||||
private _onComplete(): void {
|
||||
if (!this._entryId || !this._taskId || !this._task) return;
|
||||
// Reuse the existing rich complete-dialog by mounting it on body
|
||||
import("../dialog-mount").then(({ openCompleteDialog }) => {
|
||||
// Reuse the existing rich complete-dialog by mounting it on body.
|
||||
// Pass EVERYTHING the card's direct path passes — omitting
|
||||
// required_completion_fields let a mandatory note be skipped, and a
|
||||
// reading task without type+unit never rendered its value field
|
||||
// (bug audit 2026-08-22).
|
||||
import("../dialog-mount").then(async ({ openCompleteDialog }) => {
|
||||
const task = this._task!;
|
||||
const isBuy = !!(task as { part_ref?: string }).part_ref;
|
||||
let parts: Parameters<typeof openCompleteDialog>[0]["parts"] = [];
|
||||
if (!isBuy) {
|
||||
try {
|
||||
const r = await this.hass.connection.sendMessagePromise<{
|
||||
objects: MaintenanceObjectResponse[];
|
||||
}>({ type: "maintenance_supporter/objects", compact: true });
|
||||
parts = partsForCompletion(task, this._entryId!, r.objects || [], this._lang);
|
||||
} catch {
|
||||
// Parts stay empty — the dialog still completes without them.
|
||||
}
|
||||
}
|
||||
const ok = openCompleteDialog({
|
||||
entry_id: this._entryId!,
|
||||
task_id: this._taskId!,
|
||||
task_name: this._task!.name,
|
||||
checklist: this._task!.checklist || [],
|
||||
adaptive_enabled: !!this._task!.adaptive_config?.enabled,
|
||||
task_name: task.name,
|
||||
checklist: task.checklist || [],
|
||||
adaptive_enabled: !!task.adaptive_config?.enabled,
|
||||
required_completion_fields: task.required_completion_fields || [],
|
||||
task_type: task.type || "",
|
||||
reading_unit: (task as { reading_unit?: string }).reading_unit || "",
|
||||
parts,
|
||||
consumes_parts: isBuy ? [] : (task.consumes_parts || []),
|
||||
});
|
||||
if (ok) {
|
||||
this._notifyChanged("complete");
|
||||
|
||||
@@ -156,7 +156,14 @@ export class MaintenanceTriggerChart extends LitElement {
|
||||
}
|
||||
|
||||
const tsMin = pts[0].ts;
|
||||
const tsMax = pts[pts.length - 1].ts;
|
||||
// The dashed projection extends the TIME domain. sparkline.ts builds it
|
||||
// as [last sample, last sample + 30 d]; with a data-only domain its
|
||||
// start sat exactly on the right plot edge and the x2 clamp below
|
||||
// collapsed the line to zero width — the production degradation
|
||||
// projection never rendered (found via the design-system previews,
|
||||
// 2026-08-24).
|
||||
const projEnd = this.projection && this.projection.length === 2 ? this.projection[1].ts : null;
|
||||
const tsMax = projEnd != null ? Math.max(pts[pts.length - 1].ts, projEnd) : pts[pts.length - 1].ts;
|
||||
const tsSpan = tsMax - tsMin || 1;
|
||||
const withYear = needsYear(tsMin, tsMax);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user