diff --git a/data/furniture.json b/data/furniture.json index 0938506..c65662a 100644 --- a/data/furniture.json +++ b/data/furniture.json @@ -166,6 +166,52 @@ "depth_in": 9.5 } ] + }, + { + "name": "Library Left Shelving", + "description": "To the right of the Kallax", + "openings": [ + { + "label": "A1", + "zone": "", + "width_in": 23.25, + "height_in": 10.25, + "depth_in": 9.5, + "id": "library-right-shelving-2-a1" + }, + { + "label": "B1", + "zone": "", + "width_in": 23.25, + "height_in": 10.25, + "depth_in": 9.5, + "id": "library-right-shelving-2-b1" + }, + { + "label": "C1", + "zone": "", + "width_in": 23.25, + "height_in": 10.25, + "depth_in": 9.5, + "id": "library-right-shelving-2-c1" + }, + { + "label": "D1", + "zone": "", + "width_in": 23.25, + "height_in": 10.25, + "depth_in": 9.5, + "id": "library-right-shelving-2-d1" + }, + { + "label": "E1", + "zone": "", + "width_in": 23.25, + "height_in": 10.25, + "depth_in": 9.5, + "id": "library-right-shelving-2-e1" + } + ] } ] } diff --git a/src/bggpipe/static/app.css b/src/bggpipe/static/app.css index 4840225..db88321 100644 --- a/src/bggpipe/static/app.css +++ b/src/bggpipe/static/app.css @@ -552,6 +552,9 @@ select { margin-bottom: .8rem; } .orow { display: flex; gap: .5rem; } .formlabel { margin: 1.2rem 0 .35rem; } +.editform label.attop { flex-direction: row; align-items: center; gap: .35rem; + text-transform: none; letter-spacing: normal; align-self: end; + padding-bottom: .45rem; } .renamerow:not([hidden]) { display: flex; gap: .4rem; margin: .3rem 0 .6rem; } .renamerow input { font: inherit; font-size: .9rem; border: 2px solid var(--board-edge); border-radius: var(--radius); diff --git a/src/bggpipe/templates/pages/shelves.html b/src/bggpipe/templates/pages/shelves.html index 48b7e70..bf14bb9 100644 --- a/src/bggpipe/templates/pages/shelves.html +++ b/src/bggpipe/templates/pages/shelves.html @@ -148,6 +148,7 @@ function unitBlock(u) { + @@ -325,7 +326,7 @@ function wire() { name: b.dataset.unit, new_name: b.closest(".renamerow").querySelector("input").value, })); - document.querySelectorAll(".editunitdesc").forEach(b = + document.querySelectorAll(".editunitdesc").forEach(b => b.onclick = () => { const form = b.closest(".unitdesc").querySelector(".descform"); form.hidden = !form.hidden; @@ -359,6 +360,7 @@ function wire() { try { await post("/api/furniture/add-openings", { unit: form.dataset.unit, zone: f.zone.value, + at_top: f.at_top.checked, ...(grid ? {rows, cols} : {label: f.label.value}), width_in: parseFloat(f.width_in.value) || null, height_in: parseFloat(f.height_in.value) || null, diff --git a/src/bggpipe/webreview.py b/src/bggpipe/webreview.py index d0e0504..6d11979 100644 --- a/src/bggpipe/webreview.py +++ b/src/bggpipe/webreview.py @@ -237,6 +237,7 @@ class OpeningsBody(BaseModel): unit: str zone: str = "" + at_top: bool = False # new openings land ABOVE the existing rows label: str = "" # single mode rows: int = 0 # grid mode when rows*cols > 0 cols: int = 0 @@ -1307,6 +1308,14 @@ def create_app( # a half-sized opening would silently become limitless: # fit checks need all three axes, or none (virtual) raise HTTPException(400, "give all three interior dimensions, or none") + added: list[dict] = [] + + def place(new_openings): + if body.at_top: + unit["openings"][:0] = new_openings + else: + unit["openings"].extend(new_openings) + if body.rows and body.cols: # a second grid on the same unit continues the row letters # (A1 already exists -> next section starts at C, say): @@ -1329,7 +1338,7 @@ def create_app( break for c in range(body.cols): label = f"{letters}{c + 1}" - unit["openings"].append( + added.append( { "id": new_opening_id(units, unit["name"], label), "label": label, @@ -1341,7 +1350,7 @@ def create_app( label = body.label.strip() if not label: raise HTTPException(400, "the opening needs a label") - unit["openings"].append( + added.append( { "id": new_opening_id(units, unit["name"], label), "label": label, @@ -1349,6 +1358,7 @@ def create_app( **dims, } ) + place(added) return _mutate_furniture(add) diff --git a/tests/test_shelves.py b/tests/test_shelves.py index a1c44ff..92a7d41 100644 --- a/tests/test_shelves.py +++ b/tests/test_shelves.py @@ -978,3 +978,34 @@ def test_rename_unit_keeps_openings_locations_and_description(tmp_path): ).status_code == 409 ) + + +def test_add_openings_at_top(tmp_path): + """Things live ON the furniture too: an opening added at_top lands + above the existing rows and renders first in the wall diagram.""" + web, cfg = _web(tmp_path, {}) + web.post("/api/furniture/add-unit", json={"name": "Kallax"}) + web.post( + "/api/furniture/add-openings", + json={ + "unit": "Kallax", + "rows": 2, + "cols": 2, + "width_in": 13.25, + "height_in": 13.25, + "depth_in": 15.4, + }, + ) + web.post( + "/api/furniture/add-openings", + json={ + "unit": "Kallax", + "label": "on top", + "zone": "display", + "at_top": True, + }, + ) + labels = [ + o["label"] for o in web.get("/api/shelves").json()["units"][0]["openings"] + ] + assert labels == ["on top", "A1", "A2", "B1", "B2"]