diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py index 2636f98..55a3624 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-03-29 21:23 +# Generated by Django 5.1.2 on 2025-03-31 22:48 import accounts.models import django.contrib.auth.models @@ -33,6 +33,7 @@ class Migration(migrations.Migration): ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), ('show_friend_code_on_link_previews', models.BooleanField(default=False, help_text='This will primarily affect share link previews on X, Discord, etc.', verbose_name='Show Friend Code on Link Previews')), + ('reputation_score', models.PositiveIntegerField(default=0)), ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), ], diff --git a/accounts/models.py b/accounts/models.py index f0bb6f9..1db3d36 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -17,6 +17,7 @@ class CustomUser(AbstractUser): verbose_name="Show Friend Code on Link Previews", help_text="This will primarily affect share link previews on X, Discord, etc." ) + reputation_score = models.PositiveIntegerField(default=0) def __str__(self): return self.email diff --git a/accounts/urls.py b/accounts/urls.py index 597c281..88c27f4 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -5,8 +5,7 @@ from .views import ( DeleteFriendCodeView, ChangeDefaultFriendCodeView, EditFriendCodeView, - SettingsView, - ProfileView, + DashboardView, ) urlpatterns = [ @@ -16,6 +15,5 @@ urlpatterns = [ path("friend-codes/edit//", EditFriendCodeView.as_view(), name="edit_friend_code"), path("friend-codes/delete//", DeleteFriendCodeView.as_view(), name="delete_friend_code"), path("friend-codes/default//", ChangeDefaultFriendCodeView.as_view(), name="change_default_friend_code"), - path("settings/", SettingsView.as_view(), name="settings"), - path("profile/", ProfileView.as_view(), name="profile"), + path("dashboard/", DashboardView.as_view(), name="dashboard"), ] \ No newline at end of file diff --git a/accounts/views.py b/accounts/views.py index 354235e..3fb1fc5 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -7,6 +7,8 @@ from accounts.models import FriendCode, CustomUser from accounts.forms import FriendCodeForm, UserSettingsForm from django.db.models import Case, When, Value, BooleanField from trades.models import TradeOffer, TradeAcceptance +from django.core.exceptions import PermissionDenied +from trades.mixins import FriendCodeRequiredMixin class ListFriendCodesView(LoginRequiredMixin, ListView): """ @@ -127,30 +129,6 @@ class ChangeDefaultFriendCodeView(LoginRequiredMixin, View): messages.success(request, "Default friend code updated successfully.") return redirect("list_friend_codes") -# Updated SettingsView to update the new user setting. -class SettingsView(LoginRequiredMixin, UpdateView): - """ - Display account navigation links and allow the user to update their friend code - visibility setting. - """ - model = CustomUser - form_class = UserSettingsForm - template_name = "account/settings.html" - success_url = reverse_lazy("settings") - - def get_object(self): - return self.request.user - - def form_valid(self, form): - messages.success(self.request, "Settings updated successfully.") - return super().form_valid(form) - -class ProfileView(LoginRequiredMixin, TemplateView): - """ - Display the user's profile. - """ - template_name = "account/profile.html" - class EditFriendCodeView(LoginRequiredMixin, UpdateView): """ Edit the in-game name for a friend code. @@ -170,4 +148,147 @@ class EditFriendCodeView(LoginRequiredMixin, UpdateView): def form_valid(self, form): messages.success(self.request, "Friend code updated successfully.") - return super().form_valid(form) \ No newline at end of file + return super().form_valid(form) + +class DashboardView(LoginRequiredMixin, FriendCodeRequiredMixin, TemplateView): + template_name = "account/dashboard.html" + + def post(self, request, *args, **kwargs): + if 'update_settings' in request.POST: + from accounts.forms import UserSettingsForm + form = UserSettingsForm(request.POST, instance=request.user) + if form.is_valid(): + form.save() + messages.success(request, "Settings updated successfully.") + else: + messages.error(request, "Please correct the errors below.") + return self.get(request, *args, **kwargs) + + def get_selected_friend_code(self): + friend_codes = self.request.user.friend_codes.all() + friend_code_param = self.request.GET.get("friend_code") + if friend_code_param: + try: + selected_friend_code = friend_codes.get(pk=friend_code_param) + except friend_codes.model.DoesNotExist: + selected_friend_code = self.request.user.default_friend_code or friend_codes.first() + else: + selected_friend_code = self.request.user.default_friend_code or friend_codes.first() + if not selected_friend_code: + raise PermissionDenied("You do not have an active friend code associated with your account.") + return selected_friend_code + + def get_dashboard_offers_paginated(self, page_param): + from django.core.paginator import Paginator + selected_friend_code = self.get_selected_friend_code() + queryset = TradeOffer.objects.filter(initiated_by=selected_friend_code, is_closed=False) + return Paginator(queryset, 10).get_page(page_param) + + def get_involved_acceptances(self, selected_friend_code): + from django.db.models import Q + terminal_states = [ + TradeAcceptance.AcceptanceState.THANKED_BY_INITIATOR, + TradeAcceptance.AcceptanceState.THANKED_BY_ACCEPTOR, + TradeAcceptance.AcceptanceState.THANKED_BY_BOTH, + TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR, + TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR, + ] + involved = TradeAcceptance.objects.filter( + Q(trade_offer__initiated_by=selected_friend_code) | Q(accepted_by=selected_friend_code) + ).order_by("-updated_at") + return involved.exclude(state__in=terminal_states) + + def get_trade_acceptances_waiting_paginated(self, page_param): + selected_friend_code = self.get_selected_friend_code() + involved = self.get_involved_acceptances(selected_friend_code) + from django.db.models import Q + waiting = involved.filter( + Q(trade_offer__initiated_by=selected_friend_code, state__in=[ + TradeAcceptance.AcceptanceState.ACCEPTED, + TradeAcceptance.AcceptanceState.RECEIVED, + ]) | + Q(accepted_by=selected_friend_code, state__in=[TradeAcceptance.AcceptanceState.SENT]) + ) + from django.core.paginator import Paginator + return Paginator(waiting, 10).get_page(page_param) + + def get_other_party_trade_acceptances_paginated(self, page_param): + selected_friend_code = self.get_selected_friend_code() + involved = self.get_involved_acceptances(selected_friend_code) + from django.db.models import Q + waiting = involved.filter( + Q(trade_offer__initiated_by=selected_friend_code, state__in=[ + TradeAcceptance.AcceptanceState.ACCEPTED, + TradeAcceptance.AcceptanceState.RECEIVED, + ]) | + Q(accepted_by=selected_friend_code, state__in=[TradeAcceptance.AcceptanceState.SENT]) + ) + others = involved.exclude(pk__in=waiting.values("pk")) + from django.core.paginator import Paginator + return Paginator(others, 10).get_page(page_param) + + def get_closed_offers_paginated(self, page_param): + from django.core.paginator import Paginator + selected_friend_code = self.get_selected_friend_code() + queryset = TradeOffer.objects.filter(initiated_by=selected_friend_code, is_closed=True) + return Paginator(queryset, 10).get_page(page_param) + + def get_closed_acceptances_paginated(self, page_param): + from django.db.models import Q + from django.core.paginator import Paginator + selected_friend_code = self.get_selected_friend_code() + terminal_success_states = [ + TradeAcceptance.AcceptanceState.THANKED_BY_INITIATOR, + TradeAcceptance.AcceptanceState.THANKED_BY_ACCEPTOR, + TradeAcceptance.AcceptanceState.THANKED_BY_BOTH, + ] + acceptance_qs = TradeAcceptance.objects.filter( + Q(trade_offer__initiated_by=selected_friend_code) | Q(accepted_by=selected_friend_code), + state__in=terminal_success_states + ).order_by("-updated_at") + return Paginator(acceptance_qs, 10).get_page(page_param) + + def get_rejected_by_me_paginated(self, page_param): + from django.db.models import Q + from django.core.paginator import Paginator + selected_friend_code = self.get_selected_friend_code() + rejection = TradeAcceptance.objects.filter( + Q(trade_offer__initiated_by=selected_friend_code, state=TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR) | + Q(accepted_by=selected_friend_code, state=TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR) + ).order_by("-updated_at") + return Paginator(rejection, 10).get_page(page_param) + + def get_rejected_by_them_paginated(self, page_param): + from django.db.models import Q + from django.core.paginator import Paginator + selected_friend_code = self.get_selected_friend_code() + rejection = TradeAcceptance.objects.filter( + Q(trade_offer__initiated_by=selected_friend_code, state=TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR) | + Q(accepted_by=selected_friend_code, state=TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR) + ).order_by("-updated_at") + return Paginator(rejection, 10).get_page(page_param) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + request = self.request + selected_friend_code = self.get_selected_friend_code() + context["selected_friend_code"] = selected_friend_code + context["friend_codes"] = request.user.friend_codes.all() + offers_page = request.GET.get("offers_page", 1) + waiting_page = request.GET.get("waiting_page", 1) + other_page = request.GET.get("other_page", 1) + closed_offers_page = request.GET.get("closed_offers_page", 1) + closed_acceptances_page = request.GET.get("closed_acceptances_page", 1) + rejected_by_me_page = request.GET.get("rejected_by_me_page", 1) + rejected_by_them_page = request.GET.get("rejected_by_them_page", 1) + context["dashboard_offers_paginated"] = self.get_dashboard_offers_paginated(offers_page) + context["trade_acceptances_waiting_paginated"] = self.get_trade_acceptances_waiting_paginated(waiting_page) + context["other_party_trade_acceptances_paginated"] = self.get_other_party_trade_acceptances_paginated(other_page) + context["closed_offers_paginated"] = self.get_closed_offers_paginated(closed_offers_page) + context["closed_acceptances_paginated"] = self.get_closed_acceptances_paginated(closed_acceptances_page) + context["rejected_by_me_paginated"] = self.get_rejected_by_me_paginated(rejected_by_me_page) + context["rejected_by_them_paginated"] = self.get_rejected_by_them_paginated(rejected_by_them_page) + from accounts.forms import UserSettingsForm + context["settings_form"] = UserSettingsForm(instance=request.user) + context["active_tab"] = request.GET.get("tab", "dash") + return context \ No newline at end of file diff --git a/cards/migrations/0001_initial.py b/cards/migrations/0001_initial.py index 8589f72..db757c3 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-03-29 21:23 +# Generated by Django 5.1.2 on 2025-03-31 22:48 import django.db.models.deletion from django.db import migrations, models diff --git a/cards/templatetags/pagination_tags.py b/cards/templatetags/pagination_tags.py deleted file mode 100644 index a2d5ee0..0000000 --- a/cards/templatetags/pagination_tags.py +++ /dev/null @@ -1,11 +0,0 @@ -from django import template - -register = template.Library() - -@register.inclusion_tag("templatetags/pagination_controls.html", takes_context=True) -def render_pagination(context, page_obj): - """ - Renders pagination controls given a page_obj. - The controls use values like page_obj.number, page_obj.has_previous, etc. - """ - return {"page_obj": page_obj} \ No newline at end of file diff --git a/cards/views.py b/cards/views.py index cf5dc50..46e49fe 100644 --- a/cards/views.py +++ b/cards/views.py @@ -148,13 +148,13 @@ class CardListView(ReusablePaginationMixin, ListView): # For non-grouped mode, transform the built-in paginator page if "page_obj" in context: page = context["page_obj"] - # Create a unified pagination context dict + # Create a unified pagination context dict with updated keys custom_page_obj = { "number": page.number, "has_previous": page.has_previous(), "has_next": page.has_next(), - "previous_page": page.previous_page_number() if page.has_previous() else 1, - "next_page": page.next_page_number() if page.has_next() else page.paginator.num_pages, + "previous_page_number": page.previous_page_number() if page.has_previous() else 1, + "next_page_number": page.next_page_number() if page.has_next() else page.paginator.num_pages, "paginator": {"num_pages": page.paginator.num_pages}, } context["page_obj"] = custom_page_obj diff --git a/common/__init__.py b/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/common/apps.py b/common/apps.py new file mode 100644 index 0000000..5f2f078 --- /dev/null +++ b/common/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CommonConfig(AppConfig): + name = 'common' diff --git a/common/templatetags/__init__.py b/common/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/common/templatetags/pagination_tags.py b/common/templatetags/pagination_tags.py new file mode 100644 index 0000000..a9a2890 --- /dev/null +++ b/common/templatetags/pagination_tags.py @@ -0,0 +1,10 @@ +from django import template + +register = template.Library() + +@register.inclusion_tag("templatetags/pagination_controls.html", takes_context=True) +def render_pagination(context, page_obj, hide_if_one_page=True): + """ + Renders the pagination controls given a page_obj. Optionally hides the controls if there is only one page. + """ + return {"page_obj": page_obj, "hide_if_one_page": hide_if_one_page} \ No newline at end of file diff --git a/django_project/settings.py b/django_project/settings.py index dab1abf..75072e2 100644 --- a/django_project/settings.py +++ b/django_project/settings.py @@ -52,6 +52,7 @@ INSTALLED_APPS = [ "tailwind", "theme", #"django_browser_reload", + "common", "accounts", "cards", "home", diff --git a/theme/templates/account/dashboard.html b/theme/templates/account/dashboard.html new file mode 100644 index 0000000..f129dd3 --- /dev/null +++ b/theme/templates/account/dashboard.html @@ -0,0 +1,179 @@ +{% extends "base.html" %} +{% load i18n static crispy_forms_tags gravatar %} + +{% block head_title %}{{ _('Dashboard') }}{% endblock %} + +{% block content %} +
+ + +
+ + + + + + + +
+ + + + +
+
+
+

