Finish packaging and change to src-based packaging layout, replace caddy with haproxy for performance, and update docker-compose and Dockerfiles for new packaging.
This commit is contained in:
parent
959b06c425
commit
762361a21b
210 changed files with 235 additions and 168 deletions
0
src/pkmntrade_club/theme/__init__.py
Normal file
0
src/pkmntrade_club/theme/__init__.py
Normal file
5
src/pkmntrade_club/theme/apps.py
Normal file
5
src/pkmntrade_club/theme/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ThemeConfig(AppConfig):
|
||||
name = 'pkmntrade_club.theme'
|
||||
1777
src/pkmntrade_club/theme/static_src/package-lock.json
generated
Normal file
1777
src/pkmntrade_club/theme/static_src/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
30
src/pkmntrade_club/theme/static_src/package.json
Normal file
30
src/pkmntrade_club/theme/static_src/package.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "theme",
|
||||
"version": "3.8.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "npm run dev",
|
||||
"build": "npm run build:clean && npm run build:tailwind",
|
||||
"build:clean": "rimraf ../static/css/dist",
|
||||
"build:tailwind": "cross-env NODE_ENV=production tailwindcss --postcss -i ./src/styles.css -o ../static/css/dist/styles.css --minify",
|
||||
"dev": "cross-env NODE_ENV=development tailwindcss --postcss -i ./src/styles.css -o ../static/css/dist/styles.css -w",
|
||||
"tailwindcss": "node ./node_modules/tailwindcss/lib/cli.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/cli": "^4.0.0",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"cross-env": "^7.0.3",
|
||||
"daisyui": "^5.0.0-beta.9",
|
||||
"postcss": "^8.5.1",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-nested": "^7.0.2",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"tailwindcss": "^4.0.0"
|
||||
}
|
||||
}
|
||||
8
src/pkmntrade_club/theme/static_src/postcss.config.js
Normal file
8
src/pkmntrade_club/theme/static_src/postcss.config.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
//"postcss-import": {},
|
||||
//"postcss-simple-vars": {},
|
||||
//"postcss-nested": {},
|
||||
"@tailwindcss/postcss": {}
|
||||
},
|
||||
}
|
||||
116
src/pkmntrade_club/theme/static_src/src/styles.css
Normal file
116
src/pkmntrade_club/theme/static_src/src/styles.css
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* This is where you can configure the folders that Tailwind will scan.
|
||||
*
|
||||
* For detailed documents, check the Tailwind docs at:
|
||||
*
|
||||
* https://tailwindcss.com/docs/detecting-classes-in-source-files#explicitly-registering-sources
|
||||
*
|
||||
* This default configuration will scan all folder in your root project directory.
|
||||
*
|
||||
* Here is an example configuration that will only scan your templates/ folder:
|
||||
*
|
||||
* @import "tailwindcss" source(none);
|
||||
*
|
||||
* @source "../../../templates";
|
||||
*/
|
||||
|
||||
@import "tailwindcss" source("../../");
|
||||
|
||||
/*
|
||||
* If you would like to customise you theme, you can do that here too.
|
||||
*
|
||||
* https://tailwindcss.com/docs/theme
|
||||
*
|
||||
*/
|
||||
|
||||
@theme {
|
||||
--breakpoint-xs: 24rem;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* You can install tailwind plugins like below.
|
||||
*
|
||||
* https://tailwindcss.com/docs/functions-and-directives#plugin-directive
|
||||
*
|
||||
*/
|
||||
|
||||
@plugin "@tailwindcss/forms";
|
||||
@plugin "@tailwindcss/typography";
|
||||
@plugin "@tailwindcss/aspect-ratio";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@plugin "daisyui";
|
||||
/* @plugin "daisyui/theme" {
|
||||
name: "light";
|
||||
default: true;
|
||||
prefersdark: false;
|
||||
color-scheme: light;
|
||||
--color-base-100: oklch(100% 0 0);
|
||||
--color-base-200: oklch(98% 0 0);
|
||||
--color-base-300: oklch(95% 0 0);
|
||||
--color-base-content: oklch(21% 0.006 285.885);
|
||||
--color-primary: #CF36E0;
|
||||
--color-primary-content: oklch(100% 0 0);
|
||||
--color-secondary: #8040E0;
|
||||
--color-secondary-content: oklch(100% 0 0);
|
||||
--color-accent: #302FD9;
|
||||
--color-accent-content: oklch(100% 0 0);
|
||||
--color-neutral: oklch(37% 0 0);
|
||||
--color-neutral-content: oklch(100% 0 0);
|
||||
--color-info: #1070EB;
|
||||
--color-info-content: oklch(100% 0 0);
|
||||
--color-success: #20AA80;
|
||||
--color-success-content: oklch(100% 0 0);
|
||||
--color-warning: #EA8200;
|
||||
--color-warning-content: oklch(100% 0 0);
|
||||
--color-error: #E00202;
|
||||
--color-error-content: oklch(100% 0 0);
|
||||
--radius-selector: 0.5rem;
|
||||
--radius-field: 0rem;
|
||||
--radius-box: 0rem;
|
||||
--size-selector: 0.3125rem;
|
||||
--size-field: 0.3125rem;
|
||||
--border: 1px;
|
||||
--depth: 1;
|
||||
--noise: 0;
|
||||
}
|
||||
@plugin "daisyui/theme" {
|
||||
name: "dark";
|
||||
default: false;
|
||||
prefersdark: true;
|
||||
color-scheme: dark;
|
||||
--color-base-100: oklch(25.33% 0.016 252.42);
|
||||
--color-base-200: oklch(23.26% 0.014 253.1);
|
||||
--color-base-300: oklch(21.15% 0.012 254.09);
|
||||
--color-base-content: oklch(97.807% 0.029 256.847);
|
||||
--color-primary: #CF36E0;
|
||||
--color-primary-content: oklch(100% 0 0);
|
||||
--color-secondary: #8040E0;
|
||||
--color-secondary-content: oklch(100% 0 0);
|
||||
--color-accent: #302FD9;
|
||||
--color-accent-content: oklch(100% 0 0);
|
||||
--color-neutral: oklch(37% 0 0);
|
||||
--color-neutral-content: oklch(100% 0 0);
|
||||
--color-info: #1070EB;
|
||||
--color-info-content: oklch(100% 0 0);
|
||||
--color-success: #20AA80;
|
||||
--color-success-content: oklch(100% 0 0);
|
||||
--color-warning: #EA8200;
|
||||
--color-warning-content: oklch(100% 0 0);
|
||||
--color-error: #E00202;
|
||||
--color-error-content: oklch(100% 0 0);
|
||||
--radius-selector: 0.5rem;
|
||||
--radius-field: 0rem;
|
||||
--radius-box: 0rem;
|
||||
--size-selector: 0.3125rem;
|
||||
--size-field: 0.3125rem;
|
||||
--border: 1px;
|
||||
--depth: 1;
|
||||
--noise: 0;
|
||||
} */
|
||||
73
src/pkmntrade_club/theme/static_src/tailwind.config.js
Normal file
73
src/pkmntrade_club/theme/static_src/tailwind.config.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* This is a minimal config.
|
||||
*
|
||||
* If you need the full config, get it from here:
|
||||
* https://unpkg.com/browse/tailwindcss@latest/stubs/defaultConfig.stub.js
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
content: [
|
||||
/**
|
||||
* HTML. Paths to Django template files that will contain Tailwind CSS classes.
|
||||
*/
|
||||
|
||||
/* Templates within theme app (<tailwind_app_name>/templates), e.g. base.html. */
|
||||
"../templates/**/*.html",
|
||||
|
||||
/*
|
||||
* Main templates directory of the project (BASE_DIR/templates).
|
||||
* Adjust the following line to match your project structure.
|
||||
*/
|
||||
"../../templates/**/*.html",
|
||||
|
||||
/*
|
||||
* Templates in other django apps (BASE_DIR/<any_app_name>/templates).
|
||||
* Adjust the following line to match your project structure.
|
||||
*/
|
||||
"../../**/templates/**/*.html",
|
||||
|
||||
/**
|
||||
* JS: If you use Tailwind CSS in JavaScript, uncomment the following lines and make sure
|
||||
* patterns match your project structure.
|
||||
*/
|
||||
/* JS 1: Ignore any JavaScript in node_modules folder. */
|
||||
// '!../../**/node_modules',
|
||||
/* JS 2: Process all JavaScript files in the project. */
|
||||
// '../../**/*.js',
|
||||
|
||||
/**
|
||||
* Python: If you use Tailwind CSS classes in Python, uncomment the following line
|
||||
* and make sure the pattern below matches your project structure.
|
||||
*/
|
||||
// '../../**/*.py'
|
||||
],
|
||||
safelist: [
|
||||
"alert-info",
|
||||
"alert-success",
|
||||
"alert-warning",
|
||||
"alert-error",
|
||||
"btn-info",
|
||||
"btn-success",
|
||||
"btn-warning",
|
||||
"btn-error",
|
||||
"bg-info",
|
||||
"bg-success",
|
||||
"bg-warning",
|
||||
"bg-error",
|
||||
"text-gray-700",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
/**
|
||||
* '@tailwindcss/forms' is the forms plugin that provides a minimal styling
|
||||
* for forms. If you don't like it or have own styling for forms,
|
||||
* comment the line below to disable '@tailwindcss/forms'.
|
||||
*/
|
||||
require("@tailwindcss/forms"),
|
||||
require("@tailwindcss/typography"),
|
||||
require("@tailwindcss/aspect-ratio"),
|
||||
],
|
||||
darkMode: "class",
|
||||
};
|
||||
10
src/pkmntrade_club/theme/templates/403_csrf.html
Normal file
10
src/pkmntrade_club/theme/templates/403_csrf.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}Forbidden (403){% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-12 text-center">
|
||||
<h1 class="text-5xl font-bold mb-4">Forbidden (403)</h1>
|
||||
<p class="text-xl mb-4">CSRF verification failed. Request aborted.</p>
|
||||
<a href="{% url 'home' %}" class="btn btn-primary">Return Home</a>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
10
src/pkmntrade_club/theme/templates/404.html
Normal file
10
src/pkmntrade_club/theme/templates/404.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}404 Page not found{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-12 text-center">
|
||||
<h1 class="text-5xl font-bold mb-4">404</h1>
|
||||
<p class="text-xl mb-4">Page not found</p>
|
||||
<a href="{% url 'home' %}" class="btn btn-primary">Return Home</a>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
10
src/pkmntrade_club/theme/templates/500.html
Normal file
10
src/pkmntrade_club/theme/templates/500.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}500 Server Error{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-12 text-center">
|
||||
<h1 class="text-5xl font-bold mb-4">500</h1>
|
||||
<p class="text-xl mb-4">Looks like something went wrong!</p>
|
||||
<a href="{% url 'home' %}" class="btn btn-primary">Return Home</a>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
10
src/pkmntrade_club/theme/templates/_messages.html
Normal file
10
src/pkmntrade_club/theme/templates/_messages.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{% if messages %}
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for message in messages %}
|
||||
<div class="alert {% if message.tags %}alert-{{ message.tags }} text-(--color-{{ message.tags }}-content){% else %}alert-info text-(--color-info-content){% endif %} font-semibold mb-4 flex justify-between items-center">
|
||||
<span>{{ message }}</span>
|
||||
<button class="btn btn-xs btn-circle border-none bg-black/20" onclick="this.parentElement.remove();" aria-label="Dismiss">✕</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth %}
|
||||
|
||||
{% block head_title %}{% trans "Account Inactive" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6 text-center">
|
||||
<h1 class="text-3xl font-bold mb-6">{% trans "Account Inactive" %}</h1>
|
||||
<p>
|
||||
{% trans "This account is inactive." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth account %}
|
||||
|
||||
{% block head_title %}{% trans "Email Verification" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Enter Email Verification Code" %}</h1>
|
||||
<p class="text-center mb-4">
|
||||
<a class="text-primary underline" href="mailto:{{ email }}">{{ email }}</a>
|
||||
</p>
|
||||
{% url 'account_email_verification_sent' as action_url %}
|
||||
<form method="post" action="{{ action_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div>
|
||||
<label for="{{ form.code.id_for_label }}" class="block font-medium>{% trans "Verification Code" %}</label>
|
||||
{{ form.code }}
|
||||
{{ form.code.errors }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Confirm" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth account %}
|
||||
|
||||
{% block head_title %}{% trans "Sign In" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Enter Sign-In Code" %}</h1>
|
||||
<p class="text-center mb-4">
|
||||
{% if email %}
|
||||
<a class="text-primary underline" href="mailto:{{ email }}">{{ email }}</a>
|
||||
{% else %}
|
||||
<a class="text-primary underline" href="tel:{{ phone }}">{{ phone }}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% url 'account_confirm_login_code' as action_url %}
|
||||
<form method="post" action="{{ action_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div>
|
||||
<label for="{{ form.code.id_for_label }}" class="block font-medium>{% trans "Sign-In Code" %}</label>
|
||||
{{ form.code }}
|
||||
{{ form.code.errors }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Confirm" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth account %}
|
||||
|
||||
{% block head_title %}{% trans "Password Reset" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Enter Password Reset Code" %}</h1>
|
||||
<p class="text-center mb-4">
|
||||
<a class="text-primary underline" href="mailto:{{ email }}">{{ email }}</a>
|
||||
</p>
|
||||
{% url 'account_confirm_password_reset_code' as action_url %}
|
||||
<form method="post" action="{{ action_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div>
|
||||
<label for="{{ form.code.id_for_label }}" class="block font-medium>{% trans "Reset Code" %}</label>
|
||||
{{ form.code }}
|
||||
{{ form.code.errors }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Confirm" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth account %}
|
||||
|
||||
{% block head_title %}{% trans "Phone Verification" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Enter Phone Verification Code" %}</h1>
|
||||
<p class="text-center mb-4">
|
||||
<a class="text-primary underline" href="tel:{{ phone }}">{{ phone }}</a>
|
||||
</p>
|
||||
{% url 'account_verify_phone' as action_url %}
|
||||
<form method="post" action="{{ action_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div>
|
||||
<label for="{{ form.code.id_for_label }}" class="block font-medium>{% trans "Verification Code" %}</label>
|
||||
{{ form.code }}
|
||||
{{ form.code.errors }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Confirm" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
228
src/pkmntrade_club/theme/templates/account/dashboard.html
Normal file
228
src/pkmntrade_club/theme/templates/account/dashboard.html
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n static crispy_forms_tags gravatar %}
|
||||
|
||||
{% block head_title %}{{ _('Dashboard') }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto" x-data="{ activeTab: '{{ active_tab|default:'dash' }}' }">
|
||||
|
||||
<!-- Tab Navigation -->
|
||||
<div class="tabs tabs-border mb-8">
|
||||
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'dash'}" @click="activeTab = 'dash'">{{ _('Dash') }}</button>
|
||||
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'dashboard_offers'}" @click="activeTab = 'dashboard_offers'">{{ _('Your Trade Offers') }} ({{ dashboard_offers_paginated.page_obj.count }})</button>
|
||||
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'waiting_on_you'}" @click="activeTab = 'waiting_on_you'">{{ _('Waiting on You') }} ({{ trade_acceptances_waiting_paginated.page_obj.count }})</button>
|
||||
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'waiting_on_them'}" @click="activeTab = 'waiting_on_them'">{{ _('Waiting on Them') }} ({{ other_party_trade_acceptances_paginated.page_obj.count }})</button>
|
||||
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'trade_history'}" @click="activeTab = 'trade_history'">{{ _('Trade History') }}</button>
|
||||
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'profile'}" @click="activeTab = 'profile'">{{ _('Profile') }}</button>
|
||||
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'friend_codes'}" @click="activeTab = 'friend_codes'">{{ _('Friend Codes') }}</button>
|
||||
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'settings'}" @click="activeTab = 'settings'">{{ _('Settings') }}</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab Panels -->
|
||||
|
||||
<!-- Dash Tab - Dashboard Summary -->
|
||||
<div x-show="activeTab === 'dash'">
|
||||
<div class="card bg-base-100 shadow-xl mb-4">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-2">{{ _('Trade Summary') }}</h2>
|
||||
<div class="flex flex-col md:flex-row justify-center gap-4">
|
||||
<div class="stats shadow-lg bg-base-300">
|
||||
<div class="stat">
|
||||
<div class="stat-title">{{ _('Your Reputation') }}</div>
|
||||
<div class="stat-value">{{ request.user.reputation_score }}</div>
|
||||
<div class="stat-desc">{{ _('Current Score') }}</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-title">{{ _('Your Trade Offers') }}</div>
|
||||
<div class="stat-value">{{ dashboard_offers_paginated.page_obj.count }}</div>
|
||||
<div class="stat-desc">{{ _('Active Offers') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats shadow-lg bg-base-300">
|
||||
<div class="stat">
|
||||
<div class="stat-title">{{ _('Waiting on You') }}</div>
|
||||
<div class="stat-value">{{ trade_acceptances_waiting_paginated.page_obj.count }}</div>
|
||||
<div class="stat-desc">{{ _('Pending Requests') }}</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-title">{{ _('Waiting on Them') }}</div>
|
||||
<div class="stat-value">{{ other_party_trade_acceptances_paginated.page_obj.count }}</div>
|
||||
<div class="stat-desc">{{ _('Pending Responses') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-2">{{ _('Quick Actions') }}</h2>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Your Trade Offers Tab -->
|
||||
<div x-show="activeTab === 'dashboard_offers'" x-data="tradeOffersPagination('{% url 'dashboard' %}?ajax_section=dashboard_offers')">
|
||||
{% include 'trades/_trade_offer_list.html' with offers=dashboard_offers_paginated.object_list page_obj=dashboard_offers_paginated.page_obj %}
|
||||
</div>
|
||||
|
||||
<!-- Waiting on You Tab -->
|
||||
<div x-show="activeTab === 'waiting_on_you'" x-data="tradeOffersPagination('{% url 'dashboard' %}?ajax_section=waiting_acceptances')">
|
||||
{% include 'trades/_trade_offer_list.html' with offers=trade_acceptances_waiting_paginated.object_list page_obj=trade_acceptances_waiting_paginated.page_obj %}
|
||||
</div>
|
||||
|
||||
<!-- Waiting on Them Tab -->
|
||||
<div x-show="activeTab === 'waiting_on_them'" x-data="tradeOffersPagination('{% url 'dashboard' %}?ajax_section=other_party_acceptances')">
|
||||
{% include 'trades/_trade_offer_list.html' with offers=other_party_trade_acceptances_paginated.object_list page_obj=other_party_trade_acceptances_paginated.page_obj %}
|
||||
</div>
|
||||
|
||||
<!-- Trade History Tab -->
|
||||
<div x-show="activeTab === 'trade_history'">
|
||||
<div class="divider">{{ _('Closed Offers') }} ({{ closed_offers_paginated.page_obj.count }})</div>
|
||||
<div class="mb-8">
|
||||
{% include 'trades/_trade_offer_list.html' with offers=closed_offers_paginated.object_list page_obj=closed_offers_paginated.page_obj %}
|
||||
</div>
|
||||
<div class="divider">{{ _('Closed Acceptances') }} ({{ closed_acceptances_paginated.page_obj.count }})</div>
|
||||
<div class="mb-8">
|
||||
{% include 'trades/_trade_offer_list.html' with offers=closed_acceptances_paginated.object_list page_obj=closed_acceptances_paginated.page_obj %}
|
||||
</div>
|
||||
<div class="divider">{{ _('Rejected by Them') }} ({{ rejected_by_them_paginated.page_obj.count }})</div>
|
||||
<div class="mb-8">
|
||||
{% include 'trades/_trade_offer_list.html' with offers=rejected_by_them_paginated.object_list page_obj=rejected_by_them_paginated.page_obj %}
|
||||
</div>
|
||||
<div class="divider">{{ _('Rejected by Me') }} ({{ rejected_by_me_paginated.page_obj.count }})</div>
|
||||
<div class="mb-8">
|
||||
{% include 'trades/_trade_offer_list.html' with offers=rejected_by_me_paginated.object_list page_obj=rejected_by_me_paginated.page_obj %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Profile Tab -->
|
||||
<div x-show="activeTab === 'profile'">
|
||||
<div class="card card-border bg-base-100 shadow-lg mx-auto p-6 mb-4">
|
||||
{% with gravatar_profile=request.user.email|gravatar_profile_data %}
|
||||
|
||||
<div class="hovercard-profile mb-4 text-center">
|
||||
<div class="avatar block mx-auto max-w-32">
|
||||
<div class="rounded-full">
|
||||
{{ request.user.email|gravatar:128 }}
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://gravatar.com/profile/avatars" target="_blank" rel="noopener noreferrer" class="btn btn-primary mt-4">
|
||||
Edit Avatar on Gravatar
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
{% endwith %}
|
||||
<div class="divider"></div>
|
||||
<h2 class="text-base font-semibold pt-0">What is Gravatar?</h2>
|
||||
<p class="mb-4 text-sm">Gravatar (Globally Recognized Avatar) is a free service that links your email address to a profile picture. Many websites, including this one, use Gravatar to display your preferred avatar automatically.</p>
|
||||
|
||||
<h2 class="text-base font-semibold">How does it work?</h2>
|
||||
<p class="mb-4 text-sm">If you've set up a Gravatar, your profile picture will appear here whenever you use your email on supported sites. If you don't have a Gravatar yet, you'll see a default randomly-generated avatar instead.</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 visible to anyone and only a hashed version is shown on the page and sent to Gravatar, protecting your identity while ensuring that your email address is not exposed to bots or scrapers.</p>
|
||||
|
||||
<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. Your updates will appear here once saved!</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Friend Codes Tab -->
|
||||
<div x-show="activeTab === 'friend_codes'">
|
||||
<div class="card card-border bg-base-100 shadow-lg mx-auto p-6 mb-4">
|
||||
{% 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 %}" class="link link-hover">{{ 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 justify-end">
|
||||
<a href="{% url 'add_friend_code' %}" class="btn btn-primary">Add a New Friend Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Tab -->
|
||||
<div x-show="activeTab === 'settings'">
|
||||
<div class="card card-border bg-base-100 shadow-lg mx-auto p-6 mb-4">
|
||||
<form method="post" action="{% url 'dashboard' %}">
|
||||
{% csrf_token %}
|
||||
{{ settings_form|crispy }}
|
||||
<div class="flex justify-end">
|
||||
<button type="submit" name="update_settings" class="btn btn-success mt-4">{{ _('Save Settings') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function tradeOffersPagination(baseUrl) {
|
||||
return {
|
||||
baseUrl: baseUrl,
|
||||
_hasChangePageListener: false,
|
||||
loadPage(page) {
|
||||
let url = new URL(this.baseUrl, window.location.origin);
|
||||
url.searchParams.set("page", page);
|
||||
fetch(url, {
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
this.$el.innerHTML = html;
|
||||
this.init();
|
||||
window.processMarqueeElements && window.processMarqueeElements();
|
||||
});
|
||||
},
|
||||
init() {
|
||||
if (!this._hasChangePageListener) {
|
||||
this.$el.addEventListener('change-page', event => {
|
||||
let page = event.detail.page;
|
||||
this.loadPage(page);
|
||||
});
|
||||
this._hasChangePageListener = true;
|
||||
}
|
||||
this.$el.querySelectorAll("a.ajax-page-link").forEach(link => {
|
||||
link.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
let page = link.getAttribute("data-page");
|
||||
this.loadPage(page);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
67
src/pkmntrade_club/theme/templates/account/email.html
Normal file
67
src/pkmntrade_club/theme/templates/account/email.html
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static allauth i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Email Addresses" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Email Addresses" %}</h1>
|
||||
|
||||
{% if emailaddresses %}
|
||||
<p class="mb-4">
|
||||
{% trans "The following email addresses are associated with your account:" %}
|
||||
</p>
|
||||
{% url 'account_email' as email_url %}
|
||||
<form method="post" action="{{ email_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{% for radio in emailaddress_radios %}
|
||||
<div class="flex items-center">
|
||||
<input type="radio" id="{{ radio.id }}" name="email" value="{{ radio.emailaddress.email }}" class="mr-2" {% if radio.checked %}checked{% endif %}>
|
||||
<label for="{{ radio.id }}">
|
||||
{{ radio.emailaddress.email }}
|
||||
{% if radio.emailaddress.verified %}
|
||||
<span class="ml-1 bg-green-200 text-green-800 px-2 py-1 rounded text-xs">{% trans "Verified" %}</span>
|
||||
{% else %}
|
||||
<span class="ml-1 bg-yellow-200 text-yellow-800 px-2 py-1 rounded text-xs">{% trans "Unverified" %}</span>
|
||||
{% endif %}
|
||||
{% if radio.emailaddress.primary %}
|
||||
<span class="ml-1 bg-blue-200 text-blue-800 px-2 py-1 rounded text-xs">{% trans "Primary" %}</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="flex flex-col md:flex-row md:space-x-2">
|
||||
<button type="submit" name="action_primary" class="btn btn-primary w-full">{% trans "Make Primary" %}</button>
|
||||
<button type="submit" name="action_send" class="btn btn-secondary w-full">{% trans "Re-send Verification" %}</button>
|
||||
<button type="submit" name="action_remove" class="btn btn-danger w-full">{% trans "Remove" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
{% include "account/snippets/warn_no_email.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% if can_add_email %}
|
||||
<h2 class="text-2xl font-bold mt-8 mb-4">{% trans "Add Email Address" %}</h2>
|
||||
{% url 'account_email' as action_url %}
|
||||
<form method="post" action="{{ action_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
<label for="{{ form.email.id_for_label }}" class="block font-medium>{{ form.email.label }}</label>
|
||||
{{ form.email }}
|
||||
{{ form.email.errors }}
|
||||
</div>
|
||||
<button type="submit" name="action_add" class="btn btn-primary w-full">{% trans "Add Email" %}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
{% block extra_body %}
|
||||
<script src="{% static 'account/js/account.js' %}"></script>
|
||||
<script src="{% static 'account/js/onload.js' %}"></script>
|
||||
<script data-allauth-onload="allauth.account.forms.manageEmailForm" type="application/json">
|
||||
{
|
||||
"i18n": {"confirmDelete": "{% trans 'Do you really want to remove the selected email address?' %}"}
|
||||
}
|
||||
</script>
|
||||
{% endblock extra_body %}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{% extends "account/email/base_message.txt" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}{% autoescape off %}{% blocktrans %}You are receiving this email because you or someone else tried to signup for an
|
||||
account using email address:
|
||||
|
||||
{{ email }}
|
||||
|
||||
However, an account using that email address already exists. In case you have
|
||||
forgotten about this, please use the password forgotten procedure to recover
|
||||
your account:
|
||||
|
||||
{{ password_reset_url }}{% endblocktrans %}{% endautoescape %}{% endblock content %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Account Already Exists{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% load i18n %}{% autoescape off %}{% blocktrans with site_name=current_site.name %}Hello from {{ site_name }}!{% endblocktrans %}
|
||||
|
||||
{% block content %}{% endblock content %}
|
||||
|
||||
{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Thank you for using {{ site_name }}!
|
||||
{{ site_domain }}{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{% extends "account/email/base_message.txt" %}
|
||||
{% load account %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}{% autoescape off %}{% blocktrans %}You are receiving this mail because the following change was made to your account:{% endblocktrans %}
|
||||
|
||||
{% block notification_message %}
|
||||
{% endblock notification_message%}
|
||||
|
||||
{% blocktrans %}If you do not recognize this change then please take proper security precautions immediately. The change to your account originates from:
|
||||
|
||||
- IP address: {{ip}}
|
||||
- Browser: {{user_agent}}
|
||||
- Date: {{timestamp}}{% endblocktrans %}{% endautoescape %}{% endblock %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "account/email/base_notification.txt" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block notification_message %}{% blocktrans %}Your email has been changed from {{ from_email }} to {{ to_email }}.{% endblocktrans %}{% endblock notification_message %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Email Changed{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "account/email/base_notification.txt" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block notification_message %}{% blocktrans %}Your email has been confirmed.{% endblocktrans %}{% endblock notification_message %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Email Confirmation{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{% extends "account/email/base_message.txt" %}
|
||||
{% load account %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}{% autoescape off %}{% user_display user as user_display %}{% blocktranslate with site_name=current_site.name site_domain=current_site.domain %}You're receiving this email because user {{ user_display }} has given your email address to register an account on {{ site_domain }}.{% endblocktranslate %}
|
||||
|
||||
{% if code %}{% blocktranslate %}Your email verification code is listed below. Please enter it in your open browser window.{% endblocktranslate %}
|
||||
|
||||
{{ code }}{% else %}{% blocktranslate %}To confirm this is correct, go to {{ activate_url }}{% endblocktranslate %}{% endif %}{% endautoescape %}{% endblock content %}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{% include "account/email/email_confirmation_message.txt" %}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{% include "account/email/email_confirmation_subject.txt" %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Please Confirm Your Email Address{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "account/email/base_notification.txt" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block notification_message %}{% blocktrans %}Email address {{ deleted_email }} has been removed from your account.{% endblocktrans %}{% endblock notification_message %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Email Removed{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{% extends "account/email/base_message.txt" %}
|
||||
{% load account %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}{% autoescape off %}{% blocktranslate %}Your sign-in code is listed below. Please enter it in your open browser window.{% endblocktranslate %}{% endautoescape %}
|
||||
|
||||
{{ code }}
|
||||
|
||||
{% blocktranslate %}This mail can be safely ignored if you did not initiate this action.{% endblocktranslate %}{% endblock content %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Sign-In Code{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "account/email/base_notification.txt" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block notification_message %}{% blocktrans %}Your password has been changed.{% endblocktrans %}{% endblock notification_message %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Password Changed{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{% extends "account/email/base_message.txt" %}
|
||||
{% load account %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}{% autoescape off %}{% blocktranslate %}Your password reset code is listed below. Please enter it in your open browser window.{% endblocktranslate %}{% endautoescape %}
|
||||
|
||||
{{ code }}
|
||||
|
||||
{% blocktranslate %}This mail can be safely ignored if you did not initiate this action.{% endblocktranslate %}{% endblock content %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Password Reset Code{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{% load i18n %}
|
||||
{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Hello from {{ site_name }}!
|
||||
|
||||
We've received a request to reset your password. If you didn't make this request, you can safely ignore this email. Otherwise, click the button below to reset your password.{% endblocktrans %}
|
||||
|
||||
{{ password_reset_url }}
|
||||
|
||||
{% if username %}{% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %}
|
||||
|
||||
{% endif %}{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Thank you for using {{ site_name }}!
|
||||
{{ site_domain }}{% endblocktrans %}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
Password Reset E-mail
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "account/email/base_notification.txt" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block notification_message %}{% blocktrans %}Your password has been reset.{% endblocktrans %}{% endblock notification_message %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Password Reset{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "account/email/base_notification.txt" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block notification_message %}{% blocktrans %}Your password has been set.{% endblocktrans %}{% endblock notification_message %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Password Set{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{% extends "account/email/base_message.txt" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}{% autoescape off %}{% blocktranslate %}You are receiving this email because you, or someone else, tried to access an account with email {{ email }}. However, we do not have any record of such an account in our database.{% endblocktranslate %}
|
||||
|
||||
{% blocktranslate %}This mail can be safely ignored if you did not initiate this action.{% endblocktranslate %}
|
||||
|
||||
{% blocktranslate %}If it was you, you can sign up for an account using the link below.{% endblocktranslate %}
|
||||
|
||||
{{ signup_url }}{% endautoescape %}{% endblock content %}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% blocktrans %}Unknown Account{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
59
src/pkmntrade_club/theme/templates/account/email_change.html
Normal file
59
src/pkmntrade_club/theme/templates/account/email_change.html
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth %}
|
||||
|
||||
{% block head_title %}{% trans "Email Address" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Email Address" %}</h1>
|
||||
{% if not emailaddresses %}
|
||||
{% include "account/snippets/warn_no_email.html" %}
|
||||
{% endif %}
|
||||
{% url 'account_email' as action_url %}
|
||||
<form method="post" action="{{ action_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{% if current_emailaddress %}
|
||||
<div>
|
||||
<label for="current_email" class="block font-medium>{% trans "Current email" %}:</label>
|
||||
<input id="current_email" type="email" value="{{ current_emailaddress.email }}" disabled class="input input-bordered w-full">
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if new_emailaddress %}
|
||||
<div>
|
||||
<label for="new_email" class="block font-medium>
|
||||
{% if not current_emailaddress %}
|
||||
{% trans "Current email" %}:
|
||||
{% else %}
|
||||
{% trans "Changing to" %}:
|
||||
{% endif %}
|
||||
</label>
|
||||
<input id="new_email" type="email" value="{{ new_emailaddress.email }}" disabled class="input input-bordered w-full">
|
||||
<p class="text-sm text-gray-600 mt-1">
|
||||
{% trans "Your email address is still pending verification." %}
|
||||
</p>
|
||||
<div class="flex space-x-2 mt-2">
|
||||
<button form="pending-email" type="submit" name="action_send" class="btn btn-secondary btn-sm">{% trans "Re-send Verification" %}</button>
|
||||
{% if current_emailaddress %}
|
||||
<button form="pending-email" type="submit" name="action_remove" class="btn btn-danger btn-sm">{% trans "Cancel Change" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<label for="{{ form.email.id_for_label }}" class="block font-medium>{% trans "Change to" %}:</label>
|
||||
{{ form.email }}
|
||||
{{ form.email.errors }}
|
||||
</div>
|
||||
{{ redirect_field }}
|
||||
<button type="submit" name="action_add" class="btn btn-primary w-full">{% trans "Change Email" %}</button>
|
||||
</form>
|
||||
|
||||
{% if new_emailaddress %}
|
||||
<form style="display: none" id="pending-email" method="post" action="{% url 'account_email' %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="email" value="{{ new_emailaddress.email }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load account allauth %}
|
||||
|
||||
{% block head_title %}{% trans "Confirm Email Address" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Confirm Email Address" %}</h1>
|
||||
{% if confirmation %}
|
||||
{% user_display confirmation.email_address.user as user_display %}
|
||||
{% if can_confirm %}
|
||||
<p class="mb-4">
|
||||
{% blocktrans with confirmation.email_address.email as email %}
|
||||
Please confirm that <a class="text-primary underline" href="mailto:{{ email }}">{{ email }}</a> is an email address for user {{ user_display }}.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% url 'account_confirm_email' confirmation.key as action_url %}
|
||||
<form method="post" action="{{ action_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ redirect_field }}
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Confirm" %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>
|
||||
{% blocktrans %}Unable to confirm {{ confirmation.email_address.email }} because it is already confirmed by a different account.{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% url 'account_email' as email_url %}
|
||||
<p>
|
||||
{% blocktrans %}This email confirmation link expired or is invalid. Please <a class="text-primary underline" href="{{ email_url }}">issue a new email confirmation request</a>.{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
34
src/pkmntrade_club/theme/templates/account/login.html
Normal file
34
src/pkmntrade_club/theme/templates/account/login.html
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Log In" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Log In" %}</h1>
|
||||
<form method="post" action="{% url 'account_login' %}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div>
|
||||
<label for="{{ form.login.id_for_label }}" class="block font-medium">{{ form.login.label }}</label>
|
||||
{{ form.login }}
|
||||
{{ form.login.errors }}
|
||||
</div>
|
||||
<div>
|
||||
<label for="{{ form.password.id_for_label }}" class="block font-medium">{{ form.password.label }}</label>
|
||||
{{ form.password }}
|
||||
{{ form.password.errors }}
|
||||
</div>
|
||||
{% if form.remember %}
|
||||
<div class="flex items-center">
|
||||
{{ form.remember }}
|
||||
<label for="{{ form.remember.id_for_label }}" class="ml-2">{% trans "Remember Me" %}</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Log In" %}</button>
|
||||
</form>
|
||||
<div class="mt-4 text-center">
|
||||
<a href="{% url 'account_request_login_code' %}" class="text-primary underline">{% trans "Login by Code" %}</a> | <a href="{% url 'account_reset_password' %}" class="text-primary underline">{% trans "Forgot Password?" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
16
src/pkmntrade_club/theme/templates/account/logout.html
Normal file
16
src/pkmntrade_club/theme/templates/account/logout.html
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Log Out" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h2 class="text-3xl font-bold text-center mb-6">{% trans "Sign Out" %}</h2>
|
||||
<p class="text-center mb-6">{% trans "Are you sure you want to sign out?" %}</p>
|
||||
<form method="post" action="{% url 'account_logout' %}" class="space-y-4 text-center">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button class="btn btn-error w-full" type="submit">{% trans "Sign Out" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Change Password" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h2 class="text-3xl font-bold text-center mb-6">{% trans "Change Password" %}</h2>
|
||||
<form method="post" action="{% url 'account_change_password' %}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button class="btn btn-success w-full" type="submit">{% trans "Change Password" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags i18n widget_tweaks %}
|
||||
|
||||
{% block head_title %}{% trans "Reset Password" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h2 class="text-3xl font-bold text-center mb-6">{% trans "Reset Password" %}</h2>
|
||||
<p class="mb-4 text-center">{% trans "Enter your email address and we'll send you a link to reset your password." %}</p>
|
||||
<form method="post" action="{% url 'account_reset_password' %}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div>
|
||||
<label for="{{ form.email.id_for_label }}" class="block font-medium>{{ form.email.label }}</label>
|
||||
{{ form.email|add_class:"input input-bordered w-full" }}
|
||||
{{ form.email.errors }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Reset Password" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Password Reset Done" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6 text-center">
|
||||
<h2 class="text-3xl font-bold mb-4">{% trans "Password Reset" %}</h2>
|
||||
<p>{% trans "We have sent you an e-mail. Please contact us if you do not receive it in a few minutes." %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Change Password" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
{% if token_fail %}
|
||||
<h2 class="text-3xl font-bold text-center mb-4">{% trans "Bad Token" %}</h2>
|
||||
<p class="mb-4 text-center">
|
||||
{% trans "The password reset link was invalid. Perhaps it has already been used? Please request a" %}
|
||||
<a href="{% url 'account_reset_password' %}" class="text-primary underline">{% trans "new password reset" %}</a>.
|
||||
</p>
|
||||
{% else %}
|
||||
{% if form %}
|
||||
<h2 class="text-3xl font-bold text-center mb-6">{% trans "Change Password" %}</h2>
|
||||
<form method="POST" action="." class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button class="btn btn-primary w-full" type="submit">{% trans "Change Password" %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<h2 class="text-3xl font-bold text-center mb-4">{% trans "Password Changed" %}</h2>
|
||||
<p class="text-center">{% trans "Your password is now changed." %}</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Password Change Done" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6 text-center">
|
||||
<h2 class="text-3xl font-bold mb-4">{% trans "Password Change Done" %}</h2>
|
||||
<p>{% trans "Your password has been changed." %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load allauth i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Reauthenticate" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<p class="mb-4">{% trans "Enter your password:" %}</p>
|
||||
{% url 'account_reauthenticate' as action_url %}
|
||||
<form method="post" action="{{ action_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
{% for field in form %}
|
||||
<div>
|
||||
<label for="{{ field.id_for_label }}" class="block font-medium>{{ field.label }}</label>
|
||||
{{ field }}
|
||||
{{ field.errors }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{{ redirect_field }}
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Confirm" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth account %}
|
||||
|
||||
{% block head_title %}{% trans "Sign In" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Send me a sign-in code" %}</h1>
|
||||
<p class="mb-4 text-center">
|
||||
{% trans "You will receive a special code for a password-free sign-in." %}
|
||||
</p>
|
||||
{% url 'account_request_login_code' as login_url %}
|
||||
<form method="post" action="{{ login_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
{% for field in form %}
|
||||
<div>
|
||||
<label for="{{ field.id_for_label }}" class="block font-medium>{{ field.label }}</label>
|
||||
{{ field }}
|
||||
{{ field.errors }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{{ redirect_field }}
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Request Code" %}</button>
|
||||
</form>
|
||||
{% url 'account_login' as login_url %}
|
||||
<div class="mt-4 text-center">
|
||||
<a href="{{ login_url }}" class="text-primary underline">{% trans "Other sign-in options" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
49
src/pkmntrade_club/theme/templates/account/signup.html
Normal file
49
src/pkmntrade_club/theme/templates/account/signup.html
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
{% block head_title %}{% trans "Sign Up" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Sign Up" %}</h1>
|
||||
<form method="post" action="{% url 'account_signup' %}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div>
|
||||
<label for="{{ form.username.id_for_label }}" class="block font-medium">{{ form.username.label }}</label>
|
||||
{{ form.username }}
|
||||
{{ form.username.errors }}
|
||||
</div>
|
||||
<div>
|
||||
<label for="{{ form.email.id_for_label }}" class="block font-medium">{{ form.email.label }}</label>
|
||||
{{ form.email }}
|
||||
{{ form.email.errors }}
|
||||
</div>
|
||||
<div>
|
||||
<label for="{{ form.password1.id_for_label }}" class="block font-medium">{{ form.password1.label }}</label>
|
||||
{{ form.password1 }}
|
||||
{{ form.password1.errors }}
|
||||
</div>
|
||||
<div>
|
||||
<label for="{{ form.password2.id_for_label }}" class="block font-medium">{{ form.password2.label }}</label>
|
||||
{{ form.password2 }}
|
||||
{{ form.password2.errors }}
|
||||
</div>
|
||||
<div>
|
||||
<label for="{{ form.friend_code.id_for_label }}" class="block font-medium">{{ form.friend_code.label }}</label>
|
||||
{{ form.friend_code }}
|
||||
{{ form.friend_code.errors }}
|
||||
</div>
|
||||
<div>
|
||||
<label for="{{ form.in_game_name.id_for_label }}" class="block font-medium">{{ form.in_game_name.label }}</label>
|
||||
{{ form.in_game_name }}
|
||||
{{ form.in_game_name.errors }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Sign Up" %}</button>
|
||||
</form>
|
||||
<div class="mt-4 text-center">
|
||||
<p>{% trans "Already have an account?" %} <a href="{% url 'account_login' %}" class="text-primary underline">{% trans "Log In" %}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load allauth i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Signup" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Passkey Sign Up" %}</h1>
|
||||
<p class="mb-4 text-center">
|
||||
{% blocktranslate %}
|
||||
Already have an account? Then please <a href="{{ login_url }}" class="text-primary underline">{% trans "sign in" %}</a>.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
{% url 'account_signup_by_passkey' as action_url %}
|
||||
<form method="post" action="{{ action_url }}" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
{% for field in form %}
|
||||
<div>
|
||||
<label for="{{ field.id_for_label }}" class="block font-medium>{{ field.label }}</label>
|
||||
{{ field }}
|
||||
{{ field.errors }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{{ redirect_field }}
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Sign Up" %}</button>
|
||||
</form>
|
||||
<hr class="my-6">
|
||||
<div class="text-center">
|
||||
<a href="{{ signup_url }}" class="btn btn-outline-primary">{% trans "Other options" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth %}
|
||||
|
||||
{% block head_title %}{% trans "Sign Up Closed" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6 text-center">
|
||||
<h1 class="text-3xl font-bold mb-6">{% trans "Sign Up Closed" %}</h1>
|
||||
<p>
|
||||
{% trans "We are sorry, but the sign up is currently closed." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth %}
|
||||
|
||||
{% block head_title %}{% trans "Verify Your Email Address" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Verify Your Email Address" %}</h1>
|
||||
<p>
|
||||
{% blocktrans %}
|
||||
We have sent an email to you for verification. Follow the link provided to finalize the signup process. If you do not see the verification email in your main inbox, check your spam folder. Please contact us if you do not receive the verification email within a few minutes.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load allauth %}
|
||||
|
||||
{% block head_title %}{% trans "Verify Your Email Address" %}{% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold text-center mb-6">{% trans "Verify Your Email Address" %}</h1>
|
||||
{% url 'account_email' as email_url %}
|
||||
<p class="mb-4">
|
||||
{% blocktrans %}
|
||||
This part of the site requires us to verify that you are who you claim to be. For this purpose, we require that you verify ownership of your email address.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p class="mb-4">
|
||||
{% blocktrans %}
|
||||
We have sent an email to you for verification. Please click on the link inside that email. If you do not see the verification email in your main inbox, check your spam folder. Otherwise contact us if you do not receive it within a few minutes.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p>
|
||||
{% blocktrans %}
|
||||
<strong>Note:</strong> you can still <a class="text-primary underline" href="{{ email_url }}">change your email address</a>.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
146
src/pkmntrade_club/theme/templates/base.html
Normal file
146
src/pkmntrade_club/theme/templates/base.html
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
{% load static tailwind_tags gravatar %}
|
||||
{% url 'home' as home_url %}
|
||||
{% url 'trade_offer_list' as trade_offer_list_url %}
|
||||
{% url 'cards:card_list' as cards_list_url %}
|
||||
{% url 'dashboard' as dashboard_url %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% include 'meta/meta.html' %}
|
||||
|
||||
<!-- Inline script to set the theme before rendering -->
|
||||
<script>
|
||||
(function () {
|
||||
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
||||
document.documentElement.classList.toggle(
|
||||
"dark",
|
||||
localStorage.getItem("theme") === "dark" ||
|
||||
(!(localStorage.getItem("theme")) && window.matchMedia("(prefers-color-scheme: dark)").matches)
|
||||
);
|
||||
if (localStorage.getItem("theme")) {
|
||||
document.documentElement.setAttribute("data-theme", localStorage.getItem("theme"));
|
||||
} else {
|
||||
document.documentElement.setAttribute("data-theme", window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<title>{% block title %}{% endblock title %} - PᴋMɴ Trade Club</title>
|
||||
|
||||
<link rel="shortcut icon" href="{% static 'images/favicon.ico' %}">
|
||||
<!-- Choices.js -->
|
||||
<link rel="stylesheet" href="{% static 'css/choices.min.css' %}" />
|
||||
<script async src="{% static 'js/choices.min.js' %}"></script>
|
||||
<!-- Tailwind CSS and Base stylesheet -->
|
||||
{% tailwind_css %}
|
||||
<link rel="stylesheet" href="{% static 'css/base.css' %}">
|
||||
|
||||
<!-- Floating UI -->
|
||||
<script src="{% static 'js/floating-ui_core@1.6.9.9.min.js' %}"></script>
|
||||
<script src="{% static 'js/floating-ui_dom@1.6.13.13.min.js' %}"></script>
|
||||
|
||||
<script defer src="{% static 'js/card-multiselect.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/card-multiselect.css' %}">
|
||||
|
||||
<script src="{% static 'js/base.js' %}"></script>
|
||||
|
||||
{% block css %}{% endblock %}
|
||||
|
||||
{% block javascript_head %}{% endblock %}
|
||||
</head>
|
||||
<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 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>
|
||||
</span>
|
||||
<span aria-hidden="false" class="sr-only">PKMN Trade Club</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-center hidden sm:flex">
|
||||
<ul class="menu menu-horizontal px-1">
|
||||
<li><a href="{% url 'home' %}">Home</a></li>
|
||||
<li><a href="{% url 'cards:card_list' %}">Cards</a></li>
|
||||
<li><a href="{% url 'trade_offer_list' %}">Trades</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
|
||||
<button id="theme-toggle-btn" class="btn btn-ghost btn-circle me-2" title="Toggle Theme">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 dark:hidden">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 hidden dark:block">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z" />
|
||||
</svg>
|
||||
</button>
|
||||
{% if user.is_authenticated %}
|
||||
<div class="hidden sm:block">
|
||||
<div class="btn btn-ghost btn-circle avatar">
|
||||
<div class="max-w-10 rounded-full">
|
||||
<a tabindex="0" role="button" href="{% url 'dashboard' %}">{{ user.email|gravatar_no_hover:40 }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="gap-2 hidden sm:flex">
|
||||
<a tabindex="0" role="button" class="btn btn-primary" href="{% url 'account_login' %}">Login</a>
|
||||
<a tabindex="1" role="button" class="btn btn-secondary" href="{% url 'account_signup' %}">Sign Up</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto p-4 w-full xl:max-w-256">
|
||||
{% include '_messages.html' %}
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<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>
|
||||
</footer>
|
||||
|
||||
<!-- Dock -->
|
||||
<div x-data class="dock bg-neutral text-neutral-content sm:hidden">
|
||||
<button @click="window.location.href = '{{ home_url }}'" class="{% if request.path == home_url %}dock-active{% endif %}">
|
||||
<svg class="size-[1.2em]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke-linejoin="miter" stroke-linecap="butt"><polyline points="1 11 12 2 23 11" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="2"></polyline><path d="m5,13v7c0,1.105.895,2,2,2h10c1.105,0,2-.895,2-2v-7" fill="none" stroke="currentColor" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2"></path><line x1="12" y1="22" x2="12" y2="18" fill="none" stroke="currentColor" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2"></line></g></svg>
|
||||
<span class="dock-label">Home</span>
|
||||
</button>
|
||||
<button @click="window.location.href = '{{ cards_list_url }}'" class="{% if request.path == cards_list_url %}dock-active{% endif %}">
|
||||
<svg class="size-[1.2em]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 0 0-2.25-2.25H6A2.25 2.25 0 0 0 3.75 6v8.25A2.25 2.25 0 0 0 6 16.5h2.25m8.25-8.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-7.5A2.25 2.25 0 0 1 8.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 0 0-2.25 2.25v6" /></svg>
|
||||
<span class="dock-label">Cards</span>
|
||||
</button>
|
||||
<button @click="window.location.href = '{{ trade_offer_list_url }}'" class="{% if request.path == trade_offer_list_url %}dock-active{% endif %}">
|
||||
<svg class="size-[1.2em]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z" /></svg>
|
||||
<span class="dock-label">Trades</span>
|
||||
</button>
|
||||
<button @click="window.location.href = '{{ dashboard_url }}'" class="{% if request.path == dashboard_url or request.path == settings_url %}dock-active{% endif %}">
|
||||
{% if user.is_authenticated %}<div tabindex="0" role="button" class="avatar"><div class="max-w-6 rounded-full">{{ user.email|gravatar_no_hover:40 }}</div></div>{% else %}<svg class="size-[1.2em]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke-linejoin="miter" stroke-linecap="butt"><circle cx="12" cy="12" r="3" fill="none" stroke="currentColor" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2"></circle><path d="m22,13.25v-2.5l-2.318-.966c-.167-.581-.395-1.135-.682-1.654l.954-2.318-1.768-1.768-2.318.954c-.518-.287-1.073-.515-1.654-.682l-.966-2.318h-2.5l-.966,2.318c-.581.167-1.135.395-1.654.682l-2.318-.954-1.768,1.768.954,2.318c-.287.518-.515,1.073-.682,1.654l-2.318.966v2.5l2.318.966c.167.581.395,1.135.682,1.654l-.954,2.318,1.768,1.768,2.318-.954c.518.287,1.073.515,1.654.682l.966,2.318h2.5l.966-2.318c.581-.167,1.135-.395,1.654-.682l2.318.954,1.768-1.768-.954-2.318c.287-.518.515-1.073.682-1.654l2.318-.966Z" fill="none" stroke="currentColor" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2"></path></g></svg>{% endif %}
|
||||
<span class="dock-label">Dashboard</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Alpine Plugins -->
|
||||
<script defer src="{% static 'js/alpinejs.collapse@3.14.8.min.js' %}"></script>
|
||||
|
||||
<!-- Alpine Core -->
|
||||
<script defer src="{% static 'js/alpinejs@3.14.8.min.js' %}"></script>
|
||||
|
||||
<script defer src="{% static 'js/tooltip.js' %}"></script>
|
||||
|
||||
<!-- 100% privacy-first, no tracking analytics -->
|
||||
<script data-collect-dnt="true" async src="https://scripts.simpleanalyticscdn.com/latest.js"></script>
|
||||
<noscript><img src="https://queue.simpleanalyticscdn.com/noscript.gif?collect-dnt=true" alt="" referrerpolicy="no-referrer-when-downgrade"/></noscript>
|
||||
|
||||
{% block javascript %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
23
src/pkmntrade_club/theme/templates/cards/_card_list.html
Normal file
23
src/pkmntrade_club/theme/templates/cards/_card_list.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{% load card_badge %}
|
||||
{% load pagination_tags %}
|
||||
{% if group_by and groups %}
|
||||
{% for group in groups %}
|
||||
<div class="divider">{{ group.group }}</div>
|
||||
<div class="flex justify-center flex-wrap gap-2">
|
||||
{% for card in group.cards %}
|
||||
{% card_badge card expanded=True %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="flex justify-center flex-wrap gap-2">
|
||||
{% for card in cards %}
|
||||
{% card_badge card expanded=True %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Somewhere in your template, e.g., after the card list: -->
|
||||
{% if page_obj %}
|
||||
{% render_pagination page_obj %}
|
||||
{% endif %}
|
||||
105
src/pkmntrade_club/theme/templates/cards/card_detail.html
Normal file
105
src/pkmntrade_club/theme/templates/cards/card_detail.html
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static card_badge %}
|
||||
{% block content %}
|
||||
<!-- Card header with badge and details -->
|
||||
<div class="flex items-center mb-6">
|
||||
<div class="ml-4">
|
||||
<h1 class="text-3xl font-bold">{{card.name}}</h1>
|
||||
<h2 class="text-lg text-gray-500">{{ card.cardset }} #{{ card.cardnum }} • {{ card.rarity_icon }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trade Offers sections -->
|
||||
<div class="flex flex-col gap-8">
|
||||
|
||||
<!-- Trade Offers: Have -->
|
||||
<div x-data="{
|
||||
order: 'newest',
|
||||
page: 1,
|
||||
loadOffers() {
|
||||
document.activeElement.blur();
|
||||
fetch(`{% url 'cards:card_trade_offer_have_list' card.pk %}?order=` + this.order + '&page=' + this.page)
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
this.$refs.offerList.innerHTML = html;
|
||||
Alpine.initTree(this.$refs.offerList); // reinitialize Alpine on the injected content
|
||||
window.processMarqueeElements && window.processMarqueeElements();
|
||||
});
|
||||
}
|
||||
}"
|
||||
x-init="loadOffers()"
|
||||
x-on:change-page="page = $event.detail.page; loadOffers()"
|
||||
class="p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<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">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 max-w-26 p-2 shadow-sm">
|
||||
<li>
|
||||
<a href="#" @click.prevent="order = 'newest'; page = 1; loadOffers()">
|
||||
Newest
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @click.prevent="order = 'oldest'; page = 1; loadOffers()">
|
||||
Oldest
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div x-ref="offerList">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trade Offers: Want -->
|
||||
<div x-data="{
|
||||
order: 'newest',
|
||||
page: 1,
|
||||
loadOffers() {
|
||||
fetch(`{% url 'cards:card_trade_offer_want_list' card.pk %}?order=` + this.order + '&page=' + this.page)
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
this.$refs.offerList.innerHTML = html;
|
||||
Alpine.initTree(this.$refs.offerList); // reinitialize Alpine on the injected content
|
||||
window.processMarqueeElements && window.processMarqueeElements();
|
||||
});
|
||||
}
|
||||
}"
|
||||
x-init="loadOffers()"
|
||||
x-on:change-page="page = $event.detail.page; loadOffers()"
|
||||
class="p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<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">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 max-w-26 p-2 shadow-sm">
|
||||
<li>
|
||||
<a href="#" @click.prevent="order = 'newest'; page = 1; loadOffers()">
|
||||
Newest
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @click.prevent="order = 'oldest'; page = 1; loadOffers()">
|
||||
Oldest
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div x-ref="offerList">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
63
src/pkmntrade_club/theme/templates/cards/card_list.html
Normal file
63
src/pkmntrade_club/theme/templates/cards/card_list.html
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static card_badge %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto"
|
||||
x-data="{
|
||||
order: '{{ order }}',
|
||||
groupBy: '{{ group_by|default:'none' }}',
|
||||
page: 1,
|
||||
loadCards() {
|
||||
// Construct URL using current pathname and query parameters.
|
||||
let groupParam = this.groupBy === 'none' ? '' : this.groupBy;
|
||||
let url = window.location.pathname + '?order=' + this.order + '&group_by=' + groupParam + '&page=' + this.page;
|
||||
fetch(url, { headers: { 'x-requested-with': 'XMLHttpRequest' } })
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
this.$refs.cardList.innerHTML = html;
|
||||
window.processMarqueeElements && window.processMarqueeElements();
|
||||
});
|
||||
}
|
||||
}"
|
||||
x-on:change-page.window="page = $event.detail.page; loadCards()">
|
||||
<div class="flex flex-wrap items-center justify-between mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">Cards</h1>
|
||||
</div>
|
||||
<div>
|
||||
<!-- Sort Dropdown -->
|
||||
<div class="dropdown dropdown-end m-1">
|
||||
<div tabindex="0" class="btn">
|
||||
Sort by: <span x-text="order === 'absolute' ? 'Cardset' : (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 max-w-52">
|
||||
<li><a href="#" @click.prevent="order = 'absolute'; page = 1; loadCards()">Cardset</a></li>
|
||||
<li><a href="#" @click.prevent="order = 'alphabetical'; page = 1; loadCards()">Alphabetical</a></li>
|
||||
<li><a href="#" @click.prevent="order = 'rarity'; page = 1; loadCards()">Rarity</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- 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>
|
||||
<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 max-w-52">
|
||||
<li><a href="#" @click.prevent="groupBy = 'none'; page = 1; loadCards()">None</a></li>
|
||||
<li><a href="#" @click.prevent="groupBy = 'deck'; page = 1; loadCards()">Deck</a></li>
|
||||
<li><a href="#" @click.prevent="groupBy = 'cardset'; page = 1; loadCards()">Cardset</a></li>
|
||||
<li><a href="#" @click.prevent="groupBy = 'rarity'; page = 1; loadCards()">Rarity</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container for the partial card list -->
|
||||
<div x-ref="cardList">
|
||||
{% include "cards/_card_list.html" with cards=cards page_obj=page_obj %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Thank you for using PᴋMɴ Trade Club. Happy trading!
|
||||
|
|
@ -0,0 +1 @@
|
|||
Howdy {{ recipient_user }}!
|
||||
|
|
@ -0,0 +1 @@
|
|||
[PᴋMɴ Trade Club]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{% include 'email/common/header.txt' %}
|
||||
|
||||
Great news! {{ acting_user }} ({{ acting_user_friend_code }} • in-game name: {{ acting_user_ign }}) has accepted your trade offer.
|
||||
|
||||
Trade Details:
|
||||
- They have: {{ 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 the trade update page to mark it "Sent":
|
||||
{{ domain }}{% url 'trade_acceptance_update' pk %}
|
||||
|
||||
{% include 'email/common/footer.txt' %}
|
||||
|
||||
Trade ID: #{{ hash }}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Trade Accepted
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{% include 'email/common/header.txt' %}
|
||||
|
||||
Great news! {{ acting_user }} ({{ acting_user_friend_code }} • in-game name: {{ acting_user_ign }}) has marked your trade as "Received".
|
||||
|
||||
Trade Details:
|
||||
- They sent: {{ want_card }}
|
||||
- They received: {{ has_card }}
|
||||
|
||||
What's next? Send a thank you to this user to increase their reputation!
|
||||
|
||||
Visit the trade update page to send thanks:
|
||||
{{ domain }}{% url 'trade_acceptance_update' pk %}
|
||||
|
||||
{% include 'email/common/footer.txt' %}
|
||||
|
||||
Trade ID: #{{ hash }}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Trade Received
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{% include 'email/common/header.txt' %}
|
||||
|
||||
We're sorry to inform you that {{ acting_user }} ({{ acting_user_friend_code }} • in-game name: {{ acting_user_ign }}) has canceled their trade acceptance.
|
||||
|
||||
Trade Details:
|
||||
- They had: {{ want_card }}
|
||||
- They wanted: {{ has_card }}
|
||||
|
||||
Your trade offer is still active and available for other users to accept.
|
||||
|
||||
Visit your dashboard:
|
||||
{{ domain }}{% url 'dashboard' %}
|
||||
|
||||
{% include 'email/common/footer.txt' %}
|
||||
|
||||
Trade ID: #{{ hash }}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Trade Was Rejected
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{% include 'email/common/header.txt' %}
|
||||
|
||||
We're sorry to inform you that {{ acting_user }} ({{ acting_user_friend_code }} • in-game name: {{ acting_user_ign }}) has rejected the trade.
|
||||
|
||||
Trade Details:
|
||||
- You had: {{ has_card }}
|
||||
- You wanted: {{ want_card }}
|
||||
|
||||
Don't worry - there are plenty of other trade opportunities available! You can browse our marketplace for similar trades.
|
||||
|
||||
Visit the marketplace:
|
||||
{{ domain }}{% url 'trade_offer_list' %}
|
||||
|
||||
Visit your dashboard:
|
||||
{{ domain }}{% url 'dashboard' %}
|
||||
|
||||
{% include 'email/common/footer.txt' %}
|
||||
|
||||
Trade ID: #{{ hash }}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Trade Was Rejected
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{% include 'email/common/header.txt' %}
|
||||
|
||||
Great news! {{ acting_user }} ({{ acting_user_friend_code }} • in-game name: {{ acting_user_ign }}) has marked your trade as "Sent".
|
||||
|
||||
Trade Details:
|
||||
- You have: {{ want_card }}
|
||||
- You want: {{ has_card }}
|
||||
|
||||
What's next? Once you respond to the trade in the app, please mark the trade as "Received" in your dashboard.
|
||||
|
||||
Visit the trade update page to mark it "Received":
|
||||
{{ domain }}{% url 'trade_acceptance_update' pk %}
|
||||
|
||||
{% include 'email/common/footer.txt' %}
|
||||
|
||||
Trade ID: #{{ hash }}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Trade Sent
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{% include 'email/common/header.txt' %}
|
||||
|
||||
{{ acting_user }} ({{ acting_user_friend_code }} • in-game name: {{ acting_user_ign }}) has sent their thanks for the successful trade!
|
||||
|
||||
Trade Details:
|
||||
- They sent: {{ want_card }}
|
||||
- They received: {{ has_card }}
|
||||
|
||||
What's next? Send a thank you to this user to increase their reputation!
|
||||
|
||||
Visit the trade update page to send thanks:
|
||||
{{ domain }}{% url 'trade_acceptance_update' pk %}
|
||||
|
||||
{% include 'email/common/footer.txt' %}
|
||||
|
||||
Trade ID: #{{ hash }}
|
||||
|
|
@ -0,0 +1 @@
|
|||
You Received A Thanks!
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{% include 'email/common/header.txt' %}
|
||||
|
||||
{{ acting_user }} ({{ acting_user_friend_code }} • in-game name: {{ acting_user_ign }}) has sent their thanks for the successful trade!
|
||||
|
||||
Trade Details:
|
||||
- {% if is_initiator %}You{% else %}They{% endif %} sent: {{ want_card }}
|
||||
- {% if is_initiator %}You{% else %}They{% endif %} received: {{ has_card }}
|
||||
|
||||
This trade is now completed; no further actions can be made.
|
||||
|
||||
Visit your dashboard:
|
||||
{{ domain }}{% url 'dashboard' %}
|
||||
|
||||
{% include 'email/common/footer.txt' %}
|
||||
|
||||
Trade ID: #{{ hash }}
|
||||
|
|
@ -0,0 +1 @@
|
|||
You Received A Thanks!
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{% include 'email/common/header.txt' %}
|
||||
|
||||
{{ acting_user }} ({{ acting_user_friend_code }} • in-game name: {{ acting_user_ign }}) has sent their thanks for the successful trade!
|
||||
|
||||
Trade Details:
|
||||
- You sent: {{ want_card }}
|
||||
- You received: {{ has_card }}
|
||||
|
||||
What's next? Send a thank you to this user to increase their reputation!
|
||||
|
||||
Visit the trade update page to send thanks:
|
||||
{{ domain }}{% url 'trade_acceptance_update' pk %}
|
||||
|
||||
{% include 'email/common/footer.txt' %}
|
||||
|
||||
Trade ID: #{{ hash }}
|
||||
|
|
@ -0,0 +1 @@
|
|||
You Received A Thanks!
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags i18n %}
|
||||
|
||||
{% block title %}Add Friend Code{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold mb-4">Add Friend Code</h1>
|
||||
<form method="post" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div>
|
||||
<label for="{{ form.friend_code.id_for_label }}" class="block font-medium">{{ form.friend_code.label }}</label>
|
||||
{{ form.friend_code }}
|
||||
{{ form.friend_code.errors }}
|
||||
</div>
|
||||
<div>
|
||||
<label for="{{ form.in_game_name.id_for_label }}" class="block font-medium">{{ form.in_game_name.label }}</label>
|
||||
{{ form.in_game_name }}
|
||||
{{ form.in_game_name.errors }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-full">{% trans "Add Friend Code" %}</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Include Cleave Zen from a CDN -->
|
||||
<script src="https://unpkg.com/cleave-zen@0.0.17/dist/cleave-zen.umd.js"></script>
|
||||
<script defer>
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
// Initialize Cleave Zen on the friend code input field.
|
||||
// Make sure that the input ID is correct (e.g., provided by Django's widget rendering).
|
||||
cleaveZen('#id_friend_code', {
|
||||
delimiters: ['-', '-', '-'], // Inserts dashes between the blocks.
|
||||
blocks: [4, 4, 4, 4],
|
||||
numericOnly: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Delete Friend Code{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-md mt-6">
|
||||
<h1 class="text-3xl font-bold mb-4">Delete Friend Code</h1>
|
||||
<p class="mb-4">
|
||||
Are you sure you want to delete friend code:
|
||||
<span class="font-mono">{{ friend_code.friend_code }}</span>?
|
||||
</p>
|
||||
|
||||
{% if error_message %}
|
||||
<div class="alert alert-warning mb-4">
|
||||
{{ error_message }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" class="flex space-x-4">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-error"
|
||||
{% if disable_delete %} disabled {% endif %}>
|
||||
{% if disable_delete %}
|
||||
Delete Not Allowed
|
||||
{% else %}
|
||||
Confirm Delete
|
||||
{% endif %}
|
||||
</button>
|
||||
<a href="{% url 'dashboard' %}?tab=friend_codes" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Edit Friend Code{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-3xl font-bold mb-4">Edit Friend Code</h1>
|
||||
|
||||
<!-- Display the friend code as plain text -->
|
||||
<div class="mb-4 p-4 bg-base-100 dark:bg-base-900 rounded shadow">
|
||||
<span class="font-mono text-sm sm:text-base">Friend Code: {{ friend_code.friend_code }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Update form for editing the in-game name -->
|
||||
<form method="post" novalidate id="edit-friendcode-form">
|
||||
{% csrf_token %}
|
||||
<div class="mb-4">
|
||||
{{ form.in_game_name.as_widget }}
|
||||
{% if form.in_game_name.errors %}
|
||||
<div class="text-red-600 text-sm">
|
||||
{{ form.in_game_name.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="flex flex-col md:flex-row justify-between items-center mt-6">
|
||||
<!-- Left group: Set Default & Delete -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<form method="post" action="{% url 'change_default_friend_code' friend_code.id %}" class="inline">
|
||||
{% csrf_token %}
|
||||
{% if not friend_code.is_default %}
|
||||
<button type="submit" class="btn btn-secondary">Set as Default</button>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-secondary" disabled>Default</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
<a href="{% url 'delete_friend_code' friend_code.id %}" class="btn btn-error">Delete</a>
|
||||
</div>
|
||||
|
||||
<!-- Right group: Cancel & Update -->
|
||||
<div class="flex items-center space-x-4 mt-4 md:mt-0">
|
||||
<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>
|
||||
</div>
|
||||
{% endblock %}
|
||||
10
src/pkmntrade_club/theme/templates/home/_card_list.html
Normal file
10
src/pkmntrade_club/theme/templates/home/_card_list.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{% load card_badge %}
|
||||
{% if cards %}
|
||||
<div class="mx-4 grid gap-3 grid-cols-[repeat(auto-fit,minmax(150px,1fr))] justify-items-center">
|
||||
{% for card in cards %}
|
||||
{% card_badge card quantity=card.offer_count expanded=True %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-center">No cards found</p>
|
||||
{% endif %}
|
||||
130
src/pkmntrade_club/theme/templates/home/home.html
Normal file
130
src/pkmntrade_club/theme/templates/home/home.html
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static trade_offer_tags card_badge cache card_multiselect %}
|
||||
|
||||
{% block title %}
|
||||
Welcome
|
||||
{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-center text-4xl font-bold mb-8 pt-4">
|
||||
<span class="inline leading-none" aria-hidden="true">
|
||||
<span class="inline-block relative">Welcome to</span>
|
||||
<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>
|
||||
</span>
|
||||
<span aria-hidden="false" class="sr-only">Welcome to PKMN Trade Club</span>
|
||||
</h1>
|
||||
|
||||
<!-- Search/Create Form Section -->
|
||||
<section id="trade-search" class="mb-8">
|
||||
<form id="tradeSearchForm" method="post" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
{% card_multiselect "have_cards" "I Have:" "Select some cards..." cards %}
|
||||
</div>
|
||||
<div>
|
||||
{% card_multiselect "want_cards" "I Want:" "Select some cards..." cards %}
|
||||
</div>
|
||||
</div>
|
||||
{# Pass the user's default friend code as a hidden field for creation #}
|
||||
<input type="hidden" name="initiated_by" value="{{ request.user.default_friend_code.pk }}">
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<button type="submit" name="search" formaction="{% url 'trade_offer_search' %}" class="btn btn-primary grow">
|
||||
Find a Trade Offer
|
||||
</button>
|
||||
<button type="submit" name="preview" formaction="{% url 'trade_offer_confirm_create' %}" class="btn btn-secondary grow">
|
||||
Create Trade Offer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<!-- Card Stats Section -->
|
||||
<section aria-labelledby="stats-heading" class="mb-8">
|
||||
<h2 id="stats-heading" class="text-2xl font-semibold mb-4">Card Stats</h2>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
<!-- Most Offered Cards -->
|
||||
{% cache CACHE_TIMEOUT most_offered_cards cache_key_most_offered_cards %}
|
||||
<div>
|
||||
<div class="card card-border bg-base-100 shadow-lg">
|
||||
<div class="card-header text-base-content p-4">
|
||||
<h5 class="text-xl text-center font-semibold whitespace-nowrap truncate mb-0">Most Offered</h5>
|
||||
</div>
|
||||
<div class="card-body my-4 p-0">
|
||||
{% include "home/_card_list.html" with cards=most_offered_cards %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcache %}
|
||||
<!-- Most Wanted Cards -->
|
||||
{% cache CACHE_TIMEOUT most_wanted_cards cache_key_most_wanted_cards %}
|
||||
<div>
|
||||
<div class="card card-border bg-base-100 shadow-lg">
|
||||
<div class="card-header text-base-content p-4">
|
||||
<h5 class="text-xl text-center font-semibold whitespace-nowrap truncate mb-0">Most Wanted</h5>
|
||||
</div>
|
||||
<div class="card-body my-4 p-0">
|
||||
{% include "home/_card_list.html" with cards=most_wanted_cards %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcache %}
|
||||
<!-- Least Offered Cards (Last Group) -->
|
||||
{% cache CACHE_TIMEOUT least_offered_cards cache_key_least_offered_cards %}
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<div class="card card-border bg-base-100 shadow-lg">
|
||||
<div class="card-header text-base-content p-4">
|
||||
<h5 class="text-xl text-center font-semibold whitespace-nowrap truncate mb-0">Least Offered</h5>
|
||||
</div>
|
||||
<div class="card-body my-4 p-0">
|
||||
{% include "home/_card_list.html" with cards=least_offered_cards %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcache %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Featured Offers and Recent Offers Section -->
|
||||
<section class="mb-8">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- Featured Offers -->
|
||||
{% cache CACHE_TIMEOUT featured_offers cache_key_featured_offers %}
|
||||
<div>
|
||||
<div class="p-4 text-center ">
|
||||
<h5 class="text-xl font-semibold whitespace-nowrap truncate mb-0">Featured Offers</h5>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
{% if featured_offers.All %}
|
||||
<div class="flex flex-col items-center gap-6 w-auto mx-auto">
|
||||
{% for offer in featured_offers.All %}
|
||||
{% render_trade_offer offer %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-center">No featured offers available.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endcache %}
|
||||
<!-- Recent Offers -->
|
||||
{% cache CACHE_TIMEOUT recent_offers cache_key_recent_offers %}
|
||||
<div>
|
||||
<div class="text-center p-4">
|
||||
<h5 class="text-xl font-semibold whitespace-nowrap truncate mb-0">Recent Offers</h5>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="flex flex-col items-center gap-6">
|
||||
{% for offer in recent_offers %}
|
||||
{% render_trade_offer offer %}
|
||||
{% empty %}
|
||||
<p>No recent offers available.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcache %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
||||
37
src/pkmntrade_club/theme/templates/tailwind/field.html
Normal file
37
src/pkmntrade_club/theme/templates/tailwind/field.html
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{% load tailwind_field %}
|
||||
|
||||
{% if field.is_hidden %}
|
||||
{{ field }}
|
||||
{% else %}
|
||||
{# Opening Div and Label first #}
|
||||
|
||||
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="{% if wrapper_class %}{{ wrapper_class }} {% endif %}{% if field_class %}{{ field_class }}{% else %}mb-3{% endif %}">
|
||||
{% if field.label and form_show_labels %}
|
||||
<label for="{{ field.id_for_label }}" class="{% if label_class %}{{ label_class }}{% else %}block text-base-content bg-base-100 text-sm font-bold mb-2{% endif %}">
|
||||
{{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
|
||||
</label>
|
||||
{% endif %}
|
||||
|
||||
{# if field has a special template then use this #}
|
||||
{% if field|is_select %}
|
||||
<div class="{% if field_class %}{{ field_class }}{% else %}mb-3{% endif %}"{% if flat_attrs %} {{ flat_attrs|safe }}{% endif %}>
|
||||
{% include 'tailwind/layout/select.html' %}
|
||||
</div>
|
||||
{% elif field|is_checkboxselectmultiple %}
|
||||
<div class="{% if field_class %}{{ field_class }}{% else %}mb-3{% endif %}"{% if flat_attrs %} {{ flat_attrs|safe }}{% endif %}>
|
||||
{% include 'tailwind/layout/checkboxselectmultiple.html' %}
|
||||
</div>
|
||||
{% elif field|is_radioselect %}
|
||||
<div class="{% if field_class %}{{ field_class }}{% else %}mb-3{% endif %}"{% if flat_attrs %} {{ flat_attrs|safe }}{% endif %}>
|
||||
{% include 'tailwind/layout/radioselect.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
{# otherwise use django rendering with additional classes added #}
|
||||
{% tailwind_field field %}
|
||||
{% endif %}
|
||||
|
||||
{% include 'tailwind/layout/help_text_and_errors.html' %}
|
||||
|
||||
</{% if tag %}{{ tag }}{% else %}div{% endif %}>
|
||||
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% if field.help_text %}
|
||||
{% if help_text_inline %}
|
||||
<p {% if field.id_for_label %}id="{{ field.id_for_label }}_helptext" {% endif %}class="text-base-content bg-base-100">{{ field.help_text|safe }}</p>
|
||||
{% else %}
|
||||
<small {% if field.id_for_label %}id="{{ field.id_for_label }}_helptext" {% endif %}class="text-base-content bg-base-100">{{ field.help_text|safe }}</small>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{% comment %}
|
||||
This fragment renders a friend code selector used for filtering or form submissions.
|
||||
Expected variables:
|
||||
- friend_codes: A list or QuerySet of FriendCode objects.
|
||||
- selected_friend_code (optional): The currently selected FriendCode. If not provided, the user's default friend code is used.
|
||||
- field_name (optional): The name/id for the input element (default "friend_code").
|
||||
- label (optional): The label text (default None).
|
||||
{% endcomment %}
|
||||
|
||||
{% with field_name=field_name|default:"friend_code" label=label|default:"" %}
|
||||
{% with effective_friend_code=selected_friend_code|default:request.user.default_friend_code %}
|
||||
{% if friend_codes|length > 1 %}
|
||||
<div class="form-control">
|
||||
{% if label and label != "" %}
|
||||
<label for="{{ field_name }}" class="label">
|
||||
<span class="label-text p-2 rounded">{{ label }}</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
<select id="{{ field_name }}" name="{{ field_name }}" class="select select-bordered w-full" @change="$el.form.submit()">
|
||||
{% for code in friend_codes %}
|
||||
<option value="{{ code.pk }}" {% if effective_friend_code and code.pk|stringformat:"s" == effective_friend_code.pk|stringformat:"s" %}selected{% endif %}>
|
||||
{{ code.in_game_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% else %}
|
||||
<input type="hidden" id="{{ field_name }}" name="{{ field_name }}" value="{{ friend_codes.0.pk }}">
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{% load trade_offer_tags %}
|
||||
{% if have_cards or want_cards %}
|
||||
<hr class="my-8 border-t border-base-300">
|
||||
<h2 class="text-2xl font-bold mb-4">Results</h2>
|
||||
{% if search_results %}
|
||||
{% include "trades/_trade_offer_list.html" with offers=search_results %}
|
||||
{% else %}
|
||||
<div class="alert alert-info mt-4">No trade offers found.</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{% load trade_offer_tags pagination_tags %}
|
||||
|
||||
<div class="flex flex-row flex-wrap gap-6 md:gap-2 lg:gap-6 justify-center items-start py-6">
|
||||
{% for offer in offers %}
|
||||
{% if offer.accepted_by %}
|
||||
{# Render a trade acceptance using our new tag #}
|
||||
{% render_trade_acceptance offer %}
|
||||
{% else %}
|
||||
{% render_trade_offer offer %}
|
||||
{% endif %}
|
||||
{% empty %}
|
||||
<div>No trade offers available.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% render_pagination page_obj %}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Accept Trade Offer{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-xl mt-6">
|
||||
<h2 class="text-2xl font-bold">Accept Trade Offer</h2>
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn btn-primary">Accept Trade</button>
|
||||
</form>
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-error mt-4">
|
||||
<strong>Please correct the errors below:</strong>
|
||||
<ul>
|
||||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load trade_offer_tags card_badge %}
|
||||
|
||||
{% block title %}Update Trade{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
{% if form and form.errors %}
|
||||
<div class="alert alert-error mt-4">
|
||||
<strong>Please correct the errors below:</strong>
|
||||
<ul>
|
||||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h2 class="text-2xl font-bold">Trade Details</h2>
|
||||
|
||||
<div class="card card-border bg-base-100 shadow-lg mx-auto p-6 m-4 text-sm">
|
||||
<div class="text-center mb-4">
|
||||
<ul class="steps">
|
||||
{% if object.is_thanked %}
|
||||
<li class="step step-primary">Accepted</li>
|
||||
<li class="step step-primary">Card Sent</li>
|
||||
<li class="step step-primary">Card Received</li>
|
||||
<li class="step step-primary">Thanks Sent</li>
|
||||
<li class="step step-primary">Thanks Received</li>
|
||||
<li class="step step-primary">Completed</li>
|
||||
{% elif object.is_rejected %}
|
||||
<li class="step step-primary">Accepted</li>
|
||||
<li class="step step-error">
|
||||
<span class="step-icon">X</span>{{ object.get_state_display }}
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="step step-primary">Accepted</li>
|
||||
<li class="step {% if object.get_step_number >= 2 %}step-primary{% endif %}">Card Sent</li>
|
||||
<li class="step {% if object.get_step_number >= 3 %}step-primary{% endif %}">Card Received</li>
|
||||
{% if object.state == 'THANKED_BY_INITIATOR' %}
|
||||
<li class="step step-primary">Thanks Sent</li>
|
||||
<li class="step">Thanks Received</li>
|
||||
<li class="step">Completed</li>
|
||||
{% elif object.state == 'THANKED_BY_ACCEPTOR' %}
|
||||
<li class="step step-primary">Thanks Received</li>
|
||||
<li class="step">Thanks Sent</li>
|
||||
<li class="step">Completed</li>
|
||||
{% elif object.state == 'THANKED_BY_BOTH' %}
|
||||
<li class="step step-primary">Thanks Sent</li>
|
||||
<li class="step step-primary">Thanks Received</li>
|
||||
<li class="step step-primary">Completed</li>
|
||||
{% else %}
|
||||
<li class="step">Thanks Sent</li>
|
||||
<li class="step">Thanks Received</li>
|
||||
<li class="step">Completed</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="grid grid-cols-2 justify-items-end items-baseline gap-4">
|
||||
<div class="font-semibold">Initiator:</div>
|
||||
<div class="justify-self-start">{{ object.trade_offer.initiated_by.user.username }}</div>
|
||||
<div class="font-semibold">Initiator's Card:</div>
|
||||
<div class="justify-self-start">{% card_badge object.offered_card %}</div>
|
||||
<div class="font-semibold">Acceptor:</div>
|
||||
<div class="justify-self-start">{{ object.accepted_by.user.username }}</div>
|
||||
<div class="font-semibold">Acceptor's Card:</div>
|
||||
<div class="justify-self-start">{% card_badge object.requested_card %}</div>
|
||||
<div class="font-semibold">State:</div>
|
||||
<div class="justify-self-start">{{ object.get_state_display }}, Waiting on {% if object.is_initiator_state %}{{ object.accepted_by.user.username }}{% else %}{{ object.trade_offer.initiated_by.user.username }}{% endif %} to {{ object.next_action_label }}</div>
|
||||
<div class="font-semibold">ID:</div>
|
||||
<div class="justify-self-start">#{{ object.hash }}</div>
|
||||
<div class="font-semibold">Trade Offer:</div>
|
||||
<div class="justify-self-start"><a class="link link-hover" href="{% url 'trade_offer_detail' object.trade_offer.id %}">#{{ object.trade_offer.hash }}</a></div>
|
||||
<div class="font-semibold">Created At:</div>
|
||||
<div class="justify-self-start">{{ object.created_at }}</div>
|
||||
<div class="font-semibold">Last Updated:</div>
|
||||
<div class="justify-self-start">{{ object.updated_at }}</div>
|
||||
</div>
|
||||
{% if form.fields.state.choices %}
|
||||
<div class="divider"></div>
|
||||
<div class="flex flex-row gap-2">
|
||||
{% for state_value, state_label in form.fields.state.choices %}
|
||||
<form method="post" class="mb-2 flex-1">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="state" value="{{ state_value }}">
|
||||
<button type="submit" class="{{ state_value|action_button_class }} w-full">
|
||||
{{ object|get_action_label:state_value }}
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static pagination_tags %}
|
||||
|
||||
{% block title %}Trade Offers{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div x-data="{
|
||||
page: {{ page_obj.number|default:1 }},
|
||||
loadOffers() {
|
||||
let url = new URL('{% url 'trade_offer_list' %}', window.location.origin);
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
params.set('page', this.page);
|
||||
url.search = params.toString();
|
||||
fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' }})
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
this.$refs.offersContainer.innerHTML = html;
|
||||
window.processMarqueeElements && window.processMarqueeElements();
|
||||
});
|
||||
}
|
||||
}"
|
||||
x-on:change-page.window="page = $event.detail.page; loadOffers()">
|
||||
|
||||
<!-- Header without Expand All -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h1 class="text-2xl font-bold">Trade Offers</h1>
|
||||
<div class="flex items-center gap-4">
|
||||
<form method="get" class="flex items-center gap-4" x-data>
|
||||
<label class="cursor-pointer flex items-center gap-2">
|
||||
<span>Only Closed</span>
|
||||
<input type="checkbox" name="show_closed" value="true" class="toggle toggle-primary"
|
||||
@change="$el.form.submit()" {% if show_closed %}checked{% endif %}>
|
||||
</label>
|
||||
<button type="submit" class="btn btn-primary" x-show="false">Apply</button>
|
||||
</form>
|
||||
<div>
|
||||
<a href="{% url 'trade_offer_create' %}" class="btn btn-success">Create New Offer</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Trade Offers -->
|
||||
<section class="mb-12">
|
||||
|
||||
<div id="all-trade-offers" x-ref="offersContainer">
|
||||
{% include "trades/_trade_offer_list.html" with offers=offers page_obj=page_obj %}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static card_badge trade_offer_tags %}
|
||||
|
||||
{% block title %}Confirm Trade Offer{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-xl mt-6">
|
||||
<h2 class="text-2xl font-bold mb-4">Confirm Trade Offer</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{# Re-create hidden inputs from POST data, except the preview button #}
|
||||
{% for key, values in post_data.lists %}
|
||||
{% for value in values %}
|
||||
{% if key != "preview" %}
|
||||
<input type="hidden" name="{{ key }}" value="{{ value }}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% render_trade_offer dummy_trade_offer %}
|
||||
|
||||
<div class="flex justify-between mt-4">
|
||||
<button type="submit" name="edit" class="btn btn-secondary">Edit</button>
|
||||
<button type="submit" name="confirm" class="btn btn-primary">Confirm Trade Offer</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load card_multiselect %}
|
||||
|
||||
{% block title %}Create Trade Offer{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-xl mt-6">
|
||||
<h2 class="text-2xl font-bold mb-4">Create a Trade Offer</h2>
|
||||
<form method="post" action="{% url 'trade_offer_confirm_create' %}" novalidate class="space-y-4">
|
||||
{% csrf_token %}
|
||||
|
||||
{% include "trades/_friend_code_select.html" with friend_codes=friend_codes selected_friend_code=selected_friend_code field_name=form.initiated_by.html_name label="Initiated by" %}
|
||||
|
||||
<!-- Card Selectors: "Have" and "Want" -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="form-control">
|
||||
{% with have_values=form.have_cards.value|default:form.initial.have_cards %}
|
||||
{% card_multiselect "have_cards" "I Have:" "Select some cards..." cards have_values %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="form-control">
|
||||
{% with want_values=form.want_cards.value|default:form.initial.want_cards %}
|
||||
{% card_multiselect "want_cards" "I Want:" "Select some cards..." cards want_values %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" name="preview" class="btn btn-primary w-full">Preview Trade Offer</button>
|
||||
</form>
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-error mt-4">
|
||||
<strong>Please correct the errors below:</strong>
|
||||
<ul class="mt-2">
|
||||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue