diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py index f53f39c..44fc0b9 100644 --- a/accounts/migrations/0001_initial.py +++ b/accounts/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2025-04-08 06:24 +# Generated by Django 5.1.2 on 2025-04-13 05:10 import accounts.models import django.contrib.auth.models diff --git a/cards/migrations/0001_initial.py b/cards/migrations/0001_initial.py index 6428bab..5510967 100644 --- a/cards/migrations/0001_initial.py +++ b/cards/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2025-04-08 06:24 +# Generated by Django 5.1.2 on 2025-04-13 05:10 import django.db.models.deletion from django.db import migrations, models diff --git a/cards/templatetags/card_badge.py b/cards/templatetags/card_badge.py index 721942b..03584de 100644 --- a/cards/templatetags/card_badge.py +++ b/cards/templatetags/card_badge.py @@ -28,4 +28,9 @@ def card_badge_inline(card, quantity=None): 'cardset': card.cardset, 'expanded': True, }) - return mark_safe(html) \ No newline at end of file + return mark_safe(html) + +@register.filter +def addstr(arg1, arg2): + """concatenate arg1 & arg2""" + return str(arg1) + str(arg2) \ No newline at end of file diff --git a/seed/0004_TestUsers.json b/seed/0004_TestUsers.json index 0b584ac..9c42fd7 100644 --- a/seed/0004_TestUsers.json +++ b/seed/0004_TestUsers.json @@ -27,7 +27,7 @@ "password": "pbkdf2_sha256$870000$NxQDOyPzAvM3FgLL5z0SRy$JuET4f8HI55Oy1umkzg6WtjFVpYTt+UfpZWqPff4EO4=", "last_login": "2025-03-13T04:52:57.949Z", "is_superuser": false, - "username": "nathanward2016@gmail.com", + "username": "backrolls", "first_name": "", "last_name": "", "email": "nathanward2016@gmail.com", diff --git a/theme/static_src/tailwind.config.js b/theme/static_src/tailwind.config.js index b8e3664..c5908ab 100644 --- a/theme/static_src/tailwind.config.js +++ b/theme/static_src/tailwind.config.js @@ -50,6 +50,7 @@ module.exports = { 'bg-success', 'bg-warning', 'bg-error', + 'text-gray-700' ], theme: { extend: {}, diff --git a/theme/templates/cards/_card_list.html b/theme/templates/cards/_card_list.html index fdc6ad0..759b29a 100644 --- a/theme/templates/cards/_card_list.html +++ b/theme/templates/cards/_card_list.html @@ -3,7 +3,7 @@ {% if group_by and groups %} {% for group in groups %}
{{ group.group }}
-
+
{% for card in group.cards %} {% card_badge card expanded=True %} @@ -12,7 +12,7 @@
{% endfor %} {% else %} -
+
{% for card in cards %} {% card_badge card expanded=True %} diff --git a/theme/templates/trades/trade_acceptance_update.html b/theme/templates/trades/trade_acceptance_update.html index 141d196..413ce30 100644 --- a/theme/templates/trades/trade_acceptance_update.html +++ b/theme/templates/trades/trade_acceptance_update.html @@ -1,57 +1,88 @@ {% extends 'base.html' %} -{% load trade_offer_tags %} +{% load trade_offer_tags card_badge %} {% block title %}Update Trade{% endblock title %} {% block content %} -
-

Update Trade

