Fixes to trade expansion and trade acceptance appearance
This commit is contained in:
parent
63e20bace6
commit
01becbee48
20 changed files with 105 additions and 148 deletions
|
|
@ -1,6 +1,5 @@
|
|||
from django.urls import path
|
||||
from .views import (
|
||||
ListFriendCodesView,
|
||||
AddFriendCodeView,
|
||||
DeleteFriendCodeView,
|
||||
ChangeDefaultFriendCodeView,
|
||||
|
|
@ -9,8 +8,6 @@ from .views import (
|
|||
)
|
||||
|
||||
urlpatterns = [
|
||||
# ... other account URLs ...
|
||||
path("friend-codes/", ListFriendCodesView.as_view(), name="list_friend_codes"),
|
||||
path("friend-codes/add/", AddFriendCodeView.as_view(), name="add_friend_code"),
|
||||
path("friend-codes/edit/<int:pk>/", EditFriendCodeView.as_view(), name="edit_friend_code"),
|
||||
path("friend-codes/delete/<int:pk>/", DeleteFriendCodeView.as_view(), name="delete_friend_code"),
|
||||
|
|
|
|||
|
|
@ -10,27 +10,8 @@ from trades.models import TradeOffer, TradeAcceptance
|
|||
from django.core.exceptions import PermissionDenied
|
||||
from trades.mixins import FriendCodeRequiredMixin
|
||||
from common.mixins import ReusablePaginationMixin
|
||||
|
||||
class ListFriendCodesView(LoginRequiredMixin, ListView):
|
||||
"""
|
||||
Display the current user's friend codes.
|
||||
"""
|
||||
model = FriendCode
|
||||
template_name = "friend_codes/list_friend_codes.html"
|
||||
context_object_name = "friend_codes"
|
||||
|
||||
def get_queryset(self):
|
||||
# Get the default friend code's primary key if it exists.
|
||||
default_pk = getattr(self.request.user.default_friend_code, "pk", None)
|
||||
|
||||
# Annotate each friend code with is_default=True if its pk matches.
|
||||
return self.request.user.friend_codes.all().annotate(
|
||||
is_default=Case(
|
||||
When(pk=default_pk, then=Value(True)),
|
||||
default=Value(False),
|
||||
output_field=BooleanField()
|
||||
)
|
||||
)
|
||||
from django.urls import reverse
|
||||
from django.utils.http import urlencode
|
||||
|
||||
class AddFriendCodeView(LoginRequiredMixin, CreateView):
|
||||
"""
|
||||
|
|
@ -40,7 +21,9 @@ class AddFriendCodeView(LoginRequiredMixin, CreateView):
|
|||
model = FriendCode
|
||||
form_class = FriendCodeForm
|
||||
template_name = "friend_codes/add_friend_code.html"
|
||||
success_url = reverse_lazy("list_friend_codes")
|
||||
def get_success_url(self):
|
||||
base_url = reverse("dashboard")
|
||||
return f"{base_url}?{urlencode({'tab': 'friend_codes'})}"
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.user = self.request.user
|
||||
|
|
@ -57,7 +40,9 @@ class DeleteFriendCodeView(LoginRequiredMixin, DeleteView):
|
|||
model = FriendCode
|
||||
template_name = "friend_codes/confirm_delete_friend_code.html"
|
||||
context_object_name = "friend_code"
|
||||
success_url = reverse_lazy("list_friend_codes")
|
||||
def get_success_url(self):
|
||||
base_url = reverse("dashboard")
|
||||
return f"{base_url}?{urlencode({'tab': 'friend_codes'})}"
|
||||
|
||||
def get_queryset(self):
|
||||
# Only allow deletion of friend codes owned by the current user.
|
||||
|
|
@ -90,20 +75,16 @@ class DeleteFriendCodeView(LoginRequiredMixin, DeleteView):
|
|||
self.object = self.get_object()
|
||||
user = self.object.user
|
||||
|
||||
# Check if the friend code is the only one; prevent deletion.
|
||||
if user.friend_codes.count() == 1:
|
||||
messages.error(request, "Cannot remove your only friend code.")
|
||||
return redirect(self.success_url)
|
||||
|
||||
# Check if the friend code is set as default; prevent deletion.
|
||||
return redirect(self.get_success_url())
|
||||
if user.default_friend_code == self.object:
|
||||
messages.error(
|
||||
request,
|
||||
"Cannot delete your default friend code. Please set a different default first."
|
||||
)
|
||||
return redirect(self.success_url)
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
# Use the unfiltered manager and filter by the friend code's primary key
|
||||
trade_offer_exists = TradeOffer.all_offers.filter(initiated_by_id=self.object.pk).exists()
|
||||
trade_acceptance_exists = TradeAcceptance.objects.filter(accepted_by_id=self.object.pk).exists()
|
||||
|
||||
|
|
@ -112,12 +93,11 @@ class DeleteFriendCodeView(LoginRequiredMixin, DeleteView):
|
|||
request,
|
||||
"Cannot remove this friend code because there are existing trade offers associated with it."
|
||||
)
|
||||
return redirect(self.success_url)
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
# Proceed to safe deletion.
|
||||
self.object.delete()
|
||||
messages.success(request, "Friend code removed successfully.")
|
||||
return redirect(self.success_url)
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
class ChangeDefaultFriendCodeView(LoginRequiredMixin, View):
|
||||
"""
|
||||
|
|
@ -128,7 +108,9 @@ class ChangeDefaultFriendCodeView(LoginRequiredMixin, View):
|
|||
friend_code = get_object_or_404(FriendCode, pk=friend_code_id, user=request.user)
|
||||
request.user.set_default_friend_code(friend_code)
|
||||
messages.success(request, "Default friend code updated successfully.")
|
||||
return redirect("list_friend_codes")
|
||||
base_url = reverse("dashboard")
|
||||
query_string = urlencode({"tab": "friend_codes"})
|
||||
return redirect(f"{base_url}?{query_string}")
|
||||
|
||||
class EditFriendCodeView(LoginRequiredMixin, UpdateView):
|
||||
"""
|
||||
|
|
@ -141,7 +123,9 @@ class EditFriendCodeView(LoginRequiredMixin, UpdateView):
|
|||
fields = ['in_game_name']
|
||||
template_name = "friend_codes/edit_friend_code.html"
|
||||
context_object_name = "friend_code"
|
||||
success_url = reverse_lazy("list_friend_codes")
|
||||
def get_success_url(self):
|
||||
base_url = reverse("dashboard")
|
||||
return f"{base_url}?{urlencode({'tab': 'friend_codes'})}"
|
||||
|
||||
def get_queryset(self):
|
||||
# Ensure the user can only edit their own friend codes
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|||
from trades.models import TradeOffer, TradeAcceptance, TradeOfferHaveCard, TradeOfferWantCard
|
||||
from cards.models import Card
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.cache import cache_page
|
||||
from django.template.response import TemplateResponse
|
||||
from django.http import HttpResponseRedirect
|
||||
import logging
|
||||
|
|
@ -112,7 +111,6 @@ class HomePageView(TemplateView):
|
|||
|
||||
return context
|
||||
|
||||
@method_decorator(cache_page(60 * 10)) # Cache for 10 minutes
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Override get method to add caching"""
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
|
@ -1,5 +1,16 @@
|
|||
[x-cloak] { display: none !important; }
|
||||
|
||||
/* Beta Badge */
|
||||
#navbar-logo::after {
|
||||
content: 'BETA';
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: var(--color-base-content);
|
||||
background-color: var(--color-base-300);
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
select.card-multiselect {
|
||||
height: calc(var(--spacing) * 35);
|
||||
/*background-image: linear-gradient(45deg, #0000 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, #0000 50%); */
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-2">{{ _('Quick Actions') }}</h2>
|
||||
<div class="flex space-x-4">
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<a href="{% url 'trade_offer_create' %}" class="btn btn-primary grow">{{ _('Create New Offer') }}</a>
|
||||
<a href="{% url 'trade_offer_list' %}" class="btn btn-secondary grow">{{ _('View All Offers') }}</a>
|
||||
<a href="{% url 'account_logout' %}" class="btn btn-warning grow">{{ _('Sign Out') }}</a>
|
||||
|
|
@ -121,17 +121,17 @@
|
|||
{% endif %}
|
||||
{% endwith %}
|
||||
<div class="divider"></div>
|
||||
<h2 class="text-lg font-semibold pt-0">What is Gravatar?</h2>
|
||||
<p class="mb-4">Gravatar (Globally Recognized Avatar) is a free service that links your email address to a profile picture and, optionally, a profile. Many websites, including this one, use Gravatar to display your avatar and profile automatically.</p>
|
||||
<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 and, optionally, a profile. Many websites, including this one, use Gravatar to display your avatar and profile automatically.</p>
|
||||
|
||||
<h2 class="text-lg font-semibold">How does it work?</h2>
|
||||
<p class="mb-4">If you've set up a Gravatar, your profile picture will appear here whenever you use your email on supported sites. When someone hovers over or clicks on your avatar, your Gravatar profile will also appear if you have one. If you don't have a Gravatar yet, you'll see a default image instead.</p>
|
||||
<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. When someone hovers over or clicks on your avatar, your Gravatar profile will also appear if you have one. If you don't have a Gravatar yet, you'll see a default image instead.</p>
|
||||
|
||||
<h2 class="text-lg font-semibold">Is it safe? What about privacy?</h2>
|
||||
<p class="mb-4">Gravatar is completely optional, opt-in, and prioritizes your security and privacy. Your email is never shared and only a hashed version is sent to Gravatar, protecting your identity while ensuring that your email address is not exposed to bots or scrapers. Your personal data remains secure, and you maintain full control over your public profile.</p>
|
||||
<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 shared and only a hashed version is sent to Gravatar, protecting your identity while ensuring that your email address is not exposed to bots or scrapers. Your personal data remains secure, and you maintain full control over your public profile.</p>
|
||||
|
||||
<h2 class="text-lg font-semibold">Want to update or add a Gravatar?</h2>
|
||||
<p class="mb-4">Go to Gravatar.com to set up or change your avatar or profile. Your updates will appear here once saved!</p>
|
||||
<h2 class="text-base font-semibold">Want to update or add a Gravatar?</h2>
|
||||
<p class="mb-4 text-sm">Go to Gravatar.com to set up or change your avatar or profile. Your updates will appear here once saved!</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@
|
|||
|
||||
{% block javascript_head %}{% endblock %}
|
||||
</head>
|
||||
<body class="min-h-screen bg-base-200" id="body">
|
||||
<body class="min-h-screen bg-base-200 dark:bg-base-300" id="body">
|
||||
<!-- Header and Navigation -->
|
||||
<div class="navbar bg-base-100 shadow-sm">
|
||||
<div class="navbar-start">
|
||||
<a class="btn btn-ghost text-xl" href="{% url 'home' %}">
|
||||
<a id="navbar-logo" class="btn btn-ghost text-xl" href="{% url 'home' %}">
|
||||
<span class="inline leading-none" aria-hidden="true">
|
||||
<span class="inline-block relative align-text-top">P</span><span class="inline-block relative align-text-bottom">K</span><span class="inline-block relative align-text-top">M</span><span class="inline-block relative align-text-bottom">N</span>
|
||||
<span class="inline-block relative">Trade Club</span>
|
||||
|
|
@ -111,13 +111,13 @@
|
|||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto p-4 sm:w-4/5 md:w-full xl:w-256">
|
||||
<main class="container mx-auto p-4 w-full xl:w-256">
|
||||
{% include '_messages.html' %}
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-base-200 text-base-content p-4">
|
||||
<footer class="bg-base-200 dark:bg-base-300 text-base-content p-4">
|
||||
<div class="container mx-auto text-center text-sm">
|
||||
<p>© {% now "Y" %} PKMNTrade.Club. All rights reserved.</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@
|
|||
<h2 class="text-xl font-semibold">Have this Card ({{ trade_offer_have_count }})</h2>
|
||||
<!-- DaisyUI dropdown replacing the select -->
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn m-1" x-text="order === 'newest' ? 'Newest 🞃' : 'Oldest 🞃'"></div>
|
||||
<div tabindex="0" role="button" class="btn m-1">Sort by: <span x-text="order === 'newest' ? 'Newest' : 'Oldest'"></span>
|
||||
<svg class="size-4" 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>
|
||||
</div>
|
||||
<ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-1 w-26 p-2 shadow-sm">
|
||||
<li>
|
||||
<a href="#" @click.prevent="order = 'newest'; page = 1; loadOffers()">
|
||||
|
|
@ -67,7 +71,11 @@
|
|||
<h2 class="text-xl font-semibold">Want this Card ({{ trade_offer_want_count }})</h2>
|
||||
<!-- DaisyUI dropdown replacing the select -->
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn m-1" x-text="order === 'newest' ? 'Newest 🞃' : 'Oldest 🞃'"></div>
|
||||
<div tabindex="0" role="button" class="btn m-1">Sort by: <span x-text="order === 'newest' ? 'Newest' : 'Oldest'"></span>
|
||||
<svg class="size-4" 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>
|
||||
</div>
|
||||
<ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-1 w-26 p-2 shadow-sm">
|
||||
<li>
|
||||
<a href="#" @click.prevent="order = 'newest'; page = 1; loadOffers()">
|
||||
|
|
|
|||
|
|
@ -27,7 +27,10 @@
|
|||
<!-- Sort Dropdown -->
|
||||
<div class="dropdown dropdown-end m-1">
|
||||
<div tabindex="0" class="btn">
|
||||
Sort by: <span x-text="order === 'absolute' ? 'None' : (order === 'alphabetical' ? 'Alphabetical' : 'Rarity')"></span> 🞃
|
||||
Sort by: <span x-text="order === 'absolute' ? 'None' : (order === 'alphabetical' ? 'Alphabetical' : 'Rarity')"></span>
|
||||
<svg class="size-4" 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>
|
||||
</div>
|
||||
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-52">
|
||||
<li><a href="#" @click.prevent="order = 'absolute'; page = 1; loadCards()">None</a></li>
|
||||
|
|
@ -38,7 +41,10 @@
|
|||
<!-- Grouping Dropdown -->
|
||||
<div class="dropdown dropdown-end m-1">
|
||||
<div tabindex="0" class="btn">
|
||||
Group by: <span x-text="groupBy === 'none' ? 'None' : (groupBy.charAt(0).toUpperCase() + groupBy.slice(1))"></span> 🞃
|
||||
Group by: <span x-text="groupBy === 'none' ? 'None' : (groupBy.charAt(0).toUpperCase() + groupBy.slice(1))"></span>
|
||||
<svg class="size-4" 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>
|
||||
</div>
|
||||
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-52">
|
||||
<li><a href="#" @click.prevent="groupBy = 'none'; page = 1; loadCards()">None</a></li>
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@
|
|||
<button type="submit" class="btn btn-primary w-full">{% trans "Add Friend Code" %}</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'list_friend_codes' %}" class="btn btn-secondary">Back to Friend Codes</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Include Cleave Zen from a CDN -->
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
Confirm Delete
|
||||
{% endif %}
|
||||
</button>
|
||||
<a href="{% url 'list_friend_codes' %}" class="btn btn-secondary">Cancel</a>
|
||||
<a href="{% url 'dashboard' %}?tab=friend_codes" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
<!-- Right group: Cancel & Update -->
|
||||
<div class="flex items-center space-x-4 mt-4 md:mt-0">
|
||||
<a href="{% url 'list_friend_codes' %}" class="btn btn-secondary">Cancel</a>
|
||||
<a href="{% url 'dashboard' %}?tab=friend_codes" class="btn btn-secondary">Cancel</a>
|
||||
<!-- This update button is outside the form but tied to it with the HTML5 'form' attribute -->
|
||||
<button type="submit" form="edit-friendcode-form" class="btn btn-primary">Update</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}My Friend Codes{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-3xl mt-6">
|
||||
<h1 class="text-3xl font-bold mb-4">My Friend Codes</h1>
|
||||
|
||||
{% if friend_codes %}
|
||||
<ul class="space-y-2">
|
||||
{% for code in friend_codes %}
|
||||
<li class="w-full grid grid-cols-2 grid-rows-2 md:grid-cols-8 md:grid-rows-1 items-center {% if code.is_default %}bg-green-200 dark:bg-green-300 dark:text-base-100{% else %}bg-base-100 dark:bg-base-900 dark:text-white{% endif %} p-4 rounded shadow">
|
||||
<div class="row-start-1 md:col-span-3">
|
||||
<span class="align-baseline"><a href="{% url 'edit_friend_code' code.id %}">{{ code.in_game_name }}</a></span>
|
||||
{% if code.is_default %}
|
||||
<span class="badge badge-success ml-2 align-baseline">Default</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row-start-2 col-start-1 md:row-start-1 md:col-span-3 {% if not code.is_default %}mr-4{% endif %}">
|
||||
<span class="font-mono text-sm sm:text-base align-baseline">{{ code.friend_code }}</span>
|
||||
</div>
|
||||
<div class="row-start-2 col-start-2 md:row-start-1 md:col-span-2 flex justify-end space-x-2">
|
||||
{% if not code.is_default %}
|
||||
<form method="post" action="{% url 'change_default_friend_code' code.id %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-secondary btn-sm align-baseline">Set Default</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<a href="{% url 'delete_friend_code' code.id %}" class="btn btn-error btn-sm align-baseline">Delete</a>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>You do not have any friend codes added yet.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-4 flex flex-row justify-between">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-secondary">Back to Dashboard</a>
|
||||
<a href="{% url 'add_friend_code' %}" class="btn btn-primary">Add a New Friend Code</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- Featured Offers -->
|
||||
<div>
|
||||
{% cache 60 featured_offers %}
|
||||
{% cache 3600 featured_offers %}
|
||||
<div class="p-4 text-center ">
|
||||
<h5 class="text-xl font-semibold whitespace-nowrap truncate mb-0">Featured Offers</h5>
|
||||
</div>
|
||||
|
|
@ -140,7 +140,7 @@
|
|||
|
||||
<!-- Recent Offers -->
|
||||
<div>
|
||||
{% cache 60 recent_offers %}
|
||||
{% cache 3600 recent_offers %}
|
||||
<div class="text-center p-4">
|
||||
<h5 class="text-xl font-semibold whitespace-nowrap truncate mb-0">Recent Offers</h5>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,8 +25,5 @@
|
|||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="mt-6">
|
||||
<a href="{% url 'trade_offer_detail' pk=trade_offer.pk %}" class="btn btn-secondary">Back to Offer Details</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -84,11 +84,5 @@
|
|||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-6">
|
||||
<a href="{% url 'trade_offer_detail' pk=object.trade_offer.pk %}" class="btn btn-secondary">
|
||||
Back to Offer Details
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -47,11 +47,10 @@
|
|||
{% endif %}
|
||||
|
||||
<div class="mt-6 flex justify-between">
|
||||
<a href="{% url 'trade_offer_list' %}" class="btn btn-secondary">Back to Trade Offers</a>
|
||||
{% if is_initiator %}
|
||||
<a href="{{ delete_close_url }}" class="btn btn-danger">Delete/Close Trade Offer</a>
|
||||
{% elif request.user.is_authenticated %}
|
||||
<button type="submit" class="btn btn-primary">Accept Offer</button>
|
||||
<button type="submit" class="btn btn-primary">Accept Trade</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@
|
|||
{% endif %}
|
||||
|
||||
<div class="flex space-x-4">
|
||||
<a href="{% url 'trade_offer_list' %}" class="btn btn-secondary">Back to Trade Offers</a>
|
||||
{% if can_delete %}
|
||||
<a href="{% url 'trade_offer_delete' object.pk %}" class="btn btn-error">Delete Trade Offer</a>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
{% load gravatar card_badge cache %}
|
||||
{% load gravatar card_badge %}
|
||||
|
||||
{% cache 60 trade_acceptance acceptance.pk %}
|
||||
<div class="card card-border bg-base-100 shadow-lg w-96 md:w-80 lg:w-96 mx-auto">
|
||||
<div class="card card-border bg-base-100 shadow-lg w-84 mx-auto">
|
||||
<!-- Header -->
|
||||
<div class="py-4 mx-2 sm:mx-4">
|
||||
<div class="py-4 mx-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<!-- Left: Initiator's avatar (moved from center) and "Has" -->
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center ms-8">
|
||||
<div class="avatar mr-2">
|
||||
<div class="w-10 rounded-full">
|
||||
{{ acceptance.trade_offer.initiated_by.user.email|gravatar:40 }}
|
||||
|
|
@ -15,7 +14,7 @@
|
|||
<span class="text-sm font-semibold">Has</span>
|
||||
</div>
|
||||
<!-- Right: "Wants" with the acceptor's avatar -->
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center me-8">
|
||||
<span class="text-sm font-semibold mr-2">Wants</span>
|
||||
<div class="avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
|
|
@ -29,7 +28,7 @@
|
|||
<!-- Main Card Row: Single row with the acceptance's cards -->
|
||||
<a href="{% url 'trade_acceptance_update' pk=acceptance.pk %}" class="no-underline block">
|
||||
<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">
|
||||
<div class="flex flex-col items-center">
|
||||
{% card_badge acceptance.requested_card %}
|
||||
</div>
|
||||
|
|
@ -41,13 +40,15 @@
|
|||
</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 }}">
|
||||
<div class="flex justify-between px-2 pb-2">
|
||||
<div class="text-gray-500 text-sm">
|
||||
{{ acceptance.get_state_display }}
|
||||
</div>
|
||||
<div class="text-gray-500 text-sm tooltip tooltip-left" data-tip="Trade 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>
|
||||
{% endcache %}
|
||||
</div>
|
||||
|
|
@ -40,26 +40,33 @@
|
|||
</div>
|
||||
<!-- Main Trade Offer Row -->
|
||||
<div class="flip-face-body self-start">
|
||||
<a href="{% url 'trade_offer_detail' pk=offer_pk %}" class="no-underline block">
|
||||
<div class="px-2 main-badges pb-0">
|
||||
<!-- Normal mode: just use an outer grid with 2 columns -->
|
||||
<div class="flex flex-row gap-2 justify-around">
|
||||
<!-- Has Side -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in have_cards_available|slice:"0:1" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- Wants Side -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in want_cards_available|slice:"0:1" %}
|
||||
{% if not flipped %}
|
||||
<a href="{% url 'trade_offer_detail' pk=offer_pk %}" class="no-underline block">
|
||||
<div class="px-2 main-badges pb-0">
|
||||
<!-- Normal mode: just use an outer grid with 2 columns -->
|
||||
<div class="flex flex-row gap-2 justify-around">
|
||||
<!-- Has Side -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in have_cards_available|slice:"0:1" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- Wants Side -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in want_cards_available|slice:"0:1" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="flex justify-center mt-8">
|
||||
<div class="text-sm">
|
||||
All cards have been accepted.
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Extra Card Badges (Collapsible) -->
|
||||
<div x-show="badgeExpanded" x-collapse.duration.500ms x-cloak class="px-2 extra-badges">
|
||||
<a href="{% url 'trade_offer_detail' pk=offer_pk %}" class="no-underline block">
|
||||
|
|
@ -80,6 +87,7 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% if have_cards_available|length > 1 or want_cards_available|length > 1 %}
|
||||
<div @click="badgeExpanded = !badgeExpanded" class="flex justify-center h-5 cursor-pointer">
|
||||
<svg x-bind:class="{ 'rotate-180': badgeExpanded }"
|
||||
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
||||
|
|
@ -88,6 +96,9 @@
|
|||
d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="h-5"></div>
|
||||
{% endif %}
|
||||
<div class="flip-face-footer 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 }}">
|
||||
|
|
@ -102,7 +113,6 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
from django.urls import path
|
||||
from django.views.decorators.cache import cache_page
|
||||
from .views import (
|
||||
TradeOfferCreateView,
|
||||
TradeOfferCreateConfirmView,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue