Replace browser confirm() dialogs with custom modal; add live label preview on battery add form

- base.html: add CSS/HTML/JS for styled in-app confirmation modal (dark-mode compatible via CSS vars)
- device_list, battery_detail: convert onsubmit confirm() to declarative data-confirm attributes
- dashboard: convert bulk Delete/Install buttons to use modal helpers (submitWithAction pattern)
- app.py: pass brand_counts dict to battery_add template
- battery_add.html: show live "Will create: Brand 001 → Brand 003" preview as brand/quantity change
- tests: add two tests covering brand_counts server-side rendering
This commit is contained in:
2026-04-13 09:53:21 -05:00
parent 3c2b2dc389
commit 39b52a3fa4
7 changed files with 135 additions and 14 deletions
+24 -9
View File
@@ -80,8 +80,8 @@
<span id="selected-count" style="font-size:0.85rem;color:#64748b;margin-right:0.25rem;"></span>
<button class="btn btn-sm btn-warning" name="action" value="unassign" type="submit">Unassign</button>
<button class="btn btn-sm btn-secondary" name="action" value="retire" type="submit">Retire</button>
<button class="btn btn-sm btn-danger" name="action" value="delete" type="submit"
onclick="return confirm('Permanently delete selected batteries?')">Delete</button>
<button class="btn btn-sm btn-danger" name="action" value="delete" type="button"
onclick="bulkActionConfirm(this, 'Permanently delete selected batteries?', 'Delete', 'btn-danger')">Delete</button>
<span style="display:flex;gap:0.35rem;align-items:center;flex-wrap:wrap;">
<input type="hidden" name="field_name" id="bulk-field-name" value="storage_location">
<select id="bulk-field-select" onchange="updateBulkField(this)"
@@ -121,8 +121,8 @@
<option value="{{ d.id }}">{{ d.name }} ({{ d.installed_count() }}/{{ d.battery_slots }})</option>
{% endfor %}
</select>
<button class="btn btn-sm btn-primary" name="action" value="install_device" type="submit"
onclick="return confirmInstallDevice()">Install in device</button>
<button class="btn btn-sm btn-primary" name="action" value="install_device" type="button"
onclick="confirmInstallDevice(this)">Install in device</button>
</span>
<span style="display:flex;gap:0.35rem;align-items:center;flex-wrap:wrap;">
<input type="date" name="charged_date" id="bulk-charged-date"
@@ -278,21 +278,36 @@ function applyFilters() {
updateToolbar();
}
function confirmInstallDevice() {
function confirmInstallDevice(btn) {
var deviceSel = document.getElementById('bulk-device-select');
if (!deviceSel.value) { deviceSel.focus(); return false; }
if (!deviceSel.value) { deviceSel.focus(); return; }
var movers = Array.prototype.filter.call(
document.querySelectorAll('.row-cb:checked'),
function(cb) { return cb.closest('tr').dataset.status === 'installed'; }
);
if (movers.length > 0) {
var n = movers.length;
return confirm(
showConfirm(
n + ' selected batter' + (n === 1 ? 'y is' : 'ies are') +
' already installed elsewhere. Unassign and move to the selected device?'
' already installed elsewhere. Unassign and move to the selected device?',
function() { submitWithAction(btn); },
'Move', 'btn-warning'
);
} else {
submitWithAction(btn);
}
return true;
}
function bulkActionConfirm(btn, msg, okLabel, okClass) {
showConfirm(msg, function() { submitWithAction(btn); }, okLabel, okClass);
}
function submitWithAction(btn) {
var form = btn.form || document.getElementById('bulk-form');
var inp = document.createElement('input');
inp.type = 'hidden'; inp.name = btn.name; inp.value = btn.value;
form.appendChild(inp);
form.submit();
}
function quickAssign(action, batteryId) {