Add all the relevant search messages

This commit is contained in:
Jake Howard 2022-08-02 21:11:35 +01:00
parent bcc9a2c2f2
commit 996f7b9c2a
Signed by: jake
GPG Key ID: 57AFB45680EDD477
6 changed files with 40 additions and 15 deletions

View File

@ -1,10 +1,15 @@
window.addEventListener("load", () => {
window.addEventListener("DOMContentLoaded", () => {
const searchResults = document.getElementById("search-results");
const resultsCount = document.getElementById("result-count");
const resultsCountDisplay = document.getElementById("result-count");
function handleSearchResults(event) {
resultsCount.textContent =
event.target.querySelectorAll(".listing-item").length;
const resultsCount = event.target.querySelectorAll(".listing-item").length;
if (resultsCount) {
resultsCountDisplay.textContent =
`Found ${resultsCount} result` + (resultsCount > 1 ? "s" : "");
} else {
resultsCountDisplay.textContent = "";
}
}
searchResults.addEventListener("htmx:after-swap", handleSearchResults);
searchResults.addEventListener("htmx:afterSwap", handleSearchResults);
});

View File

@ -6,6 +6,7 @@
transform: rotate(360deg);
}
}
body.page-searchpage {
.search-controls {
margin: 2rem auto;
@ -15,4 +16,9 @@ body.page-searchpage {
#search-indicator {
animation: search-loading 1.5s linear infinite;
}
#search-results > p {
font-size: 1.5rem;
text-align: center;
}
}

View File

@ -1,6 +1,6 @@
from django.core.paginator import EmptyPage, Paginator
from django.http.request import HttpRequest
from django.http.response import HttpResponse, HttpResponseBadRequest
from django.http.response import HttpResponse
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
@ -24,9 +24,10 @@ class SearchPage(BaseContentMixin, RoutablePageMixin, BasePage): # type: ignore
content_panels = BasePage.content_panels + BaseContentMixin.content_panels
search_fields = BasePage.search_fields + BaseContentMixin.search_fields
PAGE_SIZE = 15
MIN_SEARCH_TERM = 3
class SearchParamsSerializer(serializers.Serializer):
q = serializers.CharField()
q = serializers.CharField(min_length=3)
page = serializers.IntegerField(min_value=1, default=1)
@cached_property
@ -44,18 +45,20 @@ class SearchPage(BaseContentMixin, RoutablePageMixin, BasePage): # type: ignore
context = super().get_context(request)
context["search_query"] = request.GET.get("q", "")
context["search_url"] = self.reverse_subpage("results")
context["MIN_SEARCH_TERM"] = self.MIN_SEARCH_TERM
return context
@route(r"^results/$")
@method_decorator(require_GET)
def results(self, request: HttpRequest) -> HttpResponse:
if not request.GET.get("q", None):
return HttpResponse()
serializer = self.SearchParamsSerializer(data=request.GET)
if not serializer.is_valid():
return HttpResponseBadRequest(serializer.errors)
return render(
request,
"search/enter-search-term.html",
{"MIN_SEARCH_TERM": self.MIN_SEARCH_TERM},
)
search_query = serializer.validated_data["q"]
page_num = serializer.validated_data["page"]

View File

@ -0,0 +1 @@
<p>Enter a search term (of at least {{ MIN_SEARCH_TERM }} characters) to search</p>

View File

@ -21,7 +21,7 @@
name="q"
placeholder="Search"
hx-get="{{ search_url }}"
hx-trigger="keyup changed delay:300ms, search{% if search_query %}, revealed{% endif %}"
hx-trigger="keyup changed delay:300ms, search{% if search_query %}, load{% endif %}"
hx-target="#search-results"
autocomplete="off"
value="{{ search_query }}"
@ -35,11 +35,17 @@
</span>
</p>
</div>
<p>Showing <span id="result-count">0</span> results</p>
</section>
<section class="container" id="search-results"></section>
<section class="container" id="search-results-container">
<p id="result-count"></p>
<div id="search-results">
{% if not search_query %}
{% include "search/enter-search-term.html" %}
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -4,6 +4,10 @@
{% include "common/listing-item.html" %}
{% endfor %}
{% if not results and page_num == 1 %}
<p>No results found</p>
{% endif %}
{% if results.has_next %}
<span hx-get="{{ search_url }}{% querystring page=results.next_page_number %}" hx-trigger="revealed" hx-swap="outerHTML"></span>
{% endif %}