331 files
This commit is contained in:
@@ -231,7 +231,7 @@
|
||||
},
|
||||
{
|
||||
"id": "449ff43140654e4bbe2c7b2d361dfacc",
|
||||
"url": "/hacsfiles/clock-weather-card/clock-weather-card.js?hacstag=522634019295",
|
||||
"url": "/hacsfiles/clock-weather-card/clock-weather-card.js?hacstag=522634019296",
|
||||
"type": "module"
|
||||
},
|
||||
{
|
||||
@@ -261,7 +261,7 @@
|
||||
},
|
||||
{
|
||||
"id": "6c3f7d87683d4473b5dc8fe8177535f6",
|
||||
"url": "/hacsfiles/simple-thermostat/simple-thermostat.js?hacstag=1230152807450",
|
||||
"url": "/hacsfiles/simple-thermostat/simple-thermostat.js?hacstag=1230152807451",
|
||||
"type": "module"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -71,6 +71,7 @@ from .const import (
|
||||
MAX_NAME_LENGTH,
|
||||
MAX_TEXT_LENGTH,
|
||||
MAX_TYPE_LENGTH,
|
||||
MIN_COST,
|
||||
PLATFORMS,
|
||||
SERVICE_ADD_OBJECT,
|
||||
SERVICE_ADD_TASK,
|
||||
@@ -146,7 +147,7 @@ SERVICE_COMPLETE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_ENTITY_ID): vol.All(cv.entity_ids, vol.Length(min=1)),
|
||||
vol.Optional("notes"): vol.All(cv.string, vol.Length(max=2000)),
|
||||
vol.Optional("cost"): vol.All(vol.Coerce(float), vol.Range(min=0, max=MAX_COST)),
|
||||
vol.Optional("cost"): vol.All(vol.Coerce(float), vol.Range(min=MIN_COST, max=MAX_COST)),
|
||||
vol.Optional("duration"): vol.All(vol.Coerce(int), vol.Range(min=0, max=MAX_DURATION_MINUTES)),
|
||||
# Meter readings (v2.20, #83): recorded value for `reading` tasks.
|
||||
vol.Optional("reading_value"): vol.All(vol.Coerce(float), vol.Range(min=-1e12, max=1e12)),
|
||||
|
||||
@@ -856,6 +856,9 @@ MAX_PANEL_TITLE_LENGTH = 50 # sidebar panel title override
|
||||
# config-flow-path sanitiser (they must agree, else a value the sanitiser
|
||||
# accepts can bypass the WS cap or vice-versa).
|
||||
MAX_COST = 1_000_000 # per completion / history entry
|
||||
# #200: a credit — the old unit sold, a refund — is a negative cost; every
|
||||
# total nets it (budget, task, object, area).
|
||||
MIN_COST = -MAX_COST
|
||||
MAX_DURATION_MINUTES = 525_600 # one year in minutes
|
||||
MAX_TYPE_LENGTH = 50 # task_type, schedule_type
|
||||
MAX_CHECKLIST_ITEMS = 100
|
||||
|
||||
@@ -112,6 +112,7 @@ def _inert_task_result(task: MaintenanceTask, status: str, **extra: Any) -> dict
|
||||
task_result["_trigger_active"] = False
|
||||
task_result["_times_performed"] = task.times_performed
|
||||
task_result["_total_cost"] = task.total_cost
|
||||
task_result["_average_cost"] = task.average_cost
|
||||
task_result["_average_duration"] = task.average_duration
|
||||
task_result["_last_entry"] = task.last_entry
|
||||
task_result.update(extra)
|
||||
@@ -526,6 +527,7 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
task_result["_trigger_current_delta"] = current - baseline
|
||||
task_result["_times_performed"] = task.times_performed
|
||||
task_result["_total_cost"] = task.total_cost
|
||||
task_result["_average_cost"] = task.average_cost
|
||||
task_result["_average_duration"] = task.average_duration
|
||||
task_result["_last_entry"] = task.last_entry
|
||||
|
||||
|
||||
+48
-6
@@ -47,6 +47,21 @@ function setInput(el: MaintenanceCompleteDialog, index: number, value: string) {
|
||||
input.dispatchEvent(new Event("input"));
|
||||
}
|
||||
|
||||
/** The cost has its own element (#200: Cost | Credit and the amount). */
|
||||
function costInput(el: MaintenanceCompleteDialog) {
|
||||
return el.shadowRoot!.querySelector("ms-cost-input")! as HTMLElement & { value: string; updateComplete: Promise<unknown> };
|
||||
}
|
||||
|
||||
function costAmount(el: MaintenanceCompleteDialog): HTMLInputElement {
|
||||
return costInput(el).shadowRoot!.querySelector<HTMLInputElement>("input.amount")!;
|
||||
}
|
||||
|
||||
function setCost(el: MaintenanceCompleteDialog, value: string) {
|
||||
const input = costAmount(el);
|
||||
input.value = value;
|
||||
input.dispatchEvent(new Event("input"));
|
||||
}
|
||||
|
||||
function clickComplete(el: MaintenanceCompleteDialog) {
|
||||
const buttons = [...el.shadowRoot!.querySelectorAll(".dialog-actions ha-button")];
|
||||
(buttons[buttons.length - 1] as HTMLElement).click();
|
||||
@@ -60,8 +75,8 @@ describe("complete-dialog", () => {
|
||||
});
|
||||
|
||||
setInput(el, 0, "oil changed");
|
||||
setInput(el, 1, "12.5");
|
||||
setInput(el, 2, "30");
|
||||
setCost(el, "12.5");
|
||||
setInput(el, 1, "30");
|
||||
// Tick the second checklist step (click the checkbox; the event bubbles
|
||||
// to the row's toggle handler exactly once).
|
||||
const boxes = [...el.shadowRoot!.querySelectorAll<HTMLInputElement>(".checklist-item input")];
|
||||
@@ -105,6 +120,34 @@ describe("complete-dialog", () => {
|
||||
expect("completed_at" in msg).to.be.false;
|
||||
});
|
||||
|
||||
it("a credit goes out as a negative cost (#200)", async () => {
|
||||
// The old unit sold for 150: Credit, then the amount — a phone keypad
|
||||
// has no minus key, so the switch carries the sign.
|
||||
const { el, sent } = await mount();
|
||||
const cost = costInput(el);
|
||||
await cost.updateComplete;
|
||||
cost.shadowRoot!.querySelector<HTMLButtonElement>(".kind.credit")!.click();
|
||||
await cost.updateComplete;
|
||||
expect(cost.shadowRoot!.querySelector(".hint")!.textContent).to.include("Money back");
|
||||
setCost(el, "150");
|
||||
clickComplete(el);
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
const msg = sent.find((m) => m.type === "maintenance_supporter/task/complete")!;
|
||||
expect(msg.cost).to.equal(-150);
|
||||
});
|
||||
|
||||
it("notes may run over several lines (#202): a text area, sent trimmed, no Markdown promise", async () => {
|
||||
const { el, sent } = await mount();
|
||||
expect(el.shadowRoot!.querySelector("textarea.field-input"), "the notes field is a text area").to.exist;
|
||||
// The history and the printouts show notes as plain text.
|
||||
expect(el.shadowRoot!.textContent).not.to.include("Markdown");
|
||||
setInput(el, 0, "Filter changed\nSeal checked\n");
|
||||
clickComplete(el);
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
const msg = sent.find((m) => m.type === "maintenance_supporter/task/complete")!;
|
||||
expect(msg.notes, "the line break inside stays, the one at the end goes").to.equal("Filter changed\nSeal checked");
|
||||
});
|
||||
|
||||
/** The optional backdate starts as a "Set date & time" button (#163): the
|
||||
* HA datetime selector has no empty state, so the field only appears once
|
||||
* the user asks for it, seeded with the current minute. */
|
||||
@@ -396,10 +439,9 @@ describe("complete-dialog parts cost (#104)", () => {
|
||||
expect(chip(el)!.textContent).to.include("18");
|
||||
chip(el)!.click();
|
||||
await el.updateComplete;
|
||||
// Buy task: restock qty, notes, cost.
|
||||
const cost = [...el.shadowRoot!.querySelectorAll<HTMLInputElement>(".field-input")][2];
|
||||
await costInput(el).updateComplete;
|
||||
// Machine value for the <input type="number"> — never profile-formatted.
|
||||
expect(cost.value).to.equal("18");
|
||||
expect(costAmount(el).value).to.equal("18");
|
||||
expect(chip(el), "chip hides once cost is set").to.equal(null);
|
||||
});
|
||||
|
||||
@@ -423,7 +465,7 @@ describe("complete-dialog parts cost (#104)", () => {
|
||||
it("buy task: no chip once the user typed a cost themselves", async () => {
|
||||
const el = await mountWithParts({ restockDefault: 1, restockUnitCost: 5 });
|
||||
expect(chip(el)).to.exist;
|
||||
setInput(el, 2, "3.10");
|
||||
setCost(el, "3.10");
|
||||
await el.updateComplete;
|
||||
expect(chip(el)).to.equal(null);
|
||||
});
|
||||
|
||||
+4
-2
@@ -128,8 +128,10 @@ describe("task-dialog completion-actions sections", () => {
|
||||
"ms-textfield",
|
||||
);
|
||||
expect(qcInputs[0]?.value, "qc notes").to.equal("Quick note");
|
||||
expect(qcInputs[1]?.value, "qc cost").to.equal("4.5");
|
||||
expect(qcInputs[2]?.value, "qc duration").to.equal("10");
|
||||
// The cost has its own element (#200: Cost | Credit and the amount).
|
||||
const qcCost = sections[1].querySelector<HTMLElement & { value: string }>("ms-cost-input");
|
||||
expect(qcCost?.value, "qc cost").to.equal("4.5");
|
||||
expect(qcInputs[1]?.value, "qc duration").to.equal("10");
|
||||
|
||||
const qcSelect = sections[1].querySelector<HTMLSelectElement>("select.qc-feedback");
|
||||
expect(qcSelect?.value, "qc feedback").to.equal("needed");
|
||||
|
||||
+42
-13
@@ -444,7 +444,9 @@ export class MaintenanceBatteryFleetSection extends LitElement {
|
||||
: b.no_sensor
|
||||
? html`<span class="bf-offline bf-nosensor">${t("battery_fleet_no_sensor", L)}</span>`
|
||||
: nothing;
|
||||
const type = html`<span class="bf-type">${b.quantity}× ${b.battery_type}</span>`;
|
||||
// The full type as a tooltip: a long one ends in an ellipsis (D#162).
|
||||
const typeText = `${b.quantity}× ${b.battery_type}`;
|
||||
const type = html`<span class="bf-type" title=${typeText}>${typeText}</span>`;
|
||||
const writer = this._canWrite;
|
||||
const showMark = writer && (o.mark === "always" || b.no_sensor || b.can_mark_replaced);
|
||||
return html`
|
||||
@@ -659,7 +661,10 @@ export class MaintenanceBatteryFleetSection extends LitElement {
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
/* The query container of the row layout (see "In a narrow list" below):
|
||||
* the list's own width, not the screen's. */
|
||||
.bf-card {
|
||||
container: bf-fleet / inline-size;
|
||||
background: var(--card-background-color, #fff);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 10px;
|
||||
@@ -817,9 +822,16 @@ export class MaintenanceBatteryFleetSection extends LitElement {
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
/* Every row shares the list's columns, so ONE long type ("1× BATTERY
|
||||
* PACK") widened the type column of all of them (D#162): it stops at
|
||||
* 12em and ends in an ellipsis, the full text is the tooltip. */
|
||||
.bf-type {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 13px;
|
||||
max-width: 12em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.bf-recharge {
|
||||
color: var(--secondary-text-color);
|
||||
@@ -835,18 +847,37 @@ export class MaintenanceBatteryFleetSection extends LitElement {
|
||||
flex: 0 0 auto;
|
||||
cursor: help;
|
||||
}
|
||||
/* On phones the row cannot fit name + chips + curve + bar + date in ONE
|
||||
* line: the decorations yield (the percentage still carries the number)
|
||||
* and the row wraps to two lines - the name spans the full width, the
|
||||
* status chip moves under it (left, into the name column) and the rest
|
||||
* keeps its pinned subgrid column, so type / percentage / date / eye
|
||||
* stay aligned across rows. Without this the fixed max-content columns
|
||||
* overflowed 400 px and the chips overlapped the wrapped names. */
|
||||
@media (max-width: 640px) {
|
||||
/* In a narrow list the row cannot fit name + chips + curve + bar + date
|
||||
* in ONE line: the decorations yield (the percentage still carries the
|
||||
* number) and the row wraps to two lines - the name spans the full
|
||||
* width, the status chip moves under it (left, into the name column)
|
||||
* and the rest keeps its pinned subgrid column, so type / percentage /
|
||||
* date / eye stay aligned across rows. Without this the fixed
|
||||
* max-content columns overflowed 400 px and the chips overlapped the
|
||||
* wrapped names.
|
||||
*
|
||||
* The LIST's width decides, not the screen's (D#162): the Lovelace card
|
||||
* sits in a dashboard column of 400-500 px on a wide screen, kept the
|
||||
* one-line layout there and cut off the badge, the date and the hide
|
||||
* button. 680 px of list is where the one-line rows stop fitting;
|
||||
* desktop task pages (700 px and more) keep them. */
|
||||
@container bf-fleet (max-width: 680px) {
|
||||
.bf-spark,
|
||||
.bf-bar {
|
||||
display: none;
|
||||
}
|
||||
/* No fixed gaps: a column a phone leaves empty (status chip moved,
|
||||
* curve and bar hidden) cost its 8 px gap all the same, and together
|
||||
* with one long type the rows ran 25 px past a 402 px phone. Each
|
||||
* occupied column brings its own 6 px instead, and the type column
|
||||
* may shrink - its text ends in an ellipsis. */
|
||||
.bf-rows {
|
||||
column-gap: 0;
|
||||
grid-template-columns: minmax(0, 1fr) max-content minmax(0, max-content) repeat(7, max-content);
|
||||
}
|
||||
.bf-row > :not(.bf-dev) {
|
||||
margin-inline-start: 6px;
|
||||
}
|
||||
.bf-row {
|
||||
row-gap: 2px;
|
||||
}
|
||||
@@ -864,12 +895,10 @@ export class MaintenanceBatteryFleetSection extends LitElement {
|
||||
grid-row: 1;
|
||||
justify-self: end;
|
||||
}
|
||||
/* The column shrinks, the ellipsis comes from the base rule (a cap in
|
||||
* vw measured the screen, not the list). */
|
||||
.bf-type {
|
||||
grid-row: 2;
|
||||
max-width: 44vw;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.bf-recharge,
|
||||
.bf-level,
|
||||
|
||||
+28
-14
@@ -14,6 +14,7 @@ import { REQUIRED_COMPLETION_LABELS } from "./required-completion-labels";
|
||||
import { PhotoUploadController } from "../helpers/photo-upload-controller";
|
||||
import { parseDurationMinutes } from "../helpers/duration";
|
||||
import "./ms-date-field";
|
||||
import "./ms-cost-input";
|
||||
import "./ms-photo-picker";
|
||||
import { photoPickerStyles } from "./ms-photo-picker";
|
||||
|
||||
@@ -173,10 +174,14 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
entry_id: this.entryId,
|
||||
task_id: this.taskId,
|
||||
};
|
||||
if (this._notes) data.notes = this._notes;
|
||||
// Trimmed like the required check reads it: a text area easily keeps a
|
||||
// trailing line break, which the history would show as an empty line.
|
||||
const notes = this._notes.trim();
|
||||
if (notes) data.notes = notes;
|
||||
if (this._cost) {
|
||||
// #200: a credit is negative; the server holds the bounds.
|
||||
const cost = parseFloat(this._cost);
|
||||
if (!isNaN(cost) && cost >= 0) data.cost = cost;
|
||||
if (!isNaN(cost)) data.cost = cost;
|
||||
}
|
||||
// Whole minutes — the server coerces to int (helpers/duration).
|
||||
const dur = parseDurationMinutes(this._duration);
|
||||
@@ -309,9 +314,15 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
return this.requiredFields.filter((f) => !filled[f]);
|
||||
}
|
||||
|
||||
/** Does the task demand this detail? Its label then drops "(optional)" —
|
||||
* "Notes (optional) *" contradicted itself. */
|
||||
private _demands(field: string): boolean {
|
||||
return this.requiredFields.includes(field);
|
||||
}
|
||||
|
||||
/** Marker appended to a required field's label. */
|
||||
private _req(field: string) {
|
||||
return this.requiredFields.includes(field) ? html`<span class="req-mark" aria-hidden="true">*</span>` : nothing;
|
||||
return this._demands(field) ? html`<span class="req-mark" aria-hidden="true">*</span>` : nothing;
|
||||
}
|
||||
|
||||
/** A buy task's price: restock qty (packages) × the part's price per
|
||||
@@ -485,20 +496,22 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
only sees the title + Cancel/Complete buttons — the original
|
||||
bug report. Native inputs always render. -->
|
||||
<label class="field">
|
||||
<span class="field-label">${t("notes_optional", L)}${this._req("notes")}</span>
|
||||
<input type="text" class="field-input"
|
||||
<span class="field-label">${this._demands("notes") ? t("notes_label", L) : t("notes_optional", L)}${this._req("notes")}</span>
|
||||
<!-- Several lines (#202): what was done, one line each. The
|
||||
history and the printouts keep the line breaks; they show
|
||||
notes as plain text, so no Markdown hint here. -->
|
||||
<textarea class="field-input notes-input" rows="3"
|
||||
.value=${this._notes}
|
||||
@input=${(e: Event) => (this._notes = (e.target as HTMLInputElement).value)} />
|
||||
@input=${(e: Event) => (this._notes = (e.target as HTMLTextAreaElement).value)}></textarea>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span class="field-label">${t("cost_optional", L)}${this._req("cost")}</span>
|
||||
<input type="number" step="0.01" min="0" class="field-input"
|
||||
.value=${this._cost}
|
||||
@input=${(e: Event) => (this._cost = (e.target as HTMLInputElement).value)} />
|
||||
<div class="field">
|
||||
<span class="field-label">${this._demands("cost") ? t("cost", L) : t("cost_optional", L)}${this._req("cost")}</span>
|
||||
<ms-cost-input .lang=${L} .value=${this._cost}
|
||||
@value-changed=${(e: CustomEvent<{ value: string }>) => (this._cost = e.detail.value)}></ms-cost-input>
|
||||
${this._renderCostSuggestion(L)}
|
||||
</label>
|
||||
</div>
|
||||
<label class="field">
|
||||
<span class="field-label">${t("duration_minutes", L)}${this._req("duration")}</span>
|
||||
<span class="field-label">${this._demands("duration") ? t("quick_complete_defaults_duration", L) : t("duration_minutes", L)}${this._req("duration")}</span>
|
||||
<input type="number" step="1" min="0" inputmode="numeric" class="field-input"
|
||||
.value=${this._duration}
|
||||
@input=${(e: Event) => (this._duration = (e.target as HTMLInputElement).value)} />
|
||||
@@ -519,7 +532,7 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
</button>`}
|
||||
</div>
|
||||
<div class="field">
|
||||
<span class="field-label">${t("completion_photos_optional", L)}${this._req("photo")}</span>
|
||||
<span class="field-label">${this._demands("photo") ? t("completion_photos", L) : t("completion_photos_optional", L)}${this._req("photo")}</span>
|
||||
${this._photos.photos.length > 0
|
||||
? html`<div class="photo-strip">
|
||||
${this._photos.photos.map((p) => html`
|
||||
@@ -582,6 +595,7 @@ export class MaintenanceCompleteDialog extends LitElement {
|
||||
margin-left: 2px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.notes-input { resize: vertical; line-height: 1.4; }
|
||||
/* #104: one-click cost suggestion from parts — quiet link-style chip. */
|
||||
.cost-note {
|
||||
font-size: 12px;
|
||||
|
||||
@@ -106,8 +106,10 @@ export class MaintenanceGroupDialog extends LitElement {
|
||||
const L = this._lang;
|
||||
const title = this._groupId ? t("edit_group", L) : t("new_group", L);
|
||||
|
||||
// Our own title element: Home Assistant's dialog draws no `heading`.
|
||||
return html`
|
||||
<ha-dialog open @closed=${this._close} heading="${title}">
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${title}</div>
|
||||
<div class="content">
|
||||
${this._error ? html`<div class="error">${this._error}</div>` : nothing}
|
||||
<ms-textfield
|
||||
@@ -169,6 +171,7 @@ export class MaintenanceGroupDialog extends LitElement {
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.dialog-title { font-size: 18px; font-weight: 500; padding-bottom: 12px; }
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
+9
-7
@@ -18,6 +18,7 @@ import { PhotoUploadController } from "../helpers/photo-upload-controller";
|
||||
import { parseDurationMinutes } from "../helpers/duration";
|
||||
import { confirmAction } from "../helpers/confirm";
|
||||
import "./ms-date-field";
|
||||
import "./ms-cost-input";
|
||||
import "./history-photo";
|
||||
import "./ms-photo-picker";
|
||||
import { photoPickerStyles } from "./ms-photo-picker";
|
||||
@@ -404,15 +405,14 @@ export class MaintenanceHistoryEditDialog extends LitElement {
|
||||
.value=${d.notes ?? ""}></textarea>
|
||||
</label>
|
||||
<div class="row">
|
||||
<label>
|
||||
<div class="cost-field">
|
||||
<span>${t("cost", L)}</span>
|
||||
<input type="number" min="0" step="0.01"
|
||||
.value=${d.cost != null ? String(d.cost) : ""}
|
||||
@input=${(e: Event) => {
|
||||
const v = (e.target as HTMLInputElement).value;
|
||||
<ms-cost-input .lang=${L} .value=${d.cost != null ? String(d.cost) : ""}
|
||||
@value-changed=${(e: CustomEvent<{ value: string }>) => {
|
||||
const v = e.detail.value;
|
||||
this._set("cost", v ? Number(v) : null);
|
||||
}} />
|
||||
</label>
|
||||
}}></ms-cost-input>
|
||||
</div>
|
||||
<label>
|
||||
<span>${t("duration", L)}</span>
|
||||
<input type="number" min="0" step="1" inputmode="numeric"
|
||||
@@ -531,6 +531,8 @@ export class MaintenanceHistoryEditDialog extends LitElement {
|
||||
}
|
||||
label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
|
||||
label span { color: var(--secondary-text-color); }
|
||||
.cost-field { display: flex; flex-direction: column; gap: 4px; font-size: 13px; min-width: 0; }
|
||||
.cost-field > span { color: var(--secondary-text-color); }
|
||||
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||
input, textarea {
|
||||
padding: 8px; font-size: 14px;
|
||||
|
||||
+4
-1
@@ -100,8 +100,10 @@ export class SeasonalOverridesDialog extends LitElement {
|
||||
render() {
|
||||
if (!this._open) return html``;
|
||||
const L = this._lang;
|
||||
// Our own title element: Home Assistant's dialog draws no `heading`.
|
||||
return html`
|
||||
<ha-dialog open @closed=${this._close} heading="${t("seasonal_overrides_title", L)}">
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${t("seasonal_overrides_title", L)}</div>
|
||||
<div class="content">
|
||||
<p class="hint">${t("seasonal_overrides_hint", L)}</p>
|
||||
${this._error ? html`<div class="error">${this._error}</div>` : nothing}
|
||||
@@ -138,6 +140,7 @@ export class SeasonalOverridesDialog extends LitElement {
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.dialog-title { font-size: 18px; font-weight: 500; padding-bottom: 12px; }
|
||||
.content {
|
||||
min-width: 320px;
|
||||
max-width: 480px;
|
||||
|
||||
@@ -1240,6 +1240,14 @@ export class MaintenanceSettingsView extends LitElement {
|
||||
<span class="setting-label">${t("settings_max_per_day", L)}</span>
|
||||
${this._intSetting("max_notifications_per_day", n.max_per_day)}
|
||||
</label>
|
||||
<!-- Every Snooze uses it — the task menu, a card, voice, the phone's
|
||||
button — so it is not tied to the Snooze action button (D#193:
|
||||
it only showed while that button was on). -->
|
||||
<label class="setting-row snooze-duration">
|
||||
<span class="setting-label">${t("settings_snooze_hours", L)}</span>
|
||||
${this._intSetting("snooze_duration_hours", a.snooze_duration_hours)}
|
||||
</label>
|
||||
<div class="setting-hint">${t("settings_snooze_hours_hint", L)}</div>
|
||||
|
||||
<label class="setting-row">
|
||||
<span class="setting-label">${t("settings_bundling", L)}</span>
|
||||
@@ -1336,12 +1344,6 @@ export class MaintenanceSettingsView extends LitElement {
|
||||
<input type="checkbox" .checked=${a.snooze_enabled}
|
||||
@change=${(e: Event) => this._updateSetting("action_snooze_enabled", (e.target as HTMLInputElement).checked)} />
|
||||
</label>
|
||||
${a.snooze_enabled ? html`
|
||||
<label class="setting-row sub-row">
|
||||
<span class="setting-desc">${t("settings_snooze_hours", L)}</span>
|
||||
${this._intSetting("snooze_duration_hours", a.snooze_duration_hours)}
|
||||
</label>
|
||||
` : nothing}
|
||||
<label class="setting-row">
|
||||
<span class="setting-label">${t("settings_weekly_digest", L)}</span>
|
||||
<input type="checkbox" .checked=${a.weekly_digest_enabled}
|
||||
|
||||
@@ -29,6 +29,7 @@ import { REQUIRED_COMPLETION_KEYS, REQUIRED_COMPLETION_LABELS } from "./required
|
||||
import "./ms-textfield";
|
||||
import { defaultNotifyIcon } from "../helpers/notify-icons";
|
||||
import "./ms-date-field";
|
||||
import "./ms-cost-input";
|
||||
|
||||
const MAINTENANCE_TYPE_KEYS = ["cleaning", "inspection", "replacement", "calibration", "service", "reading", "custom"];
|
||||
const PRIORITY_KEYS = ["low", "normal", "high"];
|
||||
@@ -1034,12 +1035,11 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
.value=${this._qcNotes}
|
||||
@input=${(e: Event) => { this._qcNotes = (e.target as HTMLInputElement).value; }}
|
||||
></ms-textfield>
|
||||
<ms-textfield
|
||||
label="${t("quick_complete_defaults_cost", L)}"
|
||||
type="number" min="0" step="0.01"
|
||||
.value=${this._qcCost}
|
||||
@input=${(e: Event) => { this._qcCost = (e.target as HTMLInputElement).value; }}
|
||||
></ms-textfield>
|
||||
<div class="qc-cost">
|
||||
<span class="qc-cost-label">${t("quick_complete_defaults_cost", L)}</span>
|
||||
<ms-cost-input .lang=${L} .value=${this._qcCost}
|
||||
@value-changed=${(e: CustomEvent<{ value: string }>) => { this._qcCost = e.detail.value; }}></ms-cost-input>
|
||||
</div>
|
||||
<ms-textfield
|
||||
label="${t("quick_complete_defaults_duration", L)}"
|
||||
type="number" min="0" step="1"
|
||||
@@ -1788,7 +1788,7 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
const qcd: Record<string, unknown> = {};
|
||||
if (this._qcNotes.trim()) qcd.notes = this._qcNotes.trim();
|
||||
const cost = parseFloat(this._qcCost);
|
||||
if (!isNaN(cost) && cost >= 0) qcd.cost = cost;
|
||||
if (!isNaN(cost)) qcd.cost = cost;
|
||||
const dur = parseDurationMinutes(this._qcDuration);
|
||||
if (dur !== null) qcd.duration = dur;
|
||||
if (this._qcFeedback) qcd.feedback = this._qcFeedback;
|
||||
@@ -3588,6 +3588,8 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
font-weight: 500;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.qc-cost { display: flex; flex-direction: column; gap: 4px; margin: 8px 0; }
|
||||
.qc-cost-label { font-size: 12px; color: var(--secondary-text-color); }
|
||||
.field-help {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
|
||||
@@ -106,11 +106,12 @@ function addDaysIso(iso: string, days: number): string {
|
||||
/** Average recorded cost across history rows that HAVE a cost (mean of
|
||||
* non-null `cost` values). Note this differs from the panel KPI, which divides
|
||||
* total_cost by times_performed — completions without a recorded cost are
|
||||
* counted differently. This mean is used only for the calendar tooltip. */
|
||||
* counted differently. This mean is used only for the calendar tooltip — what
|
||||
* the next occurrence will likely cost, so a credit (#200) stays out. */
|
||||
function computeAvgCost(history: ReadonlyArray<HistoryEntry> | undefined): number | null {
|
||||
if (!history || history.length === 0) return null;
|
||||
// What each entry counts (#104), not only the typed cost.
|
||||
const costs = history.map((h) => entrySpend(h)).filter((c): c is number => c !== null);
|
||||
const costs = history.map((h) => entrySpend(h)).filter((c): c is number => c !== null && c >= 0);
|
||||
if (costs.length === 0) return null;
|
||||
return costs.reduce((a, b) => a + b, 0) / costs.length;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Smazat {n} objektů včetně všech jejich úkolů? Archivace místo toho zachová historii.",
|
||||
"bulk_objects_deleted": "Smazáno objektů: {n}",
|
||||
"bulk_objects_archived": "Archivováno objektů: {n}",
|
||||
"bulk_assign": "Přiřadit",
|
||||
"bulk_assign_one": "Jedna osoba",
|
||||
"bulk_assign_rotate": "Více lidí, střídavě",
|
||||
"bulk_assign_hint": "Nahradí přiřazení vybraných úkolů — včetně rotace.",
|
||||
"bulk_rotation_min_two": "Vyberte alespoň dvě osoby.",
|
||||
"bulk_labels_add": "Přidat štítky",
|
||||
"bulk_labels_remove": "Odebrat štítky",
|
||||
"bulk_edit_hint": "Změní se jen zaškrtnutá nastavení, vše ostatní zůstane beze změny.",
|
||||
"bulk_no_notifications": "Bez oznámení pro tyto úkoly",
|
||||
"bulk_updated": "Aktualizováno {n} úkolů",
|
||||
"bulk_paused": "Pozastaveno {n} úkolů",
|
||||
"bulk_resumed": "Obnoveno {n} úkolů",
|
||||
"bulk_objects_area": "Oblast nastavena pro objekty: {n}",
|
||||
"bulk_failed": "Nezdařilo se: {n} — {reason}",
|
||||
"completing": "Dokončování…",
|
||||
"interval": "Interval",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Vlastní",
|
||||
"history": "Historie",
|
||||
"cost": "Náklady",
|
||||
"cost_kind_credit": "Dobropis",
|
||||
"cost_credit_hint": "Peníze zpět — prodej, vrácení. Snižuje součty; průměr na dokončení ho nezapočítává.",
|
||||
"report_button": "Zpráva",
|
||||
"report_title": "Zpráva o údržbě",
|
||||
"report_generated": "Vytvořeno",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dny před vypršením",
|
||||
"settings_warranty_reminder_hint": "Upozornit jednou, když do vypršení záruky objektu zbývá tolik dní.",
|
||||
"settings_snooze_hours": "Doba odložení (hodiny)",
|
||||
"settings_snooze_hours_hint": "Jak dlouho „Odložit“ ztiší připomínky úkolu — z nabídky úkolu, z karty, hlasem nebo tlačítkem Odložit v telefonu. Termín zůstává beze změny.",
|
||||
"settings_budget": "Rozpočet",
|
||||
"settings_currency": "Měna",
|
||||
"settings_currency_decimals": "Desetinná místa částek",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Slet {n} objekter og alle deres opgaver? Arkivering bevarer i stedet historikken.",
|
||||
"bulk_objects_deleted": "{n} objekter slettet",
|
||||
"bulk_objects_archived": "{n} objekter arkiveret",
|
||||
"bulk_assign": "Tildel",
|
||||
"bulk_assign_one": "Én person",
|
||||
"bulk_assign_rotate": "Flere personer, på skift",
|
||||
"bulk_assign_hint": "Erstatter, hvem de valgte opgaver er tildelt — også en rotation.",
|
||||
"bulk_rotation_min_two": "Vælg mindst to personer.",
|
||||
"bulk_labels_add": "Tilføj etiketter",
|
||||
"bulk_labels_remove": "Fjern etiketter",
|
||||
"bulk_edit_hint": "Kun de afkrydsede indstillinger ændres; alt andet forbliver som det er.",
|
||||
"bulk_no_notifications": "Ingen notifikationer for disse opgaver",
|
||||
"bulk_updated": "{n} opgaver opdateret",
|
||||
"bulk_paused": "{n} opgaver sat på pause",
|
||||
"bulk_resumed": "{n} opgaver genoptaget",
|
||||
"bulk_objects_area": "Område angivet for {n} objekter",
|
||||
"bulk_failed": "{n} mislykkedes: {reason}",
|
||||
"completing": "Fuldfører…",
|
||||
"interval": "Interval",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Tilpasset",
|
||||
"history": "Historik",
|
||||
"cost": "Omkostning",
|
||||
"cost_kind_credit": "Kreditering",
|
||||
"cost_credit_hint": "Penge tilbage — et salg, en refusion. Sænker totalerne; gennemsnittet pr. udførelse medregner det ikke.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Vedligeholdelsesrapport",
|
||||
"report_generated": "Genereret",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Dage før udløb",
|
||||
"settings_warranty_reminder_hint": "Giv besked én gang, når et objekts garanti udløber om så mange dage.",
|
||||
"settings_snooze_hours": "Udsættelsesvarighed (timer)",
|
||||
"settings_snooze_hours_hint": "Hvor længe „Udsæt“ gør en opgaves påmindelser tavse — fra opgavemenuen, et kort, stemmen eller telefonens Udsæt-knap. Forfaldsdatoen forbliver uændret.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Decimaler for beløb",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} Objekte mit allen ihren Aufgaben löschen? Archivieren behält stattdessen die Historie.",
|
||||
"bulk_objects_deleted": "{n} Objekte gelöscht",
|
||||
"bulk_objects_archived": "{n} Objekte archiviert",
|
||||
"bulk_assign": "Zuweisen",
|
||||
"bulk_assign_one": "Eine Person",
|
||||
"bulk_assign_rotate": "Mehrere Personen, abwechselnd",
|
||||
"bulk_assign_hint": "Ersetzt, wem die ausgewählten Aufgaben zugewiesen sind — eine Rotation eingeschlossen.",
|
||||
"bulk_rotation_min_two": "Mindestens zwei Personen auswählen.",
|
||||
"bulk_labels_add": "Labels hinzufügen",
|
||||
"bulk_labels_remove": "Labels entfernen",
|
||||
"bulk_edit_hint": "Nur die angehakten Einstellungen ändern sich, alles andere bleibt, wie es ist.",
|
||||
"bulk_no_notifications": "Keine Benachrichtigungen für diese Aufgaben",
|
||||
"bulk_updated": "{n} Aufgaben aktualisiert",
|
||||
"bulk_paused": "{n} Aufgaben pausiert",
|
||||
"bulk_resumed": "{n} Aufgaben fortgesetzt",
|
||||
"bulk_objects_area": "Bereich für {n} Objekte gesetzt",
|
||||
"bulk_failed": "{n} fehlgeschlagen: {reason}",
|
||||
"completing": "Wird erledigt…",
|
||||
"interval": "Intervall",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Benutzerdefiniert",
|
||||
"history": "Verlauf",
|
||||
"cost": "Kosten",
|
||||
"cost_kind_credit": "Gutschrift",
|
||||
"cost_credit_hint": "Geld zurück – ein Verkauf, eine Erstattung. Senkt die Summen; der Durchschnitt pro Erledigung lässt es weg.",
|
||||
"report_button": "Bericht",
|
||||
"report_title": "Wartungsbericht",
|
||||
"report_generated": "Erstellt",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Tage vor Ablauf",
|
||||
"settings_warranty_reminder_hint": "Einmal benachrichtigen, wenn die Garantie eines Objekts so viele Tage vor Ablauf steht.",
|
||||
"settings_snooze_hours": "Schlummerdauer (Stunden)",
|
||||
"settings_snooze_hours_hint": "Wie lange „Schlummern“ die Erinnerungen einer Aufgabe stummschaltet — im Aufgabenmenü, auf einer Karte, per Sprache oder mit der Schlummern-Taste am Handy. Das Fälligkeitsdatum bleibt, wie es ist.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Währung",
|
||||
"settings_currency_decimals": "Nachkommastellen für Beträge",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Delete {n} objects and all their tasks? Archiving keeps their history instead.",
|
||||
"bulk_objects_deleted": "{n} objects deleted",
|
||||
"bulk_objects_archived": "{n} objects archived",
|
||||
"bulk_assign": "Assign",
|
||||
"bulk_assign_one": "One person",
|
||||
"bulk_assign_rotate": "Several people, in turns",
|
||||
"bulk_assign_hint": "Replaces who is assigned to the selected tasks — a rotation included.",
|
||||
"bulk_rotation_min_two": "Pick at least two people.",
|
||||
"bulk_labels_add": "Add labels",
|
||||
"bulk_labels_remove": "Remove labels",
|
||||
"bulk_edit_hint": "Only the ticked settings change; everything else stays as it is.",
|
||||
"bulk_no_notifications": "No notifications for these tasks",
|
||||
"bulk_updated": "{n} tasks updated",
|
||||
"bulk_paused": "{n} tasks paused",
|
||||
"bulk_resumed": "{n} tasks resumed",
|
||||
"bulk_objects_area": "Area set for {n} objects",
|
||||
"bulk_failed": "{n} failed: {reason}",
|
||||
"completing": "Completing…",
|
||||
"interval": "Interval",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Custom",
|
||||
"history": "History",
|
||||
"cost": "Cost",
|
||||
"cost_kind_credit": "Credit",
|
||||
"cost_credit_hint": "Money back — a sale, a refund. It lowers the totals; the average per completion leaves it out.",
|
||||
"report_button": "Report",
|
||||
"report_title": "Maintenance report",
|
||||
"report_generated": "Generated",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Days before expiry",
|
||||
"settings_warranty_reminder_hint": "Notify once when an object's warranty is this many days from expiring.",
|
||||
"settings_snooze_hours": "Snooze duration (hours)",
|
||||
"settings_snooze_hours_hint": "How long Snooze mutes a task's reminders — from the task menu, a card, voice or the phone's Snooze button. The due date stays as it is.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Currency",
|
||||
"settings_currency_decimals": "Decimal places for amounts",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "¿Eliminar {n} objetos y todas sus tareas? Archivar conserva su historial.",
|
||||
"bulk_objects_deleted": "{n} objetos eliminados",
|
||||
"bulk_objects_archived": "{n} objetos archivados",
|
||||
"bulk_assign": "Asignar",
|
||||
"bulk_assign_one": "Una persona",
|
||||
"bulk_assign_rotate": "Varias personas, por turnos",
|
||||
"bulk_assign_hint": "Sustituye a quién están asignadas las tareas seleccionadas, incluida una rotación.",
|
||||
"bulk_rotation_min_two": "Elige al menos dos personas.",
|
||||
"bulk_labels_add": "Añadir etiquetas",
|
||||
"bulk_labels_remove": "Quitar etiquetas",
|
||||
"bulk_edit_hint": "Solo cambian los ajustes marcados; todo lo demás se queda como está.",
|
||||
"bulk_no_notifications": "Sin notificaciones para estas tareas",
|
||||
"bulk_updated": "{n} tareas actualizadas",
|
||||
"bulk_paused": "{n} tareas pausadas",
|
||||
"bulk_resumed": "{n} tareas reanudadas",
|
||||
"bulk_objects_area": "Área asignada a {n} objetos",
|
||||
"bulk_failed": "{n} fallidos: {reason}",
|
||||
"completing": "Completando…",
|
||||
"interval": "Intervalo",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Personalizado",
|
||||
"history": "Historial",
|
||||
"cost": "Coste",
|
||||
"cost_kind_credit": "Abono",
|
||||
"cost_credit_hint": "Dinero que vuelve — una venta, un reembolso. Reduce los totales; la media por realización lo excluye.",
|
||||
"report_button": "Informe",
|
||||
"report_title": "Informe de mantenimiento",
|
||||
"report_generated": "Generado",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Días antes del vencimiento",
|
||||
"settings_warranty_reminder_hint": "Notificar una vez cuando a la garantía de un objeto le queden estos días para vencer.",
|
||||
"settings_snooze_hours": "Duración de posposición (horas)",
|
||||
"settings_snooze_hours_hint": "Cuánto tiempo «Posponer» silencia los recordatorios de una tarea: desde el menú de la tarea, una tarjeta, la voz o el botón Posponer del teléfono. La fecha de vencimiento no cambia.",
|
||||
"settings_budget": "Presupuesto",
|
||||
"settings_currency": "Moneda",
|
||||
"settings_currency_decimals": "Decimales de los importes",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Poistetaanko {n} kohdetta kaikkine tehtävineen? Arkistointi säilyttää historian.",
|
||||
"bulk_objects_deleted": "{n} kohdetta poistettu",
|
||||
"bulk_objects_archived": "{n} kohdetta arkistoitu",
|
||||
"bulk_assign": "Määritä",
|
||||
"bulk_assign_one": "Yksi henkilö",
|
||||
"bulk_assign_rotate": "Useita henkilöitä vuorotellen",
|
||||
"bulk_assign_hint": "Korvaa valittujen tehtävien määritykset — myös kierron.",
|
||||
"bulk_rotation_min_two": "Valitse vähintään kaksi henkilöä.",
|
||||
"bulk_labels_add": "Lisää tunnisteita",
|
||||
"bulk_labels_remove": "Poista tunnisteita",
|
||||
"bulk_edit_hint": "Vain valitut asetukset muuttuvat; kaikki muu pysyy ennallaan.",
|
||||
"bulk_no_notifications": "Ei ilmoituksia näistä tehtävistä",
|
||||
"bulk_updated": "{n} tehtävää päivitetty",
|
||||
"bulk_paused": "{n} tehtävää keskeytetty",
|
||||
"bulk_resumed": "{n} tehtävää jatkettu",
|
||||
"bulk_objects_area": "Alue asetettu {n} kohteelle",
|
||||
"bulk_failed": "{n} epäonnistui: {reason}",
|
||||
"completing": "Suoritetaan…",
|
||||
"interval": "Väli",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Mukautettu",
|
||||
"history": "Historia",
|
||||
"cost": "Kustannus",
|
||||
"cost_kind_credit": "Hyvitys",
|
||||
"cost_credit_hint": "Takaisin saatu raha — myynti, palautus. Pienentää summia; keskiarvo suoritusta kohden jättää sen pois.",
|
||||
"report_button": "Raportti",
|
||||
"report_title": "Huoltoraportti",
|
||||
"report_generated": "Luotu",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Päivää ennen päättymistä",
|
||||
"settings_warranty_reminder_hint": "Ilmoita kerran, kun objektin takuun päättymiseen on näin monta päivää.",
|
||||
"settings_snooze_hours": "Torkun kesto (tuntia)",
|
||||
"settings_snooze_hours_hint": "Kuinka kauan Torkku mykistää tehtävän muistutukset — tehtävävalikosta, kortilta, äänellä tai puhelimen Torkku-painikkeella. Eräpäivä pysyy ennallaan.",
|
||||
"settings_budget": "Budjetti",
|
||||
"settings_currency": "Valuutta",
|
||||
"settings_currency_decimals": "Summien desimaalit",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Supprimer {n} objets et toutes leurs tâches ? L'archivage conserve leur historique.",
|
||||
"bulk_objects_deleted": "{n} objets supprimés",
|
||||
"bulk_objects_archived": "{n} objets archivés",
|
||||
"bulk_assign": "Assigner",
|
||||
"bulk_assign_one": "Une personne",
|
||||
"bulk_assign_rotate": "Plusieurs personnes, à tour de rôle",
|
||||
"bulk_assign_hint": "Remplace l'attribution des tâches sélectionnées — rotation comprise.",
|
||||
"bulk_rotation_min_two": "Choisissez au moins deux personnes.",
|
||||
"bulk_labels_add": "Ajouter des étiquettes",
|
||||
"bulk_labels_remove": "Retirer des étiquettes",
|
||||
"bulk_edit_hint": "Seuls les réglages cochés changent ; tout le reste reste tel quel.",
|
||||
"bulk_no_notifications": "Aucune notification pour ces tâches",
|
||||
"bulk_updated": "{n} tâches mises à jour",
|
||||
"bulk_paused": "{n} tâches mises en pause",
|
||||
"bulk_resumed": "{n} tâches reprises",
|
||||
"bulk_objects_area": "Zone définie pour {n} objets",
|
||||
"bulk_failed": "{n} en échec : {reason}",
|
||||
"completing": "En cours…",
|
||||
"interval": "Intervalle",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Personnalisé",
|
||||
"history": "Historique",
|
||||
"cost": "Coût",
|
||||
"cost_kind_credit": "Crédit",
|
||||
"cost_credit_hint": "Argent récupéré — une vente, un remboursement. Réduit les totaux ; la moyenne par réalisation l'ignore.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Rapport d'entretien",
|
||||
"report_generated": "Généré",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Jours avant l'expiration",
|
||||
"settings_warranty_reminder_hint": "Notifier une fois lorsque la garantie d'un objet expire dans ce nombre de jours.",
|
||||
"settings_snooze_hours": "Durée de report (heures)",
|
||||
"settings_snooze_hours_hint": "Durée pendant laquelle « Mettre en attente » coupe les rappels d'une tâche — depuis le menu de la tâche, une carte, la voix ou le bouton du téléphone. La date d'échéance ne change pas.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Devise",
|
||||
"settings_currency_decimals": "Décimales des montants",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} ऑब्जेक्ट और उनके सभी कार्य हटाएँ? संग्रहित करने से उनका इतिहास बना रहता है।",
|
||||
"bulk_objects_deleted": "{n} ऑब्जेक्ट हटाए गए",
|
||||
"bulk_objects_archived": "{n} ऑब्जेक्ट संग्रहित किए गए",
|
||||
"bulk_assign": "असाइन करें",
|
||||
"bulk_assign_one": "एक व्यक्ति",
|
||||
"bulk_assign_rotate": "कई लोग, बारी-बारी से",
|
||||
"bulk_assign_hint": "चुने गए कार्यों का असाइनमेंट बदल देता है — रोटेशन सहित।",
|
||||
"bulk_rotation_min_two": "कम से कम दो लोग चुनें।",
|
||||
"bulk_labels_add": "लेबल जोड़ें",
|
||||
"bulk_labels_remove": "लेबल हटाएँ",
|
||||
"bulk_edit_hint": "केवल चुनी गई सेटिंग्स बदलती हैं; बाकी सब वैसा ही रहता है।",
|
||||
"bulk_no_notifications": "इन कार्यों के लिए कोई सूचना नहीं",
|
||||
"bulk_updated": "{n} कार्य अपडेट किए गए",
|
||||
"bulk_paused": "{n} कार्य रोके गए",
|
||||
"bulk_resumed": "{n} कार्य फिर से शुरू किए गए",
|
||||
"bulk_objects_area": "{n} ऑब्जेक्ट के लिए क्षेत्र सेट किया गया",
|
||||
"bulk_failed": "{n} विफल: {reason}",
|
||||
"completing": "पूर्ण किया जा रहा है…",
|
||||
"interval": "अंतराल",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "कस्टम",
|
||||
"history": "इतिहास",
|
||||
"cost": "लागत",
|
||||
"cost_kind_credit": "वापसी",
|
||||
"cost_credit_hint": "वापस मिला पैसा — बिक्री, रिफ़ंड। यह कुल योग घटाता है; प्रति पूर्णता औसत में शामिल नहीं होता।",
|
||||
"report_button": "रिपोर्ट",
|
||||
"report_title": "रखरखाव रिपोर्ट",
|
||||
"report_generated": "जनरेट किया गया",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "समाप्ति से पहले के दिन",
|
||||
"settings_warranty_reminder_hint": "जब किसी वस्तु की वारंटी समाप्त होने में इतने दिन बचें तो एक बार सूचित करें।",
|
||||
"settings_snooze_hours": "स्नूज़ अवधि (घंटे)",
|
||||
"settings_snooze_hours_hint": "स्नूज़ किसी कार्य के रिमाइंडर कितनी देर तक बंद रखता है — कार्य मेनू, कार्ड, आवाज़ या फ़ोन के स्नूज़ बटन से। नियत तिथि नहीं बदलती।",
|
||||
"settings_budget": "बजट",
|
||||
"settings_currency": "मुद्रा",
|
||||
"settings_currency_decimals": "राशियों के दशमलव स्थान",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Törli a(z) {n} objektumot az összes feladatával együtt? Az archiválás ehelyett megőrzi az előzményeket.",
|
||||
"bulk_objects_deleted": "{n} objektum törölve",
|
||||
"bulk_objects_archived": "{n} objektum archiválva",
|
||||
"bulk_assign": "Hozzárendelés",
|
||||
"bulk_assign_one": "Egy személy",
|
||||
"bulk_assign_rotate": "Több személy, felváltva",
|
||||
"bulk_assign_hint": "Lecseréli, kihez vannak rendelve a kijelölt feladatok — a rotációt is.",
|
||||
"bulk_rotation_min_two": "Válassz legalább két személyt.",
|
||||
"bulk_labels_add": "Címkék hozzáadása",
|
||||
"bulk_labels_remove": "Címkék eltávolítása",
|
||||
"bulk_edit_hint": "Csak a bejelölt beállítások változnak, minden más marad.",
|
||||
"bulk_no_notifications": "Nincs értesítés ezekhez a feladatokhoz",
|
||||
"bulk_updated": "{n} feladat frissítve",
|
||||
"bulk_paused": "{n} feladat szüneteltetve",
|
||||
"bulk_resumed": "{n} feladat folytatva",
|
||||
"bulk_objects_area": "Terület beállítva {n} objektumhoz",
|
||||
"bulk_failed": "{n} sikertelen: {reason}",
|
||||
"completing": "Elvégzés…",
|
||||
"interval": "Intervallum",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Egyéni",
|
||||
"history": "Előzmények",
|
||||
"cost": "Költség",
|
||||
"cost_kind_credit": "Jóváírás",
|
||||
"cost_credit_hint": "Visszakapott pénz — eladás, visszatérítés. Csökkenti az összegeket; az elvégzésenkénti átlag nem számolja.",
|
||||
"report_button": "Jelentés",
|
||||
"report_title": "Karbantartási jelentés",
|
||||
"report_generated": "Létrehozva",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Nappal a lejárat előtt",
|
||||
"settings_warranty_reminder_hint": "Egyszeri értesítés, amikor egy objektum garanciája ennyi napra van a lejárattól.",
|
||||
"settings_snooze_hours": "Szundi időtartama (óra)",
|
||||
"settings_snooze_hours_hint": "Mennyi ideig némítja a Szundi egy feladat emlékeztetőit — a feladat menüjéből, egy kártyáról, hanggal vagy a telefon Szundi gombjával. A határidő nem változik.",
|
||||
"settings_budget": "Költségkeret",
|
||||
"settings_currency": "Pénznem",
|
||||
"settings_currency_decimals": "Összegek tizedesjegyei",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Eliminare {n} oggetti con tutte le loro attività? L'archiviazione conserva invece la cronologia.",
|
||||
"bulk_objects_deleted": "{n} oggetti eliminati",
|
||||
"bulk_objects_archived": "{n} oggetti archiviati",
|
||||
"bulk_assign": "Assegna",
|
||||
"bulk_assign_one": "Una persona",
|
||||
"bulk_assign_rotate": "Più persone, a turno",
|
||||
"bulk_assign_hint": "Sostituisce a chi sono assegnate le attività selezionate, rotazione compresa.",
|
||||
"bulk_rotation_min_two": "Scegli almeno due persone.",
|
||||
"bulk_labels_add": "Aggiungi etichette",
|
||||
"bulk_labels_remove": "Rimuovi etichette",
|
||||
"bulk_edit_hint": "Cambiano solo le impostazioni spuntate; tutto il resto resta com'è.",
|
||||
"bulk_no_notifications": "Nessuna notifica per queste attività",
|
||||
"bulk_updated": "{n} attività aggiornate",
|
||||
"bulk_paused": "{n} attività in pausa",
|
||||
"bulk_resumed": "{n} attività riprese",
|
||||
"bulk_objects_area": "Area impostata per {n} oggetti",
|
||||
"bulk_failed": "{n} non riusciti: {reason}",
|
||||
"completing": "Completamento…",
|
||||
"interval": "Intervallo",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Personalizzato",
|
||||
"history": "Cronologia",
|
||||
"cost": "Costo",
|
||||
"cost_kind_credit": "Accredito",
|
||||
"cost_credit_hint": "Denaro rientrato — una vendita, un rimborso. Riduce i totali; la media per completamento lo esclude.",
|
||||
"report_button": "Report",
|
||||
"report_title": "Report di manutenzione",
|
||||
"report_generated": "Generato",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Giorni prima della scadenza",
|
||||
"settings_warranty_reminder_hint": "Notifica una volta quando alla garanzia di un oggetto mancano questi giorni alla scadenza.",
|
||||
"settings_snooze_hours": "Durata posticipo (ore)",
|
||||
"settings_snooze_hours_hint": "Per quanto tempo «Posponi» silenzia i promemoria di un'attività — dal menu dell'attività, da una scheda, con la voce o con il pulsante del telefono. La scadenza resta invariata.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Decimali degli importi",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} 件のオブジェクトとそのすべてのタスクを削除しますか?アーカイブなら履歴が残ります。",
|
||||
"bulk_objects_deleted": "{n} 件のオブジェクトを削除しました",
|
||||
"bulk_objects_archived": "{n} 件のオブジェクトをアーカイブしました",
|
||||
"bulk_assign": "割り当て",
|
||||
"bulk_assign_one": "1人",
|
||||
"bulk_assign_rotate": "複数人で交代制",
|
||||
"bulk_assign_hint": "選択したタスクの担当者を置き換えます(ローテーションを含む)。",
|
||||
"bulk_rotation_min_two": "2人以上を選んでください。",
|
||||
"bulk_labels_add": "ラベルを追加",
|
||||
"bulk_labels_remove": "ラベルを削除",
|
||||
"bulk_edit_hint": "チェックした設定だけが変わり、それ以外はそのままです。",
|
||||
"bulk_no_notifications": "これらのタスクは通知しない",
|
||||
"bulk_updated": "{n} 件のタスクを更新しました",
|
||||
"bulk_paused": "{n} 件のタスクを一時停止しました",
|
||||
"bulk_resumed": "{n} 件のタスクを再開しました",
|
||||
"bulk_objects_area": "{n} 件のオブジェクトにエリアを設定しました",
|
||||
"bulk_failed": "{n} 件失敗:{reason}",
|
||||
"completing": "完了処理中…",
|
||||
"interval": "間隔",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "カスタム",
|
||||
"history": "履歴",
|
||||
"cost": "費用",
|
||||
"cost_kind_credit": "収入",
|
||||
"cost_credit_hint": "戻ってきたお金(売却、返金など)。合計を減らしますが、1回あたりの平均には含まれません。",
|
||||
"report_button": "レポート",
|
||||
"report_title": "メンテナンスレポート",
|
||||
"report_generated": "作成日",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "期限切れ前の日数",
|
||||
"settings_warranty_reminder_hint": "オブジェクトの保証期限までこの日数になったら一度通知します。",
|
||||
"settings_snooze_hours": "スヌーズ時間(時間)",
|
||||
"settings_snooze_hours_hint": "スヌーズでタスクのリマインダーを止める時間です。タスクのメニュー、カード、音声、スマートフォンのスヌーズボタンのどこから操作しても同じです。期日は変わりません。",
|
||||
"settings_budget": "予算",
|
||||
"settings_currency": "通貨",
|
||||
"settings_currency_decimals": "金額の小数点以下の桁数",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "개체 {n}개와 모든 작업을 삭제할까요? 보관하면 기록이 유지됩니다.",
|
||||
"bulk_objects_deleted": "개체 {n}개 삭제됨",
|
||||
"bulk_objects_archived": "개체 {n}개 보관됨",
|
||||
"bulk_assign": "배정",
|
||||
"bulk_assign_one": "한 사람",
|
||||
"bulk_assign_rotate": "여러 사람, 번갈아",
|
||||
"bulk_assign_hint": "선택한 작업의 담당자를 바꿉니다 — 교대 방식 포함.",
|
||||
"bulk_rotation_min_two": "두 명 이상 선택하세요.",
|
||||
"bulk_labels_add": "라벨 추가",
|
||||
"bulk_labels_remove": "라벨 제거",
|
||||
"bulk_edit_hint": "체크한 설정만 바뀌고 나머지는 그대로입니다.",
|
||||
"bulk_no_notifications": "이 작업들은 알림 없음",
|
||||
"bulk_updated": "작업 {n}개 업데이트됨",
|
||||
"bulk_paused": "작업 {n}개 일시 중지됨",
|
||||
"bulk_resumed": "작업 {n}개 재개됨",
|
||||
"bulk_objects_area": "개체 {n}개의 구역을 설정함",
|
||||
"bulk_failed": "{n}개 실패: {reason}",
|
||||
"completing": "완료 처리 중…",
|
||||
"interval": "주기",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "사용자 지정",
|
||||
"history": "기록",
|
||||
"cost": "비용",
|
||||
"cost_kind_credit": "수입",
|
||||
"cost_credit_hint": "돌려받은 돈(판매, 환불 등). 합계를 줄이며, 완료당 평균에는 포함되지 않습니다.",
|
||||
"report_button": "보고서",
|
||||
"report_title": "유지보수 보고서",
|
||||
"report_generated": "생성일",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "만료 전 일수",
|
||||
"settings_warranty_reminder_hint": "객체의 보증 만료가 설정한 일수만큼 남았을 때 한 번 알립니다.",
|
||||
"settings_snooze_hours": "다시 알림 대기 시간 (시간)",
|
||||
"settings_snooze_hours_hint": "다시 알림이 작업 알림을 끄는 시간입니다 — 작업 메뉴, 카드, 음성, 휴대폰의 다시 알림 버튼 어디에서 해도 같습니다. 기한은 바뀌지 않습니다.",
|
||||
"settings_budget": "예산",
|
||||
"settings_currency": "통화",
|
||||
"settings_currency_decimals": "금액 소수 자릿수",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Slette {n} objekter og alle oppgavene deres? Arkivering beholder historikken i stedet.",
|
||||
"bulk_objects_deleted": "{n} objekter slettet",
|
||||
"bulk_objects_archived": "{n} objekter arkivert",
|
||||
"bulk_assign": "Tildel",
|
||||
"bulk_assign_one": "Én person",
|
||||
"bulk_assign_rotate": "Flere personer, på omgang",
|
||||
"bulk_assign_hint": "Erstatter hvem de valgte oppgavene er tildelt — også en rotasjon.",
|
||||
"bulk_rotation_min_two": "Velg minst to personer.",
|
||||
"bulk_labels_add": "Legg til etiketter",
|
||||
"bulk_labels_remove": "Fjern etiketter",
|
||||
"bulk_edit_hint": "Bare de avkryssede innstillingene endres; alt annet forblir som det er.",
|
||||
"bulk_no_notifications": "Ingen varsler for disse oppgavene",
|
||||
"bulk_updated": "{n} oppgaver oppdatert",
|
||||
"bulk_paused": "{n} oppgaver satt på pause",
|
||||
"bulk_resumed": "{n} oppgaver gjenopptatt",
|
||||
"bulk_objects_area": "Område satt for {n} objekter",
|
||||
"bulk_failed": "{n} mislyktes: {reason}",
|
||||
"completing": "Fullfører…",
|
||||
"interval": "Intervall",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Egendefinert",
|
||||
"history": "Historikk",
|
||||
"cost": "Kostnad",
|
||||
"cost_kind_credit": "Kreditering",
|
||||
"cost_credit_hint": "Penger tilbake — et salg, en refusjon. Senker summene; snittet per utførelse tar det ikke med.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Vedlikeholdsrapport",
|
||||
"report_generated": "Generert",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Dager før utløp",
|
||||
"settings_warranty_reminder_hint": "Varsle én gang når garantien til et objekt utløper om så mange dager.",
|
||||
"settings_snooze_hours": "Slumrevarighet (timer)",
|
||||
"settings_snooze_hours_hint": "Hvor lenge «Slumre» demper en oppgaves påminnelser — fra oppgavemenyen, et kort, stemmen eller telefonens Slumre-knapp. Forfallsdatoen forblir uendret.",
|
||||
"settings_budget": "Budsjett",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Desimaler for beløp",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} objecten met al hun taken verwijderen? Archiveren bewaart in plaats daarvan de geschiedenis.",
|
||||
"bulk_objects_deleted": "{n} objecten verwijderd",
|
||||
"bulk_objects_archived": "{n} objecten gearchiveerd",
|
||||
"bulk_assign": "Toewijzen",
|
||||
"bulk_assign_one": "Eén persoon",
|
||||
"bulk_assign_rotate": "Meerdere personen, om de beurt",
|
||||
"bulk_assign_hint": "Vervangt aan wie de geselecteerde taken zijn toegewezen — een rotatie inbegrepen.",
|
||||
"bulk_rotation_min_two": "Kies ten minste twee personen.",
|
||||
"bulk_labels_add": "Labels toevoegen",
|
||||
"bulk_labels_remove": "Labels verwijderen",
|
||||
"bulk_edit_hint": "Alleen de aangevinkte instellingen veranderen; al het andere blijft zoals het is.",
|
||||
"bulk_no_notifications": "Geen notificaties voor deze taken",
|
||||
"bulk_updated": "{n} taken bijgewerkt",
|
||||
"bulk_paused": "{n} taken gepauzeerd",
|
||||
"bulk_resumed": "{n} taken hervat",
|
||||
"bulk_objects_area": "Gebied ingesteld voor {n} objecten",
|
||||
"bulk_failed": "{n} mislukt: {reason}",
|
||||
"completing": "Wordt voltooid…",
|
||||
"interval": "Interval",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Aangepast",
|
||||
"history": "Geschiedenis",
|
||||
"cost": "Kosten",
|
||||
"cost_kind_credit": "Tegoed",
|
||||
"cost_credit_hint": "Geld terug — een verkoop, een terugbetaling. Verlaagt de totalen; het gemiddelde per voltooiing laat het weg.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Onderhoudsrapport",
|
||||
"report_generated": "Gegenereerd",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dagen vóór afloop",
|
||||
"settings_warranty_reminder_hint": "Eén keer melden wanneer de garantie van een object over zoveel dagen afloopt.",
|
||||
"settings_snooze_hours": "Snoozeduur (uren)",
|
||||
"settings_snooze_hours_hint": "Hoe lang ‘Sluimeren’ de herinneringen van een taak dempt — vanuit het taakmenu, een kaart, spraak of de sluimerknop op de telefoon. De vervaldatum blijft zoals hij is.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Decimalen voor bedragen",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Usunąć {n} obiektów wraz ze wszystkimi zadaniami? Archiwizacja zachowa ich historię.",
|
||||
"bulk_objects_deleted": "Usunięto obiektów: {n}",
|
||||
"bulk_objects_archived": "Zarchiwizowano obiektów: {n}",
|
||||
"bulk_assign": "Przypisz",
|
||||
"bulk_assign_one": "Jedna osoba",
|
||||
"bulk_assign_rotate": "Kilka osób, na zmianę",
|
||||
"bulk_assign_hint": "Zastępuje przypisanie wybranych zadań — łącznie z rotacją.",
|
||||
"bulk_rotation_min_two": "Wybierz co najmniej dwie osoby.",
|
||||
"bulk_labels_add": "Dodaj etykiety",
|
||||
"bulk_labels_remove": "Usuń etykiety",
|
||||
"bulk_edit_hint": "Zmieniają się tylko zaznaczone ustawienia; wszystko inne pozostaje bez zmian.",
|
||||
"bulk_no_notifications": "Bez powiadomień dla tych zadań",
|
||||
"bulk_updated": "Zaktualizowano {n} zadań",
|
||||
"bulk_paused": "Wstrzymano {n} zadań",
|
||||
"bulk_resumed": "Wznowiono {n} zadań",
|
||||
"bulk_objects_area": "Ustawiono obszar dla obiektów: {n}",
|
||||
"bulk_failed": "Nieudane: {n} — {reason}",
|
||||
"completing": "Wykonywanie…",
|
||||
"interval": "Interwał",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Niestandardowy",
|
||||
"history": "Historia",
|
||||
"cost": "Koszt",
|
||||
"cost_kind_credit": "Zwrot",
|
||||
"cost_credit_hint": "Odzyskane pieniądze — sprzedaż, zwrot. Obniża sumy; średnia na wykonanie go pomija.",
|
||||
"report_button": "Raport",
|
||||
"report_title": "Raport konserwacji",
|
||||
"report_generated": "Wygenerowano",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dni przed wygaśnięciem",
|
||||
"settings_warranty_reminder_hint": "Powiadom raz, gdy do wygaśnięcia gwarancji obiektu pozostanie tyle dni.",
|
||||
"settings_snooze_hours": "Czas drzemki (godziny)",
|
||||
"settings_snooze_hours_hint": "Jak długo „Drzemka” wycisza przypomnienia zadania — z menu zadania, z karty, głosem lub przyciskiem Drzemka w telefonie. Termin pozostaje bez zmian.",
|
||||
"settings_budget": "Budżet",
|
||||
"settings_currency": "Waluta",
|
||||
"settings_currency_decimals": "Miejsca po przecinku w kwotach",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Excluir {n} objetos e todas as suas tarefas? Arquivar mantém o histórico.",
|
||||
"bulk_objects_deleted": "{n} objetos excluídos",
|
||||
"bulk_objects_archived": "{n} objetos arquivados",
|
||||
"bulk_assign": "Atribuir",
|
||||
"bulk_assign_one": "Uma pessoa",
|
||||
"bulk_assign_rotate": "Várias pessoas, em rodízio",
|
||||
"bulk_assign_hint": "Substitui a quem as tarefas selecionadas estão atribuídas — incluindo um rodízio.",
|
||||
"bulk_rotation_min_two": "Escolha pelo menos duas pessoas.",
|
||||
"bulk_labels_add": "Adicionar etiquetas",
|
||||
"bulk_labels_remove": "Remover etiquetas",
|
||||
"bulk_edit_hint": "Só as configurações marcadas mudam; todo o resto continua como está.",
|
||||
"bulk_no_notifications": "Sem notificações para estas tarefas",
|
||||
"bulk_updated": "{n} tarefas atualizadas",
|
||||
"bulk_paused": "{n} tarefas pausadas",
|
||||
"bulk_resumed": "{n} tarefas retomadas",
|
||||
"bulk_objects_area": "Área definida para {n} objetos",
|
||||
"bulk_failed": "{n} falharam: {reason}",
|
||||
"completing": "Concluindo…",
|
||||
"interval": "Intervalo",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Personalizado",
|
||||
"history": "Histórico",
|
||||
"cost": "Custo",
|
||||
"cost_kind_credit": "Crédito",
|
||||
"cost_credit_hint": "Dinheiro de volta — uma venda, um reembolso. Reduz os totais; a média por conclusão não o conta.",
|
||||
"report_button": "Relatório",
|
||||
"report_title": "Relatório de manutenção",
|
||||
"report_generated": "Gerado em",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Dias antes do vencimento",
|
||||
"settings_warranty_reminder_hint": "Notifica uma vez quando faltar este número de dias para a garantia de um objeto expirar.",
|
||||
"settings_snooze_hours": "Duração da soneca (horas)",
|
||||
"settings_snooze_hours_hint": "Por quanto tempo a Soneca silencia os lembretes de uma tarefa — pelo menu da tarefa, por um cartão, pela voz ou pelo botão Soneca do celular. A data de vencimento não muda.",
|
||||
"settings_budget": "Orçamento",
|
||||
"settings_currency": "Moeda",
|
||||
"settings_currency_decimals": "Casas decimais dos valores",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Eliminar {n} objetos e todas as suas tarefas? Arquivar mantém o histórico.",
|
||||
"bulk_objects_deleted": "{n} objetos eliminados",
|
||||
"bulk_objects_archived": "{n} objetos arquivados",
|
||||
"bulk_assign": "Atribuir",
|
||||
"bulk_assign_one": "Uma pessoa",
|
||||
"bulk_assign_rotate": "Várias pessoas, à vez",
|
||||
"bulk_assign_hint": "Substitui a quem as tarefas selecionadas estão atribuídas — incluindo uma rotação.",
|
||||
"bulk_rotation_min_two": "Escolha pelo menos duas pessoas.",
|
||||
"bulk_labels_add": "Adicionar etiquetas",
|
||||
"bulk_labels_remove": "Remover etiquetas",
|
||||
"bulk_edit_hint": "Só as definições assinaladas mudam; tudo o resto fica como está.",
|
||||
"bulk_no_notifications": "Sem notificações para estas tarefas",
|
||||
"bulk_updated": "{n} tarefas atualizadas",
|
||||
"bulk_paused": "{n} tarefas pausadas",
|
||||
"bulk_resumed": "{n} tarefas retomadas",
|
||||
"bulk_objects_area": "Área definida para {n} objetos",
|
||||
"bulk_failed": "{n} falharam: {reason}",
|
||||
"completing": "A concluir…",
|
||||
"interval": "Intervalo",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Personalizado",
|
||||
"history": "Histórico",
|
||||
"cost": "Custo",
|
||||
"cost_kind_credit": "Crédito",
|
||||
"cost_credit_hint": "Dinheiro que regressa — uma venda, um reembolso. Reduz os totais; a média por conclusão não o conta.",
|
||||
"report_button": "Relatório",
|
||||
"report_title": "Relatório de manutenção",
|
||||
"report_generated": "Gerado",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dias antes do fim",
|
||||
"settings_warranty_reminder_hint": "Notificar uma vez quando faltarem estes dias para a garantia de um objeto expirar.",
|
||||
"settings_snooze_hours": "Duração do adiamento (horas)",
|
||||
"settings_snooze_hours_hint": "Durante quanto tempo «Adiar» silencia os lembretes de uma tarefa — a partir do menu da tarefa, de um cartão, por voz ou do botão Adiar do telemóvel. A data limite mantém-se.",
|
||||
"settings_budget": "Orçamento",
|
||||
"settings_currency": "Moeda",
|
||||
"settings_currency_decimals": "Casas decimais dos montantes",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Удалить {n} объектов со всеми их задачами? Архивация вместо этого сохранит историю.",
|
||||
"bulk_objects_deleted": "Удалено объектов: {n}",
|
||||
"bulk_objects_archived": "Архивировано объектов: {n}",
|
||||
"bulk_assign": "Назначить",
|
||||
"bulk_assign_one": "Один человек",
|
||||
"bulk_assign_rotate": "Несколько человек по очереди",
|
||||
"bulk_assign_hint": "Заменяет назначение выбранных задач — включая ротацию.",
|
||||
"bulk_rotation_min_two": "Выберите не менее двух человек.",
|
||||
"bulk_labels_add": "Добавить метки",
|
||||
"bulk_labels_remove": "Удалить метки",
|
||||
"bulk_edit_hint": "Меняются только отмеченные настройки, всё остальное остаётся как есть.",
|
||||
"bulk_no_notifications": "Без уведомлений для этих задач",
|
||||
"bulk_updated": "Обновлено задач: {n}",
|
||||
"bulk_paused": "Приостановлено задач: {n}",
|
||||
"bulk_resumed": "Возобновлено задач: {n}",
|
||||
"bulk_objects_area": "Зона задана для объектов: {n}",
|
||||
"bulk_failed": "Не удалось: {n} — {reason}",
|
||||
"completing": "Выполнение…",
|
||||
"interval": "Интервал",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Своё",
|
||||
"history": "История",
|
||||
"cost": "Стоимость",
|
||||
"cost_kind_credit": "Возврат",
|
||||
"cost_credit_hint": "Деньги вернулись — продажа, возврат. Уменьшает итоги; среднее за выполнение его не учитывает.",
|
||||
"report_button": "Отчёт",
|
||||
"report_title": "Отчёт об обслуживании",
|
||||
"report_generated": "Создан",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Дней до окончания",
|
||||
"settings_warranty_reminder_hint": "Уведомить один раз, когда до окончания гарантии объекта останется столько дней.",
|
||||
"settings_snooze_hours": "Длительность откладывания (часы)",
|
||||
"settings_snooze_hours_hint": "Как долго «Отложить» глушит напоминания задачи — из меню задачи, с карточки, голосом или кнопкой «Отложить» на телефоне. Срок выполнения не меняется.",
|
||||
"settings_budget": "Бюджет",
|
||||
"settings_currency": "Валюта",
|
||||
"settings_currency_decimals": "Знаков после запятой в суммах",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Ta bort {n} objekt och alla deras uppgifter? Arkivering behåller historiken i stället.",
|
||||
"bulk_objects_deleted": "{n} objekt borttagna",
|
||||
"bulk_objects_archived": "{n} objekt arkiverade",
|
||||
"bulk_assign": "Tilldela",
|
||||
"bulk_assign_one": "En person",
|
||||
"bulk_assign_rotate": "Flera personer, turvis",
|
||||
"bulk_assign_hint": "Ersätter vem de valda uppgifterna är tilldelade — även en rotation.",
|
||||
"bulk_rotation_min_two": "Välj minst två personer.",
|
||||
"bulk_labels_add": "Lägg till etiketter",
|
||||
"bulk_labels_remove": "Ta bort etiketter",
|
||||
"bulk_edit_hint": "Bara de markerade inställningarna ändras; allt annat förblir som det är.",
|
||||
"bulk_no_notifications": "Inga aviseringar för de här uppgifterna",
|
||||
"bulk_updated": "{n} uppgifter uppdaterade",
|
||||
"bulk_paused": "{n} uppgifter pausade",
|
||||
"bulk_resumed": "{n} uppgifter återupptagna",
|
||||
"bulk_objects_area": "Område angivet för {n} objekt",
|
||||
"bulk_failed": "{n} misslyckades: {reason}",
|
||||
"completing": "Slutför…",
|
||||
"interval": "Intervall",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Anpassad",
|
||||
"history": "Historik",
|
||||
"cost": "Kostnad",
|
||||
"cost_kind_credit": "Kreditering",
|
||||
"cost_credit_hint": "Pengar tillbaka — en försäljning, en återbetalning. Sänker summorna; snittet per utförande räknar inte med det.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Underhållsrapport",
|
||||
"report_generated": "Genererad",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dagar före utgång",
|
||||
"settings_warranty_reminder_hint": "Meddela en gång när ett objekts garanti har så här många dagar kvar.",
|
||||
"settings_snooze_hours": "Snooza-tid (timmar)",
|
||||
"settings_snooze_hours_hint": "Hur länge ”Snooza” tystar en uppgifts påminnelser — från uppgiftsmenyn, ett kort, rösten eller telefonens Snooza-knapp. Förfallodatumet ändras inte.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Decimaler för belopp",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} nesne ve tüm görevleri silinsin mi? Arşivlemek geçmişi korur.",
|
||||
"bulk_objects_deleted": "{n} nesne silindi",
|
||||
"bulk_objects_archived": "{n} nesne arşivlendi",
|
||||
"bulk_assign": "Ata",
|
||||
"bulk_assign_one": "Bir kişi",
|
||||
"bulk_assign_rotate": "Birden çok kişi, sırayla",
|
||||
"bulk_assign_hint": "Seçili görevlerin kime atandığını değiştirir — rotasyon dahil.",
|
||||
"bulk_rotation_min_two": "En az iki kişi seçin.",
|
||||
"bulk_labels_add": "Etiket ekle",
|
||||
"bulk_labels_remove": "Etiketleri kaldır",
|
||||
"bulk_edit_hint": "Yalnızca işaretli ayarlar değişir; geri kalan her şey olduğu gibi kalır.",
|
||||
"bulk_no_notifications": "Bu görevler için bildirim yok",
|
||||
"bulk_updated": "{n} görev güncellendi",
|
||||
"bulk_paused": "{n} görev duraklatıldı",
|
||||
"bulk_resumed": "{n} göreve devam edildi",
|
||||
"bulk_objects_area": "{n} nesne için alan ayarlandı",
|
||||
"bulk_failed": "{n} başarısız: {reason}",
|
||||
"completing": "Tamamlanıyor…",
|
||||
"interval": "Aralık",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Özel",
|
||||
"history": "Geçmiş",
|
||||
"cost": "Maliyet",
|
||||
"cost_kind_credit": "İade",
|
||||
"cost_credit_hint": "Geri gelen para — bir satış, bir iade. Toplamları düşürür; tamamlama başına ortalamaya katılmaz.",
|
||||
"report_button": "Rapor",
|
||||
"report_title": "Bakım raporu",
|
||||
"report_generated": "Oluşturulma",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Bitişten gün önce",
|
||||
"settings_warranty_reminder_hint": "Bir nesnenin garantisinin bitmesine bu kadar gün kaldığında bir kez bildir.",
|
||||
"settings_snooze_hours": "Erteleme süresi (saat)",
|
||||
"settings_snooze_hours_hint": "Ertele'nin bir görevin hatırlatmalarını ne kadar süre sessize aldığı — görev menüsünden, bir karttan, sesle veya telefondaki Ertele düğmesiyle. Son tarih değişmez.",
|
||||
"settings_budget": "Bütçe",
|
||||
"settings_currency": "Para birimi",
|
||||
"settings_currency_decimals": "Tutarlarda ondalık basamak",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Видалити {n} об'єктів з усіма їхніми завданнями? Архівування натомість збереже історію.",
|
||||
"bulk_objects_deleted": "Видалено об'єктів: {n}",
|
||||
"bulk_objects_archived": "Архівовано об'єктів: {n}",
|
||||
"bulk_assign": "Призначити",
|
||||
"bulk_assign_one": "Одна людина",
|
||||
"bulk_assign_rotate": "Кілька людей по черзі",
|
||||
"bulk_assign_hint": "Замінює призначення вибраних завдань — разом із ротацією.",
|
||||
"bulk_rotation_min_two": "Виберіть щонайменше двох людей.",
|
||||
"bulk_labels_add": "Додати мітки",
|
||||
"bulk_labels_remove": "Вилучити мітки",
|
||||
"bulk_edit_hint": "Змінюються лише позначені налаштування, усе інше лишається як є.",
|
||||
"bulk_no_notifications": "Без сповіщень для цих завдань",
|
||||
"bulk_updated": "Оновлено завдань: {n}",
|
||||
"bulk_paused": "Призупинено завдань: {n}",
|
||||
"bulk_resumed": "Відновлено завдань: {n}",
|
||||
"bulk_objects_area": "Зону задано для об'єктів: {n}",
|
||||
"bulk_failed": "Не вдалося: {n} — {reason}",
|
||||
"completing": "Виконується…",
|
||||
"interval": "Інтервал",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Власний",
|
||||
"history": "Історія",
|
||||
"cost": "Вартість",
|
||||
"cost_kind_credit": "Повернення",
|
||||
"cost_credit_hint": "Гроші повернулися — продаж, відшкодування. Зменшує підсумки; середнє за виконання його не враховує.",
|
||||
"report_button": "Звіт",
|
||||
"report_title": "Звіт про обслуговування",
|
||||
"report_generated": "Створено",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Днів до завершення",
|
||||
"settings_warranty_reminder_hint": "Сповістити один раз, коли до завершення гарантії об'єкта залишиться стільки днів.",
|
||||
"settings_snooze_hours": "Тривалість відкладення (години)",
|
||||
"settings_snooze_hours_hint": "Як довго «Відкласти» вимикає нагадування завдання — з меню завдання, картки, голосом або кнопкою «Відкласти» на телефоні. Термін не змінюється.",
|
||||
"settings_budget": "Бюджет",
|
||||
"settings_currency": "Валюта",
|
||||
"settings_currency_decimals": "Знаків після коми в сумах",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "删除 {n} 个对象及其全部任务?归档则会保留其历史记录。",
|
||||
"bulk_objects_deleted": "已删除 {n} 个对象",
|
||||
"bulk_objects_archived": "已归档 {n} 个对象",
|
||||
"bulk_assign": "分配",
|
||||
"bulk_assign_one": "一个人",
|
||||
"bulk_assign_rotate": "多人轮流",
|
||||
"bulk_assign_hint": "替换所选任务的负责人——包括轮换。",
|
||||
"bulk_rotation_min_two": "请至少选择两个人。",
|
||||
"bulk_labels_add": "添加标签",
|
||||
"bulk_labels_remove": "移除标签",
|
||||
"bulk_edit_hint": "只有勾选的设置会改变,其余保持不变。",
|
||||
"bulk_no_notifications": "这些任务不发送通知",
|
||||
"bulk_updated": "已更新 {n} 个任务",
|
||||
"bulk_paused": "已暂停 {n} 个任务",
|
||||
"bulk_resumed": "已继续 {n} 个任务",
|
||||
"bulk_objects_area": "已为 {n} 个对象设置区域",
|
||||
"bulk_failed": "{n} 项失败:{reason}",
|
||||
"completing": "正在完成…",
|
||||
"interval": "间隔",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "自定义",
|
||||
"history": "历史记录",
|
||||
"cost": "成本",
|
||||
"cost_kind_credit": "收入",
|
||||
"cost_credit_hint": "回收的钱——出售、退款。会降低总计;每次完成的平均值不计入。",
|
||||
"report_button": "报告",
|
||||
"report_title": "维护报告",
|
||||
"report_generated": "生成于",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "到期前天数",
|
||||
"settings_warranty_reminder_hint": "当物品保修距到期还有这么多天时通知一次。",
|
||||
"settings_snooze_hours": "稍后提醒间隔 (小时)",
|
||||
"settings_snooze_hours_hint": "“稍后提醒”让任务提醒静音的时长——无论来自任务菜单、卡片、语音还是手机上的稍后提醒按钮。截止日期保持不变。",
|
||||
"settings_budget": "预算",
|
||||
"settings_currency": "货币单位",
|
||||
"settings_currency_decimals": "金额小数位数",
|
||||
|
||||
@@ -438,7 +438,7 @@ export class MaintenanceCalendarCard extends LitElement {
|
||||
${predictionSubtitle}
|
||||
${recurEvery}
|
||||
</div>
|
||||
${ev.avg_cost != null && ev.avg_cost > 0
|
||||
${ev.avg_cost != null && ev.avg_cost !== 0
|
||||
? html`<span class="cal-event-cost">${formatCost(ev.avg_cost, currencySymbol, L)}</span>`
|
||||
: nothing}
|
||||
</div>
|
||||
|
||||
@@ -104,6 +104,7 @@ import type {
|
||||
} from "./components/history-edit-dialog";
|
||||
import "./components/confirm-dialog";
|
||||
import type { MaintenanceConfirmDialog } from "./components/confirm-dialog";
|
||||
import type { BulkChanges, BulkMode, MaintenanceBulkEditDialog } from "./components/bulk-edit-dialog";
|
||||
import "./components/storage-section-card";
|
||||
import "./components/seasonal-overrides-dialog";
|
||||
import type { SeasonalOverridesDialog } from "./components/seasonal-overrides-dialog";
|
||||
@@ -130,7 +131,7 @@ import { renderStatusBadge } from "./renderers/status";
|
||||
import { TOAST_MS, ACTION_TOAST_MS } from "./helpers/toast";
|
||||
import { buildHistoryEntryDraft } from "./helpers/history-draft";
|
||||
import { readingSlotDelta } from "./helpers/reading-slots";
|
||||
import { bulkResultMessage, runWs, runWsEach } from "./helpers/ws-run";
|
||||
import { bulkResultMessage, codedFailures, runWs, runWsEach } from "./helpers/ws-run";
|
||||
import { snoozedMessage, type SnoozeResult } from "./helpers/snooze";
|
||||
import { STATUS_ORDER, statusRank } from "./status-constants";
|
||||
|
||||
@@ -451,6 +452,7 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
import("./components/adopt-problem-sensors-dialog"),
|
||||
import("./components/suggested-setups-dialog"),
|
||||
import("./components/settings-view"),
|
||||
import("./components/bulk-edit-dialog"),
|
||||
]).then(() => this.updateComplete);
|
||||
}
|
||||
return this._lazyUi;
|
||||
@@ -2269,10 +2271,15 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
|
||||
/** One WS call per selected object, sequentially (config entries are
|
||||
* removed one at a time on the backend). */
|
||||
private _runObjBulk(type: string, doneMsg: (n: number) => string, undo?: (ids: string[]) => Promise<void>): Promise<void> {
|
||||
private _runObjBulk(
|
||||
type: string,
|
||||
doneMsg: (n: number) => string,
|
||||
undo?: (ids: string[]) => Promise<void>,
|
||||
extra: Record<string, unknown> = {},
|
||||
): Promise<void> {
|
||||
return this._runBulkItems(
|
||||
[...this._objBulkSelected],
|
||||
(id) => ({ type, entry_id: id }),
|
||||
(id) => ({ type, entry_id: id, ...extra }),
|
||||
doneMsg,
|
||||
() => {
|
||||
this._objBulkSelected = new Set();
|
||||
@@ -2296,6 +2303,40 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
await this._runObjBulk("maintenance_supporter/object/delete", (k) => t("bulk_objects_deleted", this._lang).replace("{n}", String(k)));
|
||||
}
|
||||
|
||||
/** D#199: one Home Assistant area for every selected object (after a big
|
||||
* template import, the step that otherwise takes one dialog per object). */
|
||||
private async _objBulkArea(): Promise<void> {
|
||||
const ids = [...this._objBulkSelected];
|
||||
if (ids.length === 0) return;
|
||||
const areas = Object.values(this.hass?.areas || {}).sort((a, b) => a.name.localeCompare(b.name));
|
||||
const dlg = this.shadowRoot!.querySelector<MaintenanceConfirmDialog>("maintenance-confirm-dialog");
|
||||
const result = await dlg?.prompt({
|
||||
title: t("area", this._lang),
|
||||
message: t("bulk_n_selected", this._lang).replace("{n}", String(ids.length)),
|
||||
confirmText: t("save", this._lang),
|
||||
inputLabel: t("area", this._lang),
|
||||
inputValue: "",
|
||||
options: [{ value: "", label: t("no_area", this._lang) }, ...areas.map((a) => ({ value: a.area_id, label: a.name }))],
|
||||
});
|
||||
if (!result?.confirmed) return;
|
||||
const before = new Map(ids.map((id) => [id, this._getObject(id)?.object.area_id ?? null]));
|
||||
void this._runObjBulk(
|
||||
"maintenance_supporter/object/update",
|
||||
(n) => t("bulk_objects_area", this._lang).replace("{n}", String(n)),
|
||||
async (done) => {
|
||||
for (const id of done) {
|
||||
try {
|
||||
await this.hass.connection.sendMessagePromise({
|
||||
type: "maintenance_supporter/object/update", entry_id: id, area_id: before.get(id) ?? null,
|
||||
});
|
||||
} catch { /* best effort */ }
|
||||
}
|
||||
await this._loadData();
|
||||
},
|
||||
{ area_id: result.value || null },
|
||||
);
|
||||
}
|
||||
|
||||
private _objBulkArchive(): void {
|
||||
void this._runObjBulk(
|
||||
"maintenance_supporter/object/archive",
|
||||
@@ -2322,6 +2363,9 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
</label>
|
||||
<span class="bulk-count">${t("bulk_n_selected", L).replace("{n}", String(n))}</span>
|
||||
<span class="bulk-actions">
|
||||
<ha-button appearance="plain" class="obj-bulk-area" .disabled=${n === 0 || this._actionLoading} @click=${() => void this._objBulkArea()}>
|
||||
<ha-icon icon="mdi:floor-plan"></ha-icon> ${t("area", L)}…
|
||||
</ha-button>
|
||||
<ha-button appearance="plain" class="obj-bulk-archive" .disabled=${n === 0 || this._actionLoading} @click=${() => this._objBulkArchive()}>
|
||||
<ha-icon icon="mdi:archive-outline"></ha-icon> ${t("archive", L)}
|
||||
</ha-button>
|
||||
@@ -2360,6 +2404,90 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
/** D#199: one change — assignment, labels or settings — to every selected
|
||||
* task. tasks/update_many writes and reloads each object once (looping
|
||||
* task/update reloaded an object once per task) and answers with the
|
||||
* replaced values, which become the undo. */
|
||||
private async _bulkEdit(mode: BulkMode, rows: TaskRow[]): Promise<void> {
|
||||
const selected = rows.filter((r) => this._bulkSelected.has(this._bulkKey(r)));
|
||||
if (selected.length === 0) return;
|
||||
const dlg = await this._ui<MaintenanceBulkEditDialog>("maintenance-bulk-edit-dialog");
|
||||
const users = this._userService ? await this._userService.getUsers() : [];
|
||||
const changes = await dlg?.open(mode, selected, users);
|
||||
if (!changes) return;
|
||||
await this._sendBulkUpdate(selected.map((r) => ({ entry_id: r.entry_id, task_id: r.task_id })), changes);
|
||||
}
|
||||
|
||||
private async _sendBulkUpdate(
|
||||
items: Array<{ entry_id: string; task_id: string; changes?: BulkChanges }>,
|
||||
changes: BulkChanges | null,
|
||||
): Promise<void> {
|
||||
const res = await runWs<{
|
||||
updated: unknown[];
|
||||
failed: Array<{ entry_id: string; task_id: string; code: string }>;
|
||||
previous: Array<{ entry_id: string; task_id: string; changes: BulkChanges }>;
|
||||
}>(this, { type: "maintenance_supporter/tasks/update_many", items, ...(changes ? { changes } : {}) }, {
|
||||
busy: (b) => { this._actionLoading = b; },
|
||||
onError: (m) => this._showToast(m, "error"),
|
||||
});
|
||||
if (!res) return;
|
||||
const isUndo = changes === null;
|
||||
if (!isUndo) {
|
||||
this._bulkSelected = new Set();
|
||||
this._bulkMode = false;
|
||||
}
|
||||
await this._loadData();
|
||||
const failed = codedFailures(res.failed, this._lang);
|
||||
const msg = bulkResultMessage(t("bulk_updated", this._lang).replace("{n}", String(res.updated.length)), failed, this._lang);
|
||||
if (!isUndo && res.previous.length > 0) {
|
||||
this._showUndoToast(msg, () => void this._sendBulkUpdate(res.previous, null));
|
||||
} else {
|
||||
this._showToast(msg, failed.length > 0 ? "error" : "info");
|
||||
}
|
||||
}
|
||||
|
||||
/** Pause every selected task that runs (one date for all, optional). */
|
||||
private async _bulkPause(rows: TaskRow[]): Promise<void> {
|
||||
const running = rows.filter((r) => this._bulkSelected.has(this._bulkKey(r)) && r.status !== "paused");
|
||||
if (running.length === 0) return;
|
||||
const dlg = this.shadowRoot!.querySelector<MaintenanceConfirmDialog>("maintenance-confirm-dialog");
|
||||
const result = await dlg?.prompt({
|
||||
title: t("pause_task", this._lang),
|
||||
message: t("pause_task_prompt", this._lang),
|
||||
confirmText: t("pause_task", this._lang),
|
||||
inputLabel: t("pause_until_label", this._lang),
|
||||
inputType: "date",
|
||||
});
|
||||
if (!result?.confirmed) return;
|
||||
void this._runBulk(
|
||||
running,
|
||||
(row) => ({
|
||||
type: "maintenance_supporter/task/pause", entry_id: row.entry_id, task_id: row.task_id,
|
||||
...(result.value ? { until: result.value } : {}),
|
||||
}),
|
||||
(n) => t("bulk_paused", this._lang).replace("{n}", String(n)),
|
||||
async (done) => {
|
||||
for (const k of done) {
|
||||
try {
|
||||
await this.hass.connection.sendMessagePromise({
|
||||
type: "maintenance_supporter/task/resume", entry_id: k.entry_id, task_id: k.task_id,
|
||||
});
|
||||
} catch { /* best effort */ }
|
||||
}
|
||||
await this._loadData();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** Resume every selected paused task — each starts a fresh cycle. */
|
||||
private _bulkResume(rows: TaskRow[]): void {
|
||||
void this._runBulk(
|
||||
rows.filter((r) => r.status === "paused"),
|
||||
(row) => ({ type: "maintenance_supporter/task/resume", entry_id: row.entry_id, task_id: row.task_id }),
|
||||
(n) => t("bulk_resumed", this._lang).replace("{n}", String(n)),
|
||||
);
|
||||
}
|
||||
|
||||
// --- Actions ---
|
||||
|
||||
/** Run a mutating WS action: loading state, data reload, and — on failure —
|
||||
@@ -3085,6 +3213,7 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
.objects=${this._objects}
|
||||
@group-saved=${this._onDialogEvent}
|
||||
></maintenance-group-dialog>
|
||||
<maintenance-bulk-edit-dialog .hass=${this.hass}></maintenance-bulk-edit-dialog>
|
||||
<maintenance-adopt-problem-sensors-dialog
|
||||
.hass=${this.hass}
|
||||
@problem-sensors-adopted=${(e: CustomEvent) => this._onProblemSensorsAdopted(e)}
|
||||
@@ -3693,6 +3822,11 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
${this._bulkMenuOpen ? html`
|
||||
<div class="popup-menu" @click=${(e: Event) => e.stopPropagation()}>
|
||||
<div class="popup-menu-item bulk-move" @click=${() => { this._bulkMenuOpen = false; void this._bulkMove(rows); }}>${t("move_task", L)}</div>
|
||||
<div class="popup-menu-item bulk-assign" @click=${() => { this._bulkMenuOpen = false; void this._bulkEdit("assign", rows); }}>${t("bulk_assign", L)}…</div>
|
||||
<div class="popup-menu-item bulk-labels" @click=${() => { this._bulkMenuOpen = false; void this._bulkEdit("labels", rows); }}>${t("labels", L)}…</div>
|
||||
<div class="popup-menu-item bulk-edit" @click=${() => { this._bulkMenuOpen = false; void this._bulkEdit("edit", rows); }}>${t("edit", L)}…</div>
|
||||
<div class="popup-menu-item bulk-pause" @click=${() => { this._bulkMenuOpen = false; void this._bulkPause(rows); }}>${t("pause_task", L)}…</div>
|
||||
<div class="popup-menu-item bulk-resume" @click=${() => { this._bulkMenuOpen = false; this._bulkResume(rows); }}>${t("resume_task", L)}</div>
|
||||
</div>
|
||||
` : nothing}
|
||||
</span>
|
||||
|
||||
@@ -9,21 +9,21 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"pdfjs-dist": "^4.10.38",
|
||||
"playwright": "1.62"
|
||||
"playwright": "1.63"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@open-wc/testing": "^5.0.0",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/react": "^19.2.18",
|
||||
"@types/react": "^19.3.0",
|
||||
"@web/dev-server-esbuild": "^2.0.0",
|
||||
"@web/test-runner": "^1.0.0",
|
||||
"@web/test-runner-commands": "^1.0.1",
|
||||
"@web/test-runner-playwright": "^1.0.0",
|
||||
"esbuild": "^0.28.2",
|
||||
"lit": "^3.3.3",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8",
|
||||
"react": "^19.3.0",
|
||||
"react-dom": "^19.3.0",
|
||||
"typescript": "^7.0.2"
|
||||
}
|
||||
},
|
||||
@@ -1607,9 +1607,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
|
||||
"integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
|
||||
"version": "19.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz",
|
||||
"integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3273,6 +3273,7 @@
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -4441,27 +4442,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
|
||||
"integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
|
||||
"version": "1.63.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz",
|
||||
"integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.62.1"
|
||||
"playwright-core": "1.63.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
|
||||
"integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
|
||||
"version": "1.63.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz",
|
||||
"integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
@@ -4620,9 +4618,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.8",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
|
||||
"integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
|
||||
"version": "19.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz",
|
||||
"integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -4630,16 +4628,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.2.8",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
|
||||
"integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
|
||||
"version": "19.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz",
|
||||
"integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
"scheduler": "^0.28.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.2.8"
|
||||
"react": "^19.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
@@ -4873,9 +4871,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
||||
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz",
|
||||
"integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -13,20 +13,20 @@
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@open-wc/testing": "^5.0.0",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/react": "^19.2.18",
|
||||
"@types/react": "^19.3.0",
|
||||
"@web/dev-server-esbuild": "^2.0.0",
|
||||
"@web/test-runner": "^1.0.0",
|
||||
"@web/test-runner-commands": "^1.0.1",
|
||||
"@web/test-runner-playwright": "^1.0.0",
|
||||
"esbuild": "^0.28.2",
|
||||
"lit": "^3.3.3",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8",
|
||||
"react": "^19.3.0",
|
||||
"react-dom": "^19.3.0",
|
||||
"typescript": "^7.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"pdfjs-dist": "^4.10.38",
|
||||
"playwright": "1.62"
|
||||
"playwright": "1.63"
|
||||
},
|
||||
"overrides": {
|
||||
"esbuild": "$esbuild",
|
||||
|
||||
@@ -55,11 +55,16 @@ export const panelStyles = css`
|
||||
color: var(--app-header-text-color, white);
|
||||
}
|
||||
|
||||
.breadcrumbs { display: flex; align-items: center; gap: 4px; }
|
||||
.breadcrumbs a { color: inherit; opacity: 0.8; cursor: pointer; text-decoration: none; }
|
||||
/* One line, never wider than the header: the path broke only at its
|
||||
spaces, so on a phone "Maintenance / HVAC System / Filter Replacement"
|
||||
pushed the search button off screen. The way up shrinks first; the
|
||||
current page keeps its name as long as it can. */
|
||||
.breadcrumbs { display: flex; align-items: center; gap: 4px; min-width: 0; }
|
||||
.breadcrumbs a, .breadcrumbs .current { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.breadcrumbs a { flex: 0 10 auto; color: inherit; opacity: 0.8; cursor: pointer; text-decoration: none; }
|
||||
.breadcrumbs a:hover { opacity: 1; text-decoration: underline; }
|
||||
.breadcrumbs .sep { opacity: 0.5; margin: 0 4px; }
|
||||
.breadcrumbs .current { font-weight: 500; }
|
||||
.breadcrumbs .sep { flex: none; opacity: 0.5; margin: 0 4px; }
|
||||
.breadcrumbs .current { flex: 0 1 auto; font-weight: 500; }
|
||||
|
||||
.content { flex: 1; overflow-y: auto; padding: 0 16px 16px; }
|
||||
|
||||
@@ -742,11 +747,15 @@ export const panelStyles = css`
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Name, object and up to six badges: on a phone they wrap — one row drew
|
||||
a postponed, assigned, muted task 505 px wide on a 360 px screen. */
|
||||
.task-header-title {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.task-name-breadcrumb,
|
||||
@@ -754,6 +763,7 @@ export const panelStyles = css`
|
||||
cursor: pointer;
|
||||
color: var(--primary-text-color);
|
||||
text-decoration: none;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.task-name-breadcrumb:hover,
|
||||
@@ -994,13 +1004,19 @@ export const panelStyles = css`
|
||||
.area-chart { margin: 4px 0 8px; }
|
||||
.area-chart-max { font-size: 11px; color: var(--secondary-text-color); padding-bottom: 2px; }
|
||||
/* Hairline gridline at the maximum (labelled above) and the baseline;
|
||||
bars <= 24px with a 2px gap and a rounded data end (dataviz specs). */
|
||||
bars <= 24px with a 2px gap and a rounded data end (dataviz specs).
|
||||
With a credit month (#200) the baseline is the minimum (labelled
|
||||
below) and a zero line runs between. */
|
||||
.area-chart-bars {
|
||||
position: relative;
|
||||
display: flex; align-items: flex-end; gap: 2px; height: 140px;
|
||||
border-top: 1px solid var(--divider-color); border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
.area-chart-zero { position: absolute; left: 0; right: 0; border-top: 1px solid var(--secondary-text-color); opacity: 0.5; pointer-events: none; }
|
||||
.area-chart-min { font-size: 11px; color: var(--secondary-text-color); padding-top: 2px; }
|
||||
.area-bar { flex: 1 1 0; min-width: 0; height: 100%; display: flex; align-items: flex-end; justify-content: center; }
|
||||
.area-bar-fill { width: 100%; max-width: 24px; min-height: 1px; background: var(--primary-color); border-radius: 4px 4px 0 0; }
|
||||
.area-bar-fill { position: relative; width: 100%; max-width: 24px; min-height: 1px; background: var(--primary-color); border-radius: 4px 4px 0 0; }
|
||||
.area-bar-fill.credit { background: var(--success-color, #43a047); border-radius: 0 0 4px 4px; }
|
||||
.area-bar:hover .area-bar-fill { opacity: 0.75; }
|
||||
.area-chart-axis { display: flex; gap: 2px; margin-top: 4px; }
|
||||
.area-bar-label {
|
||||
|
||||
@@ -28,7 +28,8 @@ export function renderCostDurationCard(
|
||||
const completedEntries = task.history.filter((h) => h.type === "completed" && (entrySpend(h) != null || h.duration != null));
|
||||
if (completedEntries.length < 2) return nothing;
|
||||
|
||||
const anyCost = completedEntries.some((h) => (entrySpend(h) ?? 0) > 0);
|
||||
// A credit (#200) is a cost too: a bar below the zero line.
|
||||
const anyCost = completedEntries.some((h) => (entrySpend(h) ?? 0) !== 0);
|
||||
const anyDuration = completedEntries.some((h) => (h.duration ?? 0) > 0);
|
||||
if (!anyCost && !anyDuration) return nothing;
|
||||
|
||||
@@ -67,7 +68,8 @@ function renderHistoryChart(task: MaintenanceTask, lang: string, toggle: "cost"
|
||||
|
||||
if (entries.length < 2) return nothing;
|
||||
|
||||
const dataCost = entries.some((e) => e.cost > 0);
|
||||
const dataCost = entries.some((e) => e.cost !== 0);
|
||||
const anyCredit = entries.some((e) => e.cost < 0);
|
||||
const dataDuration = entries.some((e) => e.duration > 0);
|
||||
if (!dataCost && !dataDuration) return nothing;
|
||||
|
||||
@@ -93,9 +95,13 @@ function renderHistoryChart(task: MaintenanceTask, lang: string, toggle: "cost"
|
||||
const withYear = needsYear(tsMin, tsMax);
|
||||
const toX = (ts: number) => PAD_L + ((ts - t0) / (t1 - t0)) * plotW;
|
||||
|
||||
const costAxis = niceTicks(0, Math.max(...entries.map((e) => e.cost)) || 1, 3);
|
||||
// The cost axis reaches below zero only for a credit (#200).
|
||||
const costMin = Math.min(0, ...entries.map((e) => e.cost));
|
||||
const costMax = Math.max(0, ...entries.map((e) => e.cost));
|
||||
const costAxis = niceTicks(costMin, costMax > costMin ? costMax : costMin + 1, 3);
|
||||
const durAxis = niceTicks(0, Math.max(...entries.map((e) => e.duration)) || 1, 3);
|
||||
const costY = (v: number) => PAD_T + (1 - v / (costAxis.niceMax || 1)) * plotH;
|
||||
const costSpan = costAxis.niceMax - costAxis.niceMin || 1;
|
||||
const costY = (v: number) => PAD_T + (1 - (v - costAxis.niceMin) / costSpan) * plotH;
|
||||
const durY = (v: number) => PAD_T + (1 - v / (durAxis.niceMax || 1)) * plotH;
|
||||
|
||||
// Bars keep a readable width even when completions crowd together.
|
||||
@@ -122,12 +128,18 @@ function renderHistoryChart(task: MaintenanceTask, lang: string, toggle: "cost"
|
||||
return svg`<text x="${W - PAD_R + 6}" y="${px(y + 3.5)}" text-anchor="start" fill="var(--accent-color, #ff9800)" font-size="10.5">${fmtNum(v, lang)}m</text>`;
|
||||
}) : nothing}
|
||||
|
||||
${showCost ? entries.filter((e) => e.cost > 0).map((e) => svg`
|
||||
<rect x="${px(toX(e.ts) - barW / 2)}" y="${px(costY(e.cost))}" width="${px(barW)}" height="${px(plotB - costY(e.cost))}"
|
||||
fill="var(--primary-color)" opacity="0.6" rx="2">
|
||||
<title>${fmtDateTick(e.ts, lang, true)}: ${formatCost(e.cost, currencySymbol, lang)}${e.duration ? ` · ${e.duration}m` : ""}</title>
|
||||
</rect>
|
||||
`) : nothing}
|
||||
${showCost && costAxis.niceMin < 0 ? svg`
|
||||
<line class="cost-zero" x1="${PAD_L}" y1="${px(costY(0))}" x2="${W - PAD_R}" y2="${px(costY(0))}" stroke="var(--secondary-text-color)" stroke-width="1" opacity="0.7" />
|
||||
` : nothing}
|
||||
${showCost ? entries.filter((e) => e.cost !== 0).map((e) => {
|
||||
const top = costY(Math.max(e.cost, 0));
|
||||
const credit = e.cost < 0;
|
||||
return svg`
|
||||
<rect class="${credit ? "credit-bar" : "cost-bar"}" x="${px(toX(e.ts) - barW / 2)}" y="${px(top)}" width="${px(barW)}" height="${px(costY(Math.min(e.cost, 0)) - top)}"
|
||||
fill="${credit ? "var(--success-color, #43a047)" : "var(--primary-color)"}" opacity="0.6" rx="2">
|
||||
<title>${fmtDateTick(e.ts, lang, true)}: ${credit ? `${t("cost_kind_credit", lang)} ` : ""}${formatCost(Math.abs(e.cost), currencySymbol, lang)}${e.duration ? ` · ${e.duration}m` : ""}</title>
|
||||
</rect>`;
|
||||
}) : nothing}
|
||||
${showDuration ? svg`
|
||||
<polyline points="${entries.map((e) => `${px(toX(e.ts))},${px(durY(e.duration))}`).join(" ")}"
|
||||
fill="none" stroke="var(--accent-color, #ff9800)" stroke-width="2" stroke-linejoin="round" />
|
||||
@@ -147,6 +159,7 @@ function renderHistoryChart(task: MaintenanceTask, lang: string, toggle: "cost"
|
||||
</div>
|
||||
<div class="chart-legend">
|
||||
${showCost ? html`<span class="legend-item"><span class="legend-swatch" style="background:var(--primary-color);opacity:0.6"></span>${t("cost", lang)}</span>` : nothing}
|
||||
${showCost && anyCredit ? html`<span class="legend-item"><span class="legend-swatch" style="background:var(--success-color, #43a047);opacity:0.6"></span>${t("cost_kind_credit", lang)}</span>` : nothing}
|
||||
${showDuration ? html`<span class="legend-item"><span class="legend-swatch" style="background:var(--accent-color, #ff9800)"></span>${t("duration", lang)}</span>` : nothing}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -225,7 +225,7 @@ export function renderHistoryEntry(entry: HistoryEntry, ctx: HistoryEntryContext
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="history-date">${formatDateTime(entry.timestamp, L)}</div>
|
||||
${entry.notes ? html`<div>${historyNoteText(entry.notes, L)}</div>` : nothing}
|
||||
${entry.notes ? html`<div class="history-notes">${historyNoteText(entry.notes, L)}</div>` : nothing}
|
||||
${renderHistoryPhotos(ctx.hass, entry)}
|
||||
${renderHistoryReadings(entry, ctx)}
|
||||
${entry.cost != null || entry.duration != null || entry.trigger_value != null || partsCost != null
|
||||
@@ -233,7 +233,9 @@ export function renderHistoryEntry(entry: HistoryEntry, ctx: HistoryEntryContext
|
||||
${entry.cost != null
|
||||
? stockPurchase
|
||||
? html`<span>${t("history_purchase_stock", L).replace("{amount}", formatCost(entry.cost, ctx.currencySymbol, L))}</span>`
|
||||
: html`<span>${t("cost", L)}: ${formatCost(entry.cost, ctx.currencySymbol, L)}</span>`
|
||||
: entry.cost < 0
|
||||
? html`<span class="history-credit">${t("cost_kind_credit", L)}: ${formatCost(-entry.cost, ctx.currencySymbol, L)}</span>`
|
||||
: html`<span>${t("cost", L)}: ${formatCost(entry.cost, ctx.currencySymbol, L)}</span>`
|
||||
: nothing}
|
||||
${partsCost != null
|
||||
? html`<span title=${t(partsCounted ? "history_parts_counted_hint" : "history_parts_info_hint", L)}>${t(partsCounted ? "history_parts_counted" : "history_parts_info", L).replace("{amount}", formatCost(partsCost, ctx.currencySymbol, L))}</span>`
|
||||
|
||||
@@ -352,7 +352,11 @@ function renderTaskMeta(task: MaintenanceTask, ctx: TaskDetailContext) {
|
||||
/** KPI bar with 7 cards. */
|
||||
function renderKPIBar(task: MaintenanceTask, ctx: TaskDetailContext) {
|
||||
const L = ctx.lang;
|
||||
const avgCost = task.times_performed > 0 ? task.total_cost / task.times_performed : 0;
|
||||
// Per completion with the credits left out (#200: a sale is no job); the
|
||||
// old division where a payload predates the field.
|
||||
const avgCost = task.average_cost !== undefined
|
||||
? task.average_cost ?? 0
|
||||
: task.times_performed > 0 ? task.total_cost / task.times_performed : 0;
|
||||
const daysClass = task.days_until_due !== null && task.days_until_due !== undefined
|
||||
? (task.days_until_due < 0 ? "overdue" : (task.days_until_due <= task.warning_days ? "warning" : ""))
|
||||
: "";
|
||||
|
||||
@@ -181,9 +181,12 @@ export function formatNumber(n: number, lang?: string, digits?: number | Intl.Nu
|
||||
/** Amount + currency symbol ("13 €" / "12.50 €"; the symbol is optional) —
|
||||
* every cost figure in the UI and the printables goes through here. The
|
||||
* fraction digits come from the global `currency_decimals` setting (default
|
||||
* 0) unless a caller pins them. */
|
||||
* 0) unless a caller pins them. A credit (#200) is a negative amount; one
|
||||
* that rounds away prints "0", never Intl's "-0". */
|
||||
export function formatCost(amount: number, symbol?: string, lang?: string, digits?: number): string {
|
||||
const num = formatNumber(amount, lang, digits ?? currencyDecimals());
|
||||
const d = digits ?? currencyDecimals();
|
||||
const shown = Math.abs(amount) < 0.5 / Math.pow(10, d) ? 0 : amount;
|
||||
const num = formatNumber(shown, lang, d);
|
||||
return symbol ? `${num} ${symbol}` : num;
|
||||
}
|
||||
|
||||
@@ -779,6 +782,10 @@ export const sharedStyles = css`
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
/* A note keeps its line breaks — what was done, one line each (#202) —
|
||||
and a long link wraps instead of widening the card. */
|
||||
.history-notes { white-space: pre-wrap; overflow-wrap: anywhere; }
|
||||
|
||||
.history-details {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
@@ -786,6 +793,7 @@ export const sharedStyles = css`
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
.history-details .history-credit { color: var(--success-color, #43a047); }
|
||||
|
||||
/* History filter chips */
|
||||
.history-filters {
|
||||
|
||||
@@ -321,7 +321,10 @@ export interface MaintenanceTask {
|
||||
trigger_baseline_value?: number | null;
|
||||
trigger_entity_state?: string;
|
||||
times_performed: number;
|
||||
/** Net of credits (#200): a negative cost is money back. */
|
||||
total_cost: number;
|
||||
/** Per completion with the credits left out; null without a completion. */
|
||||
average_cost?: number | null;
|
||||
average_duration?: number | null;
|
||||
// Adaptive scheduling
|
||||
adaptive_config?: AdaptiveConfig | null;
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Smazat {n} objektů včetně všech jejich úkolů? Archivace místo toho zachová historii.",
|
||||
"bulk_objects_deleted": "Smazáno objektů: {n}",
|
||||
"bulk_objects_archived": "Archivováno objektů: {n}",
|
||||
"bulk_assign": "Přiřadit",
|
||||
"bulk_assign_one": "Jedna osoba",
|
||||
"bulk_assign_rotate": "Více lidí, střídavě",
|
||||
"bulk_assign_hint": "Nahradí přiřazení vybraných úkolů — včetně rotace.",
|
||||
"bulk_rotation_min_two": "Vyberte alespoň dvě osoby.",
|
||||
"bulk_labels_add": "Přidat štítky",
|
||||
"bulk_labels_remove": "Odebrat štítky",
|
||||
"bulk_edit_hint": "Změní se jen zaškrtnutá nastavení, vše ostatní zůstane beze změny.",
|
||||
"bulk_no_notifications": "Bez oznámení pro tyto úkoly",
|
||||
"bulk_updated": "Aktualizováno {n} úkolů",
|
||||
"bulk_paused": "Pozastaveno {n} úkolů",
|
||||
"bulk_resumed": "Obnoveno {n} úkolů",
|
||||
"bulk_objects_area": "Oblast nastavena pro objekty: {n}",
|
||||
"bulk_failed": "Nezdařilo se: {n} — {reason}",
|
||||
"completing": "Dokončování…",
|
||||
"interval": "Interval",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Vlastní",
|
||||
"history": "Historie",
|
||||
"cost": "Náklady",
|
||||
"cost_kind_credit": "Dobropis",
|
||||
"cost_credit_hint": "Peníze zpět — prodej, vrácení. Snižuje součty; průměr na dokončení ho nezapočítává.",
|
||||
"report_button": "Zpráva",
|
||||
"report_title": "Zpráva o údržbě",
|
||||
"report_generated": "Vytvořeno",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dny před vypršením",
|
||||
"settings_warranty_reminder_hint": "Upozornit jednou, když do vypršení záruky objektu zbývá tolik dní.",
|
||||
"settings_snooze_hours": "Doba odložení (hodiny)",
|
||||
"settings_snooze_hours_hint": "Jak dlouho „Odložit“ ztiší připomínky úkolu — z nabídky úkolu, z karty, hlasem nebo tlačítkem Odložit v telefonu. Termín zůstává beze změny.",
|
||||
"settings_budget": "Rozpočet",
|
||||
"settings_currency": "Měna",
|
||||
"settings_currency_decimals": "Desetinná místa částek",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Slet {n} objekter og alle deres opgaver? Arkivering bevarer i stedet historikken.",
|
||||
"bulk_objects_deleted": "{n} objekter slettet",
|
||||
"bulk_objects_archived": "{n} objekter arkiveret",
|
||||
"bulk_assign": "Tildel",
|
||||
"bulk_assign_one": "Én person",
|
||||
"bulk_assign_rotate": "Flere personer, på skift",
|
||||
"bulk_assign_hint": "Erstatter, hvem de valgte opgaver er tildelt — også en rotation.",
|
||||
"bulk_rotation_min_two": "Vælg mindst to personer.",
|
||||
"bulk_labels_add": "Tilføj etiketter",
|
||||
"bulk_labels_remove": "Fjern etiketter",
|
||||
"bulk_edit_hint": "Kun de afkrydsede indstillinger ændres; alt andet forbliver som det er.",
|
||||
"bulk_no_notifications": "Ingen notifikationer for disse opgaver",
|
||||
"bulk_updated": "{n} opgaver opdateret",
|
||||
"bulk_paused": "{n} opgaver sat på pause",
|
||||
"bulk_resumed": "{n} opgaver genoptaget",
|
||||
"bulk_objects_area": "Område angivet for {n} objekter",
|
||||
"bulk_failed": "{n} mislykkedes: {reason}",
|
||||
"completing": "Fuldfører…",
|
||||
"interval": "Interval",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Tilpasset",
|
||||
"history": "Historik",
|
||||
"cost": "Omkostning",
|
||||
"cost_kind_credit": "Kreditering",
|
||||
"cost_credit_hint": "Penge tilbage — et salg, en refusion. Sænker totalerne; gennemsnittet pr. udførelse medregner det ikke.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Vedligeholdelsesrapport",
|
||||
"report_generated": "Genereret",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Dage før udløb",
|
||||
"settings_warranty_reminder_hint": "Giv besked én gang, når et objekts garanti udløber om så mange dage.",
|
||||
"settings_snooze_hours": "Udsættelsesvarighed (timer)",
|
||||
"settings_snooze_hours_hint": "Hvor længe „Udsæt“ gør en opgaves påmindelser tavse — fra opgavemenuen, et kort, stemmen eller telefonens Udsæt-knap. Forfaldsdatoen forbliver uændret.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Decimaler for beløb",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} Objekte mit allen ihren Aufgaben löschen? Archivieren behält stattdessen die Historie.",
|
||||
"bulk_objects_deleted": "{n} Objekte gelöscht",
|
||||
"bulk_objects_archived": "{n} Objekte archiviert",
|
||||
"bulk_assign": "Zuweisen",
|
||||
"bulk_assign_one": "Eine Person",
|
||||
"bulk_assign_rotate": "Mehrere Personen, abwechselnd",
|
||||
"bulk_assign_hint": "Ersetzt, wem die ausgewählten Aufgaben zugewiesen sind — eine Rotation eingeschlossen.",
|
||||
"bulk_rotation_min_two": "Mindestens zwei Personen auswählen.",
|
||||
"bulk_labels_add": "Labels hinzufügen",
|
||||
"bulk_labels_remove": "Labels entfernen",
|
||||
"bulk_edit_hint": "Nur die angehakten Einstellungen ändern sich, alles andere bleibt, wie es ist.",
|
||||
"bulk_no_notifications": "Keine Benachrichtigungen für diese Aufgaben",
|
||||
"bulk_updated": "{n} Aufgaben aktualisiert",
|
||||
"bulk_paused": "{n} Aufgaben pausiert",
|
||||
"bulk_resumed": "{n} Aufgaben fortgesetzt",
|
||||
"bulk_objects_area": "Bereich für {n} Objekte gesetzt",
|
||||
"bulk_failed": "{n} fehlgeschlagen: {reason}",
|
||||
"completing": "Wird erledigt…",
|
||||
"interval": "Intervall",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Benutzerdefiniert",
|
||||
"history": "Verlauf",
|
||||
"cost": "Kosten",
|
||||
"cost_kind_credit": "Gutschrift",
|
||||
"cost_credit_hint": "Geld zurück – ein Verkauf, eine Erstattung. Senkt die Summen; der Durchschnitt pro Erledigung lässt es weg.",
|
||||
"report_button": "Bericht",
|
||||
"report_title": "Wartungsbericht",
|
||||
"report_generated": "Erstellt",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Tage vor Ablauf",
|
||||
"settings_warranty_reminder_hint": "Einmal benachrichtigen, wenn die Garantie eines Objekts so viele Tage vor Ablauf steht.",
|
||||
"settings_snooze_hours": "Schlummerdauer (Stunden)",
|
||||
"settings_snooze_hours_hint": "Wie lange „Schlummern“ die Erinnerungen einer Aufgabe stummschaltet — im Aufgabenmenü, auf einer Karte, per Sprache oder mit der Schlummern-Taste am Handy. Das Fälligkeitsdatum bleibt, wie es ist.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Währung",
|
||||
"settings_currency_decimals": "Nachkommastellen für Beträge",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Delete {n} objects and all their tasks? Archiving keeps their history instead.",
|
||||
"bulk_objects_deleted": "{n} objects deleted",
|
||||
"bulk_objects_archived": "{n} objects archived",
|
||||
"bulk_assign": "Assign",
|
||||
"bulk_assign_one": "One person",
|
||||
"bulk_assign_rotate": "Several people, in turns",
|
||||
"bulk_assign_hint": "Replaces who is assigned to the selected tasks — a rotation included.",
|
||||
"bulk_rotation_min_two": "Pick at least two people.",
|
||||
"bulk_labels_add": "Add labels",
|
||||
"bulk_labels_remove": "Remove labels",
|
||||
"bulk_edit_hint": "Only the ticked settings change; everything else stays as it is.",
|
||||
"bulk_no_notifications": "No notifications for these tasks",
|
||||
"bulk_updated": "{n} tasks updated",
|
||||
"bulk_paused": "{n} tasks paused",
|
||||
"bulk_resumed": "{n} tasks resumed",
|
||||
"bulk_objects_area": "Area set for {n} objects",
|
||||
"bulk_failed": "{n} failed: {reason}",
|
||||
"completing": "Completing…",
|
||||
"interval": "Interval",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Custom",
|
||||
"history": "History",
|
||||
"cost": "Cost",
|
||||
"cost_kind_credit": "Credit",
|
||||
"cost_credit_hint": "Money back — a sale, a refund. It lowers the totals; the average per completion leaves it out.",
|
||||
"report_button": "Report",
|
||||
"report_title": "Maintenance report",
|
||||
"report_generated": "Generated",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Days before expiry",
|
||||
"settings_warranty_reminder_hint": "Notify once when an object's warranty is this many days from expiring.",
|
||||
"settings_snooze_hours": "Snooze duration (hours)",
|
||||
"settings_snooze_hours_hint": "How long Snooze mutes a task's reminders — from the task menu, a card, voice or the phone's Snooze button. The due date stays as it is.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Currency",
|
||||
"settings_currency_decimals": "Decimal places for amounts",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "¿Eliminar {n} objetos y todas sus tareas? Archivar conserva su historial.",
|
||||
"bulk_objects_deleted": "{n} objetos eliminados",
|
||||
"bulk_objects_archived": "{n} objetos archivados",
|
||||
"bulk_assign": "Asignar",
|
||||
"bulk_assign_one": "Una persona",
|
||||
"bulk_assign_rotate": "Varias personas, por turnos",
|
||||
"bulk_assign_hint": "Sustituye a quién están asignadas las tareas seleccionadas, incluida una rotación.",
|
||||
"bulk_rotation_min_two": "Elige al menos dos personas.",
|
||||
"bulk_labels_add": "Añadir etiquetas",
|
||||
"bulk_labels_remove": "Quitar etiquetas",
|
||||
"bulk_edit_hint": "Solo cambian los ajustes marcados; todo lo demás se queda como está.",
|
||||
"bulk_no_notifications": "Sin notificaciones para estas tareas",
|
||||
"bulk_updated": "{n} tareas actualizadas",
|
||||
"bulk_paused": "{n} tareas pausadas",
|
||||
"bulk_resumed": "{n} tareas reanudadas",
|
||||
"bulk_objects_area": "Área asignada a {n} objetos",
|
||||
"bulk_failed": "{n} fallidos: {reason}",
|
||||
"completing": "Completando…",
|
||||
"interval": "Intervalo",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Personalizado",
|
||||
"history": "Historial",
|
||||
"cost": "Coste",
|
||||
"cost_kind_credit": "Abono",
|
||||
"cost_credit_hint": "Dinero que vuelve — una venta, un reembolso. Reduce los totales; la media por realización lo excluye.",
|
||||
"report_button": "Informe",
|
||||
"report_title": "Informe de mantenimiento",
|
||||
"report_generated": "Generado",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Días antes del vencimiento",
|
||||
"settings_warranty_reminder_hint": "Notificar una vez cuando a la garantía de un objeto le queden estos días para vencer.",
|
||||
"settings_snooze_hours": "Duración de posposición (horas)",
|
||||
"settings_snooze_hours_hint": "Cuánto tiempo «Posponer» silencia los recordatorios de una tarea: desde el menú de la tarea, una tarjeta, la voz o el botón Posponer del teléfono. La fecha de vencimiento no cambia.",
|
||||
"settings_budget": "Presupuesto",
|
||||
"settings_currency": "Moneda",
|
||||
"settings_currency_decimals": "Decimales de los importes",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Poistetaanko {n} kohdetta kaikkine tehtävineen? Arkistointi säilyttää historian.",
|
||||
"bulk_objects_deleted": "{n} kohdetta poistettu",
|
||||
"bulk_objects_archived": "{n} kohdetta arkistoitu",
|
||||
"bulk_assign": "Määritä",
|
||||
"bulk_assign_one": "Yksi henkilö",
|
||||
"bulk_assign_rotate": "Useita henkilöitä vuorotellen",
|
||||
"bulk_assign_hint": "Korvaa valittujen tehtävien määritykset — myös kierron.",
|
||||
"bulk_rotation_min_two": "Valitse vähintään kaksi henkilöä.",
|
||||
"bulk_labels_add": "Lisää tunnisteita",
|
||||
"bulk_labels_remove": "Poista tunnisteita",
|
||||
"bulk_edit_hint": "Vain valitut asetukset muuttuvat; kaikki muu pysyy ennallaan.",
|
||||
"bulk_no_notifications": "Ei ilmoituksia näistä tehtävistä",
|
||||
"bulk_updated": "{n} tehtävää päivitetty",
|
||||
"bulk_paused": "{n} tehtävää keskeytetty",
|
||||
"bulk_resumed": "{n} tehtävää jatkettu",
|
||||
"bulk_objects_area": "Alue asetettu {n} kohteelle",
|
||||
"bulk_failed": "{n} epäonnistui: {reason}",
|
||||
"completing": "Suoritetaan…",
|
||||
"interval": "Väli",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Mukautettu",
|
||||
"history": "Historia",
|
||||
"cost": "Kustannus",
|
||||
"cost_kind_credit": "Hyvitys",
|
||||
"cost_credit_hint": "Takaisin saatu raha — myynti, palautus. Pienentää summia; keskiarvo suoritusta kohden jättää sen pois.",
|
||||
"report_button": "Raportti",
|
||||
"report_title": "Huoltoraportti",
|
||||
"report_generated": "Luotu",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Päivää ennen päättymistä",
|
||||
"settings_warranty_reminder_hint": "Ilmoita kerran, kun objektin takuun päättymiseen on näin monta päivää.",
|
||||
"settings_snooze_hours": "Torkun kesto (tuntia)",
|
||||
"settings_snooze_hours_hint": "Kuinka kauan Torkku mykistää tehtävän muistutukset — tehtävävalikosta, kortilta, äänellä tai puhelimen Torkku-painikkeella. Eräpäivä pysyy ennallaan.",
|
||||
"settings_budget": "Budjetti",
|
||||
"settings_currency": "Valuutta",
|
||||
"settings_currency_decimals": "Summien desimaalit",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Supprimer {n} objets et toutes leurs tâches ? L'archivage conserve leur historique.",
|
||||
"bulk_objects_deleted": "{n} objets supprimés",
|
||||
"bulk_objects_archived": "{n} objets archivés",
|
||||
"bulk_assign": "Assigner",
|
||||
"bulk_assign_one": "Une personne",
|
||||
"bulk_assign_rotate": "Plusieurs personnes, à tour de rôle",
|
||||
"bulk_assign_hint": "Remplace l'attribution des tâches sélectionnées — rotation comprise.",
|
||||
"bulk_rotation_min_two": "Choisissez au moins deux personnes.",
|
||||
"bulk_labels_add": "Ajouter des étiquettes",
|
||||
"bulk_labels_remove": "Retirer des étiquettes",
|
||||
"bulk_edit_hint": "Seuls les réglages cochés changent ; tout le reste reste tel quel.",
|
||||
"bulk_no_notifications": "Aucune notification pour ces tâches",
|
||||
"bulk_updated": "{n} tâches mises à jour",
|
||||
"bulk_paused": "{n} tâches mises en pause",
|
||||
"bulk_resumed": "{n} tâches reprises",
|
||||
"bulk_objects_area": "Zone définie pour {n} objets",
|
||||
"bulk_failed": "{n} en échec : {reason}",
|
||||
"completing": "En cours…",
|
||||
"interval": "Intervalle",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Personnalisé",
|
||||
"history": "Historique",
|
||||
"cost": "Coût",
|
||||
"cost_kind_credit": "Crédit",
|
||||
"cost_credit_hint": "Argent récupéré — une vente, un remboursement. Réduit les totaux ; la moyenne par réalisation l'ignore.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Rapport d'entretien",
|
||||
"report_generated": "Généré",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Jours avant l'expiration",
|
||||
"settings_warranty_reminder_hint": "Notifier une fois lorsque la garantie d'un objet expire dans ce nombre de jours.",
|
||||
"settings_snooze_hours": "Durée de report (heures)",
|
||||
"settings_snooze_hours_hint": "Durée pendant laquelle « Mettre en attente » coupe les rappels d'une tâche — depuis le menu de la tâche, une carte, la voix ou le bouton du téléphone. La date d'échéance ne change pas.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Devise",
|
||||
"settings_currency_decimals": "Décimales des montants",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} ऑब्जेक्ट और उनके सभी कार्य हटाएँ? संग्रहित करने से उनका इतिहास बना रहता है।",
|
||||
"bulk_objects_deleted": "{n} ऑब्जेक्ट हटाए गए",
|
||||
"bulk_objects_archived": "{n} ऑब्जेक्ट संग्रहित किए गए",
|
||||
"bulk_assign": "असाइन करें",
|
||||
"bulk_assign_one": "एक व्यक्ति",
|
||||
"bulk_assign_rotate": "कई लोग, बारी-बारी से",
|
||||
"bulk_assign_hint": "चुने गए कार्यों का असाइनमेंट बदल देता है — रोटेशन सहित।",
|
||||
"bulk_rotation_min_two": "कम से कम दो लोग चुनें।",
|
||||
"bulk_labels_add": "लेबल जोड़ें",
|
||||
"bulk_labels_remove": "लेबल हटाएँ",
|
||||
"bulk_edit_hint": "केवल चुनी गई सेटिंग्स बदलती हैं; बाकी सब वैसा ही रहता है।",
|
||||
"bulk_no_notifications": "इन कार्यों के लिए कोई सूचना नहीं",
|
||||
"bulk_updated": "{n} कार्य अपडेट किए गए",
|
||||
"bulk_paused": "{n} कार्य रोके गए",
|
||||
"bulk_resumed": "{n} कार्य फिर से शुरू किए गए",
|
||||
"bulk_objects_area": "{n} ऑब्जेक्ट के लिए क्षेत्र सेट किया गया",
|
||||
"bulk_failed": "{n} विफल: {reason}",
|
||||
"completing": "पूर्ण किया जा रहा है…",
|
||||
"interval": "अंतराल",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "कस्टम",
|
||||
"history": "इतिहास",
|
||||
"cost": "लागत",
|
||||
"cost_kind_credit": "वापसी",
|
||||
"cost_credit_hint": "वापस मिला पैसा — बिक्री, रिफ़ंड। यह कुल योग घटाता है; प्रति पूर्णता औसत में शामिल नहीं होता।",
|
||||
"report_button": "रिपोर्ट",
|
||||
"report_title": "रखरखाव रिपोर्ट",
|
||||
"report_generated": "जनरेट किया गया",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "समाप्ति से पहले के दिन",
|
||||
"settings_warranty_reminder_hint": "जब किसी वस्तु की वारंटी समाप्त होने में इतने दिन बचें तो एक बार सूचित करें।",
|
||||
"settings_snooze_hours": "स्नूज़ अवधि (घंटे)",
|
||||
"settings_snooze_hours_hint": "स्नूज़ किसी कार्य के रिमाइंडर कितनी देर तक बंद रखता है — कार्य मेनू, कार्ड, आवाज़ या फ़ोन के स्नूज़ बटन से। नियत तिथि नहीं बदलती।",
|
||||
"settings_budget": "बजट",
|
||||
"settings_currency": "मुद्रा",
|
||||
"settings_currency_decimals": "राशियों के दशमलव स्थान",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Törli a(z) {n} objektumot az összes feladatával együtt? Az archiválás ehelyett megőrzi az előzményeket.",
|
||||
"bulk_objects_deleted": "{n} objektum törölve",
|
||||
"bulk_objects_archived": "{n} objektum archiválva",
|
||||
"bulk_assign": "Hozzárendelés",
|
||||
"bulk_assign_one": "Egy személy",
|
||||
"bulk_assign_rotate": "Több személy, felváltva",
|
||||
"bulk_assign_hint": "Lecseréli, kihez vannak rendelve a kijelölt feladatok — a rotációt is.",
|
||||
"bulk_rotation_min_two": "Válassz legalább két személyt.",
|
||||
"bulk_labels_add": "Címkék hozzáadása",
|
||||
"bulk_labels_remove": "Címkék eltávolítása",
|
||||
"bulk_edit_hint": "Csak a bejelölt beállítások változnak, minden más marad.",
|
||||
"bulk_no_notifications": "Nincs értesítés ezekhez a feladatokhoz",
|
||||
"bulk_updated": "{n} feladat frissítve",
|
||||
"bulk_paused": "{n} feladat szüneteltetve",
|
||||
"bulk_resumed": "{n} feladat folytatva",
|
||||
"bulk_objects_area": "Terület beállítva {n} objektumhoz",
|
||||
"bulk_failed": "{n} sikertelen: {reason}",
|
||||
"completing": "Elvégzés…",
|
||||
"interval": "Intervallum",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Egyéni",
|
||||
"history": "Előzmények",
|
||||
"cost": "Költség",
|
||||
"cost_kind_credit": "Jóváírás",
|
||||
"cost_credit_hint": "Visszakapott pénz — eladás, visszatérítés. Csökkenti az összegeket; az elvégzésenkénti átlag nem számolja.",
|
||||
"report_button": "Jelentés",
|
||||
"report_title": "Karbantartási jelentés",
|
||||
"report_generated": "Létrehozva",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Nappal a lejárat előtt",
|
||||
"settings_warranty_reminder_hint": "Egyszeri értesítés, amikor egy objektum garanciája ennyi napra van a lejárattól.",
|
||||
"settings_snooze_hours": "Szundi időtartama (óra)",
|
||||
"settings_snooze_hours_hint": "Mennyi ideig némítja a Szundi egy feladat emlékeztetőit — a feladat menüjéből, egy kártyáról, hanggal vagy a telefon Szundi gombjával. A határidő nem változik.",
|
||||
"settings_budget": "Költségkeret",
|
||||
"settings_currency": "Pénznem",
|
||||
"settings_currency_decimals": "Összegek tizedesjegyei",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Eliminare {n} oggetti con tutte le loro attività? L'archiviazione conserva invece la cronologia.",
|
||||
"bulk_objects_deleted": "{n} oggetti eliminati",
|
||||
"bulk_objects_archived": "{n} oggetti archiviati",
|
||||
"bulk_assign": "Assegna",
|
||||
"bulk_assign_one": "Una persona",
|
||||
"bulk_assign_rotate": "Più persone, a turno",
|
||||
"bulk_assign_hint": "Sostituisce a chi sono assegnate le attività selezionate, rotazione compresa.",
|
||||
"bulk_rotation_min_two": "Scegli almeno due persone.",
|
||||
"bulk_labels_add": "Aggiungi etichette",
|
||||
"bulk_labels_remove": "Rimuovi etichette",
|
||||
"bulk_edit_hint": "Cambiano solo le impostazioni spuntate; tutto il resto resta com'è.",
|
||||
"bulk_no_notifications": "Nessuna notifica per queste attività",
|
||||
"bulk_updated": "{n} attività aggiornate",
|
||||
"bulk_paused": "{n} attività in pausa",
|
||||
"bulk_resumed": "{n} attività riprese",
|
||||
"bulk_objects_area": "Area impostata per {n} oggetti",
|
||||
"bulk_failed": "{n} non riusciti: {reason}",
|
||||
"completing": "Completamento…",
|
||||
"interval": "Intervallo",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Personalizzato",
|
||||
"history": "Cronologia",
|
||||
"cost": "Costo",
|
||||
"cost_kind_credit": "Accredito",
|
||||
"cost_credit_hint": "Denaro rientrato — una vendita, un rimborso. Riduce i totali; la media per completamento lo esclude.",
|
||||
"report_button": "Report",
|
||||
"report_title": "Report di manutenzione",
|
||||
"report_generated": "Generato",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Giorni prima della scadenza",
|
||||
"settings_warranty_reminder_hint": "Notifica una volta quando alla garanzia di un oggetto mancano questi giorni alla scadenza.",
|
||||
"settings_snooze_hours": "Durata posticipo (ore)",
|
||||
"settings_snooze_hours_hint": "Per quanto tempo «Posponi» silenzia i promemoria di un'attività — dal menu dell'attività, da una scheda, con la voce o con il pulsante del telefono. La scadenza resta invariata.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Decimali degli importi",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} 件のオブジェクトとそのすべてのタスクを削除しますか?アーカイブなら履歴が残ります。",
|
||||
"bulk_objects_deleted": "{n} 件のオブジェクトを削除しました",
|
||||
"bulk_objects_archived": "{n} 件のオブジェクトをアーカイブしました",
|
||||
"bulk_assign": "割り当て",
|
||||
"bulk_assign_one": "1人",
|
||||
"bulk_assign_rotate": "複数人で交代制",
|
||||
"bulk_assign_hint": "選択したタスクの担当者を置き換えます(ローテーションを含む)。",
|
||||
"bulk_rotation_min_two": "2人以上を選んでください。",
|
||||
"bulk_labels_add": "ラベルを追加",
|
||||
"bulk_labels_remove": "ラベルを削除",
|
||||
"bulk_edit_hint": "チェックした設定だけが変わり、それ以外はそのままです。",
|
||||
"bulk_no_notifications": "これらのタスクは通知しない",
|
||||
"bulk_updated": "{n} 件のタスクを更新しました",
|
||||
"bulk_paused": "{n} 件のタスクを一時停止しました",
|
||||
"bulk_resumed": "{n} 件のタスクを再開しました",
|
||||
"bulk_objects_area": "{n} 件のオブジェクトにエリアを設定しました",
|
||||
"bulk_failed": "{n} 件失敗:{reason}",
|
||||
"completing": "完了処理中…",
|
||||
"interval": "間隔",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "カスタム",
|
||||
"history": "履歴",
|
||||
"cost": "費用",
|
||||
"cost_kind_credit": "収入",
|
||||
"cost_credit_hint": "戻ってきたお金(売却、返金など)。合計を減らしますが、1回あたりの平均には含まれません。",
|
||||
"report_button": "レポート",
|
||||
"report_title": "メンテナンスレポート",
|
||||
"report_generated": "作成日",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "期限切れ前の日数",
|
||||
"settings_warranty_reminder_hint": "オブジェクトの保証期限までこの日数になったら一度通知します。",
|
||||
"settings_snooze_hours": "スヌーズ時間(時間)",
|
||||
"settings_snooze_hours_hint": "スヌーズでタスクのリマインダーを止める時間です。タスクのメニュー、カード、音声、スマートフォンのスヌーズボタンのどこから操作しても同じです。期日は変わりません。",
|
||||
"settings_budget": "予算",
|
||||
"settings_currency": "通貨",
|
||||
"settings_currency_decimals": "金額の小数点以下の桁数",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "개체 {n}개와 모든 작업을 삭제할까요? 보관하면 기록이 유지됩니다.",
|
||||
"bulk_objects_deleted": "개체 {n}개 삭제됨",
|
||||
"bulk_objects_archived": "개체 {n}개 보관됨",
|
||||
"bulk_assign": "배정",
|
||||
"bulk_assign_one": "한 사람",
|
||||
"bulk_assign_rotate": "여러 사람, 번갈아",
|
||||
"bulk_assign_hint": "선택한 작업의 담당자를 바꿉니다 — 교대 방식 포함.",
|
||||
"bulk_rotation_min_two": "두 명 이상 선택하세요.",
|
||||
"bulk_labels_add": "라벨 추가",
|
||||
"bulk_labels_remove": "라벨 제거",
|
||||
"bulk_edit_hint": "체크한 설정만 바뀌고 나머지는 그대로입니다.",
|
||||
"bulk_no_notifications": "이 작업들은 알림 없음",
|
||||
"bulk_updated": "작업 {n}개 업데이트됨",
|
||||
"bulk_paused": "작업 {n}개 일시 중지됨",
|
||||
"bulk_resumed": "작업 {n}개 재개됨",
|
||||
"bulk_objects_area": "개체 {n}개의 구역을 설정함",
|
||||
"bulk_failed": "{n}개 실패: {reason}",
|
||||
"completing": "완료 처리 중…",
|
||||
"interval": "주기",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "사용자 지정",
|
||||
"history": "기록",
|
||||
"cost": "비용",
|
||||
"cost_kind_credit": "수입",
|
||||
"cost_credit_hint": "돌려받은 돈(판매, 환불 등). 합계를 줄이며, 완료당 평균에는 포함되지 않습니다.",
|
||||
"report_button": "보고서",
|
||||
"report_title": "유지보수 보고서",
|
||||
"report_generated": "생성일",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "만료 전 일수",
|
||||
"settings_warranty_reminder_hint": "객체의 보증 만료가 설정한 일수만큼 남았을 때 한 번 알립니다.",
|
||||
"settings_snooze_hours": "다시 알림 대기 시간 (시간)",
|
||||
"settings_snooze_hours_hint": "다시 알림이 작업 알림을 끄는 시간입니다 — 작업 메뉴, 카드, 음성, 휴대폰의 다시 알림 버튼 어디에서 해도 같습니다. 기한은 바뀌지 않습니다.",
|
||||
"settings_budget": "예산",
|
||||
"settings_currency": "통화",
|
||||
"settings_currency_decimals": "금액 소수 자릿수",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Slette {n} objekter og alle oppgavene deres? Arkivering beholder historikken i stedet.",
|
||||
"bulk_objects_deleted": "{n} objekter slettet",
|
||||
"bulk_objects_archived": "{n} objekter arkivert",
|
||||
"bulk_assign": "Tildel",
|
||||
"bulk_assign_one": "Én person",
|
||||
"bulk_assign_rotate": "Flere personer, på omgang",
|
||||
"bulk_assign_hint": "Erstatter hvem de valgte oppgavene er tildelt — også en rotasjon.",
|
||||
"bulk_rotation_min_two": "Velg minst to personer.",
|
||||
"bulk_labels_add": "Legg til etiketter",
|
||||
"bulk_labels_remove": "Fjern etiketter",
|
||||
"bulk_edit_hint": "Bare de avkryssede innstillingene endres; alt annet forblir som det er.",
|
||||
"bulk_no_notifications": "Ingen varsler for disse oppgavene",
|
||||
"bulk_updated": "{n} oppgaver oppdatert",
|
||||
"bulk_paused": "{n} oppgaver satt på pause",
|
||||
"bulk_resumed": "{n} oppgaver gjenopptatt",
|
||||
"bulk_objects_area": "Område satt for {n} objekter",
|
||||
"bulk_failed": "{n} mislyktes: {reason}",
|
||||
"completing": "Fullfører…",
|
||||
"interval": "Intervall",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Egendefinert",
|
||||
"history": "Historikk",
|
||||
"cost": "Kostnad",
|
||||
"cost_kind_credit": "Kreditering",
|
||||
"cost_credit_hint": "Penger tilbake — et salg, en refusjon. Senker summene; snittet per utførelse tar det ikke med.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Vedlikeholdsrapport",
|
||||
"report_generated": "Generert",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Dager før utløp",
|
||||
"settings_warranty_reminder_hint": "Varsle én gang når garantien til et objekt utløper om så mange dager.",
|
||||
"settings_snooze_hours": "Slumrevarighet (timer)",
|
||||
"settings_snooze_hours_hint": "Hvor lenge «Slumre» demper en oppgaves påminnelser — fra oppgavemenyen, et kort, stemmen eller telefonens Slumre-knapp. Forfallsdatoen forblir uendret.",
|
||||
"settings_budget": "Budsjett",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Desimaler for beløp",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} objecten met al hun taken verwijderen? Archiveren bewaart in plaats daarvan de geschiedenis.",
|
||||
"bulk_objects_deleted": "{n} objecten verwijderd",
|
||||
"bulk_objects_archived": "{n} objecten gearchiveerd",
|
||||
"bulk_assign": "Toewijzen",
|
||||
"bulk_assign_one": "Eén persoon",
|
||||
"bulk_assign_rotate": "Meerdere personen, om de beurt",
|
||||
"bulk_assign_hint": "Vervangt aan wie de geselecteerde taken zijn toegewezen — een rotatie inbegrepen.",
|
||||
"bulk_rotation_min_two": "Kies ten minste twee personen.",
|
||||
"bulk_labels_add": "Labels toevoegen",
|
||||
"bulk_labels_remove": "Labels verwijderen",
|
||||
"bulk_edit_hint": "Alleen de aangevinkte instellingen veranderen; al het andere blijft zoals het is.",
|
||||
"bulk_no_notifications": "Geen notificaties voor deze taken",
|
||||
"bulk_updated": "{n} taken bijgewerkt",
|
||||
"bulk_paused": "{n} taken gepauzeerd",
|
||||
"bulk_resumed": "{n} taken hervat",
|
||||
"bulk_objects_area": "Gebied ingesteld voor {n} objecten",
|
||||
"bulk_failed": "{n} mislukt: {reason}",
|
||||
"completing": "Wordt voltooid…",
|
||||
"interval": "Interval",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Aangepast",
|
||||
"history": "Geschiedenis",
|
||||
"cost": "Kosten",
|
||||
"cost_kind_credit": "Tegoed",
|
||||
"cost_credit_hint": "Geld terug — een verkoop, een terugbetaling. Verlaagt de totalen; het gemiddelde per voltooiing laat het weg.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Onderhoudsrapport",
|
||||
"report_generated": "Gegenereerd",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dagen vóór afloop",
|
||||
"settings_warranty_reminder_hint": "Eén keer melden wanneer de garantie van een object over zoveel dagen afloopt.",
|
||||
"settings_snooze_hours": "Snoozeduur (uren)",
|
||||
"settings_snooze_hours_hint": "Hoe lang ‘Sluimeren’ de herinneringen van een taak dempt — vanuit het taakmenu, een kaart, spraak of de sluimerknop op de telefoon. De vervaldatum blijft zoals hij is.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Decimalen voor bedragen",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Usunąć {n} obiektów wraz ze wszystkimi zadaniami? Archiwizacja zachowa ich historię.",
|
||||
"bulk_objects_deleted": "Usunięto obiektów: {n}",
|
||||
"bulk_objects_archived": "Zarchiwizowano obiektów: {n}",
|
||||
"bulk_assign": "Przypisz",
|
||||
"bulk_assign_one": "Jedna osoba",
|
||||
"bulk_assign_rotate": "Kilka osób, na zmianę",
|
||||
"bulk_assign_hint": "Zastępuje przypisanie wybranych zadań — łącznie z rotacją.",
|
||||
"bulk_rotation_min_two": "Wybierz co najmniej dwie osoby.",
|
||||
"bulk_labels_add": "Dodaj etykiety",
|
||||
"bulk_labels_remove": "Usuń etykiety",
|
||||
"bulk_edit_hint": "Zmieniają się tylko zaznaczone ustawienia; wszystko inne pozostaje bez zmian.",
|
||||
"bulk_no_notifications": "Bez powiadomień dla tych zadań",
|
||||
"bulk_updated": "Zaktualizowano {n} zadań",
|
||||
"bulk_paused": "Wstrzymano {n} zadań",
|
||||
"bulk_resumed": "Wznowiono {n} zadań",
|
||||
"bulk_objects_area": "Ustawiono obszar dla obiektów: {n}",
|
||||
"bulk_failed": "Nieudane: {n} — {reason}",
|
||||
"completing": "Wykonywanie…",
|
||||
"interval": "Interwał",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Niestandardowy",
|
||||
"history": "Historia",
|
||||
"cost": "Koszt",
|
||||
"cost_kind_credit": "Zwrot",
|
||||
"cost_credit_hint": "Odzyskane pieniądze — sprzedaż, zwrot. Obniża sumy; średnia na wykonanie go pomija.",
|
||||
"report_button": "Raport",
|
||||
"report_title": "Raport konserwacji",
|
||||
"report_generated": "Wygenerowano",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dni przed wygaśnięciem",
|
||||
"settings_warranty_reminder_hint": "Powiadom raz, gdy do wygaśnięcia gwarancji obiektu pozostanie tyle dni.",
|
||||
"settings_snooze_hours": "Czas drzemki (godziny)",
|
||||
"settings_snooze_hours_hint": "Jak długo „Drzemka” wycisza przypomnienia zadania — z menu zadania, z karty, głosem lub przyciskiem Drzemka w telefonie. Termin pozostaje bez zmian.",
|
||||
"settings_budget": "Budżet",
|
||||
"settings_currency": "Waluta",
|
||||
"settings_currency_decimals": "Miejsca po przecinku w kwotach",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "Excluir {n} objetos e todas as suas tarefas? Arquivar mantém o histórico.",
|
||||
"bulk_objects_deleted": "{n} objetos excluídos",
|
||||
"bulk_objects_archived": "{n} objetos arquivados",
|
||||
"bulk_assign": "Atribuir",
|
||||
"bulk_assign_one": "Uma pessoa",
|
||||
"bulk_assign_rotate": "Várias pessoas, em rodízio",
|
||||
"bulk_assign_hint": "Substitui a quem as tarefas selecionadas estão atribuídas — incluindo um rodízio.",
|
||||
"bulk_rotation_min_two": "Escolha pelo menos duas pessoas.",
|
||||
"bulk_labels_add": "Adicionar etiquetas",
|
||||
"bulk_labels_remove": "Remover etiquetas",
|
||||
"bulk_edit_hint": "Só as configurações marcadas mudam; todo o resto continua como está.",
|
||||
"bulk_no_notifications": "Sem notificações para estas tarefas",
|
||||
"bulk_updated": "{n} tarefas atualizadas",
|
||||
"bulk_paused": "{n} tarefas pausadas",
|
||||
"bulk_resumed": "{n} tarefas retomadas",
|
||||
"bulk_objects_area": "Área definida para {n} objetos",
|
||||
"bulk_failed": "{n} falharam: {reason}",
|
||||
"completing": "Concluindo…",
|
||||
"interval": "Intervalo",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Personalizado",
|
||||
"history": "Histórico",
|
||||
"cost": "Custo",
|
||||
"cost_kind_credit": "Crédito",
|
||||
"cost_credit_hint": "Dinheiro de volta — uma venda, um reembolso. Reduz os totais; a média por conclusão não o conta.",
|
||||
"report_button": "Relatório",
|
||||
"report_title": "Relatório de manutenção",
|
||||
"report_generated": "Gerado em",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Dias antes do vencimento",
|
||||
"settings_warranty_reminder_hint": "Notifica uma vez quando faltar este número de dias para a garantia de um objeto expirar.",
|
||||
"settings_snooze_hours": "Duração da soneca (horas)",
|
||||
"settings_snooze_hours_hint": "Por quanto tempo a Soneca silencia os lembretes de uma tarefa — pelo menu da tarefa, por um cartão, pela voz ou pelo botão Soneca do celular. A data de vencimento não muda.",
|
||||
"settings_budget": "Orçamento",
|
||||
"settings_currency": "Moeda",
|
||||
"settings_currency_decimals": "Casas decimais dos valores",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Eliminar {n} objetos e todas as suas tarefas? Arquivar mantém o histórico.",
|
||||
"bulk_objects_deleted": "{n} objetos eliminados",
|
||||
"bulk_objects_archived": "{n} objetos arquivados",
|
||||
"bulk_assign": "Atribuir",
|
||||
"bulk_assign_one": "Uma pessoa",
|
||||
"bulk_assign_rotate": "Várias pessoas, à vez",
|
||||
"bulk_assign_hint": "Substitui a quem as tarefas selecionadas estão atribuídas — incluindo uma rotação.",
|
||||
"bulk_rotation_min_two": "Escolha pelo menos duas pessoas.",
|
||||
"bulk_labels_add": "Adicionar etiquetas",
|
||||
"bulk_labels_remove": "Remover etiquetas",
|
||||
"bulk_edit_hint": "Só as definições assinaladas mudam; tudo o resto fica como está.",
|
||||
"bulk_no_notifications": "Sem notificações para estas tarefas",
|
||||
"bulk_updated": "{n} tarefas atualizadas",
|
||||
"bulk_paused": "{n} tarefas pausadas",
|
||||
"bulk_resumed": "{n} tarefas retomadas",
|
||||
"bulk_objects_area": "Área definida para {n} objetos",
|
||||
"bulk_failed": "{n} falharam: {reason}",
|
||||
"completing": "A concluir…",
|
||||
"interval": "Intervalo",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Personalizado",
|
||||
"history": "Histórico",
|
||||
"cost": "Custo",
|
||||
"cost_kind_credit": "Crédito",
|
||||
"cost_credit_hint": "Dinheiro que regressa — uma venda, um reembolso. Reduz os totais; a média por conclusão não o conta.",
|
||||
"report_button": "Relatório",
|
||||
"report_title": "Relatório de manutenção",
|
||||
"report_generated": "Gerado",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dias antes do fim",
|
||||
"settings_warranty_reminder_hint": "Notificar uma vez quando faltarem estes dias para a garantia de um objeto expirar.",
|
||||
"settings_snooze_hours": "Duração do adiamento (horas)",
|
||||
"settings_snooze_hours_hint": "Durante quanto tempo «Adiar» silencia os lembretes de uma tarefa — a partir do menu da tarefa, de um cartão, por voz ou do botão Adiar do telemóvel. A data limite mantém-se.",
|
||||
"settings_budget": "Orçamento",
|
||||
"settings_currency": "Moeda",
|
||||
"settings_currency_decimals": "Casas decimais dos montantes",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Удалить {n} объектов со всеми их задачами? Архивация вместо этого сохранит историю.",
|
||||
"bulk_objects_deleted": "Удалено объектов: {n}",
|
||||
"bulk_objects_archived": "Архивировано объектов: {n}",
|
||||
"bulk_assign": "Назначить",
|
||||
"bulk_assign_one": "Один человек",
|
||||
"bulk_assign_rotate": "Несколько человек по очереди",
|
||||
"bulk_assign_hint": "Заменяет назначение выбранных задач — включая ротацию.",
|
||||
"bulk_rotation_min_two": "Выберите не менее двух человек.",
|
||||
"bulk_labels_add": "Добавить метки",
|
||||
"bulk_labels_remove": "Удалить метки",
|
||||
"bulk_edit_hint": "Меняются только отмеченные настройки, всё остальное остаётся как есть.",
|
||||
"bulk_no_notifications": "Без уведомлений для этих задач",
|
||||
"bulk_updated": "Обновлено задач: {n}",
|
||||
"bulk_paused": "Приостановлено задач: {n}",
|
||||
"bulk_resumed": "Возобновлено задач: {n}",
|
||||
"bulk_objects_area": "Зона задана для объектов: {n}",
|
||||
"bulk_failed": "Не удалось: {n} — {reason}",
|
||||
"completing": "Выполнение…",
|
||||
"interval": "Интервал",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Своё",
|
||||
"history": "История",
|
||||
"cost": "Стоимость",
|
||||
"cost_kind_credit": "Возврат",
|
||||
"cost_credit_hint": "Деньги вернулись — продажа, возврат. Уменьшает итоги; среднее за выполнение его не учитывает.",
|
||||
"report_button": "Отчёт",
|
||||
"report_title": "Отчёт об обслуживании",
|
||||
"report_generated": "Создан",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Дней до окончания",
|
||||
"settings_warranty_reminder_hint": "Уведомить один раз, когда до окончания гарантии объекта останется столько дней.",
|
||||
"settings_snooze_hours": "Длительность откладывания (часы)",
|
||||
"settings_snooze_hours_hint": "Как долго «Отложить» глушит напоминания задачи — из меню задачи, с карточки, голосом или кнопкой «Отложить» на телефоне. Срок выполнения не меняется.",
|
||||
"settings_budget": "Бюджет",
|
||||
"settings_currency": "Валюта",
|
||||
"settings_currency_decimals": "Знаков после запятой в суммах",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Ta bort {n} objekt och alla deras uppgifter? Arkivering behåller historiken i stället.",
|
||||
"bulk_objects_deleted": "{n} objekt borttagna",
|
||||
"bulk_objects_archived": "{n} objekt arkiverade",
|
||||
"bulk_assign": "Tilldela",
|
||||
"bulk_assign_one": "En person",
|
||||
"bulk_assign_rotate": "Flera personer, turvis",
|
||||
"bulk_assign_hint": "Ersätter vem de valda uppgifterna är tilldelade — även en rotation.",
|
||||
"bulk_rotation_min_two": "Välj minst två personer.",
|
||||
"bulk_labels_add": "Lägg till etiketter",
|
||||
"bulk_labels_remove": "Ta bort etiketter",
|
||||
"bulk_edit_hint": "Bara de markerade inställningarna ändras; allt annat förblir som det är.",
|
||||
"bulk_no_notifications": "Inga aviseringar för de här uppgifterna",
|
||||
"bulk_updated": "{n} uppgifter uppdaterade",
|
||||
"bulk_paused": "{n} uppgifter pausade",
|
||||
"bulk_resumed": "{n} uppgifter återupptagna",
|
||||
"bulk_objects_area": "Område angivet för {n} objekt",
|
||||
"bulk_failed": "{n} misslyckades: {reason}",
|
||||
"completing": "Slutför…",
|
||||
"interval": "Intervall",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Anpassad",
|
||||
"history": "Historik",
|
||||
"cost": "Kostnad",
|
||||
"cost_kind_credit": "Kreditering",
|
||||
"cost_credit_hint": "Pengar tillbaka — en försäljning, en återbetalning. Sänker summorna; snittet per utförande räknar inte med det.",
|
||||
"report_button": "Rapport",
|
||||
"report_title": "Underhållsrapport",
|
||||
"report_generated": "Genererad",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Dagar före utgång",
|
||||
"settings_warranty_reminder_hint": "Meddela en gång när ett objekts garanti har så här många dagar kvar.",
|
||||
"settings_snooze_hours": "Snooza-tid (timmar)",
|
||||
"settings_snooze_hours_hint": "Hur länge ”Snooza” tystar en uppgifts påminnelser — från uppgiftsmenyn, ett kort, rösten eller telefonens Snooza-knapp. Förfallodatumet ändras inte.",
|
||||
"settings_budget": "Budget",
|
||||
"settings_currency": "Valuta",
|
||||
"settings_currency_decimals": "Decimaler för belopp",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "{n} nesne ve tüm görevleri silinsin mi? Arşivlemek geçmişi korur.",
|
||||
"bulk_objects_deleted": "{n} nesne silindi",
|
||||
"bulk_objects_archived": "{n} nesne arşivlendi",
|
||||
"bulk_assign": "Ata",
|
||||
"bulk_assign_one": "Bir kişi",
|
||||
"bulk_assign_rotate": "Birden çok kişi, sırayla",
|
||||
"bulk_assign_hint": "Seçili görevlerin kime atandığını değiştirir — rotasyon dahil.",
|
||||
"bulk_rotation_min_two": "En az iki kişi seçin.",
|
||||
"bulk_labels_add": "Etiket ekle",
|
||||
"bulk_labels_remove": "Etiketleri kaldır",
|
||||
"bulk_edit_hint": "Yalnızca işaretli ayarlar değişir; geri kalan her şey olduğu gibi kalır.",
|
||||
"bulk_no_notifications": "Bu görevler için bildirim yok",
|
||||
"bulk_updated": "{n} görev güncellendi",
|
||||
"bulk_paused": "{n} görev duraklatıldı",
|
||||
"bulk_resumed": "{n} göreve devam edildi",
|
||||
"bulk_objects_area": "{n} nesne için alan ayarlandı",
|
||||
"bulk_failed": "{n} başarısız: {reason}",
|
||||
"completing": "Tamamlanıyor…",
|
||||
"interval": "Aralık",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "Özel",
|
||||
"history": "Geçmiş",
|
||||
"cost": "Maliyet",
|
||||
"cost_kind_credit": "İade",
|
||||
"cost_credit_hint": "Geri gelen para — bir satış, bir iade. Toplamları düşürür; tamamlama başına ortalamaya katılmaz.",
|
||||
"report_button": "Rapor",
|
||||
"report_title": "Bakım raporu",
|
||||
"report_generated": "Oluşturulma",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "Bitişten gün önce",
|
||||
"settings_warranty_reminder_hint": "Bir nesnenin garantisinin bitmesine bu kadar gün kaldığında bir kez bildir.",
|
||||
"settings_snooze_hours": "Erteleme süresi (saat)",
|
||||
"settings_snooze_hours_hint": "Ertele'nin bir görevin hatırlatmalarını ne kadar süre sessize aldığı — görev menüsünden, bir karttan, sesle veya telefondaki Ertele düğmesiyle. Son tarih değişmez.",
|
||||
"settings_budget": "Bütçe",
|
||||
"settings_currency": "Para birimi",
|
||||
"settings_currency_decimals": "Tutarlarda ondalık basamak",
|
||||
|
||||
@@ -47,6 +47,19 @@
|
||||
"bulk_delete_objects_confirm": "Видалити {n} об'єктів з усіма їхніми завданнями? Архівування натомість збереже історію.",
|
||||
"bulk_objects_deleted": "Видалено об'єктів: {n}",
|
||||
"bulk_objects_archived": "Архівовано об'єктів: {n}",
|
||||
"bulk_assign": "Призначити",
|
||||
"bulk_assign_one": "Одна людина",
|
||||
"bulk_assign_rotate": "Кілька людей по черзі",
|
||||
"bulk_assign_hint": "Замінює призначення вибраних завдань — разом із ротацією.",
|
||||
"bulk_rotation_min_two": "Виберіть щонайменше двох людей.",
|
||||
"bulk_labels_add": "Додати мітки",
|
||||
"bulk_labels_remove": "Вилучити мітки",
|
||||
"bulk_edit_hint": "Змінюються лише позначені налаштування, усе інше лишається як є.",
|
||||
"bulk_no_notifications": "Без сповіщень для цих завдань",
|
||||
"bulk_updated": "Оновлено завдань: {n}",
|
||||
"bulk_paused": "Призупинено завдань: {n}",
|
||||
"bulk_resumed": "Відновлено завдань: {n}",
|
||||
"bulk_objects_area": "Зону задано для об'єктів: {n}",
|
||||
"bulk_failed": "Не вдалося: {n} — {reason}",
|
||||
"completing": "Виконується…",
|
||||
"interval": "Інтервал",
|
||||
@@ -114,6 +127,8 @@
|
||||
"custom": "Власний",
|
||||
"history": "Історія",
|
||||
"cost": "Вартість",
|
||||
"cost_kind_credit": "Повернення",
|
||||
"cost_credit_hint": "Гроші повернулися — продаж, відшкодування. Зменшує підсумки; середнє за виконання його не враховує.",
|
||||
"report_button": "Звіт",
|
||||
"report_title": "Звіт про обслуговування",
|
||||
"report_generated": "Створено",
|
||||
@@ -670,6 +685,7 @@
|
||||
"settings_warranty_reminder_days": "Днів до завершення",
|
||||
"settings_warranty_reminder_hint": "Сповістити один раз, коли до завершення гарантії об'єкта залишиться стільки днів.",
|
||||
"settings_snooze_hours": "Тривалість відкладення (години)",
|
||||
"settings_snooze_hours_hint": "Як довго «Відкласти» вимикає нагадування завдання — з меню завдання, картки, голосом або кнопкою «Відкласти» на телефоні. Термін не змінюється.",
|
||||
"settings_budget": "Бюджет",
|
||||
"settings_currency": "Валюта",
|
||||
"settings_currency_decimals": "Знаків після коми в сумах",
|
||||
|
||||
@@ -49,6 +49,19 @@
|
||||
"bulk_delete_objects_confirm": "删除 {n} 个对象及其全部任务?归档则会保留其历史记录。",
|
||||
"bulk_objects_deleted": "已删除 {n} 个对象",
|
||||
"bulk_objects_archived": "已归档 {n} 个对象",
|
||||
"bulk_assign": "分配",
|
||||
"bulk_assign_one": "一个人",
|
||||
"bulk_assign_rotate": "多人轮流",
|
||||
"bulk_assign_hint": "替换所选任务的负责人——包括轮换。",
|
||||
"bulk_rotation_min_two": "请至少选择两个人。",
|
||||
"bulk_labels_add": "添加标签",
|
||||
"bulk_labels_remove": "移除标签",
|
||||
"bulk_edit_hint": "只有勾选的设置会改变,其余保持不变。",
|
||||
"bulk_no_notifications": "这些任务不发送通知",
|
||||
"bulk_updated": "已更新 {n} 个任务",
|
||||
"bulk_paused": "已暂停 {n} 个任务",
|
||||
"bulk_resumed": "已继续 {n} 个任务",
|
||||
"bulk_objects_area": "已为 {n} 个对象设置区域",
|
||||
"bulk_failed": "{n} 项失败:{reason}",
|
||||
"completing": "正在完成…",
|
||||
"interval": "间隔",
|
||||
@@ -116,6 +129,8 @@
|
||||
"custom": "自定义",
|
||||
"history": "历史记录",
|
||||
"cost": "成本",
|
||||
"cost_kind_credit": "收入",
|
||||
"cost_credit_hint": "回收的钱——出售、退款。会降低总计;每次完成的平均值不计入。",
|
||||
"report_button": "报告",
|
||||
"report_title": "维护报告",
|
||||
"report_generated": "生成于",
|
||||
@@ -672,6 +687,7 @@
|
||||
"settings_warranty_reminder_days": "到期前天数",
|
||||
"settings_warranty_reminder_hint": "当物品保修距到期还有这么多天时通知一次。",
|
||||
"settings_snooze_hours": "稍后提醒间隔 (小时)",
|
||||
"settings_snooze_hours_hint": "“稍后提醒”让任务提醒静音的时长——无论来自任务菜单、卡片、语音还是手机上的稍后提醒按钮。截止日期保持不变。",
|
||||
"settings_budget": "预算",
|
||||
"settings_currency": "货币单位",
|
||||
"settings_currency_decimals": "金额小数位数",
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-250
@@ -1,250 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.88.0 */
|
||||
import{e as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z4FODB56.js";import{a as p}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-XRSEIZKJ.js";import{a as o,b as _,c as i,f as l,h,l as g,m as n,q as t,s as u,u as v}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OXPKT66L.js";var r=class extends h{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._users=[];this._responsible="";this._forMinutes="0";this._localeReady=!1;this._userService=null;this._toggle=s=>{let a=new Set(this._selected);a.has(s)?a.delete(s):a.add(s),this._selected=a};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(s=>s.entity_id))};this._adopt=async()=>{if(!(this._selected.size===0||this._adopting)){this._adopting=!0,this._error="";try{let s=this._sensors.filter(e=>this._selected.has(e.entity_id)).map(e=>({entity_id:e.entity_id,name:e.name,entry_id:e.suggested_entry_id??void 0,object_name:e.suggested_object_name,device_id:e.device_id??void 0,part_id:e.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0,for_minutes:parseInt(this._forMinutes,10)>0?parseInt(this._forMinutes,10):void 0})),a=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/adopt",selections:s});this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:a})),this._open=!1}catch(s){this._error=p(s,this._lang)}finally{this._adopting=!1}}}}get _lang(){return u(this.hass)}updated(s){s.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,v(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._responsible="",this._forMinutes="0";try{this._userService?this._userService.updateHass(this.hass):this._userService=new f(this.hass);let[s,a]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/discover"}),this._userService.getUsers().catch(()=>[])]);this._sensors=s.sensors||[],this._selected=new Set(this._sensors.map(e=>e.entity_id)),this._users=a}catch(s){this._error=p(s,this._lang)}finally{this._loading=!1}}_close(){this._open=!1}render(){if(!this._open)return i``;let s=this._lang,a=this._sensors.length>0&&this._selected.size===this._sensors.length;return i`
|
||||
<div class="overlay" @click=${this._close}>
|
||||
<div class="card" @click=${e=>e.stopPropagation()}>
|
||||
<div class="title">${t("adopt_problem_title",s)}</div>
|
||||
<div class="hint">${t("adopt_problem_hint",s)}</div>
|
||||
${this._error?i`<div class="error">${this._error}</div>`:l}
|
||||
|
||||
${this._loading?i`<div class="loading">…</div>`:this._sensors.length===0?i`<div class="empty">${t("adopt_problem_none",s)}</div>`:i`
|
||||
<label class="select-all">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${a}
|
||||
@change=${this._toggleAll}
|
||||
/>
|
||||
<span>${t("selected",s)}: ${this._selected.size} / ${this._sensors.length}</span>
|
||||
</label>
|
||||
<div class="list">
|
||||
${this._sensors.map(e=>{let m=this._selected.has(e.entity_id),d=e.state==="on",c=[e.device_name,e.area_name].filter(Boolean).join(" \xB7 ");return i`
|
||||
<label class="row">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${m}
|
||||
@change=${()=>this._toggle(e.entity_id)}
|
||||
/>
|
||||
<div class="row-main">
|
||||
<div class="row-top">
|
||||
<span class="row-name">${e.name}</span>
|
||||
<span class="chip ${d?"chip-active":"chip-ok"}">
|
||||
${d?t("adopt_problem_active",s):t("adopt_problem_ok",s)}
|
||||
</span>
|
||||
</div>
|
||||
${c?i`<div class="row-sub">${c}</div>`:l}
|
||||
<div class="row-target">
|
||||
→ ${e.suggested_object_name}${e.suggested_entry_id?l:i` <span class="new-tag">${t("adopt_problem_new_object",s)}</span>`}
|
||||
</div>
|
||||
${e.suggested_part_name?i`<div class="row-part">
|
||||
<ha-icon icon="mdi:package-variant-closed"></ha-icon>
|
||||
${t("adopt_problem_part",s).replace("{name}",e.suggested_part_name)}
|
||||
</div>`:l}
|
||||
</div>
|
||||
</label>
|
||||
`})}
|
||||
</div>
|
||||
`}
|
||||
|
||||
${!this._loading&&this._sensors.length>0?i`
|
||||
<label class="responsible">
|
||||
<span>${t("for_at_least_minutes",s)}</span>
|
||||
<input
|
||||
class="for-input"
|
||||
type="number"
|
||||
min="0"
|
||||
max="1440"
|
||||
.value=${this._forMinutes}
|
||||
@input=${e=>this._forMinutes=e.target.value}
|
||||
/>
|
||||
</label>
|
||||
<div class="for-hint">${t("adopt_for_minutes_hint",s)}</div>
|
||||
`:l}
|
||||
|
||||
${!this._loading&&this._sensors.length>0&&this._users.length>0?i`
|
||||
<label class="responsible">
|
||||
<span>${t("adopt_problem_responsible",s)}</span>
|
||||
<select
|
||||
.value=${this._responsible}
|
||||
@change=${e=>{this._responsible=e.target.value}}
|
||||
>
|
||||
<option value="" ?selected=${!this._responsible}>${t("no_user_assigned",s)}</option>
|
||||
${this._users.map(e=>i`<option value=${e.id} ?selected=${e.id===this._responsible}>${e.name}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
`:l}
|
||||
|
||||
<div class="actions">
|
||||
<ha-button appearance="plain" @click=${this._close}>
|
||||
${t("cancel",s)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
@click=${this._adopt}
|
||||
.disabled=${this._selected.size===0||this._adopting}
|
||||
>
|
||||
${t("adopt_problem_adopt",s)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}};r.styles=_`
|
||||
.overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-width: min(360px, calc(100vw - 24px));
|
||||
max-width: 560px;
|
||||
width: 90vw;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.hint {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.error {
|
||||
color: var(--error-color, #f44336);
|
||||
font-size: 13px;
|
||||
}
|
||||
.loading,
|
||||
.empty {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
.select-all {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
.select-all input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
overflow-y: auto;
|
||||
max-height: 50vh;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row input {
|
||||
margin-top: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.row-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.row-name {
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
}
|
||||
.row-sub {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-target {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-part {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.row-part ha-icon {
|
||||
--mdc-icon-size: 14px;
|
||||
}
|
||||
.new-tag {
|
||||
font-style: italic;
|
||||
}
|
||||
.chip {
|
||||
font-size: 11px;
|
||||
padding: 1px 8px;
|
||||
border-radius: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chip-active {
|
||||
background: var(--error-color, #f44336);
|
||||
color: #fff;
|
||||
}
|
||||
.chip-ok {
|
||||
background: var(--divider-color);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.responsible {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.for-input {
|
||||
width: 70px;
|
||||
background: var(--card-background-color);
|
||||
color: var(--primary-text-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
.for-hint {
|
||||
font-size: 11px;
|
||||
color: var(--secondary-text-color);
|
||||
margin: -4px 0 2px;
|
||||
}
|
||||
.responsible select {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
padding: 4px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--divider-color);
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
`,o([g({attribute:!1})],r.prototype,"hass",2),o([n()],r.prototype,"_open",2),o([n()],r.prototype,"_loading",2),o([n()],r.prototype,"_adopting",2),o([n()],r.prototype,"_error",2),o([n()],r.prototype,"_sensors",2),o([n()],r.prototype,"_selected",2),o([n()],r.prototype,"_users",2),o([n()],r.prototype,"_responsible",2),o([n()],r.prototype,"_forMinutes",2);customElements.get("maintenance-adopt-problem-sensors-dialog")||customElements.define("maintenance-adopt-problem-sensors-dialog",r);export{r as MaintenanceAdoptProblemSensorsDialog};
|
||||
-301
@@ -1,301 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.94.0 */
|
||||
import{e as y}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-6LZYA7IL.js";import{d as m}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-63C4ZGBD.js";import{a as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-K4SNNHPR.js";import{a as o,b as u,c as n,f as l,h as v,l as g,m as d,s as r,u as f,w as b}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-JE577H3E.js";var a=class extends v{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._objectNames={};this._objects=[];this._covered={};this._users=[];this._responsible="";this._forMinutes="0";this._localeReady=!1;this._userService=null;this._toggle=t=>{let i=new Set(this._selected);i.has(t)?i.delete(t):i.add(t),this._selected=i};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(t=>t.entity_id))};this._adopt=async()=>{if(this._selected.size===0||this._adopting)return;this._error="";let t=this._sensors.filter(e=>this._selected.has(e.entity_id)).map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)??void 0,object_name:this._effectiveName(e),device_id:e.device_id??void 0,part_id:e.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0,for_minutes:parseInt(this._forMinutes,10)>0?parseInt(this._forMinutes,10):void 0})),i=await h(this,{type:"maintenance_supporter/problem_sensors/adopt",selections:t},{busy:e=>{this._adopting=e},onError:e=>{this._error=e}});i!==void 0&&(this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:i})),this._open=!1)}}get _lang(){return f(this.hass)}updated(t){t.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,b(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._objectNames={},this._responsible="",this._forMinutes="0",this._userService?this._userService.updateHass(this.hass):this._userService=new y(this.hass);let[t,i,e]=await Promise.all([h(this,{type:"maintenance_supporter/problem_sensors/discover"},{onError:s=>{this._error=s}}),this._userService.getUsers().catch(()=>[]),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}).catch(()=>({objects:[]}))]);this._loading=!1,t!==void 0&&(this._objects=(e?.objects||[]).filter(s=>!s.object.archived_at).map(s=>({entry_id:s.entry_id,name:s.object.name,tasks:(s.tasks||[]).length})),this._sensors=t?.sensors||[],this._covered=Object.fromEntries(this._sensors.map(s=>[s.entity_id,s.covered_by??null])),this._selected=new Set(this._sensors.filter(s=>!s.covered_by).map(s=>s.entity_id)),this._users=i)}_defaultName(t){return t.suggested_entry_id?t.suggested_object_name:t.candidate?.name??t.suggested_object_name}_effectiveName(t){return(this._objectNames[t.entity_id]??"").trim()||this._defaultName(t)}_existingEntryFor(t){let i=this._effectiveName(t),e=t.suggested_entry_id||t.target_entry_id||null;if(i===this._defaultName(t)&&e)return e;let s=this._objects.find(c=>c.name.trim().toLowerCase()===i.toLowerCase());return s?s.entry_id:null}_schedulePreview(){clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._preview()},350)}async _preview(){let t=this._sensors.map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)})),i=await h(this,{type:"maintenance_supporter/problem_sensors/preview",selections:t},{onError:()=>{}});i?.covered&&(this._covered={...this._covered,...i.covered})}_summary(t){let i=new Map;for(let e of this._sensors){if(!this._selected.has(e.entity_id))continue;let s=this._existingEntryFor(e),c=this._effectiveName(e),_=s??`new:${c.toLowerCase()}`,p=i.get(_)??{name:c,entry:s,adding:0};p.adding+=1,i.set(_,p)}return i.size===0?l:n`
|
||||
<div class="summary">
|
||||
<div class="summary-title">${r("adopt_summary_title",t)}</div>
|
||||
${[...i.values()].map(e=>{if(!e.entry)return n`<div class="summary-line new">
|
||||
${r("setups_new_object_count",t).replace("{name}",e.name).replace("{count}",String(e.adding))}
|
||||
</div>`;let s=this._objects.find(c=>c.entry_id===e.entry)?.tasks??0;return n`<div class="summary-line">
|
||||
${r("adopt_summary_existing",t).replace("{name}",e.name).replace("{before}",String(s)).replace("{after}",String(s+e.adding))}
|
||||
</div>`})}
|
||||
</div>
|
||||
`}_close(){clearTimeout(this._previewTimer),this._open=!1}_targetLabel(t,i){if(t.candidate&&this._existingEntryFor(t)===t.candidate.entry_id){let e=t.candidate.reasons.map(s=>r(`setups_reason_${s}`,i)).join(", ");return r("setups_target_match",i).replace("{name}",t.candidate.name).replace("{reasons}",e)}return this._effectiveName(t)}render(){if(!this._open)return n``;let t=this._lang,i=this._sensors.length>0&&this._selected.size===this._sensors.length;return n`
|
||||
<div class="overlay" @click=${this._close}>
|
||||
<div class="card" @click=${e=>e.stopPropagation()}>
|
||||
<div class="title">${r("adopt_problem_title",t)}</div>
|
||||
<div class="hint">${r("adopt_problem_hint",t)}</div>
|
||||
${this._error?n`<div class="error">${this._error}</div>`:l}
|
||||
|
||||
${this._loading?n`<div class="loading">…</div>`:this._sensors.length===0?n`<div class="empty">${r("adopt_problem_none",t)}</div>`:n`
|
||||
<label class="select-all">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${i}
|
||||
@change=${this._toggleAll}
|
||||
/>
|
||||
<span>${r("selected",t)}: ${this._selected.size} / ${this._sensors.length}</span>
|
||||
</label>
|
||||
<div class="row-sub adopt-object-hint">${r("adopt_object_hint",t)}</div>
|
||||
<datalist id="adopt-object-names">
|
||||
${this._objects.map(e=>n`<option value=${e.name}></option>`)}
|
||||
</datalist>
|
||||
<div class="list">
|
||||
${this._sensors.map(e=>{let s=this._selected.has(e.entity_id),c=e.state==="on",_=[e.device_name,e.area_name].filter(Boolean).join(" \xB7 ");return n`
|
||||
<label class="row">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${s}
|
||||
@change=${()=>this._toggle(e.entity_id)}
|
||||
/>
|
||||
<div class="row-main">
|
||||
<div class="row-top">
|
||||
<span class="row-name">${e.name}</span>
|
||||
<span class="chip ${c?"chip-active":"chip-ok"}">
|
||||
${c?r("adopt_problem_active",t):r("adopt_problem_ok",t)}
|
||||
</span>
|
||||
</div>
|
||||
${_?n`<div class="row-sub">${_}</div>`:l}
|
||||
${s?n`<div class="row-object" @click=${p=>p.stopPropagation()}>
|
||||
<input
|
||||
class="adopt-object"
|
||||
list="adopt-object-names"
|
||||
aria-label=${r("object",t)}
|
||||
placeholder=${this._defaultName(e)}
|
||||
.value=${this._objectNames[e.entity_id]??this._defaultName(e)}
|
||||
@input=${p=>{this._objectNames={...this._objectNames,[e.entity_id]:p.target.value},this._schedulePreview()}}
|
||||
/>
|
||||
</div>`:l}
|
||||
<div class="row-target">
|
||||
→ ${this._targetLabel(e,t)}${this._existingEntryFor(e)?l:n` <span class="new-tag">${r("adopt_problem_new_object",t)}</span>`}
|
||||
</div>
|
||||
${this._covered[e.entity_id]?n`<div class="maybe">
|
||||
${r("setups_maybe_covered",t).replace("{name}",this._covered[e.entity_id].name)}
|
||||
</div>`:l}
|
||||
${e.suggested_part_name?n`<div class="row-part">
|
||||
<ha-icon icon="mdi:package-variant-closed"></ha-icon>
|
||||
${r("adopt_problem_part",t).replace("{name}",e.suggested_part_name)}
|
||||
</div>`:l}
|
||||
</div>
|
||||
</label>
|
||||
`})}
|
||||
</div>
|
||||
`}
|
||||
|
||||
${!this._loading&&this._sensors.length>0?this._summary(t):l}
|
||||
|
||||
${!this._loading&&this._sensors.length>0?n`
|
||||
<label class="responsible">
|
||||
<span>${r("for_at_least_minutes",t)}</span>
|
||||
<input
|
||||
class="for-input"
|
||||
type="number"
|
||||
min=${m[0]}
|
||||
max=${m[1]}
|
||||
.value=${this._forMinutes}
|
||||
@input=${e=>this._forMinutes=e.target.value}
|
||||
/>
|
||||
</label>
|
||||
<div class="for-hint">${r("adopt_for_minutes_hint",t)}</div>
|
||||
`:l}
|
||||
|
||||
${!this._loading&&this._sensors.length>0&&this._users.length>0?n`
|
||||
<label class="responsible">
|
||||
<span>${r("adopt_problem_responsible",t)}</span>
|
||||
<select
|
||||
.value=${this._responsible}
|
||||
@change=${e=>{this._responsible=e.target.value}}
|
||||
>
|
||||
<option value="" ?selected=${!this._responsible}>${r("no_user_assigned",t)}</option>
|
||||
${this._users.map(e=>n`<option value=${e.id} ?selected=${e.id===this._responsible}>${e.name}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
`:l}
|
||||
|
||||
<div class="actions">
|
||||
<ha-button appearance="plain" @click=${this._close}>
|
||||
${r("cancel",t)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
@click=${this._adopt}
|
||||
.disabled=${this._selected.size===0||this._adopting}
|
||||
>
|
||||
${r("adopt_problem_adopt",t)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}};a.styles=u`
|
||||
.overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-width: min(360px, calc(100vw - 24px));
|
||||
max-width: 560px;
|
||||
width: 90vw;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.hint {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.error {
|
||||
color: var(--error-color, #f44336);
|
||||
font-size: 13px;
|
||||
}
|
||||
.loading,
|
||||
.empty {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
.select-all {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
.select-all input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
overflow-y: auto;
|
||||
max-height: 50vh;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row input {
|
||||
margin-top: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.row-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.row-name {
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
}
|
||||
.row-sub {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-object { margin-top: 6px; }
|
||||
.row-object input {
|
||||
width: 100%; box-sizing: border-box; padding: 6px 8px; font: inherit;
|
||||
border: 1px solid var(--divider-color); border-radius: 6px;
|
||||
background: var(--card-background-color); color: var(--primary-text-color);
|
||||
}
|
||||
.adopt-object-hint { margin: 0 0 8px; }
|
||||
.row-target {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-part {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.row-part ha-icon {
|
||||
--mdc-icon-size: 14px;
|
||||
}
|
||||
.new-tag {
|
||||
font-style: italic;
|
||||
}
|
||||
.maybe {
|
||||
font-size: 11px;
|
||||
color: var(--warning-color, #ff9800);
|
||||
}
|
||||
.summary {
|
||||
font-size: 12px;
|
||||
border-top: 1px dashed var(--divider-color);
|
||||
padding-top: 6px;
|
||||
}
|
||||
.summary-title {
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.summary-line {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.chip {
|
||||
font-size: 11px;
|
||||
padding: 1px 8px;
|
||||
border-radius: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chip-active {
|
||||
background: var(--error-color, #f44336);
|
||||
color: #fff;
|
||||
}
|
||||
.chip-ok {
|
||||
background: var(--divider-color);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.responsible {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.for-input {
|
||||
width: 70px;
|
||||
background: var(--card-background-color);
|
||||
color: var(--primary-text-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
.for-hint {
|
||||
font-size: 11px;
|
||||
color: var(--secondary-text-color);
|
||||
margin: -4px 0 2px;
|
||||
}
|
||||
.responsible select {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
padding: 4px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--divider-color);
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
`,o([g({attribute:!1})],a.prototype,"hass",2),o([d()],a.prototype,"_open",2),o([d()],a.prototype,"_loading",2),o([d()],a.prototype,"_adopting",2),o([d()],a.prototype,"_error",2),o([d()],a.prototype,"_sensors",2),o([d()],a.prototype,"_selected",2),o([d()],a.prototype,"_objectNames",2),o([d()],a.prototype,"_objects",2),o([d()],a.prototype,"_covered",2),o([d()],a.prototype,"_users",2),o([d()],a.prototype,"_responsible",2),o([d()],a.prototype,"_forMinutes",2);customElements.get("maintenance-adopt-problem-sensors-dialog")||customElements.define("maintenance-adopt-problem-sensors-dialog",a);export{a as MaintenanceAdoptProblemSensorsDialog};
|
||||
-301
@@ -1,301 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.95.0 */
|
||||
import{e as y}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-UJICOAU3.js";import{d as m}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-LEWJHPDY.js";import{a as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-GGJ6YWZO.js";import{a as o,b as u,c as n,f as l,h as v,l as g,m as d,s as r,u as f,y as b}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-4XNVTWGC.js";var a=class extends v{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._objectNames={};this._objects=[];this._covered={};this._users=[];this._responsible="";this._forMinutes="0";this._localeReady=!1;this._userService=null;this._toggle=t=>{let i=new Set(this._selected);i.has(t)?i.delete(t):i.add(t),this._selected=i};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(t=>t.entity_id))};this._adopt=async()=>{if(this._selected.size===0||this._adopting)return;this._error="";let t=this._sensors.filter(e=>this._selected.has(e.entity_id)).map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)??void 0,object_name:this._effectiveName(e),device_id:e.device_id??void 0,part_id:e.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0,for_minutes:parseInt(this._forMinutes,10)>0?parseInt(this._forMinutes,10):void 0})),i=await h(this,{type:"maintenance_supporter/problem_sensors/adopt",selections:t},{busy:e=>{this._adopting=e},onError:e=>{this._error=e}});i!==void 0&&(this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:i})),this._open=!1)}}get _lang(){return b(this.hass)}updated(t){t.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,f(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._objectNames={},this._responsible="",this._forMinutes="0",this._userService?this._userService.updateHass(this.hass):this._userService=new y(this.hass);let[t,i,e]=await Promise.all([h(this,{type:"maintenance_supporter/problem_sensors/discover"},{onError:s=>{this._error=s}}),this._userService.getUsers().catch(()=>[]),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}).catch(()=>({objects:[]}))]);this._loading=!1,t!==void 0&&(this._objects=(e?.objects||[]).filter(s=>!s.object.archived_at).map(s=>({entry_id:s.entry_id,name:s.object.name,tasks:(s.tasks||[]).length})),this._sensors=t?.sensors||[],this._covered=Object.fromEntries(this._sensors.map(s=>[s.entity_id,s.covered_by??null])),this._selected=new Set(this._sensors.filter(s=>!s.covered_by).map(s=>s.entity_id)),this._users=i)}_defaultName(t){return t.suggested_entry_id?t.suggested_object_name:t.candidate?.name??t.suggested_object_name}_effectiveName(t){return(this._objectNames[t.entity_id]??"").trim()||this._defaultName(t)}_existingEntryFor(t){let i=this._effectiveName(t),e=t.suggested_entry_id||t.target_entry_id||null;if(i===this._defaultName(t)&&e)return e;let s=this._objects.find(c=>c.name.trim().toLowerCase()===i.toLowerCase());return s?s.entry_id:null}_schedulePreview(){clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._preview()},350)}async _preview(){let t=this._sensors.map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)})),i=await h(this,{type:"maintenance_supporter/problem_sensors/preview",selections:t},{onError:()=>{}});i?.covered&&(this._covered={...this._covered,...i.covered})}_summary(t){let i=new Map;for(let e of this._sensors){if(!this._selected.has(e.entity_id))continue;let s=this._existingEntryFor(e),c=this._effectiveName(e),_=s??`new:${c.toLowerCase()}`,p=i.get(_)??{name:c,entry:s,adding:0};p.adding+=1,i.set(_,p)}return i.size===0?l:n`
|
||||
<div class="summary">
|
||||
<div class="summary-title">${r("adopt_summary_title",t)}</div>
|
||||
${[...i.values()].map(e=>{if(!e.entry)return n`<div class="summary-line new">
|
||||
${r("setups_new_object_count",t).replace("{name}",e.name).replace("{count}",String(e.adding))}
|
||||
</div>`;let s=this._objects.find(c=>c.entry_id===e.entry)?.tasks??0;return n`<div class="summary-line">
|
||||
${r("adopt_summary_existing",t).replace("{name}",e.name).replace("{before}",String(s)).replace("{after}",String(s+e.adding))}
|
||||
</div>`})}
|
||||
</div>
|
||||
`}_close(){clearTimeout(this._previewTimer),this._open=!1}_targetLabel(t,i){if(t.candidate&&this._existingEntryFor(t)===t.candidate.entry_id){let e=t.candidate.reasons.map(s=>r(`setups_reason_${s}`,i)).join(", ");return r("setups_target_match",i).replace("{name}",t.candidate.name).replace("{reasons}",e)}return this._effectiveName(t)}render(){if(!this._open)return n``;let t=this._lang,i=this._sensors.length>0&&this._selected.size===this._sensors.length;return n`
|
||||
<div class="overlay" @click=${this._close}>
|
||||
<div class="card" @click=${e=>e.stopPropagation()}>
|
||||
<div class="title">${r("adopt_problem_title",t)}</div>
|
||||
<div class="hint">${r("adopt_problem_hint",t)}</div>
|
||||
${this._error?n`<div class="error">${this._error}</div>`:l}
|
||||
|
||||
${this._loading?n`<div class="loading">…</div>`:this._sensors.length===0?n`<div class="empty">${r("adopt_problem_none",t)}</div>`:n`
|
||||
<label class="select-all">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${i}
|
||||
@change=${this._toggleAll}
|
||||
/>
|
||||
<span>${r("selected",t)}: ${this._selected.size} / ${this._sensors.length}</span>
|
||||
</label>
|
||||
<div class="row-sub adopt-object-hint">${r("adopt_object_hint",t)}</div>
|
||||
<datalist id="adopt-object-names">
|
||||
${this._objects.map(e=>n`<option value=${e.name}></option>`)}
|
||||
</datalist>
|
||||
<div class="list">
|
||||
${this._sensors.map(e=>{let s=this._selected.has(e.entity_id),c=e.state==="on",_=[e.device_name,e.area_name].filter(Boolean).join(" \xB7 ");return n`
|
||||
<label class="row">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${s}
|
||||
@change=${()=>this._toggle(e.entity_id)}
|
||||
/>
|
||||
<div class="row-main">
|
||||
<div class="row-top">
|
||||
<span class="row-name">${e.name}</span>
|
||||
<span class="chip ${c?"chip-active":"chip-ok"}">
|
||||
${c?r("adopt_problem_active",t):r("adopt_problem_ok",t)}
|
||||
</span>
|
||||
</div>
|
||||
${_?n`<div class="row-sub">${_}</div>`:l}
|
||||
${s?n`<div class="row-object" @click=${p=>p.stopPropagation()}>
|
||||
<input
|
||||
class="adopt-object"
|
||||
list="adopt-object-names"
|
||||
aria-label=${r("object",t)}
|
||||
placeholder=${this._defaultName(e)}
|
||||
.value=${this._objectNames[e.entity_id]??this._defaultName(e)}
|
||||
@input=${p=>{this._objectNames={...this._objectNames,[e.entity_id]:p.target.value},this._schedulePreview()}}
|
||||
/>
|
||||
</div>`:l}
|
||||
<div class="row-target">
|
||||
→ ${this._targetLabel(e,t)}${this._existingEntryFor(e)?l:n` <span class="new-tag">${r("adopt_problem_new_object",t)}</span>`}
|
||||
</div>
|
||||
${this._covered[e.entity_id]?n`<div class="maybe">
|
||||
${r("setups_maybe_covered",t).replace("{name}",this._covered[e.entity_id].name)}
|
||||
</div>`:l}
|
||||
${e.suggested_part_name?n`<div class="row-part">
|
||||
<ha-icon icon="mdi:package-variant-closed"></ha-icon>
|
||||
${r("adopt_problem_part",t).replace("{name}",e.suggested_part_name)}
|
||||
</div>`:l}
|
||||
</div>
|
||||
</label>
|
||||
`})}
|
||||
</div>
|
||||
`}
|
||||
|
||||
${!this._loading&&this._sensors.length>0?this._summary(t):l}
|
||||
|
||||
${!this._loading&&this._sensors.length>0?n`
|
||||
<label class="responsible">
|
||||
<span>${r("for_at_least_minutes",t)}</span>
|
||||
<input
|
||||
class="for-input"
|
||||
type="number"
|
||||
min=${m[0]}
|
||||
max=${m[1]}
|
||||
.value=${this._forMinutes}
|
||||
@input=${e=>this._forMinutes=e.target.value}
|
||||
/>
|
||||
</label>
|
||||
<div class="for-hint">${r("adopt_for_minutes_hint",t)}</div>
|
||||
`:l}
|
||||
|
||||
${!this._loading&&this._sensors.length>0&&this._users.length>0?n`
|
||||
<label class="responsible">
|
||||
<span>${r("adopt_problem_responsible",t)}</span>
|
||||
<select
|
||||
.value=${this._responsible}
|
||||
@change=${e=>{this._responsible=e.target.value}}
|
||||
>
|
||||
<option value="" ?selected=${!this._responsible}>${r("no_user_assigned",t)}</option>
|
||||
${this._users.map(e=>n`<option value=${e.id} ?selected=${e.id===this._responsible}>${e.name}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
`:l}
|
||||
|
||||
<div class="actions">
|
||||
<ha-button appearance="plain" @click=${this._close}>
|
||||
${r("cancel",t)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
@click=${this._adopt}
|
||||
.disabled=${this._selected.size===0||this._adopting}
|
||||
>
|
||||
${r("adopt_problem_adopt",t)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}};a.styles=u`
|
||||
.overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-width: min(360px, calc(100vw - 24px));
|
||||
max-width: 560px;
|
||||
width: 90vw;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.hint {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.error {
|
||||
color: var(--error-color, #f44336);
|
||||
font-size: 13px;
|
||||
}
|
||||
.loading,
|
||||
.empty {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
.select-all {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
.select-all input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
overflow-y: auto;
|
||||
max-height: 50vh;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row input {
|
||||
margin-top: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.row-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.row-name {
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
}
|
||||
.row-sub {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-object { margin-top: 6px; }
|
||||
.row-object input {
|
||||
width: 100%; box-sizing: border-box; padding: 6px 8px; font: inherit;
|
||||
border: 1px solid var(--divider-color); border-radius: 6px;
|
||||
background: var(--card-background-color); color: var(--primary-text-color);
|
||||
}
|
||||
.adopt-object-hint { margin: 0 0 8px; }
|
||||
.row-target {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-part {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.row-part ha-icon {
|
||||
--mdc-icon-size: 14px;
|
||||
}
|
||||
.new-tag {
|
||||
font-style: italic;
|
||||
}
|
||||
.maybe {
|
||||
font-size: 11px;
|
||||
color: var(--warning-color, #ff9800);
|
||||
}
|
||||
.summary {
|
||||
font-size: 12px;
|
||||
border-top: 1px dashed var(--divider-color);
|
||||
padding-top: 6px;
|
||||
}
|
||||
.summary-title {
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.summary-line {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.chip {
|
||||
font-size: 11px;
|
||||
padding: 1px 8px;
|
||||
border-radius: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chip-active {
|
||||
background: var(--error-color, #f44336);
|
||||
color: #fff;
|
||||
}
|
||||
.chip-ok {
|
||||
background: var(--divider-color);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.responsible {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.for-input {
|
||||
width: 70px;
|
||||
background: var(--card-background-color);
|
||||
color: var(--primary-text-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
.for-hint {
|
||||
font-size: 11px;
|
||||
color: var(--secondary-text-color);
|
||||
margin: -4px 0 2px;
|
||||
}
|
||||
.responsible select {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
padding: 4px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--divider-color);
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
`,o([g({attribute:!1})],a.prototype,"hass",2),o([d()],a.prototype,"_open",2),o([d()],a.prototype,"_loading",2),o([d()],a.prototype,"_adopting",2),o([d()],a.prototype,"_error",2),o([d()],a.prototype,"_sensors",2),o([d()],a.prototype,"_selected",2),o([d()],a.prototype,"_objectNames",2),o([d()],a.prototype,"_objects",2),o([d()],a.prototype,"_covered",2),o([d()],a.prototype,"_users",2),o([d()],a.prototype,"_responsible",2),o([d()],a.prototype,"_forMinutes",2);customElements.get("maintenance-adopt-problem-sensors-dialog")||customElements.define("maintenance-adopt-problem-sensors-dialog",a);export{a as MaintenanceAdoptProblemSensorsDialog};
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*! maintenance_supporter frontend 2.96.0 */
|
||||
import{e as y}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-4PTWLV7V.js";import{d as m}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-WJT4RRLS.js";import{a as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-BFBOHWBW.js";import{a as o,b as u,c as n,f as l,h as v,l as g,m as d,s as r,u as f,y as b}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-N6F5RX6S.js";var a=class extends v{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._objectNames={};this._objects=[];this._covered={};this._users=[];this._responsible="";this._forMinutes="0";this._localeReady=!1;this._userService=null;this._toggle=t=>{let i=new Set(this._selected);i.has(t)?i.delete(t):i.add(t),this._selected=i};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(t=>t.entity_id))};this._adopt=async()=>{if(this._selected.size===0||this._adopting)return;this._error="";let t=this._sensors.filter(e=>this._selected.has(e.entity_id)).map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)??void 0,object_name:this._effectiveName(e),device_id:e.device_id??void 0,part_id:e.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0,for_minutes:parseInt(this._forMinutes,10)>0?parseInt(this._forMinutes,10):void 0})),i=await h(this,{type:"maintenance_supporter/problem_sensors/adopt",selections:t},{busy:e=>{this._adopting=e},onError:e=>{this._error=e}});i!==void 0&&(this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:i})),this._open=!1)}}get _lang(){return b(this.hass)}updated(t){t.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,f(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._objectNames={},this._responsible="",this._forMinutes="0",this._userService?this._userService.updateHass(this.hass):this._userService=new y(this.hass);let[t,i,e]=await Promise.all([h(this,{type:"maintenance_supporter/problem_sensors/discover"},{onError:s=>{this._error=s}}),this._userService.getUsers().catch(()=>[]),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}).catch(()=>({objects:[]}))]);this._loading=!1,t!==void 0&&(this._objects=(e?.objects||[]).filter(s=>!s.object.archived_at).map(s=>({entry_id:s.entry_id,name:s.object.name,tasks:(s.tasks||[]).length})),this._sensors=t?.sensors||[],this._covered=Object.fromEntries(this._sensors.map(s=>[s.entity_id,s.covered_by??null])),this._selected=new Set(this._sensors.filter(s=>!s.covered_by).map(s=>s.entity_id)),this._users=i)}_defaultName(t){return t.suggested_entry_id?t.suggested_object_name:t.candidate?.name??t.suggested_object_name}_effectiveName(t){return(this._objectNames[t.entity_id]??"").trim()||this._defaultName(t)}_existingEntryFor(t){let i=this._effectiveName(t),e=t.suggested_entry_id||t.target_entry_id||null;if(i===this._defaultName(t)&&e)return e;let s=this._objects.find(c=>c.name.trim().toLowerCase()===i.toLowerCase());return s?s.entry_id:null}_schedulePreview(){clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._preview()},350)}async _preview(){let t=this._sensors.map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)})),i=await h(this,{type:"maintenance_supporter/problem_sensors/preview",selections:t},{onError:()=>{}});i?.covered&&(this._covered={...this._covered,...i.covered})}_summary(t){let i=new Map;for(let e of this._sensors){if(!this._selected.has(e.entity_id))continue;let s=this._existingEntryFor(e),c=this._effectiveName(e),_=s??`new:${c.toLowerCase()}`,p=i.get(_)??{name:c,entry:s,adding:0};p.adding+=1,i.set(_,p)}return i.size===0?l:n`
|
||||
/*! maintenance_supporter frontend 2.97.1 */
|
||||
import{e as y}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-D4KM62SJ.js";import{d as m}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-GSRF4WCQ.js";import{a as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-GVLO7RZ4.js";import{a as o,b as u,c as n,f as l,h as v,l as g,m as d,s as r,u as f,y as b}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YJXVPPWG.js";var a=class extends v{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._objectNames={};this._objects=[];this._covered={};this._users=[];this._responsible="";this._forMinutes="0";this._localeReady=!1;this._userService=null;this._toggle=t=>{let i=new Set(this._selected);i.has(t)?i.delete(t):i.add(t),this._selected=i};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(t=>t.entity_id))};this._adopt=async()=>{if(this._selected.size===0||this._adopting)return;this._error="";let t=this._sensors.filter(e=>this._selected.has(e.entity_id)).map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)??void 0,object_name:this._effectiveName(e),device_id:e.device_id??void 0,part_id:e.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0,for_minutes:parseInt(this._forMinutes,10)>0?parseInt(this._forMinutes,10):void 0})),i=await h(this,{type:"maintenance_supporter/problem_sensors/adopt",selections:t},{busy:e=>{this._adopting=e},onError:e=>{this._error=e}});i!==void 0&&(this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:i})),this._open=!1)}}get _lang(){return b(this.hass)}updated(t){t.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,f(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._objectNames={},this._responsible="",this._forMinutes="0",this._userService?this._userService.updateHass(this.hass):this._userService=new y(this.hass);let[t,i,e]=await Promise.all([h(this,{type:"maintenance_supporter/problem_sensors/discover"},{onError:s=>{this._error=s}}),this._userService.getUsers().catch(()=>[]),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}).catch(()=>({objects:[]}))]);this._loading=!1,t!==void 0&&(this._objects=(e?.objects||[]).filter(s=>!s.object.archived_at).map(s=>({entry_id:s.entry_id,name:s.object.name,tasks:(s.tasks||[]).length})),this._sensors=t?.sensors||[],this._covered=Object.fromEntries(this._sensors.map(s=>[s.entity_id,s.covered_by??null])),this._selected=new Set(this._sensors.filter(s=>!s.covered_by).map(s=>s.entity_id)),this._users=i)}_defaultName(t){return t.suggested_entry_id?t.suggested_object_name:t.candidate?.name??t.suggested_object_name}_effectiveName(t){return(this._objectNames[t.entity_id]??"").trim()||this._defaultName(t)}_existingEntryFor(t){let i=this._effectiveName(t),e=t.suggested_entry_id||t.target_entry_id||null;if(i===this._defaultName(t)&&e)return e;let s=this._objects.find(c=>c.name.trim().toLowerCase()===i.toLowerCase());return s?s.entry_id:null}_schedulePreview(){clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._preview()},350)}async _preview(){let t=this._sensors.map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)})),i=await h(this,{type:"maintenance_supporter/problem_sensors/preview",selections:t},{onError:()=>{}});i?.covered&&(this._covered={...this._covered,...i.covered})}_summary(t){let i=new Map;for(let e of this._sensors){if(!this._selected.has(e.entity_id))continue;let s=this._existingEntryFor(e),c=this._effectiveName(e),_=s??`new:${c.toLowerCase()}`,p=i.get(_)??{name:c,entry:s,adding:0};p.adding+=1,i.set(_,p)}return i.size===0?l:n`
|
||||
<div class="summary">
|
||||
<div class="summary-title">${r("adopt_summary_title",t)}</div>
|
||||
${[...i.values()].map(e=>{if(!e.entry)return n`<div class="summary-line new">
|
||||
-271
@@ -1,271 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.90.0 */
|
||||
import{e as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-73EHFI76.js";import{a as p}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-WQXORDNI.js";import{a,b as g,c as i,f as c,h as u,l as v,m as n,q as s,s as m,u as b}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-RAW6RFRE.js";var r=class extends u{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._objectNames={};this._objects=[];this._users=[];this._responsible="";this._forMinutes="0";this._localeReady=!1;this._userService=null;this._toggle=e=>{let o=new Set(this._selected);o.has(e)?o.delete(e):o.add(e),this._selected=o};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(e=>e.entity_id))};this._adopt=async()=>{if(!(this._selected.size===0||this._adopting)){this._adopting=!0,this._error="";try{let e=this._sensors.filter(t=>this._selected.has(t.entity_id)).map(t=>({entity_id:t.entity_id,name:t.name,entry_id:this._existingEntryFor(t)??void 0,object_name:this._effectiveName(t),device_id:t.device_id??void 0,part_id:t.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0,for_minutes:parseInt(this._forMinutes,10)>0?parseInt(this._forMinutes,10):void 0})),o=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/adopt",selections:e});this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:o})),this._open=!1}catch(e){this._error=p(e,this._lang)}finally{this._adopting=!1}}}}get _lang(){return m(this.hass)}updated(e){e.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,b(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._objectNames={},this._responsible="",this._forMinutes="0";try{this._userService?this._userService.updateHass(this.hass):this._userService=new f(this.hass);let[e,o,t]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/discover"}),this._userService.getUsers().catch(()=>[]),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}).catch(()=>({objects:[]}))]);this._objects=(t.objects||[]).filter(l=>!l.object.archived_at).map(l=>({entry_id:l.entry_id,name:l.object.name})),this._sensors=e.sensors||[],this._selected=new Set(this._sensors.map(l=>l.entity_id)),this._users=o}catch(e){this._error=p(e,this._lang)}finally{this._loading=!1}}_effectiveName(e){return(this._objectNames[e.entity_id]??"").trim()||e.suggested_object_name}_existingEntryFor(e){let o=this._effectiveName(e);if(o===e.suggested_object_name&&e.suggested_entry_id)return e.suggested_entry_id;let t=this._objects.find(l=>l.name.trim().toLowerCase()===o.toLowerCase());return t?t.entry_id:null}_close(){this._open=!1}render(){if(!this._open)return i``;let e=this._lang,o=this._sensors.length>0&&this._selected.size===this._sensors.length;return i`
|
||||
<div class="overlay" @click=${this._close}>
|
||||
<div class="card" @click=${t=>t.stopPropagation()}>
|
||||
<div class="title">${s("adopt_problem_title",e)}</div>
|
||||
<div class="hint">${s("adopt_problem_hint",e)}</div>
|
||||
${this._error?i`<div class="error">${this._error}</div>`:c}
|
||||
|
||||
${this._loading?i`<div class="loading">…</div>`:this._sensors.length===0?i`<div class="empty">${s("adopt_problem_none",e)}</div>`:i`
|
||||
<label class="select-all">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${o}
|
||||
@change=${this._toggleAll}
|
||||
/>
|
||||
<span>${s("selected",e)}: ${this._selected.size} / ${this._sensors.length}</span>
|
||||
</label>
|
||||
<div class="row-sub adopt-object-hint">${s("adopt_object_hint",e)}</div>
|
||||
<datalist id="adopt-object-names">
|
||||
${this._objects.map(t=>i`<option value=${t.name}></option>`)}
|
||||
</datalist>
|
||||
<div class="list">
|
||||
${this._sensors.map(t=>{let l=this._selected.has(t.entity_id),_=t.state==="on",h=[t.device_name,t.area_name].filter(Boolean).join(" \xB7 ");return i`
|
||||
<label class="row">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${l}
|
||||
@change=${()=>this._toggle(t.entity_id)}
|
||||
/>
|
||||
<div class="row-main">
|
||||
<div class="row-top">
|
||||
<span class="row-name">${t.name}</span>
|
||||
<span class="chip ${_?"chip-active":"chip-ok"}">
|
||||
${_?s("adopt_problem_active",e):s("adopt_problem_ok",e)}
|
||||
</span>
|
||||
</div>
|
||||
${h?i`<div class="row-sub">${h}</div>`:c}
|
||||
${l?i`<div class="row-object" @click=${d=>d.stopPropagation()}>
|
||||
<input
|
||||
class="adopt-object"
|
||||
list="adopt-object-names"
|
||||
aria-label=${s("object",e)}
|
||||
placeholder=${t.suggested_object_name}
|
||||
.value=${this._objectNames[t.entity_id]??t.suggested_object_name}
|
||||
@input=${d=>{this._objectNames={...this._objectNames,[t.entity_id]:d.target.value}}}
|
||||
/>
|
||||
</div>`:c}
|
||||
<div class="row-target">
|
||||
→ ${this._effectiveName(t)}${this._existingEntryFor(t)?c:i` <span class="new-tag">${s("adopt_problem_new_object",e)}</span>`}
|
||||
</div>
|
||||
${t.suggested_part_name?i`<div class="row-part">
|
||||
<ha-icon icon="mdi:package-variant-closed"></ha-icon>
|
||||
${s("adopt_problem_part",e).replace("{name}",t.suggested_part_name)}
|
||||
</div>`:c}
|
||||
</div>
|
||||
</label>
|
||||
`})}
|
||||
</div>
|
||||
`}
|
||||
|
||||
${!this._loading&&this._sensors.length>0?i`
|
||||
<label class="responsible">
|
||||
<span>${s("for_at_least_minutes",e)}</span>
|
||||
<input
|
||||
class="for-input"
|
||||
type="number"
|
||||
min="0"
|
||||
max="1440"
|
||||
.value=${this._forMinutes}
|
||||
@input=${t=>this._forMinutes=t.target.value}
|
||||
/>
|
||||
</label>
|
||||
<div class="for-hint">${s("adopt_for_minutes_hint",e)}</div>
|
||||
`:c}
|
||||
|
||||
${!this._loading&&this._sensors.length>0&&this._users.length>0?i`
|
||||
<label class="responsible">
|
||||
<span>${s("adopt_problem_responsible",e)}</span>
|
||||
<select
|
||||
.value=${this._responsible}
|
||||
@change=${t=>{this._responsible=t.target.value}}
|
||||
>
|
||||
<option value="" ?selected=${!this._responsible}>${s("no_user_assigned",e)}</option>
|
||||
${this._users.map(t=>i`<option value=${t.id} ?selected=${t.id===this._responsible}>${t.name}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
`:c}
|
||||
|
||||
<div class="actions">
|
||||
<ha-button appearance="plain" @click=${this._close}>
|
||||
${s("cancel",e)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
@click=${this._adopt}
|
||||
.disabled=${this._selected.size===0||this._adopting}
|
||||
>
|
||||
${s("adopt_problem_adopt",e)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}};r.styles=g`
|
||||
.overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-width: min(360px, calc(100vw - 24px));
|
||||
max-width: 560px;
|
||||
width: 90vw;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.hint {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.error {
|
||||
color: var(--error-color, #f44336);
|
||||
font-size: 13px;
|
||||
}
|
||||
.loading,
|
||||
.empty {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
.select-all {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
.select-all input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
overflow-y: auto;
|
||||
max-height: 50vh;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row input {
|
||||
margin-top: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.row-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.row-name {
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
}
|
||||
.row-sub {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-object { margin-top: 6px; }
|
||||
.row-object input {
|
||||
width: 100%; box-sizing: border-box; padding: 6px 8px; font: inherit;
|
||||
border: 1px solid var(--divider-color); border-radius: 6px;
|
||||
background: var(--card-background-color); color: var(--primary-text-color);
|
||||
}
|
||||
.adopt-object-hint { margin: 0 0 8px; }
|
||||
.row-target {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-part {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.row-part ha-icon {
|
||||
--mdc-icon-size: 14px;
|
||||
}
|
||||
.new-tag {
|
||||
font-style: italic;
|
||||
}
|
||||
.chip {
|
||||
font-size: 11px;
|
||||
padding: 1px 8px;
|
||||
border-radius: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chip-active {
|
||||
background: var(--error-color, #f44336);
|
||||
color: #fff;
|
||||
}
|
||||
.chip-ok {
|
||||
background: var(--divider-color);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.responsible {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.for-input {
|
||||
width: 70px;
|
||||
background: var(--card-background-color);
|
||||
color: var(--primary-text-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
.for-hint {
|
||||
font-size: 11px;
|
||||
color: var(--secondary-text-color);
|
||||
margin: -4px 0 2px;
|
||||
}
|
||||
.responsible select {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
padding: 4px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--divider-color);
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
`,a([v({attribute:!1})],r.prototype,"hass",2),a([n()],r.prototype,"_open",2),a([n()],r.prototype,"_loading",2),a([n()],r.prototype,"_adopting",2),a([n()],r.prototype,"_error",2),a([n()],r.prototype,"_sensors",2),a([n()],r.prototype,"_selected",2),a([n()],r.prototype,"_objectNames",2),a([n()],r.prototype,"_objects",2),a([n()],r.prototype,"_users",2),a([n()],r.prototype,"_responsible",2),a([n()],r.prototype,"_forMinutes",2);customElements.get("maintenance-adopt-problem-sensors-dialog")||customElements.define("maintenance-adopt-problem-sensors-dialog",r);export{r as MaintenanceAdoptProblemSensorsDialog};
|
||||
@@ -1,277 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.95.0 */
|
||||
import{a as W}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-APJICCRG.js";import{a as o}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-XSFV5MUM.js";import{f as T,g as K,h as z,i as M,j as Y,k as Q,l as U,m as q,n as R,o as E,p as G}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YKVD2DJ5.js";import{a as A}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-E7KPGZZO.js";import{e as F,f as I}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-S7ZP4PVX.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-ODPGZXCU.js";import{b as P}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-JEWIFUZC.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-UVBHIULM.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-WUMSAU4G.js";import{B as $,C as g,F as O,G as S,J as w,L as N,a as b,c as p,f as y,h as B,l as v,m as k,n as C,q as D,s as a,u as H,y as L}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-4XNVTWGC.js";function X(_,r,t,s){let d=_.entries.filter(e=>e.type==="completed").sort((e,i)=>e.ts-i.ts),l=`${_.from?t.date(_.from):"\u2026"} \u2013 ${_.to?t.date(_.to):"\u2026"}`,n=_.totals,h=[[r.completions,t.number(n.completions)],[r.totalCost,t.cost(n.totalCost)],[r.avgCost,n.avgCost!=null?t.cost(n.avgCost):r.none],[r.totalTime,n.totalDuration?t.duration(n.totalDuration):r.none]].map(([e,i])=>`<div class="kpi"><div class="k">${o(e)}</div><div class="v">${o(i)}</div></div>`).join(""),c=_.byObject.map(e=>`<tr>
|
||||
<td>${o(e.objectName)}</td>
|
||||
<td class="num">${o(t.number(e.completions))}</td>
|
||||
<td class="num">${o(t.cost(e.cost))}</td>
|
||||
<td class="num">${o(t.share(e.share))}</td>
|
||||
</tr>`).join(""),m=_.buckets.buckets.reduce((e,i)=>Math.max(e,i.cost),0),f=_.buckets.buckets.map(e=>{let i=m>0?Math.round(e.cost/m*100):0;return`<tr>
|
||||
<td class="nowrap">${o(t.bucket(e))}</td>
|
||||
<td class="num">${o(t.number(e.completions))}</td>
|
||||
<td class="bar-cell">${i>0?`<span class="bar" style="width:${i}%"></span>`:""}</td>
|
||||
<td class="num">${o(t.cost(e.cost))}</td>
|
||||
</tr>`}).join(""),j=d.map(e=>{let i=[e.notes,e.completedBy?`${r.completedBy}: ${e.completedBy}`:null].filter(Boolean).join(" \xB7 "),x=e.phaseName?`${e.taskName} \xB7 ${e.phaseName}`:e.taskName;return`<tr>
|
||||
<td class="nowrap">${o(t.date(e.timestamp))}</td>
|
||||
<td>${o(e.objectName)}</td>
|
||||
<td>${o(x)}</td>
|
||||
<td class="num">${e.cost!=null?o(t.cost(e.cost)):o(r.none)}</td>
|
||||
<td class="num">${e.duration!=null?o(t.duration(e.duration)):o(r.none)}</td>
|
||||
<td class="notes">${o(i)||o(r.none)}</td>
|
||||
</tr>`}).join(`
|
||||
`);return`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="color-scheme" content="light">
|
||||
<title>${o(r.title)} \u2014 ${o(_.areaName)}</title>
|
||||
<style>
|
||||
/* Printable sheet: it opens as a blob in whatever viewer the OS supplies
|
||||
(Companion = WebView, dark phones paint a dark default canvas), so the
|
||||
document states its own light scheme and paints its background. */
|
||||
:root { color-scheme: light; }
|
||||
* { box-sizing: border-box; }
|
||||
body { font: 13px/1.5 -apple-system, Segoe UI, Roboto, sans-serif; color: #1a1a1a; background: #fff; margin: 32px; }
|
||||
h1 { font-size: 22px; margin: 0 0 2px; }
|
||||
h2 { font-size: 15px; margin: 24px 0 8px; }
|
||||
.sub { color: #666; margin: 0 0 16px; }
|
||||
.kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin: 0 0 8px; }
|
||||
.kpi { border: 1px solid #ddd; border-radius: 6px; padding: 8px 10px; }
|
||||
.kpi .k { color: #777; font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; }
|
||||
.kpi .v { font-size: 16px; font-weight: 600; }
|
||||
table { border-collapse: collapse; width: 100%; }
|
||||
th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: #666; border-bottom: 2px solid #ccc; padding: 6px 8px; }
|
||||
td { border-bottom: 1px solid #e5e5e5; padding: 6px 8px; vertical-align: top; }
|
||||
td.num, th.num { text-align: right; white-space: nowrap; }
|
||||
td.nowrap { white-space: nowrap; }
|
||||
td.notes { color: #444; }
|
||||
td.bar-cell { width: 40%; vertical-align: middle; }
|
||||
.bar { display: block; height: 10px; background: #9aa7b8; border-radius: 0 3px 3px 0;
|
||||
-webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
tfoot td { border-bottom: none; border-top: 2px solid #ccc; font-weight: 600; }
|
||||
tr { break-inside: avoid; }
|
||||
.cap-note { margin-top: 14px; color: #888; font-size: 11px; }
|
||||
@media print { body { margin: 12mm; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>${o(r.title)} \u2014 ${o(_.areaName)}</h1>
|
||||
<p class="sub">${o(r.period)}: ${o(l)} \xB7 ${o(r.generated)} ${o(t.date(s))}</p>
|
||||
<div class="kpis">${h}</div>
|
||||
|
||||
<h2>${o(r.costPerObject)}</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>${o(r.colObject)}</th><th class="num">${o(r.completions)}</th>
|
||||
<th class="num">${o(r.colCost)}</th><th class="num">${o(r.share)}</th>
|
||||
</tr></thead>
|
||||
<tbody>${c||`<tr><td colspan="4">${o(r.none)}</td></tr>`}</tbody>
|
||||
<tfoot><tr>
|
||||
<td>${o(r.totalCost)}</td><td class="num">${o(t.number(n.completions))}</td>
|
||||
<td class="num">${o(t.cost(n.totalCost))}</td><td></td>
|
||||
</tr></tfoot>
|
||||
</table>
|
||||
|
||||
<h2>${o(r.costPerBucket)}</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>${o(r.period)}</th><th class="num">${o(r.completions)}</th>
|
||||
<th></th><th class="num">${o(r.colCost)}</th>
|
||||
</tr></thead>
|
||||
<tbody>${f||`<tr><td colspan="4">${o(r.none)}</td></tr>`}</tbody>
|
||||
</table>
|
||||
|
||||
<h2>${o(r.historyHeading)} (${o(t.number(d.length))})</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>${o(r.colDate)}</th><th>${o(r.colObject)}</th><th>${o(r.colTask)}</th>
|
||||
<th class="num">${o(r.colCost)}</th><th class="num">${o(r.colDuration)}</th><th>${o(r.colNotes)}</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
${j||`<tr><td colspan="6">${o(r.none)}</td></tr>`}
|
||||
</tbody>
|
||||
<tfoot><tr>
|
||||
<td colspan="3">${o(r.totalCost)}</td>
|
||||
<td class="num">${o(t.cost(n.totalCost))}</td>
|
||||
<td class="num">${n.totalDuration?o(t.duration(n.totalDuration)):""}</td><td></td>
|
||||
</tr></tfoot>
|
||||
</table>
|
||||
${_.capped?`<p class="cap-note">${o(r.capNote)}</p>`:""}
|
||||
</body>
|
||||
</html>`}var J=25,Z=5,V={completed:C.ok,missed:C.overdue,reset:C.paused,skipped:"var(--secondary-text-color)"},u=class extends B{constructor(){super(...arguments);this.areaKey="";this.objects=[];this.showArchived=!1;this.currencySymbol="";this.userName=()=>null;this._from="";this._to="";this._entryFilter="";this._taskQuery="";this._expanded=!1;this._printing=!1;this._histories=new W(this);this._localeReady=!1;this._filtersFor=null;this._memo=null}createRenderRoot(){return this}get _lang(){return L(this.hass)}willUpdate(t){if(super.willUpdate(t),t.has("areaKey")&&this._filtersFor!==this.areaKey){this._filtersFor=this.areaKey;let s=R();this._from=s.from,this._to=s.to,this._entryFilter="",this._taskQuery="",this._expanded=!1}if(t.has("objects")||t.has("areaKey")||t.has("showArchived")||t.has("hass")&&t.get("hass")===void 0){let s=this._areaObjects().flatMap(d=>d.tasks.map(l=>({entryId:d.entry_id,task:l})));this._histories.sync(this.hass,s)}}updated(t){super.updated(t),!this._localeReady&&this.hass&&(this._localeReady=!0,H(this._lang).then(()=>this.requestUpdate()))}_areaObjects(){return z(this.objects,this.areaKey,this.showArchived).sort((t,s)=>t.object.name.localeCompare(s.object.name))}_entries(t){let s=[this.objects,this.areaKey,this.showArchived,this._histories.version];if(this._memo&&this._memo.deps.every((l,n)=>l===s[n]))return this._memo.entries;let d=M(t,(l,n)=>this._histories.historyOf(l,n));return this._memo={deps:s,entries:d},d}_figures(){let t=this._areaObjects(),s=this._entries(t),d={from:this._from||null,to:this._to||null},l=Y(s,{...d,entryId:this._entryFilter||null,taskQuery:this._taskQuery}),n=this._entryFilter?t.filter(h=>h.entry_id===this._entryFilter):t;return{objects:t,entries:s,filtered:l,totals:Q(l),buckets:q(l,d),byObject:U(l,n)}}_setRange(t){this._from=t.from,this._to=t.to,this._expanded=!1}_openObject(t){this.dispatchEvent(new CustomEvent("open-object",{detail:{entryId:t},bubbles:!0,composed:!0}))}_openTask(t,s){this.dispatchEvent(new CustomEvent("open-task",{detail:{entryId:t,taskId:s},bubbles:!0,composed:!0}))}_bucketLabel(t,s){return t.month==null?String(t.year):`${N(t.month,this._lang,s)} ${t.year}`}_countText(t,s,d){let l=this._lang;return d===1?a(s,l):a(t,l).replace("{n}",$(d,l))}async _print(){if(this._printing)return;let t=I();this._printing=!0;try{await this._histories.settled()}finally{this._printing=!1}let s=this._lang,d=this.currencySymbol,{filtered:l,totals:n,buckets:h,byObject:c}=this._figures(),m={title:a("area_report_title",s),generated:a("report_generated",s),period:a("area_report_period",s),completions:a("area_kpi_completions",s),totalCost:a("total_cost",s),avgCost:a("avg_cost",s),totalTime:a("area_kpi_total_time",s),costPerObject:a("area_cost_per_object",s),costPerBucket:h.unit==="month"?a("area_cost_per_month",s):a("area_cost_per_year",s),share:a("area_cost_share",s),historyHeading:a("area_history_section",s),colDate:a("date",s),colObject:a("object",s),colTask:a("task_name",s),colCost:a("cost",s),colDuration:a("duration",s),colNotes:a("notes_label",s),completedBy:a("completed_by",s),capNote:a("object_history_cap_note",s),none:"\u2014"},f=l.filter(i=>i.type==="completed"),j=l.map(i=>({...i,completedBy:i.completedBy?this.userName(i.completedBy):null,notes:i.notes?A(i.notes,s):null})),e=X({areaName:T(this.areaKey,this.hass?.areas,a("no_area",s)),from:this._from||f[f.length-1]?.timestamp||null,to:this._to||P(new Date),totals:n,byObject:c,buckets:h,entries:j,capped:this._histories.capped},m,{date:i=>O(i,s),cost:i=>g(i,d,s),duration:i=>w(i,s),bucket:i=>this._bucketLabel(i,"long"),share:i=>`${$(i*100,s,0)} %`,number:i=>$(i,s)},new Date().toISOString());F(e,t)}_renderRangeChips(t){let s=this._lang,d=new Date,l=R(d),n=c=>this._from===c.from&&this._to===c.to,h=G(t,d.getFullYear()).slice(0,Z);return p`
|
||||
<div class="filter-chips area-range-chips">
|
||||
<button class="filter-chip ${n(l)?"active":""}" @click=${()=>this._setRange(R())}>
|
||||
${a("area_range_12m",s)}
|
||||
</button>
|
||||
${h.map(c=>p`
|
||||
<button class="filter-chip ${n(E(c))?"active":""}" @click=${()=>this._setRange(E(c))}>${c}</button>
|
||||
`)}
|
||||
<button class="filter-chip ${!this._from&&!this._to?"active":""}" @click=${()=>this._setRange({from:"",to:""})}>
|
||||
${a("area_range_all",s)}
|
||||
</button>
|
||||
</div>
|
||||
`}_renderChart(t){let s=this._lang,d=this.currencySymbol,l=t.reduce((c,m)=>Math.max(c,m.cost),0);if(l<=0)return p`<p class="area-empty">${a("area_no_costs",s)}</p>`;let n=Math.max(1,Math.ceil(t.length/12)),h=t.map((c,m)=>({b:c,i:m})).filter(({i:c})=>c%n===0);return p`
|
||||
<div class="area-chart">
|
||||
<div class="area-chart-max">${g(l,d,s)}</div>
|
||||
<div class="area-chart-bars">
|
||||
${t.map(c=>{let m=`${this._bucketLabel(c,"long")}: ${g(c.cost,d,s)} \xB7 ${a("area_kpi_completions",s)}: ${$(c.completions,s)}`;return p`<div class="area-bar" title=${m} aria-label=${m}>
|
||||
${c.cost>0?p`<div class="area-bar-fill" style="height: ${Math.round(c.cost/l*1e3)/10}%"></div>`:y}
|
||||
</div>`})}
|
||||
</div>
|
||||
<div class="area-chart-axis ${n>1?"grouped":""}">
|
||||
${h.map(({b:c,i:m})=>p`<span class="area-bar-label" style="flex-grow: ${Math.min(n,t.length-m)}">${c.month==null?String(c.year):p`${N(c.month,s,"short")}${c.month===0||m===0?p`<br />${c.year}`:y}`}</span>`)}
|
||||
</div>
|
||||
</div>
|
||||
`}render(){if(!this.hass||!this.areaKey)return y;let t=this._lang,s=this.currencySymbol,{objects:d,entries:l,filtered:n,totals:h,buckets:c,byObject:m}=this._figures(),f=d.reduce((e,i)=>e+i.tasks.filter(x=>this.showArchived||!x.archived).length,0),j=this._expanded?n:n.slice(0,J);return p`
|
||||
<div class="detail-section area-view">
|
||||
<div class="detail-header">
|
||||
<h2 class="area-title">
|
||||
<ha-icon .icon=${K(this.areaKey,this.hass.areas)}></ha-icon>
|
||||
${T(this.areaKey,this.hass.areas,a("no_area",t))}
|
||||
</h2>
|
||||
<div class="action-buttons">
|
||||
<ha-button appearance="plain" class="area-print" .disabled=${this._printing} @click=${()=>this._print()}>
|
||||
<ha-icon icon="mdi:printer-outline"></ha-icon>
|
||||
${this._printing?a("loading",t):a("area_report_print",t)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="meta">
|
||||
${this._countText("area_object_count","area_object_count_one",d.length)} ·
|
||||
${this._countText("templates_task_count","templates_task_count_one",f)}
|
||||
${this._histories.loading?p` · <span class="area-loading">${a("loading",t)}</span>`:y}
|
||||
</p>
|
||||
|
||||
${this._renderRangeChips(l)}
|
||||
<div class="filter-bar area-filters">
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${t}
|
||||
.label=${a("date_from",t)}
|
||||
.value=${this._from}
|
||||
@value-changed=${e=>{this._from=e.detail.value}}
|
||||
></ms-date-field>
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${t}
|
||||
.label=${a("date_to",t)}
|
||||
.value=${this._to}
|
||||
@value-changed=${e=>{this._to=e.detail.value}}
|
||||
></ms-date-field>
|
||||
<label class="filter-field">
|
||||
<span class="filter-label">${a("object",t)}</span>
|
||||
<select class="area-object-filter" .value=${this._entryFilter}
|
||||
@change=${e=>{this._entryFilter=e.target.value}}>
|
||||
<option value="" ?selected=${!this._entryFilter}>${a("all_objects",t)}</option>
|
||||
${d.map(e=>p`<option value=${e.entry_id} ?selected=${e.entry_id===this._entryFilter}>${e.object.name}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
class="search-input area-task-filter"
|
||||
aria-label=${a("area_task_filter",t)}
|
||||
placeholder=${a("area_task_filter",t)}
|
||||
.value=${this._taskQuery}
|
||||
@input=${e=>{this._taskQuery=e.target.value}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="kpi-bar area-kpis">
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("area_kpi_completions",t)}</div>
|
||||
<div class="kpi-value-large area-kpi-completions">${$(h.completions,t)}</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("total_cost",t)}</div>
|
||||
<div class="kpi-value-large area-kpi-cost">${g(h.totalCost,s,t)}</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("avg_cost",t)}</div>
|
||||
<div class="kpi-value-large">${h.avgCost!=null?g(h.avgCost,s,t):"\u2014"}</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("area_kpi_total_time",t)}</div>
|
||||
<div class="kpi-value-large">${h.totalDuration?w(h.totalDuration,t):"\u2014"}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>${c.unit==="month"?a("area_cost_per_month",t):a("area_cost_per_year",t)}</h3>
|
||||
${this._renderChart(c.buckets)}
|
||||
|
||||
<h3>${a("area_cost_per_object",t)}</h3>
|
||||
<div class="objects-table-wrap">
|
||||
<table class="objects-table area-object-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>${a("object",t)}</th>
|
||||
<th class="num">${a("area_kpi_completions",t)}</th>
|
||||
<th class="num">${a("cost",t)}</th>
|
||||
<th class="num">${a("area_cost_share",t)}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${m.map(e=>p`
|
||||
<tr class="objects-table-row" @click=${()=>this._openObject(e.entryId)}>
|
||||
<td><span class="objects-table-name">${e.objectName}</span></td>
|
||||
<td class="num">${$(e.completions,t)}</td>
|
||||
<td class="num">${g(e.cost,s,t)}</td>
|
||||
<td class="num">
|
||||
<span class="area-share"><span class="area-share-fill" style="width: ${Math.round(e.share*100)}%"></span></span>
|
||||
${$(e.share*100,t,0)} %
|
||||
</td>
|
||||
</tr>
|
||||
`)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
${a("area_history_section",t)}
|
||||
<span class="area-count">${$(n.length,t)}</span>
|
||||
</h3>
|
||||
${n.length===0?p`<p class="area-empty">${a("object_history_empty",t)}</p>`:p`
|
||||
<div class="objects-table-wrap">
|
||||
<table class="objects-table area-history-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>${a("date",t)}</th>
|
||||
<th></th>
|
||||
<th>${a("object",t)}</th>
|
||||
<th>${a("task_name",t)}</th>
|
||||
<th class="num">${a("cost",t)}</th>
|
||||
<th class="num">${a("duration",t)}</th>
|
||||
<th>${a("notes_label",t)}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${j.map(e=>{let i=e.notes?A(e.notes,t):"";return p`
|
||||
<tr class="objects-table-row area-history-row" @click=${()=>this._openTask(e.entryId,e.taskId)}>
|
||||
<td title=${S(e.timestamp,t)}>${O(e.timestamp,t)}</td>
|
||||
<td>
|
||||
<span class="area-entry-type" style="color: ${V[e.type]??"inherit"}">
|
||||
<ha-icon .icon=${D[e.type]||"mdi:circle"}></ha-icon>${a(e.type,t)}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="area-object-link" @click=${x=>{x.stopPropagation(),this._openObject(e.entryId)}}>
|
||||
${e.objectName}
|
||||
</button>
|
||||
</td>
|
||||
<td><button class="area-task-link">${e.taskName}${e.phaseName?` \xB7 ${e.phaseName}`:""}</button></td>
|
||||
<td class="num">${e.cost!=null?g(e.cost,s,t):"\u2014"}</td>
|
||||
<td class="num">${e.duration!=null?w(e.duration,t):"\u2014"}</td>
|
||||
<td class="oc-notes" title=${i}>${i||"\u2014"}</td>
|
||||
</tr>
|
||||
`})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
${n.length>j.length?p`<ha-button appearance="plain" class="area-more" @click=${()=>{this._expanded=!0}}>
|
||||
${a("show_all",t)} (${$(n.length,t)})
|
||||
</ha-button>`:y}
|
||||
`}
|
||||
${this._histories.capped?p`<p class="area-cap-note">${a("object_history_cap_note",t)}</p>`:y}
|
||||
</div>
|
||||
`}};b([v({attribute:!1})],u.prototype,"hass",2),b([v()],u.prototype,"areaKey",2),b([v({attribute:!1})],u.prototype,"objects",2),b([v({type:Boolean})],u.prototype,"showArchived",2),b([v()],u.prototype,"currencySymbol",2),b([v({attribute:!1})],u.prototype,"userName",2),b([k()],u.prototype,"_from",2),b([k()],u.prototype,"_to",2),b([k()],u.prototype,"_entryFilter",2),b([k()],u.prototype,"_taskQuery",2),b([k()],u.prototype,"_expanded",2),b([k()],u.prototype,"_printing",2);customElements.get("maintenance-area-view")||customElements.define("maintenance-area-view",u);export{u as MaintenanceAreaView};
|
||||
+69
-67
@@ -1,28 +1,28 @@
|
||||
/*! maintenance_supporter frontend 2.96.0 */
|
||||
import{a as W}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-I7NCFUZX.js";import{a as o}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-VZYRPSO6.js";import{f as T,g as K,h as z,i as M,j as Y,k as Q,l as U,m as q,n as R,o as E,p as G}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-PWKMLGJR.js";import{a as A}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-RJY4VEHX.js";import{e as F,f as I}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3VJOJBZ5.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-4OFQFCKD.js";import{f as P}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-BYGTDXRD.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-QLW72JHE.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-2U4DNN5T.js";import{B as $,C as g,F as O,G as S,J as w,L as N,a as b,c as p,f as y,h as B,l as v,m as k,n as C,q as D,s as a,u as H,y as L}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-N6F5RX6S.js";function X(_,r,t,s){let d=_.entries.filter(e=>e.type==="completed").sort((e,i)=>e.ts-i.ts),l=`${_.from?t.date(_.from):"\u2026"} \u2013 ${_.to?t.date(_.to):"\u2026"}`,n=_.totals,h=[[r.completions,t.number(n.completions)],[r.totalCost,t.cost(n.totalCost)],[r.avgCost,n.avgCost!=null?t.cost(n.avgCost):r.none],[r.totalTime,n.totalDuration?t.duration(n.totalDuration):r.none]].map(([e,i])=>`<div class="kpi"><div class="k">${o(e)}</div><div class="v">${o(i)}</div></div>`).join(""),c=_.byObject.map(e=>`<tr>
|
||||
/*! maintenance_supporter frontend 2.97.1 */
|
||||
import{a as W}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-AKHM2FQS.js";import{a as o}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-R3GKJJ55.js";import{f as T,g as K,h as M,i as z,j as Y,k as Q,l as U,m as q,n as R,o as E,p as G}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-BEGYKXHI.js";import{a as A}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5ROMA2NQ.js";import{e as F,f as I}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z5US4BJZ.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-O7BSORZB.js";import{f as P}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5SGCD2J3.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-HVQDZ5QR.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-KV46GCLQ.js";import{B as g,C as y,F as O,G as S,J as C,L as N,a as b,c as p,f as v,h as B,l as k,m as j,n as w,q as D,s as a,u as H,y as L}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YJXVPPWG.js";function X($,i,t,s){let l=$.entries.filter(e=>e.type==="completed").sort((e,n)=>e.ts-n.ts),d=`${$.from?t.date($.from):"\u2026"} \u2013 ${$.to?t.date($.to):"\u2026"}`,r=$.totals,h=[[i.completions,t.number(r.completions)],[i.totalCost,t.cost(r.totalCost)],[i.avgCost,r.avgCost!=null?t.cost(r.avgCost):i.none],[i.totalTime,r.totalDuration?t.duration(r.totalDuration):i.none]].map(([e,n])=>`<div class="kpi"><div class="k">${o(e)}</div><div class="v">${o(n)}</div></div>`).join(""),u=$.byObject.map(e=>`<tr>
|
||||
<td>${o(e.objectName)}</td>
|
||||
<td class="num">${o(t.number(e.completions))}</td>
|
||||
<td class="num">${o(t.cost(e.cost))}</td>
|
||||
<td class="num">${o(t.share(e.share))}</td>
|
||||
</tr>`).join(""),m=_.buckets.buckets.reduce((e,i)=>Math.max(e,i.cost),0),f=_.buckets.buckets.map(e=>{let i=m>0?Math.round(e.cost/m*100):0;return`<tr>
|
||||
</tr>`).join(""),f=$.buckets.buckets.reduce((e,n)=>Math.max(e,n.cost),0),c=$.buckets.buckets.map(e=>{let n=f>0?Math.round(e.cost/f*100):0;return`<tr>
|
||||
<td class="nowrap">${o(t.bucket(e))}</td>
|
||||
<td class="num">${o(t.number(e.completions))}</td>
|
||||
<td class="bar-cell">${i>0?`<span class="bar" style="width:${i}%"></span>`:""}</td>
|
||||
<td class="bar-cell">${n>0?`<span class="bar" style="width:${n}%"></span>`:""}</td>
|
||||
<td class="num">${o(t.cost(e.cost))}</td>
|
||||
</tr>`}).join(""),j=d.map(e=>{let i=[e.notes,e.completedBy?`${r.completedBy}: ${e.completedBy}`:null].filter(Boolean).join(" \xB7 "),x=e.phaseName?`${e.taskName} \xB7 ${e.phaseName}`:e.taskName;return`<tr>
|
||||
</tr>`}).join(""),m=l.map(e=>{let n=[e.notes,e.completedBy?`${i.completedBy}: ${e.completedBy}`:null].filter(Boolean).join(" \xB7 "),x=e.phaseName?`${e.taskName} \xB7 ${e.phaseName}`:e.taskName;return`<tr>
|
||||
<td class="nowrap">${o(t.date(e.timestamp))}</td>
|
||||
<td>${o(e.objectName)}</td>
|
||||
<td>${o(x)}</td>
|
||||
<td class="num">${e.cost!=null?o(t.cost(e.cost)):o(r.none)}</td>
|
||||
<td class="num">${e.duration!=null?o(t.duration(e.duration)):o(r.none)}</td>
|
||||
<td class="notes">${o(i)||o(r.none)}</td>
|
||||
<td class="num">${e.cost!=null?o(t.cost(e.cost)):o(i.none)}</td>
|
||||
<td class="num">${e.duration!=null?o(t.duration(e.duration)):o(i.none)}</td>
|
||||
<td class="notes">${o(n)||o(i.none)}</td>
|
||||
</tr>`}).join(`
|
||||
`);return`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="color-scheme" content="light">
|
||||
<title>${o(r.title)} \u2014 ${o(_.areaName)}</title>
|
||||
<title>${o(i.title)} \u2014 ${o($.areaName)}</title>
|
||||
<style>
|
||||
/* Printable sheet: it opens as a blob in whatever viewer the OS supplies
|
||||
(Companion = WebView, dark phones paint a dark default canvas), so the
|
||||
@@ -42,7 +42,7 @@ import{a as W}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-I7NCFUZX
|
||||
td { border-bottom: 1px solid #e5e5e5; padding: 6px 8px; vertical-align: top; }
|
||||
td.num, th.num { text-align: right; white-space: nowrap; }
|
||||
td.nowrap { white-space: nowrap; }
|
||||
td.notes { color: #444; }
|
||||
td.notes { color: #444; white-space: pre-wrap; }
|
||||
td.bar-cell { width: 40%; vertical-align: middle; }
|
||||
.bar { display: block; height: 10px; background: #9aa7b8; border-radius: 0 3px 3px 0;
|
||||
-webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
@@ -53,74 +53,76 @@ import{a as W}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-I7NCFUZX
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>${o(r.title)} \u2014 ${o(_.areaName)}</h1>
|
||||
<p class="sub">${o(r.period)}: ${o(l)} \xB7 ${o(r.generated)} ${o(t.date(s))}</p>
|
||||
<h1>${o(i.title)} \u2014 ${o($.areaName)}</h1>
|
||||
<p class="sub">${o(i.period)}: ${o(d)} \xB7 ${o(i.generated)} ${o(t.date(s))}</p>
|
||||
<div class="kpis">${h}</div>
|
||||
|
||||
<h2>${o(r.costPerObject)}</h2>
|
||||
<h2>${o(i.costPerObject)}</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>${o(r.colObject)}</th><th class="num">${o(r.completions)}</th>
|
||||
<th class="num">${o(r.colCost)}</th><th class="num">${o(r.share)}</th>
|
||||
<th>${o(i.colObject)}</th><th class="num">${o(i.completions)}</th>
|
||||
<th class="num">${o(i.colCost)}</th><th class="num">${o(i.share)}</th>
|
||||
</tr></thead>
|
||||
<tbody>${c||`<tr><td colspan="4">${o(r.none)}</td></tr>`}</tbody>
|
||||
<tbody>${u||`<tr><td colspan="4">${o(i.none)}</td></tr>`}</tbody>
|
||||
<tfoot><tr>
|
||||
<td>${o(r.totalCost)}</td><td class="num">${o(t.number(n.completions))}</td>
|
||||
<td class="num">${o(t.cost(n.totalCost))}</td><td></td>
|
||||
<td>${o(i.totalCost)}</td><td class="num">${o(t.number(r.completions))}</td>
|
||||
<td class="num">${o(t.cost(r.totalCost))}</td><td></td>
|
||||
</tr></tfoot>
|
||||
</table>
|
||||
|
||||
<h2>${o(r.costPerBucket)}</h2>
|
||||
<h2>${o(i.costPerBucket)}</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>${o(r.period)}</th><th class="num">${o(r.completions)}</th>
|
||||
<th></th><th class="num">${o(r.colCost)}</th>
|
||||
<th>${o(i.period)}</th><th class="num">${o(i.completions)}</th>
|
||||
<th></th><th class="num">${o(i.colCost)}</th>
|
||||
</tr></thead>
|
||||
<tbody>${f||`<tr><td colspan="4">${o(r.none)}</td></tr>`}</tbody>
|
||||
<tbody>${c||`<tr><td colspan="4">${o(i.none)}</td></tr>`}</tbody>
|
||||
</table>
|
||||
|
||||
<h2>${o(r.historyHeading)} (${o(t.number(d.length))})</h2>
|
||||
<h2>${o(i.historyHeading)} (${o(t.number(l.length))})</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>${o(r.colDate)}</th><th>${o(r.colObject)}</th><th>${o(r.colTask)}</th>
|
||||
<th class="num">${o(r.colCost)}</th><th class="num">${o(r.colDuration)}</th><th>${o(r.colNotes)}</th>
|
||||
<th>${o(i.colDate)}</th><th>${o(i.colObject)}</th><th>${o(i.colTask)}</th>
|
||||
<th class="num">${o(i.colCost)}</th><th class="num">${o(i.colDuration)}</th><th>${o(i.colNotes)}</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
${j||`<tr><td colspan="6">${o(r.none)}</td></tr>`}
|
||||
${m||`<tr><td colspan="6">${o(i.none)}</td></tr>`}
|
||||
</tbody>
|
||||
<tfoot><tr>
|
||||
<td colspan="3">${o(r.totalCost)}</td>
|
||||
<td class="num">${o(t.cost(n.totalCost))}</td>
|
||||
<td class="num">${n.totalDuration?o(t.duration(n.totalDuration)):""}</td><td></td>
|
||||
<td colspan="3">${o(i.totalCost)}</td>
|
||||
<td class="num">${o(t.cost(r.totalCost))}</td>
|
||||
<td class="num">${r.totalDuration?o(t.duration(r.totalDuration)):""}</td><td></td>
|
||||
</tr></tfoot>
|
||||
</table>
|
||||
${_.capped?`<p class="cap-note">${o(r.capNote)}</p>`:""}
|
||||
${$.capped?`<p class="cap-note">${o(i.capNote)}</p>`:""}
|
||||
</body>
|
||||
</html>`}var J=25,Z=5,V={completed:C.ok,missed:C.overdue,reset:C.paused,skipped:"var(--secondary-text-color)"},u=class extends B{constructor(){super(...arguments);this.areaKey="";this.objects=[];this.showArchived=!1;this.currencySymbol="";this.userName=()=>null;this._from="";this._to="";this._entryFilter="";this._taskQuery="";this._expanded=!1;this._printing=!1;this._histories=new W(this);this._localeReady=!1;this._filtersFor=null;this._memo=null}createRenderRoot(){return this}get _lang(){return L(this.hass)}willUpdate(t){if(super.willUpdate(t),t.has("areaKey")&&this._filtersFor!==this.areaKey){this._filtersFor=this.areaKey;let s=R();this._from=s.from,this._to=s.to,this._entryFilter="",this._taskQuery="",this._expanded=!1}if(t.has("objects")||t.has("areaKey")||t.has("showArchived")||t.has("hass")&&t.get("hass")===void 0){let s=this._areaObjects().flatMap(d=>d.tasks.map(l=>({entryId:d.entry_id,task:l})));this._histories.sync(this.hass,s)}}updated(t){super.updated(t),!this._localeReady&&this.hass&&(this._localeReady=!0,H(this._lang).then(()=>this.requestUpdate()))}_areaObjects(){return z(this.objects,this.areaKey,this.showArchived).sort((t,s)=>t.object.name.localeCompare(s.object.name))}_entries(t){let s=[this.objects,this.areaKey,this.showArchived,this._histories.version];if(this._memo&&this._memo.deps.every((l,n)=>l===s[n]))return this._memo.entries;let d=M(t,(l,n)=>this._histories.historyOf(l,n));return this._memo={deps:s,entries:d},d}_figures(){let t=this._areaObjects(),s=this._entries(t),d={from:this._from||null,to:this._to||null},l=Y(s,{...d,entryId:this._entryFilter||null,taskQuery:this._taskQuery}),n=this._entryFilter?t.filter(h=>h.entry_id===this._entryFilter):t;return{objects:t,entries:s,filtered:l,totals:Q(l),buckets:q(l,d),byObject:U(l,n)}}_setRange(t){this._from=t.from,this._to=t.to,this._expanded=!1}_openObject(t){this.dispatchEvent(new CustomEvent("open-object",{detail:{entryId:t},bubbles:!0,composed:!0}))}_openTask(t,s){this.dispatchEvent(new CustomEvent("open-task",{detail:{entryId:t,taskId:s},bubbles:!0,composed:!0}))}_bucketLabel(t,s){return t.month==null?String(t.year):`${N(t.month,this._lang,s)} ${t.year}`}_countText(t,s,d){let l=this._lang;return d===1?a(s,l):a(t,l).replace("{n}",$(d,l))}async _print(){if(this._printing)return;let t=I();this._printing=!0;try{await this._histories.settled()}finally{this._printing=!1}let s=this._lang,d=this.currencySymbol,{filtered:l,totals:n,buckets:h,byObject:c}=this._figures(),m={title:a("area_report_title",s),generated:a("report_generated",s),period:a("area_report_period",s),completions:a("area_kpi_completions",s),totalCost:a("total_cost",s),avgCost:a("avg_cost",s),totalTime:a("area_kpi_total_time",s),costPerObject:a("area_cost_per_object",s),costPerBucket:h.unit==="month"?a("area_cost_per_month",s):a("area_cost_per_year",s),share:a("area_cost_share",s),historyHeading:a("area_history_section",s),colDate:a("date",s),colObject:a("object",s),colTask:a("task_name",s),colCost:a("cost",s),colDuration:a("duration",s),colNotes:a("notes_label",s),completedBy:a("completed_by",s),capNote:a("object_history_cap_note",s),none:"\u2014"},f=l.filter(i=>i.type==="completed"),j=l.map(i=>({...i,completedBy:i.completedBy?this.userName(i.completedBy):null,notes:i.notes?A(i.notes,s):null})),e=X({areaName:T(this.areaKey,this.hass?.areas,a("no_area",s)),from:this._from||f[f.length-1]?.timestamp||null,to:this._to||P(new Date),totals:n,byObject:c,buckets:h,entries:j,capped:this._histories.capped},m,{date:i=>O(i,s),cost:i=>g(i,d,s),duration:i=>w(i,s),bucket:i=>this._bucketLabel(i,"long"),share:i=>`${$(i*100,s,0)} %`,number:i=>$(i,s)},new Date().toISOString());F(e,t)}_renderRangeChips(t){let s=this._lang,d=new Date,l=R(d),n=c=>this._from===c.from&&this._to===c.to,h=G(t,d.getFullYear()).slice(0,Z);return p`
|
||||
</html>`}var J=25,Z=5,V={completed:w.ok,missed:w.overdue,reset:w.paused,skipped:"var(--secondary-text-color)"},_=class extends B{constructor(){super(...arguments);this.areaKey="";this.objects=[];this.showArchived=!1;this.currencySymbol="";this.userName=()=>null;this._from="";this._to="";this._entryFilter="";this._taskQuery="";this._expanded=!1;this._printing=!1;this._histories=new W(this);this._localeReady=!1;this._filtersFor=null;this._memo=null}createRenderRoot(){return this}get _lang(){return L(this.hass)}willUpdate(t){if(super.willUpdate(t),t.has("areaKey")&&this._filtersFor!==this.areaKey){this._filtersFor=this.areaKey;let s=R();this._from=s.from,this._to=s.to,this._entryFilter="",this._taskQuery="",this._expanded=!1}if(t.has("objects")||t.has("areaKey")||t.has("showArchived")||t.has("hass")&&t.get("hass")===void 0){let s=this._areaObjects().flatMap(l=>l.tasks.map(d=>({entryId:l.entry_id,task:d})));this._histories.sync(this.hass,s)}}updated(t){super.updated(t),!this._localeReady&&this.hass&&(this._localeReady=!0,H(this._lang).then(()=>this.requestUpdate()))}_areaObjects(){return M(this.objects,this.areaKey,this.showArchived).sort((t,s)=>t.object.name.localeCompare(s.object.name))}_entries(t){let s=[this.objects,this.areaKey,this.showArchived,this._histories.version];if(this._memo&&this._memo.deps.every((d,r)=>d===s[r]))return this._memo.entries;let l=z(t,(d,r)=>this._histories.historyOf(d,r));return this._memo={deps:s,entries:l},l}_figures(){let t=this._areaObjects(),s=this._entries(t),l={from:this._from||null,to:this._to||null},d=Y(s,{...l,entryId:this._entryFilter||null,taskQuery:this._taskQuery}),r=this._entryFilter?t.filter(h=>h.entry_id===this._entryFilter):t;return{objects:t,entries:s,filtered:d,totals:Q(d),buckets:q(d,l),byObject:U(d,r)}}_setRange(t){this._from=t.from,this._to=t.to,this._expanded=!1}_openObject(t){this.dispatchEvent(new CustomEvent("open-object",{detail:{entryId:t},bubbles:!0,composed:!0}))}_openTask(t,s){this.dispatchEvent(new CustomEvent("open-task",{detail:{entryId:t,taskId:s},bubbles:!0,composed:!0}))}_bucketLabel(t,s){return t.month==null?String(t.year):`${N(t.month,this._lang,s)} ${t.year}`}_countText(t,s,l){let d=this._lang;return l===1?a(s,d):a(t,d).replace("{n}",g(l,d))}async _print(){if(this._printing)return;let t=I();this._printing=!0;try{await this._histories.settled()}finally{this._printing=!1}let s=this._lang,l=this.currencySymbol,{filtered:d,totals:r,buckets:h,byObject:u}=this._figures(),f={title:a("area_report_title",s),generated:a("report_generated",s),period:a("area_report_period",s),completions:a("area_kpi_completions",s),totalCost:a("total_cost",s),avgCost:a("avg_cost",s),totalTime:a("area_kpi_total_time",s),costPerObject:a("area_cost_per_object",s),costPerBucket:h.unit==="month"?a("area_cost_per_month",s):a("area_cost_per_year",s),share:a("area_cost_share",s),historyHeading:a("area_history_section",s),colDate:a("date",s),colObject:a("object",s),colTask:a("task_name",s),colCost:a("cost",s),colDuration:a("duration",s),colNotes:a("notes_label",s),completedBy:a("completed_by",s),capNote:a("object_history_cap_note",s),none:"\u2014"},c=d.filter(n=>n.type==="completed"),m=d.map(n=>({...n,completedBy:n.completedBy?this.userName(n.completedBy):null,notes:n.notes?A(n.notes,s):null})),e=X({areaName:T(this.areaKey,this.hass?.areas,a("no_area",s)),from:this._from||c[c.length-1]?.timestamp||null,to:this._to||P(new Date),totals:r,byObject:u,buckets:h,entries:m,capped:this._histories.capped},f,{date:n=>O(n,s),cost:n=>y(n,l,s),duration:n=>C(n,s),bucket:n=>this._bucketLabel(n,"long"),share:n=>`${g(n*100,s,0)} %`,number:n=>g(n,s)},new Date().toISOString());F(e,t)}_renderRangeChips(t){let s=this._lang,l=new Date,d=R(l),r=u=>this._from===u.from&&this._to===u.to,h=G(t,l.getFullYear()).slice(0,Z);return p`
|
||||
<div class="filter-chips area-range-chips">
|
||||
<button class="filter-chip ${n(l)?"active":""}" @click=${()=>this._setRange(R())}>
|
||||
<button class="filter-chip ${r(d)?"active":""}" @click=${()=>this._setRange(R())}>
|
||||
${a("area_range_12m",s)}
|
||||
</button>
|
||||
${h.map(c=>p`
|
||||
<button class="filter-chip ${n(E(c))?"active":""}" @click=${()=>this._setRange(E(c))}>${c}</button>
|
||||
${h.map(u=>p`
|
||||
<button class="filter-chip ${r(E(u))?"active":""}" @click=${()=>this._setRange(E(u))}>${u}</button>
|
||||
`)}
|
||||
<button class="filter-chip ${!this._from&&!this._to?"active":""}" @click=${()=>this._setRange({from:"",to:""})}>
|
||||
${a("area_range_all",s)}
|
||||
</button>
|
||||
</div>
|
||||
`}_renderChart(t){let s=this._lang,d=this.currencySymbol,l=t.reduce((c,m)=>Math.max(c,m.cost),0);if(l<=0)return p`<p class="area-empty">${a("area_no_costs",s)}</p>`;let n=Math.max(1,Math.ceil(t.length/12)),h=t.map((c,m)=>({b:c,i:m})).filter(({i:c})=>c%n===0);return p`
|
||||
`}_renderChart(t){let s=this._lang,l=this.currencySymbol,d=t.reduce((c,m)=>Math.max(c,m.cost),0),r=t.reduce((c,m)=>Math.min(c,m.cost),0);if(d<=0&&r>=0)return p`<p class="area-empty">${a("area_no_costs",s)}</p>`;let h=c=>Math.round(c/(d-r)*1e3)/10,u=Math.max(1,Math.ceil(t.length/12)),f=t.map((c,m)=>({b:c,i:m})).filter(({i:c})=>c%u===0);return p`
|
||||
<div class="area-chart">
|
||||
<div class="area-chart-max">${g(l,d,s)}</div>
|
||||
<div class="area-chart-max">${y(d,l,s)}</div>
|
||||
<div class="area-chart-bars">
|
||||
${t.map(c=>{let m=`${this._bucketLabel(c,"long")}: ${g(c.cost,d,s)} \xB7 ${a("area_kpi_completions",s)}: ${$(c.completions,s)}`;return p`<div class="area-bar" title=${m} aria-label=${m}>
|
||||
${c.cost>0?p`<div class="area-bar-fill" style="height: ${Math.round(c.cost/l*1e3)/10}%"></div>`:y}
|
||||
${r<0?p`<div class="area-chart-zero" style="bottom: ${h(-r)}%"></div>`:v}
|
||||
${t.map(c=>{let m=`${this._bucketLabel(c,"long")}: ${y(c.cost,l,s)} \xB7 ${a("area_kpi_completions",s)}: ${g(c.completions,s)}`,e=c.cost<0?h(c.cost-r):h(-r);return p`<div class="area-bar" title=${m} aria-label=${m}>
|
||||
${c.cost!==0?p`<div class="area-bar-fill ${c.cost<0?"credit":""}" style="height: ${h(Math.abs(c.cost))}%${e?`; bottom: ${e}%`:""}"></div>`:v}
|
||||
</div>`})}
|
||||
</div>
|
||||
<div class="area-chart-axis ${n>1?"grouped":""}">
|
||||
${h.map(({b:c,i:m})=>p`<span class="area-bar-label" style="flex-grow: ${Math.min(n,t.length-m)}">${c.month==null?String(c.year):p`${N(c.month,s,"short")}${c.month===0||m===0?p`<br />${c.year}`:y}`}</span>`)}
|
||||
${r<0?p`<div class="area-chart-min">${y(r,l,s)}</div>`:v}
|
||||
<div class="area-chart-axis ${u>1?"grouped":""}">
|
||||
${f.map(({b:c,i:m})=>p`<span class="area-bar-label" style="flex-grow: ${Math.min(u,t.length-m)}">${c.month==null?String(c.year):p`${N(c.month,s,"short")}${c.month===0||m===0?p`<br />${c.year}`:v}`}</span>`)}
|
||||
</div>
|
||||
</div>
|
||||
`}render(){if(!this.hass||!this.areaKey)return y;let t=this._lang,s=this.currencySymbol,{objects:d,entries:l,filtered:n,totals:h,buckets:c,byObject:m}=this._figures(),f=d.reduce((e,i)=>e+i.tasks.filter(x=>this.showArchived||!x.archived).length,0),j=this._expanded?n:n.slice(0,J);return p`
|
||||
`}render(){if(!this.hass||!this.areaKey)return v;let t=this._lang,s=this.currencySymbol,{objects:l,entries:d,filtered:r,totals:h,buckets:u,byObject:f}=this._figures(),c=l.reduce((e,n)=>e+n.tasks.filter(x=>this.showArchived||!x.archived).length,0),m=this._expanded?r:r.slice(0,J);return p`
|
||||
<div class="detail-section area-view">
|
||||
<div class="detail-header">
|
||||
<h2 class="area-title">
|
||||
@@ -135,12 +137,12 @@ ${_.capped?`<p class="cap-note">${o(r.capNote)}</p>`:""}
|
||||
</div>
|
||||
</div>
|
||||
<p class="meta">
|
||||
${this._countText("area_object_count","area_object_count_one",d.length)} ·
|
||||
${this._countText("templates_task_count","templates_task_count_one",f)}
|
||||
${this._histories.loading?p` · <span class="area-loading">${a("loading",t)}</span>`:y}
|
||||
${this._countText("area_object_count","area_object_count_one",l.length)} ·
|
||||
${this._countText("templates_task_count","templates_task_count_one",c)}
|
||||
${this._histories.loading?p` · <span class="area-loading">${a("loading",t)}</span>`:v}
|
||||
</p>
|
||||
|
||||
${this._renderRangeChips(l)}
|
||||
${this._renderRangeChips(d)}
|
||||
<div class="filter-bar area-filters">
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
@@ -165,7 +167,7 @@ ${_.capped?`<p class="cap-note">${o(r.capNote)}</p>`:""}
|
||||
<select class="area-object-filter" .value=${this._entryFilter}
|
||||
@change=${e=>{this._entryFilter=e.target.value}}>
|
||||
<option value="" ?selected=${!this._entryFilter}>${a("all_objects",t)}</option>
|
||||
${d.map(e=>p`<option value=${e.entry_id} ?selected=${e.entry_id===this._entryFilter}>${e.object.name}</option>`)}
|
||||
${l.map(e=>p`<option value=${e.entry_id} ?selected=${e.entry_id===this._entryFilter}>${e.object.name}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
<input
|
||||
@@ -181,24 +183,24 @@ ${_.capped?`<p class="cap-note">${o(r.capNote)}</p>`:""}
|
||||
<div class="kpi-bar area-kpis">
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("area_kpi_completions",t)}</div>
|
||||
<div class="kpi-value-large area-kpi-completions">${$(h.completions,t)}</div>
|
||||
<div class="kpi-value-large area-kpi-completions">${g(h.completions,t)}</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("total_cost",t)}</div>
|
||||
<div class="kpi-value-large area-kpi-cost">${g(h.totalCost,s,t)}</div>
|
||||
<div class="kpi-value-large area-kpi-cost">${y(h.totalCost,s,t)}</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("avg_cost",t)}</div>
|
||||
<div class="kpi-value-large">${h.avgCost!=null?g(h.avgCost,s,t):"\u2014"}</div>
|
||||
<div class="kpi-value-large">${h.avgCost!=null?y(h.avgCost,s,t):"\u2014"}</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("area_kpi_total_time",t)}</div>
|
||||
<div class="kpi-value-large">${h.totalDuration?w(h.totalDuration,t):"\u2014"}</div>
|
||||
<div class="kpi-value-large">${h.totalDuration?C(h.totalDuration,t):"\u2014"}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>${c.unit==="month"?a("area_cost_per_month",t):a("area_cost_per_year",t)}</h3>
|
||||
${this._renderChart(c.buckets)}
|
||||
<h3>${u.unit==="month"?a("area_cost_per_month",t):a("area_cost_per_year",t)}</h3>
|
||||
${this._renderChart(u.buckets)}
|
||||
|
||||
<h3>${a("area_cost_per_object",t)}</h3>
|
||||
<div class="objects-table-wrap">
|
||||
@@ -212,14 +214,14 @@ ${_.capped?`<p class="cap-note">${o(r.capNote)}</p>`:""}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${m.map(e=>p`
|
||||
${f.map(e=>p`
|
||||
<tr class="objects-table-row" @click=${()=>this._openObject(e.entryId)}>
|
||||
<td><span class="objects-table-name">${e.objectName}</span></td>
|
||||
<td class="num">${$(e.completions,t)}</td>
|
||||
<td class="num">${g(e.cost,s,t)}</td>
|
||||
<td class="num">${g(e.completions,t)}</td>
|
||||
<td class="num">${y(e.cost,s,t)}</td>
|
||||
<td class="num">
|
||||
<span class="area-share"><span class="area-share-fill" style="width: ${Math.round(e.share*100)}%"></span></span>
|
||||
${$(e.share*100,t,0)} %
|
||||
${g(e.share*100,t,0)} %
|
||||
</td>
|
||||
</tr>
|
||||
`)}
|
||||
@@ -229,9 +231,9 @@ ${_.capped?`<p class="cap-note">${o(r.capNote)}</p>`:""}
|
||||
|
||||
<h3>
|
||||
${a("area_history_section",t)}
|
||||
<span class="area-count">${$(n.length,t)}</span>
|
||||
<span class="area-count">${g(r.length,t)}</span>
|
||||
</h3>
|
||||
${n.length===0?p`<p class="area-empty">${a("object_history_empty",t)}</p>`:p`
|
||||
${r.length===0?p`<p class="area-empty">${a("object_history_empty",t)}</p>`:p`
|
||||
<div class="objects-table-wrap">
|
||||
<table class="objects-table area-history-table">
|
||||
<thead>
|
||||
@@ -246,7 +248,7 @@ ${_.capped?`<p class="cap-note">${o(r.capNote)}</p>`:""}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${j.map(e=>{let i=e.notes?A(e.notes,t):"";return p`
|
||||
${m.map(e=>{let n=e.notes?A(e.notes,t):"";return p`
|
||||
<tr class="objects-table-row area-history-row" @click=${()=>this._openTask(e.entryId,e.taskId)}>
|
||||
<td title=${S(e.timestamp,t)}>${O(e.timestamp,t)}</td>
|
||||
<td>
|
||||
@@ -260,18 +262,18 @@ ${_.capped?`<p class="cap-note">${o(r.capNote)}</p>`:""}
|
||||
</button>
|
||||
</td>
|
||||
<td><button class="area-task-link">${e.taskName}${e.phaseName?` \xB7 ${e.phaseName}`:""}</button></td>
|
||||
<td class="num">${e.cost!=null?g(e.cost,s,t):"\u2014"}</td>
|
||||
<td class="num">${e.duration!=null?w(e.duration,t):"\u2014"}</td>
|
||||
<td class="oc-notes" title=${i}>${i||"\u2014"}</td>
|
||||
<td class="num">${e.cost!=null?y(e.cost,s,t):"\u2014"}</td>
|
||||
<td class="num">${e.duration!=null?C(e.duration,t):"\u2014"}</td>
|
||||
<td class="oc-notes" title=${n}>${n||"\u2014"}</td>
|
||||
</tr>
|
||||
`})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
${n.length>j.length?p`<ha-button appearance="plain" class="area-more" @click=${()=>{this._expanded=!0}}>
|
||||
${a("show_all",t)} (${$(n.length,t)})
|
||||
</ha-button>`:y}
|
||||
${r.length>m.length?p`<ha-button appearance="plain" class="area-more" @click=${()=>{this._expanded=!0}}>
|
||||
${a("show_all",t)} (${g(r.length,t)})
|
||||
</ha-button>`:v}
|
||||
`}
|
||||
${this._histories.capped?p`<p class="area-cap-note">${a("object_history_cap_note",t)}</p>`:y}
|
||||
${this._histories.capped?p`<p class="area-cap-note">${a("object_history_cap_note",t)}</p>`:v}
|
||||
</div>
|
||||
`}};b([v({attribute:!1})],u.prototype,"hass",2),b([v()],u.prototype,"areaKey",2),b([v({attribute:!1})],u.prototype,"objects",2),b([v({type:Boolean})],u.prototype,"showArchived",2),b([v()],u.prototype,"currencySymbol",2),b([v({attribute:!1})],u.prototype,"userName",2),b([k()],u.prototype,"_from",2),b([k()],u.prototype,"_to",2),b([k()],u.prototype,"_entryFilter",2),b([k()],u.prototype,"_taskQuery",2),b([k()],u.prototype,"_expanded",2),b([k()],u.prototype,"_printing",2);customElements.get("maintenance-area-view")||customElements.define("maintenance-area-view",u);export{u as MaintenanceAreaView};
|
||||
`}};b([k({attribute:!1})],_.prototype,"hass",2),b([k()],_.prototype,"areaKey",2),b([k({attribute:!1})],_.prototype,"objects",2),b([k({type:Boolean})],_.prototype,"showArchived",2),b([k()],_.prototype,"currencySymbol",2),b([k({attribute:!1})],_.prototype,"userName",2),b([j()],_.prototype,"_from",2),b([j()],_.prototype,"_to",2),b([j()],_.prototype,"_entryFilter",2),b([j()],_.prototype,"_taskQuery",2),b([j()],_.prototype,"_expanded",2),b([j()],_.prototype,"_printing",2);customElements.get("maintenance-area-view")||customElements.define("maintenance-area-view",_);export{_ as MaintenanceAreaView};
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*! maintenance_supporter frontend 2.95.0 */
|
||||
import{a as C}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-APJICCRG.js";import{q as A,r as j,s as E,t as L,u as R}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YKVD2DJ5.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-ODPGZXCU.js";import{a as _,b as w,c as S}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YQT7IJJB.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-JEWIFUZC.js";import{B as g,C as v,F as k,a as l,c as a,f as m,h as y,l as p,m as b,s as r,u as $,y as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-4XNVTWGC.js";var O=[{key:"name",label:"area",num:!1},{key:"objects",label:"objects",num:!0},{key:"tasks",label:"tasks",num:!0},{key:"overdue",label:"overdue",num:!0},{key:"due_soon",label:"due_soon",num:!0},{key:"cost_year",label:"area_cost_year",num:!0},{key:"cost_total",label:"total_cost",num:!0},{key:"last",label:"last_performed",num:!1}],i=class extends y{constructor(){super(...arguments);this.objects=[];this.showArchived=!1;this.currencySymbol="";this._query="";this._sort=j(w(_.areaSort));this._histories=new C(this);this._localeReady=!1;this._memo=null}createRenderRoot(){return this}get _lang(){return f(this.hass)}willUpdate(e){if(super.willUpdate(e),e.has("objects")||e.has("showArchived")||e.has("hass")&&e.get("hass")===void 0){let o=this.objects.filter(s=>this.showArchived||!s.object.archived).flatMap(s=>s.tasks.map(n=>({entryId:s.entry_id,task:n})));this._histories.sync(this.hass,o)}}updated(e){super.updated(e),!this._localeReady&&this.hass&&(this._localeReady=!0,$(this._lang).then(()=>this.requestUpdate()))}_rows(e){let o=this._lang,s=[this.objects,this.showArchived,this._histories.version,this.hass?.areas,o,e];if(this._memo&&this._memo.deps.every((c,h)=>c===s[h]))return this._memo.rows;let n=A(this.objects,{showArchived:this.showArchived,areas:this.hass?.areas,noAreaLabel:r("no_area",o),year:e,historyOf:(c,h)=>this._histories.historyOf(c,h)});return this._memo={deps:s,rows:n},n}_setSort(e){this._sort=E(this._sort,e),S(_.areaSort,`${this._sort.key}:${this._sort.dir}`)}_open(e){this.dispatchEvent(new CustomEvent("open-area",{detail:{areaKey:e},bubbles:!0,composed:!0}))}_toggleArchived(){this.dispatchEvent(new CustomEvent("archived-toggle",{bubbles:!0,composed:!0}))}_headerLabel(e,o,s){return e==="area_cost_year"?r("area_cost_year",s).replace("{year}",String(o)):r(e,s)}render(){if(!this.hass)return m;let e=this._lang,o=new Date().getFullYear(),s=this.currencySymbol,n=this._rows(o),c=L(R(n,this._query),this._sort),h=this.objects.filter(t=>t.object.archived).length,d=t=>g(t,e);return a`
|
||||
/*! maintenance_supporter frontend 2.97.1 */
|
||||
import{a as C}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-AKHM2FQS.js";import{q as A,r as j,s as E,t as L,u as R}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-BEGYKXHI.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-O7BSORZB.js";import{a as _,b as w,c as S}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-EPK25IMX.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-5SGCD2J3.js";import{B as g,C as v,F as k,a as l,c as a,f as m,h as y,l as p,m as b,s as r,u as $,y as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YJXVPPWG.js";var O=[{key:"name",label:"area",num:!1},{key:"objects",label:"objects",num:!0},{key:"tasks",label:"tasks",num:!0},{key:"overdue",label:"overdue",num:!0},{key:"due_soon",label:"due_soon",num:!0},{key:"cost_year",label:"area_cost_year",num:!0},{key:"cost_total",label:"total_cost",num:!0},{key:"last",label:"last_performed",num:!1}],i=class extends y{constructor(){super(...arguments);this.objects=[];this.showArchived=!1;this.currencySymbol="";this._query="";this._sort=j(w(_.areaSort));this._histories=new C(this);this._localeReady=!1;this._memo=null}createRenderRoot(){return this}get _lang(){return f(this.hass)}willUpdate(e){if(super.willUpdate(e),e.has("objects")||e.has("showArchived")||e.has("hass")&&e.get("hass")===void 0){let o=this.objects.filter(s=>this.showArchived||!s.object.archived).flatMap(s=>s.tasks.map(n=>({entryId:s.entry_id,task:n})));this._histories.sync(this.hass,o)}}updated(e){super.updated(e),!this._localeReady&&this.hass&&(this._localeReady=!0,$(this._lang).then(()=>this.requestUpdate()))}_rows(e){let o=this._lang,s=[this.objects,this.showArchived,this._histories.version,this.hass?.areas,o,e];if(this._memo&&this._memo.deps.every((c,h)=>c===s[h]))return this._memo.rows;let n=A(this.objects,{showArchived:this.showArchived,areas:this.hass?.areas,noAreaLabel:r("no_area",o),year:e,historyOf:(c,h)=>this._histories.historyOf(c,h)});return this._memo={deps:s,rows:n},n}_setSort(e){this._sort=E(this._sort,e),S(_.areaSort,`${this._sort.key}:${this._sort.dir}`)}_open(e){this.dispatchEvent(new CustomEvent("open-area",{detail:{areaKey:e},bubbles:!0,composed:!0}))}_toggleArchived(){this.dispatchEvent(new CustomEvent("archived-toggle",{bubbles:!0,composed:!0}))}_headerLabel(e,o,s){return e==="area_cost_year"?r("area_cost_year",s).replace("{year}",String(o)):r(e,s)}render(){if(!this.hass)return m;let e=this._lang,o=new Date().getFullYear(),s=this.currencySymbol,n=this._rows(o),c=L(R(n,this._query),this._sort),h=this.objects.filter(t=>t.object.archived).length,d=t=>g(t,e);return a`
|
||||
<div class="filter-bar area-filter-bar">
|
||||
<input
|
||||
type="search"
|
||||
@@ -1,54 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.96.0 */
|
||||
import{a as C}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-I7NCFUZX.js";import{q as A,r as j,s as E,t as L,u as R}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-PWKMLGJR.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-4OFQFCKD.js";import{a as _,b as w,c as S}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5YKNSANW.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-BYGTDXRD.js";import{B as g,C as v,F as k,a as l,c as a,f as m,h as y,l as p,m as b,s as r,u as $,y as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-N6F5RX6S.js";var O=[{key:"name",label:"area",num:!1},{key:"objects",label:"objects",num:!0},{key:"tasks",label:"tasks",num:!0},{key:"overdue",label:"overdue",num:!0},{key:"due_soon",label:"due_soon",num:!0},{key:"cost_year",label:"area_cost_year",num:!0},{key:"cost_total",label:"total_cost",num:!0},{key:"last",label:"last_performed",num:!1}],i=class extends y{constructor(){super(...arguments);this.objects=[];this.showArchived=!1;this.currencySymbol="";this._query="";this._sort=j(w(_.areaSort));this._histories=new C(this);this._localeReady=!1;this._memo=null}createRenderRoot(){return this}get _lang(){return f(this.hass)}willUpdate(e){if(super.willUpdate(e),e.has("objects")||e.has("showArchived")||e.has("hass")&&e.get("hass")===void 0){let o=this.objects.filter(s=>this.showArchived||!s.object.archived).flatMap(s=>s.tasks.map(n=>({entryId:s.entry_id,task:n})));this._histories.sync(this.hass,o)}}updated(e){super.updated(e),!this._localeReady&&this.hass&&(this._localeReady=!0,$(this._lang).then(()=>this.requestUpdate()))}_rows(e){let o=this._lang,s=[this.objects,this.showArchived,this._histories.version,this.hass?.areas,o,e];if(this._memo&&this._memo.deps.every((c,h)=>c===s[h]))return this._memo.rows;let n=A(this.objects,{showArchived:this.showArchived,areas:this.hass?.areas,noAreaLabel:r("no_area",o),year:e,historyOf:(c,h)=>this._histories.historyOf(c,h)});return this._memo={deps:s,rows:n},n}_setSort(e){this._sort=E(this._sort,e),S(_.areaSort,`${this._sort.key}:${this._sort.dir}`)}_open(e){this.dispatchEvent(new CustomEvent("open-area",{detail:{areaKey:e},bubbles:!0,composed:!0}))}_toggleArchived(){this.dispatchEvent(new CustomEvent("archived-toggle",{bubbles:!0,composed:!0}))}_headerLabel(e,o,s){return e==="area_cost_year"?r("area_cost_year",s).replace("{year}",String(o)):r(e,s)}render(){if(!this.hass)return m;let e=this._lang,o=new Date().getFullYear(),s=this.currencySymbol,n=this._rows(o),c=L(R(n,this._query),this._sort),h=this.objects.filter(t=>t.object.archived).length,d=t=>g(t,e);return a`
|
||||
<div class="filter-bar area-filter-bar">
|
||||
<input
|
||||
type="search"
|
||||
class="search-input"
|
||||
aria-label=${r("areas_filter",e)}
|
||||
placeholder=${r("areas_filter",e)}
|
||||
.value=${this._query}
|
||||
@input=${t=>{this._query=t.target.value}}
|
||||
/>
|
||||
${this._histories.loading?a`<span class="area-loading">${r("loading",e)}</span>`:m}
|
||||
${h>0?a`<ha-button class="archived-toggle ${this.showArchived?"active":""}" @click=${()=>this._toggleArchived()}>
|
||||
<ha-icon icon="mdi:archive-outline"></ha-icon>
|
||||
${this.showArchived?r("hide_archived",e):`${r("show_archived",e)} (${h})`}
|
||||
</ha-button>`:m}
|
||||
</div>
|
||||
${n.length===0?a`<div class="empty-state">${r("no_objects",e)}</div>`:c.length===0?a`<div class="empty-state">${r("areas_filter_none",e)}</div>`:a`
|
||||
<div class="objects-table-wrap">
|
||||
<table class="objects-table areas-table">
|
||||
<thead>
|
||||
<tr>
|
||||
${O.map(t=>{let u=this._sort.key===t.key;return a`<th class=${t.num?"num":""}
|
||||
aria-sort=${u?this._sort.dir==="asc"?"ascending":"descending":"none"}>
|
||||
<button class="area-sort ${u?"active":""}" title=${r("sort_label",e)} @click=${()=>this._setSort(t.key)}>
|
||||
${this._headerLabel(t.label,o,e)}
|
||||
${u?a`<ha-icon icon=${this._sort.dir==="asc"?"mdi:arrow-up":"mdi:arrow-down"}></ha-icon>`:m}
|
||||
</button>
|
||||
</th>`})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${c.map(t=>a`
|
||||
<tr class="objects-table-row" tabindex="0"
|
||||
@click=${()=>this._open(t.key)}
|
||||
@keydown=${u=>{u.key==="Enter"&&this._open(t.key)}}>
|
||||
<td>
|
||||
<ha-icon class="area-row-icon" .icon=${t.icon}></ha-icon>
|
||||
<span class="objects-table-name">${t.name}</span>
|
||||
</td>
|
||||
<td class="num">${d(t.objects)}</td>
|
||||
<td class="num">${d(t.tasks)}</td>
|
||||
<td class="num">${t.overdue?a`<span class="area-count overdue">${d(t.overdue)}</span>`:d(0)}</td>
|
||||
<td class="num">${t.dueSoon?a`<span class="area-count due-soon">${d(t.dueSoon)}</span>`:d(0)}</td>
|
||||
<td class="num">${v(t.costYear,s,e)}</td>
|
||||
<td class="num">${v(t.costTotal,s,e)}</td>
|
||||
<td>${t.lastCompletion?k(t.lastCompletion,e):"\u2014"}</td>
|
||||
</tr>
|
||||
`)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`}
|
||||
`}};l([p({attribute:!1})],i.prototype,"hass",2),l([p({attribute:!1})],i.prototype,"objects",2),l([p({type:Boolean})],i.prototype,"showArchived",2),l([p()],i.prototype,"currencySymbol",2),l([b()],i.prototype,"_query",2),l([b()],i.prototype,"_sort",2);customElements.get("maintenance-areas-view")||customElements.define("maintenance-areas-view",i);export{i as MaintenanceAreasView};
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*! maintenance_supporter frontend 2.97.1 */
|
||||
import"/maintenance_supporter_panelfiles/panel-chunks/chunk-4CRDSGBT.js";import{a as n,b as _,c as o,f as p,h as c,l as g,m as a,s,y as u}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YJXVPPWG.js";var d=["round_robin","least_completed","random"],v=["low","normal","high"],m=365,i=class extends c{constructor(){super(...arguments);this._open=!1;this._mode="assign";this._count=0;this._users=[];this._present=[];this._assignKind="one";this._person="";this._pool=new Set;this._rotation=d[0];this._addText="";this._remove=new Set;this._setWarning=!1;this._warning=7;this._setPriority=!1;this._priority="normal";this._setMute=!1;this._mute=!1;this._resolve=null}get _lang(){return u(this.hass)}open(t,e,r){return this._resolve?.(null),this._mode=t,this._count=e.length,this._users=[...r].sort((l,h)=>l.name.localeCompare(h.name)),this._present=[...new Set(e.flatMap(l=>l.labels||[]))].sort((l,h)=>l.localeCompare(h)),this._assignKind="one",this._person="",this._pool=new Set,this._rotation=d[0],this._addText="",this._remove=new Set,this._setWarning=!1,this._warning=7,this._setPriority=!1,this._priority="normal",this._setMute=!1,this._mute=!1,this._open=!0,new Promise(l=>{this._resolve=l})}_finish(t){this._open=!1;let e=this._resolve;this._resolve=null,e?.(t)}changes(){if(this._mode==="assign"){if(this._assignKind==="one")return{responsible_user_id:this._person||null,assignee_pool:null,rotation_strategy:null};let e=this._users.map(r=>r.id).filter(r=>this._pool.has(r));return e.length<2?null:{assignee_pool:e,rotation_strategy:this._rotation,responsible_user_id:null}}if(this._mode==="labels"){let e=[...new Set(this._addText.split(",").map(l=>l.trim()).filter(Boolean))],r=[...this._remove];return e.length||r.length?{labels_add:e,labels_remove:r}:null}let t={};return this._setWarning&&(t.warning_days=this._warning),this._setPriority&&(t.priority=this._priority),this._setMute&&(t.notify_enabled=!this._mute),Object.keys(t).length?t:null}_toggle(t,e){let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}_renderAssign(t){return o`
|
||||
<div class="hint">${s("bulk_assign_hint",t)}</div>
|
||||
<label class="choice">
|
||||
<input type="radio" name="kind" .checked=${this._assignKind==="one"} @change=${()=>this._assignKind="one"} />
|
||||
${s("bulk_assign_one",t)}
|
||||
</label>
|
||||
${this._assignKind==="one"?o`<select class="person" .value=${this._person} @change=${e=>this._person=e.target.value}>
|
||||
<option value="" ?selected=${!this._person}>${s("unassigned",t)}</option>
|
||||
${this._users.map(e=>o`<option value=${e.id} ?selected=${this._person===e.id}>${e.name}</option>`)}
|
||||
</select>`:p}
|
||||
<label class="choice">
|
||||
<input type="radio" name="kind" .checked=${this._assignKind==="rotate"} @change=${()=>this._assignKind="rotate"} />
|
||||
${s("bulk_assign_rotate",t)}
|
||||
</label>
|
||||
${this._assignKind==="rotate"?o`<div class="pool">
|
||||
${this._users.map(e=>o`<label class="row">
|
||||
<input type="checkbox" .checked=${this._pool.has(e.id)} @change=${()=>this._pool=this._toggle(this._pool,e.id)} />
|
||||
${e.name}
|
||||
</label>`)}
|
||||
</div>
|
||||
<label class="field">
|
||||
<span>${s("rotation_strategy",t)}</span>
|
||||
<select class="rotation" .value=${this._rotation} @change=${e=>this._rotation=e.target.value}>
|
||||
${d.map(e=>o`<option value=${e} ?selected=${this._rotation===e}>${s(`rotation_${e}`,t)}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
${this._pool.size<2?o`<div class="hint">${s("bulk_rotation_min_two",t)}</div>`:p}`:p}
|
||||
`}_renderLabels(t){return o`
|
||||
<ms-textfield
|
||||
class="labels-add"
|
||||
label=${s("bulk_labels_add",t)}
|
||||
.helper=${s("labels_help",t)}
|
||||
.value=${this._addText}
|
||||
@input=${e=>this._addText=e.target.value}
|
||||
></ms-textfield>
|
||||
${this._present.length?o`<div class="section-title">${s("bulk_labels_remove",t)}</div>
|
||||
<div class="pool">
|
||||
${this._present.map(e=>o`<label class="row">
|
||||
<input type="checkbox" .checked=${this._remove.has(e)} @change=${()=>this._remove=this._toggle(this._remove,e)} />
|
||||
${e}
|
||||
</label>`)}
|
||||
</div>`:p}
|
||||
`}_renderEdit(t){return o`
|
||||
<div class="hint">${s("bulk_edit_hint",t)}</div>
|
||||
<div class="row edit-row">
|
||||
<input type="checkbox" class="set-warning" .checked=${this._setWarning} @change=${e=>this._setWarning=e.target.checked} />
|
||||
<span class="grow">${s("warning_days",t)}</span>
|
||||
<input type="number" class="warning" min="0" max=${m} .value=${String(this._warning)} ?disabled=${!this._setWarning}
|
||||
@change=${e=>{let r=parseInt(e.target.value,10);this._warning=Number.isInteger(r)?Math.min(Math.max(r,0),m):this._warning}} />
|
||||
</div>
|
||||
<div class="row edit-row">
|
||||
<input type="checkbox" class="set-priority" .checked=${this._setPriority} @change=${e=>this._setPriority=e.target.checked} />
|
||||
<span class="grow">${s("priority",t)}</span>
|
||||
<select class="priority" .value=${this._priority} ?disabled=${!this._setPriority} @change=${e=>this._priority=e.target.value}>
|
||||
${v.map(e=>o`<option value=${e} ?selected=${this._priority===e}>${s(`priority_${e}`,t)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
<div class="row edit-row">
|
||||
<input type="checkbox" class="set-mute" .checked=${this._setMute} @change=${e=>this._setMute=e.target.checked} />
|
||||
<span class="grow">${s("bulk_no_notifications",t)}</span>
|
||||
<input type="checkbox" class="mute" .checked=${this._mute} ?disabled=${!this._setMute} @change=${e=>this._mute=e.target.checked} />
|
||||
</div>
|
||||
`}render(){if(!this._open)return o``;let t=this._lang,e=this._mode==="assign"?s("bulk_assign",t):this._mode==="labels"?s("labels",t):s("edit",t),r=this.changes()!==null;return o`
|
||||
<ha-dialog open @closed=${()=>this._finish(null)}>
|
||||
<div class="dialog-title">${e}</div>
|
||||
<div class="content">
|
||||
<div class="count">${s("bulk_n_selected",t).replace("{n}",String(this._count))}</div>
|
||||
${this._mode==="assign"?this._renderAssign(t):this._mode==="labels"?this._renderLabels(t):this._renderEdit(t)}
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<ha-button appearance="plain" @click=${()=>this._finish(null)}>${s("cancel",t)}</ha-button>
|
||||
<ha-button class="apply" .disabled=${!r} @click=${()=>this._finish(this.changes())}>${s("save",t)}</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};i.styles=_`
|
||||
.dialog-title { font-size: 18px; font-weight: 500; padding-bottom: 12px; }
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
min-width: min(360px, calc(100vw - 24px));
|
||||
max-width: 520px;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.content { min-width: 0; max-width: none; max-height: none; }
|
||||
}
|
||||
.count, .hint { color: var(--secondary-text-color); font-size: 13px; }
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
.choice, .row { display: flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; }
|
||||
.pool { display: flex; flex-direction: column; gap: 4px; padding-left: 26px; }
|
||||
.field { display: flex; align-items: center; gap: 8px; justify-content: space-between; font-size: 14px; }
|
||||
.grow { flex: 1; min-width: 0; }
|
||||
select, input[type="number"] {
|
||||
font: inherit;
|
||||
padding: 4px 6px;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 4px;
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
max-width: 100%;
|
||||
}
|
||||
input[type="number"] { width: 72px; }
|
||||
select.person { margin-left: 26px; }
|
||||
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; padding-top: 16px; }
|
||||
`,n([g({attribute:!1})],i.prototype,"hass",2),n([a()],i.prototype,"_open",2),n([a()],i.prototype,"_mode",2),n([a()],i.prototype,"_count",2),n([a()],i.prototype,"_users",2),n([a()],i.prototype,"_present",2),n([a()],i.prototype,"_assignKind",2),n([a()],i.prototype,"_person",2),n([a()],i.prototype,"_pool",2),n([a()],i.prototype,"_rotation",2),n([a()],i.prototype,"_addText",2),n([a()],i.prototype,"_remove",2),n([a()],i.prototype,"_setWarning",2),n([a()],i.prototype,"_warning",2),n([a()],i.prototype,"_setPriority",2),n([a()],i.prototype,"_priority",2),n([a()],i.prototype,"_setMute",2),n([a()],i.prototype,"_mute",2);customElements.get("maintenance-bulk-edit-dialog")||customElements.define("maintenance-bulk-edit-dialog",i);export{i as MaintenanceBulkEditDialog};
|
||||
@@ -0,0 +1,37 @@
|
||||
/*! maintenance_supporter frontend 2.97.1 */
|
||||
import{B as R,D as k,R as x,a as m,b as h,c as g,f as v,h as b,l as y,m as _,s as l}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YJXVPPWG.js";function w(i){return`${i.entry_id??""}\0${i.part_id}`}function E(i,t,e,o){let n=!!i.entry_id&&i.entry_id!==t,r=n?i.entry_id:t,s=e.find(p=>p.entry_id===r),a=(s?.parts||[]).find(p=>p.id===i.part_id)||null,c=n&&s?.object?.name||"",u=a?.name||l("shared_part_unknown",o);return{part:a,foreign:n,ownerName:c,label:c?`${u} (${c})`:u}}function L(i,t,e,o){let{part:n,label:r}=E(i,t,e,o),s=n&&n.stock!==null&&n.stock!==void 0?` (${k(n.stock,n.unit,o)})`:"",a=n?.storage_location?` \u2014 ${n.storage_location}`:"";return`${R(i.quantity,o)}\xD7 ${r}${s}${a}`}function $(i,t,e,o){let r=(e.find(a=>a.entry_id===t)?.parts||[]).map(a=>({...a})),s=new Set(r.map(a=>w({part_id:a.id})));for(let a of i?.consumes_parts||[]){if(!a.entry_id||a.entry_id===t)continue;let c=w(a);if(s.has(c))continue;s.add(c);let{part:u,ownerName:p}=E(a,t,e,o);r.push({id:a.part_id,name:u?.name||l("shared_part_unknown",o),unit:u?.unit,stock:u?.stock??null,storage_location:u?.storage_location,entry_id:a.entry_id,owner_name:p})}return r}function O(){let i=new Uint8Array(4);return(globalThis.crypto??{getRandomValues:t=>t.map(()=>Math.floor(Math.random()*256))}).getRandomValues(i),Array.from(i,t=>t.toString(16).padStart(2,"0")).join("")}function f(i){let t=i?.reading_values;if(!Array.isArray(t))return[];let e=[],o=new Set;for(let n of t){if(!n||typeof n!="object")continue;let r=n;typeof r.id!="string"||!r.id||o.has(r.id)||typeof r.value!="number"||!Number.isFinite(r.value)||(o.add(r.id),e.push({id:r.id,name:typeof r.name=="string"?r.name:r.id,unit:typeof r.unit=="string"?r.unit:null,value:r.value}))}return e}function S(i){return i.filter(t=>t.type==="completed"&&(t.reading_value!=null||f(t).length>0)).sort((t,e)=>t.timestamp.localeCompare(e.timestamp))}function V(i){return S(i??[]).map(t=>({timestamp:t.timestamp,values:f(t)})).filter(t=>t.values.length>0)}function H(i,t,e){let o;for(let n of i){if(e!==void 0){let s=new Date(n.timestamp).getTime();if(!isNaN(s)&&s>=e)break}let r=n.values.find(s=>s.id===t);r&&(o=r)}return o}function T(i,t,e){let o=f(t).find(r=>r.id===e);if(!o)return null;let n=null;for(let r of S(i)){if(r.timestamp>=t.timestamp)break;let s=f(r).find(a=>a.id===e);s&&(n=s.value)}return n==null?null:o.value-n}function A(i){let t=new Set,e=new Set;for(let o of i){let n=(o.name||"").trim().toLowerCase();n&&(t.has(n)?e.add(o.id):t.add(n))}return e}function M(i){let t=[],e=new Set,o=new Set;for(let n of i){let r=(n.name||"").trim();if(!(!r||e.has(n.id)||o.has(r.toLowerCase()))&&(e.add(n.id),o.add(r.toLowerCase()),t.push({id:n.id,name:r,unit:(n.unit||"").trim()||null}),t.length>=20))break}return t}function C(i){let t=(i??"").trim();if(t==="")return null;let e=Number(t.replace(",","."));return!Number.isFinite(e)||e<0?null:Math.round(e)}var U=["notes","cost","duration","photo","user"],j={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"};var d=class extends b{constructor(){super(...arguments);this.value="";this.lang="en";this._credit=!1;this._amount=""}willUpdate(e){if(!e.has("value"))return;let o=(this.value??"").trim();if(o===this._signed())return;let n=parseFloat(o);if(o===""||Number.isNaN(n)){this._amount="",this._credit=!1;return}this._credit=n<0,this._amount=String(Math.abs(n))}_signed(){let e=this._amount.trim();return e===""?"":this._credit?`-${e}`:e}_emit(){this.value=this._signed(),this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:this.value},bubbles:!0,composed:!0}))}_setCredit(e){this._credit!==e&&(this._credit=e,this._emit())}_onInput(e){let o=e.target.value;o.startsWith("-")&&(this._credit=!0),this._amount=o.replace(/^-+/,""),this._emit()}render(){let e=this.lang;return g`
|
||||
<div class="wrap">
|
||||
<div class="sign" role="group">
|
||||
<button type="button" class="kind cost ${this._credit?"":"on"}" aria-pressed=${String(!this._credit)}
|
||||
@click=${()=>this._setCredit(!1)}>${l("cost",e)}</button>
|
||||
<button type="button" class="kind credit ${this._credit?"on":""}" aria-pressed=${String(this._credit)}
|
||||
@click=${()=>this._setCredit(!0)}>${l("cost_kind_credit",e)}</button>
|
||||
</div>
|
||||
<input type="number" step="0.01" min="0" inputmode="decimal" class="field-input amount"
|
||||
.value=${this._amount} @input=${this._onInput} />
|
||||
</div>
|
||||
${this._credit?g`<div class="hint">${l("cost_credit_hint",e)}</div>`:v}
|
||||
`}};d.styles=[x,h`
|
||||
:host { display: block; }
|
||||
.wrap { display: flex; gap: 8px; align-items: stretch; }
|
||||
.sign {
|
||||
display: inline-flex;
|
||||
flex: none;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.kind {
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
padding: 0 10px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
.kind.on { background: var(--primary-color); color: var(--text-primary-color, #fff); }
|
||||
.kind.credit.on { background: var(--success-color, #43a047); }
|
||||
.amount { flex: 1; min-width: 0; }
|
||||
.hint { margin-top: 4px; font-size: 12px; color: var(--secondary-text-color); }
|
||||
`],m([y()],d.prototype,"value",2),m([y()],d.prototype,"lang",2),m([_()],d.prototype,"_credit",2),m([_()],d.prototype,"_amount",2);customElements.get("ms-cost-input")||customElements.define("ms-cost-input",d);export{w as a,L as b,$ as c,O as d,f as e,V as f,H as g,T as h,A as i,M as j,C as k,U as l,j as m};
|
||||
@@ -1,153 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.90.0 */
|
||||
import{a as u}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-WQXORDNI.js";import{a as r,b as _,c as l,f as o,h,l as d,m as s,q as i,s as p}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-RAW6RFRE.js";var a=class extends h{constructor(){super(...arguments);this.objects=[];this._open=!1;this._loading=!1;this._error="";this._name="";this._manufacturer="";this._model="";this._serialNumber="";this._areaId="";this._installationDate="";this._warrantyExpiry="";this._documentationUrl="";this._notes="";this._haDeviceId="";this._parentEntryId="";this._entryId=null}get _lang(){return p(this.hass)}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._error="",this._open=!0}openEdit(e,n){this._entryId=e,this._name=n.name||"",this._manufacturer=n.manufacturer||"",this._model=n.model||"",this._serialNumber=n.serial_number||"",this._areaId=n.area_id||"",this._installationDate=n.installation_date||"",this._warrantyExpiry=n.warranty_expiry||"",this._documentationUrl=n.documentation_url||"",this._notes=n.notes||"",this._haDeviceId=n.ha_device_id||"",this._parentEntryId=n.parent_entry_id||"",this._error="",this._open=!0}async _save(){if(!this._loading&&this._name.trim()){this._loading=!0,this._error="";try{this._entryId?await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/update",entry_id:this._entryId,name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}):await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/create",name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}),this._open=!1,this.dispatchEvent(new CustomEvent("object-saved"))}catch(e){this._error=u(e,this._lang,i("save_error",this._lang))}finally{this._loading=!1}}}_parentChoices(){return(this.objects||[]).filter(e=>e.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return l``;let e=this._lang,n=this._entryId?i("edit_object",e):i("new_object",e);return l`
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${n}</div>
|
||||
<div class="content">
|
||||
${this._error?l`<div class="error">${this._error}</div>`:o}
|
||||
<ms-textfield
|
||||
label="${i("name",e)}"
|
||||
required
|
||||
.value=${this._name}
|
||||
@input=${t=>this._name=t.target.value}
|
||||
></ms-textfield>
|
||||
<ms-textfield
|
||||
label="${i("manufacturer_optional",e)}"
|
||||
.value=${this._manufacturer}
|
||||
@input=${t=>this._manufacturer=t.target.value}
|
||||
></ms-textfield>
|
||||
<ms-textfield
|
||||
label="${i("model_optional",e)}"
|
||||
.value=${this._model}
|
||||
@input=${t=>this._model=t.target.value}
|
||||
></ms-textfield>
|
||||
<ms-textfield
|
||||
label="${i("serial_number_optional",e)}"
|
||||
.value=${this._serialNumber}
|
||||
@input=${t=>this._serialNumber=t.target.value}
|
||||
></ms-textfield>
|
||||
<ms-textfield
|
||||
label="${i("documentation_url_optional",e)}"
|
||||
type="url"
|
||||
.value=${this._documentationUrl}
|
||||
@input=${t=>this._documentationUrl=t.target.value}
|
||||
></ms-textfield>
|
||||
<ha-area-picker
|
||||
.hass=${this.hass}
|
||||
label="${i("area_id_optional",e)}"
|
||||
.value=${this._areaId}
|
||||
@value-changed=${t=>this._areaId=t.detail.value||""}
|
||||
></ha-area-picker>
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${e}
|
||||
label="${i("installation_date_optional",e)}"
|
||||
.value=${this._installationDate}
|
||||
@value-changed=${t=>this._installationDate=t.detail.value}
|
||||
></ms-date-field>
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${e}
|
||||
label="${i("warranty_expiry_optional",e)}"
|
||||
.value=${this._warrantyExpiry}
|
||||
@value-changed=${t=>this._warrantyExpiry=t.detail.value}
|
||||
></ms-date-field>
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${{device:this._haDeviceId||void 0}}
|
||||
.schema=${[{name:"device",selector:{device:{}}}]}
|
||||
.computeLabel=${()=>i("link_device_optional",e)}
|
||||
@value-changed=${t=>this._haDeviceId=t.detail.value?.device||""}
|
||||
></ha-form>
|
||||
${this._parentChoices().length?l`<label class="textarea-field">
|
||||
<span class="textarea-label">${i("parent_object_optional",e)}</span>
|
||||
<select
|
||||
class="parent-select"
|
||||
.value=${this._parentEntryId}
|
||||
@change=${t=>this._parentEntryId=t.target.value}
|
||||
>
|
||||
<option value="" ?selected=${!this._parentEntryId}>
|
||||
${i("parent_none",e)}
|
||||
</option>
|
||||
${this._parentChoices().map(t=>l`<option
|
||||
value=${t.entry_id}
|
||||
?selected=${this._parentEntryId===t.entry_id}
|
||||
>${t.object.name}</option>`)}
|
||||
</select>
|
||||
</label>`:o}
|
||||
<label class="textarea-field">
|
||||
<span class="textarea-label">${i("object_notes_optional",e)}</span>
|
||||
<textarea
|
||||
rows="3"
|
||||
.value=${this._notes}
|
||||
@input=${t=>this._notes=t.target.value}
|
||||
></textarea>
|
||||
<span class="md-hint">${i("notes_markdown_hint",e)}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<ha-button appearance="plain" @click=${this._close}>
|
||||
${i("cancel",this._lang)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
@click=${this._save}
|
||||
.disabled=${this._loading||!this._name.trim()}
|
||||
>
|
||||
${this._loading?i("saving",this._lang):i("save",this._lang)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};a.styles=_`
|
||||
.dialog-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-width: 300px;
|
||||
}
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
ms-textfield {
|
||||
display: block;
|
||||
}
|
||||
.textarea-field {
|
||||
display: flex; flex-direction: column; gap: 4px;
|
||||
}
|
||||
.textarea-label {
|
||||
font-size: 12px; color: var(--secondary-text-color, #888); font-weight: 500;
|
||||
}
|
||||
.textarea-field textarea {
|
||||
padding: 8px 10px; font-size: 14px; font-family: inherit;
|
||||
background: var(--secondary-background-color, rgba(0,0,0,0.06));
|
||||
color: var(--primary-text-color);
|
||||
border: 1px solid var(--divider-color); border-radius: 6px;
|
||||
resize: vertical;
|
||||
}
|
||||
.textarea-field textarea:focus {
|
||||
outline: none; border-color: var(--primary-color);
|
||||
}
|
||||
.md-hint {
|
||||
font-size: 11px; color: var(--secondary-text-color); font-style: italic;
|
||||
}
|
||||
.parent-select {
|
||||
padding: 8px 10px; font-size: 14px; font-family: inherit;
|
||||
background: var(--secondary-background-color, rgba(0,0,0,0.06));
|
||||
color: var(--primary-text-color);
|
||||
border: 1px solid var(--divider-color); border-radius: 6px;
|
||||
}
|
||||
.error {
|
||||
color: var(--error-color, #f44336);
|
||||
font-size: 13px;
|
||||
}
|
||||
`,r([d({attribute:!1})],a.prototype,"hass",2),r([d({attribute:!1})],a.prototype,"objects",2),r([s()],a.prototype,"_open",2),r([s()],a.prototype,"_loading",2),r([s()],a.prototype,"_error",2),r([s()],a.prototype,"_name",2),r([s()],a.prototype,"_manufacturer",2),r([s()],a.prototype,"_model",2),r([s()],a.prototype,"_serialNumber",2),r([s()],a.prototype,"_areaId",2),r([s()],a.prototype,"_installationDate",2),r([s()],a.prototype,"_warrantyExpiry",2),r([s()],a.prototype,"_documentationUrl",2),r([s()],a.prototype,"_notes",2),r([s()],a.prototype,"_haDeviceId",2),r([s()],a.prototype,"_parentEntryId",2),r([s()],a.prototype,"_entryId",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",a);export{a};
|
||||
@@ -1,88 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.90.0 */
|
||||
import{A as m,a,b as y,c as s,f as l,h as g,l as o,m as f,q as d}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-RAW6RFRE.js";function $(n){let t=m("2001-02-03",n).match(/\d+/g)||[],e=v=>t.findIndex(_=>Number(_)===v),h=e(2001),c=e(2),u=e(3);return h===0?"YMD":c>=0&&u>=0&&c<u?"MDY":"DMY"}function p(n,i,t){if(n<1e3||n>9999||i<1||i>12||t<1||t>31)return null;let e=new Date(n,i-1,t);return e.getFullYear()!==n||e.getMonth()!==i-1||e.getDate()!==t?null:`${n}-${String(i).padStart(2,"0")}-${String(t).padStart(2,"0")}`}function b(n,i){let t=n.trim(),e;if(e=t.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/))return p(+e[1],+e[2],+e[3]);if(e=t.match(/^(\d{4})$/))return p(+e[1],1,1);if(e=t.match(/^(\d{1,2})[./](\d{4})$/))return p(+e[2],+e[1],1);if(e=t.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/))return p(+e[3],+e[2],+e[1]);if(e=t.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{4})$/)){let h=+e[1],c=+e[2],u=+e[3];return $(i)==="MDY"?p(u,h,c):p(u,c,h)}return null}function x(n,i){if(i)switch(n){case"date":return i.split("T")[0];case"time":return i.length===5?`${i}:00`:i;case"datetime":{let[t,e="00:00:00"]=i.split("T");return`${t} ${e.length===5?`${e}:00`:e}`}}}function k(n,i){if(typeof i!="string"||!i)return"";switch(n){case"date":return i.slice(0,10);case"time":return i.slice(0,5);case"datetime":{let[t,e="00:00:00"]=i.split(" ");return`${t}T${e.length===5?`${e}:00`:e}`}}}var r=class extends g{constructor(){super(...arguments);this.kind="date";this.label="";this.value="";this.clearable=!1;this.disabled=!1;this.required=!1;this.lang="en";this._typing=!1;this._typed="";this._typedInvalid=!1}_startTyping(){this._typed=this.value,this._typedInvalid=!1,this._typing=!0,this.updateComplete.then(()=>this.shadowRoot?.querySelector("input.typed")?.focus())}_commitTyped(){if(!this._typed.trim()){this._typing=!1;return}let t=b(this._typed,this.lang);if(!t){this._typedInvalid=!0;return}this._typing=!1,this._typedInvalid=!1,this._emit(t)}_onTypedKey(t){t.key==="Enter"?(t.preventDefault(),this._commitTyped()):t.key==="Escape"&&(t.preventDefault(),this._typing=!1,this._typedInvalid=!1)}_selector(){switch(this.kind){case"date":return{date:{}};case"time":return{time:{no_second:!0}};case"datetime":return{datetime:{}}}}_onSelectorChange(t){t.stopPropagation(),this._emit(k(this.kind,t.detail?.value))}_clear(){this._emit("")}_emit(t){t!==this.value&&(this.value=t,this.dispatchEvent(new CustomEvent("value-changed",{bubbles:!0,composed:!0,detail:{value:t}})))}render(){return s`
|
||||
<div class="field">
|
||||
${this.label?s`<span class="label">${this.label}${this.required?s`<span class="req">*</span>`:l}</span>`:l}
|
||||
<div class="row">
|
||||
${this._typing?s`<input class="typed" type="text" inputmode="numeric" autocomplete="off"
|
||||
.value=${this._typed}
|
||||
placeholder=${m("1978-03-15",this.lang)}
|
||||
aria-invalid=${this._typedInvalid?"true":"false"}
|
||||
@input=${t=>{this._typed=t.target.value,this._typedInvalid=!1}}
|
||||
@keydown=${this._onTypedKey}
|
||||
@blur=${this._commitTyped} />`:s`<ha-selector
|
||||
.hass=${this.hass}
|
||||
.selector=${this._selector()}
|
||||
.value=${x(this.kind,this.value)}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._onSelectorChange}
|
||||
></ha-selector>`}
|
||||
${this.kind==="date"&&!this.disabled&&!this._typing?s`<button type="button" class="type-toggle" title=${d("date_type_toggle",this.lang)} aria-label=${d("date_type_toggle",this.lang)} @click=${this._startTyping}>
|
||||
<ha-icon icon="mdi:keyboard-outline"></ha-icon>
|
||||
</button>`:l}
|
||||
${this.clearable&&this.value&&!this.disabled&&!this._typing?s`<button type="button" class="clear" title=${d("clear",this.lang)} aria-label=${d("clear",this.lang)} @click=${this._clear}>
|
||||
<ha-icon icon="mdi:close"></ha-icon>
|
||||
</button>`:l}
|
||||
</div>
|
||||
${this._typedInvalid?s`<span class="helper invalid">${d("date_type_invalid",this.lang)}</span>`:this.helper?s`<span class="helper">${this.helper}</span>`:l}
|
||||
</div>
|
||||
`}};r.styles=y`
|
||||
:host { display: block; }
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.label {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color, #888);
|
||||
font-weight: 500;
|
||||
}
|
||||
.req { color: var(--error-color, #f44336); margin-left: 2px; }
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
ha-selector {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.clear, .type-toggle {
|
||||
flex: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
--mdc-icon-size: 20px;
|
||||
}
|
||||
.clear:hover, .type-toggle:hover { background: rgba(var(--rgb-primary-text-color, 0, 0, 0), 0.08); }
|
||||
.clear:focus-visible, .type-toggle:focus-visible { outline: 2px solid var(--primary-color); }
|
||||
.helper {
|
||||
font-size: 11px;
|
||||
color: var(--secondary-text-color);
|
||||
font-style: italic;
|
||||
}
|
||||
.helper.invalid { color: var(--error-color, #f44336); font-style: normal; }
|
||||
input.typed {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 12px;
|
||||
border: 1px solid var(--divider-color, #ccc);
|
||||
border-radius: 4px;
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
font: inherit;
|
||||
}
|
||||
input.typed[aria-invalid="true"] { border-color: var(--error-color, #f44336); }
|
||||
input.typed:focus { outline: 2px solid var(--primary-color); outline-offset: -1px; }
|
||||
`,a([o({attribute:!1})],r.prototype,"hass",2),a([o()],r.prototype,"kind",2),a([o()],r.prototype,"label",2),a([o()],r.prototype,"value",2),a([o()],r.prototype,"helper",2),a([o({type:Boolean})],r.prototype,"clearable",2),a([o({type:Boolean})],r.prototype,"disabled",2),a([o({type:Boolean})],r.prototype,"required",2),a([o()],r.prototype,"lang",2),a([f()],r.prototype,"_typing",2),a([f()],r.prototype,"_typed",2),a([f()],r.prototype,"_typedInvalid",2);customElements.get("ms-date-field")||customElements.define("ms-date-field",r);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.88.0 */
|
||||
function c(o,t,e){let l=new Blob([o],{type:e}),d=URL.createObjectURL(l),n=document.createElement("a");n.href=d,n.download=t,n.target="_blank",n.rel="noopener",n.style.display="none",document.body.appendChild(n),n.dispatchEvent(new MouseEvent("click")),document.body.removeChild(n),setTimeout(()=>URL.revokeObjectURL(d),6e4)}function r(o,t){let e=document.createElement("a");e.href=o,e.download=t,e.target="_blank",e.rel="noopener",e.style.display="none",document.body.appendChild(e),e.dispatchEvent(new MouseEvent("click")),document.body.removeChild(e)}export{c as a,r as b};
|
||||
@@ -1,153 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.95.0 */
|
||||
import{a as u}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-GGJ6YWZO.js";import{a as r,b as _,c as l,f as o,h as p,l as d,m as s,s as i,y as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-4XNVTWGC.js";var a=class extends p{constructor(){super(...arguments);this.objects=[];this._open=!1;this._loading=!1;this._error="";this._name="";this._manufacturer="";this._model="";this._serialNumber="";this._areaId="";this._installationDate="";this._warrantyExpiry="";this._documentationUrl="";this._notes="";this._haDeviceId="";this._parentEntryId="";this._entryId=null}get _lang(){return h(this.hass)}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._error="",this._open=!0}openEdit(e,n){this._entryId=e,this._name=n.name||"",this._manufacturer=n.manufacturer||"",this._model=n.model||"",this._serialNumber=n.serial_number||"",this._areaId=n.area_id||"",this._installationDate=n.installation_date||"",this._warrantyExpiry=n.warranty_expiry||"",this._documentationUrl=n.documentation_url||"",this._notes=n.notes||"",this._haDeviceId=n.ha_device_id||"",this._parentEntryId=n.parent_entry_id||"",this._error="",this._open=!0}async _save(){if(this._loading||!this._name.trim())return;this._error="";let e={name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null};await u(this,this._entryId?{type:"maintenance_supporter/object/update",entry_id:this._entryId,...e}:{type:"maintenance_supporter/object/create",...e},{busy:t=>{this._loading=t},fallbackKey:"save_error",onError:t=>{this._error=t}})!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-saved")))}_parentChoices(){return(this.objects||[]).filter(e=>e.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return l``;let e=this._lang,n=this._entryId?i("edit_object",e):i("new_object",e);return l`
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${n}</div>
|
||||
<div class="content">
|
||||
${this._error?l`<div class="error">${this._error}</div>`:o}
|
||||
<ms-textfield
|
||||
label="${i("name",e)}"
|
||||
required
|
||||
.value=${this._name}
|
||||
@input=${t=>this._name=t.target.value}
|
||||
></ms-textfield>
|
||||
<ms-textfield
|
||||
label="${i("manufacturer_optional",e)}"
|
||||
.value=${this._manufacturer}
|
||||
@input=${t=>this._manufacturer=t.target.value}
|
||||
></ms-textfield>
|
||||
<ms-textfield
|
||||
label="${i("model_optional",e)}"
|
||||
.value=${this._model}
|
||||
@input=${t=>this._model=t.target.value}
|
||||
></ms-textfield>
|
||||
<ms-textfield
|
||||
label="${i("serial_number_optional",e)}"
|
||||
.value=${this._serialNumber}
|
||||
@input=${t=>this._serialNumber=t.target.value}
|
||||
></ms-textfield>
|
||||
<ms-textfield
|
||||
label="${i("documentation_url_optional",e)}"
|
||||
type="url"
|
||||
.value=${this._documentationUrl}
|
||||
@input=${t=>this._documentationUrl=t.target.value}
|
||||
></ms-textfield>
|
||||
<ha-area-picker
|
||||
.hass=${this.hass}
|
||||
label="${i("area_id_optional",e)}"
|
||||
.value=${this._areaId}
|
||||
@value-changed=${t=>this._areaId=t.detail.value||""}
|
||||
></ha-area-picker>
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${e}
|
||||
label="${i("installation_date_optional",e)}"
|
||||
.value=${this._installationDate}
|
||||
@value-changed=${t=>this._installationDate=t.detail.value}
|
||||
></ms-date-field>
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${e}
|
||||
label="${i("warranty_expiry_optional",e)}"
|
||||
.value=${this._warrantyExpiry}
|
||||
@value-changed=${t=>this._warrantyExpiry=t.detail.value}
|
||||
></ms-date-field>
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${{device:this._haDeviceId||void 0}}
|
||||
.schema=${[{name:"device",selector:{device:{}}}]}
|
||||
.computeLabel=${()=>i("link_device_optional",e)}
|
||||
@value-changed=${t=>this._haDeviceId=t.detail.value?.device||""}
|
||||
></ha-form>
|
||||
${this._parentChoices().length?l`<label class="textarea-field">
|
||||
<span class="textarea-label">${i("parent_object_optional",e)}</span>
|
||||
<select
|
||||
class="parent-select"
|
||||
.value=${this._parentEntryId}
|
||||
@change=${t=>this._parentEntryId=t.target.value}
|
||||
>
|
||||
<option value="" ?selected=${!this._parentEntryId}>
|
||||
${i("parent_none",e)}
|
||||
</option>
|
||||
${this._parentChoices().map(t=>l`<option
|
||||
value=${t.entry_id}
|
||||
?selected=${this._parentEntryId===t.entry_id}
|
||||
>${t.object.name}</option>`)}
|
||||
</select>
|
||||
</label>`:o}
|
||||
<label class="textarea-field">
|
||||
<span class="textarea-label">${i("object_notes_optional",e)}</span>
|
||||
<textarea
|
||||
rows="3"
|
||||
.value=${this._notes}
|
||||
@input=${t=>this._notes=t.target.value}
|
||||
></textarea>
|
||||
<span class="md-hint">${i("notes_markdown_hint",e)}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<ha-button appearance="plain" @click=${this._close}>
|
||||
${i("cancel",this._lang)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
@click=${this._save}
|
||||
.disabled=${this._loading||!this._name.trim()}
|
||||
>
|
||||
${this._loading?i("saving",this._lang):i("save",this._lang)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};a.styles=_`
|
||||
.dialog-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-width: 300px;
|
||||
}
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
ms-textfield {
|
||||
display: block;
|
||||
}
|
||||
.textarea-field {
|
||||
display: flex; flex-direction: column; gap: 4px;
|
||||
}
|
||||
.textarea-label {
|
||||
font-size: 12px; color: var(--secondary-text-color, #888); font-weight: 500;
|
||||
}
|
||||
.textarea-field textarea {
|
||||
padding: 8px 10px; font-size: 14px; font-family: inherit;
|
||||
background: var(--secondary-background-color, rgba(0,0,0,0.06));
|
||||
color: var(--primary-text-color);
|
||||
border: 1px solid var(--divider-color); border-radius: 6px;
|
||||
resize: vertical;
|
||||
}
|
||||
.textarea-field textarea:focus {
|
||||
outline: none; border-color: var(--primary-color);
|
||||
}
|
||||
.md-hint {
|
||||
font-size: 11px; color: var(--secondary-text-color); font-style: italic;
|
||||
}
|
||||
.parent-select {
|
||||
padding: 8px 10px; font-size: 14px; font-family: inherit;
|
||||
background: var(--secondary-background-color, rgba(0,0,0,0.06));
|
||||
color: var(--primary-text-color);
|
||||
border: 1px solid var(--divider-color); border-radius: 6px;
|
||||
}
|
||||
.error {
|
||||
color: var(--error-color, #f44336);
|
||||
font-size: 13px;
|
||||
}
|
||||
`,r([d({attribute:!1})],a.prototype,"hass",2),r([d({attribute:!1})],a.prototype,"objects",2),r([s()],a.prototype,"_open",2),r([s()],a.prototype,"_loading",2),r([s()],a.prototype,"_error",2),r([s()],a.prototype,"_name",2),r([s()],a.prototype,"_manufacturer",2),r([s()],a.prototype,"_model",2),r([s()],a.prototype,"_serialNumber",2),r([s()],a.prototype,"_areaId",2),r([s()],a.prototype,"_installationDate",2),r([s()],a.prototype,"_warrantyExpiry",2),r([s()],a.prototype,"_documentationUrl",2),r([s()],a.prototype,"_notes",2),r([s()],a.prototype,"_haDeviceId",2),r([s()],a.prototype,"_parentEntryId",2),r([s()],a.prototype,"_entryId",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",a);export{a};
|
||||
@@ -1,2 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.95.0 */
|
||||
import{a as r}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-LEWJHPDY.js";var a={operatorWriteEnabled:!1,operatorIds:[]};function p(e,t=a){return e?e.is_admin?!0:t.operatorWriteEnabled&&t.operatorIds.includes(e.id):!1}var d=["buttons_compact","buttons","icons"];function _(e){let[t,n]=r.default_warning_days,s=e?.general?.default_warning_days,i=e?.general?.row_action_style;return{features:{...o.features,...e?.features??{}},defaultWarningDays:typeof s=="number"&&Number.isFinite(s)&&s>=t&&s<=n?s:o.defaultWarningDays,rowActionStyle:d.includes(i??"")?i:o.rowActionStyle,access:{operatorWriteEnabled:e?.operator_write_enabled===!0,operatorIds:Array.isArray(e?.admin_panel_user_ids)?e.admin_panel_user_ids.filter(u=>typeof u=="string"):[]},budget:e?.budget??null,rowActionNoticePending:e?.general?.row_action_notice_pending===!0,refsInLists:e?.general?.ref_numbers_in_lists===!0,objectsTableColumns:Array.isArray(e?.objects_table_columns)?e.objects_table_columns:void 0}}var o={features:{adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1},defaultWarningDays:7,rowActionStyle:"buttons_compact",access:a,budget:null,rowActionNoticePending:!1,refsInLists:!1,objectsTableColumns:void 0};function l(){let e=window;return e.__msSettingsCache??={promise:null}}function b(e){let t=l();if(t.promise)return t.promise;let n=e.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(s=>_(s)).catch(()=>(t.promise===n&&(t.promise=null),o));return t.promise=n,n}function h(){l().promise=null}var c=class{constructor(){this._handle=null}schedule(t,n=4e3){this.clear(),this._handle=setTimeout(()=>{this._handle=null,t()},n)}clear(){this._handle!==null&&(clearTimeout(this._handle),this._handle=null)}get pending(){return this._handle!==null}};export{a,p as b,d as c,_ as d,b as e,h as f,c as g};
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*! maintenance_supporter frontend 2.96.0 */
|
||||
import{a as r,b as l,c as i,f as o,h as n,l as t}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-N6F5RX6S.js";var e=class extends n{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1;this.multiline=!1;this.rows=3}_onInput(s){let a=s.target.value;this.value=a,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:a}}))}render(){return i`
|
||||
/*! maintenance_supporter frontend 2.97.1 */
|
||||
import{a as r,b as l,c as i,f as o,h as n,l as t}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YJXVPPWG.js";var e=class extends n{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1;this.multiline=!1;this.rows=3}_onInput(s){let a=s.target.value;this.value=a,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:a}}))}render(){return i`
|
||||
<label class="field">
|
||||
${this.label?i`<span class="label">${this.label}${this.required?i`<span class="req">*</span>`:o}</span>`:o}
|
||||
${this.multiline?i`
|
||||
File diff suppressed because one or more lines are too long
+2
-2
@@ -1,2 +1,2 @@
|
||||
/*! maintenance_supporter frontend 2.95.0 */
|
||||
import{F as n,s as o}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-4XNVTWGC.js";var l={"Completed from dashboard button":"hist_note_button","Completed from the To-do list":"hist_note_todo","Completed by voice":"hist_note_voice","Completed from the notification":"hist_note_notification","Completed via NFC tag":"hist_note_nfc","Completed from the shopping list":"hist_note_shopping_list","Completed from a mirrored to-do list":"hist_note_todo_mirror","Skipped from notification":"hist_note_skipped_notification","Skipped from dashboard button":"hist_note_skipped_button","Sensor trigger activated":"hist_note_sensor_triggered","Initial value set during task creation":"hist_note_initial"},m=[[/^Auto-completed: sensor recovered \((.+)\)$/,"hist_note_auto_recovered",t=>({value:t[1]})],[/^Reset to (\d{4}-\d{2}-\d{2})$/,"hist_note_reset",(t,e)=>({date:n(t[1],e)})],[/^Trigger entity replaced: (.+) → (.+)$/,"hist_note_trigger_replaced",t=>({old:t[1],new:t[2]})],[/^Sensor trigger removed \(entity was: (.+)\)\. Schedule converted to (\w+)\.$/,"hist_note_trigger_removed",(t,e)=>({entity:t[1],schedule:o(t[2],e)})],[/^Entity (.+) removed from compound trigger; (\d+) conditions remain\.$/,"hist_note_compound_removed",t=>({entity:t[1],count:t[2]})]];function h(t,e){if(!t)return"";let i=l[t.trim()];if(i)return o(i,e);for(let[s,d,_]of m){let r=t.trim().match(s);if(r){let c=_(r,e);return o(d,e).replace(/\{(\w+)\}/g,(a,g)=>c[g]??a)}}return t}export{h as a};
|
||||
/*! maintenance_supporter frontend 2.97.1 */
|
||||
import{F as n,s as o}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-YJXVPPWG.js";var l={"Completed from dashboard button":"hist_note_button","Completed from the To-do list":"hist_note_todo","Completed by voice":"hist_note_voice","Completed from the notification":"hist_note_notification","Completed via NFC tag":"hist_note_nfc","Completed from the shopping list":"hist_note_shopping_list","Completed from a mirrored to-do list":"hist_note_todo_mirror","Skipped from notification":"hist_note_skipped_notification","Skipped from dashboard button":"hist_note_skipped_button","Sensor trigger activated":"hist_note_sensor_triggered","Initial value set during task creation":"hist_note_initial"},m=[[/^Auto-completed: sensor recovered \((.+)\)$/,"hist_note_auto_recovered",t=>({value:t[1]})],[/^Reset to (\d{4}-\d{2}-\d{2})$/,"hist_note_reset",(t,e)=>({date:n(t[1],e)})],[/^Trigger entity replaced: (.+) → (.+)$/,"hist_note_trigger_replaced",t=>({old:t[1],new:t[2]})],[/^Sensor trigger removed \(entity was: (.+)\)\. Schedule converted to (\w+)\.$/,"hist_note_trigger_removed",(t,e)=>({entity:t[1],schedule:o(t[2],e)})],[/^Entity (.+) removed from compound trigger; (\d+) conditions remain\.$/,"hist_note_compound_removed",t=>({entity:t[1],count:t[2]})]];function h(t,e){if(!t)return"";let i=l[t.trim()];if(i)return o(i,e);for(let[s,d,_]of m){let r=t.trim().match(s);if(r){let c=_(r,e);return o(d,e).replace(/\{(\w+)\}/g,(a,g)=>c[g]??a)}}return t}export{h as a};
|
||||
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.90.0 */
|
||||
import{q as p}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-RAW6RFRE.js";function f(r){return`${r.entry_id??""}\0${r.part_id}`}function g(r,n,i,o){let t=!!r.entry_id&&r.entry_id!==n,e=t?r.entry_id:n,s=i.find(l=>l.entry_id===e),a=(s?.parts||[]).find(l=>l.id===r.part_id)||null,u=t&&s?.object?.name||"",d=a?.name||p("shared_part_unknown",o);return{part:a,foreign:t,ownerName:u,label:u?`${d} (${u})`:d}}function _(r,n,i,o){let{part:t,label:e}=g(r,n,i,o),s=t&&t.stock!==null&&t.stock!==void 0?` (${t.stock}${t.unit?" "+t.unit:""})`:"",a=t?.storage_location?` \u2014 ${t.storage_location}`:"";return`${r.quantity}\xD7 ${e}${s}${a}`}function R(r,n,i,o){let e=(i.find(a=>a.entry_id===n)?.parts||[]).map(a=>({...a})),s=new Set(e.map(a=>f({part_id:a.id})));for(let a of r?.consumes_parts||[]){if(!a.entry_id||a.entry_id===n)continue;let u=f(a);if(s.has(u))continue;s.add(u);let{part:d,ownerName:l}=g(a,n,i,o);e.push({id:a.part_id,name:d?.name||p("shared_part_unknown",o),unit:d?.unit,stock:d?.stock??null,storage_location:d?.storage_location,entry_id:a.entry_id,owner_name:l})}return e}function w(){let r=new Uint8Array(4);return(globalThis.crypto??{getRandomValues:n=>n.map(()=>Math.floor(Math.random()*256))}).getRandomValues(r),Array.from(r,n=>n.toString(16).padStart(2,"0")).join("")}function c(r){let n=r?.reading_values;if(!Array.isArray(n))return[];let i=[],o=new Set;for(let t of n){if(!t||typeof t!="object")continue;let e=t;typeof e.id!="string"||!e.id||o.has(e.id)||typeof e.value!="number"||!Number.isFinite(e.value)||(o.add(e.id),i.push({id:e.id,name:typeof e.name=="string"?e.name:e.id,unit:typeof e.unit=="string"?e.unit:null,value:e.value}))}return i}function y(r){return r.filter(n=>n.type==="completed"&&(n.reading_value!=null||c(n).length>0)).sort((n,i)=>n.timestamp.localeCompare(i.timestamp))}function S(r){return y(r??[]).map(n=>({timestamp:n.timestamp,values:c(n)})).filter(n=>n.values.length>0)}function P(r,n,i){let o;for(let t of r){if(i!==void 0){let s=new Date(t.timestamp).getTime();if(!isNaN(s)&&s>=i)break}let e=t.values.find(s=>s.id===n);e&&(o=e)}return o}function b(r,n,i){let o=c(n).find(e=>e.id===i);if(!o)return null;let t=null;for(let e of y(r)){if(e.timestamp>=n.timestamp)break;let s=c(e).find(a=>a.id===i);s&&(t=s.value)}return t==null?null:o.value-t}function E(r){let n=new Set,i=new Set;for(let o of r){let t=(o.name||"").trim().toLowerCase();t&&(n.has(t)?i.add(o.id):n.add(t))}return i}function v(r){let n=[],i=new Set,o=new Set;for(let t of r){let e=(t.name||"").trim();if(!(!e||i.has(t.id)||o.has(e.toLowerCase()))&&(i.add(t.id),o.add(e.toLowerCase()),n.push({id:t.id,name:e,unit:(t.unit||"").trim()||null}),n.length>=20))break}return n}var x=["notes","cost","duration","photo","user"],h={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"};export{f as a,_ as b,R as c,w as d,c as e,S as f,P as g,b as h,E as i,v as j,x as k,h as l};
|
||||
@@ -1,2 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.94.0 */
|
||||
var n={default_warning_days:[0,365],default_consumable_threshold:[1,90],battery_low_percent:[1,90],battery_recovered_percent:[20,100],archive_oneoff_days:[0,3650],delete_archived_oneoff_days:[0,3650],notify_due_soon_interval_hours:[0,720],notify_overdue_interval_hours:[0,720],notify_triggered_interval_hours:[0,720],max_notifications_per_day:[0,1e3],notification_bundle_threshold:[2,20],snooze_duration_hours:[1,168],warranty_reminder_days:[1,365],budget_alert_threshold:[10,100],currency_decimals:[0,3]};function o(e){let r=n[e];if(!r)throw new Error(`not an int-ranged setting: ${e}`);return r}var t=[0,14],_=[0,1440],a=[1,1e4],u=15,d=[.1,.9],s=[0,365],i=[.01,999],b=[.01,9999],A=[.1,5];export{n as a,o as b,t as c,_ as d,a as e,u as f,d as g,s as h,i,b as j,A as k};
|
||||
@@ -1,2 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.94.0 */
|
||||
import{c as r,f as i}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-JE577H3E.js";var a=["#c62828","#ad1457","#6a1b9a","#4527a0","#283593","#1565c0","#00838f","#2e7d32","#558b2f","#ef6c00","#6d4c41","#546e7a"];function u(e){let s=(e||"").split(/\s+/).filter(Boolean);return s.length===0?"?":s.length===1?s[0][0].toUpperCase():(s[0][0]+s[s.length-1][0]).toUpperCase()}function c(e){let s=0;for(let n of e)s=s*31+n.charCodeAt(0)>>>0;return a[s%a.length]}function o(e){return e?{id:e.id,name:e.name,initials:e.initials||u(e.name),color:e.color||c(e.id)}:null}function h(e){return e?r`<span class="person-avatar" style="--person-color: ${e.color}" title=${e.name}>${e.initials}</span>`:i}function m(e,s=""){return e?r`<span class="person-chip ${s}" title=${e.name}>${h(e)}<span class="person-name">${e.name}</span></span>`:i}var l=class{constructor(s){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=s}updateHass(s){this.hass=s}async getUsers(s=!1){let n=Date.now();if(!s&&this.usersCache&&n-this.cacheTimestamp<this.CACHE_TTL_MS)return this.usersCache;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/users/list"});return this.usersCache=t.users,this.cacheTimestamp=n,this.usersCache}catch(t){return console.error("Failed to fetch users:",t),this.usersCache||[]}}async assignUser(s,n,t){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:s,task_id:n,user_id:t})}async getTasksByUser(s){return(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tasks/by_user",user_id:s})).tasks}getUserName(s){return!s||!this.usersCache?null:this.usersCache.find(t=>t.id===s)?.name||null}getPerson(s){return o(this.getUser(s))}getUser(s){return!s||!this.usersCache?null:this.usersCache.find(n=>n.id===s)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(s){return s?s===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}};export{a,o as b,h as c,m as d,l as e};
|
||||
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.90.0 */
|
||||
import{c as r,f as i}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-RAW6RFRE.js";var a=["#c62828","#ad1457","#6a1b9a","#4527a0","#283593","#1565c0","#00838f","#2e7d32","#558b2f","#ef6c00","#6d4c41","#546e7a"];function u(e){let s=(e||"").split(/\s+/).filter(Boolean);return s.length===0?"?":s.length===1?s[0][0].toUpperCase():(s[0][0]+s[s.length-1][0]).toUpperCase()}function c(e){let s=0;for(let n of e)s=s*31+n.charCodeAt(0)>>>0;return a[s%a.length]}function o(e){return e?{id:e.id,name:e.name,initials:e.initials||u(e.name),color:e.color||c(e.id)}:null}function h(e){return e?r`<span class="person-avatar" style="--person-color: ${e.color}" title=${e.name}>${e.initials}</span>`:i}function m(e,s=""){return e?r`<span class="person-chip ${s}" title=${e.name}>${h(e)}<span class="person-name">${e.name}</span></span>`:i}var l=class{constructor(s){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=s}updateHass(s){this.hass=s}async getUsers(s=!1){let n=Date.now();if(!s&&this.usersCache&&n-this.cacheTimestamp<this.CACHE_TTL_MS)return this.usersCache;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/users/list"});return this.usersCache=t.users,this.cacheTimestamp=n,this.usersCache}catch(t){return console.error("Failed to fetch users:",t),this.usersCache||[]}}async assignUser(s,n,t){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:s,task_id:n,user_id:t})}async getTasksByUser(s){return(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tasks/by_user",user_id:s})).tasks}getUserName(s){return!s||!this.usersCache?null:this.usersCache.find(t=>t.id===s)?.name||null}getPerson(s){return o(this.getUser(s))}getUser(s){return!s||!this.usersCache?null:this.usersCache.find(n=>n.id===s)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(s){return s?s===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}};export{a,o as b,h as c,m as d,l as e};
|
||||
@@ -1,88 +0,0 @@
|
||||
/*! maintenance_supporter frontend 2.94.0 */
|
||||
import{D as m,a,b as y,c as s,f as l,h as g,l as o,m as f,s as d}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-JE577H3E.js";function $(n){let t=m("2001-02-03",n).match(/\d+/g)||[],e=v=>t.findIndex(_=>Number(_)===v),h=e(2001),c=e(2),u=e(3);return h===0?"YMD":c>=0&&u>=0&&c<u?"MDY":"DMY"}function p(n,i,t){if(n<1e3||n>9999||i<1||i>12||t<1||t>31)return null;let e=new Date(n,i-1,t);return e.getFullYear()!==n||e.getMonth()!==i-1||e.getDate()!==t?null:`${n}-${String(i).padStart(2,"0")}-${String(t).padStart(2,"0")}`}function b(n,i){let t=n.trim(),e;if(e=t.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/))return p(+e[1],+e[2],+e[3]);if(e=t.match(/^(\d{4})$/))return p(+e[1],1,1);if(e=t.match(/^(\d{1,2})[./](\d{4})$/))return p(+e[2],+e[1],1);if(e=t.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/))return p(+e[3],+e[2],+e[1]);if(e=t.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{4})$/)){let h=+e[1],c=+e[2],u=+e[3];return $(i)==="MDY"?p(u,h,c):p(u,c,h)}return null}function x(n,i){if(i)switch(n){case"date":return i.split("T")[0];case"time":return i.length===5?`${i}:00`:i;case"datetime":{let[t,e="00:00:00"]=i.split("T");return`${t} ${e.length===5?`${e}:00`:e}`}}}function k(n,i){if(typeof i!="string"||!i)return"";switch(n){case"date":return i.slice(0,10);case"time":return i.slice(0,5);case"datetime":{let[t,e="00:00:00"]=i.split(" ");return`${t}T${e.length===5?`${e}:00`:e}`}}}var r=class extends g{constructor(){super(...arguments);this.kind="date";this.label="";this.value="";this.clearable=!1;this.disabled=!1;this.required=!1;this.lang="en";this._typing=!1;this._typed="";this._typedInvalid=!1}_startTyping(){this._typed=this.value,this._typedInvalid=!1,this._typing=!0,this.updateComplete.then(()=>this.shadowRoot?.querySelector("input.typed")?.focus())}_commitTyped(){if(!this._typed.trim()){this._typing=!1;return}let t=b(this._typed,this.lang);if(!t){this._typedInvalid=!0;return}this._typing=!1,this._typedInvalid=!1,this._emit(t)}_onTypedKey(t){t.key==="Enter"?(t.preventDefault(),this._commitTyped()):t.key==="Escape"&&(t.preventDefault(),this._typing=!1,this._typedInvalid=!1)}_selector(){switch(this.kind){case"date":return{date:{}};case"time":return{time:{no_second:!0}};case"datetime":return{datetime:{}}}}_onSelectorChange(t){t.stopPropagation(),this._emit(k(this.kind,t.detail?.value))}_clear(){this._emit("")}_emit(t){t!==this.value&&(this.value=t,this.dispatchEvent(new CustomEvent("value-changed",{bubbles:!0,composed:!0,detail:{value:t}})))}render(){return s`
|
||||
<div class="field">
|
||||
${this.label?s`<span class="label">${this.label}${this.required?s`<span class="req">*</span>`:l}</span>`:l}
|
||||
<div class="row">
|
||||
${this._typing?s`<input class="typed" type="text" inputmode="numeric" autocomplete="off"
|
||||
.value=${this._typed}
|
||||
placeholder=${m("1978-03-15",this.lang)}
|
||||
aria-invalid=${this._typedInvalid?"true":"false"}
|
||||
@input=${t=>{this._typed=t.target.value,this._typedInvalid=!1}}
|
||||
@keydown=${this._onTypedKey}
|
||||
@blur=${this._commitTyped} />`:s`<ha-selector
|
||||
.hass=${this.hass}
|
||||
.selector=${this._selector()}
|
||||
.value=${x(this.kind,this.value)}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._onSelectorChange}
|
||||
></ha-selector>`}
|
||||
${this.kind==="date"&&!this.disabled&&!this._typing?s`<button type="button" class="type-toggle" title=${d("date_type_toggle",this.lang)} aria-label=${d("date_type_toggle",this.lang)} @click=${this._startTyping}>
|
||||
<ha-icon icon="mdi:keyboard-outline"></ha-icon>
|
||||
</button>`:l}
|
||||
${this.clearable&&this.value&&!this.disabled&&!this._typing?s`<button type="button" class="clear" title=${d("clear",this.lang)} aria-label=${d("clear",this.lang)} @click=${this._clear}>
|
||||
<ha-icon icon="mdi:close"></ha-icon>
|
||||
</button>`:l}
|
||||
</div>
|
||||
${this._typedInvalid?s`<span class="helper invalid">${d("date_type_invalid",this.lang)}</span>`:this.helper?s`<span class="helper">${this.helper}</span>`:l}
|
||||
</div>
|
||||
`}};r.styles=y`
|
||||
:host { display: block; }
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.label {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color, #888);
|
||||
font-weight: 500;
|
||||
}
|
||||
.req { color: var(--error-color, #f44336); margin-left: 2px; }
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
ha-selector {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.clear, .type-toggle {
|
||||
flex: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
--mdc-icon-size: 20px;
|
||||
}
|
||||
.clear:hover, .type-toggle:hover { background: rgba(var(--rgb-primary-text-color, 0, 0, 0), 0.08); }
|
||||
.clear:focus-visible, .type-toggle:focus-visible { outline: 2px solid var(--primary-color); }
|
||||
.helper {
|
||||
font-size: 11px;
|
||||
color: var(--secondary-text-color);
|
||||
font-style: italic;
|
||||
}
|
||||
.helper.invalid { color: var(--error-color, #f44336); font-style: normal; }
|
||||
input.typed {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 12px;
|
||||
border: 1px solid var(--divider-color, #ccc);
|
||||
border-radius: 4px;
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
font: inherit;
|
||||
}
|
||||
input.typed[aria-invalid="true"] { border-color: var(--error-color, #f44336); }
|
||||
input.typed:focus { outline: 2px solid var(--primary-color); outline-offset: -1px; }
|
||||
`,a([o({attribute:!1})],r.prototype,"hass",2),a([o()],r.prototype,"kind",2),a([o()],r.prototype,"label",2),a([o()],r.prototype,"value",2),a([o()],r.prototype,"helper",2),a([o({type:Boolean})],r.prototype,"clearable",2),a([o({type:Boolean})],r.prototype,"disabled",2),a([o({type:Boolean})],r.prototype,"required",2),a([o()],r.prototype,"lang",2),a([f()],r.prototype,"_typing",2),a([f()],r.prototype,"_typed",2),a([f()],r.prototype,"_typedInvalid",2);customElements.get("ms-date-field")||customElements.define("ms-date-field",r);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user