updated interface

This commit is contained in:
2026-03-30 15:49:26 +03:00
parent fbf5802c69
commit 86bfecca26
8 changed files with 1002 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
{% extends "base.html" %}
{% block title %}Devices Location Management{% endblock %}
{% block content %}
<div class="d-flex align-items-center justify-content-between mb-4">
<h2 class="fw-bold mb-0">
<i class="bi bi-hdd-stack me-2 text-info"></i>Devices
</h2>
{% if current_user.is_admin() %}
<a href="{{ url_for('devices.add_device') }}" class="btn btn-primary">
<i class="bi bi-plus-circle me-1"></i> Add Device
</a>
{% endif %}
</div>
<p class="text-secondary mb-4">
Define named, personalized devices (lights, switches, pumps, sensors…) that map to
specific relay or input channels on your boards. Devices can be placed on Layout pages
as interactive widgets.
</p>
{% if devices %}
{# Group by area #}
{% set areas = devices | map(attribute='area') | unique | list %}
{% set no_area = devices | selectattr('area', 'none') | list
+ devices | selectattr('area', 'equalto', '') | list
+ devices | selectattr('area', 'equalto', None) | list %}
{# Collect non-empty areas #}
{% set named_areas = [] %}
{% for d in devices %}
{% if d.area and d.area != '' and d.area not in named_areas %}
{% set _ = named_areas.append(d.area) %}
{% endif %}
{% endfor %}
{# Devices with no area #}
{% set ungrouped = [] %}
{% for d in devices %}
{% if not d.area or d.area == '' %}
{% set _ = ungrouped.append(d) %}
{% endif %}
{% endfor %}
{% for area in named_areas %}
<h5 class="text-secondary fw-semibold mb-3 mt-4">
<i class="bi bi-geo-alt me-1"></i>{{ area }}
</h5>
<div class="row g-3 mb-2">
{% for device in devices %}
{% if device.area == area %}
<div class="col-md-6 col-xl-4" id="device-card-{{ device.id }}">
{% include "devices/_card.html" %}
</div>
{% endif %}
{% endfor %}
</div>
{% endfor %}
{% if ungrouped %}
{% if named_areas %}
<h5 class="text-secondary fw-semibold mb-3 mt-4">
<i class="bi bi-three-dots me-1"></i>Other
</h5>
{% endif %}
<div class="row g-3 mb-2">
{% for device in ungrouped %}
<div class="col-md-6 col-xl-4" id="device-card-{{ device.id }}">
{% include "devices/_card.html" %}
</div>
{% endfor %}
</div>
{% endif %}
{% else %}
<div class="text-center py-5 text-secondary">
<i class="bi bi-hdd-stack display-4 d-block mb-3 opacity-25"></i>
<p class="mb-1">No devices defined yet.</p>
{% if current_user.is_admin() %}
<a href="{{ url_for('devices.add_device') }}" class="btn btn-sm btn-outline-primary mt-2">
<i class="bi bi-plus-circle me-1"></i> Add your first device
</a>
{% endif %}
</div>
{% endif %}
{% endblock %}
{% block scripts %}
<script>
function deviceToggle(btn, deviceId) {
btn.disabled = true;
fetch(`/devices/${deviceId}/toggle`, {
method: "POST",
headers: {"X-Requested-With": "XMLHttpRequest"}
})
.then(r => r.json())
.then(data => {
if (!data.ok) { btn.disabled = false; return; }
const card = document.getElementById("device-card-" + deviceId);
const badge = card.querySelector(".device-state-badge");
if (badge) {
badge.className = "badge device-state-badge text-bg-" + data.state_color;
badge.textContent = data.state_label;
}
// Update toggle icon
btn.className = btn.className.replace(/btn-(outline-)?[a-z]+/, "btn-" + data.state_color);
if (data.state) {
btn.innerHTML = '<i class="bi bi-power me-1"></i>ON';
} else {
btn.innerHTML = '<i class="bi bi-power me-1"></i>OFF';
}
btn.disabled = false;
})
.catch(() => { btn.disabled = false; });
}
</script>
{% endblock %}