Make sidebar category/manufacturer sorting selectable

Kategorien and Hersteller/Quellen lists were hardcoded to sort by
count descending. Add a small dropdown (Name A-Z / Anzahl) above the
lists on both the Links and Prompts pages, backed by a new facet_sort
query param plumbed through facets()/prompt_facets(). Defaults to
alphabetical, which was the actual ask; count sort stays available
for anyone who preferred the old behavior. All sidebar facet links
carry the current facet_sort so it doesn't reset when filtering.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Erik Thiele
2026-07-31 12:27:21 +02:00
parent af65171d87
commit d13b8ce5bc
3 changed files with 49 additions and 13 deletions

View File

@@ -110,18 +110,25 @@ def normalize_url(url: str) -> str:
return url
def facets(db: Session, user: User) -> dict:
# Sortierung der Kategorie-/Hersteller-Listen in der Seitenleiste.
FACET_SORT_OPTIONS = {"name": "Name AZ", "count": "Anzahl"}
DEFAULT_FACET_SORT = "name"
def facets(db: Session, user: User, facet_sort: str = DEFAULT_FACET_SORT) -> dict:
cat_order = Link.category if facet_sort == "name" else func.count().desc()
cat_rows = db.execute(
select(Link.category, func.count())
.where(Link.user_id == user.id, Link.category != "")
.group_by(Link.category)
.order_by(func.count().desc())
.order_by(cat_order)
).all()
man_order = Link.manufacturer if facet_sort == "name" else func.count().desc()
man_rows = db.execute(
select(Link.manufacturer, func.count())
.where(Link.user_id == user.id, Link.manufacturer != "")
.group_by(Link.manufacturer)
.order_by(func.count().desc())
.order_by(man_order)
).all()
review_count = db.scalar(
select(func.count())
@@ -200,12 +207,13 @@ def query_links(
return sort_links(matches, sort)
def prompt_facets(db: Session, user: User) -> dict:
def prompt_facets(db: Session, user: User, facet_sort: str = DEFAULT_FACET_SORT) -> dict:
cat_order = Prompt.category if facet_sort == "name" else func.count().desc()
cat_rows = db.execute(
select(Prompt.category, func.count())
.where(Prompt.user_id == user.id, Prompt.category != "")
.group_by(Prompt.category)
.order_by(func.count().desc())
.order_by(cat_order)
).all()
return {"categories": [(name, count) for name, count in cat_rows]}
@@ -343,6 +351,7 @@ def index(
semantic: str = "",
sort: str = DEFAULT_SORT,
review: str = "",
facet_sort: str = DEFAULT_FACET_SORT,
db: Session = Depends(get_db),
):
user = current_user(request, db)
@@ -351,6 +360,8 @@ def index(
if sort not in SORT_OPTIONS:
sort = DEFAULT_SORT
if facet_sort not in FACET_SORT_OPTIONS:
facet_sort = DEFAULT_FACET_SORT
is_semantic = semantic in ("1", "on", "true")
review_only = review in ("1", "on", "true")
links = query_links(
@@ -362,7 +373,7 @@ def index(
"request": request,
"user": user,
"links": links,
"facets": facets(db, user),
"facets": facets(db, user, facet_sort),
"q": q,
"active_category": category,
"active_manufacturer": manufacturer,
@@ -371,6 +382,8 @@ def index(
"sort": sort,
"sort_options": SORT_OPTIONS,
"review_only": review_only,
"facet_sort": facet_sort,
"facet_sort_options": FACET_SORT_OPTIONS,
},
)
@@ -632,6 +645,7 @@ def prompts_index(
q: str = "",
category: str = "",
sort: str = DEFAULT_SORT,
facet_sort: str = DEFAULT_FACET_SORT,
db: Session = Depends(get_db),
):
user = current_user(request, db)
@@ -640,6 +654,8 @@ def prompts_index(
if sort not in SORT_OPTIONS:
sort = DEFAULT_SORT
if facet_sort not in FACET_SORT_OPTIONS:
facet_sort = DEFAULT_FACET_SORT
prompts = query_prompts(db, user, q, category, sort)
return templates.TemplateResponse(
"prompts.html",
@@ -647,12 +663,14 @@ def prompts_index(
"request": request,
"user": user,
"prompts": prompts,
"facets": prompt_facets(db, user),
"facets": prompt_facets(db, user, facet_sort),
"q": q,
"active_category": category,
"ai_enabled": config.AI_ENABLED,
"sort": sort,
"sort_options": SORT_OPTIONS,
"facet_sort": facet_sort,
"facet_sort_options": FACET_SORT_OPTIONS,
},
)

View File

@@ -7,14 +7,23 @@
<div class="layout">
<aside class="sidebar">
<div class="panel">
<a href="/?sort={{ sort }}" class="facet {% if not active_category and not active_manufacturer and not review_only %}active{% endif %}">
<a href="/?sort={{ sort }}&amp;facet_sort={{ facet_sort }}" class="facet {% if not active_category and not active_manufacturer and not review_only %}active{% endif %}">
<span>{{ icons.grid(size=14) }} Alle Links</span>
</a>
<div style="display:flex; justify-content:flex-end; margin-top:12px;">
<select onchange="const u=new URL(location); u.searchParams.set('facet_sort', this.value); location.href=u;"
title="Sortierung der Kategorie-/Hersteller-Listen" style="font-size:.75rem; padding:3px 6px;">
{% for value, label in facet_sort_options.items() %}
<option value="{{ value }}" {% if facet_sort == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="facet-group">
<h3>Kategorien</h3>
{% for name, count in facets.categories %}
<a href="/?category={{ name | urlencode }}&amp;sort={{ sort }}"
<a href="/?category={{ name | urlencode }}&amp;sort={{ sort }}&amp;facet_sort={{ facet_sort }}"
class="facet {% if active_category == name and not review_only %}active{% endif %}">
<span>{{ name }}</span><span class="count">{{ count }}</span>
</a>
@@ -26,7 +35,7 @@
<div class="facet-group">
<h3>Hersteller / Quellen</h3>
{% for name, count in facets.manufacturers %}
<a href="/?manufacturer={{ name | urlencode }}&amp;sort={{ sort }}"
<a href="/?manufacturer={{ name | urlencode }}&amp;sort={{ sort }}&amp;facet_sort={{ facet_sort }}"
class="facet {% if active_manufacturer == name and not review_only %}active{% endif %}">
<span>{{ name }}</span><span class="count">{{ count }}</span>
</a>
@@ -37,7 +46,7 @@
<div class="facet-group">
<h3>Merkliste</h3>
<a href="/?review=1&amp;sort={{ sort }}" class="facet {% if review_only %}active{% endif %}">
<a href="/?review=1&amp;sort={{ sort }}&amp;facet_sort={{ facet_sort }}" class="facet {% if review_only %}active{% endif %}">
<span>{{ icons.bookmark(size=14) }} Zur Durchsicht</span><span class="count">{{ facets.review_count }}</span>
</a>
</div>

View File

@@ -7,14 +7,23 @@
<div class="layout">
<aside class="sidebar">
<div class="panel">
<a href="/prompts?sort={{ sort }}" class="facet {% if not active_category %}active{% endif %}">
<a href="/prompts?sort={{ sort }}&amp;facet_sort={{ facet_sort }}" class="facet {% if not active_category %}active{% endif %}">
<span>{{ icons.grid(size=14) }} Alle Prompts</span>
</a>
<div style="display:flex; justify-content:flex-end; margin-top:12px;">
<select onchange="const u=new URL(location); u.searchParams.set('facet_sort', this.value); location.href=u;"
title="Sortierung der Kategorie-Liste" style="font-size:.75rem; padding:3px 6px;">
{% for value, label in facet_sort_options.items() %}
<option value="{{ value }}" {% if facet_sort == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="facet-group">
<h3>Kategorien</h3>
{% for name, count in facets.categories %}
<a href="/prompts?category={{ name | urlencode }}&amp;sort={{ sort }}"
<a href="/prompts?category={{ name | urlencode }}&amp;sort={{ sort }}&amp;facet_sort={{ facet_sort }}"
class="facet {% if active_category == name %}active{% endif %}">
<span>{{ name }}</span><span class="count">{{ count }}</span>
</a>