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:
@@ -56,6 +56,13 @@
|
||||
--count-retired: #64748b;
|
||||
}
|
||||
|
||||
/* Confirmation modal */
|
||||
#confirm-modal { display:none; position:fixed; inset:0; z-index:1000; background:rgba(0,0,0,.45); align-items:center; justify-content:center; }
|
||||
#confirm-modal.open { display:flex; }
|
||||
#confirm-modal-box { background:var(--bg-card); border-radius:8px; padding:1.5rem; max-width:400px; width:calc(100% - 2rem); box-shadow:0 8px 24px rgba(0,0,0,.25); }
|
||||
#confirm-modal-msg { margin-bottom:1.25rem; color:var(--text-body); font-size:0.95rem; line-height:1.5; }
|
||||
#confirm-modal-actions { display:flex; gap:0.75rem; justify-content:flex-end; }
|
||||
|
||||
/* ─── Dark mode variables ────────────────────────────────────────── */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
@@ -326,10 +333,62 @@
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<div id="confirm-modal" role="dialog" aria-modal="true">
|
||||
<div id="confirm-modal-box">
|
||||
<p id="confirm-modal-msg"></p>
|
||||
<div id="confirm-modal-actions">
|
||||
<button id="confirm-modal-cancel" class="btn btn-secondary">Cancel</button>
|
||||
<button id="confirm-modal-ok" class="btn btn-danger">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/sw.js');
|
||||
}
|
||||
|
||||
(function() {
|
||||
var modal = document.getElementById('confirm-modal');
|
||||
var msgEl = document.getElementById('confirm-modal-msg');
|
||||
var okBtn = document.getElementById('confirm-modal-ok');
|
||||
var cancelBtn = document.getElementById('confirm-modal-cancel');
|
||||
var _cb = null;
|
||||
|
||||
window.showConfirm = function(msg, onOk, okLabel, okClass) {
|
||||
msgEl.textContent = msg;
|
||||
okBtn.textContent = okLabel || 'Confirm';
|
||||
okBtn.className = 'btn ' + (okClass || 'btn-danger');
|
||||
modal.classList.add('open');
|
||||
_cb = onOk;
|
||||
cancelBtn.focus();
|
||||
};
|
||||
|
||||
function closeModal() { modal.classList.remove('open'); _cb = null; }
|
||||
|
||||
okBtn.addEventListener('click', function() {
|
||||
var cb = _cb; closeModal(); if (cb) cb();
|
||||
});
|
||||
cancelBtn.addEventListener('click', closeModal);
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeModal(); });
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && modal.classList.contains('open')) closeModal();
|
||||
});
|
||||
|
||||
// Global handler: forms with data-confirm attribute
|
||||
document.addEventListener('submit', function(e) {
|
||||
var form = e.target;
|
||||
var msg = form.dataset.confirm;
|
||||
if (!msg || form.dataset.confirmed) return;
|
||||
e.preventDefault();
|
||||
var okLabel = form.dataset.confirmOk || 'Confirm';
|
||||
var okClass = form.dataset.confirmClass || 'btn-danger';
|
||||
window.showConfirm(msg, function() {
|
||||
form.dataset.confirmed = '1';
|
||||
form.submit();
|
||||
}, okLabel, okClass);
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user