Files
linkvault/app/templates/index.html
Erik Thiele 547374aa6d Fix facet_sort dropdown silently failing to navigate
The onchange handler used `new URL(location)`. Inline event handler
attributes execute inside an implicit `with(document){...}` scope, and
`document.URL` is a built-in string property — so the bare `URL`
identifier resolved to that string instead of the global URL
constructor, throwing "URL is not a constructor" on every change.
The error wasn't visible anywhere in the UI, so the dropdown appeared
to update (its own selected option changed) while the page silently
never navigated, and a reload reverted it to the default.

Fix: reference `window.URL` explicitly to bypass the shadowing.
Verified the dropdown now actually re-sorts the sidebar and the
change survives a reload on both the Links and Prompts pages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 13:02:08 +02:00

120 lines
5.4 KiB
HTML

{% extends "base.html" %}
{% import "_icons.html" as icons %}
{% block title %}Meine Links · LinkVault{% endblock %}
{% block body %}
{% include "_topbar.html" %}
<div class="layout">
<aside class="sidebar">
<div class="panel">
<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 window.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 }}&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>
{% else %}
<div class="muted" style="font-size:.85rem; padding:4px 8px;">noch keine</div>
{% endfor %}
</div>
<div class="facet-group">
<h3>Hersteller / Quellen</h3>
{% for name, count in facets.manufacturers %}
<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>
{% else %}
<div class="muted" style="font-size:.85rem; padding:4px 8px;">noch keine</div>
{% endfor %}
</div>
<div class="facet-group">
<h3>Merkliste</h3>
<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>
</div>
</aside>
<main class="main">
<div class="panel">
<form hx-post="/links" hx-target="#links" hx-swap="afterbegin"
hx-disabled-elt="button"
hx-on::after-request="if(event.detail.successful){this.reset(); var e=document.getElementById('empty-state'); if(e) e.remove();}">
<div style="display:flex; gap:8px; flex-wrap:wrap;">
<input type="url" name="url" placeholder="Link einfügen, z.B. https://hersteller.de/produkt" required
style="flex:1 1 220px; width:auto;">
<button type="submit" style="white-space:nowrap;">
<span class="htmx-indicator icon-spin">{{ icons.spinner(size=14) }}</span>
{{ icons.plus(size=14) }} Hinzufügen
</button>
</div>
<div class="muted" style="font-size:.8rem; margin-top:6px;">
Der Link wird automatisch geladen, zusammengefasst und einsortiert.
</div>
</form>
</div>
<div class="panel">
<form id="searchform" onsubmit="return false;" style="margin:0;">
<input type="hidden" name="category" value="{{ active_category }}">
<input type="hidden" name="manufacturer" value="{{ active_manufacturer }}">
<input type="hidden" name="review" value="{{ '1' if review_only else '' }}">
<div class="input-icon">
{{ icons.search(size=15) }}
<input type="text" name="q" value="{{ q }}"
placeholder="Suche in Titel, Zusammenfassung, Tags..."
hx-get="/search" hx-target="#links" hx-swap="innerHTML"
hx-trigger="keyup changed delay:400ms, search"
hx-include="#searchform">
</div>
<div style="display:flex; justify-content:space-between; align-items:center; gap:12px; margin-top:10px; flex-wrap:wrap;">
{% if ai_enabled %}
<label class="muted" style="display:inline-flex; align-items:center; gap:6px; font-size:.85rem;">
<input type="checkbox" name="semantic" value="1" {% if semantic %}checked{% endif %}
hx-get="/search" hx-target="#links" hx-swap="innerHTML"
hx-trigger="change" hx-include="#searchform" style="width:auto;">
KI-Suche (findet auch sinnverwandte Treffer)
</label>
{% else %}
<span></span>
{% endif %}
<label class="muted" style="display:inline-flex; align-items:center; gap:6px; font-size:.85rem;">
Sortierung:
<select name="sort"
hx-get="/search" hx-target="#links" hx-swap="innerHTML"
hx-trigger="change" hx-include="#searchform">
{% for value, label in sort_options.items() %}
<option value="{{ value }}" {% if sort == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</label>
</div>
</form>
</div>
<div id="links">
{% include "_links_list.html" %}
</div>
</main>
</div>
{% endblock %}