{{ _('Account Summary') }}

+

{{ _('Username:') }} {{ request.user.username }}

+

{{ _('Default Friend Code:') }} {{ selected_friend_code.friend_code }}

+

{{ _('Reputation Score:') }} {{ request.user.reputation_score }}

+
+
+
+
+

{{ _('Trade Summary') }}

+
+
+
{{ _('Your Trade Offers') }}
+
{{ dashboard_offers_paginated.paginator.count }}
+
{{ _('Active Offers') }}
+
+
+
{{ _('Waiting on You') }}
+
{{ trade_acceptances_waiting_paginated.paginator.count }}
+
{{ _('Pending Requests') }}
+
+
+
{{ _('Waiting on Them') }}
+
{{ other_party_trade_acceptances_paginated.paginator.count }}
+
{{ _('Pending Responses') }}
+
+
+
+
+
+
+

{{ _('Quick Actions') }}

+ +
+
+
+ + +
+ {% include 'trades/_trade_offer_list.html' with offers=dashboard_offers_paginated %} +
+ + +
+ {% include 'trades/_trade_offer_list.html' with offers=trade_acceptances_waiting_paginated %} +
+ + +
+ {% include 'trades/_trade_offer_list.html' with offers=other_party_trade_acceptances_paginated %} +
+ + +
+
{{ _('Closed Offers') }} ({{ closed_offers_paginated.paginator.count }})
+
+ {% include 'trades/_trade_offer_list.html' with offers=closed_offers_paginated %} +
+
{{ _('Closed Acceptances') }} ({{ closed_acceptances_paginated.paginator.count }})
+
+ {% include 'trades/_trade_offer_list.html' with offers=closed_acceptances_paginated %} +
+
{{ _('Rejected by Them') }} ({{ rejected_by_them_paginated.paginator.count }})
+
+ {% include 'trades/_trade_offer_list.html' with offers=rejected_by_them_paginated %} +
+
{{ _('Rejected by Me') }} ({{ rejected_by_me_paginated.paginator.count }})
+
+ {% include 'trades/_trade_offer_list.html' with offers=rejected_by_me_paginated %} +
+
+ + +
+
+ {% with gravatar_profile=request.user.email|gravatar_profile_data %} + {% if gravatar_profile %} +
+ {{ gravatar_profile.displayName|default:request.user.username }} +

