Change style

This commit is contained in:
Phin Wolkwitz
2024-01-22 16:39:03 +01:00
parent b47f57cf8e
commit 64838c53f1
3 changed files with 174 additions and 103 deletions
@@ -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">
&middot;&middot;&middot;
</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 %}
+64 -47
View File
@@ -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 {