diff --git a/inventory/static/inventory/css/main.css b/inventory/static/inventory/css/main.css
index bbaf413..c8e4bb8 100644
--- a/inventory/static/inventory/css/main.css
+++ b/inventory/static/inventory/css/main.css
@@ -207,7 +207,6 @@ ul.tag-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
- max-width: 500px;
text-indent: 0;
padding-left: 0;
}
@@ -291,4 +290,23 @@ table.list tbody td {
.right {
text-align: right;
-}
\ No newline at end of file
+}
+
+#search-container {
+ display: none;
+ position: absolute;
+ background-color: #292981;
+ padding: 10px;
+ border: 1px solid white;
+ box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.8);
+}
+
+.search-result em {
+ color: #c00000;
+}
+
+.search-result .small {
+ font-size: 9pt;
+ font-weight: normal;
+ color: #808080;
+}
diff --git a/inventory/static/inventory/img/search.svg b/inventory/static/inventory/img/search.svg
new file mode 100644
index 0000000..97cb287
--- /dev/null
+++ b/inventory/static/inventory/img/search.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/inventory/templates/base.html b/inventory/templates/base.html
index 0d507b9..2f65625 100644
--- a/inventory/templates/base.html
+++ b/inventory/templates/base.html
@@ -23,6 +23,29 @@
{% endblock %}
diff --git a/inventory/templates/inventory/search.html b/inventory/templates/inventory/search.html
new file mode 100644
index 0000000..53a0af3
--- /dev/null
+++ b/inventory/templates/inventory/search.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+{% load static %}
+
+{% block title %}Search{% endblock %}
+
+{% block header_bar %}
+ Search
+{% endblock %}
+
+{% block content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/inventory/templates/inventory/search_result.html b/inventory/templates/inventory/search_result.html
new file mode 100644
index 0000000..ee74ecd
--- /dev/null
+++ b/inventory/templates/inventory/search_result.html
@@ -0,0 +1,43 @@
+{% extends "base.html" %}
+{% load static %}
+{% load hilight %}
+
+{% block title %}Search{% endblock %}
+
+{% block header_bar %}
+ Search
+{% endblock %}
+
+{% block content %}
+
+
+ Search result for '{{ q }}'
+
+ {% for result in results %}
+
+
+ {{ result.text | safe }}
+
+
+ {% empty %}
+ Noting found
+ {% endfor %}
+
+ {% if pages > 1 %}
+
+ {% endif %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/inventory/templates/inventory/search_result_item.html b/inventory/templates/inventory/search_result_item.html
new file mode 100644
index 0000000..ea8c9e3
--- /dev/null
+++ b/inventory/templates/inventory/search_result_item.html
@@ -0,0 +1,17 @@
+{% load static %}
+{% load hilight %}
+
+{% if item.documentation.all %}
+
+{% endif %}
+{{ item | hilight:tokens }}
+{{item.form_factor.name}}
+
+Contained in: {{ item.container.display_name }}
+
+ {% for tag in item.all_tags %}
+ - {{ tag.name }}
+ {% empty %}
+ No tags
+ {% endfor %}
+
diff --git a/inventory/urls.py b/inventory/urls.py
index 6576519..fcc3f68 100644
--- a/inventory/urls.py
+++ b/inventory/urls.py
@@ -33,7 +33,8 @@ from .views import (
DistributorView,
ManufacturerView,
IndexView,
- TagView
+ TagView,
+ SearchView
)
urlpatterns = [
@@ -52,5 +53,6 @@ urlpatterns = [
path('distributor/', DistributorView.as_view(), name='distributor-detail'),
path('tags', TagListView.as_view(), name='tag-list'),
path('tag/', TagView.as_view(), name='tag-detail'),
+ path('search', SearchView.as_view(), name='search'),
path('', IndexView.as_view(), name='index')
]
diff --git a/inventory/views/__init__.py b/inventory/views/__init__.py
index 36ff231..a5ea7b5 100644
--- a/inventory/views/__init__.py
+++ b/inventory/views/__init__.py
@@ -6,6 +6,7 @@ from .manufacturer import ManufacturerView, ManufacturerListView
from .workshop import WorkshopView, WorkshopListView
from .index import IndexView
from .tag import TagListView, TagView
+from .search import SearchView
__all__ = [
AreaView, AreaListView,
@@ -15,5 +16,6 @@ __all__ = [
ManufacturerView, ManufacturerListView,
WorkshopView, WorkshopListView,
IndexView,
- TagView, TagListView
+ TagView, TagListView,
+ SearchView
]
\ No newline at end of file
diff --git a/inventory/views/search.py b/inventory/views/search.py
new file mode 100644
index 0000000..cc60b3e
--- /dev/null
+++ b/inventory/views/search.py
@@ -0,0 +1,84 @@
+from django.contrib.auth.decorators import login_required
+from django.utils.decorators import method_decorator
+from django.views.generic import View
+from django.shortcuts import render
+from django.urls import reverse
+from django.template.loader import get_template
+from django.db.models import Q
+from django.core.paginator import Paginator
+from django.conf import settings
+
+from re import finditer
+
+from inventory.models import Item
+
+@method_decorator(login_required, name='dispatch')
+class SearchView(View):
+
+ def get(self, request):
+ page = int(request.GET.get('page', "1"))
+ query = request.GET.get('q', None)
+ if not query:
+ return render(request, "inventory/search.html")
+
+ results: list[dict[str, str]] = []
+
+ tokens = query.split(" ")
+ tokens = map(str.lower, map(str.strip, tokens))
+
+ q: Q = None
+ item_template = get_template("inventory/search_result_item.html")
+ t: list[str] = []
+ for token in tokens:
+ combiner = 'or'
+ if token.startswith("+"):
+ token = token[1:]
+ combiner = 'and'
+ elif token.startswith("-"):
+ token = token[1:]
+ combiner = 'and not'
+
+ t.append(token)
+
+ q1 = Q(name__icontains=f'{token}')
+ q2 = Q(description__icontains=f'{token}')
+ q3 = Q(tags__name__icontains=f'{token}')
+ q4 = Q(tags__description__icontains=f'{token}')
+ q5 = Q(form_factor__tags__name__icontains=f'{token}')
+ q6 = Q(form_factor__tags__description__icontains=f'{token}')
+
+ qx = q1 | q2 | q3 | q4 | q5 | q6
+
+ if q == None:
+ q = qx
+ elif combiner == 'or':
+ q = q | qx
+ elif combiner == 'and':
+ q = q & qx
+ elif combiner == 'and not':
+ q = q & ~qx
+ items = Item.objects.filter(q).select_related("container", "form_factor").prefetch_related("tags", "documentation").distinct()
+
+ # FIXME: Rank search results
+
+ paginator = Paginator(items, getattr(settings, "PAGE_SIZE", 10))
+
+ for item in paginator.get_page(page):
+ text = item_template.render({
+ "item": item,
+ "tokens": t
+ })
+ result = {
+ "title": item.name,
+ "text": text,
+ "url": reverse('item-detail', kwargs={"pk": item.pk})
+ }
+ results.append(result)
+
+ return render(request, "inventory/search_result.html", {
+ "q": query,
+ "results": results,
+ "tokens": t,
+ "page": page,
+ "pages": paginator.num_pages
+ })
\ No newline at end of file