🚜 WIP working search

* Bootstrapped API viewset to search libraries
* Updated templates
* Added htmx for search
* Search results template to display results

Issue: #88
This commit is contained in:
Greg Newman
2023-02-10 11:04:33 -05:00
parent 78b15a1e23
commit 28d64a4ef8
8 changed files with 88 additions and 16 deletions

View File

@@ -21,6 +21,7 @@ from libraries.views import (
LibraryByCategory,
LibraryDetail,
)
from libraries.api import LibrarySearchView
from support.views import SupportView, ContactView
from versions.api import VersionViewSet
from versions.views import VersionList, VersionDetail
@@ -29,6 +30,7 @@ router = routers.SimpleRouter()
router.register(r"users", UserViewSet, basename="users")
router.register(r"versions", VersionViewSet, basename="versions")
router.register(r"libraries", LibrarySearchView, basename="libraries")
urlpatterns = [

50
libraries/api.py Normal file
View File

@@ -0,0 +1,50 @@
from django.db.models import Q
from rest_framework import permissions
from rest_framework import viewsets
from rest_framework import serializers
from rest_framework import renderers
from rest_framework.response import Response
from .models import Library
class LibrarySearchSerializer(serializers.ModelSerializer):
class Meta:
model = Library
fields = (
"name",
"description",
)
class LibrarySearchView(viewsets.ModelViewSet):
model = Library
serializer_class = LibrarySearchSerializer
permission_classes = [permissions.AllowAny]
queryset = Library.objects.all()
renderer_classes = (renderers.TemplateHTMLRenderer, )
def filter_queryset(self, queryset):
"""
This view should return a list of all the libraries that
match the search params limited to 5 results
"""
value = self.request.query_params.get("q")
f = (
Q(name__icontains=value)
| Q(description__icontains=value)
| Q(categories__name__icontains=value)
| Q(authors__first_name__icontains=value)
| Q(authors__last_name__icontains=value)
)
return Library.objects.filter(f)[:5]
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(queryset, many=True)
return Response(
{"libraries": serializer.data},
template_name="libraries/includes/search_results.html"
)

View File

@@ -15,6 +15,7 @@
"alpinejs": "^3.10.2",
"autoprefixer": "^10.4.12",
"cssnano": "^5.1.14",
"htmx": "^0.0.2",
"tailwindcss": "^3.2.1"
}
}

View File

@@ -16,6 +16,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.css" />
<script defer src="https://unpkg.com/alpinejs@3.10.5/dist/cdn.min.js"></script>
<script src="https://unpkg.com/htmx.org@1.8.5" integrity="sha384-7aHh9lqPYGYZ7sTHvzP1t3BAfLhYSTy9ArHdP3Xsr9/3TlGurYgcPBoFmXX2TX/w" crossorigin="anonymous"></script>
{% block extra_head %}{% endblock %}
<style>

View File

@@ -9,29 +9,28 @@
<div class="fixed inset-0 bg-black bg-opacity-70 transition-opacity"></div>
<div class="fixed inset-0 overflow-y-auto p-4 sm:p-6 md:p-20">
<div class="mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-20 overflow-hidden rounded-xl bg-charcoal shadow-2xl transition-all" x-on:click.away="showSearch = false">
<div class="mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-20 overflow-hidden rounded-xl bg-charcoal shadow-2xl transition-all"
x-on:click.away="showSearch = false"
>
<div class="relative">
<!-- Heroicon name: outline/magnifying-glass -->
<svg class="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
</svg>
<input type="text" id="librarySearch" class="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-white placeholder-gray-500 focus:ring-0 sm:text-sm" placeholder="Search...">
<input type="text"
id="q"
name="q"
class="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-white placeholder-gray-500 focus:ring-0 sm:text-sm"
placeholder="Search..."
hx-get="/api/v1/libraries/"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#search_results"
hx-indicator=".htmx-indicator"
>
</div>
<!-- Results, show/hide based on command palette state. -->
<ul class="max-h-96 overflow-y-auto p-2 text-sm text-gray-400">
<!-- Active: "bg-gray-800 text-white" -->
<li class="group flex cursor-default select-none items-center rounded-md px-3 py-2">
<!-- Active: "text-white", Not Active: "text-gray-500" -->
<!-- Heroicon name: outline/folder -->
<svg class="h-6 w-6 flex-none text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" />
</svg>
<span class="ml-3 flex-auto truncate">Dev Note: Results display here</span>
<!-- Not Active: "hidden" -->
<span class="ml-3 hidden flex-none text-gray-400">Jump to...</span>
</li>
</ul>
<ul class="max-h-96 overflow-y-auto p-2 text-sm text-gray-400" id="search_results"></ul>
<!-- Empty state, show/hide based on command palette state. -->
<div class="py-14 px-6 text-center sm:px-14">

View File

@@ -0,0 +1,14 @@
{% for library in libraries %}
<li class="group flex cursor-default select-none items-center rounded-md px-3 py-2">
<!-- Active: "text-white", Not Active: "text-gray-500" -->
<!-- Heroicon name: outline/folder -->
<svg class="h-6 w-6 flex-none text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" />
</svg>
<span class="ml-3 flex-auto truncate" id="search_results">
{{ library.name }}
</span>
<!-- Not Active: "hidden" -->
<span class="ml-3 hidden flex-none text-gray-400">Jump to...</span>
</li>
{% endfor %}

View File

@@ -20,7 +20,7 @@
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-4 h-4"><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
</button>
</span>
<input @click="showSearch = true; $nextTick(() => { setTimeout(() => { document.getElementById('librarySearch').focus(); }, 300);});"
<input @click="showSearch = true; $nextTick(() => { setTimeout(() => { document.getElementById('q').focus(); }, 300);});"
type="search" name="q" class="w-full md:w-1/3 text-sm text-white bg-charcoal focus:text-charcoal text-orange px-3 py-2 rounded-md" type="text" value="" placeholder="Search Library" />
</div>

View File

@@ -428,6 +428,11 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
htmx@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/htmx/-/htmx-0.0.2.tgz#790b3cd816b74f9f2648326e5f4f8a9b320f9fb0"
integrity sha512-FfUo3ynRYr6Ra4vqmS4Nq9g47607FSmvHYCOuU8bvbW8s4kPMhAmCbMBjuW2cEZI6DauaFNZKinfgV91cc9Feg==
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"