Admin: serie-detail, billedhåndtering, dark mode fix, source_url kolonne + verify

- Serie-detail side: sæt under serie + enkelt/bulk opret
- Billedhentning: URL→lokal download med source_url til re-sync
- Advarselsbadge (⚠) på kort med eksterne URLs (ingen streaming)
- Sync/Verify endpoints + knapper i admin UI
- Brugerstyring: opret/slet (self-delete beskyttet)
- Dark mode flash fix: render-blokerende script i <head>
- Persistent volume til billeder (pokedexter_images)
This commit is contained in:
2026-07-01 23:11:55 +02:00
parent 8361066f2f
commit 54465f751c
10 changed files with 678 additions and 113 deletions
+34 -1
View File
@@ -31,6 +31,15 @@
</form>
<div id="cards-import-result" style="margin-top:8px;"></div>
</div>
<div class="card" style="flex:1;min-width:280px;">
<h3>Synkronisér billeder</h3>
<p style="font-size:0.85rem;color:var(--text-muted);margin:8px 0;">
Hent alle eksterne billeder for {{ set.code }} og gem lokalt.
</p>
<button class="btn btn-primary" onclick="syncImages('{{ set.code }}')">📥 Synkronisér</button>
<div id="sync-result" style="margin-top:8px;"></div>
</div>
</div>
<h2>Kort i {{ set.code }}</h2>
@@ -45,7 +54,11 @@
<td><a href="/card/{{ set.code }}/{{ card.number }}" style="color:var(--text);">{{ card.name }}</a></td>
<td><span class="tag">{{ card.full_code }}</span></td>
<td>
{% if card.image_url %}
{% if card.image_path and (card.image_path.startswith('http://') or card.image_path.startswith('https://')) %}
<span class="warning-badge" title="Billede ikke synkroniseret — eksternt URL: {{ card.image_path }}">⚠️</span>
<span style="color:var(--accent);font-size:0.75rem;cursor:help;"
onclick="alert('{{ card.image_path }}')">eksternt</span>
{% elif card.image_url %}
<a href="{{ card.image_url }}" target="_blank">
<img src="{{ card.image_url }}" alt="{{ card.name }}"
style="height:40px;border-radius:4px;">
@@ -142,5 +155,25 @@ async function uploadImage(e, cardId) {
alert(data.detail || 'Fejl ved upload');
}
}
async function syncImages(setCode) {
const btn = event.target;
btn.disabled = true;
btn.textContent = 'Synkroniserer...';
const res = await fetch('/api/admin/sets/' + setCode + '/sync-images', {method: 'POST'});
const data = await res.json();
const el = document.getElementById('sync-result');
if (data.ok) {
let msg = '\u2713 ' + data.downloaded + ' billed(er) downloadet';
if (data.skipped > 0) msg += ', ' + data.skipped + ' sprunget over (allerede lokale)';
if (data.errors.length) msg += '<br><span style="color:var(--accent);">' + data.errors.join('<br>') + '</span>';
el.innerHTML = '<p style="color:green;">' + msg + '</p>';
setTimeout(() => location.reload(), 1500);
} else {
el.innerHTML = '<p style="color:var(--accent);">Fejl: ' + (data.detail || 'Ukendt') + '</p>';
}
btn.disabled = false;
btn.textContent = '\ud83d\udce5 Synkronis\u00e9r';
}
</script>
{% endblock %}