mirror of
https://github.com/pretix/pretix.git
synced 2026-08-11 10:57:00 +00:00
Change style
This commit is contained in:
@@ -30,9 +30,14 @@
|
||||
</button>
|
||||
{% include "pretixcontrol/event/fragment_qr_dropdown.html" with url=0 %}
|
||||
</div>
|
||||
<span id="shop_state" class="pull-right">
|
||||
<br>
|
||||
<span id="shop_state" class="shop_state">
|
||||
{{ shop_state.content|safe }}
|
||||
<a href="{{ shop_state.url }}">
|
||||
{{ shop_state.content|safe }}
|
||||
<button type="button" class="btn btn-default btn-xs btn-wrench js-only" href="{{ shop_state.url }}">
|
||||
<i class="fa fa-wrench" aria-hidden="true"></i>
|
||||
<span class="sr-only">{% trans "Change shop state" %}</span>
|
||||
</button>
|
||||
</a>
|
||||
</span>
|
||||
<div class="clearfix"></div>
|
||||
@@ -88,6 +93,26 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Warnings ----------------------------------------------------------------------------------------------------->
|
||||
<!-- Comments ----------------------------------------------------------------------------------------------------->
|
||||
<div class="widget-container widget-small">
|
||||
{% trans "Internal comment" %}
|
||||
<form class="form" method="post"
|
||||
action="{% url "control:event.comment" event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
{% bootstrap_field comment_form.comment layout="horizontal" show_help=True show_label=False horizontal_field_class="col-md-12" %}
|
||||
</div>
|
||||
{% if not comment_form.readonly %}
|
||||
<p class="text-right flip">
|
||||
<br>
|
||||
<button class="btn btn-default">
|
||||
{% trans "Update comment" %}
|
||||
</button>
|
||||
</p>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
<!-- Comments ----------------------------------------------------------------------------------------------------->
|
||||
|
||||
{% eventsignal request.event "pretix.control.signals.event_dashboard_top" request=request %}
|
||||
<!-- Timeline ----------------------------------------------------------------------------------------------------->
|
||||
@@ -102,7 +127,7 @@
|
||||
<!-- Timeline ----------------------------------------------------------------------------------------------------->
|
||||
<!-- Big numbers ---------------------------------------------------------------------------------------------->
|
||||
<div class="dashboard">
|
||||
<div class="widget-container widget-small">
|
||||
<!--<div class="widget-container widget-small">
|
||||
<a href="{{ attendees_ordered.url }}" class="widget">
|
||||
{{ attendees_ordered.content|safe }}
|
||||
</a>
|
||||
@@ -111,8 +136,13 @@
|
||||
<a href="{{ attendees_paid.url }}" class="widget">
|
||||
{{ attendees_paid.content|safe }}
|
||||
</a>
|
||||
</div>-->
|
||||
<div class="widget-container widget-small no-background">
|
||||
<a href="{{ attendees_paid_ordered.url }}" class="widget">
|
||||
{{ attendees_paid_ordered.content|safe }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="widget-container widget-small">
|
||||
<div class="widget-container widget-small no-background">
|
||||
<a href="{{ total_revenue.url }}" class="widget">
|
||||
{{ total_revenue.content|safe }}
|
||||
</a>
|
||||
@@ -243,58 +273,70 @@
|
||||
</div>
|
||||
<!-- Check-Ins ---------------------------------------------------------------------------------------------------->
|
||||
<!-- Quotas ------------------------------------------------------------------------------------------------------->
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{% trans "Name" %}
|
||||
</th>
|
||||
<th>
|
||||
{% trans "Begin" %}
|
||||
<a href="? url_replace request 'filter-ordering' '-date_from' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="? url_replace request 'filter-ordering' 'date_from' %}"><i class="fa fa-caret-up"></i></a>
|
||||
</th>
|
||||
<th>
|
||||
{% trans "Paid tickets per quota" %}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for s in subevents %}
|
||||
<td>
|
||||
<strong><a href="{% url "control:event.subevent" organizer=request.event.organizer.slug event=request.event.slug subevent=s.id %}?returnto={{ request.GET.urlencode|urlencode }}">
|
||||
{{ s.name }}</a></strong><br>
|
||||
<small class="text-muted">
|
||||
#{{ s.pk }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
{{ s.get_date_from_display }}<br>
|
||||
<span class="text-muted">
|
||||
{{ s.date_from|date:"l" }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{% for q in s.first_quotas|slice:":3" %}
|
||||
{% include "pretixcontrol/fragment_quota_box_paid.html" with quota=q %}
|
||||
{% endfor %}
|
||||
{% if s.first_quotas|length > 3 %}
|
||||
<a href="{% url "control:event.items.quotas" organizer=request.event.organizer.slug event=request.event.slug %}?subevent={{ s.id }}"
|
||||
class="quotabox-more" data-toggle="tooltip" title="{% trans "More quotas" %}"
|
||||
data-placement="top">
|
||||
···
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<!-- Quotas ------------------------------------------------------------------------------------------------------->
|
||||
<div class="dashboard dashboard-panels">
|
||||
<!-- Comments ----------------------------------------------------------------------------------------------------->
|
||||
<div class="col-md-6 px-md-0">
|
||||
<div class="panel panel-default items">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Internal comment" %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form class="form" method="post"
|
||||
action="{% url "control:event.comment" event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
{% bootstrap_field comment_form.comment layout="horizontal" show_help=True show_label=False horizontal_field_class="col-md-12" %}
|
||||
</div>
|
||||
{% if not comment_form.readonly %}
|
||||
<p class="text-right flip">
|
||||
<br>
|
||||
<button class="btn btn-default">
|
||||
{% trans "Update comment" %}
|
||||
</button>
|
||||
</p>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Comments ----------------------------------------------------------------------------------------------------->
|
||||
<!-- Logs --------------------------------------------------------------------------------------------------------->
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Event logs" %}
|
||||
</h3>
|
||||
</div>
|
||||
<ul class="list-group" id="logs_target">
|
||||
<div class="logs-lazy-loading">
|
||||
<span class="fa fa-cog fa-4x"></span>
|
||||
</div>
|
||||
</ul>
|
||||
<div class="panel-footer">
|
||||
<a href="{% url "control:event.log" event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||
{% trans "Show more logs" %}
|
||||
</a>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Event logs" %}
|
||||
</h3>
|
||||
</div>
|
||||
<ul class="list-group" id="logs_target">
|
||||
<div class="logs-lazy-loading">
|
||||
<span class="fa fa-cog fa-4x"></span>
|
||||
</div>
|
||||
</ul>
|
||||
<div class="panel-footer">
|
||||
<a href="{% url "control:event.log" event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||
{% trans "Show more logs" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Logs --------------------------------------------------------------------------------------------------------->
|
||||
</div>
|
||||
<script type="application/json" id="rev-data">{{ rev_data|escapejson }}</script>
|
||||
<script type="application/text" id="currency">{{ request.event.currency }}</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
import dateutil
|
||||
@@ -20,7 +20,10 @@ from pretix.base.timeline import timeline_for_event
|
||||
from pretix.control.forms.event import CommentForm
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.control.views import ChartContainingView
|
||||
from pretix.control.views.dashboards import NUM_WIDGET
|
||||
|
||||
NUM_WIDGET = str('<div class="numwidget"><span class="num">{num}</span>'
|
||||
'<span class="text">{text}</span>'
|
||||
'<span class="text-add">{text_add}</span></div>')
|
||||
|
||||
|
||||
class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView):
|
||||
@@ -54,15 +57,11 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
|
||||
else:
|
||||
opqs = OrderPosition.objects
|
||||
|
||||
tickc = opqs.filter(
|
||||
order__event=self.request.event, item__admission=True,
|
||||
order__status__in=(Order.STATUS_PAID, Order.STATUS_PENDING),
|
||||
).count()
|
||||
|
||||
ctx['shop_state'] = {
|
||||
'display_size': 'small',
|
||||
'priority': 1000,
|
||||
'content': '<div class="shopstate pull-left"><span class="{cls}"><span class="fa {icon}"></span> {state}</span></div>'.format(
|
||||
'content': '<span class="{cls}">{t1} {state} <span class="fa {icon}"></span></span>'.format(
|
||||
t1=_('Your ticket shop is'),
|
||||
state=_('live') if self.request.event.live and not self.request.event.testmode else (
|
||||
_('live and in test mode') if self.request.event.live else (
|
||||
_('not yet public') if not self.request.event.testmode else (
|
||||
@@ -73,7 +72,7 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
|
||||
icon='fa-check-circle' if self.request.event.live and not self.request.event.testmode else (
|
||||
'fa-warning' if self.request.event.live else (
|
||||
'fa-times-circle' if not self.request.event.testmode else (
|
||||
'fa-times-circle'
|
||||
'fa-lock'
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -93,22 +92,38 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
|
||||
cl.auto_checkin_sales_channels = [sales_channels[channel] for channel in cl.auto_checkin_sales_channels]
|
||||
ctx['checkinlists'] = qs
|
||||
|
||||
tickc = opqs.filter(
|
||||
order__event=self.request.event, item__admission=True,
|
||||
order__status__in=(Order.STATUS_PAID, Order.STATUS_PENDING),
|
||||
).count()
|
||||
ctx['attendees_ordered'] = {
|
||||
'content': NUM_WIDGET.format(num=tickc, text=_('Attendees (ordered)')),
|
||||
'content': NUM_WIDGET.format(num=f'{tickc} <span class="fa fa-users"></span>',
|
||||
text=_('Attendees (ordered)'),
|
||||
text_add=''),
|
||||
'priority': 100,
|
||||
'url': reverse('control:event.orders', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug
|
||||
}) + ('?subevent={}'.format(subevent.pk) if subevent else '')
|
||||
}
|
||||
|
||||
paidc = opqs.filter(
|
||||
order__event=self.request.event, item__admission=True,
|
||||
order__status=Order.STATUS_PAID,
|
||||
).count()
|
||||
|
||||
ctx['attendees_paid'] = {
|
||||
'content': NUM_WIDGET.format(num=paidc, text=_('Attendees (paid)')),
|
||||
'content': NUM_WIDGET.format(num=f'{paidc} <span class="fa fa-money"></span>', text=_('Attendees (paid)'),
|
||||
text_add=''),
|
||||
'priority': 100,
|
||||
'url': reverse('control:event.orders.overview', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug
|
||||
}) + ('?subevent={}'.format(subevent.pk) if subevent else '')
|
||||
}
|
||||
ctx['attendees_paid_ordered'] = {
|
||||
'content': NUM_WIDGET.format(
|
||||
num=f'<span class="fa fa-user"></span> {tickc}',
|
||||
text=_('Attendees'),
|
||||
text_add=_(f'{paidc} paid, {tickc - paidc} pending')),
|
||||
'priority': 100,
|
||||
'url': reverse('control:event.orders.overview', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
@@ -131,8 +146,8 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
|
||||
ctx['total_revenue'] = {
|
||||
'content': NUM_WIDGET.format(
|
||||
num=formats.localize(round_decimal(rev, self.request.event.currency)),
|
||||
text=_('Total revenue ({currency})').format(currency=self.request.event.currency)
|
||||
),
|
||||
text=_('Total revenue ({currency})').format(currency=self.request.event.currency),
|
||||
text_add=''),
|
||||
'priority': 100,
|
||||
'url': reverse('control:event.orders.overview', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
@@ -143,15 +158,15 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
|
||||
cache = self.request.event.cache
|
||||
ckey = str(subevent.pk) if subevent else 'all'
|
||||
tz = timezone.get_current_timezone()
|
||||
# op_date = OrderPayment.objects.filter(
|
||||
# order=OuterRef('order'),
|
||||
# state__in=(OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED),
|
||||
# payment_date__isnull=False
|
||||
# ).values('order').annotate(
|
||||
# m=Max('payment_date')
|
||||
# ).values(
|
||||
# 'm'
|
||||
# ).order_by()
|
||||
op_date = OrderPayment.objects.filter(
|
||||
order=OuterRef('order'),
|
||||
state__in=(OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED),
|
||||
payment_date__isnull=False
|
||||
).values('order').annotate(
|
||||
m=Max('payment_date')
|
||||
).values(
|
||||
'm'
|
||||
).order_by()
|
||||
p_date = OrderPayment.objects.filter(
|
||||
order=OuterRef('pk'),
|
||||
state__in=(OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED),
|
||||
@@ -164,23 +179,23 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
|
||||
ctx['rev_data'] = cache.get('statistics_rev_data' + ckey)
|
||||
if not ctx['rev_data']:
|
||||
rev_by_day = {}
|
||||
# if subevent:
|
||||
# for o in OrderPosition.objects.annotate(
|
||||
# payment_date=Subquery(op_date, output_field=DateTimeField())
|
||||
# ).filter(order__event=self.request.event,
|
||||
# subevent=subevent,
|
||||
# order__status=Order.STATUS_PAID,
|
||||
# payment_date__isnull=False).values('payment_date', 'price'):
|
||||
# day = o['payment_date'].astimezone(tz).date()
|
||||
# rev_by_day[day] = rev_by_day.get(day, 0) + o['price']
|
||||
# else:
|
||||
for o in Order.objects.annotate(
|
||||
payment_date=Subquery(p_date, output_field=DateTimeField())
|
||||
).filter(event=self.request.event,
|
||||
status=Order.STATUS_PAID,
|
||||
payment_date__isnull=False).values('payment_date', 'total'):
|
||||
day = o['payment_date'].astimezone(tz).date()
|
||||
rev_by_day[day] = rev_by_day.get(day, 0) + o['total']
|
||||
if subevent:
|
||||
for o in OrderPosition.objects.annotate(
|
||||
payment_date=Subquery(op_date, output_field=DateTimeField())
|
||||
).filter(order__event=self.request.event,
|
||||
subevent=subevent,
|
||||
order__status=Order.STATUS_PAID,
|
||||
payment_date__isnull=False).values('payment_date', 'price'):
|
||||
day = o['payment_date'].astimezone(tz).date()
|
||||
rev_by_day[day] = rev_by_day.get(day, 0) + o['price']
|
||||
else:
|
||||
for o in Order.objects.annotate(
|
||||
payment_date=Subquery(p_date, output_field=DateTimeField())
|
||||
).filter(event=self.request.event,
|
||||
status=Order.STATUS_PAID,
|
||||
payment_date__isnull=False).values('payment_date', 'total'):
|
||||
day = o['payment_date'].astimezone(tz).date()
|
||||
rev_by_day[day] = rev_by_day.get(day, 0) + o['total']
|
||||
|
||||
data = []
|
||||
total = 0
|
||||
@@ -189,11 +204,13 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
|
||||
dtstart=min(rev_by_day.keys() if rev_by_day else [datetime.date.today()]),
|
||||
until=max(rev_by_day.keys() if rev_by_day else [datetime.date.today()])):
|
||||
d = d.date()
|
||||
total += float(rev_by_day.get(d, 0))
|
||||
data.append({
|
||||
'date': d.strftime('%Y-%m-%d'),
|
||||
'revenue': round(total, 2),
|
||||
})
|
||||
rev = float(rev_by_day.get(d, 0))
|
||||
if True: # rev != 0:
|
||||
total += rev
|
||||
data.append({
|
||||
'date': d.strftime('%Y-%m-%d'),
|
||||
'revenue': round(total, 2),
|
||||
})
|
||||
ctx['rev_data'] = json.dumps(data)
|
||||
cache.set('statistics_rev_data' + ckey, ctx['rev_data'])
|
||||
# ctx = ctx | statistics.get_context_data(IndexView(request=request))
|
||||
@@ -228,6 +245,6 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
|
||||
for t in timeline_for_event(self.request.event, subevent)
|
||||
]
|
||||
ctx['today'] = now().astimezone(self.request.event.timezone).date()
|
||||
ctx['nearly_now'] = now().astimezone(self.request.event.timezone) - timedelta(seconds=20)
|
||||
ctx['nearly_now'] = now().astimezone(self.request.event.timezone) - datetime.timedelta(seconds=20)
|
||||
# resp['Content-Security-Policy'] = "style-src 'unsafe-inline'"
|
||||
return ctx
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
margin-right: -15px;
|
||||
}
|
||||
|
||||
.no-background {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.dashboard .widget-container {
|
||||
flex: 1 0 auto;
|
||||
align-self: stretch;
|
||||
@@ -57,23 +61,27 @@
|
||||
.dashboard .numwidget {
|
||||
.num {
|
||||
display: block;
|
||||
padding: 10px 0 10px;
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.text-add {
|
||||
display: block;
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.shopstate {
|
||||
text-align: center;
|
||||
|
||||
.shop_state {
|
||||
span.live, span.off {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
span.live {
|
||||
@@ -83,6 +91,10 @@
|
||||
span.off {
|
||||
color: $brand-danger;
|
||||
}
|
||||
|
||||
a {
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-container.widget-container-event {
|
||||
|
||||
Reference in New Issue
Block a user