155 files
This commit is contained in:
1 parent
a212615470
commit
0ff8c287a8
155 files changed
+19264
-3148
No files matched your search
@@ -4,9 +4,9 @@ import { LitElement, html, css, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
import type { HomeAssistant, ReadingSlot, TaskPartLink } from "../types";
|
||||
import { lastReadingBefore, type ReadingHistoryEntry } from "../helpers/reading-slots";
|
||||
import { t, nativeFieldStyles, formatCost, formatNumber, formatQty } from "../styles";
|
||||
import { t, nativeFieldStyles, formatCost, formatNumber, formatQty, haTimeZoneHint } from "../styles";
|
||||
import { runWs } from "../helpers/ws-run";
|
||||
import { isoMinuteLocal } from "../helpers/calendar-bucket";
|
||||
import { haNowMinute, stampMs } from "../helpers/ha-time";
|
||||
import { PART_QTY_RANGE, RESTOCK_QTY_RANGE } from "../helpers/setting-ranges";
|
||||
import { partLinkKey, type LinkedPart } from "../helpers/shared-parts";
|
||||
import { unitPrice, type PartsCostMode } from "../helpers/parts-cost";
|
||||
@@ -23,6 +23,15 @@ import { photoPickerStyles } from "./ms-photo-picker";
|
||||
* One TS source (helpers/setting-ranges), pinned to Python. */
|
||||
const USED_QTY_RANGE = PART_QTY_RANGE;
|
||||
|
||||
/** `task-completed`'s detail: which task, and whether the server remembered
|
||||
* the completion so the opener may offer Undo. */
|
||||
export interface TaskCompletedDetail {
|
||||
entryId: string;
|
||||
taskId: string;
|
||||
taskName: string;
|
||||
undo: boolean;
|
||||
}
|
||||
|
||||
export class MaintenanceCompleteDialog extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@property() public entryId = "";
|
||||
@@ -97,7 +106,8 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
/** #161 phase 2: typed text per slot id (parsed on save; "" = unread). */
|
||||
@state() private _readingValues: Record<string, string> = {};
|
||||
@state() private _restockQty = "";
|
||||
/** #133: optional backdated completion moment ("YYYY-MM-DDTHH:MM:SS" local; "" = now). */
|
||||
/** #133: optional backdated completion moment ("YYYY-MM-DDTHH:MM:SS" in
|
||||
* HA local time — zone-less, the way the backend reads it; "" = now). */
|
||||
@state() private _completedAt = "";
|
||||
/** Keyed by `partLinkKey` — the (entry_id, part_id) pair — because two
|
||||
* objects can carry the same part id, so part_id alone would merge pools. */
|
||||
@@ -203,8 +213,10 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
}
|
||||
if (this._completedAt) {
|
||||
// Client-side guard mirrors the backend rule — a picked future moment
|
||||
// fails fast with a localized message instead of a WS roundtrip.
|
||||
if (new Date(this._completedAt).getTime() > Date.now()) {
|
||||
// fails fast with a localized message instead of a WS roundtrip. The
|
||||
// value is HA local time, so it is judged on HA's clock: read in the
|
||||
// browser's zone, a moment just past was "future" east of HA.
|
||||
if (stampMs(this._completedAt) > Date.now()) {
|
||||
this._error = t("completed_at_future_error", this.lang);
|
||||
return;
|
||||
}
|
||||
@@ -270,14 +282,22 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
if (res === undefined) return;
|
||||
this._photos.markAttached(); // attached now — Cancel cleanup must not touch them
|
||||
this._open = false;
|
||||
this.dispatchEvent(new CustomEvent("task-completed"));
|
||||
// The opener offers Undo when the server remembered the completion for
|
||||
// this person (task/undo, ten minutes, one level).
|
||||
const detail: TaskCompletedDetail = {
|
||||
entryId: this.entryId,
|
||||
taskId: this.taskId,
|
||||
taskName: this.taskName,
|
||||
undo: (res as { undo?: boolean } | null)?.undo === true,
|
||||
};
|
||||
this.dispatchEvent(new CustomEvent<TaskCompletedDetail>("task-completed", { detail }));
|
||||
}
|
||||
|
||||
/** #161 phase 2: one field per slot with the previous value as a hint;
|
||||
* a value below the last one gets a warning (meters count up — a
|
||||
* replaced meter or a typo), never a block. */
|
||||
private _renderReadingField(slot: ReadingSlot, L: string) {
|
||||
const at = this._completedAt ? new Date(this._completedAt).getTime() : NaN;
|
||||
const at = this._completedAt ? stampMs(this._completedAt) : NaN;
|
||||
const last = lastReadingBefore(this.readingHistory, slot.id, isNaN(at) ? undefined : at);
|
||||
const unit = slot.unit || this.readingUnit;
|
||||
const raw = (this._readingValues[slot.id] ?? "").trim();
|
||||
@@ -386,9 +406,12 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
this._photos.discardOrphans();
|
||||
}
|
||||
|
||||
/** Seed the backdate field with the current minute (local, seconds zeroed). */
|
||||
/** Seed the backdate field with HA's current minute (seconds zeroed) —
|
||||
* the field means HA local time. The browser's minute was refused as
|
||||
* "future" east of HA and stored hours early west of it (next_due a day
|
||||
* early once that crossed midnight). */
|
||||
private _pickCompletedAt(): void {
|
||||
this._completedAt = isoMinuteLocal(new Date());
|
||||
this._completedAt = `${haNowMinute()}:00`;
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -525,6 +548,7 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.lang=${L}
|
||||
.value=${this._completedAt}
|
||||
.helper=${haTimeZoneHint(L)}
|
||||
@value-changed=${(e: CustomEvent) => (this._completedAt = e.detail.value as string)}
|
||||
></ms-date-field>`
|
||||
: html`<button type="button" class="backdate-pick" @click=${this._pickCompletedAt}>
|
||||
|
||||
Reference in new issue
Block a user