From 51f8122201f56936abe9946f0d0851c80fc4454b Mon Sep 17 00:00:00 2001 From: Jon Seager Date: Sat, 7 Feb 2026 12:10:56 +0000 Subject: [PATCH] feat(ui): improve form UX with collapsible equipment and clickable controls - Split equipment into separate Grinder and Brewer collapsible sections with summary bars showing current selection - Make all summary bars, matched-entity cards, and searchable-select selected values clickable anywhere to change (not just the X/Change button) - Fix stepper buttons broken after manual input by switching from data-attr:value to data-bind with Number() coercion - Merge Water + Brew Time into single Recipe section - Make all add/confirm buttons full-width - Add sticky submit button on mobile - Fix missing closing > on cafe indicator div - Fix broken Askama syntax on checkin page from formatter --- src/application/routes/api/brews.rs | 2 +- src/presentation/web/views/brews.rs | 34 +-- static/css/input.css | 27 ++- static/js/components/searchable-select.js | 8 +- templates/pages/add.html | 220 ++++++++++-------- templates/pages/checkin.html | 12 +- .../partials/forms/scan_result_form.html | 4 +- 7 files changed, 183 insertions(+), 124 deletions(-) diff --git a/src/application/routes/api/brews.rs b/src/application/routes/api/brews.rs index 21d903f..46511f9 100644 --- a/src/application/routes/api/brews.rs +++ b/src/application/routes/api/brews.rs @@ -79,7 +79,7 @@ pub(crate) async fn load_brew_form_data(state: &AppState) -> Result Self { Self { bag_id: String::new(), - grinder_id: String::new(), + grinder_name: String::new(), brewer_id: String::new(), + brewer_name: String::new(), filter_paper_id: String::new(), + filter_paper_name: String::new(), coffee_weight: 15.0, grind_setting: 6.0, water_volume: 250, @@ -157,22 +162,25 @@ impl Default for BrewDefaultsView { } } -impl From for BrewDefaultsView { - fn from(brew: Brew) -> Self { +impl From for BrewDefaultsView { + fn from(brew: BrewWithDetails) -> Self { Self { - bag_id: brew.bag_id.to_string(), - - grinder_id: brew.grinder_id.to_string(), - brewer_id: brew.brewer_id.to_string(), + bag_id: brew.brew.bag_id.to_string(), + grinder_id: brew.brew.grinder_id.to_string(), + grinder_name: brew.grinder_name, + brewer_id: brew.brew.brewer_id.to_string(), + brewer_name: brew.brewer_name, filter_paper_id: brew + .brew .filter_paper_id .map(|id| id.to_string()) .unwrap_or_default(), - coffee_weight: brew.coffee_weight, - grind_setting: brew.grind_setting, - water_volume: brew.water_volume, - water_temp: brew.water_temp, - brew_time: brew.brew_time, + filter_paper_name: brew.filter_paper_name.unwrap_or_default(), + coffee_weight: brew.brew.coffee_weight, + grind_setting: brew.brew.grind_setting, + water_volume: brew.brew.water_volume, + water_temp: brew.brew.water_temp, + brew_time: brew.brew.brew_time, } } } diff --git a/static/css/input.css b/static/css/input.css index 5ef29d3..145246d 100644 --- a/static/css/input.css +++ b/static/css/input.css @@ -134,8 +134,8 @@ input.input-field[type="number"] { align-items: center; justify-content: center; border-radius: 0.375rem; - border: none; - background-color: var(--accent-subtle); + border: 1px solid var(--accent); + background-color: var(--surface); color: var(--accent); font-weight: 700; transition: background-color 150ms ease, color 150ms ease; @@ -146,6 +146,29 @@ input.input-field[type="number"] { color: var(--accent-text); } +/* ── Sticky Submit ────────────────────────────────────────────── */ + +.sticky-submit { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 20; + padding: 0.75rem 1.5rem; + padding-bottom: max(0.75rem, env(safe-area-inset-bottom)); + background-color: var(--surface); + border-top: 1px solid var(--border); +} + +@media (min-width: 768px) { + .sticky-submit { + position: static; + padding: 0; + background-color: transparent; + border-top: none; + } +} + /* ── Pills ─────────────────────────────────────────────────────── */ .pill { diff --git a/static/js/components/searchable-select.js b/static/js/components/searchable-select.js index a232caa..2bc79dc 100644 --- a/static/js/components/searchable-select.js +++ b/static/js/components/searchable-select.js @@ -37,7 +37,7 @@ customElements.define("searchable-select", class extends HTMLElement { clear.innerHTML = ''; const selectedWrap = document.createElement("div"); - selectedWrap.className = "relative hidden"; + selectedWrap.className = "relative hidden cursor-pointer"; selectedWrap.appendChild(display); selectedWrap.appendChild(clear); @@ -87,7 +87,7 @@ customElements.define("searchable-select", class extends HTMLElement { })); }); - clear.addEventListener("click", () => { + const doClear = () => { hidden.value = ""; display.textContent = ""; search.value = ""; @@ -97,6 +97,8 @@ customElements.define("searchable-select", class extends HTMLElement { setTimeout(() => search.focus(), 0); this.dispatchEvent(new CustomEvent("clear", { bubbles: true })); - }); + }; + + selectedWrap.addEventListener("click", doClear); } }); diff --git a/templates/pages/add.html b/templates/pages/add.html index 4403135..c415a6e 100644 --- a/templates/pages/add.html +++ b/templates/pages/add.html @@ -69,6 +69,11 @@ data-signals:_brew-volume="{{ defaults.water_volume }}" data-signals:_brew-weight="{{ defaults.coffee_weight }}" data-signals:_brew-time="{{ defaults.brew_time.unwrap_or(120) }}" + data-signals:_edit-grinder="{% if defaults.grinder_name.is_empty() %}true{% else %}false{% endif %}" + data-signals:_edit-brewer="{% if defaults.brewer_name.is_empty() %}true{% else %}false{% endif %}" + data-signals:_grinder-display="'{{ defaults.grinder_name }}'" + data-signals:_brewer-display="'{{ defaults.brewer_name }}'" + data-signals:_filter-display="'{{ defaults.filter_paper_name }}'" data-signals:_cafe-id="''" data-signals:_cafe-name="''" data-signals:_cafe-city="''" @@ -108,24 +113,24 @@
-
@@ -147,7 +152,7 @@
- Roaster * + Roaster* {% for roaster in roaster_options %}
@@ -206,7 +211,7 @@
- Roast * + Roast* {% for roast in roast_options %}
@@ -253,13 +258,13 @@

New Brew

Log a cup of coffee.

- +
-

Coffee

+

Coffee

- Bag * + Bag* {% for bag in bag_options %}
- Weight (g) * + Weight (g)*
- - - + + +
@@ -283,22 +288,36 @@
-

Grinder

-
- -
- Grind Setting * -
- - - +

Grinder

+ +
+
+ + · + +
+ +
+ +
+
+ +
+ Grind Setting* +
+ + + +
@@ -306,56 +325,65 @@
-

Brewer

-
- - +

Brewer

+ +
+
+ + + +
+ +
+ +
+
+ + +
- +
-

Water

-
+

Recipe

+
- Volume (ml) * + Volume (ml)*
- - - + + +
- Temp (°C) * + Temp (°C)*
- - - + + +
-
-
- -
-

Brew Time

-
- Time + Time
@@ -368,7 +396,7 @@
-

Quick Notes

+

Quick Notes

-
-
@@ -408,9 +436,9 @@

Add brewing equipment to your collection.

-
+
-
@@ -449,7 +477,7 @@
-