From a74b550406f2046d5ddb2c77bffae456309f9d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Hells=C3=A9n?= Date: Tue, 21 Jul 2026 06:38:09 +0200 Subject: [PATCH] Add icons to dashboard --- core/views.py | 55 +++- templates/core/dashboard.html | 405 ++++++++++++-------------- templates/kick/campaign_detail.html | 2 +- templates/twitch/campaign_detail.html | 2 +- 4 files changed, 240 insertions(+), 224 deletions(-) diff --git a/core/views.py b/core/views.py index e80920e..feac188 100644 --- a/core/views.py +++ b/core/views.py @@ -1015,7 +1015,7 @@ def search_view(request: HttpRequest) -> HttpResponse: # MARK: / -def dashboard(request: HttpRequest) -> HttpResponse: +def dashboard(request: HttpRequest) -> HttpResponse: # ruff:ignore[too-many-locals, too-many-statements] """Dashboard view showing summary stats and latest campaigns. Args: @@ -1038,6 +1038,27 @@ def dashboard(request: HttpRequest) -> HttpResponse: to_attr="channels_ordered", ), ) + .prefetch_related( + Prefetch( + "time_based_drops", + queryset=TimeBasedDrop.objects.only( + "twitch_id", + "name", + "required_minutes_watched", + "campaign_id", + ).prefetch_related( + Prefetch( + "benefits", + queryset=DropBenefit.objects.only( + "twitch_id", + "name", + "image_asset_url", + "image_file", + ), + ), + ), + ), + ) .order_by("-start_at") ) @@ -1052,9 +1073,41 @@ def dashboard(request: HttpRequest) -> HttpResponse: "owners": list(game.owners.all()), "campaigns": [], } + + # Collect unique benefits and metadata across all time-based drops + seen_benefits: set[str] = set() + benefits_list: list[DropBenefit] = [] + min_watch: int = 0 + max_watch: int = 0 + for drop in campaign.time_based_drops.all(): # pyright: ignore[reportAttributeAccessIssue] + if drop.required_minutes_watched is not None: + if min_watch == 0 or drop.required_minutes_watched < min_watch: + min_watch = drop.required_minutes_watched + max_watch = max(max_watch, drop.required_minutes_watched) + for benefit in drop.benefits.all(): + if benefit.twitch_id not in seen_benefits: + seen_benefits.add(benefit.twitch_id) + benefits_list.append(benefit) + + watch_range: str = "" + if min_watch and max_watch: + if min_watch == max_watch: + watch_range = f"{min_watch}m watch" + else: + watch_range = f"{min_watch}m-{max_watch}m watch" + elif min_watch: + watch_range = f"{min_watch}m watch" + + # Get the publisher (first owner, if any) + owners: list[Organization] = getattr(game, "owners_for_dashboard", []) + publisher: str = owners[0].name if owners else "" + twitch_campaigns_by_game[game_id]["campaigns"].append({ "campaign": campaign, "allowed_channels": getattr(campaign, "channels_ordered", []), + "benefits": benefits_list, + "watch_range": watch_range, + "publisher": publisher, }) active_kick_campaigns: QuerySet[KickDropCampaign] = ( diff --git a/templates/core/dashboard.html b/templates/core/dashboard.html index 1c95756..a28343d 100644 --- a/templates/core/dashboard.html +++ b/templates/core/dashboard.html @@ -30,6 +30,11 @@ href="{% url 'kick:campaign_feed_discord' %}" /> {% endblock extra_head %} {% block content %} +

Open Drop Campaigns

@@ -54,112 +59,90 @@ {% if campaigns_by_game %} {% for game_id, game_data in campaigns_by_game.items %} -

-
-

- {{ game_data.name }} -

- {% if game_data.owners %} -
- Organizations: - {% for org in game_data.owners %} - {{ org.name }} - {% if not forloop.last %},{% endif %} - {% endfor %} -
- {% endif %} +
+
+ + {% picture game_data.box_art alt="Box art for "|add:game_data.name width=72 %} + +
+

+ {{ game_data.name }} +

+ {% if game_data.owners %} +
+ {% for org in game_data.owners %} + {{ org.name }} + {% if not forloop.last %}, {% endif %} + {% endfor %} +
+ {% endif %} +
-
-
{% picture game_data.box_art alt="Box art for "|add:game_data.name width=200 %}
-
-
+
+
{% for campaign_data in game_data.campaigns %} -
-
- - {% picture campaign_data.campaign.image_best_url|default:campaign_data.campaign.image_url alt="Image for "|add:campaign_data.campaign.name width=120 %} -

{{ campaign_data.campaign.clean_name }}

+ flex-shrink: 0; + width: 450px; + padding: 0.9rem; + border-radius: 8px;"> +
+ + {% picture campaign_data.campaign.image_best_url|default:campaign_data.campaign.image_url alt="Image for "|add:campaign_data.campaign.name width=120 %} + +
+ +

{{ campaign_data.campaign.clean_name }}

- - - -
- Channels: -
    - {% if campaign_data.campaign.allow_is_enabled %} - {% if campaign_data.allowed_channels %} - {% for channel in campaign_data.allowed_channels|slice:":5" %} -
  • - - {{ channel.display_name }}[i] -
  • - {% endfor %} - {% else %} - {% if campaign_data.campaign.game.twitch_directory_url %} -
  • - - Go to a participating live channel - -
  • - {% else %} -
  • Failed to get Twitch directory URL :(
  • - {% endif %} - {% endif %} - {% if campaign_data.allowed_channels|length > 5 %} -
  • - ... and {{ campaign_data.allowed_channels|length|add:"-5" }} more -
  • - {% endif %} - {% else %} - {% if campaign_data.campaign.game.twitch_directory_url %} -
  • - - Go to a participating live channel - -
  • - {% else %} -
  • Failed to get Twitch directory URL :(
  • - {% endif %} - {% endif %} -
-
+ {% if campaign_data.publisher %} +
{{ campaign_data.publisher }}
+ {% endif %} +
+ {% if campaign_data.watch_range %}{{ campaign_data.watch_range }} · {% endif %} + ends in {{ campaign_data.campaign.end_at|timeuntil }}
-
+ +
+
+ {% if campaign_data.benefits %} +
+ {% for benefit in campaign_data.benefits %} + + {% if benefit.image_best_url %} + {{ benefit.name }} + {% endif %} + {{ benefit.name }} + + {% endfor %} +
+ {% endif %} +
{% endfor %} - -
+ {% endfor %} {% else %}

No active Twitch campaigns at the moment.

@@ -249,133 +232,113 @@ {% if kick_campaigns_by_game %} {% for game_id, game_data in kick_campaigns_by_game.items %} -
-
-

- {% if game_data.kick_id %} - {{ game_data.name }} - {% elif game_data.organizations %} - {% for org in game_data.organizations %} - {{ org.name }} - {% if not forloop.last %}, {% endif %} - {% endfor %} - {% else %} - {{ game_data.name }} +
+
+ {% if game_data.image %} + Image for {{ game_data.name }} + {% endif %} +
+

+ {% if game_data.kick_id %} + {{ game_data.name }} + {% elif game_data.organizations %} + {% for org in game_data.organizations %} + {{ org.name }} + {% if not forloop.last %}, {% endif %} + {% endfor %} + {% else %} + {{ game_data.name }} + {% endif %} +

+ {% if game_data.kick_id and game_data.organizations %} +
+ {% for org in game_data.organizations %} + {{ org.name }} + {% if not forloop.last %}, {% endif %} + {% endfor %} +
{% endif %} -

- {% if game_data.kick_id and game_data.organizations %} -
- Organization: - {% for org in game_data.organizations %} - {{ org.name }} - {% if not forloop.last %}, {% endif %} +
+
+
+
+ {% for campaign_data in game_data.campaigns %} + {% with rewards=campaign_data.rewards %} + {% with min_watch=rewards.0.required_units|default:0 max_watch=rewards.last.required_units|default:0 %} +
+
+ + {% if campaign_data.campaign.image_url %} + Image for {{ campaign_data.campaign.name }} + {% endif %} + +
+ +

{{ campaign_data.campaign.name }}

+
+ {% if campaign_data.campaign.organization %} +
{{ campaign_data.campaign.organization.name }}
+ {% endif %} +
+ {% if min_watch %}{% if min_watch == max_watch %}{{ min_watch }}m watch{% else %}{{ min_watch }}m–{{ max_watch }}m watch{% endif %} · {% endif %} + ends in {{ campaign_data.campaign.ends_at|timeuntil }} +
+ +
+
+ {% if rewards %} +
+ {% for reward in rewards %} + + {% if reward.full_image_url %} + {{ reward.name }} + {% endif %} + {{ reward.name }} + {% endfor %}
{% endif %} - -
-
- {% if game_data.image %} - Image for {{ game_data.name }} - {% else %} -
No Image
- {% endif %} -
-
-
- {% for campaign_data in game_data.campaigns %} -
-
- - {% if campaign_data.campaign.image_url %} - Image for {{ campaign_data.campaign.name }} - {% endif %} -

{{ campaign_data.campaign.name }}

-
- - -
- Channels: - -
- {% if campaign_data.rewards %} -
- Rewards: -
    - {% for reward in campaign_data.rewards|slice:":3" %} -
  • {{ reward.name }} ({{ reward.required_units }} min)
  • - {% endfor %} - {% if campaign_data.rewards|length > 3 %} -
  • ... and {{ campaign_data.rewards|length|add:"-3" }} more
  • - {% endif %} -
-
- {% endif %} -
-
- {% endfor %} -
-
-
-
+
+ {% endwith %} + {% endwith %} + {% endfor %} + + + {% endfor %} {% else %}

No active Kick campaigns at the moment.

diff --git a/templates/kick/campaign_detail.html b/templates/kick/campaign_detail.html index 4c3dde3..db3b82d 100644 --- a/templates/kick/campaign_detail.html +++ b/templates/kick/campaign_detail.html @@ -121,7 +121,7 @@
{% if campaign.channels.all %} - Participating channels: + Participating channels: {% for channel in campaign.channels.all|slice:":5" %} {% if channel.user %} {{ channel.user.username }} diff --git a/templates/twitch/campaign_detail.html b/templates/twitch/campaign_detail.html index 94fa6eb..fb7e35b 100644 --- a/templates/twitch/campaign_detail.html +++ b/templates/twitch/campaign_detail.html @@ -192,7 +192,7 @@ {% if allowed_channels %} -
Allowed Channels
+
Allowed Channels
{% for channel in allowed_channels %} {{ channel.display_name }}