diff --git a/static/src/js/search.js b/static/src/js/search.js index 47965a5..63043a7 100644 --- a/static/src/js/search.js +++ b/static/src/js/search.js @@ -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); }); diff --git a/static/src/scss/_search.scss b/static/src/scss/_search.scss index 429ed7f..5c9e5df 100644 --- a/static/src/scss/_search.scss +++ b/static/src/scss/_search.scss @@ -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; + } } diff --git a/website/search/models.py b/website/search/models.py index 80870ca..78e50af 100644 --- a/website/search/models.py +++ b/website/search/models.py @@ -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"] diff --git a/website/search/templates/search/enter-search-term.html b/website/search/templates/search/enter-search-term.html new file mode 100644 index 0000000..ecb4e9f --- /dev/null +++ b/website/search/templates/search/enter-search-term.html @@ -0,0 +1 @@ +

Enter a search term (of at least {{ MIN_SEARCH_TERM }} characters) to search

diff --git a/website/search/templates/search/search_page.html b/website/search/templates/search/search_page.html index 6c774b9..965a645 100644 --- a/website/search/templates/search/search_page.html +++ b/website/search/templates/search/search_page.html @@ -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 @@

- -

Showing 0 results

-
+
+

+ +
+ {% if not search_query %} + {% include "search/enter-search-term.html" %} + {% endif %} +
+
{% endblock %} diff --git a/website/search/templates/search/search_results.html b/website/search/templates/search/search_results.html index ecc38d8..adf28e3 100644 --- a/website/search/templates/search/search_results.html +++ b/website/search/templates/search/search_results.html @@ -4,6 +4,10 @@ {% include "common/listing-item.html" %} {% endfor %} +{% if not results and page_num == 1 %} +

No results found

+{% endif %} + {% if results.has_next %} {% endif %}