Initial working version with minor bugs

This commit is contained in:
badblocks 2025-02-26 00:06:42 -08:00
parent f946e4933a
commit 71b3993326
83 changed files with 34485 additions and 173 deletions

View file

@ -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>

View 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 %}

View 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 %}

View 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 %}

View 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
View 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 %}

View 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 %}

View 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>

View 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>

View file

@ -1,7 +0,0 @@
{% extends '_base.html' %}
{% block title %}About page{% endblock %}
{% block content %}
<h1>About page</h1>
{% endblock content %}

View file

@ -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 %}

View 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 nonSelect2 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 %}

View 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 %}

View 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;">&#x27F6;</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 %}

View 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 %}