inital checkin

This commit is contained in:
2026-01-20 05:22:38 -06:00
parent 9784e14c77
commit c43603bfb5
75 changed files with 4327 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
{% extends 'base/layout.html' %}
{% block content %}
<div style="max-width: 1000px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 3rem;">
<h1 style="font-size: 2.5rem; margin-bottom: 0.5rem; background: linear-gradient(to right, #fbbf24, #d97706); -webkit-background-clip: text; color: transparent;">Community Bounty Board</h1>
<p style="color: #94a3b8; font-size: 1.125rem;">We're looking for these cards! Sell them to us for a bonus.</p>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1.5rem;">
{% for bounty in bounties %}
<div style="background: var(--card-bg); border: 1px solid #d97706; border-radius: 0.75rem; overflow: hidden; position: relative;">
<div style="background: #d97706; color: white; padding: 0.25rem 0.5rem; position: absolute; top: 0; right: 0; font-weight: 700; font-size: 0.75rem; border-bottom-left-radius: 0.5rem;">
WANTED
</div>
<div style="padding: 1.5rem;">
<h3 style="margin: 0 0 0.5rem; font-size: 1.25rem;">{{ bounty.card.name }}</h3>
<p style="margin: 0; color: #94a3b8; font-size: 0.875rem;">{{ bounty.card.set.name }}</p>
<div style="margin-top: 1.5rem; display: flex; justify-content: space-between; align-items: flex-end;">
<div>
<div style="font-size: 0.75rem; color: #94a3b8;">Buying At</div>
<div style="font-size: 1.5rem; font-weight: 800; color: #fbbf24;">${{ bounty.target_price }}</div>
</div>
<div style="text-align: right;">
<div style="font-size: 0.75rem; color: #94a3b8; margin-bottom: 0.25rem;">Qty Wanted: {{ bounty.quantity_wanted }}</div>
<button onclick="alert('Sell Offer Submitted! (Mockup)')" class="btn" style="padding: 0.25rem 0.75rem; font-size: 0.875rem;">I Have This</button>
</div>
</div>
</div>
</div>
{% empty %}
<div style="grid-column: 1 / -1; text-align: center; padding: 4rem; background: var(--card-bg); border-radius: 0.5rem; border: 1px dashed var(--border-color);">
<p style="color: #94a3b8; font-size: 1.25rem;">No active bounties at the moment.</p>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,125 @@
{% extends 'base/layout.html' %}
{% block content %}
<div style="display: grid; grid-template-columns: 1fr 2fr; gap: 3rem;">
<!-- Image Column -->
<div>
<div
style="background: #000; border-radius: 0.75rem; overflow: hidden; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5);">
{% if card.image_url %}
<img src="{{ card.image_url }}" alt="{{ card.name }}" style="width: 100%; display: block;">
{% else %}
<div
style="aspect-ratio: 2.5/3.5; display: flex; align-items: center; justify-content: center; color: #64748b; background: #334155;">
No Image</div>
{% endif %}
</div>
<!-- External Links -->
<div style="margin-top: 1.5rem; display: grid; gap: 0.5rem;">
{% if card.tcgplayer_id %}
<a href="https://www.tcgplayer.com/product/{{ card.tcgplayer_id }}" target="_blank"
style="display: block; text-align: center; padding: 0.75rem; background: #27272a; color: white; border-radius: 0.375rem; text-decoration: none; font-size: 0.875rem;">
View on TCGPlayer
</a>
{% endif %}
<a href="https://www.ebay.com/sch/i.html?_nkw={{ card.name|urlencode }}+{{ card.set.name|urlencode }}"
target="_blank"
style="display: block; text-align: center; padding: 0.75rem; background: #27272a; color: white; border-radius: 0.375rem; text-decoration: none; font-size: 0.875rem;">
Search on eBay
</a>
</div>
</div>
<!-- Info Column -->
<div>
<div style="margin-bottom: 2rem;">
<h4 style="margin: 0; color: var(--primary-color);">{{ card.set.game.name }} &bull; {{ card.set.name }}</h4>
<h1 style="margin: 0.5rem 0 0; font-size: 2.5rem;">{{ card.name }}</h1>
<div style="margin-top: 1rem; display: flex; gap: 1rem; color: #94a3b8;">
<span>Rarity: <strong style="color: var(--text-color);">{{ card.rarity }}</strong></span>
<span>Collector #: <strong style="color: var(--text-color);">{{ card.collector_number }}</strong></span>
</div>
</div>
<h3 style="border-bottom: 1px solid var(--border-color); padding-bottom: 0.5rem; margin-bottom: 1rem;">Available
Listings</h3>
<div style="margin-bottom: 1rem;">
<label style="font-size: 0.875rem; color: #94a3b8;">Filter Condition:</label>
<div style="display: flex; gap: 0.5rem; margin-top: 0.25rem;">
<button onclick="filterCondition('ALL')" class="btn" style="padding: 0.25rem 0.75rem; font-size: 0.75rem; background: var(--card-bg); border: 1px solid var(--border-color);">All</button>
<button onclick="filterCondition('NM')" class="btn" style="padding: 0.25rem 0.75rem; font-size: 0.75rem; background: var(--card-bg); border: 1px solid var(--border-color);">NM</button>
<button onclick="filterCondition('LP')" class="btn" style="padding: 0.25rem 0.75rem; font-size: 0.75rem; background: var(--card-bg); border: 1px solid var(--border-color);">LP</button>
<button onclick="filterCondition('MP')" class="btn" style="padding: 0.25rem 0.75rem; font-size: 0.75rem; background: var(--card-bg); border: 1px solid var(--border-color);">MP</button>
<button onclick="filterCondition('HP')" class="btn" style="padding: 0.25rem 0.75rem; font-size: 0.75rem; background: var(--card-bg); border: 1px solid var(--border-color);">HP</button>
</div>
</div>
<div id="listings-container" style="display: flex; flex-direction: column; gap: 1rem;">
{% for listing in listings %}
<div class="listing-item" data-condition="{{ listing.condition }}"
style="display: flex; justify-content: space-between; align-items: center; background: var(--card-bg); padding: 1rem; border-radius: 0.5rem; border: 1px solid var(--border-color);">
<div style="display: flex; gap: 1rem; align-items: center;">
<span style="font-weight: 700; font-size: 1.25rem; width: 3rem; text-align: center;">{{
listing.condition }}</span>
<div>
<div style="font-size: 0.875rem; color: #94a3b8;">Condition</div>
{% if listing.is_foil %}
<span
style="background: linear-gradient(45deg, #f59e0b, #d97706); -webkit-background-clip: text; color: transparent; font-weight: 700; font-size: 0.75rem; text-transform: uppercase;">Foil</span>
{% endif %}
</div>
</div>
<div style="display: flex; align-items: center; gap: 2rem;">
<div style="text-align: right;">
<div style="font-weight: 700; font-size: 1.5rem;">${{ listing.price }}</div>
<div style="font-size: 0.75rem; color: #94a3b8;">{{ listing.quantity }} available</div>
</div>
{% if user.is_authenticated %}
<form action="{% url 'store:add_to_cart' listing.id %}" method="post">
{% csrf_token %}
<button type="submit" class="btn">Add to Cart</button>
</form>
{% else %}
<a href="{% url 'login' %}?next={{ request.path }}" class="btn" style="background: #334155;">Login
to Buy</a>
{% endif %}
</div>
</div>
{% empty %}
<div
style="text-align: center; padding: 2rem; background: var(--card-bg); border-radius: 0.5rem; border: 1px dashed var(--border-color);">
<p style="margin: 0; color: #94a3b8;">No listings currently available for this card.</p>
</div>
{% endfor %}
</div>
<!-- Proxy Service -->
<div style="margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border-color);">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>
<h3 style="margin: 0 0 0.5rem;">Playtest Proxy Service</h3>
<p style="margin: 0; color: #94a3b8; font-size: 0.875rem;">Download a high-res proxy for playtesting. Credit offered if you buy later.</p>
</div>
<button onclick="alert('Proxy PDF generated! (Mockup)')" class="btn" style="background: transparent; border: 1px solid var(--border-color);">Download Proxy</button>
</div>
</div>
<script>
function filterCondition(cond) {
const items = document.querySelectorAll('.listing-item');
items.forEach(item => {
if (cond === 'ALL' || item.dataset.condition === cond) {
item.style.display = 'flex';
} else {
item.style.display = 'none';
}
});
}
</script>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,142 @@
{% extends 'base/layout.html' %}
{% load static %}
{% block content %}
<div style="display: grid; grid-template-columns: 250px 1fr; gap: 2rem;">
<!-- Sidebar Filters -->
<aside
style="background: var(--card-bg); padding: 1.5rem; border-radius: 0.5rem; border: 1px solid var(--border-color); height: fit-content;">
<h3 style="margin-top: 0;">Filters</h3>
<form method="get">
<div style="margin-bottom: 1rem;">
<label style="display: block; font-size: 0.875rem; margin-bottom: 0.5rem;">Game</label>
<select name="game"
style="width: 100%; padding: 0.5rem; border-radius: 0.25rem; background: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color);"
onchange="this.form.submit()">
<option value="">All Games</option>
{% for game in games %}
<option value="{{ game.slug }}" {% if current_game|slugify == game.slug %}selected{% endif %}>
{{game.name}}
</option>
{% endfor %}
</select>
</div>
{% if current_game %}
<div style="margin-bottom: 1rem;">
<label style="display: block; font-size: 0.875rem; margin-bottom: 0.5rem;">Set</label>
<select name="set"
style="width: 100%; padding: 0.5rem; border-radius: 0.25rem; background: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color);">
<option value="">All Sets</option>
{% for set in sets %}
<option value="{{ set.id }}" {% if request.GET.set|add:"0" == set.id %}selected{% endif %}>{{ set.name
}}</option>
{% endfor %}
</select>
</div>
{% endif %}
<div style="margin-bottom: 1rem;">
<label style="display: block; font-size: 0.875rem; margin-bottom: 0.5rem;">Search</label>
<input type="text" name="q" value="{{ search_query|default:'' }}" placeholder="Card name..."
style="width: 90%; padding: 0.5rem; border-radius: 0.25rem; background: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color);">
</div>
<button type="submit" class="btn" style="width: 100%;">Apply Filters</button>
<a href="{% url 'store:card_list' %}"
style="display: block; text-align: center; margin-top: 1rem; color: #94a3b8; font-size: 0.875rem; text-decoration: none;">Clear
Filters</a>
</form>
</aside>
<!-- Card Grid -->
<div>
<h2 style="margin-top: 0;">Browse Cards</h2>
<div class="card-grid">
{% for card in page_obj %}
<div class="tcg-card">
<a href="{% url 'store:card_detail' card.id %}" style="text-decoration: none; color: inherit;">
<div style="aspect-ratio: 2.5/3.5; background: #000; position: relative;">
<!-- Placeholder or Real Image -->
{% if card.image_url %}
<img src="{{ card.image_url }}" alt="{{ card.name }}"
style="width: 100%; height: 100%; object-fit: cover;">
{% else %}
<div
style="display: flex; align-items: center; justify-content: center; height: 100%; color: #64748b; background: #334155;">
No Image</div>
{% endif %}
</div>
<div class="tcg-card-body">
<h4
style="margin: 0 0 0.5rem; font-size: 1rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
{{ card.name }}</h4>
<div
style="display: flex; justify-content: space-between; align-items: center; font-size: 0.875rem; color: #94a3b8;">
<span>{{ card.set.code|default:card.set.game.name }}</span>
<span>{{ card.rarity }}</span>
</div>
<div
style="margin-top: 0.75rem; padding-top: 0.75rem; border-top: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center;">
{% with card.listings.first as cheapest %}
{% if cheapest %}
<span style="font-weight: 700; color: var(--text-color);">From ${{ cheapest.price }}</span>
{% else %}
<span style="color: #64748b;">Out of Stock</span>
{% endif %}
{% endwith %}
<span id="stock-{{ card.id }}" class="stock-counter" data-card-id="{{ card.id }}" style="font-size: 0.75rem; color: #94a3b8; margin-left: auto;">...</span>
</div>
</div>
</a>
</div>
{% empty %}
<p>No cards found matching your criteria.</p>
{% endfor %}
</div>
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<div style="margin-top: 2rem; display: flex; justify-content: center; gap: 0.5rem;">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}&game={{ current_game }}&q={{ search_query }}" class="btn"
style="padding: 0.25rem 0.5rem; font-size: 0.875rem;">Prev</a>
{% endif %}
<span
style="padding: 0.25rem 0.75rem; background: var(--card-bg); border-radius: 0.25rem; border: 1px solid var(--border-color);">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}&game={{ current_game }}&q={{ search_query }}" class="btn"
style="padding: 0.25rem 0.5rem; font-size: 0.875rem;">Next</a>
{% endif %}
</div>
{% endif %}
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
const stockCounters = document.querySelectorAll('.stock-counter');
stockCounters.forEach(counter => {
const cardId = counter.getAttribute('data-card-id');
fetch(`/store/api/stock/${cardId}/`)
.then(response => response.json())
.then(data => {
if (data.total_stock > 0) {
counter.textContent = `${data.total_stock} in stock`;
counter.style.color = '#10b981'; // green
} else {
counter.textContent = 'Out of Stock';
counter.style.color = '#ef4444'; // red
}
})
.catch(err => {
counter.textContent = 'Stock unknown';
});
});
});
</script>
{% endblock %}

80
templates/store/cart.html Normal file
View File

@@ -0,0 +1,80 @@
{% extends 'base/layout.html' %}
{% block content %}
<div style="max-width: 800px; margin: 0 auto;">
<h1 style="margin-bottom: 2rem;">Shopping Cart</h1>
{% if cart and cart.items.count > 0 %}
<div
style="background: var(--card-bg); border-radius: 0.5rem; border: 1px solid var(--border-color); overflow: hidden;">
{% for item in cart.items.all %}
<div
style="display: flex; justify-content: space-between; align-items: center; padding: 1.5rem; border-bottom: 1px solid var(--border-color);">
<div style="display: flex; gap: 1.5rem; align-items: center;">
{% if item.listing %}
{% if item.listing.card.image_url %}
<img src="{{ item.listing.card.image_url }}"
style="width: 50px; height: 70px; object-fit: cover; border-radius: 4px;">
{% endif %}
<div>
<h3 style="margin: 0; font-size: 1.125rem;">{{ item.listing.card.name }}</h3>
<p style="margin: 0.25rem 0 0; color: #94a3b8; font-size: 0.875rem;">
{{ item.listing.get_condition_display }}
{% if item.listing.is_foil %}&bull; Foil{% endif %}
</p>
</div>
{% else %}
{% if item.pack_listing.image_url %}
<img src="{{ item.pack_listing.image_url }}" style="width: 50px; height: 70px; object-fit: cover; border-radius: 4px;">
{% else %}
<div style="width: 50px; height: 70px; background: #6366f1; border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: 1.5rem;">📦</div>
{% endif %}
<div>
<h3 style="margin: 0; font-size: 1.125rem;">{{ item.pack_listing.name }}</h3>
<p style="margin: 0.25rem 0 0; color: #94a3b8; font-size: 0.875rem;">Booster Pack</p>
</div>
{% endif %}
</div>
<div style="display: flex; align-items: center; gap: 2rem;">
<div style="text-align: right;">
<div style="font-weight: 600;">{{ item.quantity }} x ${% if item.listing %}{{ item.listing.price }}{% else %}{{ item.pack_listing.price }}{% endif %}</div>
</div>
<div style="font-weight: 700; font-size: 1.25rem;">
${{ item.total_price }}
</div>
<a href="{% url 'store:remove_from_cart' item.id %}"
style="color: #ef4444; text-decoration: none; font-size: 1.25rem;">&times;</a>
</div>
</div>
{% endfor %}
<div style="padding: 1rem 1.5rem; background: rgba(0,0,0,0.1); border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center;">
<div style="display: flex; align-items: center; gap: 0.75rem;">
<a href="{% url 'store:toggle_insurance' %}" style="text-decoration: none; display: flex; align-items: center; gap: 0.5rem; color: var(--text-color);">
<div style="width: 20px; height: 20px; border: 2px solid var(--border-color); border-radius: 4px; display: flex; align-items: center; justify-content: center; background: {% if cart.insurance %}var(--primary-color, #3b82f6){% else %}transparent{% endif %};">
{% if cart.insurance %}<span style="color: white; font-size: 14px;"></span>{% endif %}
</div>
<span><strong>Add Shipping Insurance</strong> (Protects against damage/loss)</span>
</a>
</div>
<span>+$5.00</span>
</div>
<div
style="padding: 1.5rem; background: rgba(0,0,0,0.2); display: flex; justify-content: space-between; align-items: center;">
<span style="font-size: 1.25rem; font-weight: 600;">Total</span>
<span style="font-size: 1.5rem; font-weight: 800;">${{ cart.total_price }}</span>
</div>
</div>
<div style="margin-top: 2rem; text-align: right;">
<a href="{% url 'store:checkout' %}" class="btn" style="padding: 1rem 2rem; font-size: 1.125rem;">Proceed to Checkout</a>
</div>
{% else %}
<div style="text-align: center; padding: 4rem; background: var(--card-bg); border-radius: 0.5rem; color: #94a3b8;">
<p style="font-size: 1.25rem; margin-bottom: 1.5rem;">Your cart is empty.</p>
<a href="{% url 'store:card_list' %}" class="btn">Browse Cards</a>
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,60 @@
{% extends 'base/layout.html' %}
{% block content %}
<div style="max-width: 800px; margin: 0 auto;">
<h2 style="margin-bottom: 1.5rem;">Mass Deck Buyer</h2>
<p style="margin-bottom: 2rem; color: #94a3b8;">Paste your deck list below to instantly find the cheapest printings for every card.</p>
{% if preview %}
<div style="background: var(--card-bg); padding: 1.5rem; border-radius: 0.5rem; border: 1px solid var(--border-color); margin-bottom: 2rem;">
<h3 style="margin-top: 0;">Review Purchase</h3>
<p style="font-size: 1.25rem; font-weight: bold; margin-bottom: 1rem;">Total Estimated Cost: <span style="color: #10b981;">${{ total_cost }}</span></p>
<h4 style="border-bottom: 1px solid var(--border-color); padding-bottom: 0.5rem;">Found Items</h4>
<ul style="list-style: none; padding: 0; margin-bottom: 1.5rem;">
{% for item in found_items %}
<li style="display: flex; justify-content: space-between; padding: 0.5rem 0; border-bottom: 1px solid #334155;">
<span>{{ item.quantity }}x <strong>{{ item.card_name }}</strong> <small style="color: #94a3b8;">({{ item.listing.get_condition_display }})</small></span>
<span>${{ item.total }}</span>
</li>
{% endfor %}
</ul>
{% if missing_items %}
<h4 style="border-bottom: 1px solid var(--border-color); padding-bottom: 0.5rem; color: #ef4444;">Missing / Out of Stock</h4>
<ul style="list-style: none; padding: 0; margin-bottom: 1.5rem;">
{% for item in missing_items %}
<li style="padding: 0.5rem 0; color: #f87171;">
{{ item.quantity }}x {{ item.name }}
</li>
{% endfor %}
</ul>
{% endif %}
<form method="post" style="display: flex; gap: 1rem;">
{% csrf_token %}
<input type="hidden" name="action" value="add_to_cart">
<input type="hidden" name="deck_text" value="{{ deck_text }}">
<button type="submit" class="btn" style="flex: 1;">Confirm & Add to Cart</button>
<a href="{% url 'store:deck_buyer' %}" class="btn" style="background: var(--bg-color); border: 1px solid var(--border-color); color: var(--text-color); flex: 1; text-align: center; text-decoration: none;">Cancel</a>
</form>
</div>
{% else %}
<form method="post" style="display: flex; flex-direction: column; gap: 1rem;">
{% csrf_token %}
<input type="hidden" name="action" value="preview">
<div style="background: var(--bg-color); padding: 1rem; border: 1px solid var(--border-color); border-radius: 0.5rem;">
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" name="ignore_owned" id="ignore_owned">
<span><strong>Complete My Deck:</strong> Exclude cards I already own from my purchase.</span>
</label>
</div>
<label for="deck_text" style="font-weight: bold;">Deck List</label>
<textarea name="deck_text" id="deck_text" rows="15" placeholder="4 Lightning Bolt&#10;2 Counterspell&#10;1 Sol Ring" style="padding: 1rem; border-radius: 0.5rem; background: var(--card-bg); color: var(--text-color); border: 1px solid var(--border-color); font-family: monospace;"></textarea>
<button type="submit" class="btn">Find Cards</button>
</form>
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,30 @@
{% extends 'base/layout.html' %}
{% block content %}
<div style="max-width: 1200px; margin: 0 auto;">
<h1 style="margin-bottom: 2rem;">My Packs</h1>
{% if packs %}
<div class="card-grid">
{% for pack in packs %}
<div class="tcg-card">
<div style="aspect-ratio: 2.5/3.5; background: linear-gradient(135deg, #4f46e5, #0ea5e9); position: relative; display: flex; align-items: center; justify-content: center;">
<div style="text-align: center; color: white;">
<div style="font-size: 3rem;">🎁</div>
<div>{{ pack.listing.name }}</div>
</div>
</div>
<div class="tcg-card-body" style="text-align: center;">
<h4 style="margin: 0 0 0.5rem; font-size: 1rem;">{{ pack.listing.name }}</h4>
<a href="{% url 'store:open_pack' pack.id %}" class="btn" style="width: 100%; display: block; margin-top: 1rem;">Open Pack</a>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div style="text-align: center; padding: 4rem; background: var(--card-bg); border-radius: 0.5rem;">
<p>You don't have any sealed packs.</p>
<a href="{% url 'store:pack_list' %}" class="btn">Buy Packs</a>
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,125 @@
{% extends 'base/layout.html' %}
{% load static %}
{% block content %}
<div style="max-width: 1000px; margin: 0 auto; text-align: center; min-height: 600px;">
<h1 id="pack-title">{{ pack.listing.name }}</h1>
<div id="pack-container" style="margin: 4rem auto; perspective: 1000px; width: 300px; height: 420px; cursor: pointer;">
<div id="pack-wrapper" style="width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.6s;">
<div class="pack-face" style="position: absolute; width: 100%; height: 100%; backface-visibility: hidden; background: linear-gradient(135deg, #4f46e5, #0ea5e9); border-radius: 1rem; display: flex; align-items: center; justify-content: center; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);">
<div style="font-size: 5rem;">🎁</div>
<div style="position: absolute; bottom: 2rem; color: white; font-weight: bold;">Click to Open</div>
</div>
</div>
</div>
<div id="cards-container" style="display: none; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-top: 2rem;">
<!-- Cards injected here -->
</div>
<div id="actions" style="margin-top: 2rem; display: none;">
<a href="{% url 'store:my_packs' %}" class="btn">Back to My Packs</a>
</div>
</div>
<style>
@keyframes shake {
0% { transform: translate(1px, 1px) rotate(0deg); }
10% { transform: translate(-1px, -2px) rotate(-1deg); }
20% { transform: translate(-3px, 0px) rotate(1deg); }
30% { transform: translate(3px, 2px) rotate(0deg); }
40% { transform: translate(1px, -1px) rotate(1deg); }
50% { transform: translate(-1px, 2px) rotate(-1deg); }
60% { transform: translate(-3px, 1px) rotate(0deg); }
70% { transform: translate(3px, 1px) rotate(-1deg); }
80% { transform: translate(-1px, -1px) rotate(1deg); }
90% { transform: translate(1px, 2px) rotate(0deg); }
100% { transform: translate(1px, -2px) rotate(-1deg); }
}
.shaking {
animation: shake 0.5s;
animation-iteration-count: infinite;
}
.card-reveal {
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.5s forwards;
}
@keyframes fadeInUp {
to { opacity: 1; transform: translateY(0); }
}
</style>
<script>
const packWrapper = document.getElementById('pack-wrapper');
const packContainer = document.getElementById('pack-container');
const cardsContainer = document.getElementById('cards-container');
const actions = document.getElementById('actions');
const packTitle = document.getElementById('pack-title');
packContainer.addEventListener('click', function() {
if (packContainer.classList.contains('opened')) return;
packWrapper.classList.add('shaking');
// Use fetch with CSRF token
const csrftoken = document.cookie.split('; ').find(row => row.startsWith('csrftoken='))?.split('=')[1];
fetch(window.location.href, {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token }}',
'Content-Type': 'application/json'
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(data => {
setTimeout(() => {
packWrapper.classList.remove('shaking');
packWrapper.style.transform = 'scale(0) rotate(720deg)';
packWrapper.style.opacity = '0';
setTimeout(() => {
packContainer.style.display = 'none';
cardsContainer.style.display = 'grid';
actions.style.display = 'block';
packTitle.textContent = "New Cards!";
data.cards.forEach((card, index) => {
const cardEl = document.createElement('div');
cardEl.className = 'tcg-card card-reveal';
cardEl.style.animationDelay = `${index * 0.2}s`;
let imgHtml = '';
if (card.image_url) {
imgHtml = `<img src="${card.image_url}" style="width: 100%; height: auto;">`;
} else {
imgHtml = `<div style="aspect-ratio: 2.5/3.5; background: #000; display: flex; align-items: center; justify-content: center; color: white;">${card.name}</div>`;
}
cardEl.innerHTML = `
<div style="aspect-ratio: 2.5/3.5; background: #000; position: relative;">
${imgHtml}
</div>
<div class="tcg-card-body">
<h4>${card.name}</h4>
<p>${card.set} - ${card.rarity}</p>
</div>
`;
cardsContainer.appendChild(cardEl);
});
}, 500);
}, 1000);
})
.catch(err => {
console.error(err);
packWrapper.classList.remove('shaking');
alert('Error opening pack');
});
packContainer.classList.add('opened');
});
</script>
{% endblock %}

