diff --git a/assets/views/shop/_results.html b/assets/views/shop/_results.html
index 178dbb7..ae19509 100644
--- a/assets/views/shop/_results.html
+++ b/assets/views/shop/_results.html
@@ -2,6 +2,10 @@
server-side on first load. Holds the result summary, the product grid and
pagination. #}
{% set L = lang | default(value='sk') %}
+{# On htmx responses the toolbar's Sort dropdown isn't in this swapped region;
+ re-render it out-of-band so a search-triggered "newest → relevance" switch is
+ reflected in the visible selection. #}
+{% if is_fragment | default(value=false) %}{% set oob = true %}{% include "shop/_sort_select.html" %}{% endif %}
diff --git a/assets/views/shop/_sort_select.html b/assets/views/shop/_sort_select.html
new file mode 100644
index 0000000..ef0ce0a
--- /dev/null
+++ b/assets/views/shop/_sort_select.html
@@ -0,0 +1,12 @@
+{# Sort dropdown, shared by the toolbar (in the search form) and the results
+ fragment. A search promotes the default "newest" to "relevance" server-side,
+ but the toolbar select lives outside the swapped #shop-results region — so on
+ htmx responses _results.html re-renders this with `oob = true` (hx-swap-oob)
+ to keep the visible selection in sync with the actual ordering. #}
+{% set L = lang | default(value='sk') %}
+
diff --git a/src/controllers/shop.rs b/src/controllers/shop.rs
index 89429d7..522051f 100644
--- a/src/controllers/shop.rs
+++ b/src/controllers/shop.rs
@@ -194,12 +194,17 @@ async fn run_search(
items.retain(|i| view::category_filter_keep(&filter, i.product.category_id));
// 6. Sort. Newest-first is the default; relevance (the ranked search order)
- // is available explicitly via the sort control.
- let sort = params
+ // is available explicitly via the sort control. When a search runs, the
+ // default "newest" becomes "relevance" (a query implies relevance matters
+ // most); any explicitly chosen non-newest sort is left untouched.
+ let mut sort = params
.sort
.clone()
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "newest".to_string());
+ if !q_trim.is_empty() && sort == "newest" {
+ sort = "relevance".to_string();
+ }
match sort.as_str() {
"price_asc" => items.sort_by(|a, b| a.priced.price_cents.cmp(&b.priced.price_cents)),
"price_desc" => items.sort_by(|a, b| b.priced.price_cents.cmp(&a.priced.price_cents)),
@@ -391,6 +396,9 @@ async fn search(
if fragment {
if let Some(map) = context.as_object_mut() {
map.insert("lang".into(), json!(lang));
+ // Lets _results.html out-of-band swap the toolbar's Sort dropdown
+ // (which lives outside the swapped region) to match the ordering.
+ map.insert("is_fragment".into(), json!(true));
}
return format::view(&v, "shop/_results.html", context);
}