Add size, chemistry, capacity, purchase date to bulk add form

Shared metadata fields settable at creation time apply to all
batteries in the batch, avoiding per-battery detail page visits.
This commit is contained in:
2026-04-12 15:15:07 -05:00
parent 604d7bb699
commit 5a7bbd46ab
2 changed files with 83 additions and 25 deletions
+19 -4
View File
@@ -37,10 +37,11 @@ def create_app(config_object="config"):
@app.route("/battery/add", methods=["GET", "POST"]) @app.route("/battery/add", methods=["GET", "POST"])
def battery_add(): def battery_add():
if request.method == "POST": if request.method == "POST":
brand = request.form.get("brand", "").strip() f = request.form
notes = request.form.get("notes", "").strip() or None brand = f.get("brand", "").strip()
notes = f.get("notes", "").strip() or None
try: try:
count = max(1, min(50, int(request.form.get("count", 1) or 1))) count = max(1, min(50, int(f.get("count", 1) or 1)))
except (ValueError, TypeError): except (ValueError, TypeError):
count = 1 count = 1
@@ -51,10 +52,24 @@ def create_app(config_object="config"):
form_brand="", form_count=1, form_notes=notes or "", form_brand="", form_count=1, form_notes=notes or "",
brands=brands), 400 brands=brands), 400
def _int(key):
v = f.get(key, "").strip()
try:
return int(v) if v else None
except ValueError:
return None
size = f.get("size", "").strip() or None
chemistry = f.get("chemistry", "").strip() or None
capacity_mah = _int("capacity_mah")
purchase_date = f.get("purchase_date", "").strip() or None
existing = db.query(func.count(Battery.id)).filter_by(brand=brand).scalar() existing = db.query(func.count(Battery.id)).filter_by(brand=brand).scalar()
for i in range(count): for i in range(count):
label = f"{brand} {existing + i + 1:03d}" label = f"{brand} {existing + i + 1:03d}"
db.add(Battery(label=label, brand=brand, status="available", notes=notes)) db.add(Battery(label=label, brand=brand, status="available", notes=notes,
size=size, chemistry=chemistry,
capacity_mah=capacity_mah, purchase_date=purchase_date))
db.commit() db.commit()
flash(f"Added {count} {brand} batter{'y' if count == 1 else 'ies'}.", "success") flash(f"Added {count} {brand} batter{'y' if count == 1 else 'ies'}.", "success")
return redirect(url_for("dashboard")) return redirect(url_for("dashboard"))
+64 -21
View File
@@ -6,29 +6,72 @@
<div class="card"> <div class="card">
<form method="post" action="{{ url_for('battery_add') }}"> <form method="post" action="{{ url_for('battery_add') }}">
<div class="form-group">
<label for="brand-select">Brand <span class="text-danger">*</span></label>
<select id="brand-select" onchange="brandSelectChanged(this, 'brand')">
<option value="">— select a brand —</option>
{% for b in brands|default([]) %}
<option value="{{ b }}">{{ b }}</option>
{% endfor %}
<option value="__new__"> New brand…</option>
</select>
<input type="text" id="brand" name="brand" value="{{ form_brand|default('') }}"
placeholder="Type new brand name" required
style="display:none;margin-top:0.4rem;">
</div>
<div class="form-group"> <div style="display:grid;grid-template-columns:1fr 1fr;gap:0 1rem;">
<label for="count">Quantity</label>
<input type="number" id="count" name="count" value="{{ form_count|default(1) }}" min="1" max="50"> <div class="form-group" style="grid-column:1/-1;">
<small class="text-muted">Labels are auto-generated (e.g. Eneloop 001, Eneloop 002)</small> <label for="brand-select">Brand <span class="text-danger">*</span></label>
<select id="brand-select" onchange="metaSelectChanged(this, 'brand')">
<option value="">— select a brand —</option>
{% for b in brands|default([]) %}
<option value="{{ b }}">{{ b }}</option>
{% endfor %}
<option value="__new__"> New brand…</option>
</select>
<input type="text" id="brand" name="brand" value="{{ form_brand|default('') }}"
placeholder="Type new brand name" required
style="display:none;margin-top:0.4rem;">
</div>
<div class="form-group">
<label for="count">Quantity</label>
<input type="number" id="count" name="count" value="{{ form_count|default(1) }}" min="1" max="50">
<small class="text-muted">Labels are auto-generated (e.g. Eneloop 001, Eneloop 002)</small>
</div>
<div class="form-group">
<label for="capacity_mah">Capacity (mAh)</label>
<input type="number" id="capacity_mah" name="capacity_mah" min="0"
value="" placeholder="e.g. 2000 — optional">
</div>
<div class="form-group">
<label>Size</label>
<select id="size-select" onchange="metaSelectChanged(this,'size')">
<option value="">— none —</option>
{% for opt in ['AA','AAA','C','D','9V','18650','21700','14500','26650','CR2032','CR123A'] %}
<option value="{{ opt }}">{{ opt }}</option>
{% endfor %}
<option value="__new__">Other…</option>
</select>
<input type="text" id="size" name="size" value=""
placeholder="Enter size" style="display:none;margin-top:0.4rem;">
</div>
<div class="form-group">
<label>Chemistry</label>
<select id="chemistry-select" onchange="metaSelectChanged(this,'chemistry')">
<option value="">— none —</option>
{% for opt in ['NiMH','Alkaline','Li-ion','LiFePO4','NiCd','Zinc-Carbon','Li-MnO2'] %}
<option value="{{ opt }}">{{ opt }}</option>
{% endfor %}
<option value="__new__">Other…</option>
</select>
<input type="text" id="chemistry" name="chemistry" value=""
placeholder="Enter chemistry" style="display:none;margin-top:0.4rem;">
</div>
<div class="form-group">
<label for="purchase_date">Purchase Date</label>
<input type="date" id="purchase_date" name="purchase_date" value="">
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="notes">Notes</label> <label for="notes">Notes</label>
<textarea id="notes" name="notes" placeholder="Optional notes applied to all batteries…">{{ form_notes|default('') }}</textarea> <textarea id="notes" name="notes"
placeholder="Optional notes applied to all batteries…">{{ form_notes|default('') }}</textarea>
</div> </div>
<div class="form-actions"> <div class="form-actions">
@@ -39,9 +82,9 @@
</div> </div>
<script> <script>
function brandSelectChanged(sel, inputId) { function metaSelectChanged(sel, inputId) {
var input = document.getElementById(inputId); var input = document.getElementById(inputId);
if (sel.value === '__new__' || sel.value === '') { if (sel.value === '__new__') {
input.style.display = ''; input.style.display = '';
input.value = ''; input.value = '';
input.focus(); input.focus();
@@ -51,7 +94,7 @@ function brandSelectChanged(sel, inputId) {
} }
} }
// Restore state on error re-render (form_brand set) // Restore brand state on error re-render
(function () { (function () {
var input = document.getElementById('brand'); var input = document.getElementById('brand');
var sel = document.getElementById('brand-select'); var sel = document.getElementById('brand-select');