From 540733699e006f4eb22f15af2371d5f1e7bbbf86 Mon Sep 17 00:00:00 2001 From: OpenClaw Sub-agent Date: Sun, 2 Aug 2026 11:35:44 +1000 Subject: [PATCH] fix(home): restore tbody binding, sticky mobile delete bar, 20-row recent-first list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix Uncaught ReferenceError: tbody is not defined — the client-side filter rewrite dropped the const tbody declaration still used by load-more - Mobile: Delete Selected becomes a sticky frosted bottom bar (safe-area aware); disabled (grayed) until a row is selected, then red - Mobile: library search input full-width 48px (primary interaction) - Default list: 20 rows (was 50) sorted by updated_at desc (recent first) instead of click_count; load-more keeps paging +20 --- links/templates/links/link_list.html | 23 +++++++++++++++++++++-- links/views.py | 14 +++++++------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/links/templates/links/link_list.html b/links/templates/links/link_list.html index 22b4a5a..c380302 100644 --- a/links/templates/links/link_list.html +++ b/links/templates/links/link_list.html @@ -208,6 +208,10 @@ .apple-btn-primary:hover { background: var(--apple-blue-hover); color: #fff; } .apple-btn-danger { background: transparent; color: var(--apple-red); border: 1px solid rgba(255, 59, 48, 0.4); } .apple-btn-danger:hover { background: rgba(255, 59, 48, 0.08); } + .apple-btn-danger:disabled { + opacity: 0.45; color: var(--apple-gray); border-color: rgba(0, 0, 0, 0.12); + cursor: default; pointer-events: none; background: transparent; + } /* ---------- Link library (table) ---------- */ .library-header { @@ -579,7 +583,19 @@ /* ---------- Small screens ---------- */ @media (max-width: 640px) { .library-header { padding: 1.35rem 1.1rem 1rem; } - .library-footer { padding: 1rem 1.1rem 1.25rem; } + .library-footer { + position: sticky; bottom: 0; z-index: 5; + background: rgba(245, 245, 247, 0.88); + -webkit-backdrop-filter: blur(20px) saturate(180%); + backdrop-filter: blur(20px) saturate(180%); + border-top: 1px solid rgba(0, 0, 0, 0.05); + border-radius: 0 0 var(--apple-radius-lg) var(--apple-radius-lg); + margin: 0 -1.1rem -1.25rem; + padding: 0.7rem 1.1rem calc(0.7rem + env(safe-area-inset-bottom, 0px)); + } + /* Search takes the full row on mobile (it is the primary interaction) */ + .library-filter { flex: 1 1 100%; } + .library-filter input { width: 100%; height: 48px; font-size: 1rem; } .apple-table thead th, .apple-table tbody td { padding-left: 0.8rem; padding-right: 0.8rem; } .hero-stats { gap: 1.25rem; } .posts-panel-header { padding: 1.15rem 1.1rem 0.6rem; } @@ -965,7 +981,7 @@ {% endif %} @@ -1406,6 +1422,7 @@ /* ---------- Load more links ---------- */ const loadMoreBtn = document.getElementById('load-more-links'); + const tbody = document.getElementById('links-table-body'); if (loadMoreBtn && tbody) { loadMoreBtn.addEventListener('click', function () { const offset = loadMoreBtn.dataset.offset; @@ -1444,6 +1461,8 @@ if (!selectionHint) return; const count = document.querySelectorAll('input[name="selected_links"]:checked').length; selectionHint.textContent = count > 0 ? count + ' {% trans "selected" %}' : ''; + const delBtn = document.getElementById('delete-selected-btn'); + if (delBtn) delBtn.disabled = count === 0; } if (selectAll) { diff --git a/links/views.py b/links/views.py index ab84216..17901a0 100644 --- a/links/views.py +++ b/links/views.py @@ -47,7 +47,7 @@ from .search_views import search_aliases, search logger = logging.getLogger(__name__) # Number of link rows rendered on the initial home page load / per "Load more" click. -LINKS_PAGE_SIZE = 50 +LINKS_PAGE_SIZE = 20 def _ordering_for(sort_by, order): @@ -74,14 +74,14 @@ class LinkListView(ListView): def get_queryset(self): queryset = super().get_queryset() - sort_by = self.request.GET.get('sort', 'click_count') - order = self.request.GET.get('order', 'asc') + sort_by = self.request.GET.get('sort', 'updated_at') + order = self.request.GET.get('order', 'desc') return queryset.order_by(_ordering_for(sort_by, order)) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['current_sort'] = self.request.GET.get('sort', 'click_count') - context['current_order'] = self.request.GET.get('order', 'asc') + context['current_sort'] = self.request.GET.get('sort', 'updated_at') + context['current_order'] = self.request.GET.get('order', 'desc') # Modern color palette colors = [ @@ -128,8 +128,8 @@ def load_more_links(request): """Return the next page of link rows (rendered as HTML) for the home page's "Load more" button, so the initial page load doesn't have to render the entire link table at once.""" - sort_by = request.GET.get('sort', 'click_count') - order = request.GET.get('order', 'asc') + sort_by = request.GET.get('sort', 'updated_at') + order = request.GET.get('order', 'desc') try: offset = max(int(request.GET.get('offset', 0)), 0) except (TypeError, ValueError):