Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proof-of-concept: Introduce HTMX-based replacement for QuickSelect component in Maintenance tool #2883

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions python/nav/django/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
'nav.web.auth.middleware.AuthorizationMiddleware',
'nav.django.legacy.LegacyCleanupMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django_htmx.middleware.HtmxMiddleware',
)

SESSION_SERIALIZER = 'nav.web.session_serializer.PickleSerializer'
Expand Down Expand Up @@ -223,6 +224,7 @@
'django.contrib.sessions',
'django.contrib.humanize',
'django_filters',
'django_htmx',
'crispy_forms',
'crispy_forms_foundation',
'rest_framework',
Expand Down
1 change: 1 addition & 0 deletions python/nav/web/maintenance/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
re_path(r'^active/$', views.active, name='maintenance-active'),
re_path(r'^planned/$', views.planned, name='maintenance-planned'),
re_path(r'^historic/$', views.historic, name='maintenance-historic'),
re_path(r'^search/$', views.component_search, name='maintenance-crazy-shit'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why stick with re_path? path is easier to read.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could ask you the same. I used the same pattern as the surrounding lines. They were all last modified by you as part of some Django upgrade, I believe :)

re_path(r'^new/$', views.edit, name='maintenance-new'),
re_path(
r'^new/(?P<start_time>\d{4}-\d{2}-\d{2})/$',
Expand Down
30 changes: 26 additions & 4 deletions python/nav/web/maintenance/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@
#

import logging

import time
from datetime import datetime, date
from datetime import datetime
from typing import List, Tuple

from django.db import transaction, connection
from django.db.models import Count, Q
from django.db.models import Count, Model, Q
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.safestring import mark_safe

from nav.django.utils import get_account
from nav.models.manage import Netbox
from nav.models.manage import Location, Netbox, NetboxGroup, Room
from nav.models.msgmaint import MaintenanceTask, MaintenanceComponent
from nav.models.service import Service
from nav.web.message import new_message, Messages
from nav.web.quickselect import QuickSelect

Expand Down Expand Up @@ -363,6 +364,27 @@ def edit(request, task_id=None, start_time=None, **_):
)


def component_search(request):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefix with @require_http_methods(["POST"])

"""HTMX endpoint for component searches from maintenance task form"""
search = request.POST.get("search")
results = {}
searches: List[Tuple[Model, Q]] = [
(Location, Q(id__icontains=search)),
(Room, Q(id__icontains=search)),
(Netbox, Q(sysname__icontains=search)),
(NetboxGroup, Q(id__icontains=search)),
(Service, Q(handler__icontains=search) | Q(netbox__sysname__icontains=search)),
]

for component_type, query in searches:
component_result = component_type.objects.filter(query)
if component_result:
component_title = component_type._meta.verbose_name.title()
results[component_title] = component_result

return render(request, 'maintenance/search-results.html', {'results': results})


def add_box_to_maintenance(request):
"""
This view puts a Netbox on immediate, indefinite maintenance and
Expand Down
1 change: 1 addition & 0 deletions python/nav/web/static/js/libs/htmx.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions python/nav/web/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<link rel="stylesheet" href="{{ STATIC_URL }}css/nav/quickselect.css" />
{% comment %} Javascript global variables {% endcomment %}
<script src="{{ STATIC_URL }}js/libs/jquery.js"></script>
<script src="{{ STATIC_URL }}js/libs/htmx.min.js"></script>
{% include 'navurls.html' %}
<script src="{{ STATIC_URL }}js/require_config.js"></script>
{% if debug %}
Expand Down
15 changes: 15 additions & 0 deletions python/nav/web/templates/maintenance/new_task.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ <h4>{{ heading }}</h4>
<legend>Select components</legend>
{{ quickselect }}
</fieldset>

<h3>Search components
</h3>
<input class="form-control" type="search"
name="search" placeholder="Begin Typing To Search for stuff..."
hx-post="{% url 'maintenance-crazy-shit' %}"
hx-trigger="input changed delay:500ms, search"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
<span class="htmx-indicator">
<img src="/static/images/select2/select2-spinner.gif"/> Searching...
</span>
<div id="search-results"></div>

</div>

<div class="large-4 columns">
Expand Down Expand Up @@ -88,6 +102,7 @@ <h4>{{ heading }}</h4>
value="Remove selected" disabled="disabled" class="button small secondary"/>
{% endif %}
</fieldset>

</div> {# column #}
</div> {# row #}

Expand Down
14 changes: 14 additions & 0 deletions python/nav/web/templates/maintenance/search-results.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% if results %}
{% for component_type, values in results.items %}
{% if values %}
<h4>{{ component_type }}</h4>
<ul>
{% for value in values %}
<li>{{ value }}</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
{% else %}
<strong>No hits</strong>
{% endif %}
1 change: 1 addition & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ django-filter>=2
djangorestframework>=3.12,<3.13
django-crispy-forms>=1.8,<1.9
crispy-forms-foundation>=0.7,<0.8
django-htmx

# REST framework
iso8601
Expand Down
Loading