{{ gravatar_profile.displayName|default:request.user.username }}

+ {{ _('View Gravatar Profile') }} +
+ {% else %} +

{{ _('No Gravatar profile data available.') }}

+ {% endif %} + {% endwith %} +
+

{{ _('What is Gravatar?') }}

+

{{ _('Gravatar (Globally Recognized Avatar) is a free service that links your email address to a profile picture and, optionally, a profile. Many websites use Gravatar to display your avatar automatically.') }}

+

{{ _('How does it work?') }}

+

{{ _('If you have set up a Gravatar, your profile picture will appear whenever you use your email on supported sites. Updates made on Gravatar will reflect here.') }}

+

{{ _('Is it safe? What about privacy?') }}

+

{{ _('Gravatar is optional, and your email is hashed to maintain privacy. Your personal data remains secure.') }}

+

{{ _('Want to update or add a Gravatar?') }}

+

{{ _('Go to Gravatar.com to set up or change your avatar or profile. Your changes will appear here once saved!') }}

+
+
+ + +
+
+
+ {% csrf_token %} + {{ settings_form|crispy }} + {{ _('Edit Friend Codes') }} + +
+
+ +
+ +
+ + +{% endblock %} \ No newline at end of file diff --git a/theme/templates/account/profile.html b/theme/templates/account/profile.html deleted file mode 100644 index 0ab842d..0000000 --- a/theme/templates/account/profile.html +++ /dev/null @@ -1,39 +0,0 @@ -{% extends 'base.html' %} -{% load i18n gravatar%} - -{% block head_title %}{% trans "Settings" %}{% endblock %} - -{% block content %} -
-

