Files
battery-tracker-app/templates/device_list.html
T

144 lines
6.4 KiB
HTML

{% extends "base.html" %}
{% block title %}Devices — Battery Tracker{% endblock %}
{% block content %}
<h1>Devices</h1>
<div class="card">
<div id="device-filter-bar" style="display:flex;gap:0.5rem;flex-wrap:wrap;align-items:center;margin-bottom:0.75rem;">
<select id="filter-type" onchange="applyDeviceFilters()"
style="padding:0.25rem 0.5rem;font-size:0.85rem;border:1px solid #cbd5e1;border-radius:4px;">
<option value="">All Types</option>
{% for t in device_types|default([]) %}
<option value="{{ t }}">{{ t }}</option>
{% endfor %}
</select>
<select id="filter-location" onchange="applyDeviceFilters()"
style="padding:0.25rem 0.5rem;font-size:0.85rem;border:1px solid #cbd5e1;border-radius:4px;">
<option value="">All Locations</option>
{% for loc in device_locations|default([]) %}
<option value="{{ loc }}">{{ loc }}</option>
{% endfor %}
</select>
<select id="filter-fill" onchange="applyDeviceFilters()"
style="padding:0.25rem 0.5rem;font-size:0.85rem;border:1px solid #cbd5e1;border-radius:4px;">
<option value="">Any Fill</option>
<option value="empty">Empty</option>
<option value="partial">Partial</option>
<option value="full">Full</option>
</select>
<input type="text" id="filter-device-text" oninput="applyDeviceFilters()" placeholder="Search…"
style="padding:0.25rem 0.5rem;font-size:0.85rem;border:1px solid #cbd5e1;border-radius:4px;width:140px;">
<button type="button" onclick="resetDeviceFilters()" class="btn btn-sm btn-secondary"
id="device-filter-reset" style="display:none;">✕ Reset</button>
<span id="device-filter-count" style="font-size:0.8rem;color:#64748b;"></span>
</div>
<div class="table-wrap">
<table class="responsive-table">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Location</th>
<th>Slots</th>
<th>Installed</th>
<th>Brands</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for d in devices %}
{% set installed = d.installed_count() %}
{% if installed == 0 %}{% set fill_state = 'empty' %}
{% elif installed >= d.battery_slots %}{% set fill_state = 'full' %}
{% else %}{% set fill_state = 'partial' %}{% endif %}
<tr data-type="{{ d.device_type or '' }}"
data-location="{{ d.location or '' }}"
data-fill="{{ fill_state }}"
data-name="{{ d.name|lower }}">
<td data-label="Device"><a href="{{ url_for('device_detail', device_id=d.id) }}"><strong>{{ d.name }}</strong></a></td>
<td data-label="Type">{{ d.device_type or '—' }}</td>
<td data-label="Location">{{ d.location or '—' }}</td>
<td data-label="Slots">{{ d.battery_slots }}</td>
<td data-label="Installed">
{{ installed }} / {{ d.battery_slots }}
{% if installed >= d.battery_slots %}
<span class="badge badge-retired">Full</span>
{% endif %}
</td>
<td data-label="Brands">
{% set brands = d.installed_brands() %}
{% if brands %}
{{ brands|join(', ') }}
{% if d.has_mixed_brands() %}
<span class="badge badge-warning">⚠ mixed</span>
{% endif %}
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td data-label="Actions" style="white-space:nowrap;">
<a class="btn btn-sm btn-secondary" href="{{ url_for('device_detail', device_id=d.id) }}">View</a>
{% if installed > 0 %}
<form class="inline" method="post" action="{{ url_for('device_unassign_all', device_id=d.id) }}"
data-confirm="Unassign all batteries from {{ d.name }}?"
data-confirm-ok="Unassign" data-confirm-class="btn-warning">
<button class="btn btn-sm btn-warning" type="submit">Unassign All</button>
</form>
{% endif %}
<form class="inline" method="post" action="{{ url_for('device_delete', device_id=d.id) }}"
data-confirm="Delete {{ d.name }}? All installed batteries will be unassigned."
data-confirm-ok="Delete" data-confirm-class="btn-danger">
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
</form>
</td>
</tr>
{% else %}
<tr><td colspan="7" class="text-muted" style="text-align:center;padding:1rem;">No devices yet. <a href="{{ url_for('device_add') }}">Add one.</a></td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<a class="btn btn-primary" href="{{ url_for('device_add') }}">+ Add Device</a>
<script>
function applyDeviceFilters() {
var typeVal = document.getElementById('filter-type').value;
var locationVal = document.getElementById('filter-location').value;
var fillVal = document.getElementById('filter-fill').value;
var textVal = document.getElementById('filter-device-text').value.toLowerCase();
var rows = document.querySelectorAll('tbody tr[data-name]');
var visible = 0;
rows.forEach(function(row) {
var rowType = row.dataset.type || '';
var rowLocation = row.dataset.location || '';
var rowFill = row.dataset.fill || '';
var rowName = row.dataset.name || '';
var show = (!typeVal || rowType === typeVal) &&
(!locationVal || rowLocation === locationVal) &&
(!fillVal || rowFill === fillVal) &&
(!textVal || rowName.includes(textVal) ||
rowType.toLowerCase().includes(textVal) ||
rowLocation.toLowerCase().includes(textVal));
row.style.display = show ? '' : 'none';
if (show) visible++;
});
var active = typeVal || locationVal || fillVal || textVal;
document.getElementById('device-filter-reset').style.display = active ? '' : 'none';
document.getElementById('device-filter-count').textContent =
active ? (visible + ' of ' + rows.length + ' shown') : '';
}
function resetDeviceFilters() {
document.getElementById('filter-type').value = '';
document.getElementById('filter-location').value = '';
document.getElementById('filter-fill').value = '';
document.getElementById('filter-device-text').value = '';
applyDeviceFilters();
}
</script>
{% endblock %}