Add inline assign from dashboard, specific battery picker on device, dynamic install rows
- Dashboard: replace Assign link with device dropdown + arrow button for quick inline assignment without leaving the page - Device detail: replace hardcoded 4-row install form with 1 row + '+ Add brand' button that clones rows dynamically - Device detail: add 'Install Specific Battery' card with dropdown of all available batteries (label, brand, size, notes) via new /device/<id>/install-one route - Tests: 4 new acceptance tests covering dashboard quick-assign and install-one, including capacity enforcement on both paths (39 total)
This commit is contained in:
@@ -33,8 +33,9 @@ def create_app(config_object="config"):
|
||||
.filter(Battery.storage_location.isnot(None))
|
||||
.distinct().order_by(Battery.storage_location).all()
|
||||
]
|
||||
devices = db.query(Device).order_by(Device.name).all()
|
||||
return render_template("dashboard.html", batteries=batteries,
|
||||
storage_locations=storage_locations)
|
||||
storage_locations=storage_locations, devices=devices)
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Battery — add
|
||||
@@ -361,7 +362,11 @@ def create_app(config_object="config"):
|
||||
if device is None:
|
||||
abort(404)
|
||||
brands = [r[0] for r in db.query(Battery.brand).distinct().order_by(Battery.brand).all()]
|
||||
return render_template("device_detail.html", device=device, brands=brands)
|
||||
available_batteries = (db.query(Battery)
|
||||
.filter_by(status="available")
|
||||
.order_by(Battery.label).all())
|
||||
return render_template("device_detail.html", device=device, brands=brands,
|
||||
available_batteries=available_batteries)
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Devices — install batteries
|
||||
@@ -436,6 +441,40 @@ def create_app(config_object="config"):
|
||||
)
|
||||
return redirect(url_for("device_detail", device_id=device_id))
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Devices — install one specific battery
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
@app.route("/device/<int:device_id>/install-one", methods=["POST"])
|
||||
def device_install_one(device_id):
|
||||
device = db.get(Device, device_id)
|
||||
if device is None:
|
||||
abort(404)
|
||||
battery_id = request.form.get("battery_id", type=int)
|
||||
battery = db.get(Battery, battery_id)
|
||||
if battery is None or not battery.is_available():
|
||||
flash("Battery not found or not available.", "error")
|
||||
return redirect(url_for("device_detail", device_id=device_id))
|
||||
if device.installed_count() >= device.battery_slots:
|
||||
flash(
|
||||
f"{device.name} is full "
|
||||
f"({device.battery_slots}/{device.battery_slots} slots used).",
|
||||
"error",
|
||||
)
|
||||
return redirect(url_for("device_detail", device_id=device_id))
|
||||
existing_brands = device.installed_brands()
|
||||
if existing_brands and battery.brand not in existing_brands:
|
||||
flash(
|
||||
f"Warning: {device.name} already has batteries from "
|
||||
f"{', '.join(existing_brands)}. Mixing brands is not recommended.",
|
||||
"warning",
|
||||
)
|
||||
battery.status = "installed"
|
||||
battery.device_id = device.id
|
||||
db.commit()
|
||||
flash(f"{battery.label} installed in {device.name}.", "success")
|
||||
return redirect(url_for("device_detail", device_id=device_id))
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Devices — delete
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
Reference in New Issue
Block a user