-
-
    - {% if object.is_thanked %} -
  • Accepted
  • -
  • Card Sent
  • -
  • Card Received
  • -
  • Thanks Sent
  • -
  • Thanks Received
  • -
  • Completed
  • - {% elif object.is_rejected %} -
  • Accepted
  • -
  • - X{{ object.get_state_display }} -
  • - {% else %} -
  • Accepted
  • -
  • Card Sent
  • -
  • Card Received
  • - {% if object.state == 'THANKED_BY_INITIATOR' %} -
  • Thanks Sent
  • -
  • Thanks Received
  • -
  • Completed
  • - {% elif object.state == 'THANKED_BY_ACCEPTOR' %} -
  • Thanks Received
  • -
  • Thanks Sent
  • -
  • Completed
  • - {% elif object.state == 'THANKED_BY_BOTH' %} +{% if form and form.errors %} +
    + Please correct the errors below: +
      + {% for field in form %} + {% for error in field.errors %} +
    • {{ error }}
    • + {% endfor %} + {% endfor %} + {% for error in form.non_field_errors %} +
    • {{ error }}
    • + {% endfor %} +
    +
    +{% endif %} +

    Trade Details

    + +
    +
    +
      + {% if object.is_thanked %} +
    • Accepted
    • +
    • Card Sent
    • +
    • Card Received
    • Thanks Sent
    • Thanks Received
    • Completed
    • + {% elif object.is_rejected %} +
    • Accepted
    • +
    • + X{{ object.get_state_display }} +
    • {% else %} -
    • Thanks Sent
    • -
    • Thanks Received
    • -
    • Completed
    • +
    • Accepted
    • +
    • Card Sent
    • +
    • Card Received
    • + {% if object.state == 'THANKED_BY_INITIATOR' %} +
    • Thanks Sent
    • +
    • Thanks Received
    • +
    • Completed
    • + {% elif object.state == 'THANKED_BY_ACCEPTOR' %} +
    • Thanks Received
    • +
    • Thanks Sent
    • +
    • Completed
    • + {% elif object.state == 'THANKED_BY_BOTH' %} +
    • Thanks Sent
    • +
    • Thanks Received
    • +
    • Completed
    • + {% else %} +
    • Thanks Sent
    • +
    • Thanks Received
    • +
    • Completed
    • + {% endif %} {% endif %} - {% endif %} -
    -
    - -
    - {% render_trade_acceptance object %} -
    - -
    -

    Select an action:

    +
+
+
+
{% if form.fields.state.choices %} +
{% for state_value, state_label in form.fields.state.choices %}
@@ -63,26 +94,6 @@
{% endfor %}
- {% else %} -

No available actions.

- {% endif %}
- - {% if form and form.errors %} -
- Please correct the errors below: -
    - {% for field in form %} - {% for error in field.errors %} -
  • {{ error }}
  • - {% endfor %} - {% endfor %} - {% for error in form.non_field_errors %} -
  • {{ error }}
  • - {% endfor %} -
-
- {% endif %} -
{% endblock content %} \ No newline at end of file diff --git a/theme/templates/trades/trade_offer_detail.html b/theme/templates/trades/trade_offer_detail.html index fc78880..e64ef3f 100644 --- a/theme/templates/trades/trade_offer_detail.html +++ b/theme/templates/trades/trade_offer_detail.html @@ -1,40 +1,38 @@ {% extends 'base.html' %} -{% load trade_offer_tags %} +{% load trade_offer_tags card_badge %} {% block title %}{{title}}{% endblock title %} {% block content %} -
+

Trade Offer Details

-
- {% render_trade_offer object %} -
- {% if acceptance_form %} -
-

Accept This Offer

+
+ {% if acceptance_form %} +
+

Accept A Trade

{% csrf_token %} - +
- + {{ acceptance_form.offered_card }} {% for error in acceptance_form.offered_card.errors %}

{{ error }}

{% endfor %}
- +
- + {{ acceptance_form.requested_card }} {% for error in acceptance_form.requested_card.errors %}

{{ error }}

