mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2026-03-18 11:16:30 +01:00
refactor: backend standardization (service layer, validation, route splitting) + image cache and Scryfall API fixes
This commit is contained in:
parent
e81b47bccf
commit
f784741416
35 changed files with 7054 additions and 4344 deletions
|
|
@ -10,11 +10,6 @@
|
|||
</aside>
|
||||
<div class="grow" data-skeleton>
|
||||
<div hx-get="/build/banner" hx-trigger="load"></div>
|
||||
{% if locks_restored and locks_restored > 0 %}
|
||||
<div class="muted" style="margin:.35rem 0;">
|
||||
<span class="chip" title="Locks restored from permalink">🔒 {{ locks_restored }} locks restored</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h4>Chosen Ideals</h4>
|
||||
<ul>
|
||||
{% for key, label in labels.items() %}
|
||||
|
|
|
|||
|
|
@ -186,9 +186,7 @@
|
|||
<span class="chip" title="Multi-Copy package summary"><span class="dot dot-purple"></span> {{ mc_summary }}</span>
|
||||
{% endif %}
|
||||
<span id="locks-chip">{% if locks and locks|length > 0 %}<span class="chip" title="Locked cards">🔒 {{ locks|length }} locked</span>{% endif %}</span>
|
||||
<button type="button" class="btn ml-auto" title="Copy permalink"
|
||||
onclick="(async()=>{try{const r=await fetch('/build/permalink');const j=await r.json();const url=(j.permalink?location.origin+j.permalink:location.href+'#'+btoa(JSON.stringify(j.state||{}))); await navigator.clipboard.writeText(url); toast && toast('Permalink copied');}catch(e){alert('Copied state to console'); console.log(e);}})()">Copy Permalink</button>
|
||||
<button type="button" class="btn" title="Open a saved permalink" onclick="(function(){try{var token = prompt('Paste a /build/from?state=... URL or token:'); if(!token) return; var m = token.match(/state=([^&]+)/); var t = m? m[1] : token.trim(); if(!t) return; window.location.href = '/build/from?state=' + encodeURIComponent(t); }catch(_){}})()">Open Permalink…</button>
|
||||
|
||||
</div>
|
||||
{% set pct = ((deck_count / 100.0) * 100.0) if deck_count else 0 %}
|
||||
{% set pct_clamped = (pct if pct <= 100 else 100) %}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
<a href="/decks/compare" class="btn" role="button" title="Compare two finished decks">Compare</a>
|
||||
<button id="deck-compare-selected" type="button" title="Compare two selected decks" disabled>Compare selected</button>
|
||||
<button id="deck-compare-latest" type="button" title="Pick the latest two decks">Latest two</button>
|
||||
<button id="deck-open-permalink" type="button" title="Open a saved permalink">Open Permalink…</button>
|
||||
<button id="deck-reset-all" type="button" title="Reset filter, sort, and theme">Reset all</button>
|
||||
<button id="deck-help" type="button" title="Keyboard shortcuts and tips" aria-haspopup="dialog" aria-controls="deck-help-modal">Help</button>
|
||||
<span id="deck-count" class="muted" aria-live="polite"></span>
|
||||
|
|
@ -127,7 +126,6 @@
|
|||
var txtOnlyCb = document.getElementById('deck-txt-only');
|
||||
var cmpSelBtn = document.getElementById('deck-compare-selected');
|
||||
var cmpLatestBtn = document.getElementById('deck-compare-latest');
|
||||
var openPermalinkBtn = document.getElementById('deck-open-permalink');
|
||||
if (!list) return;
|
||||
|
||||
// Panels and themes discovery from data-tags-pipe
|
||||
|
|
@ -416,18 +414,6 @@
|
|||
} catch(_){ }
|
||||
});
|
||||
|
||||
// Open permalink prompt
|
||||
if (openPermalinkBtn) openPermalinkBtn.addEventListener('click', function(){
|
||||
try{
|
||||
var token = prompt('Paste a /build/from?state=... URL or token:');
|
||||
if(!token) return;
|
||||
var m = token.match(/state=([^&]+)/);
|
||||
var t = m ? m[1] : token.trim();
|
||||
if(!t) return;
|
||||
window.location.href = '/build/from?state=' + encodeURIComponent(t);
|
||||
}catch(_){ }
|
||||
});
|
||||
|
||||
if (resetAllBtn) resetAllBtn.addEventListener('click', function(){
|
||||
// Clear UI state
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -18,9 +18,7 @@
|
|||
<div class="random-meta" style="display:flex; gap:12px; align-items:center; flex-wrap:wrap;">
|
||||
<span class="seed">Seed: <strong>{{ seed }}</strong></span>
|
||||
{% if theme %}<span class="theme">Theme: <strong>{{ theme }}</strong></span>{% endif %}
|
||||
{% if permalink %}
|
||||
<button class="btn btn-compact" type="button" aria-label="Copy permalink for this exact build" onclick="(async()=>{try{await navigator.clipboard.writeText(location.origin + '{{ permalink }}');(window.toast&&toast('Permalink copied'))||console.log('Permalink copied');}catch(e){alert('Copy failed');}})()">Copy Permalink</button>
|
||||
{% endif %}
|
||||
|
||||
{% if show_diagnostics and diagnostics %}
|
||||
<span class="diag-badges" aria-label="Diagnostics" role="status" aria-live="polite" aria-atomic="true">
|
||||
<span class="diag-badge" title="Attempts tried before acceptance" aria-label="Attempts tried before acceptance">
|
||||
|
|
|
|||
|
|
@ -148,10 +148,10 @@
|
|||
</section>
|
||||
<script>
|
||||
(function(){
|
||||
// Minimal styling helper to unify button widths
|
||||
// Minimal styling helper to unify button widths (only for content buttons)
|
||||
try {
|
||||
var style = document.createElement('style');
|
||||
style.textContent = '.btn{min-width:180px;}';
|
||||
style.textContent = '.content .btn{min-width:180px;}';
|
||||
document.head.appendChild(style);
|
||||
} catch(e){}
|
||||
function update(data){
|
||||
|
|
@ -325,27 +325,38 @@
|
|||
statusLine.style.color = '#94a3b8';
|
||||
if (statsLine) statsLine.style.display = 'none';
|
||||
} else {
|
||||
var totalCount = 0;
|
||||
var totalSizeMB = 0;
|
||||
|
||||
if (stats.small) {
|
||||
totalCount += stats.small.count || 0;
|
||||
totalSizeMB += stats.small.size_mb || 0;
|
||||
}
|
||||
if (stats.normal) {
|
||||
totalCount += stats.normal.count || 0;
|
||||
totalSizeMB += stats.normal.size_mb || 0;
|
||||
}
|
||||
|
||||
if (totalCount > 0) {
|
||||
// Card count = use the max across sizes (each card has one image per size, so avoid double-counting)
|
||||
var cardCount = Math.max(
|
||||
(stats.small && stats.small.count) || 0,
|
||||
(stats.normal && stats.normal.count) || 0
|
||||
);
|
||||
var totalSizeMB = ((stats.small && stats.small.size_mb) || 0) + ((stats.normal && stats.normal.size_mb) || 0);
|
||||
var sizeCount = (stats.small ? 1 : 0) + (stats.normal ? 1 : 0);
|
||||
|
||||
if (cardCount > 0) {
|
||||
statusLine.textContent = 'Cache exists';
|
||||
statusLine.style.color = '#34d399';
|
||||
if (statsLine) {
|
||||
statsLine.style.display = '';
|
||||
statsLine.textContent = totalCount.toLocaleString() + ' images cached • ' + totalSizeMB.toFixed(1) + ' MB';
|
||||
var statsText = cardCount.toLocaleString() + ' cards cached • ' + totalSizeMB.toFixed(1) + ' MB';
|
||||
// If we have last download info, append new card count
|
||||
if (data.last_download) {
|
||||
var ld = data.last_download;
|
||||
if (ld.stats && typeof ld.stats.downloaded === 'number') {
|
||||
var newCards = sizeCount > 0 ? Math.round(ld.stats.downloaded / sizeCount) : ld.stats.downloaded;
|
||||
if (newCards > 0) {
|
||||
statsText += ' • Last run: +' + newCards.toLocaleString() + ' new cards';
|
||||
} else {
|
||||
statsText += ' • Last run: fully up to date';
|
||||
}
|
||||
} else if (ld.message) {
|
||||
statsText += ' • ' + ld.message;
|
||||
}
|
||||
}
|
||||
statsLine.textContent = statsText;
|
||||
}
|
||||
} else {
|
||||
statusLine.textContent = 'No images cached';
|
||||
statusLine.textContent = 'No cards cached';
|
||||
statusLine.style.color = '#94a3b8';
|
||||
if (statsLine) statsLine.style.display = 'none';
|
||||
}
|
||||
|
|
@ -354,6 +365,23 @@
|
|||
// Hide download progress
|
||||
if (downloadStatus) downloadStatus.style.display = 'none';
|
||||
if (progressBar) progressBar.style.display = 'none';
|
||||
} else if (data.phase === 'error' || data.message) {
|
||||
// Previous download failed - show error and allow retry
|
||||
statusLine.textContent = 'Last download failed';
|
||||
statusLine.style.color = '#f87171';
|
||||
if (statsLine) {
|
||||
statsLine.style.display = '';
|
||||
statsLine.textContent = data.message || 'Unknown error';
|
||||
}
|
||||
if (downloadStatus) downloadStatus.style.display = 'none';
|
||||
if (progressBar) progressBar.style.display = 'none';
|
||||
} else {
|
||||
// No stats, no error - likely no download attempted yet
|
||||
statusLine.textContent = 'No cards cached';
|
||||
statusLine.style.color = '#94a3b8';
|
||||
if (statsLine) statsLine.style.display = 'none';
|
||||
if (downloadStatus) downloadStatus.style.display = 'none';
|
||||
if (progressBar) progressBar.style.display = 'none';
|
||||
}
|
||||
})
|
||||
.catch(function(){
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue