The arrow that unbound half of wire(), and openings for the unit's top
Eric: styled buttons look great, but edit-description does nothing. One missing character — .forEach(b = instead of (b => — turned the callback into a strict-mode ReferenceError that killed wire() midway: everything bound before the typo (rename, duplicate) worked, everything after (edit description, its save, the add-opening submit) silently never bound, and the throw hid inside an async refresh where no pageerror fires. Fixed and verified live: description edits save, add-opening works again. And Eric's next question answered in code: things live ON the furniture too. The add-opening form gains an "at the top" checkbox — new openings (or whole grids) land above the existing rows instead of below, so "on top of the Kallax" is one labeled opening away. Give it real width/depth and the height to your ceiling, or no sizes for a no-limit display spot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
co-authored by
Claude Fable 5
parent
2f526335e8
commit
4ce7706521
@@ -166,6 +166,52 @@
|
|||||||
"depth_in": 9.5
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -552,6 +552,9 @@ select {
|
|||||||
margin-bottom: .8rem; }
|
margin-bottom: .8rem; }
|
||||||
.orow { display: flex; gap: .5rem; }
|
.orow { display: flex; gap: .5rem; }
|
||||||
.formlabel { margin: 1.2rem 0 .35rem; }
|
.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:not([hidden]) { display: flex; gap: .4rem; margin: .3rem 0 .6rem; }
|
||||||
.renamerow input { font: inherit; font-size: .9rem;
|
.renamerow input { font: inherit; font-size: .9rem;
|
||||||
border: 2px solid var(--board-edge); border-radius: var(--radius);
|
border: 2px solid var(--board-edge); border-radius: var(--radius);
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ function unitBlock(u) {
|
|||||||
<label>Columns <input name="cols" inputmode="numeric" size="3" value="1"></label>
|
<label>Columns <input name="cols" inputmode="numeric" size="3" value="1"></label>
|
||||||
<label>Label <input name="label" placeholder="wide top — for a single opening"></label>
|
<label>Label <input name="label" placeholder="wide top — for a single opening"></label>
|
||||||
<label>Zone <input name="zone"></label>
|
<label>Zone <input name="zone"></label>
|
||||||
|
<label class="attop"><input type="checkbox" name="at_top"> at the top</label>
|
||||||
<label>W <input name="width_in" size="5" placeholder="13.25"></label>
|
<label>W <input name="width_in" size="5" placeholder="13.25"></label>
|
||||||
<label>H <input name="height_in" size="5" placeholder="13.25"></label>
|
<label>H <input name="height_in" size="5" placeholder="13.25"></label>
|
||||||
<label>D <input name="depth_in" size="5" placeholder="15.4"></label>
|
<label>D <input name="depth_in" size="5" placeholder="15.4"></label>
|
||||||
@@ -325,7 +326,7 @@ function wire() {
|
|||||||
name: b.dataset.unit,
|
name: b.dataset.unit,
|
||||||
new_name: b.closest(".renamerow").querySelector("input").value,
|
new_name: b.closest(".renamerow").querySelector("input").value,
|
||||||
}));
|
}));
|
||||||
document.querySelectorAll(".editunitdesc").forEach(b =
|
document.querySelectorAll(".editunitdesc").forEach(b =>
|
||||||
b.onclick = () => {
|
b.onclick = () => {
|
||||||
const form = b.closest(".unitdesc").querySelector(".descform");
|
const form = b.closest(".unitdesc").querySelector(".descform");
|
||||||
form.hidden = !form.hidden;
|
form.hidden = !form.hidden;
|
||||||
@@ -359,6 +360,7 @@ function wire() {
|
|||||||
try {
|
try {
|
||||||
await post("/api/furniture/add-openings", {
|
await post("/api/furniture/add-openings", {
|
||||||
unit: form.dataset.unit, zone: f.zone.value,
|
unit: form.dataset.unit, zone: f.zone.value,
|
||||||
|
at_top: f.at_top.checked,
|
||||||
...(grid ? {rows, cols} : {label: f.label.value}),
|
...(grid ? {rows, cols} : {label: f.label.value}),
|
||||||
width_in: parseFloat(f.width_in.value) || null,
|
width_in: parseFloat(f.width_in.value) || null,
|
||||||
height_in: parseFloat(f.height_in.value) || null,
|
height_in: parseFloat(f.height_in.value) || null,
|
||||||
|
|||||||
@@ -237,6 +237,7 @@ class OpeningsBody(BaseModel):
|
|||||||
|
|
||||||
unit: str
|
unit: str
|
||||||
zone: str = ""
|
zone: str = ""
|
||||||
|
at_top: bool = False # new openings land ABOVE the existing rows
|
||||||
label: str = "" # single mode
|
label: str = "" # single mode
|
||||||
rows: int = 0 # grid mode when rows*cols > 0
|
rows: int = 0 # grid mode when rows*cols > 0
|
||||||
cols: int = 0
|
cols: int = 0
|
||||||
@@ -1307,6 +1308,14 @@ def create_app(
|
|||||||
# a half-sized opening would silently become limitless:
|
# a half-sized opening would silently become limitless:
|
||||||
# fit checks need all three axes, or none (virtual)
|
# fit checks need all three axes, or none (virtual)
|
||||||
raise HTTPException(400, "give all three interior dimensions, or none")
|
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:
|
if body.rows and body.cols:
|
||||||
# a second grid on the same unit continues the row letters
|
# a second grid on the same unit continues the row letters
|
||||||
# (A1 already exists -> next section starts at C, say):
|
# (A1 already exists -> next section starts at C, say):
|
||||||
@@ -1329,7 +1338,7 @@ def create_app(
|
|||||||
break
|
break
|
||||||
for c in range(body.cols):
|
for c in range(body.cols):
|
||||||
label = f"{letters}{c + 1}"
|
label = f"{letters}{c + 1}"
|
||||||
unit["openings"].append(
|
added.append(
|
||||||
{
|
{
|
||||||
"id": new_opening_id(units, unit["name"], label),
|
"id": new_opening_id(units, unit["name"], label),
|
||||||
"label": label,
|
"label": label,
|
||||||
@@ -1341,7 +1350,7 @@ def create_app(
|
|||||||
label = body.label.strip()
|
label = body.label.strip()
|
||||||
if not label:
|
if not label:
|
||||||
raise HTTPException(400, "the opening needs a label")
|
raise HTTPException(400, "the opening needs a label")
|
||||||
unit["openings"].append(
|
added.append(
|
||||||
{
|
{
|
||||||
"id": new_opening_id(units, unit["name"], label),
|
"id": new_opening_id(units, unit["name"], label),
|
||||||
"label": label,
|
"label": label,
|
||||||
@@ -1349,6 +1358,7 @@ def create_app(
|
|||||||
**dims,
|
**dims,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
place(added)
|
||||||
|
|
||||||
return _mutate_furniture(add)
|
return _mutate_furniture(add)
|
||||||
|
|
||||||
|
|||||||
@@ -978,3 +978,34 @@ def test_rename_unit_keeps_openings_locations_and_description(tmp_path):
|
|||||||
).status_code
|
).status_code
|
||||||
== 409
|
== 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"]
|
||||||
|
|||||||
Reference in New Issue
Block a user