Initial working version with minor bugs
This commit is contained in:
parent
f946e4933a
commit
71b3993326
83 changed files with 34485 additions and 173 deletions
|
|
@ -1,29 +1,68 @@
|
|||
{% load static %}
|
||||
{% load static card_badge %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-bs-theme="light">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
|
||||
<title>{% block title %}DjangoX{% endblock title %}</title>
|
||||
<title>{% block title %}Pocket.Trade{% endblock title %}</title>
|
||||
<meta name="description" content="A framework for launching new Django projects quickly.">
|
||||
<meta name="author" content="">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{% static 'images/favicon.ico' %}">
|
||||
|
||||
{% block css %}
|
||||
<!-- DaisyUI (disabled for now)-->
|
||||
<!-- <link href="https://cdn.jsdelivr.net/npm/daisyui@5.0.0-beta.8/daisyui.min.css" rel="stylesheet"> -->
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
{# <link rel="stylesheet" href="{% static 'css/darkly.min.css' %}"> #}
|
||||
{# <link rel="stylesheet" href="{% static 'css/flatly.min.css' %}"> #}
|
||||
<link rel="stylesheet" href="{% static 'css/base.css' %}">
|
||||
{% block css %}
|
||||
{% endblock %}
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<!-- Bootstrap JavaScript -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<!-- Tailwind CSS (disabled for now)-->
|
||||
<!-- <script src="https://unpkg.com/@tailwindcss/browser@4"></script> -->
|
||||
<!-- DaisyUI (disabled for now)-->
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/daisyui@5.0.0-beta.8/index.min.js"></!-->
|
||||
<!-- Select2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<!-- Gravatar -->
|
||||
<script src="https://www.gravatar.com/js/hovercards/hovercards.min.js"></script>
|
||||
<!-- Project JS -->
|
||||
<script src="{% static 'js/base.js' %}"></script>
|
||||
<script>
|
||||
function formatOption(option) {
|
||||
if (!option.id) return option.text;
|
||||
var $option = $(option.element);
|
||||
var cardName = $option.data('name');
|
||||
var rarity = $option.data('rarity');
|
||||
var cardset = $option.data('cardset');
|
||||
var style = $option.data('style');
|
||||
var $container = $(
|
||||
{% card_badge None %}
|
||||
);
|
||||
return $container;
|
||||
}
|
||||
</script>
|
||||
{% block javascript %}
|
||||
{% endblock javascript %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<nav class="navbar navbar-expand-lg bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{% url 'home' %}">DjangoX</a>
|
||||
<a class="navbar-brand" href="{% url 'home' %}">Pocket.Trade</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
|
|
@ -33,13 +72,26 @@
|
|||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="{% url 'home' %}">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'about' %}">About</a>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="tradeOffersDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Trade Offers
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="tradeOffersDropdown">
|
||||
<li><a class="dropdown-item" href="{% url 'trade_offer_list' %}">All Offers</a></li>
|
||||
{% if user.is_authenticated %}<li><a class="dropdown-item" href="{% url 'trade_offer_list' %}?my_trades=true">My Trades</a></li>{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% if user.is_authenticated %}
|
||||
<div class="mr-auto">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item"><a href="{% url 'trade_offer_create' %}" class="btn btn-primary me-2 mb-2">
|
||||
Create Trade Offer
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mr-auto">
|
||||
<div class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
|
|
@ -50,11 +102,12 @@
|
|||
<li>
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
<li><a class="dropdown-item" href="{% url 'list_friend_codes' %}">My Friend Codes</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'account_change_password' %}">Change password</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'account_logout' %}">Sign out</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="mr-auto">
|
||||
|
|
@ -79,17 +132,6 @@
|
|||
<span class="text-muted">Footer...</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{% block javascript %}
|
||||
<!-- Bootstrap JavaScript -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<!-- Project JS -->
|
||||
<script src="{% static 'js/base.js' %}"></script>
|
||||
|
||||
{% endblock javascript %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
17
templates/friend_codes/add_friend_code.html
Normal file
17
templates/friend_codes/add_friend_code.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Add Friend Code{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Add Friend Code</h1>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit">Add Friend Code</button>
|
||||
</form>
|
||||
|
||||
<p>
|
||||
<a href="{% url 'list_friend_codes' %}">Back to Friend Codes</a>
|
||||
</p>
|
||||
{% endblock %}
|
||||
13
templates/friend_codes/confirm_delete_friend_code.html
Normal file
13
templates/friend_codes/confirm_delete_friend_code.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Log in{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Are you sure you want to delete friend code: {{ friend_code.friend_code }}?</h1>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit">Confirm Delete</button>
|
||||
<a href="{% url 'list_friend_codes' %}">Cancel</a>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
25
templates/friend_codes/list_friend_codes.html
Normal file
25
templates/friend_codes/list_friend_codes.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{% extends '_base.html' %}
|
||||
|
||||
{% block title %}My Friend Codes{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>My Friend Codes</h1>
|
||||
|
||||
{% if friend_codes %}
|
||||
<ul>
|
||||
{% for code in friend_codes %}
|
||||
<li>
|
||||
{{ code.friend_code }}
|
||||
<!-- Link to the delete confirmation page for this friend code -->
|
||||
<a href="{% url 'delete_friend_code' code.id %}">Delete</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>You do not have any friend codes added yet.</p>
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
<a href="{% url 'add_friend_code' %}">Add a New Friend Code</a>
|
||||
</p>
|
||||
{% endblock %}
|
||||
42
templates/home/_search_results.html
Normal file
42
templates/home/_search_results.html
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{% load trade_offer_tags %}
|
||||
{% if offered_cards or wanted_cards %}
|
||||
<hr class="my-5">
|
||||
<h2 class="mb-4">Results</h2>
|
||||
{% if search_results and search_results.object_list %}
|
||||
<ul class="list-group">
|
||||
{% for offer in search_results %}
|
||||
<li class="list-group-item border-0">
|
||||
<a href="{% url 'trade_offer_update' offer.pk %}" class="d-flex align-items-center text-decoration-none">
|
||||
{% render_trade_offer offer %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- Pagination Controls -->
|
||||
<nav aria-label="Search results pagination" class="mt-4">
|
||||
<ul class="pagination">
|
||||
{% if search_results.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link ajax-page-link" data-page="{{ search_results.previous_page_number }}" href="#">Previous</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled"><span class="page-link">Previous</span></li>
|
||||
{% endif %}
|
||||
{% for num in search_results.paginator.page_range %}
|
||||
<li class="page-item {% if search_results.number == num %}active{% endif %}">
|
||||
<a class="page-link ajax-page-link" data-page="{{ num }}" href="#">{{ num }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if search_results.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link ajax-page-link" data-page="{{ search_results.next_page_number }}" href="#">Next</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled"><span class="page-link">Next</span></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% else %}
|
||||
<div class="alert alert-info">No trade offers found.</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
239
templates/home/home.html
Normal file
239
templates/home/home.html
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
{% load trade_offer_tags card_badge %}
|
||||
{% load cache %}
|
||||
{% load card_multiselect %}
|
||||
|
||||
{% block content %}
|
||||
<main class="container my-5">
|
||||
<h1 class="text-center mb-5">Welcome to Pocket.Trade</h1>
|
||||
<!-- Search Form Section -->
|
||||
<section id="trade-search" class="mb-5">
|
||||
<form method="post" action=".">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
{% card_multiselect "offered_cards" "Have:" available_cards "Select zero or more cards..." offered_cards %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
{% card_multiselect "wanted_cards" "Want:" available_cards "Select zero or more cards..." wanted_cards %}
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Find a Trade Offer</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<!-- Search Results Section -->
|
||||
<section id="search-results">
|
||||
{% include "home/_search_results.html" %}
|
||||
</section>
|
||||
|
||||
<!-- Market Stats Section -->
|
||||
<section aria-labelledby="stats-heading" class="mb-5">
|
||||
<h2 id="stats-heading" class="mb-4">Market Stats</h2>
|
||||
<div class="row gx-5">
|
||||
<!-- Most Offered Cards (cached for 3600 seconds / 1 hour) -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<h5 class="mb-3">Most Offered Cards</h5>
|
||||
<div class="card h-100 shadow border-0">
|
||||
<div class="card-body">
|
||||
{% cache 3600 most_offered_cards %}
|
||||
{% if most_offered_cards %}
|
||||
<div class="d-flex flex-column gap-3">
|
||||
{% for card in most_offered_cards %}
|
||||
{% if card.offer_count > 0 %}
|
||||
<a href="?wanted_cards={{ card.id }}"
|
||||
class="d-flex justify-content-between align-items-center text-decoration-none text-primary">
|
||||
{% card_badge card %}
|
||||
<span>{{ card.offer_count }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No cards found</p>
|
||||
{% endif %}
|
||||
{% endcache %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Most Wanted Cards (cached for 3600 seconds / 1 hour) -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<h5 class="mb-3">Most Wanted Cards</h5>
|
||||
<div class="card h-100 shadow border-0">
|
||||
<div class="card-body">
|
||||
{% cache 3600 most_wanted_cards %}
|
||||
{% if most_wanted_cards %}
|
||||
<div class="d-flex flex-column gap-3">
|
||||
{% for card in most_wanted_cards %}
|
||||
{% if card.offer_count > 0 %}
|
||||
<a href="?offered_cards={{ card.id }}"
|
||||
class="d-flex justify-content-between align-items-center text-decoration-none text-primary">
|
||||
{% card_badge card %}
|
||||
<span>{{ card.offer_count }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No cards found</p>
|
||||
{% endif %}
|
||||
{% endcache %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Featured Offers and Recent Offers Section -->
|
||||
<div class="row mb-5">
|
||||
<!-- Featured Offers Card (cached for 86400 seconds / 1 day) -->
|
||||
<div class="col-md-6 mb-3">
|
||||
{% cache 86400 featured_offers %}
|
||||
<div class="card h-100 border-0">
|
||||
<div class="card-header border-0 bg-transparent">
|
||||
<h5 class="card-title mb-0">Featured Offers</h5>
|
||||
<ul class="nav nav-tabs card-header-tabs mt-3" id="cardsetTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="all-tab" data-bs-toggle="tab" data-bs-target="#all"
|
||||
type="button" role="tab" aria-controls="all" aria-selected="true">All</button>
|
||||
</li>
|
||||
{% for cardset, offers in featured_offers.items %}
|
||||
{% if cardset != "All" %}
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="{{ cardset|slugify }}-tab" data-bs-toggle="tab" data-bs-target="#{{ cardset|slugify }}"
|
||||
type="button" role="tab" aria-controls="{{ cardset|slugify }}" aria-selected="false">
|
||||
{{ cardset }}
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tab-content" id="cardsetTabsContent">
|
||||
<!-- All Offers Tab Pane -->
|
||||
<div class="tab-pane fade show active" id="all" role="tabpanel" aria-labelledby="all-tab">
|
||||
{% if featured_offers.All %}
|
||||
<div class="d-flex flex-column gap-3">
|
||||
{% for offer in featured_offers.All %}
|
||||
<a href="{% url 'trade_offer_update' offer.pk %}" class="d-flex align-items-center text-decoration-none">
|
||||
{% render_trade_offer offer %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No featured offers available.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Other Cardset Tab Panes -->
|
||||
{% for cardset, offers in featured_offers.items %}
|
||||
{% if cardset != "All" %}
|
||||
<div class="tab-pane fade" id="{{ cardset|slugify }}" role="tabpanel" aria-labelledby="{{ cardset|slugify }}-tab">
|
||||
{% if offers %}
|
||||
<div class="d-flex flex-column gap-3">
|
||||
{% for offer in offers %}
|
||||
<a href="{% url 'trade_offer_update' offer.pk %}" class="d-flex align-items-center text-decoration-none">
|
||||
{% render_trade_offer offer %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No featured offers for {{ cardset }}.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcache %}
|
||||
</div>
|
||||
|
||||
<!-- Recent Offers Card (cached for 60 seconds) -->
|
||||
<div class="col-md-6 mb-3">
|
||||
{% cache 60 recent_offers %}
|
||||
<div class="card h-100 border-0">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Recent Offers</h5>
|
||||
<div class="d-flex flex-column gap-3">
|
||||
{% for offer in recent_offers %}
|
||||
<a href="{% url 'trade_offer_update' offer.pk %}" class="text-decoration-none">
|
||||
{% render_trade_offer offer %}
|
||||
</a>
|
||||
{% empty %}
|
||||
<div>No offers available</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcache %}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock content %}
|
||||
|
||||
{% block javascript %}
|
||||
<!-- <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script> -->
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// function formatOption(option) {
|
||||
// if (!option.id) return option.text;
|
||||
// var $option = $(option.element);
|
||||
// var cardName = $option.data('name');
|
||||
// var rarity = $option.data('rarity');
|
||||
// var cardset = $option.data('cardset');
|
||||
// var style = $option.data('style');
|
||||
// return $('<span>').text(cardName + " " + rarity + " " + cardset).attr('style', style);
|
||||
// }
|
||||
|
||||
// $('.select2-field').select2({
|
||||
// placeholder: function() {
|
||||
// return $(this).data('placeholder');
|
||||
// },
|
||||
// templateResult: formatOption,
|
||||
// templateSelection: formatOption,
|
||||
// width: '100%',
|
||||
// dropdownAutoWidth: true,
|
||||
// allowClear: true
|
||||
// });
|
||||
|
||||
// AJAX form submission for trade search
|
||||
$("#trade-search form").on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
type: $(this).attr("method"),
|
||||
url: $(this).attr("action"),
|
||||
data: $(this).serialize(),
|
||||
headers: { "X-Requested-With": "XMLHttpRequest" },
|
||||
success: function(data) {
|
||||
$("#search-results").html(data);
|
||||
},
|
||||
error: function() {
|
||||
alert("There was an error processing your search.");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// AJAX pagination for search results
|
||||
$(document).on('click', '.ajax-page-link', function(e){
|
||||
e.preventDefault();
|
||||
var page = $(this).data('page');
|
||||
if($("#page").length) {
|
||||
$("#page").val(page);
|
||||
} else {
|
||||
$("<input>").attr({
|
||||
type: "hidden",
|
||||
id: "page",
|
||||
name: "page",
|
||||
value: page
|
||||
}).appendTo("#trade-search form");
|
||||
}
|
||||
$("#trade-search form").submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
13
templates/includes/card_badge.html
Normal file
13
templates/includes/card_badge.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{% if decks|length == 1 %}
|
||||
{% if dropdown %}'{% endif %}<span class="badge card-badge-grid m-1" style="{% if dropdown %}'+ style +'{% else %}background-color: {{ decks.0.hex_color }}; color: white;{% endif %}">{% if dropdown %}' + {% endif %}
|
||||
{% elif decks|length == 2 %}
|
||||
{% if dropdown %}'{% endif %}<span class="badge card-badge-grid m-1" style="{% if dropdown %}'+ style +'{% else %}background: linear-gradient(to right, {{ decks.0.hex_color }}, {{ decks.1.hex_color }}); color: white;{% endif %}">{% if dropdown %}' + {% endif %}
|
||||
{% elif decks|length >= 3 %}
|
||||
{% if dropdown %}'{% endif %}<span class="badge card-badge-grid m-1" style="{% if dropdown %}'+ style +'{% else %}background: linear-gradient(to right, {{ decks.0.hex_color }}, {{ decks.1.hex_color }}, {{ decks.2.hex_color }}); color: white;{% endif %}">{% if dropdown %}' + {% endif %}
|
||||
{% else %}
|
||||
{% if dropdown %}'{% endif %}<span class="badge card-badge-grid m-1" style="{% if dropdown %}'+ style +'{% else %}background-color: #cccccc; color: white;{% endif %}">{% if dropdown %}' + {% endif %}
|
||||
{% endif %}
|
||||
{% if dropdown %}'{% endif %}<span class="card-badge-name">{% if dropdown %}'+ cardName +'{% else %}{{ card.name }}{% endif %}</span>{% if dropdown %}' + {% endif %}
|
||||
{% if dropdown %}'{% endif %}<span class="card-badge-rarity">{% if dropdown %}'+ rarity +'{% else %}{{ card.rarity.icons }}{% endif %}</span>{% if dropdown %}' + {% endif %}
|
||||
{% if dropdown %}'{% endif %}<span class="card-badge-cardset">{% if dropdown %}'+ cardset +'{% else %}{{ card.cardset.name }}{% endif %}</span>{% if dropdown %}' + {% endif %}
|
||||
{% if dropdown %}'{% endif %}</span>{% if dropdown %}'{% endif %}
|
||||
29
templates/includes/card_multiselect.html
Normal file
29
templates/includes/card_multiselect.html
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{% load cache card_badge %}
|
||||
<label for="{{ field_id }}" class="form-label">{{ label }}</label>
|
||||
<select name="{{ field_name }}" id="{{ field_id }}" class="form-select select2-field" data-placeholder="{{ placeholder }}" multiple="multiple">
|
||||
{% cache cache_timeout cache_key %}
|
||||
<option value="" disabled="disabled">{{ placeholder }}</option>
|
||||
{% for card in available_cards %}
|
||||
<option value="{{ card.pk }}"
|
||||
data-name="{{ card.name }}"
|
||||
data-rarity="{{ card.rarity.icons }}"
|
||||
data-cardset="{{ card.cardset.name }}"
|
||||
data-style="{{ card.style }}"
|
||||
{{ card.name }} {{ card.rarity.icons }} {{ card.cardset.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% endcache %}
|
||||
</select>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#{{ field_id }}').select2({
|
||||
placeholder: $('#{{ field_id }}').data('placeholder'),
|
||||
templateResult: formatOption,
|
||||
templateSelection: formatOption,
|
||||
width: '100%',
|
||||
dropdownAutoWidth: true,
|
||||
allowClear: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
62
templates/includes/trade_offer.html
Normal file
62
templates/includes/trade_offer.html
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{% load gravatar card_badge %}
|
||||
|
||||
<div class="card trade-offer mb-3 mx-auto shadow-lg unified-card" style="border: none;">
|
||||
<div class="card-body trade-offer-body">
|
||||
<!-- Header Row: Using Grid, with relative positioning for avatar placement -->
|
||||
<div class="row no-gutters">
|
||||
<!-- Has Side -->
|
||||
<div class="col-6 position-relative" style="padding: 1rem;">
|
||||
{% if offer.initiated_by and offer.initiated_by.user.email %}
|
||||
<!-- Positioned to the left -->
|
||||
<div class="avatar position-absolute" style="left: 1rem; top: 50%; transform: translateY(-50%);">
|
||||
{{ offer.initiated_by.user.email|gravatar:40 }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Centered text remains in the normal flow -->
|
||||
<div class="text-center">
|
||||
<h6 class="card-subtitle text-muted mb-0">Has</h6>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Wants Side -->
|
||||
<div class="col-6 position-relative" style="padding: 1rem;">
|
||||
{% if offer.accepted_by and offer.accepted_by.user.email %}
|
||||
<!-- Positioned to the right -->
|
||||
<div class="avatar position-absolute" style="right: 1rem; top: 50%; transform: translateY(-50%);">
|
||||
{{ offer.accepted_by.user.email|gravatar:40 }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Centered text remains in the normal flow -->
|
||||
<div class="text-center">
|
||||
<h6 class="card-subtitle text-muted mb-0">Wants</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Body Row: Using Grid, no separators; badge spacing is consistent -->
|
||||
<div class="row no-gutters">
|
||||
<div class="col-6" style="padding: 1rem;">
|
||||
<div class="trade-offer-cards d-flex flex-wrap justify-content-center gap-2">
|
||||
{% if offer.have_cards.all %}
|
||||
{% for card in offer.have_cards.all %}
|
||||
{% card_badge card %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6" style="padding: 1rem;">
|
||||
<div class="trade-off-offer-cards d-flex flex-wrap justify-content-center gap-2">
|
||||
{% if offer.want_cards.all %}
|
||||
{% for card in offer.want_cards.all %}
|
||||
{% card_badge card %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trade ID Footer with Info Icon -->
|
||||
<small class="text-muted mt-auto d-block text-end pe-2">
|
||||
<i class="bi bi-info-circle-fill" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Trade ID: {{ offer.hash }}" style="cursor: pointer;"></i>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{% extends '_base.html' %}
|
||||
|
||||
{% block title %}About page{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>About page</h1>
|
||||
{% endblock content %}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Home page{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
|
||||
<img src="{% static 'images/logo.png' %}" class="img-fluid" alt="DjangoX logo"/>
|
||||
<p class="lead">A Django starter project with batteries.</p>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
41
templates/trades/trade_offer_create.html
Normal file
41
templates/trades/trade_offer_create.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
{% load card_multiselect %}
|
||||
|
||||
{% block title %}Create Trade Offer{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Create a Trade Offer</h2>
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{# Render the non–Select2 field normally (e.g. initiated_by) #}
|
||||
<div class="mb-3">
|
||||
<label for="initiated_by" class="form-label">Initiated by</label>
|
||||
{{ form.initiated_by }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
{% card_multiselect "have_cards" "Have:" available_cards "Select one or more cards..." form.have_cards.value %}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
{% card_multiselect "want_cards" "Want:" available_cards "Select one or more cards..." form.want_cards.value %}
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<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 %}
|
||||
{% endblock content %}
|
||||
14
templates/trades/trade_offer_delete.html
Normal file
14
templates/trades/trade_offer_delete.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Delete Trade Offer{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Delete Trade Offer</h2>
|
||||
<p>Are you sure you want to delete this trade offer?</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger">Confirm Delete</button>
|
||||
<a href="{% url 'trade_offer_list' %}" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
62
templates/trades/trade_offer_list.html
Normal file
62
templates/trades/trade_offer_list.html
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
{% load el_pagination_tags %}
|
||||
|
||||
{% block title %}Trade Offer List{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<form method="get" class="d-flex align-items-center">
|
||||
<div class="form-check me-3">
|
||||
<input class="form-check-input" type="checkbox" name="show_completed" id="show_completed" value="true" {% if show_completed %}checked{% endif %}>
|
||||
<label class="form-check-label" for="show_completed">
|
||||
Only Completed
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Apply</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<h2>Trade Offers</h2>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Offer</th>
|
||||
<th>State</th>
|
||||
<th>Updated At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% paginate 10 object_list as paginated_offers %}
|
||||
{% for offer in paginated_offers %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'trade_offer_update' offer.id %}" class="d-flex align-items-center text-decoration-none">
|
||||
<div class="flex-grow-1 text-start">
|
||||
FT: {% for card in offer.cards_ft.all %}
|
||||
{{ card.name }}{% if not forloop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="px-2 text-center" style="min-width: 50px;">⟶</div>
|
||||
<div class="flex-grow-1 text-end">
|
||||
LF: {% for card in offer.cards_lf.all %}
|
||||
{{ card.name }}{% if not forloop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ offer.get_state_display }}</td>
|
||||
<td>{{ offer.updated_at }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="3">No trade offers available.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pagination">
|
||||
{% show_pages %}
|
||||
</div>
|
||||
<a href="{% url 'trade_offer_create' %}" class="btn btn-success">Create New Offer</a>
|
||||
{% endblock content %}
|
||||
100
templates/trades/trade_offer_update.html
Normal file
100
templates/trades/trade_offer_update.html
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Trade Offer Details & Update{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
<h2 class="mb-4">Trade Offer Details</h2>
|
||||
|
||||
<!-- Offer Details Card -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
Offer Information
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>
|
||||
<strong>Created At:</strong> {{ object.created_at|date:"M d, Y H:i" }}<br>
|
||||
<strong>Updated At:</strong> {{ object.updated_at|date:"M d, Y H:i" }}<br>
|
||||
|
||||
{% comment %}
|
||||
Only display these fields if the current user is associated with the initiating friend code
|
||||
or (if available) with the accepted friend code.
|
||||
{% endcomment %}
|
||||
{% if object.initiated_by.user == request.user or object.accepted_by and object.accepted_by.user == request.user %}
|
||||
<strong>Initiated By:</strong> {{ object.initiated_by }}<br>
|
||||
<strong>Accepted By:</strong>
|
||||
{% if object.accepted_by %}
|
||||
{{ object.accepted_by }}
|
||||
{% else %}
|
||||
Not yet accepted
|
||||
{% endif %}<br>
|
||||
{% endif %}
|
||||
|
||||
<strong>Cards You Have:</strong>
|
||||
{% for card in object.have_cards.all %}
|
||||
{{ card.name }}{% if not forloop.last %}, {% endif %}
|
||||
{% endfor %}<br>
|
||||
<strong>Cards You Want:</strong>
|
||||
{% for card in object.want_cards.all %}
|
||||
{{ card.name }}{% if not forloop.last %}, {% endif %}
|
||||
{% endfor %}<br>
|
||||
<strong>Current State:</strong> {{ object.get_state_display }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if form.fields %}
|
||||
<!-- Form Card -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
{% if action == "accept" %}
|
||||
Accept Trade Offer
|
||||
{% else %}
|
||||
Update Trade Offer
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn {% if action == 'accept' %}btn-success{% else %}btn-primary{% endif %}">
|
||||
{% if action == "accept" %}
|
||||
Accept Trade Offer
|
||||
{% else %}
|
||||
Submit
|
||||
{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
You are not authorized to perform any status changes on this trade offer.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if form and form.errors %}
|
||||
<div class="alert alert-danger mt-3">
|
||||
<strong>Please correct the errors below:</strong>
|
||||
<ul class="mb-0">
|
||||
{% 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 class="mt-3">
|
||||
<a href="{% url 'trade_offer_list' %}" class="btn btn-secondary">Back to Trade Offers</a>
|
||||
{% if can_delete %}
|
||||
<a href="{% url 'trade_offer_delete' object.pk %}" class="btn btn-danger ms-2">Delete Trade Offer</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue