182 files

This commit is contained in:
Home Assistant Version Control
2026-08-17 12:11:35 +00:00
parent 7dddf6bb13
commit ece15a1c1b
183 changed files with 11457 additions and 3092 deletions
@@ -483,6 +483,7 @@
"settings_export_json": "Exportovat JSON",
"settings_export_yaml": "Exportovat YAML",
"settings_export_csv": "Exportovat CSV",
"settings_export_settings": "Exportovat nastavení (JSON)",
"settings_import_csv": "Importovat CSV",
"settings_import_placeholder": "Vložte sem obsah JSON nebo CSV…",
"settings_import_btn": "Importovat",
@@ -484,6 +484,7 @@
"settings_export_json": "Eksporter JSON",
"settings_export_yaml": "Eksporter YAML",
"settings_export_csv": "Eksporter CSV",
"settings_export_settings": "Eksportér indstillinger (JSON)",
"settings_import_csv": "Importer CSV",
"settings_import_placeholder": "Indsæt JSON- eller CSV-indhold her…",
"settings_import_btn": "Importer",
@@ -484,6 +484,7 @@
"settings_export_json": "JSON exportieren",
"settings_export_yaml": "YAML exportieren",
"settings_export_csv": "CSV exportieren",
"settings_export_settings": "Einstellungen exportieren (JSON)",
"settings_import_csv": "CSV importieren",
"settings_import_placeholder": "JSON- oder CSV-Inhalt hier einfügen…",
"settings_import_btn": "Importieren",
@@ -484,6 +484,7 @@
"settings_export_json": "Export JSON",
"settings_export_yaml": "Export YAML",
"settings_export_csv": "Export CSV",
"settings_export_settings": "Export settings (JSON)",
"settings_import_csv": "Import CSV",
"settings_import_placeholder": "Paste JSON or CSV content here…",
"settings_import_btn": "Import",
@@ -483,6 +483,7 @@
"settings_export_json": "Exportar JSON",
"settings_export_yaml": "Exportar YAML",
"settings_export_csv": "Exportar CSV",
"settings_export_settings": "Exportar ajustes (JSON)",
"settings_import_csv": "Importar CSV",
"settings_import_placeholder": "Pegue el contenido JSON o CSV aquí…",
"settings_import_btn": "Importar",
@@ -484,6 +484,7 @@
"settings_export_json": "Vie JSON",
"settings_export_yaml": "Vie YAML",
"settings_export_csv": "Vie CSV",
"settings_export_settings": "Vie asetukset (JSON)",
"settings_import_csv": "Tuo CSV",
"settings_import_placeholder": "Liitä JSON- tai CSV-sisältö tähän…",
"settings_import_btn": "Tuo",
@@ -483,6 +483,7 @@
"settings_export_json": "Exporter JSON",
"settings_export_yaml": "Exporter YAML",
"settings_export_csv": "Exporter CSV",
"settings_export_settings": "Exporter les réglages (JSON)",
"settings_import_csv": "Importer CSV",
"settings_import_placeholder": "Collez le contenu JSON ou CSV ici…",
"settings_import_btn": "Importer",
@@ -484,6 +484,7 @@
"settings_export_json": "JSON निर्यात करें",
"settings_export_yaml": "YAML निर्यात करें",
"settings_export_csv": "CSV निर्यात करें",
"settings_export_settings": "सेटिंग्स निर्यात करें (JSON)",
"settings_import_csv": "CSV आयात करें",
"settings_import_placeholder": "JSON या CSV सामग्री यहाँ चिपकाएँ…",
"settings_import_btn": "आयात करें",
@@ -484,6 +484,7 @@
"settings_export_json": "JSON exportálása",
"settings_export_yaml": "YAML exportálása",
"settings_export_csv": "CSV exportálása",
"settings_export_settings": "Beállítások exportálása (JSON)",
"settings_import_csv": "CSV importálása",
"settings_import_placeholder": "Illessze be ide a JSON vagy CSV tartalmat…",
"settings_import_btn": "Importálás",
@@ -483,6 +483,7 @@
"settings_export_json": "Esporta JSON",
"settings_export_yaml": "Esporta YAML",
"settings_export_csv": "Esporta CSV",
"settings_export_settings": "Esporta impostazioni (JSON)",
"settings_import_csv": "Importa CSV",
"settings_import_placeholder": "Incolla il contenuto JSON o CSV qui…",
"settings_import_btn": "Importa",
@@ -484,6 +484,7 @@
"settings_export_json": "JSONをエクスポート",
"settings_export_yaml": "YAMLをエクスポート",
"settings_export_csv": "CSVをエクスポート",
"settings_export_settings": "設定をエクスポート(JSON)",
"settings_import_csv": "CSVをインポート",
"settings_import_placeholder": "JSONまたはCSVの内容をここに貼り付け…",
"settings_import_btn": "インポート",
@@ -484,6 +484,7 @@
"settings_export_json": "JSON 내보내기",
"settings_export_yaml": "YAML 내보내기",
"settings_export_csv": "CSV 내보내기",
"settings_export_settings": "설정 내보내기 (JSON)",
"settings_import_csv": "CSV 가져오기",
"settings_import_placeholder": "JSON 또는 CSV 내용을 여기에 붙여넣으세요…",
"settings_import_btn": "가져오기",
@@ -484,6 +484,7 @@
"settings_export_json": "Eksporter JSON",
"settings_export_yaml": "Eksporter YAML",
"settings_export_csv": "Eksporter CSV",
"settings_export_settings": "Eksporter innstillinger (JSON)",
"settings_import_csv": "Importer CSV",
"settings_import_placeholder": "Lim inn JSON- eller CSV-innhold her…",
"settings_import_btn": "Importer",
@@ -483,6 +483,7 @@
"settings_export_json": "JSON exporteren",
"settings_export_yaml": "YAML exporteren",
"settings_export_csv": "CSV exporteren",
"settings_export_settings": "Instellingen exporteren (JSON)",
"settings_import_csv": "CSV importeren",
"settings_import_placeholder": "Plak JSON- of CSV-inhoud hier…",
"settings_import_btn": "Importeren",
@@ -483,6 +483,7 @@
"settings_export_json": "Eksportuj JSON",
"settings_export_yaml": "Eksportuj YAML",
"settings_export_csv": "Eksportuj CSV",
"settings_export_settings": "Eksportuj ustawienia (JSON)",
"settings_import_csv": "Importuj CSV",
"settings_import_placeholder": "Wklej tutaj zawartość JSON lub CSV…",
"settings_import_btn": "Importuj",
@@ -484,6 +484,7 @@
"settings_export_json": "Exportar JSON",
"settings_export_yaml": "Exportar YAML",
"settings_export_csv": "Exportar CSV",
"settings_export_settings": "Exportar configurações (JSON)",
"settings_import_csv": "Importar CSV",
"settings_import_placeholder": "Cole o conteúdo JSON ou CSV aqui…",
"settings_import_btn": "Importar",
@@ -483,6 +483,7 @@
"settings_export_json": "Exportar JSON",
"settings_export_yaml": "Exportar YAML",
"settings_export_csv": "Exportar CSV",
"settings_export_settings": "Exportar definições (JSON)",
"settings_import_csv": "Importar CSV",
"settings_import_placeholder": "Cole o conteúdo JSON ou CSV aqui…",
"settings_import_btn": "Importar",
@@ -483,6 +483,7 @@
"settings_export_json": "Экспорт JSON",
"settings_export_yaml": "Экспорт YAML",
"settings_export_csv": "Экспорт CSV",
"settings_export_settings": "Экспорт настроек (JSON)",
"settings_import_csv": "Импорт CSV",
"settings_import_placeholder": "Вставьте содержимое JSON или CSV здесь…",
"settings_import_btn": "Импортировать",
@@ -483,6 +483,7 @@
"settings_export_json": "Exportera JSON",
"settings_export_yaml": "Exportera YAML",
"settings_export_csv": "Exportera CSV",
"settings_export_settings": "Exportera inställningar (JSON)",
"settings_import_csv": "Importera CSV",
"settings_import_placeholder": "Klistra in JSON- eller CSV-innehåll här…",
"settings_import_btn": "Importera",
@@ -484,6 +484,7 @@
"settings_export_json": "JSON dışa aktar",
"settings_export_yaml": "YAML dışa aktar",
"settings_export_csv": "CSV dışa aktar",
"settings_export_settings": "Ayarları dışa aktar (JSON)",
"settings_import_csv": "CSV içe aktar",
"settings_import_placeholder": "JSON veya CSV içeriğini buraya yapıştırın…",
"settings_import_btn": "İçe aktar",
@@ -483,6 +483,7 @@
"settings_export_json": "Експортувати JSON",
"settings_export_yaml": "Експортувати YAML",
"settings_export_csv": "Експортувати CSV",
"settings_export_settings": "Експорт налаштувань (JSON)",
"settings_import_csv": "Імпортувати CSV",
"settings_import_placeholder": "Вставте вміст JSON або CSV сюди…",
"settings_import_btn": "Імпортувати",
@@ -484,6 +484,7 @@
"settings_export_json": "导出 JSON",
"settings_export_yaml": "导出 YAML",
"settings_export_csv": "导出 CSV",
"settings_export_settings": "导出设置(JSON)",
"settings_import_csv": "导入 CSV",
"settings_import_placeholder": "在此粘贴 JSON 或 CSV 内容…",
"settings_import_btn": "导入",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
/*! maintenance_supporter frontend 2.57.0 */
var S="2.57.0";var l="maintenance-supporter",T=`ll-strategy-dashboard-${l}`,D="hui-maintenance-supporter-strategy-editor",C=`/maintenance_supporter_strategy/maintenance-dashboard-strategy.js?v=${S}`,m=null;function v(){return m||(m=import(C)),m}async function I(){let r=await v();if(!r.MaintenanceDashboardStrategy)throw new Error("[maintenance-supporter] strategy bundle loaded but did not export MaintenanceDashboardStrategy");return r.MaintenanceDashboardStrategy}var p=class extends HTMLElement{static getCreateSuggestions(c){return{title:"Maintenance Supporter",icon:"mdi:wrench-clock"}}static async getConfigElement(){return await v(),document.createElement(D)}static async generate(c,f){return(await I()).generate(c,f)}};function M(){try{customElements.define(T,p)}catch{}}M();var w=window;w.customStrategies=w.customStrategies||[];w.customStrategies.some(r=>r.type===l&&r.strategyType==="dashboard")||w.customStrategies.push({type:l,strategyType:"dashboard",name:"Maintenance Supporter",description:"Auto-generated dashboard. Group views by area, status, floor, or due date \u2014 picked from the strategy editor or YAML.",documentationURL:"https://github.com/iluebbe/maintenance_supporter#dashboard-strategy"});(()=>{let r=window;if(r.__msStrategyHealActive)return;r.__msStrategyHealActive=!0;let c=/^\/(auth|config|developer-tools|profile|hassio|history|logbook|map|media-browser|energy|todo|calendar)\b/,f=/Timeout waiting for strategy element ll-strategy-(dashboard-)?maintenance-supporter/i,g=`custom:${l}`;function R(a){let t=[document.documentElement],n=0;for(;t.length&&n<9e3;){let o=t.pop();if(n++,!o)continue;let e=o;if(e.nodeType===1&&e.tagName&&e.tagName.toLowerCase()===a)return e;e.shadowRoot&&t.push(e.shadowRoot);let i=o.children;if(i)for(let d of Array.from(i))t.push(d)}return null}function k(a){let t=a?.views;if(!Array.isArray(t)||!t.length)return null;let n=window.location.pathname.split("/").filter(Boolean).pop()||"",o=t.find(i=>i?.path===n);if(o)return o;let e=Number(n);return Number.isInteger(e)&&t[e]?t[e]:t[0]}function b(){try{let t=R("ha-panel-lovelace")?.lovelace;if(!t)return!1;let n=o=>o?.type;for(let o of[t.config,t.rawConfig]){if(!o)continue;if(n(o.strategy)===g)return!0;let e=k(o);if(e&&n(e.strategy)===g)return!0}return!1}catch{return!1}}function A(){let a=!1,t=0,n=!1,o=!1,e=[document.documentElement],i=0;for(;e.length&&i<9e3;){let d=e.pop();if(i++,!d)continue;let u=d;if(u.nodeType===1&&u.tagName){let s=u.tagName.toLowerCase();(s==="hui-view"||s==="hui-sections-view")&&(a=!0),(s==="ha-card"||s==="hui-card")&&t++,s==="hui-empty-state-card"&&(o=!0),s==="hui-error-card"&&f.test(u.textContent||"")&&(n=!0)}u.shadowRoot&&e.push(u.shadowRoot);let E=d.children;if(E)for(let s of Array.from(E))e.push(s)}return n?!0:o?!1:a&&t<3&&b()}let N="/maintenance_supporter_strategy_shim.js",y=0,_=0;function L(){let a=Date.now();a-_<5e3||y>=3||(_=a,y+=1,import(`${N}?heal=${a}`).catch(()=>{}).finally(()=>{let t=window.location.pathname+window.location.search;history.pushState(null,"","/lovelace"),window.dispatchEvent(new CustomEvent("location-changed")),window.setTimeout(()=>{history.pushState(null,"",t),window.dispatchEvent(new CustomEvent("location-changed"))},200)}))}function h(){if(c.test(window.location.pathname))return;let a=0,t=Date.now(),n=window.setInterval(()=>{a++;try{if(Date.now()-t<6e3)return;if(c.test(window.location.pathname)){window.clearInterval(n);return}A()?L():window.clearInterval(n),a>=30&&window.clearInterval(n)}catch{window.clearInterval(n)}},500)}try{document.readyState==="loading"?window.addEventListener("DOMContentLoaded",h):h(),window.addEventListener("location-changed",()=>{c.test(window.location.pathname)||h()})}catch{}})();
/*! maintenance_supporter frontend 2.58.0 */
var S="2.58.0";var l="maintenance-supporter",T=`ll-strategy-dashboard-${l}`,D="hui-maintenance-supporter-strategy-editor",C=`/maintenance_supporter_strategy/maintenance-dashboard-strategy.js?v=${S}`,m=null;function v(){return m||(m=import(C)),m}async function I(){let r=await v();if(!r.MaintenanceDashboardStrategy)throw new Error("[maintenance-supporter] strategy bundle loaded but did not export MaintenanceDashboardStrategy");return r.MaintenanceDashboardStrategy}var p=class extends HTMLElement{static getCreateSuggestions(c){return{title:"Maintenance Supporter",icon:"mdi:wrench-clock"}}static async getConfigElement(){return await v(),document.createElement(D)}static async generate(c,f){return(await I()).generate(c,f)}};function M(){try{customElements.define(T,p)}catch{}}M();var w=window;w.customStrategies=w.customStrategies||[];w.customStrategies.some(r=>r.type===l&&r.strategyType==="dashboard")||w.customStrategies.push({type:l,strategyType:"dashboard",name:"Maintenance Supporter",description:"Auto-generated dashboard. Group views by area, status, floor, or due date \u2014 picked from the strategy editor or YAML.",documentationURL:"https://github.com/iluebbe/maintenance_supporter#dashboard-strategy"});(()=>{let r=window;if(r.__msStrategyHealActive)return;r.__msStrategyHealActive=!0;let c=/^\/(auth|config|developer-tools|profile|hassio|history|logbook|map|media-browser|energy|todo|calendar)\b/,f=/Timeout waiting for strategy element ll-strategy-(dashboard-)?maintenance-supporter/i,g=`custom:${l}`;function R(a){let t=[document.documentElement],n=0;for(;t.length&&n<9e3;){let o=t.pop();if(n++,!o)continue;let e=o;if(e.nodeType===1&&e.tagName&&e.tagName.toLowerCase()===a)return e;e.shadowRoot&&t.push(e.shadowRoot);let i=o.children;if(i)for(let d of Array.from(i))t.push(d)}return null}function k(a){let t=a?.views;if(!Array.isArray(t)||!t.length)return null;let n=window.location.pathname.split("/").filter(Boolean).pop()||"",o=t.find(i=>i?.path===n);if(o)return o;let e=Number(n);return Number.isInteger(e)&&t[e]?t[e]:t[0]}function b(){try{let t=R("ha-panel-lovelace")?.lovelace;if(!t)return!1;let n=o=>o?.type;for(let o of[t.config,t.rawConfig]){if(!o)continue;if(n(o.strategy)===g)return!0;let e=k(o);if(e&&n(e.strategy)===g)return!0}return!1}catch{return!1}}function A(){let a=!1,t=0,n=!1,o=!1,e=[document.documentElement],i=0;for(;e.length&&i<9e3;){let d=e.pop();if(i++,!d)continue;let u=d;if(u.nodeType===1&&u.tagName){let s=u.tagName.toLowerCase();(s==="hui-view"||s==="hui-sections-view")&&(a=!0),(s==="ha-card"||s==="hui-card")&&t++,s==="hui-empty-state-card"&&(o=!0),s==="hui-error-card"&&f.test(u.textContent||"")&&(n=!0)}u.shadowRoot&&e.push(u.shadowRoot);let E=d.children;if(E)for(let s of Array.from(E))e.push(s)}return n?!0:o?!1:a&&t<3&&b()}let N="/maintenance_supporter_strategy_shim.js",y=0,_=0;function L(){let a=Date.now();a-_<5e3||y>=3||(_=a,y+=1,import(`${N}?heal=${a}`).catch(()=>{}).finally(()=>{let t=window.location.pathname+window.location.search;history.pushState(null,"","/lovelace"),window.dispatchEvent(new CustomEvent("location-changed")),window.setTimeout(()=>{history.pushState(null,"",t),window.dispatchEvent(new CustomEvent("location-changed"))},200)}))}function h(){if(c.test(window.location.pathname))return;let a=0,t=Date.now(),n=window.setInterval(()=>{a++;try{if(Date.now()-t<6e3)return;if(c.test(window.location.pathname)){window.clearInterval(n);return}A()?L():window.clearInterval(n),a>=30&&window.clearInterval(n)}catch{window.clearInterval(n)}},500)}try{document.readyState==="loading"?window.addEventListener("DOMContentLoaded",h):h(),window.addEventListener("location-changed",()=>{c.test(window.location.pathname)||h()})}catch{}})();
@@ -0,0 +1,222 @@
/*! maintenance_supporter frontend 2.58.0 */
import{a as d}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-F567KB5W.js";import{a as m}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-2TYYX7JG.js";import{a,b as _,c as t,f as l,g as h,i as g,j as n,n as i,o as v,q as u}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5LZFNUMR.js";var r=class extends h{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._users=[];this._responsible="";this._localeReady=!1;this._userService=null;this._toggle=s=>{let o=new Set(this._selected);o.has(s)?o.delete(s):o.add(s),this._selected=o};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(s=>s.entity_id))};this._adopt=async()=>{if(!(this._selected.size===0||this._adopting)){this._adopting=!0,this._error="";try{let s=this._sensors.filter(e=>this._selected.has(e.entity_id)).map(e=>({entity_id:e.entity_id,name:e.name,entry_id:e.suggested_entry_id??void 0,object_name:e.suggested_object_name,device_id:e.device_id??void 0,part_id:e.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0})),o=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/adopt",selections:s});this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:o})),this._open=!1}catch(s){this._error=d(s,this._lang)}finally{this._adopting=!1}}}}get _lang(){return v(this.hass)}updated(s){s.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,u(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._responsible="";try{this._userService?this._userService.updateHass(this.hass):this._userService=new m(this.hass);let[s,o]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/discover"}),this._userService.getUsers().catch(()=>[])]);this._sensors=s.sensors||[],this._selected=new Set(this._sensors.map(e=>e.entity_id)),this._users=o}catch(s){this._error=d(s,this._lang)}finally{this._loading=!1}}_close(){this._open=!1}render(){if(!this._open)return t``;let s=this._lang,o=this._sensors.length>0&&this._selected.size===this._sensors.length;return t`
<div class="overlay" @click=${this._close}>
<div class="card" @click=${e=>e.stopPropagation()}>
<div class="title">${i("adopt_problem_title",s)}</div>
<div class="hint">${i("adopt_problem_hint",s)}</div>
${this._error?t`<div class="error">${this._error}</div>`:l}
${this._loading?t`<div class="loading">…</div>`:this._sensors.length===0?t`<div class="empty">${i("adopt_problem_none",s)}</div>`:t`
<label class="select-all">
<input
type="checkbox"
.checked=${o}
@change=${this._toggleAll}
/>
<span>${i("selected",s)}: ${this._selected.size} / ${this._sensors.length}</span>
</label>
<div class="list">
${this._sensors.map(e=>{let f=this._selected.has(e.entity_id),p=e.state==="on",c=[e.device_name,e.area_name].filter(Boolean).join(" \xB7 ");return t`
<label class="row">
<input
type="checkbox"
.checked=${f}
@change=${()=>this._toggle(e.entity_id)}
/>
<div class="row-main">
<div class="row-top">
<span class="row-name">${e.name}</span>
<span class="chip ${p?"chip-active":"chip-ok"}">
${p?i("adopt_problem_active",s):i("adopt_problem_ok",s)}
</span>
</div>
${c?t`<div class="row-sub">${c}</div>`:l}
<div class="row-target">
→ ${e.suggested_object_name}${e.suggested_entry_id?l:t` <span class="new-tag">${i("adopt_problem_new_object",s)}</span>`}
</div>
${e.suggested_part_name?t`<div class="row-part">
<ha-icon icon="mdi:package-variant-closed"></ha-icon>
${i("adopt_problem_part",s).replace("{name}",e.suggested_part_name)}
</div>`:l}
</div>
</label>
`})}
</div>
`}
${!this._loading&&this._sensors.length>0&&this._users.length>0?t`
<label class="responsible">
<span>${i("adopt_problem_responsible",s)}</span>
<select
.value=${this._responsible}
@change=${e=>{this._responsible=e.target.value}}
>
<option value="" ?selected=${!this._responsible}>${i("no_user_assigned",s)}</option>
${this._users.map(e=>t`<option value=${e.id} ?selected=${e.id===this._responsible}>${e.name}</option>`)}
</select>
</label>
`:l}
<div class="actions">
<ha-button appearance="plain" @click=${this._close}>
${i("cancel",s)}
</ha-button>
<ha-button
@click=${this._adopt}
.disabled=${this._selected.size===0||this._adopting}
>
${i("adopt_problem_adopt",s)}
</ha-button>
</div>
</div>
</div>
`}};r.styles=_`
.overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.card {
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 12px;
min-width: min(360px, calc(100vw - 24px));
max-width: 560px;
width: 90vw;
max-height: 80vh;
overflow: hidden;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}
.title {
font-size: 18px;
font-weight: 500;
}
.hint {
color: var(--secondary-text-color);
font-size: 13px;
}
.error {
color: var(--error-color, #f44336);
font-size: 13px;
}
.loading,
.empty {
color: var(--secondary-text-color);
font-size: 14px;
padding: 12px 0;
}
.select-all {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--secondary-text-color);
cursor: pointer;
}
.select-all input {
cursor: pointer;
}
.list {
display: flex;
flex-direction: column;
gap: 6px;
overflow-y: auto;
max-height: 50vh;
}
.row {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 8px;
border: 1px solid var(--divider-color);
border-radius: 6px;
cursor: pointer;
}
.row input {
margin-top: 2px;
cursor: pointer;
}
.row-main {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
flex: 1;
}
.row-top {
display: flex;
align-items: center;
gap: 8px;
}
.row-name {
font-weight: 500;
font-size: 13px;
}
.row-sub {
color: var(--secondary-text-color);
font-size: 12px;
}
.row-target {
color: var(--secondary-text-color);
font-size: 12px;
}
.row-part {
color: var(--secondary-text-color);
font-size: 12px;
display: flex;
align-items: center;
gap: 4px;
}
.row-part ha-icon {
--mdc-icon-size: 14px;
}
.new-tag {
font-style: italic;
}
.chip {
font-size: 11px;
padding: 1px 8px;
border-radius: 10px;
white-space: nowrap;
}
.chip-active {
background: var(--error-color, #f44336);
color: #fff;
}
.chip-ok {
background: var(--divider-color);
color: var(--secondary-text-color);
}
.responsible {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--secondary-text-color);
flex-wrap: wrap;
}
.responsible select {
flex: 1;
min-width: 140px;
padding: 4px 6px;
border-radius: 4px;
border: 1px solid var(--divider-color);
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
font-size: 13px;
}
.actions {
display: flex;
justify-content: flex-end;
gap: 8px;
padding-top: 8px;
}
`,a([g({attribute:!1})],r.prototype,"hass",2),a([n()],r.prototype,"_open",2),a([n()],r.prototype,"_loading",2),a([n()],r.prototype,"_adopting",2),a([n()],r.prototype,"_error",2),a([n()],r.prototype,"_sensors",2),a([n()],r.prototype,"_selected",2),a([n()],r.prototype,"_users",2),a([n()],r.prototype,"_responsible",2);customElements.get("maintenance-adopt-problem-sensors-dialog")||customElements.define("maintenance-adopt-problem-sensors-dialog",r);export{r as MaintenanceAdoptProblemSensorsDialog};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.58.0 */
var r=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 e=Date.now();if(!s&&this.usersCache&&e-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=e,this.usersCache}catch(t){return console.error("Failed to fetch users:",t),this.usersCache||[]}}async assignUser(s,e,t){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:s,task_id:e,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}getUser(s){return!s||!this.usersCache?null:this.usersCache.find(e=>e.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{r as a};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.58.0 */
var o=["notes","cost","duration","photo","user"],t={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"};export{o as a,t as b};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.58.0 */
import{n as u}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5LZFNUMR.js";function _(e){return`${e.entry_id??""}\0${e.part_id}`}function l(e,r,s,c){let t=!!e.entry_id&&e.entry_id!==r,a=t?e.entry_id:r,o=s.find(p=>p.entry_id===a),n=(o?.parts||[]).find(p=>p.id===e.part_id)||null,d=t&&o?.object?.name||"",i=n?.name||u("shared_part_unknown",c);return{part:n,foreign:t,ownerName:d,label:d?`${i} (${d})`:i}}function f(e,r,s,c){let{part:t,label:a}=l(e,r,s,c),o=t&&t.stock!==null&&t.stock!==void 0?` (${t.stock}${t.unit?" "+t.unit:""})`:"",n=t?.storage_location?` \u2014 ${t.storage_location}`:"";return`${e.quantity}\xD7 ${a}${o}${n}`}function g(e,r,s,c){let a=(s.find(n=>n.entry_id===r)?.parts||[]).map(n=>({...n})),o=new Set(a.map(n=>_({part_id:n.id})));for(let n of e?.consumes_parts||[]){if(!n.entry_id||n.entry_id===r)continue;let d=_(n);if(o.has(d))continue;o.add(d);let{part:i,ownerName:p}=l(n,r,s,c);a.push({id:n.part_id,name:i?.name||u("shared_part_unknown",c),unit:i?.unit,stock:i?.stock??null,storage_location:i?.storage_location,entry_id:n.entry_id,owner_name:p})}return a}export{_ as a,f as b,g as c};
@@ -0,0 +1,54 @@
/*! maintenance_supporter frontend 2.58.0 */
import{a as t,b as a,c as i,f as l,g as p,i as r}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5LZFNUMR.js";var e=class extends p{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1}_onInput(n){let o=n.target.value;this.value=o,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:o}}))}render(){return i`
<label class="field">
${this.label?i`<span class="label">${this.label}${this.required?i`<span class="req">*</span>`:l}</span>`:l}
<input
.value=${this.value??""}
.type=${this.type}
?required=${this.required}
?disabled=${this.disabled}
placeholder=${this.placeholder}
step=${this.step??l}
min=${this.min??l}
max=${this.max??l}
pattern=${this.pattern??l}
@input=${this._onInput}
@change=${this._onInput}
/>
${this.helper?i`<span class="helper">${this.helper}</span>`:l}
</label>
`}};e.styles=a`
: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; }
.helper {
font-size: 11px;
color: var(--secondary-text-color);
font-style: italic;
}
`,t([r()],e.prototype,"label",2),t([r()],e.prototype,"value",2),t([r()],e.prototype,"placeholder",2),t([r()],e.prototype,"type",2),t([r({type:Boolean})],e.prototype,"required",2),t([r({type:Boolean})],e.prototype,"disabled",2),t([r()],e.prototype,"step",2),t([r()],e.prototype,"min",2),t([r()],e.prototype,"max",2),t([r()],e.prototype,"pattern",2),t([r()],e.prototype,"helper",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",e);
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.58.0 */
import{n as a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5LZFNUMR.js";var s={name:"name",task_type:"maintenance_type",schedule_type:"schedule_type",interval_days:"interval_days",interval_anchor:"interval_anchor",warning_days:"warning_days",last_performed:"last_performed_optional",notes:"notes_optional",documentation_url:"documentation_url_optional",custom_icon:"custom_icon_optional",nfc_tag_id:"nfc_tag_id_optional",responsible_user_id:"responsible_user",entity_slug:"entity_slug",entity_id:"entity_id",area_id:"area_id_optional",manufacturer:"manufacturer_optional",model:"model_optional",serial_number:"serial_number_optional",installation_date:"installation_date_optional",warranty_expiry:"warranty_expiry_optional",checklist:"checklist_steps_optional",reason:"reason",feedback:"feedback",cost:"cost",duration:"duration",description:"description_optional",group_name:"name",group_description:"description_optional",environmental_entity:"environmental_entity_optional",environmental_attribute:"environmental_attribute_optional",trigger_above:"trigger_above",trigger_below:"trigger_below",trigger_for_minutes:"trigger_for_minutes"};function c(r,o){let e=s[r];if(!e)return r;let t=a(e,o);return t&&t!==e?t:r}function d(r){let e=r.match(/data\['([^']+)'\]/)?.[1],t;return(t=r.match(/length of value must be at most (\d+)/))?{field:e,rule:"too_long",param:t[1]}:(t=r.match(/length of value must be at least (\d+)/))?{field:e,rule:"too_short",param:t[1]}:(t=r.match(/value must be at most (\S+)/))?{field:e,rule:"value_too_high",param:t[1]}:(t=r.match(/value must be at least (\S+)/))?{field:e,rule:"value_too_low",param:t[1]}:/required key not provided/.test(r)?{field:e,rule:"required"}:(t=r.match(/expected (\w+)/))?{field:e,rule:"wrong_type",param:t[1]}:/value must be one of/.test(r)?{field:e,rule:"invalid_choice"}:/not a valid value/.test(r)?{field:e,rule:"invalid_value"}:{field:e,rule:"unknown"}}function g(r,o,e){if(e=e??a("action_error",o),typeof r=="string")return r;if(typeof r!="object"||r===null)return e;let t=r,_=t.message||t.error?.message||"";if(!_)return e;let i=d(_),l=i.field?c(i.field,o):"",n=u=>a(u,o).replace("{field}",l).replace("{n}",i.param??"");switch(i.rule){case"too_long":return n("err_too_long");case"too_short":return n("err_too_short");case"value_too_high":return n("err_value_too_high");case"value_too_low":return n("err_value_too_low");case"required":return n("err_required");case"wrong_type":return n("err_wrong_type").replace("{type}",i.param??"");case"invalid_choice":return n("err_invalid_choice");case"invalid_value":return n("err_invalid_value");default:return _||e}}export{g as a};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.58.0 */
function m(n,t,e){let o=new Blob([n],{type:e}),r=URL.createObjectURL(o),a=document.createElement("a");a.href=r,a.download=t,a.target="_blank",a.rel="noopener",a.style.display="none",document.body.appendChild(a),a.dispatchEvent(new MouseEvent("click")),document.body.removeChild(a),setTimeout(()=>URL.revokeObjectURL(r),6e4)}function i(n,t){let e=document.createElement("a");e.href=n,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)}async function c(n,t,e=300){return(await n.connection.sendMessagePromise({type:"auth/sign_path",path:t,expires:e})).path}async function s(n,t,e=300){return c(n,`/api/maintenance_supporter/document/${t}`,e)}async function b(n,t,e=""){let o=window.open("about:blank","_blank");try{let r=await s(n,t);o&&(o.location.href=new URL(r+e,window.location.origin).href)}catch(r){throw o&&o.close(),r}}async function g(n,t,e){i(await s(n,t,30),e)}var d=[{key:"name",labelKey:"name",required:!0},{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=d.map(n=>n.key),l=["name","manufacturer","model","serial_number","installation_date","warranty_expiry","area_id","task_count","actions"];function _(n){if(!Array.isArray(n))return[...l];let t=new Set,e=[];for(let o of n)typeof o=="string"&&u.includes(o)&&!t.has(o)&&(t.add(o),e.push(o));return e.length?(e.includes("name")||e.unshift("name"),e):[...l]}export{m as a,i as b,c,s as d,b as e,g as f,d as g,l as h,_ as i};
@@ -0,0 +1,292 @@
/*! maintenance_supporter frontend 2.58.0 */
import{b as m}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-6HOHOTKU.js";import{a as _}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-6LSK7BBU.js";import{a as g}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-F567KB5W.js";import{a as s,b,c as a,f as d,g as v,i as n,j as l,n as r,y as k}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5LZFNUMR.js";var i=class extends v{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";this.restockDefault=null;this.restockUnitCost=null;this.currencySymbol="";this.parts=[];this.consumesParts=[];this.consumesInfo=[];this.requiredFields=[];this._open=!1;this._notes="";this._cost="";this._duration="";this._loading=!1;this._error="";this._checklistState={};this._feedback="needed";this._photoDocId="";this._photoPreview="";this._photoUploading=!1;this._readingValue="";this._restockQty="";this._usedParts={};this.checklistPrefill={}}open(){this._open||(this._open=!0,this._notes="",this._cost="",this._duration="",this._error="",this._checklistState=Object.fromEntries(this.checklist.map((e,t)=>[String(t),!!this.checklistPrefill[e]]).filter(([,e])=>e)),this._feedback="needed",this._photoDocId="",this._photoPreview="",this._photoUploading=!1,this._readingValue="",this._restockQty=this.restockDefault!==null?String(this.restockDefault):"",this._usedParts=Object.fromEntries(this.consumesParts.map(e=>[_(e),{...e}])))}_toggleCheck(e){let t=String(e);this._checklistState={...this._checklistState,[t]:!this._checklistState[t]}}_setFeedback(e){this._feedback=e}async _onPhotoInput(e){let t=e.target,o=t.files?.[0];if(t.value="",!!o){this._photoUploading=!0,this._error="";try{let c=new FormData;c.append("entry_id",this.entryId),c.append("tags","photo"),c.append("file",o,o.name);let p=await fetch("/api/maintenance_supporter/document/upload",{method:"POST",headers:{Authorization:`Bearer ${this.hass.auth?.data?.access_token??""}`},body:c});if(!p.ok){this._error=p.status===413?r("doc_too_large",this.lang):r("doc_upload_failed",this.lang);return}let u=await p.json();u.id&&(this._photoDocId=u.id,this._photoPreview=URL.createObjectURL(o))}catch{this._error=r("doc_upload_failed",this.lang)}finally{this._photoUploading=!1}}}_removePhoto(){this._photoPreview&&URL.revokeObjectURL(this._photoPreview),this._photoDocId="",this._photoPreview=""}async _complete(){this._loading=!0,this._error="";try{let e={type:"maintenance_supporter/task/complete",entry_id:this.entryId,task_id:this.taskId};if(this._notes&&(e.notes=this._notes),this._cost){let t=parseFloat(this._cost);!isNaN(t)&&t>=0&&(e.cost=t)}if(this._duration){let t=parseInt(this._duration,10);!isNaN(t)&&t>=0&&(e.duration=t)}if(this.checklist.length>0&&(e.checklist_state=this._checklistState),this.adaptiveEnabled&&(e.feedback=this._feedback),this._photoDocId&&(e.photo_doc_id=this._photoDocId),this._readingValue!==""){let t=parseFloat(this._readingValue);isNaN(t)||(e.reading_value=t)}if(this.restockDefault!==null&&this._restockQty!==""){let t=parseFloat(this._restockQty);!isNaN(t)&&t>=1&&(e.restock_quantity=t)}this.parts.length>0&&(e.used_parts=Object.values(this._usedParts).filter(t=>Number.isFinite(t.quantity)&&t.quantity>0).map(t=>t.entry_id?{part_id:t.part_id,quantity:t.quantity,entry_id:t.entry_id}:{part_id:t.part_id,quantity:t.quantity})),await this.hass.connection.sendMessagePromise(e),this._open=!1,this.dispatchEvent(new CustomEvent("task-completed"))}catch(e){this._error=g(e,this.lang,r("save_error",this.lang))}finally{this._loading=!1}}get _missingRequired(){let e={notes:this._notes.trim()!=="",cost:this._cost.trim()!=="",duration:this._duration.trim()!=="",photo:this._photoDocId!=="",user:!!this.hass?.user};return this.requiredFields.filter(t=>!e[t])}_req(e){return this.requiredFields.includes(e)?a`<span class="req-mark" aria-hidden="true">*</span>`:d}_partsCostSuggestion(){if(this.restockDefault!==null){let o=parseFloat(this._restockQty);return this.restockUnitCost==null||!Number.isFinite(o)||o<=0?null:Math.round(this.restockUnitCost*o*100)/100}if(!this.parts.length)return null;let e=0,t=!1;for(let o of Object.values(this._usedParts)){let c=this.parts.find(p=>_({part_id:p.id,entry_id:p.entry_id})===_(o));c?.cost!=null&&(e+=c.cost*(o.quantity||1),t=!0)}return t?Math.round(e*100)/100:null}_renderCostSuggestion(e){if(this._cost.trim()!=="")return d;let t=this._partsCostSuggestion();if(t==null||t<=0)return d;let o=`${t.toFixed(2)}${this.currencySymbol?` ${this.currencySymbol}`:""}`;return a`<button
type="button"
class="cost-suggestion"
@click=${()=>this._cost=t.toFixed(2)}
>${r("cost_from_parts",e).replace("{amount}",o)}</button>`}_close(){this._open=!1}render(){if(!this._open)return a``;let e=this.lang||this.hass?.language||"en";return a`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${r("complete_title",e)}${this.taskName}</div>
<div class="content">
${this._error?a`<div class="error">${this._error}</div>`:d}
${this.checklist.length>0?a`
<div class="checklist-section">
<label class="checklist-label">${r("checklist",e)}</label>
${this.checklist.map((t,o)=>a`
<label class="checklist-item" @click=${()=>this._toggleCheck(o)}>
<input type="checkbox" .checked=${!!this._checklistState[String(o)]} />
<span>${t}</span>
</label>
`)}
</div>
`:d}
${this.taskType==="reading"?a`
<label class="field">
<span class="field-label">${r("reading_value_label",e)}${this.readingUnit?` (${this.readingUnit})`:""}</span>
<input type="number" step="any" class="field-input"
.value=${this._readingValue}
@input=${t=>this._readingValue=t.target.value} />
</label>`:d}
${this.parts.length?a`<div class="used-parts">
<span class="field-label">${r("complete_parts_used",e)}</span>
${this.parts.map(t=>{let o=_({part_id:t.id,entry_id:t.entry_id}),c=this._usedParts[o],p=c!==void 0,u=t.entry_id?{part_id:t.id,quantity:1,entry_id:t.entry_id}:{part_id:t.id,quantity:1};return a`<div class="used-part-row">
<label class="used-part-check">
<input type="checkbox" .checked=${p}
@change=${f=>{let h={...this._usedParts};f.target.checked?h[o]=h[o]||u:delete h[o],this._usedParts=h}} />
<span
>${t.name}${t.owner_name?a`<span class="used-part-owner"> (${t.owner_name})</span>`:d}${t.stock!==null&&t.stock!==void 0?` (${t.stock}${t.unit?" "+t.unit:""})`:""}</span
>
</label>
${p?a`<input class="used-part-qty" type="number" min="0.01" max="999" step="0.01"
.value=${String(c.quantity)}
@input=${f=>{let h=parseFloat(f.target.value);this._usedParts={...this._usedParts,[o]:{...u,quantity:Number.isFinite(h)&&h>=.01?h:1}}}} />`:d}
</div>`})}
</div>`:this.consumesInfo.length?a`<div class="consumes-hint">
${this.consumesInfo.map(t=>a`<div>${t}</div>`)}
</div>`:d}
${this.restockDefault!==null?a`
<label class="field">
<span class="field-label">${r("restock_quantity_label",e)}</span>
<input type="number" step="0.01" min="0.01" class="field-input"
.value=${this._restockQty}
@input=${t=>this._restockQty=t.target.value} />
</label>`:d}
<!-- Native <input>s rather than <ha-textfield>: when this dialog
is opened from a Lovelace card via dialog-mount, ha-textfield
isn't yet registered (HA loads it lazily when its own panels
need it) so the elements render with zero height and the user
only sees the title + Cancel/Complete buttons — the original
bug report. Native inputs always render. -->
<label class="field">
<span class="field-label">${r("notes_optional",e)}${this._req("notes")}</span>
<input type="text" class="field-input"
.value=${this._notes}
@input=${t=>this._notes=t.target.value} />
</label>
<label class="field">
<span class="field-label">${r("cost_optional",e)}${this._req("cost")}</span>
<input type="number" step="0.01" min="0" class="field-input"
.value=${this._cost}
@input=${t=>this._cost=t.target.value} />
${this._renderCostSuggestion(e)}
</label>
<label class="field">
<span class="field-label">${r("duration_minutes",e)}${this._req("duration")}</span>
<input type="number" step="0.01" min="0" class="field-input"
.value=${this._duration}
@input=${t=>this._duration=t.target.value} />
</label>
<div class="field">
<span class="field-label">${r("completion_photo_optional",e)}${this._req("photo")}</span>
${this._photoPreview?a`
<div class="photo-preview">
<img src=${this._photoPreview} alt="" />
<button type="button" class="photo-remove" @click=${this._removePhoto}
title="${r("remove",e)}">✕</button>
</div>`:a`
<label class="photo-pick">
<ha-icon icon="mdi:camera"></ha-icon>
<span>${this._photoUploading?r("uploading",e):r("add_photo",e)}</span>
<input type="file" accept="image/*" capture="environment"
?disabled=${this._photoUploading}
@change=${this._onPhotoInput} />
</label>`}
</div>
${this.adaptiveEnabled?a`
<div class="feedback-section">
<label class="feedback-label">${r("was_maintenance_needed",e)}</label>
<div class="feedback-buttons">
<button
class="feedback-btn ${this._feedback==="needed"?"selected":""}"
@click=${()=>this._setFeedback("needed")}
>${r("feedback_needed",e)}</button>
<button
class="feedback-btn ${this._feedback==="not_needed"?"selected":""}"
@click=${()=>this._setFeedback("not_needed")}
>${r("feedback_not_needed",e)}</button>
<button
class="feedback-btn ${this._feedback==="not_sure"?"selected":""}"
@click=${()=>this._setFeedback("not_sure")}
>${r("feedback_not_sure",e)}</button>
</div>
</div>
`:d}
</div>
<div class="dialog-actions">
<ha-button appearance="plain" @click=${this._close}>
${r("cancel",e)}
</ha-button>
<ha-button
@click=${this._complete}
.disabled=${this._loading||this._missingRequired.length>0}
title=${this._missingRequired.length?this._missingRequired.map(t=>r("err_required",e).replace("{field}",r(m[t]??t,e))).join(" \xB7 "):""}
>
${this._loading?r("completing",e):r("complete",e)}
</ha-button>
</div>
</ha-dialog>
`}};i.styles=[k,b`
.req-mark {
color: var(--error-color, #f44336);
margin-left: 2px;
font-weight: 600;
}
/* #104: one-click cost suggestion from parts — quiet link-style chip. */
.cost-suggestion {
align-self: flex-start;
margin-top: 4px;
padding: 0;
border: none;
background: none;
color: var(--primary-color);
font-size: 12.5px;
cursor: pointer;
text-decoration: underline dotted;
text-underline-offset: 2px;
}
.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;
}
.consumes-hint {
font-size: 13px;
color: var(--secondary-text-color);
border-left: 3px solid var(--primary-color);
padding: 4px 8px;
margin: 4px 0 8px;
}
/* #99: editable per-completion parts selection */
.used-parts { margin: 4px 0 8px; display: flex; flex-direction: column; gap: 4px; }
.used-part-row { display: flex; align-items: center; gap: 8px; }
.used-part-check {
display: flex; align-items: center; gap: 6px; flex: 1;
font-size: 13px; cursor: pointer;
}
.used-part-check input { cursor: pointer; }
/* #111: whose stock this row draws on. Muted but never omitted — an
unlabelled foreign pool is indistinguishable from an own part. */
.used-part-owner { color: var(--secondary-text-color); }
.used-part-qty {
width: 76px; padding: 4px 6px; border-radius: 4px; font: inherit; font-size: 13px;
border: 1px solid var(--divider-color);
background: var(--card-background-color);
color: var(--primary-text-color);
}
.error {
color: var(--error-color, #f44336);
font-size: 13px;
}
/* .field/.field-label/.field-input come from nativeFieldStyles */
.photo-pick {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border: 1px dashed var(--divider-color);
border-radius: 8px;
cursor: pointer;
font-size: 13px;
color: var(--secondary-text-color);
width: fit-content;
}
.photo-pick:hover { border-color: var(--primary-color); }
.photo-pick input[type="file"] { display: none; }
.photo-preview {
position: relative;
width: fit-content;
}
.photo-preview img {
max-width: 160px;
max-height: 160px;
border-radius: 8px;
display: block;
}
.photo-remove {
position: absolute;
top: -8px;
right: -8px;
width: 24px;
height: 24px;
border-radius: 50%;
border: none;
background: var(--error-color, #db4437);
color: #fff;
cursor: pointer;
font-size: 12px;
line-height: 1;
}
.checklist-section {
display: flex;
flex-direction: column;
gap: 8px;
padding: 8px 0;
border-bottom: 1px solid var(--divider-color);
margin-bottom: 4px;
}
.checklist-label {
font-weight: 500;
font-size: 13px;
color: var(--secondary-text-color);
}
.checklist-item {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
padding: 4px 0;
font-size: 14px;
}
.checklist-item input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.feedback-section {
display: flex;
flex-direction: column;
gap: 8px;
padding: 8px 0;
border-top: 1px solid var(--divider-color);
}
.feedback-label {
font-weight: 500;
font-size: 13px;
color: var(--secondary-text-color);
}
.feedback-buttons {
display: flex;
gap: 8px;
}
.feedback-btn {
flex: 1;
padding: 8px 12px;
border: 1px solid var(--divider-color);
border-radius: 8px;
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
font-size: 13px;
cursor: pointer;
text-align: center;
transition: all 0.2s;
}
.feedback-btn:hover {
background: var(--secondary-background-color, #f5f5f5);
}
.feedback-btn.selected {
background: var(--primary-color);
color: var(--text-primary-color, #fff);
border-color: var(--primary-color);
}
`],s([n({attribute:!1})],i.prototype,"hass",2),s([n()],i.prototype,"entryId",2),s([n()],i.prototype,"taskId",2),s([n()],i.prototype,"taskName",2),s([n()],i.prototype,"lang",2),s([n({type:Array})],i.prototype,"checklist",2),s([n({type:Boolean})],i.prototype,"adaptiveEnabled",2),s([n()],i.prototype,"taskType",2),s([n()],i.prototype,"readingUnit",2),s([n({attribute:!1})],i.prototype,"restockDefault",2),s([n({attribute:!1})],i.prototype,"restockUnitCost",2),s([n()],i.prototype,"currencySymbol",2),s([n({attribute:!1})],i.prototype,"parts",2),s([n({attribute:!1})],i.prototype,"consumesParts",2),s([n({type:Array})],i.prototype,"consumesInfo",2),s([n({type:Array})],i.prototype,"requiredFields",2),s([l()],i.prototype,"_open",2),s([l()],i.prototype,"_notes",2),s([l()],i.prototype,"_cost",2),s([l()],i.prototype,"_duration",2),s([l()],i.prototype,"_loading",2),s([l()],i.prototype,"_error",2),s([l()],i.prototype,"_checklistState",2),s([l()],i.prototype,"_feedback",2),s([l()],i.prototype,"_photoDocId",2),s([l()],i.prototype,"_photoPreview",2),s([l()],i.prototype,"_photoUploading",2),s([l()],i.prototype,"_readingValue",2),s([l()],i.prototype,"_restockQty",2),s([l()],i.prototype,"_usedParts",2),s([n({attribute:!1})],i.prototype,"checklistPrefill",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",i);export{i as MaintenanceCompleteDialog};
@@ -0,0 +1,143 @@
/*! maintenance_supporter frontend 2.58.0 */
import"/maintenance_supporter_panelfiles/panel-chunks/chunk-7UOPU2CL.js";import{a as u}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-F567KB5W.js";import{a as r,b as _,c as l,f as o,g as p,i as d,j as s,n as i,o as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5LZFNUMR.js";var e=class extends p{constructor(){super(...arguments);this.objects=[];this._open=!1;this._loading=!1;this._error="";this._name="";this._manufacturer="";this._model="";this._serialNumber="";this._areaId="";this._installationDate="";this._warrantyExpiry="";this._documentationUrl="";this._notes="";this._haDeviceId="";this._parentEntryId="";this._entryId=null}get _lang(){return h(this.hass)}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._error="",this._open=!0}openEdit(a,n){this._entryId=a,this._name=n.name||"",this._manufacturer=n.manufacturer||"",this._model=n.model||"",this._serialNumber=n.serial_number||"",this._areaId=n.area_id||"",this._installationDate=n.installation_date||"",this._warrantyExpiry=n.warranty_expiry||"",this._documentationUrl=n.documentation_url||"",this._notes=n.notes||"",this._haDeviceId=n.ha_device_id||"",this._parentEntryId=n.parent_entry_id||"",this._error="",this._open=!0}async _save(){if(!this._loading&&this._name.trim()){this._loading=!0,this._error="";try{this._entryId?await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/update",entry_id:this._entryId,name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}):await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/create",name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}),this._open=!1,this.dispatchEvent(new CustomEvent("object-saved"))}catch(a){this._error=u(a,this._lang,i("save_error",this._lang))}finally{this._loading=!1}}}_parentChoices(){return(this.objects||[]).filter(a=>a.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return l``;let a=this._lang,n=this._entryId?i("edit_object",a):i("new_object",a);return l`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${n}</div>
<div class="content">
${this._error?l`<div class="error">${this._error}</div>`:o}
<ms-textfield
label="${i("name",a)}"
required
.value=${this._name}
@input=${t=>this._name=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("manufacturer_optional",a)}"
.value=${this._manufacturer}
@input=${t=>this._manufacturer=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("model_optional",a)}"
.value=${this._model}
@input=${t=>this._model=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("serial_number_optional",a)}"
.value=${this._serialNumber}
@input=${t=>this._serialNumber=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("documentation_url_optional",a)}"
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",a)}"
.value=${this._areaId}
@value-changed=${t=>this._areaId=t.detail.value||""}
></ha-area-picker>
<ms-textfield
label="${i("installation_date_optional",a)}"
type="date"
.value=${this._installationDate}
@input=${t=>this._installationDate=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("warranty_expiry_optional",a)}"
type="date"
.value=${this._warrantyExpiry}
@input=${t=>this._warrantyExpiry=t.target.value}
></ms-textfield>
<ha-form
.hass=${this.hass}
.data=${{device:this._haDeviceId||void 0}}
.schema=${[{name:"device",selector:{device:{}}}]}
.computeLabel=${()=>i("link_device_optional",a)}
@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",a)}</span>
<select
class="parent-select"
.value=${this._parentEntryId}
@change=${t=>this._parentEntryId=t.target.value}
>
<option value="" ?selected=${!this._parentEntryId}>
${i("parent_none",a)}
</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",a)}</span>
<textarea
rows="3"
.value=${this._notes}
@input=${t=>this._notes=t.target.value}
></textarea>
</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>
`}};e.styles=_`
.dialog-title {
font-size: 18px;
font-weight: 500;
padding-bottom: 12px;
}
.content {
display: flex;
flex-direction: column;
gap: 16px;
min-width: 300px;
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
padding-top: 16px;
}
ms-textfield {
display: block;
}
.textarea-field {
display: flex; flex-direction: column; gap: 4px;
}
.textarea-label {
font-size: 12px; color: var(--secondary-text-color, #888); font-weight: 500;
}
.textarea-field textarea {
padding: 8px 10px; font-size: 14px; font-family: inherit;
background: var(--secondary-background-color, rgba(0,0,0,0.06));
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
resize: vertical;
}
.textarea-field textarea:focus {
outline: none; border-color: var(--primary-color);
}
.parent-select {
padding: 8px 10px; font-size: 14px; font-family: inherit;
background: var(--secondary-background-color, rgba(0,0,0,0.06));
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
}
.error {
color: var(--error-color, #f44336);
font-size: 13px;
}
`,r([d({attribute:!1})],e.prototype,"hass",2),r([d({attribute:!1})],e.prototype,"objects",2),r([s()],e.prototype,"_open",2),r([s()],e.prototype,"_loading",2),r([s()],e.prototype,"_error",2),r([s()],e.prototype,"_name",2),r([s()],e.prototype,"_manufacturer",2),r([s()],e.prototype,"_model",2),r([s()],e.prototype,"_serialNumber",2),r([s()],e.prototype,"_areaId",2),r([s()],e.prototype,"_installationDate",2),r([s()],e.prototype,"_warrantyExpiry",2),r([s()],e.prototype,"_documentationUrl",2),r([s()],e.prototype,"_notes",2),r([s()],e.prototype,"_haDeviceId",2),r([s()],e.prototype,"_parentEntryId",2),r([s()],e.prototype,"_entryId",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",e);export{e as MaintenanceObjectDialog};
@@ -0,0 +1,213 @@
/*! maintenance_supporter frontend 2.58.0 */
import{a,b as v,c as n,f as g,g as b,i as m,j as c,n as t}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5LZFNUMR.js";function p(l){return l.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function x(l){return!l.startsWith("data:image/svg+xml,")&&!l.startsWith("data:image/png;base64,")?"":p(l)}function $(l){return l.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var r=class extends b{constructor(){super(...arguments);this.lang="en";this._open=!1;this._loading=!1;this._error="";this._viewResult=null;this._completeResult=null;this._urlMode="companion";this._entryId="";this._taskId=null;this._objectName="";this._taskName="";this._generateSeq=0}openForObject(e,i){this._entryId=e,this._taskId=null,this._objectName=i,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}openForTask(e,i,o,s){this._entryId=e,this._taskId=i,this._objectName=o,this._taskName=s,this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}async _generate(){let e=++this._generateSeq;this._loading=!0,this._error="",this._viewResult=null,this._completeResult=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 o=[this.hass.connection.sendMessagePromise({...i,action:"view"})];this._taskId&&o.push(this.hass.connection.sendMessagePromise({...i,action:"complete"}));let s=await Promise.all(o);if(e!==this._generateSeq)return;this._viewResult=s[0],s.length>1&&(this._completeResult=s[1])}catch(i){if(e!==this._generateSeq)return;let o=i?.code,s=i?.message;this._error=o==="no_url"||typeof s=="string"&&s.includes("No Home Assistant URL")?t("qr_error_no_url",this.lang):t("qr_error",this.lang)}finally{e===this._generateSeq&&(this._loading=!1)}}_setUrlMode(e){this._urlMode!==e&&(this._urlMode=e,this._generate())}_print(){if(!this._viewResult)return;let e=this._viewResult,i=e.label.task_name?`${e.label.object_name} \u2014 ${e.label.task_name}`:e.label.object_name,o=[e.label.manufacturer,e.label.model].filter(Boolean).join(" "),s=window.open("","_blank","width=600,height=500");if(!s)return;let h=this.lang||"en",d=p(i),u=p(o),_=!!this._completeResult,f=p(t("qr_action_view",h)),w=p(t("qr_action_complete",h));s.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
<meta name="color-scheme" content="light">
<title>${d}</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:${_?"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>${d}</h2>
${u?`<div class="sub">${u}</div>`:""}
<div class="qr-row">
<div class="qr-col">
<img src="${x(this._viewResult.svg_data_uri)}" alt="QR Info" />
<div class="qr-label">${f}</div>
</div>
${_?`<div class="qr-col">
<img src="${x(this._completeResult.svg_data_uri)}" alt="QR Complete" />
<div class="qr-label">${w}</div>
</div>`:""}
</div>
<div class="url">${p(this._viewResult.url)}</div>
<script>setTimeout(()=>window.print(),300)<\/script>
</body></html>`),s.document.close()}_downloadSvg(e,i){let o=decodeURIComponent(e.svg_data_uri.replace("data:image/svg+xml,","")),s=new Blob([o],{type:"image/svg+xml"}),h=URL.createObjectURL(s),d=document.createElement("a");d.href=h;let u=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;d.download=`qr-${$(u)}-${i}.svg`,d.click(),URL.revokeObjectURL(h)}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._error="",this._loading=!1}render(){if(!this._open)return n``;let e=this.lang||this.hass?.language||"en",i=this._taskName?`${t("qr_code",e)}: ${this._objectName} \u2014 ${this._taskName}`:`${t("qr_code",e)}: ${this._objectName}`,o=!!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">${t("qr_generating",e)}</div>`:this._error?n`<div class="error">${this._error}</div>`:o?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">${t("qr_action_view",e)}</div>
<button class="dl-btn"
@click=${()=>this._downloadSvg(this._viewResult,"info")}>
<ha-icon icon="mdi:download"></ha-icon>
${t("qr_download",e)}
</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">${t("qr_action_complete",e)}</div>
<button class="dl-btn"
@click=${()=>this._downloadSvg(this._completeResult,"complete")}>
<ha-icon icon="mdi:download"></ha-icon>
${t("qr_download",e)}
</button>
</div>
`:g}
</div>
<div class="url-display">${this._viewResult.url}</div>
`:g}
<div class="action-row">
<label>${t("qr_url_mode",e)}</label>
<div class="action-toggle">
<button class="toggle-btn ${this._urlMode==="companion"?"active":""}"
@click=${()=>this._setUrlMode("companion")}>${t("qr_mode_companion",e)}</button>
<button class="toggle-btn ${this._urlMode==="local"?"active":""}"
@click=${()=>this._setUrlMode("local")}>${t("qr_mode_local",e)}</button>
<button class="toggle-btn ${this._urlMode==="server"?"active":""}"
@click=${()=>this._setUrlMode("server")}>${t("qr_mode_server",e)}</button>
</div>
</div>
</div>
<div class="dialog-actions">
<ha-button appearance="plain" @click=${this._close}>
${t("cancel",e)}
</ha-button>
<ha-button
@click=${this._print}
.disabled=${!o}
>
${t("qr_print",e)}
</ha-button>
</div>
</ha-dialog>
`}};r.styles=v`
.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;
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 {
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([m({attribute:!1})],r.prototype,"hass",2),a([m()],r.prototype,"lang",2),a([c()],r.prototype,"_open",2),a([c()],r.prototype,"_loading",2),a([c()],r.prototype,"_error",2),a([c()],r.prototype,"_viewResult",2),a([c()],r.prototype,"_completeResult",2),a([c()],r.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",r);export{r as MaintenanceQrDialog};
@@ -0,0 +1,146 @@
/*! maintenance_supporter frontend 2.58.0 */
import{a as g}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-F567KB5W.js";import{a as l,b as m,c as i,f as h,g as v,i as u,j as d,n as p,o as f,q as x}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5LZFNUMR.js";var a=class extends v{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._objects=[];this._localeReady=!1;this._toggle=t=>{let e=new Set(this._selected);e.has(t)?e.delete(t):e.add(t),this._selected=e};this._adopt=async()=>{if(!(this._selected.size===0||this._adopting)){this._adopting=!0,this._error="";try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/integration_setups/adopt",selections:[...this._selected].map(e=>{let r={device_id:e},c=this._targets.get(e);c&&(r.entry_id=c);let s=this._setups.find(n=>n.device_id===e);for(let n of s?.tasks??[]){let o=this._baselines.get(`${e} ${n.task_name}`),_=o?parseFloat(o):NaN;!isNaN(_)&&_>=0&&((r.baselines??={})[n.task_name]=_)}return r})});this.dispatchEvent(new CustomEvent("integration-setups-adopted",{bubbles:!0,composed:!0,detail:t})),this._open=!1}catch(t){this._error=g(t,this._lang)}finally{this._adopting=!1}}}}get _lang(){return f(this.hass)}updated(t){t.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,x(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._setups=[],this._selected=new Set;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/integration_setups/discover"});this._setups=t.setups||[],this._selected=new Set(this._setups.map(e=>e.device_id)),this._baselines=new Map,this._targets=new Map;try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"});this._objects=(e.objects||[]).map(r=>({entry_id:r.entry_id,name:r.object?.name||r.entry_id})).sort((r,c)=>r.name.localeCompare(c.name))}catch{this._objects=[]}}catch(t){this._error=g(t,this._lang)}finally{this._loading=!1}}_close(){this._open=!1}render(){if(!this._open)return i``;let t=this._lang;return i`
<div class="overlay" @click=${this._close}>
<div class="card" @click=${e=>e.stopPropagation()}>
<div class="title">${p("setups_title",t)}</div>
<div class="hint">${p("setups_hint",t)}</div>
${this._error?i`<div class="error">${this._error}</div>`:h}
${this._loading?i`<div class="loading">…</div>`:this._setups.length===0?i`<div class="empty">${p("setups_none",t)}</div>`:i`
<div class="list">
${this._setups.map(e=>{let r=this._selected.has(e.device_id),c=[e.integration_name,e.area_name].filter(Boolean).join(" \xB7 ");return i`
<label class="row">
<input
type="checkbox"
.checked=${r}
@change=${()=>this._toggle(e.device_id)}
/>
<div class="row-main">
<div class="row-top">
<span class="row-name">${e.device_name}</span>
</div>
<div class="row-sub">${c}</div>
<div class="row-target" @click=${s=>s.preventDefault()}>
→
${r&&this._objects.length>0?i`
<select
class="target-select"
@change=${s=>{let n=new Map(this._targets),o=s.target.value;o?n.set(e.device_id,o):n.delete(e.device_id),this._targets=n}}
>
<option value="" ?selected=${!this._targets.get(e.device_id)}>
${e.suggested_entry_id?e.suggested_object_name:p("setups_target_new",t).replace("{name}",e.suggested_object_name)}
</option>
${this._objects.filter(s=>s.entry_id!==e.suggested_entry_id).map(s=>i`<option
value=${s.entry_id}
?selected=${this._targets.get(e.device_id)===s.entry_id}
>
${s.name}
</option>`)}
</select>
`:i`${e.suggested_object_name}${e.suggested_entry_id?h:i` <span class="new-tag">${p("adopt_problem_new_object",t)}</span>`}`}
</div>
<div class="row-tasks">
${e.tasks.map(s=>i`<span class="chip" title=${s.entity_ids.join(", ")}>
<ha-icon icon="mdi:link-variant"></ha-icon>${s.task_name_localized||s.task_name}
</span>`)}
</div>
${r?e.tasks.filter(s=>s.direction==="usage_delta").map(s=>{let n=`${e.device_id} ${s.task_name}`;return i`
<div class="baseline-field" @click=${o=>o.preventDefault()}>
<span class="baseline-label"
>${s.task_name_localized||s.task_name} —
${p("setups_baseline_hint",t)}</span
>
<input
type="number"
step="any"
min="0"
.value=${this._baselines.get(n)??""}
@click=${o=>o.preventDefault()}
@input=${o=>{let _=new Map(this._baselines);_.set(n,o.target.value),this._baselines=_}}
/>
</div>
`}):h}
</div>
</label>
`})}
</div>
`}
<div class="actions">
<ha-button appearance="plain" @click=${this._close}>
${p("cancel",t)}
</ha-button>
<ha-button
@click=${this._adopt}
.disabled=${this._selected.size===0||this._adopting}
>
${p("setups_adopt",t)}
</ha-button>
</div>
</div>
</div>
`}};a.styles=m`
.overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.card {
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 12px;
min-width: min(360px, calc(100vw - 24px));
max-width: 560px;
width: 90vw;
max-height: 80vh;
overflow: hidden;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}
.title { font-size: 18px; font-weight: 500; }
.hint { color: var(--secondary-text-color); font-size: 13px; }
.error { color: var(--error-color, #f44336); font-size: 13px; }
.loading, .empty { color: var(--secondary-text-color); font-size: 14px; padding: 12px 0; }
.list { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; max-height: 50vh; }
.row {
display: flex; align-items: flex-start; gap: 10px; padding: 8px;
border: 1px solid var(--divider-color); border-radius: 6px; cursor: pointer;
}
.row input { margin-top: 2px; cursor: pointer; }
.row-main { display: flex; flex-direction: column; gap: 3px; 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; }
.new-tag { font-style: italic; }
.row-tasks { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.chip {
display: inline-flex; align-items: center; gap: 4px;
font-size: 11px; padding: 2px 8px; border-radius: 10px;
background: var(--secondary-background-color, rgba(0, 0, 0, 0.06));
color: var(--primary-text-color); white-space: nowrap;
}
.chip ha-icon { --mdc-icon-size: 12px; color: var(--primary-color); }
.target-select {
font-size: 12px; padding: 2px 4px; max-width: 100%;
border: 1px solid var(--divider-color); border-radius: 4px;
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
}
.baseline-field {
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
margin-top: 4px; 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; }
`,l([u({attribute:!1})],a.prototype,"hass",2),l([d()],a.prototype,"_open",2),l([d()],a.prototype,"_loading",2),l([d()],a.prototype,"_adopting",2),l([d()],a.prototype,"_error",2),l([d()],a.prototype,"_setups",2),l([d()],a.prototype,"_selected",2),l([d()],a.prototype,"_baselines",2),l([d()],a.prototype,"_targets",2),l([d()],a.prototype,"_objects",2);customElements.get("maintenance-suggested-setups-dialog")||customElements.define("maintenance-suggested-setups-dialog",a);export{a as MaintenanceSuggestedSetupsDialog};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,125 @@
/*! maintenance_supporter frontend 2.58.0 */
import{a as m,b as f}from"./chunk-OVB7GYEL.js";import{a as u,b as l,d as h,e as b,f as y,g as o,h as g,i as a,j as v,l as _,u as p}from"./chunk-UF6NLI3A.js";import{a as n}from"./chunk-PAZTCVYX.js";var k=80,s=class extends b{constructor(){super(...arguments);this._config={type:""};this._status=null;this._busy=!1;this._error="";this._localMonthly="";this._localYearly="";this._dirty=!1;this._loaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return v(this.hass)}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._loaded&&(this._loaded=!0,this._load(),_(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/budget_status"});this._status=t,this._localMonthly=t.monthly_budget?String(t.monthly_budget):"",this._localYearly=t.yearly_budget?String(t.yearly_budget):"",this._dirty=!1}catch(t){this._error=p(t,this._lang)}}async _save(){if(this._isAdmin){this._busy=!0,this._error="";try{let t=parseFloat(this._localMonthly),r=parseFloat(this._localYearly),i={};!isNaN(t)&&t>=0&&(i.budget_monthly=t),!isNaN(r)&&r>=0&&(i.budget_yearly=r),await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/global/update",settings:i}),await this._load()}catch(t){this._error=p(t,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_budget"),window.dispatchEvent(new CustomEvent("location-changed"))}render(){let t=this._lang,r=this._status;if(!r)return l`<ha-card><div class="loading">${a("loading",t)||"Loading\u2026"}</div></ha-card>`;let i=r.currency_symbol||g,x=r.alert_threshold_pct??k,$=[{label:a("budget_monthly",t)||"Monthly",spent:r.monthly_spent||0,budget:r.monthly_budget||0},{label:a("budget_yearly",t)||"Yearly",spent:r.yearly_spent||0,budget:r.yearly_budget||0}];return l`
<ha-card>
<div class="card-content">
<div class="header">
<div class="title">
<span class="emoji">💰</span>
<span>${this._config.title||a("settings_budget",t)||"Budget"}</span>
</div>
<span class="currency">${i}</span>
</div>
${this._error?l`<div class="error">${this._error}</div>`:h}
${$.map(e=>{if(!(e.budget>0))return l`
<div class="track spent-only">
<div class="track-label-row">
<label>${e.label}</label>
<span class="track-numbers ok">${e.spent.toFixed(0)} ${i}</span>
</div>
</div>
`;let d=Math.min(100,Math.max(0,e.spent/e.budget*100)),c=d>=100?"danger":d>=x?"warning":"ok";return l`
<div class="track">
<div class="track-label-row">
<label>${e.label}</label>
<span class="track-numbers ${c}">
${e.spent.toFixed(0)} / ${e.budget.toFixed(0)} ${i}
</span>
</div>
<div class="bar"><div class="bar-fill ${c}" style="width:${d}%"></div></div>
</div>
`})}
${this._isAdmin?l`
<div class="inputs-row">
<div class="input-field">
<label>${a("budget_monthly_set",t)||"Set monthly"}</label>
<div class="input-wrap">
<input type="number" min="0" step="1"
.value=${this._localMonthly}
?disabled=${this._busy}
@input=${e=>{this._localMonthly=e.target.value,this._dirty=!0}} />
<span class="input-suffix">${i}</span>
</div>
</div>
<div class="input-field">
<label>${a("budget_yearly_set",t)||"Set yearly"}</label>
<div class="input-wrap">
<input type="number" min="0" step="1"
.value=${this._localYearly}
?disabled=${this._busy}
@input=${e=>{this._localYearly=e.target.value,this._dirty=!0}} />
<span class="input-suffix">${i}</span>
</div>
</div>
</div>
<div class="actions">
<button class="btn ${this._dirty?"primary":"muted"}"
@click=${this._save}
?disabled=${this._busy||!this._dirty}>
<ha-icon icon="${this._dirty?"mdi:content-save":"mdi:check"}"></ha-icon>
${this._dirty?a("save",t)||"Save":a("saved",t)||"Saved"}
</button>
<button class="btn link" @click=${this._onDeepLink}>
${a("budget_advanced",t)||"Currency, alerts\u2026"}
</button>
</div>
`:l`
<button class="btn link" @click=${this._onDeepLink}>
${a("budget_open_panel",t)||"Open in panel"}
</button>
`}
</div>
</ha-card>
`}};s.styles=[f,u`
.currency {
font-size: 14px; font-weight: 600;
color: var(--secondary-text-color);
background: var(--secondary-background-color);
padding: 2px 10px; border-radius: 999px;
}
.track { display: flex; flex-direction: column; gap: 4px; }
.track-label-row {
display: flex; align-items: center; justify-content: space-between;
}
.track-label-row label {
font-size: 12px; color: var(--secondary-text-color);
text-transform: uppercase; letter-spacing: 0.5px;
}
.track-numbers { font-size: 13px; font-weight: 600; }
.track-numbers.ok { color: var(--primary-text-color); }
.track-numbers.warning { color: #ff9800; }
.track-numbers.danger { color: var(--error-color, #f44336); }
.bar {
height: 6px; background: var(--secondary-background-color);
border-radius: 3px; overflow: hidden;
}
.bar-fill { height: 100%; transition: width 0.3s; border-radius: 3px; }
.bar-fill.ok { background: var(--primary-color); }
.bar-fill.warning { background: #ff9800; }
.bar-fill.danger { background: var(--error-color, #f44336); }
.inputs-row {
display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
padding-top: 4px; border-top: 1px solid var(--divider-color);
}
.input-field { display: flex; flex-direction: column; gap: 4px; }
.input-field label {
font-size: 11px; color: var(--secondary-text-color);
text-transform: uppercase; letter-spacing: 0.3px;
}
.input-wrap { position: relative; display: flex; align-items: center; }
.input-wrap input {
flex: 1; padding: 6px 32px 6px 8px; font-size: 13px;
background: var(--secondary-background-color, #2c2c2c);
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
font-family: inherit;
}
.input-suffix {
position: absolute; right: 8px;
color: var(--secondary-text-color); font-size: 13px;
pointer-events: none;
}
.actions { display: flex; gap: 8px; align-items: center; }
`],n([y({attribute:!1})],s.prototype,"hass",2),n([o()],s.prototype,"_config",2),n([o()],s.prototype,"_status",2),n([o()],s.prototype,"_busy",2),n([o()],s.prototype,"_error",2),n([o()],s.prototype,"_localMonthly",2),n([o()],s.prototype,"_localYearly",2),n([o()],s.prototype,"_dirty",2);customElements.get("maintenance-budget-section-card")||customElements.define("maintenance-budget-section-card",s);m({type:"maintenance-budget-section-card",name:"Maintenance Supporter \u2014 Budget",description:"Inline monthly + yearly budget editor",preview:!1});export{s as MaintenanceBudgetSectionCard};
@@ -0,0 +1,60 @@
/*! maintenance_supporter frontend 2.58.0 */
import{a as t}from"./chunk-UF6NLI3A.js";function e(o){let r=window;r.customCards=r.customCards||[],r.customCards.some(a=>a.type===o.type)||r.customCards.push(o)}var d=t`
ha-card { overflow: hidden; }
.card-content {
padding: 16px;
display: flex; flex-direction: column;
gap: 12px;
}
.header {
display: flex; align-items: center; justify-content: space-between;
gap: 12px;
}
.title {
display: flex; align-items: center; gap: 8px;
font-size: 16px; font-weight: 500;
}
.emoji { font-size: 20px; }
/* Button family — primary action / muted-saved-state / link / icon-with-text */
.btn {
padding: 6px 12px; font-size: 13px;
border-radius: 6px; cursor: pointer;
border: 1px solid var(--divider-color);
background: var(--secondary-background-color, transparent);
color: var(--primary-text-color);
font-weight: 500;
display: inline-flex; align-items: center; gap: 4px;
}
.btn:hover { background: var(--state-icon-color, rgba(255,255,255,0.06)); }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; }
.btn.primary {
background: var(--primary-color);
color: var(--text-primary-color, white);
border-color: var(--primary-color);
}
.btn.primary[disabled] { opacity: 0.6; }
.btn.muted {
background: transparent;
color: var(--secondary-text-color);
border-style: dashed;
}
.btn.muted[disabled] { opacity: 1; cursor: default; }
.btn.muted ha-icon, .btn.primary ha-icon { --mdc-icon-size: 14px; }
.btn.link {
background: transparent; border: none; padding: 6px 4px;
color: var(--primary-color); margin-left: auto;
}
.btn.link:hover { background: transparent; text-decoration: underline; }
/* Error + loading states */
.error {
padding: 8px; border-radius: 6px;
background: rgba(211, 47, 47, 0.1);
color: var(--error-color, #d32f2f); font-size: 13px;
}
.loading {
padding: 24px; text-align: center;
color: var(--secondary-text-color);
}
`;export{e as a,d as b};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.58.0 */
var s=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var t=(a,r,c,o)=>{for(var e=o>1?void 0:o?l(r,c):r,i=a.length-1,d;i>=0;i--)(d=a[i])&&(e=(o?d(r,c,e):d(e))||e);return o&&e&&s(r,c,e),e};var m={ok:"var(--success-color, #4caf50)",due_soon:"var(--warning-color, #ff9800)",overdue:"var(--error-color, #f44336)",triggered:"var(--deep-orange-color, #ff5722)",archived:"var(--disabled-color, #9e9e9e)",paused:"var(--info-color, #2196f3)"},v={ok:"mdi:check-circle",due_soon:"mdi:alert-circle",overdue:"mdi:alert-octagon",triggered:"mdi:bell-alert",archived:"mdi:archive-outline",paused:"mdi:pause-circle-outline",completed:"mdi:check-circle",skipped:"mdi:skip-next",missed:"mdi:calendar-remove",reset:"mdi:refresh"};export{t as a,m as b,v as c};
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,137 @@
/*! maintenance_supporter frontend 2.58.0 */
import{a as v,b}from"./chunk-OVB7GYEL.js";import{a as u,b as s,d as c,e as h,f as g,g as o,i,j as _,l as m,u as p}from"./chunk-UF6NLI3A.js";import{a}from"./chunk-PAZTCVYX.js";var e=class extends h{constructor(){super(...arguments);this._config={type:""};this._groups={};this._loaded=!1;this._busy=!1;this._error="";this._newName="";this._editingId=null;this._editingName="";this._hasInitiallyLoaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return _(this.hass)}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._hasInitiallyLoaded&&(this._hasInitiallyLoaded=!0,this._load(),m(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/groups"});this._groups=t.groups||{},this._loaded=!0}catch(t){this._error=p(t,this._lang)}}async _addGroup(){if(!this._isAdmin)return;let t=this._newName.trim();if(t){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/create",name:t}),this._newName="",await this._load()}catch(r){this._error=p(r,this._lang)}finally{this._busy=!1}}}_startEdit(t){this._editingId=t,this._editingName=this._groups[t]?.name||""}async _saveEdit(){if(!this._isAdmin||!this._editingId)return;let t=this._editingName.trim();if(t){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/update",group_id:this._editingId,name:t}),this._editingId=null,this._editingName="",await this._load()}catch(r){this._error=p(r,this._lang)}finally{this._busy=!1}}}async _deleteGroup(t,r){if(!this._isAdmin)return;let n=(i("group_delete_confirm",this._lang)||'Delete group "{name}"?').replace("{name}",r);if(window.confirm(n)){this._busy=!0;try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/delete",group_id:t}),await this._load()}catch(d){this._error=p(d,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_groups"),window.dispatchEvent(new CustomEvent("location-changed"))}_onKeyDown(t,r){t.key==="Enter"?(t.preventDefault(),r()):t.key==="Escape"&&(t.preventDefault(),this._editingId=null,this._editingName="")}render(){let t=this._lang;if(!this._loaded)return s`<ha-card><div class="loading">${i("loading",t)||"Loading\u2026"}</div></ha-card>`;let r=Object.keys(this._groups);return s`
<ha-card>
<div class="card-content">
<div class="header">
<div class="title">
<span class="emoji">🏷️</span>
<span>${this._config.title||i("groups",t)||"Groups"}</span>
<span class="count">${r.length}</span>
</div>
</div>
${this._error?s`<div class="error">${this._error}</div>`:c}
${r.length===0?s`<div class="empty">${i("groups_empty",t)||"No groups yet."}</div>`:s`
<div class="group-list">
${r.map(n=>{let d=this._groups[n],y=d.task_refs?.length??0,f=this._editingId===n;return s`
<div class="group-row">
${f?s`
<input class="edit-input" type="text"
.value=${this._editingName}
?disabled=${this._busy}
@input=${l=>{this._editingName=l.target.value}}
@keydown=${l=>this._onKeyDown(l,this._saveEdit.bind(this))} />
<button class="btn small primary"
@click=${this._saveEdit}
?disabled=${this._busy||!this._editingName.trim()}>
${i("save",t)||"Save"}
</button>
<button class="btn small"
@click=${()=>{this._editingId=null}}>
${i("cancel",t)||"Cancel"}
</button>
`:s`
<span class="group-name">${d.name||"Unnamed"}</span>
<span class="task-count">${y}</span>
${this._isAdmin?s`
<button class="icon-btn"
title="${i("edit",t)||"Edit"}"
@click=${()=>this._startEdit(n)}
?disabled=${this._busy}>
<ha-icon icon="mdi:pencil"></ha-icon>
</button>
<button class="icon-btn danger"
title="${i("delete",t)||"Delete"}"
@click=${()=>this._deleteGroup(n,d.name||"Unnamed")}
?disabled=${this._busy}>
<ha-icon icon="mdi:delete"></ha-icon>
</button>
`:c}
`}
</div>
`})}
</div>
`}
${this._isAdmin?s`
<div class="add-row">
<input type="text"
placeholder="${i("group_new_placeholder",t)||"Add group\u2026"}"
.value=${this._newName}
?disabled=${this._busy}
@input=${n=>{this._newName=n.target.value}}
@keydown=${n=>this._onKeyDown(n,this._addGroup.bind(this))} />
<button class="btn primary"
@click=${this._addGroup}
?disabled=${this._busy||!this._newName.trim()}>
<ha-icon icon="mdi:plus"></ha-icon>
${i("add",t)||"Add"}
</button>
</div>
<button class="btn link" @click=${this._onDeepLink}>
${i("groups_manage_tasks",t)||"Manage task assignments\u2026"}
</button>
`:s`
<button class="btn link" @click=${this._onDeepLink}>
${i("groups_open_panel",t)||"Open in panel"}
</button>
`}
</div>
</ha-card>
`}};e.styles=[b,u`
.count {
font-size: 12px; color: var(--secondary-text-color);
background: var(--secondary-background-color);
padding: 2px 8px; border-radius: 999px;
}
.empty {
padding: 16px; text-align: center;
color: var(--secondary-text-color); font-style: italic;
}
.group-list { display: flex; flex-direction: column; gap: 4px; }
.group-row {
display: flex; align-items: center; gap: 8px;
padding: 6px 8px; border-radius: 6px;
background: var(--secondary-background-color, rgba(255,255,255,0.03));
}
.group-name { flex: 1; font-size: 14px; }
.task-count {
font-size: 11px; color: var(--secondary-text-color);
background: var(--card-background-color, rgba(0,0,0,0.2));
padding: 1px 8px; border-radius: 999px;
font-weight: 500;
}
.edit-input {
flex: 1; padding: 4px 8px; font-size: 14px;
background: var(--card-background-color, #1c1c1c);
color: var(--primary-text-color);
border: 1px solid var(--primary-color); border-radius: 4px;
font-family: inherit;
}
.icon-btn {
background: transparent; border: none; cursor: pointer;
color: var(--secondary-text-color); padding: 4px;
border-radius: 4px;
}
.icon-btn:hover {
background: var(--state-icon-color, rgba(255,255,255,0.06));
color: var(--primary-text-color);
}
.icon-btn.danger:hover { color: var(--error-color); }
.icon-btn ha-icon { --mdc-icon-size: 18px; }
.add-row {
display: flex; gap: 6px;
padding-top: 8px; border-top: 1px solid var(--divider-color);
}
.add-row input {
flex: 1; padding: 6px 8px; font-size: 13px;
background: var(--secondary-background-color, #2c2c2c);
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
font-family: inherit;
}
/* Card-specific overrides on the shared .btn */
.btn.small { padding: 4px 8px; font-size: 12px; }
.btn ha-icon { --mdc-icon-size: 16px; }
`],a([g({attribute:!1})],e.prototype,"hass",2),a([o()],e.prototype,"_config",2),a([o()],e.prototype,"_groups",2),a([o()],e.prototype,"_loaded",2),a([o()],e.prototype,"_busy",2),a([o()],e.prototype,"_error",2),a([o()],e.prototype,"_newName",2),a([o()],e.prototype,"_editingId",2),a([o()],e.prototype,"_editingName",2);customElements.get("maintenance-groups-section-card")||customElements.define("maintenance-groups-section-card",e);v({type:"maintenance-groups-section-card",name:"Maintenance Supporter \u2014 Groups",description:"Inline group CRUD",preview:!1});export{e as MaintenanceGroupsSectionCard};
@@ -0,0 +1,122 @@
/*! maintenance_supporter frontend 2.58.0 */
import{a as m,b as g}from"./chunk-OVB7GYEL.js";import{a as h,b as n,d as c,e as _,f as v,g as r,i as e,j as f,l as b,u as l}from"./chunk-UF6NLI3A.js";import{a as i}from"./chunk-PAZTCVYX.js";var a=class extends _{constructor(){super(...arguments);this._config={type:""};this._state=null;this._busy=!1;this._error="";this._localStart="";this._localEnd="";this._localBuffer=7;this._dirty=!1;this._loaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return f(this.hass)}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._loaded&&(this._loaded=!0,this._load(),b(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/state"});this._state=t,this._localStart=t.start||"",this._localEnd=t.end||"",this._localBuffer=t.buffer_days??7,this._dirty=!1}catch(t){this._error=l(t,this._lang)}}async _toggleEnabled(t){this._busy=!0,this._error="";try{let s=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/update",enabled:t});this._state=s}catch(s){this._error=l(s,this._lang)}finally{this._busy=!1}}async _save(){if(this._isAdmin){this._busy=!0,this._error="";try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/update",start:this._localStart||null,end:this._localEnd||null,buffer_days:this._localBuffer});this._state=t,this._dirty=!1}catch(t){this._error=l(t,this._lang)}finally{this._busy=!1}}}async _endNow(){if(this._isAdmin&&window.confirm(e("vacation_end_now_confirm",this._lang)||"End vacation immediately?")){this._busy=!0;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/end_now"});this._state=t,this._localStart=t.start||"",this._localEnd=t.end||""}catch(t){this._error=l(t,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_vacation"),window.dispatchEvent(new CustomEvent("location-changed"))}render(){let t=this._lang,s=this._state;if(!s)return n`<ha-card><div class="loading">${e("loading",t)||"Loading\u2026"}</div></ha-card>`;let p=s.is_active===!0,d=s.enabled===!0,u=s.exempt_task_ids?.length??0,y=p?e("vacation_status_active",t)||"Active now":d?e("vacation_status_scheduled",t)||"Scheduled":e("vacation_status_inactive",t)||"Inactive",$=p?"active":d?"scheduled":"inactive";return n`
<ha-card>
<div class="card-content">
<div class="header">
<div class="title">
<span class="emoji">🏖️</span>
<span>${this._config.title||e("vacation_mode",t)||"Vacation mode"}</span>
</div>
<span class="status-pill ${$}">${y}</span>
</div>
${this._error?n`<div class="error">${this._error}</div>`:c}
${this._isAdmin?n`
<div class="row toggle-row">
<label>${e("enable",t)||"Enable"}</label>
<ha-switch
.checked=${d}
.disabled=${this._busy}
@change=${o=>this._toggleEnabled(o.target.checked)}
></ha-switch>
</div>
<div class="dates-row">
<div class="date-field">
<label>${e("vacation_start",t)||"Start"}</label>
<input type="date" .value=${this._localStart}
?disabled=${this._busy}
@input=${o=>{this._localStart=o.target.value,this._dirty=!0}} />
</div>
<div class="date-field">
<label>${e("vacation_end",t)||"End"}</label>
<input type="date" .value=${this._localEnd}
?disabled=${this._busy}
@input=${o=>{this._localEnd=o.target.value,this._dirty=!0}} />
</div>
<div class="date-field buffer">
<label>${e("vacation_buffer",t)||"Buffer days"}</label>
<input type="number" min="0" max="14"
.value=${String(this._localBuffer)}
?disabled=${this._busy}
@input=${o=>{this._localBuffer=parseInt(o.target.value,10)||0,this._dirty=!0}} />
</div>
</div>
<div class="actions">
<button class="btn ${this._dirty?"primary":"muted"}"
@click=${this._save}
?disabled=${this._busy||!this._dirty}>
<ha-icon icon="${this._dirty?"mdi:content-save":"mdi:check"}"></ha-icon>
${this._dirty?e("save",t)||"Save":e("saved",t)||"Saved"}
</button>
${p?n`<button class="btn"
@click=${this._endNow}
?disabled=${this._busy}>
${e("vacation_end_now",t)||"End now"}
</button>`:c}
${u>0?n`<button class="btn link"
@click=${this._onDeepLink}>
${u} ${e("vacation_exempt_count",t)||"exempt"}…
</button>`:n`<button class="btn link"
@click=${this._onDeepLink}>
${e("vacation_advanced",t)||"Advanced\u2026"}
</button>`}
</div>
`:n`
<div class="readonly">
${d&&s.start&&s.end?n`<div>${s.start} → ${s.end}</div>`:c}
<button class="btn link" @click=${this._onDeepLink}>
${e("vacation_open_panel",t)||"Open in panel"}
</button>
</div>
`}
</div>
</ha-card>
`}};a.styles=[g,h`
.status-pill {
font-size: 11px; font-weight: 600;
padding: 3px 8px; border-radius: 999px;
text-transform: uppercase; letter-spacing: 0.5px;
}
.status-pill.active {
background: rgba(76, 175, 80, 0.15);
color: #4caf50;
}
.status-pill.scheduled {
background: rgba(255, 152, 0, 0.15);
color: #ff9800;
}
.status-pill.inactive {
background: rgba(158, 158, 158, 0.15);
color: var(--secondary-text-color);
}
.row.toggle-row {
display: flex; align-items: center; justify-content: space-between;
}
.row.toggle-row label {
font-size: 14px; color: var(--primary-text-color);
}
.dates-row {
display: grid; grid-template-columns: 1fr 1fr 100px; gap: 10px;
}
.date-field.buffer label { white-space: nowrap; }
.date-field { display: flex; flex-direction: column; gap: 4px; }
.date-field label {
font-size: 11px; color: var(--secondary-text-color);
text-transform: uppercase; letter-spacing: 0.3px;
}
.date-field input {
padding: 6px 8px; font-size: 13px;
background: var(--secondary-background-color, #2c2c2c);
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
font-family: inherit;
}
.date-field input:disabled { opacity: 0.5; cursor: not-allowed; }
.actions {
display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
}
.readonly { display: flex; flex-direction: column; gap: 8px; }
`],i([v({attribute:!1})],a.prototype,"hass",2),i([r()],a.prototype,"_config",2),i([r()],a.prototype,"_state",2),i([r()],a.prototype,"_busy",2),i([r()],a.prototype,"_error",2),i([r()],a.prototype,"_localStart",2),i([r()],a.prototype,"_localEnd",2),i([r()],a.prototype,"_localBuffer",2),i([r()],a.prototype,"_dirty",2);customElements.get("maintenance-vacation-section-card")||customElements.define("maintenance-vacation-section-card",a);m({type:"maintenance-vacation-section-card",name:"Maintenance Supporter \u2014 Vacation",description:"Inline vacation mode toggle + dates",preview:!1});export{a as MaintenanceVacationSectionCard};
File diff suppressed because one or more lines are too long