forked from CGM_Public/pretix_original
* Use flexbox for better tiling Replaces 'width' by 'display_size' and reduces sorting complexity. * CSS improvements, Responsiveness
This commit is contained in:
committed by
Raphael Michel
parent
bfd87f11dd
commit
3930fc749a
@@ -50,8 +50,8 @@ This signal is sent out to include widgets in the event dashboard. Receivers
|
|||||||
should return a list of dictionaries, where each dictionary can have the keys:
|
should return a list of dictionaries, where each dictionary can have the keys:
|
||||||
|
|
||||||
* content (str, containing HTML)
|
* content (str, containing HTML)
|
||||||
* minimal width (int, widget width in 1/12ths of the page, default ist 3, can be
|
* display_size (str, one of "full" (whole row), "big" (half a row) or "small"
|
||||||
ignored on small displays)
|
(quarter of a row). May be ignored on small displays, default is "small")
|
||||||
* priority (int, used for ordering, higher comes first, default is 1)
|
* priority (int, used for ordering, higher comes first, default is 1)
|
||||||
* link (str, optional, if the full widget should be a link)
|
* link (str, optional, if the full widget should be a link)
|
||||||
|
|
||||||
@@ -66,8 +66,8 @@ This signal is sent out to include widgets in the personal user dashboard. Recei
|
|||||||
should return a list of dictionaries, where each dictionary can have the keys:
|
should return a list of dictionaries, where each dictionary can have the keys:
|
||||||
|
|
||||||
* content (str, containing HTML)
|
* content (str, containing HTML)
|
||||||
* minimal width (int, widget width in 1/12ths of the page, default ist 3, can be
|
* display_size (str, one of "full" (whole row), "big" (half a row) or "small"
|
||||||
ignored on small displays)
|
(quarter of a row). May be ignored on small displays, default is "small")
|
||||||
* priority (int, used for ordering, higher comes first, default is 1)
|
* priority (int, used for ordering, higher comes first, default is 1)
|
||||||
* link (str, optional, if the full widget should be a link)
|
* link (str, optional, if the full widget should be a link)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<h1>{% trans "Dashboard" %}</h1>
|
<h1>{% trans "Dashboard" %}</h1>
|
||||||
<div class="row dashboard">
|
<div class="row dashboard">
|
||||||
{% for w in widgets %}
|
{% for w in widgets %}
|
||||||
<div class="col-xs-12 col-sm-{% if w.width > 6 %}12{% else %}6{% endif %} col-md-{{ w.width }}">
|
<div class="widget-container widget-{{ w.display_size|default:"small" }}">
|
||||||
{% if w.url %}
|
{% if w.url %}
|
||||||
<a href="{{ w.url }}" class="widget">
|
<a href="{{ w.url }}" class="widget">
|
||||||
{{ w.content|safe }}
|
{{ w.content|safe }}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
</h1>
|
</h1>
|
||||||
<div class="row dashboard">
|
<div class="row dashboard">
|
||||||
{% for w in widgets %}
|
{% for w in widgets %}
|
||||||
<div class="col-xs-12 col-sm-{% if w.width > 6 %}12{% else %}6{% endif %} col-md-{{ w.width }}">
|
<div class="widget-container widget-{{ w.display_size|default:"small" }}">
|
||||||
{% if w.url %}
|
{% if w.url %}
|
||||||
<a href="{{ w.url }}" class="widget">
|
<a href="{{ w.url }}" class="widget">
|
||||||
{{ w.content|safe }}
|
{{ w.content|safe }}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def base_widgets(sender, **kwargs):
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'content': NUM_WIDGET.format(num=tickc, text=_('Attendees (ordered)')),
|
'content': NUM_WIDGET.format(num=tickc, text=_('Attendees (ordered)')),
|
||||||
'width': 3,
|
'display_size': 'small',
|
||||||
'priority': 100,
|
'priority': 100,
|
||||||
'url': reverse('control:event.orders', kwargs={
|
'url': reverse('control:event.orders', kwargs={
|
||||||
'event': sender.slug,
|
'event': sender.slug,
|
||||||
@@ -50,7 +50,7 @@ def base_widgets(sender, **kwargs):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'content': NUM_WIDGET.format(num=paidc, text=_('Attendees (paid)')),
|
'content': NUM_WIDGET.format(num=paidc, text=_('Attendees (paid)')),
|
||||||
'width': 3,
|
'display_size': 'small',
|
||||||
'priority': 100,
|
'priority': 100,
|
||||||
'url': reverse('control:event.orders.overview', kwargs={
|
'url': reverse('control:event.orders.overview', kwargs={
|
||||||
'event': sender.slug,
|
'event': sender.slug,
|
||||||
@@ -60,7 +60,7 @@ def base_widgets(sender, **kwargs):
|
|||||||
{
|
{
|
||||||
'content': NUM_WIDGET.format(
|
'content': NUM_WIDGET.format(
|
||||||
num=formats.localize(rev), text=_('Total revenue ({currency})').format(currency=sender.currency)),
|
num=formats.localize(rev), text=_('Total revenue ({currency})').format(currency=sender.currency)),
|
||||||
'width': 3,
|
'display_size': 'small',
|
||||||
'priority': 100,
|
'priority': 100,
|
||||||
'url': reverse('control:event.orders.overview', kwargs={
|
'url': reverse('control:event.orders.overview', kwargs={
|
||||||
'event': sender.slug,
|
'event': sender.slug,
|
||||||
@@ -69,7 +69,7 @@ def base_widgets(sender, **kwargs):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'content': NUM_WIDGET.format(num=prodc, text=_('Active products')),
|
'content': NUM_WIDGET.format(num=prodc, text=_('Active products')),
|
||||||
'width': 3,
|
'display_size': 'small',
|
||||||
'priority': 100,
|
'priority': 100,
|
||||||
'url': reverse('control:event.items', kwargs={
|
'url': reverse('control:event.items', kwargs={
|
||||||
'event': sender.slug,
|
'event': sender.slug,
|
||||||
@@ -87,7 +87,7 @@ def quota_widgets(sender, **kwargs):
|
|||||||
widgets.append({
|
widgets.append({
|
||||||
'content': NUM_WIDGET.format(num='{}/{}'.format(left, q.size) if q.size is not None else '\u221e',
|
'content': NUM_WIDGET.format(num='{}/{}'.format(left, q.size) if q.size is not None else '\u221e',
|
||||||
text=_('{quota} left').format(quota=q.name)),
|
text=_('{quota} left').format(quota=q.name)),
|
||||||
'width': 3,
|
'display_size': 'small',
|
||||||
'priority': 50,
|
'priority': 50,
|
||||||
'url': reverse('control:event.items.quotas.show', kwargs={
|
'url': reverse('control:event.items.quotas.show', kwargs={
|
||||||
'event': sender.slug,
|
'event': sender.slug,
|
||||||
@@ -101,7 +101,7 @@ def quota_widgets(sender, **kwargs):
|
|||||||
@receiver(signal=event_dashboard_widgets)
|
@receiver(signal=event_dashboard_widgets)
|
||||||
def shop_state_widget(sender, **kwargs):
|
def shop_state_widget(sender, **kwargs):
|
||||||
return [{
|
return [{
|
||||||
'width': 3,
|
'display_size': 'small',
|
||||||
'priority': 1000,
|
'priority': 1000,
|
||||||
'content': '<div class="shopstate">{t1}<br><span class="{cls}"><span class="fa {icon}"></span> {state}</span>{t2}</div>'.format(
|
'content': '<div class="shopstate">{t1}<br><span class="{cls}"><span class="fa {icon}"></span> {state}</span>{t2}</div>'.format(
|
||||||
t1=_('Your ticket shop is'), t2=_('Click here to change'),
|
t1=_('Your ticket shop is'), t2=_('Click here to change'),
|
||||||
@@ -144,7 +144,7 @@ def welcome_wizard_widget(sender, **kwargs):
|
|||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
return [{
|
return [{
|
||||||
'width': 12,
|
'display_size': 'full',
|
||||||
'priority': 2000,
|
'priority': 2000,
|
||||||
'content': template.render(ctx)
|
'content': template.render(ctx)
|
||||||
}]
|
}]
|
||||||
@@ -171,7 +171,7 @@ def user_event_widgets(**kwargs):
|
|||||||
event=event.name, df=date_format(event.date_from, 'SHORT_DATE_FORMAT') if event.date_from else '',
|
event=event.name, df=date_format(event.date_from, 'SHORT_DATE_FORMAT') if event.date_from else '',
|
||||||
dt=date_format(event.date_to, 'SHORT_DATE_FORMAT') if event.date_to else ''
|
dt=date_format(event.date_to, 'SHORT_DATE_FORMAT') if event.date_to else ''
|
||||||
),
|
),
|
||||||
'width': 3,
|
'display_size': 'small',
|
||||||
'priority': 100,
|
'priority': 100,
|
||||||
'url': reverse('control:event.index', kwargs={
|
'url': reverse('control:event.index', kwargs={
|
||||||
'event': event.slug,
|
'event': event.slug,
|
||||||
@@ -188,7 +188,7 @@ def new_event_widgets(**kwargs):
|
|||||||
'content': '<div class="newevent"><span class="fa fa-plus-circle"></span>{t}</div>'.format(
|
'content': '<div class="newevent"><span class="fa fa-plus-circle"></span>{t}</div>'.format(
|
||||||
t=_('Create a new event')
|
t=_('Create a new event')
|
||||||
),
|
),
|
||||||
'width': 3,
|
'display_size': 'small',
|
||||||
'priority': 50,
|
'priority': 50,
|
||||||
'url': reverse('control:events.add')
|
'url': reverse('control:events.add')
|
||||||
}
|
}
|
||||||
@@ -207,22 +207,18 @@ def user_index(request):
|
|||||||
|
|
||||||
def rearrange(widgets: list):
|
def rearrange(widgets: list):
|
||||||
"""
|
"""
|
||||||
Small and stupid algorithm to arrange widget boxes without too many gaps while respecting
|
Sort widget boxes according to priority.
|
||||||
priority. Doing this siginificantly better might be *really* hard.
|
|
||||||
"""
|
"""
|
||||||
oldlist = sorted(widgets, key=lambda w: -1 * w.get('priority', 1))
|
mapping = {
|
||||||
newlist = []
|
'small': 1,
|
||||||
cpos = 0
|
'big': 2,
|
||||||
while len(oldlist) > 0:
|
'full': 3,
|
||||||
max_prio = max([w.get('priority', 1) for w in oldlist])
|
}
|
||||||
try:
|
|
||||||
best = max([w for w in oldlist if w.get('priority', 1) == max_prio and cpos + w.get('width', 3) <= 12],
|
|
||||||
key=lambda w: w.get('width', 3))
|
|
||||||
cpos = (cpos + best.get('width', 3)) % 12
|
|
||||||
except ValueError: # max() arg is an empty sequence
|
|
||||||
best = [w for w in oldlist if w.get('priority', 1) == max_prio][0]
|
|
||||||
cpos = best.get('width', 3)
|
|
||||||
oldlist.remove(best)
|
|
||||||
newlist.append(best)
|
|
||||||
|
|
||||||
return newlist
|
def sort_key(element):
|
||||||
|
return (
|
||||||
|
element.get('priority', 1),
|
||||||
|
mapping.get(element.get('display_size', 'small'), 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
return sorted(widgets, key=sort_key, reverse=True)
|
||||||
|
|||||||
@@ -1,23 +1,38 @@
|
|||||||
|
.dashboard {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.dashboard .widget-container {
|
||||||
|
flex:1 0 auto;
|
||||||
|
align-self: stretch;
|
||||||
|
padding: 15px 5px;
|
||||||
|
border: 5px solid white;
|
||||||
|
min-height: 160px;
|
||||||
|
background: #F8F8F8;
|
||||||
|
}
|
||||||
|
.dashboard .widget-container.widget-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.dashboard .widget-container.widget-big {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
.dashboard .widget-container.widget-small {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
.dashboard-panels .panel-heading .fa {
|
.dashboard-panels .panel-heading .fa {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
.dashboard > div {
|
.dashboard .widget-container:hover,.dashboard .widget-container:focus {
|
||||||
padding: 5px;
|
background: #EEEEEE;
|
||||||
}
|
|
||||||
.dashboard .widget {
|
|
||||||
min-height: 160px;
|
|
||||||
background: #F8F8F8;
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
.dashboard .widget:hover,.dashboard .widget:focus {
|
.dashboard .widget:hover,.dashboard .widget:focus {
|
||||||
background: #EEEEEE;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
.dashboard .numwidget {
|
.dashboard .numwidget {
|
||||||
.num {
|
.num {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 28px 0 10px;
|
padding: 10px 0 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
}
|
}
|
||||||
@@ -29,7 +44,7 @@
|
|||||||
}
|
}
|
||||||
.dashboard .shopstate {
|
.dashboard .shopstate {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 36px 0;
|
padding: 18px 0;
|
||||||
|
|
||||||
span.live, span.off {
|
span.live, span.off {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -45,7 +60,7 @@
|
|||||||
}
|
}
|
||||||
.dashboard .event {
|
.dashboard .event {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 30px;
|
padding: 15px 30px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
||||||
span.from, span.to {
|
span.from, span.to {
|
||||||
@@ -65,7 +80,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.dashboard .welcome-wizard {
|
.dashboard .welcome-wizard {
|
||||||
padding: 30px;
|
padding: 5px 15px;
|
||||||
h3 {
|
h3 {
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
}
|
}
|
||||||
@@ -77,3 +92,14 @@
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media (max-width: $screen-sm-max) {
|
||||||
|
.dashboard .widget-container.widget-small {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: $screen-xs-max) {
|
||||||
|
.dashboard .widget-container.widget-small,
|
||||||
|
.dashboard .widget-container.widget-big {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user