View File

@@ -0,0 +1,74 @@
{% extends 'base/layout.html' %}
{% block content %}
<div class="container" style="max-width: 800px; margin: 0 auto; padding: 2rem;">
<div style="margin-bottom: 2rem; display: flex; justify-content: space-between; align-items: center;">
<h1 style="margin: 0;">Order #{{ order.id }}</h1>
<a href="{% url 'users:profile' %}" class="btn" style="background-color: var(--card-bg); border: 1px solid var(--border-color); color: var(--text-color);">Back to Profile</a>
</div>
<div style="background: var(--card-bg); padding: 2rem; border-radius: 0.5rem; border: 1px solid var(--border-color); margin-bottom: 2rem;">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 2rem;">
<div>
<p style="color: #94a3b8; font-size: 0.875rem; margin-bottom: 0.5rem;">Date Placed</p>
<p style="font-weight: 600;">{{ order.created_at|date:"F j, Y, P" }}</p>
</div>
<div>
<p style="color: #94a3b8; font-size: 0.875rem; margin-bottom: 0.5rem;">Status</p>
<span style="display: inline-block; padding: 0.25rem 0.75rem; border-radius: 999px; background: #334155; font-size: 0.875rem; font-weight: 600;">
{{ order.get_status_display }}
</span>
</div>
<div>
<p style="color: #94a3b8; font-size: 0.875rem; margin-bottom: 0.5rem;">Total Amount</p>
<p style="font-weight: 600; font-size: 1.25rem;">${{ order.total_price }}</p>
</div>
</div>
{% if order.insurance_purchased %}
<div style="margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border-color);">
<p style="display: flex; align-items: center; gap: 0.5rem; color: #10b981;">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
Shipping Insurance Included
</p>
</div>
{% endif %}
</div>
<h3 style="margin-bottom: 1.5rem;">Order Items</h3>
<div style="display: grid; gap: 1rem;">
{% for item in order.items.all %}
<div style="background: var(--card-bg); padding: 1.5rem; border-radius: 0.5rem; border: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center;">
<div style="display: flex; align-items: center; gap: 1rem;">
{% if item.listing and item.listing.card.image_url %}
<img src="{{ item.listing.card.image_url }}" alt="{{ item.listing.card.name }}" style="width: 50px; border-radius: 4px;">
{% elif item.pack_listing and item.pack_listing.image_url %}
<img src="{{ item.pack_listing.image_url }}" alt="{{ item.pack_listing.name }}" style="width: 50px; border-radius: 4px;">
{% else %}
<div style="width: 50px; height: 70px; background: #334155; border-radius: 4px;"></div>
{% endif %}
<div>
{% if item.listing %}
<h4 style="margin: 0; font-size: 1rem;">{{ item.listing.card.name }}</h4>
<p style="margin: 0.25rem 0 0; color: #94a3b8; font-size: 0.875rem;">
{{ item.listing.card.set.name }} • {{ item.listing.get_condition_display }} • {% if item.listing.is_foil %}Foil{% else %}Non-Foil{% endif %}
</p>
{% elif item.pack_listing %}
<h4 style="margin: 0; font-size: 1rem;">{{ item.pack_listing.name }}</h4>
<p style="margin: 0.25rem 0 0; color: #94a3b8; font-size: 0.875rem;">Booster Pack</p>
{% else %}
<h4 style="margin: 0; font-size: 1rem;">Unknown Item</h4>
{% endif %}
</div>
</div>
<div style="text-align: right;">
<p style="margin: 0; font-weight: 600;">${{ item.price_at_purchase }}</p>
<p style="margin: 0.25rem 0 0; color: #94a3b8; font-size: 0.875rem;">Qty: {{ item.quantity }}</p>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,30 @@
{% extends 'base/layout.html' %}
{% block content %}
<div style="max-width: 1200px; margin: 0 auto;">
<h1 style="margin-bottom: 2rem;">Booster Packs</h1>
<div class="card-grid">
{% for pack in packs %}
<div class="tcg-card">
<div style="aspect-ratio: 2.5/3.5; background: #334155; position: relative; display: flex; align-items: center; justify-content: center;">
{% if pack.image_url %}
<img src="{{ pack.image_url }}" alt="{{ pack.name }}" style="width: 100%; height: 100%; object-fit: cover;">
{% else %}
<div style="text-align: center; padding: 1rem;">
<div style="font-size: 3rem;">📦</div>
<div>{{ pack.game.name }}</div>
</div>
{% endif %}
</div>
<div class="tcg-card-body">
<h4 style="margin: 0 0 0.5rem; font-size: 1rem;">{{ pack.name }}</h4>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 1rem;">
<span style="font-weight: 700;">${{ pack.price }}</span>
<a href="{% url 'store:add_pack_to_cart' pack.id %}" class="btn" style="padding: 0.25rem 0.75rem; font-size: 0.875rem;">Add to Cart</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}