147 files
This commit is contained in:
1 parent
52cbcb4187
commit
69f4dc3f17
149 files changed
+13414
-1155
No files matched your search
+1
-1
@@ -1 +1 @@
|
|||||||
{"pid": 71, "version": 1, "ha_version": "2026.8.3", "start_ts": 1787776418.1303492}
|
{"pid": 72, "version": 1, "ha_version": "2026.8.3", "start_ts": 1787864016.8899596}
|
||||||
+25
-25
@@ -81,117 +81,117 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "88b90f2b1d6b45f0b44bee786ee1a525",
|
"id": "88b90f2b1d6b45f0b44bee786ee1a525",
|
||||||
"url": "/taskmate/taskmate-attr-resolver.js?v=5.3.0",
|
"url": "/taskmate/taskmate-attr-resolver.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "0796c92931dd458e81dc9c2677248274",
|
"id": "0796c92931dd458e81dc9c2677248274",
|
||||||
"url": "/taskmate/taskmate-localize.js?v=5.3.0",
|
"url": "/taskmate/taskmate-localize.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "d2e281920ac7409583eafff1f1db69bd",
|
"id": "d2e281920ac7409583eafff1f1db69bd",
|
||||||
"url": "/taskmate/taskmate-design.js?v=5.3.0",
|
"url": "/taskmate/taskmate-design.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9eae9632f0734480be5edd784b030673",
|
"id": "9eae9632f0734480be5edd784b030673",
|
||||||
"url": "/taskmate/taskmate-badges-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-badges-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5df49cd8a4eb420497b5df22cb31362c",
|
"id": "5df49cd8a4eb420497b5df22cb31362c",
|
||||||
"url": "/taskmate/taskmate-child-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-child-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "b44c87d0d74f468cb96c5eabc340f9ee",
|
"id": "b44c87d0d74f468cb96c5eabc340f9ee",
|
||||||
"url": "/taskmate/taskmate-rewards-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-rewards-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "787a96f9c30548708165a48457c99feb",
|
"id": "787a96f9c30548708165a48457c99feb",
|
||||||
"url": "/taskmate/taskmate-approvals-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-approvals-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "84031597de424ad68e7c2bc66d06b72e",
|
"id": "84031597de424ad68e7c2bc66d06b72e",
|
||||||
"url": "/taskmate/taskmate-points-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-points-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "02613225844746b699bfbfac9642eca2",
|
"id": "02613225844746b699bfbfac9642eca2",
|
||||||
"url": "/taskmate/taskmate-reorder-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-reorder-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7a06a07e17ba4b31b712bc91b77360b4",
|
"id": "7a06a07e17ba4b31b712bc91b77360b4",
|
||||||
"url": "/taskmate/taskmate-overview-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-overview-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "08b42b53b33b4fa88db932455cf8d5aa",
|
"id": "08b42b53b33b4fa88db932455cf8d5aa",
|
||||||
"url": "/taskmate/taskmate-activity-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-activity-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "15907eff51784acdba88ff694b574509",
|
"id": "15907eff51784acdba88ff694b574509",
|
||||||
"url": "/taskmate/taskmate-streak-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-streak-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1bd6bb5c0a6d4f0d916e3d82cb7fac39",
|
"id": "1bd6bb5c0a6d4f0d916e3d82cb7fac39",
|
||||||
"url": "/taskmate/taskmate-weekly-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-weekly-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "289ce5943f5a4972a593697db4c96ece",
|
"id": "289ce5943f5a4972a593697db4c96ece",
|
||||||
"url": "/taskmate/taskmate-graph-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-graph-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "d2904acee4c74a398dcc7b996c23e7e7",
|
"id": "d2904acee4c74a398dcc7b996c23e7e7",
|
||||||
"url": "/taskmate/taskmate-reward-progress-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-reward-progress-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "46d378736fd54a26be253f2dfb27202e",
|
"id": "46d378736fd54a26be253f2dfb27202e",
|
||||||
"url": "/taskmate/taskmate-leaderboard-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-leaderboard-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "db11ae36457842fa826c7991e1d9c6f4",
|
"id": "db11ae36457842fa826c7991e1d9c6f4",
|
||||||
"url": "/taskmate/taskmate-parent-dashboard-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-parent-dashboard-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ce85f301b59a412e9abfe5ffd51cb9a2",
|
"id": "ce85f301b59a412e9abfe5ffd51cb9a2",
|
||||||
"url": "/taskmate/taskmate-penalties-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-penalties-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1959e1629de2422daa8adaf87069c288",
|
"id": "1959e1629de2422daa8adaf87069c288",
|
||||||
"url": "/taskmate/taskmate-bonuses-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-bonuses-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "2548524b269c4959bc7e0e967df943af",
|
"id": "2548524b269c4959bc7e0e967df943af",
|
||||||
"url": "/taskmate/taskmate-points-display-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-points-display-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "36ada49368db4187a283fbbbef45454f",
|
"id": "36ada49368db4187a283fbbbef45454f",
|
||||||
"url": "/taskmate/taskmate-calendar-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-calendar-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "67c32687acd2497b90030ef141f4b6db",
|
"id": "67c32687acd2497b90030ef141f4b6db",
|
||||||
"url": "/taskmate/taskmate-photo-gallery-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-photo-gallery-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "6f0c8348a91f4b74a9b846ab3f62621f",
|
"id": "6f0c8348a91f4b74a9b846ab3f62621f",
|
||||||
"url": "/taskmate/taskmate-family-goal-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-family-goal-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -276,7 +276,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "73dbd2de03de4530b451a55bfe6c46cf",
|
"id": "73dbd2de03de4530b451a55bfe6c46cf",
|
||||||
"url": "/taskmate/taskmate-routine-card.js?v=5.3.0",
|
"url": "/taskmate/taskmate-routine-card.js?v=5.4.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -315,7 +315,7 @@
|
|||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9227b25fc2ea4cb5be22e4436912268a",
|
"id": "17e81c8801114b2d941a7d5f22eb47e6",
|
||||||
"url": "/climate_scheduler/static/climate-scheduler-card.js?v=1.15.1",
|
"url": "/climate_scheduler/static/climate-scheduler-card.js?v=1.15.1",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -564,6 +564,12 @@ async def async_setup_entry(
|
|||||||
_LOGGER.debug("Maintenance calendar entity created")
|
_LOGGER.debug("Maintenance calendar entity created")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _task_label(task: MaintenanceTask) -> str:
|
||||||
|
"""Task name, with the due cycle phase appended for phased tasks (#139)."""
|
||||||
|
phase = task.current_phase_name
|
||||||
|
return f"{task.name} · {phase}" if phase else task.name
|
||||||
|
|
||||||
class MaintenanceCalendar(CalendarEntity):
|
class MaintenanceCalendar(CalendarEntity):
|
||||||
"""Calendar entity aggregating all maintenance tasks."""
|
"""Calendar entity aggregating all maintenance tasks."""
|
||||||
|
|
||||||
@@ -713,7 +719,7 @@ class MaintenanceCalendar(CalendarEntity):
|
|||||||
today = dt_util.now().date()
|
today = dt_util.now().date()
|
||||||
if start_d <= today <= end_d:
|
if start_d <= today <= end_d:
|
||||||
return CalendarEvent(
|
return CalendarEvent(
|
||||||
summary=f"{STATUS_PREFIX.get(MaintenanceStatus.TRIGGERED, '🔔')} {task.name} ({object_name})",
|
summary=f"{STATUS_PREFIX.get(MaintenanceStatus.TRIGGERED, '🔔')} {_task_label(task)} ({object_name})",
|
||||||
start=today,
|
start=today,
|
||||||
end=today + timedelta(days=1),
|
end=today + timedelta(days=1),
|
||||||
description=_cal_t("manually_triggered", lang),
|
description=_cal_t("manually_triggered", lang),
|
||||||
@@ -728,7 +734,7 @@ class MaintenanceCalendar(CalendarEntity):
|
|||||||
today = dt_util.now().date()
|
today = dt_util.now().date()
|
||||||
if start_d <= today <= end_d:
|
if start_d <= today <= end_d:
|
||||||
return CalendarEvent(
|
return CalendarEvent(
|
||||||
summary=f"{STATUS_PREFIX.get(MaintenanceStatus.TRIGGERED, '🔔')} {task.name} ({object_name})",
|
summary=f"{STATUS_PREFIX.get(MaintenanceStatus.TRIGGERED, '🔔')} {_task_label(task)} ({object_name})",
|
||||||
start=today,
|
start=today,
|
||||||
end=today + timedelta(days=1),
|
end=today + timedelta(days=1),
|
||||||
description=_cal_t("sensor_triggered", lang, name=task.name),
|
description=_cal_t("sensor_triggered", lang, name=task.name),
|
||||||
@@ -770,7 +776,7 @@ class MaintenanceCalendar(CalendarEntity):
|
|||||||
pass # malformed schedule_time → fall back to all-day
|
pass # malformed schedule_time → fall back to all-day
|
||||||
|
|
||||||
return CalendarEvent(
|
return CalendarEvent(
|
||||||
summary=f"{prefix} {task.name} ({object_name})",
|
summary=f"{prefix} {_task_label(task)} ({object_name})",
|
||||||
start=start,
|
start=start,
|
||||||
end=end,
|
end=end,
|
||||||
description=(
|
description=(
|
||||||
|
|||||||
@@ -143,6 +143,9 @@ class _OptionsFlowBase(TriggerConfigMixin, OptionsFlow):
|
|||||||
menu.append("remove_trigger")
|
menu.append("remove_trigger")
|
||||||
if global_opts.get(CONF_ADVANCED_CHECKLISTS, False):
|
if global_opts.get(CONF_ADVANCED_CHECKLISTS, False):
|
||||||
menu.append("edit_checklist")
|
menu.append("edit_checklist")
|
||||||
|
# #139: cycle phases are not feature-gated (the panel editor isn't
|
||||||
|
# either) — the minimal flow editor is always reachable.
|
||||||
|
menu.append("edit_phases")
|
||||||
if global_opts.get(CONF_ADVANCED_ADAPTIVE, False):
|
if global_opts.get(CONF_ADVANCED_ADAPTIVE, False):
|
||||||
menu.append("adaptive_scheduling")
|
menu.append("adaptive_scheduling")
|
||||||
menu.extend(["delete_task", "manage_tasks"])
|
menu.extend(["delete_task", "manage_tasks"])
|
||||||
|
|||||||
@@ -539,6 +539,73 @@ class TaskCrudMixin:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_edit_phases(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
|
||||||
|
"""Minimal cycle-phase editor (#139): one textarea, one line per
|
||||||
|
sequence step, optional per-phase checklist as "Name: item; item".
|
||||||
|
|
||||||
|
Names matching existing phases keep their ids (history phase_id and
|
||||||
|
the Store cursor stay stable) AND their panel-only overrides (parts /
|
||||||
|
required fields). Sanitizing + storage go through the SAME
|
||||||
|
``_apply_phase_fields`` as the WS write path, and the Store cursor is
|
||||||
|
clamped the same way ws_update_task clamps it.
|
||||||
|
"""
|
||||||
|
from .helpers.phases import clamp_phase_cursor, parse_phases_text, phases_to_text
|
||||||
|
from .websocket.tasks_crud import _apply_phase_fields
|
||||||
|
|
||||||
|
tasks_data = self.config_entry.data.get(CONF_TASKS, {})
|
||||||
|
task = tasks_data.get(self._selected_task_id or "", {})
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
if user_input.get("go_back"):
|
||||||
|
return self._show_task_action_menu()
|
||||||
|
|
||||||
|
raw_defs, raw_seq = parse_phases_text(
|
||||||
|
user_input.get("phases_text", ""), task.get("phases") or {}
|
||||||
|
)
|
||||||
|
new_data = dict(self.config_entry.data)
|
||||||
|
new_tasks = dict(new_data.get(CONF_TASKS, {}))
|
||||||
|
updated_task = dict(new_tasks.get(self._selected_task_id or "", {}))
|
||||||
|
_apply_phase_fields(self.hass, self.config_entry, updated_task, raw_defs, raw_seq)
|
||||||
|
new_tasks[self._selected_task_id or ""] = updated_task
|
||||||
|
new_data[CONF_TASKS] = new_tasks
|
||||||
|
self._update_config_entry(new_data)
|
||||||
|
|
||||||
|
# Clamp/clear the Store cursor against the edited sequence —
|
||||||
|
# the Store wins on restore, so an out-of-range cursor would
|
||||||
|
# otherwise survive this edit forever (same block as ws_update).
|
||||||
|
rd = getattr(self.config_entry, "runtime_data", None)
|
||||||
|
store = getattr(rd, "store", None)
|
||||||
|
if store is not None and self._selected_task_id:
|
||||||
|
seq = updated_task.get("phase_sequence") or []
|
||||||
|
if seq:
|
||||||
|
cur = store.get_task_state(self._selected_task_id).get("phase_cursor", 0)
|
||||||
|
store.set_phase_cursor(self._selected_task_id, clamp_phase_cursor(cur, len(seq)))
|
||||||
|
else:
|
||||||
|
store.get_task_state(self._selected_task_id).pop("phase_cursor", None)
|
||||||
|
store.async_delay_save()
|
||||||
|
|
||||||
|
return self._show_task_action_menu()
|
||||||
|
|
||||||
|
default_text = phases_to_text(task.get("phases") or {}, task.get("phase_sequence") or [])
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="edit_phases",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional("phases_text", default=default_text): selector.TextSelector(
|
||||||
|
selector.TextSelectorConfig(
|
||||||
|
type=selector.TextSelectorType.TEXT,
|
||||||
|
multiline=True,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vol.Optional("go_back", default=False): selector.BooleanSelector(),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
description_placeholders={
|
||||||
|
"task_name": task.get("name", ""),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
async def async_step_delete_task(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
|
async def async_step_delete_task(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
|
||||||
"""Confirm and delete a task."""
|
"""Confirm and delete a task."""
|
||||||
tasks_data = self.config_entry.data.get(CONF_TASKS, {})
|
tasks_data = self.config_entry.data.get(CONF_TASKS, {})
|
||||||
|
|||||||
@@ -59,6 +59,16 @@ from .storage import MaintenanceStore
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _notify_task_label(task: dict[str, Any]) -> str:
|
||||||
|
"""Task name for notifications — phased tasks (#139) name the due step,
|
||||||
|
so "Mower blades · Replace blades" tells the user what the work IS."""
|
||||||
|
from .helpers.phases import current_phase
|
||||||
|
|
||||||
|
name = task.get("name", "")
|
||||||
|
phase = current_phase(task)
|
||||||
|
return f"{name} · {phase['name']}" if phase else name
|
||||||
|
|
||||||
|
|
||||||
def _inert_task_result(task: MaintenanceTask, status: str, **extra: Any) -> dict[str, Any]:
|
def _inert_task_result(task: MaintenanceTask, status: str, **extra: Any) -> dict[str, Any]:
|
||||||
"""Coordinator payload for a task that gets NO live evaluation (archived /
|
"""Coordinator payload for a task that gets NO live evaluation (archived /
|
||||||
paused): due fields nulled, trigger off, only cost/history-derived fields
|
paused): due fields nulled, trigger off, only cost/history-derived fields
|
||||||
@@ -744,7 +754,7 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
tasks=[
|
tasks=[
|
||||||
{
|
{
|
||||||
"task_id": tid,
|
"task_id": tid,
|
||||||
"task_name": tr.get("name", ""),
|
"task_name": _notify_task_label(tr),
|
||||||
"status": status,
|
"status": status,
|
||||||
"days_until_due": tr.get("_days_until_due"),
|
"days_until_due": tr.get("_days_until_due"),
|
||||||
}
|
}
|
||||||
@@ -756,7 +766,7 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
await nm.async_task_status_changed(
|
await nm.async_task_status_changed(
|
||||||
entry_id=self.entry.entry_id,
|
entry_id=self.entry.entry_id,
|
||||||
task_id=task_id,
|
task_id=task_id,
|
||||||
task_name=task_result.get("name", ""),
|
task_name=_notify_task_label(task_result),
|
||||||
object_name=obj_name,
|
object_name=obj_name,
|
||||||
new_status=new_status,
|
new_status=new_status,
|
||||||
days_until_due=task_result.get("_days_until_due"),
|
days_until_due=task_result.get("_days_until_due"),
|
||||||
@@ -862,6 +872,10 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
state["due_override"] = do
|
state["due_override"] = do
|
||||||
elif "due_override" in state:
|
elif "due_override" in state:
|
||||||
del state["due_override"]
|
del state["due_override"]
|
||||||
|
# Phase cursor (#139): to_dict emits it only for phase-carrying tasks.
|
||||||
|
pc = td.get("phase_cursor")
|
||||||
|
if pc is not None:
|
||||||
|
self._store.set_phase_cursor(task_id, pc)
|
||||||
self._store.set_history(task_id, td.get("history", []))
|
self._store.set_history(task_id, td.get("history", []))
|
||||||
if task.adaptive_config:
|
if task.adaptive_config:
|
||||||
self._store.set_adaptive_config(task_id, task.adaptive_config)
|
self._store.set_adaptive_config(task_id, task.adaptive_config)
|
||||||
@@ -1061,8 +1075,10 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
# will consume below — go on the record, so completions from the
|
# will consume below — go on the record, so completions from the
|
||||||
# no-dialog surfaces (service, button, QR, to-do, voice, recovery)
|
# no-dialog surfaces (service, button, QR, to-do, voice, recovery)
|
||||||
# stay correctable through the history editor like dialog ones.
|
# stay correctable through the history editor like dialog ones.
|
||||||
|
from .helpers.phases import effective_field as _phase_effective
|
||||||
|
|
||||||
record_links = (
|
record_links = (
|
||||||
used_parts if used_parts is not None else (merged[task_id].get("consumes_parts") or [])
|
used_parts if used_parts is not None else (_phase_effective(merged[task_id], "consumes_parts") or [])
|
||||||
)
|
)
|
||||||
enriched_used: list[dict[str, Any]] | None = None
|
enriched_used: list[dict[str, Any]] | None = None
|
||||||
if used_parts is not None or record_links:
|
if used_parts is not None or record_links:
|
||||||
|
|||||||
@@ -134,6 +134,12 @@ def _build_export_object(
|
|||||||
"required_completion_fields": tdata.get("required_completion_fields"),
|
"required_completion_fields": tdata.get("required_completion_fields"),
|
||||||
"rotation_strategy": tdata.get("rotation_strategy"),
|
"rotation_strategy": tdata.get("rotation_strategy"),
|
||||||
"reading_unit": tdata.get("reading_unit"),
|
"reading_unit": tdata.get("reading_unit"),
|
||||||
|
# Task phases (#139): defs + cycle are static config; the cursor is
|
||||||
|
# Store-merged by merged_tasks, so a restore resumes mid-cycle
|
||||||
|
# instead of silently restarting at step one.
|
||||||
|
"phases": tdata.get("phases"),
|
||||||
|
"phase_sequence": tdata.get("phase_sequence"),
|
||||||
|
"phase_cursor": tdata.get("phase_cursor"),
|
||||||
# Spare parts: consumption links + the auto-buy-task marker.
|
# Spare parts: consumption links + the auto-buy-task marker.
|
||||||
"consumes_parts": tdata.get("consumes_parts"),
|
"consumes_parts": tdata.get("consumes_parts"),
|
||||||
"part_ref": tdata.get("part_ref"),
|
"part_ref": tdata.get("part_ref"),
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ export class MaintenanceCompleteDialog extends LitElement {
|
|||||||
* enforces the same list at every completion surface; blocking Save here
|
* enforces the same list at every completion surface; blocking Save here
|
||||||
* just means the user never has to meet that rejection. */
|
* just means the user never has to meet that rejection. */
|
||||||
@property({ type: Array }) public requiredFields: string[] = [];
|
@property({ type: Array }) public requiredFields: string[] = [];
|
||||||
|
/** #139: "2/4 · Flip blades" — which phase of the cycle this completion
|
||||||
|
* records. Purely informative; the backend advances the cursor itself. */
|
||||||
|
@property() public phaseLabel = "";
|
||||||
@state() private _open = false;
|
@state() private _open = false;
|
||||||
@state() private _notes = "";
|
@state() private _notes = "";
|
||||||
@state() private _cost = "";
|
@state() private _cost = "";
|
||||||
@@ -286,6 +289,7 @@ export class MaintenanceCompleteDialog extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<ha-dialog open @closed=${this._close}>
|
<ha-dialog open @closed=${this._close}>
|
||||||
<div class="dialog-title">${t("complete_title", L)}${this.taskName}</div>
|
<div class="dialog-title">${t("complete_title", L)}${this.taskName}</div>
|
||||||
|
${this.phaseLabel ? html`<div class="phase-line">${t("phase_current", L)}: ${this.phaseLabel}</div>` : nothing}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${this._error ? html`<div class="error">${this._error}</div>` : nothing}
|
${this._error ? html`<div class="error">${this._error}</div>` : nothing}
|
||||||
${this.checklist.length > 0 ? html`
|
${this.checklist.length > 0 ? html`
|
||||||
@@ -473,6 +477,12 @@ export class MaintenanceCompleteDialog extends LitElement {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
.phase-line {
|
||||||
|
margin-top: -8px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
.content {
|
.content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -260,6 +260,18 @@ export class MaintenanceTaskDialog extends LitElement {
|
|||||||
|
|
||||||
// Checklist (newline-separated steps, one per line)
|
// Checklist (newline-separated steps, one per line)
|
||||||
@state() private _checklistText = "";
|
@state() private _checklistText = "";
|
||||||
|
// Task phases (#139): editable defs + the cycle order. The v1 editor keeps
|
||||||
|
// each phase to name + checklist + at most ONE own-object part (the mower
|
||||||
|
// "14 new blades" case); richer per-phase config exists at the WS level.
|
||||||
|
// `carry` holds def fields the editor has no form for (notes,
|
||||||
|
// required_completion_fields, future keys) and `extraParts` any links
|
||||||
|
// beyond the first — both re-merged on save so a no-op edit never wipes
|
||||||
|
// them (#106 pattern; same passthrough idea as trigger_config's carry).
|
||||||
|
@state() private _phaseDefs: Array<{
|
||||||
|
id: string; name: string; checklistText: string; partId: string; partQty: string;
|
||||||
|
extraParts: TaskPartLink[]; carry: Record<string, unknown>;
|
||||||
|
}> = [];
|
||||||
|
@state() private _phaseSeq: string[] = [];
|
||||||
/** Details this task demands on completion — enforced by the backend on
|
/** Details this task demands on completion — enforced by the backend on
|
||||||
* every surface, so a button press or voice command cannot bypass it. */
|
* every surface, so a button press or voice command cannot bypass it. */
|
||||||
@state() private _requiredCompletion: string[] = [];
|
@state() private _requiredCompletion: string[] = [];
|
||||||
@@ -388,6 +400,19 @@ export class MaintenanceTaskDialog extends LitElement {
|
|||||||
this._rotationStrategy = task.rotation_strategy || "";
|
this._rotationStrategy = task.rotation_strategy || "";
|
||||||
|
|
||||||
this._checklistText = (task.checklist || []).join("\n");
|
this._checklistText = (task.checklist || []).join("\n");
|
||||||
|
this._phaseDefs = Object.entries(task.phases || {}).map(([id, def]) => {
|
||||||
|
const { name: _n, checklist: _c, consumes_parts: _p, ...carry } = def as unknown as Record<string, unknown>;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: def.name || id,
|
||||||
|
checklistText: (def.checklist || []).join("\n"),
|
||||||
|
partId: def.consumes_parts?.[0]?.part_id || "",
|
||||||
|
partQty: def.consumes_parts?.[0]?.quantity != null ? String(def.consumes_parts[0].quantity) : "",
|
||||||
|
extraParts: (def.consumes_parts || []).slice(1),
|
||||||
|
carry,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this._phaseSeq = [...(task.phase_sequence || [])];
|
||||||
this._requiredCompletion = [...(task.required_completion_fields || [])];
|
this._requiredCompletion = [...(task.required_completion_fields || [])];
|
||||||
this._scheduleTime = task.schedule_time || "";
|
this._scheduleTime = task.schedule_time || "";
|
||||||
|
|
||||||
@@ -506,6 +531,8 @@ export class MaintenanceTaskDialog extends LitElement {
|
|||||||
this._assigneePool = [];
|
this._assigneePool = [];
|
||||||
this._rotationStrategy = "";
|
this._rotationStrategy = "";
|
||||||
this._checklistText = "";
|
this._checklistText = "";
|
||||||
|
this._phaseDefs = [];
|
||||||
|
this._phaseSeq = [];
|
||||||
this._requiredCompletion = [];
|
this._requiredCompletion = [];
|
||||||
this._scheduleTime = "";
|
this._scheduleTime = "";
|
||||||
this._environmentalEntity = "";
|
this._environmentalEntity = "";
|
||||||
@@ -993,6 +1020,103 @@ export class MaintenanceTaskDialog extends LitElement {
|
|||||||
this._requiredCompletion = [...next];
|
this._requiredCompletion = [...next];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Task phases (#139) ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
private _phaseSlug(name: string): string {
|
||||||
|
const base = name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 24) || "phase";
|
||||||
|
let slug = base;
|
||||||
|
let n = 2;
|
||||||
|
while (this._phaseDefs.some((d) => d.id === slug)) slug = `${base}-${n++}`;
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addPhaseDef(): void {
|
||||||
|
const id = this._phaseSlug(`phase-${this._phaseDefs.length + 1}`);
|
||||||
|
this._phaseDefs = [...this._phaseDefs, { id, name: "", checklistText: "", partId: "", partQty: "", extraParts: [], carry: {} }];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removePhaseDef(id: string): void {
|
||||||
|
this._phaseDefs = this._phaseDefs.filter((d) => d.id !== id);
|
||||||
|
this._phaseSeq = this._phaseSeq.filter((s) => s !== id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _patchPhaseDef(id: string, patch: Partial<{ name: string; checklistText: string; partId: string; partQty: string }>): void {
|
||||||
|
this._phaseDefs = this._phaseDefs.map((d) => (d.id === id ? { ...d, ...patch } : d));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderPhasesEditor(L: string) {
|
||||||
|
const defName = (id: string) => this._phaseDefs.find((d) => d.id === id)?.name || id;
|
||||||
|
return html`
|
||||||
|
<h3>${t("phases_section", L)}</h3>
|
||||||
|
<div class="field-help">${t("phases_hint", L)}</div>
|
||||||
|
${this._phaseDefs.map((d) => html`
|
||||||
|
<div class="phase-def">
|
||||||
|
<div class="phase-def-head">
|
||||||
|
<ms-textfield
|
||||||
|
label="${t("phase_name", L)}"
|
||||||
|
.value=${d.name}
|
||||||
|
@input=${(e: Event) => this._patchPhaseDef(d.id, { name: (e.target as HTMLInputElement).value })}
|
||||||
|
></ms-textfield>
|
||||||
|
${this.parts.length ? html`
|
||||||
|
<select
|
||||||
|
class="phase-part"
|
||||||
|
.value=${d.partId}
|
||||||
|
@change=${(e: Event) => this._patchPhaseDef(d.id, { partId: (e.target as HTMLSelectElement).value })}
|
||||||
|
>
|
||||||
|
<option value="">—</option>
|
||||||
|
${this.parts.map((p) => html`<option value=${p.id} ?selected=${p.id === d.partId}>${p.name}</option>`)}
|
||||||
|
</select>
|
||||||
|
${d.partId ? html`
|
||||||
|
<input class="phase-qty" type="number" min="0.01" step="0.01" .value=${d.partQty || "1"}
|
||||||
|
@input=${(e: Event) => this._patchPhaseDef(d.id, { partQty: (e.target as HTMLInputElement).value })} />
|
||||||
|
` : nothing}
|
||||||
|
` : nothing}
|
||||||
|
<mwc-icon-button class="phase-remove" @click=${() => this._removePhaseDef(d.id)}>
|
||||||
|
<ha-icon icon="mdi:delete-outline"></ha-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
</div>
|
||||||
|
${this.checklistsEnabled ? html`
|
||||||
|
<textarea
|
||||||
|
class="checklist-textarea phase-checklist"
|
||||||
|
rows="2"
|
||||||
|
placeholder="${t("checklist_placeholder", L)}"
|
||||||
|
.value=${d.checklistText}
|
||||||
|
@input=${(e: Event) => this._patchPhaseDef(d.id, { checklistText: (e.target as HTMLTextAreaElement).value })}
|
||||||
|
></textarea>
|
||||||
|
` : nothing}
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
<ha-button appearance="plain" @click=${this._addPhaseDef}>
|
||||||
|
<ha-icon icon="mdi:plus"></ha-icon> ${t("phase_add", L)}
|
||||||
|
</ha-button>
|
||||||
|
${this._phaseDefs.some((d) => d.name.trim()) ? html`
|
||||||
|
<div class="phase-seq-label">${t("phase_sequence_label", L)}</div>
|
||||||
|
<div class="phase-seq">
|
||||||
|
${this._phaseSeq.map((id, i) => html`
|
||||||
|
<span class="phase-chip">
|
||||||
|
${i + 1}. ${defName(id)}
|
||||||
|
<button class="phase-chip-x" @click=${() => {
|
||||||
|
this._phaseSeq = this._phaseSeq.filter((_, idx) => idx !== i);
|
||||||
|
}}>✕</button>
|
||||||
|
</span>
|
||||||
|
`)}
|
||||||
|
<select
|
||||||
|
class="phase-seq-add"
|
||||||
|
.value=${""}
|
||||||
|
@change=${(e: Event) => {
|
||||||
|
const id = (e.target as HTMLSelectElement).value;
|
||||||
|
if (id) this._phaseSeq = [...this._phaseSeq, id];
|
||||||
|
(e.target as HTMLSelectElement).value = "";
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="">+ ${t("phase_sequence_add_step", L)}</option>
|
||||||
|
${this._phaseDefs.filter((d) => d.name.trim()).map((d) => html`<option value=${d.id}>${d.name}</option>`)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
` : nothing}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
private async _save(): Promise<void> {
|
private async _save(): Promise<void> {
|
||||||
if (this._loading) return; // synchronous re-entry guard (double-click)
|
if (this._loading) return; // synchronous re-entry guard (double-click)
|
||||||
if (!this._name.trim()) return;
|
if (!this._name.trim()) return;
|
||||||
@@ -1073,6 +1197,27 @@ export class MaintenanceTaskDialog extends LitElement {
|
|||||||
data.last_performed = this._lastPerformed || null;
|
data.last_performed = this._lastPerformed || null;
|
||||||
data.nfc_tag_id = this._nfcTagId || null;
|
data.nfc_tag_id = this._nfcTagId || null;
|
||||||
data.reading_unit = this._readingUnit.trim() || null;
|
data.reading_unit = this._readingUnit.trim() || null;
|
||||||
|
// Task phases (#139): always sent — null clears a removed cycle.
|
||||||
|
{
|
||||||
|
const defs: Record<string, unknown> = {};
|
||||||
|
for (const d of this._phaseDefs) {
|
||||||
|
if (!d.name.trim()) continue;
|
||||||
|
const def: Record<string, unknown> = { ...d.carry, name: d.name.trim() };
|
||||||
|
const items = d.checklistText.split("\n").map((s) => s.trim()).filter(Boolean);
|
||||||
|
if (items.length) def.checklist = items;
|
||||||
|
const links: TaskPartLink[] = [];
|
||||||
|
if (d.partId) {
|
||||||
|
const qty = parseFloat(d.partQty);
|
||||||
|
links.push({ part_id: d.partId, quantity: Number.isFinite(qty) && qty > 0 ? qty : 1 });
|
||||||
|
}
|
||||||
|
links.push(...d.extraParts);
|
||||||
|
if (links.length) def.consumes_parts = links;
|
||||||
|
defs[d.id] = def;
|
||||||
|
}
|
||||||
|
const seq = this._phaseSeq.filter((id) => id in defs);
|
||||||
|
data.phases = Object.keys(defs).length && seq.length ? defs : null;
|
||||||
|
data.phase_sequence = data.phases ? seq : null;
|
||||||
|
}
|
||||||
// Only send when a picker was actually rendered. A failed parts load
|
// Only send when a picker was actually rendered. A failed parts load
|
||||||
// leaves both lists empty, and sending [] then would silently wipe links
|
// leaves both lists empty, and sending [] then would silently wipe links
|
||||||
// the user never saw. entry_id is written ONLY for a foreign pick, so an
|
// the user never saw. entry_id is written ONLY for a foreign pick, so an
|
||||||
@@ -2463,6 +2608,7 @@ export class MaintenanceTaskDialog extends LitElement {
|
|||||||
></textarea>
|
></textarea>
|
||||||
<div class="field-help">${t("checklist_help", L)}</div>
|
<div class="field-help">${t("checklist_help", L)}</div>
|
||||||
` : nothing}
|
` : nothing}
|
||||||
|
${this._renderPhasesEditor(L)}
|
||||||
<h3>${t("require_on_completion", L)}</h3>
|
<h3>${t("require_on_completion", L)}</h3>
|
||||||
<div class="required-completion">
|
<div class="required-completion">
|
||||||
${REQUIRED_COMPLETION_KEYS.map((field) => html`
|
${REQUIRED_COMPLETION_KEYS.map((field) => html`
|
||||||
@@ -2742,6 +2888,82 @@ export class MaintenanceTaskDialog extends LitElement {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
}
|
}
|
||||||
|
.phase-def {
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
.phase-def-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.phase-def-head ms-textfield {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.phase-part {
|
||||||
|
max-width: 160px;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--card-background-color);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.phase-qty {
|
||||||
|
width: 64px;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--card-background-color);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.phase-checklist {
|
||||||
|
min-height: 56px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
.phase-remove {
|
||||||
|
--mdc-icon-button-size: 36px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.phase-seq-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.phase-seq {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.phase-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--secondary-background-color);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.phase-chip-x {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.phase-seq-add {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: 1px dashed var(--divider-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
.consumes-check {
|
.consumes-check {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
+13
-4
@@ -18,6 +18,7 @@ import { sharedStyles, t, STATUS_COLORS, formatDate, formatDateTime, formatInter
|
|||||||
import { describeWsError } from "../ws-errors";
|
import { describeWsError } from "../ws-errors";
|
||||||
import { isoDateLocal } from "../helpers/calendar-bucket";
|
import { isoDateLocal } from "../helpers/calendar-bucket";
|
||||||
import { partsForCompletion } from "../helpers/shared-parts";
|
import { partsForCompletion } from "../helpers/shared-parts";
|
||||||
|
import { effectivePhase, phaseLabel } from "../helpers/phases";
|
||||||
import { renderWeibullSection } from "../renderers/weibull";
|
import { renderWeibullSection } from "../renderers/weibull";
|
||||||
import { renderPredictionSection } from "../renderers/prediction";
|
import { renderPredictionSection } from "../renderers/prediction";
|
||||||
import { renderRecommendationBars } from "../renderers/recommendation";
|
import { renderRecommendationBars } from "../renderers/recommendation";
|
||||||
@@ -158,13 +159,17 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
|
|||||||
import("../dialog-mount").then(async ({ openCompleteDialog }) => {
|
import("../dialog-mount").then(async ({ openCompleteDialog }) => {
|
||||||
const task = this._task!;
|
const task = this._task!;
|
||||||
const isBuy = !!(task as { part_ref?: string }).part_ref;
|
const isBuy = !!(task as { part_ref?: string }).part_ref;
|
||||||
|
// #139: a phased task completes the phase currently due, so the phase's
|
||||||
|
// checklist/parts/required fields replace the task-level ones here.
|
||||||
|
const phase = effectivePhase(task);
|
||||||
|
const links = phase ? phase.consumesParts : (task.consumes_parts || []);
|
||||||
let parts: Parameters<typeof openCompleteDialog>[0]["parts"] = [];
|
let parts: Parameters<typeof openCompleteDialog>[0]["parts"] = [];
|
||||||
if (!isBuy) {
|
if (!isBuy) {
|
||||||
try {
|
try {
|
||||||
const r = await this.hass.connection.sendMessagePromise<{
|
const r = await this.hass.connection.sendMessagePromise<{
|
||||||
objects: MaintenanceObjectResponse[];
|
objects: MaintenanceObjectResponse[];
|
||||||
}>({ type: "maintenance_supporter/objects", compact: true });
|
}>({ type: "maintenance_supporter/objects", compact: true });
|
||||||
parts = partsForCompletion(task, this._entryId!, r.objects || [], this._lang);
|
parts = partsForCompletion({ consumes_parts: links }, this._entryId!, r.objects || [], this._lang);
|
||||||
} catch {
|
} catch {
|
||||||
// Parts stay empty — the dialog still completes without them.
|
// Parts stay empty — the dialog still completes without them.
|
||||||
}
|
}
|
||||||
@@ -173,13 +178,14 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
|
|||||||
entry_id: this._entryId!,
|
entry_id: this._entryId!,
|
||||||
task_id: this._taskId!,
|
task_id: this._taskId!,
|
||||||
task_name: task.name,
|
task_name: task.name,
|
||||||
checklist: task.checklist || [],
|
checklist: phase ? phase.checklist : (task.checklist || []),
|
||||||
adaptive_enabled: !!task.adaptive_config?.enabled,
|
adaptive_enabled: !!task.adaptive_config?.enabled,
|
||||||
required_completion_fields: task.required_completion_fields || [],
|
required_completion_fields: phase ? phase.requiredFields : (task.required_completion_fields || []),
|
||||||
task_type: task.type || "",
|
task_type: task.type || "",
|
||||||
reading_unit: (task as { reading_unit?: string }).reading_unit || "",
|
reading_unit: (task as { reading_unit?: string }).reading_unit || "",
|
||||||
parts,
|
parts,
|
||||||
consumes_parts: isBuy ? [] : (task.consumes_parts || []),
|
consumes_parts: isBuy ? [] : links,
|
||||||
|
phase_label: phaseLabel(task),
|
||||||
});
|
});
|
||||||
if (ok) {
|
if (ok) {
|
||||||
this._notifyChanged("complete");
|
this._notifyChanged("complete");
|
||||||
@@ -542,6 +548,9 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
|
|||||||
${(task.schedule?.kind && !["manual", "one_time"].includes(task.schedule.kind)) || task.interval_days != null
|
${(task.schedule?.kind && !["manual", "one_time"].includes(task.schedule.kind)) || task.interval_days != null
|
||||||
? html`<span><strong>${t("interval", L) || "Interval"}:</strong> ${formatRecurrence(task, L)}</span>`
|
? html`<span><strong>${t("interval", L) || "Interval"}:</strong> ${formatRecurrence(task, L)}</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
${phaseLabel(task)
|
||||||
|
? html`<span><strong>${t("phase_current", L)}:</strong> ${phaseLabel(task)}</span>`
|
||||||
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -289,6 +289,8 @@ export function openCompleteDialog(args: {
|
|||||||
/** #99/#111: per-completion parts selection incl. shared pools. */
|
/** #99/#111: per-completion parts selection incl. shared pools. */
|
||||||
parts?: MaintenanceCompleteDialog["parts"];
|
parts?: MaintenanceCompleteDialog["parts"];
|
||||||
consumes_parts?: MaintenanceCompleteDialog["consumesParts"];
|
consumes_parts?: MaintenanceCompleteDialog["consumesParts"];
|
||||||
|
/** #139: "2/4 · Flip blades" — the phase this completion records. */
|
||||||
|
phase_label?: string;
|
||||||
}): boolean {
|
}): boolean {
|
||||||
const dlg = getOrCreate<MaintenanceCompleteDialog>(COMPLETE_DIALOG_TAG);
|
const dlg = getOrCreate<MaintenanceCompleteDialog>(COMPLETE_DIALOG_TAG);
|
||||||
if (!syncHass(dlg)) return false;
|
if (!syncHass(dlg)) return false;
|
||||||
@@ -304,6 +306,7 @@ export function openCompleteDialog(args: {
|
|||||||
dlg.readingUnit = args.reading_unit ?? "";
|
dlg.readingUnit = args.reading_unit ?? "";
|
||||||
dlg.parts = args.parts ?? [];
|
dlg.parts = args.parts ?? [];
|
||||||
dlg.consumesParts = args.consumes_parts ?? [];
|
dlg.consumesParts = args.consumes_parts ?? [];
|
||||||
|
dlg.phaseLabel = args.phase_label ?? "";
|
||||||
dlg.lang = (getHass()?.language) || "en";
|
dlg.lang = (getHass()?.language) || "en";
|
||||||
dlg.open();
|
dlg.open();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "záznamů",
|
"service_record_entries": "záznamů",
|
||||||
"completed_by": "Dokončil",
|
"completed_by": "Dokončil",
|
||||||
"date_from": "Od",
|
"date_from": "Od",
|
||||||
"date_to": "Do"
|
"date_to": "Do",
|
||||||
|
"phases_section": "Fáze cyklu (volitelné)",
|
||||||
|
"phases_hint": "Různé práce ve stejném rytmu — každé dokončení posune na další krok (např. malý, malý, velký servis).",
|
||||||
|
"phase_add": "Přidat fázi",
|
||||||
|
"phase_name": "Název fáze",
|
||||||
|
"phase_sequence_label": "Pořadí cyklu",
|
||||||
|
"phase_sequence_add_step": "Přidat krok",
|
||||||
|
"phase_current": "Aktuální fáze",
|
||||||
|
"phase_set": "Nastavit jako aktuální",
|
||||||
|
"chart_history_fallback": "Pro tuto entitu nejsou dlouhodobé statistiky — zobrazuje se historie stavů recorderu (obvykle posledních ~10 dní)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "poster",
|
"service_record_entries": "poster",
|
||||||
"completed_by": "Udført af",
|
"completed_by": "Udført af",
|
||||||
"date_from": "Fra",
|
"date_from": "Fra",
|
||||||
"date_to": "Til"
|
"date_to": "Til",
|
||||||
|
"phases_section": "Cyklusfaser (valgfrit)",
|
||||||
|
"phases_hint": "Forskelligt arbejde i samme rytme — hver udførelse går videre til næste trin (f.eks. lille, lille, stor service).",
|
||||||
|
"phase_add": "Tilføj fase",
|
||||||
|
"phase_name": "Fasenavn",
|
||||||
|
"phase_sequence_label": "Cyklusrækkefølge",
|
||||||
|
"phase_sequence_add_step": "Tilføj trin",
|
||||||
|
"phase_current": "Aktuel fase",
|
||||||
|
"phase_set": "Sæt som aktuel",
|
||||||
|
"chart_history_fallback": "Ingen langtidsstatistik for denne enhed — recorderens tilstandshistorik vises (typisk de seneste ~10 dage)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "Einträge",
|
"service_record_entries": "Einträge",
|
||||||
"completed_by": "Erledigt von",
|
"completed_by": "Erledigt von",
|
||||||
"date_from": "Von",
|
"date_from": "Von",
|
||||||
"date_to": "Bis"
|
"date_to": "Bis",
|
||||||
|
"phases_section": "Zyklus-Phasen (optional)",
|
||||||
|
"phases_hint": "Unterschiedliche Arbeiten im gleichen Takt — jede Erledigung schaltet zum nächsten Schritt (z. B. klein, klein, groß).",
|
||||||
|
"phase_add": "Phase hinzufügen",
|
||||||
|
"phase_name": "Phasenname",
|
||||||
|
"phase_sequence_label": "Zyklus-Reihenfolge",
|
||||||
|
"phase_sequence_add_step": "Schritt anhängen",
|
||||||
|
"phase_current": "Aktuelle Phase",
|
||||||
|
"phase_set": "Als aktuell setzen",
|
||||||
|
"chart_history_fallback": "Keine Langzeitstatistik für diese Entität — angezeigt wird der Recorder-Zustandsverlauf (üblicherweise die letzten ~10 Tage)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "entries",
|
"service_record_entries": "entries",
|
||||||
"completed_by": "Completed by",
|
"completed_by": "Completed by",
|
||||||
"date_from": "From",
|
"date_from": "From",
|
||||||
"date_to": "To"
|
"date_to": "To",
|
||||||
|
"phases_section": "Cycle phases (optional)",
|
||||||
|
"phases_hint": "Different work on one shared schedule — each completion moves to the next step (e.g. small service, small service, big service).",
|
||||||
|
"phase_add": "Add phase",
|
||||||
|
"phase_name": "Phase name",
|
||||||
|
"phase_sequence_label": "Cycle order",
|
||||||
|
"phase_sequence_add_step": "Add step",
|
||||||
|
"phase_current": "Current phase",
|
||||||
|
"phase_set": "Set as current",
|
||||||
|
"chart_history_fallback": "No long-term statistics for this entity — showing recorder state history (typically the last ~10 days)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "entradas",
|
"service_record_entries": "entradas",
|
||||||
"completed_by": "Realizado por",
|
"completed_by": "Realizado por",
|
||||||
"date_from": "Desde",
|
"date_from": "Desde",
|
||||||
"date_to": "Hasta"
|
"date_to": "Hasta",
|
||||||
|
"phases_section": "Fases del ciclo (opcional)",
|
||||||
|
"phases_hint": "Trabajos distintos con un mismo ritmo: cada finalización pasa al siguiente paso (p. ej. pequeño, pequeño, grande).",
|
||||||
|
"phase_add": "Añadir fase",
|
||||||
|
"phase_name": "Nombre de la fase",
|
||||||
|
"phase_sequence_label": "Orden del ciclo",
|
||||||
|
"phase_sequence_add_step": "Añadir paso",
|
||||||
|
"phase_current": "Fase actual",
|
||||||
|
"phase_set": "Establecer como actual",
|
||||||
|
"chart_history_fallback": "Sin estadísticas a largo plazo para esta entidad: se muestra el historial de estados del recorder (normalmente los últimos ~10 días)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "merkintää",
|
"service_record_entries": "merkintää",
|
||||||
"completed_by": "Suorittanut",
|
"completed_by": "Suorittanut",
|
||||||
"date_from": "Alkaen",
|
"date_from": "Alkaen",
|
||||||
"date_to": "Asti"
|
"date_to": "Asti",
|
||||||
|
"phases_section": "Syklin vaiheet (valinnainen)",
|
||||||
|
"phases_hint": "Eri työt samassa tahdissa — jokainen suoritus siirtyy seuraavaan vaiheeseen (esim. pieni, pieni, iso huolto).",
|
||||||
|
"phase_add": "Lisää vaihe",
|
||||||
|
"phase_name": "Vaiheen nimi",
|
||||||
|
"phase_sequence_label": "Syklin järjestys",
|
||||||
|
"phase_sequence_add_step": "Lisää askel",
|
||||||
|
"phase_current": "Nykyinen vaihe",
|
||||||
|
"phase_set": "Aseta nykyiseksi",
|
||||||
|
"chart_history_fallback": "Tälle entiteetille ei ole pitkän aikavälin tilastoja — näytetään recorderin tilahistoria (yleensä viimeiset ~10 päivää)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "entrées",
|
"service_record_entries": "entrées",
|
||||||
"completed_by": "Réalisé par",
|
"completed_by": "Réalisé par",
|
||||||
"date_from": "Du",
|
"date_from": "Du",
|
||||||
"date_to": "Au"
|
"date_to": "Au",
|
||||||
|
"phases_section": "Phases du cycle (optionnel)",
|
||||||
|
"phases_hint": "Des travaux différents sur un même rythme — chaque réalisation passe à l'étape suivante (p. ex. petit, petit, grand entretien).",
|
||||||
|
"phase_add": "Ajouter une phase",
|
||||||
|
"phase_name": "Nom de la phase",
|
||||||
|
"phase_sequence_label": "Ordre du cycle",
|
||||||
|
"phase_sequence_add_step": "Ajouter une étape",
|
||||||
|
"phase_current": "Phase actuelle",
|
||||||
|
"phase_set": "Définir comme actuelle",
|
||||||
|
"chart_history_fallback": "Pas de statistiques à long terme pour cette entité — affichage de l'historique d'états du recorder (généralement les ~10 derniers jours)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "प्रविष्टियाँ",
|
"service_record_entries": "प्रविष्टियाँ",
|
||||||
"completed_by": "द्वारा पूर्ण",
|
"completed_by": "द्वारा पूर्ण",
|
||||||
"date_from": "से",
|
"date_from": "से",
|
||||||
"date_to": "तक"
|
"date_to": "तक",
|
||||||
|
"phases_section": "चक्र चरण (वैकल्पिक)",
|
||||||
|
"phases_hint": "एक ही लय में अलग-अलग कार्य — प्रत्येक पूर्णता अगले चरण पर ले जाती है (जैसे छोटा, छोटा, बड़ा)।",
|
||||||
|
"phase_add": "चरण जोड़ें",
|
||||||
|
"phase_name": "चरण का नाम",
|
||||||
|
"phase_sequence_label": "चक्र क्रम",
|
||||||
|
"phase_sequence_add_step": "कदम जोड़ें",
|
||||||
|
"phase_current": "वर्तमान चरण",
|
||||||
|
"phase_set": "वर्तमान बनाएँ",
|
||||||
|
"chart_history_fallback": "इस एंटिटी के लिए दीर्घकालिक आँकड़े नहीं हैं — recorder की स्थिति का इतिहास दिखाया जा रहा है (आमतौर पर पिछले ~10 दिन)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "bejegyzés",
|
"service_record_entries": "bejegyzés",
|
||||||
"completed_by": "Elvégezte",
|
"completed_by": "Elvégezte",
|
||||||
"date_from": "Ettől",
|
"date_from": "Ettől",
|
||||||
"date_to": "Eddig"
|
"date_to": "Eddig",
|
||||||
|
"phases_section": "Ciklusfázisok (opcionális)",
|
||||||
|
"phases_hint": "Különböző munkák azonos ütemben — minden elvégzés a következő lépésre vált (pl. kis, kis, nagy szerviz).",
|
||||||
|
"phase_add": "Fázis hozzáadása",
|
||||||
|
"phase_name": "Fázis neve",
|
||||||
|
"phase_sequence_label": "Ciklus sorrendje",
|
||||||
|
"phase_sequence_add_step": "Lépés hozzáadása",
|
||||||
|
"phase_current": "Aktuális fázis",
|
||||||
|
"phase_set": "Beállítás aktuálisként",
|
||||||
|
"chart_history_fallback": "Ehhez az entitáshoz nincs hosszú távú statisztika — a recorder állapotelőzményei jelennek meg (jellemzően az utolsó ~10 nap)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "voci",
|
"service_record_entries": "voci",
|
||||||
"completed_by": "Completato da",
|
"completed_by": "Completato da",
|
||||||
"date_from": "Dal",
|
"date_from": "Dal",
|
||||||
"date_to": "Al"
|
"date_to": "Al",
|
||||||
|
"phases_section": "Fasi del ciclo (opzionale)",
|
||||||
|
"phases_hint": "Lavori diversi sullo stesso ritmo — ogni completamento passa al passo successivo (es. piccolo, piccolo, grande).",
|
||||||
|
"phase_add": "Aggiungi fase",
|
||||||
|
"phase_name": "Nome della fase",
|
||||||
|
"phase_sequence_label": "Ordine del ciclo",
|
||||||
|
"phase_sequence_add_step": "Aggiungi passo",
|
||||||
|
"phase_current": "Fase attuale",
|
||||||
|
"phase_set": "Imposta come attuale",
|
||||||
|
"chart_history_fallback": "Nessuna statistica a lungo termine per questa entità — viene mostrata la cronologia degli stati del recorder (di solito gli ultimi ~10 giorni)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "件",
|
"service_record_entries": "件",
|
||||||
"completed_by": "実施者",
|
"completed_by": "実施者",
|
||||||
"date_from": "開始",
|
"date_from": "開始",
|
||||||
"date_to": "終了"
|
"date_to": "終了",
|
||||||
|
"phases_section": "サイクルフェーズ(任意)",
|
||||||
|
"phases_hint": "同じ周期で異なる作業を行います。完了するたびに次のステップへ進みます(例:小・小・大整備)。",
|
||||||
|
"phase_add": "フェーズを追加",
|
||||||
|
"phase_name": "フェーズ名",
|
||||||
|
"phase_sequence_label": "サイクルの順序",
|
||||||
|
"phase_sequence_add_step": "ステップを追加",
|
||||||
|
"phase_current": "現在のフェーズ",
|
||||||
|
"phase_set": "現在に設定",
|
||||||
|
"chart_history_fallback": "このエンティティには長期統計がありません — レコーダーの状態履歴を表示しています(通常は直近約10日分)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "건",
|
"service_record_entries": "건",
|
||||||
"completed_by": "수행자",
|
"completed_by": "수행자",
|
||||||
"date_from": "시작",
|
"date_from": "시작",
|
||||||
"date_to": "종료"
|
"date_to": "종료",
|
||||||
|
"phases_section": "주기 단계(선택)",
|
||||||
|
"phases_hint": "같은 주기에 다른 작업을 수행합니다. 완료할 때마다 다음 단계로 넘어갑니다(예: 소·소·대 정비).",
|
||||||
|
"phase_add": "단계 추가",
|
||||||
|
"phase_name": "단계 이름",
|
||||||
|
"phase_sequence_label": "주기 순서",
|
||||||
|
"phase_sequence_add_step": "스텝 추가",
|
||||||
|
"phase_current": "현재 단계",
|
||||||
|
"phase_set": "현재로 설정",
|
||||||
|
"chart_history_fallback": "이 엔티티에는 장기 통계가 없습니다 — 레코더 상태 기록을 표시합니다(보통 최근 약 10일)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "oppføringer",
|
"service_record_entries": "oppføringer",
|
||||||
"completed_by": "Utført av",
|
"completed_by": "Utført av",
|
||||||
"date_from": "Fra",
|
"date_from": "Fra",
|
||||||
"date_to": "Til"
|
"date_to": "Til",
|
||||||
|
"phases_section": "Syklusfaser (valgfritt)",
|
||||||
|
"phases_hint": "Ulikt arbeid i samme rytme — hver fullføring går videre til neste trinn (f.eks. liten, liten, stor service).",
|
||||||
|
"phase_add": "Legg til fase",
|
||||||
|
"phase_name": "Fasenavn",
|
||||||
|
"phase_sequence_label": "Syklusrekkefølge",
|
||||||
|
"phase_sequence_add_step": "Legg til trinn",
|
||||||
|
"phase_current": "Gjeldende fase",
|
||||||
|
"phase_set": "Sett som gjeldende",
|
||||||
|
"chart_history_fallback": "Ingen langtidsstatistikk for denne enheten — recorderens tilstandshistorikk vises (vanligvis de siste ~10 dagene)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "items",
|
"service_record_entries": "items",
|
||||||
"completed_by": "Voltooid door",
|
"completed_by": "Voltooid door",
|
||||||
"date_from": "Van",
|
"date_from": "Van",
|
||||||
"date_to": "Tot"
|
"date_to": "Tot",
|
||||||
|
"phases_section": "Cyclusfasen (optioneel)",
|
||||||
|
"phases_hint": "Verschillend werk op één ritme — elke afronding schuift naar de volgende stap (bijv. klein, klein, groot).",
|
||||||
|
"phase_add": "Fase toevoegen",
|
||||||
|
"phase_name": "Fasenaam",
|
||||||
|
"phase_sequence_label": "Cyclusvolgorde",
|
||||||
|
"phase_sequence_add_step": "Stap toevoegen",
|
||||||
|
"phase_current": "Huidige fase",
|
||||||
|
"phase_set": "Instellen als huidige",
|
||||||
|
"chart_history_fallback": "Geen langetermijnstatistieken voor deze entiteit — de recorder-statusgeschiedenis wordt getoond (meestal de laatste ~10 dagen)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "wpisów",
|
"service_record_entries": "wpisów",
|
||||||
"completed_by": "Wykonane przez",
|
"completed_by": "Wykonane przez",
|
||||||
"date_from": "Od",
|
"date_from": "Od",
|
||||||
"date_to": "Do"
|
"date_to": "Do",
|
||||||
|
"phases_section": "Fazy cyklu (opcjonalnie)",
|
||||||
|
"phases_hint": "Różne prace w jednym rytmie — każde ukończenie przechodzi do następnego kroku (np. mały, mały, duży).",
|
||||||
|
"phase_add": "Dodaj fazę",
|
||||||
|
"phase_name": "Nazwa fazy",
|
||||||
|
"phase_sequence_label": "Kolejność cyklu",
|
||||||
|
"phase_sequence_add_step": "Dodaj krok",
|
||||||
|
"phase_current": "Bieżąca faza",
|
||||||
|
"phase_set": "Ustaw jako bieżącą",
|
||||||
|
"chart_history_fallback": "Brak statystyk długoterminowych dla tej encji — wyświetlana jest historia stanów recordera (zwykle ostatnie ~10 dni)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "registros",
|
"service_record_entries": "registros",
|
||||||
"completed_by": "Concluído por",
|
"completed_by": "Concluído por",
|
||||||
"date_from": "De",
|
"date_from": "De",
|
||||||
"date_to": "Até"
|
"date_to": "Até",
|
||||||
|
"phases_section": "Fases do ciclo (opcional)",
|
||||||
|
"phases_hint": "Trabalhos diferentes no mesmo ritmo — cada conclusão avança para o próximo passo (ex.: pequeno, pequeno, grande).",
|
||||||
|
"phase_add": "Adicionar fase",
|
||||||
|
"phase_name": "Nome da fase",
|
||||||
|
"phase_sequence_label": "Ordem do ciclo",
|
||||||
|
"phase_sequence_add_step": "Adicionar passo",
|
||||||
|
"phase_current": "Fase atual",
|
||||||
|
"phase_set": "Definir como atual",
|
||||||
|
"chart_history_fallback": "Sem estatísticas de longo prazo para esta entidade — mostrando o histórico de estados do recorder (normalmente os últimos ~10 dias)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "registos",
|
"service_record_entries": "registos",
|
||||||
"completed_by": "Concluído por",
|
"completed_by": "Concluído por",
|
||||||
"date_from": "De",
|
"date_from": "De",
|
||||||
"date_to": "Até"
|
"date_to": "Até",
|
||||||
|
"phases_section": "Fases do ciclo (opcional)",
|
||||||
|
"phases_hint": "Trabalhos diferentes no mesmo ritmo — cada conclusão avança para o passo seguinte (ex.: pequeno, pequeno, grande).",
|
||||||
|
"phase_add": "Adicionar fase",
|
||||||
|
"phase_name": "Nome da fase",
|
||||||
|
"phase_sequence_label": "Ordem do ciclo",
|
||||||
|
"phase_sequence_add_step": "Adicionar passo",
|
||||||
|
"phase_current": "Fase atual",
|
||||||
|
"phase_set": "Definir como atual",
|
||||||
|
"chart_history_fallback": "Sem estatísticas de longo prazo para esta entidade — a mostrar o histórico de estados do recorder (normalmente os últimos ~10 dias)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "записей",
|
"service_record_entries": "записей",
|
||||||
"completed_by": "Выполнил",
|
"completed_by": "Выполнил",
|
||||||
"date_from": "С",
|
"date_from": "С",
|
||||||
"date_to": "По"
|
"date_to": "По",
|
||||||
|
"phases_section": "Фазы цикла (необязательно)",
|
||||||
|
"phases_hint": "Разные работы в одном ритме — каждое выполнение переходит к следующему шагу (напр., малое, малое, большое ТО).",
|
||||||
|
"phase_add": "Добавить фазу",
|
||||||
|
"phase_name": "Название фазы",
|
||||||
|
"phase_sequence_label": "Порядок цикла",
|
||||||
|
"phase_sequence_add_step": "Добавить шаг",
|
||||||
|
"phase_current": "Текущая фаза",
|
||||||
|
"phase_set": "Сделать текущей",
|
||||||
|
"chart_history_fallback": "Для этой сущности нет долгосрочной статистики — показана история состояний recorder (обычно последние ~10 дней)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "poster",
|
"service_record_entries": "poster",
|
||||||
"completed_by": "Utförd av",
|
"completed_by": "Utförd av",
|
||||||
"date_from": "Från",
|
"date_from": "Från",
|
||||||
"date_to": "Till"
|
"date_to": "Till",
|
||||||
|
"phases_section": "Cykelfaser (valfritt)",
|
||||||
|
"phases_hint": "Olika arbeten i samma rytm — varje slutförande går vidare till nästa steg (t.ex. liten, liten, stor service).",
|
||||||
|
"phase_add": "Lägg till fas",
|
||||||
|
"phase_name": "Fasnamn",
|
||||||
|
"phase_sequence_label": "Cykelordning",
|
||||||
|
"phase_sequence_add_step": "Lägg till steg",
|
||||||
|
"phase_current": "Aktuell fas",
|
||||||
|
"phase_set": "Ange som aktuell",
|
||||||
|
"chart_history_fallback": "Ingen långtidsstatistik för denna entitet — recorderns tillståndshistorik visas (vanligtvis de senaste ~10 dagarna)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "kayıt",
|
"service_record_entries": "kayıt",
|
||||||
"completed_by": "Tamamlayan",
|
"completed_by": "Tamamlayan",
|
||||||
"date_from": "Başlangıç",
|
"date_from": "Başlangıç",
|
||||||
"date_to": "Bitiş"
|
"date_to": "Bitiş",
|
||||||
|
"phases_section": "Döngü aşamaları (isteğe bağlı)",
|
||||||
|
"phases_hint": "Aynı ritimde farklı işler — her tamamlama bir sonraki adıma geçer (örn. küçük, küçük, büyük bakım).",
|
||||||
|
"phase_add": "Aşama ekle",
|
||||||
|
"phase_name": "Aşama adı",
|
||||||
|
"phase_sequence_label": "Döngü sırası",
|
||||||
|
"phase_sequence_add_step": "Adım ekle",
|
||||||
|
"phase_current": "Geçerli aşama",
|
||||||
|
"phase_set": "Geçerli olarak ayarla",
|
||||||
|
"chart_history_fallback": "Bu varlık için uzun dönem istatistik yok — recorder durum geçmişi gösteriliyor (genellikle son ~10 gün)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "записів",
|
"service_record_entries": "записів",
|
||||||
"completed_by": "Виконав",
|
"completed_by": "Виконав",
|
||||||
"date_from": "Від",
|
"date_from": "Від",
|
||||||
"date_to": "До"
|
"date_to": "До",
|
||||||
|
"phases_section": "Фази циклу (необов'язково)",
|
||||||
|
"phases_hint": "Різні роботи в одному ритмі — кожне виконання переходить до наступного кроку (напр., мале, мале, велике ТО).",
|
||||||
|
"phase_add": "Додати фазу",
|
||||||
|
"phase_name": "Назва фази",
|
||||||
|
"phase_sequence_label": "Порядок циклу",
|
||||||
|
"phase_sequence_add_step": "Додати крок",
|
||||||
|
"phase_current": "Поточна фаза",
|
||||||
|
"phase_set": "Зробити поточною",
|
||||||
|
"chart_history_fallback": "Для цієї сутності немає довгострокової статистики — показано історію станів recorder (зазвичай останні ~10 днів)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "条记录",
|
"service_record_entries": "条记录",
|
||||||
"completed_by": "完成人",
|
"completed_by": "完成人",
|
||||||
"date_from": "从",
|
"date_from": "从",
|
||||||
"date_to": "至"
|
"date_to": "至",
|
||||||
|
"phases_section": "循环阶段(可选)",
|
||||||
|
"phases_hint": "同一节奏下的不同工作——每次完成后进入下一步(例如:小保养、小保养、大保养)。",
|
||||||
|
"phase_add": "添加阶段",
|
||||||
|
"phase_name": "阶段名称",
|
||||||
|
"phase_sequence_label": "循环顺序",
|
||||||
|
"phase_sequence_add_step": "添加步骤",
|
||||||
|
"phase_current": "当前阶段",
|
||||||
|
"phase_set": "设为当前",
|
||||||
|
"chart_history_fallback": "该实体没有长期统计数据——显示 recorder 状态历史(通常为最近约 10 天)"
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@ import type {
|
|||||||
} from "./types";
|
} from "./types";
|
||||||
import { UserService } from "./user-service";
|
import { UserService } from "./user-service";
|
||||||
import { partsForCompletion } from "./helpers/shared-parts";
|
import { partsForCompletion } from "./helpers/shared-parts";
|
||||||
|
import { effectivePhase, phaseLabel } from "./helpers/phases";
|
||||||
import "./maintenance-card-editor";
|
import "./maintenance-card-editor";
|
||||||
import "./components/complete-dialog";
|
import "./components/complete-dialog";
|
||||||
// The Battery Fleet card ships in this globally-loaded bundle so it is
|
// The Battery Fleet card ships in this globally-loaded bundle so it is
|
||||||
@@ -469,7 +470,7 @@ export class MaintenanceSupporterCard extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
${!compact
|
${!compact
|
||||||
? html`<div class="task-meta">
|
? html`<div class="task-meta">
|
||||||
${object_name} · ${t(task.type, L)}${this._assigneeName(task)
|
${object_name} · ${t(task.type, L)}${phaseLabel(task) ? html` · ${phaseLabel(task)}` : nothing}${this._assigneeName(task)
|
||||||
? html` · <span class="assignee"
|
? html` · <span class="assignee"
|
||||||
><ha-icon icon="mdi:account"></ha-icon>${this._assigneeName(task)}</span
|
><ha-icon icon="mdi:account"></ha-icon>${this._assigneeName(task)}</span
|
||||||
>`
|
>`
|
||||||
@@ -514,14 +515,19 @@ export class MaintenanceSupporterCard extends LitElement {
|
|||||||
// Stop the row's open-task handler from also firing
|
// Stop the row's open-task handler from also firing
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const dlg = this.shadowRoot!.querySelector("maintenance-complete-dialog") as any;
|
const dlg = this.shadowRoot!.querySelector("maintenance-complete-dialog") as any;
|
||||||
|
// #139: a phased task completes the phase
|
||||||
|
// currently due — its fields override the
|
||||||
|
// task-level ones (fallthrough included).
|
||||||
|
const phase = effectivePhase(task);
|
||||||
dlg.entryId = entry_id;
|
dlg.entryId = entry_id;
|
||||||
dlg.taskId = task.id;
|
dlg.taskId = task.id;
|
||||||
dlg.taskName = task.name;
|
dlg.taskName = task.name;
|
||||||
dlg.checklist = task.checklist || [];
|
dlg.checklist = phase ? phase.checklist : (task.checklist || []);
|
||||||
dlg.adaptiveEnabled = !!task.adaptive_config?.enabled;
|
dlg.adaptiveEnabled = !!task.adaptive_config?.enabled;
|
||||||
dlg.taskType = task.type || "";
|
dlg.taskType = task.type || "";
|
||||||
dlg.readingUnit = (task as any).reading_unit || "";
|
dlg.readingUnit = (task as any).reading_unit || "";
|
||||||
dlg.requiredFields = task.required_completion_fields || [];
|
dlg.requiredFields = phase ? phase.requiredFields : (task.required_completion_fields || []);
|
||||||
|
dlg.phaseLabel = phaseLabel(task);
|
||||||
dlg.lang = L;
|
dlg.lang = L;
|
||||||
// #99: editable per-completion parts selection
|
// #99: editable per-completion parts selection
|
||||||
// (skip on buy tasks — those restock instead).
|
// (skip on buy tasks — those restock instead).
|
||||||
@@ -530,8 +536,9 @@ export class MaintenanceSupporterCard extends LitElement {
|
|||||||
// owner — resolving against the object's own
|
// owner — resolving against the object's own
|
||||||
// parts alone left a foreign link invisible.
|
// parts alone left a foreign link invisible.
|
||||||
const isBuy = !!(task as any).part_ref;
|
const isBuy = !!(task as any).part_ref;
|
||||||
dlg.parts = isBuy ? [] : partsForCompletion(task, entry_id, this._objects, L);
|
const links = phase ? phase.consumesParts : (task.consumes_parts || []);
|
||||||
dlg.consumesParts = isBuy ? [] : (task.consumes_parts || []);
|
dlg.parts = isBuy ? [] : partsForCompletion({ consumes_parts: links }, entry_id, this._objects, L);
|
||||||
|
dlg.consumesParts = isBuy ? [] : links;
|
||||||
dlg.open();
|
dlg.open();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { OBJECT_COLUMNS, DEFAULT_OBJECTS_TABLE_COLUMNS, sanitizeColumns } from "
|
|||||||
import { downloadTextFile } from "./helpers/download";
|
import { downloadTextFile } from "./helpers/download";
|
||||||
import { buildTaskWorksheetHtml, type WorksheetExcerpt, type WorksheetLabels } from "./helpers/worksheet";
|
import { buildTaskWorksheetHtml, type WorksheetExcerpt, type WorksheetLabels } from "./helpers/worksheet";
|
||||||
import { describePartLink, partsForCompletion } from "./helpers/shared-parts";
|
import { describePartLink, partsForCompletion } from "./helpers/shared-parts";
|
||||||
|
import { effectivePhase, phaseLabel } from "./helpers/phases";
|
||||||
import { describeWsError } from "./ws-errors";
|
import { describeWsError } from "./ws-errors";
|
||||||
import { panelStyles } from "./panel-styles";
|
import { panelStyles } from "./panel-styles";
|
||||||
import type {
|
import type {
|
||||||
@@ -1861,7 +1862,9 @@ export class MaintenanceSupporterPanel extends LitElement {
|
|||||||
const task = obj?.tasks.find((x) => x.id === taskId);
|
const task = obj?.tasks.find((x) => x.id === taskId);
|
||||||
if (!task) return;
|
if (!task) return;
|
||||||
const state: Record<string, boolean> = {};
|
const state: Record<string, boolean> = {};
|
||||||
for (const step of task.checklist || []) {
|
// #139: a phased task ticks the checklist of the phase currently due.
|
||||||
|
const steps = effectivePhase(task)?.checklist ?? (task.checklist || []);
|
||||||
|
for (const step of steps) {
|
||||||
const current = task.checklist_progress?.[step] ?? false;
|
const current = task.checklist_progress?.[step] ?? false;
|
||||||
state[step] = step === item ? done : current;
|
state[step] = step === item ? done : current;
|
||||||
}
|
}
|
||||||
@@ -1888,11 +1891,20 @@ export class MaintenanceSupporterPanel extends LitElement {
|
|||||||
const tk = this._objects
|
const tk = this._objects
|
||||||
.find((o) => o.entry_id === entryId)
|
.find((o) => o.entry_id === entryId)
|
||||||
?.tasks.find((tsk) => tsk.id === taskId);
|
?.tasks.find((tsk) => tsk.id === taskId);
|
||||||
|
// #139: a phased task completes the phase currently due — its checklist,
|
||||||
|
// parts and required fields override the task-level values (fallthrough
|
||||||
|
// is already baked into effectivePhase). Resolved HERE so every call
|
||||||
|
// site of _openCompleteDialog rotates for free.
|
||||||
|
const phase = tk ? effectivePhase(tk) : null;
|
||||||
|
dlg.phaseLabel = phase ? phaseLabel(tk) : "";
|
||||||
dlg.taskType = tk?.type || "";
|
dlg.taskType = tk?.type || "";
|
||||||
dlg.readingUnit = tk?.reading_unit || "";
|
dlg.readingUnit = tk?.reading_unit || "";
|
||||||
// #73: ticks recorded during the cycle prefill the dialog's checklist.
|
// #73: ticks recorded during the cycle prefill the dialog's checklist.
|
||||||
dlg.checklistPrefill = tk?.checklist_progress || {};
|
dlg.checklistPrefill = tk?.checklist_progress || {};
|
||||||
dlg.requiredFields = tk?.required_completion_fields || [];
|
dlg.requiredFields = phase ? phase.requiredFields : (tk?.required_completion_fields || []);
|
||||||
|
if (phase) {
|
||||||
|
dlg.checklist = this._features.checklists ? phase.checklist : [];
|
||||||
|
}
|
||||||
// Spare parts: a buy task gets an editable restock-qty field; a consuming
|
// Spare parts: a buy task gets an editable restock-qty field; a consuming
|
||||||
// task shows what it will decrement (incl. the storage location).
|
// task shows what it will decrement (incl. the storage location).
|
||||||
const objParts = this._objects.find((o) => o.entry_id === entryId)?.parts || [];
|
const objParts = this._objects.find((o) => o.entry_id === entryId)?.parts || [];
|
||||||
@@ -1904,15 +1916,16 @@ export class MaintenanceSupporterPanel extends LitElement {
|
|||||||
dlg.currencySymbol = this._currencySymbol;
|
dlg.currencySymbol = this._currencySymbol;
|
||||||
// #111: a link may point at another object's pool — name that object, and
|
// #111: a link may point at another object's pool — name that object, and
|
||||||
// never drop a line that fails to resolve (the old .filter(Boolean) hid it).
|
// never drop a line that fails to resolve (the old .filter(Boolean) hid it).
|
||||||
dlg.consumesInfo = (tk?.consumes_parts || []).map((link) =>
|
const links = phase ? phase.consumesParts : (tk?.consumes_parts || []);
|
||||||
|
dlg.consumesInfo = links.map((link) =>
|
||||||
describePartLink(link, entryId, this._objects, this._lang),
|
describePartLink(link, entryId, this._objects, this._lang),
|
||||||
);
|
);
|
||||||
// #99: editable per-completion parts selection (not on buy tasks — those
|
// #99: editable per-completion parts selection (not on buy tasks — those
|
||||||
// RESTOCK via the qty field instead of consuming). The list carries the
|
// RESTOCK via the qty field instead of consuming). The list carries the
|
||||||
// object's own parts plus the shared pools this task draws on, so a foreign
|
// object's own parts plus the shared pools this task draws on, so a foreign
|
||||||
// link is visible and untickable rather than silently absent.
|
// link is visible and untickable rather than silently absent.
|
||||||
dlg.parts = tk?.part_ref ? [] : partsForCompletion(tk, entryId, this._objects, this._lang);
|
dlg.parts = tk?.part_ref ? [] : partsForCompletion({ consumes_parts: links }, entryId, this._objects, this._lang);
|
||||||
dlg.consumesParts = tk?.part_ref ? [] : (tk?.consumes_parts || []);
|
dlg.consumesParts = tk?.part_ref ? [] : links;
|
||||||
dlg.open();
|
dlg.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3535,6 +3548,7 @@ export class MaintenanceSupporterPanel extends LitElement {
|
|||||||
lang: this._lang,
|
lang: this._lang,
|
||||||
detailStatsData: this._detailStatsData,
|
detailStatsData: this._detailStatsData,
|
||||||
hasStatsService: !!this._statsService,
|
hasStatsService: !!this._statsService,
|
||||||
|
historyFallbackIds: this._statsService?.historyFallbackIds,
|
||||||
isCounterEntity: (tc) => this._isCounterEntity(tc),
|
isCounterEntity: (tc) => this._isCounterEntity(tc),
|
||||||
rangeDays: this._chartRangeDays,
|
rangeDays: this._chartRangeDays,
|
||||||
setRangeDays: (days) => this._setChartRange(days),
|
setRangeDays: (days) => this._setChartRange(days),
|
||||||
@@ -3579,6 +3593,10 @@ export class MaintenanceSupporterPanel extends LitElement {
|
|||||||
setSearch: (s) => { this._historySearch = s; },
|
setSearch: (s) => { this._historySearch = s; },
|
||||||
openEdit: (entry) => this._openHistoryEdit(entry),
|
openEdit: (entry) => this._openHistoryEdit(entry),
|
||||||
readingUnit: task?.reading_unit ?? null,
|
readingUnit: task?.reading_unit ?? null,
|
||||||
|
// #139: name the phase badge on entries stamped with a phase_id.
|
||||||
|
phaseNames: Object.fromEntries(
|
||||||
|
Object.entries(task?.phases || {}).map(([id, def]) => [id, def.name]),
|
||||||
|
),
|
||||||
readingDelta: (entry) => {
|
readingDelta: (entry) => {
|
||||||
const idx = readings.findIndex((h) => h.timestamp === entry.timestamp);
|
const idx = readings.findIndex((h) => h.timestamp === entry.timestamp);
|
||||||
if (idx <= 0) return null;
|
if (idx <= 0) return null;
|
||||||
@@ -3604,6 +3622,12 @@ export class MaintenanceSupporterPanel extends LitElement {
|
|||||||
objectManualDocs: obj?.object?.manual_docs ?? [],
|
objectManualDocs: obj?.object?.manual_docs ?? [],
|
||||||
openManualDoc: (doc) => this._openManualDoc(doc),
|
openManualDoc: (doc) => this._openManualDoc(doc),
|
||||||
setChecklistItem: (item, done) => this._setChecklistItem(entryId, taskId, item, done),
|
setChecklistItem: (item, done) => this._setChecklistItem(entryId, taskId, item, done),
|
||||||
|
setPhaseCursor: (cursor) => {
|
||||||
|
void this._runAction({
|
||||||
|
type: "maintenance_supporter/task/set_phase",
|
||||||
|
entry_id: entryId, task_id: taskId, cursor,
|
||||||
|
});
|
||||||
|
},
|
||||||
isOperator: this._isOperator,
|
isOperator: this._isOperator,
|
||||||
actionLoading: this._actionLoading,
|
actionLoading: this._actionLoading,
|
||||||
moreMenuOpen: this._moreMenuOpen,
|
moreMenuOpen: this._moreMenuOpen,
|
||||||
|
|||||||
@@ -12,12 +12,16 @@
|
|||||||
"playwright": "1.62"
|
"playwright": "1.62"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@mdi/js": "^7.4.47",
|
||||||
"@open-wc/testing": "^5.0.0",
|
"@open-wc/testing": "^5.0.0",
|
||||||
|
"@types/react": "^19.2.18",
|
||||||
"@web/dev-server-esbuild": "^2.0.0",
|
"@web/dev-server-esbuild": "^2.0.0",
|
||||||
"@web/test-runner": "^1.0.0",
|
"@web/test-runner": "^1.0.0",
|
||||||
"@web/test-runner-playwright": "^1.0.0",
|
"@web/test-runner-playwright": "^1.0.0",
|
||||||
"esbuild": "^0.28.1",
|
"esbuild": "^0.28.1",
|
||||||
"lit": "^3.3.3",
|
"lit": "^3.3.3",
|
||||||
|
"react": "^19.2.8",
|
||||||
|
"react-dom": "^19.2.8",
|
||||||
"typescript": "^7.0.2"
|
"typescript": "^7.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -550,6 +554,13 @@
|
|||||||
"@lit-labs/ssr-dom-shim": "^1.5.0"
|
"@lit-labs/ssr-dom-shim": "^1.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mdi/js": {
|
||||||
|
"version": "7.4.47",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz",
|
||||||
|
"integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/@mdn/browser-compat-data": {
|
"node_modules/@mdn/browser-compat-data": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-4.2.1.tgz",
|
||||||
@@ -1586,6 +1597,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react": {
|
||||||
|
"version": "19.2.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
|
||||||
|
"integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": "^3.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/resolve": {
|
"node_modules/@types/resolve": {
|
||||||
"version": "1.20.2",
|
"version": "1.20.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||||
@@ -2921,6 +2942,13 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/csstype": {
|
||||||
|
"version": "3.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/data-uri-to-buffer": {
|
"node_modules/data-uri-to-buffer": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
|
||||||
@@ -4981,6 +5009,29 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react": {
|
||||||
|
"version": "19.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
|
||||||
|
"integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-dom": {
|
||||||
|
"version": "19.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
|
||||||
|
"integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"scheduler": "^0.27.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^19.2.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||||
@@ -5221,6 +5272,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/scheduler": {
|
||||||
|
"version": "0.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
||||||
|
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.4",
|
"version": "7.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||||
|
|||||||
@@ -9,12 +9,16 @@
|
|||||||
"test:watch": "web-test-runner --config web-test-runner.config.mjs --watch"
|
"test:watch": "web-test-runner --config web-test-runner.config.mjs --watch"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@mdi/js": "^7.4.47",
|
||||||
"@open-wc/testing": "^5.0.0",
|
"@open-wc/testing": "^5.0.0",
|
||||||
|
"@types/react": "^19.2.18",
|
||||||
"@web/dev-server-esbuild": "^2.0.0",
|
"@web/dev-server-esbuild": "^2.0.0",
|
||||||
"@web/test-runner": "^1.0.0",
|
"@web/test-runner": "^1.0.0",
|
||||||
"@web/test-runner-playwright": "^1.0.0",
|
"@web/test-runner-playwright": "^1.0.0",
|
||||||
"esbuild": "^0.28.1",
|
"esbuild": "^0.28.1",
|
||||||
"lit": "^3.3.3",
|
"lit": "^3.3.3",
|
||||||
|
"react": "^19.2.8",
|
||||||
|
"react-dom": "^19.2.8",
|
||||||
"typescript": "^7.0.2"
|
"typescript": "^7.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1053,6 +1053,53 @@ export const panelStyles = css`
|
|||||||
border-color: var(--primary-color);
|
border-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* #139: cycle-phase strip in the task overview */
|
||||||
|
.phases-card {
|
||||||
|
background: var(--card-background-color, #fff);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.phases-card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.phases-card-header ha-icon {
|
||||||
|
--mdc-icon-size: 18px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.phases-strip {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.phase-step {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.phase-step.current {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
background: color-mix(in srgb, var(--primary-color) 12%, transparent);
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.phase-step-last {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
/* Checklist preview card (read-only display in task overview) */
|
/* Checklist preview card (read-only display in task overview) */
|
||||||
.checklist-preview-card {
|
.checklist-preview-card {
|
||||||
background: var(--card-background-color, #fff);
|
background: var(--card-background-color, #fff);
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ export interface HistoryContext {
|
|||||||
/** v2.20 (#83): unit + delta-vs-previous for reading-task entries. */
|
/** v2.20 (#83): unit + delta-vs-previous for reading-task entries. */
|
||||||
readingUnit?: string | null;
|
readingUnit?: string | null;
|
||||||
readingDelta?: (entry: HistoryEntry) => number | null;
|
readingDelta?: (entry: HistoryEntry) => number | null;
|
||||||
|
/** #139: phase id → display name, for entries stamped with a phase_id. */
|
||||||
|
phaseNames?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every HistoryEntryType is filterable (drift audit 2026-08: the repair-flow
|
// Every HistoryEntryType is filterable (drift audit 2026-08: the repair-flow
|
||||||
@@ -96,6 +98,7 @@ export function renderHistoryEntry(entry: HistoryEntry, ctx: HistoryContext) {
|
|||||||
<div class="history-content">
|
<div class="history-content">
|
||||||
<div class="history-row">
|
<div class="history-row">
|
||||||
<strong>${t(entry.type, L)}</strong>
|
<strong>${t(entry.type, L)}</strong>
|
||||||
|
${entry.phase_id ? html`<span class="history-phase-badge">${ctx.phaseNames?.[entry.phase_id] || entry.phase_id}</span>` : nothing}
|
||||||
${entry.auto ? html`<span class="history-auto-badge">${t("history_auto", L)}</span>` : nothing}
|
${entry.auto ? html`<span class="history-auto-badge">${t("history_auto", L)}</span>` : nothing}
|
||||||
${editable
|
${editable
|
||||||
? html`<button class="history-edit-btn"
|
? html`<button class="history-edit-btn"
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ export interface SparklineContext {
|
|||||||
lang: string;
|
lang: string;
|
||||||
detailStatsData: Map<string, StatisticsPoint[]>;
|
detailStatsData: Map<string, StatisticsPoint[]>;
|
||||||
hasStatsService: boolean;
|
hasStatsService: boolean;
|
||||||
|
/** #141: entities whose series is recorder STATE HISTORY (no long-term
|
||||||
|
* statistics exist — binary sensors etc.). Changes the footnote and
|
||||||
|
* suppresses the trigger_current_value tail point (for a state_change
|
||||||
|
* trigger that is the change COUNTER, not the plotted state). */
|
||||||
|
historyFallbackIds?: Set<string>;
|
||||||
isCounterEntity: (tc: TriggerConfig) => boolean;
|
isCounterEntity: (tc: TriggerConfig) => boolean;
|
||||||
rangeDays: number;
|
rangeDays: number;
|
||||||
setRangeDays: (days: number) => void;
|
setRangeDays: (days: number) => void;
|
||||||
@@ -252,7 +257,11 @@ function rawStatsPoints(task: MaintenanceTask, ctx: SparklineContext): ChartPoin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (task.trigger_current_value != null) {
|
// #141: a history-fallback series already runs to "now" and plots the
|
||||||
|
// STATE — appending trigger_current_value (a change counter on
|
||||||
|
// state_change triggers) would graft a wrong-dimension point onto it.
|
||||||
|
const fromHistory = !!entityId && !!ctx.historyFallbackIds?.has(entityId) && statsPoints.length >= 2;
|
||||||
|
if (task.trigger_current_value != null && !fromHistory) {
|
||||||
points.push({ ts: Date.now(), val: task.trigger_current_value });
|
points.push({ ts: Date.now(), val: task.trigger_current_value });
|
||||||
}
|
}
|
||||||
points.sort((a, b) => a.ts - b.ts);
|
points.sort((a, b) => a.ts - b.ts);
|
||||||
@@ -364,11 +373,23 @@ function renderChart(task: MaintenanceTask, unit: string, ctx: SparklineContext)
|
|||||||
@range-change=${(e: CustomEvent<{ days: number }>) => ctx.setRangeDays(e.detail.days)}
|
@range-change=${(e: CustomEvent<{ days: number }>) => ctx.setRangeDays(e.detail.days)}
|
||||||
@outlier-toggle=${(e: CustomEvent<{ hide: boolean }>) => ctx.setHideOutliers(e.detail.hide)}
|
@outlier-toggle=${(e: CustomEvent<{ hide: boolean }>) => ctx.setHideOutliers(e.detail.hide)}
|
||||||
></maintenance-trigger-chart>
|
></maintenance-trigger-chart>
|
||||||
${statsFetchedEmpty && !loading
|
${(() => {
|
||||||
? html`<div class="chart-note">
|
if (loading) return nothing;
|
||||||
|
// #141: the series came from recorder state history (binary sensors
|
||||||
|
// never have long-term statistics) — say so, incl. the shorter window.
|
||||||
|
if (entityId && ctx.historyFallbackIds?.has(entityId) && !statsFetchedEmpty) {
|
||||||
|
return html`<div class="chart-note">
|
||||||
|
<ha-icon icon="mdi:information-outline"></ha-icon>
|
||||||
|
${t("chart_history_fallback", ctx.lang)}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
if (statsFetchedEmpty) {
|
||||||
|
return html`<div class="chart-note">
|
||||||
<ha-icon icon="mdi:information-outline"></ha-icon>
|
<ha-icon icon="mdi:information-outline"></ha-icon>
|
||||||
${t("chart_no_stats", ctx.lang)}
|
${t("chart_no_stats", ctx.lang)}
|
||||||
</div>`
|
</div>`;
|
||||||
: nothing}
|
}
|
||||||
|
return nothing;
|
||||||
|
})()}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ import { renderSeasonalCardCompact, renderSeasonalCardExpanded } from "./seasona
|
|||||||
import { renderCostDurationCard } from "./charts";
|
import { renderCostDurationCard } from "./charts";
|
||||||
import { renderDaysProgress } from "./progress";
|
import { renderDaysProgress } from "./progress";
|
||||||
import { renderHistoryFilters, renderHistoryList, type HistoryContext } from "./history";
|
import { renderHistoryFilters, renderHistoryList, type HistoryContext } from "./history";
|
||||||
|
import { clampPhaseCursor, effectivePhase, hasPhases } from "../helpers/phases";
|
||||||
import "../components/task-documents";
|
import "../components/task-documents";
|
||||||
|
|
||||||
export interface TaskDetailContext {
|
export interface TaskDetailContext {
|
||||||
@@ -39,6 +40,8 @@ export interface TaskDetailContext {
|
|||||||
openManualDoc: (doc: ManualDocRef) => void;
|
openManualDoc: (doc: ManualDocRef) => void;
|
||||||
/** #73: persist one checklist tick (panel sends the full state via WS). */
|
/** #73: persist one checklist tick (panel sends the full state via WS). */
|
||||||
setChecklistItem: (item: string, done: boolean) => void;
|
setChecklistItem: (item: string, done: boolean) => void;
|
||||||
|
/** #139: re-point the phase cursor (operator repair / mid-cycle adoption). */
|
||||||
|
setPhaseCursor: (cursor: number) => void;
|
||||||
isOperator: boolean;
|
isOperator: boolean;
|
||||||
actionLoading: boolean;
|
actionLoading: boolean;
|
||||||
moreMenuOpen: boolean;
|
moreMenuOpen: boolean;
|
||||||
@@ -190,9 +193,51 @@ function collapsible(key: string, titleKey: string, body: unknown, ctx: TaskDeta
|
|||||||
* reloads, prefills the complete dialog) and resets when the task is
|
* reloads, prefills the complete dialog) and resets when the task is
|
||||||
* completed or skipped. Only rendered when the Checklists feature is
|
* completed or skipped. Only rendered when the Checklists feature is
|
||||||
* enabled and steps are set. */
|
* enabled and steps are set. */
|
||||||
|
/** #139: the cycle strip — every step of the phase sequence as a chip, the
|
||||||
|
* step currently due highlighted, each phase's last completion underneath.
|
||||||
|
* Operators can re-point the cursor (mis-click repair, mid-cycle adoption). */
|
||||||
|
function renderPhasesCard(task: MaintenanceTask, ctx: TaskDetailContext) {
|
||||||
|
if (!hasPhases(task)) return nothing;
|
||||||
|
const L = ctx.lang;
|
||||||
|
const seq = task.phase_sequence!;
|
||||||
|
const cursor = clampPhaseCursor(task.phase_cursor, seq.length);
|
||||||
|
// Last completion per phase id — history is append-ordered, so walk it
|
||||||
|
// backwards and keep the first hit.
|
||||||
|
const lastByPhase = new Map<string, string>();
|
||||||
|
for (let i = task.history.length - 1; i >= 0; i--) {
|
||||||
|
const h = task.history[i];
|
||||||
|
if (h.phase_id && h.type === "completed" && !lastByPhase.has(h.phase_id)) {
|
||||||
|
lastByPhase.set(h.phase_id, h.timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<div class="phases-card">
|
||||||
|
<div class="phases-card-header">
|
||||||
|
<ha-icon icon="mdi:rotate-right"></ha-icon>
|
||||||
|
<span>${t("phase_sequence_label", L)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="phases-strip">
|
||||||
|
${seq.map((id, i) => {
|
||||||
|
const name = task.phases?.[id]?.name || id;
|
||||||
|
const last = lastByPhase.get(id);
|
||||||
|
return html`
|
||||||
|
<div class="phase-step ${i === cursor ? "current" : ""}"
|
||||||
|
title=${i === cursor ? t("phase_current", L) : (ctx.isOperator ? t("phase_set", L) : "")}
|
||||||
|
@click=${() => { if (ctx.isOperator && i !== cursor) ctx.setPhaseCursor(i); }}>
|
||||||
|
<span class="phase-step-name">${i + 1}. ${name}</span>
|
||||||
|
${last ? html`<span class="phase-step-last">${formatDate(last, L)}</span>` : nothing}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
function renderChecklistCard(task: MaintenanceTask, ctx: TaskDetailContext) {
|
function renderChecklistCard(task: MaintenanceTask, ctx: TaskDetailContext) {
|
||||||
if (!ctx.features.checklists) return nothing;
|
if (!ctx.features.checklists) return nothing;
|
||||||
const items = task.checklist || [];
|
// #139: a phased task shows the checklist of the phase currently due.
|
||||||
|
const items = effectivePhase(task)?.checklist ?? (task.checklist || []);
|
||||||
if (items.length === 0) return nothing;
|
if (items.length === 0) return nothing;
|
||||||
const L = ctx.lang;
|
const L = ctx.lang;
|
||||||
const progress = task.checklist_progress || {};
|
const progress = task.checklist_progress || {};
|
||||||
@@ -439,6 +484,7 @@ export function renderOverviewTab(task: MaintenanceTask, ctx: TaskDetailContext)
|
|||||||
</div>
|
</div>
|
||||||
`, ctx)
|
`, ctx)
|
||||||
: nothing}
|
: nothing}
|
||||||
|
${renderPhasesCard(task, ctx)}
|
||||||
${renderChecklistCard(task, ctx)}
|
${renderChecklistCard(task, ctx)}
|
||||||
${renderRecentActivities(task, ctx)}
|
${renderRecentActivities(task, ctx)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,11 +8,27 @@ const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|||||||
const DETAIL_DAYS = 30;
|
const DETAIL_DAYS = 30;
|
||||||
const MINI_DAYS = 14;
|
const MINI_DAYS = 14;
|
||||||
|
|
||||||
|
/** history/history_during_period rows — compressed keys over WS ({s, lu});
|
||||||
|
* the uncompressed REST shape is tolerated defensively. */
|
||||||
|
interface HAHistoryRow {
|
||||||
|
s?: string;
|
||||||
|
lu?: number;
|
||||||
|
state?: string;
|
||||||
|
last_updated?: string | number;
|
||||||
|
last_changed?: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
export class StatisticsService {
|
export class StatisticsService {
|
||||||
private _hass: HomeAssistant;
|
private _hass: HomeAssistant;
|
||||||
private _cache = new Map<string, EntityStatisticsCache>();
|
private _cache = new Map<string, EntityStatisticsCache>();
|
||||||
private _pending = new Map<string, Promise<StatisticsPoint[]>>();
|
private _pending = new Map<string, Promise<StatisticsPoint[]>>();
|
||||||
|
|
||||||
|
/** #141: entities whose DETAIL series came from recorder state history
|
||||||
|
* instead of long-term statistics (binary sensors and other entities
|
||||||
|
* without a state_class never have statistics) — the chart footnote
|
||||||
|
* tells the two apart. */
|
||||||
|
public readonly historyFallbackIds = new Set<string>();
|
||||||
|
|
||||||
constructor(hass: HomeAssistant) {
|
constructor(hass: HomeAssistant) {
|
||||||
this._hass = hass;
|
this._hass = hass;
|
||||||
}
|
}
|
||||||
@@ -141,7 +157,23 @@ export class StatisticsService {
|
|||||||
})) as Record<string, HAStatisticsRow[]>;
|
})) as Record<string, HAStatisticsRow[]>;
|
||||||
|
|
||||||
const rows = result[entityId] || [];
|
const rows = result[entityId] || [];
|
||||||
const points = this._normalizeRows(rows, isCounter);
|
let points = this._normalizeRows(rows, isCounter);
|
||||||
|
|
||||||
|
// #141: entities without long-term statistics (binary sensors, sensors
|
||||||
|
// without a state_class) always come back empty here — fall back to
|
||||||
|
// recorder STATE HISTORY (typically ~10 days) so a problem sensor's
|
||||||
|
// on/off timeline draws as a 0/1 step line instead of a flat value.
|
||||||
|
if (points.length < 2) {
|
||||||
|
const historyPoints = await this._fetchHistoryFallback(entityId, startTime);
|
||||||
|
if (historyPoints.length >= 2) {
|
||||||
|
points = historyPoints;
|
||||||
|
this.historyFallbackIds.add(entityId);
|
||||||
|
} else {
|
||||||
|
this.historyFallbackIds.delete(entityId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.historyFallbackIds.delete(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
this._cache.set(cacheKey, {
|
this._cache.set(cacheKey, {
|
||||||
entityId,
|
entityId,
|
||||||
@@ -157,6 +189,60 @@ export class StatisticsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Recorder state history as chart points. Binary states map to 0/1, and
|
||||||
|
* every value CHANGE gets a doubled point ((t, old), (t, new)) so the
|
||||||
|
* line renderer draws a step instead of a slope between samples. */
|
||||||
|
private async _fetchHistoryFallback(entityId: string, startTime: string): Promise<StatisticsPoint[]> {
|
||||||
|
try {
|
||||||
|
const result = (await this._hass.connection.sendMessagePromise({
|
||||||
|
type: "history/history_during_period",
|
||||||
|
start_time: startTime,
|
||||||
|
end_time: new Date().toISOString(),
|
||||||
|
entity_ids: [entityId],
|
||||||
|
minimal_response: true,
|
||||||
|
no_attributes: true,
|
||||||
|
})) as Record<string, HAHistoryRow[]>;
|
||||||
|
|
||||||
|
let rows = result?.[entityId] || [];
|
||||||
|
// A chatty numeric entity can return thousands of rows — stride-sample
|
||||||
|
// BEFORE the step expansion (the expansion preserves the edges that
|
||||||
|
// survive sampling).
|
||||||
|
if (rows.length > 1000) {
|
||||||
|
const stride = Math.ceil(rows.length / 500);
|
||||||
|
rows = rows.filter((_, i) => i % stride === 0 || i === rows.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const points: StatisticsPoint[] = [];
|
||||||
|
let prev: number | null = null;
|
||||||
|
for (const row of rows) {
|
||||||
|
const state = row.s ?? row.state;
|
||||||
|
if (state == null || state === "unknown" || state === "unavailable") continue;
|
||||||
|
let val: number;
|
||||||
|
if (state === "on" || state === "open" || state === "true") val = 1;
|
||||||
|
else if (state === "off" || state === "closed" || state === "false") val = 0;
|
||||||
|
else {
|
||||||
|
val = parseFloat(state);
|
||||||
|
if (!Number.isFinite(val)) continue;
|
||||||
|
}
|
||||||
|
const raw = row.lu ?? row.last_updated ?? row.last_changed;
|
||||||
|
const ts = typeof raw === "number" ? raw * 1000 : raw != null ? Date.parse(raw) : NaN;
|
||||||
|
if (!Number.isFinite(ts)) continue;
|
||||||
|
if (prev != null && prev !== val) points.push({ ts, val: prev });
|
||||||
|
points.push({ ts, val });
|
||||||
|
prev = val;
|
||||||
|
}
|
||||||
|
points.sort((a, b) => a.ts - b.ts);
|
||||||
|
// Extend the last known state to "now" so the line reaches the right
|
||||||
|
// edge (the caller must NOT append trigger_current_value here — for a
|
||||||
|
// state_change trigger that is the change COUNTER, not the state).
|
||||||
|
if (points.length && prev != null) points.push({ ts: Date.now(), val: prev });
|
||||||
|
return points;
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`[maintenance-supporter] History fallback failed for ${entityId}:`, err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async _fetchBatch(
|
private async _fetchBatch(
|
||||||
entityIds: string[],
|
entityIds: string[],
|
||||||
period: "hour" | "day",
|
period: "hour" | "day",
|
||||||
|
|||||||
@@ -604,6 +604,21 @@ export const sharedStyles = css`
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
/* #139 — names the cycle phase a completion recorded. Shares the auto-badge
|
||||||
|
look; carries the left-pinning margin itself unless the auto badge (which
|
||||||
|
already has it) follows. */
|
||||||
|
.history-phase-badge {
|
||||||
|
margin-right: auto;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 1px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--secondary-background-color);
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.history-phase-badge:has(+ .history-auto-badge) {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
.history-edit-btn {
|
.history-edit-btn {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
|
|||||||
@@ -103,6 +103,9 @@ export interface HistoryEntry {
|
|||||||
/** v2.37: completion recorded by the system itself (trigger recovered),
|
/** v2.37: completion recorded by the system itself (trigger recovered),
|
||||||
* not performed by a user in the UI. */
|
* not performed by a user in the UI. */
|
||||||
auto?: boolean;
|
auto?: boolean;
|
||||||
|
/** #139: which cycle phase this LATEST completion recorded (backfills
|
||||||
|
* carry none — they never advanced the cursor). */
|
||||||
|
phase_id?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdaptiveConfig {
|
export interface AdaptiveConfig {
|
||||||
@@ -269,6 +272,22 @@ export interface MaintenanceTask {
|
|||||||
consumes_parts?: TaskPartLink[] | null;
|
consumes_parts?: TaskPartLink[] | null;
|
||||||
/** Present on an auto-created "buy" reminder: the owning part. */
|
/** Present on an auto-created "buy" reminder: the owning part. */
|
||||||
part_ref?: { part_id: string } | null;
|
part_ref?: { part_id: string } | null;
|
||||||
|
/** Task phases (#139): cyclic content rotation on one shared cadence. */
|
||||||
|
phases?: Record<string, TaskPhaseDef> | null;
|
||||||
|
phase_sequence?: string[] | null;
|
||||||
|
phase_cursor?: number;
|
||||||
|
/** Backend-resolved phase currently due (null for phase-less tasks). */
|
||||||
|
current_phase?: { id: string; name: string; index: number; count: number } | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** One phase definition (#139). A set field OVERRIDES the task-level field
|
||||||
|
* for completions performed while this phase is due; unset falls through. */
|
||||||
|
export interface TaskPhaseDef {
|
||||||
|
name: string;
|
||||||
|
notes?: string;
|
||||||
|
checklist?: string[];
|
||||||
|
consumes_parts?: TaskPartLink[];
|
||||||
|
required_completion_fields?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** One entry of a task's `consumes_parts`.
|
/** One entry of a task's `consumes_parts`.
|
||||||
|
|||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "záznamů",
|
"service_record_entries": "záznamů",
|
||||||
"completed_by": "Dokončil",
|
"completed_by": "Dokončil",
|
||||||
"date_from": "Od",
|
"date_from": "Od",
|
||||||
"date_to": "Do"
|
"date_to": "Do",
|
||||||
|
"phases_section": "Fáze cyklu (volitelné)",
|
||||||
|
"phases_hint": "Různé práce ve stejném rytmu — každé dokončení posune na další krok (např. malý, malý, velký servis).",
|
||||||
|
"phase_add": "Přidat fázi",
|
||||||
|
"phase_name": "Název fáze",
|
||||||
|
"phase_sequence_label": "Pořadí cyklu",
|
||||||
|
"phase_sequence_add_step": "Přidat krok",
|
||||||
|
"phase_current": "Aktuální fáze",
|
||||||
|
"phase_set": "Nastavit jako aktuální",
|
||||||
|
"chart_history_fallback": "Pro tuto entitu nejsou dlouhodobé statistiky — zobrazuje se historie stavů recorderu (obvykle posledních ~10 dní)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "poster",
|
"service_record_entries": "poster",
|
||||||
"completed_by": "Udført af",
|
"completed_by": "Udført af",
|
||||||
"date_from": "Fra",
|
"date_from": "Fra",
|
||||||
"date_to": "Til"
|
"date_to": "Til",
|
||||||
|
"phases_section": "Cyklusfaser (valgfrit)",
|
||||||
|
"phases_hint": "Forskelligt arbejde i samme rytme — hver udførelse går videre til næste trin (f.eks. lille, lille, stor service).",
|
||||||
|
"phase_add": "Tilføj fase",
|
||||||
|
"phase_name": "Fasenavn",
|
||||||
|
"phase_sequence_label": "Cyklusrækkefølge",
|
||||||
|
"phase_sequence_add_step": "Tilføj trin",
|
||||||
|
"phase_current": "Aktuel fase",
|
||||||
|
"phase_set": "Sæt som aktuel",
|
||||||
|
"chart_history_fallback": "Ingen langtidsstatistik for denne enhed — recorderens tilstandshistorik vises (typisk de seneste ~10 dage)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "Einträge",
|
"service_record_entries": "Einträge",
|
||||||
"completed_by": "Erledigt von",
|
"completed_by": "Erledigt von",
|
||||||
"date_from": "Von",
|
"date_from": "Von",
|
||||||
"date_to": "Bis"
|
"date_to": "Bis",
|
||||||
|
"phases_section": "Zyklus-Phasen (optional)",
|
||||||
|
"phases_hint": "Unterschiedliche Arbeiten im gleichen Takt — jede Erledigung schaltet zum nächsten Schritt (z. B. klein, klein, groß).",
|
||||||
|
"phase_add": "Phase hinzufügen",
|
||||||
|
"phase_name": "Phasenname",
|
||||||
|
"phase_sequence_label": "Zyklus-Reihenfolge",
|
||||||
|
"phase_sequence_add_step": "Schritt anhängen",
|
||||||
|
"phase_current": "Aktuelle Phase",
|
||||||
|
"phase_set": "Als aktuell setzen",
|
||||||
|
"chart_history_fallback": "Keine Langzeitstatistik für diese Entität — angezeigt wird der Recorder-Zustandsverlauf (üblicherweise die letzten ~10 Tage)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "entries",
|
"service_record_entries": "entries",
|
||||||
"completed_by": "Completed by",
|
"completed_by": "Completed by",
|
||||||
"date_from": "From",
|
"date_from": "From",
|
||||||
"date_to": "To"
|
"date_to": "To",
|
||||||
|
"phases_section": "Cycle phases (optional)",
|
||||||
|
"phases_hint": "Different work on one shared schedule — each completion moves to the next step (e.g. small service, small service, big service).",
|
||||||
|
"phase_add": "Add phase",
|
||||||
|
"phase_name": "Phase name",
|
||||||
|
"phase_sequence_label": "Cycle order",
|
||||||
|
"phase_sequence_add_step": "Add step",
|
||||||
|
"phase_current": "Current phase",
|
||||||
|
"phase_set": "Set as current",
|
||||||
|
"chart_history_fallback": "No long-term statistics for this entity — showing recorder state history (typically the last ~10 days)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "entradas",
|
"service_record_entries": "entradas",
|
||||||
"completed_by": "Realizado por",
|
"completed_by": "Realizado por",
|
||||||
"date_from": "Desde",
|
"date_from": "Desde",
|
||||||
"date_to": "Hasta"
|
"date_to": "Hasta",
|
||||||
|
"phases_section": "Fases del ciclo (opcional)",
|
||||||
|
"phases_hint": "Trabajos distintos con un mismo ritmo: cada finalización pasa al siguiente paso (p. ej. pequeño, pequeño, grande).",
|
||||||
|
"phase_add": "Añadir fase",
|
||||||
|
"phase_name": "Nombre de la fase",
|
||||||
|
"phase_sequence_label": "Orden del ciclo",
|
||||||
|
"phase_sequence_add_step": "Añadir paso",
|
||||||
|
"phase_current": "Fase actual",
|
||||||
|
"phase_set": "Establecer como actual",
|
||||||
|
"chart_history_fallback": "Sin estadísticas a largo plazo para esta entidad: se muestra el historial de estados del recorder (normalmente los últimos ~10 días)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "merkintää",
|
"service_record_entries": "merkintää",
|
||||||
"completed_by": "Suorittanut",
|
"completed_by": "Suorittanut",
|
||||||
"date_from": "Alkaen",
|
"date_from": "Alkaen",
|
||||||
"date_to": "Asti"
|
"date_to": "Asti",
|
||||||
|
"phases_section": "Syklin vaiheet (valinnainen)",
|
||||||
|
"phases_hint": "Eri työt samassa tahdissa — jokainen suoritus siirtyy seuraavaan vaiheeseen (esim. pieni, pieni, iso huolto).",
|
||||||
|
"phase_add": "Lisää vaihe",
|
||||||
|
"phase_name": "Vaiheen nimi",
|
||||||
|
"phase_sequence_label": "Syklin järjestys",
|
||||||
|
"phase_sequence_add_step": "Lisää askel",
|
||||||
|
"phase_current": "Nykyinen vaihe",
|
||||||
|
"phase_set": "Aseta nykyiseksi",
|
||||||
|
"chart_history_fallback": "Tälle entiteetille ei ole pitkän aikavälin tilastoja — näytetään recorderin tilahistoria (yleensä viimeiset ~10 päivää)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "entrées",
|
"service_record_entries": "entrées",
|
||||||
"completed_by": "Réalisé par",
|
"completed_by": "Réalisé par",
|
||||||
"date_from": "Du",
|
"date_from": "Du",
|
||||||
"date_to": "Au"
|
"date_to": "Au",
|
||||||
|
"phases_section": "Phases du cycle (optionnel)",
|
||||||
|
"phases_hint": "Des travaux différents sur un même rythme — chaque réalisation passe à l'étape suivante (p. ex. petit, petit, grand entretien).",
|
||||||
|
"phase_add": "Ajouter une phase",
|
||||||
|
"phase_name": "Nom de la phase",
|
||||||
|
"phase_sequence_label": "Ordre du cycle",
|
||||||
|
"phase_sequence_add_step": "Ajouter une étape",
|
||||||
|
"phase_current": "Phase actuelle",
|
||||||
|
"phase_set": "Définir comme actuelle",
|
||||||
|
"chart_history_fallback": "Pas de statistiques à long terme pour cette entité — affichage de l'historique d'états du recorder (généralement les ~10 derniers jours)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "प्रविष्टियाँ",
|
"service_record_entries": "प्रविष्टियाँ",
|
||||||
"completed_by": "द्वारा पूर्ण",
|
"completed_by": "द्वारा पूर्ण",
|
||||||
"date_from": "से",
|
"date_from": "से",
|
||||||
"date_to": "तक"
|
"date_to": "तक",
|
||||||
|
"phases_section": "चक्र चरण (वैकल्पिक)",
|
||||||
|
"phases_hint": "एक ही लय में अलग-अलग कार्य — प्रत्येक पूर्णता अगले चरण पर ले जाती है (जैसे छोटा, छोटा, बड़ा)।",
|
||||||
|
"phase_add": "चरण जोड़ें",
|
||||||
|
"phase_name": "चरण का नाम",
|
||||||
|
"phase_sequence_label": "चक्र क्रम",
|
||||||
|
"phase_sequence_add_step": "कदम जोड़ें",
|
||||||
|
"phase_current": "वर्तमान चरण",
|
||||||
|
"phase_set": "वर्तमान बनाएँ",
|
||||||
|
"chart_history_fallback": "इस एंटिटी के लिए दीर्घकालिक आँकड़े नहीं हैं — recorder की स्थिति का इतिहास दिखाया जा रहा है (आमतौर पर पिछले ~10 दिन)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "bejegyzés",
|
"service_record_entries": "bejegyzés",
|
||||||
"completed_by": "Elvégezte",
|
"completed_by": "Elvégezte",
|
||||||
"date_from": "Ettől",
|
"date_from": "Ettől",
|
||||||
"date_to": "Eddig"
|
"date_to": "Eddig",
|
||||||
|
"phases_section": "Ciklusfázisok (opcionális)",
|
||||||
|
"phases_hint": "Különböző munkák azonos ütemben — minden elvégzés a következő lépésre vált (pl. kis, kis, nagy szerviz).",
|
||||||
|
"phase_add": "Fázis hozzáadása",
|
||||||
|
"phase_name": "Fázis neve",
|
||||||
|
"phase_sequence_label": "Ciklus sorrendje",
|
||||||
|
"phase_sequence_add_step": "Lépés hozzáadása",
|
||||||
|
"phase_current": "Aktuális fázis",
|
||||||
|
"phase_set": "Beállítás aktuálisként",
|
||||||
|
"chart_history_fallback": "Ehhez az entitáshoz nincs hosszú távú statisztika — a recorder állapotelőzményei jelennek meg (jellemzően az utolsó ~10 nap)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "voci",
|
"service_record_entries": "voci",
|
||||||
"completed_by": "Completato da",
|
"completed_by": "Completato da",
|
||||||
"date_from": "Dal",
|
"date_from": "Dal",
|
||||||
"date_to": "Al"
|
"date_to": "Al",
|
||||||
|
"phases_section": "Fasi del ciclo (opzionale)",
|
||||||
|
"phases_hint": "Lavori diversi sullo stesso ritmo — ogni completamento passa al passo successivo (es. piccolo, piccolo, grande).",
|
||||||
|
"phase_add": "Aggiungi fase",
|
||||||
|
"phase_name": "Nome della fase",
|
||||||
|
"phase_sequence_label": "Ordine del ciclo",
|
||||||
|
"phase_sequence_add_step": "Aggiungi passo",
|
||||||
|
"phase_current": "Fase attuale",
|
||||||
|
"phase_set": "Imposta come attuale",
|
||||||
|
"chart_history_fallback": "Nessuna statistica a lungo termine per questa entità — viene mostrata la cronologia degli stati del recorder (di solito gli ultimi ~10 giorni)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "件",
|
"service_record_entries": "件",
|
||||||
"completed_by": "実施者",
|
"completed_by": "実施者",
|
||||||
"date_from": "開始",
|
"date_from": "開始",
|
||||||
"date_to": "終了"
|
"date_to": "終了",
|
||||||
|
"phases_section": "サイクルフェーズ(任意)",
|
||||||
|
"phases_hint": "同じ周期で異なる作業を行います。完了するたびに次のステップへ進みます(例:小・小・大整備)。",
|
||||||
|
"phase_add": "フェーズを追加",
|
||||||
|
"phase_name": "フェーズ名",
|
||||||
|
"phase_sequence_label": "サイクルの順序",
|
||||||
|
"phase_sequence_add_step": "ステップを追加",
|
||||||
|
"phase_current": "現在のフェーズ",
|
||||||
|
"phase_set": "現在に設定",
|
||||||
|
"chart_history_fallback": "このエンティティには長期統計がありません — レコーダーの状態履歴を表示しています(通常は直近約10日分)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "건",
|
"service_record_entries": "건",
|
||||||
"completed_by": "수행자",
|
"completed_by": "수행자",
|
||||||
"date_from": "시작",
|
"date_from": "시작",
|
||||||
"date_to": "종료"
|
"date_to": "종료",
|
||||||
|
"phases_section": "주기 단계(선택)",
|
||||||
|
"phases_hint": "같은 주기에 다른 작업을 수행합니다. 완료할 때마다 다음 단계로 넘어갑니다(예: 소·소·대 정비).",
|
||||||
|
"phase_add": "단계 추가",
|
||||||
|
"phase_name": "단계 이름",
|
||||||
|
"phase_sequence_label": "주기 순서",
|
||||||
|
"phase_sequence_add_step": "스텝 추가",
|
||||||
|
"phase_current": "현재 단계",
|
||||||
|
"phase_set": "현재로 설정",
|
||||||
|
"chart_history_fallback": "이 엔티티에는 장기 통계가 없습니다 — 레코더 상태 기록을 표시합니다(보통 최근 약 10일)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "oppføringer",
|
"service_record_entries": "oppføringer",
|
||||||
"completed_by": "Utført av",
|
"completed_by": "Utført av",
|
||||||
"date_from": "Fra",
|
"date_from": "Fra",
|
||||||
"date_to": "Til"
|
"date_to": "Til",
|
||||||
|
"phases_section": "Syklusfaser (valgfritt)",
|
||||||
|
"phases_hint": "Ulikt arbeid i samme rytme — hver fullføring går videre til neste trinn (f.eks. liten, liten, stor service).",
|
||||||
|
"phase_add": "Legg til fase",
|
||||||
|
"phase_name": "Fasenavn",
|
||||||
|
"phase_sequence_label": "Syklusrekkefølge",
|
||||||
|
"phase_sequence_add_step": "Legg til trinn",
|
||||||
|
"phase_current": "Gjeldende fase",
|
||||||
|
"phase_set": "Sett som gjeldende",
|
||||||
|
"chart_history_fallback": "Ingen langtidsstatistikk for denne enheten — recorderens tilstandshistorikk vises (vanligvis de siste ~10 dagene)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "items",
|
"service_record_entries": "items",
|
||||||
"completed_by": "Voltooid door",
|
"completed_by": "Voltooid door",
|
||||||
"date_from": "Van",
|
"date_from": "Van",
|
||||||
"date_to": "Tot"
|
"date_to": "Tot",
|
||||||
|
"phases_section": "Cyclusfasen (optioneel)",
|
||||||
|
"phases_hint": "Verschillend werk op één ritme — elke afronding schuift naar de volgende stap (bijv. klein, klein, groot).",
|
||||||
|
"phase_add": "Fase toevoegen",
|
||||||
|
"phase_name": "Fasenaam",
|
||||||
|
"phase_sequence_label": "Cyclusvolgorde",
|
||||||
|
"phase_sequence_add_step": "Stap toevoegen",
|
||||||
|
"phase_current": "Huidige fase",
|
||||||
|
"phase_set": "Instellen als huidige",
|
||||||
|
"chart_history_fallback": "Geen langetermijnstatistieken voor deze entiteit — de recorder-statusgeschiedenis wordt getoond (meestal de laatste ~10 dagen)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "wpisów",
|
"service_record_entries": "wpisów",
|
||||||
"completed_by": "Wykonane przez",
|
"completed_by": "Wykonane przez",
|
||||||
"date_from": "Od",
|
"date_from": "Od",
|
||||||
"date_to": "Do"
|
"date_to": "Do",
|
||||||
|
"phases_section": "Fazy cyklu (opcjonalnie)",
|
||||||
|
"phases_hint": "Różne prace w jednym rytmie — każde ukończenie przechodzi do następnego kroku (np. mały, mały, duży).",
|
||||||
|
"phase_add": "Dodaj fazę",
|
||||||
|
"phase_name": "Nazwa fazy",
|
||||||
|
"phase_sequence_label": "Kolejność cyklu",
|
||||||
|
"phase_sequence_add_step": "Dodaj krok",
|
||||||
|
"phase_current": "Bieżąca faza",
|
||||||
|
"phase_set": "Ustaw jako bieżącą",
|
||||||
|
"chart_history_fallback": "Brak statystyk długoterminowych dla tej encji — wyświetlana jest historia stanów recordera (zwykle ostatnie ~10 dni)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "registros",
|
"service_record_entries": "registros",
|
||||||
"completed_by": "Concluído por",
|
"completed_by": "Concluído por",
|
||||||
"date_from": "De",
|
"date_from": "De",
|
||||||
"date_to": "Até"
|
"date_to": "Até",
|
||||||
|
"phases_section": "Fases do ciclo (opcional)",
|
||||||
|
"phases_hint": "Trabalhos diferentes no mesmo ritmo — cada conclusão avança para o próximo passo (ex.: pequeno, pequeno, grande).",
|
||||||
|
"phase_add": "Adicionar fase",
|
||||||
|
"phase_name": "Nome da fase",
|
||||||
|
"phase_sequence_label": "Ordem do ciclo",
|
||||||
|
"phase_sequence_add_step": "Adicionar passo",
|
||||||
|
"phase_current": "Fase atual",
|
||||||
|
"phase_set": "Definir como atual",
|
||||||
|
"chart_history_fallback": "Sem estatísticas de longo prazo para esta entidade — mostrando o histórico de estados do recorder (normalmente os últimos ~10 dias)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "registos",
|
"service_record_entries": "registos",
|
||||||
"completed_by": "Concluído por",
|
"completed_by": "Concluído por",
|
||||||
"date_from": "De",
|
"date_from": "De",
|
||||||
"date_to": "Até"
|
"date_to": "Até",
|
||||||
|
"phases_section": "Fases do ciclo (opcional)",
|
||||||
|
"phases_hint": "Trabalhos diferentes no mesmo ritmo — cada conclusão avança para o passo seguinte (ex.: pequeno, pequeno, grande).",
|
||||||
|
"phase_add": "Adicionar fase",
|
||||||
|
"phase_name": "Nome da fase",
|
||||||
|
"phase_sequence_label": "Ordem do ciclo",
|
||||||
|
"phase_sequence_add_step": "Adicionar passo",
|
||||||
|
"phase_current": "Fase atual",
|
||||||
|
"phase_set": "Definir como atual",
|
||||||
|
"chart_history_fallback": "Sem estatísticas de longo prazo para esta entidade — a mostrar o histórico de estados do recorder (normalmente os últimos ~10 dias)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "записей",
|
"service_record_entries": "записей",
|
||||||
"completed_by": "Выполнил",
|
"completed_by": "Выполнил",
|
||||||
"date_from": "С",
|
"date_from": "С",
|
||||||
"date_to": "По"
|
"date_to": "По",
|
||||||
|
"phases_section": "Фазы цикла (необязательно)",
|
||||||
|
"phases_hint": "Разные работы в одном ритме — каждое выполнение переходит к следующему шагу (напр., малое, малое, большое ТО).",
|
||||||
|
"phase_add": "Добавить фазу",
|
||||||
|
"phase_name": "Название фазы",
|
||||||
|
"phase_sequence_label": "Порядок цикла",
|
||||||
|
"phase_sequence_add_step": "Добавить шаг",
|
||||||
|
"phase_current": "Текущая фаза",
|
||||||
|
"phase_set": "Сделать текущей",
|
||||||
|
"chart_history_fallback": "Для этой сущности нет долгосрочной статистики — показана история состояний recorder (обычно последние ~10 дней)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "poster",
|
"service_record_entries": "poster",
|
||||||
"completed_by": "Utförd av",
|
"completed_by": "Utförd av",
|
||||||
"date_from": "Från",
|
"date_from": "Från",
|
||||||
"date_to": "Till"
|
"date_to": "Till",
|
||||||
|
"phases_section": "Cykelfaser (valfritt)",
|
||||||
|
"phases_hint": "Olika arbeten i samma rytm — varje slutförande går vidare till nästa steg (t.ex. liten, liten, stor service).",
|
||||||
|
"phase_add": "Lägg till fas",
|
||||||
|
"phase_name": "Fasnamn",
|
||||||
|
"phase_sequence_label": "Cykelordning",
|
||||||
|
"phase_sequence_add_step": "Lägg till steg",
|
||||||
|
"phase_current": "Aktuell fas",
|
||||||
|
"phase_set": "Ange som aktuell",
|
||||||
|
"chart_history_fallback": "Ingen långtidsstatistik för denna entitet — recorderns tillståndshistorik visas (vanligtvis de senaste ~10 dagarna)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "kayıt",
|
"service_record_entries": "kayıt",
|
||||||
"completed_by": "Tamamlayan",
|
"completed_by": "Tamamlayan",
|
||||||
"date_from": "Başlangıç",
|
"date_from": "Başlangıç",
|
||||||
"date_to": "Bitiş"
|
"date_to": "Bitiş",
|
||||||
|
"phases_section": "Döngü aşamaları (isteğe bağlı)",
|
||||||
|
"phases_hint": "Aynı ritimde farklı işler — her tamamlama bir sonraki adıma geçer (örn. küçük, küçük, büyük bakım).",
|
||||||
|
"phase_add": "Aşama ekle",
|
||||||
|
"phase_name": "Aşama adı",
|
||||||
|
"phase_sequence_label": "Döngü sırası",
|
||||||
|
"phase_sequence_add_step": "Adım ekle",
|
||||||
|
"phase_current": "Geçerli aşama",
|
||||||
|
"phase_set": "Geçerli olarak ayarla",
|
||||||
|
"chart_history_fallback": "Bu varlık için uzun dönem istatistik yok — recorder durum geçmişi gösteriliyor (genellikle son ~10 gün)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "записів",
|
"service_record_entries": "записів",
|
||||||
"completed_by": "Виконав",
|
"completed_by": "Виконав",
|
||||||
"date_from": "Від",
|
"date_from": "Від",
|
||||||
"date_to": "До"
|
"date_to": "До",
|
||||||
|
"phases_section": "Фази циклу (необов'язково)",
|
||||||
|
"phases_hint": "Різні роботи в одному ритмі — кожне виконання переходить до наступного кроку (напр., мале, мале, велике ТО).",
|
||||||
|
"phase_add": "Додати фазу",
|
||||||
|
"phase_name": "Назва фази",
|
||||||
|
"phase_sequence_label": "Порядок циклу",
|
||||||
|
"phase_sequence_add_step": "Додати крок",
|
||||||
|
"phase_current": "Поточна фаза",
|
||||||
|
"phase_set": "Зробити поточною",
|
||||||
|
"chart_history_fallback": "Для цієї сутності немає довгострокової статистики — показано історію станів recorder (зазвичай останні ~10 днів)"
|
||||||
}
|
}
|
||||||
@@ -889,5 +889,14 @@
|
|||||||
"service_record_entries": "条记录",
|
"service_record_entries": "条记录",
|
||||||
"completed_by": "完成人",
|
"completed_by": "完成人",
|
||||||
"date_from": "从",
|
"date_from": "从",
|
||||||
"date_to": "至"
|
"date_to": "至",
|
||||||
|
"phases_section": "循环阶段(可选)",
|
||||||
|
"phases_hint": "同一节奏下的不同工作——每次完成后进入下一步(例如:小保养、小保养、大保养)。",
|
||||||
|
"phase_add": "添加阶段",
|
||||||
|
"phase_name": "阶段名称",
|
||||||
|
"phase_sequence_label": "循环顺序",
|
||||||
|
"phase_sequence_add_step": "添加步骤",
|
||||||
|
"phase_current": "当前阶段",
|
||||||
|
"phase_set": "设为当前",
|
||||||
|
"chart_history_fallback": "该实体没有长期统计数据——显示 recorder 状态历史(通常为最近约 10 天)"
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,9 +1,9 @@
|
|||||||
/*! maintenance_supporter frontend 2.64.0 */
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
var Tt=Object.defineProperty;var Xi=Object.getOwnPropertyDescriptor;var w=(a,s,e)=>()=>{if(e)throw e[0];try{return a&&(s=a(a=0)),s}catch(t){throw e=[t],t}};var es=(a,s)=>{for(var e in s)Tt(a,e,{get:s[e],enumerable:!0})};var c=(a,s,e,t)=>{for(var i=t>1?void 0:t?Xi(s,e):s,n=a.length-1,l;n>=0;n--)(l=a[n])&&(i=(t?l(s,e,i):l(i))||i);return t&&i&&Tt(s,e,i),i};var Pe,je,Ye,Ct,me,It,A,Lt,Qe,Je=w(()=>{Pe=globalThis,je=Pe.ShadowRoot&&(Pe.ShadyCSS===void 0||Pe.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Ye=Symbol(),Ct=new WeakMap,me=class{constructor(s,e,t){if(this._$cssResult$=!0,t!==Ye)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=s,this.t=e}get styleSheet(){let s=this.o,e=this.t;if(je&&s===void 0){let t=e!==void 0&&e.length===1;t&&(s=Ct.get(e)),s===void 0&&((this.o=s=new CSSStyleSheet).replaceSync(this.cssText),t&&Ct.set(e,s))}return s}toString(){return this.cssText}},It=a=>new me(typeof a=="string"?a:a+"",void 0,Ye),A=(a,...s)=>{let e=a.length===1?a[0]:s.reduce((t,i,n)=>t+(l=>{if(l._$cssResult$===!0)return l.cssText;if(typeof l=="number")return l;throw Error("Value passed to 'css' function must be a 'css' function result: "+l+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+a[n+1],a[0]);return new me(e,a,Ye)},Lt=(a,s)=>{if(je)a.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of s){let t=document.createElement("style"),i=Pe.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,a.appendChild(t)}},Qe=je?a=>a:a=>a instanceof CSSStyleSheet?(s=>{let e="";for(let t of s.cssRules)e+=t.cssText;return It(e)})(a):a});var ts,is,ss,rs,as,ns,Re,Pt,os,ls,fe,ve,Ne,jt,Y,be=w(()=>{Je();Je();({is:ts,defineProperty:is,getOwnPropertyDescriptor:ss,getOwnPropertyNames:rs,getOwnPropertySymbols:as,getPrototypeOf:ns}=Object),Re=globalThis,Pt=Re.trustedTypes,os=Pt?Pt.emptyScript:"",ls=Re.reactiveElementPolyfillSupport,fe=(a,s)=>a,ve={toAttribute(a,s){switch(s){case Boolean:a=a?os:null;break;case Object:case Array:a=a==null?a:JSON.stringify(a)}return a},fromAttribute(a,s){let e=a;switch(s){case Boolean:e=a!==null;break;case Number:e=a===null?null:Number(a);break;case Object:case Array:try{e=JSON.parse(a)}catch{e=null}}return e}},Ne=(a,s)=>!ts(a,s),jt={attribute:!0,type:String,converter:ve,reflect:!1,useDefault:!1,hasChanged:Ne};Symbol.metadata??=Symbol("metadata"),Re.litPropertyMetadata??=new WeakMap;Y=class extends HTMLElement{static addInitializer(s){this._$Ei(),(this.l??=[]).push(s)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(s,e=jt){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(s)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(s,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(s,t,e);i!==void 0&&is(this.prototype,s,i)}}static getPropertyDescriptor(s,e,t){let{get:i,set:n}=ss(this.prototype,s)??{get(){return this[e]},set(l){this[e]=l}};return{get:i,set(l){let d=i?.call(this);n?.call(this,l),this.requestUpdate(s,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(s){return this.elementProperties.get(s)??jt}static _$Ei(){if(this.hasOwnProperty(fe("elementProperties")))return;let s=ns(this);s.finalize(),s.l!==void 0&&(this.l=[...s.l]),this.elementProperties=new Map(s.elementProperties)}static finalize(){if(this.hasOwnProperty(fe("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(fe("properties"))){let e=this.properties,t=[...rs(e),...as(e)];for(let i of t)this.createProperty(i,e[i])}let s=this[Symbol.metadata];if(s!==null){let e=litPropertyMetadata.get(s);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){let e=[];if(Array.isArray(s)){let t=new Set(s.flat(1/0).reverse());for(let i of t)e.unshift(Qe(i))}else s!==void 0&&e.push(Qe(s));return e}static _$Eu(s,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof s=="string"?s.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(s=>this.enableUpdating=s),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(s=>s(this))}addController(s){(this._$EO??=new Set).add(s),this.renderRoot!==void 0&&this.isConnected&&s.hostConnected?.()}removeController(s){this._$EO?.delete(s)}_$E_(){let s=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(s.set(t,this[t]),delete this[t]);s.size>0&&(this._$Ep=s)}createRenderRoot(){let s=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return Lt(s,this.constructor.elementStyles),s}connectedCallback(){this.renderRoot??=thiLine truncated
|
var Pt=Object.defineProperty;var is=Object.getOwnPropertyDescriptor;var w=(a,s,e)=>()=>{if(e)throw e[0];try{return a&&(s=a(a=0)),s}catch(t){throw e=[t],t}};var ss=(a,s)=>{for(var e in s)Pt(a,e,{get:s[e],enumerable:!0})};var c=(a,s,e,t)=>{for(var i=t>1?void 0:t?is(s,e):s,n=a.length-1,l;n>=0;n--)(l=a[n])&&(i=(t?l(s,e,i):l(i))||i);return t&&i&&Pt(s,e,i),i};var je,Ne,Je,Lt,fe,Rt,A,jt,Ze,Xe=w(()=>{je=globalThis,Ne=je.ShadowRoot&&(je.ShadyCSS===void 0||je.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Je=Symbol(),Lt=new WeakMap,fe=class{constructor(s,e,t){if(this._$cssResult$=!0,t!==Je)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=s,this.t=e}get styleSheet(){let s=this.o,e=this.t;if(Ne&&s===void 0){let t=e!==void 0&&e.length===1;t&&(s=Lt.get(e)),s===void 0&&((this.o=s=new CSSStyleSheet).replaceSync(this.cssText),t&&Lt.set(e,s))}return s}toString(){return this.cssText}},Rt=a=>new fe(typeof a=="string"?a:a+"",void 0,Je),A=(a,...s)=>{let e=a.length===1?a[0]:s.reduce((t,i,n)=>t+(l=>{if(l._$cssResult$===!0)return l.cssText;if(typeof l=="number")return l;throw Error("Value passed to 'css' function must be a 'css' function result: "+l+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+a[n+1],a[0]);return new fe(e,a,Je)},jt=(a,s)=>{if(Ne)a.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of s){let t=document.createElement("style"),i=je.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,a.appendChild(t)}},Ze=Ne?a=>a:a=>a instanceof CSSStyleSheet?(s=>{let e="";for(let t of s.cssRules)e+=t.cssText;return Rt(e)})(a):a});var rs,as,ns,os,ls,cs,qe,Nt,ds,ps,ve,be,He,qt,Y,ye=w(()=>{Xe();Xe();({is:rs,defineProperty:as,getOwnPropertyDescriptor:ns,getOwnPropertyNames:os,getOwnPropertySymbols:ls,getPrototypeOf:cs}=Object),qe=globalThis,Nt=qe.trustedTypes,ds=Nt?Nt.emptyScript:"",ps=qe.reactiveElementPolyfillSupport,ve=(a,s)=>a,be={toAttribute(a,s){switch(s){case Boolean:a=a?ds:null;break;case Object:case Array:a=a==null?a:JSON.stringify(a)}return a},fromAttribute(a,s){let e=a;switch(s){case Boolean:e=a!==null;break;case Number:e=a===null?null:Number(a);break;case Object:case Array:try{e=JSON.parse(a)}catch{e=null}}return e}},He=(a,s)=>!rs(a,s),qt={attribute:!0,type:String,converter:be,reflect:!1,useDefault:!1,hasChanged:He};Symbol.metadata??=Symbol("metadata"),qe.litPropertyMetadata??=new WeakMap;Y=class extends HTMLElement{static addInitializer(s){this._$Ei(),(this.l??=[]).push(s)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(s,e=qt){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(s)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(s,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(s,t,e);i!==void 0&&as(this.prototype,s,i)}}static getPropertyDescriptor(s,e,t){let{get:i,set:n}=ns(this.prototype,s)??{get(){return this[e]},set(l){this[e]=l}};return{get:i,set(l){let d=i?.call(this);n?.call(this,l),this.requestUpdate(s,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(s){return this.elementProperties.get(s)??qt}static _$Ei(){if(this.hasOwnProperty(ve("elementProperties")))return;let s=cs(this);s.finalize(),s.l!==void 0&&(this.l=[...s.l]),this.elementProperties=new Map(s.elementProperties)}static finalize(){if(this.hasOwnProperty(ve("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(ve("properties"))){let e=this.properties,t=[...os(e),...ls(e)];for(let i of t)this.createProperty(i,e[i])}let s=this[Symbol.metadata];if(s!==null){let e=litPropertyMetadata.get(s);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){let e=[];if(Array.isArray(s)){let t=new Set(s.flat(1/0).reverse());for(let i of t)e.unshift(Ze(i))}else s!==void 0&&e.push(Ze(s));return e}static _$Eu(s,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof s=="string"?s.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(s=>this.enableUpdating=s),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(s=>s(this))}addController(s){(this._$EO??=new Set).add(s),this.renderRoot!==void 0&&this.isConnected&&s.hostConnected?.()}removeController(s){this._$EO?.delete(s)}_$E_(){let s=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(s.set(t,this[t]),delete this[t]);s.size>0&&(this._$Ep=s)}createRenderRoot(){let s=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return jt(s,this.constructor.elementStyles),s}connectedCallback(){this.renderRoot??=thiLine truncated
|
||||||
\f\r]`,ye=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ht=/-->/g,qt=/>/g,se=RegExp(`>|${Ze}(?:([^\\s"'>=/]+)(${Ze}*=${Ze}*(?:[^
|
\f\r]`,xe=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Mt=/-->/g,Dt=/>/g,re=RegExp(`>|${et}(?:([^\\s"'>=/]+)(${et}*=${et}*(?:[^
|
||||||
\f\r"'\`<>=]|("|')|))|$)`,"g"),Ot=/'/g,Mt=/"/g,Ut=/^(?:script|style|textarea|title)$/i,nt=a=>(s,...e)=>({_$litType$:a,strings:s,values:e}),o=nt(1),pe=nt(2),gr=nt(3),ne=Symbol.for("lit-noChange"),u=Symbol.for("lit-nothing"),Dt=new WeakMap,re=ae.createTreeWalker(ae,129);ps=(a,s)=>{let e=a.length-1,t=[],i,n=s===2?"<svg>":s===3?"<math>":"",l=ye;for(let d=0;d<e;d++){let p=a[d],_,v,m=-1,y=0;for(;y<p.length&&(l.lastIndex=y,v=l.exec(p),v!==null);)y=l.lastIndex,l===ye?v[1]==="!--"?l=Ht:v[1]!==void 0?l=qt:v[2]!==void 0?(Ut.test(v[2])&&(i=RegExp("</"+v[2],"g")),l=se):v[3]!==void 0&&(l=se):l===se?v[0]===">"?(l=i??ye,m=-1):v[1]===void 0?m=-2:(m=l.lastIndex-v[2].length,_=v[1],l=v[3]===void 0?se:v[3]==='"'?Mt:Ot):l===Mt||l===Ot?l=se:l===Ht||l===qt?l=ye:(l=se,i=void 0);let x=l===se&&a[d+1].startsWith("/>")?" ":"";n+=l===ye?p+cs:m>=0?(t.push(_),p.slice(0,m)+zt+p.slice(m)+te+x):p+te+(m===-2?d:x)}return[Bt(a,n+(a[e]||"<?>")+(s===2?"</svg>":s===3?"</math>":"")),t]},$e=class a{constructor({strings:s,_$litType$:e},t){let i;this.parts=[];let n=0,l=0,d=s.length-1,p=this.parts,[_,v]=ps(s,e);if(this.el=a.createElement(_,t),re.currentNode=this.el.content,e===2||e===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes)}for(;(i=re.nextNode())!==null&&p.length<d;){if(i.nodeType===1){if(i.hasAttributes())for(let m of i.getAttributeNames())if(m.endsWith(zt)){let y=v[l++],x=i.getAttribute(m).split(te),f=/([.?@])?(.*)/.exec(y);p.push({type:1,index:n,name:f[2],strings:x,ctor:f[1]==="."?et:f[1]==="?"?tt:f[1]==="@"?it:de}),i.removeAttribute(m)}else m.startsWith(te)&&(p.push({type:6,index:n}),i.removeAttribute(m));if(Ut.test(i.tagName)){let m=i.textContent.split(te),y=m.length-1;if(y>0){i.textContent=He?He.emptyScript:"";for(let x=0;x<y;x++)i.append(m[x],xe()),re.nextNode(),p.push({type:2,index:++n});i.append(m[y],xe())}}}else if(i.nodeType===8)if(i.data===Ft)p.push({type:2,index:n});else{let m=-1;for(;(m=i.data.indexOf(te,m+1))!==-1;)p.push({type:7,index:n}),m+=te.length-1}n++}}static createElement(s,e){let t=ae.createElement("template");return t.innerHTML=s,t}};Xe=class{constructor(s,e){this._$AV=[],this._$AN=void 0,this._$AD=s,this._$AM=e}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(s){let{el:{content:e},parts:t}=this._$AD,i=(s?.creationScope??ae).importNode(e,!0);re.currentNode=i;let n=re.nextNode(),l=0,d=0,p=t[0];for(;p!==void 0;){if(l===p.index){let _;p.type===2?_=new ke(n,n.nextSibling,this,s):p.type===1?_=new p.ctor(n,p.name,p.strings,this,s):p.type===6&&(_=new st(n,this,s)),this._$AV.push(_),p=t[++d]}l!==p?.index&&(n=re.nextNode(),l++)}return re.currentNode=ae,i}p(s){let e=0;for(let t of this._$AV)t!==void 0&&(t.strings!==void 0?(t._$AI(s,t,e),e+=t.strings.length-2):t._$AI(s[e])),e++}},ke=class a{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(s,e,t,i){this.type=2,this._$AH=u,this._$AN=void 0,this._$AA=s,this._$AB=e,this._$AM=t,this.options=i,this._$Cv=i?.isConnected??!0}get parentNode(){let s=this._$AA.parentNode,e=this._$AM;return e!==void 0&&s?.nodeType===11&&(s=e.parentNode),s}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(s,e=this){s=ce(this,s,e),we(s)?s===u||s==null||s===""?(this._$AH!==u&&this._$AR(),this._$AH=u):s!==this._$AH&&s!==ne&&this._(s):s._$litType$!==void 0?this.$(s):s.nodeType!==void 0?this.T(s):ds(s)?this.k(s):this._(s)}O(s){return this._$AA.parentNode.insertBefore(s,this._$AB)}T(s){this._$AH!==s&&(this._$AR(),this._$AH=this.O(s))}_(s){this._$AH!==u&&we(this._$AH)?this._$AA.nextSibling.data=s:this.T(ae.createTextNode(s)),this._$AH=s}$(s){let{values:e,_$litType$:t}=s,i=typeof t=="number"?this._$AC(s):(t.el===void 0&&(t.el=$e.createElement(Bt(t.h,t.h[0]),this.options)),t);if(this._$AH?._$AD===i)this._$AH.p(e);else{let n=new Xe(i,this),l=n.u(this.options);n.p(e),this.T(l),this._$AH=n}}_$AC(s){let e=Dt.get(s.strings);return e===void 0&&Dt.set(s.strings,e=new $e(s)),e}k(s){at(this._$AH)||(this._$AH=[],this._$AR());let e=this._$AH,t,i=0;for(let n of s)i===e.length?e.push(t=new a(this.O(xe()),this.O(xe()),this,this.options)):t=e[i],t._$AI(n),i++;i<e.length&&(this._$AR(t&&t._$AB.nextSibling,i),e.length=i)}_$AR(s=this._$AA.nextSibling,e){for(this._$AP?.(!1,!0,e);s!==this._$AB;){let t=Rt(s).nextSibling;Rt(s).remove(),s=t}}setConnected(s){this._$AM===void 0&&(this._$Cv=s,this._$AP?.(s))}},de=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(s,e,t,i,n){this.type=1,this._$AH=u,this._$AN=void 0,this.element=s,this.name=e,this._$AM=i,this.options=n,t.length>2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=u}_$AI(s,e=this,t,i){let n=this.strings,l=!1;if(n===void 0)s=ce(this,s,e,0),l=!we(s)||s!==this._$AH&&s!==ne,l&&(this._$AH=s);else{let d=s,p,_;for(s=n[0],p=0;p<n.length-1;p++)_=ce(this,d[t+p],e,p),_===ne&&(_=this._$AH[p]),l||=!we(_)||_!==this._$AH[p],_===u?s=u:s!==u&&(s+=(_??"")+n[p+1]),this._$AH[p]=_}l&&!i&&this.j(s)}j(s){s===u?this.element.removeAttribute(this.naLine truncated
|
\f\r"'\`<>=]|("|')|))|$)`,"g"),zt=/'/g,Ft=/"/g,Wt=/^(?:script|style|textarea|title)$/i,lt=a=>(s,...e)=>({_$litType$:a,strings:s,values:e}),o=lt(1),he=lt(2),yr=lt(3),oe=Symbol.for("lit-noChange"),u=Symbol.for("lit-nothing"),Ut=new WeakMap,ae=ne.createTreeWalker(ne,129);_s=(a,s)=>{let e=a.length-1,t=[],i,n=s===2?"<svg>":s===3?"<math>":"",l=xe;for(let d=0;d<e;d++){let p=a[d],_,v,m=-1,b=0;for(;b<p.length&&(l.lastIndex=b,v=l.exec(p),v!==null);)b=l.lastIndex,l===xe?v[1]==="!--"?l=Mt:v[1]!==void 0?l=Dt:v[2]!==void 0?(Wt.test(v[2])&&(i=RegExp("</"+v[2],"g")),l=re):v[3]!==void 0&&(l=re):l===re?v[0]===">"?(l=i??xe,m=-1):v[1]===void 0?m=-2:(m=l.lastIndex-v[2].length,_=v[1],l=v[3]===void 0?re:v[3]==='"'?Ft:zt):l===Ft||l===zt?l=re:l===Mt||l===Dt?l=xe:(l=re,i=void 0);let y=l===re&&a[d+1].startsWith("/>")?" ":"";n+=l===xe?p+hs:m>=0?(t.push(_),p.slice(0,m)+Bt+p.slice(m)+te+y):p+te+(m===-2?d:y)}return[Kt(a,n+(a[e]||"<?>")+(s===2?"</svg>":s===3?"</math>":"")),t]},ke=class a{constructor({strings:s,_$litType$:e},t){let i;this.parts=[];let n=0,l=0,d=s.length-1,p=this.parts,[_,v]=_s(s,e);if(this.el=a.createElement(_,t),ae.currentNode=this.el.content,e===2||e===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes)}for(;(i=ae.nextNode())!==null&&p.length<d;){if(i.nodeType===1){if(i.hasAttributes())for(let m of i.getAttributeNames())if(m.endsWith(Bt)){let b=v[l++],y=i.getAttribute(m).split(te),f=/([.?@])?(.*)/.exec(b);p.push({type:1,index:n,name:f[2],strings:y,ctor:f[1]==="."?it:f[1]==="?"?st:f[1]==="@"?rt:pe}),i.removeAttribute(m)}else m.startsWith(te)&&(p.push({type:6,index:n}),i.removeAttribute(m));if(Wt.test(i.tagName)){let m=i.textContent.split(te),b=m.length-1;if(b>0){i.textContent=Oe?Oe.emptyScript:"";for(let y=0;y<b;y++)i.append(m[y],we()),ae.nextNode(),p.push({type:2,index:++n});i.append(m[b],we())}}}else if(i.nodeType===8)if(i.data===Vt)p.push({type:2,index:n});else{let m=-1;for(;(m=i.data.indexOf(te,m+1))!==-1;)p.push({type:7,index:n}),m+=te.length-1}n++}}static createElement(s,e){let t=ne.createElement("template");return t.innerHTML=s,t}};tt=class{constructor(s,e){this._$AV=[],this._$AN=void 0,this._$AD=s,this._$AM=e}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(s){let{el:{content:e},parts:t}=this._$AD,i=(s?.creationScope??ne).importNode(e,!0);ae.currentNode=i;let n=ae.nextNode(),l=0,d=0,p=t[0];for(;p!==void 0;){if(l===p.index){let _;p.type===2?_=new Ee(n,n.nextSibling,this,s):p.type===1?_=new p.ctor(n,p.name,p.strings,this,s):p.type===6&&(_=new at(n,this,s)),this._$AV.push(_),p=t[++d]}l!==p?.index&&(n=ae.nextNode(),l++)}return ae.currentNode=ne,i}p(s){let e=0;for(let t of this._$AV)t!==void 0&&(t.strings!==void 0?(t._$AI(s,t,e),e+=t.strings.length-2):t._$AI(s[e])),e++}},Ee=class a{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(s,e,t,i){this.type=2,this._$AH=u,this._$AN=void 0,this._$AA=s,this._$AB=e,this._$AM=t,this.options=i,this._$Cv=i?.isConnected??!0}get parentNode(){let s=this._$AA.parentNode,e=this._$AM;return e!==void 0&&s?.nodeType===11&&(s=e.parentNode),s}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(s,e=this){s=de(this,s,e),$e(s)?s===u||s==null||s===""?(this._$AH!==u&&this._$AR(),this._$AH=u):s!==this._$AH&&s!==oe&&this._(s):s._$litType$!==void 0?this.$(s):s.nodeType!==void 0?this.T(s):us(s)?this.k(s):this._(s)}O(s){return this._$AA.parentNode.insertBefore(s,this._$AB)}T(s){this._$AH!==s&&(this._$AR(),this._$AH=this.O(s))}_(s){this._$AH!==u&&$e(this._$AH)?this._$AA.nextSibling.data=s:this.T(ne.createTextNode(s)),this._$AH=s}$(s){let{values:e,_$litType$:t}=s,i=typeof t=="number"?this._$AC(s):(t.el===void 0&&(t.el=ke.createElement(Kt(t.h,t.h[0]),this.options)),t);if(this._$AH?._$AD===i)this._$AH.p(e);else{let n=new tt(i,this),l=n.u(this.options);n.p(e),this.T(l),this._$AH=n}}_$AC(s){let e=Ut.get(s.strings);return e===void 0&&Ut.set(s.strings,e=new ke(s)),e}k(s){ot(this._$AH)||(this._$AH=[],this._$AR());let e=this._$AH,t,i=0;for(let n of s)i===e.length?e.push(t=new a(this.O(we()),this.O(we()),this,this.options)):t=e[i],t._$AI(n),i++;i<e.length&&(this._$AR(t&&t._$AB.nextSibling,i),e.length=i)}_$AR(s=this._$AA.nextSibling,e){for(this._$AP?.(!1,!0,e);s!==this._$AB;){let t=Ht(s).nextSibling;Ht(s).remove(),s=t}}setConnected(s){this._$AM===void 0&&(this._$Cv=s,this._$AP?.(s))}},pe=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(s,e,t,i,n){this.type=1,this._$AH=u,this._$AN=void 0,this.element=s,this.name=e,this._$AM=i,this.options=n,t.length>2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=u}_$AI(s,e=this,t,i){let n=this.strings,l=!1;if(n===void 0)s=de(this,s,e,0),l=!$e(s)||s!==this._$AH&&s!==oe,l&&(this._$AH=s);else{let d=s,p,_;for(s=n[0],p=0;p<n.length-1;p++)_=de(this,d[t+p],e,p),_===oe&&(_=this._$AH[p]),l||=!$e(_)||_!==this._$AH[p],_===u?s=u:s!==u&&(s+=(_??"")+n[p+1]),this._$AH[p]=_}l&&!i&&this.j(s)}j(s){s===u?this.element.removeAttribute(this.naLine truncated
|
||||||
Replace seal
|
Replace seal
|
||||||
Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"{field}: too long (max {n} characters)",err_too_short:"{field}: too short (min {n} characters)",err_value_too_high:"{field}: too large (max {n})",err_value_too_low:"{field}: too small (min {n})",err_required:"{field}: required",err_wrong_type:"{field}: wrong type (expected: {type})",err_invalid_choice:"{field}: not an allowed value",err_invalid_value:"{field}: invalid value",feat_schedule_time:"Time-of-day scheduling",feat_schedule_time_desc:"Tasks become overdue at a specific time of day instead of midnight.",schedule_time_optional:"Due at time (optional, HH:MM)",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",completed_at_optional:"Completed at (optional, empty = now)",completed_at_future_error:"The completion date cannot be in the future.",days:"days",day:"day",today:"Today",d_overdue:"d overdue",no_tasks:"No maintenance tasks yet. Create an object to get started.",no_tasks_short:"No tasks",no_history:"No history entries yet.",show_all:"Show all",cost_duration_chart:"Cost & Duration",installed:"Installed",confirm_delete_object:"Delete this object and all its tasks?",confirm_delete_task:"Delete this task?",min:"Min",max:"Max",save:"Save",saving:"Saving\u2026",edit_task:"Edit Task",new_task:"New Maintenance Task",task_name:"Task name",maintenance_type:"Maintenance type",priority:"Priority",labels:"Labels",labels_placeholder:"e.g. safety, seasonal, tenant-visible",labels_help:"Comma-separated tags for filtering and reporting.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",all_priorities:"All priorities",schedule_type:"Schedule type",interval_days:"Interval (days)",warning_days:"Warning days",earliest_completion_days:"Earliest completion (days before due)",earliest_completion_days_help:"Leave empty to allow completing any time. 0 = only on/after the due date.",last_performed_optional:"Last performed (optional)",interval_anchor:"Interval anchor",anchor_completion:"From completion date",anchor_planned:"From planned date (no drift)",edit_object:"Edit Object",name:"Name",manufacturer_optional:"Manufacturer (optional)",model_optional:"Model (optional)",serial_number_optional:"Serial number (optional)",serial_number_label:"S/N",documentation_url_label:"Manual",object_notes_label:"Notes",sort_due_date:"Due date",sort_object:"Object name",sort_type:"Type",sort_task_name:"Task name",all_objects:"All objects",all_parts:"All parts",tasks_lower:"tasks",no_tasks_yet:"No tasks yet",add_first_task:"Add first task",trigger_configuration:"Trigger Configuration",entity_id:"Entity ID",comma_separated:"comma-separated",entity_logic:"Entity logic",entity_logic_any:"Any entity triggers",entity_logic_all:"All entities must trigger",entities:"entities",attribute_optional:"Attribute (optional, blank = state)",use_entity_state:"Use entity state (no attribute)",trigger_above:"Trigger above",trigger_below:"Trigger below",trigger_equals:"Trigger when equal to (=)",trigger_not_equals:"Trigger when different from (\u2260)",for_at_least_minutes:"For at least (minutes)",safety_interval_days:"Safety interval (days, optional)",safety_interval:"Safety interval (optional)",trigger_combinator:"Combine trigger and interval",trigger_combinator_any:"Trigger or interval (whichever first)",trigger_combinator_all:"Trigger and interval (both required)",delta_mode:"Delta mode",from_state_optional:"From state (optional)",to_state_optional:"To state (optional)",documentation_url_optional:"Documentation URL (optional)",object_notes_optional:"Notes (optional)",nfc_tag_id_optional:"NFC Tag ID (optional)",nfc_tags_empty_help:"No NFC tags registered in Home Assistant yet.",nfc_tags_open_settings:"Open Tags settings",nfc_tags_refresh:"Refresh",environmental_entity_optional:"Environmental sensor (optional)",environmental_entity_helper:"e.g. sensor.outdoor_temperature \u2014 adjusts the interval based on environmental conditions",adaptive_prediction_enabled:"Enable sensor-driven predictions",adaptive_seasonal_enabled:"Enable seasonal awareness",adaptive_max_interval:"Maximum interval (days)",adaptive_min_interval:"Minimum interval (days)",adaptive_ewa_alpha:"Learning rate (alpha)",adaptive_enabled:"Enable adaptive scheduling",adaptive_section_title:"Adaptive Scheduling",environmental_attribute_optional:"Environmental attribute (optional)",nfc_tag_id:"NFC Tag ID",nfc_linked:"NFC tag linked",nfc_link_hint:"Click to link NFC tag",responsible_user:"Responsible User",shared_with:"Shared with (rotation)",shared_with_help:"Pick multiple people to share this task; the responsible person rotates on each completion.",rotation_strategy:"Rotation",rotation_none:"No rotation",rotation_round_robin:"Round-robin",rotation_least_completed:"Least completed",rotation_random:"Random",no_user_assigned:"(No user assigned)",all_users:"All Users",m
Line truncated
|
Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"{field}: too long (max {n} characters)",err_too_short:"{field}: too short (min {n} characters)",err_value_too_high:"{field}: too large (max {n})",err_value_too_low:"{field}: too small (min {n})",err_required:"{field}: required",err_wrong_type:"{field}: wrong type (expected: {type})",err_invalid_choice:"{field}: not an allowed value",err_invalid_value:"{field}: invalid value",feat_schedule_time:"Time-of-day scheduling",feat_schedule_time_desc:"Tasks become overdue at a specific time of day instead of midnight.",schedule_time_optional:"Due at time (optional, HH:MM)",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",completed_at_optional:"Completed at (optional, empty = now)",completed_at_future_error:"The completion date cannot be in the future.",days:"days",day:"day",today:"Today",d_overdue:"d overdue",no_tasks:"No maintenance tasks yet. Create an object to get started.",no_tasks_short:"No tasks",no_history:"No history entries yet.",show_all:"Show all",cost_duration_chart:"Cost & Duration",installed:"Installed",confirm_delete_object:"Delete this object and all its tasks?",confirm_delete_task:"Delete this task?",min:"Min",max:"Max",save:"Save",saving:"Saving\u2026",edit_task:"Edit Task",new_task:"New Maintenance Task",task_name:"Task name",maintenance_type:"Maintenance type",priority:"Priority",labels:"Labels",labels_placeholder:"e.g. safety, seasonal, tenant-visible",labels_help:"Comma-separated tags for filtering and reporting.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",all_priorities:"All priorities",schedule_type:"Schedule type",interval_days:"Interval (days)",warning_days:"Warning days",earliest_completion_days:"Earliest completion (days before due)",earliest_completion_days_help:"Leave empty to allow completing any time. 0 = only on/after the due date.",last_performed_optional:"Last performed (optional)",interval_anchor:"Interval anchor",anchor_completion:"From completion date",anchor_planned:"From planned date (no drift)",edit_object:"Edit Object",name:"Name",manufacturer_optional:"Manufacturer (optional)",model_optional:"Model (optional)",serial_number_optional:"Serial number (optional)",serial_number_label:"S/N",documentation_url_label:"Manual",object_notes_label:"Notes",sort_due_date:"Due date",sort_object:"Object name",sort_type:"Type",sort_task_name:"Task name",all_objects:"All objects",all_parts:"All parts",tasks_lower:"tasks",no_tasks_yet:"No tasks yet",add_first_task:"Add first task",trigger_configuration:"Trigger Configuration",entity_id:"Entity ID",comma_separated:"comma-separated",entity_logic:"Entity logic",entity_logic_any:"Any entity triggers",entity_logic_all:"All entities must trigger",entities:"entities",attribute_optional:"Attribute (optional, blank = state)",use_entity_state:"Use entity state (no attribute)",trigger_above:"Trigger above",trigger_below:"Trigger below",trigger_equals:"Trigger when equal to (=)",trigger_not_equals:"Trigger when different from (\u2260)",for_at_least_minutes:"For at least (minutes)",safety_interval_days:"Safety interval (days, optional)",safety_interval:"Safety interval (optional)",trigger_combinator:"Combine trigger and interval",trigger_combinator_any:"Trigger or interval (whichever first)",trigger_combinator_all:"Trigger and interval (both required)",delta_mode:"Delta mode",from_state_optional:"From state (optional)",to_state_optional:"To state (optional)",documentation_url_optional:"Documentation URL (optional)",object_notes_optional:"Notes (optional)",nfc_tag_id_optional:"NFC Tag ID (optional)",nfc_tags_empty_help:"No NFC tags registered in Home Assistant yet.",nfc_tags_open_settings:"Open Tags settings",nfc_tags_refresh:"Refresh",environmental_entity_optional:"Environmental sensor (optional)",environmental_entity_helper:"e.g. sensor.outdoor_temperature \u2014 adjusts the interval based on environmental conditions",adaptive_prediction_enabled:"Enable sensor-driven predictions",adaptive_seasonal_enabled:"Enable seasonal awareness",adaptive_max_interval:"Maximum interval (days)",adaptive_min_interval:"Minimum interval (days)",adaptive_ewa_alpha:"Learning rate (alpha)",adaptive_enabled:"Enable adaptive scheduling",adaptive_section_title:"Adaptive Scheduling",environmental_attribute_optional:"Environmental attribute (optional)",nfc_tag_id:"NFC Tag ID",nfc_linked:"NFC tag linked",nfc_link_hint:"Click to link NFC tag",responsible_user:"Responsible User",shared_with:"Shared with (rotation)",shared_with_help:"Pick multiple people to share this task; the responsible person rotates on each completion.",rotation_strategy:"Rotation",rotation_none:"No rotation",rotation_round_robin:"Round-robin",rotation_least_completed:"Least completed",rotation_random:"Random",no_user_assigned:"(No user assigned)",all_users:"All Users",m
Line truncated
|
||||||
.field { display: flex; flex-direction: column; gap: 4px; }
|
.field { display: flex; flex-direction: column; gap: 4px; }
|
||||||
.field-label { font-size: 12px; color: var(--secondary-text-color); }
|
.field-label { font-size: 12px; color: var(--secondary-text-color); }
|
||||||
.field-input {
|
.field-input {
|
||||||
@@ -14,7 +14,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
font-family: inherit; width: 100%; box-sizing: border-box;
|
font-family: inherit; width: 100%; box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.field-input:focus { outline: none; border-color: var(--primary-color); }
|
.field-input:focus { outline: none; border-color: var(--primary-color); }
|
||||||
`,Fe=A`
|
`,Be=A`
|
||||||
:host {
|
:host {
|
||||||
--maint-ok-color: var(--success-color, #4caf50);
|
--maint-ok-color: var(--success-color, #4caf50);
|
||||||
--maint-due-soon-color: var(--warning-color, #ff9800);
|
--maint-due-soon-color: var(--warning-color, #ff9800);
|
||||||
@@ -258,6 +258,21 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
/* #139 — names the cycle phase a completion recorded. Shares the auto-badge
|
||||||
|
look; carries the left-pinning margin itself unless the auto badge (which
|
||||||
|
already has it) follows. */
|
||||||
|
.history-phase-badge {
|
||||||
|
margin-right: auto;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 1px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--secondary-background-color);
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.history-phase-badge:has(+ .history-auto-badge) {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
.history-edit-btn {
|
.history-edit-btn {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
@@ -1129,13 +1144,14 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
.stat-item .stat-label { font-size: 11px; white-space: normal; text-align: center; line-height: 1.2; }
|
.stat-item .stat-label { font-size: 11px; white-space: normal; text-align: center; line-height: 1.2; }
|
||||||
.stat-value { font-size: 20px; }
|
.stat-value { font-size: 20px; }
|
||||||
}
|
}
|
||||||
`});function gi(a,s,e){let t=new Blob([a],{type:e}),i=URL.createObjectURL(t),n=document.createElement("a");n.href=i,n.download=s,n.target="_blank",n.rel="noopener",n.style.display="none",document.body.appendChild(n),n.dispatchEvent(new MouseEvent("click")),document.body.removeChild(n),setTimeout(()=>URL.revokeObjectURL(i),6e4)}var ut=w(()=>{"use strict"});function Ue(a){return!!a&&/^https?:\/\//i.test(a)}var _t=w(()=>{"use strict"});var ue,gt=w(()=>{"use strict";ue=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}}});function V(a){return`${a.entry_id??""}\0${a.part_id}`}function Ls(a,s,e,t){let i=!!a.entry_id&&a.entry_id!==s,n=i?a.entry_id:s,l=e.find(v=>v.entry_id===n),d=(l?.parts||[]).find(v=>v.id===a.part_id)||null,p=i&&l?.object?.name||"",_=d?.name||r("shared_part_unknown",t);return{part:d,foreign:i,ownerName:p,label:p?`${_} (${p})`:_}}function Ve(a,s,e,t){let n=(e.find(d=>d.entry_id===s)?.parts||[]).map(d=>({...d})),l=new Set(n.map(d=>V({part_id:d.id})));for(let d of a?.consumes_parts||[]){if(!d.entry_id||d.entry_id===s)continue;let p=V(d);if(l.has(p))continue;l.add(p);let{part:_,ownerName:v}=Ls(d,s,e,t);n.push({id:d.part_id,name:_?.name||r("shared_part_unknown",t),unit:_?.unit,stock:_?.stock??null,storage_location:_?.storage_location,entry_id:d.entry_id,owner_name:v})}return n}var Ie=w(()=>{"use strict";R()});function Rs(a,s){let e=js[a];if(!e)return a;let t=r(e,s);return t&&t!==e?t:a}function Ns(a){let e=a.match(/data\['([^']+)'\]/)?.[1],t;return(t=a.match(/length of value must be at most (\d+)/))?{field:e,rule:"too_long",param:t[1]}:(t=a.match(/length of value must be at least (\d+)/))?{field:e,rule:"too_short",param:t[1]}:(t=a.match(/value must be at most (\S+)/))?{field:e,rule:"value_too_high",param:t[1]}:(t=a.match(/value must be at least (\S+)/))?{field:e,rule:"value_too_low",param:t[1]}:/required key not provided/.test(a)?{field:e,rule:"required"}:(t=a.match(/expected (\w+)/))?{field:e,rule:"wrong_type",param:t[1]}:/value must be one of/.test(a)?{field:e,rule:"invalid_choice"}:/not a valid value/.test(a)?{field:e,rule:"invalid_value"}:{field:e,rule:"unknown"}}function T(a,s,e){if(e=e??r("action_error",s),typeof a=="string")return a;if(typeof a!="object"||a===null)return e;let t=a,i=t.message||t.error?.message||"";if(!i)return e;let n=Ns(i),l=n.field?Rs(n.field,s):"",d=p=>r(p,s).replace("{field}",l).replace("{n}",n.param??"");switch(n.rule){case"too_long":return d("err_too_long");case"too_short":return d("err_too_short");case"value_too_high":return d("err_value_too_high");case"value_too_low":return d("err_value_too_low");case"required":return d("err_required");case"wrong_type":return d("err_wrong_type").replace("{type}",n.param??"");case"invalid_choice":return d("err_invalid_choice");case"invalid_value":return d("err_invalid_value");default:return i||e}}var js,ie=w(()=>{"use strict";R();js={entry_id:"object",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_equals:"trigger_equals",tri
Line truncated
|
`});function vi(a,s,e){let t=new Blob([a],{type:e}),i=URL.createObjectURL(t),n=document.createElement("a");n.href=i,n.download=s,n.target="_blank",n.rel="noopener",n.style.display="none",document.body.appendChild(n),n.dispatchEvent(new MouseEvent("click")),document.body.removeChild(n),setTimeout(()=>URL.revokeObjectURL(i),6e4)}var gt=w(()=>{"use strict"});function Ve(a){return!!a&&/^https?:\/\//i.test(a)}var mt=w(()=>{"use strict"});var _e,ft=w(()=>{"use strict";_e=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}}});function V(a){return`${a.entry_id??""}\0${a.part_id}`}function js(a,s,e,t){let i=!!a.entry_id&&a.entry_id!==s,n=i?a.entry_id:s,l=e.find(v=>v.entry_id===n),d=(l?.parts||[]).find(v=>v.id===a.part_id)||null,p=i&&l?.object?.name||"",_=d?.name||r("shared_part_unknown",t);return{part:d,foreign:i,ownerName:p,label:p?`${_} (${p})`:_}}function Ke(a,s,e,t){let n=(e.find(d=>d.entry_id===s)?.parts||[]).map(d=>({...d})),l=new Set(n.map(d=>V({part_id:d.id})));for(let d of a?.consumes_parts||[]){if(!d.entry_id||d.entry_id===s)continue;let p=V(d);if(l.has(p))continue;l.add(p);let{part:_,ownerName:v}=js(d,s,e,t);n.push({id:d.part_id,name:_?.name||r("shared_part_unknown",t),unit:_?.unit,stock:_?.stock??null,storage_location:_?.storage_location,entry_id:d.entry_id,owner_name:v})}return n}var Pe=w(()=>{"use strict";j()});function Ns(a,s){if(s<=0)return 0;let e=typeof a=="number"&&Number.isFinite(a)?Math.trunc(a):0;return e<0?0:e%s}function qs(a){return!!(a?.phases&&a.phase_sequence&&a.phase_sequence.length>0)}function Le(a){if(!a||!qs(a))return null;let s=a.phase_sequence,e=Ns(a.phase_cursor,s.length),t=s[e],i=a.phases?.[t];return i?{id:t,name:i.name,index:e,count:s.length,notes:i.notes,checklist:i.checklist!==void 0?i.checklist:a.checklist??[],consumesParts:i.consumes_parts!==void 0?i.consumes_parts:a.consumes_parts??[],requiredFields:i.required_completion_fields!==void 0?i.required_completion_fields:a.required_completion_fields??[]}:null}function ie(a){let s=Le(a);return s?`${s.index+1}/${s.count} \xB7 ${s.name}`:""}var vt=w(()=>{"use strict"});function Ms(a,s){let e=Os[a];if(!e)return a;let t=r(e,s);return t&&t!==e?t:a}function Ds(a){let e=a.match(/data\['([^']+)'\]/)?.[1],t;return(t=a.match(/length of value must be at most (\d+)/))?{field:e,rule:"too_long",param:t[1]}:(t=a.match(/length of value must be at least (\d+)/))?{field:e,rule:"too_short",param:t[1]}:(t=a.match(/value must be at most (\S+)/))?{field:e,rule:"value_too_high",param:t[1]}:(t=a.match(/value must be at least (\S+)/))?{field:e,rule:"value_too_low",param:t[1]}:/required key not provided/.test(a)?{field:e,rule:"required"}:(t=a.match(/expected (\w+)/))?{field:e,rule:"wrong_type",param:t[1]}:/value must be one of/.test(a)?{field:e,rule:"invalid_choice"}:/not a valid value/.test(a)?{field:e,rule:"invalid_value"}:{field:e,rule:"unknown"}}function T(a,s,e){if(e=e??r("action_error",s),typeof a=="string")return a;if(typeof a!="object"||a===null)return e;let t=a,i=t.message||t.error?.message||"";if(!i)return e;let n=Ds(i),l=n.field?Ms(n.field,s):"",d=p=>r(p,s).replace("{field}",l).replace("{n}",n.param??"");switch(n.rule){case"too_long":return d("err_too_long");case"too_short":return d("err_too_short");case"value_too_high":return d("err_value_too_high");case"value_too_low":return d("err_value_too_low");case"required":return d("err_required");case"wrong_type":return d("err_wrong_type").replace("{type}",n.param??"");case"invalid_choice":return d("err_invalid_choice");case"invalid_value":return d("err_invalid_value");default:return i||e}}var Os,se=w(()=>{"use strict";j();Os={entry_id:"object",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",nf
Line truncated
|
||||||
type="button"
|
type="button"
|
||||||
class="cost-suggestion"
|
class="cost-suggestion"
|
||||||
@click=${()=>this._cost=t.toFixed(2)}
|
@click=${()=>this._cost=t.toFixed(2)}
|
||||||
>${r("cost_from_parts",e).replace("{amount}",i)}</button>`}_close(){this._open=!1}render(){if(!this._open)return o``;let e=this.lang||this.hass?.language||"en";return o`
|
>${r("cost_from_parts",e).replace("{amount}",i)}</button>`}_close(){this._open=!1}render(){if(!this._open)return o``;let e=this.lang||this.hass?.language||"en";return o`
|
||||||
<ha-dialog open @closed=${this._close}>
|
<ha-dialog open @closed=${this._close}>
|
||||||
<div class="dialog-title">${r("complete_title",e)}${this.taskName}</div>
|
<div class="dialog-title">${r("complete_title",e)}${this.taskName}</div>
|
||||||
|
${this.phaseLabel?o`<div class="phase-line">${r("phase_current",e)}: ${this.phaseLabel}</div>`:u}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${this._error?o`<div class="error">${this._error}</div>`:u}
|
${this._error?o`<div class="error">${this._error}</div>`:u}
|
||||||
${this.checklist.length>0?o`
|
${this.checklist.length>0?o`
|
||||||
@@ -1255,13 +1271,13 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
<ha-button
|
<ha-button
|
||||||
@click=${this._complete}
|
@click=${this._complete}
|
||||||
.disabled=${this._loading||this._missingRequired.length>0}
|
.disabled=${this._loading||this._missingRequired.length>0}
|
||||||
title=${this._missingRequired.length?this._missingRequired.map(t=>r("err_required",e).replace("{field}",r(We[t]??t,e))).join(" \xB7 "):""}
|
title=${this._missingRequired.length?this._missingRequired.map(t=>r("err_required",e).replace("{field}",r(Ge[t]??t,e))).join(" \xB7 "):""}
|
||||||
>
|
>
|
||||||
${this._loading?r("completing",e):r("complete",e)}
|
${this._loading?r("completing",e):r("complete",e)}
|
||||||
</ha-button>
|
</ha-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`}};k.styles=[_i,A`
|
`}};k.styles=[fi,A`
|
||||||
.req-mark {
|
.req-mark {
|
||||||
color: var(--error-color, #f44336);
|
color: var(--error-color, #f44336);
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
@@ -1285,6 +1301,12 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
.phase-line {
|
||||||
|
margin-top: -8px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
.content {
|
.content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -1426,7 +1448,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
color: var(--text-primary-color, #fff);
|
color: var(--text-primary-color, #fff);
|
||||||
border-color: var(--primary-color);
|
border-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
`],c([b({attribute:!1})],k.prototype,"hass",2),c([b()],k.prototype,"entryId",2),c([b()],k.prototype,"taskId",2),c([b()],k.prototype,"taskName",2),c([b()],k.prototype,"lang",2),c([b({type:Array})],k.prototype,"checklist",2),c([b({type:Boolean})],k.prototype,"adaptiveEnabled",2),c([b()],k.prototype,"taskType",2),c([b()],k.prototype,"readingUnit",2),c([b({attribute:!1})],k.prototype,"restockDefault",2),c([b({attribute:!1})],k.prototype,"restockUnitCost",2),c([b()],k.prototype,"currencySymbol",2),c([b({attribute:!1})],k.prototype,"parts",2),c([b({attribute:!1})],k.prototype,"consumesParts",2),c([b({type:Array})],k.prototype,"consumesInfo",2),c([b({type:Array})],k.prototype,"requiredFields",2),c([h()],k.prototype,"_open",2),c([h()],k.prototype,"_notes",2),c([h()],k.prototype,"_cost",2),c([h()],k.prototype,"_duration",2),c([h()],k.prototype,"_loading",2),c([h()],k.prototype,"_error",2),c([h()],k.prototype,"_checklistState",2),c([h()],k.prototype,"_feedback",2),c([h()],k.prototype,"_photoDocId",2),c([h()],k.prototype,"_photoPreview",2),c([h()],k.prototype,"_photoUploading",2),c([h()],k.prototype,"_readingValue",2),c([h()],k.prototype,"_restockQty",2),c([h()],k.prototype,"_completedAt",2),c([h()],k.prototype,"_usedParts",2),c([b({attribute:!1})],k.prototype,"checklistPrefill",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",k)});var H,yt=w(()=>{"use strict";P();O();H=class extends S{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1}_onInput(e){let t=e.target.value;this.value=t,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:t}}))}render(){return o`
|
`],c([x({attribute:!1})],k.prototype,"hass",2),c([x()],k.prototype,"entryId",2),c([x()],k.prototype,"taskId",2),c([x()],k.prototype,"taskName",2),c([x()],k.prototype,"lang",2),c([x({type:Array})],k.prototype,"checklist",2),c([x({type:Boolean})],k.prototype,"adaptiveEnabled",2),c([x()],k.prototype,"taskType",2),c([x()],k.prototype,"readingUnit",2),c([x({attribute:!1})],k.prototype,"restockDefault",2),c([x({attribute:!1})],k.prototype,"restockUnitCost",2),c([x()],k.prototype,"currencySymbol",2),c([x({attribute:!1})],k.prototype,"parts",2),c([x({attribute:!1})],k.prototype,"consumesParts",2),c([x({type:Array})],k.prototype,"consumesInfo",2),c([x({type:Array})],k.prototype,"requiredFields",2),c([x()],k.prototype,"phaseLabel",2),c([h()],k.prototype,"_open",2),c([h()],k.prototype,"_notes",2),c([h()],k.prototype,"_cost",2),c([h()],k.prototype,"_duration",2),c([h()],k.prototype,"_loading",2),c([h()],k.prototype,"_error",2),c([h()],k.prototype,"_checklistState",2),c([h()],k.prototype,"_feedback",2),c([h()],k.prototype,"_photoDocId",2),c([h()],k.prototype,"_photoPreview",2),c([h()],k.prototype,"_photoUploading",2),c([h()],k.prototype,"_readingValue",2),c([h()],k.prototype,"_restockQty",2),c([h()],k.prototype,"_completedAt",2),c([h()],k.prototype,"_usedParts",2),c([x({attribute:!1})],k.prototype,"checklistPrefill",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",k)});var q,$t=w(()=>{"use strict";L();M();q=class extends S{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1}_onInput(e){let t=e.target.value;this.value=t,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:t}}))}render(){return o`
|
||||||
<label class="field">
|
<label class="field">
|
||||||
${this.label?o`<span class="label">${this.label}${this.required?o`<span class="req">*</span>`:u}</span>`:u}
|
${this.label?o`<span class="label">${this.label}${this.required?o`<span class="req">*</span>`:u}</span>`:u}
|
||||||
<input
|
<input
|
||||||
@@ -1444,7 +1466,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
/>
|
/>
|
||||||
${this.helper?o`<span class="helper">${this.helper}</span>`:u}
|
${this.helper?o`<span class="helper">${this.helper}</span>`:u}
|
||||||
</label>
|
</label>
|
||||||
`}};H.styles=A`
|
`}};q.styles=A`
|
||||||
:host { display: block; }
|
:host { display: block; }
|
||||||
.field {
|
.field {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1478,7 +1500,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
`,c([b()],H.prototype,"label",2),c([b()],H.prototype,"value",2),c([b()],H.prototype,"placeholder",2),c([b()],H.prototype,"type",2),c([b({type:Boolean})],H.prototype,"required",2),c([b({type:Boolean})],H.prototype,"disabled",2),c([b()],H.prototype,"step",2),c([b()],H.prototype,"min",2),c([b()],H.prototype,"max",2),c([b()],H.prototype,"pattern",2),c([b()],H.prototype,"helper",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",H)});var C,yi=w(()=>{"use strict";P();O();R();ie();yt();C=class extends S{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 j(this.hass)}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._error="",this._open=!0}openEdit(e,t){this._entryId=e,this._name=t.name||"",this._manufacturer=t.manufacturer||"",this._model=t.model||"",this._serialNumber=t.serial_number||"",this._areaId=t.area_id||"",this._installationDate=t.installation_date||"",this._warrantyExpiry=t.warranty_expiry||"",this._documentationUrl=t.documentation_url||"",this._notes=t.notes||"",this._haDeviceId=t.ha_device_id||"",this._parentEntryId=t.parent_entry_id||"",this._error="",this._open=!0}async _save(){if(!this._loading&&this._name.trim()){this._loading=!0,this._error="";try{this._entryId?await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/update",entry_id:this._entryId,name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}):await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/create",name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}),this._open=!1,this.dispatchEvent(new CustomEvent("object-saved"))}catch(e){this._error=T(e,this._lang,r("save_error",this._lang))}finally{this._loading=!1}}}_parentChoices(){return(this.objects||[]).filter(e=>e.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return o``;let e=this._lang,t=this._entryId?r("edit_object",e):r("new_object",e);return o`
|
`,c([x()],q.prototype,"label",2),c([x()],q.prototype,"value",2),c([x()],q.prototype,"placeholder",2),c([x()],q.prototype,"type",2),c([x({type:Boolean})],q.prototype,"required",2),c([x({type:Boolean})],q.prototype,"disabled",2),c([x()],q.prototype,"step",2),c([x()],q.prototype,"min",2),c([x()],q.prototype,"max",2),c([x()],q.prototype,"pattern",2),c([x()],q.prototype,"helper",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",q)});var C,$i=w(()=>{"use strict";L();M();j();se();$t();C=class extends S{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 R(this.hass)}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._error="",this._open=!0}openEdit(e,t){this._entryId=e,this._name=t.name||"",this._manufacturer=t.manufacturer||"",this._model=t.model||"",this._serialNumber=t.serial_number||"",this._areaId=t.area_id||"",this._installationDate=t.installation_date||"",this._warrantyExpiry=t.warranty_expiry||"",this._documentationUrl=t.documentation_url||"",this._notes=t.notes||"",this._haDeviceId=t.ha_device_id||"",this._parentEntryId=t.parent_entry_id||"",this._error="",this._open=!0}async _save(){if(!this._loading&&this._name.trim()){this._loading=!0,this._error="";try{this._entryId?await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/update",entry_id:this._entryId,name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}):await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/create",name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}),this._open=!1,this.dispatchEvent(new CustomEvent("object-saved"))}catch(e){this._error=T(e,this._lang,r("save_error",this._lang))}finally{this._loading=!1}}}_parentChoices(){return(this.objects||[]).filter(e=>e.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return o``;let e=this._lang,t=this._entryId?r("edit_object",e):r("new_object",e);return o`
|
||||||
<ha-dialog open @closed=${this._close}>
|
<ha-dialog open @closed=${this._close}>
|
||||||
<div class="dialog-title">${t}</div>
|
<div class="dialog-title">${t}</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -1619,8 +1641,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
color: var(--error-color, #f44336);
|
color: var(--error-color, #f44336);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
`,c([b({attribute:!1})],C.prototype,"hass",2),c([b({attribute:!1})],C.prototype,"objects",2),c([h()],C.prototype,"_open",2),c([h()],C.prototype,"_loading",2),c([h()],C.prototype,"_error",2),c([h()],C.prototype,"_name",2),c([h()],C.prototype,"_manufacturer",2),c([h()],C.prototype,"_model",2),c([h()],C.prototype,"_serialNumber",2),c([h()],C.prototype,"_areaId",2),c([h()],C.prototype,"_installationDate",2),c([h()],C.prototype,"_warrantyExpiry",2),c([h()],C.prototype,"_documentationUrl",2),c([h()],C.prototype,"_notes",2),c([h()],C.prototype,"_haDeviceId",2),c([h()],C.prototype,"_parentEntryId",2),c([h()],C.prototype,"_entryId",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",C)});var xt,xi,wi,$i=w(()=>{"use strict";xt=["sensor","binary_sensor","number","input_number","input_boolean","switch","climate","vacuum","cover","fan","light","water_heater","humidifier","media_player","weather","air_quality","valve","lawn_mower","lock"],xi=["sensor"],wi=["temperature","humidity","pressure"]});function Ds(){return{entityIds:"",type:"threshold",attribute:"",above:"",below:"",equals:"",notEquals:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:"",onStates:"",carry:{}}}function Fs(a){return{entityIds:(a.entity_ids||(a.entity_id?[a.entity_id]:[])).join(", "),type:a.type||"threshold",attribute:a.attribute||"",above:a.trigger_above?.toString()??"",below:a.trigger_below?.toString()??"",equals:a.trigger_equals?.toString()??"",notEquals:a.trigger_not_equals?.toString()??"",forMinutes:a.trigger_for_minutes?.toString()??"0",targetValue:a.trigger_target_value?.toString()??"",deltaMode:a.trigger_delta_mode||!1,fromState:a.trigger_from_state||"",toState:a.trigger_to_state||"",targetChanges:a.trigger_target_changes?.toString()??"",runtimeHours:a.trigger_runtime_hours?.toString()??"",onStates:(a.trigger_on_states||[]).join(", "),carry:Object.fromEntries(Object.entries(a).filter(([e])=>!zs.has(e)&&!e.startsWith("_")))}}function Us(a){let s=a.entityIds.split(",").map(t=>t.trim()).filter(Boolean);if(s.length===0)return null;let e={...a.carry||{},entity_id:s[0],entity_ids:s,type:a.type};if(a.attribute&&(e.attribute=a.attribute),a.type==="threshold"){let t=parseFloat(a.above);isNaN(t)||(e.trigger_above=t);let i=parseFloat(a.below);isNaN(i)||(e.trigger_below=i);let n=parseFloat(a.equals);isNaN(n)||(e.trigger_equals=n);let l=parseFloat(a.notEquals);isNaN(l)||(e.trigger_not_equals=l);let d=parseInt(a.forMinutes,10);isNaN(d)||(e.trigger_for_minutes=d)}else if(a.type==="counter"){let t=parseFloat(a.targetValue);isNaN(t)||(e.trigger_target_value=t),e.trigger_delta_mode=a.deltaMode}else if(a.type==="state_change"){a.fromState&&(e.trigger_from_state=a.fromState),a.toState&&(e.trigger_to_state=a.toState);let t=parseInt(a.targetChanges,10);isNaN(t)||(e.trigger_target_changes=t)}else if(a.type==="runtime"){let t=parseFloat(a.runtimeHours);isNaN(t)||(e.trigger_runtime_hours=t);let i=(a.onStates||"").split(",").map(n=>n.trim()).filter(Boolean);i.length>0&&(e.trigger_on_states=i)}return e}function Bs(a){return Array.from({length:7},(s,e)=>Te(e,a,"short"))}function Vs(a){let s=new Intl.DateTimeFormat(a||"en",{month:"short"});return Array.from({length:12},(e,t)=>s.format(new Date(2021,t,1)))}var Hs,qs,Os,wt,ki,Ms,J,zs,g,$t,Ei=w(()=>{"use strict";P();O();R();gt();Ie();$i();ie();mt();yt();Hs=["cleaning","inspection","replacement","calibration","service","reading","custom"],qs=["low","normal","high"],Os=["time_based","weekdays","nth_weekday","day_of_month","sensor_based","one_time","manual"],wt=["weekdays","nth_weekday","day_of_month"],ki=["threshold","counter","state_change","runtime"],Ms=[...ki,"compound"],J={alpha:"0.3",min:"7",max:"365"};zs=new Set(["entity_id","entity_ids","type","attribute","trigger_above","trigger_below","trigger_equals","trigger_not_equals","trigger_for_minutes","trigger_target_value","trigger_delta_mode","trigger_from_state","trigger_to_state","trigger_target_changes","trigger_runtime_hours","trigger_on_states"]);g=class g extends S{constructor(){super(...arguments);this.checklistsEnabled=!1;this.scheduleTimeEnabled=!1;this.completionActionsEnabled=!1;this.defaultWarningDays=7;this.parts=[];this._foreignOwners=[];this._open=!1;this._entityPickerFallback=!1;this._pickerProbeStrikes=0;this._loading=!1;this._error="";this._entryId="";this._taskId=null;this._objectChoices=[];this._name="";this._type="custom";this._scheduleType="time_based";this._intervalDays="30";this._intervalUnit="days";this._dueDate="";this._warningDays="7";this._earliestCompletionDays="";this._intervalAnchor="completion";this._weekdays=[];this._nth="1";this._nthWeekday="5";this._domDay="1";this._domLastDay=!1;this._domBusiness=!1;this._calOffset="0";this._seasonMonths=[];this._endsMode="never";this._endsCount="";this._endsUntil="";this._schedulePreview=[];this._schedulePreviewEnded=!1;this._previewSeq=0;this._notes="";this._documentationUrl="";this._customIcon="";t
Line truncated
|
`,c([x({attribute:!1})],C.prototype,"hass",2),c([x({attribute:!1})],C.prototype,"objects",2),c([h()],C.prototype,"_open",2),c([h()],C.prototype,"_loading",2),c([h()],C.prototype,"_error",2),c([h()],C.prototype,"_name",2),c([h()],C.prototype,"_manufacturer",2),c([h()],C.prototype,"_model",2),c([h()],C.prototype,"_serialNumber",2),c([h()],C.prototype,"_areaId",2),c([h()],C.prototype,"_installationDate",2),c([h()],C.prototype,"_warrantyExpiry",2),c([h()],C.prototype,"_documentationUrl",2),c([h()],C.prototype,"_notes",2),c([h()],C.prototype,"_haDeviceId",2),c([h()],C.prototype,"_parentEntryId",2),c([h()],C.prototype,"_entryId",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",C)});var kt,ki,Ei,Si=w(()=>{"use strict";kt=["sensor","binary_sensor","number","input_number","input_boolean","switch","climate","vacuum","cover","fan","light","water_heater","humidifier","media_player","weather","air_quality","valve","lawn_mower","lock"],ki=["sensor"],Ei=["temperature","humidity","pressure"]});function Vs(){return{entityIds:"",type:"threshold",attribute:"",above:"",below:"",equals:"",notEquals:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:"",onStates:"",carry:{}}}function Ks(a){return{entityIds:(a.entity_ids||(a.entity_id?[a.entity_id]:[])).join(", "),type:a.type||"threshold",attribute:a.attribute||"",above:a.trigger_above?.toString()??"",below:a.trigger_below?.toString()??"",equals:a.trigger_equals?.toString()??"",notEquals:a.trigger_not_equals?.toString()??"",forMinutes:a.trigger_for_minutes?.toString()??"0",targetValue:a.trigger_target_value?.toString()??"",deltaMode:a.trigger_delta_mode||!1,fromState:a.trigger_from_state||"",toState:a.trigger_to_state||"",targetChanges:a.trigger_target_changes?.toString()??"",runtimeHours:a.trigger_runtime_hours?.toString()??"",onStates:(a.trigger_on_states||[]).join(", "),carry:Object.fromEntries(Object.entries(a).filter(([e])=>!Ws.has(e)&&!e.startsWith("_")))}}function Gs(a){let s=a.entityIds.split(",").map(t=>t.trim()).filter(Boolean);if(s.length===0)return null;let e={...a.carry||{},entity_id:s[0],entity_ids:s,type:a.type};if(a.attribute&&(e.attribute=a.attribute),a.type==="threshold"){let t=parseFloat(a.above);isNaN(t)||(e.trigger_above=t);let i=parseFloat(a.below);isNaN(i)||(e.trigger_below=i);let n=parseFloat(a.equals);isNaN(n)||(e.trigger_equals=n);let l=parseFloat(a.notEquals);isNaN(l)||(e.trigger_not_equals=l);let d=parseInt(a.forMinutes,10);isNaN(d)||(e.trigger_for_minutes=d)}else if(a.type==="counter"){let t=parseFloat(a.targetValue);isNaN(t)||(e.trigger_target_value=t),e.trigger_delta_mode=a.deltaMode}else if(a.type==="state_change"){a.fromState&&(e.trigger_from_state=a.fromState),a.toState&&(e.trigger_to_state=a.toState);let t=parseInt(a.targetChanges,10);isNaN(t)||(e.trigger_target_changes=t)}else if(a.type==="runtime"){let t=parseFloat(a.runtimeHours);isNaN(t)||(e.trigger_runtime_hours=t);let i=(a.onStates||"").split(",").map(n=>n.trim()).filter(Boolean);i.length>0&&(e.trigger_on_states=i)}return e}function Ys(a){return Array.from({length:7},(s,e)=>Ce(e,a,"short"))}function Qs(a){let s=new Intl.DateTimeFormat(a||"en",{month:"short"});return Array.from({length:12},(e,t)=>s.format(new Date(2021,t,1)))}var zs,Fs,Us,Et,Ai,Bs,J,Ws,g,St,Ti=w(()=>{"use strict";L();M();j();ft();Pe();Si();se();bt();$t();zs=["cleaning","inspection","replacement","calibration","service","reading","custom"],Fs=["low","normal","high"],Us=["time_based","weekdays","nth_weekday","day_of_month","sensor_based","one_time","manual"],Et=["weekdays","nth_weekday","day_of_month"],Ai=["threshold","counter","state_change","runtime"],Bs=[...Ai,"compound"],J={alpha:"0.3",min:"7",max:"365"};Ws=new Set(["entity_id","entity_ids","type","attribute","trigger_above","trigger_below","trigger_equals","trigger_not_equals","trigger_for_minutes","trigger_target_value","trigger_delta_mode","trigger_from_state","trigger_to_state","trigger_target_changes","trigger_runtime_hours","trigger_on_states"]);g=class g extends S{constructor(){super(...arguments);this.checklistsEnabled=!1;this.scheduleTimeEnabled=!1;this.completionActionsEnabled=!1;this.defaultWarningDays=7;this.parts=[];this._foreignOwners=[];this._open=!1;this._entityPickerFallback=!1;this._pickerProbeStrikes=0;this._loading=!1;this._error="";this._entryId="";this._taskId=null;this._objectChoices=[];this._name="";this._type="custom";this._scheduleType="time_based";this._intervalDays="30";this._intervalUnit="days";this._dueDate="";this._warningDays="7";this._earliestCompletionDays="";this._intervalAnchor="completion";this._weekdays=[];this._nth="1";this._nthWeekday="5";this._domDay="1";this._domLastDay=!1;this._domBusiness=!1;this._calOffset="0";this._seasonMonths=[];this._endsMode="never";this._endsCount="";this._endsUntil="";this._schedulePreview=[];this._schedulePreviewEnded=!1;this._previewSeq=0;this._notes="";this._documentationUrl="";this._customIcon="";t
Line truncated
|
||||||
`),this._requiredCompletion=[...t.required_completion_fields||[]],this._scheduleTime=t.schedule_time||"";let l=t.on_complete_action;if(l&&l.service){this._actionService=l.service;let _=l.target?.entity_id;this._actionTargetEntity=Array.isArray(_)?_[0]||"":_||"",this._actionData=l.data&&typeof l.data=="object"?{...l.data}:{},this._actionDataJsonFallback=""}else this._actionService="",this._actionTargetEntity="",this._actionData={},this._actionDataJsonFallback="";let d=t.quick_complete_defaults;this._qcNotes=d?.notes||"",this._qcCost=d?.cost!=null?String(d.cost):"",this._qcDuration=d?.duration!=null?String(d.duration):"",this._qcFeedback=d?.feedback||"";let p=t.adaptive_config||{};if(this._environmentalEntity=p.environmental_entity||"",this._environmentalAttribute=p.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,this._adaptiveEnabled=!!p.enabled,this._adaptiveAlpha=p.ewa_alpha?.toString()??J.alpha,this._adaptiveMin=p.min_interval_days?.toString()??J.min,this._adaptiveMax=p.max_interval_days?.toString()??J.max,this._adaptiveSeasonal=p.seasonal_enabled!==!1,this._adaptivePrediction=p.sensor_prediction_enabled!==!1,this._adaptiveInitial=this._adaptiveSnapshot(),t.trigger_config){let _=t.trigger_config;this._triggerEntityId=_.entity_id||_.entity_ids&&_.entity_ids[0]||"",this._triggerEntityIds=_.entity_ids||(_.entity_id?[_.entity_id]:[]),this._triggerEntityLogic=_.entity_logic||"any",this._triggerAttribute=_.attribute||"",this._triggerType=_.type||"threshold",this._triggerAbove=_.trigger_above?.toString()||"",this._triggerBelow=_.trigger_below?.toString()||"",this._triggerEquals=_.trigger_equals?.toString()||"",this._triggerNotEquals=_.trigger_not_equals?.toString()||"",this._triggerForMinutes=_.trigger_for_minutes?.toString()||"0",this._triggerCombinator=_.trigger_combinator==="all"?"all":"any",this._triggerTargetValue=_.trigger_target_value?.toString()||"",this._triggerDeltaMode=_.trigger_delta_mode||!1,this._triggerBaselineValue=_.trigger_baseline_value?.toString()||"",this._liveBaselineValue=t.trigger_baseline_value??null,this._autoCompleteOnRecovery=_.auto_complete_on_recovery||!1,this._triggerFromState=_.trigger_from_state||"",this._triggerToState=_.trigger_to_state||"",this._triggerTargetChanges=_.trigger_target_changes?.toString()||"",this._triggerRuntimeHours=_.trigger_runtime_hours?.toString()||"",this._triggerOnStates=(_.trigger_on_states||[]).join(", "),_.type==="compound"?(this._compoundLogic=_.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(_.conditions||[]).map(Fs)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),this._open=!0}_resetFields(){this._name="",this._type="custom",this._scheduleType="time_based",this._intervalDays="30",this._intervalUnit="days",this._dueDate="",this._warningDays=String(this.defaultWarningDays),this._earliestCompletionDays="",this._intervalAnchor="completion",this._weekdays=[],this._nth="1",this._nthWeekday="5",this._domDay="1",this._domLastDay=!1,this._domBusiness=!1,this._calOffset="0",this._seasonMonths=[],this._endsMode="never",this._endsCount="",this._endsUntil="",this._notes="",this._documentationUrl="",this._customIcon="",this._priority="normal",this._labels="",this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._readingUnit="",this._consumesParts={},this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._requiredCompletion=[],this._scheduleTime="",this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._adaptiveEnabled=!1,this._adaptiveAlpha=J.alpha,this._adaptiveMin=J.min,this._adaptiveMax=J.max,this._adaptiveSeasonal=!0,this._adaptivePrediction=!0,this._adaptiveInitial=this._adaptiveSnapshot(),this._actionService="",this._actionTargetEntity="",this._actionData={},this._actionDataJsonFallback="",this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_resetTriggerFields(){this._triggerEntityId="",this._triggerEntityIds=[],this._triggerEntityLogic="any",this._triggerAttribute="",this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="",this._triggerType="threshold",this._triggerAbove="",this._triggerBelow="",this._triggerEquals="",this._triggerNotEquals="",this._triggerForMinutes="0",this._triggerCombinator="any",this._triggerTargetValue="",this._triggerDeltaMode=!1,this._triggerBaselineValue="",this._liveBaselineValue=null,this._autoCompleteOnRecovery=!1,this._triggerFromState="",this._triggerToState="",this._triggerTargetChanges="",this._triggerRuntimeHours="",this._triggerOnStates="",this._comp
Line truncated
|
`),this._phaseDefs=Object.entries(t.phases||{}).map(([_,v])=>{let{name:m,checklist:b,consumes_parts:y,...f}=v;return{id:_,name:v.name||_,checklistText:(v.checklist||[]).join(`
|
||||||
|
`),partId:v.consumes_parts?.[0]?.part_id||"",partQty:v.consumes_parts?.[0]?.quantity!=null?String(v.consumes_parts[0].quantity):"",extraParts:(v.consumes_parts||[]).slice(1),carry:f}}),this._phaseSeq=[...t.phase_sequence||[]],this._requiredCompletion=[...t.required_completion_fields||[]],this._scheduleTime=t.schedule_time||"";let l=t.on_complete_action;if(l&&l.service){this._actionService=l.service;let _=l.target?.entity_id;this._actionTargetEntity=Array.isArray(_)?_[0]||"":_||"",this._actionData=l.data&&typeof l.data=="object"?{...l.data}:{},this._actionDataJsonFallback=""}else this._actionService="",this._actionTargetEntity="",this._actionData={},this._actionDataJsonFallback="";let d=t.quick_complete_defaults;this._qcNotes=d?.notes||"",this._qcCost=d?.cost!=null?String(d.cost):"",this._qcDuration=d?.duration!=null?String(d.duration):"",this._qcFeedback=d?.feedback||"";let p=t.adaptive_config||{};if(this._environmentalEntity=p.environmental_entity||"",this._environmentalAttribute=p.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,this._adaptiveEnabled=!!p.enabled,this._adaptiveAlpha=p.ewa_alpha?.toString()??J.alpha,this._adaptiveMin=p.min_interval_days?.toString()??J.min,this._adaptiveMax=p.max_interval_days?.toString()??J.max,this._adaptiveSeasonal=p.seasonal_enabled!==!1,this._adaptivePrediction=p.sensor_prediction_enabled!==!1,this._adaptiveInitial=this._adaptiveSnapshot(),t.trigger_config){let _=t.trigger_config;this._triggerEntityId=_.entity_id||_.entity_ids&&_.entity_ids[0]||"",this._triggerEntityIds=_.entity_ids||(_.entity_id?[_.entity_id]:[]),this._triggerEntityLogic=_.entity_logic||"any",this._triggerAttribute=_.attribute||"",this._triggerType=_.type||"threshold",this._triggerAbove=_.trigger_above?.toString()||"",this._triggerBelow=_.trigger_below?.toString()||"",this._triggerEquals=_.trigger_equals?.toString()||"",this._triggerNotEquals=_.trigger_not_equals?.toString()||"",this._triggerForMinutes=_.trigger_for_minutes?.toString()||"0",this._triggerCombinator=_.trigger_combinator==="all"?"all":"any",this._triggerTargetValue=_.trigger_target_value?.toString()||"",this._triggerDeltaMode=_.trigger_delta_mode||!1,this._triggerBaselineValue=_.trigger_baseline_value?.toString()||"",this._liveBaselineValue=t.trigger_baseline_value??null,this._autoCompleteOnRecovery=_.auto_complete_on_recovery||!1,this._triggerFromState=_.trigger_from_state||"",this._triggerToState=_.trigger_to_state||"",this._triggerTargetChanges=_.trigger_target_changes?.toString()||"",this._triggerRuntimeHours=_.trigger_runtime_hours?.toString()||"",this._triggerOnStates=(_.trigger_on_states||[]).join(", "),_.type==="compound"?(this._compoundLogic=_.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(_.conditions||[]).map(Ks)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),this._open=!0}_resetFields(){this._name="",this._type="custom",this._scheduleType="time_based",this._intervalDays="30",this._intervalUnit="days",this._dueDate="",this._warningDays=String(this.defaultWarningDays),this._earliestCompletionDays="",this._intervalAnchor="completion",this._weekdays=[],this._nth="1",this._nthWeekday="5",this._domDay="1",this._domLastDay=!1,this._domBusiness=!1,this._calOffset="0",this._seasonMonths=[],this._endsMode="never",this._endsCount="",this._endsUntil="",this._notes="",this._documentationUrl="",this._customIcon="",this._priority="normal",this._labels="",this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._readingUnit="",this._consumesParts={},this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._phaseDefs=[],this._phaseSeq=[],this._requiredCompletion=[],this._scheduleTime="",this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._adaptiveEnabled=!1,this._adaptiveAlpha=J.alpha,this._adaptiveMin=J.min,this._adaptiveMax=J.max,this._adaptiveSeasonal=!0,this._adaptivePrediction=!0,this._adaptiveInitial=this._adaptiveSnapshot(),this._actionService="",this._actionTargetEntity="",this._actionData={},this._actionDataJsonFallback="",this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_resetTriggerFields(){this._triggerEntityId="",this._triggerEntityIds=[],this._triggerEntityLogic="any",this._triggerAttribute="",this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="",this._triggerType="threshold",this._triggerAbove="",this._triggerBelow="",this._triggerEquals="",this._triggerNotEquals="",this._triggerForMinutes="0",this._triggerCombinator="any",this._triggerTargetValue="",t
Line truncated
|
||||||
<details class="ca-section">
|
<details class="ca-section">
|
||||||
<summary>${r("on_complete_action_title",e)}</summary>
|
<summary>${r("on_complete_action_title",e)}</summary>
|
||||||
<p class="field-help">${r("on_complete_action_desc",e)}</p>
|
<p class="field-help">${r("on_complete_action_desc",e)}</p>
|
||||||
@@ -1716,8 +1739,71 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
@input=${d=>{let p=parseFloat(d.target.value);this._consumesParts={...this._consumesParts,[i]:{...l,quantity:Number.isFinite(p)&&p>=.01?p:1}}}}
|
@input=${d=>{let p=parseFloat(d.target.value);this._consumesParts={...this._consumesParts,[i]:{...l,quantity:Number.isFinite(p)&&p>=.01?p:1}}}}
|
||||||
/>`:u}
|
/>`:u}
|
||||||
</div>
|
</div>
|
||||||
`}_toggleRequired(e,t){let i=new Set(this._requiredCompletion);t?i.add(e):i.delete(e),this._requiredCompletion=[...i]}async _save(){if(!this._loading&&this._name.trim()){if(this._adaptiveSnapshot()!==this._adaptiveInitial){let e=parseInt(this._adaptiveMin,10),t=parseInt(this._adaptiveMax,10);if(!isNaN(e)&&!isNaN(t)&&e>t){this._error=`${r("adaptive_min_interval",this._lang)} > ${r("adaptive_max_interval",this._lang)}`;return}}this._loading=!0,this._error="";try{let e={type:this._taskId?"maintenance_supporter/task/update":"maintenance_supporter/task/create",entry_id:this._entryId,name:this._name,task_type:this._type,schedule_type:this._scheduleType,warning_days:Number.isNaN(parseInt(this._warningDays,10))?this.defaultWarningDays:Math.max(0,parseInt(this._warningDays,10))},t=this._earliestCompletionDays.trim();if(e.earliest_completion_days=t===""?null:Math.max(0,parseInt(t,10)||0),this._taskId&&(e.task_id=this._taskId),this._scheduleType==="one_time"?(e.due_date=this._dueDate||null,e.interval_days=null):wt.includes(this._scheduleType)?(e.schedule={...this._buildSchedule(),...this._recurrenceExtras()},e.interval_days=null,this._taskId&&(e.due_date=null)):(this._taskId&&(e.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(e.interval_days=parseInt(this._intervalDays,10),e.interval_unit=this._intervalUnit,e.interval_anchor=this._intervalAnchor,this._scheduleType==="time_based"&&(e.schedule={kind:"interval",...this._recurrenceExtras()})):this._taskId&&(e.interval_days=null,e.interval_anchor="completion")),e.notes=this._notes||null,e.documentation_url=this._documentationUrl||null,e.custom_icon=this._customIcon||null,e.priority=this._priority,e.labels=this._labels.split(",").map(d=>d.trim()).filter(Boolean),e.enabled=this._enabled,e.last_performed=this._lastPerformed||null,e.nfc_tag_id=this._nfcTagId||null,e.reading_unit=this._readingUnit.trim()||null,(this.parts.length||this._foreignOwners.length)&&(e.consumes_parts=Object.values(this._consumesParts).map(d=>d.entry_id?{part_id:d.part_id,quantity:d.quantity,entry_id:d.entry_id}:{part_id:d.part_id,quantity:d.quantity})),e.responsible_user_id=this._responsibleUserId,e.assignee_pool=this._assigneePool,e.required_completion_fields=this._requiredCompletion,e.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let d=this._compoundConditions.map(Us).filter(p=>p!==null);if(d.length>0){let p={type:"compound",compound_logic:this._compoundLogic,conditions:d};this._autoCompleteOnRecovery&&(p.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(p.trigger_combinator="all"),e.trigger_config=p}else this._taskId&&(e.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let d=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],p={entity_id:d[0],entity_ids:d,type:this._triggerType};if(this._triggerAttribute&&(p.attribute=this._triggerAttribute),this._autoCompleteOnRecovery&&(p.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(p.trigger_combinator="all"),d.length>1&&(p.entity_logic=this._triggerEntityLogic),this._triggerType==="threshold"){if(this._triggerAbove){let _=parseFloat(this._triggerAbove);isNaN(_)||(p.trigger_above=_)}if(this._triggerBelow){let _=parseFloat(this._triggerBelow);isNaN(_)||(p.trigger_below=_)}if(this._triggerEquals){let _=parseFloat(this._triggerEquals);isNaN(_)||(p.trigger_equals=_)}if(this._triggerNotEquals){let _=parseFloat(this._triggerNotEquals);isNaN(_)||(p.trigger_not_equals=_)}if(this._triggerForMinutes){let _=parseInt(this._triggerForMinutes,10);isNaN(_)||(p.trigger_for_minutes=_)}}else if(this._triggerType==="counter"){if(this._triggerTargetValue){let _=parseFloat(this._triggerTargetValue);isNaN(_)||(p.trigger_target_value=_)}if(p.trigger_delta_mode=this._triggerDeltaMode,this._triggerDeltaMode&&this._triggerBaselineValue){let _=parseFloat(this._triggerBaselineValue);!isNaN(_)&&_>=0&&(p.trigger_baseline_value=_)}}else if(this._triggerType==="state_change"){if(this._triggerFromState&&(p.trigger_from_state=this._triggerFromState),this._triggerToState&&(p.trigger_to_state=this._triggerToState),this._triggerTargetChanges){let _=parseInt(this._triggerTargetChanges,10);isNaN(_)||(p.trigger_target_changes=_)}if(this._triggerForMinutes){let _=parseInt(this._triggerForMinutes,10);isNaN(_)||(p.trigger_for_minutes=_)}}else if(this._triggerType==="runtime"){if(this._triggerRuntimeHours){let v=parseFloat(this._triggerRuntimeHours);isNaN(v)||(p.trigger_runtime_hours=v)}let _=this._triggerOnStates.split(",").map(v=>v.trim()).filter(Boolean);_.length>0&&(p.trigger_on_states=_)}e.trigger_config=p}else this._taskId&&(e.trigger_config=null);if(this.scheduleTimeEnabled&&this._scheduleType==="time_based"){let d=this._scheduleTime.trim();e.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(d)?d:null}if(this.checklistsEnabled){let d=this._checklistText.split(`
|
`}_toggleRequired(e,t){let i=new Set(this._requiredCompletion);t?i.add(e):i.delete(e),this._requiredCompletion=[...i]}_phaseSlug(e){let t=e.toLowerCase().replace(/[^a-z0-9_-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,24)||"phase",i=t,n=2;for(;this._phaseDefs.some(l=>l.id===i);)i=`${t}-${n++}`;return i}_addPhaseDef(){let e=this._phaseSlug(`phase-${this._phaseDefs.length+1}`);this._phaseDefs=[...this._phaseDefs,{id:e,name:"",checklistText:"",partId:"",partQty:"",extraParts:[],carry:{}}]}_removePhaseDef(e){this._phaseDefs=this._phaseDefs.filter(t=>t.id!==e),this._phaseSeq=this._phaseSeq.filter(t=>t!==e)}_patchPhaseDef(e,t){this._phaseDefs=this._phaseDefs.map(i=>i.id===e?{...i,...t}:i)}_renderPhasesEditor(e){let t=i=>this._phaseDefs.find(n=>n.id===i)?.name||i;return o`
|
||||||
`).map(p=>p.trim()).filter(Boolean).slice(0,100);e.checklist=d.length?d:null}if(this.completionActionsEnabled){let d=this._actionService.trim();if(d&&/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(d)){let m={service:d},y=this._actionTargetEntity.trim();y&&(m.target={entity_id:y});let x=this._buildActionData();Object.keys(x).length>0&&(m.data=x),e.on_complete_action=m}else e.on_complete_action=null;let p={};this._qcNotes.trim()&&(p.notes=this._qcNotes.trim());let _=parseFloat(this._qcCost);!isNaN(_)&&_>=0&&(p.cost=_);let v=parseInt(this._qcDuration,10);!isNaN(v)&&v>=0&&(p.duration=v),this._qcFeedback&&(p.feedback=this._qcFeedback),e.quick_complete_defaults=Object.keys(p).length?p:null}let i=await this.hass.connection.sendMessagePromise(e),n=this._taskId||i?.task_id,l=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;if(n&&this._scheduleType==="sensor_based"&&l)try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:n,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null}),this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute}catch{}if(n&&this._adaptiveSnapshot()!==this._adaptiveInitial){let d=parseFloat(this._adaptiveAlpha),p=parseInt(this._adaptiveMin,10),_=parseInt(this._adaptiveMax,10);try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/set_adaptive",entry_id:this._entryId,task_id:n,enabled:this._adaptiveEnabled,...d>=.1&&d<=.9?{ewa_alpha:d}:{},...!isNaN(p)&&p>=1?{min_interval_days:p}:{},...!isNaN(_)&&_>=1?{max_interval_days:_}:{},seasonal_enabled:this._adaptiveSeasonal,sensor_prediction_enabled:this._adaptivePrediction}),this._adaptiveInitial=this._adaptiveSnapshot()}catch{}}this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}catch(e){this._error=T(e,this._lang,r("save_error",this._lang))}finally{this._loading=!1}}}_close(){this._open=!1,this._pickerProbeTimer!==void 0&&(clearTimeout(this._pickerProbeTimer),this._pickerProbeTimer=void 0),this._pickerProbeStrikes=0}_renderTriggerFields(){if(this._scheduleType!=="sensor_based")return u;let e=this._lang,t=this._triggerType==="compound";return o`
|
<h3>${r("phases_section",e)}</h3>
|
||||||
|
<div class="field-help">${r("phases_hint",e)}</div>
|
||||||
|
${this._phaseDefs.map(i=>o`
|
||||||
|
<div class="phase-def">
|
||||||
|
<div class="phase-def-head">
|
||||||
|
<ms-textfield
|
||||||
|
label="${r("phase_name",e)}"
|
||||||
|
.value=${i.name}
|
||||||
|
@input=${n=>this._patchPhaseDef(i.id,{name:n.target.value})}
|
||||||
|
></ms-textfield>
|
||||||
|
${this.parts.length?o`
|
||||||
|
<select
|
||||||
|
class="phase-part"
|
||||||
|
.value=${i.partId}
|
||||||
|
@change=${n=>this._patchPhaseDef(i.id,{partId:n.target.value})}
|
||||||
|
>
|
||||||
|
<option value="">—</option>
|
||||||
|
${this.parts.map(n=>o`<option value=${n.id} ?selected=${n.id===i.partId}>${n.name}</option>`)}
|
||||||
|
</select>
|
||||||
|
${i.partId?o`
|
||||||
|
<input class="phase-qty" type="number" min="0.01" step="0.01" .value=${i.partQty||"1"}
|
||||||
|
@input=${n=>this._patchPhaseDef(i.id,{partQty:n.target.value})} />
|
||||||
|
`:u}
|
||||||
|
`:u}
|
||||||
|
<mwc-icon-button class="phase-remove" @click=${()=>this._removePhaseDef(i.id)}>
|
||||||
|
<ha-icon icon="mdi:delete-outline"></ha-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
</div>
|
||||||
|
${this.checklistsEnabled?o`
|
||||||
|
<textarea
|
||||||
|
class="checklist-textarea phase-checklist"
|
||||||
|
rows="2"
|
||||||
|
placeholder="${r("checklist_placeholder",e)}"
|
||||||
|
.value=${i.checklistText}
|
||||||
|
@input=${n=>this._patchPhaseDef(i.id,{checklistText:n.target.value})}
|
||||||
|
></textarea>
|
||||||
|
`:u}
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
<ha-button appearance="plain" @click=${this._addPhaseDef}>
|
||||||
|
<ha-icon icon="mdi:plus"></ha-icon> ${r("phase_add",e)}
|
||||||
|
</ha-button>
|
||||||
|
${this._phaseDefs.some(i=>i.name.trim())?o`
|
||||||
|
<div class="phase-seq-label">${r("phase_sequence_label",e)}</div>
|
||||||
|
<div class="phase-seq">
|
||||||
|
${this._phaseSeq.map((i,n)=>o`
|
||||||
|
<span class="phase-chip">
|
||||||
|
${n+1}. ${t(i)}
|
||||||
|
<button class="phase-chip-x" @click=${()=>{this._phaseSeq=this._phaseSeq.filter((l,d)=>d!==n)}}>✕</button>
|
||||||
|
</span>
|
||||||
|
`)}
|
||||||
|
<select
|
||||||
|
class="phase-seq-add"
|
||||||
|
.value=${""}
|
||||||
|
@change=${i=>{let n=i.target.value;n&&(this._phaseSeq=[...this._phaseSeq,n]),i.target.value=""}}
|
||||||
|
>
|
||||||
|
<option value="">+ ${r("phase_sequence_add_step",e)}</option>
|
||||||
|
${this._phaseDefs.filter(i=>i.name.trim()).map(i=>o`<option value=${i.id}>${i.name}</option>`)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
`:u}
|
||||||
|
`}async _save(){if(!this._loading&&this._name.trim()){if(this._adaptiveSnapshot()!==this._adaptiveInitial){let e=parseInt(this._adaptiveMin,10),t=parseInt(this._adaptiveMax,10);if(!isNaN(e)&&!isNaN(t)&&e>t){this._error=`${r("adaptive_min_interval",this._lang)} > ${r("adaptive_max_interval",this._lang)}`;return}}this._loading=!0,this._error="";try{let e={type:this._taskId?"maintenance_supporter/task/update":"maintenance_supporter/task/create",entry_id:this._entryId,name:this._name,task_type:this._type,schedule_type:this._scheduleType,warning_days:Number.isNaN(parseInt(this._warningDays,10))?this.defaultWarningDays:Math.max(0,parseInt(this._warningDays,10))},t=this._earliestCompletionDays.trim();e.earliest_completion_days=t===""?null:Math.max(0,parseInt(t,10)||0),this._taskId&&(e.task_id=this._taskId),this._scheduleType==="one_time"?(e.due_date=this._dueDate||null,e.interval_days=null):Et.includes(this._scheduleType)?(e.schedule={...this._buildSchedule(),...this._recurrenceExtras()},e.interval_days=null,this._taskId&&(e.due_date=null)):(this._taskId&&(e.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(e.interval_days=parseInt(this._intervalDays,10),e.interval_unit=this._intervalUnit,e.interval_anchor=this._intervalAnchor,this._scheduleType==="time_based"&&(e.schedule={kind:"interval",...this._recurrenceExtras()})):this._taskId&&(e.interval_days=null,e.interval_anchor="completion")),e.notes=this._notes||null,e.documentation_url=this._documentationUrl||null,e.custom_icon=this._customIcon||null,e.priority=this._priority,e.labels=this._labels.split(",").map(d=>d.trim()).filter(Boolean),e.enabled=this._enabled,e.last_performed=this._lastPerformed||null,e.nfc_tag_id=this._nfcTagId||null,e.reading_unit=this._readingUnit.trim()||null;{let d={};for(let _ of this._phaseDefs){if(!_.name.trim())continue;let v={..._.carry,name:_.name.trim()},m=_.checklistText.split(`
|
||||||
|
`).map(y=>y.trim()).filter(Boolean);m.length&&(v.checklist=m);let b=[];if(_.partId){let y=parseFloat(_.partQty);b.push({part_id:_.partId,quantity:Number.isFinite(y)&&y>0?y:1})}b.push(..._.extraParts),b.length&&(v.consumes_parts=b),d[_.id]=v}let p=this._phaseSeq.filter(_=>_ in d);e.phases=Object.keys(d).length&&p.length?d:null,e.phase_sequence=e.phases?p:null}if((this.parts.length||this._foreignOwners.length)&&(e.consumes_parts=Object.values(this._consumesParts).map(d=>d.entry_id?{part_id:d.part_id,quantity:d.quantity,entry_id:d.entry_id}:{part_id:d.part_id,quantity:d.quantity})),e.responsible_user_id=this._responsibleUserId,e.assignee_pool=this._assigneePool,e.required_completion_fields=this._requiredCompletion,e.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let d=this._compoundConditions.map(Gs).filter(p=>p!==null);if(d.length>0){let p={type:"compound",compound_logic:this._compoundLogic,conditions:d};this._autoCompleteOnRecovery&&(p.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(p.trigger_combinator="all"),e.trigger_config=p}else this._taskId&&(e.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let d=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],p={entity_id:d[0],entity_ids:d,type:this._triggerType};if(this._triggerAttribute&&(p.attribute=this._triggerAttribute),this._autoCompleteOnRecovery&&(p.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(p.trigger_combinator="all"),d.length>1&&(p.entity_logic=this._triggerEntityLogic),this._triggerType==="threshold"){if(this._triggerAbove){let _=parseFloat(this._triggerAbove);isNaN(_)||(p.trigger_above=_)}if(this._triggerBelow){let _=parseFloat(this._triggerBelow);isNaN(_)||(p.trigger_below=_)}if(this._triggerEquals){let _=parseFloat(this._triggerEquals);isNaN(_)||(p.trigger_equals=_)}if(this._triggerNotEquals){let _=parseFloat(this._triggerNotEquals);isNaN(_)||(p.trigger_not_equals=_)}if(this._triggerForMinutes){let _=parseInt(this._triggerForMinutes,10);isNaN(_)||(p.trigger_for_minutes=_)}}else if(this._triggerType==="counter"){if(this._triggerTargetValue){let _=parseFloat(this._triggerTargetValue);isNaN(_)||(p.trigger_target_value=_)}if(p.trigger_delta_mode=this._triggerDeltaMode,this._triggerDeltaMode&&this._triggerBaselineValue){let _=parseFloat(this._triggerBaselineValue);!isNaN(_)&&_>=0&&(p.trigger_baseline_value=_)}}else if(this._triggerType==="state_change"){if(this._triggerFromState&&(p.trigger_from_state=this._triggerFromState),this._triggerToState&&(p.trigger_to_state=this._triggerToState),this._triggerTargetChanges){let _=parseInt(this._triggerTargetChanges,10);isNaN(_)||(p.trigger_target_changes=_)}if(this._triggerForMinutes){let _=parseInt(this._triggerForMinutes,10);isNaN(_)||(p.trigger_for_minutes=_)}}else if(this._triggerType==="runtime"){if(this._triggerRuntimeHours){let v=parseFloat(this._triggerRuntimeHours);isNaN(v)||(p.trigger_runtime_hours=v)}let _=this._triggerOnStates.split(",").map(v=>v.trim()).filter(Boolean);_.length>0&&(p.trigger_on_states=_)}e.trigger_config=p}else this._taskId&&(e.trigger_config=null);if(this.scheduleTimeEnabled&&this._scheduleType==="time_based"){let d=this._scheduleTime.trim();e.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(d)?d:null}if(this.checklistsEnabled){let d=this._checklistText.split(`
|
||||||
|
`).map(p=>p.trim()).filter(Boolean).slice(0,100);e.checklist=d.length?d:null}if(this.completionActionsEnabled){let d=this._actionService.trim();if(d&&/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(d)){let m={service:d},b=this._actionTargetEntity.trim();b&&(m.target={entity_id:b});let y=this._buildActionData();Object.keys(y).length>0&&(m.data=y),e.on_complete_action=m}else e.on_complete_action=null;let p={};this._qcNotes.trim()&&(p.notes=this._qcNotes.trim());let _=parseFloat(this._qcCost);!isNaN(_)&&_>=0&&(p.cost=_);let v=parseInt(this._qcDuration,10);!isNaN(v)&&v>=0&&(p.duration=v),this._qcFeedback&&(p.feedback=this._qcFeedback),e.quick_complete_defaults=Object.keys(p).length?p:null}let i=await this.hass.connection.sendMessagePromise(e),n=this._taskId||i?.task_id,l=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;if(n&&this._scheduleType==="sensor_based"&&l)try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:n,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null}),this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute}catch{}if(n&&this._adaptiveSnapshot()!==this._adaptiveInitial){let d=parseFloat(this._adaptiveAlpha),p=parseInt(this._adaptiveMin,10),_=parseInt(this._adaptiveMax,10);try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/set_adaptive",entry_id:this._entryId,task_id:n,enabled:this._adaptiveEnabled,...d>=.1&&d<=.9?{ewa_alpha:d}:{},...!isNaN(p)&&p>=1?{min_interval_days:p}:{},...!isNaN(_)&&_>=1?{max_interval_days:_}:{},seasonal_enabled:this._adaptiveSeasonal,sensor_prediction_enabled:this._adaptivePrediction}),this._adaptiveInitial=this._adaptiveSnapshot()}catch{}}this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}catch(e){this._error=T(e,this._lang,r("save_error",this._lang))}finally{this._loading=!1}}}_close(){this._open=!1,this._pickerProbeTimer!==void 0&&(clearTimeout(this._pickerProbeTimer),this._pickerProbeTimer=void 0),this._pickerProbeStrikes=0}_renderTriggerFields(){if(this._scheduleType!=="sensor_based")return u;let e=this._lang,t=this._triggerType==="compound";return o`
|
||||||
<h3>${r("trigger_configuration",e)}</h3>
|
<h3>${r("trigger_configuration",e)}</h3>
|
||||||
<div class="select-row">
|
<div class="select-row">
|
||||||
<label>${r("trigger_type",e)}</label>
|
<label>${r("trigger_type",e)}</label>
|
||||||
@@ -1725,7 +1811,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
.value=${this._triggerType}
|
.value=${this._triggerType}
|
||||||
@change=${i=>this._triggerType=i.target.value}
|
@change=${i=>this._triggerType=i.target.value}
|
||||||
>
|
>
|
||||||
${Ms.map(i=>o`<option value=${i} ?selected=${i===this._triggerType}>${r(i,e)}</option>`)}
|
${Bs.map(i=>o`<option value=${i} ?selected=${i===this._triggerType}>${r(i,e)}</option>`)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
${t?this._renderCompoundEditor():o`
|
${t?this._renderCompoundEditor():o`
|
||||||
@@ -1739,7 +1825,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
<ha-form
|
<ha-form
|
||||||
class="entity-picker-form"
|
class="entity-picker-form"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.schema=${[{name:"trigger_entities",selector:{entity:{multiple:!0,domain:xt}}}]}
|
.schema=${[{name:"trigger_entities",selector:{entity:{multiple:!0,domain:kt}}}]}
|
||||||
.data=${{trigger_entities:this._triggerEntityIds.length>0?this._triggerEntityIds:this._triggerEntityId?[this._triggerEntityId]:[]}}
|
.data=${{trigger_entities:this._triggerEntityIds.length>0?this._triggerEntityIds:this._triggerEntityId?[this._triggerEntityId]:[]}}
|
||||||
.computeLabel=${()=>r("entity_id",e)}
|
.computeLabel=${()=>r("entity_id",e)}
|
||||||
@value-changed=${i=>{let n=(i.detail.value.trigger_entities||[]).filter(Boolean);this._triggerEntityId=n[0]||"",this._triggerEntityIds=n,n[0]?this._fetchEntityAttributes(n[0]):this._fetchEntityAttributes("")}}
|
@value-changed=${i=>{let n=(i.detail.value.trigger_entities||[]).filter(Boolean);this._triggerEntityId=n[0]||"",this._triggerEntityIds=n,n[0]?this._fetchEntityAttributes(n[0]):this._fetchEntityAttributes("")}}
|
||||||
@@ -1787,7 +1873,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
`:u}
|
`:u}
|
||||||
`}_patchCondition(e,t){this._compoundConditions=this._compoundConditions.map((i,n)=>n===e?{...i,...t}:i)}_addCondition(){this._compoundConditions=[...this._compoundConditions,Ds()]}_removeCondition(e){this._compoundConditions=this._compoundConditions.filter((t,i)=>i!==e)}_renderCompoundEditor(){let e=this._lang;return o`
|
`}_patchCondition(e,t){this._compoundConditions=this._compoundConditions.map((i,n)=>n===e?{...i,...t}:i)}_addCondition(){this._compoundConditions=[...this._compoundConditions,Vs()]}_removeCondition(e){this._compoundConditions=this._compoundConditions.filter((t,i)=>i!==e)}_renderCompoundEditor(){let e=this._lang;return o`
|
||||||
<div class="select-row">
|
<div class="select-row">
|
||||||
<label>${r("compound_logic",e)}</label>
|
<label>${r("compound_logic",e)}</label>
|
||||||
<select
|
<select
|
||||||
@@ -1824,7 +1910,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
<ha-form
|
<ha-form
|
||||||
class="entity-picker-form"
|
class="entity-picker-form"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.schema=${[{name:"condition_entities",selector:{entity:{multiple:!0,domain:xt}}}]}
|
.schema=${[{name:"condition_entities",selector:{entity:{multiple:!0,domain:kt}}}]}
|
||||||
.data=${{condition_entities:e.entityIds.split(",").map(l=>l.trim()).filter(Boolean)}}
|
.data=${{condition_entities:e.entityIds.split(",").map(l=>l.trim()).filter(Boolean)}}
|
||||||
.computeLabel=${()=>r("entity_id",i)}
|
.computeLabel=${()=>r("entity_id",i)}
|
||||||
@value-changed=${l=>{let d=(l.detail.value.condition_entities||[]).filter(Boolean);this._patchCondition(t,{entityIds:d.join(", ")})}}
|
@value-changed=${l=>{let d=(l.detail.value.condition_entities||[]).filter(Boolean);this._patchCondition(t,{entityIds:d.join(", ")})}}
|
||||||
@@ -1836,7 +1922,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
.value=${e.type}
|
.value=${e.type}
|
||||||
@change=${l=>this._patchCondition(t,{type:l.target.value})}
|
@change=${l=>this._patchCondition(t,{type:l.target.value})}
|
||||||
>
|
>
|
||||||
${ki.map(l=>o`<option value=${l} ?selected=${l===e.type}>${r(l,i)}</option>`)}
|
${Ai.map(l=>o`<option value=${l} ?selected=${l===e.type}>${r(l,i)}</option>`)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
${this._renderConditionTypeFields(e,t)}
|
${this._renderConditionTypeFields(e,t)}
|
||||||
@@ -1980,12 +2066,12 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
>
|
>
|
||||||
${["days","weeks","months","years"].map(t=>o`<option value=${t} ?selected=${t===this._intervalUnit}>${r("unit_"+t,e)}</option>`)}
|
${["days","weeks","months","years"].map(t=>o`<option value=${t} ?selected=${t===this._intervalUnit}>${r("unit_"+t,e)}</option>`)}
|
||||||
</select>
|
</select>
|
||||||
</div>`}_toggleWeekday(e){this._weekdays=this._weekdays.includes(e)?this._weekdays.filter(t=>t!==e):[...this._weekdays,e]}_previewScheduleDict(){if(this._scheduleType==="one_time")return this._dueDate?{kind:"one_time",due_date:this._dueDate}:null;if(wt.includes(this._scheduleType))return{...this._buildSchedule(),...this._recurrenceExtras()};let e=parseInt(this._intervalDays,10);return this._scheduleType==="manual"||!e||e<=0?null:{kind:"interval",every:e,unit:this._intervalUnit,anchor:this._intervalAnchor,...this._recurrenceExtras()}}updated(e){super.updated?.(e),this._scheduleEntityPickerProbe();for(let t of e.keys())if(g._PREVIEW_RELEVANT.has(String(t))){this._schedulePreviewRefresh();return}}_scheduleEntityPickerProbe(){this._entityPickerFallback||this._pickerProbeTimer!==void 0||!this._open||this._scheduleType!=="sensor_based"||(this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),1500))}_probeEntityPickers(){if(this._pickerProbeTimer=void 0,this._entityPickerFallback||!this._open)return;let e=this.shadowRoot?.querySelector("ha-form.entity-picker-form"),t=(this.shadowRoot?.querySelector(".content")?.offsetHeight??0)>0;if(!e||!t){this._pickerProbeStrikes=0;return}let i=(p,_,v=0)=>{if(!(!p||v>10)){(p.tagName?.toLowerCase()??"")==="ha-entity-picker"&&_.push(p);for(let m of[p.shadowRoot,p])if(m)for(let y of Array.from(m.children??[]))i(y,_,v+1)}},n=[...this.shadowRoot?.querySelectorAll("ha-form.entity-picker-form")??[]],l=[];for(let p of n)i(p,l);let d=l.length===0||l.some(p=>p.offsetHeight===0);if(e.offsetHeight===0||d){if(this._pickerProbeStrikes+=1,this._pickerProbeStrikes>=2){this._entityPickerFallback=!0;return}this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),700)}else this._pickerProbeStrikes=0}_schedulePreviewRefresh(){this._previewTimer&&clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._fetchSchedulePreview()},300)}async _fetchSchedulePreview(){let e=this._open?this._previewScheduleDict():null;if(!e){this._schedulePreview=[],this._schedulePreviewEnded=!1;return}let t=++this._previewSeq;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/schedule/preview",schedule:e,...this._lastPerformed?{last_performed:this._lastPerformed}:{}});if(t!==this._previewSeq)return;this._schedulePreview=i.occurrences||[],this._schedulePreviewEnded=!!i.series_ended}catch{}}_renderSchedulePreview(){if(this._schedulePreview.length===0)return u;let e=this._lang,t=this.scheduleTimeEnabled&&this._scheduleTime?` ${this._scheduleTime}`:"",i=this._schedulePreview.map((l,d)=>{let p=new Date(`${l}T12:00:00`).getDay();return`${Te(p===0?6:p-1,e,"short")} ${Q(l,e)}${d===0?t:""}`}).join(" \xB7 "),n=this._scheduleType==="time_based"&&this._intervalAnchor==="completion"?o`<div class="field-help">${r("schedule_preview_ontime",e)}</div>`:u;return o`
|
</div>`}_toggleWeekday(e){this._weekdays=this._weekdays.includes(e)?this._weekdays.filter(t=>t!==e):[...this._weekdays,e]}_previewScheduleDict(){if(this._scheduleType==="one_time")return this._dueDate?{kind:"one_time",due_date:this._dueDate}:null;if(Et.includes(this._scheduleType))return{...this._buildSchedule(),...this._recurrenceExtras()};let e=parseInt(this._intervalDays,10);return this._scheduleType==="manual"||!e||e<=0?null:{kind:"interval",every:e,unit:this._intervalUnit,anchor:this._intervalAnchor,...this._recurrenceExtras()}}updated(e){super.updated?.(e),this._scheduleEntityPickerProbe();for(let t of e.keys())if(g._PREVIEW_RELEVANT.has(String(t))){this._schedulePreviewRefresh();return}}_scheduleEntityPickerProbe(){this._entityPickerFallback||this._pickerProbeTimer!==void 0||!this._open||this._scheduleType!=="sensor_based"||(this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),1500))}_probeEntityPickers(){if(this._pickerProbeTimer=void 0,this._entityPickerFallback||!this._open)return;let e=this.shadowRoot?.querySelector("ha-form.entity-picker-form"),t=(this.shadowRoot?.querySelector(".content")?.offsetHeight??0)>0;if(!e||!t){this._pickerProbeStrikes=0;return}let i=(p,_,v=0)=>{if(!(!p||v>10)){(p.tagName?.toLowerCase()??"")==="ha-entity-picker"&&_.push(p);for(let m of[p.shadowRoot,p])if(m)for(let b of Array.from(m.children??[]))i(b,_,v+1)}},n=[...this.shadowRoot?.querySelectorAll("ha-form.entity-picker-form")??[]],l=[];for(let p of n)i(p,l);let d=l.length===0||l.some(p=>p.offsetHeight===0);if(e.offsetHeight===0||d){if(this._pickerProbeStrikes+=1,this._pickerProbeStrikes>=2){this._entityPickerFallback=!0;return}this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),700)}else this._pickerProbeStrikes=0}_schedulePreviewRefresh(){this._previewTimer&&clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._fetchSchedulePreview()},300)}async _fetchSchedulePreview(){let e=this._open?this._previewScheduleDict():null;if(!e){this._schedulePreview=[],this._schedulePreviewEnded=!1;return}let t=++this._previewSeq;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/schedule/preview",schedule:e,...this._lastPerformed?{last_performed:this._lastPerformed}:{}});if(t!==this._previewSeq)return;this._schedulePreview=i.occurrences||[],this._schedulePreviewEnded=!!i.series_ended}catch{}}_renderSchedulePreview(){if(this._schedulePreview.length===0)return u;let e=this._lang,t=this.scheduleTimeEnabled&&this._scheduleTime?` ${this._scheduleTime}`:"",i=this._schedulePreview.map((l,d)=>{let p=new Date(`${l}T12:00:00`).getDay();return`${Ce(p===0?6:p-1,e,"short")} ${Q(l,e)}${d===0?t:""}`}).join(" \xB7 "),n=this._scheduleType==="time_based"&&this._intervalAnchor==="completion"?o`<div class="field-help">${r("schedule_preview_ontime",e)}</div>`:u;return o`
|
||||||
<div class="trigger-live-hint schedule-preview">
|
<div class="trigger-live-hint schedule-preview">
|
||||||
${r("schedule_preview_title",e)}: ${i}${this._schedulePreviewEnded?o` <span class="field-help">${r("schedule_preview_ends",e)}</span>`:u}
|
${r("schedule_preview_title",e)}: ${i}${this._schedulePreviewEnded?o` <span class="field-help">${r("schedule_preview_ends",e)}</span>`:u}
|
||||||
${n}
|
${n}
|
||||||
</div>
|
</div>
|
||||||
`}_buildSchedule(){let e=i=>{let n=parseInt(this._calOffset,10)||0;return n&&(i.offset=Math.max(-15,Math.min(n,15))),i};if(this._scheduleType==="weekdays")return e({kind:"weekdays",weekdays:[...this._weekdays].sort((i,n)=>i-n)});if(this._scheduleType==="nth_weekday")return e({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});let t={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(t.business=!0),e(t)}_recurrenceExtras(){let e={};if(this._seasonMonths.length&&(e.season_months=[...this._seasonMonths].sort((t,i)=>t-i)),this._endsMode==="count"){let t=parseInt(this._endsCount,10);t>=1&&(e.ends={count:t})}else this._endsMode==="until"&&this._endsUntil&&(e.ends={until:this._endsUntil});return e}_toggleSeasonMonth(e){this._seasonMonths=this._seasonMonths.includes(e)?this._seasonMonths.filter(t=>t!==e):[...this._seasonMonths,e]}_renderRecurrenceExtras(){let e=this._lang;if(!(this._scheduleType==="time_based"||wt.includes(this._scheduleType)))return u;let i=Vs(e);return o`
|
`}_buildSchedule(){let e=i=>{let n=parseInt(this._calOffset,10)||0;return n&&(i.offset=Math.max(-15,Math.min(n,15))),i};if(this._scheduleType==="weekdays")return e({kind:"weekdays",weekdays:[...this._weekdays].sort((i,n)=>i-n)});if(this._scheduleType==="nth_weekday")return e({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});let t={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(t.business=!0),e(t)}_recurrenceExtras(){let e={};if(this._seasonMonths.length&&(e.season_months=[...this._seasonMonths].sort((t,i)=>t-i)),this._endsMode==="count"){let t=parseInt(this._endsCount,10);t>=1&&(e.ends={count:t})}else this._endsMode==="until"&&this._endsUntil&&(e.ends={until:this._endsUntil});return e}_toggleSeasonMonth(e){this._seasonMonths=this._seasonMonths.includes(e)?this._seasonMonths.filter(t=>t!==e):[...this._seasonMonths,e]}_renderRecurrenceExtras(){let e=this._lang;if(!(this._scheduleType==="time_based"||Et.includes(this._scheduleType)))return u;let i=Qs(e);return o`
|
||||||
<label class="field-label">${r("season_window_label",e)}</label>
|
<label class="field-label">${r("season_window_label",e)}</label>
|
||||||
<div class="field-help">${r("season_window_hint",e)}</div>
|
<div class="field-help">${r("season_window_hint",e)}</div>
|
||||||
<div class="weekday-chips season-chips">
|
<div class="weekday-chips season-chips">
|
||||||
@@ -2020,7 +2106,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
.value=${this._endsUntil}
|
.value=${this._endsUntil}
|
||||||
@input=${n=>this._endsUntil=n.target.value}
|
@input=${n=>this._endsUntil=n.target.value}
|
||||||
></ms-textfield>`:u}
|
></ms-textfield>`:u}
|
||||||
`}_renderCalendarFields(){let e=this._lang,t=Bs(e);if(this._scheduleType==="weekdays")return o`
|
`}_renderCalendarFields(){let e=this._lang,t=Ys(e);if(this._scheduleType==="weekdays")return o`
|
||||||
<label class="field-label">${r("recurrence_on_days",e)}</label>
|
<label class="field-label">${r("recurrence_on_days",e)}</label>
|
||||||
<div class="weekday-chips">
|
<div class="weekday-chips">
|
||||||
${t.map((i,n)=>o`
|
${t.map((i,n)=>o`
|
||||||
@@ -2072,7 +2158,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
max="15"
|
max="15"
|
||||||
.value=${this._calOffset}
|
.value=${this._calOffset}
|
||||||
@input=${t=>this._calOffset=t.target.value}
|
@input=${t=>this._calOffset=t.target.value}
|
||||||
></ms-textfield>`}_renderTriggerLiveHint(){if(this._triggerType==="compound")return u;let e=this._triggerEntityId||this._triggerEntityIds[0];if(!e||!this.hass?.states)return u;let t=this.hass.states[e];if(!t)return u;let i=this._lang,n=t.attributes?.unit_of_measurement,l=typeof n=="string"&&n?` ${n}`:"",d=this._triggerAttribute?t.attributes?.[this._triggerAttribute]:t.state,p=typeof d=="number"?d:parseFloat(String(d)),_=d!=="unknown"&&d!=="unavailable"&&d!=null&&!isNaN(p),v=y=>Number.isInteger(y)?String(y):String(Math.round(y*10)/10),m=[];if(this._triggerType==="threshold"){let y=parseFloat(this._triggerAbove),x=parseFloat(this._triggerBelow);if(isNaN(y)&&isNaN(x))return u;_&&m.push(r("trigger_hint_now",i).replace("{value}",v(p)+l)),isNaN(y)||m.push(r("trigger_hint_above",i).replace("{target}",v(y)+l)),isNaN(x)||m.push(r("trigger_hint_below",i).replace("{target}",v(x)+l))}else if(this._triggerType==="counter"){let y=parseFloat(this._triggerTargetValue);if(isNaN(y))return u;this._triggerDeltaMode?this._taskId?m.push(r("trigger_hint_counter_delta_edit",i).replace("{target}",v(y)+l)):_?m.push(r("trigger_hint_counter_delta",i).replace("{value}",v(p)+l).replace("{due}",v(p+y)+l).replace("{target}",v(y)+l)):m.push(r("trigger_hint_counter_delta_edit",i).replace("{target}",v(y)+l)):(_&&m.push(r("trigger_hint_now",i).replace("{value}",v(p)+l)),m.push(r("trigger_hint_counter_abs",i).replace("{target}",v(y)+l)))}else if(this._triggerType==="runtime"){let y=parseFloat(this._triggerRuntimeHours);if(isNaN(y))return u;m.push(r("trigger_hint_runtime",i).replace("{hours}",v(y))),m.push(r("trigger_hint_state_now",i).replace("{value}",String(t.state)))}else if(this._triggerType==="state_change"){let y=parseInt(this._triggerTargetChanges,10)||1,x=this._triggerToState.trim();m.push((x?r("trigger_hint_state_change_to",i).replace("{state}",x):r("trigger_hint_state_change",i)).replace("{count}",String(y))),m.push(r("trigger_hint_state_now",i).replace("{value}",String(t.state)))}return m.length?o`<div class="trigger-live-hint">${m.join(" ")}</div>`:u}_renderTriggerTypeFields(){let e=this._lang;return this._triggerType==="threshold"?o`
|
></ms-textfield>`}_renderTriggerLiveHint(){if(this._triggerType==="compound")return u;let e=this._triggerEntityId||this._triggerEntityIds[0];if(!e||!this.hass?.states)return u;let t=this.hass.states[e];if(!t)return u;let i=this._lang,n=t.attributes?.unit_of_measurement,l=typeof n=="string"&&n?` ${n}`:"",d=this._triggerAttribute?t.attributes?.[this._triggerAttribute]:t.state,p=typeof d=="number"?d:parseFloat(String(d)),_=d!=="unknown"&&d!=="unavailable"&&d!=null&&!isNaN(p),v=b=>Number.isInteger(b)?String(b):String(Math.round(b*10)/10),m=[];if(this._triggerType==="threshold"){let b=parseFloat(this._triggerAbove),y=parseFloat(this._triggerBelow);if(isNaN(b)&&isNaN(y))return u;_&&m.push(r("trigger_hint_now",i).replace("{value}",v(p)+l)),isNaN(b)||m.push(r("trigger_hint_above",i).replace("{target}",v(b)+l)),isNaN(y)||m.push(r("trigger_hint_below",i).replace("{target}",v(y)+l))}else if(this._triggerType==="counter"){let b=parseFloat(this._triggerTargetValue);if(isNaN(b))return u;this._triggerDeltaMode?this._taskId?m.push(r("trigger_hint_counter_delta_edit",i).replace("{target}",v(b)+l)):_?m.push(r("trigger_hint_counter_delta",i).replace("{value}",v(p)+l).replace("{due}",v(p+b)+l).replace("{target}",v(b)+l)):m.push(r("trigger_hint_counter_delta_edit",i).replace("{target}",v(b)+l)):(_&&m.push(r("trigger_hint_now",i).replace("{value}",v(p)+l)),m.push(r("trigger_hint_counter_abs",i).replace("{target}",v(b)+l)))}else if(this._triggerType==="runtime"){let b=parseFloat(this._triggerRuntimeHours);if(isNaN(b))return u;m.push(r("trigger_hint_runtime",i).replace("{hours}",v(b))),m.push(r("trigger_hint_state_now",i).replace("{value}",String(t.state)))}else if(this._triggerType==="state_change"){let b=parseInt(this._triggerTargetChanges,10)||1,y=this._triggerToState.trim();m.push((y?r("trigger_hint_state_change_to",i).replace("{state}",y):r("trigger_hint_state_change",i)).replace("{count}",String(b))),m.push(r("trigger_hint_state_now",i).replace("{value}",String(t.state)))}return m.length?o`<div class="trigger-live-hint">${m.join(" ")}</div>`:u}_renderTriggerTypeFields(){let e=this._lang;return this._triggerType==="threshold"?o`
|
||||||
<ms-textfield
|
<ms-textfield
|
||||||
label="${r("trigger_above",e)}"
|
label="${r("trigger_above",e)}"
|
||||||
type="number"
|
type="number"
|
||||||
@@ -2196,7 +2282,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
.value=${this._type}
|
.value=${this._type}
|
||||||
@change=${i=>this._type=i.target.value}
|
@change=${i=>this._type=i.target.value}
|
||||||
>
|
>
|
||||||
${Hs.map(i=>o`<option value=${i} ?selected=${i===this._type}>${r(i,e)}</option>`)}
|
${zs.map(i=>o`<option value=${i} ?selected=${i===this._type}>${r(i,e)}</option>`)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
${this._type==="reading"?o`
|
${this._type==="reading"?o`
|
||||||
@@ -2230,7 +2316,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
.value=${this._priority}
|
.value=${this._priority}
|
||||||
@change=${i=>this._priority=i.target.value}
|
@change=${i=>this._priority=i.target.value}
|
||||||
>
|
>
|
||||||
${qs.map(i=>o`<option value=${i} ?selected=${i===this._priority}>${r("priority_"+i,e)}</option>`)}
|
${Fs.map(i=>o`<option value=${i} ?selected=${i===this._priority}>${r("priority_"+i,e)}</option>`)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -2249,7 +2335,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
.value=${this._scheduleType}
|
.value=${this._scheduleType}
|
||||||
@change=${i=>this._scheduleType=i.target.value}
|
@change=${i=>this._scheduleType=i.target.value}
|
||||||
>
|
>
|
||||||
${Os.map(i=>o`<option value=${i} ?selected=${i===this._scheduleType}>${r(i,e)}</option>`)}
|
${Us.map(i=>o`<option value=${i} ?selected=${i===this._scheduleType}>${r(i,e)}</option>`)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
${this._scheduleType==="time_based"?o`
|
${this._scheduleType==="time_based"?o`
|
||||||
@@ -2318,16 +2404,17 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
></textarea>
|
></textarea>
|
||||||
<div class="field-help">${r("checklist_help",e)}</div>
|
<div class="field-help">${r("checklist_help",e)}</div>
|
||||||
`:u}
|
`:u}
|
||||||
|
${this._renderPhasesEditor(e)}
|
||||||
<h3>${r("require_on_completion",e)}</h3>
|
<h3>${r("require_on_completion",e)}</h3>
|
||||||
<div class="required-completion">
|
<div class="required-completion">
|
||||||
${fi.map(i=>o`
|
${yi.map(i=>o`
|
||||||
<label class="req-option">
|
<label class="req-option">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
.checked=${this._requiredCompletion.includes(i)}
|
.checked=${this._requiredCompletion.includes(i)}
|
||||||
@change=${n=>this._toggleRequired(i,n.target.checked)}
|
@change=${n=>this._toggleRequired(i,n.target.checked)}
|
||||||
/>
|
/>
|
||||||
<span>${r(We[i],e)}</span>
|
<span>${r(Ge[i],e)}</span>
|
||||||
</label>
|
</label>
|
||||||
`)}
|
`)}
|
||||||
</div>
|
</div>
|
||||||
@@ -2386,7 +2473,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
<ha-form
|
<ha-form
|
||||||
class="entity-picker-form"
|
class="entity-picker-form"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.schema=${[{name:"environmental_entity",selector:{entity:{domain:xi,device_class:wi}}}]}
|
.schema=${[{name:"environmental_entity",selector:{entity:{domain:ki,device_class:Ei}}}]}
|
||||||
.data=${{environmental_entity:this._environmentalEntity}}
|
.data=${{environmental_entity:this._environmentalEntity}}
|
||||||
.computeLabel=${()=>r("environmental_entity_optional",e)}
|
.computeLabel=${()=>r("environmental_entity_optional",e)}
|
||||||
.computeHelper=${()=>r("environmental_entity_helper",e)}
|
.computeHelper=${()=>r("environmental_entity_helper",e)}
|
||||||
@@ -2573,6 +2660,82 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
}
|
}
|
||||||
|
.phase-def {
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
.phase-def-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.phase-def-head ms-textfield {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.phase-part {
|
||||||
|
max-width: 160px;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--card-background-color);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.phase-qty {
|
||||||
|
width: 64px;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--card-background-color);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.phase-checklist {
|
||||||
|
min-height: 56px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
.phase-remove {
|
||||||
|
--mdc-icon-button-size: 36px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.phase-seq-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.phase-seq {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.phase-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--secondary-background-color);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.phase-chip-x {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.phase-seq-add {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: 1px dashed var(--divider-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
.consumes-check {
|
.consumes-check {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -2750,7 +2913,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
`,c([b({attribute:!1})],g.prototype,"hass",2),c([b({type:Boolean,attribute:"checklists-enabled"})],g.prototype,"checklistsEnabled",2),c([b({type:Boolean,attribute:"schedule-time-enabled"})],g.prototype,"scheduleTimeEnabled",2),c([b({type:Boolean,attribute:"completion-actions-enabled"})],g.prototype,"completionActionsEnabled",2),c([b({type:Number,attribute:"default-warning-days"})],g.prototype,"defaultWarningDays",2),c([h()],g.prototype,"parts",2),c([h()],g.prototype,"_foreignOwners",2),c([h()],g.prototype,"_open",2),c([h()],g.prototype,"_entityPickerFallback",2),c([h()],g.prototype,"_loading",2),c([h()],g.prototype,"_error",2),c([h()],g.prototype,"_entryId",2),c([h()],g.prototype,"_taskId",2),c([h()],g.prototype,"_objectChoices",2),c([h()],g.prototype,"_name",2),c([h()],g.prototype,"_type",2),c([h()],g.prototype,"_scheduleType",2),c([h()],g.prototype,"_intervalDays",2),c([h()],g.prototype,"_intervalUnit",2),c([h()],g.prototype,"_dueDate",2),c([h()],g.prototype,"_warningDays",2),c([h()],g.prototype,"_earliestCompletionDays",2),c([h()],g.prototype,"_intervalAnchor",2),c([h()],g.prototype,"_weekdays",2),c([h()],g.prototype,"_nth",2),c([h()],g.prototype,"_nthWeekday",2),c([h()],g.prototype,"_domDay",2),c([h()],g.prototype,"_domLastDay",2),c([h()],g.prototype,"_domBusiness",2),c([h()],g.prototype,"_calOffset",2),c([h()],g.prototype,"_seasonMonths",2),c([h()],g.prototype,"_endsMode",2),c([h()],g.prototype,"_endsCount",2),c([h()],g.prototype,"_endsUntil",2),c([h()],g.prototype,"_schedulePreview",2),c([h()],g.prototype,"_schedulePreviewEnded",2),c([h()],g.prototype,"_notes",2),c([h()],g.prototype,"_documentationUrl",2),c([h()],g.prototype,"_customIcon",2),c([h()],g.prototype,"_priority",2),c([h()],g.prototype,"_labels",2),c([h()],g.prototype,"_enabled",2),c([h()],g.prototype,"_triggerEntityId",2),c([h()],g.prototype,"_triggerEntityIds",2),c([h()],g.prototype,"_triggerEntityLogic",2),c([h()],g.prototype,"_triggerAttribute",2),c([h()],g.prototype,"_triggerType",2),c([h()],g.prototype,"_triggerAbove",2),c([h()],g.prototype,"_triggerBelow",2),c([h()],g.prototype,"_triggerEquals",2),c([h()],g.prototype,"_triggerNotEquals",2),c([h()],g.prototype,"_triggerForMinutes",2),c([h()],g.prototype,"_triggerCombinator",2),c([h()],g.prototype,"_triggerTargetValue",2),c([h()],g.prototype,"_triggerDeltaMode",2),c([h()],g.prototype,"_triggerBaselineValue",2),c([h()],g.prototype,"_liveBaselineValue",2),c([h()],g.prototype,"_autoCompleteOnRecovery",2),c([h()],g.prototype,"_triggerFromState",2),c([h()],g.prototype,"_triggerToState",2),c([h()],g.prototype,"_triggerTargetChanges",2),c([h()],g.prototype,"_triggerRuntimeHours",2),c([h()],g.prototype,"_triggerOnStates",2),c([h()],g.prototype,"_compoundLogic",2),c([h()],g.prototype,"_compoundConditions",2),c([h()],g.prototype,"_suggestedAttributes",2),c([h()],g.prototype,"_availableAttributes",2),c([h()],g.prototype,"_entityDomain",2),c([h()],g.prototype,"_lastPerformed",2),c([h()],g.prototype,"_nfcTagId",2),c([h()],g.prototype,"_readingUnit",2),c([h()],g.prototype,"_consumesParts",2),c([h()],g.prototype,"_partsLoadFailed",2),c([h()],g.prototype,"_availableTags",2),c([h()],g.prototype,"_responsibleUserId",2),c([h()],g.prototype,"_assigneePool",2),c([h()],g.prototype,"_rotationStrategy",2),c([h()],g.prototype,"_availableUsers",2),c([h()],g.prototype,"_checklistText",2),c([h()],g.prototype,"_requiredCompletion",2),c([h()],g.prototype,"_scheduleTime",2),c([h()],g.prototype,"_actionService",2),c([h()],g.prototype,"_actionTargetEntity",2),c([h()],g.prototype,"_actionData",2),c([h()],g.prototype,"_actionDataJsonFallback",2),c([h()],g.prototype,"_actionTesting",2),c([h()],g.prototype,"_actionTestResult",2),c([h()],g.prototype,"_actionTestError",2),c([h()],g.prototype,"_qcNotes",2),c([h()],g.prototype,"_qcCost",2),c([h()],g.prototype,"_qcDuration",2),c([h()],g.prototype,"_qcFeedback",2),c([h()],g.prototype,"_environmentalEntity",2),c([h()],g.prototype,"_environmentalAttribute",2),c([h()],g.prototype,"_adaptiveEnabled",2),c([h()],g.prototype,"_adaptiveAlpha",2),c([h()],g.prototype,"_adaptiveMin",2),c([h()],g.prototype,"_adaptiveMax",2),c([h()],g.prototype,"_adaptiveSeasonal",2),c([h()],g.prototype,"_adaptivePrediction",2),c([h()],g.prototype,"_conditionAttrOptions",2);$t=g;customElements.get("maintenance-task-dialog")||customElements.define("maintenance-task-dialog",$t)});var U,Si=w(()=>{"use strict";P();O();R();ie();U=class extends S{constructor(){super(...arguments);this._open=!1;this._saving=!1;this._error="";this._draft=null;this._originalSnapshot=null;this._partOptions=null;this._partQty={};this._partQtyOriginal=""}get _lang(){return j(this.hass)}openEdit(e){this._draft={...e},this._originalSnapshot={...e},this._error="",this._open=!0,this._partOptions=null,this._partQty={},this._partQtyOriginal="",this._loadPartOptions()}async _loadPartOptions(){let e=this._draft;if(e)try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/parts/overview"}),i=[];for(let l of t.part
Line truncated
|
`,c([x({attribute:!1})],g.prototype,"hass",2),c([x({type:Boolean,attribute:"checklists-enabled"})],g.prototype,"checklistsEnabled",2),c([x({type:Boolean,attribute:"schedule-time-enabled"})],g.prototype,"scheduleTimeEnabled",2),c([x({type:Boolean,attribute:"completion-actions-enabled"})],g.prototype,"completionActionsEnabled",2),c([x({type:Number,attribute:"default-warning-days"})],g.prototype,"defaultWarningDays",2),c([h()],g.prototype,"parts",2),c([h()],g.prototype,"_foreignOwners",2),c([h()],g.prototype,"_open",2),c([h()],g.prototype,"_entityPickerFallback",2),c([h()],g.prototype,"_loading",2),c([h()],g.prototype,"_error",2),c([h()],g.prototype,"_entryId",2),c([h()],g.prototype,"_taskId",2),c([h()],g.prototype,"_objectChoices",2),c([h()],g.prototype,"_name",2),c([h()],g.prototype,"_type",2),c([h()],g.prototype,"_scheduleType",2),c([h()],g.prototype,"_intervalDays",2),c([h()],g.prototype,"_intervalUnit",2),c([h()],g.prototype,"_dueDate",2),c([h()],g.prototype,"_warningDays",2),c([h()],g.prototype,"_earliestCompletionDays",2),c([h()],g.prototype,"_intervalAnchor",2),c([h()],g.prototype,"_weekdays",2),c([h()],g.prototype,"_nth",2),c([h()],g.prototype,"_nthWeekday",2),c([h()],g.prototype,"_domDay",2),c([h()],g.prototype,"_domLastDay",2),c([h()],g.prototype,"_domBusiness",2),c([h()],g.prototype,"_calOffset",2),c([h()],g.prototype,"_seasonMonths",2),c([h()],g.prototype,"_endsMode",2),c([h()],g.prototype,"_endsCount",2),c([h()],g.prototype,"_endsUntil",2),c([h()],g.prototype,"_schedulePreview",2),c([h()],g.prototype,"_schedulePreviewEnded",2),c([h()],g.prototype,"_notes",2),c([h()],g.prototype,"_documentationUrl",2),c([h()],g.prototype,"_customIcon",2),c([h()],g.prototype,"_priority",2),c([h()],g.prototype,"_labels",2),c([h()],g.prototype,"_enabled",2),c([h()],g.prototype,"_triggerEntityId",2),c([h()],g.prototype,"_triggerEntityIds",2),c([h()],g.prototype,"_triggerEntityLogic",2),c([h()],g.prototype,"_triggerAttribute",2),c([h()],g.prototype,"_triggerType",2),c([h()],g.prototype,"_triggerAbove",2),c([h()],g.prototype,"_triggerBelow",2),c([h()],g.prototype,"_triggerEquals",2),c([h()],g.prototype,"_triggerNotEquals",2),c([h()],g.prototype,"_triggerForMinutes",2),c([h()],g.prototype,"_triggerCombinator",2),c([h()],g.prototype,"_triggerTargetValue",2),c([h()],g.prototype,"_triggerDeltaMode",2),c([h()],g.prototype,"_triggerBaselineValue",2),c([h()],g.prototype,"_liveBaselineValue",2),c([h()],g.prototype,"_autoCompleteOnRecovery",2),c([h()],g.prototype,"_triggerFromState",2),c([h()],g.prototype,"_triggerToState",2),c([h()],g.prototype,"_triggerTargetChanges",2),c([h()],g.prototype,"_triggerRuntimeHours",2),c([h()],g.prototype,"_triggerOnStates",2),c([h()],g.prototype,"_compoundLogic",2),c([h()],g.prototype,"_compoundConditions",2),c([h()],g.prototype,"_suggestedAttributes",2),c([h()],g.prototype,"_availableAttributes",2),c([h()],g.prototype,"_entityDomain",2),c([h()],g.prototype,"_lastPerformed",2),c([h()],g.prototype,"_nfcTagId",2),c([h()],g.prototype,"_readingUnit",2),c([h()],g.prototype,"_consumesParts",2),c([h()],g.prototype,"_partsLoadFailed",2),c([h()],g.prototype,"_availableTags",2),c([h()],g.prototype,"_responsibleUserId",2),c([h()],g.prototype,"_assigneePool",2),c([h()],g.prototype,"_rotationStrategy",2),c([h()],g.prototype,"_availableUsers",2),c([h()],g.prototype,"_checklistText",2),c([h()],g.prototype,"_phaseDefs",2),c([h()],g.prototype,"_phaseSeq",2),c([h()],g.prototype,"_requiredCompletion",2),c([h()],g.prototype,"_scheduleTime",2),c([h()],g.prototype,"_actionService",2),c([h()],g.prototype,"_actionTargetEntity",2),c([h()],g.prototype,"_actionData",2),c([h()],g.prototype,"_actionDataJsonFallback",2),c([h()],g.prototype,"_actionTesting",2),c([h()],g.prototype,"_actionTestResult",2),c([h()],g.prototype,"_actionTestError",2),c([h()],g.prototype,"_qcNotes",2),c([h()],g.prototype,"_qcCost",2),c([h()],g.prototype,"_qcDuration",2),c([h()],g.prototype,"_qcFeedback",2),c([h()],g.prototype,"_environmentalEntity",2),c([h()],g.prototype,"_environmentalAttribute",2),c([h()],g.prototype,"_adaptiveEnabled",2),c([h()],g.prototype,"_adaptiveAlpha",2),c([h()],g.prototype,"_adaptiveMin",2),c([h()],g.prototype,"_adaptiveMax",2),c([h()],g.prototype,"_adaptiveSeasonal",2),c([h()],g.prototype,"_adaptivePrediction",2),c([h()],g.prototype,"_conditionAttrOptions",2);St=g;customElements.get("maintenance-task-dialog")||customElements.define("maintenance-task-dialog",St)});var U,Ci=w(()=>{"use strict";L();M();j();se();U=class extends S{constructor(){super(...arguments);this._open=!1;this._saving=!1;this._error="";this._draft=null;this._originalSnapshot=null;this._partOptions=null;this._partQty={};this._partQtyOriginal=""}get _lang(){return R(this.hass)}openEdit(e){this._draft={...e},this._originalSnapshot={...e},this._error="",this._open=!0,this._partOptions=null,this._partQty={},this._partQtyOriginal="",this._loadPartOptions()}async _loadPartOptions(){let e=this._draft;if(e)try{let t=await this.hass.connection.sendMessagePromise(
Line truncated
|
||||||
<div class="backdrop" @click=${this.close}></div>
|
<div class="backdrop" @click=${this.close}></div>
|
||||||
<div class="dialog" role="dialog" aria-modal="true">
|
<div class="dialog" role="dialog" aria-modal="true">
|
||||||
<h2>${r("history_edit_title",e)||"Edit history entry"}</h2>
|
<h2>${r("history_edit_title",e)||"Edit history entry"}</h2>
|
||||||
@@ -2888,7 +3051,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
.part-row-edit input[type="checkbox"] { width: auto; }
|
.part-row-edit input[type="checkbox"] { width: auto; }
|
||||||
.part-label { flex: 1; color: var(--primary-text-color); }
|
.part-label { flex: 1; color: var(--primary-text-color); }
|
||||||
.part-qty { width: 76px; }
|
.part-qty { width: 76px; }
|
||||||
`,c([b({attribute:!1})],U.prototype,"hass",2),c([h()],U.prototype,"_open",2),c([h()],U.prototype,"_saving",2),c([h()],U.prototype,"_error",2),c([h()],U.prototype,"_draft",2),c([h()],U.prototype,"_partOptions",2),c([h()],U.prototype,"_partQty",2);customElements.get("maintenance-history-edit-dialog")||customElements.define("maintenance-history-edit-dialog",U)});function ge(a){return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function Ai(a){return!a.startsWith("data:image/svg+xml,")&&!a.startsWith("data:image/png;base64,")?"":ge(a)}function Ws(a){return a.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var M,Ti=w(()=>{"use strict";P();O();R();ut();M=class extends S{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,t){this._entryId=e,this._taskId=null,this._objectName=t,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}openForTask(e,t,i,n){this._entryId=e,this._taskId=t,this._objectName=i,this._taskName=n,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 t={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(t.task_id=this._taskId);let i=[this.hass.connection.sendMessagePromise({...t,action:"view"})];this._taskId&&i.push(this.hass.connection.sendMessagePromise({...t,action:"complete"}));let n=await Promise.all(i);if(e!==this._generateSeq)return;this._viewResult=n[0],n.length>1&&(this._completeResult=n[1])}catch(t){if(e!==this._generateSeq)return;let i=t?.code,n=t?.message;this._error=i==="no_url"||typeof n=="string"&&n.includes("No Home Assistant URL")?r("qr_error_no_url",this.lang):r("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,t=e.label.task_name?`${e.label.object_name} \u2014 ${e.label.task_name}`:e.label.object_name,i=[e.label.manufacturer,e.label.model].filter(Boolean).join(" "),n=window.open("","_blank","width=600,height=500");if(!n)return;let l=this.lang||"en",d=ge(t),p=ge(i),_=!!this._completeResult,v=ge(r("qr_action_view",l)),m=ge(r("qr_action_complete",l));n.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
|
`,c([x({attribute:!1})],U.prototype,"hass",2),c([h()],U.prototype,"_open",2),c([h()],U.prototype,"_saving",2),c([h()],U.prototype,"_error",2),c([h()],U.prototype,"_draft",2),c([h()],U.prototype,"_partOptions",2),c([h()],U.prototype,"_partQty",2);customElements.get("maintenance-history-edit-dialog")||customElements.define("maintenance-history-edit-dialog",U)});function me(a){return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function Ii(a){return!a.startsWith("data:image/svg+xml,")&&!a.startsWith("data:image/png;base64,")?"":me(a)}function Js(a){return a.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var D,Pi=w(()=>{"use strict";L();M();j();gt();D=class extends S{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,t){this._entryId=e,this._taskId=null,this._objectName=t,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}openForTask(e,t,i,n){this._entryId=e,this._taskId=t,this._objectName=i,this._taskName=n,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 t={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(t.task_id=this._taskId);let i=[this.hass.connection.sendMessagePromise({...t,action:"view"})];this._taskId&&i.push(this.hass.connection.sendMessagePromise({...t,action:"complete"}));let n=await Promise.all(i);if(e!==this._generateSeq)return;this._viewResult=n[0],n.length>1&&(this._completeResult=n[1])}catch(t){if(e!==this._generateSeq)return;let i=t?.code,n=t?.message;this._error=i==="no_url"||typeof n=="string"&&n.includes("No Home Assistant URL")?r("qr_error_no_url",this.lang):r("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,t=e.label.task_name?`${e.label.object_name} \u2014 ${e.label.task_name}`:e.label.object_name,i=[e.label.manufacturer,e.label.model].filter(Boolean).join(" "),n=window.open("","_blank","width=600,height=500");if(!n)return;let l=this.lang||"en",d=me(t),p=me(i),_=!!this._completeResult,v=me(r("qr_action_view",l)),m=me(r("qr_action_complete",l));n.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
<meta name="color-scheme" content="light">
|
<meta name="color-scheme" content="light">
|
||||||
<title>${d}</title>
|
<title>${d}</title>
|
||||||
<style>
|
<style>
|
||||||
@@ -2910,17 +3073,17 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
|||||||
${p?`<div class="sub">${p}</div>`:""}
|
${p?`<div class="sub">${p}</div>`:""}
|
||||||
<div class="qr-row">
|
<div class="qr-row">
|
||||||
<div class="qr-col">
|
<div class="qr-col">
|
||||||
<img src="${Ai(this._viewResult.svg_data_uri)}" alt="QR Info" />
|
<img src="${Ii(this._viewResult.svg_data_uri)}" alt="QR Info" />
|
||||||
<div class="qr-label">${v}</div>
|
<div class="qr-label">${v}</div>
|
||||||
</div>
|
</div>
|
||||||
${_?`<div class="qr-col">
|
${_?`<div class="qr-col">
|
||||||
<img src="${Ai(this._completeResult.svg_data_uri)}" alt="QR Complete" />
|
<img src="${Ii(this._completeResult.svg_data_uri)}" alt="QR Complete" />
|
||||||
<div class="qr-label">${m}</div>
|
<div class="qr-label">${m}</div>
|
||||||
</div>`:""}
|
</div>`:""}
|
||||||
</div>
|
</div>
|
||||||
<div class="url">${ge(this._viewResult.url)}</div>
|
<div class="url">${me(this._viewResult.url)}</div>
|
||||||
<script>setTimeout(()=>window.print(),300)<\/script>
|
<script>setTimeout(()=>window.print(),300)<\/script>
|
||||||
</body></html>`),n.document.close()}_downloadSvg(e,t){let i=decodeURIComponent(e.svg_data_uri.replace("data:image/svg+xml,","")),n=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;gi(i,`qr-${Ws(n)}-${t}.svg`,"image/svg+xml")}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._error="",this._loading=!1}render(){if(!this._open)return o``;let e=this.lang||this.hass?.language||"en",t=this._taskName?`${r("qr_code",e)}: ${this._objectName} \u2014 ${this._taskName}`:`${r("qr_code",e)}: ${this._objectName}`,i=!!this._viewResult;return o`
|
</body></html>`),n.document.close()}_downloadSvg(e,t){let i=decodeURIComponent(e.svg_data_uri.replace("data:image/svg+xml,","")),n=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;vi(i,`qr-${Js(n)}-${t}.svg`,"image/svg+xml")}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._error="",this._loading=!1}render(){if(!this._open)return o``;let e=this.lang||this.hass?.language||"en",t=this._taskName?`${r("qr_code",e)}: ${this._objectName} \u2014 ${this._taskName}`:`${r("qr_code",e)}: ${this._objectName}`,i=!!this._viewResult;return o`
|
||||||
<ha-dialog open @closed=${this._close}>
|
<ha-dialog open @closed=${this._close}>
|
||||||
<div class="dialog-title">${t}</div>
|
<div class="dialog-title">${t}</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -2981,7 +3144,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
</ha-button>
|
</ha-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`}};M.styles=A`
|
`}};D.styles=A`
|
||||||
.dialog-title {
|
.dialog-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -3099,26 +3262,26 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
color: var(--text-primary-color, #fff);
|
color: var(--text-primary-color, #fff);
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
`,c([b({attribute:!1})],M.prototype,"hass",2),c([b()],M.prototype,"lang",2),c([h()],M.prototype,"_open",2),c([h()],M.prototype,"_loading",2),c([h()],M.prototype,"_error",2),c([h()],M.prototype,"_viewResult",2),c([h()],M.prototype,"_completeResult",2),c([h()],M.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",M)});var Ci=w(()=>{"use strict"});function Ii(a){let s=a.getFullYear(),e=String(a.getMonth()+1).padStart(2,"0"),t=String(a.getDate()).padStart(2,"0");return`${s}-${e}-${t}`}var Li=w(()=>{"use strict";Ci()});function Pi(a,s){let e=a.interval_analysis,t=e?.weibull_beta,i=e?.weibull_eta;if(t==null||i==null||i<=0)return u;let n=a.interval_days??0,l=a.suggested_interval??n;return o`
|
`,c([x({attribute:!1})],D.prototype,"hass",2),c([x()],D.prototype,"lang",2),c([h()],D.prototype,"_open",2),c([h()],D.prototype,"_loading",2),c([h()],D.prototype,"_error",2),c([h()],D.prototype,"_viewResult",2),c([h()],D.prototype,"_completeResult",2),c([h()],D.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",D)});var Li=w(()=>{"use strict"});function Ri(a){let s=a.getFullYear(),e=String(a.getMonth()+1).padStart(2,"0"),t=String(a.getDate()).padStart(2,"0");return`${s}-${e}-${t}`}var ji=w(()=>{"use strict";Li()});function Ni(a,s){let e=a.interval_analysis,t=e?.weibull_beta,i=e?.weibull_eta;if(t==null||i==null||i<=0)return u;let n=a.interval_days??0,l=a.suggested_interval??n;return o`
|
||||||
<div class="weibull-section">
|
<div class="weibull-section">
|
||||||
<div class="weibull-title">
|
<div class="weibull-title">
|
||||||
<ha-svg-icon aria-hidden="true" path="M3,14L3.5,14.07L8.07,9.5C7.89,8.85 8.06,8.11 8.59,7.59C9.37,6.8 10.63,6.8 11.41,7.59C11.94,8.11 12.11,8.85 11.93,9.5L14.5,12.07L15,12C15.18,12 15.35,12 15.5,12.07L19.07,8.5C19,8.35 19,8.18 19,8A2,2 0 0,1 21,6A2,2 0 0,1 23,8A2,2 0 0,1 21,10C20.82,10 20.65,10 20.5,9.93L16.93,13.5C17,13.65 17,13.82 17,14A2,2 0 0,1 15,16A2,2 0 0,1 13,14L13.07,13.5L10.5,10.93C10.18,11 9.82,11 9.5,10.93L4.93,15.5L5,16A2,2 0 0,1 3,18A2,2 0 0,1 1,16A2,2 0 0,1 3,14Z"></ha-svg-icon>
|
<ha-svg-icon aria-hidden="true" path="M3,14L3.5,14.07L8.07,9.5C7.89,8.85 8.06,8.11 8.59,7.59C9.37,6.8 10.63,6.8 11.41,7.59C11.94,8.11 12.11,8.85 11.93,9.5L14.5,12.07L15,12C15.18,12 15.35,12 15.5,12.07L19.07,8.5C19,8.35 19,8.18 19,8A2,2 0 0,1 21,6A2,2 0 0,1 23,8A2,2 0 0,1 21,10C20.82,10 20.65,10 20.5,9.93L16.93,13.5C17,13.65 17,13.82 17,14A2,2 0 0,1 15,16A2,2 0 0,1 13,14L13.07,13.5L10.5,10.93C10.18,11 9.82,11 9.5,10.93L4.93,15.5L5,16A2,2 0 0,1 3,18A2,2 0 0,1 1,16A2,2 0 0,1 3,14Z"></ha-svg-icon>
|
||||||
${r("weibull_reliability_curve",s)}
|
${r("weibull_reliability_curve",s)}
|
||||||
${Ks(t,s)}
|
${Zs(t,s)}
|
||||||
</div>
|
</div>
|
||||||
${Gs(t,i,n,l,s)}
|
${Xs(t,i,n,l,s)}
|
||||||
${Ys(e,s)}
|
${er(e,s)}
|
||||||
${e?.confidence_interval_low!=null?Qs(e,a,s):u}
|
${e?.confidence_interval_low!=null?tr(e,a,s):u}
|
||||||
</div>
|
</div>
|
||||||
`}function Ks(a,s){let e,t,i;return a<.8?(e="early_failures",t="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z",i="beta_early_failures"):a<=1.2?(e="random_failures",t="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M13,17H11V15H13V17M13,13H11V7H13V13Z",i="beta_random_failures"):a<=3.5?(e="wear_out",t="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M12,6A6,6 0 0,1 18,12H12V6Z",i="beta_wear_out"):(e="highly_predictable",t="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z",i="beta_highly_predictable"),o`
|
`}function Zs(a,s){let e,t,i;return a<.8?(e="early_failures",t="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z",i="beta_early_failures"):a<=1.2?(e="random_failures",t="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M13,17H11V15H13V17M13,13H11V7H13V13Z",i="beta_random_failures"):a<=3.5?(e="wear_out",t="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M12,6A6,6 0 0,1 18,12H12V6Z",i="beta_wear_out"):(e="highly_predictable",t="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z",i="beta_highly_predictable"),o`
|
||||||
<span class="beta-badge ${e}">
|
<span class="beta-badge ${e}">
|
||||||
<ha-svg-icon path="${t}"></ha-svg-icon>
|
<ha-svg-icon path="${t}"></ha-svg-icon>
|
||||||
${r(i,s)} (\u03B2=${a.toFixed(2)})
|
${r(i,s)} (\u03B2=${a.toFixed(2)})
|
||||||
</span>
|
</span>
|
||||||
`}function Gs(a,s,e,t,i){let x=Math.max(e,t,s,1)*1.3,f=50,$=[];for(let F=0;F<=f;F++){let B=F/f*x,Qi=1-Math.exp(-Math.pow(B/s,a)),Ji=32+B/x*260,Zi=136-Qi*128;$.push([Ji,Zi])}let z=$.map(([F,B])=>`${F.toFixed(1)},${B.toFixed(1)}`).join(" "),L="M32,136 "+$.map(([F,B])=>`L${F.toFixed(1)},${B.toFixed(1)}`).join(" ")+` L${$[f][0].toFixed(1)},136 Z`,E=32+e/x*260,N=1-Math.exp(-Math.pow(e/s,a)),Le=136-N*128,Gi=((1-N)*100).toFixed(0),At=32+t/x*260,Yi=[0,.25,.5,.75,1];return o`
|
`}function Xs(a,s,e,t,i){let y=Math.max(e,t,s,1)*1.3,f=50,$=[];for(let F=0;F<=f;F++){let B=F/f*y,Xi=1-Math.exp(-Math.pow(B/s,a)),es=32+B/y*260,ts=136-Xi*128;$.push([es,ts])}let H=$.map(([F,B])=>`${F.toFixed(1)},${B.toFixed(1)}`).join(" "),P="M32,136 "+$.map(([F,B])=>`L${F.toFixed(1)},${B.toFixed(1)}`).join(" ")+` L${$[f][0].toFixed(1)},136 Z`,E=32+e/y*260,N=1-Math.exp(-Math.pow(e/s,a)),Re=136-N*128,Ji=((1-N)*100).toFixed(0),It=32+t/y*260,Zi=[0,.25,.5,.75,1];return o`
|
||||||
<div class="weibull-chart">
|
<div class="weibull-chart">
|
||||||
<svg viewBox="0 0 ${300} ${160}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${r("chart_weibull",i)}">
|
<svg viewBox="0 0 ${300} ${160}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${r("chart_weibull",i)}">
|
||||||
${Yi.map(F=>{let B=136-F*128;return pe`
|
${Zi.map(F=>{let B=136-F*128;return he`
|
||||||
<line x1="${32}" y1="${B.toFixed(1)}" x2="${292}" y2="${B.toFixed(1)}"
|
<line x1="${32}" y1="${B.toFixed(1)}" x2="${292}" y2="${B.toFixed(1)}"
|
||||||
stroke="var(--divider-color)" stroke-width="0.5" stroke-dasharray="${F===.5?"4,3":u}" />
|
stroke="var(--divider-color)" stroke-width="0.5" stroke-dasharray="${F===.5?"4,3":u}" />
|
||||||
<text x="${28}" y="${(B+3).toFixed(1)}" fill="var(--secondary-text-color)"
|
<text x="${28}" y="${(B+3).toFixed(1)}" fill="var(--secondary-text-color)"
|
||||||
@@ -3126,24 +3289,24 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
`})}
|
`})}
|
||||||
|
|
||||||
<text x="${32}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">0</text>
|
<text x="${32}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">0</text>
|
||||||
<text x="${324/2}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">${Math.round(x/2)}</text>
|
<text x="${324/2}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">${Math.round(y/2)}</text>
|
||||||
<text x="${292}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">${Math.round(x)}</text>
|
<text x="${292}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">${Math.round(y)}</text>
|
||||||
|
|
||||||
<path d="${L}" fill="var(--primary-color, #03a9f4)" opacity="0.08" />
|
<path d="${P}" fill="var(--primary-color, #03a9f4)" opacity="0.08" />
|
||||||
<polyline points="${z}" fill="none"
|
<polyline points="${H}" fill="none"
|
||||||
stroke="var(--primary-color, #03a9f4)" stroke-width="2" />
|
stroke="var(--primary-color, #03a9f4)" stroke-width="2" />
|
||||||
|
|
||||||
${e>0?pe`
|
${e>0?he`
|
||||||
<line x1="${E.toFixed(1)}" y1="${8}" x2="${E.toFixed(1)}" y2="${136 .toFixed(1)}"
|
<line x1="${E.toFixed(1)}" y1="${8}" x2="${E.toFixed(1)}" y2="${136 .toFixed(1)}"
|
||||||
stroke="var(--primary-color, #03a9f4)" stroke-width="1.5" stroke-dasharray="4,3" />
|
stroke="var(--primary-color, #03a9f4)" stroke-width="1.5" stroke-dasharray="4,3" />
|
||||||
<circle cx="${E.toFixed(1)}" cy="${Le.toFixed(1)}" r="3"
|
<circle cx="${E.toFixed(1)}" cy="${Re.toFixed(1)}" r="3"
|
||||||
fill="var(--primary-color, #03a9f4)" />
|
fill="var(--primary-color, #03a9f4)" />
|
||||||
<text x="${(E+4).toFixed(1)}" y="${(Le-6).toFixed(1)}" fill="var(--primary-color, #03a9f4)"
|
<text x="${(E+4).toFixed(1)}" y="${(Re-6).toFixed(1)}" fill="var(--primary-color, #03a9f4)"
|
||||||
font-size="9" font-weight="600">R=${Gi}%</text>
|
font-size="9" font-weight="600">R=${Ji}%</text>
|
||||||
`:u}
|
`:u}
|
||||||
|
|
||||||
${t>0&&t!==e?pe`
|
${t>0&&t!==e?he`
|
||||||
<line x1="${At.toFixed(1)}" y1="${8}" x2="${At.toFixed(1)}" y2="${136 .toFixed(1)}"
|
<line x1="${It.toFixed(1)}" y1="${8}" x2="${It.toFixed(1)}" y2="${136 .toFixed(1)}"
|
||||||
stroke="var(--success-color, #4caf50)" stroke-width="1.5" stroke-dasharray="4,3" />
|
stroke="var(--success-color, #4caf50)" stroke-width="1.5" stroke-dasharray="4,3" />
|
||||||
`:u}
|
`:u}
|
||||||
|
|
||||||
@@ -3158,7 +3321,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
${e>0?o`<span class="legend-item"><span class="legend-swatch" style="background:var(--primary-color, #03a9f4); opacity:0.5"></span> ${r("current_interval_marker",i)}</span>`:u}
|
${e>0?o`<span class="legend-item"><span class="legend-swatch" style="background:var(--primary-color, #03a9f4); opacity:0.5"></span> ${r("current_interval_marker",i)}</span>`:u}
|
||||||
${t>0&&t!==e?o`<span class="legend-item"><span class="legend-swatch" style="background:var(--success-color, #4caf50)"></span> ${r("recommended_marker",i)}</span>`:u}
|
${t>0&&t!==e?o`<span class="legend-item"><span class="legend-swatch" style="background:var(--success-color, #4caf50)"></span> ${r("recommended_marker",i)}</span>`:u}
|
||||||
</div>
|
</div>
|
||||||
`}function Ys(a,s){return o`
|
`}function er(a,s){return o`
|
||||||
<div class="weibull-info-row">
|
<div class="weibull-info-row">
|
||||||
<div class="weibull-info-item">
|
<div class="weibull-info-item">
|
||||||
<span>${r("characteristic_life",s)}</span>
|
<span>${r("characteristic_life",s)}</span>
|
||||||
@@ -3171,22 +3334,22 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
</div>
|
</div>
|
||||||
`:u}
|
`:u}
|
||||||
</div>
|
</div>
|
||||||
`}function Qs(a,s,e){let t=a.confidence_interval_low,i=a.confidence_interval_high,n=s.suggested_interval??s.interval_days??0,l=s.interval_days??0,d=Math.max(0,t-5),_=i+5-d,v=(t-d)/_*100,m=(i-t)/_*100,y=(n-d)/_*100,x=l>0?(l-d)/_*100:-1;return o`
|
`}function tr(a,s,e){let t=a.confidence_interval_low,i=a.confidence_interval_high,n=s.suggested_interval??s.interval_days??0,l=s.interval_days??0,d=Math.max(0,t-5),_=i+5-d,v=(t-d)/_*100,m=(i-t)/_*100,b=(n-d)/_*100,y=l>0?(l-d)/_*100:-1;return o`
|
||||||
<div class="confidence-range">
|
<div class="confidence-range">
|
||||||
<div class="confidence-range-title">
|
<div class="confidence-range-title">
|
||||||
${r("confidence_interval",e)}: ${n} ${r("days",e)} (${t}\u2013${i})
|
${r("confidence_interval",e)}: ${n} ${r("days",e)} (${t}\u2013${i})
|
||||||
</div>
|
</div>
|
||||||
<div class="confidence-bar">
|
<div class="confidence-bar">
|
||||||
<div class="confidence-fill" style="left:${v.toFixed(1)}%;width:${m.toFixed(1)}%"></div>
|
<div class="confidence-fill" style="left:${v.toFixed(1)}%;width:${m.toFixed(1)}%"></div>
|
||||||
${x>=0?o`<div class="confidence-marker current" style="left:${x.toFixed(1)}%"></div>`:u}
|
${y>=0?o`<div class="confidence-marker current" style="left:${y.toFixed(1)}%"></div>`:u}
|
||||||
<div class="confidence-marker recommended" style="left:${y.toFixed(1)}%"></div>
|
<div class="confidence-marker recommended" style="left:${b.toFixed(1)}%"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="confidence-labels">
|
<div class="confidence-labels">
|
||||||
<span class="confidence-text low">${r("confidence_conservative",e)} (${t}${r("days",e).charAt(0)})</span>
|
<span class="confidence-text low">${r("confidence_conservative",e)} (${t}${r("days",e).charAt(0)})</span>
|
||||||
<span class="confidence-text high">${r("confidence_aggressive",e)} (${i}${r("days",e).charAt(0)})</span>
|
<span class="confidence-text high">${r("confidence_aggressive",e)} (${i}${r("days",e).charAt(0)})</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`}var ji=w(()=>{"use strict";P();R()});function Ri(a,s,e){let t=a.degradation_trend!=null&&a.degradation_trend!=="insufficient_data",i=a.days_until_threshold!=null,n=a.environmental_factor!=null&&a.environmental_factor!==1;if(!t&&!i&&!n)return u;let l=a.degradation_trend==="rising"?"M16,6L18.29,8.29L13.41,13.17L9.41,9.17L2,16.59L3.41,18L9.41,12L13.41,16L19.71,9.71L22,12V6H16Z":a.degradation_trend==="falling"?"M16,18L18.29,15.71L13.41,10.83L9.41,14.83L2,7.41L3.41,6L9.41,12L13.41,8L19.71,14.29L22,12V18H16Z":"M22,12L18,8V11H3V13H18V16L22,12Z";return o`
|
`}var qi=w(()=>{"use strict";L();j()});function Hi(a,s,e){let t=a.degradation_trend!=null&&a.degradation_trend!=="insufficient_data",i=a.days_until_threshold!=null,n=a.environmental_factor!=null&&a.environmental_factor!==1;if(!t&&!i&&!n)return u;let l=a.degradation_trend==="rising"?"M16,6L18.29,8.29L13.41,13.17L9.41,9.17L2,16.59L3.41,18L9.41,12L13.41,16L19.71,9.71L22,12V6H16Z":a.degradation_trend==="falling"?"M16,18L18.29,15.71L13.41,10.83L9.41,14.83L2,7.41L3.41,6L9.41,12L13.41,8L19.71,14.29L22,12V18H16Z":"M22,12L18,8V11H3V13H18V16L22,12Z";return o`
|
||||||
<div class="prediction-section">
|
<div class="prediction-section">
|
||||||
${a.sensor_prediction_urgency?o`
|
${a.sensor_prediction_urgency?o`
|
||||||
<div class="prediction-urgency-banner">
|
<div class="prediction-urgency-banner">
|
||||||
@@ -3221,12 +3384,12 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
<ha-svg-icon path="M15,13V5A3,3 0 0,0 12,2A3,3 0 0,0 9,5V13A5,5 0 0,0 7,17A5,5 0 0,0 12,22A5,5 0 0,0 17,17A5,5 0 0,0 15,13M12,4A1,1 0 0,1 13,5V8H11V5A1,1 0 0,1 12,4Z"></ha-svg-icon>
|
<ha-svg-icon path="M15,13V5A3,3 0 0,0 12,2A3,3 0 0,0 9,5V13A5,5 0 0,0 7,17A5,5 0 0,0 12,22A5,5 0 0,0 17,17A5,5 0 0,0 15,13M12,4A1,1 0 0,1 13,5V8H11V5A1,1 0 0,1 12,4Z"></ha-svg-icon>
|
||||||
<span class="prediction-label">${r("environmental_adjustment",s)}</span>
|
<span class="prediction-label">${r("environmental_adjustment",s)}</span>
|
||||||
<span class="prediction-value">${a.environmental_factor.toFixed(2)}x</span>
|
<span class="prediction-value">${a.environmental_factor.toFixed(2)}x</span>
|
||||||
${a.environmental_entity?o`<span class="prediction-entity entity-link" @click=${d=>ui(d,a.environmental_entity)}>${a.environmental_entity}</span>`:u}
|
${a.environmental_entity?o`<span class="prediction-entity entity-link" @click=${d=>mi(d,a.environmental_entity)}>${a.environmental_entity}</span>`:u}
|
||||||
</div>
|
</div>
|
||||||
`:u}
|
`:u}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`}var Ni=w(()=>{"use strict";P();R()});function Hi(a,s,e,t){let i=Math.max(a||1,s);return o`
|
`}var Oi=w(()=>{"use strict";L();j()});function Mi(a,s,e,t){let i=Math.max(a||1,s);return o`
|
||||||
<div class="interval-comparison">
|
<div class="interval-comparison">
|
||||||
<div class="interval-bar">
|
<div class="interval-bar">
|
||||||
<div class="interval-label">
|
<div class="interval-label">
|
||||||
@@ -3244,7 +3407,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
style="width: ${Math.min(s/i*100,100)}%"></div>
|
style="width: ${Math.min(s/i*100,100)}%"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`}var qi=w(()=>{"use strict";P();R()});function Mi(a,s,e){if(!e.seasonal||!a.seasonal_factor||a.seasonal_factor===1)return u;let t=Oi.map(d=>r(d,s)),i=new Date().getMonth(),n=a.seasonal_factors||a.interval_analysis?.seasonal_factors||null,l=n&&n.length===12?n:t.map((d,p)=>{let _=a.seasonal_factor||1,v=Math.sin((p-6)*Math.PI/6)*.3;return Math.max(.7,Math.min(1.3,_+v))});return o`
|
`}var Di=w(()=>{"use strict";L();j()});function Fi(a,s,e){if(!e.seasonal||!a.seasonal_factor||a.seasonal_factor===1)return u;let t=zi.map(d=>r(d,s)),i=new Date().getMonth(),n=a.seasonal_factors||a.interval_analysis?.seasonal_factors||null,l=n&&n.length===12?n:t.map((d,p)=>{let _=a.seasonal_factor||1,v=Math.sin((p-6)*Math.PI/6)*.3;return Math.max(.7,Math.min(1.3,_+v))});return o`
|
||||||
<div class="seasonal-card-compact">
|
<div class="seasonal-card-compact">
|
||||||
<h4>${r("seasonal_awareness",s)}</h4>
|
<h4>${r("seasonal_awareness",s)}</h4>
|
||||||
<div class="seasonal-mini-chart">
|
<div class="seasonal-mini-chart">
|
||||||
@@ -3261,7 +3424,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
<span class="legend-item"><span class="dot high"></span> ${r("longer",s)||"L\xE4nger"}</span>
|
<span class="legend-item"><span class="dot high"></span> ${r("longer",s)||"L\xE4nger"}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`}function Di(a,s){return Js(a,s)}function Js(a,s){let e=a.seasonal_factors??a.interval_analysis?.seasonal_factors;if(!e||e.length!==12)return u;let t=a.interval_analysis?.seasonal_reason,i=new Date().getMonth(),n=300,l=100,d=8,_=l-d-4,v=Math.max(...e,1.5),m=n/12,y=m*.65,x=d+_-1/v*_;return o`
|
`}function Ui(a,s){return ir(a,s)}function ir(a,s){let e=a.seasonal_factors??a.interval_analysis?.seasonal_factors;if(!e||e.length!==12)return u;let t=a.interval_analysis?.seasonal_reason,i=new Date().getMonth(),n=300,l=100,d=8,_=l-d-4,v=Math.max(...e,1.5),m=n/12,b=m*.65,y=d+_-1/v*_;return o`
|
||||||
<div class="seasonal-chart">
|
<div class="seasonal-chart">
|
||||||
<div class="seasonal-chart-title">
|
<div class="seasonal-chart-title">
|
||||||
<ha-svg-icon aria-hidden="true" path="M17.75 4.09L15.22 6.03L16.13 9.09L13.5 7.28L10.87 9.09L11.78 6.03L9.25 4.09L12.44 4L13.5 1L14.56 4L17.75 4.09M21.25 11L19.61 12.25L20.2 14.23L18.5 13.06L16.8 14.23L17.39 12.25L15.75 11L17.81 10.95L18.5 9L19.19 10.95L21.25 11M18.97 15.95C19.8 15.87 20.69 17.05 20.16 17.8C19.84 18.25 19.5 18.67 19.08 19.07C15.17 23 8.84 23 4.94 19.07C1.03 15.17 1.03 8.83 4.94 4.93C5.34 4.53 5.76 4.17 6.21 3.85C6.96 3.32 8.14 4.21 8.06 5.04C7.79 7.9 8.75 10.87 10.95 13.06C13.14 15.26 16.1 16.22 18.97 15.95Z"></ha-svg-icon>
|
<ha-svg-icon aria-hidden="true" path="M17.75 4.09L15.22 6.03L16.13 9.09L13.5 7.28L10.87 9.09L11.78 6.03L9.25 4.09L12.44 4L13.5 1L14.56 4L17.75 4.09M21.25 11L19.61 12.25L20.2 14.23L18.5 13.06L16.8 14.23L17.39 12.25L15.75 11L17.81 10.95L18.5 9L19.19 10.95L21.25 11M18.97 15.95C19.8 15.87 20.69 17.05 20.16 17.8C19.84 18.25 19.5 18.67 19.08 19.07C15.17 23 8.84 23 4.94 19.07C1.03 15.17 1.03 8.83 4.94 4.93C5.34 4.53 5.76 4.17 6.21 3.85C6.96 3.32 8.14 4.21 8.06 5.04C7.79 7.9 8.75 10.87 10.95 13.06C13.14 15.26 16.1 16.22 18.97 15.95Z"></ha-svg-icon>
|
||||||
@@ -3269,22 +3432,22 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
${t?o`<span class="source-tag">${t==="learned"?r("seasonal_learned",s):r("seasonal_manual",s)}</span>`:u}
|
${t?o`<span class="source-tag">${t==="learned"?r("seasonal_learned",s):r("seasonal_manual",s)}</span>`:u}
|
||||||
</div>
|
</div>
|
||||||
<svg viewBox="0 0 ${n} ${l}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${r("chart_seasonal",s)}">
|
<svg viewBox="0 0 ${n} ${l}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${r("chart_seasonal",s)}">
|
||||||
<line x1="0" y1="${x.toFixed(1)}" x2="${n}" y2="${x.toFixed(1)}"
|
<line x1="0" y1="${y.toFixed(1)}" x2="${n}" y2="${y.toFixed(1)}"
|
||||||
stroke="var(--divider-color)" stroke-width="1" stroke-dasharray="4,3" />
|
stroke="var(--divider-color)" stroke-width="1" stroke-dasharray="4,3" />
|
||||||
${e.map((f,$)=>{let z=f/v*_,L=$*m+(m-y)/2,E=d+_-z,N=$===i,Le=f<1?"var(--success-color, #4caf50)":f>1?"var(--warning-color, #ff9800)":"var(--secondary-text-color)";return pe`
|
${e.map((f,$)=>{let H=f/v*_,P=$*m+(m-b)/2,E=d+_-H,N=$===i,Re=f<1?"var(--success-color, #4caf50)":f>1?"var(--warning-color, #ff9800)":"var(--secondary-text-color)";return he`
|
||||||
<rect x="${L.toFixed(1)}" y="${E.toFixed(1)}"
|
<rect x="${P.toFixed(1)}" y="${E.toFixed(1)}"
|
||||||
width="${y.toFixed(1)}" height="${z.toFixed(1)}"
|
width="${b.toFixed(1)}" height="${H.toFixed(1)}"
|
||||||
fill="${Le}" opacity="${N?1:.5}" rx="2" />
|
fill="${Re}" opacity="${N?1:.5}" rx="2" />
|
||||||
`})}
|
`})}
|
||||||
</svg>
|
</svg>
|
||||||
<div class="seasonal-labels">
|
<div class="seasonal-labels">
|
||||||
${Oi.map((f,$)=>o`<span class="seasonal-label ${$===i?"active-month":""}">${r(f,s)}</span>`)}
|
${zi.map((f,$)=>o`<span class="seasonal-label ${$===i?"active-month":""}">${r(f,s)}</span>`)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`}var Oi,zi=w(()=>{"use strict";P();R();Oi=["month_jan","month_feb","month_mar","month_apr","month_may","month_jun","month_jul","month_aug","month_sep","month_oct","month_nov","month_dec"]});var I,Fi=w(()=>{"use strict";P();O();R();ie();Li();Ie();ji();Ni();qi();zi();I=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._taskId=null;this._task=null;this._objectName="";this._busy=!1;this._error="";this._showSkip=!1;this._showReset=!1;this._showDetails=!1;this._showAdaptive=!1;this._skipReason="";this._resetDate="";this._features={adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1};this._toast="";this._featuresLoaded=!1}get _lang(){return j(this.hass)}async openFor(e,t){this._entryId=e,this._taskId=t,this._error="",this._showSkip=!1,this._showReset=!1,this._showAdaptive=!1,this._skipReason="",this._resetDate=Ii(new Date),this._open=!0,await Promise.all([this._loadTask(),this._loadFeatures()])}async _loadFeatures(){if(!this._featuresLoaded)try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/settings"});e?.features&&(this._features={...this._features,...e.features}),this._featuresLoaded=!0}catch{}}close(){this._open=!1,this._task=null,this._error=""}async _loadTask(){if(!(!this._entryId||!this._taskId))try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._objectName=e.object?.name||"";let t=(e.tasks||[]).find(i=>i.id===this._taskId);this._task=t??null}catch(e){this._error=T(e,this._lang)}}async _runWs(e){this._busy=!0,this._error="";try{return await this.hass.connection.sendMessagePromise(e),this._busy=!1,!0}catch(t){return this._error=T(t,this._lang),this._busy=!1,!1}}_notifyChanged(e){this.dispatchEvent(new CustomEvent("task-action-fired",{detail:{entry_id:this._entryId,task_id:this._taskId,action:e},bubbles:!0,composed:!0}))}_onComplete(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(K(),Z)).then(async({openCompleteDialog:e})=>{let t=this._task,i=!!t.part_ref,n=[];if(!i)try{let d=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0});n=Ve(t,this._entryId,d.objects||[],this._lang)}catch{}e({entry_id:this._entryId,task_id:this._taskId,task_name:t.name,checklist:t.checklist||[],adaptive_enabled:!!t.adaptive_config?.enabled,required_completion_fields:t.required_completion_fields||[],task_type:t.type||"",reading_unit:t.reading_unit||"",parts:n,consumes_parts:i?[]:t.consumes_parts||[]})&&(this._notifyChanged("complete"),this.close())})}async _onSkipConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/skip",entry_id:this._entryId,task_id:this._taskId,reason:this._skipReason.trim()||null})&&(this._notifyChanged("skip"),this.close())}async _onResetConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/reset",entry_id:this._entryId,task_id:this._taskId,date:this._resetDate||void 0})&&(this._notifyChanged("reset"),this.close())}_onEdit(){!this._entryId||!this._taskId||Promise.resolve().then(()=>(K(),Z)).then(({openEditTaskDialog:e})=>{e(this._entryId,this._taskId),this.close()})}_onQr(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(K(),Z)).then(({openQrDialog:e})=>{e({entry_id:this._entryId,task_id:this._taskId,task_name:this._task.name,object_name:this._objectName}),this.close()})}async _onDelete(){if(!this._entryId||!this._taskId)return;let e=r("delete_task_confirm",this._lang)||`Delete "${this._task?.name}"?`;if(!window.confirm(e))return;await this._runWs({type:"maintenance_supporter/task/delete",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("delete"),this.close())}async _onArchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/archive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("archive"),this.close())}async _onUnarchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/unarchive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("unarchive"),this.close())}_onOpenInPanel(){if(!this._entryId||!this._taskId)return;let e=`/maintenance-supporter?entry_id=${encodeURIComponent(this._entryId)}&task_id=${encodeURIComponent(this._taskId)}`;history.pushState(null,"",e),window.dispatchEvent(new CustomEvent("location-changed")),this.close()}async _applySuggestion(){if(!this._entryId||!this._taskId||!this._task?.suggested_interval)return;await this._runWs({type:"maintenance_supporter/task/apply_suggestion",entry_id:this._entryId,task_id:this._taskId,interval:this._task.suggested_interval})&&(this._toast=r("suggestion_applied",this._lang)||"Applied",this._notifyChanged("apply_suggestion"),await this._loadTask(),setTimeout(()=>{this._toast=""},2500))}async _reanalyzeInterval(
Line truncated
|
`}var zi,Bi=w(()=>{"use strict";L();j();zi=["month_jan","month_feb","month_mar","month_apr","month_may","month_jun","month_jul","month_aug","month_sep","month_oct","month_nov","month_dec"]});var I,Vi=w(()=>{"use strict";L();M();j();se();ji();Pe();vt();qi();Oi();Di();Bi();I=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._taskId=null;this._task=null;this._objectName="";this._busy=!1;this._error="";this._showSkip=!1;this._showReset=!1;this._showDetails=!1;this._showAdaptive=!1;this._skipReason="";this._resetDate="";this._features={adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1};this._toast="";this._featuresLoaded=!1}get _lang(){return R(this.hass)}async openFor(e,t){this._entryId=e,this._taskId=t,this._error="",this._showSkip=!1,this._showReset=!1,this._showAdaptive=!1,this._skipReason="",this._resetDate=Ri(new Date),this._open=!0,await Promise.all([this._loadTask(),this._loadFeatures()])}async _loadFeatures(){if(!this._featuresLoaded)try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/settings"});e?.features&&(this._features={...this._features,...e.features}),this._featuresLoaded=!0}catch{}}close(){this._open=!1,this._task=null,this._error=""}async _loadTask(){if(!(!this._entryId||!this._taskId))try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._objectName=e.object?.name||"";let t=(e.tasks||[]).find(i=>i.id===this._taskId);this._task=t??null}catch(e){this._error=T(e,this._lang)}}async _runWs(e){this._busy=!0,this._error="";try{return await this.hass.connection.sendMessagePromise(e),this._busy=!1,!0}catch(t){return this._error=T(t,this._lang),this._busy=!1,!1}}_notifyChanged(e){this.dispatchEvent(new CustomEvent("task-action-fired",{detail:{entry_id:this._entryId,task_id:this._taskId,action:e},bubbles:!0,composed:!0}))}_onComplete(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(K(),Z)).then(async({openCompleteDialog:e})=>{let t=this._task,i=!!t.part_ref,n=Le(t),l=n?n.consumesParts:t.consumes_parts||[],d=[];if(!i)try{let _=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0});d=Ke({consumes_parts:l},this._entryId,_.objects||[],this._lang)}catch{}e({entry_id:this._entryId,task_id:this._taskId,task_name:t.name,checklist:n?n.checklist:t.checklist||[],adaptive_enabled:!!t.adaptive_config?.enabled,required_completion_fields:n?n.requiredFields:t.required_completion_fields||[],task_type:t.type||"",reading_unit:t.reading_unit||"",parts:d,consumes_parts:i?[]:l,phase_label:ie(t)})&&(this._notifyChanged("complete"),this.close())})}async _onSkipConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/skip",entry_id:this._entryId,task_id:this._taskId,reason:this._skipReason.trim()||null})&&(this._notifyChanged("skip"),this.close())}async _onResetConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/reset",entry_id:this._entryId,task_id:this._taskId,date:this._resetDate||void 0})&&(this._notifyChanged("reset"),this.close())}_onEdit(){!this._entryId||!this._taskId||Promise.resolve().then(()=>(K(),Z)).then(({openEditTaskDialog:e})=>{e(this._entryId,this._taskId),this.close()})}_onQr(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(K(),Z)).then(({openQrDialog:e})=>{e({entry_id:this._entryId,task_id:this._taskId,task_name:this._task.name,object_name:this._objectName}),this.close()})}async _onDelete(){if(!this._entryId||!this._taskId)return;let e=r("delete_task_confirm",this._lang)||`Delete "${this._task?.name}"?`;if(!window.confirm(e))return;await this._runWs({type:"maintenance_supporter/task/delete",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("delete"),this.close())}async _onArchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/archive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("archive"),this.close())}async _onUnarchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/unarchive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("unarchive"),this.close())}_onOpenInPanel(){if(!this._entryId||!this._taskId)return;let e=`/maintenance-supporter?entry_id=${encodeURIComponent(this._entryId)}&task_id=${encodeURIComponent(this._taskId)}`;history.pushState(null,"",e),window.dispatchEvent(new CustomEvent("location-changed")),this.close()}async _applySuggestion(){if(!this._entryId||!this._taskId||!this._task?.suggested_interval)return;await this._runWs({type:"maintenance_supporter/task/apply_suggestion",entry_id:this._entryId,task_id:this._taskId,interval:this._task.suggested_interval})&&(this._toast=r("suggestion_applied",this._lang)||"Applied",this._notifyChanged("app
Line truncated
|
||||||
<div class="recommendation-card">
|
<div class="recommendation-card">
|
||||||
<h4>${r("suggested_interval",t)}</h4>
|
<h4>${r("suggested_interval",t)}</h4>
|
||||||
${Hi(e.interval_days,e.suggested_interval,e.interval_confidence||"medium",t)}
|
${Mi(e.interval_days,e.suggested_interval,e.interval_confidence||"medium",t)}
|
||||||
<div class="recommendation-actions">
|
<div class="recommendation-actions">
|
||||||
<button class="btn primary"
|
<button class="btn primary"
|
||||||
@click=${this._applySuggestion} ?disabled=${this._busy}>
|
@click=${this._applySuggestion} ?disabled=${this._busy}>
|
||||||
@@ -3304,11 +3467,11 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
<div class="adaptive-stack">
|
<div class="adaptive-stack">
|
||||||
${this._toast?o`<div class="toast">${this._toast}</div>`:u}
|
${this._toast?o`<div class="toast">${this._toast}</div>`:u}
|
||||||
${i?this._renderRecommendation(e):u}
|
${i?this._renderRecommendation(e):u}
|
||||||
${n?Ri(e,t,this._features):u}
|
${n?Hi(e,t,this._features):u}
|
||||||
${l?Pi(e,t):u}
|
${l?Ni(e,t):u}
|
||||||
${d?o`
|
${d?o`
|
||||||
${Mi(e,t,this._features)}
|
${Fi(e,t,this._features)}
|
||||||
${e.seasonal_factors?.length===12||e.interval_analysis?.seasonal_factors?.length===12?Di(e,t):u}
|
${e.seasonal_factors?.length===12||e.interval_analysis?.seasonal_factors?.length===12?Ui(e,t):u}
|
||||||
`:u}
|
`:u}
|
||||||
</div>
|
</div>
|
||||||
`}_renderDetails(e){let t=this._lang,i=e.history||[],n=i.filter(p=>p.type==="completed"),l=n.reduce((p,_)=>p+(typeof _.cost=="number"?_.cost:0),0),d=(()=>{let p=n.map(_=>typeof _.duration=="number"?_.duration:null).filter(_=>_!=null);return p.length?Math.round(p.reduce((_,v)=>_+v,0)/p.length):null})();return o`
|
`}_renderDetails(e){let t=this._lang,i=e.history||[],n=i.filter(p=>p.type==="completed"),l=n.reduce((p,_)=>p+(typeof _.cost=="number"?_.cost:0),0),d=(()=>{let p=n.map(_=>typeof _.duration=="number"?_.duration:null).filter(_=>_!=null);return p.length?Math.round(p.reduce((_,v)=>_+v,0)/p.length):null})();return o`
|
||||||
@@ -3337,7 +3500,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
<div class="history-entry">
|
<div class="history-entry">
|
||||||
<div class="history-line">
|
<div class="history-line">
|
||||||
<span class="history-type type-${p.type}">${r(p.type,t)}</span>
|
<span class="history-type type-${p.type}">${r(p.type,t)}</span>
|
||||||
<span class="history-date">${pi(p.timestamp,t)}</span>
|
<span class="history-date">${_i(p.timestamp,t)}</span>
|
||||||
${_?o`<button class="history-edit"
|
${_?o`<button class="history-edit"
|
||||||
title="${r("history_edit_button",t)||"Edit"}"
|
title="${r("history_edit_button",t)||"Edit"}"
|
||||||
@click=${()=>this._onEditHistoryEntry(p)}>
|
@click=${()=>this._onEditHistoryEntry(p)}>
|
||||||
@@ -3361,7 +3524,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
${t?o`
|
${t?o`
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="status-dot" style="background: ${oe[t.status]||"#ccc"}"></span>
|
<span class="status-dot" style="background: ${le[t.status]||"#ccc"}"></span>
|
||||||
<span class="task-name">${t.name}</span>
|
<span class="task-name">${t.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="object">
|
<div class="object">
|
||||||
@@ -3370,7 +3533,8 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
<div class="quick-info">
|
<div class="quick-info">
|
||||||
${t.next_due?o`<span><strong>${r("next_due",e)||"Next due"}:</strong> ${Q(t.next_due,e)}</span>`:u}
|
${t.next_due?o`<span><strong>${r("next_due",e)||"Next due"}:</strong> ${Q(t.next_due,e)}</span>`:u}
|
||||||
${t.last_performed?o`<span><strong>${r("last_performed",e)||"Last"}:</strong> ${Q(t.last_performed,e)}</span>`:u}
|
${t.last_performed?o`<span><strong>${r("last_performed",e)||"Last"}:</strong> ${Q(t.last_performed,e)}</span>`:u}
|
||||||
${t.schedule?.kind&&!["manual","one_time"].includes(t.schedule.kind)||t.interval_days!=null?o`<span><strong>${r("interval",e)||"Interval"}:</strong> ${hi(t,e)}</span>`:u}
|
${t.schedule?.kind&&!["manual","one_time"].includes(t.schedule.kind)||t.interval_days!=null?o`<span><strong>${r("interval",e)||"Interval"}:</strong> ${gi(t,e)}</span>`:u}
|
||||||
|
${ie(t)?o`<span><strong>${r("phase_current",e)}:</strong> ${ie(t)}</span>`:u}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -3462,7 +3626,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
`}
|
`}
|
||||||
`:o`<div class="loading">${r("loading",e)||"Loading\u2026"}</div>`}
|
`:o`<div class="loading">${r("loading",e)||"Loading\u2026"}</div>`}
|
||||||
</div>
|
</div>
|
||||||
`}};I.styles=[Fe,A`
|
`}};I.styles=[Be,A`
|
||||||
:host { display: contents; }
|
:host { display: contents; }
|
||||||
.backdrop {
|
.backdrop {
|
||||||
position: fixed; inset: 0; z-index: 100;
|
position: fixed; inset: 0; z-index: 100;
|
||||||
@@ -3642,7 +3806,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
.weibull-section, .seasonal-card-compact { max-width: 100%; }
|
.weibull-section, .seasonal-card-compact { max-width: 100%; }
|
||||||
.weibull-chart svg { max-width: 100%; height: auto; }
|
.weibull-chart svg { max-width: 100%; height: auto; }
|
||||||
.details-toggle { gap: 12px; flex-wrap: wrap; }
|
.details-toggle { gap: 12px; flex-wrap: wrap; }
|
||||||
`],c([b({attribute:!1})],I.prototype,"hass",2),c([h()],I.prototype,"_open",2),c([h()],I.prototype,"_entryId",2),c([h()],I.prototype,"_taskId",2),c([h()],I.prototype,"_task",2),c([h()],I.prototype,"_objectName",2),c([h()],I.prototype,"_busy",2),c([h()],I.prototype,"_error",2),c([h()],I.prototype,"_showSkip",2),c([h()],I.prototype,"_showReset",2),c([h()],I.prototype,"_showDetails",2),c([h()],I.prototype,"_showAdaptive",2),c([h()],I.prototype,"_skipReason",2),c([h()],I.prototype,"_resetDate",2),c([h()],I.prototype,"_features",2),c([h()],I.prototype,"_toast",2);customElements.get("maintenance-task-quick-actions-dialog")||customElements.define("maintenance-task-quick-actions-dialog",I)});var G,Ui=w(()=>{"use strict";P();_t();O();R();ie();G=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._data=null;this._busy=!1;this._error=""}get _lang(){return j(this.hass)}async openFor(e){this._entryId=e,this._error="",this._open=!0,await this._load()}close(){this._open=!1,this._data=null,this._error=""}async _load(){if(this._entryId)try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._data=e}catch(e){this._error=T(e,this._lang)}}_onEditObject(){!this._entryId||!this._data||Promise.resolve().then(()=>(K(),Z)).then(({openEditObjectDialog:e})=>{e(this._entryId,this._data.object),this.close()})}_onAddTask(){this._entryId&&Promise.resolve().then(()=>(K(),Z)).then(({openCreateTaskDialog:e})=>{e(this._entryId),this.close()})}async _onDelete(){if(!this._entryId||!this._data)return;let e=r("delete_object_confirm",this._lang)||`Delete "${this._data.object.name}" and all its tasks?`;if(window.confirm(e)){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/delete",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-deleted",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(t){this._error=T(t,this._lang)}finally{this._busy=!1}}}async _onArchiveObject(){if(!this._entryId||!this._data)return;let e=!!this._data.object.archived;if(!e){let t=r("confirm_archive_object",this._lang)||"Archive this object and its tasks?";if(!window.confirm(t))return}this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:e?"maintenance_supporter/object/unarchive":"maintenance_supporter/object/archive",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-changed",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(t){this._error=T(t,this._lang)}finally{this._busy=!1}}_onTaskClick(e){this._entryId&&Promise.resolve().then(()=>(K(),Z)).then(({openTaskQuickActions:t})=>{t(this._entryId,e)})}render(){if(!this._open)return u;let e=this._lang,t=this._data,i=t?.object,n=t?.tasks||[],l=this.hass?.user?.is_admin??!0;return o`
|
`],c([x({attribute:!1})],I.prototype,"hass",2),c([h()],I.prototype,"_open",2),c([h()],I.prototype,"_entryId",2),c([h()],I.prototype,"_taskId",2),c([h()],I.prototype,"_task",2),c([h()],I.prototype,"_objectName",2),c([h()],I.prototype,"_busy",2),c([h()],I.prototype,"_error",2),c([h()],I.prototype,"_showSkip",2),c([h()],I.prototype,"_showReset",2),c([h()],I.prototype,"_showDetails",2),c([h()],I.prototype,"_showAdaptive",2),c([h()],I.prototype,"_skipReason",2),c([h()],I.prototype,"_resetDate",2),c([h()],I.prototype,"_features",2),c([h()],I.prototype,"_toast",2);customElements.get("maintenance-task-quick-actions-dialog")||customElements.define("maintenance-task-quick-actions-dialog",I)});var G,Wi=w(()=>{"use strict";L();mt();M();j();se();G=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._data=null;this._busy=!1;this._error=""}get _lang(){return R(this.hass)}async openFor(e){this._entryId=e,this._error="",this._open=!0,await this._load()}close(){this._open=!1,this._data=null,this._error=""}async _load(){if(this._entryId)try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._data=e}catch(e){this._error=T(e,this._lang)}}_onEditObject(){!this._entryId||!this._data||Promise.resolve().then(()=>(K(),Z)).then(({openEditObjectDialog:e})=>{e(this._entryId,this._data.object),this.close()})}_onAddTask(){this._entryId&&Promise.resolve().then(()=>(K(),Z)).then(({openCreateTaskDialog:e})=>{e(this._entryId),this.close()})}async _onDelete(){if(!this._entryId||!this._data)return;let e=r("delete_object_confirm",this._lang)||`Delete "${this._data.object.name}" and all its tasks?`;if(window.confirm(e)){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/delete",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-deleted",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(t){this._error=T(t,this._lang)}finally{this._busy=!1}}}async _onArchiveObject(){if(!this._entryId||!this._data)return;let e=!!this._data.object.archived;if(!e){let t=r("confirm_archive_object",this._lang)||"Archive this object and its tasks?";if(!window.confirm(t))return}this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:e?"maintenance_supporter/object/unarchive":"maintenance_supporter/object/archive",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-changed",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(t){this._error=T(t,this._lang)}finally{this._busy=!1}}_onTaskClick(e){this._entryId&&Promise.resolve().then(()=>(K(),Z)).then(({openTaskQuickActions:t})=>{t(this._entryId,e)})}render(){if(!this._open)return u;let e=this._lang,t=this._data,i=t?.object,n=t?.tasks||[],l=this.hass?.user?.is_admin??!0;return o`
|
||||||
<div class="backdrop" @click=${this.close}></div>
|
<div class="backdrop" @click=${this.close}></div>
|
||||||
<div class="dialog" role="dialog" aria-modal="true">
|
<div class="dialog" role="dialog" aria-modal="true">
|
||||||
${t&&i?o`
|
${t&&i?o`
|
||||||
@@ -3662,7 +3826,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
<div class="task-list">
|
<div class="task-list">
|
||||||
${n.map(d=>o`
|
${n.map(d=>o`
|
||||||
<div class="task-row" @click=${()=>this._onTaskClick(d.id)}>
|
<div class="task-row" @click=${()=>this._onTaskClick(d.id)}>
|
||||||
<span class="status-dot" style="background: ${oe[d.status]||"#ccc"}"></span>
|
<span class="status-dot" style="background: ${le[d.status]||"#ccc"}"></span>
|
||||||
<span class="task-name">${d.name}</span>
|
<span class="task-name">${d.name}</span>
|
||||||
<span class="task-status">${r(d.status||"ok",e)}</span>
|
<span class="task-status">${r(d.status||"ok",e)}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -3705,7 +3869,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
${i.map(([n,l])=>o`
|
${i.map(([n,l])=>o`
|
||||||
<div class="meta-item">
|
<div class="meta-item">
|
||||||
<span class="meta-label">${n}</span>
|
<span class="meta-label">${n}</span>
|
||||||
<span class="meta-value">${Ue(l)?o`<a href="${l}" target="_blank" rel="noopener noreferrer">${l}</a>`:l}</span>
|
<span class="meta-value">${Ve(l)?o`<a href="${l}" target="_blank" rel="noopener noreferrer">${l}</a>`:l}</span>
|
||||||
</div>
|
</div>
|
||||||
`)}
|
`)}
|
||||||
</div>
|
</div>
|
||||||
@@ -3768,7 +3932,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
.btn ha-icon { --mdc-icon-size: 16px; }
|
.btn ha-icon { --mdc-icon-size: 16px; }
|
||||||
.loading { padding: 24px; text-align: center; color: var(--secondary-text-color); }
|
.loading { padding: 24px; text-align: center; color: var(--secondary-text-color); }
|
||||||
.error { padding: 8px; border-radius: 6px; background: rgba(211,47,47,0.1); color: var(--error-color); font-size: 13px; }
|
.error { padding: 8px; border-radius: 6px; background: rgba(211,47,47,0.1); color: var(--error-color); font-size: 13px; }
|
||||||
`,c([b({attribute:!1})],G.prototype,"hass",2),c([h()],G.prototype,"_open",2),c([h()],G.prototype,"_entryId",2),c([h()],G.prototype,"_data",2),c([h()],G.prototype,"_busy",2),c([h()],G.prototype,"_error",2);customElements.get("maintenance-object-quick-actions-dialog")||customElements.define("maintenance-object-quick-actions-dialog",G)});var Z={};es(Z,{openCompleteDialog:()=>or,openCreateObjectDialog:()=>kt,openCreateTaskDialog:()=>Et,openEditObjectDialog:()=>rr,openEditTaskDialog:()=>ar,openHistoryEditDialog:()=>nr,openObjectQuickActions:()=>cr,openQrDialog:()=>lr,openTaskQuickActions:()=>St});function Ge(){return document.querySelector("home-assistant")?.hass}function sr(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function X(a){let s=sr(),e=s.querySelector(a)??document.body.querySelector(a);return e?e.parentNode!==s&&s.appendChild(e):(e=document.createElement(a),s.appendChild(e)),e}function ee(a){let s=Ge();if(!s)return!1;a.hass=s;let e=j(s);return pt(e)||Ce(e).then(()=>{a.requestUpdate?.()}),!0}function Ki(a){return Ke||(Ke=a.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(s=>({features:s.features??Bi.features,defaultWarningDays:s.general?.default_warning_days??7})).catch(()=>Bi),Ke)}function kt(){let a=X(Vi);return ee(a)?(a.openCreate(),!0):!1}function rr(a,s){let e=X(Vi);return ee(e)?(e.openEdit(a,s),!0):!1}function Et(a="",s){let e=X(Wi);if(!ee(e))return!1;let t=Ge();return t?((async()=>{let i=await Ki(t),n=e;n.checklistsEnabled=i.features.checklists,n.scheduleTimeEnabled=i.features.schedule_time,n.completionActionsEnabled=i.features.completion_actions,n.defaultWarningDays=i.defaultWarningDays,n.openCreate(a,s)})(),!0):!1}function ar(a,s){let e=X(Wi);if(!ee(e))return!1;let t=Ge();return t?((async()=>{try{let[i,n]=await Promise.all([t.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:a}),Ki(t)]),l=(i.tasks||[]).find(p=>p.id===s);if(!l){console.warn(`openEditTaskDialog: task ${s} not found in entry ${a}`);return}let d=e;d.checklistsEnabled=n.features.checklists,d.scheduleTimeEnabled=n.features.schedule_time,d.completionActionsEnabled=n.features.completion_actions,d.defaultWarningDays=n.defaultWarningDays,await d.openEdit(a,l)}catch(i){console.warn("openEditTaskDialog: failed to load task/features",i)}})(),!0):!1}function nr(a){let s=X(Zs);return ee(s)?(s.openEdit(a),!0):!1}function or(a){let s=X(Xs);return ee(s)?(s.entryId=a.entry_id,s.taskId=a.task_id,s.taskName=a.task_name,s.checklist=a.checklist??[],s.adaptiveEnabled=!!a.adaptive_enabled,s.requiredFields=a.required_completion_fields??[],s.taskType=a.task_type??"",s.readingUnit=a.reading_unit??"",s.parts=a.parts??[],s.consumesParts=a.consumes_parts??[],s.lang=Ge()?.language||"en",s.open(),!0):!1}function lr(a){let s=X(er);return ee(s)?(s.openForTask(a.entry_id,a.task_id,a.object_name,a.task_name),!0):!1}function St(a,s){let e=X(tr);return ee(e)?(e.openFor(a,s),!0):!1}function cr(a){let s=X(ir);return ee(s)?(s.openFor(a),!0):!1}var Vi,Wi,Zs,Xs,er,tr,ir,Bi,Ke,K=w(()=>{"use strict";yi();Ei();ft();Si();Ti();Fi();Ui();R();Vi="maintenance-object-dialog",Wi="maintenance-task-dialog",Zs="maintenance-history-edit-dialog",Xs="maintenance-complete-dialog",er="maintenance-qr-dialog",tr="maintenance-task-quick-actions-dialog",ir="maintenance-object-quick-actions-dialog";Bi={features:{adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1},defaultWarningDays:7},Ke=null});P();var _s=["assignee_pool","required_completion_fields","checklist","labels","history"],gs=["checklist_progress"],ms=["tasks","parts"],fs=["manual_docs","battery_fleet_excluded"];function lt(a,s,e=[]){for(let t of s)a[t]===void 0&&(a[t]=[]);for(let t of e)a[t]===void 0&&(a[t]={})}function vs(a){let s=a;lt(s,ms),s.object&&typeof s.object=="object"&<(s.object,fs);for(let e of s.tasks)lt(e,_s,gs);return a}function Ee(a){for(let s of a)vs(s);return a}function bs(a,s){if(s.objects)return s.objects;let e=s.delta||[],t=s.removed||[];if(!e.length&&!t.length)return null;let i=new Map(a.map(n=>[n.entry_id,n]));for(let n of e)i.set(n.entry_id,n);for(let n of t)i.delete(n);return[...i.values()]}function Gt(a,s){return s.objects&&Ee(s.objects),s.delta&&Ee(s.delta),bs(a,s)}O();R();ut();async function Cs(a,s,e=300){return(await a.connection.sendMessagePromise({type:"auth/sign_path",path:s,expires:e})).path}async function Is(a,s,e=300){return Cs(a,`/api/maintenance_supporter/document/${s}`,e)}async function mi(a,s,e=""){let t=window.open("about:blank","_blank");try{let i=await Is(a,s);t&&(t.location.href=new URL(i+e,window.location.origin).href)}catch(i){throw t&&t.close(),i}}_t();function Be(a){let s=window;s.customCards=s.customCards||[],s.customCards.some(e=>e.type===a.type)||s.customCards.push(a)}gt();Ie();P();O();R();var Ps=["overdue","triggered","due_soon","ok"],W=class extends S{constructor(){super(...arguments);this._config={type:"custom:mainten
Line truncated
|
`,c([x({attribute:!1})],G.prototype,"hass",2),c([h()],G.prototype,"_open",2),c([h()],G.prototype,"_entryId",2),c([h()],G.prototype,"_data",2),c([h()],G.prototype,"_busy",2),c([h()],G.prototype,"_error",2);customElements.get("maintenance-object-quick-actions-dialog")||customElements.define("maintenance-object-quick-actions-dialog",G)});var Z={};ss(Z,{openCompleteDialog:()=>hr,openCreateObjectDialog:()=>At,openCreateTaskDialog:()=>Tt,openEditObjectDialog:()=>cr,openEditTaskDialog:()=>dr,openHistoryEditDialog:()=>pr,openObjectQuickActions:()=>_r,openQrDialog:()=>ur,openTaskQuickActions:()=>Ct});function Qe(){return document.querySelector("home-assistant")?.hass}function lr(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function X(a){let s=lr(),e=s.querySelector(a)??document.body.querySelector(a);return e?e.parentNode!==s&&s.appendChild(e):(e=document.createElement(a),s.appendChild(e)),e}function ee(a){let s=Qe();if(!s)return!1;a.hass=s;let e=R(s);return ut(e)||Ie(e).then(()=>{a.requestUpdate?.()}),!0}function Qi(a){return Ye||(Ye=a.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(s=>({features:s.features??Ki.features,defaultWarningDays:s.general?.default_warning_days??7})).catch(()=>Ki),Ye)}function At(){let a=X(Gi);return ee(a)?(a.openCreate(),!0):!1}function cr(a,s){let e=X(Gi);return ee(e)?(e.openEdit(a,s),!0):!1}function Tt(a="",s){let e=X(Yi);if(!ee(e))return!1;let t=Qe();return t?((async()=>{let i=await Qi(t),n=e;n.checklistsEnabled=i.features.checklists,n.scheduleTimeEnabled=i.features.schedule_time,n.completionActionsEnabled=i.features.completion_actions,n.defaultWarningDays=i.defaultWarningDays,n.openCreate(a,s)})(),!0):!1}function dr(a,s){let e=X(Yi);if(!ee(e))return!1;let t=Qe();return t?((async()=>{try{let[i,n]=await Promise.all([t.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:a}),Qi(t)]),l=(i.tasks||[]).find(p=>p.id===s);if(!l){console.warn(`openEditTaskDialog: task ${s} not found in entry ${a}`);return}let d=e;d.checklistsEnabled=n.features.checklists,d.scheduleTimeEnabled=n.features.schedule_time,d.completionActionsEnabled=n.features.completion_actions,d.defaultWarningDays=n.defaultWarningDays,await d.openEdit(a,l)}catch(i){console.warn("openEditTaskDialog: failed to load task/features",i)}})(),!0):!1}function pr(a){let s=X(sr);return ee(s)?(s.openEdit(a),!0):!1}function hr(a){let s=X(rr);return ee(s)?(s.entryId=a.entry_id,s.taskId=a.task_id,s.taskName=a.task_name,s.checklist=a.checklist??[],s.adaptiveEnabled=!!a.adaptive_enabled,s.requiredFields=a.required_completion_fields??[],s.taskType=a.task_type??"",s.readingUnit=a.reading_unit??"",s.parts=a.parts??[],s.consumesParts=a.consumes_parts??[],s.phaseLabel=a.phase_label??"",s.lang=Qe()?.language||"en",s.open(),!0):!1}function ur(a){let s=X(ar);return ee(s)?(s.openForTask(a.entry_id,a.task_id,a.object_name,a.task_name),!0):!1}function Ct(a,s){let e=X(nr);return ee(e)?(e.openFor(a,s),!0):!1}function _r(a){let s=X(or);return ee(s)?(s.openFor(a),!0):!1}var Gi,Yi,sr,rr,ar,nr,or,Ki,Ye,K=w(()=>{"use strict";$i();Ti();yt();Ci();Pi();Vi();Wi();j();Gi="maintenance-object-dialog",Yi="maintenance-task-dialog",sr="maintenance-history-edit-dialog",rr="maintenance-complete-dialog",ar="maintenance-qr-dialog",nr="maintenance-task-quick-actions-dialog",or="maintenance-object-quick-actions-dialog";Ki={features:{adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1},defaultWarningDays:7},Ye=null});L();var fs=["assignee_pool","required_completion_fields","checklist","labels","history"],vs=["checklist_progress"],bs=["tasks","parts"],ys=["manual_docs","battery_fleet_excluded"];function dt(a,s,e=[]){for(let t of s)a[t]===void 0&&(a[t]=[]);for(let t of e)a[t]===void 0&&(a[t]={})}function xs(a){let s=a;dt(s,bs),s.object&&typeof s.object=="object"&&dt(s.object,ys);for(let e of s.tasks)dt(e,fs,vs);return a}function Se(a){for(let s of a)xs(s);return a}function ws(a,s){if(s.objects)return s.objects;let e=s.delta||[],t=s.removed||[];if(!e.length&&!t.length)return null;let i=new Map(a.map(n=>[n.entry_id,n]));for(let n of e)i.set(n.entry_id,n);for(let n of t)i.delete(n);return[...i.values()]}function Jt(a,s){return s.objects&&Se(s.objects),s.delta&&Se(s.delta),ws(a,s)}M();j();gt();async function Ls(a,s,e=300){return(await a.connection.sendMessagePromise({type:"auth/sign_path",path:s,expires:e})).path}async function Rs(a,s,e=300){return Ls(a,`/api/maintenance_supporter/document/${s}`,e)}async function bi(a,s,e=""){let t=window.open("about:blank","_blank");try{let i=await Rs(a,s);t&&(t.location.href=new URL(i+e,window.location.origin).href)}catch(i){throw t&&t.close(),i}}mt();function We(a){let s=window;s.customCards=s.customCards||[],s.customCards.some(e=>e.type===a.type)||s.customCards.push(a)}ft();Pe();vt();L();M();j();var Hs=["overdue","triggered","due_soon","ok"],W=class extends S{constructor(){super(...arguments
Line truncated
|
||||||
<div class="editor">
|
<div class="editor">
|
||||||
<ha-textfield
|
<ha-textfield
|
||||||
label="${r("card_title",e)}"
|
label="${r("card_title",e)}"
|
||||||
@@ -3780,7 +3944,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="field-label">${r("card_filter_status",e)}</div>
|
<div class="field-label">${r("card_filter_status",e)}</div>
|
||||||
<div class="chip-row">
|
<div class="chip-row">
|
||||||
${Ps.map(f=>o`
|
${Hs.map(f=>o`
|
||||||
<label class="chip ${t.has(f)?"active":""}">
|
<label class="chip ${t.has(f)?"active":""}">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
.checked=${t.has(f)}
|
.checked=${t.has(f)}
|
||||||
@@ -3827,11 +3991,11 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
</div>
|
</div>
|
||||||
<div class="field-help">${r("card_filter_areas_help",e)}</div>
|
<div class="field-help">${r("card_filter_areas_help",e)}</div>
|
||||||
</div>`:u}
|
</div>`:u}
|
||||||
${y.length?o`
|
${b.length?o`
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="field-label">${r("labels",e)}</div>
|
<div class="field-label">${r("labels",e)}</div>
|
||||||
<div class="object-list">
|
<div class="object-list">
|
||||||
${y.map(f=>o`
|
${b.map(f=>o`
|
||||||
<label class="object-row">
|
<label class="object-row">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
.checked=${v.has(f)}
|
.checked=${v.has(f)}
|
||||||
@@ -3865,7 +4029,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._config.entity_ids||[]}
|
.value=${this._config.entity_ids||[]}
|
||||||
.includeDomains=${["sensor","binary_sensor"]}
|
.includeDomains=${["sensor","binary_sensor"]}
|
||||||
.includeEntities=${x}
|
.includeEntities=${y}
|
||||||
@value-changed=${this._onEntitiesChanged}
|
@value-changed=${this._onEntitiesChanged}
|
||||||
></ha-entities-picker>
|
></ha-entities-picker>
|
||||||
<div class="field-help">${r("card_filter_entities_help",e)}</div>
|
<div class="field-help">${r("card_filter_entities_help",e)}</div>
|
||||||
@@ -4015,23 +4179,23 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
}
|
}
|
||||||
`,c([b({attribute:!1})],W.prototype,"hass",2),c([h()],W.prototype,"_config",2),c([h()],W.prototype,"_objects",2),c([h()],W.prototype,"_loadingObjects",2),c([h()],W.prototype,"_loadError",2),c([h()],W.prototype,"_views",2);customElements.get("maintenance-supporter-card-editor")||customElements.define("maintenance-supporter-card-editor",W);ft();P();O();P();O();R();var vt={overviewTab:"msp-overview-tab",collapsedSections:"msp-collapsed-sections",chartRange:"msp-chart-range",chartHideOutliers:"msp-chart-hide-outliers",taskSort:"maintenance_supporter_sort",objectSort:"maintenance_supporter_object_sort",groupBy:"maintenance_supporter_groupby",objectView:"maintenance_supporter_object_view",objectsCache:"msp-objects-cache",gettingStartedDismissed:"msp-gs-dismissed",batteryRosterSort:"ms_bf_roster_sort"};function vi(a){try{return localStorage.getItem(a)}catch{return null}}function bi(a,s){try{localStorage.setItem(a,s)}catch{}}ie();var q=class q extends S{constructor(){super(...arguments);this.flat=!1;this._ov=null;this._loading=!1;this._marking=!1;this._error="";this._history=null;this._rosterSort=q._storedSort();this._typeFilter=null;this._recorded=[];this._historyRequested=!1;this._localeReady=!1;this._markAll=async()=>{await this._mark(void 0)};this._repair=async()=>{if(!this._marking){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/setup",language:this._lang}),await this._load()}catch(e){this._error=T(e,this._lang)}finally{this._marking=!1}}};this._loadHistory=async e=>{if(!(!e.target.open||this._historyRequested)){this._historyRequested=!0;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/overview_history"});this._history=t.series}catch{this._history=null}}}}get _lang(){return j(this.hass)}connectedCallback(){super.connectedCallback(),this.hass&&this._load()}updated(e){e.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,Ce(this._lang).then(()=>this.requestUpdate()),this._ov===null&&!this._loading&&this._load())}async _load(){this._loading=!0,this._error="";try{this._ov=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/overview"})}catch(e){this._error=T(e,this._lang)}finally{this._loading=!1}}async _mark(e){if(!this._marking){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/mark_replaced",...e?{entity_ids:e}:{}}),await this._load()}catch(t){this._error=T(t,this._lang)}finally{this._marking=!1}}}async _setExcluded(e,t){if(!this._marking){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/set_excluded",entity_id:e,excluded:t}),await this._load()}catch(i){this._error=T(i,this._lang)}finally{this._marking=!1}}}async _addBattery(e){let t=e.detail?.value;if(!(!t||this._marking)){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/set_included",entity_id:t,included:!0}),await this._load()}catch(i){this._error=T(i,this._lang)}finally{this._marking=!1}}}async _setTrackSelf(e){let t=e.target.checked;if(!this._marking){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/set_track_self_charging",enabled:t}),await this._load()}catch(i){this._error=T(i,this._lang)}finally{this._marking=!1}}}_sparkline(e){let t=this._history?.[e.entity_id];if(!t||t.points.length<2)return u;let i=110,n=24,l=2,d=t.points[0][0],p=t.points[t.points.length-1][0],_=Date.now()/1e3,v=e.status!=="low"&&e.predicted_source==="trend"&&e.days_until!=null?_+e.days_until*86400:null,m=Math.max(p,v??p),y=L=>m===d?l:l+(L-d)/(m-d)*(i-2*l),x=L=>l+(1-Math.min(100,Math.max(0,L))/100)*(n-2*l),f=t.points.map(([L,E])=>`${y(L).toFixed(1)},${x(E).toFixed(1)}`).join(" "),$=t.points[t.points.length-1][1],z=x(t.threshold).toFixed(1);return o`<svg
|
`,c([x({attribute:!1})],W.prototype,"hass",2),c([h()],W.prototype,"_config",2),c([h()],W.prototype,"_objects",2),c([h()],W.prototype,"_loadingObjects",2),c([h()],W.prototype,"_loadError",2),c([h()],W.prototype,"_views",2);customElements.get("maintenance-supporter-card-editor")||customElements.define("maintenance-supporter-card-editor",W);yt();L();M();L();M();j();var xt={overviewTab:"msp-overview-tab",collapsedSections:"msp-collapsed-sections",chartRange:"msp-chart-range",chartHideOutliers:"msp-chart-hide-outliers",taskSort:"maintenance_supporter_sort",objectSort:"maintenance_supporter_object_sort",groupBy:"maintenance_supporter_groupby",objectView:"maintenance_supporter_object_view",objectsCache:"msp-objects-cache",gettingStartedDismissed:"msp-gs-dismissed",batteryRosterSort:"ms_bf_roster_sort"};function xi(a){try{return localStorage.getItem(a)}catch{return null}}function wi(a,s){try{localStorage.setItem(a,s)}catch{}}se();var O=class O extends S{constructor(){super(...arguments);this.flat=!1;this._ov=null;this._loading=!1;this._marking=!1;this._error="";this._history=null;this._rosterSort=O._storedSort();this._typeFilter=null;this._recorded=[];this._historyRequested=!1;this._localeReady=!1;this._markAll=async()=>{await this._mark(void 0)};this._repair=async()=>{if(!this._marking){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/setup",language:this._lang}),await this._load()}catch(e){this._error=T(e,this._lang)}finally{this._marking=!1}}};this._loadHistory=async e=>{if(!(!e.target.open||this._historyRequested)){this._historyRequested=!0;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/overview_history"});this._history=t.series}catch{this._history=null}}}}get _lang(){return R(this.hass)}connectedCallback(){super.connectedCallback(),this.hass&&this._load()}updated(e){e.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,Ie(this._lang).then(()=>this.requestUpdate()),this._ov===null&&!this._loading&&this._load())}async _load(){this._loading=!0,this._error="";try{this._ov=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/overview"})}catch(e){this._error=T(e,this._lang)}finally{this._loading=!1}}async _mark(e){if(!this._marking){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/mark_replaced",...e?{entity_ids:e}:{}}),await this._load()}catch(t){this._error=T(t,this._lang)}finally{this._marking=!1}}}async _setExcluded(e,t){if(!this._marking){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/set_excluded",entity_id:e,excluded:t}),await this._load()}catch(i){this._error=T(i,this._lang)}finally{this._marking=!1}}}async _addBattery(e){let t=e.detail?.value;if(!(!t||this._marking)){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/set_included",entity_id:t,included:!0}),await this._load()}catch(i){this._error=T(i,this._lang)}finally{this._marking=!1}}}async _setTrackSelf(e){let t=e.target.checked;if(!this._marking){this._marking=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/set_track_self_charging",enabled:t}),await this._load()}catch(i){this._error=T(i,this._lang)}finally{this._marking=!1}}}_sparkline(e){let t=this._history?.[e.entity_id];if(!t||t.points.length<2)return u;let i=110,n=24,l=2,d=t.points[0][0],p=t.points[t.points.length-1][0],_=Date.now()/1e3,v=e.status!=="low"&&e.predicted_source==="trend"&&e.days_until!=null?_+e.days_until*86400:null,m=Math.max(p,v??p),b=P=>m===d?l:l+(P-d)/(m-d)*(i-2*l),y=P=>l+(1-Math.min(100,Math.max(0,P))/100)*(n-2*l),f=t.points.map(([P,E])=>`${b(P).toFixed(1)},${y(E).toFixed(1)}`).join(" "),$=t.points[t.points.length-1][1],H=y(t.threshold).toFixed(1);return o`<svg
|
||||||
class="bf-spark"
|
class="bf-spark"
|
||||||
viewBox="0 0 ${i} ${n}"
|
viewBox="0 0 ${i} ${n}"
|
||||||
role="img"
|
role="img"
|
||||||
aria-label=${r("battery_fleet_sparkline_hint",this._lang)}
|
aria-label=${r("battery_fleet_sparkline_hint",this._lang)}
|
||||||
>
|
>
|
||||||
<title>${r("battery_fleet_sparkline_hint",this._lang)}</title>
|
<title>${r("battery_fleet_sparkline_hint",this._lang)}</title>
|
||||||
<line class="bf-spark-th" x1="0" y1=${z} x2=${i} y2=${z}></line>
|
<line class="bf-spark-th" x1="0" y1=${H} x2=${i} y2=${H}></line>
|
||||||
<polyline class="bf-spark-line" points=${f}></polyline>
|
<polyline class="bf-spark-line" points=${f}></polyline>
|
||||||
${v!==null?o`<line
|
${v!==null?o`<line
|
||||||
class="bf-spark-proj"
|
class="bf-spark-proj"
|
||||||
x1=${y(p).toFixed(1)}
|
x1=${b(p).toFixed(1)}
|
||||||
y1=${x($).toFixed(1)}
|
y1=${y($).toFixed(1)}
|
||||||
x2=${y(v).toFixed(1)}
|
x2=${b(v).toFixed(1)}
|
||||||
y2=${z}
|
y2=${H}
|
||||||
></line>`:u}
|
></line>`:u}
|
||||||
</svg>`}static _storedSort(){return vi(vt.batteryRosterSort)==="name"?"name":"urgency"}_setSort(e){this._rosterSort=e,bi(vt.batteryRosterSort,e)}_sortedRoster(e){let t=this._typeFilter===null?e:e.filter(n=>n.battery_type===this._typeFilter);if(this._rosterSort==="name")return t;let i=n=>n.status==="low"?-1e3+(n.level??101)/101:n.days_until??1/0;return[...t].sort((n,l)=>i(n)-i(l)||n.device_name.localeCompare(l.device_name))}_predictedDate(e){return this._fmtDate(Date.now()+e*864e5)}_fmtDate(e){return new Intl.DateTimeFormat(this._lang,{day:"numeric",month:"numeric",year:"numeric"}).format(new Date(e))}_shoppingLine(e){return Object.entries(e).map(([t,i])=>o`<button
|
</svg>`}static _storedSort(){return xi(xt.batteryRosterSort)==="name"?"name":"urgency"}_setSort(e){this._rosterSort=e,wi(xt.batteryRosterSort,e)}_sortedRoster(e){let t=this._typeFilter===null?e:e.filter(n=>n.battery_type===this._typeFilter);if(this._rosterSort==="name")return t;let i=n=>n.status==="low"?-1e3+(n.level??101)/101:n.days_until??1/0;return[...t].sort((n,l)=>i(n)-i(l)||n.device_name.localeCompare(l.device_name))}_predictedDate(e){return this._fmtDate(Date.now()+e*864e5)}_fmtDate(e){return new Intl.DateTimeFormat(this._lang,{day:"numeric",month:"numeric",year:"numeric"}).format(new Date(e))}_shoppingLine(e){return Object.entries(e).map(([t,i])=>o`<button
|
||||||
class="bf-type-chip ${this._typeFilter===t?"bf-type-chip-active":""}"
|
class="bf-type-chip ${this._typeFilter===t?"bf-type-chip-active":""}"
|
||||||
title=${r("battery_fleet_filter_type",this._lang)}
|
title=${r("battery_fleet_filter_type",this._lang)}
|
||||||
@click=${()=>this._toggleTypeFilter(t)}
|
@click=${()=>this._toggleTypeFilter(t)}
|
||||||
@@ -4202,7 +4366,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
`:u}
|
`:u}
|
||||||
<div class="bf-total">${r("battery_fleet_total",e).replace("{n}",String(t.total))}</div>
|
<div class="bf-total">${r("battery_fleet_total",e).replace("{n}",String(t.total))}</div>
|
||||||
</div>
|
</div>
|
||||||
`}};q.styles=A`
|
`}};O.styles=A`
|
||||||
.bf-card {
|
.bf-card {
|
||||||
background: var(--card-background-color, #fff);
|
background: var(--card-background-color, #fff);
|
||||||
border: 1px solid var(--divider-color);
|
border: 1px solid var(--divider-color);
|
||||||
@@ -4590,20 +4754,20 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
accent-color: var(--primary-color);
|
accent-color: var(--primary-color);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
`,c([b({attribute:!1})],q.prototype,"hass",2),c([b({type:Boolean})],q.prototype,"flat",2),c([h()],q.prototype,"_ov",2),c([h()],q.prototype,"_loading",2),c([h()],q.prototype,"_marking",2),c([h()],q.prototype,"_error",2),c([h()],q.prototype,"_history",2),c([h()],q.prototype,"_rosterSort",2),c([h()],q.prototype,"_typeFilter",2),c([h()],q.prototype,"_recorded",2);var bt=q;customElements.get("maintenance-battery-fleet-section")||customElements.define("maintenance-battery-fleet-section",bt);var _e=class extends S{constructor(){super(...arguments);this._config={type:"custom:maintenance-battery-fleet-card"}}static getStubConfig(){return{type:"custom:maintenance-battery-fleet-card"}}setConfig(e){this._config=e}getCardSize(){return 6}render(){return this.hass?o`
|
`,c([x({attribute:!1})],O.prototype,"hass",2),c([x({type:Boolean})],O.prototype,"flat",2),c([h()],O.prototype,"_ov",2),c([h()],O.prototype,"_loading",2),c([h()],O.prototype,"_marking",2),c([h()],O.prototype,"_error",2),c([h()],O.prototype,"_history",2),c([h()],O.prototype,"_rosterSort",2),c([h()],O.prototype,"_typeFilter",2),c([h()],O.prototype,"_recorded",2);var wt=O;customElements.get("maintenance-battery-fleet-section")||customElements.define("maintenance-battery-fleet-section",wt);var ge=class extends S{constructor(){super(...arguments);this._config={type:"custom:maintenance-battery-fleet-card"}}static getStubConfig(){return{type:"custom:maintenance-battery-fleet-card"}}setConfig(e){this._config=e}getCardSize(){return 6}render(){return this.hass?o`
|
||||||
<ha-card .header=${this._config.title||void 0}>
|
<ha-card .header=${this._config.title||void 0}>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<maintenance-battery-fleet-section flat .hass=${this.hass}></maintenance-battery-fleet-section>
|
<maintenance-battery-fleet-section flat .hass=${this.hass}></maintenance-battery-fleet-section>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`:u}};_e.styles=A`
|
`:u}};ge.styles=A`
|
||||||
ha-card {
|
ha-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
padding: 12px 16px 14px;
|
padding: 12px 16px 14px;
|
||||||
}
|
}
|
||||||
`,c([b({attribute:!1})],_e.prototype,"hass",2),c([h()],_e.prototype,"_config",2);customElements.get("maintenance-battery-fleet-card")||customElements.define("maintenance-battery-fleet-card",_e);Be({type:"maintenance-battery-fleet-card",name:"Battery Fleet",description:"All tracked batteries: what is low now, what runs out soon, and what to buy.",preview:!1});K();var D=class extends S{constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-card"};this._objects=[];this._stats=null;this._unsub=null;this._viewFilters=null;this._userNames={};this._userService=null;this._userNamesLoaded=!1;this._taskDocs={};this._docsLoadedFor=new Set;this._dataLoaded=!1;this._lastConnection=null;this._onCompleted=async()=>{await this._loadData()}}get _lang(){return j(this.hass)}static getConfigElement(){return document.createElement("maintenance-supporter-card-editor")}static getStubConfig(){return{type:"custom:maintenance-supporter-card",show_header:!0,show_actions:!0,filter_status:["overdue","triggered","due_soon"],max_items:10}}setConfig(e){let t=e.view_id!==this._config.view_id;this._config=e,t&&this._dataLoaded&&this.hass&&this._loadViewFilters()}getCardSize(){return 3}connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),this._unsub&&(this._unsub(),this._unsub=null),this._dataLoaded=!1,this._lastConnection=null}updated(e){if(super.updated(e),ci(this,e),this.hass&&!this._userNamesLoaded&&this._config.show_assignee!==!1&&this._objects.some(t=>t.tasks.some(i=>i.responsible_user_id))&&this._loadUserNames(),this.hass&&this._config.show_documents!==!1)for(let t of this._objects)this._docsLoadedFor.has(t.entry_id)||t.tasks.some(i=>(i.document_count??0)>0)&&this._loadDocuments(t.entry_id);if(e.has("hass")&&this.hass){if(!this._dataLoaded)this._dataLoaded=!0,this._lastConnection=this.hass.connection,this._loadData(),this._subscribe();else if(this.hass.connection!==this._lastConnection){if(this._lastConnection=this.hass.connection,this._unsub){try{this._unsub()}catch{}this._unsub=null}this._subscribe(),this._loadData()}}}async _loadData(){try{let[e,t]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0}),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/statistics"})]);this._objects=Ee(e.objects),this._stats=t}catch{}await this._loadViewFilters()}_assigneeName(e){if(this._config.show_assignee===!1)return"";let t=e.responsible_user_id;return t&&this._userNames[t]||""}async _loadUserNames(){this._userNamesLoaded=!0,this._userService?this._userService.updateHass(this.hass):this._userService=new ue(this.hass);try{let e=await this._userService.getUsers();this._userNames=Object.fromEntries(e.map(t=>[t.id,t.name]))}catch{}}async _loadDocuments(e){this._docsLoadedFor.add(e);try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/documents/list",entry_id:e}),i={};for(let n of t.documents||[])for(let l of n.task_ids||[])(i[l]||=[]).push({id:n.id,title:n.title,kind:n.kind,url:n.url});this._taskDocs={...this._taskDocs,[e]:i}}catch{}}_docsFor(e,t){if(this._config.show_documents===!1)return[];let n=[...this._taskDocs[e]?.[t.id]||[]];return t.documentation_url&&n.push({id:`url:${t.id}`,title:r("documentation_label",this._lang),kind:"weblink",url:t.documentation_url}),n}async _openDoc(e){if(e.kind==="weblink"&&e.url){Ue(e.url)&&window.open(e.url,"_blank","noopener");return}try{await mi(this.hass,e.id)}catch{}}async _loadViewFilters(){if(!this._config.view_id){this._viewFilters=null;return}try{let t=((await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/views/list"})).views||[]).find(i=>i.id===this._config.view_id);this._viewFilters=t?t.filters:null}catch{this._viewFilters=null}}async _subscribe(){try{let e=await this.hass.connection.subscribeMessage(t=>{let i=Gt(this._objects,t);i!==null&&(this._objects=i)},{type:"maintenance_supporter/subscribe",deltas:!0,compact:!0});if(!this.isConnected){e();return}this._unsub=e}catch{}}get _flatTasks(){let e=[],{filter_status:t,filter_objects:i,entity_ids:n,filter_due_min_days:l,filter_due_max_days:d,filter_labels:p,filter_priority:_,filter_areas:v,max_items:m}=this._config,y=n?.length?new Set(n):null,x=l!==void 0||d!==void 0,f=this._viewFilters,$=f?.user_id==="current_user"?this.hass.user?.id??null:f?.user_id??null;for(let L of this._objects)if(!(i?.length&&!i.includes(L.object.name))){if(v?.length){let E=L.object.area_id;if(!E||!v.includes(E))continue}for(let E of L.tasks)if(!E.is_done&&!(E.archived||L.object.archived)&&!(t?.length&&!t.includes(E.status))&&!(p?.length&&!(E.labels||[]).some(N=>p.includes(N)))&&!(_?.length&&!_.includes(E.priority||"normal"))&&!(y&&!(E.sensor_entity_id&&y.has(E.sensor_entity_id)||E.binary_sensor_entity_id&&y.has(E.binary_sensor_entity_id)))){if(x){let N=E.days_until_due;if(N==null||l!==void 0&&N<l||d!==void 0&&N>d)continue}f&&(f.status&&E.status!==f.status||f.label&&!(E.labels||[]).
Line truncated
|
`,c([x({attribute:!1})],ge.prototype,"hass",2),c([h()],ge.prototype,"_config",2);customElements.get("maintenance-battery-fleet-card")||customElements.define("maintenance-battery-fleet-card",ge);We({type:"maintenance-battery-fleet-card",name:"Battery Fleet",description:"All tracked batteries: what is low now, what runs out soon, and what to buy.",preview:!1});K();var z=class extends S{constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-card"};this._objects=[];this._stats=null;this._unsub=null;this._viewFilters=null;this._userNames={};this._userService=null;this._userNamesLoaded=!1;this._taskDocs={};this._docsLoadedFor=new Set;this._dataLoaded=!1;this._lastConnection=null;this._onCompleted=async()=>{await this._loadData()}}get _lang(){return R(this.hass)}static getConfigElement(){return document.createElement("maintenance-supporter-card-editor")}static getStubConfig(){return{type:"custom:maintenance-supporter-card",show_header:!0,show_actions:!0,filter_status:["overdue","triggered","due_soon"],max_items:10}}setConfig(e){let t=e.view_id!==this._config.view_id;this._config=e,t&&this._dataLoaded&&this.hass&&this._loadViewFilters()}getCardSize(){return 3}connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),this._unsub&&(this._unsub(),this._unsub=null),this._dataLoaded=!1,this._lastConnection=null}updated(e){if(super.updated(e),hi(this,e),this.hass&&!this._userNamesLoaded&&this._config.show_assignee!==!1&&this._objects.some(t=>t.tasks.some(i=>i.responsible_user_id))&&this._loadUserNames(),this.hass&&this._config.show_documents!==!1)for(let t of this._objects)this._docsLoadedFor.has(t.entry_id)||t.tasks.some(i=>(i.document_count??0)>0)&&this._loadDocuments(t.entry_id);if(e.has("hass")&&this.hass){if(!this._dataLoaded)this._dataLoaded=!0,this._lastConnection=this.hass.connection,this._loadData(),this._subscribe();else if(this.hass.connection!==this._lastConnection){if(this._lastConnection=this.hass.connection,this._unsub){try{this._unsub()}catch{}this._unsub=null}this._subscribe(),this._loadData()}}}async _loadData(){try{let[e,t]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0}),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/statistics"})]);this._objects=Se(e.objects),this._stats=t}catch{}await this._loadViewFilters()}_assigneeName(e){if(this._config.show_assignee===!1)return"";let t=e.responsible_user_id;return t&&this._userNames[t]||""}async _loadUserNames(){this._userNamesLoaded=!0,this._userService?this._userService.updateHass(this.hass):this._userService=new _e(this.hass);try{let e=await this._userService.getUsers();this._userNames=Object.fromEntries(e.map(t=>[t.id,t.name]))}catch{}}async _loadDocuments(e){this._docsLoadedFor.add(e);try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/documents/list",entry_id:e}),i={};for(let n of t.documents||[])for(let l of n.task_ids||[])(i[l]||=[]).push({id:n.id,title:n.title,kind:n.kind,url:n.url});this._taskDocs={...this._taskDocs,[e]:i}}catch{}}_docsFor(e,t){if(this._config.show_documents===!1)return[];let n=[...this._taskDocs[e]?.[t.id]||[]];return t.documentation_url&&n.push({id:`url:${t.id}`,title:r("documentation_label",this._lang),kind:"weblink",url:t.documentation_url}),n}async _openDoc(e){if(e.kind==="weblink"&&e.url){Ve(e.url)&&window.open(e.url,"_blank","noopener");return}try{await bi(this.hass,e.id)}catch{}}async _loadViewFilters(){if(!this._config.view_id){this._viewFilters=null;return}try{let t=((await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/views/list"})).views||[]).find(i=>i.id===this._config.view_id);this._viewFilters=t?t.filters:null}catch{this._viewFilters=null}}async _subscribe(){try{let e=await this.hass.connection.subscribeMessage(t=>{let i=Jt(this._objects,t);i!==null&&(this._objects=i)},{type:"maintenance_supporter/subscribe",deltas:!0,compact:!0});if(!this.isConnected){e();return}this._unsub=e}catch{}}get _flatTasks(){let e=[],{filter_status:t,filter_objects:i,entity_ids:n,filter_due_min_days:l,filter_due_max_days:d,filter_labels:p,filter_priority:_,filter_areas:v,max_items:m}=this._config,b=n?.length?new Set(n):null,y=l!==void 0||d!==void 0,f=this._viewFilters,$=f?.user_id==="current_user"?this.hass.user?.id??null:f?.user_id??null;for(let P of this._objects)if(!(i?.length&&!i.includes(P.object.name))){if(v?.length){let E=P.object.area_id;if(!E||!v.includes(E))continue}for(let E of P.tasks)if(!E.is_done&&!(E.archived||P.object.archived)&&!(t?.length&&!t.includes(E.status))&&!(p?.length&&!(E.labels||[]).some(N=>p.includes(N)))&&!(_?.length&&!_.includes(E.priority||"normal"))&&!(b&&!(E.sensor_entity_id&&b.has(E.sensor_entity_id)||E.binary_sensor_entity_id&&b.has(E.binary_sensor_entity_id)))){if(y){let N=E.days_until_due;if(N==null||l!==void 0&&N<l||d!==void 0&&N>d)continue}f&&(f.status&&E.status!==f.status||f.label&&!(E.labels||[]).
Line truncated
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h1>${t}</h1>
|
<h1>${t}</h1>
|
||||||
@@ -4619,14 +4783,14 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
<mwc-icon-button
|
<mwc-icon-button
|
||||||
class="hdr-add"
|
class="hdr-add"
|
||||||
title="${r("new_object",e)}"
|
title="${r("new_object",e)}"
|
||||||
@click=${()=>kt()}
|
@click=${()=>At()}
|
||||||
>
|
>
|
||||||
<ha-icon icon="mdi:plus-box"></ha-icon>
|
<ha-icon icon="mdi:plus-box"></ha-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
<mwc-icon-button
|
<mwc-icon-button
|
||||||
class="hdr-add"
|
class="hdr-add"
|
||||||
title="${r("add_task",e)}"
|
title="${r("add_task",e)}"
|
||||||
@click=${()=>Et("",this._objects)}
|
@click=${()=>Tt("",this._objects)}
|
||||||
>
|
>
|
||||||
<ha-icon icon="mdi:playlist-plus"></ha-icon>
|
<ha-icon icon="mdi:playlist-plus"></ha-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
@@ -4646,7 +4810,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
<div class="task-item clickable"
|
<div class="task-item clickable"
|
||||||
@click=${()=>this._openTaskDetail(_,m.id)}
|
@click=${()=>this._openTaskDetail(_,m.id)}
|
||||||
title="${r("open_task",e)||"Open task"}">
|
title="${r("open_task",e)||"Open task"}">
|
||||||
<div class="status-dot" style="background: ${oe[m.status]||"#ccc"}"></div>
|
<div class="status-dot" style="background: ${le[m.status]||"#ccc"}"></div>
|
||||||
<div class="task-info">
|
<div class="task-info">
|
||||||
<div class="task-name">
|
<div class="task-name">
|
||||||
${m.name}
|
${m.name}
|
||||||
@@ -4659,32 +4823,32 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
${l?this._assigneeName(m)?o`<div class="task-meta compact-assignee" title="${this._assigneeName(m)}">
|
${l?this._assigneeName(m)?o`<div class="task-meta compact-assignee" title="${this._assigneeName(m)}">
|
||||||
<ha-icon icon="mdi:account"></ha-icon>${this._assigneeName(m)}
|
<ha-icon icon="mdi:account"></ha-icon>${this._assigneeName(m)}
|
||||||
</div>`:u:o`<div class="task-meta">
|
</div>`:u:o`<div class="task-meta">
|
||||||
${v} · ${r(m.type,e)}${this._assigneeName(m)?o` · <span class="assignee"
|
${v} · ${r(m.type,e)}${ie(m)?o` · ${ie(m)}`:u}${this._assigneeName(m)?o` · <span class="assignee"
|
||||||
><ha-icon icon="mdi:account"></ha-icon>${this._assigneeName(m)}</span
|
><ha-icon icon="mdi:account"></ha-icon>${this._assigneeName(m)}</span
|
||||||
>`:u}
|
>`:u}
|
||||||
</div>`}
|
</div>`}
|
||||||
</div>
|
</div>
|
||||||
${this._docsFor(_,m).length?o`<div class="doc-chips">
|
${this._docsFor(_,m).length?o`<div class="doc-chips">
|
||||||
${this._docsFor(_,m).map(y=>o`
|
${this._docsFor(_,m).map(b=>o`
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="doc-chip"
|
class="doc-chip"
|
||||||
title="${y.title}"
|
title="${b.title}"
|
||||||
@click=${x=>{x.stopPropagation(),this._openDoc(y)}}
|
@click=${y=>{y.stopPropagation(),this._openDoc(b)}}
|
||||||
>
|
>
|
||||||
<ha-icon icon=${y.kind==="weblink"?"mdi:link-variant":"mdi:file-document-outline"}></ha-icon>
|
<ha-icon icon=${b.kind==="weblink"?"mdi:link-variant":"mdi:file-document-outline"}></ha-icon>
|
||||||
<span>${y.title}</span>
|
<span>${b.title}</span>
|
||||||
</button>
|
</button>
|
||||||
`)}
|
`)}
|
||||||
</div>`:u}
|
</div>`:u}
|
||||||
<div class="task-due">
|
<div class="task-due">
|
||||||
${m.days_until_due!==null&&m.days_until_due!==void 0?m.days_until_due<0?o`<span class="overdue-text">${ht(m.days_until_due,e)}</span>`:ht(m.days_until_due,e):m.trigger_active?"\u26A1":"\u2014"}
|
${m.days_until_due!==null&&m.days_until_due!==void 0?m.days_until_due<0?o`<span class="overdue-text">${_t(m.days_until_due,e)}</span>`:_t(m.days_until_due,e):m.trigger_active?"\u26A1":"\u2014"}
|
||||||
</div>
|
</div>
|
||||||
${n?o`
|
${n?o`
|
||||||
<mwc-icon-button
|
<mwc-icon-button
|
||||||
class="complete-btn"
|
class="complete-btn"
|
||||||
title="${r("complete",e)}"
|
title="${r("complete",e)}"
|
||||||
@click=${y=>{y.stopPropagation();let x=this.shadowRoot.querySelector("maintenance-complete-dialog");x.entryId=_,x.taskId=m.id,x.taskName=m.name,x.checklist=m.checklist||[],x.adaptiveEnabled=!!m.adaptive_config?.enabled,x.taskType=m.type||"",x.readingUnit=m.reading_unit||"",x.requiredFields=m.required_completion_fields||[],x.lang=e;let f=!!m.part_ref;x.parts=f?[]:Ve(m,_,this._objects,e),x.consumesParts=f?[]:m.consumes_parts||[],x.open()}}
|
@click=${b=>{b.stopPropagation();let y=this.shadowRoot.querySelector("maintenance-complete-dialog"),f=Le(m);y.entryId=_,y.taskId=m.id,y.taskName=m.name,y.checklist=f?f.checklist:m.checklist||[],y.adaptiveEnabled=!!m.adaptive_config?.enabled,y.taskType=m.type||"",y.readingUnit=m.reading_unit||"",y.requiredFields=f?f.requiredFields:m.required_completion_fields||[],y.phaseLabel=ie(m),y.lang=e;let $=!!m.part_ref,H=f?f.consumesParts:m.consumes_parts||[];y.parts=$?[]:Ke({consumes_parts:H},_,this._objects,e),y.consumesParts=$?[]:H,y.open()}}
|
||||||
>
|
>
|
||||||
<ha-icon icon="mdi:check"></ha-icon>
|
<ha-icon icon="mdi:check"></ha-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
@@ -4698,7 +4862,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@task-completed=${this._onCompleted}
|
@task-completed=${this._onCompleted}
|
||||||
></maintenance-complete-dialog>
|
></maintenance-complete-dialog>
|
||||||
`}};D.styles=[Fe,A`
|
`}};z.styles=[Be,A`
|
||||||
ha-card { overflow: hidden; }
|
ha-card { overflow: hidden; }
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
@@ -4830,7 +4994,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
|||||||
--mdc-icon-size: 18px;
|
--mdc-icon-size: 18px;
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
`],c([b({attribute:!1})],D.prototype,"hass",2),c([h()],D.prototype,"_config",2),c([h()],D.prototype,"_objects",2),c([h()],D.prototype,"_stats",2),c([h()],D.prototype,"_unsub",2),c([h()],D.prototype,"_viewFilters",2),c([h()],D.prototype,"_userNames",2),c([h()],D.prototype,"_taskDocs",2);customElements.get("maintenance-supporter-card")||customElements.define("maintenance-supporter-card",D);Be({type:"maintenance-supporter-card",name:"Maintenance Supporter",description:"Overview of your maintenance tasks with quick actions.",preview:!0});export{D as MaintenanceSupporterCard};
|
`],c([x({attribute:!1})],z.prototype,"hass",2),c([h()],z.prototype,"_config",2),c([h()],z.prototype,"_objects",2),c([h()],z.prototype,"_stats",2),c([h()],z.prototype,"_unsub",2),c([h()],z.prototype,"_viewFilters",2),c([h()],z.prototype,"_userNames",2),c([h()],z.prototype,"_taskDocs",2);customElements.get("maintenance-supporter-card")||customElements.define("maintenance-supporter-card",z);We({type:"maintenance-supporter-card",name:"Maintenance Supporter",description:"Overview of your maintenance tasks with quick actions.",preview:!0});export{z as MaintenanceSupporterCard};
|
||||||
/*! Bundled license information:
|
/*! Bundled license information:
|
||||||
|
|
||||||
@lit/reactive-element/css-tag.js:
|
@lit/reactive-element/css-tag.js:
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -1,2 +1,2 @@
|
|||||||
/*! maintenance_supporter frontend 2.64.0 */
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
var S="2.64.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{}})();
|
var S="2.65.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{}})();
|
||||||
+250
@@ -0,0 +1,250 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-IAYX7ZWS.js";import{a as p}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z6JBBYEY.js";import{a as o,b as _,c as i,f as l,g as h,k as g,l as n,p as t,r as u,t as v}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";var r=class extends h{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._users=[];this._responsible="";this._forMinutes="0";this._localeReady=!1;this._userService=null;this._toggle=s=>{let a=new Set(this._selected);a.has(s)?a.delete(s):a.add(s),this._selected=a};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(s=>s.entity_id))};this._adopt=async()=>{if(!(this._selected.size===0||this._adopting)){this._adopting=!0,this._error="";try{let s=this._sensors.filter(e=>this._selected.has(e.entity_id)).map(e=>({entity_id:e.entity_id,name:e.name,entry_id:e.suggested_entry_id??void 0,object_name:e.suggested_object_name,device_id:e.device_id??void 0,part_id:e.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0,for_minutes:parseInt(this._forMinutes,10)>0?parseInt(this._forMinutes,10):void 0})),a=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/adopt",selections:s});this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:a})),this._open=!1}catch(s){this._error=p(s,this._lang)}finally{this._adopting=!1}}}}get _lang(){return u(this.hass)}updated(s){s.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,v(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._responsible="",this._forMinutes="0";try{this._userService?this._userService.updateHass(this.hass):this._userService=new f(this.hass);let[s,a]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/discover"}),this._userService.getUsers().catch(()=>[])]);this._sensors=s.sensors||[],this._selected=new Set(this._sensors.map(e=>e.entity_id)),this._users=a}catch(s){this._error=p(s,this._lang)}finally{this._loading=!1}}_close(){this._open=!1}render(){if(!this._open)return i``;let s=this._lang,a=this._sensors.length>0&&this._selected.size===this._sensors.length;return i`
|
||||||
|
<div class="overlay" @click=${this._close}>
|
||||||
|
<div class="card" @click=${e=>e.stopPropagation()}>
|
||||||
|
<div class="title">${t("adopt_problem_title",s)}</div>
|
||||||
|
<div class="hint">${t("adopt_problem_hint",s)}</div>
|
||||||
|
${this._error?i`<div class="error">${this._error}</div>`:l}
|
||||||
|
|
||||||
|
${this._loading?i`<div class="loading">…</div>`:this._sensors.length===0?i`<div class="empty">${t("adopt_problem_none",s)}</div>`:i`
|
||||||
|
<label class="select-all">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
.checked=${a}
|
||||||
|
@change=${this._toggleAll}
|
||||||
|
/>
|
||||||
|
<span>${t("selected",s)}: ${this._selected.size} / ${this._sensors.length}</span>
|
||||||
|
</label>
|
||||||
|
<div class="list">
|
||||||
|
${this._sensors.map(e=>{let m=this._selected.has(e.entity_id),d=e.state==="on",c=[e.device_name,e.area_name].filter(Boolean).join(" \xB7 ");return i`
|
||||||
|
<label class="row">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
.checked=${m}
|
||||||
|
@change=${()=>this._toggle(e.entity_id)}
|
||||||
|
/>
|
||||||
|
<div class="row-main">
|
||||||
|
<div class="row-top">
|
||||||
|
<span class="row-name">${e.name}</span>
|
||||||
|
<span class="chip ${d?"chip-active":"chip-ok"}">
|
||||||
|
${d?t("adopt_problem_active",s):t("adopt_problem_ok",s)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
${c?i`<div class="row-sub">${c}</div>`:l}
|
||||||
|
<div class="row-target">
|
||||||
|
→ ${e.suggested_object_name}${e.suggested_entry_id?l:i` <span class="new-tag">${t("adopt_problem_new_object",s)}</span>`}
|
||||||
|
</div>
|
||||||
|
${e.suggested_part_name?i`<div class="row-part">
|
||||||
|
<ha-icon icon="mdi:package-variant-closed"></ha-icon>
|
||||||
|
${t("adopt_problem_part",s).replace("{name}",e.suggested_part_name)}
|
||||||
|
</div>`:l}
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
`})}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
|
||||||
|
${!this._loading&&this._sensors.length>0?i`
|
||||||
|
<label class="responsible">
|
||||||
|
<span>${t("for_at_least_minutes",s)}</span>
|
||||||
|
<input
|
||||||
|
class="for-input"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="1440"
|
||||||
|
.value=${this._forMinutes}
|
||||||
|
@input=${e=>this._forMinutes=e.target.value}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div class="for-hint">${t("adopt_for_minutes_hint",s)}</div>
|
||||||
|
`:l}
|
||||||
|
|
||||||
|
${!this._loading&&this._sensors.length>0&&this._users.length>0?i`
|
||||||
|
<label class="responsible">
|
||||||
|
<span>${t("adopt_problem_responsible",s)}</span>
|
||||||
|
<select
|
||||||
|
.value=${this._responsible}
|
||||||
|
@change=${e=>{this._responsible=e.target.value}}
|
||||||
|
>
|
||||||
|
<option value="" ?selected=${!this._responsible}>${t("no_user_assigned",s)}</option>
|
||||||
|
${this._users.map(e=>i`<option value=${e.id} ?selected=${e.id===this._responsible}>${e.name}</option>`)}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
`:l}
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<ha-button appearance="plain" @click=${this._close}>
|
||||||
|
${t("cancel",s)}
|
||||||
|
</ha-button>
|
||||||
|
<ha-button
|
||||||
|
@click=${this._adopt}
|
||||||
|
.disabled=${this._selected.size===0||this._adopting}
|
||||||
|
>
|
||||||
|
${t("adopt_problem_adopt",s)}
|
||||||
|
</ha-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}};r.styles=_`
|
||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: var(--card-background-color, #fff);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
min-width: min(360px, calc(100vw - 24px));
|
||||||
|
max-width: 560px;
|
||||||
|
width: 90vw;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.hint {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: var(--error-color, #f44336);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.loading,
|
||||||
|
.empty {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 12px 0;
|
||||||
|
}
|
||||||
|
.select-all {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.select-all input {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 50vh;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.row input {
|
||||||
|
margin-top: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.row-main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.row-top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.row-name {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.row-sub {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.row-target {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.row-part {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.row-part ha-icon {
|
||||||
|
--mdc-icon-size: 14px;
|
||||||
|
}
|
||||||
|
.new-tag {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.chip {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 1px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.chip-active {
|
||||||
|
background: var(--error-color, #f44336);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.chip-ok {
|
||||||
|
background: var(--divider-color);
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.responsible {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.for-input {
|
||||||
|
width: 70px;
|
||||||
|
background: var(--card-background-color);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
}
|
||||||
|
.for-hint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
margin: -4px 0 2px;
|
||||||
|
}
|
||||||
|
.responsible select {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 140px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
background: var(--card-background-color, #fff);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
`,o([g({attribute:!1})],r.prototype,"hass",2),o([n()],r.prototype,"_open",2),o([n()],r.prototype,"_loading",2),o([n()],r.prototype,"_adopting",2),o([n()],r.prototype,"_error",2),o([n()],r.prototype,"_sensors",2),o([n()],r.prototype,"_selected",2),o([n()],r.prototype,"_users",2),o([n()],r.prototype,"_responsible",2),o([n()],r.prototype,"_forMinutes",2);customElements.get("maintenance-adopt-problem-sensors-dialog")||customElements.define("maintenance-adopt-problem-sensors-dialog",r);export{r as MaintenanceAdoptProblemSensorsDialog};
|
||||||
@@ -0,0 +1,306 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as _,e as k}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-UBKG7VLY.js";import{a as g}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z6JBBYEY.js";import{B as m,a as s,b,c as a,f as d,g as v,k as n,l,p as r}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.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.phaseLabel="";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._completedAt="";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._completedAt="",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._completedAt){if(new Date(this._completedAt).getTime()>Date.now()){this._error=r("completed_at_future_error",this.lang),this._loading=!1;return}e.completed_at=this._completedAt.length===16?`${this._completedAt}:00`:this._completedAt}if(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>
|
||||||
|
${this.phaseLabel?a`<div class="phase-line">${r("phase_current",e)}: ${this.phaseLabel}</div>`:d}
|
||||||
|
<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>
|
||||||
|
<label class="field">
|
||||||
|
<span class="field-label">${r("completed_at_optional",e)}</span>
|
||||||
|
<input type="datetime-local" class="field-input"
|
||||||
|
max=${new Date(Date.now()-new Date().getTimezoneOffset()*6e4).toISOString().slice(0,16)}
|
||||||
|
.value=${this._completedAt}
|
||||||
|
@change=${t=>this._completedAt=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(k[t]??t,e))).join(" \xB7 "):""}
|
||||||
|
>
|
||||||
|
${this._loading?r("completing",e):r("complete",e)}
|
||||||
|
</ha-button>
|
||||||
|
</div>
|
||||||
|
</ha-dialog>
|
||||||
|
`}};i.styles=[m,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;
|
||||||
|
}
|
||||||
|
.phase-line {
|
||||||
|
margin-top: -8px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.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([n()],i.prototype,"phaseLabel",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,"_completedAt",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 a};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as t,b as a,c as i,f as l,g as p,k as r}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.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,213 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as x}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-GSH25Q5Z.js";import{a,b as v,c as n,f as p,g as b,k as h,l as c,p as t}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";function d(l){return l.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function f(l){return!l.startsWith("data:image/svg+xml,")&&!l.startsWith("data:image/png;base64,")?"":d(l)}function q(l){return l.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var o=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,r,s){this._entryId=e,this._taskId=i,this._objectName=r,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 r=[this.hass.connection.sendMessagePromise({...i,action:"view"})];this._taskId&&r.push(this.hass.connection.sendMessagePromise({...i,action:"complete"}));let s=await Promise.all(r);if(e!==this._generateSeq)return;this._viewResult=s[0],s.length>1&&(this._completeResult=s[1])}catch(i){if(e!==this._generateSeq)return;let r=i?.code,s=i?.message;this._error=r==="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,r=[e.label.manufacturer,e.label.model].filter(Boolean).join(" "),s=window.open("","_blank","width=600,height=500");if(!s)return;let g=this.lang||"en",u=d(i),m=d(r),_=!!this._completeResult,w=d(t("qr_action_view",g)),$=d(t("qr_action_complete",g));s.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<meta name="color-scheme" content="light">
|
||||||
|
<title>${u}</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>${u}</h2>
|
||||||
|
${m?`<div class="sub">${m}</div>`:""}
|
||||||
|
<div class="qr-row">
|
||||||
|
<div class="qr-col">
|
||||||
|
<img src="${f(this._viewResult.svg_data_uri)}" alt="QR Info" />
|
||||||
|
<div class="qr-label">${w}</div>
|
||||||
|
</div>
|
||||||
|
${_?`<div class="qr-col">
|
||||||
|
<img src="${f(this._completeResult.svg_data_uri)}" alt="QR Complete" />
|
||||||
|
<div class="qr-label">${$}</div>
|
||||||
|
</div>`:""}
|
||||||
|
</div>
|
||||||
|
<div class="url">${d(this._viewResult.url)}</div>
|
||||||
|
<script>setTimeout(()=>window.print(),300)<\/script>
|
||||||
|
</body></html>`),s.document.close()}_downloadSvg(e,i){let r=decodeURIComponent(e.svg_data_uri.replace("data:image/svg+xml,","")),s=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;x(r,`qr-${q(s)}-${i}.svg`,"image/svg+xml")}_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}`,r=!!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>`:r?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>
|
||||||
|
`:p}
|
||||||
|
</div>
|
||||||
|
<div class="url-display">${this._viewResult.url}</div>
|
||||||
|
`:p}
|
||||||
|
<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=${!r}
|
||||||
|
>
|
||||||
|
${t("qr_print",e)}
|
||||||
|
</ha-button>
|
||||||
|
</div>
|
||||||
|
</ha-dialog>
|
||||||
|
`}};o.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([h({attribute:!1})],o.prototype,"hass",2),a([h()],o.prototype,"lang",2),a([c()],o.prototype,"_open",2),a([c()],o.prototype,"_loading",2),a([c()],o.prototype,"_error",2),a([c()],o.prototype,"_viewResult",2),a([c()],o.prototype,"_completeResult",2),a([c()],o.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",o);export{o as a};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{b as i}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-GSH25Q5Z.js";async function l(e,n,t=300){return(await e.connection.sendMessagePromise({type:"auth/sign_path",path:n,expires:t})).path}async function r(e,n,t=300){return l(e,`/api/maintenance_supporter/document/${n}`,t)}async function y(e,n,t=""){let a=window.open("about:blank","_blank");try{let o=await r(e,n);a&&(a.location.href=new URL(o+t,window.location.origin).href)}catch(o){throw a&&a.close(),o}}async function p(e,n,t){i(await r(e,n,30),t)}function d(e){let n=URL.createObjectURL(new Blob([e],{type:"text/html"}));window.open(n,"_blank"),setTimeout(()=>URL.revokeObjectURL(n),6e4)}var c=[{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=c.map(e=>e.key),s=["name","manufacturer","model","serial_number","installation_date","warranty_expiry","area_id","task_count","actions"];function g(e){if(!Array.isArray(e))return[...s];let n=new Set,t=[];for(let a of e)typeof a=="string"&&u.includes(a)&&!n.has(a)&&(n.add(a),t.push(a));return t.length?(t.includes("name")||t.unshift("name"),t):[...s]}export{l as a,r as b,y as c,p as d,d as e,c as f,s as g,g as h};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
function c(o,t,e){let l=new Blob([o],{type:e}),d=URL.createObjectURL(l),n=document.createElement("a");n.href=d,n.download=t,n.target="_blank",n.rel="noopener",n.style.display="none",document.body.appendChild(n),n.dispatchEvent(new MouseEvent("click")),document.body.removeChild(n),setTimeout(()=>URL.revokeObjectURL(d),6e4)}function r(o,t){let e=document.createElement("a");e.href=o,e.download=t,e.target="_blank",e.rel="noopener",e.style.display="none",document.body.appendChild(e),e.dispatchEvent(new MouseEvent("click")),document.body.removeChild(e)}export{c as a,r as b};
|
||||||
@@ -0,0 +1,810 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{c as nt}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-UBKG7VLY.js";import{a as A}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z6JBBYEY.js";import{A as tt,C as et,a as m,b as C,c as o,d as H,f as p,g as S,k as j,l as y,m as q,p as a,r as E,s as Z,t as Y,u as O,v as Q,x as J,z as X}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";var k=class extends S{constructor(){super(...arguments);this._open=!1;this._saving=!1;this._error="";this._draft=null;this._originalSnapshot=null;this._partOptions=null;this._partQty={};this._partQtyOriginal=""}get _lang(){return E(this.hass)}openEdit(t){this._draft={...t},this._originalSnapshot={...t},this._error="",this._open=!0,this._partOptions=null,this._partQty={},this._partQtyOriginal="",this._loadPartOptions()}async _loadPartOptions(){let t=this._draft;if(t)try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/parts/overview"}),s=[];for(let l of e.parts||[]){let d=l.entry_id===t.entry_id,u=l.consumers.some(c=>c.entry_id===t.entry_id&&c.task_id===t.task_id);!d&&!u||s.push({part_id:l.part_id,name:l.name,entry_id:l.entry_id,foreign:!d,object_name:l.object_name})}for(let l of t.used_parts||[]){let d=l.entry_id||t.entry_id;s.some(u=>u.part_id===l.part_id&&u.entry_id===d)||s.push({part_id:l.part_id,name:l.name||l.part_id,entry_id:d,foreign:d!==t.entry_id,object_name:null})}let r={};for(let l of t.used_parts||[])r[`${l.entry_id||t.entry_id}:${l.part_id}`]=l.quantity??1;this._partOptions=s,this._partQty=r,this._partQtyOriginal=this._partSelectionKey()}catch{this._partOptions=[]}}_partSelectionKey(){return JSON.stringify(Object.entries(this._partQty).filter(([,t])=>t>0).sort(([t],[e])=>t.localeCompare(e)))}close(){this._open=!1,this._error="",this._draft=null,this._originalSnapshot=null}_set(t,e){this._draft&&(this._draft={...this._draft,[t]:e})}async _save(){if(!(!this._draft||!this._originalSnapshot)){this._saving=!0,this._error="";try{let t={type:"maintenance_supporter/task/history/update",entry_id:this._draft.entry_id,task_id:this._draft.task_id,original_timestamp:this._originalSnapshot.original_timestamp};if(this._draft.timestamp!==this._originalSnapshot.timestamp&&(t.timestamp=this._draft.timestamp),this._draft.notes!==this._originalSnapshot.notes&&(t.notes=this._draft.notes),this._draft.cost!==this._originalSnapshot.cost&&(t.cost=this._draft.cost),this._draft.duration!==this._originalSnapshot.duration&&(t.duration=this._draft.duration),this._draft.completed_by!==this._originalSnapshot.completed_by&&(t.completed_by=this._draft.completed_by),this._partOptions!==null&&this._partSelectionKey()!==this._partQtyOriginal&&(t.used_parts=(this._partOptions||[]).filter(s=>(this._partQty[`${s.entry_id}:${s.part_id}`]||0)>0).map(s=>({part_id:s.part_id,quantity:this._partQty[`${s.entry_id}:${s.part_id}`],...s.foreign?{entry_id:s.entry_id}:{}}))),Object.keys(t).filter(s=>!["type","entry_id","task_id","original_timestamp"].includes(s)).length===0){this.close();return}await this.hass.connection.sendMessagePromise(t),this.dispatchEvent(new CustomEvent("history-entry-saved",{detail:{entry_id:this._draft.entry_id,task_id:this._draft.task_id,new_timestamp:this._draft.timestamp},bubbles:!0,composed:!0})),this.close()}catch(t){this._error=A(t,this._lang)}finally{this._saving=!1}}}render(){if(!this._open||!this._draft)return p;let t=this._lang,e=this._draft;return o`
|
||||||
|
<div class="backdrop" @click=${this.close}></div>
|
||||||
|
<div class="dialog" role="dialog" aria-modal="true">
|
||||||
|
<h2>${a("history_edit_title",t)||"Edit history entry"}</h2>
|
||||||
|
<div class="entry-type">
|
||||||
|
<ha-icon icon="mdi:tag-outline"></ha-icon>
|
||||||
|
<span>${a(e.type,t)||e.type}</span>
|
||||||
|
</div>
|
||||||
|
<label>
|
||||||
|
<span>${a("history_edit_timestamp",t)||"Timestamp"}</span>
|
||||||
|
<input type="datetime-local"
|
||||||
|
.value=${e.timestamp.length>=16?e.timestamp.slice(0,16):e.timestamp}
|
||||||
|
@change=${s=>{let r=s.target.value;this._set("timestamp",r.length===16?`${r}:00`:r)}} />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span>${a("notes_label",t)}</span>
|
||||||
|
<textarea
|
||||||
|
rows="3"
|
||||||
|
@input=${s=>{let r=s.target.value;this._set("notes",r||null)}}
|
||||||
|
.value=${e.notes??""}></textarea>
|
||||||
|
</label>
|
||||||
|
<div class="row">
|
||||||
|
<label>
|
||||||
|
<span>${a("cost",t)||"Cost"}</span>
|
||||||
|
<input type="number" min="0" step="0.01"
|
||||||
|
.value=${e.cost!=null?String(e.cost):""}
|
||||||
|
@input=${s=>{let r=s.target.value;this._set("cost",r?Number(r):null)}} />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span>${a("duration",t)||"Duration (min)"}</span>
|
||||||
|
<input type="number" min="0"
|
||||||
|
.value=${e.duration!=null?String(e.duration):""}
|
||||||
|
@input=${s=>{let r=s.target.value;this._set("duration",r?Number(r):null)}} />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
${this._partOptions&&this._partOptions.length>0?o`
|
||||||
|
<div class="parts-block">
|
||||||
|
<span class="parts-title">${a("complete_parts_used",t)}</span>
|
||||||
|
${this._partOptions.map(s=>{let r=`${s.entry_id}:${s.part_id}`,l=this._partQty[r]||0;return o`
|
||||||
|
<label class="part-row-edit">
|
||||||
|
<input type="checkbox" .checked=${l>0}
|
||||||
|
@change=${d=>{let u=d.target.checked;this._partQty={...this._partQty,[r]:u?1:0}}} />
|
||||||
|
<span class="part-label">${s.name}${s.foreign&&s.object_name?` (${s.object_name})`:""}</span>
|
||||||
|
${l>0?o`
|
||||||
|
<input class="part-qty" type="number" min="0.01" max="999" step="0.01"
|
||||||
|
.value=${String(l)}
|
||||||
|
@input=${d=>{let u=parseFloat(d.target.value);!isNaN(u)&&u>0&&(this._partQty={...this._partQty,[r]:u})}} />
|
||||||
|
`:p}
|
||||||
|
</label>
|
||||||
|
`})}
|
||||||
|
</div>
|
||||||
|
`:p}
|
||||||
|
${this._error?o`<div class="error">${this._error}</div>`:p}
|
||||||
|
<div class="actions">
|
||||||
|
<button class="cancel" @click=${this.close} ?disabled=${this._saving}>
|
||||||
|
${a("cancel",t)||"Cancel"}
|
||||||
|
</button>
|
||||||
|
<button class="save" @click=${this._save} ?disabled=${this._saving}>
|
||||||
|
${this._saving?a("saving",t)||"Saving\u2026":a("save",t)||"Save"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}};k.styles=C`
|
||||||
|
:host { display: contents; }
|
||||||
|
.backdrop {
|
||||||
|
position: fixed; inset: 0;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.dialog {
|
||||||
|
position: fixed; left: 50%; top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 95vw; max-width: 480px;
|
||||||
|
background: var(--card-background-color, var(--ha-card-background, #1c1c1c));
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.4);
|
||||||
|
padding: 20px;
|
||||||
|
display: flex; flex-direction: column; gap: 12px;
|
||||||
|
z-index: 101;
|
||||||
|
max-height: 90vh; overflow: auto;
|
||||||
|
}
|
||||||
|
h2 { margin: 0; font-size: 18px; }
|
||||||
|
.entry-type {
|
||||||
|
display: flex; align-items: center; gap: 6px;
|
||||||
|
color: var(--secondary-text-color); font-size: 13px;
|
||||||
|
}
|
||||||
|
label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
|
||||||
|
label span { color: var(--secondary-text-color); }
|
||||||
|
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||||
|
input, textarea {
|
||||||
|
padding: 8px; font-size: 14px;
|
||||||
|
background: var(--secondary-background-color, #2c2c2c);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
border: 1px solid var(--divider-color, #444);
|
||||||
|
border-radius: 6px;
|
||||||
|
width: 100%; box-sizing: border-box;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
.actions {
|
||||||
|
display: flex; gap: 8px; justify-content: flex-end;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
padding: 8px 16px; font-size: 14px;
|
||||||
|
border-radius: 6px; cursor: pointer;
|
||||||
|
border: none; font-weight: 500;
|
||||||
|
}
|
||||||
|
button.cancel {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
button.save {
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: var(--text-primary-color, white);
|
||||||
|
}
|
||||||
|
button[disabled] { opacity: 0.5; cursor: wait; }
|
||||||
|
.error {
|
||||||
|
color: var(--error-color, #d32f2f);
|
||||||
|
font-size: 13px; padding: 8px;
|
||||||
|
background: rgba(211,47,47,0.1);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
/* #130: parts on the entry */
|
||||||
|
.parts-block {
|
||||||
|
display: flex; flex-direction: column; gap: 6px;
|
||||||
|
border: 1px solid var(--divider-color, #444);
|
||||||
|
border-radius: 6px; padding: 8px;
|
||||||
|
}
|
||||||
|
.parts-title { color: var(--secondary-text-color); font-size: 13px; }
|
||||||
|
.part-row-edit {
|
||||||
|
display: flex; flex-direction: row; align-items: center; gap: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.part-row-edit input[type="checkbox"] { width: auto; }
|
||||||
|
.part-label { flex: 1; color: var(--primary-text-color); }
|
||||||
|
.part-qty { width: 76px; }
|
||||||
|
`,m([j({attribute:!1})],k.prototype,"hass",2),m([y()],k.prototype,"_open",2),m([y()],k.prototype,"_saving",2),m([y()],k.prototype,"_error",2),m([y()],k.prototype,"_draft",2),m([y()],k.prototype,"_partOptions",2),m([y()],k.prototype,"_partQty",2);customElements.get("maintenance-history-edit-dialog")||customElements.define("maintenance-history-edit-dialog",k);var xt={days:1,weeks:7,months:30.4368,years:365.25};function K(n,i){return!n||n<=0?0:n*(xt[i||"days"]??1)}function Kt(n,i,t){let e=K(n,t);if(e<=0||i==null)return{pct:0,overflow:!1};let s=(e-i)/e*100;return{pct:Math.max(0,Math.min(100,s)),overflow:s>100}}var st=5;function R(n){let i=n.getFullYear(),t=String(n.getMonth()+1).padStart(2,"0"),e=String(n.getDate()).padStart(2,"0");return`${i}-${t}-${e}`}function $t(n,i){let t=[];for(let e=0;e<i;e++){let s=new Date(n);s.setDate(s.getDate()+e),s.setHours(0,0,0,0),t.push(R(s))}return t}function V(n,i){let[t,e,s]=n.split("-").map(Number),r=new Date(t,e-1,s);return r.setDate(r.getDate()+i),R(r)}function kt(n){if(!n||n.length===0)return null;let i=n.map(t=>t.cost).filter(t=>typeof t=="number");return i.length===0?null:i.reduce((t,e)=>t+e,0)/i.length}function wt(n){let{windowStart:i,windowEnd:t,task:e,entryId:s,objectName:r}=n,l=[],d=(_,g)=>({date:_,entry_id:s,task_id:e.id,task_name:e.name,object_name:r,status:g&&(e.status==="overdue"||e.status==="triggered")?"ok":e.status,days_until_due:g?null:e.days_until_due??null,projected:g,schedule_type:e.schedule_type,interval_days:e.interval_days??null,interval_unit:e.interval_unit??null,responsible_user_id:e.responsible_user_id??null,avg_cost:kt(e.history),adaptive_enabled:!!e.adaptive_config?.enabled,prediction_confidence:e.threshold_prediction_confidence??null}),u=Math.max(1,Math.round(K(e.interval_days,e.interval_unit)));if(e.status==="overdue"||e.status==="triggered"){if(l.push(d(i,!1)),e.schedule_type==="time_based"&&e.interval_days&&e.interval_days>0){let _=V(i,u),g=1;for(;_<=t&&g<st;)l.push(d(_,!0)),g++,_=V(_,u)}return l}let c=e.next_due;if(typeof c!="string"||!c)return l;let h=c.slice(0,10);if(h>=i&&h<=t)l.push(d(h,!1));else if(h>t)return l;if(e.schedule_type==="time_based"&&e.interval_days&&e.interval_days>0){let _=V(h,u),g=l.length;for(;_<=t&&g<st;)_>=i&&(l.push(d(_,!0)),g++),_=V(_,u)}return l}var it={overdue:0,triggered:1,due_soon:2,ok:3};function Zt(n,i,t,e=null){let s=$t(i,t),r=s[0],l=s[s.length-1],d=[];for(let c of n){let h=c.object?.name||"",_=c.entry_id,g=c.tasks||[];for(let f of g){if(e&&f.responsible_user_id!==e||f.enabled===!1)continue;let x=wt({windowStart:r,windowEnd:l,task:f,entryId:_,objectName:h});d.push(...x)}}let u=new Map;for(let c of s)u.set(c,[]);for(let c of d){let h=u.get(c.date);h&&h.push(c)}for(let[,c]of u)c.sort((h,_)=>{let g=it[h.status]??99,f=it[_.status]??99;if(g!==f)return g-f;if(h.projected!==_.projected)return h.projected?1:-1;let x=h.object_name.localeCompare(_.object_name);return x!==0?x:h.task_name.localeCompare(_.task_name)});return s.map(c=>({date:c,events:u.get(c)??[]}))}var At={completed:"ok",reset:"ok",skipped:"due_soon",missed:"overdue",triggered:"triggered",trigger_replaced:"triggered",trigger_removed:"ok"};function Lt(n,i){let t=[];for(let e=i-1;e>=0;e--){let s=new Date(n);s.setDate(s.getDate()-e),s.setHours(0,0,0,0),t.push(R(s))}return t}function Yt(n,i,t,e=null){let s=Lt(i,t),r=s[0],l=s[s.length-1],d=new Map;for(let c of s)d.set(c,[]);for(let c of n){let h=c.object?.name||"",_=c.entry_id,g=c.tasks||[];for(let f of g){if(e&&f.responsible_user_id!==e)continue;let x=f.history||[];for(let v of x){if(typeof v?.timestamp!="string")continue;let M=v.timestamp.slice(0,10);if(M<r||M>l)continue;let I=d.get(M);if(!I)continue;let D=v.type??"completed";I.push({date:M,entry_id:_,task_id:f.id,task_name:f.name,object_name:h,status:At[D]??"ok",days_until_due:null,projected:!1,schedule_type:f.schedule_type,interval_days:f.interval_days??null,responsible_user_id:f.responsible_user_id??null,avg_cost:typeof v.cost=="number"?v.cost:null,adaptive_enabled:!!f.adaptive_config?.enabled,prediction_confidence:null,history_timestamp:v.timestamp,history_type:D,history_cost:typeof v.cost=="number"?v.cost:null,history_notes:typeof v.notes=="string"?v.notes:null,history_duration:typeof v.duration=="number"?v.duration:null})}}}let u={completed:0,reset:1,skipped:2,triggered:3,trigger_replaced:4};for(let[,c]of d)c.sort((h,_)=>{let g=u[h.history_type??""]??99,f=u[_.history_type??""]??99;if(g!==f)return g-f;let x=h.object_name.localeCompare(_.object_name);return x!==0?x:h.task_name.localeCompare(_.task_name)});return s.map(c=>({date:c,events:d.get(c)??[]}))}function Mt(n,i){if(i<=0)return 0;let t=typeof n=="number"&&Number.isFinite(n)?Math.trunc(n):0;return t<0?0:t%i}function Dt(n){return!!(n?.phases&&n.phase_sequence&&n.phase_sequence.length>0)}function U(n){if(!n||!Dt(n))return null;let i=n.phase_sequence,t=Mt(n.phase_cursor,i.length),e=i[t],s=n.phases?.[e];return s?{id:e,name:s.name,index:t,count:i.length,notes:s.notes,checklist:s.checklist!==void 0?s.che
Line truncated
|
||||||
|
<div class="weibull-section">
|
||||||
|
<div class="weibull-title">
|
||||||
|
<ha-svg-icon aria-hidden="true" path="M3,14L3.5,14.07L8.07,9.5C7.89,8.85 8.06,8.11 8.59,7.59C9.37,6.8 10.63,6.8 11.41,7.59C11.94,8.11 12.11,8.85 11.93,9.5L14.5,12.07L15,12C15.18,12 15.35,12 15.5,12.07L19.07,8.5C19,8.35 19,8.18 19,8A2,2 0 0,1 21,6A2,2 0 0,1 23,8A2,2 0 0,1 21,10C20.82,10 20.65,10 20.5,9.93L16.93,13.5C17,13.65 17,13.82 17,14A2,2 0 0,1 15,16A2,2 0 0,1 13,14L13.07,13.5L10.5,10.93C10.18,11 9.82,11 9.5,10.93L4.93,15.5L5,16A2,2 0 0,1 3,18A2,2 0 0,1 1,16A2,2 0 0,1 3,14Z"></ha-svg-icon>
|
||||||
|
${a("weibull_reliability_curve",i)}
|
||||||
|
${Pt(e,i)}
|
||||||
|
</div>
|
||||||
|
${Tt(e,s,r,l,i)}
|
||||||
|
${Et(t,i)}
|
||||||
|
${t?.confidence_interval_low!=null?It(t,n,i):p}
|
||||||
|
</div>
|
||||||
|
`}function Pt(n,i){let t,e,s;return n<.8?(t="early_failures",e="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z",s="beta_early_failures"):n<=1.2?(t="random_failures",e="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M13,17H11V15H13V17M13,13H11V7H13V13Z",s="beta_random_failures"):n<=3.5?(t="wear_out",e="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M12,6A6,6 0 0,1 18,12H12V6Z",s="beta_wear_out"):(t="highly_predictable",e="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z",s="beta_highly_predictable"),o`
|
||||||
|
<span class="beta-badge ${t}">
|
||||||
|
<ha-svg-icon path="${e}"></ha-svg-icon>
|
||||||
|
${a(s,i)} (\u03B2=${n.toFixed(2)})
|
||||||
|
</span>
|
||||||
|
`}function Tt(n,i,t,e,s){let f=Math.max(t,e,i,1)*1.3,x=50,v=[];for(let $=0;$<=x;$++){let w=$/x*f,gt=1-Math.exp(-Math.pow(w/i,n)),vt=32+w/f*260,bt=136-gt*128;v.push([vt,bt])}let M=v.map(([$,w])=>`${$.toFixed(1)},${w.toFixed(1)}`).join(" "),I="M32,136 "+v.map(([$,w])=>`L${$.toFixed(1)},${w.toFixed(1)}`).join(" ")+` L${v[x][0].toFixed(1)},136 Z`,D=32+t/f*260,F=1-Math.exp(-Math.pow(t/i,n)),z=136-F*128,ft=((1-F)*100).toFixed(0),G=32+e/f*260,yt=[0,.25,.5,.75,1];return o`
|
||||||
|
<div class="weibull-chart">
|
||||||
|
<svg viewBox="0 0 ${300} ${160}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${a("chart_weibull",s)}">
|
||||||
|
${yt.map($=>{let w=136-$*128;return H`
|
||||||
|
<line x1="${32}" y1="${w.toFixed(1)}" x2="${292}" y2="${w.toFixed(1)}"
|
||||||
|
stroke="var(--divider-color)" stroke-width="0.5" stroke-dasharray="${$===.5?"4,3":p}" />
|
||||||
|
<text x="${28}" y="${(w+3).toFixed(1)}" fill="var(--secondary-text-color)"
|
||||||
|
font-size="8" text-anchor="end">${($*100).toFixed(0)}%</text>
|
||||||
|
`})}
|
||||||
|
|
||||||
|
<text x="${32}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">0</text>
|
||||||
|
<text x="${324/2}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">${Math.round(f/2)}</text>
|
||||||
|
<text x="${292}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">${Math.round(f)}</text>
|
||||||
|
|
||||||
|
<path d="${I}" fill="var(--primary-color, #03a9f4)" opacity="0.08" />
|
||||||
|
<polyline points="${M}" fill="none"
|
||||||
|
stroke="var(--primary-color, #03a9f4)" stroke-width="2" />
|
||||||
|
|
||||||
|
${t>0?H`
|
||||||
|
<line x1="${D.toFixed(1)}" y1="${8}" x2="${D.toFixed(1)}" y2="${136 .toFixed(1)}"
|
||||||
|
stroke="var(--primary-color, #03a9f4)" stroke-width="1.5" stroke-dasharray="4,3" />
|
||||||
|
<circle cx="${D.toFixed(1)}" cy="${z.toFixed(1)}" r="3"
|
||||||
|
fill="var(--primary-color, #03a9f4)" />
|
||||||
|
<text x="${(D+4).toFixed(1)}" y="${(z-6).toFixed(1)}" fill="var(--primary-color, #03a9f4)"
|
||||||
|
font-size="9" font-weight="600">R=${ft}%</text>
|
||||||
|
`:p}
|
||||||
|
|
||||||
|
${e>0&&e!==t?H`
|
||||||
|
<line x1="${G.toFixed(1)}" y1="${8}" x2="${G.toFixed(1)}" y2="${136 .toFixed(1)}"
|
||||||
|
stroke="var(--success-color, #4caf50)" stroke-width="1.5" stroke-dasharray="4,3" />
|
||||||
|
`:p}
|
||||||
|
|
||||||
|
<line x1="${32}" y1="${8}" x2="${32}" y2="${136}"
|
||||||
|
stroke="var(--secondary-text-color)" stroke-width="1" />
|
||||||
|
<line x1="${32}" y1="${136}" x2="${292}" y2="${136}"
|
||||||
|
stroke="var(--secondary-text-color)" stroke-width="1" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="chart-legend">
|
||||||
|
<span class="legend-item"><span class="legend-swatch" style="background:var(--primary-color, #03a9f4)"></span> ${a("weibull_failure_probability",s)}</span>
|
||||||
|
${t>0?o`<span class="legend-item"><span class="legend-swatch" style="background:var(--primary-color, #03a9f4); opacity:0.5"></span> ${a("current_interval_marker",s)}</span>`:p}
|
||||||
|
${e>0&&e!==t?o`<span class="legend-item"><span class="legend-swatch" style="background:var(--success-color, #4caf50)"></span> ${a("recommended_marker",s)}</span>`:p}
|
||||||
|
</div>
|
||||||
|
`}function Et(n,i){return o`
|
||||||
|
<div class="weibull-info-row">
|
||||||
|
<div class="weibull-info-item">
|
||||||
|
<span>${a("characteristic_life",i)}</span>
|
||||||
|
<span class="weibull-info-value">${Math.round(n.weibull_eta)} ${a("days",i)}</span>
|
||||||
|
</div>
|
||||||
|
${n.weibull_r_squared!=null?o`
|
||||||
|
<div class="weibull-info-item">
|
||||||
|
<span>${a("weibull_r_squared",i)}</span>
|
||||||
|
<span class="weibull-info-value">${n.weibull_r_squared.toFixed(3)}</span>
|
||||||
|
</div>
|
||||||
|
`:p}
|
||||||
|
</div>
|
||||||
|
`}function It(n,i,t){let e=n.confidence_interval_low,s=n.confidence_interval_high,r=i.suggested_interval??i.interval_days??0,l=i.interval_days??0,d=Math.max(0,e-5),c=s+5-d,h=(e-d)/c*100,_=(s-e)/c*100,g=(r-d)/c*100,f=l>0?(l-d)/c*100:-1;return o`
|
||||||
|
<div class="confidence-range">
|
||||||
|
<div class="confidence-range-title">
|
||||||
|
${a("confidence_interval",t)}: ${r} ${a("days",t)} (${e}\u2013${s})
|
||||||
|
</div>
|
||||||
|
<div class="confidence-bar">
|
||||||
|
<div class="confidence-fill" style="left:${h.toFixed(1)}%;width:${_.toFixed(1)}%"></div>
|
||||||
|
${f>=0?o`<div class="confidence-marker current" style="left:${f.toFixed(1)}%"></div>`:p}
|
||||||
|
<div class="confidence-marker recommended" style="left:${g.toFixed(1)}%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="confidence-labels">
|
||||||
|
<span class="confidence-text low">${a("confidence_conservative",t)} (${e}${a("days",t).charAt(0)})</span>
|
||||||
|
<span class="confidence-text high">${a("confidence_aggressive",t)} (${s}${a("days",t).charAt(0)})</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}function rt(n,i,t){let e=n.degradation_trend!=null&&n.degradation_trend!=="insufficient_data",s=n.days_until_threshold!=null,r=n.environmental_factor!=null&&n.environmental_factor!==1;if(!e&&!s&&!r)return p;let l=n.degradation_trend==="rising"?"M16,6L18.29,8.29L13.41,13.17L9.41,9.17L2,16.59L3.41,18L9.41,12L13.41,16L19.71,9.71L22,12V6H16Z":n.degradation_trend==="falling"?"M16,18L18.29,15.71L13.41,10.83L9.41,14.83L2,7.41L3.41,6L9.41,12L13.41,8L19.71,14.29L22,12V18H16Z":"M22,12L18,8V11H3V13H18V16L22,12Z";return o`
|
||||||
|
<div class="prediction-section">
|
||||||
|
${n.sensor_prediction_urgency?o`
|
||||||
|
<div class="prediction-urgency-banner">
|
||||||
|
<ha-svg-icon path="M1,21H23L12,2L1,21M12,18A1,1 0 0,1 11,17A1,1 0 0,1 12,16A1,1 0 0,1 13,17A1,1 0 0,1 12,18M13,15H11V10H13V15Z"></ha-svg-icon>
|
||||||
|
${a("sensor_prediction_urgency",i).replace("{days}",String(Math.round(n.days_until_threshold||0)))}
|
||||||
|
</div>
|
||||||
|
`:p}
|
||||||
|
<div class="prediction-title">
|
||||||
|
<ha-svg-icon path="M2,2V4H7V2H2M22,2V4H13V2H22M7,7V9H2V7H7M22,7V9H13V7H22M7,12V14H2V12H7M22,12V14H13V12H22M7,17V19H2V17H7M22,17V19H13V17H22M9,2V19L12,22L15,19V2H9M11,4H13V17.17L12,18.17L11,17.17V4Z"></ha-svg-icon>
|
||||||
|
${a("sensor_prediction",i)}
|
||||||
|
</div>
|
||||||
|
<div class="prediction-grid">
|
||||||
|
${e?o`
|
||||||
|
<div class="prediction-item">
|
||||||
|
<ha-svg-icon path="${l}"></ha-svg-icon>
|
||||||
|
<span class="prediction-label">${a("degradation_trend",i)}</span>
|
||||||
|
<span class="prediction-value ${n.degradation_trend}">${a("trend_"+n.degradation_trend,i)}</span>
|
||||||
|
${n.degradation_rate!=null?o`<span class="prediction-rate">${n.degradation_rate>0?"+":""}${Math.abs(n.degradation_rate)>=10?Math.round(n.degradation_rate).toLocaleString():n.degradation_rate.toFixed(1)} ${n.trigger_entity_info?.unit_of_measurement||""}/${a("day_short",i)}</span>`:p}
|
||||||
|
</div>
|
||||||
|
`:p}
|
||||||
|
${s?o`
|
||||||
|
<div class="prediction-item">
|
||||||
|
<ha-svg-icon path="M12,20A7,7 0 0,1 5,13A7,7 0 0,1 12,6A7,7 0 0,1 19,13A7,7 0 0,1 12,20M12,4A9,9 0 0,0 3,13A9,9 0 0,0 12,22A9,9 0 0,0 21,13A9,9 0 0,0 12,4M12.5,8H11V14L15.75,16.85L16.5,15.62L12.5,13.25V8M7.88,3.39L6.6,1.86L2,5.71L3.29,7.24L7.88,3.39M22,5.72L17.4,1.86L16.11,3.39L20.71,7.25L22,5.72Z"></ha-svg-icon>
|
||||||
|
<span class="prediction-label">${a("days_until_threshold",i)}</span>
|
||||||
|
<span class="prediction-value prediction-days${n.days_until_threshold===0?" exceeded":n.sensor_prediction_urgency?" urgent":""}">${n.days_until_threshold===0?a("threshold_exceeded",i):"~"+Math.round(n.days_until_threshold)+" "+a("days",i)}</span>
|
||||||
|
${n.threshold_prediction_date?o`<span class="prediction-date">${O(n.threshold_prediction_date,i)}</span>`:p}
|
||||||
|
${n.threshold_prediction_confidence?o`<span class="confidence-dot ${n.threshold_prediction_confidence}"></span>`:p}
|
||||||
|
</div>
|
||||||
|
`:p}
|
||||||
|
${r&&t.environmental?o`
|
||||||
|
<div class="prediction-item">
|
||||||
|
<ha-svg-icon path="M15,13V5A3,3 0 0,0 12,2A3,3 0 0,0 9,5V13A5,5 0 0,0 7,17A5,5 0 0,0 12,22A5,5 0 0,0 17,17A5,5 0 0,0 15,13M12,4A1,1 0 0,1 13,5V8H11V5A1,1 0 0,1 12,4Z"></ha-svg-icon>
|
||||||
|
<span class="prediction-label">${a("environmental_adjustment",i)}</span>
|
||||||
|
<span class="prediction-value">${n.environmental_factor.toFixed(2)}x</span>
|
||||||
|
${n.environmental_entity?o`<span class="prediction-entity entity-link" @click=${d=>tt(d,n.environmental_entity)}>${n.environmental_entity}</span>`:p}
|
||||||
|
</div>
|
||||||
|
`:p}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}function ot(n,i,t,e){let s=Math.max(n||1,i);return o`
|
||||||
|
<div class="interval-comparison">
|
||||||
|
<div class="interval-bar">
|
||||||
|
<div class="interval-label">
|
||||||
|
${a("current",e)}: ${n??"\u2014"} ${n!=null?a("days",e):""}
|
||||||
|
</div>
|
||||||
|
<div class="interval-visual current"
|
||||||
|
style="width: ${n!=null?Math.min(n/s*100,100):0}%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="interval-bar">
|
||||||
|
<div class="interval-label">
|
||||||
|
${a("recommended",e)}: ${i} ${a("days",e)}
|
||||||
|
<span class="confidence-badge ${t}">${a(`confidence_${t}`,e)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="interval-visual suggested"
|
||||||
|
style="width: ${Math.min(i/s*100,100)}%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}var lt=["month_jan","month_feb","month_mar","month_apr","month_may","month_jun","month_jul","month_aug","month_sep","month_oct","month_nov","month_dec"];function dt(n,i,t){if(!t.seasonal||!n.seasonal_factor||n.seasonal_factor===1)return p;let e=lt.map(d=>a(d,i)),s=new Date().getMonth(),r=n.seasonal_factors||n.interval_analysis?.seasonal_factors||null,l=r&&r.length===12?r:e.map((d,u)=>{let c=n.seasonal_factor||1,h=Math.sin((u-6)*Math.PI/6)*.3;return Math.max(.7,Math.min(1.3,c+h))});return o`
|
||||||
|
<div class="seasonal-card-compact">
|
||||||
|
<h4>${a("seasonal_awareness",i)}</h4>
|
||||||
|
<div class="seasonal-mini-chart">
|
||||||
|
${l.map((d,u)=>{let c=d*40,h=d<.9?"low":d>1.1?"high":"normal";return o`
|
||||||
|
<div class="seasonal-bar ${h} ${u===s?"current":""}"
|
||||||
|
style="height: ${c}px"
|
||||||
|
title="${e[u]}: ${d.toFixed(2)}x">
|
||||||
|
</div>
|
||||||
|
`})}
|
||||||
|
</div>
|
||||||
|
<div class="seasonal-legend">
|
||||||
|
<span class="legend-item"><span class="dot low"></span> ${a("shorter",i)||"K\xFCrzer"}</span>
|
||||||
|
<span class="legend-item"><span class="dot normal"></span> ${a("normal",i)||"Normal"}</span>
|
||||||
|
<span class="legend-item"><span class="dot high"></span> ${a("longer",i)||"L\xE4nger"}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}function ct(n,i){return Ct(n,i)}function Ct(n,i){let t=n.seasonal_factors??n.interval_analysis?.seasonal_factors;if(!t||t.length!==12)return p;let e=n.interval_analysis?.seasonal_reason,s=new Date().getMonth(),r=300,l=100,d=8,c=l-d-4,h=Math.max(...t,1.5),_=r/12,g=_*.65,f=d+c-1/h*c;return o`
|
||||||
|
<div class="seasonal-chart">
|
||||||
|
<div class="seasonal-chart-title">
|
||||||
|
<ha-svg-icon aria-hidden="true" path="M17.75 4.09L15.22 6.03L16.13 9.09L13.5 7.28L10.87 9.09L11.78 6.03L9.25 4.09L12.44 4L13.5 1L14.56 4L17.75 4.09M21.25 11L19.61 12.25L20.2 14.23L18.5 13.06L16.8 14.23L17.39 12.25L15.75 11L17.81 10.95L18.5 9L19.19 10.95L21.25 11M18.97 15.95C19.8 15.87 20.69 17.05 20.16 17.8C19.84 18.25 19.5 18.67 19.08 19.07C15.17 23 8.84 23 4.94 19.07C1.03 15.17 1.03 8.83 4.94 4.93C5.34 4.53 5.76 4.17 6.21 3.85C6.96 3.32 8.14 4.21 8.06 5.04C7.79 7.9 8.75 10.87 10.95 13.06C13.14 15.26 16.1 16.22 18.97 15.95Z"></ha-svg-icon>
|
||||||
|
${a("seasonal_chart_title",i)}
|
||||||
|
${e?o`<span class="source-tag">${e==="learned"?a("seasonal_learned",i):a("seasonal_manual",i)}</span>`:p}
|
||||||
|
</div>
|
||||||
|
<svg viewBox="0 0 ${r} ${l}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${a("chart_seasonal",i)}">
|
||||||
|
<line x1="0" y1="${f.toFixed(1)}" x2="${r}" y2="${f.toFixed(1)}"
|
||||||
|
stroke="var(--divider-color)" stroke-width="1" stroke-dasharray="4,3" />
|
||||||
|
${t.map((x,v)=>{let M=x/h*c,I=v*_+(_-g)/2,D=d+c-M,F=v===s,z=x<1?"var(--success-color, #4caf50)":x>1?"var(--warning-color, #ff9800)":"var(--secondary-text-color)";return H`
|
||||||
|
<rect x="${I.toFixed(1)}" y="${D.toFixed(1)}"
|
||||||
|
width="${g.toFixed(1)}" height="${M.toFixed(1)}"
|
||||||
|
fill="${z}" opacity="${F?1:.5}" rx="2" />
|
||||||
|
`})}
|
||||||
|
</svg>
|
||||||
|
<div class="seasonal-labels">
|
||||||
|
${lt.map((x,v)=>o`<span class="seasonal-label ${v===s?"active-month":""}">${a(x,i)}</span>`)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}var b=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._taskId=null;this._task=null;this._objectName="";this._busy=!1;this._error="";this._showSkip=!1;this._showReset=!1;this._showDetails=!1;this._showAdaptive=!1;this._skipReason="";this._resetDate="";this._features={adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1};this._toast="";this._featuresLoaded=!1}get _lang(){return E(this.hass)}async openFor(t,e){this._entryId=t,this._taskId=e,this._error="",this._showSkip=!1,this._showReset=!1,this._showAdaptive=!1,this._skipReason="",this._resetDate=R(new Date),this._open=!0,await Promise.all([this._loadTask(),this._loadFeatures()])}async _loadFeatures(){if(!this._featuresLoaded)try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/settings"});t?.features&&(this._features={...this._features,...t.features}),this._featuresLoaded=!0}catch{}}close(){this._open=!1,this._task=null,this._error=""}async _loadTask(){if(!(!this._entryId||!this._taskId))try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._objectName=t.object?.name||"";let e=(t.tasks||[]).find(s=>s.id===this._taskId);this._task=e??null}catch(t){this._error=A(t,this._lang)}}async _runWs(t){this._busy=!0,this._error="";try{return await this.hass.connection.sendMessagePromise(t),this._busy=!1,!0}catch(e){return this._error=A(e,this._lang),this._busy=!1,!1}}_notifyChanged(t){this.dispatchEvent(new CustomEvent("task-action-fired",{detail:{entry_id:this._entryId,task_id:this._taskId,action:t},bubbles:!0,composed:!0}))}_onComplete(){!this._entryId||!this._taskId||!this._task||import("/maintenance_supporter_panelfiles/panel-chunks/dialog-mount-6AVGUS2J.js").then(async({openCompleteDialog:t})=>{let e=this._task,s=!!e.part_ref,r=U(e),l=r?r.consumesParts:e.consumes_parts||[],d=[];if(!s)try{let c=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0});d=nt({consumes_parts:l},this._entryId,c.objects||[],this._lang)}catch{}t({entry_id:this._entryId,task_id:this._taskId,task_name:e.name,checklist:r?r.checklist:e.checklist||[],adaptive_enabled:!!e.adaptive_config?.enabled,required_completion_fields:r?r.requiredFields:e.required_completion_fields||[],task_type:e.type||"",reading_unit:e.reading_unit||"",parts:d,consumes_parts:s?[]:l,phase_label:W(e)})&&(this._notifyChanged("complete"),this.close())})}async _onSkipConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/skip",entry_id:this._entryId,task_id:this._taskId,reason:this._skipReason.trim()||null})&&(this._notifyChanged("skip"),this.close())}async _onResetConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/reset",entry_id:this._entryId,task_id:this._taskId,date:this._resetDate||void 0})&&(this._notifyChanged("reset"),this.close())}_onEdit(){!this._entryId||!this._taskId||import("/maintenance_supporter_panelfiles/panel-chunks/dialog-mount-6AVGUS2J.js").then(({openEditTaskDialog:t})=>{t(this._entryId,this._taskId),this.close()})}_onQr(){!this._entryId||!this._taskId||!this._task||import("/maintenance_supporter_panelfiles/panel-chunks/dialog-mount-6AVGUS2J.js").then(({openQrDialog:t})=>{t({entry_id:this._entryId,task_id:this._taskId,task_name:this._task.name,object_name:this._objectName}),this.close()})}async _onDelete(){if(!this._entryId||!this._taskId)return;let t=a("delete_task_confirm",this._lang)||`Delete "${this._task?.name}"?`;if(!window.confirm(t))return;await this._runWs({type:"maintenance_supporter/task/delete",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("delete"),this.close())}async _onArchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/archive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("archive"),this.close())}async _onUnarchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/unarchive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("unarchive"),this.close())}_onOpenInPanel(){if(!this._entryId||!this._taskId)return;let t=`/maintenance-supporter?entry_id=${encodeURIComponent(this._entryId)}&task_id=${encodeURIComponent(this._taskId)}`;history.pushState(null,"",t),window.dispatchEvent(new CustomEvent("location-changed")),this.close()}async _applySuggestion(){if(!this._entryId||!this._taskId||!this._task?.suggested_interval)return;await this._runWs({type:"maintenance_supporter/task/apply_suggestion",entry_id:this._entryId,task_id:this._taskId,interval:this._task.suggested_interval})&&(this._toast=a("suggestion_applied",this._lang)||"Applied",this._notifyChanged("apply_suggestion"),await this._loadTask(),setTimeout(()=>{this._toast=""},2500))}async _reanalyzeInterval(){if(!(!this._entryId||!this
Line truncated
|
||||||
|
<div class="recommendation-card">
|
||||||
|
<h4>${a("suggested_interval",e)}</h4>
|
||||||
|
${ot(t.interval_days,t.suggested_interval,t.interval_confidence||"medium",e)}
|
||||||
|
<div class="recommendation-actions">
|
||||||
|
<button class="btn primary"
|
||||||
|
@click=${this._applySuggestion} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:check"></ha-icon>
|
||||||
|
${a("apply_suggestion",e)}
|
||||||
|
</button>
|
||||||
|
<button class="btn"
|
||||||
|
@click=${this._reanalyzeInterval} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:refresh"></ha-icon>
|
||||||
|
${a("reanalyze",e)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}_renderAdaptive(t){let e=this._lang,s=this._features.adaptive&&t.suggested_interval&&t.suggested_interval!==t.interval_days,r=t.degradation_trend!=null&&t.degradation_trend!=="insufficient_data"||t.days_until_threshold!=null||t.environmental_factor!=null&&t.environmental_factor!==1,l=this._features.adaptive&&t.interval_analysis?.weibull_beta!=null&&t.interval_analysis?.weibull_eta!=null,d=this._features.seasonal&&t.seasonal_factor&&t.seasonal_factor!==1;return!s&&!r&&!l&&!d?o`<div class="adaptive-empty">
|
||||||
|
${a("adaptive_no_data",e)||"Not enough completion history yet for adaptive analysis."}
|
||||||
|
</div>`:o`
|
||||||
|
<div class="adaptive-stack">
|
||||||
|
${this._toast?o`<div class="toast">${this._toast}</div>`:p}
|
||||||
|
${s?this._renderRecommendation(t):p}
|
||||||
|
${r?rt(t,e,this._features):p}
|
||||||
|
${l?at(t,e):p}
|
||||||
|
${d?o`
|
||||||
|
${dt(t,e,this._features)}
|
||||||
|
${t.seasonal_factors?.length===12||t.interval_analysis?.seasonal_factors?.length===12?ct(t,e):p}
|
||||||
|
`:p}
|
||||||
|
</div>
|
||||||
|
`}_renderDetails(t){let e=this._lang,s=t.history||[],r=s.filter(u=>u.type==="completed"),l=r.reduce((u,c)=>u+(typeof c.cost=="number"?c.cost:0),0),d=(()=>{let u=r.map(c=>typeof c.duration=="number"?c.duration:null).filter(c=>c!=null);return u.length?Math.round(u.reduce((c,h)=>c+h,0)/u.length):null})();return o`
|
||||||
|
<div class="details">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-label">${a("times_performed",e)||"Performed"}</span>
|
||||||
|
<span class="stat-value">${r.length}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-label">${a("total_cost",e)||"Total cost"}</span>
|
||||||
|
<span class="stat-value">${l.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-label">${a("avg_duration",e)||"Avg duration"}</span>
|
||||||
|
<span class="stat-value">${d!=null?`${d}m`:"\u2014"}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="history-header">
|
||||||
|
<strong>${a("history",e)||"History"}</strong>
|
||||||
|
<span class="history-count">${s.length}</span>
|
||||||
|
</div>
|
||||||
|
${s.length===0?o`<div class="history-empty">${a("history_empty",e)||"No history yet."}</div>`:o`
|
||||||
|
<div class="history-list">
|
||||||
|
${[...s].reverse().slice(0,20).map(u=>{let c=["completed","reset","skipped"].includes(u.type);return o`
|
||||||
|
<div class="history-entry">
|
||||||
|
<div class="history-line">
|
||||||
|
<span class="history-type type-${u.type}">${a(u.type,e)}</span>
|
||||||
|
<span class="history-date">${Q(u.timestamp,e)}</span>
|
||||||
|
${c?o`<button class="history-edit"
|
||||||
|
title="${a("history_edit_button",e)||"Edit"}"
|
||||||
|
@click=${()=>this._onEditHistoryEntry(u)}>
|
||||||
|
<ha-icon icon="mdi:pencil"></ha-icon>
|
||||||
|
</button>`:p}
|
||||||
|
</div>
|
||||||
|
${u.notes?o`<div class="history-notes">${u.notes}</div>`:p}
|
||||||
|
${u.cost!=null||u.duration!=null?o`<div class="history-meta">
|
||||||
|
${u.cost!=null?o`<span>💰 ${u.cost.toFixed(2)}</span>`:p}
|
||||||
|
${u.duration!=null?o`<span>⏱️ ${u.duration}m</span>`:p}
|
||||||
|
</div>`:p}
|
||||||
|
</div>
|
||||||
|
`})}
|
||||||
|
${s.length>20?o`<div class="history-more">… +${s.length-20} ${a("older_entries",e)||"older"}</div>`:p}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
`}render(){if(!this._open)return p;let t=this._lang,e=this._task,s=this.hass?.user?.is_admin??!0;return o`
|
||||||
|
<div class="backdrop" @click=${this.close}></div>
|
||||||
|
<div class="dialog" role="dialog" aria-modal="true">
|
||||||
|
${e?o`
|
||||||
|
<div class="header">
|
||||||
|
<div class="title">
|
||||||
|
<span class="status-dot" style="background: ${q[e.status]||"#ccc"}"></span>
|
||||||
|
<span class="task-name">${e.name}</span>
|
||||||
|
</div>
|
||||||
|
<div class="object">
|
||||||
|
<button class="link-inline" @click=${()=>{this._entryId&&import("/maintenance_supporter_panelfiles/panel-chunks/dialog-mount-6AVGUS2J.js").then(({openObjectQuickActions:r})=>{r(this._entryId),this.close()})}}>${this._objectName}</button>
|
||||||
|
</div>
|
||||||
|
<div class="quick-info">
|
||||||
|
${e.next_due?o`<span><strong>${a("next_due",t)||"Next due"}:</strong> ${O(e.next_due,t)}</span>`:p}
|
||||||
|
${e.last_performed?o`<span><strong>${a("last_performed",t)||"Last"}:</strong> ${O(e.last_performed,t)}</span>`:p}
|
||||||
|
${e.schedule?.kind&&!["manual","one_time"].includes(e.schedule.kind)||e.interval_days!=null?o`<span><strong>${a("interval",t)||"Interval"}:</strong> ${X(e,t)}</span>`:p}
|
||||||
|
${W(e)?o`<span><strong>${a("phase_current",t)}:</strong> ${W(e)}</span>`:p}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${this._error?o`<div class="error">${this._error}</div>`:p}
|
||||||
|
|
||||||
|
${this._showSkip?o`
|
||||||
|
<div class="inline-form">
|
||||||
|
<label>${a("skip_reason",t)||"Skip reason (optional)"}</label>
|
||||||
|
<input type="text" .value=${this._skipReason}
|
||||||
|
@input=${r=>{this._skipReason=r.target.value}} />
|
||||||
|
<div class="inline-actions">
|
||||||
|
<button class="btn cancel" @click=${()=>{this._showSkip=!1}} ?disabled=${this._busy}>
|
||||||
|
${a("cancel",t)||"Cancel"}
|
||||||
|
</button>
|
||||||
|
<button class="btn primary" @click=${this._onSkipConfirm} ?disabled=${this._busy}>
|
||||||
|
${a("skip",t)||"Skip"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`:this._showReset?o`
|
||||||
|
<div class="inline-form">
|
||||||
|
<label>${a("reset_to_date",t)||"Reset last_performed to"}</label>
|
||||||
|
<input type="date" .value=${this._resetDate}
|
||||||
|
@input=${r=>{this._resetDate=r.target.value}} />
|
||||||
|
<div class="inline-actions">
|
||||||
|
<button class="btn cancel" @click=${()=>{this._showReset=!1}} ?disabled=${this._busy}>
|
||||||
|
${a("cancel",t)||"Cancel"}
|
||||||
|
</button>
|
||||||
|
<button class="btn primary" @click=${this._onResetConfirm} ?disabled=${this._busy}>
|
||||||
|
${a("reset",t)||"Reset"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`:o`
|
||||||
|
<div class="actions primary-row">
|
||||||
|
<button class="btn primary" @click=${this._onComplete} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:check"></ha-icon>
|
||||||
|
${a("complete",t)||"Complete"}
|
||||||
|
</button>
|
||||||
|
<button class="btn" @click=${()=>{this._showSkip=!0}} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:skip-next"></ha-icon>
|
||||||
|
${a("skip",t)||"Skip"}
|
||||||
|
</button>
|
||||||
|
<button class="btn" @click=${()=>{this._showReset=!0}} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:restart"></ha-icon>
|
||||||
|
${a("reset",t)||"Reset"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
${s?o`
|
||||||
|
<div class="actions secondary-row">
|
||||||
|
<button class="btn ghost" @click=${this._onEdit} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:pencil"></ha-icon>
|
||||||
|
${a("edit",t)||"Edit"}
|
||||||
|
</button>
|
||||||
|
<button class="btn ghost" @click=${this._onQr} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:qrcode"></ha-icon>
|
||||||
|
${a("qr_code",t)||"QR"}
|
||||||
|
</button>
|
||||||
|
<button class="btn ghost"
|
||||||
|
@click=${e.archived?this._onUnarchive:this._onArchive}
|
||||||
|
?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="${e.archived?"mdi:archive-arrow-up-outline":"mdi:archive-outline"}"></ha-icon>
|
||||||
|
${e.archived?a("unarchive",t)||"Unarchive":a("archive",t)||"Archive"}
|
||||||
|
</button>
|
||||||
|
<button class="btn ghost danger" @click=${this._onDelete} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:delete"></ha-icon>
|
||||||
|
${a("delete",t)||"Delete"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`:p}
|
||||||
|
<div class="details-toggle">
|
||||||
|
<button class="link" @click=${()=>{this._showDetails=!this._showDetails}}>
|
||||||
|
<ha-icon icon="${this._showDetails?"mdi:chevron-up":"mdi:chevron-down"}"></ha-icon>
|
||||||
|
${this._showDetails?a("hide_details",t)||"Hide details":a("show_details",t)||"Show history + stats"}
|
||||||
|
</button>
|
||||||
|
${this._features.adaptive||this._features.seasonal||this._features.environmental?o`<button class="link" @click=${()=>{this._showAdaptive=!this._showAdaptive}}>
|
||||||
|
<ha-icon icon="${this._showAdaptive?"mdi:chart-line":"mdi:chart-line-variant"}"></ha-icon>
|
||||||
|
${this._showAdaptive?a("hide_stats",t)||"Hide stats":a("show_stats",t)||"Show stats + graphs"}
|
||||||
|
</button>`:p}
|
||||||
|
</div>
|
||||||
|
${this._showDetails?this._renderDetails(e):p}
|
||||||
|
${this._showAdaptive?this._renderAdaptive(e):p}
|
||||||
|
<div class="footer">
|
||||||
|
<button class="link" @click=${this._onOpenInPanel}>
|
||||||
|
<ha-icon icon="mdi:open-in-new"></ha-icon>
|
||||||
|
${a("open_in_panel",t)||"Open in Maintenance panel"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
`:o`<div class="loading">${a("loading",t)||"Loading\u2026"}</div>`}
|
||||||
|
</div>
|
||||||
|
`}};b.styles=[et,C`
|
||||||
|
:host { display: contents; }
|
||||||
|
.backdrop {
|
||||||
|
position: fixed; inset: 0; z-index: 100;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
.dialog {
|
||||||
|
position: fixed; left: 50%; top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 95vw; max-width: 460px;
|
||||||
|
max-height: 92vh; overflow: auto;
|
||||||
|
background: var(--card-background-color, var(--ha-card-background, #1c1c1c));
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.4);
|
||||||
|
padding: 20px;
|
||||||
|
display: flex; flex-direction: column; gap: 14px;
|
||||||
|
z-index: 101;
|
||||||
|
}
|
||||||
|
.header { display: flex; flex-direction: column; gap: 6px; }
|
||||||
|
.title { display: flex; align-items: center; gap: 10px; }
|
||||||
|
.status-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
|
||||||
|
.task-name { font-size: 18px; font-weight: 600; }
|
||||||
|
.object { font-size: 13px; color: var(--secondary-text-color); }
|
||||||
|
.link-inline {
|
||||||
|
background: transparent; border: none; padding: 0; cursor: pointer;
|
||||||
|
color: var(--primary-color); font-size: inherit; font-family: inherit;
|
||||||
|
}
|
||||||
|
.link-inline:hover { text-decoration: underline; }
|
||||||
|
.quick-info {
|
||||||
|
display: flex; flex-wrap: wrap; gap: 12px;
|
||||||
|
font-size: 12px; color: var(--secondary-text-color);
|
||||||
|
padding-top: 4px; border-top: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
.quick-info strong { color: var(--primary-text-color); font-weight: 500; }
|
||||||
|
.actions { display: flex; gap: 8px; }
|
||||||
|
.actions.primary-row { gap: 6px; }
|
||||||
|
.actions.primary-row .btn { flex: 1; }
|
||||||
|
/* Edit + QR are admin-tools — left-align as a group; Delete is destructive
|
||||||
|
so it gets pushed to the far right with margin-left:auto for visual
|
||||||
|
separation. Earlier this row was flex-end which left a strange empty
|
||||||
|
gap on the left (user feedback). */
|
||||||
|
.actions.secondary-row {
|
||||||
|
padding-top: 8px; border-top: 1px solid var(--divider-color);
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
.actions.secondary-row .btn.danger {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
padding: 8px 12px; font-size: 14px;
|
||||||
|
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: 6px;
|
||||||
|
transition: background 0.12s;
|
||||||
|
}
|
||||||
|
.btn:hover { background: var(--state-icon-color, rgba(255,255,255,0.06)); }
|
||||||
|
.btn[disabled] { opacity: 0.5; cursor: wait; }
|
||||||
|
.btn.primary {
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: var(--text-primary-color, white);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
.btn.cancel { background: transparent; }
|
||||||
|
.btn.ghost { padding: 6px 10px; font-size: 13px; }
|
||||||
|
.btn.danger { color: var(--error-color); }
|
||||||
|
.btn ha-icon { --mdc-icon-size: 18px; }
|
||||||
|
.inline-form { display: flex; flex-direction: column; gap: 8px; }
|
||||||
|
.inline-form label { font-size: 13px; color: var(--secondary-text-color); }
|
||||||
|
.inline-form input {
|
||||||
|
padding: 8px; font-size: 14px;
|
||||||
|
background: var(--secondary-background-color, #2c2c2c);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
border: 1px solid var(--divider-color, #444);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.inline-actions { display: flex; gap: 8px; justify-content: flex-end; }
|
||||||
|
.footer { display: flex; justify-content: center; padding-top: 4px; }
|
||||||
|
.link {
|
||||||
|
background: transparent; border: none; cursor: pointer;
|
||||||
|
color: var(--primary-color); font-size: 13px;
|
||||||
|
display: inline-flex; align-items: center; gap: 4px;
|
||||||
|
}
|
||||||
|
.link:hover { text-decoration: underline; }
|
||||||
|
.link ha-icon { --mdc-icon-size: 14px; }
|
||||||
|
.loading { padding: 24px; text-align: center; color: var(--secondary-text-color); }
|
||||||
|
.error {
|
||||||
|
padding: 8px; border-radius: 6px;
|
||||||
|
background: rgba(211,47,47,0.1);
|
||||||
|
color: var(--error-color, #d32f2f); font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Details (expandable Show details section) */
|
||||||
|
.details-toggle { display: flex; justify-content: center; margin-top: 4px; }
|
||||||
|
.details {
|
||||||
|
display: flex; flex-direction: column; gap: 12px;
|
||||||
|
border-top: 1px solid var(--divider-color);
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
.stats-grid {
|
||||||
|
display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px;
|
||||||
|
}
|
||||||
|
.stat {
|
||||||
|
display: flex; flex-direction: column; gap: 2px;
|
||||||
|
background: var(--secondary-background-color, rgba(255,255,255,0.04));
|
||||||
|
padding: 8px; border-radius: 6px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.stat-label { font-size: 11px; color: var(--secondary-text-color); text-transform: uppercase; letter-spacing: 0.5px; }
|
||||||
|
.stat-value { font-size: 16px; font-weight: 600; }
|
||||||
|
.history-header {
|
||||||
|
display: flex; align-items: baseline; gap: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.history-count {
|
||||||
|
font-size: 12px; color: var(--secondary-text-color);
|
||||||
|
background: var(--secondary-background-color); padding: 2px 8px; border-radius: 999px;
|
||||||
|
}
|
||||||
|
.history-empty { color: var(--secondary-text-color); font-style: italic; font-size: 13px; }
|
||||||
|
.history-list { display: flex; flex-direction: column; gap: 8px; max-height: 280px; overflow: auto; }
|
||||||
|
.history-entry {
|
||||||
|
padding: 6px 8px; border-radius: 6px;
|
||||||
|
background: var(--secondary-background-color, rgba(255,255,255,0.03));
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.history-line {
|
||||||
|
display: flex; align-items: center; gap: 8px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.history-type {
|
||||||
|
font-weight: 600; font-size: 11px;
|
||||||
|
padding: 2px 6px; border-radius: 4px;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
.type-completed { background: rgba(46,125,50,0.2); color: #66bb6a; }
|
||||||
|
.type-skipped { background: rgba(158,158,158,0.2); color: var(--secondary-text-color); }
|
||||||
|
.type-reset { background: rgba(33,150,243,0.2); color: #64b5f6; }
|
||||||
|
.type-triggered { background: rgba(255,87,34,0.2); color: #ff8a65; }
|
||||||
|
.history-date { font-size: 11px; color: var(--secondary-text-color); flex: 1; text-align: right; }
|
||||||
|
.history-edit {
|
||||||
|
background: transparent; border: none; cursor: pointer;
|
||||||
|
padding: 4px; border-radius: 4px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.history-edit:hover { background: var(--state-icon-color, rgba(255,255,255,0.06)); color: var(--primary-color); }
|
||||||
|
.history-edit ha-icon { --mdc-icon-size: 14px; }
|
||||||
|
.history-notes { margin-top: 4px; color: var(--primary-text-color); }
|
||||||
|
.history-meta { display: flex; gap: 12px; margin-top: 4px; color: var(--secondary-text-color); font-size: 11px; }
|
||||||
|
.history-more { padding: 8px; text-align: center; font-size: 12px; color: var(--secondary-text-color); font-style: italic; }
|
||||||
|
|
||||||
|
/* Adaptive section — wraps the panel renderers (which assume sharedStyles
|
||||||
|
are present) and adds dialog-specific layout. */
|
||||||
|
.adaptive-stack {
|
||||||
|
display: flex; flex-direction: column; gap: 12px;
|
||||||
|
border-top: 1px solid var(--divider-color);
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
.adaptive-empty {
|
||||||
|
padding: 16px; text-align: center;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-style: italic; font-size: 13px;
|
||||||
|
border-top: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
.toast {
|
||||||
|
padding: 8px 12px; border-radius: 6px;
|
||||||
|
background: rgba(76, 175, 80, 0.15);
|
||||||
|
color: #4caf50; font-size: 13px; font-weight: 500;
|
||||||
|
}
|
||||||
|
/* The panel's recommendation-card uses ha-button. We use plain <button>
|
||||||
|
in this dialog's button styles. Re-style the action row to match. */
|
||||||
|
.recommendation-actions {
|
||||||
|
display: flex; gap: 8px; margin-top: 8px;
|
||||||
|
}
|
||||||
|
/* Constrain SVG charts so they fit the dialog width even on mobile. */
|
||||||
|
.weibull-section, .seasonal-card-compact { max-width: 100%; }
|
||||||
|
.weibull-chart svg { max-width: 100%; height: auto; }
|
||||||
|
.details-toggle { gap: 12px; flex-wrap: wrap; }
|
||||||
|
`],m([j({attribute:!1})],b.prototype,"hass",2),m([y()],b.prototype,"_open",2),m([y()],b.prototype,"_entryId",2),m([y()],b.prototype,"_taskId",2),m([y()],b.prototype,"_task",2),m([y()],b.prototype,"_objectName",2),m([y()],b.prototype,"_busy",2),m([y()],b.prototype,"_error",2),m([y()],b.prototype,"_showSkip",2),m([y()],b.prototype,"_showReset",2),m([y()],b.prototype,"_showDetails",2),m([y()],b.prototype,"_showAdaptive",2),m([y()],b.prototype,"_skipReason",2),m([y()],b.prototype,"_resetDate",2),m([y()],b.prototype,"_features",2),m([y()],b.prototype,"_toast",2);customElements.get("maintenance-task-quick-actions-dialog")||customElements.define("maintenance-task-quick-actions-dialog",b);function pt(n){return!!n&&/^https?:\/\//i.test(n)}var L=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._data=null;this._busy=!1;this._error=""}get _lang(){return E(this.hass)}async openFor(t){this._entryId=t,this._error="",this._open=!0,await this._load()}close(){this._open=!1,this._data=null,this._error=""}async _load(){if(this._entryId)try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._data=t}catch(t){this._error=A(t,this._lang)}}_onEditObject(){!this._entryId||!this._data||import("/maintenance_supporter_panelfiles/panel-chunks/dialog-mount-6AVGUS2J.js").then(({openEditObjectDialog:t})=>{t(this._entryId,this._data.object),this.close()})}_onAddTask(){this._entryId&&import("/maintenance_supporter_panelfiles/panel-chunks/dialog-mount-6AVGUS2J.js").then(({openCreateTaskDialog:t})=>{t(this._entryId),this.close()})}async _onDelete(){if(!this._entryId||!this._data)return;let t=a("delete_object_confirm",this._lang)||`Delete "${this._data.object.name}" and all its tasks?`;if(window.confirm(t)){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/delete",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-deleted",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(e){this._error=A(e,this._lang)}finally{this._busy=!1}}}async _onArchiveObject(){if(!this._entryId||!this._data)return;let t=!!this._data.object.archived;if(!t){let e=a("confirm_archive_object",this._lang)||"Archive this object and its tasks?";if(!window.confirm(e))return}this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:t?"maintenance_supporter/object/unarchive":"maintenance_supporter/object/archive",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-changed",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(e){this._error=A(e,this._lang)}finally{this._busy=!1}}_onTaskClick(t){this._entryId&&import("/maintenance_supporter_panelfiles/panel-chunks/dialog-mount-6AVGUS2J.js").then(({openTaskQuickActions:e})=>{e(this._entryId,t)})}render(){if(!this._open)return p;let t=this._lang,e=this._data,s=e?.object,r=e?.tasks||[],l=this.hass?.user?.is_admin??!0;return o`
|
||||||
|
<div class="backdrop" @click=${this.close}></div>
|
||||||
|
<div class="dialog" role="dialog" aria-modal="true">
|
||||||
|
${e&&s?o`
|
||||||
|
<div class="header">
|
||||||
|
<div class="title">${s.name}</div>
|
||||||
|
${this._renderMetaRow(s)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${this._error?o`<div class="error">${this._error}</div>`:p}
|
||||||
|
|
||||||
|
<div class="tasks-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<strong>${a("tasks",t)||"Tasks"}</strong>
|
||||||
|
<span class="count">${r.length}</span>
|
||||||
|
</div>
|
||||||
|
${r.length===0?o`<div class="empty">${a("no_tasks",t)||"No tasks yet."}</div>`:o`
|
||||||
|
<div class="task-list">
|
||||||
|
${r.map(d=>o`
|
||||||
|
<div class="task-row" @click=${()=>this._onTaskClick(d.id)}>
|
||||||
|
<span class="status-dot" style="background: ${q[d.status]||"#ccc"}"></span>
|
||||||
|
<span class="task-name">${d.name}</span>
|
||||||
|
<span class="task-status">${a(d.status||"ok",t)}</span>
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${s.notes?o`
|
||||||
|
<div class="notes-section">
|
||||||
|
<strong>${a("object_notes_label",t)}</strong>
|
||||||
|
<div class="notes-body">${s.notes}</div>
|
||||||
|
</div>
|
||||||
|
`:p}
|
||||||
|
|
||||||
|
${l?o`
|
||||||
|
<div class="actions">
|
||||||
|
<button class="btn primary" @click=${this._onAddTask} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:plus"></ha-icon>
|
||||||
|
${a("add_task",t)||"Add task"}
|
||||||
|
</button>
|
||||||
|
<button class="btn" @click=${this._onEditObject} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:pencil"></ha-icon>
|
||||||
|
${a("edit",t)||"Edit"}
|
||||||
|
</button>
|
||||||
|
<button class="btn" @click=${this._onArchiveObject} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="${s.archived?"mdi:archive-arrow-up-outline":"mdi:archive-outline"}"></ha-icon>
|
||||||
|
${s.archived?a("unarchive_object",t)||"Unarchive object":a("archive_object",t)||"Archive object"}
|
||||||
|
</button>
|
||||||
|
<button class="btn danger" @click=${this._onDelete} ?disabled=${this._busy}>
|
||||||
|
<ha-icon icon="mdi:delete"></ha-icon>
|
||||||
|
${a("delete",t)||"Delete"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`:p}
|
||||||
|
`:o`<div class="loading">${a("loading",t)||"Loading\u2026"}</div>`}
|
||||||
|
</div>
|
||||||
|
`}_renderMetaRow(t){let e=this._lang,s=[];return t.area_id&&s.push([a("area",e),t.area_id]),t.manufacturer&&s.push([a("manufacturer",e),t.manufacturer]),t.model&&s.push([a("model",e),t.model]),t.serial_number&&s.push([a("serial_number_label",e),t.serial_number]),t.installation_date&&s.push([a("installed",e),t.installation_date]),t.warranty_expiry&&s.push([a("warranty",e),t.warranty_expiry]),t.documentation_url&&s.push([a("documentation_url_label",e),t.documentation_url]),s.length===0?p:o`
|
||||||
|
<div class="meta">
|
||||||
|
${s.map(([r,l])=>o`
|
||||||
|
<div class="meta-item">
|
||||||
|
<span class="meta-label">${r}</span>
|
||||||
|
<span class="meta-value">${pt(l)?o`<a href="${l}" target="_blank" rel="noopener noreferrer">${l}</a>`:l}</span>
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
`}};L.styles=C`
|
||||||
|
:host { display: contents; }
|
||||||
|
.backdrop {
|
||||||
|
position: fixed; inset: 0; z-index: 100; background: rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
.dialog {
|
||||||
|
position: fixed; left: 50%; top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 95vw; max-width: 480px;
|
||||||
|
max-height: 92vh; overflow: auto;
|
||||||
|
background: var(--card-background-color, var(--ha-card-background, #1c1c1c));
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.4);
|
||||||
|
padding: 20px; z-index: 101;
|
||||||
|
display: flex; flex-direction: column; gap: 14px;
|
||||||
|
}
|
||||||
|
.header { display: flex; flex-direction: column; gap: 6px; }
|
||||||
|
.title { font-size: 20px; font-weight: 600; }
|
||||||
|
.meta { display: flex; flex-direction: column; gap: 4px; padding-top: 4px; border-top: 1px solid var(--divider-color); }
|
||||||
|
.meta-item { display: flex; gap: 8px; font-size: 12px; }
|
||||||
|
.meta-label { color: var(--secondary-text-color); min-width: 100px; }
|
||||||
|
.meta-value { color: var(--primary-text-color); flex: 1; word-break: break-word; }
|
||||||
|
.meta-value a { color: var(--primary-color); }
|
||||||
|
.tasks-section, .notes-section { display: flex; flex-direction: column; gap: 6px; }
|
||||||
|
.section-header { display: flex; align-items: baseline; gap: 8px; }
|
||||||
|
.count {
|
||||||
|
font-size: 11px; color: var(--secondary-text-color);
|
||||||
|
background: var(--secondary-background-color); padding: 2px 8px; border-radius: 999px;
|
||||||
|
}
|
||||||
|
.empty { color: var(--secondary-text-color); font-style: italic; font-size: 13px; padding: 8px 0; }
|
||||||
|
.task-list { display: flex; flex-direction: column; gap: 4px; max-height: 200px; overflow: auto; }
|
||||||
|
.task-row {
|
||||||
|
display: flex; align-items: center; gap: 10px;
|
||||||
|
padding: 8px; border-radius: 6px; cursor: pointer;
|
||||||
|
background: var(--secondary-background-color, rgba(255,255,255,0.03));
|
||||||
|
transition: background 0.12s;
|
||||||
|
}
|
||||||
|
.task-row:hover { background: var(--state-icon-color, rgba(255,255,255,0.06)); }
|
||||||
|
.status-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
|
||||||
|
.task-name { flex: 1; font-size: 14px; }
|
||||||
|
.task-status { font-size: 11px; color: var(--secondary-text-color); text-transform: uppercase; }
|
||||||
|
.notes-body { white-space: pre-wrap; font-size: 13px; padding: 8px; background: var(--secondary-background-color); border-radius: 6px; }
|
||||||
|
.actions { display: flex; gap: 8px; padding-top: 8px; border-top: 1px solid var(--divider-color); }
|
||||||
|
.actions .btn { flex: 1; }
|
||||||
|
.btn {
|
||||||
|
padding: 8px; 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; justify-content: center; gap: 6px;
|
||||||
|
}
|
||||||
|
.btn:hover { background: var(--state-icon-color, rgba(255,255,255,0.06)); }
|
||||||
|
.btn[disabled] { opacity: 0.5; cursor: wait; }
|
||||||
|
.btn.primary { background: var(--primary-color); color: var(--text-primary-color, white); border-color: var(--primary-color); }
|
||||||
|
.btn.danger { color: var(--error-color); }
|
||||||
|
.btn ha-icon { --mdc-icon-size: 16px; }
|
||||||
|
.loading { padding: 24px; text-align: center; color: var(--secondary-text-color); }
|
||||||
|
.error { padding: 8px; border-radius: 6px; background: rgba(211,47,47,0.1); color: var(--error-color); font-size: 13px; }
|
||||||
|
`,m([j({attribute:!1})],L.prototype,"hass",2),m([y()],L.prototype,"_open",2),m([y()],L.prototype,"_entryId",2),m([y()],L.prototype,"_data",2),m([y()],L.prototype,"_busy",2),m([y()],L.prototype,"_error",2);customElements.get("maintenance-object-quick-actions-dialog")||customElements.define("maintenance-object-quick-actions-dialog",L);var ht="maintenance-object-dialog",_t="maintenance-task-dialog",Ht="maintenance-history-edit-dialog",St="maintenance-complete-dialog",jt="maintenance-qr-dialog",Ot="maintenance-task-quick-actions-dialog",Rt="maintenance-object-quick-actions-dialog";function B(){return document.querySelector("home-assistant")?.hass}function Ft(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function P(n){let i=Ft(),t=i.querySelector(n)??document.body.querySelector(n);return t?t.parentNode!==i&&i.appendChild(t):(t=document.createElement(n),i.appendChild(t)),t}function T(n){let i=B();if(!i)return!1;n.hass=i;let t=E(i);return Z(t)||Y(t).then(()=>{n.requestUpdate?.()}),!0}var ut={features:{adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1},defaultWarningDays:7},N=null;function mt(n){return N||(N=n.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(i=>({features:i.features??ut.features,defaultWarningDays:i.general?.default_warning_days??7})).catch(()=>ut),N)}function ze(){let n=P(ht);return T(n)?(n.openCreate(),!0):!1}function qe(n,i){let t=P(ht);return T(t)?(t.openEdit(n,i),!0):!1}function Ve(n="",i){let t=P(_t);if(!T(t))return!1;let e=B();return e?((async()=>{let s=await mt(e),r=t;r.checklistsEnabled=s.features.checklists,r.scheduleTimeEnabled=s.features.schedule_time,r.completionActionsEnabled=s.features.completion_actions,r.defaultWarningDays=s.defaultWarningDays,r.openCreate(n,i)})(),!0):!1}function We(n,i){let t=P(_t);if(!T(t))return!1;let e=B();return e?((async()=>{try{let[s,r]=await Promise.all([e.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:n}),mt(e)]),l=(s.tasks||[]).find(u=>u.id===i);if(!l){console.warn(`openEditTaskDialog: task ${i} not found in entry ${n}`);return}let d=t;d.checklistsEnabled=r.features.checklists,d.scheduleTimeEnabled=r.features.schedule_time,d.completionActionsEnabled=r.features.completion_actions,d.defaultWarningDays=r.defaultWarningDays,await d.openEdit(n,l)}catch(s){console.warn("openEditTaskDialog: failed to load task/features",s)}})(),!0):!1}function Ne(n){let i=P(Ht);return T(i)?(i.openEdit(n),!0):!1}function Be(n){let i=P(St);return T(i)?(i.entryId=n.entry_id,i.taskId=n.task_id,i.taskName=n.task_name,i.checklist=n.checklist??[],i.adaptiveEnabled=!!n.adaptive_enabled,i.requiredFields=n.required_completion_fields??[],i.taskType=n.task_type??"",i.readingUnit=n.reading_unit??"",i.parts=n.parts??[],i.consumesParts=n.consumes_parts??[],i.phaseLabel=n.phase_label??"",i.lang=B()?.language||"en",i.open(),!0):!1}function Ke(n){let i=P(jt);return T(i)?(i.openForTask(n.entry_id,n.task_id,n.object_name,n.task_name),!0):!1}function Ue(n,i){let t=P(Ot);return T(t)?(t.openFor(n,i),!0):!1}function Ge(n){let i=P(Rt);return T(i)?(i.openFor(n),!0):!1}export{pt as a,Kt as b,Mt as c,Dt as d,U as e,W as f,R as g,Zt as h,Yt as i,at as j,rt as k,ot as l,dt as m,ct as n,ze as o,qe as p,Ve as q,We as r,Ne as s,Be as t,Ke as u,Ue as v,Ge as w};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.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 it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{p as _}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";function u(e){return`${e.entry_id??""}\0${e.part_id}`}function l(e,r,s,c){let n=!!e.entry_id&&e.entry_id!==r,o=n?e.entry_id:r,a=s.find(p=>p.entry_id===o),t=(a?.parts||[]).find(p=>p.id===e.part_id)||null,d=n&&a?.object?.name||"",i=t?.name||_("shared_part_unknown",c);return{part:t,foreign:n,ownerName:d,label:d?`${i} (${d})`:i}}function P(e,r,s,c){let{part:n,label:o}=l(e,r,s,c),a=n&&n.stock!==null&&n.stock!==void 0?` (${n.stock}${n.unit?" "+n.unit:""})`:"",t=n?.storage_location?` \u2014 ${n.storage_location}`:"";return`${e.quantity}\xD7 ${o}${a}${t}`}function g(e,r,s,c){let o=(s.find(t=>t.entry_id===r)?.parts||[]).map(t=>({...t})),a=new Set(o.map(t=>u({part_id:t.id})));for(let t of e?.consumes_parts||[]){if(!t.entry_id||t.entry_id===r)continue;let d=u(t);if(a.has(d))continue;a.add(d);let{part:i,ownerName:p}=l(t,r,s,c);o.push({id:t.part_id,name:i?.name||_("shared_part_unknown",c),unit:i?.unit,stock:i?.stock??null,storage_location:i?.storage_location,entry_id:t.entry_id,owner_name:p})}return o}var f=["notes","cost","duration","photo","user"],m={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"};export{u as a,P as b,g as c,f as d,m as e};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{p as a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";var s={entry_id:"object",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_equals:"trigger_equals",trigger_not_equals:"trigger_not_equals",trigger_for_minutes:"trigger_for_minutes"};function c(e,o){let r=s[e];if(!r)return e;let t=a(r,o);return t&&t!==r?t:e}function d(e){let r=e.match(/data\['([^']+)'\]/)?.[1],t;return(t=e.match(/length of value must be at most (\d+)/))?{field:r,rule:"too_long",param:t[1]}:(t=e.match(/length of value must be at least (\d+)/))?{field:r,rule:"too_short",param:t[1]}:(t=e.match(/value must be at most (\S+)/))?{field:r,rule:"value_too_high",param:t[1]}:(t=e.match(/value must be at least (\S+)/))?{field:r,rule:"value_too_low",param:t[1]}:/required key not provided/.test(e)?{field:r,rule:"required"}:(t=e.match(/expected (\w+)/))?{field:r,rule:"wrong_type",param:t[1]}:/value must be one of/.test(e)?{field:r,rule:"invalid_choice"}:/not a valid value/.test(e)?{field:r,rule:"invalid_value"}:{field:r,rule:"unknown"}}function g(e,o,r){if(r=r??a("action_error",o),typeof e=="string")return e;if(typeof e!="object"||e===null)return r;let t=e,_=t.message||t.error?.message||"";if(!_)return r;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 _||r}}export{g as a};
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as u}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z6JBBYEY.js";import{a as r,b as _,c as l,f as o,g as p,k as d,l as s,p as i,r as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.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 a};
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-7PCGJNSR.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-UBKG7VLY.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z6JBBYEY.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";export{a as MaintenanceCompleteDialog};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{o as a,p as b,q as c,r as d,s as e,t as f,u as g,v as h,w as i}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-H5N2BEP6.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-ZQ7M5ZII.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-KFTLMBBT.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-EUQJA76V.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-7PCGJNSR.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-UBKG7VLY.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-FWFB6OZJ.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-GSH25Q5Z.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-IAYX7ZWS.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z6JBBYEY.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";export{f as openCompleteDialog,a as openCreateObjectDialog,c as openCreateTaskDialog,b as openEditObjectDialog,d as openEditTaskDialog,e as openHistoryEditDialog,i as openObjectQuickActions,g as openQrDialog,h as openTaskQuickActions};
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-ZQ7M5ZII.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-EUQJA76V.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z6JBBYEY.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";export{a as MaintenanceObjectDialog};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-FWFB6OZJ.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-GSH25Q5Z.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";export{a as MaintenanceQrDialog};
|
||||||
+1142
File diff suppressed because it is too large.
Load diff
+146
@@ -0,0 +1,146 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as g}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z6JBBYEY.js";import{a as l,b as m,c as i,f as h,g as v,k as u,l as d,p,r as f,t as x}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.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};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-KFTLMBBT.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-EUQJA76V.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-UBKG7VLY.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-IAYX7ZWS.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-Z6JBBYEY.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-O6LFYSTY.js";export{a as MaintenanceTaskDialog};
|
||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as m,b as f}from"./chunk-HZFXKH3I.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 _,l as v,u as p}from"./chunk-CYP6LL7W.js";import{a as n}from"./chunk-UTYWUNJT.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 _(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(),v(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=this._localMonthly.trim()===""?0:parseFloat(this._localMonthly),r=this._localYearly.trim()===""?0: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};
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,60 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as t}from"./chunk-CYP6LL7W.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.65.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};
|
||||||
+2790
File diff suppressed because it is too large.
Load diff
+137
@@ -0,0 +1,137 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as v,b}from"./chunk-HZFXKH3I.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-CYP6LL7W.js";import{a}from"./chunk-UTYWUNJT.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};
|
||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
|
import{a as m,b as g}from"./chunk-HZFXKH3I.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-CYP6LL7W.js";import{a as i}from"./chunk-UTYWUNJT.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};
|
||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
/*! maintenance_supporter frontend 2.64.0 */
|
/*! maintenance_supporter frontend 2.65.0 */
|
||||||
import{c as y}from"./chunks/chunk-3FHCH2PB.js";var L="maintenance-supporter",G=`ll-strategy-dashboard-${L}`,x="hui-maintenance-supporter-strategy-editor",V="NOT_RUNNING",H=[{status:"overdue",title:"Overdue",icon:y.overdue,path:"overdue"},{status:"triggered",title:"Triggered",icon:y.triggered,path:"triggered"},{status:"due_soon",title:"Due Soon",icon:y.due_soon,path:"due-soon"},{status:"ok",title:"OK",icon:y.ok,path:"ok"}],E=[{title:"Overdue",icon:"mdi:alert-circle",path:"overdue",filter:{filter_due_max_days:-1},matches:n=>n<=-1},{title:"Today",icon:"mdi:calendar-today",path:"today",filter:{filter_due_min_days:0,filter_due_max_days:0},matches:n=>n===0},{title:"This Week",icon:"mdi:calendar-week",path:"this-week",filter:{filter_due_min_days:1,filter_due_max_days:7},matches:n=>n>=1&&n<=7},{title:"This Month",icon:"mdi:calendar-month",path:"this-month",filter:{filter_due_min_days:8,filter_due_max_days:30},matches:n=>n>=8&&n<=30},{title:"Later",icon:"mdi:calendar-clock",path:"later",filter:{filter_due_min_days:31},matches:n=>n>=31}];function l(n){return{type:"grid",cards:[n]}}var N={condition:"view_columns",min:2};function _(n,i,e){let s=i[n];if(s)return`{{ states('${s}') }}`;let t=e[n];return t==null?"\u2014":String(t)}function B(n={},i={}){return{type:"markdown",text_only:!0,content:[`\u{1F534} **${_("overdue",n,i)}** overdue`,`\u26A1 **${_("triggered",n,i)}** triggered`,`\u{1F7E1} **${_("due_soon",n,i)}** due soon`,`\u{1F7E2} **${_("ok",n,i)}** ok`].join(" \xB7 ")}}function $(){return{title:"Maintenance",type:"panel",cards:[{type:"empty-state",icon:"mdi:wrench-clock",icon_color:"primary",content_only:!0,title:"No maintenance objects yet",content:"Open the Maintenance panel to add your first object \u2014 pool pump, HVAC filter, vehicle, anything that needs scheduled care.",buttons:[{icon:"mdi:wrench",text:"Open Maintenance panel",appearance:"filled",variant:"brand",tap_action:{action:"navigate",navigation_path:"/maintenance-supporter"}},{icon:"mdi:plus",text:"Add object",appearance:"outlined",variant:"brand",tap_action:{action:"fire-dom-event",ll_custom:{type:"maintenance-supporter:add-object"}}}]}]}}function U(n={},i={}){let e=B(n,i);return{title:"Overview",icon:"mdi:wrench-clock",path:"overview",type:"sections",header:{layout:"responsive",card:e},sidebar:{sections:[{type:"grid",cards:[{type:"heading",heading:"Status",heading_style:"title"},e]}],visibility:[N],content_label:"Tasks",sidebar_label:"Status"},sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_status:["overdue","triggered","due_soon"]})]}}function W(n,i){let e=new Map;for(let a of n){let r=a.object.area_id||null;e.has(r)||e.set(r,[]),e.get(r).push(a)}let s=Array.from(e.keys()).filter(a=>a!==null);s.sort((a,r)=>{let d=i[a]?.name||a,c=i[r]?.name||r;return d.localeCompare(c)});let t=[];for(let a of s){let r=e.get(a);if(r.length===0)continue;let d=i[a];t.push({title:d?.name||a,icon:d?.icon||"mdi:floor-plan",path:`area-${a}`,type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:r.map(c=>c.object.name)})]})}let o=e.get(null);return o&&o.length>0&&t.push({title:"Unassigned",icon:"mdi:help-circle-outline",path:"unassigned",type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:o.map(a=>a.object.name)})]}),t}function Y(n){let i=new Set;for(let s of n)for(let t of s.tasks||[])t.status&&i.add(t.status);let e=[];for(let s of H)i.has(s.status)&&e.push({title:s.title,icon:s.icon,path:s.path,type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_status:[s.status]})]});return e}function F(n,i,e){let s=new Map;for(let r of n){let d=r.object.area_id,c=d&&i[d]?.floor_id||null;s.has(c)||s.set(c,[]),s.get(c).push(r)}let t=Array.from(s.keys()).filter(r=>r!==null);t.sort((r,d)=>{let c=e[r],g=e[d],u=c?.level??0,p=g?.level??0;return u!==p?u-p:(c?.name||r).localeCompare(g?.name||d)});let o=[];for(let r of t){let d=s.get(r);if(d.length===0)continue;let c=e[r];o.push({title:c?.name||r,icon:c?.icon||"mdi:home-floor-1",path:`floor-${r}`,type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:d.map(g=>g.object.name)})]})}let a=s.get(null);return a&&a.length>0&&o.push({title:"Other",icon:"mdi:help-circle-outline",path:"other",type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:a.map(r=>r.object.name)})]}),o}function K(n){return[{title:"Week",icon:"mdi:calendar-week",path:"cal-7",window_days:7},{title:"Fortnight",icon:"mdi:calendar-week-begin",path:"cal-14",window_days:14},{title:"Month",icon:"mdi:calendar-month",path:"cal-30",window_days:30},{title:"Year",icon:"mdi:calendar-clock",path:"cal-365",window_days:365}].map(e=>({title:e.title,icon:e.icon,path:e.path,type:"panel",cards:[{type:"custom:maintenance-supporter-calendar-card",window_days:e.window_days,show_window_chips:!1,show_user_filter:!0}]}))}function q(n){let i=new Set;for(let sLine truncated
|
import{c as y}from"./chunks/chunk-UTYWUNJT.js";var L="maintenance-supporter",G=`ll-strategy-dashboard-${L}`,x="hui-maintenance-supporter-strategy-editor",V="NOT_RUNNING",H=[{status:"overdue",title:"Overdue",icon:y.overdue,path:"overdue"},{status:"triggered",title:"Triggered",icon:y.triggered,path:"triggered"},{status:"due_soon",title:"Due Soon",icon:y.due_soon,path:"due-soon"},{status:"ok",title:"OK",icon:y.ok,path:"ok"}],E=[{title:"Overdue",icon:"mdi:alert-circle",path:"overdue",filter:{filter_due_max_days:-1},matches:n=>n<=-1},{title:"Today",icon:"mdi:calendar-today",path:"today",filter:{filter_due_min_days:0,filter_due_max_days:0},matches:n=>n===0},{title:"This Week",icon:"mdi:calendar-week",path:"this-week",filter:{filter_due_min_days:1,filter_due_max_days:7},matches:n=>n>=1&&n<=7},{title:"This Month",icon:"mdi:calendar-month",path:"this-month",filter:{filter_due_min_days:8,filter_due_max_days:30},matches:n=>n>=8&&n<=30},{title:"Later",icon:"mdi:calendar-clock",path:"later",filter:{filter_due_min_days:31},matches:n=>n>=31}];function l(n){return{type:"grid",cards:[n]}}var N={condition:"view_columns",min:2};function _(n,i,e){let s=i[n];if(s)return`{{ states('${s}') }}`;let t=e[n];return t==null?"\u2014":String(t)}function B(n={},i={}){return{type:"markdown",text_only:!0,content:[`\u{1F534} **${_("overdue",n,i)}** overdue`,`\u26A1 **${_("triggered",n,i)}** triggered`,`\u{1F7E1} **${_("due_soon",n,i)}** due soon`,`\u{1F7E2} **${_("ok",n,i)}** ok`].join(" \xB7 ")}}function $(){return{title:"Maintenance",type:"panel",cards:[{type:"empty-state",icon:"mdi:wrench-clock",icon_color:"primary",content_only:!0,title:"No maintenance objects yet",content:"Open the Maintenance panel to add your first object \u2014 pool pump, HVAC filter, vehicle, anything that needs scheduled care.",buttons:[{icon:"mdi:wrench",text:"Open Maintenance panel",appearance:"filled",variant:"brand",tap_action:{action:"navigate",navigation_path:"/maintenance-supporter"}},{icon:"mdi:plus",text:"Add object",appearance:"outlined",variant:"brand",tap_action:{action:"fire-dom-event",ll_custom:{type:"maintenance-supporter:add-object"}}}]}]}}function U(n={},i={}){let e=B(n,i);return{title:"Overview",icon:"mdi:wrench-clock",path:"overview",type:"sections",header:{layout:"responsive",card:e},sidebar:{sections:[{type:"grid",cards:[{type:"heading",heading:"Status",heading_style:"title"},e]}],visibility:[N],content_label:"Tasks",sidebar_label:"Status"},sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_status:["overdue","triggered","due_soon"]})]}}function W(n,i){let e=new Map;for(let a of n){let r=a.object.area_id||null;e.has(r)||e.set(r,[]),e.get(r).push(a)}let s=Array.from(e.keys()).filter(a=>a!==null);s.sort((a,r)=>{let d=i[a]?.name||a,c=i[r]?.name||r;return d.localeCompare(c)});let t=[];for(let a of s){let r=e.get(a);if(r.length===0)continue;let d=i[a];t.push({title:d?.name||a,icon:d?.icon||"mdi:floor-plan",path:`area-${a}`,type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:r.map(c=>c.object.name)})]})}let o=e.get(null);return o&&o.length>0&&t.push({title:"Unassigned",icon:"mdi:help-circle-outline",path:"unassigned",type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:o.map(a=>a.object.name)})]}),t}function Y(n){let i=new Set;for(let s of n)for(let t of s.tasks||[])t.status&&i.add(t.status);let e=[];for(let s of H)i.has(s.status)&&e.push({title:s.title,icon:s.icon,path:s.path,type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_status:[s.status]})]});return e}function F(n,i,e){let s=new Map;for(let r of n){let d=r.object.area_id,c=d&&i[d]?.floor_id||null;s.has(c)||s.set(c,[]),s.get(c).push(r)}let t=Array.from(s.keys()).filter(r=>r!==null);t.sort((r,d)=>{let c=e[r],g=e[d],u=c?.level??0,p=g?.level??0;return u!==p?u-p:(c?.name||r).localeCompare(g?.name||d)});let o=[];for(let r of t){let d=s.get(r);if(d.length===0)continue;let c=e[r];o.push({title:c?.name||r,icon:c?.icon||"mdi:home-floor-1",path:`floor-${r}`,type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:d.map(g=>g.object.name)})]})}let a=s.get(null);return a&&a.length>0&&o.push({title:"Other",icon:"mdi:help-circle-outline",path:"other",type:"sections",sections:[l({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:a.map(r=>r.object.name)})]}),o}function K(n){return[{title:"Week",icon:"mdi:calendar-week",path:"cal-7",window_days:7},{title:"Fortnight",icon:"mdi:calendar-week-begin",path:"cal-14",window_days:14},{title:"Month",icon:"mdi:calendar-month",path:"cal-30",window_days:30},{title:"Year",icon:"mdi:calendar-clock",path:"cal-365",window_days:365}].map(e=>({title:e.title,icon:e.icon,path:e.path,type:"panel",cards:[{type:"custom:maintenance-supporter-calendar-card",window_days:e.window_days,show_window_chips:!1,show_user_filter:!0}]}))}function q(n){let i=new Set;for(let sLine truncated
|
||||||
<style>
|
<style>
|
||||||
:host, .editor { display: block; padding: 16px 0; }
|
:host, .editor { display: block; padding: 16px 0; }
|
||||||
label { display: block; font-weight: 500; margin-bottom: 8px; }
|
label { display: block; font-weight: 500; margin-bottom: 8px; }
|
||||||
@@ -22,4 +22,4 @@ import{c as y}from"./chunks/chunk-3FHCH2PB.js";var L="maintenance-supporter",G=`
|
|||||||
The "Overview" view is always first. Empty groups are skipped.
|
The "Overview" view is always first. Empty groups are skipped.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;let t=this.querySelector("#group-by");t?.addEventListener("change",()=>{let o={...this._config,group_by:t.value};this._config=o,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:o},bubbles:!0,composed:!0}))})}},I="maintenance-supporter-section",Q=`ll-strategy-section-${I}`,v=class extends HTMLElement{static async generate(i,e){let s={type:"custom:maintenance-supporter-card",show_header:!1};i?.filter_status?.length&&(s.filter_status=i.filter_status),i?.max_items&&(s.max_items=i.max_items),i?.filter_due_min_days!==void 0&&(s.filter_due_min_days=i.filter_due_min_days),i?.filter_due_max_days!==void 0&&(s.filter_due_max_days=i.filter_due_max_days);let t=i?.filter_objects;if(i?.area_id&&!t)try{t=((await e.connection.sendMessagePromise({type:"maintenance_supporter/objects"})).objects||[]).filter(r=>r.object.area_id===i.area_id).map(r=>r.object.name)}catch{}t&&t.length>0&&(s.filter_objects=t);let o=[];return i?.title&&o.push({type:"heading",heading:i.title,heading_style:"title"}),o.push(s),{type:"grid",cards:o}}},D="2.3.6",X="[maintenance-supporter]";function M(n,i){try{return customElements.get(n)?"skipped":(customElements.define(n,i),"ok")}catch(e){return`ERROR: ${String(e)}`}}var P={editor:M(x,w),section:M(Q,v)};console.log(`${X} strategy bundle v${D} loaded \u2014 registrations:`,P,"\u2014 run maintenanceSupporterDiagnose() in console for full state");window.maintenanceSupporterDiagnose=async()=>{let n=G,i=[];try{let e=document.querySelector("home-assistant");e?.hass&&(i=(await e.hass.connection.sendMessagePromise({type:"lovelace/resources"})).resources||[])}catch(e){i=[{url:`<error: ${String(e)}>`}]}return{version:D,el_registered:!!customElements.get(n),el_class_excerpt:customElements.get(n)?.toString().slice(0,100)||null,customStrategies_dashboard:window.customStrategies?.filter(e=>e.strategyType==="dashboard").map(e=>e.type)||[],scriptTagsInPage:Array.from(document.querySelectorAll("script[type=module]")).map(e=>e.src).filter(e=>/maint|strateg/i.test(e)),lovelaceResources_matching:i.filter(e=>/maint|strateg/i.test(e.url||"")).map(e=>e.url),registrations:P}};var k=class extends HTMLElement{static async generate(i,e){return await import("./chunks/vacation-section-card-ZL6KZC6R.js"),{type:"grid",cards:[{type:"custom:maintenance-vacation-section-card",title:i?.title}]}}},C=class extends HTMLElement{static async generate(i,e){return await import("./chunks/budget-section-card-G6NY7FKK.js"),{type:"grid",cards:[{type:"custom:maintenance-budget-section-card",title:i?.title}]}}},S=class extends HTMLElement{static async generate(i,e){return await import("./chunks/groups-section-card-KRPPNUTF.js"),{type:"grid",cards:[{type:"custom:maintenance-groups-section-card",title:i?.title}]}}},j="ll-strategy-section-maintenance-supporter-vacation",R="ll-strategy-section-maintenance-supporter-budget",A="ll-strategy-section-maintenance-supporter-groups";customElements.get(j)||customElements.define(j,k);customElements.get(R)||customElements.define(R,C);customElements.get(A)||customElements.define(A,S);var h=null;function J(){return h||(h=import("./chunks/dialog-mount-VABI5TW6.js").catch(n=>{throw h=null,n})),h}var O="_msSupporterLlCustomBound";function m(n){history.pushState(null,"",n),window.dispatchEvent(new CustomEvent("location-changed"))}function Z(){let n=window;n[O]||(n[O]=!0,document.addEventListener("ll-custom",i=>{let e=i.detail;if(!e||typeof e!="object")return;let s=e.ll_custom,t=s&&typeof s=="object"?s:e;if(!t||typeof t.type!="string"||!t.type.startsWith("maintenance-supporter:"))return;let o=t.type.slice(22);if(o==="deep-link"&&typeof t.path=="string"){m(t.path);return}(async()=>{let a;try{a=await J()}catch{m("/maintenance-supporter");return}if(o==="add-object"){if(a.openCreateObjectDialog())return;m("/maintenance-supporter?ms_action=add_object");return}if(o==="open-task"&&typeof t.entry_id=="string"&&typeof t.task_id=="string"){if(a.openTaskQuickActions(t.entry_id,t.task_id)||a.openEditTaskDialog(t.entry_id,t.task_id))return;m(`/maintenance-supporter?entry_id=${encodeURIComponent(t.entry_id)}&task_id=${encodeURIComponent(t.task_id)}`);return}if(o==="open-object"&&typeof t.entry_id=="string"){if(a.openObjectQuickActions(t.entry_id))return;m(`/maintenance-supporter?entry_id=${encodeURIComponent(t.entry_id)}`);return}if(o==="edit-history"&&typeof t.entry_id=="string"&&typeof t.task_id=="string"&&typeof t.original_timestamp=="string"){let d=document.querySelector("home-assistant")?.hass;if(!d)return;try{let u=(await d.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:t.entry_id})).tasks?.find(p=>p.id===t.task_id)?.history?.find(p=>p.timestamp===t.original_timestamp);if(!u)return;a.openHistoryEditDialog({entry_id:t.entry_id,task_id:t.task_id,original_timestamp:t.original_timestamp,type:u.type||"completed",timestamp:u.timestamp||t.original_timestamp,notes:u.notes??null,cost:u.cost??null,duration:u.duration??null,completed_by:u.completed_by??null,used_parts:u.used_parts??null}
Line truncated
|
`;let t=this.querySelector("#group-by");t?.addEventListener("change",()=>{let o={...this._config,group_by:t.value};this._config=o,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:o},bubbles:!0,composed:!0}))})}},I="maintenance-supporter-section",Q=`ll-strategy-section-${I}`,v=class extends HTMLElement{static async generate(i,e){let s={type:"custom:maintenance-supporter-card",show_header:!1};i?.filter_status?.length&&(s.filter_status=i.filter_status),i?.max_items&&(s.max_items=i.max_items),i?.filter_due_min_days!==void 0&&(s.filter_due_min_days=i.filter_due_min_days),i?.filter_due_max_days!==void 0&&(s.filter_due_max_days=i.filter_due_max_days);let t=i?.filter_objects;if(i?.area_id&&!t)try{t=((await e.connection.sendMessagePromise({type:"maintenance_supporter/objects"})).objects||[]).filter(r=>r.object.area_id===i.area_id).map(r=>r.object.name)}catch{}t&&t.length>0&&(s.filter_objects=t);let o=[];return i?.title&&o.push({type:"heading",heading:i.title,heading_style:"title"}),o.push(s),{type:"grid",cards:o}}},D="2.3.6",X="[maintenance-supporter]";function M(n,i){try{return customElements.get(n)?"skipped":(customElements.define(n,i),"ok")}catch(e){return`ERROR: ${String(e)}`}}var P={editor:M(x,w),section:M(Q,v)};console.log(`${X} strategy bundle v${D} loaded \u2014 registrations:`,P,"\u2014 run maintenanceSupporterDiagnose() in console for full state");window.maintenanceSupporterDiagnose=async()=>{let n=G,i=[];try{let e=document.querySelector("home-assistant");e?.hass&&(i=(await e.hass.connection.sendMessagePromise({type:"lovelace/resources"})).resources||[])}catch(e){i=[{url:`<error: ${String(e)}>`}]}return{version:D,el_registered:!!customElements.get(n),el_class_excerpt:customElements.get(n)?.toString().slice(0,100)||null,customStrategies_dashboard:window.customStrategies?.filter(e=>e.strategyType==="dashboard").map(e=>e.type)||[],scriptTagsInPage:Array.from(document.querySelectorAll("script[type=module]")).map(e=>e.src).filter(e=>/maint|strateg/i.test(e)),lovelaceResources_matching:i.filter(e=>/maint|strateg/i.test(e.url||"")).map(e=>e.url),registrations:P}};var k=class extends HTMLElement{static async generate(i,e){return await import("./chunks/vacation-section-card-BCTJNS2F.js"),{type:"grid",cards:[{type:"custom:maintenance-vacation-section-card",title:i?.title}]}}},C=class extends HTMLElement{static async generate(i,e){return await import("./chunks/budget-section-card-QP3TTMOD.js"),{type:"grid",cards:[{type:"custom:maintenance-budget-section-card",title:i?.title}]}}},S=class extends HTMLElement{static async generate(i,e){return await import("./chunks/groups-section-card-PUPCVQTP.js"),{type:"grid",cards:[{type:"custom:maintenance-groups-section-card",title:i?.title}]}}},j="ll-strategy-section-maintenance-supporter-vacation",R="ll-strategy-section-maintenance-supporter-budget",A="ll-strategy-section-maintenance-supporter-groups";customElements.get(j)||customElements.define(j,k);customElements.get(R)||customElements.define(R,C);customElements.get(A)||customElements.define(A,S);var h=null;function J(){return h||(h=import("./chunks/dialog-mount-QSYSXY3T.js").catch(n=>{throw h=null,n})),h}var O="_msSupporterLlCustomBound";function m(n){history.pushState(null,"",n),window.dispatchEvent(new CustomEvent("location-changed"))}function Z(){let n=window;n[O]||(n[O]=!0,document.addEventListener("ll-custom",i=>{let e=i.detail;if(!e||typeof e!="object")return;let s=e.ll_custom,t=s&&typeof s=="object"?s:e;if(!t||typeof t.type!="string"||!t.type.startsWith("maintenance-supporter:"))return;let o=t.type.slice(22);if(o==="deep-link"&&typeof t.path=="string"){m(t.path);return}(async()=>{let a;try{a=await J()}catch{m("/maintenance-supporter");return}if(o==="add-object"){if(a.openCreateObjectDialog())return;m("/maintenance-supporter?ms_action=add_object");return}if(o==="open-task"&&typeof t.entry_id=="string"&&typeof t.task_id=="string"){if(a.openTaskQuickActions(t.entry_id,t.task_id)||a.openEditTaskDialog(t.entry_id,t.task_id))return;m(`/maintenance-supporter?entry_id=${encodeURIComponent(t.entry_id)}&task_id=${encodeURIComponent(t.task_id)}`);return}if(o==="open-object"&&typeof t.entry_id=="string"){if(a.openObjectQuickActions(t.entry_id))return;m(`/maintenance-supporter?entry_id=${encodeURIComponent(t.entry_id)}`);return}if(o==="edit-history"&&typeof t.entry_id=="string"&&typeof t.task_id=="string"&&typeof t.original_timestamp=="string"){let d=document.querySelector("home-assistant")?.hass;if(!d)return;try{let u=(await d.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:t.entry_id})).tasks?.find(p=>p.id===t.task_id)?.history?.find(p=>p.timestamp===t.original_timestamp);if(!u)return;a.openHistoryEditDialog({entry_id:t.entry_id,task_id:t.task_id,original_timestamp:t.original_timestamp,type:u.type||"completed",timestamp:u.timestamp||t.original_timestamp,notes:u.notes??null,cost:u.cost??null,duration:u.duration??null,completed_by:u.completed_by??null,used_parts:u.used_parts??null}
Line truncated
|
||||||
@@ -22,5 +22,5 @@
|
|||||||
"requirements": [
|
"requirements": [
|
||||||
"pypdf>=4.3.0"
|
"pypdf>=4.3.0"
|
||||||
],
|
],
|
||||||
"version": "2.64.0"
|
"version": "2.65.0"
|
||||||
}
|
}
|
||||||
@@ -62,6 +62,14 @@ class MaintenanceTask:
|
|||||||
# --- Trigger ---
|
# --- Trigger ---
|
||||||
trigger_config: dict[str, Any] | None = None
|
trigger_config: dict[str, Any] | None = None
|
||||||
|
|
||||||
|
# --- Phases (#139): a cyclic sequence of different activities on ONE
|
||||||
|
# shared cadence. Definitions + sequence are STATIC config (dict-only,
|
||||||
|
# like consumes_parts — the model carries them read-only so complete()
|
||||||
|
# can rotate); the cursor is DYNAMIC (Store, like the rotation pointer).
|
||||||
|
phases: dict[str, Any] | None = None
|
||||||
|
phase_sequence: list[str] = field(default_factory=list)
|
||||||
|
phase_cursor: int = 0
|
||||||
|
|
||||||
# --- Metadata ---
|
# --- Metadata ---
|
||||||
notes: str | None = None
|
notes: str | None = None
|
||||||
documentation_url: str | None = None
|
documentation_url: str | None = None
|
||||||
@@ -136,6 +144,17 @@ class MaintenanceTask:
|
|||||||
due_override=parse_iso_date(self.due_override),
|
due_override=parse_iso_date(self.due_override),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_phase_name(self) -> str | None:
|
||||||
|
"""Name of the cycle phase currently due (#139), or None."""
|
||||||
|
if not (self.phases and self.phase_sequence):
|
||||||
|
return None
|
||||||
|
from ..helpers.phases import clamp_phase_cursor
|
||||||
|
|
||||||
|
phase_id = self.phase_sequence[clamp_phase_cursor(self.phase_cursor, len(self.phase_sequence))]
|
||||||
|
definition = self.phases.get(phase_id)
|
||||||
|
return definition.get("name") if isinstance(definition, dict) else None
|
||||||
|
|
||||||
def _planned_grid_due(self) -> date | None:
|
def _planned_grid_due(self) -> date | None:
|
||||||
"""``next_due`` WITHOUT the postpone override — the drift-free grid.
|
"""``next_due`` WITHOUT the postpone override — the drift-free grid.
|
||||||
|
|
||||||
@@ -375,6 +394,17 @@ class MaintenanceTask:
|
|||||||
anchors.append(self.last_performed)
|
anchors.append(self.last_performed)
|
||||||
is_latest = ts_iso >= max(anchors, default="")
|
is_latest = ts_iso >= max(anchors, default="")
|
||||||
|
|
||||||
|
# Phases (#139): the step being completed is the one currently due.
|
||||||
|
# Resolved BEFORE the cursor advances; a pure backfill gets no phase
|
||||||
|
# attribution (we cannot know which step it was) and never rotates.
|
||||||
|
completed_phase_id: str | None = None
|
||||||
|
if is_latest and self.phases and self.phase_sequence:
|
||||||
|
from ..helpers.phases import clamp_phase_cursor
|
||||||
|
|
||||||
|
cursor = clamp_phase_cursor(self.phase_cursor, len(self.phase_sequence))
|
||||||
|
completed_phase_id = self.phase_sequence[cursor]
|
||||||
|
self.phase_cursor = (cursor + 1) % len(self.phase_sequence)
|
||||||
|
|
||||||
if is_latest:
|
if is_latest:
|
||||||
# Save the PLANNED grid date as the anchor before resetting — not
|
# Save the PLANNED grid date as the anchor before resetting — not
|
||||||
# next_due, which returns a postpone override and would shift the
|
# next_due, which returns a postpone override and would shift the
|
||||||
@@ -404,6 +434,7 @@ class MaintenanceTask:
|
|||||||
used_parts=used_parts,
|
used_parts=used_parts,
|
||||||
auto=auto,
|
auto=auto,
|
||||||
timestamp=ts_iso,
|
timestamp=ts_iso,
|
||||||
|
phase_id=completed_phase_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Shared tasks: rotate the "currently responsible" pointer to the next
|
# Shared tasks: rotate the "currently responsible" pointer to the next
|
||||||
@@ -508,6 +539,7 @@ class MaintenanceTask:
|
|||||||
used_parts: list[dict[str, Any]] | None = None,
|
used_parts: list[dict[str, Any]] | None = None,
|
||||||
auto: bool = False,
|
auto: bool = False,
|
||||||
timestamp: str | None = None,
|
timestamp: str | None = None,
|
||||||
|
phase_id: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Add an entry to the maintenance history.
|
"""Add an entry to the maintenance history.
|
||||||
|
|
||||||
@@ -520,6 +552,10 @@ class MaintenanceTask:
|
|||||||
}
|
}
|
||||||
if auto:
|
if auto:
|
||||||
entry["auto"] = True
|
entry["auto"] = True
|
||||||
|
# Phases (#139): which cycle step this completion performed — the key
|
||||||
|
# per-phase statistics and "last done" lines pool by.
|
||||||
|
if phase_id is not None:
|
||||||
|
entry["phase_id"] = phase_id
|
||||||
if notes is not None:
|
if notes is not None:
|
||||||
entry["notes"] = notes
|
entry["notes"] = notes
|
||||||
if cost is not None:
|
if cost is not None:
|
||||||
@@ -630,6 +666,13 @@ class MaintenanceTask:
|
|||||||
data["checklist"] = self.checklist
|
data["checklist"] = self.checklist
|
||||||
if self.adaptive_config is not None:
|
if self.adaptive_config is not None:
|
||||||
data["adaptive_config"] = self.adaptive_config
|
data["adaptive_config"] = self.adaptive_config
|
||||||
|
# Phases (#139): defs + sequence are static config (like
|
||||||
|
# trigger_config), the cursor is the dynamic pointer — emitted only
|
||||||
|
# for phase-carrying tasks so plain tasks stay lean.
|
||||||
|
if self.phases and self.phase_sequence:
|
||||||
|
data["phases"] = self.phases
|
||||||
|
data["phase_sequence"] = self.phase_sequence
|
||||||
|
data["phase_cursor"] = self.phase_cursor
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -642,6 +685,9 @@ class MaintenanceTask:
|
|||||||
``task.interval_days`` etc. is unaffected.
|
``task.interval_days`` etc. is unaffected.
|
||||||
"""
|
"""
|
||||||
sched = read_legacy_fields(data)
|
sched = read_legacy_fields(data)
|
||||||
|
raw_phases = data.get("phases")
|
||||||
|
raw_seq = data.get("phase_sequence")
|
||||||
|
raw_cursor = data.get("phase_cursor", 0)
|
||||||
return cls(
|
return cls(
|
||||||
id=data.get("id", uuid4().hex),
|
id=data.get("id", uuid4().hex),
|
||||||
object_id=data.get("object_id", ""),
|
object_id=data.get("object_id", ""),
|
||||||
@@ -677,4 +723,7 @@ class MaintenanceTask:
|
|||||||
checklist=data.get("checklist", []),
|
checklist=data.get("checklist", []),
|
||||||
adaptive_config=data.get("adaptive_config"),
|
adaptive_config=data.get("adaptive_config"),
|
||||||
history=data.get("history", []),
|
history=data.get("history", []),
|
||||||
|
phases=raw_phases if isinstance(raw_phases, dict) else None,
|
||||||
|
phase_sequence=list(raw_seq) if isinstance(raw_seq, list) else [],
|
||||||
|
phase_cursor=raw_cursor if isinstance(raw_cursor, int) else 0,
|
||||||
)
|
)
|
||||||
Loaded 100 of 149 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user