feat: implement article search with PostgreSQL full-text search
- Configure Wagtail database search backend with English search config - Add django.contrib.postgres to INSTALLED_APPS for full PG FTS support - Expand ArticlePage.search_fields: body_text (excl. code blocks), AutocompleteField(title), RelatedFields(tags), FilterFields - Add search view at /search/?q= with query guards (strip, max 200 chars, empty/whitespace handling) and pagination preserving query param - Replace nav Subscribe CTA with compact search box (desktop + mobile) - Add search box to article index page alongside category/tag filters - Create search results template reusing article_card component - Add update_index to deploy entrypoint for automated reindexing - Update existing tests for nav change, add comprehensive search tests Closes #41 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
59
templates/blog/search_results.html
Normal file
59
templates/blog/search_results.html
Normal file
@@ -0,0 +1,59 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}{% if query %}Search: {{ query }}{% else %}Search{% endif %} | No Hype AI{% endblock %}
|
||||
{% block head_meta %}
|
||||
<meta name="robots" content="noindex" />
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<!-- Page Header -->
|
||||
<div class="py-8 md:py-12 border-b border-zinc-200 dark:border-zinc-800 mb-12">
|
||||
<h1 class="font-display font-black text-4xl md:text-6xl mb-6">Search</h1>
|
||||
<form action="{% url 'search' %}" method="get" role="search" class="relative w-full md:w-96">
|
||||
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-zinc-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" /></svg>
|
||||
<input type="search" name="q" value="{{ query }}" placeholder="Search articles..." autofocus
|
||||
class="w-full bg-brand-surfaceLight dark:bg-brand-surfaceDark border border-zinc-300 dark:border-zinc-700 pl-11 pr-4 py-3 font-mono text-sm focus:outline-none focus:border-brand-cyan dark:focus:border-brand-cyan focus:ring-1 focus:ring-brand-cyan transition-shadow" />
|
||||
</form>
|
||||
{% if query %}
|
||||
<p class="mt-4 font-mono text-sm text-zinc-500">
|
||||
{% if results %}{{ results.paginator.count }} result{{ results.paginator.count|pluralize }} for "{{ query }}"{% else %}No results for "{{ query }}"{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if results %}
|
||||
<!-- Results -->
|
||||
<div class="space-y-8">
|
||||
{% for article in results %}
|
||||
{% include 'components/article_card.html' with article=article %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
{% if results.has_previous or results.has_next %}
|
||||
<nav aria-label="Pagination" class="mt-12 flex justify-center items-center gap-4 font-mono text-sm">
|
||||
{% if results.has_previous %}
|
||||
<a href="?q={{ query|urlencode }}&page={{ results.previous_page_number }}" class="px-6 py-3 border border-zinc-300 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors">← Previous</a>
|
||||
{% endif %}
|
||||
<span class="text-zinc-500">Page {{ results.number }} of {{ paginator.num_pages }}</span>
|
||||
{% if results.has_next %}
|
||||
<a href="?q={{ query|urlencode }}&page={{ results.next_page_number }}" class="px-6 py-3 border border-zinc-300 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors">Next →</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
{% elif query %}
|
||||
<!-- No Results -->
|
||||
<div class="py-16 text-center">
|
||||
<svg class="w-16 h-16 text-zinc-300 dark:text-zinc-700 mx-auto mb-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" /></svg>
|
||||
<p class="font-mono text-zinc-500 mb-2">No articles match your search.</p>
|
||||
<p class="font-mono text-sm text-zinc-400">Try different keywords or browse <a href="/articles/" class="text-brand-cyan hover:underline">all articles</a>.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- Empty State -->
|
||||
<div class="py-16 text-center">
|
||||
<svg class="w-16 h-16 text-zinc-300 dark:text-zinc-700 mx-auto mb-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" /></svg>
|
||||
<p class="font-mono text-zinc-500">Enter a search term to find articles.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user