{% endfor %}
- +
{% if not acceptance_form.accepted_by.field.widget.is_hidden %} -
-
+ +
+ {% if is_initiator %} + Delete/Close Trade Offer + {% elif request.user.is_authenticated %} + + {% endif %} +
+ +
{% endif %} - -
- {% if is_initiator %} - Delete/Close Trade Offer - {% elif request.user.is_authenticated %} - - {% endif %} - +
+
Cards They Have: +
+ {% for card in object.have_cards_available %} + {% card_badge card.card card.qty_available %} + {% endfor %} +
+
+
Cards They Want: +
+ {% for card in object.want_cards_available %} + {% card_badge card.card card.qty_available %} + {% endfor %} +
+
+
+
+
+
Initiator:
+
{{ object.initiated_by.user.username }}
+
ID:
+
#{{ object.hash }}
+
Created At:
+
{{ object.created_at }}
+
Last Updated:
+
{{ object.updated_at }}
+
+
+
Trade History:
+
+ {% for trade in object.acceptances.all %} +
+
{% card_badge trade.offered_card %}
+
{% card_badge trade.requested_card %}
+
{{ trade.accepted_by.user.username }} • #{{ trade.hash }} • {{ trade.get_state_display }}
+
+ {% empty %} +
No trades yet.
+ {% endfor %} +
{% endblock content %} \ No newline at end of file diff --git a/theme/templatetags/card_badge.html b/theme/templatetags/card_badge.html index 9f94e3c..5dfa30f 100644 --- a/theme/templatetags/card_badge.html +++ b/theme/templatetags/card_badge.html @@ -1,5 +1,5 @@ {% if not expanded %} -
+
{{ name }}
{{ cardset }}
@@ -11,7 +11,7 @@
{% else %} -
+
{{ name }}
{% if quantity != None %} diff --git a/theme/templatetags/trade_acceptance.html b/theme/templatetags/trade_acceptance.html index 6f8f6c2..024eedf 100644 --- a/theme/templatetags/trade_acceptance.html +++ b/theme/templatetags/trade_acceptance.html @@ -1,22 +1,50 @@ {% load gravatar card_badge %} -
+
+
-
-
- -
-
+
+
+ +
+
{{ acceptance.trade_offer.initiated_by.user.email|gravatar:40 }}
- Has +
+ {% if acceptance.is_initiator_state %} + {{ acceptance.action_label_2 }} + {% else %} + Waiting on {{ acceptance.trade_offer.initiated_by.user.username }} to {{ acceptance.next_action_label }}... + {% endif %} +
+
+
+ +
+
+
+
{% card_badge acceptance.requested_card %}
+
+
+
{% card_badge acceptance.offered_card %}
+
+
+
+
+
-
- Wants -
+
+
+ {% if acceptance.is_acceptor_state %} + {{ acceptance.action_label_2 }} + {% else %} + Waiting on {{ acceptance.accepted_by.user.username }} to {{ acceptance.next_action_label }}... + {% endif %} +
+
{{ acceptance.accepted_by.user.email|gravatar:40 }}
@@ -24,31 +52,5 @@
- - - -
-
-
- {% card_badge acceptance.requested_card %} -
-
- {% card_badge acceptance.offered_card %} -
-
-
-
- - -
-
- {{ acceptance.get_state_display }} -
-
- - - -
-
-
\ No newline at end of file +
+
\ No newline at end of file diff --git a/theme/templatetags/trade_offer.html b/theme/templatetags/trade_offer.html index d620f8e..c20457d 100644 --- a/theme/templatetags/trade_offer.html +++ b/theme/templatetags/trade_offer.html @@ -27,12 +27,12 @@ {% if num_cards_available > 0 %}
{% for card in have_cards_available %} -
{% card_badge card.card card.quantity %}
+
{% card_badge card.card card.quantity %}
{% endfor %}
{% for card in want_cards_available %} -
{% card_badge card.card card.quantity %}
+
{% card_badge card.card card.quantity %}
{% endfor %}
{% else %} diff --git a/trades/migrations/0001_initial.py b/trades/migrations/0001_initial.py index e3193e5..55c0835 100644 --- a/trades/migrations/0001_initial.py +++ b/trades/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2025-04-08 06:24 +# Generated by Django 5.1.2 on 2025-04-13 05:10 import django.db.models.deletion from django.db import migrations, models @@ -52,6 +52,7 @@ class Migration(migrations.Migration): ('trade_offer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='trade_offer_have_cards', to='trades.tradeoffer')), ], options={ + 'ordering': ['card__name'], 'unique_together': {('trade_offer', 'card')}, }, ), @@ -70,6 +71,7 @@ class Migration(migrations.Migration): ('trade_offer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='trade_offer_want_cards', to='trades.tradeoffer')), ], options={ + 'ordering': ['card__name'], 'unique_together': {('trade_offer', 'card')}, }, ), diff --git a/trades/models.py b/trades/models.py index ab5dafa..5ca0d84 100644 --- a/trades/models.py +++ b/trades/models.py @@ -100,7 +100,6 @@ class TradeOffer(models.Model): # Use super().save() here to avoid recursion. super(TradeOffer, self).save(update_fields=["rarity_level", "rarity_icon"]) - # New derived properties for available cards @property def have_cards_available(self): # Returns the list of have_cards (through objects) that still have available quantity. @@ -136,6 +135,10 @@ class TradeOfferHaveCard(models.Model): quantity = models.PositiveIntegerField(default=1) qty_accepted = models.PositiveIntegerField(default=0, editable=False) + @property + def qty_available(self): + return self.quantity - self.qty_accepted + def __str__(self): return f"{self.card.name} x{self.quantity} (Accepted: {self.qty_accepted})" @@ -166,6 +169,10 @@ class TradeOfferWantCard(models.Model): quantity = models.PositiveIntegerField(default=1) qty_accepted = models.PositiveIntegerField(default=0, editable=False) + @property + def qty_available(self): + return self.quantity - self.qty_accepted + def __str__(self): return f"{self.card.name} x{self.quantity} (Accepted: {self.qty_accepted})" @@ -246,6 +253,17 @@ class TradeAcceptance(models.Model): AcceptanceState.THANKED_BY_BOTH: "Send Thanks", } + ALTERNATE_ACTION_LABELS_2 = { + AcceptanceState.REJECTED_BY_INITIATOR: "Rejected this Trade", + AcceptanceState.REJECTED_BY_ACCEPTOR: "Rejected this Trade", + AcceptanceState.ACCEPTED: "Accepted this Trade", + AcceptanceState.SENT: "Sent the Card", + AcceptanceState.RECEIVED: "Received the Card and Responded", + AcceptanceState.THANKED_BY_INITIATOR: "Sent Thanks", + AcceptanceState.THANKED_BY_ACCEPTOR: "Sent Thanks", + AcceptanceState.THANKED_BY_BOTH: "Sent Thanks", + } + @classmethod def get_action_label_for_state(cls, state_value): """ @@ -262,6 +280,46 @@ class TradeAcceptance(models.Model): """ return self.get_action_label_for_state(self.state) + @property + def next_action_label(self): + """ + Returns what the next action label would be based on the current state. + """ + if self.state == self.AcceptanceState.ACCEPTED: + return self.get_action_label_for_state(self.AcceptanceState.SENT) + elif self.state == self.AcceptanceState.SENT: + return self.get_action_label_for_state(self.AcceptanceState.RECEIVED) + elif self.state == self.AcceptanceState.RECEIVED or self.state == self.AcceptanceState.THANKED_BY_ACCEPTOR or self.state == self.AcceptanceState.THANKED_BY_INITIATOR: + return self.get_action_label_for_state(self.AcceptanceState.THANKED_BY_BOTH) + else: + return None + + + @classmethod + def get_action_label_for_state_2(cls, state_value): + """ + Returns the alternate action label for the provided state_value. + If no alternate label exists, falls back to the default label. + """ + default = dict(cls.AcceptanceState.choices).get(state_value, state_value) + return cls.ALTERNATE_ACTION_LABELS_2.get(state_value, default) + + @property + def action_label_2(self): + """ + For the current acceptance state, return the alternate action label. + """ + return self.get_action_label_for_state_2(self.state) + + @property + def is_initiator_state(self): + return self.state in [self.AcceptanceState.SENT.value, self.AcceptanceState.THANKED_BY_INITIATOR.value, self.AcceptanceState.THANKED_BY_BOTH.value] + + @property + def is_acceptor_state(self): + return self.state in [self.AcceptanceState.ACCEPTED.value, self.AcceptanceState.RECEIVED.value, self.AcceptanceState.THANKED_BY_ACCEPTOR.value, self.AcceptanceState.THANKED_BY_BOTH.value] + + @property def is_completed(self): return self.state in { diff --git a/trades/templatetags/trade_offer_tags.py b/trades/templatetags/trade_offer_tags.py index 1848f58..c3b4eb9 100644 --- a/trades/templatetags/trade_offer_tags.py +++ b/trades/templatetags/trade_offer_tags.py @@ -1,5 +1,6 @@ from django import template from math import ceil +from trades.models import TradeAcceptance register = template.Library() @register.inclusion_tag('templatetags/trade_offer.html', takes_context=True) @@ -20,9 +21,7 @@ def render_trade_offer(context, offer): if card.quantity > card.qty_accepted ] - acceptances = [acceptance for acceptance in list(offer.acceptances.all()) - if acceptance.is_active - ] + acceptances = list(offer.acceptances.all()) # Determine if the offer should show its back side (acceptances view) by default. # If either side has no available cards, then flip the offer. @@ -51,6 +50,7 @@ def render_trade_acceptance(context, acceptance): """ Renders a simple trade acceptance view with a single row and simplified header/footer. """ + return { "acceptance": acceptance, "request": context.get("request"),