{% trans "Profile" %}

-
-
-
- -
-

All profile information is managed through Gravatar.

- -
-

What is Gravatar?

-

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.

- -

How does it work?

-

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.

- -

Is it safe? What about privacy?

-

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.

- -

Want to update or add a Gravatar?

-

Go to Gravatar.com to set up or change your avatar or profile. Your updates will appear here once saved!

-
-
-
-{% endblock %} \ No newline at end of file diff --git a/theme/templates/account/settings.html b/theme/templates/account/settings.html deleted file mode 100644 index 0b3ddbd..0000000 --- a/theme/templates/account/settings.html +++ /dev/null @@ -1,35 +0,0 @@ -{% extends 'base.html' %} -{% load i18n crispy_forms_tags %} - -{% block head_title %}{% trans "Settings" %}{% endblock %} - -{% block content %} -
-

{% trans "Settings" %}

- - -
-
- {% csrf_token %} - {{ form|crispy }} - -
-
- -
-
-{% endblock %} \ No newline at end of file diff --git a/theme/templates/base.html b/theme/templates/base.html index a630126..cc4d325 100644 --- a/theme/templates/base.html +++ b/theme/templates/base.html @@ -2,8 +2,7 @@ {% url 'home' as home_url %} {% url 'trade_offer_list' as trade_offer_list_url %} {% url 'cards:card_list' as cards_list_url %} -{% url 'settings' as settings_url %} - +{% url 'dashboard' as dashboard_url %} @@ -68,13 +67,7 @@ class="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
  • Home
  • Cards
  • -
  • - Trades - -
  • +
  • Trades
  • @@ -89,15 +82,7 @@ diff --git a/theme/templates/cards/_trade_offer_list.html b/theme/templates/cards/_trade_offer_list.html index 7ec36cf..d6b12d7 100644 --- a/theme/templates/cards/_trade_offer_list.html +++ b/theme/templates/cards/_trade_offer_list.html @@ -7,29 +7,6 @@ {% endfor %} - {% if is_paginated %} -
    - {% if page_obj.has_previous %} - - {% else %} - - {% endif %} - {% if paginator.num_pages > 1 %} - Page {{ page_obj.number }} of {{ paginator.num_pages }} - {% endif %} - {% if page_obj.has_next %} - - {% else %} - - {% endif %} -
    - {% endif %} {% else %}

    No trade offers found.

    {% endif %} \ No newline at end of file diff --git a/theme/templates/friend_codes/list_friend_codes.html b/theme/templates/friend_codes/list_friend_codes.html index 4629d8a..12b827b 100644 --- a/theme/templates/friend_codes/list_friend_codes.html +++ b/theme/templates/friend_codes/list_friend_codes.html @@ -36,7 +36,7 @@ {% endif %} diff --git a/theme/templates/trades/_trade_offer_list.html b/theme/templates/trades/_trade_offer_list.html index 980be3b..b1c58a6 100644 --- a/theme/templates/trades/_trade_offer_list.html +++ b/theme/templates/trades/_trade_offer_list.html @@ -1,4 +1,4 @@ -{% load trade_offer_tags %} +{% load trade_offer_tags pagination_tags %} {% comment %} This snippet renders a grid of trade offer cards (or acceptance cards) along with pagination controls. For a TradeOffer, we use {% render_trade_offer %}; for a TradeAcceptance, {% render_trade_acceptance %}. @@ -17,4 +17,5 @@ {% empty %}
    No trade offers available.
    {% endfor %} - \ No newline at end of file + +{% render_pagination offers %} \ No newline at end of file diff --git a/theme/templates/trades/_trade_offer_list_paginated.html b/theme/templates/trades/_trade_offer_list_paginated.html deleted file mode 100644 index f3ff479..0000000 --- a/theme/templates/trades/_trade_offer_list_paginated.html +++ /dev/null @@ -1,38 +0,0 @@ -{% include "trades/_trade_offer_list.html" %} -{% if offers.has_other_pages %} - -{% endif %} \ No newline at end of file diff --git a/theme/templates/trades/email/trade_update_accepted.txt b/theme/templates/trades/email/trade_update_accepted.txt new file mode 100644 index 0000000..18c4833 --- /dev/null +++ b/theme/templates/trades/email/trade_update_accepted.txt @@ -0,0 +1,16 @@ +Hello {{ recipient_user }}, + +Great news! {{ acting_user }} ({{ acting_user_friend_code }}) has accepted your trade offer. + +Trade Details: +- #{{ hash }} +- They are offering: {{ want_card }} +- They want: {{ has_card }} + +What's next? You can now mark the trade as "Sent" once you've offered the card to them in the app, or reject the trade if needed. + +Visit your dashboard to manage this trade: +{% url 'dashboard' %} + +Happy trading! +PKMN Trade Club \ No newline at end of file diff --git a/theme/templates/trades/email/trade_update_received.txt b/theme/templates/trades/email/trade_update_received.txt new file mode 100644 index 0000000..78e4e28 --- /dev/null +++ b/theme/templates/trades/email/trade_update_received.txt @@ -0,0 +1,16 @@ +Hello {{ recipient_user }}, + +{{ acting_user }} ({{ acting_user_friend_code }}) has marked your trade as "Received". + +Trade Details: +- #{{ hash }} +- Card you sent: {{ has_card }} +- Card they offered: {{ want_card }} + +What's next? Send a thank you to this user to increase their reputation! + +Visit your dashboard to send thanks: +{% url 'dashboard' %} + +Happy trading! +PKMN Trade Club \ No newline at end of file diff --git a/theme/templates/trades/email/trade_update_rejected_by_acceptor.txt b/theme/templates/trades/email/trade_update_rejected_by_acceptor.txt new file mode 100644 index 0000000..76570c7 --- /dev/null +++ b/theme/templates/trades/email/trade_update_rejected_by_acceptor.txt @@ -0,0 +1,16 @@ +Hello {{ recipient_user }}, + +We're sorry to inform you that {{ acting_user }} ({{ acting_user_friend_code }}) has canceled their trade acceptance. + +Trade Details: +- #{{ hash }} +- Card you were going to send: {{ has_card }} +- Card they were offering: {{ want_card }} + +Your trade offer is still active and available for other users to accept. + +Visit your dashboard to manage your trade offers: +{% url 'dashboard' %} + +Happy trading! +PKMN Trade Club \ No newline at end of file diff --git a/theme/templates/trades/email/trade_update_rejected_by_initiator.txt b/theme/templates/trades/email/trade_update_rejected_by_initiator.txt new file mode 100644 index 0000000..f0e7309 --- /dev/null +++ b/theme/templates/trades/email/trade_update_rejected_by_initiator.txt @@ -0,0 +1,16 @@ +Hello {{ recipient_user }}, + +We're sorry to inform you that {{ acting_user }} ({{ acting_user_friend_code }}) has rejected the trade. + +Trade Details: +- #{{ hash }} +- Card you were going to receive: {{ has_card }} +- Card you were offering: {{ want_card }} + +Don't worry - there are plenty of other trade opportunities available! You can browse our marketplace for similar trades. + +Visit the marketplace: +https://pkmntrade.club{% url 'trade_offer_list' %} + +Better luck with your next trade! +PKMN Trade Club \ No newline at end of file diff --git a/theme/templates/trades/email/trade_update_sent.txt b/theme/templates/trades/email/trade_update_sent.txt new file mode 100644 index 0000000..a49ce02 --- /dev/null +++ b/theme/templates/trades/email/trade_update_sent.txt @@ -0,0 +1,16 @@ +Hello {{ recipient_user }}, + +{{ acting_user }} ({{ acting_user_friend_code }}) has marked your trade as "Sent". + +Trade Details: +- #{{ hash }} +- Card being sent to you: {{ has_card }} +- Card you're offering: {{ want_card }} + +What's next? Once you respond to the trade in the app, please mark the trade as "Received" in your dashboard. + +Visit your dashboard to manage this trade: +{% url 'dashboard' %} + +Happy trading! +PKMN Trade Club \ No newline at end of file diff --git a/theme/templates/trades/email/trade_update_thanked_by_acceptor.txt b/theme/templates/trades/email/trade_update_thanked_by_acceptor.txt new file mode 100644 index 0000000..ec1bad1 --- /dev/null +++ b/theme/templates/trades/email/trade_update_thanked_by_acceptor.txt @@ -0,0 +1,16 @@ +Hello {{ recipient_user }}, + +{{ acting_user }} ({{ acting_user_friend_code }}) has sent their thanks for the successful trade! + +Trade Details: +- #{{ hash }} +- Card you sent: {{ has_card }} +- Card they offered: {{ want_card }} + +What's next? Send a thank you to this user to increase their reputation! + +Visit your dashboard to send thanks: +{% url 'dashboard' %} + +Happy trading! +PKMN Trade Club \ No newline at end of file diff --git a/theme/templates/trades/email/trade_update_thanked_by_both.txt b/theme/templates/trades/email/trade_update_thanked_by_both.txt new file mode 100644 index 0000000..4de1c97 --- /dev/null +++ b/theme/templates/trades/email/trade_update_thanked_by_both.txt @@ -0,0 +1,15 @@ +Hello {{ recipient_user }}, + +{{ acting_user }} ({{ acting_user_friend_code }}) has sent their thanks for the successful trade! + +Trade Details: +- #{{ hash }} +- Card {% if is_initiator %}you{% else %}they{% endif %} sent: {{ has_card }} +- Card {% if is_initiator %}they{% else %}you{% endif %} offered: {{ want_card }} + +This trade is now completed; no further actions can be made. + +Thank you for using PKMN Trade Club. + +Happy trading! +PKMN Trade Club \ No newline at end of file diff --git a/theme/templates/trades/email/trade_update_thanked_by_initiator.txt b/theme/templates/trades/email/trade_update_thanked_by_initiator.txt new file mode 100644 index 0000000..09956c0 --- /dev/null +++ b/theme/templates/trades/email/trade_update_thanked_by_initiator.txt @@ -0,0 +1,16 @@ +Hello {{ recipient_user }}, + +{{ acting_user }} ({{ acting_user_friend_code }}) has sent their thanks for the successful trade! + +Trade Details: +- #{{ hash }} +- Card they sent: {{ has_card }} +- Card you offered: {{ want_card }} + +What's next? Send a thank you to this user to increase their reputation! + +Visit your dashboard to send thanks: +{% url 'dashboard' %} + +Happy trading! +PKMN Trade Club \ No newline at end of file diff --git a/theme/templates/trades/trade_offer_all_list.html b/theme/templates/trades/trade_offer_all_list.html index 960f073..73fdcee 100644 --- a/theme/templates/trades/trade_offer_all_list.html +++ b/theme/templates/trades/trade_offer_all_list.html @@ -1,13 +1,13 @@ {% extends 'base.html' %} -{% load static %} +{% load static pagination_tags %} -{% block title %}All Trade Offers{% endblock title %} +{% block title %}Trade Offers{% endblock title %} {% block content %}
    -

    All Trade Offers

    +

    Trade Offers