diff --git a/src/pretix/control/signals.py b/src/pretix/control/signals.py index e9186e776..841cd5c49 100644 --- a/src/pretix/control/signals.py +++ b/src/pretix/control/signals.py @@ -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: * content (str, containing HTML) -* minimal width (int, widget width in 1/12ths of the page, default ist 3, can be - ignored on small displays) +* display_size (str, one of "full" (whole row), "big" (half a row) or "small" + (quarter of a row). May be ignored on small displays, default is "small") * priority (int, used for ordering, higher comes first, default is 1) * 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: * content (str, containing HTML) -* minimal width (int, widget width in 1/12ths of the page, default ist 3, can be - ignored on small displays) +* display_size (str, one of "full" (whole row), "big" (half a row) or "small" + (quarter of a row). May be ignored on small displays, default is "small") * priority (int, used for ordering, higher comes first, default is 1) * link (str, optional, if the full widget should be a link) diff --git a/src/pretix/control/templates/pretixcontrol/dashboard.html b/src/pretix/control/templates/pretixcontrol/dashboard.html index 8a15a3cc6..32538daf9 100644 --- a/src/pretix/control/templates/pretixcontrol/dashboard.html +++ b/src/pretix/control/templates/pretixcontrol/dashboard.html @@ -5,7 +5,7 @@

{% trans "Dashboard" %}

{% for w in widgets %} -
+
{% if w.url %} {{ w.content|safe }} diff --git a/src/pretix/control/templates/pretixcontrol/event/index.html b/src/pretix/control/templates/pretixcontrol/event/index.html index 267329c5a..1b35eead8 100644 --- a/src/pretix/control/templates/pretixcontrol/event/index.html +++ b/src/pretix/control/templates/pretixcontrol/event/index.html @@ -11,7 +11,7 @@
{% for w in widgets %} -
+
{% if w.url %} {{ w.content|safe }} diff --git a/src/pretix/control/views/dashboards.py b/src/pretix/control/views/dashboards.py index 9a7e713a4..dcce63009 100644 --- a/src/pretix/control/views/dashboards.py +++ b/src/pretix/control/views/dashboards.py @@ -41,7 +41,7 @@ def base_widgets(sender, **kwargs): return [ { 'content': NUM_WIDGET.format(num=tickc, text=_('Attendees (ordered)')), - 'width': 3, + 'display_size': 'small', 'priority': 100, 'url': reverse('control:event.orders', kwargs={ 'event': sender.slug, @@ -50,7 +50,7 @@ def base_widgets(sender, **kwargs): }, { 'content': NUM_WIDGET.format(num=paidc, text=_('Attendees (paid)')), - 'width': 3, + 'display_size': 'small', 'priority': 100, 'url': reverse('control:event.orders.overview', kwargs={ 'event': sender.slug, @@ -60,7 +60,7 @@ def base_widgets(sender, **kwargs): { 'content': NUM_WIDGET.format( num=formats.localize(rev), text=_('Total revenue ({currency})').format(currency=sender.currency)), - 'width': 3, + 'display_size': 'small', 'priority': 100, 'url': reverse('control:event.orders.overview', kwargs={ 'event': sender.slug, @@ -69,7 +69,7 @@ def base_widgets(sender, **kwargs): }, { 'content': NUM_WIDGET.format(num=prodc, text=_('Active products')), - 'width': 3, + 'display_size': 'small', 'priority': 100, 'url': reverse('control:event.items', kwargs={ 'event': sender.slug, @@ -87,7 +87,7 @@ def quota_widgets(sender, **kwargs): widgets.append({ 'content': NUM_WIDGET.format(num='{}/{}'.format(left, q.size) if q.size is not None else '\u221e', text=_('{quota} left').format(quota=q.name)), - 'width': 3, + 'display_size': 'small', 'priority': 50, 'url': reverse('control:event.items.quotas.show', kwargs={ 'event': sender.slug, @@ -101,7 +101,7 @@ def quota_widgets(sender, **kwargs): @receiver(signal=event_dashboard_widgets) def shop_state_widget(sender, **kwargs): return [{ - 'width': 3, + 'display_size': 'small', 'priority': 1000, 'content': '
{t1}
{state}{t2}
'.format( t1=_('Your ticket shop is'), t2=_('Click here to change'), @@ -144,7 +144,7 @@ def welcome_wizard_widget(sender, **kwargs): else: return [] return [{ - 'width': 12, + 'display_size': 'full', 'priority': 2000, '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 '', dt=date_format(event.date_to, 'SHORT_DATE_FORMAT') if event.date_to else '' ), - 'width': 3, + 'display_size': 'small', 'priority': 100, 'url': reverse('control:event.index', kwargs={ 'event': event.slug, @@ -188,7 +188,7 @@ def new_event_widgets(**kwargs): 'content': '
{t}
'.format( t=_('Create a new event') ), - 'width': 3, + 'display_size': 'small', 'priority': 50, 'url': reverse('control:events.add') } @@ -207,22 +207,18 @@ def user_index(request): def rearrange(widgets: list): """ - Small and stupid algorithm to arrange widget boxes without too many gaps while respecting - priority. Doing this siginificantly better might be *really* hard. + Sort widget boxes according to priority. """ - oldlist = sorted(widgets, key=lambda w: -1 * w.get('priority', 1)) - newlist = [] - cpos = 0 - while len(oldlist) > 0: - 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) + mapping = { + 'small': 1, + 'big': 2, + 'full': 3, + } - 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) diff --git a/src/static/pretixcontrol/scss/_dashboard.scss b/src/static/pretixcontrol/scss/_dashboard.scss index a895ccf69..8efbbb790 100644 --- a/src/static/pretixcontrol/scss/_dashboard.scss +++ b/src/static/pretixcontrol/scss/_dashboard.scss @@ -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 { opacity: 0.5; } -.dashboard > div { - padding: 5px; -} -.dashboard .widget { - min-height: 160px; - background: #F8F8F8; - display: block; - position: relative; +.dashboard .widget-container:hover,.dashboard .widget-container:focus { + background: #EEEEEE; } .dashboard .widget:hover,.dashboard .widget:focus { - background: #EEEEEE; text-decoration: none; } .dashboard .numwidget { .num { display: block; - padding: 28px 0 10px; + padding: 10px 0 10px; text-align: center; font-size: 40px; } @@ -29,7 +44,7 @@ } .dashboard .shopstate { text-align: center; - padding: 36px 0; + padding: 18px 0; span.live, span.off { display: block; @@ -45,7 +60,7 @@ } .dashboard .event { text-align: center; - padding: 30px; + padding: 15px 30px; font-size: 20px; span.from, span.to { @@ -65,7 +80,7 @@ } } .dashboard .welcome-wizard { - padding: 30px; + padding: 5px 15px; h3 { margin: 0 0 10px 0; } @@ -77,3 +92,14 @@ 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%; + } +}