mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
Autocompletion in event selection
This commit is contained in:
@@ -59,8 +59,6 @@ class PermissionMiddleware(MiddlewareMixin):
|
||||
return redirect_to_login(
|
||||
path, resolved_login_url, REDIRECT_FIELD_NAME)
|
||||
|
||||
events = request.user.get_events_with_any_permission()
|
||||
request.user.events_cache = events.order_by("organizer", "date_from").prefetch_related("organizer")
|
||||
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
||||
request.event = Event.objects.filter(
|
||||
slug=url.kwargs['event'],
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/quota.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/question.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/mail.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/typeahead.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixbase/js/asynctask.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixbase/js/asyncdownload.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "colorpicker/bootstrap-colorpicker.js" %}"></script>
|
||||
@@ -69,15 +70,15 @@
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-calendar"></i>
|
||||
<span class="event-name">{{ request.event }}</span>
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="{% url "control:events" %}">{% trans "Events overview" %}</a></li>
|
||||
{% regroup request.user.events_cache by organizer as event_list %}
|
||||
{% for g in event_list %}
|
||||
<li class="dropdown-header">{{ g.grouper }}</li>
|
||||
{% for e in g.list %}
|
||||
<li><a href="{% url "control:event.index" organizer=g.grouper.slug event=e.slug %}">{{ e.name }}</a></li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
<ul class="dropdown-menu event-dropdown" role="menu" data-event-typeahead
|
||||
data-source="{% url "control:events.typeahead" %}">
|
||||
<li>
|
||||
<div class="form-box">
|
||||
<input type="text" class="form-control"
|
||||
placeholder="{% trans "Search for events" %}"
|
||||
data-typeahead-query>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% if request.event %}
|
||||
@@ -147,16 +148,15 @@
|
||||
</li>
|
||||
</ul>
|
||||
<div class="navbar-default sidebar" role="navigation">
|
||||
<div class="sidebar-nav navbar-events-collapse navbar-collapse hidden-sm hidden-md hidden-lg">
|
||||
<ul class="nav">
|
||||
<li><a href="{% url "control:events" %}">{% trans "Event overview" %}</a></li>
|
||||
{% for e in request.user.events_cache %}
|
||||
<li>
|
||||
<a href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}">
|
||||
{{ e.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<div class="sidebar-nav navbar-events-collapse navbar-collapse hidden-sm hidden-md hidden-lg mobile-event-dropdown">
|
||||
<ul class="nav" data-event-typeahead data-source="{% url "control:events.typeahead" %}">
|
||||
<li>
|
||||
<div class="form-box">
|
||||
<input type="text" class="form-control"
|
||||
placeholder="{% trans "Search for events" %}"
|
||||
data-typeahead-query>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="sidebar-nav navbar-nav-collapse navbar-collapse">
|
||||
|
||||
@@ -2,7 +2,7 @@ from django.conf.urls import include, url
|
||||
|
||||
from pretix.control.views import (
|
||||
auth, checkin, dashboards, event, global_settings, item, main, orders,
|
||||
organizer, user, vouchers, waitinglist,
|
||||
organizer, typeahead, user, vouchers, waitinglist,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@@ -45,6 +45,7 @@ urlpatterns = [
|
||||
name='organizer.team.delete'),
|
||||
url(r'^events/$', main.EventList.as_view(), name='events'),
|
||||
url(r'^events/add$', main.EventWizard.as_view(), name='events.add'),
|
||||
url(r'^events/typeahead/$', typeahead.event_list, name='events.typeahead'),
|
||||
url(r'^event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include([
|
||||
url(r'^$', dashboards.event_index, name='event.index'),
|
||||
url(r'^live/$', event.EventLive.as_view(), name='event.live'),
|
||||
|
||||
@@ -252,7 +252,7 @@ def user_event_widgets(**kwargs):
|
||||
user = kwargs.pop('user')
|
||||
widgets = []
|
||||
|
||||
events = user.get_events_with_any_permission().order_by('-date_from', 'name').select_related('organizer')
|
||||
events = user.get_events_with_any_permission().order_by('-date_from', 'name').select_related('organizer')[:100]
|
||||
for event in events:
|
||||
widgets.append({
|
||||
'content': '<div class="event">{event}<span class="from">{df}</span><span class="to">{dt}</span></div>'.format(
|
||||
|
||||
32
src/pretix/control/views/typeahead.py
Normal file
32
src/pretix/control/views/typeahead.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import json
|
||||
|
||||
from django.db.models import Q
|
||||
from django.http import JsonResponse
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
def i18ncomp(query):
|
||||
return json.dumps(str(query))[1:-1]
|
||||
|
||||
|
||||
def event_list(request):
|
||||
query = request.GET.get('query', '')
|
||||
qs = request.user.get_events_with_any_permission().filter(
|
||||
Q(name__icontains=i18ncomp(query)) | Q(slug__icontains=query) |
|
||||
Q(organizer__name__icontains=i18ncomp(query)) | Q(organizer__slug__icontains=query)
|
||||
).order_by('-date_from')
|
||||
doc = {
|
||||
'results': [
|
||||
{
|
||||
'slug': e.slug,
|
||||
'organizer': str(e.organizer.name),
|
||||
'name': str(e.name),
|
||||
'date_range': e.get_date_range_display(),
|
||||
'url': reverse('control:event.index', kwargs={
|
||||
'event': e.slug,
|
||||
'organizer': e.organizer.slug
|
||||
})
|
||||
} for e in qs.select_related('organizer')[:10]
|
||||
]
|
||||
}
|
||||
return JsonResponse(doc)
|
||||
93
src/pretix/static/pretixcontrol/js/ui/typeahead.js
Normal file
93
src/pretix/static/pretixcontrol/js/ui/typeahead.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/*global $,u2f */
|
||||
$(function () {
|
||||
$('ul.navbar-nav .dropdown, .navbar-events-collapse').on('shown.bs.collapse shown.bs.dropdown', function () {
|
||||
$(this).parent().find("input").val("").change().focus();
|
||||
});
|
||||
$('.dropdown-menu .form-box input').click(function (e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$("[data-event-typeahead]").each(function () {
|
||||
var $container = $(this);
|
||||
var $query = $(this).find('[data-typeahead-query]');
|
||||
$query.closest("li").nextAll().remove();
|
||||
|
||||
$query.on("change", function () {
|
||||
$.getJSON(
|
||||
$container.attr("data-source") + "?query=" + encodeURIComponent($query.val()),
|
||||
function (data) {
|
||||
$query.closest("li").nextAll().remove();
|
||||
$.each(data.results, function (i, res) {
|
||||
$container.append(
|
||||
$("<li>").append(
|
||||
$("<a>").attr("href", res.url).append(
|
||||
$("<div>").append(
|
||||
$("<span>").addClass("event-name").append(res.name)
|
||||
).append(
|
||||
$("<span>").addClass("event-organizer").append(
|
||||
$("<span>").addClass("fa fa-users fa-fw")
|
||||
).append(" ").append(res.organizer)
|
||||
).append(
|
||||
$("<span>").addClass("event-daterange").append(
|
||||
$("<span>").addClass("fa fa-calendar fa-fw")
|
||||
).append(" ").append(res.date_range)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
$query.on("keydown", function (event) {
|
||||
var $selected = $container.find(".active");
|
||||
if (event.which === 13) { // enter
|
||||
var $link = $selected.find("a");
|
||||
if ($link.length) {
|
||||
location.href = $link.attr("href");
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
$query.on("keyup", function (event) {
|
||||
var $first = $query.closest("li").next();
|
||||
var $last = $query.closest("li").nextAll().last();
|
||||
var $selected = $container.find(".active");
|
||||
|
||||
if (event.which === 13) { // enter
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return true;
|
||||
} else if (event.which === 40) { // down
|
||||
if ($selected.length === 0) {
|
||||
$selected = $query.closest("li");
|
||||
}
|
||||
var $next = $selected.next();
|
||||
if ($next.length === 0) {
|
||||
$next = $first;
|
||||
}
|
||||
$selected.removeClass("active");
|
||||
$next.addClass("active");
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return true;
|
||||
} else if (event.which === 38) { // up
|
||||
if ($selected.length === 0) {
|
||||
$selected = $container.first();
|
||||
}
|
||||
var $prev = $selected.prev();
|
||||
if ($prev.length === 0 || $prev.find("input").length > 0) {
|
||||
$prev = $last;
|
||||
}
|
||||
$selected.removeClass("active");
|
||||
$prev.addClass("active");
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return true;
|
||||
} else {
|
||||
$(this).change();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -297,3 +297,28 @@ body.loading #wrapper {
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.dropdown-menu .form-box {
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
.event-dropdown {
|
||||
width: 300px;
|
||||
}
|
||||
.event-dropdown, .mobile-event-dropdown {
|
||||
.event-name {
|
||||
display: block;
|
||||
}
|
||||
.event-daterange, .event-organizer {
|
||||
display: block;
|
||||
font-size: $font-size-small;
|
||||
color: $text-muted;
|
||||
}
|
||||
.active .event-daterange, .active .event-organizer, .active a {
|
||||
color: white;
|
||||
}
|
||||
.active {
|
||||
background: $brand-primary;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user