187 files
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import { LitElement, html, css, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
import type { AdaptiveConfig, HomeAssistant, MaintenanceTask, TaskPartLink, TriggerConfig, HAUser } from "../types";
|
||||
import { formatDate, t, weekdayName, langOf } from "../styles";
|
||||
import { formatDate, t, weekdayName, monthName, langOf } from "../styles";
|
||||
import { UserService } from "../user-service";
|
||||
import { partLinkKey } from "../helpers/shared-parts";
|
||||
import {
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { describeWsError } from "../ws-errors";
|
||||
import { REQUIRED_COMPLETION_KEYS, REQUIRED_COMPLETION_LABELS } from "./required-completion-labels";
|
||||
import "./ms-textfield";
|
||||
import "./ms-date-field";
|
||||
|
||||
const MAINTENANCE_TYPE_KEYS = ["cleaning", "inspection", "replacement", "calibration", "service", "reading", "custom"];
|
||||
const PRIORITY_KEYS = ["low", "normal", "high"];
|
||||
@@ -129,8 +130,7 @@ function weekdayNames(lang?: string): string[] {
|
||||
|
||||
/** Short localized month names, indexed 0=Jan … 11=Dec. */
|
||||
function monthNames(lang?: string): string[] {
|
||||
const fmt = new Intl.DateTimeFormat(lang || "en", { month: "short" });
|
||||
return Array.from({ length: 12 }, (_, i) => fmt.format(new Date(2021, i, 1)));
|
||||
return Array.from({ length: 12 }, (_, i) => monthName(i, lang, "short"));
|
||||
}
|
||||
|
||||
export class MaintenanceTaskDialog extends LitElement {
|
||||
@@ -1190,6 +1190,10 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this._triggerType === "threshold" && this._thresholdLimitsOverlap()) {
|
||||
this._error = t("trigger_hint_overlap", this._lang);
|
||||
return;
|
||||
}
|
||||
this._loading = true;
|
||||
this._error = "";
|
||||
try {
|
||||
@@ -2204,12 +2208,14 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
@input=${(e: Event) => (this._endsCount = (e.target as HTMLInputElement).value)}
|
||||
></ms-textfield>` : nothing}
|
||||
${this._endsMode === "until" ? html`
|
||||
<ms-textfield
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
.hass=${this.hass}
|
||||
.lang=${L}
|
||||
label="${t("series_end_until_label", L)}"
|
||||
type="date"
|
||||
.value=${this._endsUntil}
|
||||
@input=${(e: Event) => (this._endsUntil = (e.target as HTMLInputElement).value)}
|
||||
></ms-textfield>` : nothing}
|
||||
@value-changed=${(e: CustomEvent) => (this._endsUntil = e.detail.value as string)}
|
||||
></ms-date-field>` : nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -2299,12 +2305,23 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
* sensor's current reading (not from zero) and restarts after each
|
||||
* completion. Renders nothing when there's no entity/state to read.
|
||||
*/
|
||||
/** #156: above/below are OR-ed server-side — `below > above` makes every
|
||||
* reading a hit, so the trigger never clears (and never auto-completes). */
|
||||
private _thresholdLimitsOverlap(): boolean {
|
||||
const above = parseFloat(this._triggerAbove);
|
||||
const below = parseFloat(this._triggerBelow);
|
||||
return !isNaN(above) && !isNaN(below) && below > above;
|
||||
}
|
||||
|
||||
private _renderTriggerLiveHint() {
|
||||
if (this._triggerType === "compound") return nothing;
|
||||
const overlap = this._triggerType === "threshold" && this._thresholdLimitsOverlap()
|
||||
? html`<div class="trigger-live-hint warn">${t("trigger_hint_overlap", this._lang)}</div>`
|
||||
: nothing;
|
||||
const entityId = this._triggerEntityId || this._triggerEntityIds[0];
|
||||
if (!entityId || !this.hass?.states) return nothing;
|
||||
if (!entityId || !this.hass?.states) return overlap;
|
||||
const st = this.hass.states[entityId];
|
||||
if (!st) return nothing;
|
||||
if (!st) return overlap;
|
||||
const L = this._lang;
|
||||
|
||||
const unitAttr = st.attributes?.unit_of_measurement;
|
||||
@@ -2362,8 +2379,8 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
);
|
||||
parts.push(t("trigger_hint_state_now", L).replace("{value}", String(st.state)));
|
||||
}
|
||||
if (!parts.length) return nothing;
|
||||
return html`<div class="trigger-live-hint">${parts.join(" ")}</div>`;
|
||||
if (!parts.length) return overlap;
|
||||
return html`<div class="trigger-live-hint">${parts.join(" ")}</div>${overlap}`;
|
||||
}
|
||||
|
||||
private _renderTriggerTypeFields() {
|
||||
@@ -2643,25 +2660,30 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
</select>
|
||||
</div>
|
||||
${this.scheduleTimeEnabled ? html`
|
||||
<ms-textfield
|
||||
<ms-date-field
|
||||
kind="time"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${L}
|
||||
label="${t("schedule_time_optional", L)}"
|
||||
type="time"
|
||||
.value=${this._scheduleTime}
|
||||
helper="${t("schedule_time_help", L)}"
|
||||
@input=${(e: Event) => (this._scheduleTime = (e.target as HTMLInputElement).value)}
|
||||
></ms-textfield>
|
||||
@value-changed=${(e: CustomEvent) => (this._scheduleTime = e.detail.value as string)}
|
||||
></ms-date-field>
|
||||
` : nothing}
|
||||
`
|
||||
: nothing}
|
||||
${this._renderCalendarFields()}
|
||||
${this._scheduleType === "one_time"
|
||||
? html`
|
||||
<ms-textfield
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
.hass=${this.hass}
|
||||
.lang=${L}
|
||||
label="${t("due_date", L)}"
|
||||
type="date"
|
||||
.value=${this._dueDate}
|
||||
@input=${(e: Event) => (this._dueDate = (e.target as HTMLInputElement).value)}
|
||||
></ms-textfield>
|
||||
@value-changed=${(e: CustomEvent) => (this._dueDate = e.detail.value as string)}
|
||||
></ms-date-field>
|
||||
`
|
||||
: nothing}
|
||||
${this._renderRecurrenceExtras()}
|
||||
@@ -2707,12 +2729,15 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
</label>
|
||||
`)}
|
||||
</div>
|
||||
<ms-textfield
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${L}
|
||||
label="${t("last_performed_optional", L)}"
|
||||
type="date"
|
||||
.value=${this._lastPerformed}
|
||||
@input=${(e: Event) => (this._lastPerformed = (e.target as HTMLInputElement).value)}
|
||||
></ms-textfield>
|
||||
@value-changed=${(e: CustomEvent) => (this._lastPerformed = e.detail.value as string)}
|
||||
></ms-date-field>
|
||||
<div class="select-row">
|
||||
<label>${t("responsible_user", L)}</label>
|
||||
<select
|
||||
@@ -3131,6 +3156,11 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
padding: 6px 10px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
.trigger-live-hint.warn {
|
||||
color: var(--primary-text-color);
|
||||
border-left-color: var(--warning-color, #ff9800);
|
||||
background: rgba(255, 152, 0, 0.1);
|
||||
}
|
||||
.field-help a,
|
||||
.link-button {
|
||||
background: none;
|
||||
|
||||
Reference in New Issue
Block a user