From 3ae3963c5b3c0d08f239fb09883244cefc9ea34b Mon Sep 17 00:00:00 2001 From: Jon Seager Date: Wed, 4 Feb 2026 12:04:09 +0000 Subject: [PATCH] fix(templates): align table position and structure across all list pages Move list partials outside the section element on bags, gear, and brews pages so they become separate flex children of
, matching the pattern used by roasters, roasts, cafes, and cups. Add mt-6 to each partial's outer div for consistent spacing. Standardise brew and gear list partials to match the canonical structure: empty-state conditional with dashed amber border,
wrapper for the table, and data-star-scope attribute on the outer div. Update CLAUDE.md to document page template and list partial structure requirements, preventing future drift. --- CLAUDE.md | 52 +++++++++++++++++++++++++------ templates/bags.html | 4 +-- templates/brews.html | 4 +-- templates/gear.html | 4 +-- templates/partials/bag_list.html | 2 +- templates/partials/brew_list.html | 18 ++++++++--- templates/partials/gear_list.html | 17 +++++----- 7 files changed, 73 insertions(+), 28 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7f9bcef..fc7f058 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -424,16 +424,41 @@ The route handler in `application/routes/cafes.rs` accepts either `lat`/`lng` qu ## Table & List Patterns -### Template Structure +### Page Template Structure + +Each list page template (`templates/{entity}.html`) places the form and list as **separate siblings** so they become independent flex children of `
` (which uses `flex flex-col gap-6`): + +```html +
+
...
+ {% if is_authenticated %} +
+ +
+ {% endif %} +
+ +{% include "partials/{entity}_list.html" %} {% endblock %} +``` + +The list include must be **outside** `
`, never inside it. Placing it inside removes the flex gap between the form section and the list, causing the table to sit higher on the page. + +### List Partial Structure List partials live in `templates/partials/` (e.g., `roaster_list.html`, `brew_list.html`). Each follows the same structure: -``` +```html {% import "partials/table.html" as table %} -
+
{% if items.is_empty() && !navigator.has_search() %} - +
+

+ No {entities} recorded yet. Use the form above to add your first {entity}. +

+
{% else %}
... {% if items.is_empty() %} - +
No {entities} match your search.
{% endif %} {% call table::pagination_header(items, navigator, "#{entity}-list") %} {% if items.has_next() %} @@ -455,10 +480,19 @@ List partials live in `templates/partials/` (e.g., `roaster_list.html`, `brew_li
``` -Key points: -- The outer `
` with `id="{entity}-list"` is the Datastar fragment target for replacements -- Empty state only shows when there are no items **and** no active search query -- When a search is active but returns no results, the table section renders with the search bar and a "no matches" message +Required attributes and elements on every list partial: + +| Element | Requirement | +|---------|-------------| +| Outer `
` | `id="{entity}-list"`, `class="mt-6"`, `data-star-scope="{entity}"` | +| Empty state | Conditional on `items.is_empty() && !navigator.has_search()`, dashed amber border | +| Table wrapper | `
` (not `
`) | +| Search header | `{% call table::search_header(...) %}` | +| No-results msg | Inside the `
`, shown when search is active but returns nothing | +| Pagination | `{% call table::pagination_header(...) %}` | +| Scroll sentinel | `class="infinite-scroll-sentinel h-4 md:hidden"` | + +**Exception**: the bags partial uses a dual-section layout (open-bag cards + history table) and does not follow this pattern exactly. ### Shared Table Macros (`templates/partials/table.html`) diff --git a/templates/bags.html b/templates/bags.html index 4ab8ea6..397c95f 100644 --- a/templates/bags.html +++ b/templates/bags.html @@ -109,6 +109,6 @@
{% endif %} {% endif %} -
{% include "partials/bag_list.html" %}
-{% endblock %} + +{% include "partials/bag_list.html" %} {% endblock %} diff --git a/templates/brews.html b/templates/brews.html index 06b84e2..4b7b637 100644 --- a/templates/brews.html +++ b/templates/brews.html @@ -159,6 +159,6 @@ {% endif %} {% endif %} -
{% include "partials/brew_list.html" %}
-{% endblock %} + +{% include "partials/brew_list.html" %} {% endblock %} diff --git a/templates/gear.html b/templates/gear.html index 44f6ea5..8bbc06e 100644 --- a/templates/gear.html +++ b/templates/gear.html @@ -91,6 +91,6 @@
{% endif %} -
{% include "partials/gear_list.html" %}
-{% endblock %} + +{% include "partials/gear_list.html" %} {% endblock %} diff --git a/templates/partials/bag_list.html b/templates/partials/bag_list.html index 744ab50..45af68f 100644 --- a/templates/partials/bag_list.html +++ b/templates/partials/bag_list.html @@ -1,6 +1,6 @@ {% import "partials/table.html" as table %} -
+
{% if !open_bags.is_empty() %}

Open Bags

diff --git a/templates/partials/brew_list.html b/templates/partials/brew_list.html index ad460c2..30042bb 100644 --- a/templates/partials/brew_list.html +++ b/templates/partials/brew_list.html @@ -1,7 +1,16 @@ {% import "partials/table.html" as table %} -
-
+ {% if brews.items.is_empty() && !navigator.has_search() %} +
+

+ No brews logged yet. Use the form above to log your first brew. +

+
+ {% else %} +
{% call table::search_header(navigator, "#brew-list") %} @@ -118,7 +127,7 @@ {% if brews.items.is_empty() %}
- No brews logged yet. Start by logging your first cup! + No brews match your search.
{% endif %} @@ -127,5 +136,6 @@ {% if brews.has_next() %} {% endif %} -
+
+ {% endif %}
diff --git a/templates/partials/gear_list.html b/templates/partials/gear_list.html index e748184..8122773 100644 --- a/templates/partials/gear_list.html +++ b/templates/partials/gear_list.html @@ -1,15 +1,16 @@ {% import "partials/table.html" as table %} -
+
{% if gear.items.is_empty() && !navigator.has_search() %} -
-

No gear found.

- {% if is_authenticated %} -

Add your first piece of brewing equipment above.

- {% endif %} +
+

+ No gear found. Use the form above to add your first piece of brewing equipment. +

{% else %} -
{% call table::search_header(navigator, "#gear-list") %} @@ -71,6 +72,6 @@ {% if gear.has_next() %} {% endif %} -
+ {% endif %}