forked from CGM_Public/pretix_original
Autocompletion in event selection
This commit is contained in:
@@ -59,8 +59,6 @@ class PermissionMiddleware(MiddlewareMixin):
|
|||||||
return redirect_to_login(
|
return redirect_to_login(
|
||||||
path, resolved_login_url, REDIRECT_FIELD_NAME)
|
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:
|
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
||||||
request.event = Event.objects.filter(
|
request.event = Event.objects.filter(
|
||||||
slug=url.kwargs['event'],
|
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/quota.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/question.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/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/asynctask.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixbase/js/asyncdownload.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixbase/js/asyncdownload.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "colorpicker/bootstrap-colorpicker.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>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-calendar"></i>
|
||||||
<span class="event-name">{{ request.event }}</span>
|
<span class="event-name">{{ request.event }}</span>
|
||||||
<span class="caret"></span></a>
|
<span class="caret"></span></a>
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu event-dropdown" role="menu" data-event-typeahead
|
||||||
<li><a href="{% url "control:events" %}">{% trans "Events overview" %}</a></li>
|
data-source="{% url "control:events.typeahead" %}">
|
||||||
{% regroup request.user.events_cache by organizer as event_list %}
|
<li>
|
||||||
{% for g in event_list %}
|
<div class="form-box">
|
||||||
<li class="dropdown-header">{{ g.grouper }}</li>
|
<input type="text" class="form-control"
|
||||||
{% for e in g.list %}
|
placeholder="{% trans "Search for events" %}"
|
||||||
<li><a href="{% url "control:event.index" organizer=g.grouper.slug event=e.slug %}">{{ e.name }}</a></li>
|
data-typeahead-query>
|
||||||
{% endfor %}
|
</div>
|
||||||
{% endfor %}
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% if request.event %}
|
{% if request.event %}
|
||||||
@@ -147,16 +148,15 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="navbar-default sidebar" role="navigation">
|
<div class="navbar-default sidebar" role="navigation">
|
||||||
<div class="sidebar-nav navbar-events-collapse navbar-collapse hidden-sm hidden-md hidden-lg">
|
<div class="sidebar-nav navbar-events-collapse navbar-collapse hidden-sm hidden-md hidden-lg mobile-event-dropdown">
|
||||||
<ul class="nav">
|
<ul class="nav" data-event-typeahead data-source="{% url "control:events.typeahead" %}">
|
||||||
<li><a href="{% url "control:events" %}">{% trans "Event overview" %}</a></li>
|
<li>
|
||||||
{% for e in request.user.events_cache %}
|
<div class="form-box">
|
||||||
<li>
|
<input type="text" class="form-control"
|
||||||
<a href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}">
|
placeholder="{% trans "Search for events" %}"
|
||||||
{{ e.name }}
|
data-typeahead-query>
|
||||||
</a>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-nav navbar-nav-collapse navbar-collapse">
|
<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 (
|
from pretix.control.views import (
|
||||||
auth, checkin, dashboards, event, global_settings, item, main, orders,
|
auth, checkin, dashboards, event, global_settings, item, main, orders,
|
||||||
organizer, user, vouchers, waitinglist,
|
organizer, typeahead, user, vouchers, waitinglist,
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@@ -45,6 +45,7 @@ urlpatterns = [
|
|||||||
name='organizer.team.delete'),
|
name='organizer.team.delete'),
|
||||||
url(r'^events/$', main.EventList.as_view(), name='events'),
|
url(r'^events/$', main.EventList.as_view(), name='events'),
|
||||||
url(r'^events/add$', main.EventWizard.as_view(), name='events.add'),
|
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'^event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include([
|
||||||
url(r'^$', dashboards.event_index, name='event.index'),
|
url(r'^$', dashboards.event_index, name='event.index'),
|
||||||
url(r'^live/$', event.EventLive.as_view(), name='event.live'),
|
url(r'^live/$', event.EventLive.as_view(), name='event.live'),
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ def user_event_widgets(**kwargs):
|
|||||||
user = kwargs.pop('user')
|
user = kwargs.pop('user')
|
||||||
widgets = []
|
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:
|
for event in events:
|
||||||
widgets.append({
|
widgets.append({
|
||||||
'content': '<div class="event">{event}<span class="from">{df}</span><span class="to">{dt}</span></div>'.format(
|
'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;
|
text-overflow: ellipsis;
|
||||||
vertical-align: bottom;
|
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