Redirect to device detail after create; sub-component install UI on parent detail
This commit is contained in:
@@ -759,7 +759,7 @@ def create_app(config_object="config"):
|
|||||||
flash(f"Device '{name}' added.", "success")
|
flash(f"Device '{name}' added.", "success")
|
||||||
if parent_device:
|
if parent_device:
|
||||||
return redirect(url_for("device_detail", device_id=parent_device.id))
|
return redirect(url_for("device_detail", device_id=parent_device.id))
|
||||||
return redirect(url_for("device_list"))
|
return redirect(url_for("device_detail", device_id=device.id))
|
||||||
|
|
||||||
return render_template("device_add.html", device_types=device_types,
|
return render_template("device_add.html", device_types=device_types,
|
||||||
device_locations=device_locations,
|
device_locations=device_locations,
|
||||||
@@ -809,11 +809,18 @@ def create_app(config_object="config"):
|
|||||||
if changed:
|
if changed:
|
||||||
db.commit()
|
db.commit()
|
||||||
flat_installed = []
|
flat_installed = []
|
||||||
|
children_avail = {}
|
||||||
if device.has_children():
|
if device.has_children():
|
||||||
for child in device.children:
|
for child in device.children:
|
||||||
for b in child.batteries:
|
for b in child.batteries:
|
||||||
if b.status == "installed":
|
if b.status == "installed":
|
||||||
flat_installed.append((b, child.name, child.id))
|
flat_installed.append((b, child.name, child.id))
|
||||||
|
avail_q = db.query(Battery).filter_by(status="available")
|
||||||
|
if child.battery_size:
|
||||||
|
avail_q = avail_q.filter(
|
||||||
|
(Battery.size == child.battery_size) | (Battery.size == None)
|
||||||
|
)
|
||||||
|
children_avail[child.id] = avail_q.order_by(Battery.label).all()
|
||||||
return render_template("device_detail.html", device=device, brands=brands,
|
return render_template("device_detail.html", device=device, brands=brands,
|
||||||
available_batteries=available_batteries,
|
available_batteries=available_batteries,
|
||||||
device_types=device_types,
|
device_types=device_types,
|
||||||
@@ -823,7 +830,8 @@ def create_app(config_object="config"):
|
|||||||
ha_enabled=ha_client.enabled,
|
ha_enabled=ha_client.enabled,
|
||||||
ha_live_pct=ha_live_pct,
|
ha_live_pct=ha_live_pct,
|
||||||
logbook_entries=device.logbook_entries,
|
logbook_entries=device.logbook_entries,
|
||||||
flat_installed=flat_installed)
|
flat_installed=flat_installed,
|
||||||
|
children_avail=children_avail)
|
||||||
|
|
||||||
# ------------------------------------------------------------------ #
|
# ------------------------------------------------------------------ #
|
||||||
# Devices — edit
|
# Devices — edit
|
||||||
|
|||||||
@@ -112,13 +112,79 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
{% if device.children %}
|
{% if device.children %}
|
||||||
<h2>Sub-components</h2>
|
<h2>Sub-components</h2>
|
||||||
<div style="display:flex;gap:0.75rem;flex-wrap:wrap;margin-bottom:0.75rem;">
|
<div style="display:flex;flex-direction:column;gap:0.5rem;margin-bottom:0.75rem;">
|
||||||
{% for child in device.children %}
|
{% for child in device.children %}
|
||||||
<a href="{{ url_for('device_detail', device_id=child.id) }}"
|
{% set child_free = child.battery_slots - child.installed_count() %}
|
||||||
style="display:block;padding:0.6rem 0.9rem;border:1px solid var(--border);border-radius:6px;text-decoration:none;min-width:140px;background:var(--bg-card);">
|
{% set child_batteries = children_avail.get(child.id, []) %}
|
||||||
<strong>{{ child.name }}</strong><br>
|
<details style="border:1px solid var(--border);border-radius:6px;background:var(--bg-card);">
|
||||||
<small class="text-muted">{{ child.installed_count() }}/{{ child.battery_slots }} slots · {{ child.battery_size }}{% if child.ha_entity_id %} · HA{% endif %}</small>
|
<summary style="padding:0.6rem 0.9rem;cursor:pointer;list-style:none;display:flex;align-items:center;justify-content:space-between;">
|
||||||
</a>
|
<span>
|
||||||
|
<strong>{{ child.name }}</strong>
|
||||||
|
<small class="text-muted" style="margin-left:0.5rem;">{{ child.installed_count() }}/{{ child.battery_slots }} slots · {{ child.battery_size }}{% if child.ha_entity_id %} · HA{% endif %}</small>
|
||||||
|
</span>
|
||||||
|
<span style="display:flex;gap:0.5rem;align-items:center;">
|
||||||
|
{% if child_free > 0 and child_batteries %}<small class="text-muted">{{ child_batteries|length }} available</small>{% endif %}
|
||||||
|
<a href="{{ url_for('device_detail', device_id=child.id) }}" onclick="event.stopPropagation();" style="font-size:0.8rem;">View</a>
|
||||||
|
</span>
|
||||||
|
</summary>
|
||||||
|
<div style="padding:0.6rem 0.9rem;border-top:1px solid var(--border);">
|
||||||
|
{% if child_free <= 0 %}
|
||||||
|
<p class="text-muted" style="margin:0;">No free slots.</p>
|
||||||
|
{% elif not child_batteries %}
|
||||||
|
<p class="text-muted" style="margin:0;">No compatible batteries available.</p>
|
||||||
|
{% else %}
|
||||||
|
<form method="post" action="{{ url_for('device_install_batch', device_id=child.id) }}">
|
||||||
|
<div style="margin-bottom:0.4rem;">
|
||||||
|
<label style="font-size:0.85rem;cursor:pointer;">
|
||||||
|
<input type="checkbox" id="select-all-child-{{ child.id }}" onchange="toggleAllChild{{ child.id }}(this)"> Select all
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div style="max-height:180px;overflow-y:auto;border:1px solid #cbd5e1;border-radius:4px;padding:0.4rem 0.6rem;">
|
||||||
|
{% for b in child_batteries %}
|
||||||
|
<div>
|
||||||
|
<label style="font-size:0.9rem;cursor:pointer;">
|
||||||
|
<input type="checkbox" class="child-bat-{{ child.id }}" name="battery_ids" value="{{ b.id }}">
|
||||||
|
{{ b.label }} — {{ b.brand }}{% if b.battery_percentage is not none %} ({{ b.battery_percentage }}%){% endif %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm btn-primary" type="submit" style="margin-top:0.5rem;">Install Selected</button>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var FREE_{{ child.id }} = {{ child_free }};
|
||||||
|
var queue_{{ child.id }} = [];
|
||||||
|
document.querySelectorAll('.child-bat-{{ child.id }}').forEach(function(cb) {
|
||||||
|
cb.addEventListener('change', function() {
|
||||||
|
if (cb.checked) {
|
||||||
|
queue_{{ child.id }}.push(cb);
|
||||||
|
if (queue_{{ child.id }}.length > FREE_{{ child.id }}) {
|
||||||
|
queue_{{ child.id }}.shift().checked = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queue_{{ child.id }} = queue_{{ child.id }}.filter(function(c) { return c !== cb; });
|
||||||
|
}
|
||||||
|
document.getElementById('select-all-child-{{ child.id }}').checked = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
window['toggleAllChild{{ child.id }}'] = function(masterCb) {
|
||||||
|
var all = Array.from(document.querySelectorAll('.child-bat-{{ child.id }}'));
|
||||||
|
if (masterCb.checked) {
|
||||||
|
queue_{{ child.id }} = [];
|
||||||
|
all.forEach(function(c) { c.checked = false; });
|
||||||
|
all.slice(0, FREE_{{ child.id }}).forEach(function(c) { c.checked = true; queue_{{ child.id }}.push(c); });
|
||||||
|
if (all.length > FREE_{{ child.id }}) masterCb.checked = false;
|
||||||
|
} else {
|
||||||
|
all.forEach(function(c) { c.checked = false; });
|
||||||
|
queue_{{ child.id }} = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
Reference in New Issue
Block a user