Enable caching via DB and update cache timeouts
This commit is contained in:
parent
9ce5d525b3
commit
27c7238a82
11 changed files with 281 additions and 283 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.1.2 on 2025-03-15 22:05
|
# Generated by Django 5.1.2 on 2025-03-16 04:58
|
||||||
|
|
||||||
import django.contrib.auth.models
|
import django.contrib.auth.models
|
||||||
import django.contrib.auth.validators
|
import django.contrib.auth.validators
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.1.2 on 2025-03-15 22:05
|
# Generated by Django 5.1.2 on 2025-03-16 04:58
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from cards.models import Card
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@register.inclusion_tag('templatetags/card_multiselect.html')
|
@register.inclusion_tag('templatetags/card_multiselect.html')
|
||||||
def card_multiselect(field_name, label, placeholder, cards=None, selected_values=None, cache_timeout=86400, cache_key="cards_multiselect"):
|
def card_multiselect(field_name, label, placeholder, cards=None, selected_values=None, cache_timeout=86400):
|
||||||
"""
|
"""
|
||||||
Renders a multiselect field for choosing cards while supporting quantity data.
|
Renders a multiselect field for choosing cards while supporting quantity data.
|
||||||
|
|
||||||
|
|
@ -50,6 +50,5 @@ def card_multiselect(field_name, label, placeholder, cards=None, selected_values
|
||||||
'cards': cards,
|
'cards': cards,
|
||||||
'placeholder': placeholder,
|
'placeholder': placeholder,
|
||||||
'selected_values': list(selected_cards.keys()),
|
'selected_values': list(selected_cards.keys()),
|
||||||
'cache_timeout': cache_timeout,
|
'cache_timeout': cache_timeout
|
||||||
'cache_key': cache_key,
|
|
||||||
}
|
}
|
||||||
|
|
@ -286,7 +286,7 @@ if DEBUG:
|
||||||
else:
|
else:
|
||||||
CACHES = {
|
CACHES = {
|
||||||
"default": {
|
"default": {
|
||||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
|
||||||
"LOCATION": "unique-snowflake",
|
"LOCATION": "site_cache",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,7 +10,6 @@ from django.views.decorators.cache import cache_page
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
@method_decorator(cache_page(60), name='get')
|
|
||||||
class HomePageView(TemplateView):
|
class HomePageView(TemplateView):
|
||||||
template_name = "home/home.html"
|
template_name = "home/home.html"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,5 +22,8 @@ uv run python manage.py migrate
|
||||||
echo "Loading seed data..."
|
echo "Loading seed data..."
|
||||||
uv run python manage.py loaddata seed/0*
|
uv run python manage.py loaddata seed/0*
|
||||||
|
|
||||||
|
echo "Creating cache table..."
|
||||||
|
uv run python manage.py createcachetable
|
||||||
|
|
||||||
echo "Seeding default friend codes..."
|
echo "Seeding default friend codes..."
|
||||||
uv run python manage.py seed_default_friend_codes
|
uv run python manage.py seed_default_friend_codes
|
||||||
|
|
@ -85,9 +85,9 @@
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<!-- Featured Offers -->
|
<!-- Featured Offers -->
|
||||||
<div>
|
<div>
|
||||||
{% cache 86400 featured_offers %}
|
{% cache 60 featured_offers %}
|
||||||
<div class="p-4">
|
<div class="p-4 text-center ">
|
||||||
<h5 class="text-xl text-center font-semibold whitespace-nowrap truncate mb-0">Featured Offers</h5>
|
<h5 class="text-xl font-semibold whitespace-nowrap truncate mb-0">Featured Offers</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<!-- Tab contents -->
|
<!-- Tab contents -->
|
||||||
|
|
@ -139,7 +139,7 @@
|
||||||
<!-- Recent Offers -->
|
<!-- Recent Offers -->
|
||||||
<div>
|
<div>
|
||||||
{% cache 60 recent_offers %}
|
{% cache 60 recent_offers %}
|
||||||
<div class="text-center text-base-content p-4">
|
<div class="text-center p-4">
|
||||||
<h5 class="text-xl font-semibold whitespace-nowrap truncate mb-0">Recent Offers</h5>
|
<h5 class="text-xl font-semibold whitespace-nowrap truncate mb-0">Recent Offers</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<span class="label-text">{{ label }}</span>
|
<span class="label-text">{{ label }}</span>
|
||||||
</label>
|
</label>
|
||||||
<select name="{{ field_name }}" id="{{ field_id }}" class="select select-bordered w-full card-multiselect" data-placeholder="{{ placeholder }}" multiple>
|
<select name="{{ field_name }}" id="{{ field_id }}" class="select select-bordered w-full card-multiselect" data-placeholder="{{ placeholder }}" multiple>
|
||||||
{% cache cache_timeout cache_key selected_values|join:"," %}
|
{% cache cache_timeout card_multiselect selected_values|join:"," %}
|
||||||
<option value="" disabled>{{ placeholder }}</option>
|
<option value="" disabled>{{ placeholder }}</option>
|
||||||
{% for card in cards %}
|
{% for card in cards %}
|
||||||
<option
|
<option
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,53 @@
|
||||||
{% load gravatar card_badge %}
|
{% load gravatar card_badge cache %}
|
||||||
|
|
||||||
<div class="card card-border bg-base-100 shadow-lg w-96 md:w-80 lg:w-96">
|
{% cache 60 trade_acceptance acceptance.pk %}
|
||||||
<!-- Header -->
|
<div class="card card-border bg-base-100 shadow-lg w-96 md:w-80 lg:w-96">
|
||||||
<div class="py-4 mx-2 sm:mx-4">
|
<!-- Header -->
|
||||||
<div class="flex justify-between items-center">
|
<div class="py-4 mx-2 sm:mx-4">
|
||||||
<!-- Left: Initiator's avatar (moved from center) and "Has" -->
|
<div class="flex justify-between items-center">
|
||||||
<div class="flex items-center">
|
<!-- Left: Initiator's avatar (moved from center) and "Has" -->
|
||||||
<div class="avatar mr-2">
|
<div class="flex items-center">
|
||||||
<div class="w-10 rounded-full">
|
<div class="avatar mr-2">
|
||||||
{{ acceptance.trade_offer.initiated_by.user.email|gravatar:40 }}
|
<div class="w-10 rounded-full">
|
||||||
|
{{ acceptance.trade_offer.initiated_by.user.email|gravatar:40 }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="text-sm font-semibold">Has</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm font-semibold">Has</span>
|
<!-- Right: "Wants" with the acceptor's avatar -->
|
||||||
</div>
|
<div class="flex items-center">
|
||||||
<!-- Right: "Wants" with the acceptor's avatar -->
|
<span class="text-sm font-semibold mr-2">Wants</span>
|
||||||
<div class="flex items-center">
|
<div class="avatar">
|
||||||
<span class="text-sm font-semibold mr-2">Wants</span>
|
<div class="w-10 rounded-full">
|
||||||
<div class="avatar">
|
{{ acceptance.accepted_by.user.email|gravatar:40 }}
|
||||||
<div class="w-10 rounded-full">
|
</div>
|
||||||
{{ acceptance.accepted_by.user.email|gravatar:40 }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<!-- Main Card Row: Single row with the acceptance's cards -->
|
||||||
<!-- Main Card Row: Single row with the acceptance's cards -->
|
<a href="{% url 'trade_acceptance_update' pk=acceptance.pk %}" class="no-underline block">
|
||||||
<a href="{% url 'trade_acceptance_update' pk=acceptance.pk %}" class="no-underline block">
|
<div class="px-2 pb-0">
|
||||||
<div class="px-2 pb-0">
|
<div class="grid grid-cols-2 items-center border-t border-gray-300">
|
||||||
<div class="grid grid-cols-2 items-center border-t border-gray-300">
|
<div class="flex flex-col items-center">
|
||||||
<div class="flex flex-col items-center">
|
{% card_badge acceptance.requested_card %}
|
||||||
{% card_badge acceptance.requested_card %}
|
</div>
|
||||||
</div>
|
<div class="flex flex-col items-center">
|
||||||
<div class="flex flex-col items-center">
|
{% card_badge acceptance.offered_card %}
|
||||||
{% card_badge acceptance.offered_card %}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Footer: Only info button with acceptance hash -->
|
||||||
|
<div class="flex justify-end px-2 pb-2">
|
||||||
|
<div class="text-gray-500 text-sm tooltip tooltip-left" data-tip="Acceptance ID: {{ acceptance.hash }}">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M11.25 11.25l.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0zm-9-3.75h.008v.008H12V8.25z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</div>
|
||||||
|
{% endcache %}
|
||||||
<!-- Footer: Only info button with acceptance hash -->
|
|
||||||
<div class="flex justify-end px-2 pb-2">
|
|
||||||
<div class="text-gray-500 text-sm tooltip tooltip-left" data-tip="Acceptance ID: {{ acceptance.hash }}">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="M11.25 11.25l.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0zm-9-3.75h.008v.008H12V8.25z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,46 +1,135 @@
|
||||||
{% load gravatar card_badge %}
|
{% load gravatar card_badge cache %}
|
||||||
|
|
||||||
<!--
|
{% cache 60 trade_offer offer.pk %}
|
||||||
Define the Alpine component only once.
|
<script>
|
||||||
-->
|
if (!window.tradeOfferCard) {
|
||||||
<script>
|
window.tradeOfferCard = function() {
|
||||||
if (!window.tradeOfferCard) {
|
return {
|
||||||
window.tradeOfferCard = function() {
|
flipped: false,
|
||||||
return {
|
badgeExpanded: false,
|
||||||
flipped: false,
|
acceptanceExpanded: false,
|
||||||
badgeExpanded: false,
|
// Helper method to set the badgeExpanded state
|
||||||
acceptanceExpanded: false,
|
setBadge(expanded) {
|
||||||
// Helper method to set the badgeExpanded state
|
this.badgeExpanded = expanded;
|
||||||
setBadge(expanded) {
|
},
|
||||||
this.badgeExpanded = expanded;
|
};
|
||||||
},
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
</script>
|
||||||
</script>
|
|
||||||
|
|
||||||
<!--
|
<div x-data="tradeOfferCard()" class="transition-all duration-500 trade-offer-card"
|
||||||
The outer div now only establishes Alpine's data context.
|
@toggle-all.window="setBadge($event.detail.expanded)">
|
||||||
The dynamic height adjustment (x-init & x-effect with x-ref) has been removed.
|
|
||||||
-->
|
|
||||||
<div x-data="tradeOfferCard()" class="transition-all duration-500 trade-offer-card"
|
|
||||||
@toggle-all.window="setBadge($event.detail.expanded)">
|
|
||||||
|
|
||||||
<!-- Flip container providing perspective -->
|
<!-- Flip container providing perspective -->
|
||||||
<div class="flip-container" style="perspective: 1000px;">
|
<div class="flip-container" style="perspective: 1000px;">
|
||||||
<!--
|
<!--
|
||||||
The rotating element (.flip-inner) now uses CSS Grid to stack its children in a single cell.
|
The rotating element (.flip-inner) now uses CSS Grid to stack its children in a single cell.
|
||||||
Persistent border, shadow, and rounding are applied here and the card rotates entirely.
|
Persistent border, shadow, and rounding are applied here and the card rotates entirely.
|
||||||
-->
|
-->
|
||||||
<div class="flip-inner grid grid-cols-1 grid-rows-1 card bg-base-100 card-border shadow-lg w-96 md:w-80 lg:w-96 transform transition-transform duration-700 ease-in-out"
|
<div class="flip-inner grid grid-cols-1 grid-rows-1 card bg-base-100 card-border shadow-lg w-96 md:w-80 lg:w-96 transform transition-transform duration-700 ease-in-out"
|
||||||
:class="{'rotate-y-180': flipped}">
|
:class="{'rotate-y-180': flipped}">
|
||||||
|
|
||||||
<!-- Front Face: Trade Offer -->
|
<!-- Front Face: Trade Offer -->
|
||||||
<!-- Using grid placement classes (col-start-1 row-start-1) ensures both faces overlap -->
|
<!-- Using grid placement classes (col-start-1 row-start-1) ensures both faces overlap -->
|
||||||
<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 col-start-1 row-start-1 grid grid-cols-1 auto-rows-min gap-2 content-between">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
|
<div class="self-start">
|
||||||
|
<a href="{% url 'trade_offer_detail' pk=offer.pk %}" class="no-underline block">
|
||||||
|
<div class="py-4 mx-2 sm:mx-4">
|
||||||
|
<div class="grid grid-cols-3 items-center">
|
||||||
|
<div class="flex justify-center items-center">
|
||||||
|
<span class="text-sm font-semibold">Has</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center items-center">
|
||||||
|
<div class="avatar">
|
||||||
|
<div class="w-10 rounded-full">
|
||||||
|
{{ offer.initiated_by.user.email|gravatar:40 }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center items-center">
|
||||||
|
<span class="text-sm font-semibold">Wants</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<!-- Main Trade Offer Row -->
|
||||||
|
<div class="self-start">
|
||||||
|
<a href="{% url 'trade_offer_detail' pk=offer.pk %}" class="no-underline block">
|
||||||
|
<div class="px-2 pb-0">
|
||||||
|
<div class="grid grid-cols-2 gap-2 items-center">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
{% if have_cards_available %}
|
||||||
|
{% with first_have=have_cards_available.0 %}
|
||||||
|
{% card_badge first_have.card first_have.quantity %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
{% if want_cards_available %}
|
||||||
|
{% with first_want=want_cards_available.0 %}
|
||||||
|
{% card_badge first_want.card first_want.quantity %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Extra Card Badges (Collapsible) -->
|
||||||
|
<div x-show="badgeExpanded" x-collapse.duration.500ms class="px-2">
|
||||||
|
<div class="grid grid-cols-2 gap-2">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
{% for th in have_cards_available|slice:"1:" %}
|
||||||
|
{% card_badge th.card th.quantity %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
{% for th in want_cards_available|slice:"1:" %}
|
||||||
|
{% card_badge th.card th.quantity %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="flex justify-center my-1 h-5">
|
||||||
|
{% if have_cards_available|length > 1 or want_cards_available|length > 1 %}
|
||||||
|
<svg @click="badgeExpanded = !badgeExpanded"
|
||||||
|
x-bind:class="{ 'rotate-180': badgeExpanded }"
|
||||||
|
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="self-end">
|
||||||
|
<div class="flex justify-between px-2 pb-2">
|
||||||
|
<div class="text-gray-500 text-sm tooltip tooltip-right" data-tip="ID: {{ offer.hash }}">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
|
stroke="currentColor" class="size-5">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<!-- Front-to-back flip button -->
|
||||||
|
<div class="cursor-pointer text-gray-500"
|
||||||
|
@click="if(badgeExpanded){ badgeExpanded = false; setTimeout(() => { flipped = true; }, 500); } else { flipped = true; }">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3 8.689c0-.864.933-1.406 1.683-.977l7.108 4.061a1.125 1.125 0 0 1 0 1.954l-7.108 4.061A1.125 1.125 0 0 1 3 16.811V8.69ZM12.75 8.689c0-.864.933-1.406 1.683-.977l7.108 4.061a1.125 1.125 0 0 1 0 1.954l-7.108 4.061a1.125 1.125 0 0 1-1.683-.977V8.69Z" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Back Face: Acceptances View -->
|
||||||
|
<!-- Placed in the same grid cell as the front face -->
|
||||||
|
<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="self-start">
|
<div class="self-start">
|
||||||
<a href="{% url 'trade_offer_detail' pk=offer.pk %}" class="no-underline block">
|
<a href="{% url 'trade_offer_detail' pk=offer.pk %}" class="no-underline">
|
||||||
<div class="py-4 mx-2 sm:mx-4">
|
<div class="py-4 mx-2 sm:mx-4">
|
||||||
<div class="grid grid-cols-3 items-center">
|
<div class="grid grid-cols-3 items-center">
|
||||||
<div class="flex justify-center items-center">
|
<div class="flex justify-center items-center">
|
||||||
|
|
@ -60,215 +149,121 @@ if (!window.tradeOfferCard) {
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<!-- Main Trade Offer Row -->
|
|
||||||
<div class="self-start">
|
<div class="self-start">
|
||||||
<a href="{% url 'trade_offer_detail' pk=offer.pk %}" class="no-underline block">
|
<div class="px-2 pb-0">
|
||||||
<div class="px-2 pb-0">
|
<div class="overflow-hidden">
|
||||||
<div class="grid grid-cols-2 gap-2 items-center">
|
{% if offer.acceptances.first %}
|
||||||
<div class="flex flex-col items-center">
|
<div class="space-y-3">
|
||||||
{% if have_cards_available %}
|
{% with acceptance=offer.acceptances.first %}
|
||||||
{% with first_have=have_cards_available.0 %}
|
<a href="{% url 'trade_acceptance_update' pk=acceptance.pk %}" class="no-underline"
|
||||||
{% card_badge first_have.card first_have.quantity %}
|
data-tooltip-html='<div class="flex items-center space-x-2">
|
||||||
{% endwith %}
|
<div class="avatar">
|
||||||
{% endif %}
|
<div class="w-10 rounded-full">
|
||||||
|
{{ acceptance.accepted_by.user.email|gravatar:"40" }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-sm">Accepted by: {{ acceptance.accepted_by.user.username }}</span>
|
||||||
|
<span class="text-sm">State: {{ acceptance.state }}</span>
|
||||||
|
<span class="text-sm">Acceptance ID: {{ acceptance.hash }}</span>
|
||||||
|
</div>
|
||||||
|
</div>'>
|
||||||
|
<div class="grid grid-cols-2 gap-4 items-center">
|
||||||
|
<div>
|
||||||
|
{% card_badge acceptance.requested_card %}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{% card_badge acceptance.offered_card %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center">
|
{% endif %}
|
||||||
{% if want_cards_available %}
|
|
||||||
{% with first_want=want_cards_available.0 %}
|
|
||||||
{% card_badge first_want.card first_want.quantity %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div x-show="acceptanceExpanded" x-collapse.duration.500ms class="space-y-3">
|
||||||
<!-- Extra Card Badges (Collapsible) -->
|
{% for acceptance in offer.acceptances.all|slice:"1:" %}
|
||||||
<div x-show="badgeExpanded" x-collapse.duration.500ms class="px-2">
|
<a href="{% url 'trade_acceptance_update' pk=acceptance.pk %}" class="no-underline"
|
||||||
<div class="grid grid-cols-2 gap-2">
|
data-tooltip-html='<div class="flex items-center space-x-2">
|
||||||
<div class="flex flex-col items-center">
|
<div class="avatar">
|
||||||
{% for th in have_cards_available|slice:"1:" %}
|
<div class="w-10 rounded-full">
|
||||||
{% card_badge th.card th.quantity %}
|
{{ acceptance.accepted_by.user.email|gravatar:"40" }}
|
||||||
{% endfor %}
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-sm">Accepted by: {{ acceptance.accepted_by.user.username }}</span>
|
||||||
|
<span class="text-sm">State: {{ acceptance.state }}</span>
|
||||||
|
<span class="text-sm">Acceptance ID: {{ acceptance.hash }}</span>
|
||||||
|
</div>
|
||||||
|
</div>'>
|
||||||
|
<div class="grid grid-cols-2 gap-4 items-center">
|
||||||
|
<div>
|
||||||
|
{% card_badge acceptance.requested_card %}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{% card_badge acceptance.offered_card %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center">
|
</a>
|
||||||
{% for th in want_cards_available|slice:"1:" %}
|
{% endfor %}
|
||||||
{% card_badge th.card th.quantity %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</div>
|
||||||
<div class="flex justify-center my-1 h-5">
|
<div class="flex justify-center my-1 h-5">
|
||||||
{% if have_cards_available|length > 1 or want_cards_available|length > 1 %}
|
{% if offer.acceptances.all|length > 1 %}
|
||||||
<svg @click="badgeExpanded = !badgeExpanded"
|
<svg @click="acceptanceExpanded = !acceptanceExpanded"
|
||||||
x-bind:class="{ 'rotate-180': badgeExpanded }"
|
x-bind:class="{ 'rotate-180': acceptanceExpanded }"
|
||||||
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
||||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
d="M19 9l-7 7-7-7" />
|
d="M19 9l-7 7-7-7" />
|
||||||
</svg>
|
</svg>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="self-end">
|
<div class="flex justify-between px-2 pb-2 self-end">
|
||||||
<div class="flex justify-between px-2 pb-2">
|
<!-- Back-to-front flip button -->
|
||||||
<div class="text-gray-500 text-sm tooltip tooltip-right" data-tip="ID: {{ offer.hash }}">
|
<div class="text-gray-500 cursor-pointer"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
@click="if(acceptanceExpanded){ acceptanceExpanded = false; setTimeout(() => { flipped = false; }, 500); } else { flipped = false; }">
|
||||||
stroke="currentColor" class="size-5">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21 16.811c0 .864-.933 1.406-1.683.977l-7.108-4.061a1.125 1.125 0 0 1 0-1.954l7.108-4.061A1.125 1.125 0 0 1 21 8.689v8.122ZM11.25 16.811c0 .864-.933 1.406-1.683.977l-7.108-4.061a1.125 1.125 0 0 1 0-1.954l7.108-4.061a1.125 1.125 0 0 1 1.683.977v8.122Z" />
|
||||||
d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
|
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
|
||||||
<!-- Front-to-back flip button -->
|
|
||||||
<div class="cursor-pointer text-gray-500"
|
|
||||||
@click="if(badgeExpanded){ badgeExpanded = false; setTimeout(() => { flipped = true; }, 500); } else { flipped = true; }">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 8.689c0-.864.933-1.406 1.683-.977l7.108 4.061a1.125 1.125 0 0 1 0 1.954l-7.108 4.061A1.125 1.125 0 0 1 3 16.811V8.69ZM12.75 8.689c0-.864.933-1.406 1.683-.977l7.108 4.061a1.125 1.125 0 0 1 0 1.954l-7.108 4.061a1.125 1.125 0 0 1-1.683-.977V8.69Z" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="px-1 text-center">
|
||||||
</div>
|
<span class="text-sm font-semibold">
|
||||||
|
Acceptances ({{ offer.acceptances.all|length }})
|
||||||
<!-- Back Face: Acceptances View -->
|
</span>
|
||||||
<!-- Placed in the same grid cell as the front face -->
|
|
||||||
<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="self-start">
|
|
||||||
<a href="{% url 'trade_offer_detail' pk=offer.pk %}" class="no-underline">
|
|
||||||
<div class="py-4 mx-2 sm:mx-4">
|
|
||||||
<div class="grid grid-cols-3 items-center">
|
|
||||||
<div class="flex justify-center items-center">
|
|
||||||
<span class="text-sm font-semibold">Has</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center items-center">
|
|
||||||
<div class="avatar">
|
|
||||||
<div class="w-10 rounded-full">
|
|
||||||
{{ offer.initiated_by.user.email|gravatar:40 }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center items-center">
|
|
||||||
<span class="text-sm font-semibold">Wants</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
<div class="text-gray-500 text-sm tooltip tooltip-left" data-tip="ID: {{ offer.hash }}">
|
||||||
</div>
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
<div class="self-start">
|
stroke="currentColor" class="size-5">
|
||||||
<div class="px-2 pb-0">
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
<div class="overflow-hidden">
|
d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
|
||||||
{% if offer.acceptances.first %}
|
|
||||||
<div class="space-y-3">
|
|
||||||
{% with acceptance=offer.acceptances.first %}
|
|
||||||
<a href="{% url 'trade_acceptance_update' pk=acceptance.pk %}" class="no-underline"
|
|
||||||
data-tooltip-html='<div class="flex items-center space-x-2">
|
|
||||||
<div class="avatar">
|
|
||||||
<div class="w-10 rounded-full">
|
|
||||||
{{ acceptance.accepted_by.user.email|gravatar:"40" }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="text-sm">Accepted by: {{ acceptance.accepted_by.user.username }}</span>
|
|
||||||
<span class="text-sm">State: {{ acceptance.state }}</span>
|
|
||||||
<span class="text-sm">Acceptance ID: {{ acceptance.hash }}</span>
|
|
||||||
</div>
|
|
||||||
</div>'>
|
|
||||||
<div class="grid grid-cols-2 gap-4 items-center">
|
|
||||||
<div>
|
|
||||||
{% card_badge acceptance.requested_card %}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{% card_badge acceptance.offered_card %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{% endwith %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div x-show="acceptanceExpanded" x-collapse.duration.500ms class="space-y-3">
|
|
||||||
{% for acceptance in offer.acceptances.all|slice:"1:" %}
|
|
||||||
<a href="{% url 'trade_acceptance_update' pk=acceptance.pk %}" class="no-underline"
|
|
||||||
data-tooltip-html='<div class="flex items-center space-x-2">
|
|
||||||
<div class="avatar">
|
|
||||||
<div class="w-10 rounded-full">
|
|
||||||
{{ acceptance.accepted_by.user.email|gravatar:"40" }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="text-sm">Accepted by: {{ acceptance.accepted_by.user.username }}</span>
|
|
||||||
<span class="text-sm">State: {{ acceptance.state }}</span>
|
|
||||||
<span class="text-sm">Acceptance ID: {{ acceptance.hash }}</span>
|
|
||||||
</div>
|
|
||||||
</div>'>
|
|
||||||
<div class="grid grid-cols-2 gap-4 items-center">
|
|
||||||
<div>
|
|
||||||
{% card_badge acceptance.requested_card %}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{% card_badge acceptance.offered_card %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center my-1 h-5">
|
|
||||||
{% if offer.acceptances.all|length > 1 %}
|
|
||||||
<svg @click="acceptanceExpanded = !acceptanceExpanded"
|
|
||||||
x-bind:class="{ 'rotate-180': acceptanceExpanded }"
|
|
||||||
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
|
||||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M19 9l-7 7-7-7" />
|
|
||||||
</svg>
|
</svg>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="flex justify-between px-2 pb-2 self-end">
|
|
||||||
<!-- Back-to-front flip button -->
|
|
||||||
<div class="text-gray-500 cursor-pointer"
|
|
||||||
@click="if(acceptanceExpanded){ acceptanceExpanded = false; setTimeout(() => { flipped = false; }, 500); } else { flipped = false; }">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 16.811c0 .864-.933 1.406-1.683.977l-7.108-4.061a1.125 1.125 0 0 1 0-1.954l7.108-4.061A1.125 1.125 0 0 1 21 8.689v8.122ZM11.25 16.811c0 .864-.933 1.406-1.683.977l-7.108-4.061a1.125 1.125 0 0 1 0-1.954l7.108-4.061a1.125 1.125 0 0 1 1.683.977v8.122Z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="px-1 text-center">
|
|
||||||
<span class="text-sm font-semibold">
|
|
||||||
Acceptances ({{ offer.acceptances.all|length }})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="text-gray-500 text-sm tooltip tooltip-left" data-tip="ID: {{ offer.hash }}">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
|
||||||
stroke="currentColor" class="size-5">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Ensure proper 3D transformations on the rotating element */
|
/* Ensure proper 3D transformations on the rotating element */
|
||||||
.flip-inner {
|
.flip-inner {
|
||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
}
|
}
|
||||||
/* Hide the back face of each card side */
|
/* Hide the back face of each card side */
|
||||||
.flip-face {
|
.flip-face {
|
||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
-webkit-backface-visibility: hidden;
|
-webkit-backface-visibility: hidden;
|
||||||
}
|
}
|
||||||
/* The front face is unrotated by default */
|
/* The front face is unrotated by default */
|
||||||
.flip-face.front {
|
.flip-face.front {
|
||||||
transform: rotateY(0);
|
transform: rotateY(0);
|
||||||
}
|
}
|
||||||
/* The .rotate-y-180 class rotates the entire element by 180deg */
|
/* The .rotate-y-180 class rotates the entire element by 180deg */
|
||||||
.rotate-y-180 {
|
.rotate-y-180 {
|
||||||
transform: rotateY(180deg);
|
transform: rotateY(180deg);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
{% endcache %}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.1.2 on 2025-03-15 22:05
|
# Generated by Django 5.1.2 on 2025-03-16 04:58
|
||||||
|
|
||||||
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