fix card badges to have better rarity visibility and other bugfixes
This commit is contained in:
parent
37d8bd5981
commit
262f0ea190
14 changed files with 4515 additions and 1255 deletions
|
|
@ -23,7 +23,7 @@ COPY .env.production /code/.env
|
||||||
ENV HOME=/code
|
ENV HOME=/code
|
||||||
|
|
||||||
# Install NPM & node.js
|
# Install NPM & node.js
|
||||||
RUN apt-get update && apt-get install -y nodejs npm xvfb
|
RUN apt-get update && apt-get install -y nodejs npm xvfb netcat-openbsd
|
||||||
|
|
||||||
RUN playwright install-deps && playwright install
|
RUN playwright install-deps && playwright install
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
from accounts.models import CustomUser, FriendCode
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = "Seed default friend codes for TestUsers after friend codes have been loaded."
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
users_updated = 0
|
|
||||||
for user in CustomUser.objects.all():
|
|
||||||
# Automatically select the earliest friend code added for the user:
|
|
||||||
default_code = FriendCode.objects.filter(user=user).order_by('created_at').first()
|
|
||||||
if default_code:
|
|
||||||
user.default_friend_code = default_code
|
|
||||||
user.save(update_fields=["default_friend_code"])
|
|
||||||
self.stdout.write(f"Set default friend code for user {user.username} to {default_code.friend_code}.")
|
|
||||||
users_updated += 1
|
|
||||||
else:
|
|
||||||
self.stdout.write(f"No friend code found for user {user.username}.")
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"Seeded default friend codes for {users_updated} user(s)."))
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.1.2 on 2025-03-31 22:48
|
# Generated by Django 5.1.2 on 2025-04-07 04:19
|
||||||
|
|
||||||
import accounts.models
|
import accounts.models
|
||||||
import django.contrib.auth.models
|
import django.contrib.auth.models
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.1.2 on 2025-03-31 22:48
|
# Generated by Django 5.1.2 on 2025-04-07 04:19
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
|
||||||
|
|
@ -45,5 +45,7 @@ def update_card_style(sender, instance, action, **kwargs):
|
||||||
else:
|
else:
|
||||||
instance.style = "background: linear-gradient(to right, #AAAAAA, #AAAAAA, #AAAAAA);"
|
instance.style = "background: linear-gradient(to right, #AAAAAA, #AAAAAA, #AAAAAA);"
|
||||||
if not color_is_dark(decks.first().hex_color):
|
if not color_is_dark(decks.first().hex_color):
|
||||||
instance.style += "color: var(--color-gray-700);"
|
instance.style += "color: var(--color-gray-700); text-shadow: 0 0 0 var(--color-gray-700);"
|
||||||
|
else:
|
||||||
|
instance.style += "text-shadow: 0 0 0 #fff;"
|
||||||
instance.save(update_fields=["style"])
|
instance.save(update_fields=["style"])
|
||||||
File diff suppressed because it is too large
Load diff
2248
seed/M4R9zUMD.txt
Normal file
2248
seed/M4R9zUMD.txt
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -104,27 +104,27 @@
|
||||||
<div x-show="activeTab === 'profile'">
|
<div x-show="activeTab === 'profile'">
|
||||||
<div class="card card-border bg-base-100 shadow-lg mx-auto p-6 mb-4">
|
<div class="card card-border bg-base-100 shadow-lg mx-auto p-6 mb-4">
|
||||||
{% with gravatar_profile=request.user.email|gravatar_profile_data %}
|
{% with gravatar_profile=request.user.email|gravatar_profile_data %}
|
||||||
{% if gravatar_profile %}
|
|
||||||
<div class="hovercard-profile mb-4 text-center">
|
<div class="hovercard-profile mb-4 text-center">
|
||||||
<img src="{{ gravatar_profile.thumbnailUrl }}" alt="{{ gravatar_profile.displayName|default:request.user.username }}" class="rounded-full mb-2 mx-auto" />
|
<div class="avatar block mx-auto w-32">
|
||||||
|
<div class="W-32 rounded-full">
|
||||||
|
{{ request.user.email|gravatar:128 }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<a href="https://gravatar.com/profile/avatars" target="_blank" rel="noopener noreferrer" class="btn btn-primary mt-4">
|
<a href="https://gravatar.com/profile/avatars" target="_blank" rel="noopener noreferrer" class="btn btn-primary mt-4">
|
||||||
Edit Avatar on Gravatar
|
Edit Avatar on Gravatar
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
|
||||||
<p class="text-center">{{ _('No Gravatar profile data available.') }}</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<h2 class="text-base font-semibold pt-0">What is Gravatar?</h2>
|
<h2 class="text-base font-semibold pt-0">What is Gravatar?</h2>
|
||||||
<p class="mb-4 text-sm">Gravatar (Globally Recognized Avatar) is a free service that links your email address to a profile picture. Many websites, including this one, use Gravatar to display your preferred avatar automatically.</p>
|
<p class="mb-4 text-sm">Gravatar (Globally Recognized Avatar) is a free service that links your email address to a profile picture. Many websites, including this one, use Gravatar to display your preferred avatar automatically.</p>
|
||||||
|
|
||||||
<h2 class="text-base font-semibold">How does it work?</h2>
|
<h2 class="text-base font-semibold">How does it work?</h2>
|
||||||
<p class="mb-4 text-sm">If you've set up a Gravatar, your profile picture will appear here whenever you use your email on supported sites. If you don't have a Gravatar yet, you'll see a default image instead.</p>
|
<p class="mb-4 text-sm">If you've set up a Gravatar, your profile picture will appear here whenever you use your email on supported sites. If you don't have a Gravatar yet, you'll see a default randomly-generated avatar instead.</p>
|
||||||
|
|
||||||
<h2 class="text-base font-semibold">Is it safe? What about privacy?</h2>
|
<h2 class="text-base font-semibold">Is it safe? What about privacy?</h2>
|
||||||
<p class="mb-4 text-sm">Gravatar is completely optional, opt-in, and prioritizes your security and privacy. Your email is never visible to anyone and only a hashed version is shown on the page and sent to Gravatar, protecting your identity while ensuring that your email address is not exposed to bots or scrapers.</p>
|
<p class="mb-4 text-sm">Gravatar is completely optional, opt-in, and prioritizes your security and privacy. Your email is never visible to anyone and only a hashed version is shown on the page and sent to Gravatar, protecting your identity while ensuring that your email address is not exposed to bots or scrapers.</p>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static card_badge %}
|
{% load static card_badge %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container mx-auto p-4"
|
<div class="container mx-auto"
|
||||||
x-data="{
|
x-data="{
|
||||||
order: '{{ order }}',
|
order: '{{ order }}',
|
||||||
groupBy: '{{ group_by|default:'none' }}',
|
groupBy: '{{ group_by|default:'none' }}',
|
||||||
|
|
@ -15,10 +15,7 @@
|
||||||
.then(html => { this.$refs.cardList.innerHTML = html; });
|
.then(html => { this.$refs.cardList.innerHTML = html; });
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
x-init="loadCards()"
|
x-on:change-page.window="page = $event.detail.page; loadCards()">
|
||||||
x-on:change-page.window="page = $event.detail.page; loadCards()"
|
|
||||||
>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center justify-between mb-6">
|
<div class="flex flex-wrap items-center justify-between mb-6">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold">Cards</h1>
|
<h1 class="text-2xl font-bold">Cards</h1>
|
||||||
|
|
@ -57,7 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- Container for the partial card list -->
|
<!-- Container for the partial card list -->
|
||||||
<div x-ref="cardList">
|
<div x-ref="cardList">
|
||||||
|
{% include "cards/_card_list.html" with cards=cards page_obj=page_obj %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
For a TradeOffer, we use {% render_trade_offer %}; for a TradeAcceptance, {% render_trade_acceptance %}.
|
For a TradeOffer, we use {% render_trade_offer %}; for a TradeAcceptance, {% render_trade_acceptance %}.
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
<div class="flex flex-row gap-2 flex-wrap justify-center items-start">
|
<div class="flex flex-row gap-6 md:gap-2 lg:gap-6 flex-wrap justify-center items-start py-6">
|
||||||
{% for offer in offers %}
|
{% for offer in offers %}
|
||||||
<div class="flex flex-none">
|
<div class="flex flex-none">
|
||||||
{% if offer.accepted_by %}
|
{% if offer.accepted_by %}
|
||||||
|
|
|
||||||
|
|
@ -5,63 +5,41 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div x-data="{
|
<div x-data="{
|
||||||
// Initialize allExpanded from the URL if present, defaulting to false.
|
|
||||||
allExpanded: new URLSearchParams(window.location.search).get('expanded') === 'true',
|
|
||||||
page: {{ page_obj.number|default:1 }},
|
page: {{ page_obj.number|default:1 }},
|
||||||
loadOffers() {
|
loadOffers() {
|
||||||
let url = new URL('{% url 'trade_offer_list' %}', window.location.origin);
|
let url = new URL('{% url 'trade_offer_list' %}', window.location.origin);
|
||||||
let params = new URLSearchParams(window.location.search);
|
let params = new URLSearchParams(window.location.search);
|
||||||
params.set('page', this.page);
|
params.set('page', this.page);
|
||||||
// Include the expanded state if active
|
|
||||||
if (this.allExpanded) {
|
|
||||||
params.set('expanded', 'true');
|
|
||||||
} else {
|
|
||||||
params.delete('expanded');
|
|
||||||
}
|
|
||||||
url.search = params.toString();
|
url.search = params.toString();
|
||||||
fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' }})
|
fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' }})
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(html => { this.$refs.offersContainer.innerHTML = html; });
|
.then(html => { this.$refs.offersContainer.innerHTML = html; });
|
||||||
},
|
|
||||||
// Update the URL so that navigation preserves our expanded state.
|
|
||||||
updateUrl() {
|
|
||||||
let params = new URLSearchParams(window.location.search);
|
|
||||||
if (this.allExpanded) {
|
|
||||||
params.set('expanded', 'true');
|
|
||||||
} else {
|
|
||||||
params.delete('expanded');
|
|
||||||
}
|
|
||||||
history.replaceState(null, '', window.location.pathname + '?' + params.toString());
|
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
x-init="loadOffers()"
|
|
||||||
x-on:change-page.window="page = $event.detail.page; loadOffers()">
|
x-on:change-page.window="page = $event.detail.page; loadOffers()">
|
||||||
|
|
||||||
<!-- Header with the toggle -->
|
<!-- Header without Expand All -->
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h1 class="text-2xl font-bold">Trade Offers</h1>
|
<h1 class="text-2xl font-bold">Trade Offers</h1>
|
||||||
<div>
|
<div class="flex items-center gap-4">
|
||||||
<form method="get" class="flex items-center gap-4" x-data>
|
<form method="get" class="flex items-center gap-4" x-data>
|
||||||
<label class="cursor-pointer flex items-center gap-2">
|
|
||||||
<span x-text="allExpanded ? 'Collapse All' : 'Expand All'"></span>
|
|
||||||
<input type="checkbox" name="all_expanded" value="true" class="toggle toggle-primary"
|
|
||||||
@click="allExpanded = !allExpanded; $dispatch('toggle-all', { expanded: allExpanded }); updateUrl();">
|
|
||||||
</label>
|
|
||||||
<label class="cursor-pointer flex items-center gap-2">
|
<label class="cursor-pointer flex items-center gap-2">
|
||||||
<span>Only Closed</span>
|
<span>Only Closed</span>
|
||||||
<input type="checkbox" name="show_closed" value="true" class="toggle toggle-primary" @change="$el.form.submit()" {% if show_closed %}checked{% endif %}>
|
<input type="checkbox" name="show_closed" value="true" class="toggle toggle-primary"
|
||||||
|
@change="$el.form.submit()" {% if show_closed %}checked{% endif %}>
|
||||||
</label>
|
</label>
|
||||||
<button type="submit" class="btn btn-primary" x-show="false">Apply</button>
|
<button type="submit" class="btn btn-primary" x-show="false">Apply</button>
|
||||||
</form>
|
</form>
|
||||||
|
<div>
|
||||||
|
<a href="{% url 'trade_offer_create' %}" class="btn btn-success">Create New Offer</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Trade Offers -->
|
<!-- Trade Offers -->
|
||||||
<section class="mb-12">
|
<section class="mb-12">
|
||||||
<div class="flex justify-end mb-4">
|
|
||||||
<a href="{% url 'trade_offer_create' %}" class="btn btn-success">Create New Offer</a>
|
|
||||||
</div>
|
|
||||||
<div id="all-trade-offers" x-ref="offersContainer">
|
<div id="all-trade-offers" x-ref="offersContainer">
|
||||||
{% include "trades/_trade_offer_list.html" with offers=offers page_obj=page_obj expanded=expanded %}
|
{% include "trades/_trade_offer_list.html" with offers=offers page_obj=page_obj %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<div class="relative block m-1">
|
<div class="relative block m-1">
|
||||||
<div class="grid grid-rows-2 grid-cols-4 h-[52px] p-1.5 w-40 text-white shadow-lg" style="{{ style }}">
|
<div class="grid grid-rows-2 grid-cols-4 h-[52px] p-1.5 w-40 text-white shadow-lg" style="{{ style }}">
|
||||||
<div class="row-start-1 col-start-1 col-span-3 truncate text-ellipsis self-start font-semibold leading-tight text-sm">{{ name }}</div>
|
<div class="row-start-1 col-start-1 col-span-3 truncate text-ellipsis self-start font-semibold leading-tight text-sm">{{ name }}</div>
|
||||||
<div class="row-start-2 col-start-1 col-span-3 truncate self-end text-xs">{{ rarity }}</div>
|
<div class="row-start-2 col-start-1 col-span-3 truncate self-end text-xs text-transparent">{{ rarity }}</div>
|
||||||
<div class="row-start-2 col-start-4 col-span-1 self-end text-right truncate font-semibold leading-tight text-sm">{{ cardset }}</div>
|
<div class="row-start-2 col-start-4 col-span-1 self-end text-right truncate font-semibold leading-tight text-sm">{{ cardset }}</div>
|
||||||
{% if quantity != None %}<div class="row-start-1 col-start-4 self-start justify-self-end -me-0.5 relative w-fit ps-1"><div class="card-quantity-badge relative bg-gray-600 text-white text-sm font-semibold rounded-full text-center size-max px-1.5">{{ quantity }}</div></div>{% endif %}
|
{% if quantity != None %}<div class="row-start-1 col-start-4 self-start justify-self-end -me-0.5 relative w-fit ps-1"><div class="card-quantity-badge relative bg-gray-600 text-white text-sm font-semibold rounded-full text-center size-max px-1.5">{{ quantity }}</div></div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
{% load gravatar card_badge cache %}
|
{% load gravatar card_badge cache %}
|
||||||
|
|
||||||
{% cache 60 trade_offer offer_pk %}
|
{% cache 60 trade_offer offer_pk %}
|
||||||
<div x-data="{ flipped: {{flipped|lower}}, offerExpanded: true, acceptanceExpanded: false }" x-ref="tradeOffer" class="transition-all duration-500 trade-offer-card my-auto h-full w-auto justify-center">
|
<div x-data="{ flipped: {{flipped|lower}}, offerExpanded: {{flipped|yesno:'false,true'}}, acceptanceExpanded: {{flipped|lower}} }" x-ref="tradeOffer" class="transition-all duration-500 trade-offer-card my-auto h-full w-auto justify-center">
|
||||||
<div class="flip-container">
|
<div class="flip-container">
|
||||||
<div{% if not on_detail_page %} @click="window.location.href = '{% url 'trade_offer_detail' pk=offer_pk %}'"{% endif %} class="flip-inner grid grid-cols-1 grid-rows-1 card bg-base-100 card-border shadow-lg w-90 transform transition-transform duration-500 ease-in-out{% if not on_detail_page %} cursor-pointer{% endif %}"
|
<div{% if not on_detail_page %} @click="window.location.href = '{% url 'trade_offer_detail' pk=offer_pk %}'"{% endif %} class="flip-inner grid grid-cols-1 grid-rows-1 card bg-base-100 card-border shadow-lg w-90 transform transition-transform duration-500 ease-in-out{% if not on_detail_page %} cursor-pointer{% endif %}{%if flipped %} rotate-y-180{% endif %}"
|
||||||
:class="{'rotate-y-180': flipped}">
|
:class="{'rotate-y-180': flipped}">
|
||||||
<div class="flip-face front col-start-1 row-start-1 grid grid-cols-1 auto-rows-min gap-2 content-between">
|
<div class="flip-face front rotate-y-0 col-start-1 row-start-1 grid grid-cols-1 auto-rows-min gap-2 content-between">
|
||||||
<div class="flip-face-header self-start">
|
<div class="flip-face-header self-start">
|
||||||
<div class="relative mt-6 mb-4 mx-2 sm:mx-4">
|
<div class="relative mt-6 mb-4 mx-2 sm:mx-4">
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flip-face back col-start-1 row-start-1 grid grid-cols-1 auto-rows-min gap-2 content-between" style="transform: rotateY(180deg);">
|
<div class="flip-face back col-start-1 row-start-1 grid grid-cols-1 auto-rows-min gap-2 content-between rotate-y-180">
|
||||||
<div class="flip-face-header self-start">
|
<div class="flip-face-header self-start">
|
||||||
<div class="relative mt-6 mb-4 mx-2 sm:mx-4">
|
<div class="relative mt-6 mb-4 mx-2 sm:mx-4">
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
|
@ -144,7 +144,7 @@
|
||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
-webkit-backface-visibility: hidden;
|
-webkit-backface-visibility: hidden;
|
||||||
}
|
}
|
||||||
.flip-face.front {
|
.rotate-y-0 {
|
||||||
transform: rotateY(0);
|
transform: rotateY(0);
|
||||||
}
|
}
|
||||||
.rotate-y-180 {
|
.rotate-y-180 {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.1.2 on 2025-03-31 22:48
|
# Generated by Django 5.1.2 on 2025-04-07 04:19
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue