291 files

This commit is contained in:
Home Assistant Version Control
2026-10-14 18:10:18 +00:00
parent c4e0178d8a
commit 280501d2e8
291 changed files with 32540 additions and 3280 deletions
@@ -0,0 +1,300 @@
/*! maintenance_supporter frontend 2.103.0 */
import{e as b}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-DPJSGXJS.js";import{d as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-2BQZ33DM.js";import{a as x,b as w,c as $}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-QVOLUT24.js";import{a as m}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3LDXEWBF.js";import{I as y,a as l,b as u,c as n,f as p,h as g,m as v,n as d,t as i,v as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";var j=3,o=class extends g{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._summaryOpen=!0;this._localeReady=!1;this._userService=null;this._toggle=e=>{let s=new Set(this._selected);s.has(e)?s.delete(e):s.add(e),this._selected=s};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)return;this._error="";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})),s=await m(this,{type:"maintenance_supporter/problem_sensors/adopt",selections:e},{busy:t=>{this._adopting=t},onError:t=>{this._error=t}});s!==void 0&&(this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:s})),this._open=!1)}}get _lang(){return y(this.hass)}updated(e){e.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,f(this._lang).then(()=>this.requestUpdate())),e.has("_open")&&this._open&&$(this.shadowRoot)}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 b(this.hass);let[e,s,t]=await Promise.all([m(this,{type:"maintenance_supporter/problem_sensors/discover"},{onError:r=>{this._error=r}}),this._userService.getUsers().catch(()=>[]),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}).catch(()=>({objects:[]}))]);this._loading=!1,e!==void 0&&(this._objects=(t?.objects||[]).filter(r=>!r.object.archived_at).map(r=>({entry_id:r.entry_id,name:r.object.name,tasks:(r.tasks||[]).length})),this._sensors=e?.sensors||[],this._covered=Object.fromEntries(this._sensors.map(r=>[r.entity_id,r.covered_by??null])),this._selected=new Set(this._sensors.filter(r=>!r.covered_by).map(r=>r.entity_id)),this._users=s,this._summaryOpen=this._summaryGroups().length<=j)}_defaultName(e){return e.suggested_entry_id?e.suggested_object_name:e.candidate?.name??e.suggested_object_name}_effectiveName(e){return(this._objectNames[e.entity_id]??"").trim()||this._defaultName(e)}_existingEntryFor(e){let s=this._effectiveName(e),t=e.suggested_entry_id||e.target_entry_id||null;if(s===this._defaultName(e)&&t)return t;let r=this._objects.find(a=>a.name.trim().toLowerCase()===s.toLowerCase());return r?r.entry_id:null}_schedulePreview(){clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._preview()},350)}async _preview(){let e=this._sensors.map(t=>({entity_id:t.entity_id,name:t.name,entry_id:this._existingEntryFor(t)})),s=await m(this,{type:"maintenance_supporter/problem_sensors/preview",selections:e},{onError:()=>{}});s?.covered&&(this._covered={...this._covered,...s.covered})}_summaryGroups(){let e=new Map;for(let s of this._sensors){if(!this._selected.has(s.entity_id))continue;let t=this._existingEntryFor(s),r=this._effectiveName(s),a=t??`new:${r.toLowerCase()}`,c=e.get(a)??{name:r,entry:t,adding:0};c.adding+=1,e.set(a,c)}return[...e.values()]}_summary(e){let s=this._summaryGroups();if(s.length===0)return p;let t=s.filter(a=>!a.entry).length,r=i(t?"adopt_summary_totals_new":"adopt_summary_totals",e).replace("{tasks}",String(s.reduce((a,c)=>a+c.adding,0))).replace("{objects}",String(s.length)).replace("{new}",String(t));return n`
<details
class="summary"
.open=${this._summaryOpen}
@toggle=${a=>{this._summaryOpen=a.target.open}}
>
<summary>
<span class="summary-title">${i("adopt_summary_title",e)}</span>
<span class="summary-totals">${r}</span>
</summary>
${s.map(a=>{if(!a.entry)return n`<div class="summary-line new">
${i("setups_new_object_count",e).replace("{name}",a.name).replace("{count}",String(a.adding))}
</div>`;let c=this._objects.find(_=>_.entry_id===a.entry)?.tasks??0;return n`<div class="summary-line">
${i("adopt_summary_existing",e).replace("{name}",a.name).replace("{before}",String(c)).replace("{after}",String(c+a.adding))}
</div>`})}
</details>
`}_close(){clearTimeout(this._previewTimer),this._open=!1}_targetLabel(e,s){if(e.candidate&&this._existingEntryFor(e)===e.candidate.entry_id){let t=e.candidate.reasons.map(r=>i(`setups_reason_${r}`,s)).join(", ");return i("setups_target_match",s).replace("{name}",e.candidate.name).replace("{reasons}",t)}return this._effectiveName(e)}render(){if(!this._open)return n``;let e=this._lang,s=this._sensors.length>0&&this._selected.size===this._sensors.length;return w(()=>this._close(),n`
<div class="title">${i("adopt_problem_title",e)}</div>
${this._error?n`<div class="error">${this._error}</div>`:p}
<div class="dialog-scroll">
<div class="hint">${i("adopt_problem_hint",e)}</div>
${this._loading?n`<div class="loading">…</div>`:this._sensors.length===0?n`<div class="empty">${i("adopt_problem_none",e)}</div>`:n`
<label class="select-all">
<input
type="checkbox"
.checked=${s}
@change=${this._toggleAll}
/>
<span>${i("selected",e)}: ${this._selected.size} / ${this._sensors.length}</span>
</label>
<div class="row-sub adopt-object-hint">${i("adopt_object_hint",e)}</div>
<datalist id="adopt-object-names">
${this._objects.map(t=>n`<option value=${t.name}></option>`)}
</datalist>
<div class="list">
${this._sensors.map(t=>{let r=this._selected.has(t.entity_id),a=t.state==="on",c=[t.device_name,t.area_name].filter(Boolean).join(" \xB7 ");return n`
<label class="row">
<input
type="checkbox"
.checked=${r}
@change=${()=>this._toggle(t.entity_id)}
/>
<div class="row-main">
<div class="row-top">
<span class="row-name">${t.name}</span>
<span class="chip ${a?"chip-active":"chip-ok"}">
${a?i("adopt_problem_active",e):i("adopt_problem_ok",e)}
</span>
</div>
${c?n`<div class="row-sub">${c}</div>`:p}
${r?n`<div class="row-object" @click=${_=>_.stopPropagation()}>
<input
class="adopt-object"
list="adopt-object-names"
aria-label=${i("object",e)}
placeholder=${this._defaultName(t)}
.value=${this._objectNames[t.entity_id]??this._defaultName(t)}
@input=${_=>{this._objectNames={...this._objectNames,[t.entity_id]:_.target.value},this._schedulePreview()}}
/>
</div>`:p}
<div class="row-target">
→ ${this._targetLabel(t,e)}${this._existingEntryFor(t)?p:n` <span class="new-tag">${i("adopt_problem_new_object",e)}</span>`}
</div>
${this._covered[t.entity_id]?n`<div class="maybe">
${i("setups_maybe_covered",e).replace("{name}",this._covered[t.entity_id].name)}
</div>`:p}
${t.suggested_part_name?n`<div class="row-part">
<ha-icon icon="mdi:package-variant-closed"></ha-icon>
${i("adopt_problem_part",e).replace("{name}",t.suggested_part_name)}
</div>`:p}
</div>
</label>
`})}
</div>
`}
${!this._loading&&this._sensors.length>0?this._summary(e):p}
${!this._loading&&this._sensors.length>0?n`
<label class="responsible">
<span>${i("for_at_least_minutes",e)}</span>
<input
class="for-input"
type="number"
min=${h[0]}
max=${h[1]}
.value=${this._forMinutes}
@input=${t=>this._forMinutes=t.target.value}
/>
</label>
<div class="for-hint">${i("adopt_for_minutes_hint",e)}</div>
`:p}
${!this._loading&&this._sensors.length>0&&this._users.length>0?n`
<label class="responsible">
<span>${i("adopt_problem_responsible",e)}</span>
<select
.value=${this._responsible}
@change=${t=>{this._responsible=t.target.value}}
>
<option value="" ?selected=${!this._responsible}>${i("no_user_assigned",e)}</option>
${this._users.map(t=>n`<option value=${t.id} ?selected=${t.id===this._responsible}>${t.name}</option>`)}
</select>
</label>
`:p}
</div>
<div class="actions">
<ha-button appearance="plain" @click=${this._close}>
${i("cancel",e)}
</ha-button>
<ha-button
@click=${this._adopt}
.disabled=${this._selected.size===0||this._adopting}
>
${i("adopt_problem_adopt",e)}
</ha-button>
</div>
`)}};o.styles=[x,u`
:host {
--ms-modal-max-width: 560px;
--ms-modal-max-height: 90dvh;
--ms-modal-gap: 12px;
--ms-modal-z: 1000;
}
.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;
}
/* Stays at the top of the scrolling middle: the count while ticking.
top: -1px + 1px more padding: on a 3x screen a fractional offset let a
line of the text scrolled under it show above it. */
.select-all {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--secondary-text-color);
cursor: pointer;
position: sticky;
top: -1px;
z-index: 1;
padding: 5px 0 4px;
background: var(--card-background-color, var(--ha-card-background, #1c1c1c));
}
.select-all input {
cursor: pointer;
}
.list {
display: flex;
flex-direction: column;
gap: 6px;
}
.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 > summary {
cursor: pointer;
margin-bottom: 2px;
}
.summary-title {
font-weight: 500;
}
.summary-totals {
color: var(--secondary-text-color);
}
.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;
}
`],l([v({attribute:!1})],o.prototype,"hass",2),l([d()],o.prototype,"_open",2),l([d()],o.prototype,"_loading",2),l([d()],o.prototype,"_adopting",2),l([d()],o.prototype,"_error",2),l([d()],o.prototype,"_sensors",2),l([d()],o.prototype,"_selected",2),l([d()],o.prototype,"_objectNames",2),l([d()],o.prototype,"_objects",2),l([d()],o.prototype,"_covered",2),l([d()],o.prototype,"_users",2),l([d()],o.prototype,"_responsible",2),l([d()],o.prototype,"_forMinutes",2),l([d()],o.prototype,"_summaryOpen",2);customElements.get("maintenance-adopt-problem-sensors-dialog")||customElements.define("maintenance-adopt-problem-sensors-dialog",o);export{o as MaintenanceAdoptProblemSensorsDialog};
@@ -0,0 +1,279 @@
/*! maintenance_supporter frontend 2.103.0 */
import{a as X}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-H44SM3WA.js";import{a as o}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-274MLN75.js";import{f as E,g as M,h as z,i as Q,j as U,k as Y,l as q,m as G,n as R,o as A,p as W}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-FH35SLMS.js";import{a as B}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-KZ3OKTLK.js";import{e as P,f as K}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-DR2AFVDA.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-EOMYEBQ4.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-MMIPM7EE.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-R77C34J4.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-IRRUA3DS.js";import{C as S,I as F,L as g,M as v,P as N,Q as I,V as w,X as T,a as $,c as p,f as y,h as H,m as k,n as j,o as C,r as D,t as a,v as L,z as O}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";function J(b,i,t,s){let l=b.entries.filter(e=>e.type==="completed").sort((e,n)=>e.ts-n.ts),d=`${b.from?t.date(b.from):"\u2026"} \u2013 ${b.to?t.date(b.to):"\u2026"}`,r=b.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=b.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(""),f=b.buckets.buckets.reduce((e,n)=>Math.max(e,n.cost),0),c=b.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">${n>0?`<span class="bar" style="width:${n}%"></span>`:""}</td>
<td class="num">${o(t.cost(e.cost))}</td>
</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(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(i.title)} \u2014 ${o(b.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; 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; }
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(i.title)} \u2014 ${o(b.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(i.costPerObject)}</h2>
<table>
<thead><tr>
<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>${u||`<tr><td colspan="4">${o(i.none)}</td></tr>`}</tbody>
<tfoot><tr>
<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(i.costPerBucket)}</h2>
<table>
<thead><tr>
<th>${o(i.period)}</th><th class="num">${o(i.completions)}</th>
<th></th><th class="num">${o(i.colCost)}</th>
</tr></thead>
<tbody>${c||`<tr><td colspan="4">${o(i.none)}</td></tr>`}</tbody>
</table>
<h2>${o(i.historyHeading)} (${o(t.number(l.length))})</h2>
<table>
<thead><tr>
<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>
${m||`<tr><td colspan="6">${o(i.none)}</td></tr>`}
</tbody>
<tfoot><tr>
<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>
${b.capped?`<p class="cap-note">${o(i.capNote)}</p>`:""}
</body>
</html>`}var Z=25,V=5,tt={completed:C.ok,missed:C.overdue,reset:C.paused,skipped:"var(--secondary-text-color)"},_=class extends H{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 X(this);this._localeReady=!1;this._filtersFor=null;this._memo=null}createRenderRoot(){return this}get _lang(){return F(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,L(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((d,r)=>d===s[r]))return this._memo.entries;let l=Q(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=U(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:Y(d),buckets:G(d,l),byObject:q(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):`${T(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=K();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?B(n.notes,s):null})),e=J({areaName:E(this.areaKey,this.hass?.areas,a("no_area",s)),from:this._from||S(c[c.length-1]?.timestamp)||null,to:this._to||O(),totals:r,byObject:u,buckets:h,entries:m,capped:this._histories.capped},f,{date:n=>N(n,s),cost:n=>v(n,l,s),duration:n=>w(n,s),bucket:n=>this._bucketLabel(n,"long"),share:n=>`${g(n*100,s,0)} %`,number:n=>g(n,s)},new Date().toISOString());P(e,t)}_renderRangeChips(t){let s=this._lang,l=O(),d=R(l),r=u=>this._from===u.from&&this._to===u.to,h=W(t,Number(l.slice(0,4))).slice(0,V);return p`
<div class="filter-chips area-range-chips">
<button class="filter-chip ${r(d)?"active":""}" @click=${()=>this._setRange(R())}>
${a("area_range_12m",s)}
</button>
${h.map(u=>p`
<button class="filter-chip ${r(A(u))?"active":""}" @click=${()=>this._setRange(A(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,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">${v(d,l,s)}</div>
<div class="area-chart-bars">
${r<0?p`<div class="area-chart-zero" style="bottom: ${h(-r)}%"></div>`:y}
${t.map(c=>{let m=`${this._bucketLabel(c,"long")}: ${v(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>`:y}
</div>`})}
</div>
${r<0?p`<div class="area-chart-min">${v(r,l,s)}</div>`:y}
<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`${T(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: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,Z);return p`
<div class="detail-section area-view">
<div class="detail-header">
<h2 class="area-title">
<ha-icon .icon=${M(this.areaKey,this.hass.areas)}></ha-icon>
${E(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",l.length)} ·
${this._countText("templates_task_count","templates_task_count_one",c)}
${this._histories.loading?p` · <span class="area-loading">${a("loading",t)}</span>`:y}
</p>
${this._renderRangeChips(d)}
<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>
${l.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">${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">${v(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?v(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>${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">
<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>
${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">${g(e.completions,t)}</td>
<td class="num">${v(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>
${g(e.share*100,t,0)} %
</td>
</tr>
`)}
</tbody>
</table>
</div>
<h3>
${a("area_history_section",t)}
<span class="area-count">${g(r.length,t)}</span>
</h3>
${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>
<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>
${m.map(e=>{let n=e.notes?B(e.notes,t):"";return p`
<tr class="objects-table-row area-history-row" @click=${()=>this._openTask(e.entryId,e.taskId)}>
<td title=${I(e.timestamp,t)}>${N(e.timestamp,t)}</td>
<td>
<span class="area-entry-type" style="color: ${tt[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?v(e.cost,s,t):"\u2014"}</td>
<td class="num">${e.duration!=null?w(e.duration,t):"\u2014"}</td>
<td class="oc-notes" title=${n}>${n||"\u2014"}</td>
</tr>
`})}
</tbody>
</table>
</div>
${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>`:y}
`}
${this._histories.capped?p`<p class="area-cap-note">${a("object_history_cap_note",t)}</p>`:y}
</div>
`}};$([k({attribute:!1})],_.prototype,"hass",2),$([k()],_.prototype,"areaKey",2),$([k({attribute:!1})],_.prototype,"objects",2),$([k({type:Boolean})],_.prototype,"showArchived",2),$([k()],_.prototype,"currencySymbol",2),$([k({attribute:!1})],_.prototype,"userName",2),$([j()],_.prototype,"_from",2),$([j()],_.prototype,"_to",2),$([j()],_.prototype,"_entryFilter",2),$([j()],_.prototype,"_taskQuery",2),$([j()],_.prototype,"_expanded",2),$([j()],_.prototype,"_printing",2);customElements.get("maintenance-area-view")||customElements.define("maintenance-area-view",_);export{_ as MaintenanceAreaView};
@@ -0,0 +1,54 @@
/*! maintenance_supporter frontend 2.103.0 */
import{a as O}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-H44SM3WA.js";import{q as j,r as E,s as L,t as R,u as C}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-FH35SLMS.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-EOMYEBQ4.js";import{a as _,b as S,c as A}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-VZUMUVFP.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-MMIPM7EE.js";import{I as g,L as k,M as v,P as w,a as l,c as a,f as m,h as y,m as p,n as b,t as r,v as $,z as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";var q=[{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=E(S(_.areaSort));this._histories=new O(this);this._localeReady=!1;this._memo=null}createRenderRoot(){return this}get _lang(){return g(this.hass)}willUpdate(t){if(super.willUpdate(t),t.has("objects")||t.has("showArchived")||t.has("hass")&&t.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(t){super.updated(t),!this._localeReady&&this.hass&&(this._localeReady=!0,$(this._lang).then(()=>this.requestUpdate()))}_rows(t){let o=this._lang,s=[this.objects,this.showArchived,this._histories.version,this.hass?.areas,o,t];if(this._memo&&this._memo.deps.every((c,h)=>c===s[h]))return this._memo.rows;let n=j(this.objects,{showArchived:this.showArchived,areas:this.hass?.areas,noAreaLabel:r("no_area",o),year:t,historyOf:(c,h)=>this._histories.historyOf(c,h)});return this._memo={deps:s,rows:n},n}_setSort(t){this._sort=L(this._sort,t),A(_.areaSort,`${this._sort.key}:${this._sort.dir}`)}_open(t){this.dispatchEvent(new CustomEvent("open-area",{detail:{areaKey:t},bubbles:!0,composed:!0}))}_toggleArchived(){this.dispatchEvent(new CustomEvent("archived-toggle",{bubbles:!0,composed:!0}))}_headerLabel(t,o,s){return t==="area_cost_year"?r("area_cost_year",s).replace("{year}",String(o)):r(t,s)}render(){if(!this.hass)return m;let t=this._lang,o=Number(f().slice(0,4)),s=this.currencySymbol,n=this._rows(o),c=R(C(n,this._query),this._sort),h=this.objects.filter(e=>e.object.archived).length,d=e=>k(e,t);return a`
<div class="filter-bar area-filter-bar">
<input
type="search"
class="search-input"
aria-label=${r("areas_filter",t)}
placeholder=${r("areas_filter",t)}
.value=${this._query}
@input=${e=>{this._query=e.target.value}}
/>
${this._histories.loading?a`<span class="area-loading">${r("loading",t)}</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",t):`${r("show_archived",t)} (${h})`}
</ha-button>`:m}
</div>
${n.length===0?a`<div class="empty-state">${r("no_objects",t)}</div>`:c.length===0?a`<div class="empty-state">${r("areas_filter_none",t)}</div>`:a`
<div class="objects-table-wrap">
<table class="objects-table areas-table">
<thead>
<tr>
${q.map(e=>{let u=this._sort.key===e.key;return a`<th class=${e.num?"num":""}
aria-sort=${u?this._sort.dir==="asc"?"ascending":"descending":"none"}>
<button class="area-sort ${u?"active":""}" title=${r("sort_label",t)} @click=${()=>this._setSort(e.key)}>
${this._headerLabel(e.label,o,t)}
${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(e=>a`
<tr class="objects-table-row" tabindex="0"
@click=${()=>this._open(e.key)}
@keydown=${u=>{u.key==="Enter"&&this._open(e.key)}}>
<td>
<ha-icon class="area-row-icon" .icon=${e.icon}></ha-icon>
<span class="objects-table-name">${e.name}</span>
</td>
<td class="num">${d(e.objects)}</td>
<td class="num">${d(e.tasks)}</td>
<td class="num">${e.overdue?a`<span class="area-count overdue">${d(e.overdue)}</span>`:d(0)}</td>
<td class="num">${e.dueSoon?a`<span class="area-count due-soon">${d(e.dueSoon)}</span>`:d(0)}</td>
<td class="num">${v(e.costYear,s,t)}</td>
<td class="num">${v(e.costTotal,s,t)}</td>
<td>${e.lastCompletion?w(e.lastCompletion,t):"\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};
@@ -0,0 +1,113 @@
/*! maintenance_supporter frontend 2.103.0 */
import"/maintenance_supporter_panelfiles/panel-chunks/chunk-5TWB2KD3.js";import{I as u,a as n,b as _,c as o,f as p,h as c,m as g,n as a,t as s}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.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,2 @@
/*! maintenance_supporter frontend 2.103.0 */
function r(t){return String(t??"").replace(/[&<>"']/g,n=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[n])}export{r as a};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.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],u=[1,1e4],a=[0,365],d=15,s=[.1,.9],b=[0,365],A=[.01,1e4],E=[.01,1e5],i=[.1,5];export{n as a,o as b,t as c,_ as d,u as e,a as f,d as g,s as h,b as i,A as j,E as k,i as l};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,256 @@
/*! maintenance_supporter frontend 2.103.0 */
import{a as p}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3LDXEWBF.js";import{I as v,a as r,b as _,c as l,f as o,h,m as c,n,t as i}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";var s=class extends h{constructor(){super(...arguments);this.objects=[];this.placesEnabled=!1;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._place="";this._remindOnSite=!1;this._entryId=null;this._replacing=!1;this._oldDeviceId="";this._replaceDevice="keep"}get _lang(){return v(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._place="",this._remindOnSite=!1,this._replacing=!1,this._error="",this._open=!0}openReplace(e,a){this._entryId=e,this._replacing=!0,this._name=a.name||"",this._oldDeviceId=a.ha_device_id||"",this._haDeviceId="",this._replaceDevice="keep",this._error="",this._open=!0}openEdit(e,a){this._entryId=e,this._name=a.name||"",this._manufacturer=a.manufacturer||"",this._model=a.model||"",this._serialNumber=a.serial_number||"",this._areaId=a.area_id||"",this._installationDate=a.installation_date||"",this._warrantyExpiry=a.warranty_expiry||"",this._documentationUrl=a.documentation_url||"",this._notes=a.notes||"",this._haDeviceId=a.ha_device_id||"",this._parentEntryId=a.parent_entry_id||"",this._place=a.place||"",this._remindOnSite=!!a.remind_on_site,this._replacing=!1,this._error="",this._open=!0}async _replace(){if(this._loading||!this._entryId)return;let e={type:"maintenance_supporter/object/replace",entry_id:this._entryId,name:this._name.trim()||null};this._oldDeviceId?(this._replaceDevice==="other"&&(e.ha_device_id=this._haDeviceId),this._replaceDevice==="none"&&(e.ha_device_id=null)):this._haDeviceId&&(e.ha_device_id=this._haDeviceId);let a=await p(this,e,{busy:t=>{this._loading=t},fallbackKey:"save_error",onError:t=>{this._error=t}});a!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-replaced",{detail:{entry_id:a?.entry_id,device_swap:a?.device_swap}})))}_deviceName(e){let t=this.hass?.devices?.[e];return t?.name_by_user||t?.name||e}_deviceSelector(e){return l`<ha-form
.hass=${this.hass}
.data=${{device:this._haDeviceId||void 0}}
.schema=${[{name:"device",selector:{device:{}}}]}
.computeLabel=${()=>e}
@value-changed=${a=>this._haDeviceId=a.detail.value?.device||""}
></ha-form>`}_renderReplace(){let e=this._lang,a=(d,u)=>l`<label class="radio-row">
<input
type="radio"
name="replace-device"
.checked=${this._replaceDevice===d}
@change=${()=>{this._replaceDevice=d}}
/>
<span>${u}</span>
</label>`,t=this._replaceDevice==="other"&&!this._haDeviceId;return l`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${i("replace_object",e)}</div>
<div class="content">
${this._error?l`<div class="error">${this._error}</div>`:o}
<div class="hint">${i("replace_object_prompt",e)}</div>
<ms-textfield
label="${i("replace_name_label",e)}"
.value=${this._name}
@input=${d=>this._name=d.target.value}
></ms-textfield>
${this._oldDeviceId?l`<div class="radio-group" role="radiogroup" aria-label=${i("replace_device_heading",e)}>
<div class="textarea-label">${i("replace_device_heading",e)}</div>
${a("keep",i("replace_device_keep",e).replace("{device}",this._deviceName(this._oldDeviceId)))}
${this._replaceDevice==="keep"?l`<div class="hint">${i("replace_device_keep_hint",e)}</div>`:o}
${a("other",i("replace_device_other",e))}
${this._replaceDevice==="other"?l`${this._deviceSelector(i("replace_device_pick",e))}
<div class="hint">${i("replace_device_other_hint",e)}</div>`:o}
${a("none",i("replace_device_none",e))}
</div>`:this._deviceSelector(i("link_device_optional",e))}
</div>
<div class="dialog-actions">
<ha-button appearance="plain" @click=${this._close}>
${i("cancel",e)}
</ha-button>
<ha-button @click=${this._replace} .disabled=${this._loading||t}>
${this._loading?i("saving",e):i("replace_object",e)}
</ha-button>
</div>
</ha-dialog>
`}_renderPlace(e){let a=!!this._place&&!this.hass?.states?.[this._place];return l`
<ha-form
.hass=${this.hass}
.data=${{place:this._place||void 0}}
.schema=${[{name:"place",selector:{entity:{domain:"zone"}}}]}
.computeLabel=${()=>i("place_optional",e)}
@value-changed=${t=>{this._place=t.detail.value?.place||"",this._place||(this._remindOnSite=!1)}}
></ha-form>
${a?l`<div class="place-missing">${i("place_missing",e)}</div>`:o}
<label class="place-on-site ${this._place?"":"disabled"}">
<input
type="checkbox"
.checked=${this._remindOnSite}
?disabled=${!this._place}
@change=${t=>this._remindOnSite=t.target.checked}
/>
<span>${i("remind_on_site",e)}</span>
</label>
${this._place?l`<div class="place-hint">${i("remind_on_site_hint",e)}</div>`:o}
`}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,...this.placesEnabled?{place:this._place||null,remind_on_site:!!this._place&&this._remindOnSite}:{}},a=await p(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}});a!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-saved",{detail:{device_swap:a?.device_swap}})))}_parentChoices(){return(this.objects||[]).filter(e=>e.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return l``;if(this._replacing)return this._renderReplace();let e=this._lang,a=this._entryId?i("edit_object",e):i("new_object",e);return l`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${a}</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>
${this.placesEnabled?this._renderPlace(e):o}
<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>
`}};s.styles=_`
.place-on-site {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--primary-text-color);
}
.place-on-site.disabled {
color: var(--disabled-text-color, var(--secondary-text-color));
}
.place-hint,
.place-missing {
font-size: 12px;
color: var(--secondary-text-color);
margin-top: -4px;
}
.place-missing {
color: var(--warning-color, #c77700);
}
.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;
}
.hint {
font-size: 13px;
color: var(--secondary-text-color);
line-height: 1.4;
}
.radio-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.radio-row {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
cursor: pointer;
}
.radio-row input {
margin: 0;
}
`,r([c({attribute:!1})],s.prototype,"hass",2),r([c({attribute:!1})],s.prototype,"objects",2),r([c({type:Boolean})],s.prototype,"placesEnabled",2),r([n()],s.prototype,"_open",2),r([n()],s.prototype,"_loading",2),r([n()],s.prototype,"_error",2),r([n()],s.prototype,"_name",2),r([n()],s.prototype,"_manufacturer",2),r([n()],s.prototype,"_model",2),r([n()],s.prototype,"_serialNumber",2),r([n()],s.prototype,"_areaId",2),r([n()],s.prototype,"_installationDate",2),r([n()],s.prototype,"_warrantyExpiry",2),r([n()],s.prototype,"_documentationUrl",2),r([n()],s.prototype,"_notes",2),r([n()],s.prototype,"_haDeviceId",2),r([n()],s.prototype,"_parentEntryId",2),r([n()],s.prototype,"_place",2),r([n()],s.prototype,"_remindOnSite",2),r([n()],s.prototype,"_entryId",2),r([n()],s.prototype,"_replacing",2),r([n()],s.prototype,"_oldDeviceId",2),r([n()],s.prototype,"_replaceDevice",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",s);export{s as a};
@@ -0,0 +1,79 @@
/*! maintenance_supporter frontend 2.103.0 */
import{a as r,b as l,c as i,f as o,h as n,m as t}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.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`
<textarea
.value=${this.value??""}
rows=${this.rows}
?required=${this.required}
?disabled=${this.disabled}
placeholder=${this.placeholder}
@input=${this._onInput}
@change=${this._onInput}
></textarea>`:i`
<input
.value=${this.value??""}
.type=${this.type}
?required=${this.required}
?disabled=${this.disabled}
placeholder=${this.placeholder}
step=${this.step??o}
min=${this.min??o}
max=${this.max??o}
pattern=${this.pattern??o}
@input=${this._onInput}
@change=${this._onInput}
/>`}
${this.helper?i`<span class="helper">${this.helper}</span>`:o}
</label>
`}};e.styles=l`
: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; }
input {
padding: 8px 10px;
font-size: 14px;
background: var(--secondary-background-color, rgba(0,0,0,0.06));
color: var(--primary-text-color);
border: 1px solid var(--divider-color, rgba(255,255,255,0.12));
border-radius: 6px;
font-family: inherit;
width: 100%;
box-sizing: border-box;
outline: none;
}
input:focus {
border-color: var(--primary-color);
}
input:disabled { opacity: 0.5; cursor: not-allowed; }
textarea {
padding: 8px 10px;
font-size: 14px;
background: var(--secondary-background-color, rgba(0,0,0,0.06));
color: var(--primary-text-color);
border: 1px solid var(--divider-color, rgba(255,255,255,0.12));
border-radius: 6px;
font-family: inherit;
width: 100%;
box-sizing: border-box;
outline: none;
resize: vertical;
}
textarea:focus { border-color: var(--primary-color); }
textarea:disabled { opacity: 0.5; cursor: not-allowed; }
.helper {
font-size: 11px;
color: var(--secondary-text-color);
font-style: italic;
}
`,r([t()],e.prototype,"label",2),r([t()],e.prototype,"value",2),r([t()],e.prototype,"placeholder",2),r([t()],e.prototype,"type",2),r([t({type:Boolean})],e.prototype,"required",2),r([t({type:Boolean})],e.prototype,"disabled",2),r([t()],e.prototype,"step",2),r([t()],e.prototype,"min",2),r([t()],e.prototype,"max",2),r([t()],e.prototype,"pattern",2),r([t()],e.prototype,"helper",2),r([t({type:Boolean})],e.prototype,"multiline",2),r([t({type:Number})],e.prototype,"rows",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",e);
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{c as r,f as i}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.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};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{b as s}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-IRRUA3DS.js";async function a(o,n,t=300){return(await o.connection.sendMessagePromise({type:"auth/sign_path",path:n,expires:t})).path}async function r(o,n,t=300){return a(o,`/api/maintenance_supporter/document/${n}`,t)}async function p(o,n,t=""){let e=window.open("about:blank","_blank");try{let i=await r(o,n);e&&(e.location.href=new URL(i+t,window.location.origin).href)}catch(i){throw e&&e.close(),i}}async function l(o,n,t){s(await r(o,n,30),t)}function m(o,n=null){let t=URL.createObjectURL(new Blob([o],{type:"text/html"}));n&&!n.closed?n.location.href=t:window.open(t,"_blank"),setTimeout(()=>URL.revokeObjectURL(t),6e4)}function u(){return window.open("about:blank","_blank")}export{a,r as b,p as c,l as d,m as e,u as f};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
var E={\u00DF:"s","\u1E9E":"s",\u00E6:"a",\u0153:"o",\u00F8:"o",\u0111:"d",\u00F0:"d",\u00FE:"t",\u0142:"l",\u0131:"i"},d=10,R=7,g=4,h=2,S=5,x=3,_=/[a-z0-9]+/g;function u(r){let e="";for(let t of r.toLowerCase()){if(t.charCodeAt(0)<128&&t.length===1){e+=t;continue}let n=E[t];if(n){e+=n;continue}let o=t.normalize("NFKD")[0]||t;e+=o.charCodeAt(0)<128?o:t}return e}function m(r){return u(r).match(_)||[]}function C(r){return u(r).replace(/[^a-z0-9]+/g,"")}function b(r){return r.replace(/ue/g,"u").replace(/oe/g,"o").replace(/ae/g,"a").replace(/ss/g,"s")}function F(r){let e=[];for(let t of m(r)){let n=[t],o=b(t);o&&o!==t&&n.push(o),e.push(n)}return e}function p(r,e){if(r===e)return!0;let t=r.length,n=e.length;if(Math.abs(t-n)>1)return!1;if(t===n){let l=[];for(let i=0;i<t;i++)r[i]!==e[i]&&l.push(i);if(l.length===1)return!0;if(l.length===2&&l[1]===l[0]+1){let i=l[0];return r[i]===e[i+1]&&r[i+1]===e[i]}return!1}let[o,s]=t>n?[r,e]:[e,r],c=0,f=0,a=!1;for(;c<o.length&&f<s.length;)if(o[c]===s[f])c++,f++;else{if(a)return!1;a=!0,c++}return!0}function N(r,e){let t=0;for(let n of r)if(n){if(e===n)return d;if(e.startsWith(n)){t=Math.max(t,R);continue}if(n.length>=x&&e.includes(n)){t=Math.max(t,g);continue}n.length>=S&&t<h&&(p(n,e)||e.length>n.length&&p(n,e.slice(0,n.length)))&&(t=h)}return t}function I(r,e){let t=0;for(let n of m(e))if(t=Math.max(t,N(r,n)),t===d)return t;if(t<g){let n=C(e);for(let o of r)if(o.length>=x&&n.includes(o))return g}return t}function O(r,e){if(!r)return!1;let t=F(e);if(!t.length){let n=u(e.trim());return n.length>0&&u(r).includes(n)}return M(t,[{text:r,weight:1}])>0}function M(r,e){if(!r.length)return 0;let t=e.filter(o=>o.text),n=0;for(let o of r){let s=0;for(let c of t){let f=I(o,c.text)*c.weight;f>s&&(s=f)}if(s===0)return 0;n+=s}return n}export{F as a,O as b,M as c};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,37 @@
/*! maintenance_supporter frontend 2.103.0 */
import{B as k,L as R,N as x,a as m,b as h,ba as w,c as g,f as v,h as b,m as y,n as _,t as l}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";function E(i){return`${i.entry_id??""}\0${i.part_id}`}function S(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 N(i,t,e,o){let{part:n,label:r}=S(i,t,e,o),s=n&&n.stock!==null&&n.stock!==void 0?` (${x(n.stock,n.unit,o)})`:"",a=n?.storage_location?` \u2014 ${n.storage_location}`:"";return`${R(i.quantity,o)}\xD7 ${r}${s}${a}`}function V(i,t,e,o){let r=(e.find(a=>a.entry_id===t)?.parts||[]).map(a=>({...a})),s=new Set(r.map(a=>E({part_id:a.id})));for(let a of i?.consumes_parts||[]){if(!a.entry_id||a.entry_id===t)continue;let c=E(a);if(s.has(c))continue;s.add(c);let{part:u,ownerName:p}=S(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}var L=20;function T(){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 P(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 M(i){return P(i??[]).map(t=>({timestamp:t.timestamp,values:f(t)})).filter(t=>t.values.length>0)}function A(i,t,e){let o;for(let n of i){if(e!==void 0){let s=k(n.timestamp);if(!isNaN(s)&&s>=e)break}let r=n.values.find(s=>s.id===t);r&&(o=r)}return o}function C(i,t,e){let o=f(t).find(r=>r.id===e);if(!o)return null;let n=null;for(let r of P(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 D(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 F(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>=L))break}return t}function j(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 B=["notes","cost","duration","photo","user"],I={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=[w,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{E as a,N as b,V as c,L as d,T as e,f,M as g,A as h,C as i,D as j,F as k,j as l,B as m,I as n};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{t as a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";var _=[{key:"name",labelKey:"name",required:!0},{key:"ref_no",labelKey:"ref_number"},{key:"manufacturer",labelKey:"manufacturer"},{key:"model",labelKey:"model"},{key:"serial_number",labelKey:"serial_number_label"},{key:"installation_date",labelKey:"installed"},{key:"warranty_expiry",labelKey:"warranty"},{key:"area_id",labelKey:"area"},{key:"documentation_url",labelKey:"documentation_url_label"},{key:"notes",labelKey:"object_notes_label"},{key:"task_count",labelKey:"tasks"},{key:"actions",labelKey:"actions"}],u=_.map(e=>e.key),i=["name","manufacturer","model","serial_number","installation_date","warranty_expiry","area_id","task_count","actions"];function g(e){if(!Array.isArray(e))return[...i];let n=new Set,r=[];for(let t of e)typeof t=="string"&&u.includes(t)&&!n.has(t)&&(n.add(t),r.push(t));return r.length?(r.includes("name")||r.unshift("name"),r):[...i]}function f(e,n){return a(`home_dwelling_${e==="house"||e==="apartment"?e:"unknown"}`,n)}function m(e,n){if(!e)return"";try{return new Intl.DisplayNames([n],{type:"region"}).of(e)||e}catch{return e}}var l={floors:"home_reason_floors",basement_floor:"home_reason_basement",area_basement:"home_reason_basement",area_garden:"home_reason_garden",area_shed:"home_reason_shed",area_driveway:"home_reason_driveway",area_garage:"home_reason_garage",entity_garage:"home_reason_garage",area_attic:"home_reason_attic",entity_lawn_mower:"home_reason_lawn_mower",entity_gate:"home_reason_gate",area_balcony:"home_reason_balcony",small_home:"home_reason_small_home"},c={ups:"home_reason_ups"};function d(e,n){let r=[];for(let t of e){let o=l[t];if(!o)continue;let s=a(o,n);r.includes(s)||r.push(s)}return r.join(", ")}function b(e,n,r){if(e==="starter")return a("home_reason_starter",n);if(e==="country")return a("home_reason_country",n).replace("{country}",m(r,n));if(e.startsWith("feature_")){let t=e.slice(8),o=l[`area_${t}`]??c[t];return a("home_reason_feature",n).replace("{feature}",o?a(o,n):e)}return a(`home_trait_${e}`,n)}export{_ as a,i as b,g as c,f as d,m as e,d as f,b as g};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{a as y}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-FH35SLMS.js";var l=6;async function g(e,i,n){let r=new Array(e.length),a=0,t=async()=>{for(;a<e.length;){let s=a++;r[s]=await n(e[s],s)}},o=Math.min(Math.max(1,i),e.length);return await Promise.all(Array.from({length:o},t)),r}function u(e){let i=e.history?.length??0;return typeof e.history_count!="number"||e.history_count>i}function d(e){return JSON.stringify([e.history_count??null,e.history??[]])}function h(e,i){return`${e}:${i}`}var c=class{constructor(i){this._host=i;this._cache=new Map;this._inFlight=new Map;this._running=0;this._pending=Promise.resolve();this.version=0;i.addController(this)}hostConnected(){}get loading(){return this._running>0}get capped(){for(let i of this._cache.values())if(i.entries.length>=y)return!0;return!1}historyOf(i,n){return this._cache.get(h(i,n.id))?.entries??n.history??[]}sync(i,n){if(!i?.connection)return this._pending;let r=n.filter(t=>u(t.task)).map(t=>({...t,key:h(t.entryId,t.task.id),sig:d(t.task)})).filter(t=>this._cache.get(t.key)?.sig!==t.sig&&this._inFlight.get(t.key)!==t.sig);if(!r.length)return this._pending;for(let t of r)this._inFlight.set(t.key,t.sig);this._running++,this._host.requestUpdate();let a=g(r,l,async t=>{let o;try{let s=await i.connection.sendMessagePromise({type:"maintenance_supporter/task/history",entry_id:t.entryId,task_id:t.task.id});o=Array.isArray(s?.history)?s.history:t.task.history??[]}catch{o=t.task.history??[]}this._inFlight.get(t.key)===t.sig&&(this._inFlight.delete(t.key),this._cache.set(t.key,{sig:t.sig,entries:o}))}).then(()=>{this._running--,this.version++,this._host.requestUpdate()});return this._pending=Promise.all([this._pending,a]).then(()=>{}),this._pending}settled(){return this._pending}};export{c as a};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.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};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{P as n,t as o}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.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
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
function c(n){return n==="use"?"use":"purchase"}var s=n=>typeof n=="number"&&Number.isFinite(n)?n:null;function a(n){let t=s(n?.cost);if(t===null)return null;let o=s(n?.package_size);return o&&o>0?t/o:t}function p(n,t){let o=s(t)&&t>0?t:s(n.restock_quantity)||1,e=s(n.package_size);return Math.round(o*(e&&e>0?e:1)*100)/100}function l(n){let t=s(n.cost);if(n.cost_basis!=="use")return t;let o=s(n.parts_cost),e=n.purchase?null:t;return e===null&&o===null?null:(e??0)+(o??0)}function _(n){if(!n)return[];let t=[],o=n.photo_doc_id;typeof o=="string"&&t.push(o);let e=n.photo_doc_ids;Array.isArray(e)&&t.push(...e);let r=[];for(let u of t){if(typeof u!="string")continue;let i=u.trim();if(!(!i||r.includes(i))&&(r.push(i),r.length>=10))break}return r}export{_ as a,c as b,a as c,p as d,l as e};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,33 @@
/*! maintenance_supporter frontend 2.103.0 */
import{b as r,c as t}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";var d=r`
.backdrop {
position: fixed; inset: 0; z-index: var(--ms-modal-z, 100);
background: rgba(0,0,0,0.5);
}
.dialog {
position: fixed; left: 50%; top: 50%;
transform: translate(-50%, -50%);
width: 95vw; max-width: var(--ms-modal-max-width, 480px);
max-height: var(--ms-modal-max-height, 92vh); overflow: auto;
background: var(--card-background-color, var(--ha-card-background, #1c1c1c));
color: var(--primary-text-color);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.4);
padding: 20px;
display: flex; flex-direction: column; gap: var(--ms-modal-gap, 14px);
z-index: calc(var(--ms-modal-z, 100) + 1);
}
/* Focused programmatically on open (so Escape works at once) — no ring. */
.dialog:focus { outline: none; }
/* The one part that shrinks and scrolls (min-height: 0); the header and the
buttons around it keep their content's height. */
.dialog-scroll {
flex: 1 1 auto; min-height: 0;
overflow-y: auto; overscroll-behavior: contain;
display: flex; flex-direction: column; gap: var(--ms-modal-gap, 14px);
}
`;function l(o,a){o.key!=="Escape"||o.defaultPrevented||o.composedPath().some(e=>e instanceof Element&&e.localName==="ms-camera-capture")||(o.preventDefault(),o.stopPropagation(),a())}function i(o,a){return t`
<div class="backdrop" @click=${o}></div>
<div class="dialog" role="dialog" aria-modal="true" tabindex="-1"
@keydown=${e=>l(e,o)}>${a}</div>
`}function c(o){o?.querySelector(".dialog")?.focus({preventScroll:!0})}export{d as a,i as b,c};
@@ -0,0 +1,88 @@
/*! maintenance_supporter frontend 2.103.0 */
import{P as m,a,b as y,c as s,f as l,h as g,m as o,n as f,t as d}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.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);
@@ -0,0 +1,234 @@
/*! maintenance_supporter frontend 2.103.0 */
import{a as q}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-IRRUA3DS.js";import{I as k,a,b as f,c as n,f as u,h as x,m as h,n as c,t as e}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";function d(r){return r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function _(r){return!r.startsWith("data:image/svg+xml,")&&!r.startsWith("data:image/png;base64,")?"":d(r)}function y(r){return r.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var l=class extends x{constructor(){super(...arguments);this.lang="";this._open=!1;this._loading=!1;this._error="";this._viewResult=null;this._completeResult=null;this._quickResult=null;this._urlMode="companion";this._entryId="";this._taskId=null;this._objectName="";this._taskName="";this._generateSeq=0}get _dialogLang(){return this.lang||k(this.hass)}openForObject(t,i){this._entryId=t,this._taskId=null,this._objectName=i,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}openForTask(t,i,s,o){this._entryId=t,this._taskId=i,this._objectName=s,this._taskName=o,this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}async _generate(){let t=++this._generateSeq;this._loading=!0,this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null;try{let i={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(i.task_id=this._taskId);let s=[this.hass.connection.sendMessagePromise({...i,action:"view"})];this._taskId&&(s.push(this.hass.connection.sendMessagePromise({...i,action:"complete"})),await this._hasQuickCompleteDefaults()&&s.push(this.hass.connection.sendMessagePromise({...i,action:"quick_complete"})));let o=await Promise.all(s);if(t!==this._generateSeq)return;this._viewResult=o[0],o.length>1&&(this._completeResult=o[1]),o.length>2&&(this._quickResult=o[2])}catch(i){if(t!==this._generateSeq)return;let s=i?.code,o=i?.message;this._error=s==="no_url"||typeof o=="string"&&o.includes("No Home Assistant URL")?e("qr_error_no_url",this._dialogLang):e("qr_error",this._dialogLang)}finally{t===this._generateSeq&&(this._loading=!1)}}async _hasQuickCompleteDefaults(){try{let i=((await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId})).tasks||[]).find(s=>s.id===this._taskId);return!!i?.quick_complete_defaults&&Object.keys(i.quick_complete_defaults).length>0}catch{return!1}}_setUrlMode(t){this._urlMode!==t&&(this._urlMode=t,this._generate())}_print(){if(!this._viewResult)return;let t=this._viewResult,i=t.label.task_name?`${t.label.object_name} \u2014 ${t.label.task_name}`:t.label.object_name,s=[t.label.manufacturer,t.label.model].filter(Boolean).join(" "),o=window.open("","_blank","width=600,height=500");if(!o)return;let p=this._dialogLang,m=d(i),g=d(s),v=!!this._completeResult,b=!!this._quickResult,w=d(e("qr_action_view",p)),$=d(e("qr_action_complete",p)),R=d(e("qr_action_quick_complete",p));o.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
<meta name="color-scheme" content="light">
<title>${m}</title>
<style>
/* Printable sheet \u2014 must not inherit the phone's dark theme. The QR images
carry their own white quiet zone and stay scannable either way, but the
labels below are explicit dark greys and would vanish on a WebView's dark
canvas. Same reasoning as helpers/report.ts. */
:root{color-scheme:light}
body{font-family:sans-serif;text-align:center;padding:20px;background:#fff;color:#1a1a1a}
h2{margin:0 0 4px}
.sub{color:#666;font-size:14px;margin-bottom:16px}
.qr-row{display:flex;justify-content:center;gap:24px;margin:12px 0}
.qr-col{display:flex;flex-direction:column;align-items:center;gap:6px}
.qr-col img{width:${b?"170px":v?"200px":"280px"}}
.qr-label{font-size:13px;font-weight:500;color:#333}
.url{font-size:10px;color:#999;word-break:break-all;margin-top:8px;max-width:480px}
</style></head><body>
<h2>${m}</h2>
${g?`<div class="sub">${g}</div>`:""}
<div class="qr-row">
<div class="qr-col">
<img src="${_(this._viewResult.svg_data_uri)}" alt="QR Info" />
<div class="qr-label">${w}</div>
</div>
${v?`<div class="qr-col">
<img src="${_(this._completeResult.svg_data_uri)}" alt="QR Complete" />
<div class="qr-label">${$}</div>
</div>`:""}
${b?`<div class="qr-col">
<img src="${_(this._quickResult.svg_data_uri)}" alt="QR Quick-complete" />
<div class="qr-label">${R}</div>
</div>`:""}
</div>
<div class="url">${d(this._viewResult.url)}</div>
<script>setTimeout(()=>window.print(),300)<\/script>
</body></html>`),o.document.close()}_downloadSvg(t,i){let s=decodeURIComponent(t.svg_data_uri.replace("data:image/svg+xml,","")),o=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;q(s,`qr-${y(o)}-${i}.svg`,"image/svg+xml")}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._quickResult=null,this._error="",this._loading=!1}render(){if(!this._open)return n``;let t=this._dialogLang,i=this._taskName?`${e("qr_code",t)}: ${this._objectName} \u2014 ${this._taskName}`:`${e("qr_code",t)}: ${this._objectName}`,s=!!this._viewResult;return n`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${i}</div>
<div class="content">
${this._loading?n`<div class="loading">${e("qr_generating",t)}</div>`:this._error?n`<div class="error">${this._error}</div>`:s?n`
<div class="qr-pair">
<div class="qr-item">
<img
class="qr-image ${this._completeResult?"small":""}"
src="${this._viewResult.svg_data_uri}"
alt="QR Info"
/>
<div class="qr-item-label">${e("qr_action_view",t)}</div>
<button class="dl-btn"
@click=${()=>this._downloadSvg(this._viewResult,"info")}>
<ha-icon icon="mdi:download"></ha-icon>
${e("qr_download",t)}
</button>
</div>
${this._completeResult?n`
<div class="qr-item">
<img
class="qr-image small"
src="${this._completeResult.svg_data_uri}"
alt="QR Complete"
/>
<div class="qr-item-label">${e("qr_action_complete",t)}</div>
<button class="dl-btn"
@click=${()=>this._downloadSvg(this._completeResult,"complete")}>
<ha-icon icon="mdi:download"></ha-icon>
${e("qr_download",t)}
</button>
</div>
`:u}
${this._quickResult?n`
<div class="qr-item quick">
<img
class="qr-image small"
src="${this._quickResult.svg_data_uri}"
alt="QR Quick-complete"
/>
<div class="qr-item-label">${e("qr_action_quick_complete",t)}</div>
<button class="dl-btn"
@click=${()=>this._downloadSvg(this._quickResult,"quick-complete")}>
<ha-icon icon="mdi:download"></ha-icon>
${e("qr_download",t)}
</button>
</div>
`:u}
</div>
<div class="url-display">${this._viewResult.url}</div>
`:u}
<div class="action-row">
<label>${e("qr_url_mode",t)}</label>
<div class="action-toggle">
<button class="toggle-btn ${this._urlMode==="companion"?"active":""}"
@click=${()=>this._setUrlMode("companion")}>${e("qr_mode_companion",t)}</button>
<button class="toggle-btn ${this._urlMode==="local"?"active":""}"
@click=${()=>this._setUrlMode("local")}>${e("qr_mode_local",t)}</button>
<button class="toggle-btn ${this._urlMode==="server"?"active":""}"
@click=${()=>this._setUrlMode("server")}>${e("qr_mode_server",t)}</button>
</div>
</div>
</div>
<div class="dialog-actions">
<ha-button appearance="plain" @click=${this._close}>
${e("cancel",t)}
</ha-button>
<ha-button
@click=${this._print}
.disabled=${!s}
>
${e("qr_print",t)}
</ha-button>
</div>
</ha-dialog>
`}};l.styles=f`
.dialog-title {
font-size: 18px;
font-weight: 500;
padding-bottom: 12px;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
min-width: 300px;
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
padding-top: 16px;
}
.qr-pair {
display: flex;
flex-wrap: wrap; /* three codes (quick-complete, #192) wrap on a phone */
gap: 20px;
justify-content: center;
width: 100%;
}
.qr-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.qr-image {
width: 240px;
height: 240px;
image-rendering: pixelated;
}
.qr-image.small {
width: 180px;
height: 180px;
}
.qr-item-label {
max-width: 180px;
font-size: 12px;
font-weight: 500;
color: var(--secondary-text-color);
text-align: center;
}
.dl-btn {
display: inline-flex;
align-items: center;
gap: 6px;
background: none;
border: 1px solid var(--divider-color, #e0e0e0);
cursor: pointer;
font-size: 13px;
color: var(--primary-text-color);
padding: 6px 14px;
border-radius: 18px;
transition: background 0.2s, border-color 0.2s;
}
.dl-btn:hover {
background: var(--secondary-background-color, #f5f5f5);
border-color: var(--primary-color);
}
.dl-btn ha-icon {
--mdc-icon-size: 18px;
}
.url-display {
font-size: 11px;
color: var(--secondary-text-color);
word-break: break-all;
text-align: center;
max-width: 400px;
}
.loading {
padding: 40px 0;
color: var(--secondary-text-color);
}
.error {
padding: 20px 0;
color: var(--error-color, #f44336);
}
.action-row {
display: flex;
flex-direction: column;
gap: 6px;
width: 100%;
}
.action-row label {
font-size: 13px;
color: var(--secondary-text-color);
}
.action-toggle {
display: flex;
gap: 4px;
background: var(--divider-color, #e0e0e0);
border-radius: 6px;
padding: 3px;
}
.toggle-btn {
flex: 1;
padding: 8px 12px;
border: none;
background: transparent;
color: var(--primary-text-color);
cursor: pointer;
border-radius: 4px;
font-size: 13px;
transition: all 0.2s;
line-height: 1.3;
}
.toggle-btn:hover {
background: rgba(0, 0, 0, 0.05);
}
.toggle-btn.active {
background: var(--primary-color);
color: var(--text-primary-color, #fff);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
}
`,a([h({attribute:!1})],l.prototype,"hass",2),a([h()],l.prototype,"lang",2),a([c()],l.prototype,"_open",2),a([c()],l.prototype,"_loading",2),a([c()],l.prototype,"_error",2),a([c()],l.prototype,"_viewResult",2),a([c()],l.prototype,"_completeResult",2),a([c()],l.prototype,"_quickResult",2),a([c()],l.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",l);export{l as a};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
var r={objectSections:"msp-object-sections",printOptions:"msp-print-options",batteryRosterOpen:"msp-bf-roster-open",docSort:"msp-doc-sort",cameraLabel:"msp-camera-label",overviewTab:"msp-overview-tab",collapsedSections:"msp-collapsed-sections",chartRange:"msp-chart-range",chartHideOutliers:"msp-chart-hide-outliers",taskSort:"maintenance_supporter_sort",objectSort:"maintenance_supporter_object_sort",groupBy:"maintenance_supporter_groupby",objectView:"maintenance_supporter_object_view",objectsCache:"msp-objects-cache",gettingStartedDismissed:"msp-gs-dismissed",batteryRosterSort:"ms_bf_roster_sort",areaSort:"msp-area-sort"};function s(t){try{return localStorage.getItem(t)}catch{return null}}function o(t,e){try{localStorage.setItem(t,e)}catch{}}export{r as a,s as b,o as c};
@@ -0,0 +1,18 @@
/*! maintenance_supporter frontend 2.103.0 */
import{b as l}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-MMIPM7EE.js";import{a as c}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-2BQZ33DM.js";import{g as a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";var i={operatorWriteEnabled:!1,operatorIds:[]};function b(e,t=i){return e?e.is_admin?!0:t.operatorWriteEnabled&&t.operatorIds.includes(e.id):!1}var m=["buttons_compact","buttons","icons"];function f(e){let[t,s]=c.default_warning_days,n=e?.general?.default_warning_days,r=e?.general?.row_action_style;return{features:{...o.features,...e?.features??{}},defaultWarningDays:typeof n=="number"&&Number.isFinite(n)&&n>=t&&n<=s?n:o.defaultWarningDays,rowActionStyle:m.includes(r??"")?r:o.rowActionStyle,access:{operatorWriteEnabled:e?.operator_write_enabled===!0,operatorIds:Array.isArray(e?.admin_panel_user_ids)?e.admin_panel_user_ids.filter(p=>typeof p=="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,partsCostMode:l(e?.budget?.parts_cost_mode)}}var o={features:{adaptive:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1,places:!1},defaultWarningDays:7,rowActionStyle:"buttons_compact",access:i,budget:null,rowActionNoticePending:!1,refsInLists:!1,objectsTableColumns:void 0,partsCostMode:"purchase"};function u(){let e=window;return e.__msSettingsCache??={promise:null}}function S(e){let t=u();if(t.promise)return t.promise;let s=e.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(n=>f(n)).catch(()=>(t.promise===s&&(t.promise=null),o));return t.promise=s,s}function $(){u().promise=null}var _=class{constructor(){this._handle=null}schedule(t,s=4e3){this.clear(),this._handle=setTimeout(()=>{this._handle=null,t()},s)}clear(){this._handle!==null&&(clearTimeout(this._handle),this._handle=null)}get pending(){return this._handle!==null}};var{I:E}=a;var M=e=>e.strings===void 0;var g={},x=(e,t=g)=>e._$AH=t;var P={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},N=e=>(...t)=>({_$litDirective$:e,values:t}),d=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,s,n){this._$Ct=t,this._$AM=s,this._$Ci=n}_$AS(t,s){return this.update(t,s)}update(t,s){return this.render(...s)}};export{M as a,x as b,P as c,N as d,d as e,i as f,b as g,m as h,f as i,S as j,$ as k,_ as l};
/*! Bundled license information:
lit-html/directive-helpers.js:
(**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*)
lit-html/directive.js:
(**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*)
*/
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
async function i(o,n,e){let t=o.auth;if(t?.expired&&t.refreshAccessToken)try{await t.refreshAccessToken()}catch{}return fetch(n,{...e,headers:{...e.headers??{},Authorization:`Bearer ${t?.data?.access_token??""}`}})}var d=100*1024*1024,l=500*1024*1024;async function c(o,n,e,t="doc_too_large",s=[]){let r=await i(o,n,{method:"POST",body:e});if(r.status===413)throw new Error(t);if(!r.ok){let a=await r.json().catch(()=>null);throw typeof a?.code=="string"&&s.includes(a.code)?new Error(a.code):new Error("doc_upload_failed")}return await r.json()}async function p(o,n,e,t){if(e.size>d)throw new Error("doc_too_large");let s=new FormData;s.append("entry_id",n);for(let a of t)s.append("tags",a);s.append("file",e,e.name);let r=await c(o,"/api/maintenance_supporter/document/upload",s);if(!r.id)throw new Error("doc_upload_failed");return{id:r.id,deduped:!!r.deduped,duplicate_in_object:r.duplicate_in_object??null}}async function m(o,n,e){return(await p(o,n,e,["photo"])).id}async function _(o,n,e){await Promise.all(e.map(t=>o.connection.sendMessagePromise({type:"maintenance_supporter/documents/discard_upload",entry_id:n,doc_id:t}).catch(()=>{})))}export{l as a,c as b,p as c,m as d,_ as e};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{d as a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-2URORCTE.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-XDU67K3J.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-VZUMUVFP.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-MMIPM7EE.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-GMGHSBQJ.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-R77C34J4.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-2BQZ33DM.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-3LDXEWBF.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";export{a as MaintenanceCompleteDialog};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{H as a,I as b,J as c,K as d,L as e,M as f,N as g,O as h,P as i,Q as j,R as k}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-L5NQ4343.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-WNGWMATB.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-KZ3OKTLK.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-DR2AFVDA.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-EOMYEBQ4.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-4DQPCVDN.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-UVFWQWIH.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-5TWB2KD3.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-2URORCTE.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-XDU67K3J.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-VZUMUVFP.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-MMIPM7EE.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-GMGHSBQJ.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-R77C34J4.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-S4GGDFOL.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-IRRUA3DS.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-DPJSGXJS.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-2BQZ33DM.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-QVOLUT24.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-3LDXEWBF.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";export{b as __resetSettingsCacheForTests,a as getRowActionStyle,h as openCompleteDialog,c as openCreateObjectDialog,e as openCreateTaskDialog,d as openEditObjectDialog,f as openEditTaskDialog,g as openHistoryEditDialog,k as openObjectQuickActions,i as openQrDialog,j as openTaskQuickActions};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-4DQPCVDN.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-5TWB2KD3.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-R77C34J4.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-3LDXEWBF.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";export{a as MaintenanceObjectDialog};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-S4GGDFOL.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-IRRUA3DS.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";export{a as MaintenanceQrDialog};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,163 @@
/*! maintenance_supporter frontend 2.103.0 */
import{a as $,b as z,c as j}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-QVOLUT24.js";import{a as g}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3LDXEWBF.js";import{I as w,a as d,b,c as n,f as c,h as x,m as y,n as l,t as r,v as k}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";var m=u=>`${u.entry_id}/${u.task_id}`,_="__new__",o=class extends x{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._setups=[];this._selected=new Set;this._baselines=new Map;this._targets=new Map;this._tasks=new Map;this._objects=[];this._offers=[];this._offerTicks=new Set;this._wiring=!1;this._localeReady=!1;this._wire=async()=>{if(this._wiring)return;let e=this._offers.filter(s=>this._offerTicks.has(m(s))).map(s=>({entry_id:s.entry_id,task_id:s.task_id}));if(e.length===0)return;this._error="";let t=await g(this,{type:"maintenance_supporter/integration_setups/wire_resets",items:e},{busy:s=>{this._wiring=s},onError:s=>{this._error=s}});t!==void 0&&(this.dispatchEvent(new CustomEvent("reset-counters-wired",{bubbles:!0,composed:!0,detail:t})),await this._loadOffers())};this._toggle=e=>{let t=new Set(this._selected);t.has(e)?t.delete(e):t.add(e),this._selected=t};this._adopt=async()=>{if(this._adopting)return;this._error="";let e=[...this._selected].map(s=>{let a=[...this._tasks.get(s)??[]],p={device_id:s,task_names:a},i=this._targets.get(s);i&&i!==_&&(p.entry_id=i);for(let f of a){let h=this._baselines.get(`${s} ${f}`),v=h?parseFloat(h):NaN;!isNaN(v)&&v>=0&&((p.baselines??={})[f]=v)}return p}).filter(s=>s.task_names.length>0);if(e.length===0)return;let t=await g(this,{type:"maintenance_supporter/integration_setups/adopt",selections:e},{busy:s=>{this._adopting=s},onError:s=>{this._error=s}});t!==void 0&&(this.dispatchEvent(new CustomEvent("integration-setups-adopted",{bubbles:!0,composed:!0,detail:t})),this._open=!1)}}get _lang(){return w(this.hass)}updated(e){e.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,k(this._lang).then(()=>this.requestUpdate())),e.has("_open")&&this._open&&j(this.shadowRoot)}_defaultTicks(e){return new Set(e.tasks.filter(t=>!t.covered_by).map(t=>t.task_name))}async open(){this._open=!0,this._loading=!0,this._error="",this._setups=[],this._selected=new Set,this._offers=[],this._loadOffers();let e=await g(this,{type:"maintenance_supporter/integration_setups/discover"},{onError:t=>{this._error=t}});if(e!==void 0){this._setups=e?.setups||[],this._selected=new Set(this._setups.map(t=>t.device_id)),this._baselines=new Map,this._targets=new Map(this._setups.map(t=>[t.device_id,t.target_entry_id||_])),this._tasks=new Map(this._setups.map(t=>[t.device_id,this._defaultTicks(t)]));try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"});this._objects=(t.objects||[]).filter(s=>!s.object?.archived_at).map(s=>({entry_id:s.entry_id,name:s.object?.name||s.entry_id})).sort((s,a)=>s.name.localeCompare(a.name))}catch{this._objects=[]}}this._loading=!1}_close(){this._open=!1}async _loadOffers(){try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/integration_setups/reset_offers"});this._offers=e.offers||[],this._offerTicks=new Set(this._offers.filter(t=>!t.renamed).map(m))}catch{this._offers=[]}}_toggleOffer(e){let t=new Set(this._offerTicks),s=m(e);t.has(s)?t.delete(s):t.add(s),this._offerTicks=t}_renderOffers(e){if(this._offers.length===0)return c;let t=this._offers.filter(s=>this._offerTicks.has(m(s))).length;return n`
<div class="offers">
<div class="offers-title">${r("reset_offers_title",e)}</div>
<div class="hint">${r("reset_offers_hint",e)}</div>
${this._offers.map(s=>n`
<label class="offer" data-task=${s.task_id}>
<input type="checkbox" class="offer-check" .checked=${this._offerTicks.has(m(s))} @change=${()=>this._toggleOffer(s)} />
<span class="task-text">
<span>${s.task_name} · ${s.object_name}</span>
${s.renamed?n`<span class="offer-renamed">${r("reset_offers_renamed",e)}</span>`:c}
<span class="offer-before">${r("reset_offers_before",e).replace("{integration}",s.integration_name)}</span>
<span class="offer-after">
${r("reset_offers_after",e).replace("{button}",s.button_name)}${s.button_disabled?` ${r("setups_reset_enable",e)}`:""}
</span>
</span>
</label>
`)}
<div class="offers-actions">
<ha-button @click=${this._wire} .disabled=${t===0||this._wiring}>${r("reset_offers_apply",e)}</ha-button>
</div>
</div>
`}_toggleTask(e,t){let s=new Set(this._tasks.get(e)??[]);s.has(t)?s.delete(t):s.add(t),this._tasks=new Map(this._tasks).set(e,s)}async _retarget(e,t){this._targets=new Map(this._targets).set(e.device_id,t);let s=await g(this,{type:"maintenance_supporter/integration_setups/preview",device_id:e.device_id,entry_id:t===_?null:t},{onError:a=>{this._error=a}});!s||!Array.isArray(s.tasks)||(this._setups=this._setups.map(a=>a.device_id===e.device_id?{...a,...s}:a),this._tasks=new Map(this._tasks).set(e.device_id,this._defaultTicks(s)))}get _anythingTicked(){return[...this._selected].some(e=>(this._tasks.get(e)?.size??0)>0)}_targetOptions(e,t){let s=this._targets.get(e.device_id)??_,a=[];if(e.suggested_entry_id)a.push({value:e.suggested_entry_id,label:e.suggested_object_name});else{if(e.candidate){let i=e.candidate.reasons.map(f=>r(`setups_reason_${f}`,t)).join(", ");a.push({value:e.candidate.entry_id,label:r("setups_target_match",t).replace("{name}",e.candidate.name).replace("{reasons}",i)})}a.push({value:_,label:r("setups_target_new",t).replace("{name}",e.device_name)})}let p=new Set(a.map(i=>i.value));for(let i of this._objects)p.has(i.entry_id)||a.push({value:i.entry_id,label:i.name});return n`
<select
class="target-select"
@click=${i=>i.stopPropagation()}
@change=${i=>{this._retarget(e,i.target.value)}}
>
${a.map(i=>n`<option value=${i.value} ?selected=${i.value===s}>${i.label}</option>`)}
</select>
`}_beforeAfter(e,t){let s=this._tasks.get(e.device_id)?.size??0;if((this._targets.get(e.device_id)??_)===_)return r("setups_new_object_count",t).replace("{name}",e.device_name).replace("{count}",String(s));let p=e.target_task_count??0;return r("setups_before_after",t).replace("{before}",String(p)).replace("{after}",String(p+s))}render(){if(!this._open)return n``;let e=this._lang;return z(()=>this._close(),n`
<div class="title">${r("setups_title",e)}</div>
${this._error?n`<div class="error">${this._error}</div>`:c}
<div class="dialog-scroll">
<div class="hint">${r("setups_hint",e)}</div>
${this._renderOffers(e)}
${this._loading?n`<div class="loading">…</div>`:this._setups.length===0?this._offers.length===0?n`<div class="empty">${r("setups_none",e)}</div>`:c:n`
<div class="list">
${this._setups.map(t=>{let s=this._selected.has(t.device_id),a=[t.integration_name,t.area_name].filter(Boolean).join(" \xB7 "),p=this._tasks.get(t.device_id)??new Set;return n`
<div class="row ${s?"":"off"}" data-device=${t.device_id}>
<input
type="checkbox"
class="device-check"
.checked=${s}
@change=${()=>this._toggle(t.device_id)}
/>
<div class="row-main">
<div class="row-top">
<span class="row-name">${t.device_name}</span>
</div>
<div class="row-sub">${a}</div>
<div class="row-target">→ ${this._targetOptions(t,e)}</div>
${s?n`
<div class="task-list">
${t.tasks.map(i=>n`
<label class="task" title=${i.entity_ids.join(", ")}>
<input
type="checkbox"
class="task-check"
.checked=${p.has(i.task_name)}
@change=${()=>this._toggleTask(t.device_id,i.task_name)}
/>
<span class="task-text">
<span>${i.task_name_localized||i.task_name}</span>
${i.covered_by?n`<span class="maybe">${r("setups_maybe_covered",e).replace("{name}",i.covered_by.name)}</span>`:c}
${i.reset?n`<span class="reset">↻ ${r("setups_reset_line",e).replace("{button}",i.reset.name)}${i.reset.disabled?` ${r("setups_reset_enable",e)}`:""}</span>`:c}
</span>
</label>
${i.direction==="usage_delta"&&p.has(i.task_name)?n`
<div class="baseline-field">
<span class="baseline-label">${r("setups_baseline_hint",e)}</span>
<input
type="number"
step="any"
min="0"
.value=${this._baselines.get(`${t.device_id} ${i.task_name}`)??""}
@input=${f=>{let h=new Map(this._baselines);h.set(`${t.device_id} ${i.task_name}`,f.target.value),this._baselines=h}}
/>
</div>
`:c}
`)}
${t.already?.length?n`<div class="already">
${r("setups_already",e).replace("{names}",t.already.map(i=>i.existing_name).join(", "))}
</div>`:c}
</div>
<div class="before-after">${this._beforeAfter(t,e)}</div>
`:c}
</div>
</div>
`})}
</div>
`}
</div>
<div class="actions">
<ha-button appearance="plain" @click=${this._close}>
${r("cancel",e)}
</ha-button>
<ha-button @click=${this._adopt} .disabled=${!this._anythingTicked||this._adopting}>
${r("setups_adopt",e)}
</ha-button>
</div>
`)}};o.styles=[$,b`
:host {
--ms-modal-max-width: 560px;
--ms-modal-max-height: 90dvh;
--ms-modal-gap: 12px;
--ms-modal-z: 1000;
}
.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; }
.list { display: flex; flex-direction: column; gap: 6px; }
.row {
display: flex; align-items: flex-start; gap: 10px; padding: 8px;
border: 1px solid var(--divider-color); border-radius: 6px;
}
.row.off .row-main { opacity: 0.6; }
.row input { margin-top: 2px; cursor: pointer; }
.row-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1; }
.row-name { font-weight: 500; font-size: 13px; }
.row-sub, .row-target { color: var(--secondary-text-color); font-size: 12px; }
.row-target { display: flex; align-items: center; gap: 6px; min-width: 0; }
.target-select {
font-size: 12px; padding: 2px 4px; min-width: 0; max-width: 100%; flex: 1 1 auto;
border: 1px solid var(--divider-color); border-radius: 4px;
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
}
.task-list { display: flex; flex-direction: column; gap: 2px; margin-top: 2px; }
.task { display: flex; align-items: flex-start; gap: 6px; font-size: 13px; cursor: pointer; }
.task-text { display: flex; flex-direction: column; min-width: 0; }
.maybe { font-size: 11px; color: var(--warning-color, #ff9800); }
.already { font-size: 11px; color: var(--secondary-text-color); margin-top: 2px; }
.reset { font-size: 11px; color: var(--secondary-text-color); }
.offers {
display: flex; flex-direction: column; gap: 6px; padding: 8px;
border: 1px solid var(--primary-color); border-radius: 6px;
}
.offers-title { font-weight: 500; font-size: 14px; }
.offer { display: flex; align-items: flex-start; gap: 6px; font-size: 13px; cursor: pointer; }
.offer-before { font-size: 11px; color: var(--secondary-text-color); }
.offer-after { font-size: 11px; color: var(--primary-color); }
.offer-renamed { font-size: 11px; color: var(--warning-color, #ff9800); }
.offers-actions { display: flex; justify-content: flex-end; }
.before-after {
font-size: 12px; font-weight: 500; color: var(--primary-color);
border-top: 1px dashed var(--divider-color); padding-top: 4px; margin-top: 2px;
}
.baseline-field {
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
margin: 0 0 2px 24px; font-size: 12px; color: var(--secondary-text-color);
}
.baseline-field input {
width: 110px; padding: 3px 6px; font-size: 12px;
border: 1px solid var(--divider-color); border-radius: 4px;
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
}
.actions { display: flex; justify-content: flex-end; gap: 8px; padding-top: 8px; }
`],d([y({attribute:!1})],o.prototype,"hass",2),d([l()],o.prototype,"_open",2),d([l()],o.prototype,"_loading",2),d([l()],o.prototype,"_adopting",2),d([l()],o.prototype,"_error",2),d([l()],o.prototype,"_setups",2),d([l()],o.prototype,"_selected",2),d([l()],o.prototype,"_baselines",2),d([l()],o.prototype,"_targets",2),d([l()],o.prototype,"_tasks",2),d([l()],o.prototype,"_objects",2),d([l()],o.prototype,"_offers",2),d([l()],o.prototype,"_offerTicks",2),d([l()],o.prototype,"_wiring",2);customElements.get("maintenance-suggested-setups-dialog")||customElements.define("maintenance-suggested-setups-dialog",o);export{o as MaintenanceSuggestedSetupsDialog};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.103.0 */
import{c as a,d as b,e as c}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-UVFWQWIH.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-5TWB2KD3.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-GMGHSBQJ.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-R77C34J4.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-DPJSGXJS.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-2BQZ33DM.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-3LDXEWBF.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-OWNF6K2Z.js";export{c as MaintenanceTaskDialog,b as applyTypeFields,a as typeFieldsFromConfig};