diff --git a/src/bggpipe/static/app.css b/src/bggpipe/static/app.css index 77425a2..291c3b8 100644 --- a/src/bggpipe/static/app.css +++ b/src/bggpipe/static/app.css @@ -556,6 +556,12 @@ select { margin-bottom: .8rem; } .orow { display: flex; gap: .5rem; } .formlabel { margin: 1.2rem 0 .35rem; } +.unitdesc { margin: -.3rem 0 .7rem; display: flex; gap: .6rem; + align-items: center; flex-wrap: wrap; } +.unitdesc .descform:not([hidden]) { display: flex; gap: .4rem; flex: 1 1 16rem; } +.unitdesc .descform input { flex: 1 1 auto; font: inherit; font-size: .85rem; + border: 2px solid var(--board-edge); border-radius: var(--radius); + padding: .25rem .45rem; } .formlabel.small { font-weight: 700; font-size: .85rem; color: var(--ink-soft); margin: .8rem 0 .3rem; } button.opening { diff --git a/src/bggpipe/templates/pages/shelves.html b/src/bggpipe/templates/pages/shelves.html index 4bccac5..10006fa 100644 --- a/src/bggpipe/templates/pages/shelves.html +++ b/src/bggpipe/templates/pages/shelves.html @@ -19,6 +19,9 @@ + @@ -113,6 +116,16 @@ function unitBlock(u) { +

+ ${esc(u.description || "")} + + +

${unitRows(u.openings)}

Add an opening

@@ -275,6 +288,17 @@ function wire() { if (confirm(`Remove "${b.dataset.unit}" and its openings? Its games become unshelved.`)) post("/api/furniture/delete-unit", {name: b.dataset.unit}); }); + document.querySelectorAll(".editunitdesc").forEach(b => + b.onclick = () => { + const form = b.closest(".unitdesc").querySelector(".descform"); + form.hidden = !form.hidden; + if (!form.hidden) form.querySelector("input").focus(); + }); + document.querySelectorAll(".saveunitdesc").forEach(b => + b.onclick = () => post("/api/furniture/edit-unit", { + name: b.dataset.unit, + description: b.closest(".descform").querySelector("input").value, + })); document.querySelectorAll(".addopeningform").forEach(form => form.onsubmit = async e => { e.preventDefault(); @@ -410,7 +434,9 @@ unitForm.addEventListener("submit", async e => { // half-failure) is fine to fill, so a 409 on step one does not stop us const existing = SHELVES.units.find(u => u.name === f.name.value.trim()); if (!existing || existing.openings.length) { - const made = await post("/api/furniture/add-unit", {name: f.name.value}); + const made = await post("/api/furniture/add-unit", { + name: f.name.value, description: f.description.value, + }); if (!made && !existing) return; } const filled = await post("/api/furniture/add-openings", { diff --git a/src/bggpipe/webreview.py b/src/bggpipe/webreview.py index d1b588d..1fbf0e7 100644 --- a/src/bggpipe/webreview.py +++ b/src/bggpipe/webreview.py @@ -228,6 +228,7 @@ class ResearchBody(BaseModel): class UnitBody(BaseModel): name: str + description: str = "" # free text: "the wall behind the couch" class OpeningsBody(BaseModel): @@ -1098,6 +1099,7 @@ def create_app( located_keys.add(key) # rides with its container out_units = [] for unit in units: + unit = {**unit, "description": unit.get("description", "")} out_openings = [] for opening in unit.get("openings", []): here = residents.get(opening["id"], []) @@ -1121,7 +1123,13 @@ def create_app( ], } ) - out_units.append({"name": unit["name"], "openings": out_openings}) + out_units.append( + { + "name": unit["name"], + "description": unit.get("description", ""), + "openings": out_openings, + } + ) unshelved = [] for key, entry in sorted( games.items(), key=lambda p: (p[1].get("name") or "").casefold() @@ -1198,10 +1206,26 @@ def create_app( def add(units): if any(u["name"] == name for u in units): raise HTTPException(409, f"a unit named {name!r} already exists") - units.append({"name": name, "openings": []}) + units.append( + { + "name": name, + "description": body.description.strip(), + "openings": [], + } + ) return _mutate_furniture(add) + @app.post("/api/furniture/edit-unit") + def api_edit_unit(body: UnitBody) -> dict: + def apply(units): + unit = next((u for u in units if u["name"] == body.name), None) + if unit is None: + raise HTTPException(404, "no such unit") + unit["description"] = body.description.strip() + + return _mutate_furniture(apply) + @app.post("/api/furniture/delete-unit") def api_delete_unit(body: UnitBody) -> dict: def drop(units): diff --git a/tests/test_shelves.py b/tests/test_shelves.py index 604b112..0ed202e 100644 --- a/tests/test_shelves.py +++ b/tests/test_shelves.py @@ -820,3 +820,23 @@ def test_opening_descriptions_round_trip(tmp_path): saved = _json.loads(cfg.furniture_path.read_text())["units"][0]["openings"][0] assert saved["description"] == "tall bookcase by the window" + + +def test_unit_descriptions_round_trip(tmp_path): + web, cfg = _web(tmp_path, {}) + web.post( + "/api/furniture/add-unit", + json={"name": "Den", "description": "the wall behind the couch"}, + ) + assert ( + web.get("/api/shelves").json()["units"][0]["description"] + == "the wall behind the couch" + ) + web.post( + "/api/furniture/edit-unit", + json={"name": "Den", "description": "now in the study"}, + ) + import json as _json + + saved = _json.loads(cfg.furniture_path.read_text())["units"][0] + assert saved["description"] == "now in the study"